diff --git a/src/plugins/value/legacy/eval_terms.ml b/src/plugins/value/legacy/eval_terms.ml index c1cb87fa3618033deefec4c588ad7d0b9ceb6762..8ee13e2c4fef29c60271f4cafe77da9f08021d23 100644 --- a/src/plugins/value/legacy/eval_terms.ml +++ b/src/plugins/value/legacy/eval_terms.ml @@ -240,6 +240,9 @@ let add_old = add_logic Logic_const.old_label let add_init state = add_logic Logic_const.init_label (Db.Value.globals_state ()) state +let add_logic_var env lv cvalue = + { env with logic_vars = LogicVarEnv.add lv cvalue env.logic_vars } + let make_env logic_env state = let transfer label map = Logic_label.Map.add label (logic_env.Abstract_domain.states label) map @@ -459,6 +462,12 @@ type 'a eval_result = { ldeps: logic_deps; } +let join_eval_result r1 r2 = + let eover = Cvalue.V.join r2.eover r1.eover in + let eunder = Cvalue.V.meet r1.eunder r2.eunder in + let ldeps = join_logic_deps r1.ldeps r2.ldeps in + { eover; eunder; ldeps; etype = r1.etype; empty = r1.empty || r2.empty; } + (* When computing an under-approximation, we make the hypothesis that the state is not Bottom. Hence, over-approximations of cardinal <= 1 are actually of cardinal 1, and are thus exact. *) @@ -1361,18 +1370,31 @@ and eval_known_logic_function ~alarm_mode env li labels args = with Cvalue.V.Not_based_on_null -> c_alarm () end - | "\\min", Some Linteger, _, [t1; t2] -> - let backward = Cvalue.V.backward_comp_int_left Comp.Le in - eval_extremum Cil.intType backward ~alarm_mode env t1 t2 - | "\\max", Some Linteger, _, [t1; t2] -> - let backward = Cvalue.V.backward_comp_int_left Comp.Ge in - eval_extremum Cil.intType backward ~alarm_mode env t1 t2 - | "\\min", Some Lreal, _, [t1; t2] -> - let backward = Cvalue.V.backward_comp_float_left_true Comp.Le Fval.Real in - eval_extremum Cil.floatType backward ~alarm_mode env t1 t2 - | "\\max", Some Lreal, _, [t1; t2] -> - let backward = Cvalue.V.backward_comp_float_left_true Comp.Ge Fval.Real in - eval_extremum Cil.doubleType backward ~alarm_mode env t1 t2 + | ("\\min" | "\\max"), Some typ, _, t1 :: t2 :: tail_args -> + begin + let comp = if lvi.lv_name = "\\min" then Comp.Le else Comp.Ge in + let backward = + match typ with + | Linteger -> Cvalue.V.backward_comp_int_left comp + | Lreal -> Cvalue.V.backward_comp_float_left_true comp Fval.Real + | _ -> assert false + in + let r1 = eval_term ~alarm_mode env t1 + and r2 = eval_term ~alarm_mode env t2 in + (* If there are 2 arguments, it is the mathematical function \min or \max. + If there are 3, it is the ACSL extended quantifiers \min or \max. *) + match tail_args with + | [] -> compute_extremum backward r1 r2 + | [ { term_node = Tlambda ([lv], term) } ] -> + (* Evaluation of [term] when the logic variable [lv] is in [cvalue]. *) + let eval_term cvalue = + let env = add_logic_var env lv cvalue in + eval_term ~alarm_mode env term + in + eval_quantifier_extremum backward ~min:r1 ~max:r2 eval_term + | _ -> assert false + end + | _ -> assert false and eval_float_builtin_arity2 ~alarm_mode env name arg1 arg2 = @@ -1401,19 +1423,68 @@ and eval_float_builtin_arity2 ~alarm_mode env name arg1 arg2 = let ldeps = join_logic_deps r1.ldeps r2.ldeps in { etype = r1.etype; eunder; eover = v; ldeps; empty = r1.empty || r2.empty; } -(* Evaluates the max (resp. the min) between the terms [t1] and [t2], + +(* Computes the max (resp. the min) between the evaluation results [r1] and [r2] according to [backward_left v1 v2] that reduces [v1] by assuming it is greater than (resp. lower than) [v2]. *) -and eval_extremum etype backward_left ~alarm_mode env t1 t2 = - let r1 = eval_term ~alarm_mode env t1 - and r2 = eval_term ~alarm_mode env t2 in - let reduced_v1 = backward_left r1.eover r2.eover - and reduced_v2 = backward_left r2.eover r1.eover in - let eover = Cvalue.V.join reduced_v1 reduced_v2 in - let eunder = Cvalue.V.meet r1.eunder r2.eunder in - let ldeps = join_logic_deps r1.ldeps r2.ldeps in - {eover; eunder; ldeps; etype; empty = r1.empty || r2.empty; } - +and compute_extremum backward_left r1 r2 = + let r1 = { r1 with eover = backward_left r1.eover r2.eover } + and r2 = { r2 with eover = backward_left r2.eover r1.eover } in + join_eval_result r1 r2 + +(* Evaluates ACSL extended quantifiers \max or \min: computes (an approximation + of) the max (resp. the min) of [eval_term i] when [i] ranges between [min] + and [max], according to [backward_left v1 v2] that reduces [v1] by assuming + it is greater (resp. lower) than [v2]. *) +and eval_quantifier_extremum backward_left ~min ~max eval_term = + (* Returns [r] as the result, with updated [empty] and [ldeps] fields. *) + let return r = + let ldeps = join_logic_deps r.ldeps (join_logic_deps min.ldeps max.ldeps) in + let empty = min.empty || max.empty || r.empty in + { r with ldeps; empty; } + in + let eval_ival ival = eval_term (Cvalue.V.inject_ival ival) in + let project r = Cvalue.V.project_ival r.eover in + match Ival.min_and_max (project min), + Ival.min_and_max (project max) with + | (min, Some b), (Some e, max) when Integer.le b e -> + (* All integers between [b] and [e] are necessarily in the range to be + considered. If [e-b] is small enough, evaluate [eval_term i] for each [i] + between [b] and [e]. Otherwise, evaluate [eval_term] for the bound [b] + and [e], and for the interval [b+1..e-1]. We could be more precise by + subdividing the interval. *) + let r = + if Integer.equal e b + then eval_term (Cvalue.V.inject_int b) + else + let fold = + if Integer.(le (sub e b) (of_int 10)) + then Ival.fold_enum + else Ival.fold_int_bounds + in + let ival = Ival.inject_range (Some b) (Some e) in + let list = fold (fun i acc -> eval_ival i :: acc) ival [] in + (* Reduce each result to only keep the possible extrema. *) + let hd, tl = List.hd list, List.tl list in + List.fold_left (compute_extremum backward_left) hd tl + in + (* Integers below [b] and above [e] may not be in the range to be considered, + so we can't reduce [r] according to them. However, we must join to [r] + the evaluation of [eval_term] for these integers, and we can reduce + these evaluations to only keep results greater (or lower, according to + [backward_left]) than [r]. *) + let below = eval_ival (Ival.inject_range min (Some (Integer.pred b))) + and above = eval_ival (Ival.inject_range (Some (Integer.succ e)) max) in + let below = { below with eover = backward_left below.eover r.eover } + and above = { above with eover = backward_left above.eover r.eover } in + join_eval_result r (join_eval_result below above) + | (b, _), (_, e) -> + (* If [min] can be greater than [max], the result is unspecified. *) + let r = eval_ival (Ival.inject_range b e) in + return { r with eover = Cvalue.V.top; eunder = Cvalue.V.bottom } + | exception Cvalue.V.Not_based_on_null -> + let r = eval_term (Cvalue.V.join min.eover max.eover) in + return { r with eover = Cvalue.V.top; eunder = Cvalue.V.bottom } let eval_tlval_as_location ~alarm_mode env t = let r = eval_term_as_lval ~alarm_mode env t in @@ -1814,8 +1885,7 @@ let reduce_by_left_relation ~alarm_mode env positive tl rel tr = let reduce = Eval_op.backward_comp_left_from_type logic_var.lv_type in let cvalue = reduce positive comp cvalue cond_v in if V.is_bottom cvalue then raise Reduce_to_bottom; - let logic_vars = LogicVarEnv.add logic_var cvalue env.logic_vars in - { env with logic_vars } + add_logic_var env logic_var cvalue | Location (typ_loc, locs) -> let reduce = Eval_op.backward_comp_left_from_type (Ctype typ_loc) in if debug then Format.printf "#Val right term %a@." V.pretty cond_v; @@ -1937,8 +2007,7 @@ let reduce_by_known_papp ~alarm_mode env positive li _labels args = let vl = LogicVarEnv.find logic_var env.logic_vars in let reduced = Cvalue.V.narrow vl vr in if V.equal V.bottom reduced then raise Reduce_to_bottom; - let logic_vars = LogicVarEnv.add logic_var reduced env.logic_vars in - { env with logic_vars } + add_logic_var env logic_var reduced | Location (_typ, locsl) -> let aux locl env = let state = env_current_state env in