From 1bf27ed935da383eb7dcf3975e78179248ad28bb Mon Sep 17 00:00:00 2001
From: Julien Signoles <julien.signoles@cea.fr>
Date: Tue, 7 Apr 2020 11:22:14 +0200
Subject: [PATCH] [e-acsl] adding the RTL AST

---
 src/plugins/e-acsl/Makefile.in                |   1 +
 .../e-acsl/src/analyses/mmodel_analysis.ml    |   2 +-
 .../e-acsl/src/code_generator/constructor.ml  |   7 +-
 .../e-acsl/src/code_generator/constructor.mli |   2 +-
 .../e-acsl/src/code_generator/injector.ml     |  12 +-
 src/plugins/e-acsl/src/libraries/functions.ml |   4 +-
 .../e-acsl/src/libraries/functions.mli        |  12 +-
 src/plugins/e-acsl/src/options.ml             |   4 +-
 src/plugins/e-acsl/src/options.mli            |   1 +
 .../e-acsl/src/project_initializer/rtl.ml     | 283 ++++++++++++++++++
 .../e-acsl/src/project_initializer/rtl.mli    |  48 +++
 11 files changed, 358 insertions(+), 18 deletions(-)
 create mode 100644 src/plugins/e-acsl/src/project_initializer/rtl.ml
 create mode 100644 src/plugins/e-acsl/src/project_initializer/rtl.mli

diff --git a/src/plugins/e-acsl/Makefile.in b/src/plugins/e-acsl/Makefile.in
index 936fc6f57fa..2eb4d2d1a7e 100644
--- a/src/plugins/e-acsl/Makefile.in
+++ b/src/plugins/e-acsl/Makefile.in
@@ -58,6 +58,7 @@ SRC_ANALYSES:=$(addprefix src/analyses/, $(SRC_ANALYSES))
 # project initializer
 SRC_PROJECT_INITIALIZER:= \
 	keep_status \
+	rtl \
 	prepare_ast
 SRC_PROJECT_INITIALIZER:=\
   $(addprefix src/project_initializer/, $(SRC_PROJECT_INITIALIZER))
diff --git a/src/plugins/e-acsl/src/analyses/mmodel_analysis.ml b/src/plugins/e-acsl/src/analyses/mmodel_analysis.ml
index 3b655683995..fb984d7073e 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
diff --git a/src/plugins/e-acsl/src/code_generator/constructor.ml b/src/plugins/e-acsl/src/code_generator/constructor.ml
index 4848a1f2e21..a38f36ce70e 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 73173483987..d21c71e53a4 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 a0357a168fd..c01f07f6269 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,7 +120,7 @@ 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
diff --git a/src/plugins/e-acsl/src/libraries/functions.ml b/src/plugins/e-acsl/src/libraries/functions.ml
index b939bcff401..37cf8f8319f 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 a99291b585f..4963870c285 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/options.ml b/src/plugins/e-acsl/src/options.ml
index da218069292..deadac84c81 100644
--- a/src/plugins/e-acsl/src/options.ml
+++ b/src/plugins/e-acsl/src/options.ml
@@ -163,7 +163,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"
diff --git a/src/plugins/e-acsl/src/options.mli b/src/plugins/e-acsl/src/options.mli
index 8e8114594f8..a3fc3697cbb 100644
--- a/src/plugins/e-acsl/src/options.mli
+++ b/src/plugins/e-acsl/src/options.mli
@@ -38,6 +38,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
 
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 00000000000..71e4991ff8b
--- /dev/null
+++ b/src/plugins/e-acsl/src/project_initializer/rtl.ml
@@ -0,0 +1,283 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  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 same settings as the user source code.
+          Modifying these settings purposely kills the current AST (if any) *)
+       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 =
+    let mem _g =
+      try
+        ignore (Globals.Types.find_type kind tname);
+        true
+      with Not_found ->
+        false
+    in
+    do_it mem 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 =
+        match Globals.Syntactic_search.find_in_scope vi.vorig_name tunit with
+        | None -> false
+        | Some _ -> true
+      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 mem _g =
+        try ignore (Globals.Functions.find_by_name vi.vname); true
+        with Not_found -> false
+      in
+      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 mem 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/rtl.mli b/src/plugins/e-acsl/src/project_initializer/rtl.mli
new file mode 100644
index 00000000000..ff7cf96526a
--- /dev/null
+++ b/src/plugins/e-acsl/src/project_initializer/rtl.mli
@@ -0,0 +1,48 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  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).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+(** This module links the E-ACSL's RTL to the user source code. *)
+
+val link: Project.t -> unit
+(** [link prj] links the RTL's AST contained in [prj] to the AST of the current
+    project. *)
+
+(** Tables that contain RTL's symbols. Useful to know whether some symbols is
+    part of the RTL. *)
+module Symbols: sig
+  open Cil_types
+
+  val mem_global: global -> bool
+  val mem_kf: kernel_function -> bool
+
+  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. *)
+
+end
+
+(*
+Local Variables:
+compile-command: "make -C ../../../../.."
+End:
+*)
-- 
GitLab