diff --git a/src/kernel_services/ast_data/property.ml b/src/kernel_services/ast_data/property.ml
index 2be8813645a9ca63fd7b2c7403205f869ed79f77..532097186425bb770369cd7a91451ad21948a687 100644
--- a/src/kernel_services/ast_data/property.ml
+++ b/src/kernel_services/ast_data/property.ml
@@ -412,6 +412,141 @@ let rec has_status = function
 (* -------------------------------------------------------------------------- *)
 (* --- Datatype                                                           --- *)
 (* -------------------------------------------------------------------------- *)
+let pp_active fmt active =
+  let sep = ref false in
+  let print_one a =
+    Format.fprintf fmt "%s%s" (if !sep then ", " else "") a;
+    sep:=true
+  in
+  Datatype.String.Set.iter print_one active
+
+let rec pretty_ip fmt = function
+  | IPPredicate {ip_kind; ip_pred} ->
+    Format.fprintf fmt "%a@ %a"
+      pretty_predicate_kind ip_kind
+      Cil_printer.pp_identified_predicate ip_pred
+  | IPExtended {ie_ext} -> Cil_printer.pp_extended fmt ie_ext
+  | IPAxiom {il_name} -> Format.fprintf fmt "axiom@ %s" il_name
+  | IPAxiomatic {iax_name} -> Format.fprintf fmt "axiomatic@ %s" iax_name
+  | IPLemma {il_name} -> Format.fprintf fmt "lemma@ %s" il_name
+  | IPTypeInvariant {iti_name; iti_type} ->
+    Format.fprintf fmt "invariant@ %s for type %a" iti_name
+      Cil_printer.pp_typ iti_type
+  | IPGlobalInvariant {igi_name} ->
+    Format.fprintf fmt "global invariant@ %s" igi_name
+  | IPBehavior {ib_bhv; ib_kinstr; ib_active} ->
+    if Cil.is_default_behavior ib_bhv then
+      Format.pp_print_string fmt "default behavior"
+    else
+      Format.fprintf fmt "behavior %s" ib_bhv.b_name;
+    (match ib_kinstr with
+     | Kstmt s -> Format.fprintf fmt " for statement %d" s.sid
+     | Kglobal -> ());
+    pp_active fmt ib_active
+  | IPCodeAnnot {ica_ca} -> Cil_printer.pp_code_annotation fmt ica_ca
+  | IPComplete {ic_active; ic_bhvs} ->
+    Format.fprintf fmt "complete@ %a"
+      (Pretty_utils.pp_iter ~sep:","
+         Datatype.String.Set.iter
+         (fun fmt s -> Format.fprintf fmt "@ %s" s))
+      ic_bhvs;
+    pp_active fmt ic_active
+  | IPDisjoint {ic_active; ic_bhvs} ->
+    Format.fprintf fmt "disjoint@ %a"
+      (Pretty_utils.pp_iter ~sep:","
+         Datatype.String.Set.iter
+         (fun fmt s -> Format.fprintf fmt "@ %s" s))
+      ic_bhvs;
+    pp_active fmt ic_active
+  | IPAllocation {ial_allocs=(f,a)} ->
+    Cil_printer.pp_allocation fmt (FreeAlloc(f,a))
+  | IPAssigns {ias_froms} -> Cil_printer.pp_assigns fmt (Writes ias_froms)
+  | IPFrom {if_from} -> Cil_printer.pp_from fmt if_from
+  | IPDecrease {id_ca=None; id_variant=v} -> Cil_printer.pp_decreases fmt v
+  | IPDecrease {id_variant=v} -> Cil_printer.pp_variant fmt v
+  | IPReachable {ir_kf=None; ir_kinstr=Kstmt _} ->  assert false
+  | IPReachable {ir_kf=None; ir_kinstr=Kglobal} ->
+    Format.fprintf fmt "reachability of entry point"
+  | IPReachable {ir_kf=Some kf; ir_kinstr=Kglobal} ->
+    Format.fprintf fmt "reachability of function %a" Kf.pretty kf
+  | IPReachable {ir_kf=Some kf; ir_kinstr=Kstmt stmt; ir_program_point=ba} ->
+    Format.fprintf fmt "reachability %s stmt %a in %a"
+      (match ba with Before -> "of" | After -> "post")
+      Cil_datatype.Location.pretty_line (Cil_datatype.Stmt.loc stmt)
+      Kf.pretty kf
+  | IPPropertyInstance {ii_kf; ii_stmt; ii_ip} ->
+    Format.fprintf fmt "status of '%a'%t %a"
+      pretty_ip ii_ip
+      (fun fmt -> match get_kf ii_ip with
+         | Some kf -> Format.fprintf fmt " of %a" Kernel_function.pretty kf
+         | None -> ())
+      pretty_instance_location (ii_kf, ii_stmt)
+  | IPOther {io_name} -> Format.pp_print_string fmt io_name
+
+let rec hash_ip =
+  let hash_bhv_loop = function
+    | Id_contract (a,b) -> (0, Hashtbl.hash (a,b.b_name))
+    | Id_loop ca -> (1, ca.annot_id)
+  in
+  function
+  | IPPredicate {ip_pred=x} -> Hashtbl.hash (1, x.ip_id)
+  | IPAxiom {il_name=x} -> Hashtbl.hash (2, (x:string))
+  | IPAxiomatic {iax_name=x} -> Hashtbl.hash (3, (x:string))
+  | IPLemma {il_name=x} -> Hashtbl.hash (4, (x:string))
+  | IPCodeAnnot {ica_ca=ca} -> Hashtbl.hash (5, ca.annot_id)
+  | IPComplete {ic_kf=f; ic_kinstr=ki; ic_bhvs=y; ic_active=x} ->
+    (* complete list is more likely to discriminate than active list. *)
+    Hashtbl.hash
+      (6, Kf.hash f, Kinstr.hash ki,
+       Datatype.String.Set.hash y, Datatype.String.Set.hash x)
+  | IPDisjoint {ic_kf=f; ic_kinstr=ki; ic_bhvs=y; ic_active=x} ->
+    Hashtbl.hash
+      (7, Kf.hash f, Kinstr.hash ki,
+       Datatype.String.Set.hash y, Datatype.String.Set.hash x)
+  | IPAssigns {ias_kf=f; ias_kinstr=ki; ias_bhv=b} ->
+    Hashtbl.hash (8, Kf.hash f, Kinstr.hash ki, hash_bhv_loop b)
+  | IPFrom {if_kf=kf; if_kinstr=ki; if_bhv=b; if_from=(t, _)} ->
+    Hashtbl.hash
+      (9, Kf.hash kf, Kinstr.hash ki,
+       hash_bhv_loop b, Identified_term.hash t)
+  | IPDecrease {id_kf=kf; id_kinstr=ki} ->
+    (* At most one loop variant per statement anyway, no
+       need to discriminate against the code annotation itself *)
+    Hashtbl.hash (10, Kf.hash kf, Kinstr.hash ki)
+  | IPBehavior {ib_kf=kf; ib_kinstr=s; ib_active=a; ib_bhv=b} ->
+    Hashtbl.hash
+      (11, Kf.hash kf, Kinstr.hash s,
+       (b.b_name:string), (a:Datatype.String.Set.t))
+  | IPReachable {ir_kf=kf; ir_kinstr=ki; ir_program_point=ba} ->
+    Hashtbl.hash(12, Option.fold ~some:Kf.hash ~none:0 kf,
+                 Kinstr.hash ki, Hashtbl.hash ba)
+  | IPAllocation {ial_kf=f; ial_kinstr=ki; ial_bhv=b} ->
+    Hashtbl.hash (13, Kf.hash f, Kinstr.hash ki, hash_bhv_loop b)
+  | IPPropertyInstance {ii_kf=kf_caller; ii_stmt=stmt; ii_ip=ip} ->
+    Hashtbl.hash (14, Kf.hash kf_caller,
+                  Stmt.hash stmt, hash_ip ip)
+  | IPOther {io_name=s} -> Hashtbl.hash (15, (s:string))
+  | IPTypeInvariant {iti_name=s} -> Hashtbl.hash (16, (s:string))
+  | IPGlobalInvariant {igi_name=s} -> Hashtbl.hash (17, (s:string))
+  | IPExtended {ie_ext={ext_id}} -> Hashtbl.hash (18, ext_id)
+
+let reprs = [
+  IPAxiom {
+    il_name="";il_labels=[];il_args=[];
+    il_pred=Logic_const.(toplevel_predicate ptrue);
+    il_attrs=[];
+    il_loc=Location.unknown
+  }]
+
+let compare_behavior_or_loop b1 b2 =
+  match b1, b2 with
+  | Id_contract (a1,b1), Id_contract (a2,b2) ->
+    let n = Datatype.String.compare b1.b_name b2.b_name in
+    if n = 0 then Datatype.String.Set.compare a1 a2 else n
+  | Id_loop ca1, Id_loop ca2 ->
+    Datatype.Int.compare ca1.annot_id ca2.annot_id
+  | Id_contract _, Id_loop _ -> -1
+  | Id_loop _, Id_contract _ -> 1
 
 include Datatype.Make_with_collections
     (struct
@@ -420,133 +555,14 @@ include Datatype.Make_with_collections
 
       type t = identified_property
       let name = "Property.t"
-      let reprs = [
-        IPAxiom {
-          il_name="";il_labels=[];il_args=[];
-          il_pred=Logic_const.(toplevel_predicate ptrue);
-          il_attrs=[];
-          il_loc=Location.unknown
-        }]
+
+      let reprs = reprs
 
       let mem_project = Datatype.never_any_project
 
-      let pp_active fmt active =
-        let sep = ref false in
-        let print_one a =
-          Format.fprintf fmt "%s%s" (if !sep then ", " else "") a;
-          sep:=true
-        in
-        Datatype.String.Set.iter print_one active
-
-      let rec pretty fmt = function
-        | IPPredicate {ip_kind; ip_pred} ->
-          Format.fprintf fmt "%a@ %a"
-            pretty_predicate_kind ip_kind
-            Cil_printer.pp_identified_predicate ip_pred
-        | IPExtended {ie_ext} -> Cil_printer.pp_extended fmt ie_ext
-        | IPAxiom {il_name} -> Format.fprintf fmt "axiom@ %s" il_name
-        | IPAxiomatic {iax_name} -> Format.fprintf fmt "axiomatic@ %s" iax_name
-        | IPLemma {il_name} -> Format.fprintf fmt "lemma@ %s" il_name
-        | IPTypeInvariant {iti_name; iti_type} ->
-          Format.fprintf fmt "invariant@ %s for type %a" iti_name
-            Cil_printer.pp_typ iti_type
-        | IPGlobalInvariant {igi_name} ->
-          Format.fprintf fmt "global invariant@ %s" igi_name
-        | IPBehavior {ib_bhv; ib_kinstr; ib_active} ->
-          if Cil.is_default_behavior ib_bhv then
-            Format.pp_print_string fmt "default behavior"
-          else
-            Format.fprintf fmt "behavior %s" ib_bhv.b_name;
-          (match ib_kinstr with
-           | Kstmt s -> Format.fprintf fmt " for statement %d" s.sid
-           | Kglobal -> ());
-          pp_active fmt ib_active
-        | IPCodeAnnot {ica_ca} -> Cil_printer.pp_code_annotation fmt ica_ca
-        | IPComplete {ic_active; ic_bhvs} ->
-          Format.fprintf fmt "complete@ %a"
-            (Pretty_utils.pp_iter ~sep:","
-               Datatype.String.Set.iter
-               (fun fmt s -> Format.fprintf fmt "@ %s" s))
-            ic_bhvs;
-          pp_active fmt ic_active
-        | IPDisjoint {ic_active; ic_bhvs} ->
-          Format.fprintf fmt "disjoint@ %a"
-            (Pretty_utils.pp_iter ~sep:","
-               Datatype.String.Set.iter
-               (fun fmt s -> Format.fprintf fmt "@ %s" s))
-            ic_bhvs;
-          pp_active fmt ic_active
-        | IPAllocation {ial_allocs=(f,a)} ->
-          Cil_printer.pp_allocation fmt (FreeAlloc(f,a))
-        | IPAssigns {ias_froms} -> Cil_printer.pp_assigns fmt (Writes ias_froms)
-        | IPFrom {if_from} -> Cil_printer.pp_from fmt if_from
-        | IPDecrease {id_ca=None; id_variant=v} -> Cil_printer.pp_decreases fmt v
-        | IPDecrease {id_variant=v} -> Cil_printer.pp_variant fmt v
-        | IPReachable {ir_kf=None; ir_kinstr=Kstmt _} ->  assert false
-        | IPReachable {ir_kf=None; ir_kinstr=Kglobal} ->
-          Format.fprintf fmt "reachability of entry point"
-        | IPReachable {ir_kf=Some kf; ir_kinstr=Kglobal} ->
-          Format.fprintf fmt "reachability of function %a" Kf.pretty kf
-        | IPReachable {ir_kf=Some kf; ir_kinstr=Kstmt stmt; ir_program_point=ba} ->
-          Format.fprintf fmt "reachability %s stmt %a in %a"
-            (match ba with Before -> "of" | After -> "post")
-            Cil_datatype.Location.pretty_line (Cil_datatype.Stmt.loc stmt)
-            Kf.pretty kf
-        | IPPropertyInstance {ii_kf; ii_stmt; ii_ip} ->
-          Format.fprintf fmt "status of '%a'%t %a"
-            pretty ii_ip
-            (fun fmt -> match get_kf ii_ip with
-               | Some kf -> Format.fprintf fmt " of %a" Kernel_function.pretty kf
-               | None -> ())
-            pretty_instance_location (ii_kf, ii_stmt)
-        | IPOther {io_name} -> Format.pp_print_string fmt io_name
-
-      let rec hash =
-        let hash_bhv_loop = function
-          | Id_contract (a,b) -> (0, Hashtbl.hash (a,b.b_name))
-          | Id_loop ca -> (1, ca.annot_id)
-        in
-        function
-        | IPPredicate {ip_pred=x} -> Hashtbl.hash (1, x.ip_id)
-        | IPAxiom {il_name=x} -> Hashtbl.hash (2, (x:string))
-        | IPAxiomatic {iax_name=x} -> Hashtbl.hash (3, (x:string))
-        | IPLemma {il_name=x} -> Hashtbl.hash (4, (x:string))
-        | IPCodeAnnot {ica_ca=ca} -> Hashtbl.hash (5, ca.annot_id)
-        | IPComplete {ic_kf=f; ic_kinstr=ki; ic_bhvs=y; ic_active=x} ->
-          (* complete list is more likely to discriminate than active list. *)
-          Hashtbl.hash
-            (6, Kf.hash f, Kinstr.hash ki,
-             Datatype.String.Set.hash y, Datatype.String.Set.hash x)
-        | IPDisjoint {ic_kf=f; ic_kinstr=ki; ic_bhvs=y; ic_active=x} ->
-          Hashtbl.hash
-            (7, Kf.hash f, Kinstr.hash ki,
-             Datatype.String.Set.hash y, Datatype.String.Set.hash x)
-        | IPAssigns {ias_kf=f; ias_kinstr=ki; ias_bhv=b} ->
-          Hashtbl.hash (8, Kf.hash f, Kinstr.hash ki, hash_bhv_loop b)
-        | IPFrom {if_kf=kf; if_kinstr=ki; if_bhv=b; if_from=(t, _)} ->
-          Hashtbl.hash
-            (9, Kf.hash kf, Kinstr.hash ki,
-             hash_bhv_loop b, Identified_term.hash t)
-        | IPDecrease {id_kf=kf; id_kinstr=ki} ->
-          (* At most one loop variant per statement anyway, no
-             need to discriminate against the code annotation itself *)
-          Hashtbl.hash (10, Kf.hash kf, Kinstr.hash ki)
-        | IPBehavior {ib_kf=kf; ib_kinstr=s; ib_active=a; ib_bhv=b} ->
-          Hashtbl.hash
-            (11, Kf.hash kf, Kinstr.hash s,
-             (b.b_name:string), (a:Datatype.String.Set.t))
-        | IPReachable {ir_kf=kf; ir_kinstr=ki; ir_program_point=ba} ->
-          Hashtbl.hash(12, Option.fold ~some:Kf.hash ~none:0 kf,
-                       Kinstr.hash ki, Hashtbl.hash ba)
-        | IPAllocation {ial_kf=f; ial_kinstr=ki; ial_bhv=b} ->
-          Hashtbl.hash (13, Kf.hash f, Kinstr.hash ki, hash_bhv_loop b)
-        | IPPropertyInstance {ii_kf=kf_caller; ii_stmt=stmt; ii_ip=ip} ->
-          Hashtbl.hash (14, Kf.hash kf_caller,
-                        Stmt.hash stmt, hash ip)
-        | IPOther {io_name=s} -> Hashtbl.hash (15, (s:string))
-        | IPTypeInvariant {iti_name=s} -> Hashtbl.hash (16, (s:string))
-        | IPGlobalInvariant {igi_name=s} -> Hashtbl.hash (17, (s:string))
-        | IPExtended {ie_ext={ext_id}} -> Hashtbl.hash (18, ext_id)
+      let pretty = pretty_ip
+
+      let hash = hash_ip
 
       let rec equal p1 p2 =
         let eq_bhv (f1,ki1,b1) (f2,ki2,b2) =
@@ -577,7 +593,8 @@ include Datatype.Make_with_collections
           IPComplete {ic_kf=f2;ic_kinstr=ki2;ic_active=a2;ic_bhvs=x2}
         | IPDisjoint {ic_kf=f1;ic_kinstr=ki1;ic_active=a1;ic_bhvs=x1},
           IPDisjoint {ic_kf=f2;ic_kinstr=ki2;ic_active=a2;ic_bhvs=x2} ->
-          Kf.equal f1 f2 && Kinstr.equal ki1 ki2 && a1 = a2 && Datatype.String.Set.equal x1 x2
+          Kf.equal f1 f2 && Kinstr.equal ki1 ki2 && a1 = a2
+          && Datatype.String.Set.equal x1 x2
         | IPAllocation {ial_kf=f1;ial_kinstr=ki1;ial_bhv=b1},
           IPAllocation {ial_kf=f2;ial_kinstr=ki2;ial_bhv=b2}
         | IPAssigns {ias_kf=f1;ias_kinstr=ki1;ias_bhv=b1},
@@ -617,14 +634,7 @@ include Datatype.Make_with_collections
           if n = 0 then
             let n = Kinstr.compare ki1 ki2 in
             if n = 0 then
-              match b1, b2 with
-              | Id_contract (a1,b1), Id_contract (a2,b2) ->
-                let n = Datatype.String.compare b1.b_name b2.b_name in
-                if n = 0 then Datatype.String.Set.compare a1 a2 else n
-              | Id_loop ca1, Id_loop ca2 ->
-                Datatype.Int.compare ca1.annot_id ca2.annot_id
-              | Id_contract _, Id_loop _ -> -1
-              | Id_loop _, Id_contract _ -> 1
+              compare_behavior_or_loop b1 b2
             else n
           else n
         in
@@ -718,6 +728,129 @@ include Datatype.Make_with_collections
 
     end)
 
+module Ordered_by_function = Datatype.Make_with_collections(
+  struct
+    include Datatype.Serializable_undefined
+    type t = identified_property
+    let name = "Property.Ordered_by_function"
+    let reprs = reprs
+    let hash = hash_ip
+    let pretty = pretty_ip
+
+    (* be sure to keep cmp_same_kind synchronized with this function. *)
+    let cmp_kind p1 p2 =
+      let nb = function
+        | IPAxiomatic _ -> 1
+        | IPAxiom _ -> 2
+        | IPLemma _ -> 3
+        | IPTypeInvariant _ -> 4
+        | IPGlobalInvariant _ -> 5
+        | IPPropertyInstance _ -> 6
+        | IPBehavior _ -> 7
+        | IPPredicate { ip_kind = PKRequires _ } -> 8
+        | IPPredicate { ip_kind = PKAssumes _ } -> 9
+        | IPPredicate { ip_kind = PKEnsures _ } -> 10
+        | IPCodeAnnot { ica_ca = { annot_content = AAssert _ }} -> 11
+        | IPCodeAnnot { ica_ca = { annot_content = AInvariant _ }} -> 12
+        | IPCodeAnnot { ica_ca = { annot_content = APragma _ }} -> 13
+        | IPAssigns _ -> 14
+        | IPFrom _ -> 15
+        | IPAllocation _ -> 16
+        | IPPredicate { ip_kind = PKTerminates } -> 17
+        | IPDecrease _ -> 18
+        | IPReachable _ -> 18
+        | IPComplete _ -> 19
+        | IPDisjoint _ -> 20
+        | IPExtended _ -> 21
+        | IPOther _ -> 22
+        | IPCodeAnnot ca ->
+          Kernel.fatal "Unexpected code annot %a in identified property"
+            Cil_printer.pp_code_annotation ca.ica_ca
+      in
+      Datatype.Int.compare (nb p1) (nb p2)
+
+    let rec cmp_same_kind p1 p2 =
+      match (p1,p2) with
+      | IPAxiomatic { iax_name = n1 }, IPAxiomatic { iax_name = n2 }
+      | IPAxiom { il_name = n1 }, IPAxiom { il_name = n2 }
+      | IPLemma { il_name = n1 }, IPAxiom { il_name = n2 }
+      | IPTypeInvariant { iti_name = n1 }, IPTypeInvariant { iti_name = n2 }
+      | IPGlobalInvariant { igi_name = n1 },
+        IPGlobalInvariant { igi_name = n2 }
+        ->
+        String.compare n1 n2
+      | IPPropertyInstance { ii_ip = p1 }, IPPropertyInstance { ii_ip = p2 }
+        ->
+        let res = cmp_kind p1 p2 in
+        if res <> 0 then res else cmp_same_kind p1 p2
+      | IPBehavior { ib_active = a1; ib_bhv = b1 },
+        IPBehavior { ib_active = a2; ib_bhv = b2 } ->
+        compare_behavior_or_loop (Id_contract(a1,b1)) (Id_contract(a2,b2))
+      | IPPredicate { ip_pred = i1 }, IPPredicate { ip_pred = i2 } ->
+        Datatype.Int.compare i1.ip_id i2.ip_id
+      | IPCodeAnnot { ica_ca = a1 }, IPCodeAnnot { ica_ca = a2 } ->
+        Datatype.Int.compare a1.annot_id a2.annot_id
+      | IPAssigns { ias_bhv = b1 }, IPAssigns { ias_bhv = b2 }
+      | IPAllocation { ial_bhv = b1 }, IPAllocation { ial_bhv = b2 } ->
+        compare_behavior_or_loop b1 b2
+      | IPFrom { if_bhv = b1; if_from = (f1,_) },
+        IPFrom { if_bhv = b2; if_from = (f2,_) } ->
+        let res = compare_behavior_or_loop b1 b2 in
+        if res <> 0 then res
+        else Datatype.Int.compare f1.it_id f2.it_id
+      (* at most one decrease per statement *)
+      | IPDecrease _, IPDecrease _ -> 0
+      | IPReachable { ir_program_point = Before },
+        IPReachable { ir_program_point = After } -> -1
+      | IPReachable { ir_program_point = After },
+        IPReachable { ir_program_point = Before } -> 1
+      | IPReachable _, IPReachable _ -> 0
+
+      | IPComplete { ic_active = b1; ic_bhvs = s1 },
+        IPComplete { ic_active = b2; ic_bhvs = s2 }
+      | IPDisjoint { ic_active = b1; ic_bhvs = s1 },
+        IPDisjoint { ic_active = b2; ic_bhvs = s2 } ->
+        let res = Datatype.String.Set.compare b1 b2 in
+        if res <> 0 then res
+        else Datatype.String.Set.compare s1 s2
+      | IPExtended { ie_ext = e1 }, IPExtended { ie_ext = e2 } ->
+        Datatype.Int.compare e1.ext_id e2.ext_id
+      | IPOther { io_name = n1; io_loc = l1 },
+        IPOther { io_name = n2; io_loc = l2 } ->
+        let res = other_loc_compare l1 l2 in
+        if res <> 0 then res
+        else String.compare n1 n2
+      | _ ->
+        Kernel.fatal
+          "Property.cmp_same_kind called with 2 arguments of different kind"
+
+    let compare p1 p2 =
+      let kf1 = get_kf p1 and kf2 = get_kf p2 in
+      let cmp_kf kf1 kf2 =
+        String.compare
+          (Kernel_function.get_name kf1) (Kernel_function.get_name kf2)
+      in
+      let res = Option.compare cmp_kf kf1 kf2 in
+      if res <> 0 then res
+      else begin
+        let ki1 = get_kinstr p1 and ki2 = get_kinstr p2 in
+        let res =
+          match ki1, ki2 with
+          | Kglobal, Kglobal -> 0
+          | Kstmt _, Kglobal -> 1
+          | Kglobal, Kstmt _ -> -1
+          | Kstmt s1, Kstmt s2 -> Datatype.Int.compare s1.sid s2.sid
+        in
+        if res <> 0 then res
+        else begin
+          let res = cmp_kind p1 p2 in
+          if res <> 0 then res
+          else cmp_same_kind p1 p2
+        end
+      end
+    let equal = Datatype.from_compare
+  end)
+
 let rec short_pretty fmt p = match p with
   | IPPredicate {ip_pred} ->
     (match (Logic_const.pred_of_id_pred ip_pred).pred_name with
diff --git a/src/kernel_services/ast_data/property.mli b/src/kernel_services/ast_data/property.mli
index 6ad7e28371f3d73dfba4d67ff5a61f5dd1d98164..ab15f381a1b032e7a41c83ed3ffad3891ab39261 100644
--- a/src/kernel_services/ast_data/property.mli
+++ b/src/kernel_services/ast_data/property.mli
@@ -214,6 +214,18 @@ and identified_property = private
 
 include Datatype.S_with_collections with type t = identified_property
 
+(** Datatype with alternative ordering, where properties are ordered according
+    the following criteria:
+    1. Kf name (global properties ranked first)
+    2. Kinstr
+    3. kind of property
+    4. id of the property
+
+    @since Frama-C+dev
+*)
+module Ordered_by_function:
+  Datatype.S_with_collections with type t = identified_property
+
 val short_pretty: Format.formatter -> t -> unit
 (** output a meaningful name for the property (e.g. the name of the
     corresponding identified predicate when available)