diff --git a/.Makefile.lint b/.Makefile.lint index 6fbfc24fc477f4a72d46a4c8059501ee61d7951a..8d204ac86b7e088b1d89cb4a304fbe0ab437c21a 100644 --- a/.Makefile.lint +++ b/.Makefile.lint @@ -381,4 +381,4 @@ ML_LINT_KO+=src/plugins/e-acsl/src/code_generator/at_with_lscope.ml ML_LINT_KO+=src/plugins/e-acsl/src/code_generator/at_with_lscope.mli ML_LINT_KO+=src/plugins/e-acsl/src/code_generator/temporal.ml ML_LINT_KO+=src/plugins/e-acsl/src/code_generator/temporal.mli -ML_LINT_KO+=src/plugins/e-acsl/src/project_initializer/dup_functions.ml + diff --git a/src/plugins/e-acsl/Makefile.in b/src/plugins/e-acsl/Makefile.in index 9321f0f5a8d863065f3ed19c1ae5b9f884a67f5e..3217ed21a1f00a3ef9700316b40609514416f95e 100644 --- a/src/plugins/e-acsl/Makefile.in +++ b/src/plugins/e-acsl/Makefile.in @@ -58,8 +58,7 @@ SRC_ANALYSES:=$(addprefix src/analyses/, $(SRC_ANALYSES)) # project initializer SRC_PROJECT_INITIALIZER:= \ keep_status \ - prepare_ast \ - dup_functions + prepare_ast SRC_PROJECT_INITIALIZER:=\ $(addprefix src/project_initializer/, $(SRC_PROJECT_INITIALIZER)) diff --git a/src/plugins/e-acsl/doc/Changelog b/src/plugins/e-acsl/doc/Changelog index 78572f83775819071f278b22995fe41e269c545c..0fea8bfb71a79a2f880769b7dcd3505a7b13e49a 100644 --- a/src/plugins/e-acsl/doc/Changelog +++ b/src/plugins/e-acsl/doc/Changelog @@ -19,6 +19,8 @@ # configure configure ############################################################################### +-* E-ACSL [2020/01/06] Fix typing bug in presence of variable-length + arrays that may lead to incorrect generated code. -* E-ACSL [2019/12/04] Fix bug with compiler built-ins. ############################ diff --git a/src/plugins/e-acsl/src/analyses/typing.ml b/src/plugins/e-acsl/src/analyses/typing.ml index c46a2d297ab13e416ef4b6c0121c958bb318bb32..12ff23ceafff79ebd718dc413e35ed46bd84e492 100644 --- a/src/plugins/e-acsl/src/analyses/typing.ml +++ b/src/plugins/e-acsl/src/analyses/typing.ml @@ -182,42 +182,37 @@ module Memo: sig val clear: unit -> unit end = struct - module H = Hashtbl.Make(struct - type t = term - (* The comparison over terms is the physical equality. It cannot be the - structural one (given by [Cil_datatype.Term.equal]) because the very - same term can be used in 2 different contexts which lead to different - casts. - - By construction, there are no physically equal terms in the AST - built by Cil. Consequently the memoisation should be fully - useless. However the translation of E-ACSL guarded quantification - generates new terms (see module {!Quantif}) which must be typed. The term - corresponding to the bound variable [x] is actually used twice: once in - the guard and once for encoding [x+1] when incrementing it. The - memoization is only useful here and indeed prevent the generation of - one extra variable in some cases. *) - let equal (t1:term) t2 = t1 == t2 - let hash = Cil_datatype.Term.hash - end) - - let tbl = H.create 97 + (* The comparison over terms is the physical equality. It cannot be the + structural one (given by [Cil_datatype.Term.equal]) because the very same + term can be used in 2 different contexts which lead to different casts. + + By construction (see prepare_ast.ml), there are no physically equal terms + in the E-ACSL's generated AST. Consequently the memoisation should be fully + useless. However: + - type info of many terms are accessed several times + - the translation of E-ACSL guarded quantifications generates + new terms (see module {!Quantif}) which must be typed. The term + corresponding to the bound variable [x] is actually used twice: once in the + guard and once for encoding [x+1] when incrementing it. The memoization is + only useful here and indeed prevent the generation of one extra variable in + some cases. *) + let tbl = Misc.Id_term.Hashtbl.create 97 let get t = - try H.find tbl t + try Misc.Id_term.Hashtbl.find tbl t with Not_found -> Options.fatal "[typing] type of term '%a' was never computed." Printer.pp_term t let memo f t = - try H.find tbl t + try Misc.Id_term.Hashtbl.find tbl t with Not_found -> let x = f t in - H.add tbl t x; + Misc.Id_term.Hashtbl.add tbl t x; x - let clear () = H.clear tbl + let clear () = Misc.Id_term.Hashtbl.clear tbl end @@ -755,6 +750,6 @@ module Datatype = D (* Local Variables: -compile-command: "make -C ../.." +compile-command: "make -C ../../../../.." End: *) diff --git a/src/plugins/e-acsl/src/analyses/typing.mli b/src/plugins/e-acsl/src/analyses/typing.mli index 7572e09419c0c5db12cf8fee2328507d4018c1df..002f03c504695e951ca0bf74919c0ce7ab3a5f1f 100644 --- a/src/plugins/e-acsl/src/analyses/typing.mli +++ b/src/plugins/e-acsl/src/analyses/typing.mli @@ -160,6 +160,6 @@ val compute_quantif_guards_ref (* Local Variables: -compile-command: "make -C ../.." +compile-command: "make -C ../../../../.." End: *) diff --git a/src/plugins/e-acsl/src/code_generator/injector.ml b/src/plugins/e-acsl/src/code_generator/injector.ml index 40ddd792664456b4fad05ce76297906aba2e64c7..c49a5317279dc372c18f06fe5d6c6648e17ecf02 100644 --- a/src/plugins/e-acsl/src/code_generator/injector.ml +++ b/src/plugins/e-acsl/src/code_generator/injector.ml @@ -757,6 +757,7 @@ let reset_all ast = (* by default, do not run E-ACSL on the generated code *) Options.Run.off (); (* reset all the E-ACSL environments to their original states *) + Mmodel_analysis.reset (); Misc.reset (); Logic_functions.reset (); Literal_strings.reset (); @@ -776,6 +777,7 @@ let inject () = Project.pretty (Project.current ()); Keep_status.before_translation (); Misc.reorder_ast (); + Gmp_types.init (); let ast = Ast.get () in inject_in_file ast; reset_all ast; diff --git a/src/plugins/e-acsl/src/libraries/gmp_types.ml b/src/plugins/e-acsl/src/libraries/gmp_types.ml index 7ed32f22bda8cf2a429e805c35f87204e2eb1be8..84bbe038d874b982bca2792335ce09ac9fffc956 100644 --- a/src/plugins/e-acsl/src/libraries/gmp_types.ml +++ b/src/plugins/e-acsl/src/libraries/gmp_types.ml @@ -63,7 +63,7 @@ module Make(X: sig end) = struct ttype = TArray( TNamed(!t_struct_torig_ref, []), Some (Cil.one ~loc:Cil_datatype.Location.unknown), - {scache = Not_Computed}, + { scache = Not_Computed }, []); treferenced = true; } @@ -112,3 +112,9 @@ let init () = end in try Cil.visitCilFileSameGlobals set_mp_t (Ast.get ()) with Exit -> () + +(* +Local Variables: +compile-command: "make -C ../../../../.." +End: +*) diff --git a/src/plugins/e-acsl/src/libraries/gmp_types.mli b/src/plugins/e-acsl/src/libraries/gmp_types.mli index c61e8b889d535a6431c25ebbdafe836cad641ed3..560d265d3011ff8ddbdb9094580fe391767f1aa0 100644 --- a/src/plugins/e-acsl/src/libraries/gmp_types.mli +++ b/src/plugins/e-acsl/src/libraries/gmp_types.mli @@ -53,3 +53,9 @@ module Z: S (** Representation of the rational type at runtime *) module Q: S + +(* +Local Variables: +compile-command: "make -C ../../../../.." +End: +*) diff --git a/src/plugins/e-acsl/src/libraries/misc.ml b/src/plugins/e-acsl/src/libraries/misc.ml index 872847c39d4d4eb06ef6aa3ba1350146fadec2d3..97b2bea3cfa4b28dcce74f7ed444ef4c563a4ea1 100644 --- a/src/plugins/e-acsl/src/libraries/misc.ml +++ b/src/plugins/e-acsl/src/libraries/misc.ml @@ -219,6 +219,19 @@ let name_of_binop = function | MinusA -> "sub" | MinusPP | MinusPI | IndexPI | PlusPI -> assert false +module Id_term = + Datatype.Make_with_collections + (struct + include Cil_datatype.Term + let name = "E_ACSL.Id_term" + let compare (t1:term) t2 = + if t1 == t2 then 0 else Cil_datatype.Term.compare t1 t2 + let equal (t1:term) t2 = t1 == t2 + let structural_descr = Structural_descr.t_abstract + let rehash = Datatype.identity + let mem_project = Datatype.never_any_project + end) + (* Local Variables: compile-command: "make -C ../../../../.." diff --git a/src/plugins/e-acsl/src/libraries/misc.mli b/src/plugins/e-acsl/src/libraries/misc.mli index 82d10e9d096b78c3b34aac33e24d6b86777fdba1..0797bc7f8d9a0804373e1b0d1676ba8e9a3e8141 100644 --- a/src/plugins/e-acsl/src/libraries/misc.mli +++ b/src/plugins/e-acsl/src/libraries/misc.mli @@ -30,7 +30,7 @@ open Cil_types exception Unregistered_library_function of string val get_lib_fun_vi: string -> varinfo -(** Return varinfo corresponding to a name of a given library function *) +(** @return varinfo corresponding to a name of a given library function *) (* ************************************************************************** *) (** {2 Handling \result} *) @@ -80,14 +80,14 @@ val is_set_of_ptr_or_array: logic_type -> bool (** Checks whether the given logic type is a set of pointers. *) val is_range_free: term -> bool -(** Returns [true] iff the given term does not contain any range. *) +(** @return true iff the given term does not contain any range. *) val is_bitfield_pointers: logic_type -> bool -(** Returns [true] iff the given logic type is a bitfield pointer or a +(** @return true iff the given logic type is a bitfield pointer or a set of bitfield pointers. *) val term_has_lv_from_vi: term -> bool -(** Return [true] iff the given term contains a variables that originates from +(** @return true iff the given term contains a variables that originates from a C varinfo, that is a non-purely logic variable. *) type pred_or_term = PoT_pred of predicate | PoT_term of term @@ -97,10 +97,13 @@ val mk_ptr_sizeof: typ -> location -> exp to a [typ] typ and returns [sizeof(typ)]. *) val name_of_binop: binop -> string -(** Returns the name of the given binop as a string *) +(** @return the name of the given binop as a string. *) val finite_min_and_max: Ival.t -> Integer.t * Integer.t -(** [finite_min_and_max i] takes the finite ival [i] and returns its bounds *) +(** [finite_min_and_max i] takes the finite ival [i] and returns its bounds. *) + +module Id_term: Datatype.S_with_collections with type t = term +(** Datatype for terms that relies on physical equality. *) (* Local Variables: diff --git a/src/plugins/e-acsl/src/main.ml b/src/plugins/e-acsl/src/main.ml index 2a5627276847a8db29b27e54c64d14c185ea4d12..68ae48a0f107699115696f85e44e98702ba1ae18 100644 --- a/src/plugins/e-acsl/src/main.ml +++ b/src/plugins/e-acsl/src/main.ml @@ -137,42 +137,27 @@ let generate_code = (fun () -> Options.feedback "beginning translation."; Temporal.enable (Options.Temporal_validity.get ()); - let prepared_prj = Prepare_ast.prepare () in - let res = - Project.on - prepared_prj - (fun () -> - let dup_prj = Dup_functions.dup () in - let copied_prj = - Project.create_by_copy name ~last:true ~src:dup_prj - in - Project.on - copied_prj - (fun () -> - Gmp_types.init (); - Mmodel_analysis.reset (); - Injector.inject (); - (* remove the RTE's results computed from E-ACSL: - they are partial and associated with the wrong - kernel function (the one of the old project). *) - (* [TODO ARCHI] what if RTE was already computed? *) - let selection = - State_selection.with_dependencies !Db.RteGen.self - in - Project.clear ~selection ~project:copied_prj (); - Resulting_projects.mark_as_computed ()) - (); - if Options.Debug.get () = 0 then - Project.remove ~project:dup_prj (); - copied_prj) - () + let copied_prj = + Project.create_by_copy name ~last:true in - if Options.Debug.get () = 0 then begin - Project.remove ~project:prepared_prj (); - end; + Project.on + copied_prj + (fun () -> + Prepare_ast.prepare (); + Injector.inject (); + (* remove the RTE's results computed from E-ACSL: they are + partial and associated with the wrong kernel function (the + one of the old project). *) + (* [TODO ARCHI] what if RTE was already computed? *) + let selection = + State_selection.with_dependencies !Db.RteGen.self + in + Project.clear ~selection ~project:copied_prj (); + Resulting_projects.mark_as_computed ()) + (); Options.feedback "translation done in project \"%s\"." (Options.Project_name.get ()); - res) + copied_prj) ()) let generate_code = @@ -249,6 +234,6 @@ let () = Db.Main.extend main (* Local Variables: -compile-command: "make -C .." +compile-command: "make -C ../../../.." End: *) diff --git a/src/plugins/e-acsl/src/options.ml b/src/plugins/e-acsl/src/options.ml index 22a51c9286df2825631ab59de445461a2bbe3338..ae196e9bc0748be6ac5115454a851af8a406e3b6 100644 --- a/src/plugins/e-acsl/src/options.ml +++ b/src/plugins/e-acsl/src/options.ml @@ -166,7 +166,7 @@ let parameter_states = let must_visit () = Run.get () || Check.get () let dkey_analysis = register_category "analysis" -let dkey_dup = register_category "duplication" +let dkey_prepare = register_category "preparation" let dkey_translation = register_category "translation" let dkey_typing = register_category "typing" diff --git a/src/plugins/e-acsl/src/options.mli b/src/plugins/e-acsl/src/options.mli index cdc235ce85e6f7eeeb29834b7fdc90a68e2de292..cecf66c0d31025e0d4b40104d03e49db80f057f1 100644 --- a/src/plugins/e-acsl/src/options.mli +++ b/src/plugins/e-acsl/src/options.mli @@ -42,7 +42,7 @@ val parameter_states: State.t list val must_visit: unit -> bool val dkey_analysis: category -val dkey_dup: category +val dkey_prepare: category val dkey_translation: category val dkey_typing: category diff --git a/src/plugins/e-acsl/src/project_initializer/dup_functions.ml b/src/plugins/e-acsl/src/project_initializer/dup_functions.ml deleted file mode 100644 index 64fc328a712e224f33e2d6f31fdc0630b825350c..0000000000000000000000000000000000000000 --- a/src/plugins/e-acsl/src/project_initializer/dup_functions.ml +++ /dev/null @@ -1,428 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of the Frama-C's E-ACSL plug-in. *) -(* *) -(* Copyright (C) 2012-2019 *) -(* CEA (Commissariat à l'énergie atomique et aux énergies *) -(* alternatives) *) -(* *) -(* you can redistribute it and/or modify it under the terms of the GNU *) -(* Lesser General Public License as published by the Free Software *) -(* Foundation, version 2.1. *) -(* *) -(* It is distributed in the hope that it will be useful, *) -(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) -(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) -(* GNU Lesser General Public License for more details. *) -(* *) -(* See the GNU Lesser General Public License version 2.1 *) -(* for more details (enclosed in the file licenses/LGPLv2.1). *) -(* *) -(**************************************************************************) - -open Cil_types - -let dkey = Options.dkey_dup - -(* ********************************************************************** *) -(* Environment *) -(* ********************************************************************** *) - -let fct_tbl: unit Kernel_function.Hashtbl.t = Kernel_function.Hashtbl.create 7 - -let actions = Queue.create () - -module Global: sig - val add_logic_info: logic_info -> unit - val mem_logic_info: logic_info -> bool - val reset: unit -> unit -end = struct - - let tbl = Cil_datatype.Logic_info.Hashtbl.create 7 - let add_logic_info x = Cil_datatype.Logic_info.Hashtbl.add tbl x () - let mem_logic_info x = Cil_datatype.Logic_info.Hashtbl.mem tbl x - let reset () = Cil_datatype.Logic_info.Hashtbl.clear tbl - -end - -let reset () = - Kernel_function.Hashtbl.clear fct_tbl; - Global.reset (); - Queue.clear actions - -(* ********************************************************************** *) -(* Duplicating functions *) -(* ********************************************************************** *) - -let dup_funspec tbl bhv spec = - (* Options.feedback "DUP SPEC %a" Cil.d_funspec spec;*) - let o = object - inherit Cil.genericCilVisitor bhv - - val already_visited = Cil_datatype.Logic_var.Hashtbl.create 7 - - method !vlogic_info_use li = - if Global.mem_logic_info li then - Cil.ChangeDoChildrenPost - ({ li with l_var_info = li.l_var_info } (* force a copy *), - Visitor_behavior.Get.logic_info bhv) - else - Cil.JustCopy - - method !vterm_offset _ = - Cil.DoChildrenPost - (function - (* no way to directly visit fieldinfo and model_info uses *) - | TField(fi, off) -> TField(Visitor_behavior.Get.fieldinfo bhv fi, off) - | TModel(mi, off) -> TModel(Visitor_behavior.Get.model_info bhv mi, off) - | off -> off) - - method !vlogic_var_use orig_lvi = - match orig_lvi.lv_origin with - | None -> - Cil.JustCopy - | Some vi -> - try - let new_lvi = - Cil_datatype.Logic_var.Hashtbl.find already_visited orig_lvi - in - Cil.ChangeTo new_lvi - with Not_found -> - Cil.ChangeDoChildrenPost - ({ orig_lvi with lv_id = orig_lvi.lv_id } (* force a copy *), - fun lvi -> - (* using [Visitor_behavior.Get.logic_var bhv lvi] is correct only because the - lv_id used to compare the lvi does not change between the - original one and this copy *) - try - let new_vi = Cil_datatype.Varinfo.Hashtbl.find tbl vi in - Cil_datatype.Logic_var.Hashtbl.add - already_visited orig_lvi lvi; - lvi.lv_id <- new_vi.vid; - lvi.lv_name <- new_vi.vname; - lvi.lv_origin <- Some new_vi; - new_vi.vlogic_var_assoc <- Some lvi; - lvi - with Not_found -> - assert vi.vglob; - Visitor_behavior.Get.logic_var bhv lvi) - - method !videntified_term _ = - Cil.DoChildrenPost Logic_const.refresh_identified_term - - method !videntified_predicate _ = - Cil.DoChildrenPost Logic_const.refresh_predicate - end in - Cil.visitCilFunspec o spec - -let dup_fundec loc spec bhv sound_verdict_vi kf vi new_vi = - new_vi.vdefined <- true; - let formals = Kernel_function.get_formals kf in - let mk_formal vi = - let name = - if vi.vname = "" then - (* unnamed formal parameter: must generate a fresh name since a fundec - cannot have unnamed formals (see bts #2303). *) - Varname.get - ~scope:Varname.Function - (Functions.RTL.mk_gen_name "unamed_formal") - else - vi.vname - in - Cil.copyVarinfo vi name - in - let new_formals = List.map mk_formal formals in - let res = - let ty = Kernel_function.get_return_type kf in - if Cil.isVoidType ty then None - else Some (Cil.makeVarinfo false false ~referenced:true "__retres" ty) - in - let return = - Cil.mkStmt ~valid_sid:true - (Return(Extlib.opt_map (Cil.evar ~loc) res, loc)) - in - let stmts = - let l = - [ Cil.mkStmtOneInstr ~valid_sid:true - (Call(Extlib.opt_map Cil.var res, - Cil.evar ~loc vi, - List.map (Cil.evar ~loc) new_formals, - loc)); - return ] - in - if Functions.instrument kf then - l - else - (* set the 'unsound_verdict' variable to 'false' whenever required *) - let unsound = - Cil.mkStmtOneInstr ~valid_sid:true - (Set((Var sound_verdict_vi, NoOffset), Cil.zero ~loc, loc)) - in - unsound :: l - in - let locals = match res with None -> [] | Some r -> [ r ] in - let body = Cil.mkBlock stmts in - body.blocals <- locals; - let tbl = Cil_datatype.Varinfo.Hashtbl.create 7 in - List.iter2 (Cil_datatype.Varinfo.Hashtbl.add tbl) formals new_formals; - let new_spec = dup_funspec tbl bhv spec in - { svar = new_vi; - sformals = new_formals; - slocals = locals; - smaxid = List.length new_formals; - sbody = body; - smaxstmtid = None; - sallstmts = []; - sspec = new_spec } - -let dup_global loc actions spec bhv sound_verdict_vi kf vi new_vi = - let name = vi.vname in - Options.feedback ~dkey ~level:2 "entering in function %s" name; - let fundec = dup_fundec loc spec bhv sound_verdict_vi kf vi new_vi in - let fct = Definition(fundec, loc) in - let new_spec = fundec.sspec in - let new_kf = { fundec = fct; spec = new_spec } in - Queue.add (fun () -> - Kernel_function.Hashtbl.add fct_tbl new_kf (); - Globals.Functions.register new_kf; - Globals.Functions.replace_by_definition new_spec fundec loc; - Annotations.register_funspec new_kf) - actions; - Options.feedback ~dkey ~level:2 "function %s" name; - (* remove the specs attached to the previous kf iff it is a definition: - it is necessary to keep stable the number of annotations in order to get - [Keep_status] working fine. *) - let kf = Visitor_behavior.Get.kernel_function bhv kf in - if Kernel_function.is_definition kf then begin - Queue.add - (fun () -> - let bhvs = - Annotations.fold_behaviors (fun e b acc -> (e, b) :: acc) kf [] - in - List.iter - (fun (e, b) -> Annotations.remove_behavior ~force:true e kf b) - bhvs; - Annotations.iter_decreases - (fun e _ -> Annotations.remove_decreases e kf) - kf; - Annotations.iter_terminates - (fun e _ -> Annotations.remove_terminates e kf) - kf; - Annotations.iter_complete - (fun e l -> Annotations.remove_complete e kf l) - kf; - Annotations.iter_disjoint - (fun e l -> Annotations.remove_disjoint e kf l) - kf) - actions - end; - GFun(fundec, loc), GFunDecl(new_spec, new_vi, loc) - -(* ********************************************************************** *) -(* Visitor *) -(* ********************************************************************** *) - -type position = Before_gmp | Gmpz | After_gmp | Memory_model | Code - -class dup_functions_visitor prj = object (self) - inherit Visitor.frama_c_copy prj - - val unduplicable_functions = - let white_list = - [ "__builtin_va_arg"; - "__builtin_va_end"; - "__builtin_va_start"; - "__builtin_va_copy" ] - in - List.fold_left - (fun acc s -> Datatype.String.Set.add s acc) - Datatype.String.Set.empty - white_list - - val fct_tbl = Cil_datatype.Varinfo.Hashtbl.create 7 - val mutable before_memory_model = Before_gmp - val mutable new_definitions: global list = [] - (* new definitions of the annotated functions which will contain the - translation of the E-ACSL contract *) - - val mutable sound_verdict_vi = - let name = Functions.RTL.mk_api_name "sound_verdict" in - let vi = Project.on prj (Cil.makeGlobalVar name) Cil.intType in - vi.vstorage <- Extern; - vi.vreferenced <- true; - vi - - method private before_memory_model = match before_memory_model with - | Before_gmp | Gmpz | After_gmp -> true - | Memory_model | Code -> false - - method private insert_libc l = - match new_definitions with - | [] -> l - | _ :: _ -> - (* add the generated definitions of libc at the end of [l]. This way, - we are sure that they have access to all of it (in particular, the - memory model, GMP and the soundness variable). - - Also add the [__e_acsl_sound_verdict] variable at the beginning *) - let res = - GVarDecl(sound_verdict_vi, Cil_datatype.Location.unknown) - :: l - @ new_definitions in - new_definitions <- []; - res - - method private next () = - match before_memory_model with - | Before_gmp -> () - | Gmpz -> before_memory_model <- After_gmp - | After_gmp -> () - | Memory_model -> before_memory_model <- Code - | Code -> () - - method !vlogic_info_decl li = - Global.add_logic_info li; - Cil.JustCopy - - method !vvrbl vi = - try - let new_vi = Cil_datatype.Varinfo.Hashtbl.find fct_tbl vi in - Cil.ChangeTo new_vi - with Not_found -> - Cil.JustCopy - - method private is_unvariadic_function vi = - match Cil.unrollType vi.vtype with - | TFun(_, _, variadic, _) -> not variadic - | _ -> false - - method !vglob_aux = function - | GFunDecl(_, vi, loc) | GFun({ svar = vi }, loc) - when (* duplicate a function iff: *) - not (Cil_datatype.Varinfo.Hashtbl.mem fct_tbl vi) - (* it is not already duplicated *) - && not (Datatype.String.Set.mem vi.vname unduplicable_functions) - (* it is duplicable *) - && self#is_unvariadic_function vi (* it is not a variadic function *) - && not (Misc.is_library_loc loc) (* it is not in the E-ACSL's RTL *) - && not (Misc.is_fc_or_compiler_builtin vi) (* it is not a built-in *) - && - (let kf = - try Globals.Functions.get vi with Not_found -> assert false - in - not (Functions.instrument kf) - (* either explicitely listed as to be not instrumented *) - || - (* or: *) - (not (Cil.is_empty_funspec - (Annotations.funspec ~populate:false - (Extlib.the self#current_kf))) - (* it has a function contract *) - && Functions.check kf - (* its annotations must be monitored *))) - -> - self#next (); - let name = Functions.RTL.mk_gen_name vi.vname in - let new_vi = - Project.on prj (Cil.makeGlobalVar name) vi.vtype - in - Cil_datatype.Varinfo.Hashtbl.add fct_tbl vi new_vi; - Cil.DoChildrenPost - (fun l -> match l with - | [ GVarDecl(vi, _) | GFunDecl(_, vi, _) | GFun({ svar = vi }, _) as g ] - -> - (match g with - | GFunDecl _ -> - if not (Kernel_function.is_definition (Extlib.the self#current_kf)) - && vi.vname <> "malloc" && vi.vname <> "free" - then - Options.warning "@[annotating undefined function `%a':@ \ -the generated program may miss memory instrumentation@ \ -if there are memory-related annotations.@]" - Printer.pp_varinfo vi - | GFun _ -> () - | _ -> assert false); - let tmp = vi.vname in - if tmp = Kernel.MainFunction.get () then begin - (* the new function becomes the new main: simply swap the name of both - functions *) - vi.vname <- new_vi.vname; - new_vi.vname <- tmp - end; - let kf = - try - Globals.Functions.get (Visitor_behavior.Get_orig.varinfo self#behavior vi) - with Not_found -> - Options.fatal - "unknown function `%s' while trying to duplicate it" - vi.vname - in - let spec = Annotations.funspec ~populate:false kf in - let vi_bhv = Visitor_behavior.Get.varinfo self#behavior vi in - let new_g, new_decl = - dup_global - loc - self#get_filling_actions - spec - self#behavior - sound_verdict_vi - kf - vi_bhv - new_vi - in - (* postpone the introduction of the new function definition to the - end *) - new_definitions <- new_g :: new_definitions; - (* put the declaration before the original function in order to solve - issue with recursive functions *) - [ new_decl; g ] - | _ -> assert false) - | GVarDecl(_, loc) | GFunDecl(_, _, loc) | GFun(_, loc) - when Misc.is_library_loc loc -> - (match before_memory_model with - | Before_gmp -> before_memory_model <- Gmpz - | Gmpz | Memory_model -> () - | After_gmp -> before_memory_model <- Memory_model - | Code -> () (* still processing the GMP and memory model headers, - but reading some libc code *)); - Cil.JustCopy - | GVarDecl(vi, _) | GFunDecl(_, vi, _) | GFun({ svar = vi }, _) - when Misc.is_fc_or_compiler_builtin vi -> - self#next (); - Cil.JustCopy - | _ -> - self#next (); - Cil.DoChildren - - method !vfile _ = - Cil.DoChildrenPost - (fun f -> - match new_definitions with - | [] -> f - | _ :: _ -> - (* required by the few cases where there is no global tagged as - [Code] in the file. *) - f.globals <- self#insert_libc f.globals; - f) - - initializer - Project.copy ~selection:(Parameter_state.get_selection ()) prj; - reset () - -end - -let dup () = - Options.feedback ~level:2 "duplicating annotated functions"; - let prj = - File.create_project_from_visitor - "e_acsl_dup_functions" - (new dup_functions_visitor) - in - Queue.iter (fun f -> f ()) actions; - prj - -(* -Local Variables: -compile-command: "make" -End: -*) diff --git a/src/plugins/e-acsl/src/project_initializer/dup_functions.mli b/src/plugins/e-acsl/src/project_initializer/dup_functions.mli deleted file mode 100644 index a200772d8d372958433b9bed83f94d5fab270719..0000000000000000000000000000000000000000 --- a/src/plugins/e-acsl/src/project_initializer/dup_functions.mli +++ /dev/null @@ -1,29 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of the Frama-C's E-ACSL plug-in. *) -(* *) -(* Copyright (C) 2012-2019 *) -(* CEA (Commissariat à l'énergie atomique et aux énergies *) -(* alternatives) *) -(* *) -(* you can redistribute it and/or modify it under the terms of the GNU *) -(* Lesser General Public License as published by the Free Software *) -(* Foundation, version 2.1. *) -(* *) -(* It is distributed in the hope that it will be useful, *) -(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) -(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) -(* GNU Lesser General Public License for more details. *) -(* *) -(* See the GNU Lesser General Public License version 2.1 *) -(* for more details (enclosed in the file licenses/LGPLv2.1). *) -(* *) -(**************************************************************************) - -val dup: unit -> Project.t - -(* -Local Variables: -compile-command: "make" -End: -*) diff --git a/src/plugins/e-acsl/src/project_initializer/prepare_ast.ml b/src/plugins/e-acsl/src/project_initializer/prepare_ast.ml index 1eee37987db0534b7caf7f8c7b39660d172e24eb..f73b359eeee82bc6cf905d347208873971dedb3c 100644 --- a/src/plugins/e-acsl/src/project_initializer/prepare_ast.ml +++ b/src/plugins/e-acsl/src/project_initializer/prepare_ast.ml @@ -22,59 +22,328 @@ open Cil_types -exception Alignment_error of string -let align_error s = raise (Alignment_error s) - -(* Returns true if the list of attributes [attrs] contains an [align] - * attribute of [algn] or greater. Returns false otherwise. - * Throws an exception if - * - [attrs] contains several [align] attributes specifying different - * alignment - * - [attrs] has a single align attribute with a value which is less than [algn] *) -let sufficiently_aligned attrs algn = - let alignment = List.fold_left (fun acc attr -> - match attr with - | Attr("align", [AInt i]) -> - let alignment = Integer.to_int i in - if acc <> 0 && acc <> alignment then - (* Multiple align attributes with different values *) - align_error "Multiple alignment attributes" - else if alignment < algn then - (* If there is an alignment attribute it should be greater - * or equal to [algn] *) - align_error "Insufficient alignment" - else - alignment - | Attr("align", _) -> - (* Align attribute with an argument other than a single number, - should not happen really *) - assert false - | _ -> acc - ) 0 attrs in alignment > 0 - -(* Given the type and the list of attributes of [varinfo] ([fieldinfo]) return - * true if that [varinfo] ([fieldinfo]) requires to be aligned at the boundary - * of [algn] (i.e., less than [algn] bytes and has no alignment attribute *) -let require_alignment typ attrs algn = - Cil.bitsSizeOf typ < algn*8 && not (sufficiently_aligned attrs algn) - -class prepare_visitor prj = object (self) - inherit Visitor.frama_c_copy prj +let dkey = Options.dkey_prepare - (* Add align attributes to local variables (required by temporal analysis) *) - method !vblock _ = - if Options.Temporal_validity.get () then - Cil.DoChildrenPost (fun blk -> - List.iter (fun vi -> - (* 4 bytes alignment is required to allow sufficient space for storage - of 32-bit timestamps in a 1:1 shadow. *) - if require_alignment vi.vtype vi.vattr 4; then begin - vi.vattr <- Attr("aligned",[AInt Integer.four]) :: vi.vattr - end) - blk.blocals; - blk) +(* ********************************************************************** *) +(* Environment *) +(* ********************************************************************** *) + +let fct_tbl: unit Kernel_function.Hashtbl.t = Kernel_function.Hashtbl.create 7 + +(* The purpose of [actions] is similar to the Frama-C visitor's + [get_filling_actions] but we need to fill it outside the visitor. So it is + our own version. *) +let actions = Queue.create () + +(* global table for ensuring that logic info are not shared between a function + definition and its duplicate *) +module Global: sig + val add_logic_info: logic_info -> unit + val mem_logic_info: logic_info -> bool + val reset: unit -> unit +end = struct + + let tbl = Cil_datatype.Logic_info.Hashtbl.create 7 + let add_logic_info x = Cil_datatype.Logic_info.Hashtbl.add tbl x () + let mem_logic_info x = Cil_datatype.Logic_info.Hashtbl.mem tbl x + let reset () = Cil_datatype.Logic_info.Hashtbl.clear tbl + +end + +let reset () = + Kernel_function.Hashtbl.clear fct_tbl; + Global.reset (); + Queue.clear actions + +(* ********************************************************************** *) +(* Duplicating a function *) +(* ********************************************************************** *) + +(* [tbl] associates the old formals to the new ones *) +let dup_funspec tbl bhv spec = + (* Options.feedback "DUP SPEC %a" Cil.d_funspec spec;*) + let o = object + inherit Cil.genericCilVisitor bhv + + val already_visited = Cil_datatype.Logic_var.Hashtbl.create 7 + + method !vlogic_info_use li = + if Global.mem_logic_info li then + Cil.ChangeDoChildrenPost + ({ li with l_var_info = li.l_var_info } (* force a copy *), + Visitor_behavior.Get.logic_info bhv) + else + Cil.JustCopy + + method !vterm_offset _ = + Cil.DoChildrenPost + (function (* no way to directly visit fieldinfo and model_info uses *) + | TField(fi, off) -> + TField(Visitor_behavior.Get.fieldinfo bhv fi, off) + | TModel(mi, off) -> + TModel(Visitor_behavior.Get.model_info bhv mi, off) + | off -> + off) + + method !vlogic_var_use orig_lvi = + match orig_lvi.lv_origin with + | None -> + Cil.JustCopy + | Some vi -> + try + let new_lvi = + Cil_datatype.Logic_var.Hashtbl.find already_visited orig_lvi + in + Cil.ChangeTo new_lvi + with Not_found -> + Cil.ChangeDoChildrenPost + ({ orig_lvi with lv_id = orig_lvi.lv_id } (* force a copy *), + fun lvi -> + try + let new_vi = Cil_datatype.Varinfo.Hashtbl.find tbl vi in + Cil_datatype.Logic_var.Hashtbl.add + already_visited orig_lvi lvi; + (* [lvi] is the logic counterpart of a formal varinfo that has + been replaced by a new one: propagate this change *) + lvi.lv_id <- new_vi.vid; + lvi.lv_name <- new_vi.vname; + lvi.lv_origin <- Some new_vi; + new_vi.vlogic_var_assoc <- Some lvi; + lvi + with Not_found -> + assert vi.vglob; + (* using [Visitor_behavior.Get.logic_var bhv lvi] is correct + only because the lv_id used to compare the lvi does not + change between the original one and this copy *) + Visitor_behavior.Get.logic_var bhv lvi) + + method !videntified_term _ = + Cil.DoChildrenPost Logic_const.refresh_identified_term + + method !videntified_predicate _ = + Cil.DoChildrenPost Logic_const.refresh_predicate + end in + Cil.visitCilFunspec o spec + +let dup_fundec loc spec bhv sound_verdict_vi kf vi new_vi = + new_vi.vdefined <- true; + let formals = Kernel_function.get_formals kf in + let mk_formal vi = + let name = + if vi.vname = "" then + (* unnamed formal parameter: must generate a fresh name since a fundec + cannot have unnamed formals (see bts #2303). *) + Varname.get + ~scope:Varname.Function + (Functions.RTL.mk_gen_name "unamed_formal") + else + vi.vname + in + Cil.copyVarinfo vi name + in + let new_formals = List.map mk_formal formals in + let res = + let ty = Kernel_function.get_return_type kf in + if Cil.isVoidType ty + then None + else Some (Cil.makeVarinfo false false ~referenced:true "__retres" ty) + in + let return = + Cil.mkStmt ~valid_sid:true + (Return(Extlib.opt_map (Cil.evar ~loc) res, loc)) + in + let stmts = + let l = + [ Cil.mkStmtOneInstr ~valid_sid:true + (Call(Extlib.opt_map Cil.var res, + Cil.evar ~loc vi, + List.map (Cil.evar ~loc) new_formals, + loc)); + return ] + in + if Functions.instrument kf then + l else - Cil.DoChildren + (* set the 'sound_verdict' variable to 'false' whenever required *) + let unsound = + Cil.mkStmtOneInstr ~valid_sid:true + (Set((Var sound_verdict_vi, NoOffset), Cil.zero ~loc, loc)) + in + unsound :: l + in + let locals = match res with None -> [] | Some r -> [ r ] in + let body = Cil.mkBlock stmts in + body.blocals <- locals; + let tbl = Cil_datatype.Varinfo.Hashtbl.create 7 in + List.iter2 (Cil_datatype.Varinfo.Hashtbl.add tbl) formals new_formals; + let new_spec = dup_funspec tbl bhv spec in + let fundec = + { svar = new_vi; + sformals = new_formals; + slocals = locals; + smaxid = List.length new_formals; + sbody = body; + smaxstmtid = None; + sallstmts = []; + sspec = new_spec } + in + (* compute the CFG of the new [fundec] *) + Cfg.clearCFGinfo ~clear_id:false fundec; + Cfg.cfgFun fundec; + fundec + +let dup_global loc actions spec bhv sound_verdict_vi kf vi new_vi = + let name = vi.vname in + Options.feedback ~dkey ~level:2 "entering in function %s" name; + let fundec = dup_fundec loc spec bhv sound_verdict_vi kf vi new_vi in + let fct = Definition(fundec, loc) in + let new_spec = fundec.sspec in + let new_kf = { fundec = fct; spec = new_spec } in + Queue.add + (fun () -> + Kernel_function.Hashtbl.add fct_tbl new_kf (); + Globals.Functions.register new_kf; + Globals.Functions.replace_by_definition new_spec fundec loc; + Annotations.register_funspec new_kf; + if new_vi.vname = "main" then begin + (* recompute the info about the old main since its name has changed; + see the corresponding part in the main visitor *) + Globals.Functions.remove vi; + Globals.Functions.register kf + end) + actions; + (* remove the specs attached to the previous kf iff it is a definition: + it is necessary to keep stable the number of annotations in order to get + [Keep_status] working fine. *) + let kf = Visitor_behavior.Get.kernel_function bhv kf in + if Kernel_function.is_definition kf then begin + Queue.add + (fun () -> + let bhvs = + Annotations.fold_behaviors (fun e b acc -> (e, b) :: acc) kf [] + in + List.iter + (fun (e, b) -> Annotations.remove_behavior ~force:true e kf b) + bhvs; + Annotations.iter_decreases + (fun e _ -> Annotations.remove_decreases e kf) + kf; + Annotations.iter_terminates + (fun e _ -> Annotations.remove_terminates e kf) + kf; + Annotations.iter_complete + (fun e l -> Annotations.remove_complete e kf l) + kf; + Annotations.iter_disjoint + (fun e l -> Annotations.remove_disjoint e kf l) + kf) + actions + end; + GFun(fundec, loc), GFunDecl(new_spec, new_vi, loc) + +(* ********************************************************************** *) +(* Alignment *) +(* ********************************************************************** *) + +(* Returns true if the list of attributes [attrs] contains an [align] attribute + of [algn] or greater. Returns false otherwise. + Throws an exception if + - [attrs] contains several [align] attributes specifying different + alignments + - [attrs] has a single align attribute with a value which is less than + [algn] *) +let sufficiently_aligned vi algn = + let alignment = + List.fold_left + (fun acc attr -> + match attr with + | Attr("align", [AInt i]) -> + let alignment = Integer.to_int i in + if acc <> 0 && acc <> alignment then begin + (* multiple align attributes with different values *) + Options.error + "multiple alignment attributes with different values for\ + variable %a. Keeping the last one." + Printer.pp_varinfo vi; + alignment + end else if alignment < algn then begin + (* if there is an alignment attribute it should be greater or equal + to [algn] *) + Options.error + "alignment of variable %a should be greater or equal to %d.@ \ + Continuing with this value." + Printer.pp_varinfo vi + algn; + algn + end else + alignment + | Attr("align", _) -> + (* align attribute with an argument other than a single number, + should not happen really *) + assert false + | _ -> acc) + 0 + vi.vattr + in + alignment > 0 + +(* return [true] iff the given [vi] requires to be aligned at the boundary + of [algn] (i.e., less than [algn] bytes and has no alignment attribute) *) +let require_alignment vi algn = + Cil.bitsSizeOf vi.vtype < algn*8 && not (sufficiently_aligned vi algn) + +(* ********************************************************************** *) +(* Visitor *) +(* ********************************************************************** *) + +class prepare_visitor = object (self) + inherit Visitor.frama_c_inplace + + val mutable has_new_stmt_in_fundec = false + + (* ---------------------------------------------------------------------- *) + (* visitor's local variable *) + (* ---------------------------------------------------------------------- *) + + val terms = Misc.Id_term.Hashtbl.create 7 + (* table for ensuring absence of term sharing *) + + val unduplicable_functions = + let white_list = + [ "__builtin_va_arg"; + "__builtin_va_end"; + "__builtin_va_start"; + "__builtin_va_copy" ] + in + List.fold_left + (fun acc s -> Datatype.String.Set.add s acc) + Datatype.String.Set.empty + white_list + + val fct_tbl = Cil_datatype.Varinfo.Hashtbl.create 7 + val mutable new_definitions: global list = [] + (* new definitions of the annotated functions which will contain the + translation of the E-ACSL contract *) + + (* the variable [sound_verdict] belongs to the E-ACSL's RTL and indicates + whether the verdict emitted by E-ACSL is sound. It needs to be visible at + that point because it is set in all function duplicates + (see [dup_fundec]). *) + val mutable sound_verdict_vi = + let name = Functions.RTL.mk_api_name "sound_verdict" in + let vi = Cil.makeGlobalVar name Cil.intType in + vi.vstorage <- Extern; + vi.vreferenced <- true; + vi + + (* ---------------------------------------------------------------------- *) + (* visitor's private methods *) + (* ---------------------------------------------------------------------- *) + + method private is_variadic_function vi = + match Cil.unrollType vi.vtype with + | TFun(_, _, variadic, _) -> variadic + | _ -> true (* IMPORTANT: for keeping property statuses, we must preserve the ordering of translation, see function [Translate.translate_pre_spec] and @@ -146,7 +415,7 @@ class prepare_visitor prj = object (self) | AVariant v -> push kf K_Variant (Property.ip_of_decreases kf (Kstmt stmt) v) | AAssigns _ -> - (* TODO: should be a postcondition, but considered as a unhandled + (* TODO: should be a postcondition, but considered as an unhandled precondition in translate.ml right now; and we need to preserve the same ordering *) Extlib.may @@ -156,8 +425,12 @@ class prepare_visitor prj = object (self) Extlib.may (push kf K_Allocation) (Property.ip_of_allocation kf (Kstmt stmt) (Property.Id_loop a) alloc) - | APragma _ -> () (* not yet translated *) - | AExtended _ -> () (* never translate extensions *) + | APragma _ -> + (* not yet translated *) + () + | AExtended _ -> + (* never translate extensions *) + () method private push_post_code_annot a = match a.annot_content with | AStmtSpec(_ (* TODO *), s) -> self#push_post_spec s @@ -169,37 +442,179 @@ class prepare_visitor prj = object (self) | APragma _ | AExtended _ -> () - (* Move variable declared in the body of a switch statement to the outer - scope *) - method !vstmt_aux init_stmt = + (* ---------------------------------------------------------------------- *) + (* visitor's method overloading *) + (* ---------------------------------------------------------------------- *) + + method !vlogic_info_decl li = + Global.add_logic_info li; + Cil.SkipChildren + + method !vvrbl vi = + try + let new_vi = Cil_datatype.Varinfo.Hashtbl.find fct_tbl vi in + (* replace functions at callsite by its duplicated version *) + Cil.ChangeTo new_vi + with Not_found -> + Cil.SkipChildren + + method !vterm _t = + Cil.DoChildrenPost + (fun t -> + if Misc.Id_term.Hashtbl.mem terms t then + (* remove term sharing for soundness of E-ACSL typing + (see typing.ml) *) + { t with term_node = t.term_node } + else begin + Misc.Id_term.Hashtbl.add terms t (); + t + end) + + (* Add align attributes to local variables (required by temporal analysis) *) + method !vblock _ = + if Options.Temporal_validity.get () then + Cil.DoChildrenPost + (fun blk -> + List.iter + (fun vi -> + (* 4 bytes alignment is required to allow sufficient space for + storage of 32-bit timestamps in a 1:1 shadow. *) + if require_alignment vi 4 then + vi.vattr <- + Attr("aligned", [ AInt Integer.four ]) :: vi.vattr) + blk.blocals; + blk) + else + Cil.DoChildren + + method !vstmt_aux stmt = Annotations.iter_code_annot (fun _ a -> self#push_pre_code_annot a) - init_stmt; + stmt; Cil.DoChildrenPost - (fun stmt -> + (fun _ -> Annotations.iter_code_annot (fun _ a -> self#push_post_code_annot a) - init_stmt; + stmt; + (* move variables declared in the body of a switch statement to the + outer scope *) match stmt.skind with | Switch(_,sw_blk,_,_) -> let new_blk = Cil.mkBlock [ stmt ] in let new_stmt = Cil.mkStmt ~valid_sid:true (Block new_blk) in new_blk.blocals <- sw_blk.blocals; sw_blk.blocals <- []; + has_new_stmt_in_fundec <- true; new_stmt - | _ -> stmt) + | _ -> + stmt) - method private is_unvariadic_function vi = - match Cil.unrollType vi.vtype with - | TFun(_, _, variadic, _) -> not variadic - | _ -> false + method !vfunc fundec = + Cil.DoChildrenPost + (fun _ -> + if has_new_stmt_in_fundec then begin + has_new_stmt_in_fundec <- false; + (* recompute the CFG *) + Cfg.clearCFGinfo ~clear_id:false fundec; + Cfg.cfgFun fundec; + end; + fundec) method !vglob_aux = function + | GFunDecl(_, vi, loc) | GFun({ svar = vi }, loc) + when (* duplicate a function iff: *) + (* it is not already duplicated *) + not (Cil_datatype.Varinfo.Hashtbl.mem fct_tbl vi) + && (* it is duplicable *) + not (Datatype.String.Set.mem vi.vname unduplicable_functions) + && (* it is not a variadic function *) + not (self#is_variadic_function vi) + && (* it is not in the E-ACSL's RTL *) + not (Misc.is_library_loc loc) + && (* it is not a built-in *) + not (Misc.is_fc_or_compiler_builtin vi) + && + (let kf = + try Globals.Functions.get vi with Not_found -> assert false + in + (* either explicitely listed as to be not instrumented *) + not (Functions.instrument kf) + || + (* or: *) + ((* it has a function contract *) + not (Cil.is_empty_funspec + (Annotations.funspec ~populate:false + (Extlib.the self#current_kf))) + && (* its annotations must be monitored *) + Functions.check kf)) + -> + let name = Functions.RTL.mk_gen_name vi.vname in + let new_vi = Cil.makeGlobalVar name vi.vtype in + Cil_datatype.Varinfo.Hashtbl.add fct_tbl vi new_vi; + Cil.DoChildrenPost + (fun l -> match l with + | [ GFunDecl(_, vi, _) | GFun({ svar = vi }, _) as g ] + -> + let kf = Extlib.the self#current_kf in + (match g with + | GFunDecl _ -> + if not (Kernel_function.is_definition kf) + && vi.vname <> "malloc" && vi.vname <> "free" + then + Options.warning + "@[annotating undefined function `%a':@ \ + the generated program may miss memory instrumentation@ \ + if there are memory-related annotations.@]" + Printer.pp_varinfo vi + | GFun _ -> () + | _ -> assert false); + let spec = Annotations.funspec ~populate:false kf in + self#push_pre_spec spec; + self#push_post_spec spec; + let tmp = vi.vname in + if tmp = "main" then begin + (* the new function becomes the new main: *) + (* 1. swap the name of both functions *) + vi.vname <- new_vi.vname; + new_vi.vname <- tmp; + (* 2. force recomputation of the entry point if necessary *) + if Kernel.MainFunction.get () = tmp then begin + let selection = + State_selection.with_dependencies Kernel.MainFunction.self + in + Project.clear ~selection () + end + (* 3. recompute what is necessary in [Globals.Functions]: + done in [dup_global] *) + end; + let new_g, new_decl = + dup_global + loc + self#get_filling_actions + spec + self#behavior + sound_verdict_vi + kf + vi + new_vi + in + (* postpone the introduction of the new function definition to the + end *) + new_definitions <- new_g :: new_definitions; + (* put the declaration before the original function in order to + solve issue with recursive functions *) + [ new_decl; g ] + | _ -> assert false) + + | GVarDecl(vi, loc) | GFunDecl(_, vi, loc) | GFun({ svar = vi }, loc) + when Misc.is_library_loc loc || Misc.is_fc_or_compiler_builtin vi -> + Cil.DoChildren + | GVarDecl(vi, loc) | GFunDecl(_, vi, loc) | GFun({ svar = vi }, loc) - when self#is_unvariadic_function vi - && not (Misc.is_library_loc loc) - && not (Misc.is_fc_or_compiler_builtin vi) + when not (self#is_variadic_function vi) -> + assert (* handled by the 2 cases above *) + (not (Misc.is_library_loc loc || Misc.is_fc_or_compiler_builtin vi)); let kf = Extlib.the self#current_kf in let s = Annotations.funspec ~populate:false kf in Cil.DoChildrenPost @@ -207,18 +622,39 @@ class prepare_visitor prj = object (self) self#push_pre_spec s; self#push_post_spec s; f) + | _ -> Cil.DoChildren - initializer Project.copy ~selection:(Parameter_state.get_selection ()) prj + method !vfile f = + Cil.DoChildrenPost + (fun _ -> + match new_definitions with + | [] -> f + | _ :: _ -> + (* add the generated definitions of libc at the end of + [new_definitions]. This way, we are sure that they have access to + all of it (in particular, the memory model, GMP and the soundness + variable). Also add the [__e_acsl_sound_verdict] variable at the + beginning *) + let new_globals = + GVarDecl(sound_verdict_vi, Cil_datatype.Location.unknown) + :: f.globals + @ new_definitions + in + f.globals <- new_globals; + f) + + initializer + reset () end let prepare () = Options.feedback ~level:2 "prepare AST for E-ACSL transformations"; - File.create_project_from_visitor - "e_acsl_prepare_ast" - (new prepare_visitor) + Visitor.visitFramacFile (new prepare_visitor) (Ast.get ()); + Queue.iter (fun f -> f ()) actions; + Ast.mark_as_grown () (* Local Variables: diff --git a/src/plugins/e-acsl/src/project_initializer/prepare_ast.mli b/src/plugins/e-acsl/src/project_initializer/prepare_ast.mli index 54e87bf224e2f25d0580dcc9c436002faa8c2221..120e25eba95e287042928d125a18439de12313f2 100644 --- a/src/plugins/e-acsl/src/project_initializer/prepare_ast.mli +++ b/src/plugins/e-acsl/src/project_initializer/prepare_ast.mli @@ -22,15 +22,19 @@ (** Prepare AST for E-ACSL generation. - So for this module performs two tasks: - - move declarations of variables declared in the bodies of switch + More precisely, this module performs the following tasks: + - generating a new definition for functions with contract; + - removing term sharing; + - moving declarations of variables declared in the bodies of switch statements to upper scopes; - - store what is necessary to translate in [Keep_status]. *) + - storing what is necessary to translate in [Keep_status]; + - in case of temporal validity checks, adding the attribute "aligned" to + variables that are not sufficiently aligned. *) -val prepare: unit -> Project.t +val prepare: unit -> unit (* Local Variables: -compile-command: "make -C ../.." +compile-command: "make -C ../../../../.." End: *) diff --git a/src/plugins/e-acsl/tests/memory/vla.c b/src/plugins/e-acsl/tests/memory/vla.c index 0d2107a0bad4d549fcfa62d1c43b9b98aefd8674..28b41333424bdc50255fde6f48198b70782ce705 100644 --- a/src/plugins/e-acsl/tests/memory/vla.c +++ b/src/plugins/e-acsl/tests/memory/vla.c @@ -1,5 +1,5 @@ /* run.config - COMMENT: Check variable-length arrays + COMMENT: check variable-length arrays */ int LEN = 10; @@ -9,9 +9,9 @@ int main(int argc, char **argv) { int i; for (i = 0; i <= LEN; i++) { if (i < LEN) { - /*@assert \valid(&arr[i]); */ + /*@ assert \valid(&arr[i]); */ } else { - /*@assert ! \valid(&arr[i]); */ + /*@ assert ! \valid(&arr[i]); */ } } return 0;