diff --git a/src/kernel_services/ast_data/globals.ml b/src/kernel_services/ast_data/globals.ml index 9e7fc4010aba93779273e9d65cb4fc7d625a29ac..cae5f59e1f52990ea1df0ee8aa9ecab4caddfe69 100644 --- a/src/kernel_services/ast_data/globals.ml +++ b/src/kernel_services/ast_data/globals.ml @@ -359,6 +359,27 @@ module Functions = struct else res + let mem_name fct_name = + try + ignore (find_by_name fct_name); + true + with Not_found -> + false + + let mem_def_name fct_name = + try + ignore (find_def_by_name fct_name); + true + with Not_found -> + false + + let mem_decl_name fct_name = + try + ignore (find_decl_by_name fct_name); + true + with Not_found -> + false + let () = Parameter_builder.find_kf_by_name := find_by_name; Parameter_builder.find_kf_def_by_name := find_def_by_name; @@ -741,10 +762,18 @@ module Types = struct TypeNameToGlobal.mark_as_computed () end + let mem_enum_tag x = + resolve_types (); + Enums.mem x + let find_enum_tag x = resolve_types (); Enums.find x + let mem_type namespace s = + resolve_types (); + Types.mem (s, namespace) + let find_type namespace s = resolve_types (); Types.find (s, namespace) diff --git a/src/kernel_services/ast_data/globals.mli b/src/kernel_services/ast_data/globals.mli index c9866e785bfb78a40bbdb1777676d179c47a94c3..9e5293c20ff883cae5aebbac55abd97051e1e499 100644 --- a/src/kernel_services/ast_data/globals.mli +++ b/src/kernel_services/ast_data/globals.mli @@ -103,6 +103,20 @@ module Functions: sig val get_params: kernel_function -> varinfo list val get_vi: kernel_function -> varinfo + (** {2 Membership} *) + + val mem_name: string -> bool + (** @return [true] iff there is a function with such a name + @since Frama-C+dev *) + + val mem_def_name: string -> bool + (** @return [true] iff there is a function definition with such a name + @since Frama-C+dev *) + + val mem_decl_name: string -> bool + (** @return [true] iff there is a function declaration with such a name + @since Frama-C+dev *) + (** {2 Searching} *) val find_by_name : string -> kernel_function @@ -219,10 +233,20 @@ module Types : sig (** The two functions below are suitable for use in functor {!Logic_typing.Make} *) + val mem_enum_tag: string -> bool + (** @return [true] iff there is an enum constant with the given name in the + AST. + @since Frama-C+dev *) + val find_enum_tag: string -> exp * typ (** Find an enum constant from its name in the AST. @raise Not_found when no such constant exists. *) + val mem_type: Logic_typing.type_namespace -> string -> bool + (** @return [true] iff there is a type with the given name in the given + namespace in the AST. + @since Frama-C+dev *) + val find_type: Logic_typing.type_namespace -> string -> typ (** Find a type from its name in the AST. @raise Not_found when no such type exists. *) diff --git a/src/kernel_services/ast_data/property_status.ml b/src/kernel_services/ast_data/property_status.ml index 441c1594ac728b5cbb08d757ac1ee7eaea8413fa..ea653a36c45d240f33ab4f23cefd7f791e13e4fb 100644 --- a/src/kernel_services/ast_data/property_status.ml +++ b/src/kernel_services/ast_data/property_status.ml @@ -168,6 +168,7 @@ module Status = end) let self = Status.self +let () = Ast.add_monotonic_state self let iter_on_statuses f ip = try @@ -231,6 +232,7 @@ end = struct end) let self = S.self + let () = Ast.add_monotonic_state self let _mem e path = try diff --git a/src/kernel_services/plugin_entry_points/db.ml b/src/kernel_services/plugin_entry_points/db.ml index 9d0a42e6ebda2f54c326da5882d51ae0154901ce..f602bf07dcbe541966723f30099c0e4fd8d7cce7 100644 --- a/src/kernel_services/plugin_entry_points/db.ml +++ b/src/kernel_services/plugin_entry_points/db.ml @@ -526,8 +526,20 @@ module Value = struct let add_formals_to_state = mk_fun "add_formals_to_state" + let get_fundec_from_stmt stmt = + let kf = + try + Kernel_function.find_englobing_kf stmt + with Not_found -> + Kernel.fatal "Unknown statement '%a'" Printer.pp_stmt stmt + in + try + Kernel_function.get_definition kf + with Kernel_function.No_Definition -> + Kernel.fatal "No definition for function %a" Kernel_function.pretty kf + let noassert_get_stmt_state ~after s = - if !no_results (Kernel_function.(get_definition (find_englobing_kf s))) + if !no_results (get_fundec_from_stmt s) then Cvalue.Model.top else let (find, add), find_by_callstack = @@ -586,7 +598,7 @@ module Value = struct exception Is_reachable let is_reachable_stmt stmt = - if !no_results (Kernel_function.(get_definition (find_englobing_kf stmt))) + if !no_results (get_fundec_from_stmt stmt) then true else let ho = try Some (Table_By_Callstack.find stmt) with Not_found -> None in diff --git a/src/plugins/e-acsl/Makefile.in b/src/plugins/e-acsl/Makefile.in index 936fc6f57fa40ce5d5602545bb8151cce84eb9a0..a44d27242911d78ca3342a8cac5779d96a85f5d7 100644 --- a/src/plugins/e-acsl/Makefile.in +++ b/src/plugins/e-acsl/Makefile.in @@ -44,6 +44,13 @@ SRC_LIBRARIES:= \ varname SRC_LIBRARIES:=$(addprefix src/libraries/, $(SRC_LIBRARIES)) +# project initializer +SRC_PROJECT_INITIALIZER:= \ + rtl \ + prepare_ast +SRC_PROJECT_INITIALIZER:=\ + $(addprefix src/project_initializer/, $(SRC_PROJECT_INITIALIZER)) + # analyses SRC_ANALYSES:= \ rte \ @@ -55,13 +62,6 @@ SRC_ANALYSES:= \ typing SRC_ANALYSES:=$(addprefix src/analyses/, $(SRC_ANALYSES)) -# project initializer -SRC_PROJECT_INITIALIZER:= \ - keep_status \ - prepare_ast -SRC_PROJECT_INITIALIZER:=\ - $(addprefix src/project_initializer/, $(SRC_PROJECT_INITIALIZER)) - # code generator SRC_CODE_GENERATOR:= \ constructor \ @@ -100,8 +100,8 @@ PLUGIN_NAME:=E_ACSL PLUGIN_CMO:= src/local_config \ src/options \ $(SRC_LIBRARIES) \ - $(SRC_ANALYSES) \ $(SRC_PROJECT_INITIALIZER) \ + $(SRC_ANALYSES) \ $(SRC_CODE_GENERATOR) \ src/main diff --git a/src/plugins/e-acsl/doc/Changelog b/src/plugins/e-acsl/doc/Changelog index ce93df0d9bec0532b42c14e7fb9742d785bcad18..caec156e2bce6ce838c1bb9d1c4957685e808264 100644 --- a/src/plugins/e-acsl/doc/Changelog +++ b/src/plugins/e-acsl/doc/Changelog @@ -25,6 +25,11 @@ Plugin E-ACSL <next-release> ############################ +-* E-ACSL [2020-08-28] Fix crash that may occur when translating + properties that have been proved valid by another plug-in + (frama-c/e-acsl#106). +-! E-ACSL [2020-08-28] Remove option -e-acsl-prepare-ast. +-! E-ACSL [2020-08-28] Remove option -e-acsl-check. - E-ACSL [2020-08-07] Add support for logical array comparison (frama-c/e-acsl#99). - E-ACSL [2020-07-28] Add support of bitwise operators diff --git a/src/plugins/e-acsl/doc/userman/biblio.bib b/src/plugins/e-acsl/doc/userman/biblio.bib index 27d9be4791f802dbe9eebcb8cd3ed69c8cbe2139..63e742e131e03df9a6f38531c3b56c3ad2b18246 100644 --- a/src/plugins/e-acsl/doc/userman/biblio.bib +++ b/src/plugins/e-acsl/doc/userman/biblio.bib @@ -59,6 +59,25 @@ url = {publis/hdr.pdf} } +@inproceedings{kosmatov20rv, + author = {Kosmatov, Nikolai and Maurica, Fonenantsoa and Signoles, Julien}, + title = {{Efficient Runtime Assertion Checking for Properties over + Mathematical Numbers}}, + booktitle = {International Conference on Runtime Verification (RV)}, + year = 2020, + month = oct, +} + +@inproceedings{ly18hilt, + author = {Dara Ly and Nikolai Kosmatov and Fr\'ed\'eric Loulergue + and Julien Signoles}, + title = {Soundness of a Dataflow Analysis for Memory Monitoring}, + booktitle = {Workshop on Languages and Tools for Ensuring Cyber-Resilience in +Critical Software-Intensive Systems (HILT)}, + year = 2018, + month = nov, +} + @inproceedings{rvcubes17tool, author = {Julien Signoles and Nikolai Kosmatov and Kostyantyn Vorobyov}, title = {{E-ACSL, a Runtime Verification Tool for Safety and Security of C diff --git a/src/plugins/e-acsl/doc/userman/changes.tex b/src/plugins/e-acsl/doc/userman/changes.tex index c69ab6038c6d344e0bbf612f2d171f4c96482979..034766eaca3e2ae51a6764834055cbb5dc55777e 100644 --- a/src/plugins/e-acsl/doc/userman/changes.tex +++ b/src/plugins/e-acsl/doc/userman/changes.tex @@ -5,6 +5,14 @@ release. First we list changes of the last release. \section*{E-ACSL \eacslpluginversion} +\begin{itemize} +\item \textbf{Simple Example}: Remove option \texttt{-e-acsl-check} +\item \textbf{Combining E-ACSL with Other PLug-ins}: \texttt{-e-acsl-prepare} is + no more necessary. +\end{itemize} + +\section*{E-ACSL 19.0 Potassium} + \begin{itemize} \item \textbf{Runtime Monitor Behavior}: Document global variable \texttt{\_\_e\_acsl\_sound\_verdict} usable in \texttt{\_\_e\_acsl\_assert}. diff --git a/src/plugins/e-acsl/doc/userman/limitations.tex b/src/plugins/e-acsl/doc/userman/limitations.tex index ec0d86ba6c26d28e71a791c5268787dcd87b2908..c42ab93808d1fb2fbfb644ce3e4293bbeb443485 100644 --- a/src/plugins/e-acsl/doc/userman/limitations.tex +++ b/src/plugins/e-acsl/doc/userman/limitations.tex @@ -152,9 +152,6 @@ $f$ such that: \end{itemize} A violation of such an annotation $a$ is undetected. There is no workaround yet. -Also, the option \optionuse{-}{e-acsl-check} does not verify the annotations of -undefined functions. There is also no workaround yet. - \subsection{Incomplete Types} \index{Type!Incomplete} diff --git a/src/plugins/e-acsl/doc/userman/main.tex b/src/plugins/e-acsl/doc/userman/main.tex index 172397d5d4013c43680dd271eb5b4d7420805874..f2f73c8229cab00bfd68472c81be15710c4f45ae 100644 --- a/src/plugins/e-acsl/doc/userman/main.tex +++ b/src/plugins/e-acsl/doc/userman/main.tex @@ -50,8 +50,9 @@ evolve in the future. \section*{Acknowledgements} We gratefully thank the people who contributed to this document: -Pierre-Lo\"ic Garoche, Jens Gerlach, Florent Kirchner, Nikola\"i Kosmatov, -Andr\'e Oliveira Maroneze, Fonenantsoa Maurica, and Guillaume Petiot. +Basile Desloges, Pierre-Lo\"ic Garoche, Jens Gerlach, Florent Kirchner, +Nikola\"i Kosmatov, Andr\'e Oliveira Maroneze, Fonenantsoa Maurica, and +Guillaume Petiot. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/plugins/e-acsl/doc/userman/provides.tex b/src/plugins/e-acsl/doc/userman/provides.tex index 18df5fe9d39c61acac7b65e4a931299f9e7f7c71..accd222ac1c299c54589bc2c69647b8855ab3ec6 100644 --- a/src/plugins/e-acsl/doc/userman/provides.tex +++ b/src/plugins/e-acsl/doc/userman/provides.tex @@ -416,7 +416,7 @@ script. mathematical integers which does not fit in any integral \C type. To circumvent this issue, \eacsl uses the \gmp library\footnote{\url{http://gmplib.org}}. Thus, even if \eacsl does its best to use standard integral types instead of -\gmp~\cite{sac13,jfla15}, it may generate such integers from time to time. It is +\gmp~\cite{kosmatov20rv}, it may generate such integers from time to time. It is notably the case if your program contains \lstinline|long long|, either signed or unsigned. @@ -544,8 +544,8 @@ model~\cite{pldi16}. \subsubsection{Maximized Memory Tracking} By default \eacsl uses static analysis to reduce instrumentation and therefore -runtime and memory overheads~\cite{scp16}. With respect to memory tracking this -means that +runtime and memory overheads~\cite{ly18hilt}. With respect to memory tracking +this means that \eacsl may omit recording memory blocks irrelevant for runtime analysis. It is, however, still possible to systematically instrument the code for handling potential memory-related annotations even when it is not required. This @@ -980,22 +980,6 @@ The present implementation of \eacslgcc does not fully support combining \eacsl with other \framac plug-ins. However it is still possible to instrument programs with \eacsl directly and use \eacslgcc to compile the generated code. -If you run the \eacsl plug-in after another one, it first generates -a new temporary project in which it links the analyzed program against its own -library in order to generate the \framac internal representation of the \C -program (\emph{aka} AST), as explained in Section~\ref{sec:run}. Consequently, -even if the \eacsl plug-in keeps the maximum amount of information, the results -of already executed analyzers (such as validity status of annotations) are not -known in this new project. - -\begin{important} -If you want to keep results of former analysis, you have to set the option -\shortopt{e-acsl-prepare} when the first analysis is asked for. The standard -example is running \eacsl after \Eva\index{Eva}: in such a case, -\shortopt{e-acsl-prepare} must be provided together with the \Eva's -\shortopt{val} option. -\end{important} - In this context, the \eacsl plug-in does not generate code for annotations proven valid by another plug-in, except if you explicitly set the option \shortopt{e-acsl-valid}. For instance, \Eva~\cite{eva} is able to @@ -1003,21 +987,14 @@ prove that there is no potential overflow in the previous program, so the \eacsl plug-in does not generate additional code for checking them if you run the following command. \begin{shell} -\$ frama-c -e-acsl-prepare -rte combine.i -then -val -then -e-acsl \ +\$ frama-c -rte combine.i -then -val -then -e-acsl \ -then-last -print -ocode monitored_combine.i \end{shell} -The additional code will be generated with one of the two following commands. +The additional code will be generated with the two following command. \begin{shell} -\$ frama-c -e-acsl-prepare -rte combine.i -then -val -then -e-acsl \ - -e-acsl-valid -then-last -print -ocode monitored_combine.i \$ frama-c -rte combine.i -then -val -then -e-acsl \ - -then-last -print -ocode monitored_combine.i + -e-acsl-valid -then-last -print -ocode monitored_combine.i \end{shell} -In the first case, that is because it is explicitly required by the option -\shortopt{e-acsl-valid} while, in the second case, that is because the option -\shortopt{e-acsl-prepare} is not provided on the command line which results in -the fact that the result of the value analysis are unknown when the \eacsl -plug-in is running. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1044,20 +1021,6 @@ check.i:4:[e-acsl] warning: E-ACSL construct `right shift' is not yet supported. [e-acsl] translation done in project "foobar". \end{shell} -Further, the \eacsl plug-in option \shortopt{e-acsl-check} does not generate a -new project but verifies that each annotation is translatable. Then it produces -a summary as shown in the following example (left or right shifts in annotations -are not yet supported by the \eacsl plug-in~\cite{eacsl-implem}). - -\begin{shell} -\$ frama-c -e-acsl-check check.i -<skip preprocessing commands> -check.i:4:[e-acsl] warning: E-ACSL construct `left/right shift' is not yet supported. - Ignoring annotation. -[e-acsl] 0 annotation was ignored, being untypable. -[e-acsl] 1 annotation was ignored, being unsupported. -\end{shell} - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%>>> diff --git a/src/plugins/e-acsl/headers/header_spec.txt b/src/plugins/e-acsl/headers/header_spec.txt index ece261d80bc5f5feb371e6a88d13f8b1b3235fc4..41708b793a5ad7c2b97a1cf4fb703068e7457a1f 100644 --- a/src/plugins/e-acsl/headers/header_spec.txt +++ b/src/plugins/e-acsl/headers/header_spec.txt @@ -100,8 +100,8 @@ src/local_config.mli: CEA_LGPL_OR_PROPRIETARY.E_ACSL src/main.ml: CEA_LGPL_OR_PROPRIETARY.E_ACSL src/options.ml: CEA_LGPL_OR_PROPRIETARY.E_ACSL src/options.mli: CEA_LGPL_OR_PROPRIETARY.E_ACSL -src/project_initializer/keep_status.ml: CEA_LGPL_OR_PROPRIETARY.E_ACSL -src/project_initializer/keep_status.mli: CEA_LGPL_OR_PROPRIETARY.E_ACSL +src/project_initializer/rtl.ml: CEA_LGPL_OR_PROPRIETARY.E_ACSL +src/project_initializer/rtl.mli: CEA_LGPL_OR_PROPRIETARY.E_ACSL src/project_initializer/prepare_ast.ml: CEA_LGPL_OR_PROPRIETARY.E_ACSL src/project_initializer/prepare_ast.mli: CEA_LGPL_OR_PROPRIETARY.E_ACSL tests/print.ml: CEA_LGPL_OR_PROPRIETARY.E_ACSL diff --git a/src/plugins/e-acsl/man/e-acsl-gcc.sh.1 b/src/plugins/e-acsl/man/e-acsl-gcc.sh.1 index d4eb7bdfe5078af7e5664c03d0d13925f7e028b7..e4c6801d21f29c194931cca19852edad9f241f9f 100644 --- a/src/plugins/e-acsl/man/e-acsl-gcc.sh.1 +++ b/src/plugins/e-acsl/man/e-acsl-gcc.sh.1 @@ -206,8 +206,7 @@ executable found in the system path is used. .TP .B --then separate with a \fB-then\fP the first \fBFrama-C\fP options from the actual -launch of the \fBE-ACSL\fP plugin. Prepends \fB-e-acsl-prepare\fP to the list -of options passed to \fBFrama-C\fP. +launch of the \fBE-ACSL\fP plugin. .TP .B --e-acsl-extra=\fI<OPTS> add \fI<OPTS>\fP to the list of options that will be given to the \fBE-ACSL\fP diff --git a/src/plugins/e-acsl/scripts/e-acsl-gcc.sh b/src/plugins/e-acsl/scripts/e-acsl-gcc.sh index 3038810fbf9bd7f54a963b46de85a3d8c94c5129..c81e38218fb5c94dd3c75e247668f17b12f8e554 100755 --- a/src/plugins/e-acsl/scripts/e-acsl-gcc.sh +++ b/src/plugins/e-acsl/scripts/e-acsl-gcc.sh @@ -557,7 +557,6 @@ do --then) shift; OPTION_THEN=-then - FRAMAC_FLAGS="-e-acsl-prepare $FRAMAC_FLAGS" ;; # Extra E-ACSL options --e-acsl-extra) @@ -754,7 +753,9 @@ CPPFLAGS="$OPTION_CPPFLAGS" LDFLAGS="$OPTION_LDFLAGS" # Extra Frama-C Flags E-ACSL needs -FRAMAC_FLAGS="$FRAMAC_FLAGS -variadic-no-translation" +FRAMAC_FLAGS="$FRAMAC_FLAGS \ + -remove-unused-specified-functions \ + -variadic-no-translation" # C, CPP and LD flags for compilation of E-ACSL-generated sources EACSL_CFLAGS="$OPTION_EXTERNAL_ASSERT" diff --git a/src/plugins/e-acsl/src/analyses/lscope.ml b/src/plugins/e-acsl/src/analyses/lscope.ml index ccf0726b033dfa66f046a0593117e3f4de5f6525..56a9e7ff0fd4d13e4598f15b369e0333686ea6bd 100644 --- a/src/plugins/e-acsl/src/analyses/lscope.ml +++ b/src/plugins/e-acsl/src/analyses/lscope.ml @@ -47,6 +47,8 @@ let exists lv t = in List.exists is_lv t +type pred_or_term = PoT_pred of predicate | PoT_term of term + exception Lscope_used let is_used lscope pot = let o = object inherit Visitor.frama_c_inplace @@ -57,8 +59,14 @@ let is_used lscope pot = in try (match pot with - | Misc.PoT_pred p -> ignore (Visitor.visitFramacPredicate o p) - | Misc.PoT_term t -> ignore (Visitor.visitFramacTerm o t)); + | PoT_pred p -> ignore (Visitor.visitFramacPredicate o p) + | PoT_term t -> ignore (Visitor.visitFramacTerm o t)); false with Lscope_used -> true + +(* +Local Variables: +compile-command: "make -C ../../../../.." +End: +*) diff --git a/src/plugins/e-acsl/src/analyses/lscope.mli b/src/plugins/e-acsl/src/analyses/lscope.mli index f84968eeaa3926b0ca8cefff034d58659b81fc1b..56728c2bdd002181bb5c4a62743ab6cfaac7afd9 100644 --- a/src/plugins/e-acsl/src/analyses/lscope.mli +++ b/src/plugins/e-acsl/src/analyses/lscope.mli @@ -48,6 +48,13 @@ val get_all: t -> lscope_var list The first element is the first [lscope_var] that was added to [t], the second element is the second [lscope_var] that was added to [t], an so on. *) -val is_used: t -> Misc.pred_or_term -> bool +type pred_or_term = PoT_pred of predicate | PoT_term of term +val is_used: t -> pred_or_term -> bool (* [is_used lscope pot] returns [true] iff [pot] uses a variable from [lscope]. *) + +(* +Local Variables: +compile-command: "make -C ../../../../.." +End: +*) diff --git a/src/plugins/e-acsl/src/analyses/mmodel_analysis.ml b/src/plugins/e-acsl/src/analyses/mmodel_analysis.ml index 3b655683995066f05739e1669e5e359c33ed4ddd..c480e318b764410dc8acfd272b8c28d27d3f75f6 100644 --- a/src/plugins/e-acsl/src/analyses/mmodel_analysis.ml +++ b/src/plugins/e-acsl/src/analyses/mmodel_analysis.ml @@ -27,7 +27,7 @@ module Dataflow = Dataflow2 let must_never_monitor vi = (* E-ACSL, please do not monitor yourself! *) - Functions.RTL.is_rtl_name vi.vname + Rtl.Symbols.mem_vi vi.vname || (* extern ghost variables are usually used (by the Frama-C libc) to represent some internal invisible states in ACSL specifications. They do @@ -620,7 +620,7 @@ end = struct let compute init_set kf = Options.feedback ~dkey ~level:2 "entering in function %a." Kernel_function.pretty kf; - assert (not (Misc.is_library_loc (Kernel_function.get_location kf))); + assert (not (Rtl.Symbols.mem_kf kf)); let tbl, is_init = try Env.find kf, true with Not_found -> Stmt.Hashtbl.create 17, false @@ -660,7 +660,7 @@ end = struct tbl let get ?init kf = - if Misc.is_library_loc (Kernel_function.get_location kf) then + if Rtl.Symbols.mem_kf kf then Varinfo.Hptset.empty else try @@ -799,6 +799,6 @@ let use_model () = (* Local Variables: -compile-command: "make -C ../.." +compile-command: "make -C ../../../../.." End: *) diff --git a/src/plugins/e-acsl/src/analyses/mmodel_analysis.mli b/src/plugins/e-acsl/src/analyses/mmodel_analysis.mli index 39090f0dfa07a4100864ff0c20883bd9d79e7f3a..8c4967c2797f9c936b3c230b40a9cb0d476f6a37 100644 --- a/src/plugins/e-acsl/src/analyses/mmodel_analysis.mli +++ b/src/plugins/e-acsl/src/analyses/mmodel_analysis.mli @@ -44,6 +44,6 @@ val must_model_exp: ?kf:kernel_function -> ?stmt:stmt -> exp -> bool (* Local Variables: - compile-command: "make" + compile-command: "make -C ../../../../.." End: *) diff --git a/src/plugins/e-acsl/src/analyses/rte.ml b/src/plugins/e-acsl/src/analyses/rte.ml index d02641f2bc68d2b157c557ea4ab5272b7fe4768d..96a1afbb988346ccc261a2181d097d949b370c8b 100644 --- a/src/plugins/e-acsl/src/analyses/rte.ml +++ b/src/plugins/e-acsl/src/analyses/rte.ml @@ -24,17 +24,6 @@ (** {2 Generic code} *) (* ************************************************************************** *) -let apply_rte f x = - let signed = Kernel.SignedOverflow.get () in - let unsigned = Kernel.UnsignedOverflow.get () in - Kernel.SignedOverflow.off (); - Kernel.UnsignedOverflow.off (); - let finally () = - Kernel.SignedOverflow.set signed; - Kernel.UnsignedOverflow.set unsigned - in - Extlib.try_finally ~finally f x - let warn_rte warn exn = if warn then Options.warning "@[@[cannot run RTE:@ %s.@]@ \ @@ -48,30 +37,24 @@ Ignoring potential runtime errors in annotations." open Cil_datatype let stmt ?(warn=true) = - try - let f = - Dynamic.get - ~plugin:"RteGen" - "stmt_annotations" - (Datatype.func2 Kernel_function.ty Stmt.ty - (let module L = Datatype.List(Code_annotation) in L.ty)) - in - (fun x y -> apply_rte (f x) y) + try + Dynamic.get + ~plugin:"RteGen" + "stmt_annotations" + (Datatype.func2 Kernel_function.ty Stmt.ty + (let module L = Datatype.List(Code_annotation) in L.ty)) with Failure _ | Dynamic.Unbound_value _ | Dynamic.Incompatible_type _ as exn -> warn_rte warn exn; fun _ _ -> [] let exp ?(warn=true) = - try - let f = - Dynamic.get - ~plugin:"RteGen" - "exp_annotations" - (Datatype.func3 Kernel_function.ty Stmt.ty Exp.ty - (let module L = Datatype.List(Code_annotation) in L.ty)) - in - (fun x y z -> apply_rte (f x y) z) + try + Dynamic.get + ~plugin:"RteGen" + "exp_annotations" + (Datatype.func3 Kernel_function.ty Stmt.ty Exp.ty + (let module L = Datatype.List(Code_annotation) in L.ty)) with Failure _ | Dynamic.Unbound_value _ | Dynamic.Incompatible_type _ as exn -> warn_rte warn exn; @@ -79,6 +62,6 @@ let exp ?(warn=true) = (* Local Variables: -compile-command: "make" +compile-command: "make -C ../../../../.." End: *) diff --git a/src/plugins/e-acsl/src/analyses/rte.mli b/src/plugins/e-acsl/src/analyses/rte.mli index 96d971650079a343cd33cd699d52260720907fe7..6f177451eddc0de564679ef1dbb4e7c8f825e862 100644 --- a/src/plugins/e-acsl/src/analyses/rte.mli +++ b/src/plugins/e-acsl/src/analyses/rte.mli @@ -32,6 +32,6 @@ val exp: ?warn:bool -> kernel_function -> stmt -> exp -> code_annotation list (* Local Variables: -compile-command: "make" +compile-command: "make -C ../../../../.." End: *) diff --git a/src/plugins/e-acsl/src/analyses/typing.ml b/src/plugins/e-acsl/src/analyses/typing.ml index a2612ee906f2595622dd37a63cc835b93b44fd9f..04731a08f3ce28a805afc6e0a7f4c52d064976fd 100644 --- a/src/plugins/e-acsl/src/analyses/typing.ml +++ b/src/plugins/e-acsl/src/analyses/typing.ml @@ -512,7 +512,7 @@ let rec type_term ~use_gmp_opt ?(arith_operand=false) ?ctx t = | LBreads _ -> Error.not_yet "logic functions performing read accesses" | LBinductive _ -> - Error.not_yet "logic functions inductively defined" + Error.not_yet "inductive logic functions" end | Tunion _ -> Error.not_yet "tset union" @@ -585,8 +585,13 @@ let rec type_predicate p = c_int | LBnone -> (* Eg: \is_finite *) Error.not_yet "predicate with no definition nor reads clause" - | LBreads _ | LBterm _ | LBinductive _ -> + | LBreads _ -> + Error.not_yet "predicate performing read accesses" + | LBinductive _ -> + Error.not_yet "inductive predicate" + | LBterm _ -> Options.fatal "unexpected logic definition" + Printer.pp_predicate p end | Pseparated _ -> Error.not_yet "\\separated" | Pdangling _ -> Error.not_yet "\\dangling" diff --git a/src/plugins/e-acsl/src/code_generator/at_with_lscope.ml b/src/plugins/e-acsl/src/code_generator/at_with_lscope.ml index 59a6acb5cf95cc7762a67ebbb5af99c854579eed..040c83991edcca505c5758fdf0ad4dea40420c12 100644 --- a/src/plugins/e-acsl/src/code_generator/at_with_lscope.ml +++ b/src/plugins/e-acsl/src/code_generator/at_with_lscope.ml @@ -225,9 +225,9 @@ let to_exp ~loc kf env pot label = in (* Creating the pointer *) let ty = match pot with - | Misc.PoT_pred _ -> + | Lscope.PoT_pred _ -> Cil.intType - | Misc.PoT_term t -> + | Lscope.PoT_term t -> begin match Typing.get_number_ty t with | Typing.(C_integer _ | C_float _ | Nan) -> Typing.get_typ t @@ -289,7 +289,7 @@ let to_exp ~loc kf env pot label = let term_to_exp = !term_to_exp_ref in let named_predicate_to_exp = !predicate_to_exp_ref in match pot with - | Misc.PoT_pred p -> + | Lscope.PoT_pred p -> let env = Env.push env in let lval, env = lval_at_index ~loc kf env (e_at, vi_at, t_index) in let e, env = named_predicate_to_exp kf env p in @@ -303,7 +303,7 @@ let to_exp ~loc kf env pot label = (* We CANNOT return [block.bstmts] because it does NOT contain variable declarations. *) [ Constructor.mk_block_stmt block ], env - | Misc.PoT_term t -> + | Lscope.PoT_term t -> begin match Typing.get_number_ty t with | Typing.(C_integer _ | C_float _ | Nan) -> let env = Env.push env in @@ -347,6 +347,6 @@ let to_exp ~loc kf env pot label = (* Local Variables: -compile-command: "make -C ../.." +compile-command: "make -C ../../../../.." End: *) diff --git a/src/plugins/e-acsl/src/code_generator/at_with_lscope.mli b/src/plugins/e-acsl/src/code_generator/at_with_lscope.mli index 3b6dda222774a0cde05bdce183bc273ef75c4742..f5b51d739bd4de58a55583a2e68cd2f95696b87e 100644 --- a/src/plugins/e-acsl/src/code_generator/at_with_lscope.mli +++ b/src/plugins/e-acsl/src/code_generator/at_with_lscope.mli @@ -32,7 +32,7 @@ open Cil_datatype val to_exp: loc:Location.t -> kernel_function -> Env.t -> - Misc.pred_or_term -> logic_label -> exp * Env.t + Lscope.pred_or_term -> logic_label -> exp * Env.t (*****************************************************************************) (**************************** Handling memory ********************************) @@ -70,6 +70,6 @@ val term_to_exp_ref: (* Local Variables: -compile-command: "make -C ../.." +compile-command: "make -C ../../../../.." End: *) diff --git a/src/plugins/e-acsl/src/code_generator/constructor.ml b/src/plugins/e-acsl/src/code_generator/constructor.ml index 4848a1f2e2191770084af933d615f5326926c81c..a38f36ce70e425255853780dbbf358bef43cc0ab 100644 --- a/src/plugins/e-acsl/src/code_generator/constructor.ml +++ b/src/plugins/e-acsl/src/code_generator/constructor.ml @@ -98,7 +98,12 @@ let mk_block stmt b = match b.bstmts with (* ********************************************************************** *) let mk_lib_call ~loc ?result fname args = - let vi = Misc.get_lib_fun_vi fname in + let vi = + try Rtl.Symbols.find_vi fname + with Rtl.Symbols.Unregistered _ as exn -> + try Builtins.find fname + with Not_found -> raise exn + in let f = Cil.evar ~loc vi in vi.vreferenced <- true; let make_args ~variadic args param_ty = diff --git a/src/plugins/e-acsl/src/code_generator/constructor.mli b/src/plugins/e-acsl/src/code_generator/constructor.mli index 731734839878fda808895f9a71e4fde5858f4d4c..d21c71e53a434f9905a89fc1ca49ab31df10a781 100644 --- a/src/plugins/e-acsl/src/code_generator/constructor.mli +++ b/src/plugins/e-acsl/src/code_generator/constructor.mli @@ -79,7 +79,7 @@ val mk_break: loc:location -> stmt val mk_lib_call: loc:Location.t -> ?result:lval -> string -> exp list -> stmt (** Construct a call to a library function with the given name. - @raise Unregistered_library_function if the given string does not represent + @raise Rtl.Symbols.Unregistered if the given string does not represent such a function or if library functions were never registered (only possible when using E-ACSL through its API). *) diff --git a/src/plugins/e-acsl/src/code_generator/injector.ml b/src/plugins/e-acsl/src/code_generator/injector.ml index a0357a168fdf9772d83de8409e5734b3062a43da..dd1222051d790f8757da083c9f9628ac3be4d877 100644 --- a/src/plugins/e-acsl/src/code_generator/injector.ml +++ b/src/plugins/e-acsl/src/code_generator/injector.ml @@ -68,9 +68,9 @@ let inject_in_local_init loc env kf vi = function when Options.Validate_format_strings.get () && Functions.Libc.is_printf_name fvi.vname -> - (* rewrite format functions (e.g., [printf]). *) - let name = Functions.RTL.get_rtl_replacement_name fvi.vname in - let new_vi = Misc.get_lib_fun_vi name in + (* rewrite libc function names (e.g., [printf]). *) + let name = Functions.RTL.libc_replacement_name fvi.vname in + let new_vi = try Builtins.find name with Not_found -> assert false in let fmt = Functions.Libc.get_printf_argument_str ~loc fvi.vname args in ConsInit(new_vi, fmt :: args, kind), env @@ -80,7 +80,7 @@ let inject_in_local_init loc env kf vi = function -> (* rewrite names of functions for which we have alternative definitions in the RTL. *) - fvi.vname <- Functions.RTL.get_rtl_replacement_name fvi.vname; + fvi.vname <- Functions.RTL.libc_replacement_name fvi.vname; init, env | AssignInit init -> @@ -109,7 +109,7 @@ let rename_caller loc args exp = match exp.enode with when Options.Replace_libc_functions.get () && Functions.RTL.has_rtl_replacement vi.vname -> - vi.vname <- Functions.RTL.get_rtl_replacement_name vi.vname; + vi.vname <- Functions.RTL.libc_replacement_name vi.vname; exp, args | Lval(Var vi, _) @@ -120,12 +120,12 @@ let rename_caller loc args exp = match exp.enode with from the above because argument list of format functions is extended with an argument describing actual variadic arguments *) (* replacement name, e.g., [printf] -> [__e_acsl_builtin_printf] *) - let name = Functions.RTL.get_rtl_replacement_name vi.vname in + let name = Functions.RTL.libc_replacement_name vi.vname in (* variadic arguments descriptor *) let fmt = Functions.Libc.get_printf_argument_str ~loc vi.vname args in - (* get the name of the library function we need. Cannot just rewrite the - name as AST check will then fail *) - let vi = Misc.get_lib_fun_vi name in + (* get the library function we need. Cannot just rewrite the name as AST + check will then fail *) + let vi = try Rtl.Symbols.find_vi name with Not_found -> assert false in Cil.evar vi, fmt :: args | _ -> @@ -232,7 +232,7 @@ let add_new_block_in_stmt env kf stmt = in let mk_post_env env stmt = Annotations.fold_code_annot - (fun _ a env -> Translate.translate_post_code_annotation kf env a) + (fun _ a env -> Translate.translate_post_code_annotation kf stmt env a) stmt env in @@ -252,7 +252,7 @@ let add_new_block_in_stmt env kf stmt = let env = mk_post_env env stmt in (* also handle the postcondition of the function and clear the env *) - Translate.translate_post_spec kf env (Annotations.funspec kf) + Translate.translate_post_spec kf Kglobal env (Annotations.funspec kf) else env in @@ -450,7 +450,7 @@ and inject_in_stmt env kf stmt = (* translate the precondition of the function *) if Functions.check kf then let funspec = Annotations.funspec kf in - Translate.translate_pre_spec kf env funspec + Translate.translate_pre_spec kf Kglobal env funspec else env else env @@ -459,7 +459,7 @@ and inject_in_stmt env kf stmt = let env = if Functions.check kf then Annotations.fold_code_annot - (fun _ a env -> Translate.translate_pre_code_annotation kf env a) + (fun _ a env -> Translate.translate_pre_code_annotation kf stmt env a) stmt env else @@ -589,6 +589,7 @@ let inject_in_fundec main fundec = in List.iter unghost_formal fundec.sformals; (* update environments *) + (* TODO: do it only for built-ins *) Builtins.update vi.vname vi; (* track function addresses but the main function that is tracked internally via RTL *) @@ -628,17 +629,15 @@ let unghost_vi vi = let inject_in_global (env, main) = function (* library functions and built-ins *) | GVarDecl(vi, _) | GVar(vi, _, _) - | GFunDecl(_, vi, _) | GFun({ svar = vi }, _) - when Misc.is_library_loc vi.vdecl || Builtins.mem vi.vname -> - Misc.register_library_function vi; - if Builtins.mem vi.vname then Builtins.update vi.vname vi; + | GFunDecl(_, vi, _) | GFun({ svar = vi }, _) when Builtins.mem vi.vname -> + Builtins.update vi.vname vi; env, main (* Cil built-ins and other library globals: nothing to do *) | GVarDecl(vi, _) | GVar(vi, _, _) | GFun({ svar = vi }, _) when Misc.is_fc_or_compiler_builtin vi -> env, main - | g when Misc.is_library_loc (Global.loc g) -> + | g when Rtl.Symbols.mem_global g -> env, main (* variable declarations *) @@ -845,7 +844,6 @@ let reset_all ast = Options.Run.off (); (* reset all the E-ACSL environments to their original states *) Mmodel_analysis.reset (); - Misc.reset (); Logic_functions.reset (); Literal_strings.reset (); Global_observer.reset (); @@ -862,8 +860,6 @@ let inject () = Options.feedback ~level:2 "injecting annotations as code in project %a" Project.pretty (Project.current ()); - Keep_status.before_translation (); - Misc.reorder_ast (); Gmp_types.init (); let ast = Ast.get () in inject_in_file ast; diff --git a/src/plugins/e-acsl/src/code_generator/translate.ml b/src/plugins/e-acsl/src/code_generator/translate.ml index b9dfc2390c42fb5519843333f0fb83f8b0bc2c0d..009763c7931d6f8d47f548698052917a7551ed21 100644 --- a/src/plugins/e-acsl/src/code_generator/translate.ml +++ b/src/plugins/e-acsl/src/code_generator/translate.ml @@ -686,7 +686,7 @@ and context_insensitive_term_to_exp kf env t = e, env, C_number, "" | Tat(t', label) -> let lscope = Env.Logic_scope.get env in - let pot = Misc.PoT_term t' in + let pot = Lscope.PoT_term t' in if Lscope.is_used lscope pot then let e, env = At_with_lscope.to_exp ~loc kf env pot label in e, env, C_number, "" @@ -947,7 +947,7 @@ and named_predicate_content_to_exp ?name kf env p = named_predicate_to_exp kf env p | Pat(p', label) -> let lscope = Env.Logic_scope.get env in - let pot = Misc.PoT_pred p' in + let pot = Lscope.PoT_pred p' in if Lscope.is_used lscope pot then At_with_lscope.to_exp ~loc kf env pot label else begin @@ -1128,19 +1128,35 @@ let term_to_exp typ t = let env = Env.rte env false in let e, env = try term_to_exp (Kernel_function.dummy ()) env t - with Misc.Unregistered_library_function _ -> raise (No_simple_translation t) + with Rtl.Symbols.Unregistered _ -> raise (No_simple_translation t) in if not (Env.has_no_new_stmt env) then raise (No_simple_translation t); e (* ************************************************************************** *) (* [translate_*] translates a given ACSL annotation into the corresponding C - statement (if any) for runtime assertion checking. - - IMPORTANT: the order of translation of pre-/post-spec must be consistent with - the pushes done in [Keep_status] *) + statement (if any) for runtime assertion checking. *) (* ************************************************************************** *) +let must_translate ppt = + Options.Valid.get () + || match Property_status.get ppt with + | Never_tried + | Inconsistent _ + | Best ((False_if_reachable | False_and_reachable | Dont_know), _) -> + true + | Best (True, _) -> + (* [TODO] generating code for "valid under hypotheses" properties could be + useful for some use cases (in particular, when E-ACSL does not stop on + the very first error). + ==> introduce a new option or modify the behavior of -e-acsl-valid, + see e-acsl#35 *) + false + +let must_translate_opt = function + | None -> false + | Some ppt -> must_translate ppt + let assumes_predicate bhv = List.fold_left (fun acc p -> @@ -1151,14 +1167,14 @@ let assumes_predicate bhv = Logic_const.ptrue bhv.b_assumes -let translate_preconditions kf env behaviors = +let translate_preconditions kf kinstr env behaviors = let env = Env.set_annotation_kind env Constructor.Precondition in let do_behavior env b = let assumes_pred = assumes_predicate b in List.fold_left (fun env p -> let do_it env = - if Keep_status.must_translate kf Keep_status.K_Requires then + if must_translate (Property.ip_of_requires kf kinstr b p) then let loc = p.ip_content.pred_loc in let p = Logic_const.pimplies @@ -1176,16 +1192,16 @@ let translate_preconditions kf env behaviors = in List.fold_left do_behavior env behaviors -let translate_postconditions kf env behaviors = +let translate_postconditions kf kinstr env behaviors = let env = Env.set_annotation_kind env Constructor.Postcondition in (* generate one guard by postcondition of each behavior *) let do_behavior env b = let env = handle_error (fun env -> - (* test ordering does matter for keeping statuses consistent *) - if b.b_assigns <> WritesAny - && Keep_status.must_translate kf Keep_status.K_Assigns + let active = [] in (* TODO: 'for' behaviors, e-acsl#109 *) + let ppt = Property.ip_assigns_of_behavior kf kinstr ~active b in + if b.b_assigns <> WritesAny && must_translate_opt ppt then not_yet env "assigns clause in behavior"; (* ignore b.b_extended since we never translate them *) env) @@ -1193,8 +1209,8 @@ let translate_postconditions kf env behaviors = in let assumes_pred = assumes_predicate b in List.fold_left - (fun env (t, p) -> - if Keep_status.must_translate kf Keep_status.K_Ensures then + (fun env ((t, p) as tp) -> + if must_translate (Property.ip_of_ensures kf kinstr b tp) then let do_it env = match t with | Normal -> @@ -1221,56 +1237,53 @@ let translate_postconditions kf env behaviors = in List.fold_left do_behavior env bhvs -let translate_pre_spec kf env spec = +let translate_pre_spec kf kinstr env spec = let unsupported f x = ignore (handle_error (fun env -> f x; env) env) in let convert_unsupported_clauses env = unsupported - (Extlib.may - (fun _ -> - if Keep_status.must_translate kf Keep_status.K_Decreases then - not_yet env "variant clause")) - spec.spec_variant; + (fun spec -> + let ppt = Property.ip_decreases_of_spec kf kinstr spec in + if must_translate_opt ppt then not_yet env "variant clause") + spec; (* TODO: spec.spec_terminates is not part of the E-ACSL subset *) unsupported - (Extlib.may - (fun _ -> - if Keep_status.must_translate kf Keep_status.K_Terminates then - not_yet env "terminates clause")) - spec.spec_terminates; - (match spec.spec_complete_behaviors with - | [] -> () - | l -> - unsupported - (List.iter - (fun _ -> - if Keep_status.must_translate kf Keep_status.K_Complete then - not_yet env "complete behaviors")) - l); - (match spec.spec_disjoint_behaviors with - | [] -> () - | l -> - unsupported - (List.iter - (fun _ -> - if Keep_status.must_translate kf Keep_status.K_Disjoint then - not_yet env "disjoint behaviors")) - l); + (fun spec -> + let ppt = Property.ip_terminates_of_spec kf kinstr spec in + if must_translate_opt ppt then not_yet env "terminates clause") + spec; + let active = [] in (* TODO: 'for' behaviors, e-acsl#109 *) + let ppts = Property.ip_complete_of_spec kf kinstr ~active spec in + unsupported + (fun ppts -> + List.iter + (fun ppt -> + if must_translate ppt then not_yet env "complete behaviors") + ppts) + ppts; + let ppts = Property.ip_disjoint_of_spec kf kinstr ~active spec in + unsupported + (fun ppts -> + List.iter + (fun ppt -> + if must_translate ppt then not_yet env "disjoint behaviors") + ppts) + ppts; env in let env = convert_unsupported_clauses env in handle_error - (fun env -> translate_preconditions kf env spec.spec_behavior) + (fun env -> translate_preconditions kf kinstr env spec.spec_behavior) env -let translate_post_spec kf env spec = +let translate_post_spec kf kinstr env spec = handle_error - (fun env -> translate_postconditions kf env spec.spec_behavior) + (fun env -> translate_postconditions kf kinstr env spec.spec_behavior) env -let translate_pre_code_annotation kf env annot = +let translate_pre_code_annotation kf stmt env annot = let convert env = match annot.annot_content with | AAssert(l, _, p) -> - if Keep_status.must_translate kf Keep_status.K_Assert then + if must_translate (Property.ip_of_code_annot_single kf stmt annot) then let env = Env.set_annotation_kind env Constructor.Assertion in if l <> [] then not_yet env "@[assertion applied only on some behaviors@]"; @@ -1280,9 +1293,9 @@ let translate_pre_code_annotation kf env annot = | AStmtSpec(l, spec) -> if l <> [] then not_yet env "@[statement contract applied only on some behaviors@]"; - translate_pre_spec kf env spec ; + translate_pre_spec kf (Kstmt stmt) env spec ; | AInvariant(l, loop_invariant, p) -> - if Keep_status.must_translate kf Keep_status.K_Invariant then + if must_translate (Property.ip_of_code_annot_single kf stmt annot) then let env = Env.set_annotation_kind env Constructor.Invariant in if l <> [] then not_yet env "@[invariant applied only on some behaviors@]"; @@ -1291,26 +1304,31 @@ let translate_pre_code_annotation kf env annot = else env | AVariant _ -> - if Keep_status.must_translate kf Keep_status.K_Variant + if must_translate (Property.ip_of_code_annot_single kf stmt annot) then not_yet env "variant" else env | AAssigns _ -> - (* TODO: it is not a precondition *) - if Keep_status.must_translate kf Keep_status.K_Assigns - then not_yet env "assigns" - else env + (* TODO: it is not a precondition --> should not be handled here, + to be fixed when implementing e-acsl#29 *) + let ppts = Property.ip_of_code_annot kf stmt annot in + List.iter + (fun ppt -> if must_translate ppt then not_yet env "assigns") + ppts; + env | AAllocation _ -> - if Keep_status.must_translate kf Keep_status.K_Allocation - then not_yet env "allocation" - else env + let ppts = Property.ip_of_code_annot kf stmt annot in + List.iter + (fun ppt -> if must_translate ppt then not_yet env "allocation") + ppts; + env | APragma _ -> not_yet env "pragma" | AExtended _ -> env (* never translate extensions. *) in handle_error convert env -let translate_post_code_annotation kf env annot = +let translate_post_code_annotation kf stmt env annot = let convert env = match annot.annot_content with - | AStmtSpec(_, spec) -> translate_post_spec kf env spec + | AStmtSpec(_, spec) -> translate_post_spec kf (Kstmt stmt) env spec | AAssert _ | AInvariant _ | AVariant _ diff --git a/src/plugins/e-acsl/src/code_generator/translate.mli b/src/plugins/e-acsl/src/code_generator/translate.mli index b38599d54403b537afbb1dec5277430e3b4d0a6c..64a83c76113bbe3e29142f4d4d6981e74ae81d00 100644 --- a/src/plugins/e-acsl/src/code_generator/translate.mli +++ b/src/plugins/e-acsl/src/code_generator/translate.mli @@ -26,12 +26,12 @@ open Cil_types statement (if any) for runtime assertion checking. This C statements are part of the resulting environment. *) -val translate_pre_spec: kernel_function -> Env.t -> funspec -> Env.t -val translate_post_spec: kernel_function -> Env.t -> funspec -> Env.t +val translate_pre_spec: kernel_function -> kinstr -> Env.t -> funspec -> Env.t +val translate_post_spec: kernel_function -> kinstr -> Env.t -> funspec -> Env.t val translate_pre_code_annotation: - kernel_function -> Env.t -> code_annotation -> Env.t + kernel_function -> stmt -> Env.t -> code_annotation -> Env.t val translate_post_code_annotation: - kernel_function -> Env.t -> code_annotation -> Env.t + kernel_function -> stmt -> Env.t -> code_annotation -> Env.t val translate_named_predicate: kernel_function -> Env.t -> predicate -> Env.t @@ -50,6 +50,6 @@ val predicate_to_exp: kernel_function -> predicate -> exp (* Local Variables: -compile-command: "make -C ../.." +compile-command: "make -C ../../../../.." End: *) diff --git a/src/plugins/e-acsl/src/libraries/builtins.ml b/src/plugins/e-acsl/src/libraries/builtins.ml index 9bfe9fe7d7bf79638487c00e265ee2a35b533a79..fa5d9adf70ae5c12f251f9ba96bbcea4bc56558c 100644 --- a/src/plugins/e-acsl/src/libraries/builtins.ml +++ b/src/plugins/e-acsl/src/libraries/builtins.ml @@ -83,6 +83,6 @@ let () = Cmdline.run_after_configuring_stage init (* Local Variables: -compile-command: "make" +compile-command: "make -C ../../../../.." End: *) diff --git a/src/plugins/e-acsl/src/libraries/builtins.mli b/src/plugins/e-acsl/src/libraries/builtins.mli index 95dce5b8aab5bc88615f90a2274bccc9b79d521c..d15f53f6ef5284904b9f4c709a95cb78e7c6d5fc 100644 --- a/src/plugins/e-acsl/src/libraries/builtins.mli +++ b/src/plugins/e-acsl/src/libraries/builtins.mli @@ -35,6 +35,6 @@ val update: string -> Cil_types.varinfo -> unit (* Local Variables: -compile-command: "make" +compile-command: "make -C ../../../../.." End: *) diff --git a/src/plugins/e-acsl/src/libraries/functions.ml b/src/plugins/e-acsl/src/libraries/functions.ml index b939bcff401d0ea330544988bf4e96d8c43e0c02..37cf8f8319f67d966635e0704b8ed32bd6a5f91e 100644 --- a/src/plugins/e-acsl/src/libraries/functions.ml +++ b/src/plugins/e-acsl/src/libraries/functions.ml @@ -92,12 +92,10 @@ module RTL = struct let is_generated_kf kf = is_generated_name (Kernel_function.get_name kf) - let is_rtl_name name = startswith e_acsl_api_prefix name - let is_generated_literal_string_name name = startswith e_acsl_lit_string_prefix name - let get_rtl_replacement_name fn = e_acsl_builtin_prefix ^ fn + let libc_replacement_name fn = e_acsl_builtin_prefix ^ fn let has_rtl_replacement = function | "strcpy" | "strncpy" | "strlen" | "strcat" | "strncat" | "strcmp" diff --git a/src/plugins/e-acsl/src/libraries/functions.mli b/src/plugins/e-acsl/src/libraries/functions.mli index a99291b585ff361c53aafa6450d5f06da1ea33bc..4963870c28539b1b89d055be1bb0285158b71366 100644 --- a/src/plugins/e-acsl/src/libraries/functions.mli +++ b/src/plugins/e-acsl/src/libraries/functions.mli @@ -59,10 +59,6 @@ module RTL: sig val is_generated_kf: kernel_function -> bool (** Same as [is_generated_name] but for kernel functions *) - val is_rtl_name: string -> bool - (** @return [true] if the prefix of the given name indicates that it - belongs to the public API of the E-ACSL Runtime Library *) - val is_generated_literal_string_name: string -> bool (** Same as [is_generated_name] but indicates that the name represents a local variable that replaced a literal string. *) @@ -71,7 +67,7 @@ module RTL: sig (** Retrieve the name of the kernel function and strip prefix that indicates that it has been generated by the instrumentation. *) - val get_rtl_replacement_name: string -> string + val libc_replacement_name: string -> string (** Given the name of C library function return the name of the RTL function that potentially replaces it. *) @@ -178,3 +174,9 @@ module Libc: sig In GCC/Clang [alloca] is typically implemented via [__builtin_alloca] *) end (* Libc *) + +(* +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 0f4d38ef3fb6f37bd2ab33dd37c1f0d1ff021063..0b2cd0d3ad79f9a6f08f442da392e7013fd38041 100644 --- a/src/plugins/e-acsl/src/libraries/misc.ml +++ b/src/plugins/e-acsl/src/libraries/misc.ml @@ -20,7 +20,6 @@ (* *) (**************************************************************************) -module RTL = Functions.RTL open Cil_types open Cil_datatype @@ -28,20 +27,6 @@ open Cil_datatype (** {2 Handling the E-ACSL's C-libraries, part I} *) (* ************************************************************************** *) -let library_files () = - List.map - (fun d -> Options.Share.get_file ~mode:`Must_exist d) - [ "e_acsl_gmp_api.h"; - "e_acsl.h" ] - -let is_library_loc (loc, _) = List.mem loc.Filepath.pos_path (library_files ()) - -let library_functions = Datatype.String.Hashtbl.create 17 -let register_library_function vi = - Datatype.String.Hashtbl.add library_functions vi.vname vi - -let reset () = Datatype.String.Hashtbl.clear library_functions - let is_fc_or_compiler_builtin vi = Cil.is_builtin vi || @@ -51,20 +36,6 @@ let is_fc_or_compiler_builtin vi = let prefix = String.sub vi.vname 0 prefix_length in Datatype.String.equal prefix "__builtin_") -(* ************************************************************************** *) -(** {2 Builders} *) -(* ************************************************************************** *) - -exception Unregistered_library_function of string -let get_lib_fun_vi fname = - try Datatype.String.Hashtbl.find library_functions fname - with Not_found -> - try Builtins.find fname - with Not_found -> - (* should not happen in normal mode, but could be raised when E-ACSL is - used as a library *) - raise (Unregistered_library_function fname) - (* ************************************************************************** *) (** {2 Handling \result} *) (* ************************************************************************** *) @@ -89,17 +60,6 @@ let result_vi kf = match result_lhost kf with let term_addr_of ~loc tlv ty = Logic_const.taddrof ~loc tlv (Ctype (TPtr(ty, []))) -let reorder_ast () = - let ast = Ast.get() in - let is_from_library = function - | GType(ti, _) when ti.tname = "size_t" || ti.tname = "FILE" - || RTL.is_rtl_name ti.tname -> true - | GCompTag (ci, _) when RTL.is_rtl_name ci.cname -> true - | GFunDecl(_, _, loc) | GVarDecl(_, loc) when is_library_loc loc -> true - | _ -> false in - let rtl, other = List.partition is_from_library ast.globals in - ast.globals <- rtl @ other - let cty = function | Ctype ty -> ty | lty -> Options.fatal "Expecting a C type. Got %a" Printer.pp_logic_type lty @@ -186,8 +146,6 @@ let term_has_lv_from_vi t = with Lv_from_vi_found -> true -type pred_or_term = PoT_pred of predicate | PoT_term of term - let mk_ptr_sizeof typ loc = match Cil.unrollType typ with | TPtr (t', _) -> Cil.new_exp ~loc (SizeOf t') diff --git a/src/plugins/e-acsl/src/libraries/misc.mli b/src/plugins/e-acsl/src/libraries/misc.mli index 1d2359202f752f482e32557377726c0719f23d3f..f9c92c19e8097a872a37edc83fa83301a79dd645 100644 --- a/src/plugins/e-acsl/src/libraries/misc.mli +++ b/src/plugins/e-acsl/src/libraries/misc.mli @@ -24,14 +24,6 @@ open Cil_types -(* ************************************************************************** *) -(** {2 Builders} *) -(* ************************************************************************** *) - -exception Unregistered_library_function of string -val get_lib_fun_vi: string -> varinfo -(** @return varinfo corresponding to a name of a given library function *) - (* ************************************************************************** *) (** {2 Handling \result} *) (* ************************************************************************** *) @@ -43,27 +35,13 @@ val result_vi: kernel_function -> varinfo (** @return the varinfo corresponding to \result in the given function *) (* ************************************************************************** *) -(** {2 Handling the E-ACSL's C-libraries} *) +(** {2 Other stuff} *) (* ************************************************************************** *) -val library_files: unit -> Datatype.Filepath.t list -val is_library_loc: location -> bool -val register_library_function: varinfo -> unit -val reset: unit -> unit - val is_fc_or_compiler_builtin: varinfo -> bool -(* ************************************************************************** *) -(** {2 Other stuff} *) -(* ************************************************************************** *) - val term_addr_of: loc:location -> term_lval -> typ -> term -val reorder_ast: unit -> unit -(* Reorder current AST by bringing all global declarations belonging to the - * E-ACSL runtime library and their dependencies (e.g., typedef size_t) to - * the very top of the file. *) - val cty: logic_type -> typ (** Assume that the logic type is indeed a C type. Just return it. *) @@ -90,8 +68,6 @@ val term_has_lv_from_vi: term -> bool (** @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 - val mk_ptr_sizeof: typ -> location -> exp (** [mk_ptr_sizeof ptr_typ loc] takes the pointer typ [ptr_typ] that points to a [typ] typ and returns [sizeof(typ)]. *) diff --git a/src/plugins/e-acsl/src/main.ml b/src/plugins/e-acsl/src/main.ml index 0c6c931c4754a04db314b86e69710985c304155f..703ef6b8c0d8d8d7e6683ebf4a88a48c84c572ce 100644 --- a/src/plugins/e-acsl/src/main.ml +++ b/src/plugins/e-acsl/src/main.ml @@ -20,101 +20,6 @@ (* *) (**************************************************************************) -let check () = assert false (* [TODO ARCHI] kill check *) - -let check = - Dynamic.register - ~plugin:"e-acsl" - ~journalize:true - "check" - (Datatype.func Datatype.unit Datatype.bool) - check - -type extended_project = - | To_be_extended - | Already_extended of Project.t option (* None = keep the current project *) - -let extended_ast_project: extended_project ref = ref To_be_extended - -let unmemoized_extend_ast () = - let extend () = - let share = Options.Share.get_dir ~mode:`Must_exist "." in - Options.feedback ~level:3 "setting kernel options for E-ACSL."; - Kernel.CppExtraArgs.add - (Format.asprintf " -DE_ACSL_MACHDEP=%s -I%a/memory_model" - (Kernel.Machdep.get ()) - Datatype.Filepath.pp_abs share); - Kernel.Keep_unused_specified_functions.off (); - if Plugin.is_present "variadic" then - Dynamic.Parameter.Bool.off "-variadic-translation" (); - let ppc, ppk = File.get_preprocessor_command () in - let register s = - File.pre_register - (File.NeedCPP - (s, - ppc - ^ Format.asprintf " -I%a" Datatype.Filepath.pp_abs share, - ppk)) - in - List.iter register (Misc.library_files ()) - in - if Ast.is_computed () then begin - (* do not modify the existing project: work on a copy. - Must also extend the current AST with the E-ACSL's library files. *) - Options.feedback ~level:2 - "AST already computed: E-ACSL is going to work on a copy."; - let name = Project.get_name (Project.current ()) in - let tmpfile = - Extlib.temp_file_cleanup_at_exit ("e_acsl_" ^ name) ".i" in - let cout = open_out tmpfile in - let fmt = Format.formatter_of_out_channel cout in - File.pretty_ast ~fmt (); - let selection = - State_selection.diff - State_selection.full - (State_selection.with_dependencies Ast.self) - in - let prj = - Project.create_by_copy - ~last:false - ~selection - (Format.asprintf "%s for E-ACSL" name) - in - Project.on prj - (fun () -> - Kernel.Files.set [ Datatype.Filepath.of_string tmpfile ]; - extend ()) - (); - Some prj - end else begin - extend (); - None - end - -let extend_ast () = match !extended_ast_project with - | To_be_extended -> - let prj = unmemoized_extend_ast () in - extended_ast_project := Already_extended prj; - (match prj with - | None -> Project.current () - | Some prj -> prj) - | Already_extended None -> - Project.current () - | Already_extended(Some prj) -> - prj - -let apply_on_e_acsl_ast f x = - let tmp_prj = extend_ast () in - let res = Project.on tmp_prj f x in - (match !extended_ast_project with - | To_be_extended -> assert false - | Already_extended None -> () - | Already_extended (Some prj) -> - assert (Project.equal prj tmp_prj); - extended_ast_project := To_be_extended; - if Options.Debug.get () = 0 then Project.remove ~project:tmp_prj ()); - res - module Resulting_projects = State_builder.Hashtbl (Datatype.String.Hashtbl) @@ -133,32 +38,65 @@ let () = let generate_code = Resulting_projects.memo (fun name -> - apply_on_e_acsl_ast + Options.feedback "beginning translation."; + Temporal.enable (Options.Temporal_validity.get ()); + if Plugin.is_present "variadic" then begin + let opt_name = "-variadic-translation" in + if Dynamic.Parameter.Bool.get opt_name () then begin + if Ast.is_computed () then + Options.abort + "The variadic translation must be turned off for E-ACSL. \ + Please use option '-variadic-no-translation'"; + Options.warning "deactivating variadic translation"; + Dynamic.Parameter.Bool.off opt_name (); + end + end; + (* slightly more efficient to copy the project before computing the AST + if it is not yet computed *) + let rtl_prj_name = Options.Project_name.get () ^ " RTL" in + let rtl_prj = Project.create_by_copy ~last:false rtl_prj_name in + (* force AST computation before copying the project it belongs to *) + Ast.compute (); + let copied_prj = Project.create_by_copy ~last:true name in + Project.on + copied_prj (fun () -> - Options.feedback "beginning translation."; - Temporal.enable (Options.Temporal_validity.get ()); - let copied_prj = - Project.create_by_copy name ~last:true + (* preparation of the AST does not concern the E-ACSL RTL: + do it first *) + Prepare_ast.prepare (); + Rtl.link rtl_prj; + (* the E-ACSL type system ensures the soundness of the generated + arithmetic operations. Therefore, deactivate the corresponding + options in order to prevent RTE to generate spurious alarms. *) + let signed = Kernel.SignedOverflow.get () in + let unsigned = Kernel.UnsignedOverflow.get () in + (* we do know that setting these flags does not modify the program's + semantics: using their unsafe variants is thus safe and preserve + all emitted property statuses. *) + Kernel.SignedOverflow.unsafe_set false; + Kernel.UnsignedOverflow.unsafe_set false; + let finally () = + Kernel.SignedOverflow.unsafe_set signed; + Kernel.UnsignedOverflow.unsafe_set unsigned + in + Extlib.try_finally ~finally 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] what if RTE was already computed? To be fixed when + redoing the RTE management system *) + let selection = + State_selection.union + (State_selection.with_dependencies !Db.RteGen.self) + (State_selection.with_dependencies Options.Run.self) in - 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 ()); - copied_prj) - ()) + Project.clear ~selection ~project:copied_prj (); + Resulting_projects.mark_as_computed ()) + (); + if not (Options.debug_atleast 1) then Project.remove ~project:rtl_prj (); + Options.feedback "translation done in project \"%s\"." + (Options.Project_name.get ()); + copied_prj) let generate_code = Dynamic.register @@ -177,15 +115,6 @@ let predicate_to_exp = Kernel_function.ty Cil_datatype.Predicate.ty Cil_datatype.Exp.ty) Translate.predicate_to_exp -let add_e_acsl_library _files = - if Options.must_visit () || Options.Prepare.get () then ignore (extend_ast ()) - -(* extending the AST as soon as possible reduce the amount of time the AST is - duplicated: - - that is faster - - locations are better (indicate an existing file, and not a temp file) *) -let () = Cmdline.run_after_configuring_stage add_e_acsl_library - (* The Frama-C standard library contains specific built-in variables prefixed by "__fc_" and declared as extern: they prevent the generated code to be linked. This modification of the default printer replaces them by their @@ -248,17 +177,10 @@ let change_printer = end let main () = - Keep_status.clear (); if Options.Run.get () then begin change_printer (); - ignore (generate_code (Options.Project_name.get ())) - end else - if Options.Check.get () then - apply_on_e_acsl_ast - (fun () -> - Gmp_types.init (); - ignore (check ())) - () + ignore (generate_code (Options.Project_name.get ())); + end let () = Db.Main.extend main diff --git a/src/plugins/e-acsl/src/options.ml b/src/plugins/e-acsl/src/options.ml index da21806929227ab689d12a3f31fcd40632a80445..8c98ca1657933ca519eef73fa5697877efa50842 100644 --- a/src/plugins/e-acsl/src/options.ml +++ b/src/plugins/e-acsl/src/options.ml @@ -31,13 +31,6 @@ module P = Plugin.Register module PP = P (* [PP] required to avoid an ocamldoc error in OCaml 4.02 *) include PP -module Check = - False - (struct - let option_name = "-e-acsl-check" - let help = "only type check E-ACSL annotated program" - end) - module Run = False (struct @@ -63,13 +56,6 @@ module Valid = let help = "translate annotation which have been proven valid" end) -module Prepare = - False - (struct - let option_name = "-e-acsl-prepare" - let help = "prepare the AST to be directly usable by E-ACSL" - end) - module Gmp_only = False (struct @@ -163,7 +149,9 @@ let parameter_states = Functions.self; Instrument.self ] -let must_visit () = Run.get () || Check.get () +let emitter = Emitter.create "E-ACSL" [ Funspec ] ~correctness:[] ~tuning:[] + +let must_visit () = Run.get () let dkey_analysis = register_category "analysis" let dkey_prepare = register_category "preparation" @@ -172,6 +160,6 @@ let dkey_typing = register_category "typing" (* Local Variables: -compile-command: "make" +compile-command: "make -C ../../../.." End: *) diff --git a/src/plugins/e-acsl/src/options.mli b/src/plugins/e-acsl/src/options.mli index 8e8114594f8b8da0f1dac3d204fef317ac144a57..d5065c6a51dbbade2b491b5cf91fea75d17e7f23 100644 --- a/src/plugins/e-acsl/src/options.mli +++ b/src/plugins/e-acsl/src/options.mli @@ -22,10 +22,8 @@ include Plugin.S (** implementation of Log.S for E-ACSL *) -module Check: Parameter_sig.Bool module Run: Parameter_sig.Bool module Valid: Parameter_sig.Bool -module Prepare: Parameter_sig.Bool module Gmp_only: Parameter_sig.Bool module Full_mmodel: Parameter_sig.Bool module Project_name: Parameter_sig.String @@ -38,6 +36,7 @@ module Functions: Parameter_sig.Kernel_function_set module Instrument: Parameter_sig.Kernel_function_set val parameter_states: State.t list +val emitter: Emitter.t val must_visit: unit -> bool @@ -48,6 +47,6 @@ val dkey_typing: category (* Local Variables: -compile-command: "make" +compile-command: "make -C ../../../.." End: *) diff --git a/src/plugins/e-acsl/src/project_initializer/keep_status.ml b/src/plugins/e-acsl/src/project_initializer/keep_status.ml deleted file mode 100644 index f4b83a501b0900b1e0599e48031a3a0f033a82b9..0000000000000000000000000000000000000000 --- a/src/plugins/e-acsl/src/project_initializer/keep_status.ml +++ /dev/null @@ -1,162 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of the Frama-C's E-ACSL plug-in. *) -(* *) -(* Copyright (C) 2012-2020 *) -(* 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). *) -(* *) -(**************************************************************************) - -(* E-ACSL needs to access to the property status of every property (in - particular for the option -e-acsl-valid). However, the necessary elements for - building a property are copied/modified several times from the original - project to the final project and the property statuses are destroyed when - creating the intermediate projects; so there is no easy way to access to - property statuses from the final project. - - This module aims at solving this issue by providing an access to property - statuses from the final project. To work properly, it requires to visit every - property during the final visit in the very same order than during the AST - preparation visit. Indeed, for each function, it associates to each - property an unique integer corresponding to its visit ordering. *) - -(* the kind is only used for a few additional consistency checks between [push] - and [must_translate]*) -type kind = - | K_Assert - | K_Invariant - | K_Variant - | K_StmtSpec - | K_Allocation - | K_Assigns - | K_Decreases - | K_Terminates - | K_Complete - | K_Disjoint - | K_Requires - | K_Ensures - -let pretty_kind fmt k = - Format.fprintf fmt "%s" - (match k with - | K_Assert -> "assert" - | K_Invariant -> "invariant" - | K_Variant -> "variant" - | K_StmtSpec -> "stmtspec" - | K_Allocation -> "allocation" - | K_Assigns -> "assigns" - | K_Decreases -> "decreases" - | K_Terminates -> "terminates" - | K_Complete -> "complete" - | K_Disjoint -> "disjoint" - | K_Requires -> "requires" - | K_Ensures -> "ensures") - -(* information attached to every kernel_function containing an annotation *) -type kf_info = - { mutable cpt: int - (* counter building the relationship between [push] and [must_translate] *); - mutable statuses: (kind * bool) Datatype.Int.Map.t - (* map associating a property as an integer to its kind and status - ([true] = proved) *) } - -(* statuses for each function represented by its name (because the [kf] itself - changes from a project to another). *) -let keep_status - : kf_info Datatype.String.Hashtbl.t - = Datatype.String.Hashtbl.create 17 - -(* will contain the value of a few options from the original project - in order to safely use them from the final project. *) -let option_valid = ref false -let option_check = ref false - -let clear () = - Datatype.String.Hashtbl.clear keep_status; - option_valid := Options.Valid.get (); - option_check := Options.Check.get () - -let push kf kind ppt = - (* Options.feedback "PUSHING %a for %a" - pretty_kind kind - Kernel_function.pretty kf;*) - (* no registration when -e-acsl-check or -e-acsl-valid *) - if not (!option_check || !option_valid) then - let keep = - let open Property_status in - match get ppt with - | Never_tried - | Inconsistent _ - | Best ((False_if_reachable | False_and_reachable | Dont_know), _) -> - true - | Best (True, _) -> - false - in - let status = kind, keep in - let name = Kernel_function.get_name kf in - try - let info = Datatype.String.Hashtbl.find keep_status name in - info.cpt <- info.cpt + 1; - info.statuses <- Datatype.Int.Map.add info.cpt status info.statuses - with Not_found -> - let info = { cpt = 1; statuses = Datatype.Int.Map.singleton 1 status } in - Datatype.String.Hashtbl.add keep_status name info - -let before_translation () = - (* reset all counters *) - Datatype.String.Hashtbl.iter (fun _ info -> info.cpt <- 0) keep_status - -let must_translate kf kind = - (* Options.feedback "GETTING %a for %a" - pretty_kind kind - Kernel_function.pretty kf;*) - !option_check - || - !option_valid - || - (* function contracts have been moved from the original function to its - duplicate by [Dup_function] but they are still associated to the original - function here *) - let name = Functions.RTL.get_original_name kf in - try - let info = - try Datatype.String.Hashtbl.find keep_status name - with Not_found -> - Options.fatal "[keep_status] unbound function" Datatype.String.pretty kf - in - info.cpt <- info.cpt + 1; - let kind', keep = - try Datatype.Int.Map.find info.cpt info.statuses - with Not_found -> - Options.fatal "[keep_status] unbound annotation (id %d)@ in function %a" - info.cpt - Kernel_function.pretty kf - in - (* check kind consistency in order to detect more abnormal behaviors *) - if kind <> kind' then - Options.fatal - "[keep_status] incorrect kind '%a' (expected: '%a')@ in function %a" - pretty_kind kind - pretty_kind kind' - Kernel_function.pretty kf; - keep - with Not_found -> true - -(* -Local Variables: -compile-command: "make -C ../../../../.." -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 57458a5688878cb8930006420b56b818aa7905d5..2f167b55c03dbf9eb5fc203cdc93d569dfcd8323 100644 --- a/src/plugins/e-acsl/src/project_initializer/prepare_ast.ml +++ b/src/plugins/e-acsl/src/project_initializer/prepare_ast.ml @@ -21,6 +21,7 @@ (**************************************************************************) open Cil_types +open Cil_datatype let dkey = Options.dkey_prepare @@ -28,81 +29,69 @@ let dkey = Options.dkey_prepare (* 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 +module Dup_functions: sig + val generate_vi: varinfo -> varinfo (* return the new varinfo *) + val mem: varinfo -> bool + val find: varinfo -> varinfo 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 + let tbl: varinfo Varinfo.Hashtbl.t = Varinfo.Hashtbl.create 7 + + (* assume [vi] is not already in [tbl] *) + let generate_vi vi = + let new_name = Functions.RTL.mk_gen_name vi.vname in + let new_vi = + Cil.makeGlobalVar + ~referenced:vi.vreferenced + ~loc:vi.vdecl + new_name + vi.vtype + in + Varinfo.Hashtbl.add tbl vi new_vi; + new_vi -end + let mem = Varinfo.Hashtbl.mem tbl + let find = Varinfo.Hashtbl.find tbl + let reset () = Varinfo.Hashtbl.clear tbl -let reset () = - Kernel_function.Hashtbl.clear fct_tbl; - Global.reset (); - Queue.clear actions +end (* ********************************************************************** *) (* Duplicating a function *) (* ********************************************************************** *) -(* [tbl] associates the old formals to the new ones *) -let dup_funspec tbl bhv spec = +(* [formals] associates the old formals to the new ones in order to + substitute them in [spec]. *) +let dup_funspec formals 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 + inherit Visitor.frama_c_refresh (Project.current ()) - 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) + val already_visited = Logic_var.Hashtbl.create 7 + (* just substituting in [!vvrbl] (when using a varinfo) does not work + because varinfo's occurrences are shared in logic_vars, so modifying the + varinfo would modify any logic_var based on it, even if it is not part of + this [spec] (e.g., if it is in another annotation of the same + function) *) 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 + let new_lvi = Logic_var.Hashtbl.find already_visited orig_lvi in + (* recreate sharing of the logic_var in this [spec] *) Cil.ChangeTo new_lvi with Not_found -> + (* first time visiting this logic var *) 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; + let new_vi = Varinfo.Hashtbl.find formals vi in + 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; @@ -112,20 +101,12 @@ let dup_funspec tbl bhv spec = 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) + 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 + Cil.visitCilFunspec (o :> Cil.cilVisitor) spec -let dup_fundec loc spec bhv sound_verdict_vi kf vi new_vi = +let dup_fundec loc spec sound_verdict_vi kf vi new_vi = new_vi.vdefined <- true; let formals = Kernel_function.get_formals kf in let mk_formal vi = @@ -174,9 +155,9 @@ let dup_fundec loc spec bhv sound_verdict_vi kf vi new_vi = 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 tbl = Varinfo.Hashtbl.create 7 in + List.iter2 (Varinfo.Hashtbl.add tbl) formals new_formals; + let new_spec = dup_funspec tbl spec in let fundec = { svar = new_vi; sformals = new_formals; @@ -192,54 +173,69 @@ let dup_fundec loc spec bhv sound_verdict_vi kf vi new_vi = Cfg.cfgFun fundec; fundec -let dup_global loc actions spec bhv sound_verdict_vi kf vi new_vi = +let dup_global loc spec 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 + let new_fundec = dup_fundec loc spec sound_verdict_vi kf vi new_vi in + let new_fct = Definition(new_fundec, loc) in + let new_spec = new_fundec.sspec in + let new_kf = { fundec = new_fct; spec = new_spec } in + Globals.Functions.register new_kf; + Globals.Functions.replace_by_definition new_spec new_fundec loc; + Annotations.register_funspec new_kf; + if Datatype.String.equal new_vi.vname "main" then begin + (* recompute kernel's 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; + (* copy property statuses to the new spec *) + let copy old_ip new_ip = + let open Property_status in + let cp status ep = + let e = Emitter.Usable_emitter.get ep.emitter in + if ep.logical_consequence + then logical_consequence e new_ip ep.properties + else emit e new_ip ~hyps:ep.properties status + in + match get old_ip with + | Never_tried -> + () + | Best(s, epl) -> + List.iter (cp s) epl + | Inconsistent icst -> + List.iter (cp True) icst.valid; + (* either the program is reachable and [False_and_reachable] is + fine, or the program point is not reachable and it does not + matter for E-ACSL that checks it at runtime. *) + List.iter (cp False_and_reachable) icst.invalid + in + let ips kf s = Property.ip_of_spec kf Kglobal ~active:[] s in + List.iter2 copy (ips kf spec) (ips new_kf new_spec); + (* remove annotations from the old spec. Yet keep them in functions only + declared since, otherwise, the kernel would complain about functions + with neither contract nor body *) 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 + let old_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) + old_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 end; - GFun(fundec, loc), GFunDecl(new_spec, new_vi, loc) + GFun(new_fundec, loc), GFunDecl(new_spec, new_vi, loc) (* ********************************************************************** *) (* Alignment *) @@ -293,356 +289,187 @@ let require_alignment vi algn = Cil.bitsSizeOf vi.vtype < algn*8 && not (sufficiently_aligned vi algn) (* ********************************************************************** *) -(* Visitor *) +(* Visiting the globals *) (* ********************************************************************** *) -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 - [Translate.translate_post_spec]: be careful when modifying it. *) - - method private push_pre_spec s = - let kf = Extlib.the self#current_kf in - let kinstr = self#current_kinstr in - let open Keep_status in - Extlib.may - (fun v -> push kf K_Decreases (Property.ip_of_decreases kf kinstr v)) - s.spec_variant; - Extlib.may - (fun t -> push kf K_Terminates (Property.ip_of_terminates kf kinstr t)) - s.spec_terminates; - List.iter - (fun l -> - push kf K_Complete (Property.ip_of_complete kf kinstr ~active:[] l)) - s.spec_complete_behaviors; - List.iter - (fun l -> - push kf K_Disjoint (Property.ip_of_disjoint kf kinstr ~active:[] l)) - s.spec_disjoint_behaviors; - List.iter - (fun b -> - List.iter - (fun p -> push kf K_Requires (Property.ip_of_requires kf kinstr b p)) - b.b_requires) - s.spec_behavior - - method private push_post_spec spec = - let do_behavior b = - let kf = Extlib.the self#current_kf in - let ki = match self#current_stmt with - | None -> Kglobal - | Some stmt -> Kstmt stmt - in - let open Keep_status in - Extlib.may - (push kf K_Assigns) - (Property.ip_of_assigns - kf - ki - (Property.Id_contract (Datatype.String.Set.empty (* TODO *), b)) - b.b_assigns); - List.iter - (fun p -> push kf K_Ensures (Property.ip_of_ensures kf ki b p)) - b.b_post_cond - in - (* fix ordering of behaviors' iterations *) - let bhvs = - List.sort - (fun b1 b2 -> String.compare b1.b_name b2.b_name) - spec.spec_behavior - in - List.iter do_behavior bhvs - - method private push_pre_code_annot a = - let kf = Extlib.the self#current_kf in - let stmt = Extlib.the self#current_stmt in - let push_single k a = - Keep_status.push kf k (Property.ip_of_code_annot_single kf stmt a) - in - let open Keep_status in - match a.annot_content with - | AAssert _ -> push_single K_Assert a - | AStmtSpec(_ (* TODO *), s) -> self#push_pre_spec s - | AInvariant _ -> push_single K_Invariant a - | AVariant v -> - push kf K_Variant (Property.ip_of_decreases kf (Kstmt stmt) v) - | AAssigns _ -> - (* 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 - (push kf K_Assigns) - (Property.ip_assigns_of_code_annot kf (Kstmt stmt) a) - | AAllocation(_ (* TODO *), alloc) -> - 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 *) - () - - method private push_post_code_annot a = match a.annot_content with - | AStmtSpec(_ (* TODO *), s) -> self#push_post_spec s - | AAssert _ - | AInvariant _ - | AVariant _ - | AAssigns _ - | AAllocation _ - | APragma _ - | AExtended _ -> () - - (* ---------------------------------------------------------------------- *) - (* 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) - stmt; - Cil.DoChildrenPost - (fun _ -> - Annotations.iter_code_annot - (fun _ a -> self#push_post_code_annot a) - stmt; - stmt) - - 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 +(* perform a few modifications in the [kf]'s code and annotations that are + required by E-ACSL's analyses and transformations *) +let prepare_fundec kf = + let o = object + inherit Visitor.frama_c_inplace + + (* table for ensuring absence of term sharing *) + val terms = Misc.Id_term.Hashtbl.create 7 + + (* substitute function's varinfos by their duplicate in function calls *) + method !vvrbl vi = + try Cil.ChangeTo (Dup_functions.find vi) + with Not_found -> Cil.SkipChildren + + (* temporal analysis requires that attributes are aligned to local + variables *) + 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 - | GVarDecl(vi, loc) | GFunDecl(_, vi, loc) | GFun({ svar = vi }, loc) - 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 + (* remove term sharing introduced by the Frama-C kernel. For instance, an + annotation such as [a <= t <= b] is replaced by [a <= t && t <= b] with + both occurrences of [t] being shared. Yet, the E-ACSL type system may + require to assign them different types. *) + method !vterm _ = Cil.DoChildrenPost - (fun f -> - self#push_pre_spec s; - self#push_post_spec s; - f) + (fun t -> + if Misc.Id_term.Hashtbl.mem terms t then + { t with term_node = t.term_node } + else begin + Misc.Id_term.Hashtbl.add terms t (); + t + end) - | _ -> + method !videntified_predicate _ = + (* when entering a new annotation, clear the context to keep it small: + anyway, no sharing is possible between two identified predicates *) + Misc.Id_term.Hashtbl.clear terms; Cil.DoChildren - 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 + end in + ignore (Visitor.visitFramacKf o kf) + +(* 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]). *) +let sound_verdict_vi = + lazy + (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) + +let is_variadic_function vi = match Cil.unrollType vi.vtype with + | TFun(_, _, variadic, _) -> variadic + | _ -> false + +(* set of functions that must never be duplicated *) +let unduplicable_functions = + let white_list = + [ "__builtin_va_arg"; + "__builtin_va_end"; + "__builtin_va_start"; + "__builtin_va_copy"; + (* *alloc and free should not be duplicated. *) + (* [TODO:] it preserves the former behavior. Should be modified latter by + checking only preconditions for any libc functions, see e-acsl#35 *) + "malloc"; + "calloc"; + "realloc"; + "free" ] + in + List.fold_left + (fun acc s -> Datatype.String.Set.add s acc) + Datatype.String.Set.empty + white_list + +let must_duplicate kf vi = + (* it is not already duplicated *) + not (Dup_functions.mem vi) + && (* it is duplicable *) + not (Datatype.String.Set.mem vi.vname unduplicable_functions) + && (* it is not a variadic function *) + not (is_variadic_function vi) + && (* it is not a built-in *) + not (Misc.is_fc_or_compiler_builtin vi) + && + ((* 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 kf)) + && (* its annotations must be monitored *) + Functions.check kf)) + +let prepare_global (globals, new_defs) = function + | GFunDecl(_, vi, loc) | GFun({ svar = vi }, loc) as g -> + let kf = try Globals.Functions.get vi with Not_found -> assert false in + if must_duplicate kf vi then begin + let new_vi = Dup_functions.generate_vi vi in + if Kernel_function.is_definition kf then + prepare_fundec kf + else + (* TODO: this warning could be more precise if emitted during code + generation; see also E-ACSL issue #85 about partial verdicts *) + Options.warning + "@[annotating undefined function `%a':@ \ + the generated program may miss memory instrumentation@ \ + if there are memory-related annotations.@]" + Printer.pp_varinfo vi; + let tmp = vi.vname in + if Datatype.String.equal 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 + end; + let new_def, new_decl = + dup_global + loc + (Annotations.funspec ~populate:false kf) + (Lazy.force sound_verdict_vi) + kf + vi + new_vi + in + (* postpone the introduction of the new function definition *) + let new_defs = new_def :: new_defs in + (* put the declaration before the original function in order to solve + issues with recursive functions *) + g :: new_decl :: globals, new_defs + end else begin (* not duplicated *) + prepare_fundec kf; + g :: globals, new_defs + end + | g -> + (* nothing to do for globals that are not functions *) + g :: globals, new_defs + +let prepare_file file = + Dup_functions.reset (); + let rev_globals, new_defs = + List.fold_left prepare_global ([], []) file.globals + in + match new_defs with + | [] -> () + | _ :: _ -> + (* insert the new_definitions at the end and reverse back the globals *) + let globals = List.fold_left (fun acc g -> g :: acc) new_defs rev_globals in + (* insert [__e_acsl_sound_verdict] at the beginning *) + let sg = GVarDecl(Lazy.force sound_verdict_vi, Location.unknown) in + file.globals <- sg :: globals let prepare () = Options.feedback ~level:2 "prepare AST for E-ACSL transformations"; - Visitor.visitFramacFile (new prepare_visitor) (Ast.get ()); - Queue.iter (fun f -> f ()) actions; + prepare_file (Ast.get ()); Ast.mark_as_grown () (* 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 5237a7d39d59ca87564a0f4d6329972f641c9f42..65ea62daf1854443f8523737c7f519d9e18e9dd6 100644 --- a/src/plugins/e-acsl/src/project_initializer/prepare_ast.mli +++ b/src/plugins/e-acsl/src/project_initializer/prepare_ast.mli @@ -25,7 +25,6 @@ More precisely, this module performs the following tasks: - generating a new definition for functions with contract; - removing term sharing; - - 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. *) diff --git a/src/plugins/e-acsl/src/project_initializer/rtl.ml b/src/plugins/e-acsl/src/project_initializer/rtl.ml new file mode 100644 index 0000000000000000000000000000000000000000..38f3fb5809f3adc1a37e721dbb726d3a46f31393 --- /dev/null +++ b/src/plugins/e-acsl/src/project_initializer/rtl.ml @@ -0,0 +1,270 @@ +(**************************************************************************) +(* *) +(* This file is part of the Frama-C's E-ACSL plug-in. *) +(* *) +(* Copyright (C) 2012-2020 *) +(* 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 +open Cil_datatype + +let rtl_files () = + List.map + (fun d -> Options.Share.get_file ~mode:`Must_exist d) + [ "e_acsl_gmp_api.h"; + "e_acsl.h" ] + +(* create the RTL AST in a fresh project *) +let create_rtl_ast prj = + Project.on + prj + (fun () -> + (* compute the RTL AST in the standard E-ACSL setting *) + if Plugin.is_present "variadic" then + Dynamic.Parameter.Bool.off "-variadic-translation" (); + Kernel.Keep_unused_specified_functions.off (); + Kernel.CppExtraArgs.add + (Format.asprintf " -DE_ACSL_MACHDEP=%s" (Kernel.Machdep.get ())); + Kernel.Files.set (rtl_files ()); + Ast.get ()) + () + +(* Note: vids, sids and eids are shared between projects (see implementation of + [Cil_const]). Therefore, no need to set them when inserting the corresponding + global into the AST. *) + +(* Tables that contain RTL's symbols. Useful to know whether some symbols is + part of the RTL. *) +module Symbols: sig + val add_global: global -> unit + val mem_global: global -> bool + val add_kf: kernel_function -> unit + val mem_kf: kernel_function -> bool + val add_vi: string -> varinfo -> unit + val mem_vi: string -> bool + exception Unregistered of string + val find_vi: string -> varinfo (* may raise Unregistered *) + (* val debug: unit -> unit*) +end = struct + + let globals: unit Global.Hashtbl.t = Global.Hashtbl.create 17 + let kfs: unit Kernel_function.Hashtbl.t = Kernel_function.Hashtbl.create 17 + let vars + : varinfo Datatype.String.Hashtbl.t + = Datatype.String.Hashtbl.create 17 + + let add_global g = Global.Hashtbl.add globals g () + let mem_global g = Global.Hashtbl.mem globals g + + let add_kf g = Kernel_function.Hashtbl.add kfs g () + let mem_kf g = Kernel_function.Hashtbl.mem kfs g + + let add_vi s vi = Datatype.String.Hashtbl.add vars s vi + let mem_vi s = Datatype.String.Hashtbl.mem vars s + exception Unregistered of string + let find_vi s = + try Datatype.String.Hashtbl.find vars s + with Not_found -> raise (Unregistered s) + +(* + let debug () = + Global.Hashtbl.iter + (fun g1 () -> Format.printf "RTL %a@." Printer.pp_global g1) + rtl; + Varinfo.Hashtbl.iter + (fun g1 g2 -> Format.printf "VAR %a -> %a@." + Printer.pp_varinfo g1 + Printer.pp_varinfo g2) + vars; + Typ.Hashtbl.iter + (fun g1 g2 -> Format.printf "TYP %a -> %a@." + Printer.pp_typ g1 + Printer.pp_typ g2) + types; + Global_annotation.Hashtbl.iter + (fun g1 g2 -> Format.printf "ANNOT %a -> %a@." + Printer.pp_global_annotation g1 + Printer.pp_global_annotation g2) + annots +*) +end + +(* NOTE: do not use [Mergecil.merge] since all [ast]'s symbols must be kept + unchanged: so do it ourselves. Thanksfully, we merge in a particular + setting because we know what the RTL is. Therefore merging is far from being + as complicated as [Mergecil]. *) + +(* lookup globals from [rtl_ast] in the current [ast] and fill the [Symbols]' + tables. + @return the list of globals to be inserted into [ast], in reverse order. *) +let lookup_rtl_globals rtl_ast = + (* [do_it ~add mem acc l g] checks whether the global does exist in the user's + AST. If not, add it to the corresponding symbol table(s). *) + let rec do_it ?(add=fun _g -> ()) mem acc l g = + if mem g then do_globals (g :: acc) l + else begin + add g; + Symbols.add_global g; + do_globals (g :: acc) l + end + (* [do_ty] is [do_it] for types *) + and do_ty acc l g kind tname = + do_it (fun _ -> Globals.Types.mem_type kind tname) acc l g + and do_globals acc globs = + match globs with + | [] -> + acc + | GType(ti, _loc) as g :: l -> + do_ty acc l g Logic_typing.Typedef ti.tname + | GCompTag(ci, _loc) | GCompTagDecl(ci, _loc) as g :: l -> + let k = if ci.cstruct then Logic_typing.Struct else Logic_typing.Union in + do_ty acc l g k ci.cname + | GEnumTag(ei, _loc) | GEnumTagDecl(ei, _loc) as g :: l -> + do_ty acc l g Logic_typing.Enum ei.ename + | GVarDecl(vi, (pos, _)) | GVar(vi, _, (pos, _)) as g :: l -> + let tunit = Translation_unit pos.Filepath.pos_path in + let mem _g = + let g = Globals.Syntactic_search.find_in_scope vi.vorig_name tunit in + Option.is_some g + in + let add g = + Symbols.add_vi vi.vname vi; + match g with + | GVarDecl _ -> Globals.Vars.add_decl vi + | GVar(_, ii, _) -> Globals.Vars.add vi ii + | _ -> assert false + in + do_it ~add mem acc l g + | GFunDecl(_, vi, _loc) | GFun({ svar = vi }, _loc) as g :: l -> + let add _g = + Symbols.add_vi vi.vname vi; + (* functions will be registered in kernel's table after substitution of + RTL's symbols inside them *) + in + do_it ~add (fun _ -> Globals.Functions.mem_name vi.vname) acc l g + | GAnnot _ :: l -> + (* ignoring annotations from the AST *) + do_globals acc l + | GAsm _ | GPragma _ | GText _ as g :: _l -> + Kernel.fatal "unexpected global %a" Printer.pp_global g + in + do_globals [] rtl_ast.globals + +(* [substitute_rtl_symbols] registers the correct symbols for RTL's functions *) +let substitute_rtl_symbols rtl_prj = function + | GVarDecl _ | GVar _ as g -> + g + | GFunDecl(_, vi, loc) | GFun({ svar = vi }, loc) as g -> + let get_kf vi = + try + Globals.Functions.get vi + with Not_found -> + Options.fatal "unknown function %s in project %a" + vi.vname + Project.pretty (Project.current ()) + in + let selection = + State_selection.of_list + [ Globals.Functions.self; Annotations.funspec_state ] + in + (* get the RTL's formals and spec *) + let formals, spec = + Project.on + rtl_prj + ~selection + (fun vi -> + let kf = get_kf vi in + Kernel_function.get_formals kf, + Annotations.funspec ~populate:false kf) + vi + in + (match g with + | GFunDecl _ -> + Globals.Functions.replace_by_declaration spec vi loc + | GFun(fundec, _) -> + Globals.Functions.replace_by_definition spec fundec loc + | _ -> assert false); + (* [Globals.Functions.replace_by_declaration] assigns new vids to formals: + get them back to their original ids in order to have the correct ids in + [spec] *) + let kf = get_kf vi in + List.iter2 + (fun rtl_vi src_vi -> src_vi.vid <- rtl_vi.vid) + formals + (Kernel_function.get_formals kf); + Cil.unsafeSetFormalsDecl vi formals; + Annotations.register_funspec ~emitter:Options.emitter kf; + Symbols.add_kf kf; + g + | GType _ | GCompTag _ | GCompTagDecl _ | GEnumTag _ | GEnumTagDecl _ + | GAnnot _ | GAsm _ | GPragma _ | GText _ -> + assert false + +(* [insert_rtl_globals] adds the rtl symbols into the user's AST *) +let insert_rtl_globals rtl_prj rtl_globals ast = + let add_ty acc old_g new_g = + let g = if Symbols.mem_global old_g then old_g else new_g in + (* keep them in the AST even if already in the user's one to prevent forward + references *) + g :: acc + in + let rec add acc = function + | [] -> + acc + | GType _ | GCompTagDecl _ | GEnumTagDecl _ as g :: l -> + (* RTL types contain no libc values: no need to substitute inside them *) + let acc = add_ty acc g g in + add acc l + | GCompTag(ci, loc) as g :: l -> + (* RTL's structure definitions that are already defined in the AST shall + be only declared *) + let acc = add_ty acc g (GCompTagDecl(ci, loc)) in + add acc l + | GEnumTag(ei, loc) as g :: l -> + (* RTL's enum definitions that are already defined in the AST shall be + only declared *) + let acc = add_ty acc g (GEnumTagDecl(ei, loc)) in + add acc l + | GVarDecl _ | GVar _ | GFunDecl _ | GFun _ as g :: l -> + let acc = + if Symbols.mem_global g then + let g = substitute_rtl_symbols rtl_prj g in + g :: acc + else + acc + in + add acc l + | GAnnot _ | GAsm _ | GPragma _ | GText _ as g :: _l -> + Kernel.fatal "unexpected global %a" Printer.pp_global g + in + ast.globals <- add ast.globals rtl_globals + +let link rtl_prj = + Options.feedback ~level:2 "link the E-ACSL RTL with the user source code"; + let rtl_ast = create_rtl_ast rtl_prj in + let ast = Ast.get () in + let rtl_globals = lookup_rtl_globals rtl_ast in + (* Symbols.debug ();*) + insert_rtl_globals rtl_prj rtl_globals ast; + Ast.mark_as_grown () + +(* +Local Variables: +compile-command: "make -C ../../../../.." +End: +*) diff --git a/src/plugins/e-acsl/src/project_initializer/keep_status.mli b/src/plugins/e-acsl/src/project_initializer/rtl.mli similarity index 61% rename from src/plugins/e-acsl/src/project_initializer/keep_status.mli rename to src/plugins/e-acsl/src/project_initializer/rtl.mli index 455662756b7abdf51f79185ee20d09bf03fddd8a..ff7cf96526a68a6ab7a22d40c14865cf2825326c 100644 --- a/src/plugins/e-acsl/src/project_initializer/keep_status.mli +++ b/src/plugins/e-acsl/src/project_initializer/rtl.mli @@ -20,41 +20,29 @@ (* *) (**************************************************************************) -(** Make the property statuses of the initial project accessible when - doing the main translation *) +(** This module links the E-ACSL's RTL to the user source code. *) -type kind = - | K_Assert - | K_Invariant - | K_Variant - | K_StmtSpec - | K_Allocation - | K_Assigns - | K_Decreases - | K_Terminates (* TODO: should be removed: not part of the E-ACSL subset *) - | K_Complete - | K_Disjoint - | K_Requires - | K_Ensures +val link: Project.t -> unit +(** [link prj] links the RTL's AST contained in [prj] to the AST of the current + project. *) -val clear: unit -> unit -(** to be called before any program transformation *) +(** Tables that contain RTL's symbols. Useful to know whether some symbols is + part of the RTL. *) +module Symbols: sig + open Cil_types -val push: Kernel_function.t -> kind -> Property.t -> unit -(** store the given property of the given kind for the given function *) + val mem_global: global -> bool + val mem_kf: kernel_function -> bool -val before_translation: unit -> unit -(** to be called just before injecting the code *) + val mem_vi: string -> bool + exception Unregistered of string + val find_vi: string -> varinfo + (** @raise Unregistered if the given name is not part of the RTL. *) -val must_translate: Kernel_function.t -> kind -> bool -(** To be called just before transforming a property of the given kind for the - given function. - VERY IMPORTANT: the property of the n-th call to this function exactly - correspond to the n-th pushed property (see {!push}). - @return true if and only if the translation must occur. *) +end (* Local Variables: -compile-command: "make -C ../.." +compile-command: "make -C ../../../../.." End: *) diff --git a/src/plugins/e-acsl/tests/bts/oracle_ci/gen_bts1386_complex_flowgraph.c b/src/plugins/e-acsl/tests/bts/oracle_ci/gen_bts1386_complex_flowgraph.c index 5f3956f765f048f6cac3858217ecfd51bfe4f7ea..b3d1e5df6a10a1ce8fb80e3003fc4f942cdff13b 100644 --- a/src/plugins/e-acsl/tests/bts/oracle_ci/gen_bts1386_complex_flowgraph.c +++ b/src/plugins/e-acsl/tests/bts/oracle_ci/gen_bts1386_complex_flowgraph.c @@ -1,4 +1,5 @@ /* Generated by Frama-C */ +#include "stddef.h" #include "stdio.h" #include "stdlib.h" void duffcopy(char *to, char *from, int count) diff --git a/src/plugins/e-acsl/tests/bts/oracle_ci/gen_bts1398.c b/src/plugins/e-acsl/tests/bts/oracle_ci/gen_bts1398.c index 6c992808644dc2b1a22a287bec261511fb770b8d..8f4aa78a77722c4c1bc7163cc8680bf7cc800d3a 100644 --- a/src/plugins/e-acsl/tests/bts/oracle_ci/gen_bts1398.c +++ b/src/plugins/e-acsl/tests/bts/oracle_ci/gen_bts1398.c @@ -1,4 +1,5 @@ /* Generated by Frama-C */ +#include "stddef.h" #include "stdio.h" #include "stdlib.h" char *__gen_e_acsl_literal_string; diff --git a/src/plugins/e-acsl/tests/bts/oracle_ci/gen_issue-eacsl-91.c b/src/plugins/e-acsl/tests/bts/oracle_ci/gen_issue-eacsl-91.c index 519c77cc7f1e326c2caa8402207cbea2b6564600..14dd1ab575af5a0d4560ecc5a1ac7ae3bc49a5d3 100644 --- a/src/plugins/e-acsl/tests/bts/oracle_ci/gen_issue-eacsl-91.c +++ b/src/plugins/e-acsl/tests/bts/oracle_ci/gen_issue-eacsl-91.c @@ -36,24 +36,6 @@ void __e_acsl_globals_init(void) __e_acsl_full_init((void *)(& b)); __e_acsl_store_block((void *)(& a),(size_t)2); __e_acsl_full_init((void *)(& a)); - __e_acsl_store_block((void *)(& __fc_p_tmpnam),(size_t)8); - __e_acsl_full_init((void *)(& __fc_p_tmpnam)); - __e_acsl_store_block((void *)(__fc_tmpnam),(size_t)2048); - __e_acsl_full_init((void *)(& __fc_tmpnam)); - __e_acsl_store_block((void *)(& __fc_p_fopen),(size_t)8); - __e_acsl_full_init((void *)(& __fc_p_fopen)); - __e_acsl_store_block((void *)(__fc_fopen),(size_t)128); - __e_acsl_full_init((void *)(& __fc_fopen)); - __e_acsl_store_block((void *)(& stdin),(size_t)8); - __e_acsl_full_init((void *)(& stdin)); - __e_acsl_store_block((void *)(& __fc_p_random48_counter),(size_t)8); - __e_acsl_full_init((void *)(& __fc_p_random48_counter)); - __e_acsl_store_block((void *)(random48_counter),(size_t)6); - __e_acsl_full_init((void *)(& random48_counter)); - __e_acsl_store_block((void *)(& __fc_random48_init),(size_t)4); - __e_acsl_full_init((void *)(& __fc_random48_init)); - __e_acsl_store_block((void *)(& __fc_rand_max),(size_t)8); - __e_acsl_full_init((void *)(& __fc_rand_max)); } return; } @@ -62,15 +44,6 @@ void __e_acsl_globals_delete(void) { __e_acsl_delete_block((void *)(& b)); __e_acsl_delete_block((void *)(& a)); - __e_acsl_delete_block((void *)(& __fc_p_tmpnam)); - __e_acsl_delete_block((void *)(__fc_tmpnam)); - __e_acsl_delete_block((void *)(& __fc_p_fopen)); - __e_acsl_delete_block((void *)(__fc_fopen)); - __e_acsl_delete_block((void *)(& stdin)); - __e_acsl_delete_block((void *)(& __fc_p_random48_counter)); - __e_acsl_delete_block((void *)(random48_counter)); - __e_acsl_delete_block((void *)(& __fc_random48_init)); - __e_acsl_delete_block((void *)(& __fc_rand_max)); } int main(void) diff --git a/src/plugins/e-acsl/tests/full-mmodel/oracle_ci/gen_addrOf.c b/src/plugins/e-acsl/tests/full-mmodel/oracle_ci/gen_addrOf.c index e7da245320f44ca617784993e0bf842400a92cff..574d904ff3e9f01ffb34f47c1945664f8330ef7b 100644 --- a/src/plugins/e-acsl/tests/full-mmodel/oracle_ci/gen_addrOf.c +++ b/src/plugins/e-acsl/tests/full-mmodel/oracle_ci/gen_addrOf.c @@ -35,24 +35,6 @@ void __e_acsl_globals_init(void) __e_acsl_already_run = 1; __e_acsl_store_block((void *)(& f),(size_t)1); __e_acsl_full_init((void *)(& f)); - __e_acsl_store_block((void *)(& __fc_p_tmpnam),(size_t)8); - __e_acsl_full_init((void *)(& __fc_p_tmpnam)); - __e_acsl_store_block((void *)(__fc_tmpnam),(size_t)2048); - __e_acsl_full_init((void *)(& __fc_tmpnam)); - __e_acsl_store_block((void *)(& __fc_p_fopen),(size_t)8); - __e_acsl_full_init((void *)(& __fc_p_fopen)); - __e_acsl_store_block((void *)(__fc_fopen),(size_t)128); - __e_acsl_full_init((void *)(& __fc_fopen)); - __e_acsl_store_block((void *)(& stdin),(size_t)8); - __e_acsl_full_init((void *)(& stdin)); - __e_acsl_store_block((void *)(& __fc_p_random48_counter),(size_t)8); - __e_acsl_full_init((void *)(& __fc_p_random48_counter)); - __e_acsl_store_block((void *)(random48_counter),(size_t)6); - __e_acsl_full_init((void *)(& random48_counter)); - __e_acsl_store_block((void *)(& __fc_random48_init),(size_t)4); - __e_acsl_full_init((void *)(& __fc_random48_init)); - __e_acsl_store_block((void *)(& __fc_rand_max),(size_t)8); - __e_acsl_full_init((void *)(& __fc_rand_max)); } return; } @@ -60,15 +42,6 @@ void __e_acsl_globals_init(void) void __e_acsl_globals_delete(void) { __e_acsl_delete_block((void *)(& f)); - __e_acsl_delete_block((void *)(& __fc_p_tmpnam)); - __e_acsl_delete_block((void *)(__fc_tmpnam)); - __e_acsl_delete_block((void *)(& __fc_p_fopen)); - __e_acsl_delete_block((void *)(__fc_fopen)); - __e_acsl_delete_block((void *)(& stdin)); - __e_acsl_delete_block((void *)(& __fc_p_random48_counter)); - __e_acsl_delete_block((void *)(random48_counter)); - __e_acsl_delete_block((void *)(& __fc_random48_init)); - __e_acsl_delete_block((void *)(& __fc_rand_max)); } int main(void) diff --git a/src/plugins/e-acsl/tests/memory/local_init.c b/src/plugins/e-acsl/tests/memory/local_init.c index 483a08acdeca626a3024b416c9922f1356da660c..8de0529d5b4ebe3a34852ca95b5cd4d28c4e9816 100644 --- a/src/plugins/e-acsl/tests/memory/local_init.c +++ b/src/plugins/e-acsl/tests/memory/local_init.c @@ -1,7 +1,7 @@ /* run.config_ci COMMENT: test of a local initializer which contains an annotation LOG: gen_@PTEST_NAME@.c - STDOPT: #"@MACHDEP@ @EACSL_PREPARE@ -lib-entry -eva -then -no-lib-entry" + STDOPT: #"@MACHDEP@ -lib-entry -eva -then -no-lib-entry" */ int X = 0; diff --git a/src/plugins/e-acsl/tests/memory/oracle_ci/gen_constructor.c b/src/plugins/e-acsl/tests/memory/oracle_ci/gen_constructor.c index 15a99b43c04dacb8866abecb8ce0ef28e7db4bbf..4e43d7e9cff59e04b077b46a1e9d90fc81d9e076 100644 --- a/src/plugins/e-acsl/tests/memory/oracle_ci/gen_constructor.c +++ b/src/plugins/e-acsl/tests/memory/oracle_ci/gen_constructor.c @@ -1,4 +1,5 @@ /* Generated by Frama-C */ +#include "stddef.h" #include "stdio.h" #include "stdlib.h" char *__gen_e_acsl_literal_string_2; diff --git a/src/plugins/e-acsl/tests/memory/oracle_ci/gen_errno.c b/src/plugins/e-acsl/tests/memory/oracle_ci/gen_errno.c index cc458e4e367b7f4f52387e3d42d727f7ff9f4df6..f38474ba466e667532e9d52c1e92653513013858 100644 --- a/src/plugins/e-acsl/tests/memory/oracle_ci/gen_errno.c +++ b/src/plugins/e-acsl/tests/memory/oracle_ci/gen_errno.c @@ -1,5 +1,6 @@ /* Generated by Frama-C */ #include "errno.h" +#include "stddef.h" #include "stdio.h" #include "stdlib.h" void __e_acsl_globals_init(void) diff --git a/src/plugins/e-acsl/tests/memory/oracle_ci/gen_local_goto.c b/src/plugins/e-acsl/tests/memory/oracle_ci/gen_local_goto.c index 2cf1cc44c0cd0dd389bce183c8fa9c4fdb3ef873..3d26e79512a996e7a619a62f0c495e06bb09a0ee 100644 --- a/src/plugins/e-acsl/tests/memory/oracle_ci/gen_local_goto.c +++ b/src/plugins/e-acsl/tests/memory/oracle_ci/gen_local_goto.c @@ -1,4 +1,5 @@ /* Generated by Frama-C */ +#include "stddef.h" #include "stdio.h" #include "stdlib.h" char *__gen_e_acsl_literal_string_2; diff --git a/src/plugins/e-acsl/tests/memory/oracle_ci/gen_mainargs.c b/src/plugins/e-acsl/tests/memory/oracle_ci/gen_mainargs.c index cd9cd223bf692bfcd3df82812937c41f4ebd306e..86e9de278227b7ff043988d6464c7b6120e761ae 100644 --- a/src/plugins/e-acsl/tests/memory/oracle_ci/gen_mainargs.c +++ b/src/plugins/e-acsl/tests/memory/oracle_ci/gen_mainargs.c @@ -1,4 +1,5 @@ /* Generated by Frama-C */ +#include "stddef.h" #include "stdio.h" #include "stdlib.h" #include "string.h" diff --git a/src/plugins/e-acsl/tests/memory/oracle_ci/gen_memalign.c b/src/plugins/e-acsl/tests/memory/oracle_ci/gen_memalign.c index 7e7362461bb1ec5e09bc5b26370fcda40ddbc4c3..f0a98343e01d91146897001094205c0d8d01a50e 100644 --- a/src/plugins/e-acsl/tests/memory/oracle_ci/gen_memalign.c +++ b/src/plugins/e-acsl/tests/memory/oracle_ci/gen_memalign.c @@ -1,6 +1,46 @@ /* Generated by Frama-C */ #include "stdio.h" #include "stdlib.h" +extern int __e_acsl_sound_verdict; + +/*@ requires valid_memptr: \valid(memptr); + requires + alignment_is_a_suitable_power_of_two: + alignment ≥ sizeof(void *) ∧ + ((size_t)alignment & ((size_t)alignment - 1)) ≡ 0; + assigns __fc_heap_status, \result; + assigns __fc_heap_status + \from (indirect: alignment), size, __fc_heap_status; + assigns \result + \from (indirect: alignment), (indirect: size), + (indirect: __fc_heap_status); + allocates *\old(memptr); + + behavior allocation: + assumes can_allocate: is_allocable(size); + ensures allocation: \fresh{Old, Here}(*\old(memptr),\old(size)); + ensures result_zero: \result ≡ 0; + assigns __fc_heap_status, \result; + assigns __fc_heap_status + \from (indirect: alignment), size, __fc_heap_status; + assigns \result + \from (indirect: alignment), (indirect: size), + (indirect: __fc_heap_status); + + behavior no_allocation: + assumes cannot_allocate: ¬is_allocable(size); + ensures result_non_zero: \result < 0 ∨ \result > 0; + assigns \result; + assigns \result \from (indirect: alignment); + allocates \nothing; + + complete behaviors no_allocation, allocation; + disjoint behaviors no_allocation, allocation; + */ +int __gen_e_acsl_posix_memalign(void **memptr, size_t alignment, size_t size); + +void *aligned_alloc(size_t alignment, size_t size); + int main(int argc, char const **argv) { int __retres; @@ -11,7 +51,8 @@ int main(int argc, char const **argv) __e_acsl_store_block((void *)(& memptr),(size_t)8); __e_acsl_full_init((void *)(& memptr)); int res2 = - posix_memalign((void **)memptr,(unsigned long)256,(unsigned long)15); + __gen_e_acsl_posix_memalign((void **)memptr,(unsigned long)256, + (unsigned long)15); /*@ assert Eva: initialization: \initialized(memptr); */ char *p = *memptr; __e_acsl_store_block((void *)(& p),(size_t)8); @@ -138,4 +179,82 @@ int main(int argc, char const **argv) return __retres; } +/*@ requires valid_memptr: \valid(memptr); + requires + alignment_is_a_suitable_power_of_two: + alignment ≥ sizeof(void *) ∧ + ((size_t)alignment & ((size_t)alignment - 1)) ≡ 0; + assigns __fc_heap_status, \result; + assigns __fc_heap_status + \from (indirect: alignment), size, __fc_heap_status; + assigns \result + \from (indirect: alignment), (indirect: size), + (indirect: __fc_heap_status); + allocates *\old(memptr); + + behavior allocation: + assumes can_allocate: is_allocable(size); + ensures allocation: \fresh{Old, Here}(*\old(memptr),\old(size)); + ensures result_zero: \result ≡ 0; + assigns __fc_heap_status, \result; + assigns __fc_heap_status + \from (indirect: alignment), size, __fc_heap_status; + assigns \result + \from (indirect: alignment), (indirect: size), + (indirect: __fc_heap_status); + + behavior no_allocation: + assumes cannot_allocate: ¬is_allocable(size); + ensures result_non_zero: \result < 0 ∨ \result > 0; + assigns \result; + assigns \result \from (indirect: alignment); + allocates \nothing; + + complete behaviors no_allocation, allocation; + disjoint behaviors no_allocation, allocation; + */ +int __gen_e_acsl_posix_memalign(void **memptr, size_t alignment, size_t size) +{ + int __retres; + { + int __gen_e_acsl_valid; + int __gen_e_acsl_and; + __e_acsl_store_block((void *)(& memptr),(size_t)8); + __gen_e_acsl_valid = __e_acsl_valid((void *)memptr,sizeof(void *), + (void *)memptr,(void *)(& memptr)); + __e_acsl_assert(__gen_e_acsl_valid,"Precondition","posix_memalign", + "\\valid(memptr)","FRAMAC_SHARE/libc/stdlib.h",666); + if (alignment >= 8UL) { + __e_acsl_mpz_t __gen_e_acsl_; + __e_acsl_mpz_t __gen_e_acsl__2; + __e_acsl_mpz_t __gen_e_acsl_sub; + __e_acsl_mpz_t __gen_e_acsl_band; + unsigned long __gen_e_acsl__3; + __gmpz_init_set_ui(__gen_e_acsl_,alignment); + __gmpz_init_set_si(__gen_e_acsl__2,1L); + __gmpz_init(__gen_e_acsl_sub); + __gmpz_sub(__gen_e_acsl_sub, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_), + (__e_acsl_mpz_struct const *)(__gen_e_acsl__2)); + __gmpz_init(__gen_e_acsl_band); + __gmpz_and(__gen_e_acsl_band, + (__e_acsl_mpz_struct const *)(__gen_e_acsl_), + (__e_acsl_mpz_struct const *)(__gen_e_acsl_sub)); + __gen_e_acsl__3 = __gmpz_get_ui((__e_acsl_mpz_struct const *)(__gen_e_acsl_band)); + __gen_e_acsl_and = __gen_e_acsl__3 == 0UL; + __gmpz_clear(__gen_e_acsl_); + __gmpz_clear(__gen_e_acsl__2); + __gmpz_clear(__gen_e_acsl_sub); + __gmpz_clear(__gen_e_acsl_band); + } + else __gen_e_acsl_and = 0; + __e_acsl_assert(__gen_e_acsl_and,"Precondition","posix_memalign", + "alignment >= sizeof(void *) &&\n((size_t)alignment & ((size_t)alignment - 1)) == 0", + "FRAMAC_SHARE/libc/stdlib.h",668); + } + __retres = posix_memalign(memptr,alignment,size); + __e_acsl_delete_block((void *)(& memptr)); + return __retres; +} + diff --git a/src/plugins/e-acsl/tests/memory/oracle_ci/gen_memsize.c b/src/plugins/e-acsl/tests/memory/oracle_ci/gen_memsize.c index 60fc5642381eca2bf0accc0e9093530f27e8f3c0..afc1d47904c708484482355ef4be0aaf6fcd4c2b 100644 --- a/src/plugins/e-acsl/tests/memory/oracle_ci/gen_memsize.c +++ b/src/plugins/e-acsl/tests/memory/oracle_ci/gen_memsize.c @@ -1,6 +1,8 @@ /* Generated by Frama-C */ #include "stdio.h" #include "stdlib.h" +extern size_t __e_acsl_heap_allocation_size; + int main(int argc, char **argv) { int __retres; diff --git a/src/plugins/e-acsl/tests/memory/oracle_ci/gen_stdout.c b/src/plugins/e-acsl/tests/memory/oracle_ci/gen_stdout.c index 49ec6cf163ec5ba0edfad147a4fa237e95bb0678..75bddabfbe9edd97d7bff4bac71129cf540e7428 100644 --- a/src/plugins/e-acsl/tests/memory/oracle_ci/gen_stdout.c +++ b/src/plugins/e-acsl/tests/memory/oracle_ci/gen_stdout.c @@ -1,4 +1,5 @@ /* Generated by Frama-C */ +#include "stddef.h" #include "stdio.h" #include "stdlib.h" void __e_acsl_globals_init(void) diff --git a/src/plugins/e-acsl/tests/memory/oracle_ci/memalign.res.oracle b/src/plugins/e-acsl/tests/memory/oracle_ci/memalign.res.oracle index 674eddac861dff410a723d4a8016853a5ce17ca5..bcfe10a812c72940b682369df2ac5f11caaaecba 100644 --- a/src/plugins/e-acsl/tests/memory/oracle_ci/memalign.res.oracle +++ b/src/plugins/e-acsl/tests/memory/oracle_ci/memalign.res.oracle @@ -1,4 +1,33 @@ [e-acsl] beginning translation. +[e-acsl] Warning: annotating undefined function `posix_memalign': + the generated program may miss memory instrumentation + if there are memory-related annotations. +[e-acsl] FRAMAC_SHARE/libc/stdlib.h:665: Warning: + E-ACSL construct `\fresh' is not yet supported. Ignoring annotation. +[e-acsl] tests/memory/memalign.c:38: Warning: + E-ACSL construct `complete behaviors' is not yet supported. + Ignoring annotation. +[e-acsl] tests/memory/memalign.c:38: Warning: + E-ACSL construct `disjoint behaviors' is not yet supported. + Ignoring annotation. +[e-acsl] FRAMAC_SHARE/libc/stdlib.h:669: Warning: + E-ACSL construct `assigns clause in behavior' is not yet supported. + Ignoring annotation. +[e-acsl] FRAMAC_SHARE/libc/stdlib.h:675: Warning: + E-ACSL construct `predicate performing read accesses' is not yet supported. + Ignoring annotation. +[e-acsl] FRAMAC_SHARE/libc/stdlib.h:675: Warning: + E-ACSL construct `assigns clause in behavior' is not yet supported. + Ignoring annotation. +[e-acsl] FRAMAC_SHARE/libc/stdlib.h:682: Warning: + E-ACSL construct `predicate performing read accesses' is not yet supported. + Ignoring annotation. [e-acsl] translation done in project "e-acsl". +[eva:alarm] FRAMAC_SHARE/libc/stdlib.h:668: Warning: + function __e_acsl_assert: precondition got status unknown. +[eva] FRAMAC_SHARE/libc/stdlib.h:665: Warning: + ignoring unsupported \allocates clause +[eva:alarm] FRAMAC_SHARE/libc/stdlib.h:679: Warning: + function __gen_e_acsl_posix_memalign, behavior allocation: postcondition 'allocation' got status unknown. [eva:alarm] tests/memory/memalign.c:14: Warning: accessing uninitialized left-value. assert \initialized(memptr); diff --git a/src/plugins/e-acsl/tests/special/e-acsl-valid.c b/src/plugins/e-acsl/tests/special/e-acsl-valid.c index 8cdb54fbcec93c33303e8fdebfa537406b57fb98..f84464d00758640dd8187876d1d0cd3bcefbcf50 100644 --- a/src/plugins/e-acsl/tests/special/e-acsl-valid.c +++ b/src/plugins/e-acsl/tests/special/e-acsl-valid.c @@ -1,6 +1,6 @@ /* run.config_ci, run.config_dev COMMENT: test option -e-acsl-no-valid - STDOPT: #"@MACHDEP@ @EACSL_PREPARE@ -eva -eva-verbose 0 -then -no-eva -e-acsl-no-valid" + STDOPT: #"@GLOBAL@ -eva -eva-verbose 0 -then -no-eva -e-acsl-no-valid" MACRO: ROOT_EACSL_GCC_FC_EXTRA_EXT -eva -eva-verbose 0 MACRO: ROOT_EACSL_GCC_OPTS_EXT --then --e-acsl-extra -e-acsl-no-valid */ diff --git a/src/plugins/e-acsl/tests/temporal/oracle_ci/gen_t_args.c b/src/plugins/e-acsl/tests/temporal/oracle_ci/gen_t_args.c index 666d954b82bcc4f6965b8b8b575a7f5f11c3a01e..3e24ef3f308c7f68f1fb6accf0ea4b6e604afe72 100644 --- a/src/plugins/e-acsl/tests/temporal/oracle_ci/gen_t_args.c +++ b/src/plugins/e-acsl/tests/temporal/oracle_ci/gen_t_args.c @@ -1,4 +1,5 @@ /* Generated by Frama-C */ +#include "stddef.h" #include "stdio.h" #include "stdlib.h" int main(int argc, char const **argv) diff --git a/src/plugins/e-acsl/tests/temporal/oracle_ci/gen_t_local_init.c b/src/plugins/e-acsl/tests/temporal/oracle_ci/gen_t_local_init.c index 9ccb27aa258ca9cd5f58e030f4523d3535aec27f..007456238249612053c6ccc263073ada9d9840ba 100644 --- a/src/plugins/e-acsl/tests/temporal/oracle_ci/gen_t_local_init.c +++ b/src/plugins/e-acsl/tests/temporal/oracle_ci/gen_t_local_init.c @@ -1,4 +1,5 @@ /* Generated by Frama-C */ +#include "stddef.h" #include "stdio.h" #include "stdlib.h" char *__gen_e_acsl_literal_string_4; diff --git a/src/plugins/e-acsl/tests/temporal/oracle_ci/gen_t_memcpy.c b/src/plugins/e-acsl/tests/temporal/oracle_ci/gen_t_memcpy.c index 0f86c7371d4c879dc64808a673a75ca10dbe1a7a..3b0511fa9b1b81d990817fb3c5eb74072f572a8d 100644 --- a/src/plugins/e-acsl/tests/temporal/oracle_ci/gen_t_memcpy.c +++ b/src/plugins/e-acsl/tests/temporal/oracle_ci/gen_t_memcpy.c @@ -1,4 +1,5 @@ /* Generated by Frama-C */ +#include "stddef.h" #include "stdio.h" #include "stdlib.h" #include "string.h" diff --git a/src/plugins/e-acsl/tests/test_config_ci.in b/src/plugins/e-acsl/tests/test_config_ci.in index 0a68607fc713abce8bf5ac09479dd477ff07e1dd..2c9748e535a049455c62e15f0f0ed3469bc5df95 100644 --- a/src/plugins/e-acsl/tests/test_config_ci.in +++ b/src/plugins/e-acsl/tests/test_config_ci.in @@ -1,7 +1,6 @@ MACRO: DEST @PTEST_RESULT@/gen_@PTEST_NAME@ MACRO: MACHDEP -machdep gcc_x86_64 -MACRO: EACSL_PREPARE -e-acsl-prepare -e-acsl-share ./share/e-acsl -MACRO: GLOBAL @MACHDEP@ -variadic-no-translation -verbose 0 +MACRO: GLOBAL @MACHDEP@ -remove-unused-specified-functions -variadic-no-translation -verbose 0 MACRO: EACSL -e-acsl -e-acsl-share ./share/e-acsl -e-acsl-verbose 1 MACRO: EVA -eva -eva-no-alloc-returns-null -eva-no-results -eva-no-print -eva-warn-key libc:unsupported-spec=inactive MACRO: EVENTUALLY -print -ocode @DEST@.c -load-script ./tests/print.cmxs