-
Julien Signoles authoredJulien Signoles authored
dup_functions.ml 13.57 KiB
(**************************************************************************)
(* *)
(* This file is part of Frama-C. *)
(* *)
(* Copyright (C) 2007-2017 *)
(* CEA (Commissariat à l'énergie atomique et aux énergies *)
(* alternatives) *)
(* *)
(* you can redistribute it and/or modify it under the terms of the GNU *)
(* Lesser General Public License as published by the Free Software *)
(* Foundation, version 2.1. *)
(* *)
(* It is distributed in the hope that it will be useful, *)
(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)
(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)
(* GNU Lesser General Public License for more details. *)
(* *)
(* See the GNU Lesser General Public License version 2.1 *)
(* for more details (enclosed in the file licenses/LGPLv2.1). *)
(* *)
(**************************************************************************)
open Cil_types
let dkey = Options.dkey_dup
(* ********************************************************************** *)
(* Environment *)
(* ********************************************************************** *)
let fct_tbl: unit Kernel_function.Hashtbl.t = Kernel_function.Hashtbl.create 7
let is_generated kf = Kernel_function.Hashtbl.mem fct_tbl kf
let actions = Queue.create ()
module Global: sig
val add_logic_info: logic_info -> unit
val mem_logic_info: logic_info -> bool
val reset: unit -> unit
end = struct
let tbl = Cil_datatype.Logic_info.Hashtbl.create 7
let add_logic_info x = Cil_datatype.Logic_info.Hashtbl.add tbl x ()
let mem_logic_info x = Cil_datatype.Logic_info.Hashtbl.mem tbl x
let reset () = Cil_datatype.Logic_info.Hashtbl.clear tbl
end
let reset () =
Kernel_function.Hashtbl.clear fct_tbl;
Global.reset ();
Queue.clear actions
(* ********************************************************************** *)
(* Duplicating property statuses *)
(* ********************************************************************** *)
let reemit = function
| Property.IPBehavior _ | Property.IPAxiom _ | Property.IPAxiomatic _
| Property.IPPredicate (Property.PKAssumes _, _, _, _) -> false
| _ -> true
let copy_ppt old_prj new_prj old_ppt new_ppt =
let module P = Property_status in
let selection = State_selection.of_list [ P.self; Emitter.self ] in
let emit s l =
Project.on ~selection new_prj
(fun s ->
let e = match l with [] -> assert false | e :: _ -> e in
let emitter = Emitter.Usable_emitter.get e.P.emitter in
match s with
| P.True | P.False_and_reachable | P.Dont_know ->
P.emit emitter ~hyps:e.P.properties new_ppt s
| P.False_if_reachable ->
(* in that case, the only hypothesis is "Reachable new_ppt" which must
be automatically added by the kernel *)
P.emit emitter ~hyps:[] new_ppt P.False_if_reachable)
s
in
let copy () =
match Project.on ~selection old_prj P.get old_ppt with
| P.Never_tried -> ()
| P.Best(s, l) -> emit s l
| P.Inconsistent i ->
emit P.True i.P.valid;
emit P.False_and_reachable i.P.invalid
in
if reemit old_ppt && not (Options.Valid.get ()) then Queue.add copy actions
(* ********************************************************************** *)
(* Duplicating functions *)
(* ********************************************************************** *)
let dup_spec_status old_prj kf new_kf old_spec new_spec =
let old_ppts = Property.ip_of_spec kf Kglobal ~active:[] old_spec in
let new_ppts = Property.ip_of_spec new_kf Kglobal ~active:[] new_spec in
List.iter2 (copy_ppt old_prj (Project.current ())) old_ppts new_ppts
let dup_funspec tbl bhv spec =
(* Options.feedback "DUP SPEC %a" Cil.d_funspec spec;*)
let o = object
inherit Cil.genericCilVisitor (Cil.copy_visit (Project.current ()))
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 *),
Cil.get_logic_info bhv)
else
Cil.JustCopy
method !vterm_offset _ =
Cil.DoChildrenPost
(function
(* no way to directly visit fieldinfo and model_info uses *)
| TField(fi, off) -> TField(Cil.get_fieldinfo bhv fi, off)
| TModel(mi, off) -> TModel(Cil.get_model_info bhv mi, off)
| off -> off)
method !vlogic_var_use orig_lvi =
match orig_lvi.lv_origin with
| None ->
Cil.JustCopy
| Some vi ->
try
let new_lvi =
Cil_datatype.Logic_var.Hashtbl.find already_visited orig_lvi
in
Cil.ChangeTo new_lvi
with Not_found ->
Cil.ChangeDoChildrenPost
({ orig_lvi with lv_id = orig_lvi.lv_id } (* force a copy *),
fun lvi ->
(* using [Cil.get_logic_var bhv lvi] is correct only because the
lv_id used to compare the lvi does not change between the
original one and this copy *)
try
let new_vi = Cil_datatype.Varinfo.Hashtbl.find tbl vi in
Cil_datatype.Logic_var.Hashtbl.add
already_visited orig_lvi lvi;
lvi.lv_id <- new_vi.vid;
lvi.lv_name <- new_vi.vname;
lvi.lv_origin <- Some new_vi;
new_vi.vlogic_var_assoc <- Some lvi;
lvi
with Not_found ->
assert vi.vglob;
Cil.get_logic_var bhv lvi)
method !videntified_term _ =
Cil.DoChildrenPost Logic_const.refresh_identified_term
method !videntified_predicate _ =
Cil.DoChildrenPost Logic_const.refresh_predicate
end in
Cil.visitCilFunspec o spec
let dup_fundec loc spec bhv kf vi new_vi =
new_vi.vdefined <- true;
let formals = Kernel_function.get_formals kf in
let mk_formal vi =
let name =
if vi.vname = "" then
(* unamed formal parameter: must generate a fresh name since a fundec
cannot have unnamed formals (see bts #2303). *)
Env.Varname.get ~scope:Env.Function
(Misc.mk_gen_name "unamed_formal")
else
vi.vname
in
Cil.copyVarinfo vi name
in
let new_formals = List.map mk_formal formals in
let res =
let ty = Kernel_function.get_return_type kf in
if Cil.isVoidType ty then None
else Some (Cil.makeVarinfo false false "__retres" ty)
in
let return =
Cil.mkStmt ~valid_sid:true
(Return(Extlib.opt_map (Cil.evar ~loc) res, loc))
in
let stmts =
[ Cil.mkStmtOneInstr ~valid_sid:true
(Call(Extlib.opt_map Cil.var res,
Cil.evar ~loc vi,
List.map (Cil.evar ~loc) new_formals,
loc));
return ]
in
let locals = match res with None -> [] | Some r -> [ r ] in
let body = Cil.mkBlock stmts in
body.blocals <- locals;
let tbl = Cil_datatype.Varinfo.Hashtbl.create 7 in
List.iter2 (Cil_datatype.Varinfo.Hashtbl.add tbl) formals new_formals;
let new_spec = dup_funspec tbl bhv spec in
{ svar = new_vi;
sformals = new_formals;
slocals = locals;
smaxid = List.length new_formals;
sbody = body;
smaxstmtid = None;
sallstmts = [];
sspec = new_spec }
let dup_global loc old_prj spec bhv 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 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
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;
dup_spec_status old_prj kf new_kf spec new_spec;
Options.feedback ~dkey ~level:2 "function %s" name;
GFun(fundec, loc), GFunDecl(new_spec, new_vi, loc)
(* ********************************************************************** *)
(* Visitor *)
(* ********************************************************************** *)
type position = Before_gmp | Gmp | After_gmp | Memory_model | Code
class dup_functions_visitor prj = object (self)
inherit Visitor.frama_c_copy prj
val fct_tbl = Cil_datatype.Varinfo.Hashtbl.create 7
val mutable before_memory_model = Before_gmp
val mutable new_definitions: global list = []
(* new definitions of the annotated functions which will contain the
translation of the E-ACSL constract *)
method private before_memory_model = match before_memory_model with
| Before_gmp | Gmp | After_gmp -> true
| Memory_model | Code -> false
method private insert_libc l =
match new_definitions with
| [] -> l
| _ :: _ ->
(* add the generated definitions of libc at the end of [l]. This way,
we are sure that they have access to all of it (in particular, the
memory model and GMP) *)
let res = l @ new_definitions in
new_definitions <- [];
res
method private next () =
match before_memory_model with
| Before_gmp -> ()
| Gmp -> before_memory_model <- After_gmp
| After_gmp -> ()
| Memory_model -> before_memory_model <- Code
| Code -> ()
method !vcode_annot a =
Cil.JustCopyPost
(fun a' ->
let get_ppt kf stmt a = Property.ip_of_code_annot kf stmt a in
let kf = Extlib.the self#current_kf in
let stmt = Extlib.the self#current_stmt in
List.iter2
(fun p p' -> copy_ppt (Project.current ()) prj p p')
(get_ppt kf stmt a)
(get_ppt
(Cil.get_kernel_function self#behavior kf)
(Cil.get_stmt self#behavior stmt)
a');
a')
method !vlogic_info_decl li =
Global.add_logic_info li;
Cil.JustCopy
method !vvrbl vi =
try
let new_vi = Cil_datatype.Varinfo.Hashtbl.find fct_tbl vi in
Cil.ChangeTo new_vi
with Not_found ->
Cil.JustCopy
method private is_unvariadic_function vi =
match Cil.unrollType vi.vtype with
| TFun(_, _, variadic, _) -> not variadic
| _ -> false
method !vglob_aux = function
| GVarDecl(vi, loc) | GFunDecl(_, vi, loc) | GFun({ svar = vi }, loc)
when self#is_unvariadic_function vi
&& not (Misc.is_library_loc loc)
&& not (Cil.is_builtin vi)
&& not (Cil_datatype.Varinfo.Hashtbl.mem fct_tbl vi)
&& not (Cil.is_empty_funspec
(Annotations.funspec ~populate:false
(Extlib.the self#current_kf)))
->
self#next ();
let name = Misc.mk_gen_name vi.vname in
let new_vi =
Project.on prj (Cil.makeGlobalVar name) vi.vtype
in
Cil_datatype.Varinfo.Hashtbl.add fct_tbl vi new_vi;
Cil.DoChildrenPost
(fun l -> match l with
| [ GVarDecl(vi, _) | GFunDecl(_, vi, _) | GFun({ svar = vi }, _) as g ]
->
(match g with
| GFunDecl _ ->
if not (Kernel_function.is_definition (Extlib.the self#current_kf))
&& vi.vname <> "malloc" && vi.vname <> "free"
then
Options.warning "@[annotating undefined function `%a':@ \
the generated program may miss memory instrumentation@ \
if there are memory-related annotations.@]"
Printer.pp_varinfo vi
| GFun _ -> ()
| _ -> assert false);
let tmp = vi.vname in
if tmp = Kernel.MainFunction.get () then begin
(* the new function becomes the new main: simply swap the name of both
functions *)
vi.vname <- new_vi.vname;
new_vi.vname <- tmp
end;
let kf =
try
Globals.Functions.get (Cil.get_original_varinfo self#behavior vi)
with Not_found ->
Options.fatal
"unknown function `%s' while trying to duplicate it"
vi.vname
in
let spec = Annotations.funspec ~populate:false kf in
let vi_bhv = Cil.get_varinfo self#behavior vi in
let new_g, new_decl =
Project.on prj
(dup_global loc (Project.current ()) spec self#behavior kf vi_bhv)
new_vi
in
(* postpone the introduction of the new function definition to the
end *)
new_definitions <- new_g :: new_definitions;
(* put the declaration before the original function in order to solve
issue with recursive functions *)
[ new_decl; g ]
| _ -> assert false)
| GVarDecl(_, loc) | GFunDecl(_, _, loc) | GFun(_, loc)
when Misc.is_library_loc loc ->
(match before_memory_model with
| Before_gmp -> before_memory_model <- Gmp
| Gmp | Memory_model -> ()
| After_gmp -> before_memory_model <- Memory_model
| Code -> () (* still processing the GMP and memory model headers,
but reading some libc code *));
Cil.JustCopy
| GVarDecl(vi, _) | GFunDecl(_, vi, _) | GFun({ svar = vi }, _)
when Cil.is_builtin vi ->
self#next ();
Cil.JustCopy
| _ ->
self#next ();
Cil.DoChildren
method !vfile _ =
Cil.DoChildrenPost
(fun f ->
match new_definitions with
| [] -> f
| _ :: _ ->
(* required by the few cases where there is no global tagged as
[Code] in the file. *)
f.globals <- self#insert_libc f.globals;
f)
initializer
Project.copy ~selection:(Parameter_state.get_selection ()) prj;
reset ()
end
let dup () =
Options.feedback ~level:2 "duplicating annotated functions";
let prj =
File.create_project_from_visitor
"e_acsl_dup_functions"
(new dup_functions_visitor)
in
Queue.iter (fun f -> f ()) actions;
prj
(*
Local Variables:
compile-command: "make"
End:
*)