From 9bde4b5d3e500a8444fd9cf3d290d7ffc6699105 Mon Sep 17 00:00:00 2001 From: Basile Desloges <basile.desloges@cea.fr> Date: Wed, 15 Jul 2020 16:06:50 +0200 Subject: [PATCH] [eacsl:codegen] Add support of `\separated` for explicit range --- .../e-acsl/src/analyses/memory_tracking.ml | 8 +- src/plugins/e-acsl/src/analyses/typing.ml | 6 +- .../src/code_generator/memory_translate.ml | 119 ++++++++++++++++-- .../src/code_generator/memory_translate.mli | 8 ++ .../e-acsl/src/code_generator/translate.ml | 29 ++++- 5 files changed, 154 insertions(+), 16 deletions(-) diff --git a/src/plugins/e-acsl/src/analyses/memory_tracking.ml b/src/plugins/e-acsl/src/analyses/memory_tracking.ml index e6225e41ed0..353db8f4f00 100644 --- a/src/plugins/e-acsl/src/analyses/memory_tracking.ml +++ b/src/plugins/e-acsl/src/analyses/memory_tracking.ml @@ -365,9 +365,15 @@ module rec Transfer (* Options.feedback "REGISTER %a" Cil.d_term t;*) state_ref := register_term kf !state_ref t; Cil.DoChildren + | Pseparated tlist -> + state_ref := + List.fold_left + (register_term kf) + !state_ref + tlist; + Cil.DoChildren | Pallocable _ -> Error.not_yet "\\allocable" | Pfresh _ -> Error.not_yet "\\fresh" - | Pseparated _ -> Error.not_yet "\\separated" | Pdangling _ -> Error.not_yet "\\dangling" | Ptrue | Pfalse | Papp _ | Prel _ | Pand _ | Por _ | Pxor _ | Pimplies _ | Piff _ | Pnot _ | Pif _ diff --git a/src/plugins/e-acsl/src/analyses/typing.ml b/src/plugins/e-acsl/src/analyses/typing.ml index 04731a08f3c..e413aea96d1 100644 --- a/src/plugins/e-acsl/src/analyses/typing.ml +++ b/src/plugins/e-acsl/src/analyses/typing.ml @@ -593,7 +593,6 @@ let rec type_predicate p = Options.fatal "unexpected logic definition" Printer.pp_predicate p end - | Pseparated _ -> Error.not_yet "\\separated" | Pdangling _ -> Error.not_yet "\\dangling" | Prel(_, t1, t2) -> let i1 = Interval.infer t1 in @@ -681,6 +680,11 @@ let rec type_predicate p = guards; (type_predicate goal).ty + | Pseparated tlist -> + List.iter + (fun t -> ignore (type_term ~use_gmp_opt:false ~ctx:Nan t)) + tlist; + c_int | Pinitialized(_, t) | Pfreeable(_, t) | Pallocable(_, t) diff --git a/src/plugins/e-acsl/src/code_generator/memory_translate.ml b/src/plugins/e-acsl/src/code_generator/memory_translate.ml index 654433624a3..f06b6096a08 100644 --- a/src/plugins/e-acsl/src/code_generator/memory_translate.ml +++ b/src/plugins/e-acsl/src/code_generator/memory_translate.ml @@ -133,16 +133,16 @@ let call ~loc kf name ctx env t = (* Take the term [size] that has been typed into GMP and return an expression of type [size_t]. - The case where [!(0 <= size < SIZE_MAX)] is an UB ==> guard against it. *) + The case where [!(0 <= size <= SIZE_MAX)] is an UB ==> guard against it. + Since the case [0 <= size] is already checked before calling this function, + only [size <= SIZE_MAX] is added as a guard. *) let gmp_to_sizet ~loc kf env size p = let sizet = Cil.(theMachine.typeOfSizeOf) in (* The guard *) let sizet_max = Logic_const.tint ~loc (Cil.max_unsigned_number (Cil.bitsSizeOf sizet)) in - let guard_upper = Logic_const.prel ~loc (Rlt, size, sizet_max) in - let guard_lower = Logic_const.prel ~loc (Rle, Cil.lzero ~loc (), size) in - let guard = Logic_const.pand ~loc (guard_lower, guard_upper) in + let guard = Logic_const.prel ~loc (Rle, size, sizet_max) in Typing.type_named_predicate ~must_clear:false guard; let guard, env = !predicate_to_exp_ref kf env guard in (* Translate term [size] into an exp of type [size_t] *) @@ -163,10 +163,12 @@ let gmp_to_sizet ~loc kf env size p = in e, env -(* Call to [__e_acsl_<name>] for terms of the form [ptr + r] - when [<name> = valid or initialized or valid_read] and - where [ptr] is an address and [r] a range offset *) -let call_memory_block ~loc kf name ctx env ptr r p = +(* Take a term of the form [ptr + r] where [ptr] is an address and [r] a range + offset, and return a tuple [(ptr, size, env)] where [ptr] is the address of + the start of the range, [size] is the size of the range in bytes and [env] is + the current environment. + [p] is the predicate under test. *) +let range_to_ptr_and_size ~loc kf env ptr r p = let n1, n2 = match r.term_node with | Trange(Some n1, Some n2) -> n1, n2 @@ -192,8 +194,7 @@ let call_memory_block ~loc kf name ctx env ptr r p = (Ctype typ_charptr) in Typing.type_term ~use_gmp_opt:false ~ctx:Typing.nan ptr; - let term_to_exp = !term_to_exp_ref in - let ptr, env = term_to_exp kf (Env.rte env true) ptr in + let ptr, env = !term_to_exp_ref kf (Env.rte env true) ptr in (* size *) let size_term = (* Since [s] and [n1] have been typed through [ptr], @@ -210,18 +211,47 @@ let call_memory_block ~loc kf name ctx env ptr r p = Logic_const.tinteger ~loc 1)) Linteger in - Logic_const.term ~loc (TBinOp (Mult, s, count)) Linteger + let size_term = Logic_const.term ~loc (TBinOp (Mult, s, count)) Linteger in + (* Create a let binding with the value of the size *) + let size_term_info = + { l_var_info = Cil_const.make_logic_var_local "size" Linteger; + l_type = None; + l_tparams = []; + l_labels = []; + l_profile = []; + l_body = LBterm size_term; + } + in + let size_term_lv = Logic_const.tvar ~loc size_term_info.l_var_info in + (* If [size_term <= 0], then the range represents an empty set and the size + should be set to exactly [0]. *) + let tzero = Logic_const.tinteger ~loc 0 in + let size_term_if = + Logic_const.term + ~loc + (Tif (Logic_const.term ~loc (TBinOp (Le, size_term_lv, tzero)) Linteger, + tzero, + size_term_lv)) + Linteger + in + Logic_const.term ~loc (Tlet (size_term_info, size_term_if)) Linteger in Typing.type_term ~use_gmp_opt:false size_term; let size, env = match Typing.get_number_ty size_term with | Typing.Gmpz -> gmp_to_sizet ~loc kf env size_term p | Typing.(C_integer _ | C_float _) -> - let size, env = term_to_exp kf env size_term in - Cil.constFold false size, env + !term_to_exp_ref kf env size_term | Typing.(Rational | Real | Nan) -> assert false in + ptr, size, env + +(* Call to [__e_acsl_<name>] for terms of the form [ptr + r] + when [<name> = valid or initialized or valid_read] and + where [ptr] is an address and [r] a range offset *) +let call_memory_block ~loc kf name ctx env ptr r p = + let ptr, size, env = range_to_ptr_and_size ~loc kf env ptr r p in (* base and base_addr *) let base, _ = Misc.ptr_index ~loc ptr in let base_addr = match base.enode with @@ -391,6 +421,69 @@ let call_valid ~loc kf name ctx env t p = p call_for_unsupported_constructs +(* \separated *) +let call_separated ~loc kf ctx env tlist p = + let term_to_ptr_and_size kf env t = + let term_to_exp = !term_to_exp_ref in + let e, env = term_to_exp kf (Env.rte env true) t in + let ty = Misc.cty t.term_type in + let sizeof = Misc.mk_ptr_sizeof ty loc in + e, sizeof, env + in + let n_args, args, env = + List.fold_left + (fun (n_args, args, env) t -> + let ptr, size, env = + if Misc.is_bitfield_pointers t.term_type then + Error.not_yet "bitfield pointer in \\separated"; + match t.term_node with + | TBinOp((PlusPI | IndexPI), + ptr, + ({ term_node = Trange _ } as r)) -> + if Misc.is_set_of_ptr_or_array ptr.term_type then + Error.not_yet + "arithmetic over set of pointers or arrays in \\separated" + else + range_to_ptr_and_size ~loc kf env ptr r p + | TAddrOf(TVar lv, TIndex({ term_node = Trange _ } as r, TNoOffset)) -> + (* Case A *) + assert (Logic_const.is_set_type t.term_type); + let lty_noset = Logic_const.type_of_element t.term_type in + let ptr = + Logic_const.taddrof ~loc (TVar lv, TNoOffset) lty_noset + in + range_to_ptr_and_size ~loc kf env ptr r p + | TAddrOf(TVar ({ lv_type = Ctype (TArray _) } as _lv), toffset) -> + if has_set_as_index toffset then + Error.not_yet "range elimination for \\separated" + else + term_to_ptr_and_size kf env t + | _ -> + term_to_ptr_and_size kf env t + in + let ptr = Cil.mkCast ~force:false ~e:ptr ~newt:Cil.voidPtrType in + let n_args = n_args + 1 in + let args = ptr :: size :: args in + n_args, args, env + ) + (0, [], env) + tlist + in + let args = (Cil.integer ~loc n_args) :: args in + let _, e, env = + Env.new_var + ~loc + ~name:"separated" + env + kf + None + ctx + (fun v _ -> [ + Constructor.mk_rtl_call ~loc ~result:(Cil.var v) "separated" args + ]) + in + e, env + (* Local Variables: compile-command: "make -C ../.." diff --git a/src/plugins/e-acsl/src/code_generator/memory_translate.mli b/src/plugins/e-acsl/src/code_generator/memory_translate.mli index 581d3c78114..8cf471dea9b 100644 --- a/src/plugins/e-acsl/src/code_generator/memory_translate.mli +++ b/src/plugins/e-acsl/src/code_generator/memory_translate.mli @@ -51,6 +51,14 @@ val call_valid: [t] can denote ranges of memory locations. [p] is the predicate under testing. *) +val call_separated: + loc:location -> kernel_function -> typ -> Env.t -> term list -> predicate -> + exp * Env.t +(* [call_separated ~loc kf ctx env tlist p] creates a call to the E-ACSL memory + builtin [separated]. + Each [term] in [tlist] can denote ranges of memory locations. + [p] is the predicate under testing. *) + (**************************************************************************) (********************** Forward references ********************************) (**************************************************************************) diff --git a/src/plugins/e-acsl/src/code_generator/translate.ml b/src/plugins/e-acsl/src/code_generator/translate.ml index 81b165d36f9..9ecf3c89082 100644 --- a/src/plugins/e-acsl/src/code_generator/translate.ml +++ b/src/plugins/e-acsl/src/code_generator/translate.ml @@ -882,7 +882,6 @@ and predicate_content_to_exp ?name kf env p = Typing.type_term ~use_gmp_opt:false ~ctx:Typing.c_int tapp; let e, env = term_to_exp kf env tapp in e, env - | Pseparated _ -> Env.not_yet env "\\separated" | Pdangling _ -> Env.not_yet env "\\dangling" | Pobject_pointer _ -> Env.not_yet env "\\object_pointer" | Pvalid_function _ -> Env.not_yet env "\\valid_function" @@ -982,6 +981,34 @@ and predicate_content_to_exp ?name kf env p = end | Pvalid _ -> Env.not_yet env "labeled \\valid" | Pvalid_read _ -> Env.not_yet env "labeled \\valid_read" + | Pseparated tlist -> + let env = + List.fold_left + (fun env t -> + let name = "separated_guard" in + let p = Logic_const.pvalid_read ~loc (BuiltinLabel Here, t) in + let p = { p with pred_name = name :: p.pred_name } in + let e, env = + predicate_content_to_exp + ~name + kf + env + p + in + let stmt = + Smart_stmt.runtime_check + Smart_stmt.RTE + kf + e + p + in + Env.add_assert kf stmt p; + Env.add_stmt env kf stmt + ) + env + tlist + in + Memory_translate.call_separated ~loc kf Cil.intType env tlist p | Pinitialized(BuiltinLabel Here, t) -> (match t.term_node with (* optimisation when we know that the initialisation is ok *) -- GitLab