From 0f6893d3fb257812bc48a242314d46d211754ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20B=C3=BChler?= <david.buhler@cea.fr> Date: Mon, 10 Dec 2018 17:41:54 +0100 Subject: [PATCH] [Eva] Octagons domain: also infers non-relational intervals of variables. Makes the join more precise. --- src/plugins/value/domains/octagons.ml | 223 +++++++++++++++++++++++--- 1 file changed, 199 insertions(+), 24 deletions(-) diff --git a/src/plugins/value/domains/octagons.ml b/src/plugins/value/domains/octagons.ml index 13dc08852bd..6ef3c073685 100644 --- a/src/plugins/value/domains/octagons.ml +++ b/src/plugins/value/domains/octagons.ml @@ -23,6 +23,12 @@ open Cil_types open Eval +(* Whether the domain infers non-relational intervals (ivals) to improve the + precision of the join operation: this avoids losing all relations that have + been inferred in only one side of the join. Enhances the domain accuracy + for a minimal drop in efficiency. *) +let infer_intervals = true + (* -------------------------------------------------------------------------- *) (* Basic types: pair of variables and Ival.t *) (* -------------------------------------------------------------------------- *) @@ -333,6 +339,9 @@ end (* This domain infers relations between pairs of variables (X, Y), by inferring intervals for the mathematical operations X+Y and X-Y. + It also infers non-relational intervals for the separate variable X and Y + (they could be seen as intervals for X+X and Y+Y, but we chose to store them + in another way). These intervals are used to make the join more precise. Geometrically, in a plan, intervals for X and Y shape a straight rectangle, while intervals for X+Y and X-Y shape a "leaning" rectangle; the intersection of these rectangles shapes an octagon. @@ -440,7 +449,7 @@ module Octagons = struct let narrow x y = try `Value (narrow_exc x y) with EBottom -> `Bottom - let join = + let simple_join = let cache = Hptmap_sig.PersistentCache "Octagons.Octagons.join" in let decide _pair x y = let r = Diamond.join x y in @@ -448,7 +457,18 @@ module Octagons = struct in inter ~cache ~symmetric:true ~idempotent:true ~decide - let widen = + let join ~decide_left ~decide_right = + let cache = Hptmap_sig.NoCache in + let decide_left = Traversing decide_left + and decide_right = Traversing decide_right in + let decide_both _pair x y = + let r = Diamond.join x y in + if Diamond.is_top r then None else Some r + in + merge ~cache ~symmetric:false ~idempotent:true + ~decide_left ~decide_right ~decide_both + + let simple_widen = let cache = Hptmap_sig.PersistentCache "Octagons.Octagons.widen" in let decide _pair x y = let r = Diamond.widen x y in @@ -456,6 +476,16 @@ module Octagons = struct in inter ~cache ~symmetric:false ~idempotent:true ~decide + let widen ~decide_left ~decide_right = + let cache = Hptmap_sig.NoCache in + let decide_left = Traversing decide_left + and decide_right = Traversing decide_right in + let decide_both _pair x y = + let r = Diamond.widen x y in + if Diamond.is_top r then None else Some r + in + merge ~cache ~symmetric:false ~idempotent:true + ~decide_left ~decide_right ~decide_both let unsafe_add = add @@ -542,6 +572,57 @@ module Relations = struct else add variable set t end +(* -------------------------------------------------------------------------- *) +(* Non-relational intervals *) +(* -------------------------------------------------------------------------- *) + +module Intervals = struct + module Initial_Values = struct let v = [[]] end + module Dependencies = struct let l = [ Ast.self ] end + + include Hptmap.Make (Variable) (Ival) + (Hptmap.Comp_unused) (Initial_Values) (Dependencies) + + let top = empty + + let is_included = + let cache = Hptmap_sig.PersistentCache "Octagons.Intervals.is_included" in + let decide_fst _ _ = true in + let decide_snd _ _ = false in + let decide_both _ x y = Ival.is_included x y in + let decide_fast t1 t2 = decide_fast_inclusion t2 t1 in + binary_predicate cache UniversalPredicate + ~decide_fast ~decide_fst ~decide_snd ~decide_both + + exception EBottom + + let narrow_exc = + let cache = Hptmap_sig.NoCache in + let decide _varinfo x y = + let ival = Ival.narrow x y in + if Ival.is_bottom ival then raise EBottom else ival + in + join ~cache ~symmetric:true ~idempotent:true ~decide + + let narrow x y = try `Value (narrow_exc x y) with EBottom -> `Bottom + + let join = + let cache = Hptmap_sig.PersistentCache "Octagons.Intervals.join" in + let decide _varinfo x y = + let r = Ival.join x y in + if Ival.(equal top r) then None else Some r + in + inter ~cache ~symmetric:true ~idempotent:true ~decide + + let widen = + let cache = Hptmap_sig.PersistentCache "Octagons.Intervals.widen" in + let decide _varinfo x y = + let r = Arith.widen x y in + if Ival.(equal top r) then None else Some r + in + inter ~cache ~symmetric:false ~idempotent:true ~decide +end + (* -------------------------------------------------------------------------- *) (* Octagon states *) (* -------------------------------------------------------------------------- *) @@ -551,7 +632,8 @@ module Zone = Locations.Zone module State = struct type state = - { octagons: Octagons.t; (* The intervals for X±Y. *) + { octagons: Octagons.t; (* The intervals for X±Y. *) + intervals: Intervals.t; (* The intervals for the variables X,Y… *) relations: Relations.t; (* The related variables in [octagons]. *) modified: Locations.Zone.t; (* The memory zone modified by a function. *) } @@ -565,17 +647,21 @@ module State = struct let structural_descr = Structural_descr.t_record [| Octagons.packed_descr; + Intervals.packed_descr; Relations.packed_descr; Zone.packed_descr |] let reprs = [ { octagons = Octagons.top; + intervals = Intervals.empty; relations = Relations.empty; modified = Zone.bottom } ] let compare s1 s2 = let c = Octagons.compare s1.octagons s2.octagons in if c <> 0 then c else - Zone.compare s1.modified s2.modified + let c = Intervals.compare s1.intervals s2.intervals in + if c <> 0 then c else + Zone.compare s1.modified s2.modified let equal = Datatype.from_compare @@ -584,41 +670,97 @@ module State = struct Relations.hash t.relations, Zone.hash t.modified) - let pretty fmt { octagons } = + let pretty fmt { octagons; intervals } = Format.fprintf fmt - "@[<hov>%a@]" - Octagons.pretty octagons + "@[<hov>%a@ %a@]" + Octagons.pretty octagons Intervals.pretty intervals end) let top = { octagons = Octagons.top; + intervals = Intervals.top; relations = Relations.empty; modified = Zone.top; } let empty () = { octagons = Octagons.top; + intervals = Intervals.top; relations = Relations.empty; modified = Zone.bottom; } let is_included t1 t2 = Octagons.is_included t1.octagons t2.octagons + && Intervals.is_included t1.intervals t2.intervals && Zone.is_included t1.modified t2.modified let join t1 t2 = - { octagons = Octagons.join t1.octagons t2.octagons; - relations = Relations.inter t1.relations t2.relations; + let octagons = + if not infer_intervals + then Octagons.simple_join t1.octagons t2.octagons + else + let decide_empty intervals pair diamond = + let v1, v2 = Pair.get pair in + try + let i1 = Intervals.find v1 intervals + and i2 = Intervals.find v2 intervals in + let add = Arith.add v1.vtype i1 i2 + and sub = Arith.sub v1.vtype i1 i2 in + let diamond = Diamond.join diamond { add; sub } in + if Diamond.is_top diamond then None else Some diamond + with Not_found -> None + in + let decide_left = decide_empty t2.intervals + and decide_right = decide_empty t1.intervals in + Octagons.join ~decide_left ~decide_right t1.octagons t2.octagons + in + let relations = + if infer_intervals + then Relations.union t1.relations t2.relations + else Relations.inter t1.relations t2.relations + in + { octagons; relations; + intervals = Intervals.join t1.intervals t2.intervals; modified = Zone.join t1.modified t2.modified; } let widen _kf _hints t1 t2 = - { octagons = Octagons.widen t1.octagons t2.octagons; - relations = Relations.inter t1.relations t2.relations; + let octagons = + if not infer_intervals + then Octagons.simple_widen t1.octagons t2.octagons + else + let decide_empty b intervals pair diamond = + let v1, v2 = Pair.get pair in + try + let i1 = Intervals.find v1 intervals + and i2 = Intervals.find v2 intervals in + let add = Arith.add v1.vtype i1 i2 + and sub = Arith.sub v1.vtype i1 i2 in + let diamond = + if b + then Diamond.widen { add; sub } diamond + else Diamond.widen diamond { add; sub } + in + if Diamond.is_top diamond then None else Some diamond + with Not_found -> None + in + let decide_left = decide_empty false t2.intervals + and decide_right = decide_empty true t1.intervals in + Octagons.widen ~decide_left ~decide_right t1.octagons t2.octagons + in + let relations = + if infer_intervals + then Relations.union t1.relations t2.relations + else Relations.inter t1.relations t2.relations + in + { octagons; relations; + intervals = Intervals.widen t1.intervals t2.intervals; modified = Zone.join t1.modified t2.modified; } let narrow t1 t2 = Octagons.narrow t1.octagons t2.octagons >>- fun octagons -> + Intervals.narrow t1.intervals t2.intervals >>- fun intervals -> let relations = Relations.union t1.relations t2.relations in let modified = Zone.narrow t1.modified t2.modified in - `Value { octagons; relations; modified; } + `Value { octagons; intervals; relations; modified; } let add_octagon state octagon = if Ival.(equal top octagon.value) @@ -629,6 +771,8 @@ module State = struct { state with octagons; relations } let remove state x = + let intervals = Intervals.remove x state.intervals in + let state = { state with intervals } in try let relations = Relations.find x state.relations in let remove_one y state = @@ -665,7 +809,6 @@ module State = struct with Not_found -> [] end - (* -------------------------------------------------------------------------- *) (* Octagon domain *) (* -------------------------------------------------------------------------- *) @@ -765,22 +908,37 @@ module Domain = struct exception EBottom + let infer_octagons evaluate expr ival state = + let octagons = Rewriting.make_octagons evaluate expr ival in + let add_octagon state octagon = + match State.add_octagon state octagon with + | `Bottom -> raise EBottom + | `Value state -> state + in + List.fold_left add_octagon state octagons + + let infer_interval expr ival state = + if not infer_intervals + then state + else + match expr.enode with + | Lval (Var varinfo, NoOffset) + when Cil.isArithmeticType varinfo.vtype -> + let intervals = Intervals.add varinfo ival state.intervals in + { state with intervals } + | _ -> state + let update valuation state = let evaluate = evaluation_function valuation in - let aux e r state = - let value = r.Eval.value in - match r.reductness, value.v, value.initialized, value.escaping with + let aux expr record state = + let value = record.Eval.value in + match record.reductness, value.v, value.initialized, value.escaping with | (Created | Reduced), `Value cvalue, true, false -> begin try let ival = Cvalue.V.project_ival cvalue in - let octagons = Rewriting.make_octagons evaluate e ival in - let add_octagon state octagon = - match State.add_octagon state octagon with - | `Bottom -> raise EBottom - | `Value state -> state - in - List.fold_left add_octagon state octagons + let state = infer_octagons evaluate expr ival state in + infer_interval expr ival state with Cvalue.V.Not_based_on_null -> state end | _ -> state @@ -788,10 +946,27 @@ module Domain = struct try `Value (Valuation.fold aux valuation state) with EBottom -> `Bottom - let assign _kinstr left_value expr _assigned valuation state = + let assign_interval varinfo assigned state = + if not infer_intervals + then state + else + match assigned with + | Assign v + | Copy (_, { v = `Value v; initialized = true; escaping = false }) -> + begin + try + let ival = Cvalue.V.project_ival v in + let intervals = Intervals.add varinfo ival state.intervals in + { state with intervals } + with Cvalue.V.Not_based_on_null -> state + end + | _ -> state + + let assign _kinstr left_value expr assigned valuation state = match left_value.lval with | Var varinfo, NoOffset when Cil.isArithmeticType varinfo.vtype -> let state = State.remove state varinfo in + let state = assign_interval varinfo assigned state in let evaluate = evaluation_function valuation in let enode = Lval left_value.lval in let left_expr = Cil.new_exp ~loc:expr.eloc enode in -- GitLab