Skip to content

Commit 8556b70

Browse files
authored
Merge pull request #1517 from goblint/witness-invariant-int
Simplify `IntDomTuple` witness invariants
2 parents 8109985 + 40b3434 commit 8556b70

File tree

6 files changed

+185
-146
lines changed

6 files changed

+185
-146
lines changed

src/cdomain/value/cdomains/intDomain.ml

+107-67
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,59 @@ module IntervalArith (Ints_t : IntOps.IntOps) = struct
582582
List.exists (Z.equal l) ts
583583
end
584584

585+
module IntInvariant =
586+
struct
587+
let of_int e ik x =
588+
if get_bool "witness.invariant.exact" then
589+
Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType))
590+
else
591+
Invariant.none
592+
593+
let of_incl_list e ik ps =
594+
match ps with
595+
| [_; _] when ik = IBool && not (get_bool "witness.invariant.inexact-type-bounds") ->
596+
assert (List.mem Z.zero ps);
597+
assert (List.mem Z.one ps);
598+
Invariant.none
599+
| [_] when get_bool "witness.invariant.exact" ->
600+
Invariant.none
601+
| _ :: _ :: _
602+
| [_] | [] ->
603+
List.fold_left (fun a x ->
604+
let i = Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) in
605+
Invariant.(a || i) [@coverage off] (* bisect_ppx cannot handle redefined (||) *)
606+
) (Invariant.bot ()) ps
607+
608+
let of_interval_opt e ik = function
609+
| (Some x1, Some x2) when Z.equal x1 x2 ->
610+
of_int e ik x1
611+
| x1_opt, x2_opt ->
612+
let (min_ik, max_ik) = Size.range ik in
613+
let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in
614+
let i1 =
615+
match x1_opt, inexact_type_bounds with
616+
| Some x1, false when Z.equal min_ik x1 -> Invariant.none
617+
| Some x1, _ -> Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik x1, e, intType))
618+
| None, _ -> Invariant.none
619+
in
620+
let i2 =
621+
match x2_opt, inexact_type_bounds with
622+
| Some x2, false when Z.equal x2 max_ik -> Invariant.none
623+
| Some x2, _ -> Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2, intType))
624+
| None, _ -> Invariant.none
625+
in
626+
Invariant.(i1 && i2)
627+
628+
let of_interval e ik (x1, x2) =
629+
of_interval_opt e ik (Some x1, Some x2)
630+
631+
let of_excl_list e ik ns =
632+
List.fold_left (fun a x ->
633+
let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in
634+
Invariant.(a && i)
635+
) (Invariant.top ()) ns
636+
end
637+
585638
module IntervalFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option =
586639
struct
587640
let name () = "intervals"
@@ -915,21 +968,10 @@ struct
915968
else if Ints_t.compare y2 x1 <= 0 then of_bool ik false
916969
else top_bool
917970

918-
let invariant_ikind e ik x =
919-
match x with
920-
| Some (x1, x2) when Ints_t.compare x1 x2 = 0 ->
921-
if get_bool "witness.invariant.exact" then
922-
let x1 = Ints_t.to_bigint x1 in
923-
Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x1, intType))
924-
else
925-
Invariant.top ()
971+
let invariant_ikind e ik = function
926972
| Some (x1, x2) ->
927-
let (min_ik, max_ik) = range ik in
928-
let (x1', x2') = BatTuple.Tuple2.mapn (Ints_t.to_bigint) (x1, x2) in
929-
let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in
930-
let i1 = if inexact_type_bounds || Ints_t.compare min_ik x1 <> 0 then Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik x1', e, intType)) else Invariant.none in
931-
let i2 = if inexact_type_bounds || Ints_t.compare x2 max_ik <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2', intType)) else Invariant.none in
932-
Invariant.(i1 && i2)
973+
let (x1', x2') = BatTuple.Tuple2.mapn Ints_t.to_bigint (x1, x2) in
974+
IntInvariant.of_interval e ik (x1', x2')
933975
| None -> Invariant.none
934976

935977
let arbitrary ik =
@@ -2297,25 +2339,14 @@ struct
22972339
let invariant_ikind e ik (x:t) =
22982340
match x with
22992341
| `Definite x ->
2300-
if get_bool "witness.invariant.exact" then
2301-
Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType))
2302-
else
2303-
Invariant.top ()
2342+
IntInvariant.of_int e ik x
23042343
| `Excluded (s, r) ->
23052344
(* Emit range invariant if tighter than ikind bounds.
23062345
This can be more precise than interval, which has been widened. *)
23072346
let (rmin, rmax) = (Exclusion.min_of_range r, Exclusion.max_of_range r) in
2308-
let (ikmin, ikmax) =
2309-
let ikr = size ik in
2310-
(Exclusion.min_of_range ikr, Exclusion.max_of_range ikr)
2311-
in
2312-
let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in
2313-
let imin = if inexact_type_bounds || Z.compare ikmin rmin <> 0 then Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik rmin, e, intType)) else Invariant.none in
2314-
let imax = if inexact_type_bounds || Z.compare rmax ikmax <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik rmax, intType)) else Invariant.none in
2315-
S.fold (fun x a ->
2316-
let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in
2317-
Invariant.(a && i)
2318-
) s Invariant.(imin && imax)
2347+
let ri = IntInvariant.of_interval e ik (rmin, rmax) in
2348+
let si = IntInvariant.of_excl_list e ik (S.elements s) in
2349+
Invariant.(ri && si)
23192350
| `Bot -> Invariant.none
23202351

23212352
let arbitrary ik =
@@ -2731,32 +2762,16 @@ module Enums : S with type int_t = Z.t = struct
27312762
let ne ik x y = c_lognot ik (eq ik x y)
27322763

27332764
let invariant_ikind e ik x =
2734-
let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in
27352765
match x with
2736-
| Inc ps when not inexact_type_bounds && ik = IBool && is_top_of ik x ->
2737-
Invariant.none
27382766
| Inc ps ->
2739-
if BISet.cardinal ps > 1 || get_bool "witness.invariant.exact" then
2740-
BISet.fold (fun x a ->
2741-
let i = Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) in
2742-
Invariant.(a || i) [@coverage off] (* bisect_ppx cannot handle redefined (||) *)
2743-
) ps (Invariant.bot ())
2744-
else
2745-
Invariant.top ()
2767+
IntInvariant.of_incl_list e ik (BISet.elements ps)
27462768
| Exc (ns, r) ->
27472769
(* Emit range invariant if tighter than ikind bounds.
27482770
This can be more precise than interval, which has been widened. *)
27492771
let (rmin, rmax) = (Exclusion.min_of_range r, Exclusion.max_of_range r) in
2750-
let (ikmin, ikmax) =
2751-
let ikr = size ik in
2752-
(Exclusion.min_of_range ikr, Exclusion.max_of_range ikr)
2753-
in
2754-
let imin = if inexact_type_bounds || Z.compare ikmin rmin <> 0 then Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik rmin, e, intType)) else Invariant.none in
2755-
let imax = if inexact_type_bounds || Z.compare rmax ikmax <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik rmax, intType)) else Invariant.none in
2756-
BISet.fold (fun x a ->
2757-
let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in
2758-
Invariant.(a && i)
2759-
) ns Invariant.(imin && imax)
2772+
let ri = IntInvariant.of_interval e ik (rmin, rmax) in
2773+
let nsi = IntInvariant.of_excl_list e ik (BISet.elements ns) in
2774+
Invariant.(ri && nsi)
27602775

27612776

27622777
let arbitrary ik =
@@ -2779,7 +2794,7 @@ module Enums : S with type int_t = Z.t = struct
27792794
| Inc e, Some (c, m) -> Inc (BISet.filter (contains c m) e)
27802795
| _ -> a
27812796

2782-
let refine_with_interval ik a b = a
2797+
let refine_with_interval ik a b = a (* TODO: refine inclusion (exclusion?) set *)
27832798

27842799
let refine_with_excl_list ik a b =
27852800
match b with
@@ -3243,10 +3258,7 @@ struct
32433258
match x with
32443259
| x when is_top x -> Invariant.top ()
32453260
| Some (c, m) when m =: Z.zero ->
3246-
if get_bool "witness.invariant.exact" then
3247-
Invariant.of_exp Cil.(BinOp (Eq, e, Cil.kintegerCilint ik c, intType))
3248-
else
3249-
Invariant.top ()
3261+
IntInvariant.of_int e ik c
32503262
| Some (c, m) ->
32513263
let open Cil in
32523264
let (c, m) = BatTuple.Tuple2.mapn (fun a -> kintegerCilint ik a) (c, m) in
@@ -3524,7 +3536,7 @@ module IntDomTupleImpl = struct
35243536
let merge ps =
35253537
let (vs, rs) = List.split ps in
35263538
let (mins, maxs) = List.split rs in
3527-
(List.concat vs, (List.min mins, List.max maxs))
3539+
(List.concat vs |> List.sort_uniq Z.compare, (List.min mins, List.max maxs))
35283540
in
35293541
mapp2 { fp2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.to_excl_list } x |> flat merge
35303542

@@ -3565,7 +3577,7 @@ module IntDomTupleImpl = struct
35653577
in
35663578
[(fun (a, b, c, d, e) -> refine_with_excl_list ik (a, b, c, d, e) (to_excl_list (a, b, c, d, e)));
35673579
(fun (a, b, c, d, e) -> refine_with_incl_list ik (a, b, c, d, e) (to_incl_list (a, b, c, d, e)));
3568-
(fun (a, b, c, d, e) -> maybe refine_with_interval ik (a, b, c, d, e) b);
3580+
(fun (a, b, c, d, e) -> maybe refine_with_interval ik (a, b, c, d, e) b); (* TODO: get interval across all domains with minimal and maximal *)
35693581
(fun (a, b, c, d, e) -> maybe refine_with_congruence ik (a, b, c, d, e) d)]
35703582

35713583
let refine ik ((a, b, c, d, e) : t ) : t =
@@ -3777,19 +3789,47 @@ module IntDomTupleImpl = struct
37773789
| Some v when not (GobConfig.get_bool "dbg.full-output") -> BatPrintf.fprintf f "<value>\n<data>\n%s\n</data>\n</value>\n" (Z.to_string v)
37783790
| _ -> BatPrintf.fprintf f "<value>\n<data>\n%s\n</data>\n</value>\n" (show x)
37793791

3780-
let invariant_ikind e ik x =
3781-
match to_int x with
3782-
| Some v ->
3783-
if get_bool "witness.invariant.exact" then
3784-
(* If definite, output single equality instead of every subdomain repeating same equality *)
3785-
Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik v, intType))
3786-
else
3787-
Invariant.top ()
3788-
| None ->
3789-
let is = to_list (mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.invariant_ikind e ik } x)
3790-
in List.fold_left (fun a i ->
3792+
let invariant_ikind e ik ((_, _, _, x_cong, x_intset) as x) =
3793+
(* TODO: do refinement before to ensure incl_list being more precise than intervals, etc (https://github.com/goblint/analyzer/pull/1517#discussion_r1693998515), requires refine functions to actually refine that *)
3794+
let simplify_int fallback =
3795+
match to_int x with
3796+
| Some v ->
3797+
(* If definite, output single equality instead of every subdomain repeating same equality (or something less precise). *)
3798+
IntInvariant.of_int e ik v
3799+
| None ->
3800+
fallback ()
3801+
in
3802+
let simplify_all () =
3803+
match to_incl_list x with
3804+
| Some ps ->
3805+
(* If inclusion set, output disjunction of equalities because it subsumes interval(s), exclusion set and congruence. *)
3806+
IntInvariant.of_incl_list e ik ps
3807+
| None ->
3808+
(* Get interval bounds from all domains (intervals and exclusion set ranges). *)
3809+
let min = minimal x in
3810+
let max = maximal x in
3811+
let ns = Option.map fst (to_excl_list x) |? [] in (* Ignore exclusion set bit range, known via interval bounds already. *)
3812+
(* "Refine" out-of-bounds exclusions for simpler output. *)
3813+
let ns = Option.map_default (fun min -> List.filter (Z.leq min) ns) ns min in
3814+
let ns = Option.map_default (fun max -> List.filter (Z.geq max) ns) ns max in
3815+
Invariant.(
3816+
IntInvariant.of_interval_opt e ik (min, max) && (* Output best interval bounds once instead of multiple subdomains repeating them (or less precise ones). *)
3817+
IntInvariant.of_excl_list e ik ns &&
3818+
Option.map_default (I4.invariant_ikind e ik) Invariant.none x_cong && (* Output congruence as is. *)
3819+
Option.map_default (I5.invariant_ikind e ik) Invariant.none x_intset (* Output interval sets as is. *)
3820+
)
3821+
in
3822+
let simplify_none () =
3823+
let is = to_list (mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.invariant_ikind e ik } x) in
3824+
List.fold_left (fun a i ->
37913825
Invariant.(a && i)
37923826
) (Invariant.top ()) is
3827+
in
3828+
match GobConfig.get_string "ana.base.invariant.int.simplify" with
3829+
| "none" -> simplify_none ()
3830+
| "int" -> simplify_int simplify_none
3831+
| "all" -> simplify_int simplify_all
3832+
| _ -> assert false
37933833

37943834
let arbitrary ik = QCheck.(set_print show @@ tup5 (option (I1.arbitrary ik)) (option (I2.arbitrary ik)) (option (I3.arbitrary ik)) (option (I4.arbitrary ik)) (option (I5.arbitrary ik)))
37953835

src/config/options.schema.json

+14
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,20 @@
807807
"type": "string",
808808
"enum": ["once", "fixpoint"],
809809
"default": "once"
810+
},
811+
"int": {
812+
"title": "ana.base.invariant.int",
813+
"type": "object",
814+
"properties": {
815+
"simplify": {
816+
"title": "ana.base.invariant.int.simplify",
817+
"description": "How much to simplify int domain invariants. Value \"int\" only simplifies definite integers. Without int domain refinement \"all\" might not be maximally precise.",
818+
"type": "string",
819+
"enum": ["none", "int", "all"],
820+
"default": "all"
821+
}
822+
},
823+
"additionalProperties": false
810824
}
811825
},
812826
"additionalProperties": false

tests/regression/56-witness/46-top-bool-invariant.t

+1-23
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ all:
144144
dead: 0
145145
total lines: 2
146146
[Info][Witness] witness generation summary:
147-
total generation entries: 3
147+
total generation entries: 1
148148

149149
$ yamlWitnessStrip < witness.yml
150150
- entry_type: location_invariant
@@ -158,28 +158,6 @@ all:
158158
string: x == (_Bool)0 || x == (_Bool)1
159159
type: assertion
160160
format: C
161-
- entry_type: location_invariant
162-
location:
163-
file_name: 46-top-bool-invariant.c
164-
file_hash: $FILE_HASH
165-
line: 5
166-
column: 3
167-
function: main
168-
location_invariant:
169-
string: x <= (_Bool)1
170-
type: assertion
171-
format: C
172-
- entry_type: location_invariant
173-
location:
174-
file_name: 46-top-bool-invariant.c
175-
file_hash: $FILE_HASH
176-
line: 5
177-
column: 3
178-
function: main
179-
location_invariant:
180-
string: (_Bool)0 <= x
181-
type: assertion
182-
format: C
183161

184162
all without inexact-type-bounds:
185163

0 commit comments

Comments
 (0)