Skip to content

Commit 2f54f58

Browse files
Merge pull request #577 from mikcp/unrolling_abstract_domain
Array unrolling abstract domain
2 parents 695a693 + d8d16b6 commit 2f54f58

File tree

101 files changed

+428
-152
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+428
-152
lines changed

conf/svcomp.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"ana": {
33
"base": {
4-
"partition-arrays": {
5-
"enabled": true
4+
"arrays": {
5+
"domain": "partitioned"
66
}
77
},
88
"sv-comp": {
@@ -69,4 +69,4 @@
6969
"signed_overflow": "assume_none"
7070
}
7171
}
72-
}
72+
}

conf/svcomp21.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
{
22
"ana": {
3-
"base": {
4-
"partition-arrays": {
5-
"enabled": true
6-
}
7-
},
83
"sv-comp": {
94
"enabled": true,
105
"functions": true
@@ -46,6 +41,11 @@
4641
"kmalloc_array",
4742
"kcalloc"
4843
]
44+
},
45+
"base": {
46+
"arrays": {
47+
"domain": "partitioned"
48+
}
4949
}
5050
},
5151
"exp": {

conf/svcomp22.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
{
22
"ana": {
3-
"base": {
4-
"partition-arrays": {
5-
"enabled": true
6-
}
7-
},
83
"sv-comp": {
94
"enabled": true,
105
"functions": true
@@ -46,6 +41,11 @@
4641
"kmalloc_array",
4742
"kcalloc"
4843
]
44+
},
45+
"base": {
46+
"arrays": {
47+
"domain": "partitioned"
48+
}
4949
}
5050
},
5151
"exp": {

src/cdomains/arrayDomain.ml

Lines changed: 186 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,94 @@ struct
6969
let update_length _ x = x
7070
end
7171

72+
module Unroll (Val: Lattice.S) (Idx:IntDomain.Z): S with type value = Val.t and type idx = Idx.t =
73+
struct
74+
module Factor = struct let x () = (get_int "ana.base.arrays.unrolling-factor") end
75+
module Base = Lattice.ProdList (Val) (Factor)
76+
include Lattice.ProdSimple(Base) (Val)
77+
78+
let name () = "unrolled arrays"
79+
type idx = Idx.t
80+
type value = Val.t
81+
let factor () =
82+
match get_int "ana.base.arrays.unrolling-factor" with
83+
| 0 -> failwith "ArrayDomain: ana.base.arrays.unrolling-factor needs to be set when using the unroll domain"
84+
| x -> x
85+
let join_of_all_parts (xl, xr) = List.fold_left Val.join xr xl
86+
let show (xl, xr) =
87+
let rec show_list xlist = match xlist with
88+
| [] -> " --- "
89+
| hd::tl -> (Val.show hd ^ " - " ^ (show_list tl)) in
90+
"Array (unrolled to " ^ (Stdlib.string_of_int (factor ())) ^ "): " ^
91+
(show_list xl) ^ Val.show xr ^ ")"
92+
let pretty () x = text "Array: " ++ text (show x)
93+
let pretty_diff () (x,y) = dprintf "%s: %a not leq %a" (name ()) pretty x pretty y
94+
let extract x = match x with
95+
| Some c -> c
96+
| None -> failwith "arrarDomain: that sould not happen"
97+
let get (ask: Q.ask) (xl, xr) (_,i) =
98+
let search_unrolled_values min_i max_i =
99+
let mi = Z.to_int min_i in
100+
let ma = Z.to_int max_i in
101+
let rec subjoin l i = match l with
102+
| [] -> Val.bot ()
103+
| hd::tl ->
104+
begin
105+
match i>ma,i<mi with
106+
| false,true -> subjoin tl (i+1)
107+
| false,false -> Val.join hd (subjoin tl (i+1))
108+
| _,_ -> Val.bot ()
109+
end in
110+
subjoin xl 0 in
111+
let f = Z.of_int (factor ()) in
112+
let min_i = extract (Idx.minimal i) in
113+
let max_i = extract (Idx.maximal i) in
114+
if Z.geq min_i f then xr
115+
else if Z.lt max_i f then search_unrolled_values min_i max_i
116+
else Val.join xr (search_unrolled_values min_i (Z.of_int ((factor ())-1)))
117+
let set (ask: Q.ask) (xl,xr) (_,i) v =
118+
let update_unrolled_values min_i max_i =
119+
let mi = Z.to_int min_i in
120+
let ma = Z.to_int max_i in
121+
let rec weak_update l i = match l with
122+
| [] -> []
123+
| hd::tl ->
124+
if i<mi then hd::(weak_update tl (i+1))
125+
else if i>ma then (hd::tl)
126+
else (Val.join hd v)::(weak_update tl (i+1)) in
127+
let rec full_update l i = match l with
128+
| [] -> []
129+
| hd::tl ->
130+
if i<mi then hd::(full_update tl (i+1))
131+
else v::tl in
132+
if mi=ma then full_update xl 0
133+
else weak_update xl 0 in
134+
let f = Z.of_int (factor ()) in
135+
let min_i = extract(Idx.minimal i) in
136+
let max_i = extract(Idx.maximal i) in
137+
if Z.geq min_i f then (xl, (Val.join xr v))
138+
else if Z.lt max_i f then ((update_unrolled_values min_i max_i), xr)
139+
else ((update_unrolled_values min_i (Z.of_int ((factor ())-1))), (Val.join xr v))
140+
let make _ v =
141+
let xl = BatList.make (factor ()) v in
142+
(xl,Val.bot ())
143+
let length _ = None
144+
let move_if_affected ?(replace_with_const=false) _ x _ _ = x
145+
let get_vars_in_e _ = []
146+
let map f (xl, xr) = ((List.map f xl), f xr)
147+
let fold_left f a x = f a (join_of_all_parts x)
148+
let fold_left2 f a x y = f a (join_of_all_parts x) (join_of_all_parts y)
149+
let printXml f (xl,xr) = BatPrintf.fprintf f "<value>\n<map>\n
150+
<key>unrolled array</key>\n
151+
<key>xl</key>\n%a\n\n
152+
<key>xm</key>\n%a\n\n
153+
</map></value>\n" Base.printXml xl Val.printXml xr
154+
let smart_join _ _ = join
155+
let smart_widen _ _ = widen
156+
let smart_leq _ _ = leq
157+
let update_length _ x = x
158+
end
159+
72160
(** Special signature so that we can use the _with_length functions from PartitionedWithLength but still match the interface *
73161
* defined for array domains *)
74162
module type SPartitioned =
@@ -689,59 +777,121 @@ struct
689777
let to_yojson (x, y) = `Assoc [ (Base.name (), Base.to_yojson x); ("length", Idx.to_yojson y) ]
690778
end
691779

780+
module UnrollWithLength (Val: Lattice.S) (Idx: IntDomain.Z): S with type value = Val.t and type idx = Idx.t =
781+
struct
782+
module Base = Unroll (Val) (Idx)
783+
include Lattice.Prod (Base) (Idx)
784+
type idx = Idx.t
785+
type value = Val.t
786+
787+
let get (ask : Q.ask) (x, (l : idx)) ((e: ExpDomain.t), v) =
788+
(array_oob_check (module Idx) (x, l) (e, v));
789+
Base.get ask x (e, v)
790+
let set (ask: Q.ask) (x,l) i v = Base.set ask x i v, l
791+
let make l x = Base.make l x, l
792+
let length (_,l) = Some l
793+
794+
let move_if_affected ?(replace_with_const=false) _ x _ _ = x
795+
let map f (x, l):t = (Base.map f x, l)
796+
let fold_left f a (x, l) = Base.fold_left f a x
797+
let fold_left2 f a (x, l) (y, l) = Base.fold_left2 f a x y
798+
let get_vars_in_e _ = []
799+
800+
let smart_join _ _ = join
801+
let smart_widen _ _ = widen
802+
let smart_leq _ _ = leq
803+
804+
(* It is not necessary to do a least-upper bound between the old and the new length here. *)
805+
(* Any array can only be declared in one location. The value for newl that we get there is *)
806+
(* the one obtained by abstractly evaluating the size expression at this location for the *)
807+
(* current state. If newl leq l this means that we somehow know more about the expression *)
808+
(* determining the size now (e.g. because of narrowing), but this holds for all the times *)
809+
(* the declaration is visited. *)
810+
let update_length newl (x, l) = (x, newl)
811+
812+
let printXml f (x,y) =
813+
BatPrintf.fprintf f "<value>\n<map>\n<key>\n%s\n</key>\n%a<key>\n%s\n</key>\n%a</map>\n</value>\n" (XmlUtil.escape (Base.name ())) Base.printXml x "length" Idx.printXml y
814+
815+
let to_yojson (x, y) = `Assoc [ (Base.name (), Base.to_yojson x); ("length", Idx.to_yojson y) ]
816+
end
817+
692818
module FlagConfiguredArrayDomain(Val: LatticeWithSmartOps) (Idx:IntDomain.Z):S with type value = Val.t and type idx = Idx.t =
693819
struct
694820
module P = PartitionedWithLength(Val)(Idx)
695821
module T = TrivialWithLength(Val)(Idx)
822+
module U = UnrollWithLength(Val)(Idx)
696823

697824
type idx = Idx.t
698825
type value = Val.t
699826

700-
include LatticeFlagHelper(P)(T)(struct
701-
let msg = "FlagConfiguredArrayDomain received a value where not exactly one component is set"
702-
let name = "FlagConfiguredArrayDomain"
703-
end)
827+
module K = struct
828+
let msg = "FlagConfiguredArrayDomain received a value where not exactly one component is set"
829+
let name = "FlagConfiguredArrayDomain"
830+
end
831+
832+
let to_t = function
833+
| (Some p, None, None) -> (Some p, None)
834+
| (None, Some t, None) -> (None, Some (Some t, None))
835+
| (None, None, Some u) -> (None, Some (None, Some u))
836+
| _ -> failwith "FlagConfiguredArrayDomain received a value where not exactly one component is set"
837+
838+
module I = struct include LatticeFlagHelper (T) (U) (K) let name () = "" end
839+
include LatticeFlagHelper (P) (I) (K)
840+
841+
let binop' opp opt opu = binop opp (I.binop opt opu)
842+
let unop' opp opt opu = unop opp (I.unop opt opu)
843+
let binop_to_t' opp opt opu = binop_to_t opp (I.binop_to_t opt opu)
844+
let unop_to_t' opp opt opu = unop_to_t opp (I.unop_to_t opt opu)
704845

705846
(* Simply call appropriate function for component that is not None *)
706-
let get a x (e,i) = unop (fun x ->
707-
if e = `Top then
708-
let e' = BatOption.map_default (fun x -> `Lifted (Cil.kintegerCilint (Cilfacade.ptrdiff_ikind ()) x)) (`Top) (Idx.to_int i) in
709-
P.get a x (e', i)
710-
else
711-
P.get a x (e, i)
712-
) (fun x -> T.get a x (e,i)) x
713-
let set (ask:Q.ask) x i a = unop_to_t (fun x -> P.set ask x i a) (fun x -> T.set ask x i a) x
714-
let length = unop P.length T.length
715-
let get_vars_in_e = unop P.get_vars_in_e T.get_vars_in_e
716-
let map f = unop_to_t (P.map f) (T.map f)
717-
let fold_left f s = unop (P.fold_left f s) (T.fold_left f s)
718-
let fold_left2 f s = binop (P.fold_left2 f s) (T.fold_left2 f s)
719-
let move_if_affected ?(replace_with_const=false) (ask:Q.ask) x v f = unop_to_t (fun x -> P.move_if_affected ~replace_with_const:replace_with_const ask x v f) (fun x -> T.move_if_affected ~replace_with_const:replace_with_const ask x v f) x
720-
let smart_join f g = binop_to_t (P.smart_join f g) (T.smart_join f g)
721-
let smart_widen f g = binop_to_t (P.smart_widen f g) (T.smart_widen f g)
722-
let smart_leq f g = binop (P.smart_leq f g) (T.smart_leq f g)
723-
let update_length newl x = unop_to_t (P.update_length newl) (T.update_length newl) x
847+
let get a x (e,i) = unop' (fun x ->
848+
if e = `Top then
849+
let e' = BatOption.map_default (fun x -> `Lifted (Cil.kintegerCilint (Cilfacade.ptrdiff_ikind ()) x)) (`Top) (Idx.to_int i) in
850+
P.get a x (e', i)
851+
else
852+
P.get a x (e, i)
853+
) (fun x -> T.get a x (e,i)) (fun x -> U.get a x (e,i)) x
854+
let set (ask:Q.ask) x i a = unop_to_t' (fun x -> P.set ask x i a) (fun x -> T.set ask x i a) (fun x -> U.set ask x i a) x
855+
let length = unop' P.length T.length U.length
856+
let map f = unop_to_t' (P.map f) (T.map f) (U.map f)
857+
let fold_left f s = unop' (P.fold_left f s) (T.fold_left f s) (U.fold_left f s)
858+
let fold_left2 f s = binop' (P.fold_left2 f s) (T.fold_left2 f s) (U.fold_left2 f s)
859+
860+
let move_if_affected ?(replace_with_const=false) (ask:Q.ask) x v f = unop_to_t' (fun x -> P.move_if_affected ~replace_with_const:replace_with_const ask x v f) (fun x -> T.move_if_affected ~replace_with_const:replace_with_const ask x v f)
861+
(fun x -> U.move_if_affected ~replace_with_const:replace_with_const ask x v f) x
862+
let get_vars_in_e = unop' P.get_vars_in_e T.get_vars_in_e U.get_vars_in_e
863+
let smart_join f g = binop_to_t' (P.smart_join f g) (T.smart_join f g) (U.smart_join f g)
864+
let smart_widen f g = binop_to_t' (P.smart_widen f g) (T.smart_widen f g) (U.smart_widen f g)
865+
let smart_leq f g = binop' (P.smart_leq f g) (T.smart_leq f g) (U.smart_leq f g)
866+
let update_length newl x = unop_to_t' (P.update_length newl) (T.update_length newl) (U.update_length newl) x
724867

725868
(* Functions that make use of the configuration flag *)
726-
let name () = "FlagConfiguredArrayDomain: " ^ if get_bool "ana.base.partition-arrays.enabled" then P.name () else T.name ()
869+
let chosen_domain () = get_string "ana.base.arrays.domain"
727870

728-
let partition_enabled () = get_bool "ana.base.partition-arrays.enabled"
871+
let name () = "FlagConfiguredArrayDomain: " ^ match chosen_domain () with
872+
| "trivial" -> T.name ()
873+
| "partitioned" -> P.name ()
874+
| "unroll" -> U.name ()
875+
| _ -> failwith "FlagConfiguredArrayDomain cannot name an array from set option"
729876

730877
let bot () =
731-
if partition_enabled () then
732-
(Some (P.bot ()), None)
733-
else
734-
(None, Some (T.bot ()))
878+
to_t @@ match chosen_domain () with
879+
| "partitioned" -> (Some (P.bot ()), None, None)
880+
| "trivial" -> (None, Some (T.bot ()), None)
881+
| "unroll" -> (None, None, Some (U.bot ()))
882+
| _ -> failwith "FlagConfiguredArrayDomain cannot construct a bot array from set option"
735883

736884
let top () =
737-
if partition_enabled () then
738-
(Some (P.top ()), None)
739-
else
740-
(None, Some (T.top ()))
885+
to_t @@ match chosen_domain () with
886+
| "partitioned" -> (Some (P.top ()), None, None)
887+
| "trivial" -> (None, Some (T.top ()), None)
888+
| "unroll" -> (None, None, Some (U.top ()))
889+
| _ -> failwith "FlagConfiguredArrayDomain cannot construct a top array from set option"
741890

742891
let make i v =
743-
if partition_enabled () then
744-
(Some (P.make i v), None)
745-
else
746-
(None, Some (T.make i v))
892+
to_t @@ match chosen_domain () with
893+
| "partitioned" -> (Some (P.make i v), None, None)
894+
| "trivial" -> (None, Some (T.make i v), None)
895+
| "unroll" -> (None, None, Some (U.make i v))
896+
| _ -> failwith "FlagConfiguredArrayDomain cannot construct an array from set option"
747897
end

src/cdomains/arrayDomain.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,4 @@ module PartitionedWithLength (Val: LatticeWithSmartOps) (Idx:IntDomain.Z): S wit
7676
(** Like partitioned but additionally manages the length of the array. *)
7777

7878
module FlagConfiguredArrayDomain(Val: LatticeWithSmartOps) (Idx:IntDomain.Z):S with type value = Val.t and type idx = Idx.t
79-
(** Switches between PartitionedWithLength and TrivialWithLength based on the value of ana.base.partition-arrays. *)
79+
(** Switches between PartitionedWithLength, TrivialWithLength and Unroll based on the value of ana.base.arrays.domain. *)

src/cdomains/valueDomain.ml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,10 @@ struct
148148
| TComp ({cstruct=true; _} as ci,_) -> `Struct (Structs.create (fun fd -> init_value fd.ftype) ci)
149149
| TComp ({cstruct=false; _},_) -> `Union (Unions.top ())
150150
| TArray (ai, None, _) ->
151-
`Array (CArrays.make (IndexDomain.bot ()) (if get_bool "ana.base.partition-arrays.enabled" then (init_value ai) else (bot_value ai)))
151+
`Array (CArrays.make (IndexDomain.bot ()) (if (get_string "ana.base.arrays.domain"="partitioned" || get_string "ana.base.arrays.domain"="unroll") then (init_value ai) else (bot_value ai)))
152152
| TArray (ai, Some exp, _) ->
153153
let l = BatOption.map Cilint.big_int_of_cilint (Cil.getInteger (Cil.constFold true exp)) in
154-
`Array (CArrays.make (BatOption.map_default (IndexDomain.of_int (Cilfacade.ptrdiff_ikind ())) (IndexDomain.bot ()) l) (if get_bool "ana.base.partition-arrays.enabled" then (init_value ai) else (bot_value ai)))
154+
`Array (CArrays.make (BatOption.map_default (IndexDomain.of_int (Cilfacade.ptrdiff_ikind ())) (IndexDomain.bot ()) l) (if (get_string "ana.base.arrays.domain"="partitioned" || get_string "ana.base.arrays.domain"="unroll") then (init_value ai) else (bot_value ai)))
155155
(* | t when is_thread_type t -> `Thread (ConcDomain.ThreadSet.empty ()) *)
156156
| TNamed ({ttype=t; _}, _) -> init_value t
157157
| _ -> `Top
@@ -163,10 +163,10 @@ struct
163163
| TComp ({cstruct=true; _} as ci,_) -> `Struct (Structs.create (fun fd -> top_value fd.ftype) ci)
164164
| TComp ({cstruct=false; _},_) -> `Union (Unions.top ())
165165
| TArray (ai, None, _) ->
166-
`Array (CArrays.make (IndexDomain.top ()) (if get_bool "ana.base.partition-arrays.enabled" then (top_value ai) else (bot_value ai)))
166+
`Array (CArrays.make (IndexDomain.top ()) (if (get_string "ana.base.arrays.domain"="partitioned" || get_string "ana.base.arrays.domain"="unroll") then (top_value ai) else (bot_value ai)))
167167
| TArray (ai, Some exp, _) ->
168168
let l = BatOption.map Cilint.big_int_of_cilint (Cil.getInteger (Cil.constFold true exp)) in
169-
`Array (CArrays.make (BatOption.map_default (IndexDomain.of_int (Cilfacade.ptrdiff_ikind ())) (IndexDomain.top_of (Cilfacade.ptrdiff_ikind ())) l) (if get_bool "ana.base.partition-arrays.enabled" then (top_value ai) else (bot_value ai)))
169+
`Array (CArrays.make (BatOption.map_default (IndexDomain.of_int (Cilfacade.ptrdiff_ikind ())) (IndexDomain.top_of (Cilfacade.ptrdiff_ikind ())) l) (if (get_string "ana.base.arrays.domain"="partitioned" || get_string "ana.base.arrays.domain"="unroll") then (top_value ai) else (bot_value ai)))
170170
| TNamed ({ttype=t; _}, _) -> top_value t
171171
| _ -> `Top
172172

src/domains/lattice.ml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,29 @@ struct
581581
Pretty.dprintf "%a not leq %a" pretty x pretty y
582582
end
583583

584+
module type Num = sig val x : unit -> int end
585+
module ProdList (Base: S) (N: Num) =
586+
struct
587+
include Printable.Liszt (Base)
588+
589+
let bot () = BatList.make (N.x ()) (Base.bot ())
590+
let is_bot = List.for_all Base.is_bot
591+
let top () = BatList.make (N.x ()) (Base.top ())
592+
let is_top = List.for_all Base.is_top
593+
594+
let leq =
595+
let f acc x y = Base.leq x y && acc in
596+
List.fold_left2 f true
597+
598+
let join = List.map2 Base.join
599+
let widen = List.map2 Base.widen
600+
let meet = List.map2 Base.meet
601+
let narrow = List.map2 Base.narrow
602+
603+
let pretty_diff () ((x:t),(y:t)): Pretty.doc =
604+
Pretty.dprintf "%a not leq %a" pretty x pretty y
605+
end
606+
584607
module Chain (P: Printable.ChainParams) =
585608
struct
586609
include Printable.Std

0 commit comments

Comments
 (0)