diff --git a/headers/header_spec.txt b/headers/header_spec.txt index 268a75ba7ac3a2a3e46b6db0999fdaab0ef45fd0..725a619c8beb36678f15ad26c8547dc70cdb929f 100644 --- a/headers/header_spec.txt +++ b/headers/header_spec.txt @@ -1782,8 +1782,6 @@ src/plugins/wp/Why3Provers.mli: CEA_WP src/plugins/wp/Wp.mli: .ignore src/plugins/wp/WpTac.ml: CEA_WP src/plugins/wp/WpTac.mli: CEA_WP -src/plugins/wp/calculus.ml: CEA_WP -src/plugins/wp/calculus.mli: CEA_WP src/plugins/wp/cfgAnnot.ml: CEA_WP src/plugins/wp/cfgAnnot.mli: CEA_WP src/plugins/wp/cfgInfos.ml: CEA_WP @@ -1798,8 +1796,6 @@ src/plugins/wp/cfgInit.ml: CEA_WP src/plugins/wp/cfgInit.mli: CEA_WP src/plugins/wp/cfgWP.ml: CEA_WP src/plugins/wp/cfgWP.mli: CEA_WP -src/plugins/wp/cil2cfg.ml: CEA_WP -src/plugins/wp/cil2cfg.mli: CEA_WP src/plugins/wp/clabels.ml: CEA_WP src/plugins/wp/clabels.mli: CEA_WP src/plugins/wp/configure.ac: CEA_WP @@ -1938,12 +1934,8 @@ src/plugins/wp/share/why3/frama_c_wp/qed.mlw: CEA_WP src/plugins/wp/share/why3/frama_c_wp/vlist.mlw: CEA_WP src/plugins/wp/share/why3/frama_c_wp/vset.mlw: CEA_WP src/plugins/wp/share/wp.driver: CEA_WP -src/plugins/wp/wpAnnot.ml: CEA_WP -src/plugins/wp/wpAnnot.mli: CEA_WP src/plugins/wp/wpContext.ml: CEA_WP src/plugins/wp/wpContext.mli: CEA_WP -src/plugins/wp/wpGenerator.ml: CEA_WP -src/plugins/wp/wpGenerator.mli: CEA_WP src/plugins/wp/wpPropId.ml: CEA_WP src/plugins/wp/wpPropId.mli: CEA_WP src/plugins/wp/wpReached.ml: CEA_WP @@ -1952,8 +1944,6 @@ src/plugins/wp/wpReport.ml: CEA_WP src/plugins/wp/wpReport.mli: CEA_WP src/plugins/wp/wpRTE.ml: CEA_WP src/plugins/wp/wpRTE.mli: CEA_WP -src/plugins/wp/wpStrategy.ml: CEA_WP -src/plugins/wp/wpStrategy.mli: CEA_WP src/plugins/wp/wpTarget.ml: CEA_WP src/plugins/wp/wpTarget.mli: CEA_WP src/plugins/wp/wp_error.ml: CEA_WP diff --git a/src/kernel_services/ast_data/globals.ml b/src/kernel_services/ast_data/globals.ml index 440ea2a0b01a171985775a675ee0be2b3015f255..d4c0e7a40739f78a3d16d0e9c599531f567de6d5 100644 --- a/src/kernel_services/ast_data/globals.ml +++ b/src/kernel_services/ast_data/globals.ml @@ -847,6 +847,13 @@ let set_entry_point name lib = Kernel.LibEntry.unsafe_set lib; end +let is_entry_point ?when_lib_entry kf = + match when_lib_entry with + | Some value when Kernel.LibEntry.get () -> value + | _ -> + try Cil_datatype.Kf.equal kf @@ fst @@ entry_point () + with No_such_entry_point _ -> false + (* ************************************************************************* *) (** {2 Global Comments} *) (* ************************************************************************* *) diff --git a/src/kernel_services/ast_data/globals.mli b/src/kernel_services/ast_data/globals.mli index d5a9e9faf8da86ccabce9e67ed20aa424d04a72c..a4379263ec9991891fc6fc1d144e32af6bfda490 100644 --- a/src/kernel_services/ast_data/globals.mli +++ b/src/kernel_services/ast_data/globals.mli @@ -293,6 +293,14 @@ val set_entry_point : string -> bool -> unit [Kernel.MainFunction] or [Kernel.LibEntry]. @plugin development guide *) +val is_entry_point : ?when_lib_entry:bool -> kernel_function -> bool +(** @return [true] iff the given kernel function is the entry point. + The optional parameter [when_lib_entry] overrides the result if we are + in -lib-entry mode. + + @since Frama-C+dev +*) + (* ************************************************************************* *) (** {2 Comments} *) (* ************************************************************************* *) diff --git a/src/kernel_services/ast_queries/cil.ml b/src/kernel_services/ast_queries/cil.ml index e40b51a88db810af67d5bbcdc17dbfe36bf3861d..6c0fad4721f7abdca0b103532ecd9763713e4618 100644 --- a/src/kernel_services/ast_queries/cil.ml +++ b/src/kernel_services/ast_queries/cil.ml @@ -3899,6 +3899,10 @@ and typeTermOffset basetyp = let isConstType typ_lval = typeHasAttributeMemoryBlock "const" typ_lval +let isGlobalInitConst vi = + (* Note: the type must be fully const, not a part of it *) + vi.vglob && vi.vstorage <> Extern && typeHasQualifier "const" vi.vtype + (**** Check for volatile attribute ****) let isVolatileType typ_lval = typeHasAttributeMemoryBlock "volatile" typ_lval diff --git a/src/kernel_services/ast_queries/cil.mli b/src/kernel_services/ast_queries/cil.mli index 73ca309ceeec5c8cad167b250cd8066755246508..bb03f8fb2cdf84315404d8dd357e75203c500abb 100644 --- a/src/kernel_services/ast_queries/cil.mli +++ b/src/kernel_services/ast_queries/cil.mli @@ -1303,6 +1303,14 @@ val isGhostFormalVarinfo: varinfo -> bool *) val isGhostFormalVarDecl: (string * typ * attributes) -> bool + +(** [true] iff the given variable is a const global variable with non extern + storage. + + @since Frama-C+dev +*) +val isGlobalInitConst: varinfo -> bool + (** Remove attributes whose name appears in the first argument that are present anywhere in the fully expanded version of the type. @since Oxygen-20120901 diff --git a/src/plugins/wp/Factory.ml b/src/plugins/wp/Factory.ml index 2661b7572e3ec5d2ce5155fbca313e9155c447ed..36fec2b95781966600bf4d0f6fe2fae24c306611 100644 --- a/src/plugins/wp/Factory.ml +++ b/src/plugins/wp/Factory.ml @@ -174,9 +174,9 @@ let is_ptr x = Cil.isPointerType x.Cil_types.vtype let is_fun_ptr x = Cil.isFunctionType x.Cil_types.vtype let is_formal_ptr x = x.Cil_types.vformal && is_ptr x let is_init kf x = - WpStrategy.is_main_init kf || + Globals.is_entry_point ~when_lib_entry:false kf || Wp_parameters.AliasInit.get () || - ( WpStrategy.isInitConst () && WpStrategy.isGlobalInitConst x ) + ( Wp_parameters.Init.get () && Cil.isGlobalInitConst x ) let refusage_param ~byref ~context x = let kf,init = match WpContext.get_scope () with diff --git a/src/plugins/wp/Generator.ml b/src/plugins/wp/Generator.ml index 425a2b4ca9bab6bf6e494abd77b7d5321d3c1a69..0cf4ea60e130dfeb02a125c0dfcf7c77fb246285 100644 --- a/src/plugins/wp/Generator.ml +++ b/src/plugins/wp/Generator.ml @@ -70,28 +70,14 @@ let user_setup () : Factory.setup = (* -------------------------------------------------------------------------- *) let create - ?dump ?legacy + ?dump ?(setup: Factory.setup option) ?(driver: Factory.driver option) () : Wpo.generator = let default f = function Some v -> v | None -> f () in let dump = default Wp_parameters.Dump.get dump in - let legacy = default Wp_parameters.Legacy.get legacy in let driver = default Driver.load_driver driver in let setup = default user_setup setup in - if legacy then - let cc = - if dump - then WpGenerator.dumper () - else WpGenerator.computer setup driver in - let the_model = cc#model in - object - method model = the_model - method compute_ip = WpGenerator.compute_ip cc - method compute_call = WpGenerator.compute_call cc - method compute_main = WpGenerator.compute_selection cc - end - else if dump then CfgGenerator.dumper setup driver else CfgGenerator.generator setup driver diff --git a/src/plugins/wp/Generator.mli b/src/plugins/wp/Generator.mli index aead35c64025fcf62bcfaf1a2fee33895da8d4b7..0ed7ad45a6c13c7328f5763a2b190b2adea8da71 100644 --- a/src/plugins/wp/Generator.mli +++ b/src/plugins/wp/Generator.mli @@ -29,7 +29,6 @@ val user_setup : unit -> Factory.setup val create : ?dump:bool -> - ?legacy:bool -> ?setup:Factory.setup -> ?driver:Factory.driver -> unit -> Wpo.generator diff --git a/src/plugins/wp/GuiNavigator.ml b/src/plugins/wp/GuiNavigator.ml index 890ac610af824bce32e788d3345a17bf97a7755b..49b85d6d68588a439e1069d0f4259f649f18e274 100644 --- a/src/plugins/wp/GuiNavigator.ml +++ b/src/plugins/wp/GuiNavigator.ml @@ -414,7 +414,7 @@ let model_varinfo : | PTermLval(Some kf, _, _, (TVar {lv_origin=Some x},TNoOffset)) when button=1 && RefUsage.is_computed () -> begin - let init = WpStrategy.is_main_init kf in + let init = Globals.is_entry_point ~when_lib_entry:false kf in let acc = RefUsage.get ~kf ~init x in let model = match acc with | RefUsage.NoAccess -> "any" diff --git a/src/plugins/wp/Makefile.in b/src/plugins/wp/Makefile.in index c45d0439131d37ea213d85f8ace3f15580456644..afc9ef27123cc365fa69e9ac81b39ccb9a6b8468 100644 --- a/src/plugins/wp/Makefile.in +++ b/src/plugins/wp/Makefile.in @@ -68,7 +68,7 @@ PLUGIN_CMO:= \ LogicUsage RefUsage \ Layout Region \ RegionAnnot RegionAccess RegionDump RegionAnalysis \ - cil2cfg normAtLabels wpPropId wpStrategy \ + normAtLabels wpPropId \ Lang Repr Matrix Passive Splitter \ LogicBuiltins Definitions \ Cmath Cint Cfloat Vset Vlist Cstring Cvalues \ @@ -82,7 +82,7 @@ PLUGIN_CMO:= \ Sigma MemLoader MemDebug \ MemEmpty MemZeroAlias MemVar \ MemMemory MemTyped MemRegion MemVal \ - wpReached wpRTE wpAnnot wpTarget \ + wpReached wpRTE wpTarget \ CfgCompiler StmtSemantics \ VCS script proof wpo wpReport \ Footprint Tactical Strategy \ @@ -99,8 +99,8 @@ PLUGIN_CMO:= \ driver prover ProverSearch ProverScript \ Factory \ cfgInit cfgAnnot cfgInfos cfgCalculus \ - calculus cfgDump cfgWP \ - wpGenerator cfgGenerator \ + cfgDump cfgWP \ + cfgGenerator \ Generator register VC PLUGIN_CMI:= \ diff --git a/src/plugins/wp/MemVar.ml b/src/plugins/wp/MemVar.ml index 092d9ea11fd3cfee16d9867de035470ccb25e26e..9d845883439b776bee5ede25aea258c3f81e3c0a 100644 --- a/src/plugins/wp/MemVar.ml +++ b/src/plugins/wp/MemVar.ml @@ -55,7 +55,8 @@ struct let hypotheses p = let kf,init = match WpContext.get_scope () with | WpContext.Global -> None,false - | WpContext.Kf f -> Some f, WpStrategy.is_main_init f in + | WpContext.Kf f -> + Some f, Globals.is_entry_point ~when_lib_entry:false f in let w = ref p in V.iter ?kf ~init (fun vi -> w := MemoryContext.set vi (V.param vi) !w) ; M.hypotheses !w diff --git a/src/plugins/wp/StmtSemantics.ml b/src/plugins/wp/StmtSemantics.ml index b068e462498a28251561510c8acd4ff0fe6a0b6e..83b9e66d8605a49169b2b8672953a2df13ea7234 100644 --- a/src/plugins/wp/StmtSemantics.ml +++ b/src/plugins/wp/StmtSemantics.ml @@ -666,7 +666,7 @@ struct let init ~is_pre_main env = let ninit = (env @: Clabels.init) in let sinit = Sigma.create () in - (** todo WpStrategy.is_main_init, need to test that seq.pre is the + (** todo Globals.is_entry_point kf, need to test that seq.pre is the start of the function *) (** todo warning *) let cfg_init = Globals.Vars.fold_in_file_order @@ -690,10 +690,10 @@ struct let sconst = Sigma.havoc_any ~call:false sinit in let havoc = Cfg.E.create {pre=sinit; post=sconst} Lang.F.p_true in let consts = - if WpStrategy.isInitConst () then + if Wp_parameters.Init.get () then Globals.Vars.fold_in_file_order (fun var _ cfg -> - if WpStrategy.isGlobalInitConst var + if Cil.isGlobalInitConst var then let h = (C.unchanged sconst sinit var) in let h = Cfg.P.create @@ -770,7 +770,8 @@ struct let posts = { pre = Cfg.node (); post = Cfg.node () } in let env = empty_env kf @* [Clabels.pre,pres.post;Clabels.post,posts.pre] in (* initialization *) - let init = init ~is_pre_main:(WpStrategy.is_main_init kf) + let init = + init ~is_pre_main:(Globals.is_entry_point ~when_lib_entry:false kf) (env @* [Clabels.here,pres.pre]) in (* pre-condition *) let pre = diff --git a/src/plugins/wp/calculus.ml b/src/plugins/wp/calculus.ml deleted file mode 100644 index 5cb2133bb68bc2fac5644b88672b91e7e740947e..0000000000000000000000000000000000000000 --- a/src/plugins/wp/calculus.ml +++ /dev/null @@ -1,683 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of WP plug-in of Frama-C. *) -(* *) -(* Copyright (C) 2007-2021 *) -(* CEA (Commissariat a l'energie atomique et aux energies *) -(* 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). *) -(* *) -(**************************************************************************) - -(** Wp computation using the CFG *) - -open Cil_types -open Cil_datatype - -module Cfg (W : Mcfg.S) = struct - - let dkey = Wp_parameters.register_category "calculus" (* Debugging key *) - let debug fmt = Wp_parameters.debug ~dkey fmt - - (** Before storing something at a program point, we have to process the label - * at that point. *) - let do_labels wenv e obj = - let do_lab s o l = W.label wenv s l o in - let obj = do_lab None obj Clabels.here in - let stmt = Cil2cfg.get_edge_stmt e in - let labels = Cil2cfg.get_edge_labels e in - List.fold_left (do_lab stmt) obj labels - - let add_hyp wenv obj h = - debug "add hyp %a@." WpPropId.pp_pred_info h; - W.add_hyp wenv h obj - - let add_goal wenv obj g = - debug "add goal %a@." WpPropId.pp_pred_info g; - W.add_goal wenv g obj - (*[LC] Adding scopes for loop invariant preservation: WHY ???? *) - (*[LC] Nevertheless, if required, this form should be used (BTS #1462) - - let open_scope wenv formals blocks = - List.fold_right - (fun b obj -> W.scope wenv b.blocals Mcfg.SC_Block_out obj) - blocks - (W.scope wenv formals Mcfg.SC_Function_out W.empty) - - - match WpPropId.is_loop_preservation (fst g) with - | None -> W.add_goal wenv g obj - | Some stmt -> - debug "add scope for loop preservation %a@." WpPropId.pp_pred_info g ; - let blocks = Kernel_function.find_all_enclosing_blocks stmt in - let kf = Kernel_function.find_englobing_kf stmt in - let formals = Kernel_function.get_formals kf in - W.merge wenv (W.add_goal wenv g (open_scope wenv formals blocks)) obj - *) - - let add_assigns_goal wenv obj g_assigns = match g_assigns with - | WpPropId.AssignsAny _ | WpPropId.NoAssignsInfo -> obj - | WpPropId.AssignsLocations a -> - debug "add assign goal (@[%a@])@." - WpPropId.pretty (WpPropId.assigns_info_id a); - W.add_assigns wenv a obj - - let add_assigns_hyp wenv obj h_assigns = match h_assigns with - | WpPropId.AssignsLocations (h_id, a) -> - let hid = Some h_id in - let obj = W.use_assigns wenv hid a obj in - Some a.WpPropId.a_label, obj - | WpPropId.AssignsAny a -> - Wp_parameters.warning ~current:true ~once:true - "Missing assigns clause (assigns 'everything' instead)" ; - let obj = W.use_assigns wenv None a obj in - Some a.WpPropId.a_label, obj - | WpPropId.NoAssignsInfo -> None, obj - - (** detect if the computation of the result at [edge] is possible, - * or if it will loop. If [strategy] are provide, - * cut are done on edges with cut properties, - * and if not, cut are done on loop node back edge if any. - * TODO: maybe this should be done while building the strategy ? - * *) - exception Stop of Cil2cfg.edge - let test_edge_loop_ok cfg strategy edge = - debug "[test_edge_loop_ok] (%s strategy) for %a" - (match strategy with None -> "without" | Some _ -> "with") - Cil2cfg.pp_edge edge; - let rec collect_edge_preds set e = - let cut = - match strategy with None -> Cil2cfg.is_back_edge e - | Some strategy -> - let e_annots = WpStrategy.get_annots strategy e in - (WpStrategy.get_cut e_annots <> []) - in - if cut then () (* normal loop cut *) - else if Cil2cfg.Eset.mem e set - then (* e is already in set : loop without cut ! *) - raise (Stop e) - else (* add e to set and continue with its preds *) - let set = Cil2cfg.Eset.add e set in - let preds = Cil2cfg.pred_e cfg (Cil2cfg.edge_src e) in - List.iter (collect_edge_preds set) preds - in - try - let _ = collect_edge_preds Cil2cfg.Eset.empty edge in - debug "[test_edge_loop_ok] ok."; - true - with Stop e -> - begin - debug "[test_edge_loop_ok] loop without cut detected at %a" - Cil2cfg.pp_edge e; - false - end - - (** to store the results of computations : - * we store a result for each edge, and also a list of proof obligations. - * - * Be careful that there are two modes of computation : - * the first one ([Pass1]) is used to prove the establishment of properties - * while the second (after [change_mode_if_needed]) prove the preservation. - * See {!R.set} for more details. - * *) - module R : sig - type t - val empty : Cil2cfg.t -> t - val is_pass1 : t -> bool - val change_mode_if_needed : t -> unit - val find : t -> Cil2cfg.edge -> W.t_prop - val set : WpStrategy.strategy -> W.t_env -> - t -> Cil2cfg.edge -> W.t_prop -> W.t_prop - val add_oblig : t -> Clabels.c_label -> W.t_prop -> unit - val add_memo : t -> Cil2cfg.edge -> W.t_prop -> unit - end = - struct - type t_mode = Pass1 | Pass2 - - module HE = Cil2cfg.HE (struct type t = W.t_prop option end) - - module LabObligs : sig - type t - val empty : t - val is_empty : t -> bool - val get_of_label : t -> Clabels.c_label -> W.t_prop list - val get_of_edge : t -> Cil2cfg.edge -> W.t_prop list - val add_to_label : t -> Clabels.c_label -> W.t_prop -> t - val add_to_edge : t -> Cil2cfg.edge -> W.t_prop -> t - end = struct - - type key = Olab of Clabels.c_label | Oedge of Cil2cfg.edge - - let cmp_key k1 k2 = match k1, k2 with - | Olab l1, Olab l2 when Clabels.equal l1 l2 -> true - | Oedge e1, Oedge e2 when Cil2cfg.same_edge e1 e2 -> true - | _ -> false - - (* TODOopt: could have a sorted list... *) - type t = (key * W.t_prop list) list - - let empty = [] - - let is_empty obligs = (obligs = []) - - let add obligs k obj = - let rec aux l_obligs = match l_obligs with - | [] -> (k, [obj])::[] - | (k', obligs)::tl when cmp_key k k' -> - (k, obj::obligs)::tl - | o::tl -> o::(aux tl) - in aux obligs - - let add_to_label obligs label obj = add obligs (Olab label) obj - - let add_to_edge obligs e obj = add obligs (Oedge e) obj - - let get obligs k = - let rec aux l_obligs = match l_obligs with - | [] -> [] - | (k', obligs)::_ when cmp_key k k' -> obligs - | _::tl -> aux tl - in aux obligs - - let get_of_label obligs label = get obligs (Olab label) - - let get_of_edge obligs e = get obligs (Oedge e) - - end - - type t = { - mutable mode : t_mode ; - cfg: Cil2cfg.t; - tbl : HE.t ; - mutable memo : LabObligs.t; - mutable obligs : LabObligs.t; - } - - let empty cfg = - debug "start computing (pass 1)@."; - { mode = Pass1; cfg = cfg; tbl = HE.create 97 ; - obligs = LabObligs.empty ; memo = LabObligs.empty ;} - - let is_pass1 res = (res.mode = Pass1) - - let add_oblig res label obj = - debug "add proof obligation at label %a =@. @[<hov2> %a@]@." - Clabels.pretty label W.pretty obj; - res.obligs <- LabObligs.add_to_label (res.obligs) label obj - - let add_memo res e obj = - debug "Memo goal for Pass2 at %a=@. @[<hov2> %a@]@." - Cil2cfg.pp_edge e W.pretty obj; - res.memo <- LabObligs.add_to_edge (res.memo) e obj - - let find res e = - let obj = HE.find res.tbl e in - match obj with None -> - Wp_parameters.warning "find edge annot twice (%a) ?" - Cil2cfg.pp_edge e; - raise Not_found - | Some obj -> - if (res.mode = Pass2) - && (List.length - (Cil2cfg.pred_e res.cfg (Cil2cfg.edge_src e)) < 2) then - begin - (* it should be used once only : can free it *) - HE.replace res.tbl e None; - debug "clear edge %a@." Cil2cfg.pp_edge e - end; - obj - - (** If needed, clear wp table to compute Pass2. - * If nothing has been stored in res.memo, there is nothing to do. *) - let change_mode_if_needed res = - if LabObligs.is_empty res.memo then () - else - begin - debug "change to Pass2 (clear wp table)@."; - begin try - let e_start = Cil2cfg.start_edge res.cfg in - let start_goal = find res e_start in - add_memo res e_start start_goal - with Not_found -> () - end; - HE.clear res.tbl; - (* move memo obligs of Pass1 to obligs for Pass2 *) - res.obligs <- res.memo; - res.memo <- LabObligs.empty; - res.mode <- Pass2 - end - - let collect_oblig wenv res e obj = - let labels = Cil2cfg.get_edge_labels e in - let add obj obligs = - List.fold_left (fun obj o -> W.merge wenv o obj) obj obligs - in - let obj = - try - debug "get proof obligation at edge %a@." Cil2cfg.pp_edge e; - let obligs = LabObligs.get_of_edge res.obligs e in - add obj obligs - with Not_found -> obj - in - let add_lab_oblig obj label = - try - debug "get proof obligation at label %a@." Clabels.pretty label; - let obligs = LabObligs.get_of_label res.obligs label in - add obj obligs - with Not_found -> obj - in - let obj = List.fold_left add_lab_oblig obj labels in - obj - - - (** We have found some assigns hypothesis in the strategy : - * it means that we skip the corresponding bloc, ie. we directly compute - * the result before the block : (forall assigns. P), - * and continue with empty. *) - let use_assigns wenv res obj h_assigns = - let lab, obj = add_assigns_hyp wenv obj h_assigns in - match lab with - | None -> obj - | Some label -> add_oblig res label obj; W.empty - - (** store the result p for the computation of the edge e. - * - * - In Compute mode : - if we have some hyps H about this edge, store H => p - if we have some goal G about this edge, store G /\ p - if we have annotation B to be used as both H and G, store B /\ B=>P - We also have to add H and G from HI (invariants computed in Pass1 mode) - So finally, we build : [ H => [ BG /\ (BH => (G /\ P)) ] ] - *) - let set strategy wenv res e obj = - try - match (HE.find res.tbl e) with - | None -> raise Not_found - | Some obj -> obj - (* cannot warn here because it can happen with CUT properties. - * We could check that obj is the same thing than the founded result *) - (* Wp_parameters.fatal "strange loop at %a ?" Cil2cfg.pp_edge e *) - with Not_found -> - begin - let e_annot = WpStrategy.get_annots strategy e in - let h_prop = WpStrategy.get_hyp_only e_annot in - let g_prop = WpStrategy.get_goal_only e_annot in - let bh_prop, bg_prop = WpStrategy.get_both_hyp_goals e_annot in - let h_assigns = WpStrategy.get_asgn_hyp e_annot in - let g_assigns = WpStrategy.get_asgn_goal e_annot in - (* get_cut is ignored : see get_wp_edge *) - let obj = collect_oblig wenv res e obj in - let is_loop_head = - match Cil2cfg.node_type (Cil2cfg.edge_src e) with - | Cil2cfg.Vloop (Some _, _) -> true - | _ -> false - in - let compute ~goal obj = - let local_add_goal obj g = - if goal then add_goal wenv obj g else obj - in - let obj = List.fold_left (local_add_goal) obj g_prop in - let obj = List.fold_left (add_hyp wenv) obj bh_prop in - let obj = - if goal then add_assigns_goal wenv obj g_assigns else obj - in - let obj = List.fold_left (local_add_goal) obj bg_prop in - let obj = List.fold_left (add_hyp wenv) obj h_prop in - obj - in - let obj = match res.mode with - | Pass1 -> compute ~goal:true obj - | Pass2 -> compute ~goal:false obj - in - let obj = do_labels wenv e obj in - let obj = - if is_loop_head then obj (* assigns used in [wp_loop] *) - else use_assigns wenv res obj h_assigns - in - debug "[set_wp_edge] %a@." Cil2cfg.pp_edge e; - debug " = @[<hov2> %a@]@." W.pretty obj; - Format.print_flush (); - HE.replace res.tbl e (Some obj); - find res e (* this should give back obj, but also do more things *) - end - - end (* module R *) - - - let use_loop_assigns strategy wenv e obj = - let e_annot = WpStrategy.get_annots strategy e in - let h_assigns = WpStrategy.get_asgn_hyp e_annot in - let label, obj = add_assigns_hyp wenv obj h_assigns in - match label with Some _ -> obj - | None -> assert false (* we should have assigns hyp for loops !*) - - (** Compute the result for edge [e] which goes to the loop node [nloop]. - * So [e] can be either a back_edge or a loop entry edge. - * Be very careful not to make an infinite loop by calling [get_loop_head]... - * *) - let wp_loop ((_, cfg, strategy, _, wenv)) nloop e get_loop_head = - let loop_with_quantif () = - if Cil2cfg.is_back_edge e then - (* Be careful not to use get_only_succ here (infinite loop) *) - (debug "[wp_loop] cut at back edge"; - W.empty) - else (* edge going into the loop from outside : quantify *) - begin - debug "[wp_loop] quantify"; - let obj = get_loop_head nloop in - let head = match Cil2cfg.succ_e cfg nloop with - | [h] -> h - | _ -> assert false (* already detected in [get_loop_head] *) - in use_loop_assigns strategy wenv head obj - end - in - (* - if WpStrategy.new_loop_computation strategy - && R.is_pass1 res - && loop_with_cut cfg strategy nloop - then - loop_with_cut_pass1 () - else (* old mode or no inv or pass2 *) - *) - match Cil2cfg.node_type nloop with - | Cil2cfg.Vloop (Some true, _) -> (* natural loop (has back edges) *) - loop_with_quantif () - | _ -> (* TODO : print info about the loop *) - Wp_error.unsupported - "non-natural loop without invariant property." - - type callenv = { - pre_annots : WpStrategy.t_annots ; - post_annots : WpStrategy.t_annots ; - exit_annots : WpStrategy.t_annots ; - } - - let callenv cfg strategy v = - let eb = match Cil2cfg.pred_e cfg v with e::_ -> e | _ -> assert false in - let en, ee = Cil2cfg.get_call_out_edges cfg v in - { - pre_annots = WpStrategy.get_annots strategy eb ; - post_annots = WpStrategy.get_annots strategy en ; - exit_annots = WpStrategy.get_annots strategy ee ; - } - - let wp_call_any wenv cenv ~p_post ~p_exit = - let obj = W.merge wenv p_post p_exit in - let call_asgn = WpStrategy.get_call_asgn cenv.post_annots None in - let lab, obj = add_assigns_hyp wenv obj call_asgn in - match lab with - | Some _ -> obj - | None -> assert false - - let add_call_post wenv annots kf obj = - List.fold_left (add_goal wenv) obj (WpStrategy.get_call_post annots kf) - - let wp_call_kf wenv cenv stmt lval kf args precond ~p_post ~p_exit = - let call_asgn = WpStrategy.get_call_asgn cenv.post_annots (Some kf) in - let assigns = match call_asgn with - | WpPropId.AssignsLocations (_, asgn_body) -> - asgn_body.WpPropId.a_assigns - | WpPropId.AssignsAny _ -> WritesAny - | WpPropId.NoAssignsInfo -> assert false (* see above *) - in - let p_post = add_call_post wenv cenv.post_annots kf p_post in - let p_exit = add_call_post wenv cenv.exit_annots kf p_exit in - let pre_hyp, pre_goals = WpStrategy.get_call_pre cenv.pre_annots kf in - let obj = W.call wenv stmt lval kf args - ~pre:(pre_hyp) - ~post:((WpStrategy.get_call_hyp cenv.post_annots kf)) - ~pexit:((WpStrategy.get_call_hyp cenv.exit_annots kf)) - ~assigns ~p_post ~p_exit in - if precond - then W.call_goal_precond wenv stmt kf args ~pre:(pre_goals) obj - else obj - - let wp_calls ((_, cfg, strategy, _, wenv)) res v stmt - lval call args p_post p_exit - = - debug "[wp_calls] %a@." Cil2cfg.pp_call_type call; - let cenv = callenv cfg strategy v in - match call with - | Cil2cfg.Static kf -> - let precond = - WpStrategy.is_default_behavior strategy && R.is_pass1 res - in - wp_call_kf wenv cenv stmt lval kf args precond ~p_post ~p_exit - | Cil2cfg.Dynamic fct -> - let bhv = WpStrategy.behavior_name_of_strategy strategy in - match Dyncall.get ?bhv stmt with - | None -> wp_call_any wenv cenv ~p_post ~p_exit - | Some (prop,calls) -> - let precond = R.is_pass1 res in - let do_call kf = - let wp = wp_call_kf wenv cenv stmt lval - kf args precond ~p_post ~p_exit in - kf , wp in - let pid = WpPropId.mk_property prop in - W.call_dynamic wenv stmt pid fct (List.map do_call calls) - - let wp_stmt wenv s obj = match s.skind with - | Return (r, _) -> W.return wenv s r obj - | Instr i -> - begin match i with - | Local_init (vi, AssignInit i, _) -> W.init wenv vi (Some i) obj - | Local_init (_, ConsInit _, _) -> assert false - | (Set (lv, e, _)) -> W.assign wenv s lv e obj - | (Asm _) -> - let asm = WpPropId.mk_asm_assigns_desc s in - W.use_assigns wenv None asm obj - | (Call _) -> assert false - | Skip _ | Code_annot _ -> obj - end - | Break _ | Continue _ | Goto _ -> obj - | Loop _-> (* this is not a real loop (exit before looping) - just ignore it ! *) obj - | If _ -> assert false - | Switch _-> assert false - | Block _-> assert false - | UnspecifiedSequence _-> assert false - | TryExcept _ | TryFinally _ | Throw _ | TryCatch _ -> assert false - - let wp_scope wenv vars scope obj = - debug "[wp_scope] %s : %a@." - (match scope with - | Mcfg.SC_Global -> "global" - | Mcfg.SC_Block_in -> "block in" - | Mcfg.SC_Block_out -> "block out" - | Mcfg.SC_Frame_in -> "frame in" - | Mcfg.SC_Frame_out -> "frame out" ) - (Pretty_utils.pp_list ~sep:", " Printer.pp_varinfo) vars; - match scope with - | Mcfg.(SC_Block_in | SC_Block_out) when vars = [] -> obj - | _ -> W.scope wenv vars scope obj - - (** @return the WP stored for edge [e]. Compute it if it is not already - * there and store it. Also handle the Acut annotations. *) - let rec get_wp_edge ((_kf, cfg, strategy, res, wenv) as env) e = - Db.yield (); - let v = Cil2cfg.edge_dst e in - debug "[get_wp_edge] get wp before %a@." Cil2cfg.pp_node v; - try - let res = R.find res e in - debug "[get_wp_edge] %a already computed@." Cil2cfg.pp_node v; - res - with Not_found -> - (* Notice that other hyp and goal are handled in R.set as usual *) - let cutp = - if R.is_pass1 res - then WpStrategy.get_cut (WpStrategy.get_annots strategy e) - else [] - in - match cutp with - | [] -> - let wp = compute_wp_edge env e in - R.set strategy wenv res e wp - | cutp -> - debug "[get_wp_edge] cut at node %a@." Cil2cfg.pp_node v; - let add_cut_goal (g,p) acc = - if g then add_goal wenv acc p else acc - in - let edge_annot = List.fold_right add_cut_goal cutp W.empty in - (* put cut goal properties as goals in e if any, else true *) - let edge_annot = R.set strategy wenv res e edge_annot in - let wp = compute_wp_edge env e in - let add_cut_hyp (_,p) acc = add_hyp wenv acc p in - let oblig = List.fold_right add_cut_hyp cutp wp in - (* TODO : we could add hyp to the oblig if we have some in strategy *) - let oblig = W.loop_step oblig in - if test_edge_loop_ok cfg None e - then R.add_memo res e oblig - else R.add_oblig res Clabels.pre (W.close wenv oblig); - edge_annot - - and get_only_succ env cfg v = match Cil2cfg.succ_e cfg v with - | [e'] -> get_wp_edge env e' - | ls -> - Wp_parameters.debug "CFG node %a has %d successors instead of 1@." - Cil2cfg.pp_node v (List.length ls); - Wp_error.unsupported "strange loop(s)." - - and compute_wp_edge ((kf, cfg, _annots, res, wenv) as env) e = - let v = Cil2cfg.edge_dst e in - debug "[compute_edge] before %a go...@." Cil2cfg.pp_node v; - let old_loc = Cil.CurrentLoc.get () in - let () = match Cil2cfg.node_stmt_opt v with - | Some s -> Cil.CurrentLoc.set (Stmt.loc s) - | None -> () - in - let formals = Kernel_function.get_formals kf in - let res = match Cil2cfg.node_type v with - | Cil2cfg.Vstart -> - Wp_parameters.debug "No CFG edge can lead to Vstart"; - Wp_error.unsupported "strange CFGs." - | Cil2cfg.VfctIn -> - let obj = get_only_succ env cfg v in - let obj = wp_scope wenv [] Mcfg.SC_Global obj in - obj - | Cil2cfg.VblkIn (Cil2cfg.Bfct, b) -> - let obj = get_only_succ env cfg v in - let obj = wp_scope wenv b.blocals Mcfg.SC_Block_in obj in - let obj = wp_scope wenv formals Mcfg.SC_Frame_in obj in - obj - | Cil2cfg.VblkOut (Cil2cfg.Bfct, b) -> - let obj = get_only_succ env cfg v in - let obj = wp_scope wenv b.blocals Mcfg.SC_Block_out obj in - obj - | Cil2cfg.VblkOut _ | Cil2cfg.VblkIn _ -> - get_only_succ env cfg v - | Cil2cfg.Vstmt s -> - let obj = get_only_succ env cfg v in - wp_stmt wenv s obj - | Cil2cfg.Vcall (stmt, lval, fct, args) -> - let en, ee = Cil2cfg.get_call_out_edges cfg v in - let objn = get_wp_edge env en in - let obje = get_wp_edge env ee in - wp_calls env res v stmt lval fct args objn obje - | Cil2cfg.Vtest (true, s, c) -> - let et, ef = Cil2cfg.get_test_edges cfg v in - let t_obj = get_wp_edge env et in - let f_obj = get_wp_edge env ef in - W.test wenv s c t_obj f_obj - | Cil2cfg.Vtest (false, _, _) -> - get_only_succ env cfg v - | Cil2cfg.Vswitch (s, e) -> - let cases, def_edge = Cil2cfg.get_switch_edges cfg v in - let cases_obj = List.map (fun (c,e) -> c, get_wp_edge env e) cases in - let def_obj = get_wp_edge env def_edge in - W.switch wenv s e cases_obj def_obj - | Cil2cfg.Vloop _ | Cil2cfg.Vloop2 _ -> - let get_loop_head = fun n -> get_only_succ env cfg n in - wp_loop env v e get_loop_head - | Cil2cfg.VfctOut | Cil2cfg.VfctErr -> - let obj = get_only_succ env cfg v (* exitpost / postcondition *) in - wp_scope wenv formals Mcfg.SC_Frame_out obj - | Cil2cfg.Vend -> - W.empty - (* LC : unused entry point... - let obj = W.empty in - wp_scope wenv formals Mcfg.SC_Function_after_POST obj - *) - in - let Cil2cfg.{ b_closed ; b_opened } = Cil2cfg.block_scope_for_edge cfg e in - let do_block sc res b = wp_scope wenv b.blocals sc res in - let res = List.fold_left (do_block Mcfg.SC_Block_in) res b_opened in - let res = List.fold_left (do_block Mcfg.SC_Block_out) res b_closed in - debug "[compute_edge] before %a done@." Cil2cfg.pp_node v; - Cil.CurrentLoc.set old_loc; - res - - module Init = CfgInit.Make(W) - - let get_weakest_precondition cfg ((kf, _g, strategy, res, wenv) as env) = - debug "[wp-cfg] start Pass1"; - Cil2cfg.iter_edges (fun e -> ignore (get_wp_edge env e)) cfg ; - debug "[wp-cfg] end of Pass1"; - R.change_mode_if_needed res; - (* Notice that [get_wp_edge] will start Pass2 if needed, - * but if not, it will only fetch Pass1 result. *) - let e_start = Cil2cfg.start_edge cfg in - let obj = get_wp_edge env e_start in - let obj = Init.process_global_init wenv kf obj in - let obj = match WpStrategy.strategy_kind strategy with - | WpStrategy.SKannots -> obj - | WpStrategy.SKfroms info -> - let pre = info.WpStrategy.get_pre () in - let pre = WpStrategy.get_hyp_only pre in - W.build_prop_of_from wenv pre obj - in - debug "before close: %a@." W.pretty obj; - W.close wenv obj - - let compute cfg strategy = - debug "[wp-cfg] start computing with the strategy for %a" - WpStrategy.pp_info_of_strategy strategy; - if WpStrategy.strategy_has_prop_goal strategy - || WpStrategy.strategy_has_asgn_goal strategy then - try - let kf = Cil2cfg.cfg_kf cfg in - if Cil2cfg.strange_loops cfg <> [] then - Wp_error.unsupported "non natural loop(s)" ; - let lvars = match WpStrategy.strategy_kind strategy with - | WpStrategy.SKfroms info -> info.WpStrategy.more_vars - | _ -> [] in - let wenv = W.new_env ~lvars kf in - let res = R.empty cfg in - let env = (kf, cfg, strategy, res, wenv) in - List.iter - (fun (pid,thm) -> W.add_axiom pid thm) - (WpStrategy.global_axioms strategy) ; - let goal = get_weakest_precondition cfg env in - debug "[get_weakest_precondition] %a@." W.pretty goal; - let pp_cfg_edges_annot res fmt e = - try W.pretty fmt (R.find res e) - with Not_found -> Format.fprintf fmt "<released>" - in - let annot_cfg = pp_cfg_edges_annot res in - debug "[wp-cfg] computing done."; - [goal] , annot_cfg - with Wp_error.Error (_, msg) -> - Wp_parameters.warning "@[calculus failed on strategy@ @[for %a@]@ \ - because@ %s (abort)@]" - WpStrategy.pp_info_of_strategy strategy - msg; - let annot_cfg fmt _e = Format.fprintf fmt "" in - [], annot_cfg - else - begin - debug "[wp-cfg] no goal in this strategy : ignore."; - let annot_cfg fmt _e = Format.fprintf fmt "" in - [], annot_cfg - end - -end diff --git a/src/plugins/wp/calculus.mli b/src/plugins/wp/calculus.mli deleted file mode 100644 index ad5bc86fb0c85bc7d9f2f2724139e99e3c6f94f0..0000000000000000000000000000000000000000 --- a/src/plugins/wp/calculus.mli +++ /dev/null @@ -1,33 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of WP plug-in of Frama-C. *) -(* *) -(* Copyright (C) 2007-2021 *) -(* CEA (Commissariat a l'energie atomique et aux energies *) -(* 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). *) -(* *) -(**************************************************************************) - -(** Generic WP calculus *) - -module Cfg(W : Mcfg.S) : -sig - - val compute : - Cil2cfg.t -> - WpStrategy.strategy -> - W.t_prop list * (Format.formatter -> Cil2cfg.edge -> unit) - -end diff --git a/src/plugins/wp/cfgAnnot.ml b/src/plugins/wp/cfgAnnot.ml index 930f3e439067597fe0408e5a2fd5c98396a940b2..e4663d5fb6710badde9749df7510bc44599f1bae 100644 --- a/src/plugins/wp/cfgAnnot.ml +++ b/src/plugins/wp/cfgAnnot.ml @@ -457,6 +457,17 @@ let get_code_assertions ?(smoking=false) kf stmt = (* --- Loop Invariants --- *) (* -------------------------------------------------------------------------- *) +let mk_variant_properties kf s ca v = + let vpos_id = WpPropId.mk_var_pos_id kf s ca in + let vdecr_id = WpPropId.mk_var_decr_id kf s ca in + let loc = v.term_loc in + let lcurr = Clabels.to_logic (Clabels.loop_current s) in + let vcurr = Logic_const.tat ~loc (v, lcurr) in + let zero = Cil.lzero ~loc () in + let vpos = Logic_const.prel ~loc (Rle, zero, vcurr) in + let vdecr = Logic_const.prel ~loc (Rlt, v, vcurr) in + (vpos_id, vpos), (vdecr_id, vdecr) + type loop_contract = { loop_terminates: predicate option; (* to be verified at loop entry *) @@ -515,7 +526,7 @@ module LoopContract = WpContext.StaticGenerator(CodeKey) } | AVariant(term, None) -> let vpos , vdec = - WpStrategy.mk_variant_properties kf stmt ca term in + mk_variant_properties kf stmt ca term in let intro_terminates (pid, v) = pid, let t = snd @@ get_terminates_hyp kf in diff --git a/src/plugins/wp/cfgAnnot.mli b/src/plugins/wp/cfgAnnot.mli index 294b030675af76c28dd493b4728c972334280dd6..4d246683c330953432587370ff67cf0f8025ddd1 100644 --- a/src/plugins/wp/cfgAnnot.mli +++ b/src/plugins/wp/cfgAnnot.mli @@ -95,6 +95,10 @@ type loop_contract = { val get_loop_contract : ?smoking:bool -> ?terminates:predicate -> kernel_function -> stmt -> loop_contract +val mk_variant_properties : + kernel_function -> stmt -> code_annotation -> term -> pred_info * pred_info + + (* -------------------------------------------------------------------------- *) (* --- Property Accessors : Call Contracts --- *) (* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/cfgCalculus.ml b/src/plugins/wp/cfgCalculus.ml index e867511bf0c67442688eab50df6a8127497d13e3..1d86603585d87124ecca7353fe0ff08ca1f6736f 100644 --- a/src/plugins/wp/cfgCalculus.ml +++ b/src/plugins/wp/cfgCalculus.ml @@ -396,7 +396,7 @@ struct let do_preconditions env ~formals (b : CfgAnnot.behavior) w = let kf = env.mode.kf in - let init = WpStrategy.is_main_init kf in + let init = Globals.is_entry_point ~when_lib_entry:false kf in let side_behaviors = if init || WpLog.PrecondWeakening.get () then [] else CfgAnnot.get_preconditions ~goal:false kf in diff --git a/src/plugins/wp/cfgInfos.ml b/src/plugins/wp/cfgInfos.ml index 839c3cf03d55e305fa4125b35e9a0559b1f47124..1ff5c4f7a9b5e692fb3b65ff1cc72244eb49ceea 100644 --- a/src/plugins/wp/cfgInfos.ml +++ b/src/plugins/wp/cfgInfos.ml @@ -303,7 +303,7 @@ let collect_loops_no_variant kf stmt = | _ -> Extlib.id in let props_of_v ca v = - let (d, _), (p, _) = WpStrategy.mk_variant_properties kf stmt ca v in + let (d, _), (p, _) = CfgAnnot.mk_variant_properties kf stmt ca v in Pset.union (Pset.singleton @@ WpPropId.property_of_id d) (Pset.singleton @@ WpPropId.property_of_id p) @@ -317,6 +317,26 @@ let collect_loops_no_variant kf stmt = | _ -> Sset.empty, Pset.empty +(* -------------------------------------------------------------------------- *) +(* --- Trivially terminates --- *) +(* -------------------------------------------------------------------------- *) + +let trivial_terminates = ref 0 + +let wp_trivially_terminates = + Emitter.create + "Trivial Termination" + [Emitter.Property_status] + ~correctness:[] (* TBC *) + ~tuning:[] (* TBC *) + +let set_trivially_terminates p hyps = + incr trivial_terminates ; + Wp_parameters.result "[CFG] Goal %a : Valid (Trivial)" WpPropId.pp_propid p ; + let pid = WpPropId.property_of_id p in + let hyps = Property.Set.elements hyps in + Property_status.emit wp_trivially_terminates ~hyps pid Property_status.True + (* -------------------------------------------------------------------------- *) (* --- Memoization Key --- *) (* -------------------------------------------------------------------------- *) @@ -404,7 +424,7 @@ let compile Key.{ kf ; smoking ; bhv ; prop } = } in let behaviors = Annotations.behaviors kf in (* Inits *) - if WpStrategy.is_main_init kf then + if Globals.is_entry_point ~when_lib_entry:false kf then infos.annots <- List.exists (selected_main_bhv ~bhv ~prop) behaviors ; (* Function Body *) Option.iter @@ -466,7 +486,7 @@ let compile Key.{ kf ; smoking ; bhv ; prop } = end body ; (* Doomed *) Bag.iter - (fun p -> if WpPropId.filter_status p then WpAnnot.set_unreachable p) + (fun p -> if WpPropId.filter_status p then WpReached.set_unreachable p) infos.doomed ; (* Termination *) let infos = @@ -498,7 +518,7 @@ let compile Key.{ kf ; smoking ; bhv ; prop } = end else if infos.calls = Fset.empty && infos.no_variant_loops = Sset.empty then begin - WpAnnot.set_trivially_terminates id infos.terminates_deps ; + set_trivially_terminates id infos.terminates_deps ; (* Drop dependencies for this terminates, we've used it. *) { infos with terminates_deps = Pset.empty } end diff --git a/src/plugins/wp/cfgInfos.mli b/src/plugins/wp/cfgInfos.mli index 45172d3653075c7ba3dd971dd286ab2022ac7bec..ba6256d0099a60679a4f083661cdab56fc8b4761 100644 --- a/src/plugins/wp/cfgInfos.mli +++ b/src/plugins/wp/cfgInfos.mli @@ -43,4 +43,6 @@ val terminates_deps : t -> Property.Set.t val is_recursive : Kernel_function.t -> bool val get_cluster : Kernel_function.t -> Kernel_function.Set.t option +val trivial_terminates : int ref + (**************************************************************************) diff --git a/src/plugins/wp/cfgInit.ml b/src/plugins/wp/cfgInit.ml index 32168850ea40fec1f7095a53a1e4594d5b9d1881..eba644dc7dd4d1fa48b39b5c04e5254444fc64ae 100644 --- a/src/plugins/wp/cfgInit.ml +++ b/src/plugins/wp/cfgInit.ml @@ -31,7 +31,7 @@ struct if var.vstorage = Extern then obj else let do_init = match filter with | `All -> true - | `InitConst -> WpStrategy.isGlobalInitConst var + | `InitConst -> Cil.isGlobalInitConst var in if not do_init then obj else let old_loc = Cil.CurrentLoc.get () in @@ -43,14 +43,14 @@ struct let process_global_const wenv obj = Globals.Vars.fold_in_file_order (fun var _initinfo obj -> - if WpStrategy.isGlobalInitConst var + if Cil.isGlobalInitConst var then W.const wenv var obj else obj ) obj (* WP of global initializations. *) let process_global_init wenv kf obj = - if WpStrategy.is_main_init kf then + if Globals.is_entry_point ~when_lib_entry:false kf then begin let obj = W.label wenv None Clabels.init obj in compute_global_init wenv `All obj @@ -58,14 +58,14 @@ struct else if W.has_init wenv then begin let obj = - if WpStrategy.isInitConst () + if Wp_parameters.Init.get () then process_global_const wenv obj else obj in let obj = W.use_assigns wenv None WpPropId.mk_init_assigns obj in let obj = W.label wenv None Clabels.init obj in compute_global_init wenv `All obj end else - if WpStrategy.isInitConst () + if Wp_parameters.Init.get () then compute_global_init wenv `InitConst obj else obj diff --git a/src/plugins/wp/cil2cfg.ml b/src/plugins/wp/cil2cfg.ml deleted file mode 100644 index 97e047cd573495d6d055cb161efa44aa7722bc58..0000000000000000000000000000000000000000 --- a/src/plugins/wp/cil2cfg.ml +++ /dev/null @@ -1,1453 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of WP plug-in of Frama-C. *) -(* *) -(* Copyright (C) 2007-2021 *) -(* CEA (Commissariat a l'energie atomique et aux energies *) -(* 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). *) -(* *) -(**************************************************************************) - -(** Build a CFG of a function keeping some information of the initial structure. - **) - -open Cil_types - -let dkey = Wp_parameters.register_category "cil2cfg" (* debugging key *) - -let debug fmt = Wp_parameters.debug ~dkey fmt -let debug2 fmt = Wp_parameters.debug ~dkey ~level:2 fmt - -(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*) -(** {2 Nodes} *) - -(** Be careful that only Bstmt are real Block statements *) -type block_type = - Bstmt of stmt | Bthen of stmt | Belse of stmt | Bloop of stmt | Bfct - (* added to identify 2 blocks for tests, else there are mixed up because same - * sid *) - -type call_type = - | Dynamic of exp - | Static of kernel_function - -let pp_call_type fmt = function - | Dynamic _ -> Format.pp_print_string fmt "dynamic" - | Static kf -> Kernel_function.pretty fmt kf - -type node_type = - | Vstart | Vend - | VfctIn | VfctOut | VfctErr - | VblkIn of block_type * block - | VblkOut of block_type * block - | Vstmt of stmt - | Vcall of stmt * lval option * call_type * exp list - | Vtest of bool * stmt * exp (** bool=true for In and false for Out *) - | Vswitch of stmt * exp - | Vloop of bool option * stmt - (** boolean is is_natural. None means the node has not been detected - * as a loop *) - | Vloop2 of bool * int - -type node_info = { kind : node_type ; mutable reachable : bool } - -type node = node_info - -let node_type n = n.kind - -let bkind_stmt bk = match bk with - | Bfct -> None - | Bstmt s | Bthen s | Belse s | Bloop s -> Some s - -let _bkind_sid bk = match bk with - | Bfct -> 0 - | Bstmt s | Bthen s | Belse s | Bloop s -> s.sid - -type node_id = int * int - -(** gives a identifier to each CFG node in order to hash them *) -let node_type_id t : node_id = match t with - | Vstart -> (0, 0) - | VfctIn -> (0, 1) - | VfctOut -> (0, 2) - | VfctErr -> (0, 3) - | Vend -> (0, 4) - | Vstmt s | Vtest (true, s, _) | Vswitch (s,_) | Vcall (s, _, _, _) -> - (1, s.sid) - | Vloop (_, s) -> (2, s.sid) - | Vloop2 (_, n) -> (3, n) - | VblkIn (Bfct, _) -> (4, 0) - | VblkIn (Bstmt s,_) -> (5, s.sid) - | VblkIn (Bthen s,_) -> (6, s.sid) - | VblkIn (Belse s,_) -> (7, s.sid) - | VblkIn (Bloop s,_) -> (8, s.sid) - | VblkOut (Bfct, _) -> (9, 0) - | VblkOut (Bstmt s,_) -> (10, s.sid) - | VblkOut (Bthen s,_) -> (11, s.sid) - | VblkOut (Belse s,_) -> (12, s.sid) - | VblkOut (Bloop s,_) -> (13, s.sid) - | Vtest (false, s, _) -> (14, s.sid) - -let node_id n = node_type_id (node_type n) - -let pp_bkind fmt bk = match bk with - | Bfct -> Format.fprintf fmt "fct" - | Bstmt s -> Format.fprintf fmt "stmt:%d" s.sid - | Bthen s -> Format.fprintf fmt "then:%d" s.sid - | Belse s -> Format.fprintf fmt "else:%d" s.sid - | Bloop s -> Format.fprintf fmt "loop:%d" s.sid - -let pp_node_type fmt n = match n with - | Vstart -> Format.fprintf fmt "<start>" - | VfctIn -> Format.fprintf fmt "<fctIn>" - | VfctOut -> Format.fprintf fmt "<fctOut>" - | VfctErr -> Format.fprintf fmt "<fctErr>" - | Vend -> Format.fprintf fmt "<end>" - | VblkIn (bk,_) -> Format.fprintf fmt "<blkIn-%a>" pp_bkind bk - | VblkOut (bk,_) -> Format.fprintf fmt "<blkOut-%a>" pp_bkind bk - | Vcall (s, _, _, _) -> Format.fprintf fmt "<callIn-%d>" s.sid - | Vstmt s -> Format.fprintf fmt "<stmt-%d>" s.sid - | Vtest (b, s, _) -> - Format.fprintf fmt "<test%s-%d>" (if b then "In" else "Out") s.sid - | Vswitch (s,_) -> Format.fprintf fmt "<switch-%d>" s.sid - | Vloop (_, s) -> Format.fprintf fmt "<loop-%d>" s.sid - | Vloop2 (_, n) -> Format.fprintf fmt "<loop-n%d>" n - -let same_node v v' = - (node_id v) = (node_id v') - -(** the CFG nodes *) -module VL = -struct - type t = node - - let hash v = let (a,b) = (node_id v) in b*17 + a - - let equal v v' = same_node v v' - - let compare v v' = Extlib.compare_basic (node_id v) (node_id v') - - let pretty fmt v = pp_node_type fmt (node_type v) -end - -let pp_node fmt v = VL.pretty fmt v - -let start_stmt_of_node v = match node_type v with - | Vstart | Vtest (false, _, _) | VblkOut _ - | VfctIn | VfctOut | VfctErr | Vend | Vloop2 _ -> None - | VblkIn (bk, _) -> bkind_stmt bk - | Vstmt s | Vtest (true, s, _) | Vloop (_, s) | Vswitch (s,_) - | Vcall (s, _, _, _) - -> Some s - -let node_stmt_opt v = match node_type v with - | Vstart | Vtest (false, _, _) - | VfctIn | VfctOut | VfctErr | Vend | Vloop2 _ -> None - | VblkIn (bk, _) | VblkOut (bk, _) -> bkind_stmt bk - | Vstmt s | Vtest (true, s, _) | Vloop (_, s) | Vswitch (s,_) - | Vcall (s, _, _, _) - -> Some s - -let node_stmt_exn v = - match node_stmt_opt v with None -> raise Not_found | Some s -> s - -(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*) -(** {2 Edge labels} *) - -type edge_type = - | Enone (** normal edge *) - | Ethen (** then branch : edge source is a Vtest *) - | Eelse (** else branch : edge source is a Vtest *) - | Eback (** back edge to a loop : the edge destination is a Vloop *) - | EbackThen (** Eback + Ethen *) - | EbackElse (** Eback + Eelse *) - | Ecase of (exp list) (** switch branch : edge source is a Vswitch. - Ecase [] for default case *) - | Enext (** not really a edge : gives the next node of a complex stmt *) - -(** the CFG edges *) -module EL = struct - - let compare_edge_type e1 e2 = - if e1 == e2 then 0 - else match e1, e2 with - | Enone, Enone | Ethen, Ethen | Eelse, Eelse | Eback, Eback - | EbackThen, EbackThen | EbackElse, EbackElse | Enext, Enext -> 0 - - | Ecase l1, Ecase l2 -> Extlib.list_compare Cil_datatype.Exp.compare l1 l2 - - | Enone, (Ethen | Eelse | Eback | EbackThen | EbackElse | Ecase _ | Enext) - | Ethen, (Eelse | Eback | EbackThen | EbackElse | Ecase _ | Enext) - | Eelse, (Eback | EbackThen | EbackElse | Ecase _ | Enext) - | Eback, (EbackThen | EbackElse | Ecase _ | Enext) - | EbackThen, (EbackElse | Ecase _ | Enext) - | EbackElse, (Ecase _ | Enext) - | Ecase _, Enext - -> -1 - - | Enext, (Ecase _ | EbackElse | EbackThen | Eback | Eelse | Ethen | Enone) - | Ecase _, (EbackElse | EbackThen | Eback | Eelse | Ethen | Enone) - | EbackElse, (EbackThen | Eback | Eelse | Ethen | Enone) - | EbackThen, (Eback | Eelse | Ethen | Enone) - | Eback, (Eelse | Ethen | Enone) - | Eelse, (Ethen | Enone) - | Ethen, Enone -> 1 - - type t = edge_type ref - - let compare (e1 : t) (e2 : t) = compare_edge_type !e1 !e2 - let default = ref Enone - let pretty fmt e = - let txt = match e with - | Enone -> "----" | Ethen -> "then" | Eelse -> "else" - | Eback -> "back" | EbackThen -> "then-back" | EbackElse -> "else-back" - | Ecase [] -> "default" - | Ecase l -> Format.asprintf "case(%a)" - (Pretty_utils.pp_list ~sep:", " Printer.pp_exp) l - | Enext -> "(next)" - in Format.fprintf fmt "%s" txt -end - -(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*) -(** {2 Graph} *) - -module PMAP(X: Graph.Sig.COMPARABLE) = struct - - module M = Map.Make(X) - type 'a t = 'a M.t ref - type key = X.t - type 'a return = unit - - let empty = () - (* never called and not visible for the user thanks to signature - constraints *) - - let create ?size () = ignore size ; ref M.empty - - let create_from h = ignore h ; ref M.empty - - let is_empty h = M.is_empty !h - - let clear h = h := M.empty - - let add k v h = h := M.add k v !h ; h - let remove k h = h := M.remove k !h ; h - let find k h = M.find k !h - let mem k h = M.mem k !h - - let find_and_raise k t s = try find k t with Not_found -> invalid_arg s - - let fold f h init = M.fold f !h init - - let map f h = - ref (M.fold (fun k v m -> let (k,v) = f k v in M.add k v m) !h M.empty) - - let iter f h = M.iter f !h - - let copy h = ref !h - -end - -(** the CFG is an ocamlgraph, but be careful to use it through the cfg function - * because some edges don't have the same meaning as some others... *) -module MyGraph = Graph.Blocks.Make(PMAP) -module CFG: - Graph.Sig.I - with type V.t = VL.t - and type V.label = VL.t - and type E.t = VL.t * EL.t * VL.t - and type E.label = EL.t -= -struct - include MyGraph.Digraph.ConcreteBidirectionalLabeled(VL)(EL) - let add_vertex g v = ignore (add_vertex g v) - let add_edge g v1 v2 = ignore (add_edge g v1 v2) - let remove_edge g v1 v2 = ignore (remove_edge g v1 v2) - let remove_edge_e g e = ignore (remove_edge_e g e) - let add_edge_e g e = ignore (add_edge_e g e) - let remove_vertex g v = - if HM.mem v g then begin - ignore (HM.remove v g); - let remove v = S.filter (fun (v2,_) -> not (V.equal v v2)) in - HM.iter (fun k (s1, s2) -> - ignore (HM.add k (remove v s1, remove v s2) g)) g - end -end - -(** Set of edges. *) -module Eset = Set.Make (CFG.E) - -(** Set of nodes. *) -module Nset = Set.Make (CFG.V) - -(** Hashtbl of node *) -module Ntbl = Hashtbl.Make (CFG.V) - -(** The final CFG is composed of the graph, but also : - * the function that it represents, - * an hashtable to find a CFG node knowing its hashcode *) -type t = { - kernel_function : kernel_function; - graph : CFG.t; - spec_only : bool; - stmt_node : ((int*int), CFG.V.t) Hashtbl.t; - unreachables : node_type list; - loop_nodes : (node list) option; - mutable loop_cpt : int; - mutable node_break : node option; - mutable node_continue : node option; -} - -let new_cfg_env spec_only kf = { - kernel_function = kf; - spec_only = spec_only ; - graph = CFG.create (); - stmt_node = Hashtbl.create 97; - unreachables = []; - loop_nodes = None; - loop_cpt = 0; - node_break = None; - node_continue = None; -} - -let cfg_kf cfg = cfg.kernel_function -let cfg_graph cfg = cfg.graph -let cfg_spec_only cfg = cfg.spec_only - -let unreachable_nodes cfg = cfg.unreachables - -(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*) -(** {2 CFG edges} *) - -type edge = CFG.E.t - -let edge_type e = !(CFG.E.label e) -let edge_src e = CFG.E.src e -let edge_dst e = CFG.E.dst e - -let pp_edge fmt e = - Format.fprintf fmt "%a -%a-> %a" - pp_node (CFG.E.src e) EL.pretty (edge_type e) pp_node (CFG.E.dst e) - -let is_back_edge e = match (edge_type e) with - | Eback | EbackThen | EbackElse -> true - | Enone | Ethen | Eelse | Ecase _ | Enext -> false - -let is_next_edge e = match (edge_type e) with - | Enext -> true - | Eback | EbackThen | EbackElse | Enone | Ethen | Eelse | Ecase _ -> false - -let pred_e cfg n = - try - let edges = CFG.pred_e cfg.graph n in - List.filter (fun e -> not (is_next_edge e)) edges - with Invalid_argument _ -> - (Wp_parameters.warning "[cfg.pred_e] pb with node %a" pp_node n; []) - -let succ_e cfg n = - try - let edges = CFG.succ_e cfg.graph n in - List.filter (fun e -> not (is_next_edge e)) edges - with Invalid_argument _ -> - (Wp_parameters.warning "[cfg.succ_e] pb with node %a" pp_node n; []) - -type edge_key = int * int * int * int - -let edge_key e : edge_key = - let a,b = node_id (edge_src e) in - let c,d = node_id (edge_dst e) in - a,b,c,d - -let same_edge e1 e2 = (edge_key e1 = edge_key e2) - -(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*) -(** {2 Iterators} ignoring the [Enext] edges *) - -let iter_nodes f cfg = CFG.iter_vertex f (cfg.graph) -let fold_nodes f cfg acc = CFG.fold_vertex f (cfg.graph) acc - -let iter_edges f cfg = - let f e = if is_next_edge e then () else f e in - CFG.iter_edges_e f (cfg.graph) - -let iter_succ f cfg n = - let f e = if is_next_edge e then () else f (CFG.E.dst e) - in try CFG.iter_succ_e f (cfg.graph) n - with Invalid_argument _ -> - (Wp_parameters.warning "[cfg.iter_succ] pb with node %a" pp_node n) - -let fold_succ f cfg n acc = - let f e acc = if is_next_edge e then acc else f (CFG.E.dst e) acc - in try CFG.fold_succ_e f (cfg.graph) n acc - with Invalid_argument _ -> - (Wp_parameters.warning "[cfg.fold_succ] pb with node %a" pp_node n; acc) - -let fold_pred f cfg n acc = - let f e acc = if is_next_edge e then acc else f (CFG.E.src e) acc in - try CFG.fold_pred_e f (cfg.graph) n acc - with Invalid_argument s -> - (Wp_parameters.warning "[cfg.fold_pred] pb with node %a: %s" pp_node n s; acc) - -let _iter_succ_e f cfg n = - let f e = if is_next_edge e then () else f e - in try CFG.iter_succ_e f (cfg.graph) n - with Invalid_argument _ -> - (Wp_parameters.warning "[cfg.iter_succ_e] pb with node %a" pp_node n) - -let iter_pred_e f cfg n = - let f e = if is_next_edge e then () else f e - in try CFG.iter_pred_e f (cfg.graph) n - with Invalid_argument _ -> - (Wp_parameters.warning "[cfg.iter_pred_e] pb with node %a" pp_node n) - -let fold_pred_e f cfg n acc = - let f e acc = if is_next_edge e then acc else f e acc - in try CFG.fold_pred_e f (cfg.graph) n acc - with Invalid_argument _ -> - (Wp_parameters.warning "[cfg.fold_pred_e] pb with node %a" pp_node n; acc) - -let fold_succ_e f cfg n acc = - let f e acc = if is_next_edge e then acc else f e acc - in try CFG.fold_succ_e f (cfg.graph) n acc - with Invalid_argument _ -> - (Wp_parameters.warning "[cfg.fold_succ_e] pb with node %a" pp_node n; acc) - - -(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*) -(** {2 Getting information} *) - -let cfg_start cfg = Hashtbl.find cfg.stmt_node (node_type_id Vstart) - -let start_edge cfg = match succ_e cfg (cfg_start cfg) with [e] -> e - | _ -> Wp_parameters.fatal "[cfg] should have exactly ONE starting edge !" - -exception Found of node -let _find_stmt_node cfg stmt = - let find n = match node_stmt_opt n with None -> () - | Some s -> if s.sid = stmt.sid then raise (Found n) - in - try (iter_nodes find cfg; raise Not_found) - with Found n -> n - -(** Get the edges going out a test node with the then branch first *) -let get_test_edges cfg v = - match succ_e cfg v with - | [e1; e2] -> - begin match (edge_type e1), (edge_type e2) with - | (Ethen|EbackThen), (Eelse|EbackElse) -> e1, e2 - | (Eelse|EbackElse), (Ethen|EbackThen) -> e2, e1 - | _, (Eelse|EbackElse) -> - Wp_parameters.fatal "[cfg] test node with invalid edges %a" - pp_edge e1 - | _, _ -> - Wp_parameters.fatal "[cfg] test node with invalid edges %a" - pp_edge e2 - end - | _ -> raise (Invalid_argument "[cfg:get_test_edges] not a test") - -let get_switch_edges cfg v = - match node_type v with - | Vswitch _ -> - begin - let get_case (cl, dl) e = match (edge_type e) with - | Ecase [] -> cl, e::dl - | Ecase c -> (c, e)::cl, dl - | _ -> Wp_parameters.fatal ("[cfg] switch node with invalid edges") - in match List.fold_left get_case ([],[]) (succ_e cfg v) with - | cl, [d] -> cl, d - | _ -> - Wp_parameters.fatal ("[cfg] switch node with several 'default' ?") - end - | _ -> raise (Invalid_argument "[cfg:get_switch_edges] not a switch") - -let get_call_out_edges cfg v = - let e1, e2 = match succ_e cfg v with - | [e1;e2] -> e1, e2 - | _ -> assert false - in - let en, ee = match node_type (edge_dst e1) , - node_type (edge_dst e2) with - | _, VfctErr -> e1, e2 - | VfctErr, _ -> e2, e1 - | _, _ -> assert false - in en, ee - -let get_edge_stmt e = - match node_type (edge_dst e) with - | Vstart | VfctIn | VfctOut | VfctErr -> None - | VblkIn (Bstmt s, _) | Vstmt s - | Vcall (s,_,_,_) | Vtest (true, s, _) | Vswitch (s,_) -> Some s - | Vloop (_,s) -> if is_back_edge e then None else Some s - | Vtest _ | VblkIn _ | VblkOut _ | Vend | Vloop2 _ -> None - -let get_edge_labels e = - let v_after = edge_dst e in - let l = match node_type v_after with - | Vstart -> assert false - | VfctIn -> [] - | VfctErr | VfctOut -> [Clabels.post] - | VblkIn (Bstmt s, _) - | Vcall (s,_,_,_) | Vstmt s | Vtest (true, s, _) | Vswitch (s,_) -> - [Clabels.stmt s] - | Vloop (_,s) -> - if is_back_edge e then [] - else [Clabels.stmt s] - | Vtest (false, _, _) | VblkIn _ | VblkOut _ | Vend -> [] - | Vloop2 _ -> [] - in - let v_before = edge_src e in - match node_type v_before with - | VfctIn -> Clabels.pre::l - | Vloop (_, s) -> (Clabels.loop_current s)::l - | _ -> l - -let next_edge cfg n = - let edges = match node_type n with - | VblkIn _ | Vswitch _ | Vtest _ | Vloop _ -> - let edges = CFG.succ_e cfg.graph n in - List.filter is_next_edge edges - | Vcall _ -> - let en, _ee = get_call_out_edges cfg n in [en] - | Vstmt _ -> - let edges = match CFG.succ_e cfg.graph n with - | (([] | _::[]) as edges) -> edges - | edges -> (* this case may happen in case of a loop - which is not really a loop : it is then a Vstmt, - and the Enext is not the succ_e. *) - List.filter is_next_edge edges - in edges - | _ -> - debug "[next_edge] not found for %a@." pp_node n; - raise Not_found (* No Enext information on this node *) - in - match edges with - | [] -> (* can append when nodes have been removed *) raise Not_found - | [e] -> e - | _ -> Wp_parameters.fatal "several (%d) Enext edges to node %a" - (List.length edges) pp_node n - -(** Find the node that follows the input node statement. - * The statement postcondition can then be stored to the edges before that node. - * @raise Not_found when the node after has been removed (unreachable) *) -let node_after cfg n = edge_dst (next_edge cfg n) - -let get_pre_edges cfg n = pred_e cfg n - -let get_post_edges cfg v = - try let v' = node_after cfg v in pred_e cfg v' - with Not_found -> [] - -let get_exit_edges cfg src = - debug "[get_exit_edges] of %a@." pp_node src; - let do_node n acc = - debug "[get_exit_edges] look at %a@." pp_node n; - let add_exit e acc = - let dst = edge_dst e in - match node_type dst with - | VfctErr -> - debug - "[get_exit_edges] add %a@." pp_edge e; - (* (succ_e cfg dst) @ acc *) - e :: acc - | _ -> acc - in match node_type n with - | Vstart -> (* In it is a problem a domination which is not solved here *) - Wp_parameters.warning "[cfg] Forget exits clause of node %a" pp_node src; - raise Exit - | _ -> fold_succ_e add_exit cfg n acc - in - let rec do_node_and_preds n (seen, edges as acc) = - if Nset.mem n seen then acc (* Don't loop over the same node. *) - else begin - let edges = do_node n edges in - if CFG.V.compare src n = 0 then (seen, edges) - else do_preds n (Nset.add n seen, edges) - end - and do_preds n acc = - fold_pred do_node_and_preds cfg n acc - in - let edges = - try - let edge = next_edge cfg src in - if is_next_edge edge then - (* needs to look at all node between the next node and the source *) - snd (do_preds (edge_dst edge) (Nset.empty, [])) - else do_node src [] - with Exit | Not_found -> [] - in - if edges = [] then - debug "[get_exit_edges] -> empty"; - edges - -let add_edges_before cfg src set e_after = - let rec add_preds set e = - let e_src = edge_src e in - if CFG.V.compare src e_src = 0 then set - else - let add_edge_and_preds e set = - if Eset.mem e set then set - else add_preds (Eset.add e set) e - in fold_pred_e add_edge_and_preds cfg e_src set - in add_preds set e_after - -let get_internal_edges cfg n = - let edges = try pred_e cfg (node_after cfg n) with Not_found -> [] in - let set = Eset.empty in - let set = List.fold_left (add_edges_before cfg n) set edges in - edges, set - -let rec get_edge_next_stmt cfg e = - let v_after = edge_dst e in - let get_next v = match succ_e cfg v with - | [e] -> get_edge_next_stmt cfg e - | [] | _ :: _ -> None (* nodes without statement should have one succ, - except the last one *) - in - match node_type v_after with - | VblkOut _ | VblkIn ((Bthen _|Belse _|Bloop _|Bfct),_) -> get_next v_after - | _ -> - match node_stmt_opt v_after with - | Some s -> Some s - | None -> get_next v_after - -let get_post_label cfg v = - match get_post_edges cfg v with - | [] -> None - | e::_ -> (* TODO: is this ok to consider only one edge ? *) - match get_edge_next_stmt cfg e with - | None -> None - | Some s -> Some (Clabels.stmt s) - -type block_scope = { b_opened : block list ; b_closed : block list } - -let no_scope = { b_opened = [] ; b_closed = [] } - -let block_scope s s' = - try - { - b_opened = Kernel_function.blocks_opened_by_edge s s' ; - b_closed = Kernel_function.blocks_closed_by_edge s s' ; - } - with Not_found | Invalid_argument _ -> - debug "[blocks_closed_by_edge] not found sid:%d -> sid:%d@." s.sid s'.sid; - no_scope - -let block_scope_for_edge cfg e = - debug "[blocks_closed_by_edge] for %a...@." pp_edge e; - let v_before = edge_src e in - match node_type v_before with - | Vstmt s | Vtest (true, s, _) | Vloop (_, s) | Vswitch (s,_) -> - begin match s.succs with - | [s'] -> block_scope s s' - | [] | _ :: _ -> - let s' = get_edge_next_stmt cfg e in - match s' with - | None -> no_scope - | Some s' -> block_scope s s' - end - | VblkIn(Bstmt _,b) -> { b_opened=[b] ; b_closed=[] } - | Vcall _ - | VblkIn _ | VblkOut _ | Vtest(false,_,_) - | VfctIn | VfctOut | VfctErr | Vstart | Vend | Vloop2 _ -> - no_scope - -let has_exit cfg = - try - let node = Hashtbl.find cfg.stmt_node (node_type_id VfctErr) in - match pred_e cfg node with - | [] -> false - | _ -> true - with Not_found | Invalid_argument _ -> false - -(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*) -(** {2 Generic table to store things on edges} *) - -module type HEsig = sig - type ti - type t - val create : int -> t - val find : t -> edge -> ti - val find_all : t -> edge -> ti list - val add : t -> edge -> ti -> unit - val replace : t -> edge -> ti -> unit - val remove : t -> edge -> unit - val clear : t -> unit -end - -module HE (I : sig type t end) = struct - type ti = I.t - type t = (edge_key, ti) Hashtbl.t - let create n = Hashtbl.create n - let find info e = Hashtbl.find info (edge_key e) - let find_all info e = Hashtbl.find_all info (edge_key e) - let add info e i = Hashtbl.add info (edge_key e) i - let replace info e i = Hashtbl.replace info (edge_key e) i - let remove info e = Hashtbl.remove info (edge_key e) - let clear info = Hashtbl.clear info -end - -(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*) -(** {2 Building the CFG} *) - -let add_node env t = - let id = node_type_id t in - let n = {kind = t ; reachable = false } in - debug "add node : %a@." VL.pretty n; - let n = CFG.V.create n in - Hashtbl.add env.stmt_node id n; - n - -let change_node_kind env n t = - let id = node_id n in - let id' = node_type_id t in - let n' = { n with kind = t } in - debug "change node kind from %a to %a" VL.pretty n VL.pretty n'; - let n' = CFG.V.create n' in - Hashtbl.remove env.stmt_node id; - Hashtbl.add env.stmt_node id' n'; - let preds = CFG.fold_pred_e (fun e acc -> e::acc) env.graph n [] in - let succs = CFG.fold_succ_e (fun e acc -> e::acc) env.graph n [] in - CFG.remove_vertex env.graph n; - List.iter - (fun e -> - let e' = CFG.E.create (CFG.E.src e) (CFG.E.label e) n' in - debug "replace edge %a %a %a" - VL.pretty (CFG.E.src e) EL.pretty !(CFG.E.label e) VL.pretty n'; - CFG.add_edge_e env.graph e') preds; - List.iter - (fun e -> - let e' = CFG.E.create n' (CFG.E.label e) (CFG.E.dst e) in - debug "replace edge %a %a %a" - VL.pretty n' EL.pretty !(CFG.E.label e) VL.pretty (CFG.E.dst e) ; - CFG.add_edge_e env.graph e') succs; - n' - -let add_edge env n1 edge_type n2 = - let e = CFG.E.create n1 (ref edge_type) n2 in - debug "add edge : %a@." pp_edge e; - CFG.add_edge_e env.graph e - -let remove_edge env e = - debug "remove edge : %a@." pp_edge e; - CFG.remove_edge_e env.graph e - -let insert_loop_node env loop_head loop_kind = - let n_loop = add_node env loop_kind in - let mv_pred_edge e = - add_edge env (edge_src e) (edge_type e) n_loop; - remove_edge env e - in iter_pred_e mv_pred_edge env loop_head; - add_edge env n_loop Enone loop_head; - n_loop - -let init_cfg spec_only kf = - let env = new_cfg_env spec_only kf in - let start = add_node env (Vstart) in - let fct_in = add_node env (VfctIn) in - let _ = add_edge env start Enone fct_in in - let fct_out = add_node env (VfctOut) in - let fct_err = add_node env (VfctErr) in - let nend = add_node env (Vend) in - let _ = add_edge env fct_out Enone nend in - let _ = add_edge env fct_err Enone nend in - env, fct_in, fct_out - -let get_node env t = - let id = node_type_id t in - debug "get_node: %a --> id:%d,%d" - pp_node_type t (fst id) (snd id); - try Hashtbl.find env.stmt_node id - with Not_found -> add_node env t - -(** Setup the preconditions at all the call points of [e_kf], when possible *) -let setup_preconditions_proxies e_kf = - match e_kf.enode with - | Lval (Var vkf, NoOffset) -> - let kf = Globals.Functions.get vkf in - Statuses_by_call.setup_all_preconditions_proxies kf - | _ -> () (* call through function pointer *) - -let get_call_type fct = - match Kernel_function.get_called fct with - | None -> Dynamic fct - | Some kf -> Static kf - -(** In some cases (goto for instance) we have to create a node before having - * processed if through [cfg_stmt]. It is important that the created node - * is the same than while the 'normal' processing ! That is why - * this pattern matching might seem redundant with the other one. *) -let get_stmt_node env s = - let do_call res fct args _loc = - get_node env (Vcall (s, res, get_call_type fct, args)) - in - match s.skind with - | Instr (Call (res, fct, args, loc)) -> do_call res fct args loc - | Instr (Local_init (v, ConsInit(f, args, kind), loc)) -> - Cil.treat_constructor_as_func do_call v f args kind loc - | Block b -> get_node env (VblkIn (Bstmt s,b)) - | UnspecifiedSequence seq -> - let b = Cil.block_from_unspecified_sequence seq in - get_node env (VblkIn (Bstmt s,b)) - | If (e, _, _, _) -> get_node env (Vtest (true, s, e)) - | Loop _ -> get_node env (Vloop (None, s)) - | Break _ | Continue _ | Goto _ - | Instr _ | Return _ -> get_node env (Vstmt s) - | Switch (e, _, _, _) -> get_node env (Vswitch (s, e)) - | TryExcept _ | TryFinally _ | Throw _ | TryCatch _ -> - Wp_parameters.not_yet_implemented - ~source:(fst (Cil_datatype.Stmt.loc s)) "[cfg] exception handling" - -let cfg_stmt_goto env s next = - let node = get_node env (Vstmt s) in - add_edge env node Enone next ; node - -let get_succ env s = - begin match s.succs with - | [s'] -> get_stmt_node env s' - | _ -> Wp_parameters.fatal "[cfg] jump with more than one successor ?" - end - -(** build the nodes for the [stmts], connect the last one with [next], - * and return the node of the first stmt. *) -let rec cfg_stmts env stmts next = match stmts with - | [] -> next - | [s] -> cfg_stmt env s next - | s::tl -> - let next = cfg_stmts env tl next in - let ns = cfg_stmt env s next in - ns - -and cfg_block env bkind b next = - let in_blk = get_node env (VblkIn (bkind, b)) in - let out_blk = get_node env (VblkOut (bkind, b)) in - let _ = add_edge env in_blk Enext out_blk in - let _ = add_edge env out_blk Enone next in - let first_in_blk = cfg_stmts env b.bstmts out_blk in - let _ = add_edge env in_blk Enone first_in_blk in - in_blk - -and cfg_switch env switch_stmt switch_exp blk case_stmts next = - let n_switch = get_node env (Vswitch (switch_stmt, switch_exp)) in - add_edge env n_switch Enext next; - begin - let s_break = env.node_break in - try - env.node_break <- Some next ; - let _first = cfg_stmts env blk.bstmts next in - env.node_break <- s_break ; - with e -> - env.node_break <- s_break ; - raise e - end ; - let branch with_def s = - let n = get_stmt_node env s in - let rec find_case l = match l with - | [] -> false, [] - | Case (e, _)::tl -> - let r = match find_case tl with - | true, [] -> true, [] - | true, _ -> assert false - | false, l -> false, e::l - in r - | Default _ :: _ -> - (* we don't check if we have several Default because it is impossible: - * CIL gives an error *) - true, [] - | _::tl -> find_case tl - in - let def, case = find_case s.labels in - if case = [] && not def then - Wp_parameters.fatal "[cfg] switch branch without label"; - add_edge env n_switch (Ecase case) n; - if def then true else with_def - in - let with_def = List.fold_left branch false case_stmts in - let _ = if not with_def then add_edge env n_switch (Ecase []) next in - n_switch - -and cfg_stmt env s next = - Db.yield (); - match s.skind with - | Instr (Call (_, f, _, _)) -> - setup_preconditions_proxies f; - let in_call = get_stmt_node env s in - add_edge env in_call Enone next; - let exit_node = get_node env VfctErr in - add_edge env in_call Enone exit_node; - in_call - | Instr (Local_init(_,ConsInit (f, _, _), _)) -> - let kf = Globals.Functions.get f in - Statuses_by_call.setup_all_preconditions_proxies kf; - let in_call = get_stmt_node env s in - add_edge env in_call Enone next; - let exit_node = get_node env VfctErr in - add_edge env in_call Enone exit_node; - in_call - | Instr _ | Return _ -> - let n = get_stmt_node env s in - add_edge env n Enone next; - n - | Block b -> - cfg_block env (Bstmt s) b next - | UnspecifiedSequence seq -> - let b = Cil.block_from_unspecified_sequence seq in - cfg_block env (Bstmt s) b next - | If (e, b1, b2, _) -> - begin - let n_in = get_stmt_node env s (*get_node env (Vtest (true, s, e))*) in - let n_out = get_node env (Vtest (false, s, e)) in - (* this node is to ensure that there is only one edge before - * the [next] node of a if to put post properties about the IF. *) - add_edge env n_out Enone next; - let in_b1 = cfg_block env (Bthen s) b1 n_out in - let in_b2 = cfg_block env (Belse s) b2 n_out in - add_edge env n_in Ethen in_b1; - add_edge env n_in Eelse in_b2; - add_edge env n_in Enext next; - n_in - end - | Loop(_, b, _, _, _) -> - let s_loop = get_stmt_node env s in - let b_loop = Bloop s in - let s_out = get_node env (VblkOut(b_loop,b)) in - let b_in = cfg_block_with - ~break:next - ~continue:s_out - env b_loop b s_loop in - add_edge env s_loop Enext next; - add_edge env s_loop Enone b_in; - s_loop - | Break _ -> - begin match env.node_break with - | None -> Wp_parameters.fatal "[cfg] missing break successor" - | Some brk -> cfg_stmt_goto env s brk - end - | Continue _ -> - begin match env.node_continue with - | None -> Wp_parameters.fatal "[cfg] missing continue successor" - | Some cnt -> cfg_stmt_goto env s cnt - end - | Goto _ -> cfg_stmt_goto env s (get_succ env s) - | Switch (e, b, lstmts, _) -> - cfg_switch env s e b lstmts next - | TryExcept _ | TryFinally _ | Throw _ | TryCatch _ -> - Wp_parameters.not_yet_implemented - ~source:(fst (Cil_datatype.Stmt.loc s)) "[cfg] exception handling" - -and cfg_block_with ?continue ?break env bkind block next = - let s_continue = env.node_continue in - let s_break = env.node_break in - try - if continue <> None then env.node_continue <- continue ; - if break <> None then env.node_break <- break ; - let head = cfg_block env bkind block next in - env.node_continue <- s_continue ; - env.node_break <- s_break ; - head - with e -> - env.node_continue <- s_continue ; - env.node_break <- s_break ; - raise e - -(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*) -(** {3 Cleaning} remove node and edges that are unreachable *) - -let clean_graph cfg = - let graph = cfg_graph cfg in - let rec reach n = - if n.reachable then () - else (n.reachable <- true; iter_succ reach cfg n) - in reach (cfg_start cfg); - let clean n acc = - if n.reachable then acc - else begin - debug "remove unreachable node %a@." VL.pretty n; - let v = node_type n in - CFG.remove_vertex graph n; - Hashtbl.remove cfg.stmt_node (node_type_id v); - v::acc - end - in - let unreach = fold_nodes clean cfg [] in - { cfg with unreachables = unreach } - - -(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*) -(** {3 About loops} - * Let's first remind some definitions about loops : - * - {b back edge} : edge n->h such that h dominates n. - * - {b natural loop} : defined by a back edge n->h - * * h is called the {b loop header}, - * * the body of the loop is the set of nodes n that are "between" h and n, - * ie all n predecessors until h. - * Because h dominates n, every backward path from n go through h. - * Notice that each node in the loop body is dominated by h. - * - * A loop is not a natural loop if it has several entries (no loop header), - * or if it has some irreducible region (no back edge). - * - * Below, we use an algorithm from the paper : - * "A New Algorithm for Identifying Loops in Decompilation" - * of Tao Wei, Jian Mao, Wei Zou, and Yu Chen, - * to gather information about the loops in the built CFG. -*) - -module type WeiMaoZouChenInput = sig - type graph - type node - type tenv - - (** build a new env from a graph, - * and also return the entry point of the graph which has to be unique. *) - val init : graph -> tenv * node - - (** apply the function on the node successors *) - val fold_succ : (tenv -> node -> tenv) -> tenv -> node -> tenv - - val eq_nodes : node -> node -> bool - - (** store the position for the node and also the fact that the node has - * been seen *) - val set_pos : tenv -> node -> int -> tenv - - (** reset the position (set the position to 0), but should keep the - * information that the node has been seen already. *) - val reset_pos : tenv -> node -> tenv - - (** get the previously stored position of the node or 0 if nothing has been - * stored *) - val get_pos : tenv -> node -> int - - (** get the previously stored position of the node if any, or None - * if [set_pos] hasn't been called already for this node. *) - val get_pos_if_traversed : tenv -> node -> int option - - (** [set_iloop_header env b h] store h as the innermost loop header for b. - * Beware that this function can be called several times for the same b - * with different values of h during the computation. Only the last one - * will give the correct information. - * *) - val set_iloop_header : tenv -> node -> node -> tenv - - (** get the node innermost loop header if any *) - val get_iloop_header : tenv -> node -> node option - - (** store the node as a loop header. *) - val add_loop_header : tenv -> node -> tenv - - (** store the node as an irreducible loop header. *) - val add_irreducible : tenv -> node -> tenv - - (** store the edge between the two nodes (n1, n2) as a reentry edge. - * n2 is the reentry point which means that it is in a loop, - * but it is not the loop header, and n1 is not in the loop. *) - val add_reentry_edge : tenv -> node -> node -> tenv - - (* val pretty_node : Format.formatter -> node -> unit *) -end - -(** Implementation of - * "A New Algorithm for Identifying Loops in Decompilation" *) -module WeiMaoZouChen (G : WeiMaoZouChenInput) : sig - val identify_loops : G.graph -> G.tenv -end = struct - - let tag_lhead env b h = - match h with - | None -> env - | Some h -> - if G.eq_nodes h b then (* already done *) env - else - let rec do_cur env cur_b cur_h = - match G.get_iloop_header env cur_b with - | None -> G.set_iloop_header env cur_b cur_h - | Some hb when G.eq_nodes hb cur_h -> (* nothing to do *) env - | Some hb -> - if (G.get_pos env hb) < (G.get_pos env cur_h) then - let env = G.set_iloop_header env cur_b cur_h in - do_cur env cur_h hb - else do_cur env hb cur_h - in do_cur env b h - - (** @return innermost loop header of b0 (None if b0 is not in a loop) *) - let rec trav_loops_DFS env b0 pos = - let env = G.set_pos env b0 pos in - let do_b env b = - match G.get_pos_if_traversed env b with - | None -> (* case A : b is not traversed already *) - let env, nh = trav_loops_DFS env b (pos + 1) in - tag_lhead env b0 nh - | Some b_pos when (b_pos > 0) -> - begin (* case B : b already in path -> it is a loop *) - let env = G.add_loop_header env b in - tag_lhead env b0 (Some b) - end - | Some 0 -> - begin - match G.get_iloop_header env b with - | None -> (* case C : do nothing *) env - | Some h when (G.get_pos env h > 0) -> - (* case D : b not in path, but h is *) - tag_lhead env b0 (Some h) - | Some h -> (* h not in path *) - begin (* case E : reentry *) - assert (G.get_pos env h = 0); - let env = G.add_irreducible env h in - let env = G.add_reentry_edge env b0 b in - let rec f env h = match G.get_iloop_header env h with - | Some h when (G.get_pos env h > 0) -> - tag_lhead env b0 (Some h) - | Some h -> - let env = G.add_irreducible env h in - f env h - | None -> env - in f env h - end - end - | _ -> assert false (* b_pos cannot be < 0 *) - in - let env = G.fold_succ do_b env b0 in - let env = G.reset_pos env b0 in - let h0 = G.get_iloop_header env b0 in - env, h0 - - let identify_loops g = - let env, start = G.init g in - let env, _ = trav_loops_DFS env start 1 in - env - -end - -(** To use WeiMaoZouChen algorithm, - * we need to define how to interact with our CFG graph *) -module LoopInfo = struct - type node = CFG.V.t - type graph = t - type tenv = { graph : t ; - dfsp : int Ntbl.t; - iloop_header : node Ntbl.t; - loop_headers : node list ; - irreducible : node list ; - unstruct_coef : int } - - let init cfg = - let env = { graph = cfg ; - dfsp = Ntbl.create 97; iloop_header = Ntbl.create 7; - loop_headers = []; irreducible = []; unstruct_coef = 0 } in - env, cfg_start cfg - - let eq_nodes = CFG.V.equal - - let set_pos env n pos = Ntbl.add env.dfsp n pos; env - let reset_pos env n = Ntbl.replace env.dfsp n 0; env - let get_pos env n = try Ntbl.find env.dfsp n with Not_found -> 0 - let get_pos_if_traversed env n = - try Some (Ntbl.find env.dfsp n) with Not_found -> None - - let set_iloop_header env b h = Ntbl.add env.iloop_header b h; env - let get_iloop_header env b = - try Some (Ntbl.find env.iloop_header b) with Not_found -> None - - let add_loop_header env h = { env with loop_headers = h :: env.loop_headers} - let add_irreducible env h = { env with irreducible = h :: env.irreducible} - let add_reentry_edge env _ _ = (* TODO *) env - - let is_irreducible env h = List.exists (eq_nodes h) env.irreducible - - let fold_succ f env n = fold_succ (fun v env -> f env v) env.graph n env - - let unstructuredness env = - let k = float_of_int env.unstruct_coef in - let k = k /. (float_of_int (CFG.nb_edges (cfg_graph env.graph))) in - let k = 1. +. k in - k - -end - -module Mloop = WeiMaoZouChen (LoopInfo) - -let set_back_edge e = - let info = CFG.E.label e in - match !info with - | Eback | EbackThen | EbackElse -> () - | Enone -> info := Eback - | Ethen -> info := EbackThen - | Eelse -> info := EbackElse - | Ecase _ | Enext -> assert false - -let mark_loops cfg = - let env = Mloop.identify_loops cfg in - let mark_loop_back_edge h = match node_stmt_opt h with - | None -> (* Because we use !Db.Dominators that work on statements, - we don't know how to detect back edge here. - TODO: compute dominators on our cfg ? *) false - | Some h_stmt -> - let mark_back_edge e = - let n = edge_src e in - let is_back_edge = - try - let n_stmt = node_stmt_exn n in - Dominators.dominates h_stmt n_stmt - with Not_found -> false (* pred of h is not a stmt *) - in - if is_back_edge then set_back_edge e; - debug "to loop edge %a@." pp_edge e - in iter_pred_e mark_back_edge cfg h; true - in - let mark_loop loops h = - debug "loop head in %a@." VL.pretty h; - let is_natural = - if (LoopInfo.is_irreducible env h) then - (debug "irreducible loop detected in %a@." VL.pretty h; false) - else true - in let back_edges_ok = - if is_natural then mark_loop_back_edge h else true - in - let loop = match node_type h with - | Vloop (_, h_stmt) -> - assert (back_edges_ok); - change_node_kind cfg h (Vloop (Some is_natural, h_stmt)) - | _ -> match node_stmt_opt h with - | Some h_stmt when back_edges_ok -> - insert_loop_node cfg h (Vloop (Some is_natural, h_stmt)) - | None when back_edges_ok -> - let n = cfg.loop_cpt in cfg.loop_cpt <- n + 1; - insert_loop_node cfg h (Vloop2 (is_natural, n)) - | _ -> (* consider it has non-natural. *) - let n = cfg.loop_cpt in cfg.loop_cpt <- n + 1; - insert_loop_node cfg h (Vloop2 (false, n)) - in loop::loops - in - let loops = List.fold_left mark_loop [] env.LoopInfo.loop_headers in - debug2 "unstructuredness coef = %f@." (LoopInfo.unstructuredness env); - { cfg with loop_nodes = Some loops } - -let loop_nodes cfg = match cfg.loop_nodes with Some l -> l - | None -> Wp_parameters.fatal - "Cannot use the loop nodes before having computed them" - -let strange_loops cfg = - let strange n = match node_type n with - | Vloop (Some is_natural, _) when is_natural -> false - | _ -> true - in let loops = loop_nodes cfg in - let strange_loops = List.filter strange loops in - debug "%d/%d strange loops" - (List.length strange_loops) (List.length loops); - strange_loops - -let very_strange_loops cfg = - let strange n = match node_type n with - | Vloop (Some _, _) | Vloop2 _ -> false - | _ -> true - in let loops = loop_nodes cfg in - let strange_loops = List.filter strange loops in - debug "%d/%d very strange loops" - (List.length strange_loops) (List.length loops); - strange_loops - -(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*) -(** {3 Create CFG} *) - -(* -Debugging code exporting the CFG in DOT: - -let pp_node_type_dot fmt = function - | Bstmt { sid } -> Format.fprintf fmt "s%d" sid - | Bthen { sid } -> Format.fprintf fmt "then_s%d" sid - | Belse { sid } -> Format.fprintf fmt "else_s%d" sid - | Bloop { sid } -> Format.fprintf fmt "loop_s%d" sid - | Bfct -> Format.fprintf fmt "fct" - -let pp_node_dot fmt { kind } = - match kind with - | Vstart -> Format.fprintf fmt "START" - | Vend -> Format.fprintf fmt "END" - | Vexit -> Format.fprintf fmt "EXIT" - | VfctIn -> Format.fprintf fmt "FIN" - | VfctOut -> Format.fprintf fmt "FOUT" - | VblkIn (b,_) -> Format.fprintf fmt "IN_%a" pp_node_type_dot b - | VblkOut (b,_) -> Format.fprintf fmt "OUT_%a" pp_node_type_dot b - | Vstmt { sid } -> Format.fprintf fmt "STMT_s%d" sid - | Vloop(_,{ sid }) -> Format.fprintf fmt "LOOP_s%d" sid - | Vloop2(_,k) -> Format.fprintf fmt "LOOP2_%d" k - | Vtest(true,{ sid },_) -> Format.fprintf fmt "TEST_IN_s%d" sid - | Vtest(false,{ sid },_) -> Format.fprintf fmt "TEST_OUT_s%d" sid - | Vswitch({ sid },_) -> Format.fprintf fmt "SWITCH_s%d" sid - | Vcall({ sid },_,_,_) -> Format.fprintf fmt "CALL_s%d" sid - -let pp_cfg_dot name { kernel_function = kf ; graph } = - Command.pp_to_file - (Printf.sprintf "%s-%s.dot" (Kernel_function.get_name kf) name) - begin fun fmt -> - Format.fprintf fmt "digraph %s {@\n" name ; - Format.fprintf fmt " rankdir = TB;@\n" ; - CFG.iter_edges_e - (fun (s,i,t) -> - match !i with - | Enext -> () - | Enone -> - Format.fprintf fmt " %a -> %a ;@\n" pp_node_dot s pp_node_dot t - | info -> - Format.fprintf fmt " %a -> %a [ label=\"%a\" ] ;@\n" - pp_node_dot s pp_node_dot t EL.pretty info - ) graph ; - Format.fprintf fmt "}@." ; - end -*) - -let cfg_from_definition kf f = - let kf_name = Kernel_function.get_name kf in - let cfg, fct_in, fct_out = init_cfg false kf in - let in_b = cfg_block cfg Bfct f.sbody fct_out in - let _ = add_edge cfg fct_in Enone in_b in - let graph = cfg_graph cfg in - debug "for function '%s': %d vertex - %d edges@." - kf_name (CFG.nb_edges graph) (CFG.nb_vertex graph); - debug - "start removing unreachable in %s@." kf_name; - Db.yield (); - let cfg = clean_graph cfg in - debug "for function '%s': %d vertex - %d edges@." - kf_name (CFG.nb_edges graph) (CFG.nb_vertex graph); - Db.yield (); - debug - "start loop analysis for %s@." kf_name; - let cfg = mark_loops cfg in - cfg - -let cfg_from_proto kf = - let cfg, fct_in, fct_out = init_cfg true kf in - let _ = add_edge cfg fct_in Enone fct_out in - let cfg = { cfg with loop_nodes = Some [] } in - cfg - -(* ------------------------------------------------------------------------ *) -(** {2 CFG management} *) - -let create kf = - let kf_name = Kernel_function.get_name kf in - debug "create cfg for function '%s'@." kf_name; - let cfg = - try - let f = Kernel_function.get_definition kf in - cfg_from_definition kf f - with Kernel_function.No_Definition -> - cfg_from_proto kf - in debug "done for %s@." kf_name; - Db.yield (); - cfg - -module KfCfg = - Kernel_function.Make_Table - (Datatype.Make - (struct - include Datatype.Undefined - type tt = t - type t = tt - let name = "WpCfg" - let mem_project = Datatype.never_any_project - let reprs = - List.map - (fun kf -> - { kernel_function = kf; - spec_only = true; - graph = CFG.create (); - stmt_node = Hashtbl.create 0; - unreachables = []; - loop_nodes = None; - loop_cpt = 0; - node_break = None; - node_continue = None; - } - ) - Kernel_function.reprs - let equal t1 t2 = - Kernel_function.equal t1.kernel_function t2.kernel_function - let hash t = Kernel_function.hash t.kernel_function - let compare t1 t2 = - Kernel_function.compare t1.kernel_function t2.kernel_function - end)) - (struct let name = "KfCfg" - let dependencies = [Ast.self] - let size = 17 - end) - -let get kf = KfCfg.memo create kf - -(* ------------------------------------------------------------------------ *) -(** {2 CFG dump} *) - -module Dump = -struct - type dot = { - name: string; - chan: out_channel; - fmt : Format.formatter; - } - - let create kf = - let name = Kernel_function.get_name kf in - let name = - Filename.concat (Wp_parameters.get_output () :> string) ("Cil2CFG_" ^ name) - in - let chan = open_out (name ^ ".dot") in - let fmt = Format.formatter_of_out_channel chan in - let out = { name ; chan ; fmt } in - Format.fprintf fmt "digraph %a {@\n" Kernel_function.pretty kf ; - Format.fprintf fmt " rankdir = TB ;@\n" ; - Format.fprintf fmt " node [ style = filled, shape = box ] ;@\n" ; - out - - let finalize out = - Format.fprintf out.fmt "}@." ; - close_out out.chan ; - let name = out.name in - ignore (Sys.command (Printf.sprintf "dot -Tpdf %s.dot > %s.pdf" name name)) - - let pp_id fmt (id, sid) = - Format.fprintf fmt "N%03d%03d" sid id - - let pp_content fmt = function - | None -> - Format.fprintf fmt "label=\"No statement\"" - | Some s -> - Format.fprintf fmt "label=\"wp:sid%d@\n%a\"" - s.sid Cil_printer.pp_stmt s - - let pp_node fmt n = - Format.fprintf fmt " %a [ %a ];@\n" - pp_id (node_id n) pp_content (node_stmt_opt n) - - let pp_edge fmt e = - Format.fprintf fmt - " %a -> %a [ label=\"%a\"];@\n" - pp_id (node_id (edge_src e)) - pp_id (node_id (edge_dst e)) - EL.pretty (edge_type e) - - let process_kf kf = - let cfg = (get kf).graph in - let dot = create kf in - CFG.iter_vertex (Format.fprintf dot.fmt "%a" pp_node) cfg ; - CFG.iter_edges_e (Format.fprintf dot.fmt "%a" pp_edge) cfg ; - finalize dot - - let process () = - Globals.Functions.iter process_kf - -end - -(* ------------------------------------------------------------------------ *) diff --git a/src/plugins/wp/cil2cfg.mli b/src/plugins/wp/cil2cfg.mli deleted file mode 100644 index 97ef821dd4005d62a566c673811065aa68da8edd..0000000000000000000000000000000000000000 --- a/src/plugins/wp/cil2cfg.mli +++ /dev/null @@ -1,184 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of WP plug-in of Frama-C. *) -(* *) -(* Copyright (C) 2007-2021 *) -(* CEA (Commissariat a l'energie atomique et aux energies *) -(* 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 Clabels - -(** abstract type of a cfg *) -type t - -(** @raise Log.FeatureRequest for non natural loops and 'exception' stmts. - * @return the graph and the list of unreachable nodes. - * *) -val get : Kernel_function.t -> t - -(** abstract type of the cfg nodes *) -type node -val pp_node : Format.formatter -> node -> unit -val same_node : node -> node -> bool - -(** abstract type of the cfg edges *) -type edge -val pp_edge : Format.formatter -> edge -> unit -val same_edge : edge -> edge -> bool - -(** get the starting edges *) -val start_edge : t -> edge - -(** set of edges *) -module Eset : Set.S with type elt = edge - -(** node and edges relations *) -val edge_src : edge -> node -val edge_dst : edge -> node -val pred_e : t -> node -> edge list -val succ_e : t -> node -> edge list - -(** iterators *) -val fold_nodes : (node -> 'a -> 'a) -> t -> 'a -> 'a -val iter_nodes : (node -> unit) -> t -> unit -val iter_edges : (edge -> unit) -> t -> unit - -(** Be careful that only Bstmt are real Block statements *) -type block_type = private - | Bstmt of stmt | Bthen of stmt | Belse of stmt | Bloop of stmt | Bfct - -type call_type = - | Dynamic of exp - | Static of kernel_function - -val pp_call_type : Format.formatter -> call_type -> unit -val get_call_type : exp -> call_type - -type node_type = private - | Vstart | Vend - | VfctIn | VfctOut | VfctErr - | VblkIn of block_type * block - | VblkOut of block_type * block - | Vstmt of stmt - | Vcall of stmt * lval option * call_type * exp list - | Vtest of bool * stmt * exp - | Vswitch of stmt * exp - | Vloop of bool option * stmt - (** boolean is is_natural. None means the node has not been - * detected as a loop. *) - | Vloop2 of bool * int - -val node_type : node -> node_type -val pp_node_type : Format.formatter -> node_type -> unit - -val node_stmt_opt : node -> stmt option -val start_stmt_of_node : node -> stmt option - -(** @return the nodes that are unreachable from the 'start' node. - * These nodes have been removed from the cfg already. *) -val unreachable_nodes : t -> node_type list - -(** similar to [succ_e g v] - * but tests the branch to return (then-edge, else-edge) - * @raise Invalid_argument if the node is not a test. - * *) -val get_test_edges : t -> node -> edge * edge - -(** similar to [succ_e g v] - but give the switch cases and the default edge *) -val get_switch_edges : t -> node -> (exp list * edge) list * edge - -(** similar to [succ_e g v] - but gives the edge to VcallOut first and the edge to Vexit second. *) -val get_call_out_edges : t -> node -> edge * edge - -type block_scope = { b_opened : block list ; b_closed : block list } - -val block_scope_for_edge : t -> edge -> block_scope - -val is_back_edge : edge -> bool - -(** detect is there are non natural loops or natural loops where we didn't - * manage to compute back edges (see [mark_loops]). Must be empty in the mode - * [-wp-no-invariants]. (see also [very_strange_loops]) *) -val strange_loops : t -> node list - -(** detect is there are natural loops where we didn't manage to compute - * back edges (see [mark_loops]). At the moment, we are not able to handle those - * loops. *) -val very_strange_loops : t -> node list - -(** @return the (normalized) labels at the program point of the edge. *) -val get_edge_labels : edge -> Clabels.c_label list - -(** Complete get_edge_labels and returns the associated stmt, if any. *) -val get_edge_stmt : edge -> stmt option - -(** @return None when the edge leads to the end of the function. *) -val get_edge_next_stmt : t -> edge -> stmt option - -(** whether an exit edge exists or not *) -val has_exit : t -> bool - -(** Find the edges where the precondition of the node statement have to be - * checked. *) -val get_pre_edges : t -> node -> edge list - -(** Find the edges where the postconditions of the node statement have to be - * checked. *) -val get_post_edges : t -> node -> edge list - -(** Get the label to be used for the Post state of the node contract if any. *) -val get_post_label : t -> node -> c_label option - -(** Find the edges [e] that goes to the [Vexit] node inside the statement - * beginning at node [n] *) -val get_exit_edges : t -> node -> edge list - -(** Find the edges [e] of the statement node [n] postcondition - * and the set of edges that are inside the statement ([e] excluded). - * For instance, for a single statement node, [e] is [succ_e n], - * and the set is empty. For a test node, [e] are the last edges of the 2 - * branches, and the set contains all the edges between [n] and the [e] edges. - * *) -val get_internal_edges : t -> node -> edge list * Eset.t - -val cfg_kf : t -> Kernel_function.t -val cfg_spec_only : t -> bool -(** returns [true] is this CFG is degenerated (no code available) *) - -(** signature of a mapping table from cfg edges to some information. *) -module type HEsig = -sig - type ti - type t - val create : int -> t - val find : t -> edge -> ti - val find_all : t -> edge -> ti list - val add : t -> edge -> ti -> unit - val replace : t -> edge -> ti -> unit - val remove : t -> edge -> unit - val clear : t -> unit -end - -module HE (I : sig type t end) : HEsig with type ti = I.t - -module Dump : -sig - val process: unit -> unit -end diff --git a/src/plugins/wp/register.ml b/src/plugins/wp/register.ml index cd673ad5553ef608a18959dc17135fe8626d4443..a141dbb8301552cde45306ca2832a0b675654a17 100644 --- a/src/plugins/wp/register.ml +++ b/src/plugins/wp/register.ml @@ -157,9 +157,9 @@ let clear_scheduled () = exercised := 0 ; session := GOALS.empty ; provers := PM.empty ; - WpAnnot.trivial_terminates := 0 ; - WpAnnot.unreachable_proved := 0 ; - WpAnnot.unreachable_failed := 0 ; + CfgInfos.trivial_terminates := 0 ; + WpReached.unreachable_proved := 0 ; + WpReached.unreachable_failed := 0 ; end let get_pstat p = @@ -439,13 +439,13 @@ let do_report_scheduled () = else let total = !scheduled + - !WpAnnot.unreachable_failed + - !WpAnnot.unreachable_proved + - !WpAnnot.trivial_terminates in + !WpReached.unreachable_failed + + !WpReached.unreachable_proved + + !CfgInfos.trivial_terminates in if total > 0 then begin let passed = - !WpAnnot.unreachable_proved + !WpAnnot.trivial_terminates + !WpReached.unreachable_proved + !CfgInfos.trivial_terminates in let passed = GOALS.fold (fun g n -> diff --git a/src/plugins/wp/wpAnnot.ml b/src/plugins/wp/wpAnnot.ml deleted file mode 100644 index 7a0dc8f397154c76cfcfc13f8aa029173ab82de8..0000000000000000000000000000000000000000 --- a/src/plugins/wp/wpAnnot.ml +++ /dev/null @@ -1,1425 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of WP plug-in of Frama-C. *) -(* *) -(* Copyright (C) 2007-2021 *) -(* CEA (Commissariat a l'energie atomique et aux energies *) -(* 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). *) -(* *) -(**************************************************************************) - -let dkey = Wp_parameters.register_category "annot" (* debugging key *) -let debug fmt = Wp_parameters.debug ~dkey fmt - -(* This file groups functions that extract some annotations - * and associates them with CFG edges. *) - -open Cil_types -open Cil_datatype -open Logic_utils - -(* -------------------------------------------------------------------------- *) -(* --- Status of Unreachable Annotations --- *) -(* -------------------------------------------------------------------------- *) - -let unreachable_proved = ref 0 -let unreachable_failed = ref 0 - -let wp_unreachable = - Emitter.create - "Unreachable Annotations" - [ Emitter.Property_status ] - ~correctness:[] (* TBC *) - ~tuning:[] (* TBC *) - -let set_unreachable pid = - if WpPropId.is_smoke_test pid then - begin - let source = WpPropId.source_of_id pid in - WpReached.set_doomed wp_unreachable pid ; - incr unreachable_failed ; - Wp_parameters.warning ~source "Failed smoke-test" - end - else - let open Property in - let emit = function - | IPPredicate {ip_kind = PKAssumes _} -> () - | p -> - debug "unreachable annotation %a@." Property.pretty p; - Property_status.emit wp_unreachable ~hyps:[] p Property_status.True - in - let pids = match WpPropId.property_of_id pid with - | IPPredicate {ip_kind = PKAssumes _} -> [] - | IPBehavior {ib_kf; ib_kinstr; ib_active; ib_bhv} -> - let active = Datatype.String.Set.elements ib_active in - (ip_post_cond_of_behavior ib_kf ib_kinstr active ib_bhv) @ - (ip_requires_of_behavior ib_kf ib_kinstr ib_bhv) - | IPExtended _ -> [] - (* Extended clauses might concern anything. Don't validate them - unless we know exactly what is going on. *) - | p -> - incr unreachable_proved ; - Wp_parameters.result "[CFG] Goal %a : Valid (Unreachable)" - WpPropId.pp_propid pid ; [p] - in - List.iter emit pids - -(* -------------------------------------------------------------------------- *) -(* --- Status of Terminates Annotations --- *) -(* -------------------------------------------------------------------------- *) - -let trivial_terminates = ref 0 - -let wp_trivially_terminates = - Emitter.create - "Trivial Termination" - [Emitter.Property_status] - ~correctness:[] (* TBC *) - ~tuning:[] (* TBC *) - -let set_trivially_terminates p hyps = - incr trivial_terminates ; - Wp_parameters.result "[CFG] Goal %a : Valid (Trivial)" WpPropId.pp_propid p ; - let pid = WpPropId.property_of_id p in - let hyps = Property.Set.elements hyps in - Property_status.emit wp_trivially_terminates ~hyps pid Property_status.True - -(* -------------------------------------------------------------------------- *) -(* --- Preconditions at Callsites --- *) -(* -------------------------------------------------------------------------- *) - -let call_preconditions = - Statuses_by_call.all_call_preconditions_at ~warn_missing:true - -let mk_call_pre_id called_kf bhv s_call called_pre = - (* TODOclean : quite dirty here ! *) - let id = WpPropId.mk_pre_id called_kf Kglobal bhv called_pre in - let called_pre = WpPropId.property_of_id id in - let called_pre_p = - Statuses_by_call.precondition_at_call called_kf called_pre s_call in - WpPropId.mk_call_pre_id called_kf s_call called_pre called_pre_p - -(* Preconditions at call-point as WpPropId.t *) -let preconditions_at_call s = function - | Cil2cfg.Static kf -> - let preconds = call_preconditions kf s in - let aux (pre, pre_call) = WpPropId.mk_call_pre_id kf s pre pre_call in - List.map aux preconds - | Cil2cfg.Dynamic _ -> [] - -(* ########################################################################## *) -(* ### WARNING: DEPRECATED API BELOW THIS LINE ### *) -(* ########################################################################## *) - -(*----------------------------------------------------------------------------*) -(* Strategy and annotations *) -(*----------------------------------------------------------------------------*) - -(* This is to code what kind of properties we want to process. *) -type asked_assigns = NoAssigns | OnlyAssigns | WithAssigns - -(* This is to code which behavior the computed strategy refers to. *) -type asked_bhv = - | FunBhv of funbehavior option (* None means default behavior - when the function has no spec. This is useful to process internal - properties even if the function has no default behavior *) - | StmtBhv of Cil2cfg.node * stmt * Datatype.String.Set.t * funbehavior - -let name_of_asked_bhv = function - | FunBhv (Some bhv) -> bhv.b_name - | FunBhv None -> Cil.default_behavior_name - | StmtBhv (_, _, _, bhv) -> bhv.b_name - -let asked_bhv = function - | FunBhv None -> None - | FunBhv (Some bhv) | StmtBhv (_,_,_,bhv) -> Some bhv.b_name - -(* This is to code what properties the user asked for in a given behavior. *) -type asked_prop = - | AllProps - | NamedProp of string list - | IdProp of Property.t - | CallPre of stmt * Property.t option (** No specified property means all *) - -(* a table to keep the information about the statement default specification - * associated with each edge in order to know in which strategy we should put a - * default annotation on this edge. When an edge has no information in the table, - * it means that the edge annotations belong to the [FunBhv] default behavior; - * and when we find a statement [s], it means that they belong to the [StmtBhv s] - * default behavior. The [int] information is only useful to build the table : - * when an edge is included in 2 different [StmtBhv] we only keep the one that - * has the fewer internal edges because it is necessarily included in the other. -*) -module HdefAnnotBhv = Cil2cfg.HE (struct type t = (stmt * int) end) - -(* Finally, a configuration is associated to a strategy computation to - * summarize what is to be computed. *) -type strategy_info = { - kf : Kernel_function.t; - cfg : Cil2cfg.t; - reachability : WpReached.reachability option ; - cur_bhv : asked_bhv; - asked_prop : asked_prop; - assigns_filter : asked_assigns; - def_annots_info : HdefAnnotBhv.t; -} - -(*----------------------------------------------------------------------------*) -(* Adding things in the strategy *) -(*----------------------------------------------------------------------------*) - -(* Select annotations to take as Hyp/Goal/... *) - -let pp_assigns_mode fmt config = - let str = match config.assigns_filter with - | NoAssigns -> "without assigns" - | OnlyAssigns -> "only with assigns" - | WithAssigns -> "both assigns or not" - in Format.fprintf fmt "%s" str - -let pp_asked_prop fmt = function - | AllProps -> Format.fprintf fmt "all properties" - | NamedProp names -> Format.fprintf fmt "properties %a" - (Pretty_utils.pp_list ~sep:"," Format.pp_print_string) names - | IdProp p -> Format.fprintf fmt "property %s" (Property.Names.get_prop_name_id p) - | CallPre (s, Some p) -> Format.fprintf fmt "pre %s at stmt %a" - (Property.Names.get_prop_name_id p) Stmt.pretty_sid s - | CallPre (s, None) -> Format.fprintf fmt "all call preconditions at stmt %a" - Stmt.pretty_sid s - -let pp_strategy_info fmt config = - Format.fprintf fmt "'%a', " Kernel_function.pretty config.kf; - let _ = match config.cur_bhv with - | FunBhv _bhv -> - Format.fprintf fmt "behavior '%s'" (name_of_asked_bhv config.cur_bhv) - | StmtBhv (_, s, a, bhv) -> - Format.fprintf fmt "behavior '%s' of statement %d%a" - bhv.b_name s.sid - (Pretty_utils.pp_list - ~pre:" (for active behaviors " ~sep:"," Format.pp_print_string) - (Datatype.String.Set.elements a) - in Format.fprintf fmt ", %a, %a" - pp_asked_prop config.asked_prop pp_assigns_mode config - -let cur_fct_default_bhv config = match config.cur_bhv with - | FunBhv None -> true - | FunBhv (Some bhv) -> bhv.b_name = Cil.default_behavior_name - | _ -> false - -let filter_assign config pid = - match config.assigns_filter, WpPropId.property_of_id pid with - | NoAssigns, Property.IPAssigns _ -> false - | (OnlyAssigns | WithAssigns), Property.IPAssigns _ -> true - | OnlyAssigns, _ -> false - | (NoAssigns | WithAssigns), _ -> true - -let filter_speconly config pid = - if Cil2cfg.cfg_spec_only config.cfg then - let open Property in - match WpPropId.property_of_id pid with - | IPPredicate {ip_kind = PKRequires _; ip_kinstr = Kglobal} -> true - | _ -> false - else true - -let filter_configstatus config pid = - (match config.asked_prop with IdProp _ -> true | _ -> false) || - (WpPropId.filter_status pid) - -let filter_asked config pid = - match config.asked_prop with - | IdProp idp -> Property.equal (WpPropId.property_of_id pid) idp - | CallPre (s_call, asked_pre) -> WpPropId.select_call_pre s_call asked_pre pid - | NamedProp names -> WpPropId.select_by_name names pid - | AllProps -> WpPropId.select_default pid - -let rec filter config pid = function - | [] -> None - | (f,name)::fs -> if f config pid then filter config pid fs else Some name - -let dkey = Wp_parameters.register_category "select" -let goal_to_select config pid = - let result = - filter config pid [ - filter_assign , "assigns/non-assigns pass" ; - filter_asked , "user selection" ; - filter_configstatus , "proved status" ; - filter_speconly , "no code and not main precondition" ; - ] in - match result with - | None -> - Wp_parameters.debug ~dkey "Goal '%a' selected" WpPropId.pp_propid pid ; - true - | Some f -> - Wp_parameters.debug ~dkey "Goal '%a' skipped (%s)" WpPropId.pp_propid pid f ; - false - -(*----------------------------------------------------------------------------*) -(* Add properties *) - -(* TODO: still have to remove these fonctions... *) - -let kind_to_select config kind id = match kind with - | WpStrategy.Agoal -> - if goal_to_select config id then Some WpStrategy.Agoal else None - | WpStrategy.Aboth goal -> - let goal = goal && goal_to_select config id in - Some (WpStrategy.Aboth goal) - | WpStrategy.AcutB goal -> - let goal = goal && goal_to_select config id in - Some (WpStrategy.AcutB goal) - | WpStrategy.AcallPre(goal,fct) -> - let goal = goal && goal_to_select config id in - Some (WpStrategy.AcallPre(goal,fct)) - | WpStrategy.AcallCheck(fct) -> - if goal_to_select config id then Some (WpStrategy.AcallCheck fct) - else None - | WpStrategy.AcallPost _ -> - if goal_to_select config id then Some kind else None - | WpStrategy.Ahyp | WpStrategy.AcallHyp _ -> Some kind - -let add_prop_loop_inv ~established config acc kind s ca p = - let id = WpPropId.mk_loop_inv_id ~established config.kf s ca in - match kind_to_select config kind id with - | None -> acc - | Some kind -> WpStrategy.add_prop_loop_inv acc kind s id p - -let add_prop_inv_fixpoint config acc kind s ca p = - let id = WpPropId.mk_inv_hyp_id config.kf s ca in - match kind_to_select config kind id with - | None -> acc - | Some kind -> WpStrategy.add_prop_loop_inv acc kind s id p - -(*----------------------------------------------------------------------------*) -(* Add Assigns *) - -let add_loop_assigns_goal config s (ca, assigns) acc = - let id = WpPropId.mk_loop_assigns_id config.kf s ca assigns in - match id with - None -> acc - | Some id -> - if goal_to_select config id then - let labels = NormAtLabels.labels_loop s in - let assigns' = NormAtLabels.preproc_assigns labels assigns in - let a_desc = WpPropId.mk_loop_assigns_desc s assigns' in - WpStrategy.add_assigns acc WpStrategy.Agoal id a_desc - else acc - -let add_stmt_assigns_goal config s active acc b l_post = match b.b_assigns with - | WritesAny -> acc - | Writes assigns -> - let id = WpPropId.mk_stmt_assigns_id config.kf s active b assigns in - match id with - | None -> acc - | Some id -> - if goal_to_select config id then - let kf = config.kf in - let labels = NormAtLabels.labels_stmt_assigns_l ~kf s l_post in - let assigns = NormAtLabels.preproc_assigns labels assigns in - let a_desc = WpPropId.mk_stmt_assigns_desc s assigns in - WpStrategy.add_assigns acc WpStrategy.Agoal id a_desc - else acc - -let add_fct_assigns_goal config acc tkind b = match b.b_assigns with - | WritesAny -> acc - | Writes assigns -> - let has_exit = Cil2cfg.has_exit (Cil2cfg.get config.kf) in - let id = WpPropId.mk_fct_assigns_id config.kf has_exit b tkind assigns in - match id with - | None -> acc - | Some id -> - if goal_to_select config id then - let labels = NormAtLabels.labels_fct_assigns ~exit:false in - let assigns' = NormAtLabels.preproc_assigns labels assigns in - let a_desc = WpPropId.mk_kf_assigns_desc assigns' in - WpStrategy.add_assigns acc WpStrategy.Agoal id a_desc - else acc - -(* ------------------------------------------------------------------------ *) -(* --- Get annotations according to the behavior --- *) -(* ------------------------------------------------------------------------ *) - -(** find the behavior named [name] in the list *) -let get_named_bhv name bhv_list = - try Some (List.find (fun b -> b.b_name = name) bhv_list) - with Not_found -> None - -(** Select in [bhv_list] the behavior that has to be processed - * according to [config] and [ki] current statement. *) -let get_behav config ki bh_list = match config.cur_bhv, ki with - | FunBhv _, Kglobal -> - get_named_bhv (name_of_asked_bhv config.cur_bhv) bh_list - | StmtBhv (_, s1, _, b), Kstmt s2 when s1.sid = s2.sid -> - get_named_bhv b.b_name bh_list - | _ -> None - -(** Tells weather the property belonging to the behaviors in [bhv_name_list] - * has to be considered according to [config]. *) -type test_behav_res = - | TBRno (* [cur_bhv] is not concerned *) - | TBRhyp (* the property belongs to [default_behavior], - but not to [cur_bhv] : it doesn't have to be a Goal - but can be considered as an hypothesis. *) - | TBRpart (* the property has to be taken as a Goal, but even if it is - proved for every [asked_bhvs], it will still be a partial proof. - TODO: use this to generate PKPartial ! *) - | TBRok (* Select as a Goal *) - -(** (see [test_behav_res] above). - * If the annotation doesn't have "for" names, it is a bit complicated because - * we have to know if the statement [s] is inside a stmt behavior or not. *) -let is_annot_for_config config ?(loopassigns=false) node s_annot bhv_name_list = - let edges_before = Cil2cfg.pred_e config.cfg node in - debug "[is_annot_for_config] at sid:%d for %a ? @." - s_annot.sid (Wp_error.pp_string_list ~sep:" " ~empty:"<default>") - bhv_name_list; - let hyp_but_not_at_post n = (* don't take assert at post pgpt (see #564) *) - let s_post = match Cil2cfg.get_post_edges config.cfg n with - | [] -> None - | e::_ -> Cil2cfg.get_edge_next_stmt config.cfg e - in match s_post with - | Some s_post when s_post.sid = s_annot.sid && not loopassigns -> TBRno - | _ -> TBRhyp - in - let res = match bhv_name_list with - | [] -> (* no spec 'for' in the property *) - begin - let e = match edges_before with - | e::_ -> e - | _ -> Wp_parameters.fatal "annot with no edge ?" - in - match config.cur_bhv with - | FunBhv _ when cur_fct_default_bhv config -> - begin - try - let _ = HdefAnnotBhv.find config.def_annots_info e in - TBRhyp - with Not_found -> TBRok - end - | StmtBhv (n, sb, _, b) when b.b_name = Cil.default_behavior_name -> - begin - try - let s,_ = HdefAnnotBhv.find config.def_annots_info e in - if s.sid = sb.sid then TBRok - else raise Not_found - with Not_found -> hyp_but_not_at_post n - end - | FunBhv _ -> TBRhyp - | StmtBhv (n,_,_,_) -> hyp_but_not_at_post n - end - | bhvs -> (* TODOopt : there is surely a better way to do this : *) - let asked_bhv = name_of_asked_bhv config.cur_bhv in - let goal = List.exists (fun bl -> bl = asked_bhv) bhvs in - if goal then - let full = (* TODO *) true - (* List.for_all (fun bl -> is_in bl config.asked_bhvs) bhvs *) - in (if full then TBRok else TBRpart) - else TBRno - in debug "[is_annot_for_config] -> %s@." - (match res with TBRok -> "ok" | TBRhyp -> "hyp" | TBRno -> "no" - | TBRpart -> "part"); - res - -let add_fct_pre config acc spec = - let kf = config.kf in - let add_bhv_pre_hyp b acc = - let kind = WpStrategy.Ahyp in - WpStrategy.add_prop_fct_bhv_pre acc kind kf b - in - let add_def_pre_hyp acc = - match Cil.find_default_behavior spec with - | None -> acc - | Some bdef -> add_bhv_pre_hyp bdef acc - in - let acc = - if WpStrategy.is_main_init kf || - Wp_parameters.PrecondWeakening.get () then acc - else let kind = WpStrategy.Ahyp in - List.fold_left (fun acc bhv -> - WpStrategy.add_prop_fct_pre_bhv acc kind kf bhv) - acc spec.spec_behavior - in - let acc = match get_behav config Kglobal spec.spec_behavior with - | None -> add_def_pre_hyp acc - | Some b -> - let acc = - if not (Cil.is_default_behavior b) then add_def_pre_hyp acc else acc - in - let acc = - if WpStrategy.is_main_init kf then - let add_both acc p = - let id = WpPropId.mk_pre_id kf Kglobal b p in - let goal = goal_to_select config id in - let kind = WpStrategy.Aboth goal in - WpStrategy.add_prop_fct_pre acc kind kf b ~assumes:None p - in - let acc = List.fold_left add_both acc b.b_requires in - let add_hyp acc p = - let kind = WpStrategy.Ahyp in - WpStrategy.add_prop_fct_pre acc kind kf b ~assumes:None p - in List.fold_left add_hyp acc b.b_assumes - else add_bhv_pre_hyp b acc - in acc - in - let acc = match get_behav config Kglobal spec.spec_behavior with - | Some bhv when Wp_parameters.SmokeTests.get () -> - WpStrategy.add_prop_fct_smoke acc kf bhv - | _ -> acc - in - acc - - -let add_variant acc spec = (* TODO *) - let _ = match spec.spec_variant with None -> () - | Some v -> - Wp_parameters.warning ~once:true "Ignored 'decrease' specification:@, %a@." - Printer.pp_decreases v - in acc - -let add_terminates acc spec = (* TODO *) - let _ = match spec.spec_terminates with None -> () - | Some p -> - Wp_parameters.warning ~once:true "Ignored 'terminates' specification:@, %a@." - Printer.pp_predicate - (Logic_const.pred_of_id_pred p) - in acc - -let add_disjoint_behaviors_props config ki active spec acc = - match spec.spec_disjoint_behaviors with - | [] -> acc - | l -> - let add_disj acc bhv_names = - let kf = config.kf in - let id = WpPropId.mk_disj_bhv_id (kf, ki, active, bhv_names) in - if goal_to_select config id then - begin - let prop = Ast_info.disjoint_behaviors spec bhv_names in - let labels = match ki with - | Kglobal -> NormAtLabels.labels_fct_pre - | Kstmt s -> NormAtLabels.labels_stmt_pre ~kf s in - let prop = WpStrategy.normalize id labels prop in - WpStrategy.add_prop acc WpStrategy.Agoal id prop - end - else acc - in List.fold_left add_disj acc l - -let add_complete_behaviors_props config ki active spec acc = - match spec.spec_complete_behaviors with - | [] -> acc - | l -> - let mk_prop acc bhv_names = - let kf = config.kf in - let id = WpPropId.mk_compl_bhv_id (kf, ki, active, bhv_names) in - if goal_to_select config id then - let prop = Ast_info.complete_behaviors spec bhv_names in - let labels = match ki with - | Kglobal -> NormAtLabels.labels_fct_pre - | Kstmt s -> NormAtLabels.labels_stmt_pre ~kf s in - let prop = WpStrategy.normalize id labels prop in - WpStrategy.add_prop acc WpStrategy.Agoal id prop - else acc - in List.fold_left mk_prop acc l - -let add_behaviors_props config ki active spec acc = - let add = match config.cur_bhv, ki with - | FunBhv _, Kglobal when cur_fct_default_bhv config -> true - | StmtBhv (_, cur_s, cur_a, b), Kstmt s - when (s.sid = cur_s.sid && - b.b_name = Cil.default_behavior_name && - Datatype.String.Set.(equal cur_a (of_list active))) - -> true - | _ -> false - in - if add then - let acc = add_complete_behaviors_props config ki active spec acc in - let acc = add_disjoint_behaviors_props config ki active spec acc in - acc - else acc - -(** Add the post condition of the whole spec as hypothesis. - * Add [old(assumes) => ensures] for all the behaviors, - * and also add an upper approximation of the merged assigns information. *) -let add_stmt_spec_post_as_hyp config v s spec acc = - let l_post = Cil2cfg.get_post_label config.cfg v in - let add_bhv_post acc b = - let assumes = Some (Ast_info.behavior_assumes b) in - let add tk acc p = - WpStrategy.add_prop_stmt_post acc WpStrategy.Ahyp config.kf - s b tk l_post ~assumes p - in - let p_acc, e_acc = - WpStrategy.fold_bhv_post_cond ~warn:false (add Normal) (add Exits) acc b - in let p_acc = - WpStrategy.add_stmt_spec_assigns_hyp p_acc config.kf s l_post spec in - (* let e_acc = TODO, but crach at the moment... why ? - * add_spec_assigns_hyp config ki l_post e_acc spec in *) - p_acc, e_acc - in List.fold_left add_bhv_post acc spec.spec_behavior - -(** we want to prove this behavior: - * - add the requires as preconditions to both prove and use as hyp, - * - add the assumes as hypotheses, - * - add the postconditions as goals. -*) -let add_stmt_bhv_as_goal config v s active b (b_acc, (p_acc, e_acc)) = - let l_post = Cil2cfg.get_post_label config.cfg v in - let assumes = None in (* [assumes] are used as separate hypotheses *) - let add_pre_hyp acc p = - WpStrategy.add_prop_stmt_pre acc WpStrategy.Ahyp config.kf s b ~assumes p - in - let add_pre_goal acc p = - let id = WpPropId.mk_pre_id config.kf (Kstmt s) b p in - let goal = goal_to_select config id in - let kind = WpStrategy.Aboth goal in - WpStrategy.add_prop_stmt_pre acc kind config.kf s b ~assumes p - in - let add_post tk acc p = - let id = WpPropId.mk_stmt_post_id config.kf s b (tk, p) in - let goal = goal_to_select config id in - let kind = WpStrategy.Aboth goal in - WpStrategy.add_prop_stmt_post acc kind config.kf s b tk l_post ~assumes p - in - - let b_acc = List.fold_left add_pre_goal b_acc b.b_requires in - let b_acc = List.fold_left add_pre_hyp b_acc b.b_assumes in - - let p_acc, e_acc = WpStrategy.fold_bhv_post_cond ~warn:true - (add_post Normal) (add_post Exits) (p_acc, e_acc) b - in - let p_acc = add_stmt_assigns_goal config s active p_acc b l_post in - (*let e_acc = TODO, but crash at the moment... why ? - add_stmt_assigns config s e_acc b l_post in *) - b_acc, (p_acc, e_acc) - -let is_empty_behavior bhv = - bhv.b_requires = [] && - bhv.b_assumes = [] && - bhv.b_post_cond = [] && - bhv.b_assigns = WritesAny && - bhv.b_allocation = FreeAllocAny - -let is_empty_spec s = - s.spec_variant = None && - s.spec_terminates = None && - List.for_all is_empty_behavior s.spec_behavior - -let add_stmt_spec_annots - config v s active spec ((b_acc, (p_acc, e_acc)) as acc) - = - if is_empty_spec spec then acc - else - let acc = add_variant acc spec in - let acc = add_terminates acc spec in - match config.cur_bhv with - | StmtBhv (_n, cur_s, cur_a, b) - when s.sid = cur_s.sid && - Datatype.String.Set.(equal cur_a (of_list active)) - -> - (* - begin match get_behav config (Kstmt s) spec.spec_behavior with - | None -> (* in some cases, it seems that we can have several spec - for the same statement -> not an error *) acc - | Some b -> - *) - let b_acc, a_acc = add_stmt_bhv_as_goal config v s active b acc in - let b_acc = add_behaviors_props config (Kstmt s) active spec b_acc in - b_acc, a_acc - | _ -> (* in all other cases, use the specification as hypothesis *) - let kind = WpStrategy.Aboth false in - let b_acc = - WpStrategy.add_prop_stmt_spec_pre b_acc kind config.kf s spec - in - let p_acc, e_acc = - add_stmt_spec_post_as_hyp config v s spec (p_acc, e_acc) - in b_acc, (p_acc, e_acc) - -(*----------------------------------------------------------------------------*) -(* Call annotations *) -(*----------------------------------------------------------------------------*) - -let add_called_pre config called_kf s spec acc = - let add_behav acc b = (* pre for behavior is [assumes => requires] *) - let assumes = (Ast_info.behavior_assumes b) in - let add_pre acc pre = - let id = mk_call_pre_id called_kf b s pre in - let kind = WpStrategy.AcallPre (goal_to_select config id,called_kf) in - WpStrategy.add_prop_call_pre acc kind id ~assumes pre - in List.fold_left add_pre acc b.b_requires - in - List.fold_left add_behav acc spec.spec_behavior - -let add_called_post called_kf termination_kind acc = - let spec = Annotations.funspec called_kf in - let add_behav acc b = - (* post for behavior is [\old(assumes) => ensures] *) - let kind = WpStrategy.AcallHyp called_kf in - let assumes = (Ast_info.behavior_assumes b) in - let add_post acc (tk, p) = - if tk = termination_kind && use_predicate p.ip_content.tp_kind - then WpStrategy.add_prop_call_post acc kind called_kf b tk ~assumes p - else acc - in List.fold_left add_post acc b.b_post_cond - in - List.fold_left add_behav acc spec.spec_behavior - -let add_call_checks config s kf posts exits = - if cur_fct_default_bhv config - && Wp_parameters.SmokeTests.get () - && Wp_parameters.SmokeDeadcall.get () - then - WpStrategy.add_prop_dead_call kf s posts exits - else - posts , exits - -let add_call_annots config s kf l_post (before,(posts,exits)) = - let spec = Annotations.funspec kf in - let before = add_called_pre config kf s spec before in - let posts = add_called_post kf Normal posts in - let posts = WpStrategy.add_call_assigns_hyp posts config.kf s - ~called_kf:kf l_post (Some spec) in - let exits = add_called_post kf Exits exits in - before , add_call_checks config s kf posts exits - -let get_call_annots config v s fct = - let l_post = Cil2cfg.get_post_label config.cfg v in - let empty = let e = WpStrategy.empty_acc in e,(e,e) in - match fct with - - | Cil2cfg.Static kf -> add_call_annots config s kf l_post empty - - | Cil2cfg.Dynamic _ -> - let bhv = asked_bhv config.cur_bhv in - match Dyncall.get ?bhv s with - | None | Some(_,[]) -> - Wp_parameters.warning ~once:true ~source:(fst (Stmt.loc s)) - "Missing 'calls' for %s" - (match bhv with - | None -> "default behavior" - | Some b -> b) ; - let annots = WpStrategy.add_call_assigns_any WpStrategy.empty_acc s in - WpStrategy.empty_acc, (annots , annots) - | Some(_,calls) -> - List.fold_left - (fun acc kf -> add_call_annots config s kf l_post acc) - empty calls - -(*----------------------------------------------------------------------------*) - -let add_variant_annot config s ca var_exp loop_entry loop_back = - let (vpos_id, vpos), (vdecr_id, vdecr) = - WpStrategy.mk_variant_properties config.kf s ca var_exp - in - let add_variant acc kind id p = - WpStrategy.add_prop_loop_inv acc kind s id p - in - let add_hyp acc = - let acc = add_variant acc WpStrategy.Ahyp vdecr_id vdecr in - add_variant acc WpStrategy.Ahyp vpos_id vpos - in - let add_goal acc = - let acc = - if goal_to_select config vdecr_id then - add_variant acc WpStrategy.Agoal vdecr_id vdecr - else acc - in if goal_to_select config vpos_id then - add_variant acc WpStrategy.Agoal vpos_id vpos - else acc - in - let loop_back = - if cur_fct_default_bhv config then add_goal loop_back else add_hyp loop_back - (*TODO: what about variant establishment ??? It seems that [0<v)] is not - * proved by induction anymore. Why ? *) - in loop_entry, loop_back - -let add_loop_invariant_annot config vloop s ca b_list inv acc = - let hyp = use_predicate inv.tp_kind in - let goal = verify_predicate inv.tp_kind in - let inv = inv.tp_statement in - let assigns, loop_entry, loop_back , loop_core = acc in - (* we have to prove that inv is true for each edge that goes - * in the loop, so we can assume that inv is true for each edge - * starting from this point. *) - match is_annot_for_config config vloop s b_list with - | TBRok - | TBRpart (* TODO: PKPartial *) - -> - begin - let loop_entry = - if goal then - add_prop_loop_inv ~established:true config loop_entry - WpStrategy.Agoal s ca inv - else loop_entry in - let loop_back = - if goal then - add_prop_loop_inv ~established:false config loop_back - WpStrategy.Agoal s ca inv - else loop_back in - let loop_core = - if hyp then - add_prop_inv_fixpoint config loop_core WpStrategy.Ahyp s ca inv - else loop_core in - assigns, loop_entry , loop_back , loop_core - end - | TBRhyp -> - if hyp then - let kind = WpStrategy.Ahyp in - let loop_core = add_prop_inv_fixpoint config loop_core kind s ca inv - in assigns, loop_entry , loop_back , loop_core - else acc - | TBRno -> acc - -(** Returns the annotations for the three edges of the loop node: - * - loop_entry : goals for the edge entering in the loop - * - loop_back : goals for the edge looping to the entry point - * - loop_core : fix-point hypothesis for the edge starting the loop core -*) -let get_loop_annots config vloop s = - let do_annot _ a (assigns, loop_entry, loop_back , loop_core as acc) = - match a.annot_content with - | AInvariant (b_list, true, inv) -> - add_loop_invariant_annot config vloop s a b_list inv acc - | AVariant (var_exp, None) -> - let loop_entry, loop_back = - add_variant_annot config s a var_exp loop_entry loop_back - in assigns, loop_entry , loop_back , loop_core - | AVariant (_v, _rel) -> - Wp_parameters.warning ~once:true "Ignored 'loop variant' specification with measure : %a" - Printer.pp_code_annotation a; - acc - | AAssigns (_,WritesAny) -> assert false - | AAssigns (b_list, Writes w) -> (* loop assigns *) - let h_assigns, g_assigns = assigns in - let check_assigns old cur = - match old with - None -> Some cur - | Some _ -> - Wp_parameters.fatal - "At most one loop assigns can be associated to a behavior" - in - let assigns = - match is_annot_for_config config ~loopassigns:true vloop s b_list with - | TBRok | TBRpart -> - check_assigns h_assigns (a,w), - check_assigns g_assigns (a,w) - | TBRhyp -> - check_assigns h_assigns (a,w), g_assigns - | TBRno -> assigns - in (assigns, loop_entry , loop_back , loop_core) - | _ -> acc (* see get_stmt_annots *) - in - let loop_core = - if cur_fct_default_bhv config - && Wp_parameters.SmokeTests.get () - && Wp_parameters.SmokeDeadloop.get () - && not (WpReached.is_dead_code s) - then WpStrategy.add_prop_dead_loop WpStrategy.empty_acc config.kf s - else WpStrategy.empty_acc in - let (h_assigns, g_assigns), loop_entry , loop_back , loop_core = - Annotations.fold_code_annot do_annot s - ((None,None), WpStrategy.empty_acc, WpStrategy.empty_acc, loop_core) - in - let loop_back = match g_assigns with - | None -> loop_back - | Some a -> add_loop_assigns_goal config s a loop_back - in - let loop_core = - WpStrategy.add_loop_assigns_hyp loop_core config.kf s h_assigns - in (loop_entry , loop_back , loop_core) - -let add_stmt_deadcode_smoke config acc s = - if cur_fct_default_bhv config - then - match config.reachability with - | Some r when WpReached.smoking r s -> - WpStrategy.add_prop_dead_code acc config.kf s - | _ -> acc - else - acc - -let get_stmt_annots config v s = - let do_annot _ a ((b_acc, (a_acc, e_acc)) as acc) = - match a.annot_content with - | AInvariant (_blist, loop_inv, _inv) -> - if loop_inv then (* see get_loop_annots *) acc - else - begin - Wp_parameters.warning ~once:true - "Unsupported generalized invariant, use loop invariant instead.\n\ - Ignored invariant @[<hov 2>%a@]" - Printer.pp_code_annotation a; - acc - end - | AAssert (b_list, p) -> - let kf = config.kf in - let acc = match is_annot_for_config config v s b_list with - | TBRno -> acc - | TBRhyp -> - if use_predicate p.tp_kind then - let b_acc = - WpStrategy.add_prop_assert - b_acc WpStrategy.Ahyp kf s a p.tp_statement - in (b_acc, (a_acc, e_acc)) - else acc - | TBRok | TBRpart -> - let id = WpPropId.mk_assert_id config.kf s a in - let goal = goal_to_select config id in - let add, kind = - match p.tp_kind with - | Admit -> true, WpStrategy.Ahyp - | Assert -> true, Aboth goal - | Check -> goal, Agoal - in if add then - let b_acc = - WpStrategy.add_prop_assert b_acc kind kf s a p.tp_statement - in (b_acc, (a_acc, e_acc)) - else acc - in acc - | AAllocation (_b_list, _frees_allocates) -> - (* [PB] TODO *) acc - | AAssigns (_b_list, _assigns) -> - (* loop assigns: see get_loop_annots *) acc - | AVariant (_v, _rel) -> (* see get_loop_annots *) acc - | APragma _ -> acc - | AStmtSpec (b_list, spec) -> - if b_list <> [] then (* TODO ! *) - Wp_parameters.warning ~once:true - "Ignored specification 'for %a' (generalize to all behavior)" - (Pretty_utils.pp_list ~sep:", " Format.pp_print_string) - b_list; - add_stmt_spec_annots config v s b_list spec acc - | AExtended _ -> acc - in - let before_acc = - add_stmt_deadcode_smoke config WpStrategy.empty_acc s in - let after_acc = WpStrategy.empty_acc in - let exits_acc = WpStrategy.empty_acc in - let acc = before_acc, (after_acc, exits_acc) in - Annotations.fold_code_annot do_annot s acc - -let get_fct_pre_annots config spec = - let acc = WpStrategy.empty_acc in - let acc = add_fct_pre config acc spec in - let acc = add_behaviors_props config Kglobal [] spec acc in - let acc = add_variant acc spec in - let acc = add_terminates acc spec in - acc - -let get_fct_post_annots config tkind spec = - let acc = WpStrategy.empty_acc in - match get_behav config Kglobal spec.spec_behavior with - | None -> acc - | Some b -> - (* add the postconditions *) - let f_nothing () _ = () in - let add tk acc p = - let id = WpPropId.mk_fct_post_id config.kf b (tk, p) in - if goal_to_select config id then - WpStrategy.add_prop_fct_post acc WpStrategy.Agoal config.kf b tk p - else acc - in - let acc = match tkind with - | Normal -> - let acc, _ = - WpStrategy.fold_bhv_post_cond ~warn:true (add Normal) f_nothing (acc, ()) b - in acc - | Exits -> - let _, acc = - WpStrategy.fold_bhv_post_cond ~warn:false f_nothing (add Exits) ((), acc) b - in acc - | _ -> assert false - in (* also add the [assigns] *) - let acc = - if Kernel_function.is_definition config.kf - then add_fct_assigns_goal config acc tkind b - else WpStrategy.add_fct_bhv_assigns_hyp acc config.kf tkind b - in acc - -(*----------------------------------------------------------------------------*) -(* Build graph annotation for the strategy *) -(*----------------------------------------------------------------------------*) - -(** Builds tables that give hypotheses and goals relative to [b] behavior - * for edges of the cfg to consider during wp computation. - * [b = None] means that we only consider internal properties to select for the - * default behavior. This is useful when the function doesn't have any - * specification. - * @param asked_prop = Some id -> select only this goal (use all hyps). -*) -let get_behavior_annots config = - debug "build strategy for %a@." pp_strategy_info config; - let cfg = config.cfg in - let spec = Annotations.funspec config.kf in - let annots = WpStrategy.create_tbl () in - - let get_node_annot v = - debug "get_node_annot for node %a" Cil2cfg.pp_node v; - match Cil2cfg.node_type v with - | Cil2cfg.Vstart | Cil2cfg.Vend -> () - - | Cil2cfg.VfctIn -> - let pre = get_fct_pre_annots config spec in - WpStrategy.add_on_edges annots pre (Cil2cfg.succ_e cfg v) - - | Cil2cfg.VfctOut -> - let post = get_fct_post_annots config Normal spec in - WpStrategy.add_on_edges annots post (Cil2cfg.succ_e cfg v) - - | Cil2cfg.VfctErr -> - let post = get_fct_post_annots config Exits spec in - WpStrategy.add_on_edges annots post (Cil2cfg.succ_e cfg v) - - | Cil2cfg.VblkIn (Cil2cfg.Bstmt s, _) - | Cil2cfg.Vstmt s - | Cil2cfg.Vswitch (s,_) | Cil2cfg.Vtest (true, s, _) - -> - let stmt_annots = get_stmt_annots config v s in - WpStrategy.add_node_annots annots cfg v stmt_annots - - | Cil2cfg.Vcall (s,_,fct,_) -> - let stmt_annots = get_stmt_annots config v s in - WpStrategy.add_node_annots annots cfg v stmt_annots; - let call_annots = get_call_annots config v s fct in - WpStrategy.add_node_annots annots cfg v call_annots - - | Cil2cfg.Vloop (_, s) -> - let stmt_annots = get_stmt_annots config v s in - let before, _after = stmt_annots in - (* TODO: what about after ? *) - WpStrategy.add_loop_annots annots cfg v ~entry:before - ~back:WpStrategy.empty_acc ~core:WpStrategy.empty_acc; - debug "add_loop_annots stmt ok"; - let (entry , back , core) = get_loop_annots config v s in - debug "get_loop_annots ok"; - WpStrategy.add_loop_annots annots cfg v ~entry ~back ~core - - | Cil2cfg.Vloop2 _ -> (* nothing to do *) () - | Cil2cfg.VblkIn (_, _) | Cil2cfg.VblkOut (_, _) -> (* nothing *) () - | Cil2cfg.Vtest (false, _s, _) -> (* done in Cil2cfg.Vtest (true) *) () - in - Cil2cfg.iter_nodes get_node_annot cfg; - annots - -(* ------------------------------------------------------------------------ *) -(* --- Global Properties --- *) -(* ------------------------------------------------------------------------ *) - -module GS = Cil_datatype.Global_annotation.Set - -let add_global_annotations annots = - let rec do_global g = - let (source,_) = Cil_datatype.Global_annotation.loc g in - match g with - | Daxiomatic (_ax_name, globs,_,_) -> do_globals globs - | Dvolatile _ -> - (* nothing to do *) () - | Dfun_or_pred _ -> - (* will be processed while translation is needed *) () - | Dtype _ -> - (* will be processed while translation is needed *) () - | Dtype_annot (linfo,_) -> - Wp_parameters.warning ~source ~once:true - "Type invariant not handled yet ('%s' ignored)" - linfo.l_var_info.lv_name; - () - | Dmodel_annot (mf,_) -> - Wp_parameters.warning ~source ~once:true - "Model fields not handled yet (model field '%s' ignored)" - mf.mi_name; - () - | Dcustom_annot (_c,_n,_,_) -> - Wp_parameters.warning ~source ~once:true - "Custom annotation not handled (ignored)"; - () - | Dextended _ -> () - (* nothing to do. It's the job of the extension's owner - to generate the appropriate standard annotations. *) - | Dinvariant (linfo,_) -> - Wp_parameters.warning ~source ~once:true - "Global invariant not handled yet ('%s' ignored)" - linfo.l_var_info.lv_name; - () - | Dlemma (name,_,_,p,_,_) -> - if use_predicate p.tp_kind then - WpStrategy.add_axiom annots (LogicUsage.logic_lemma name) - - and do_globals gs = List.iter do_global gs in - (*[LC]: forcing order of iteration: hash is not the same on 32 and 64 bits *) - let pool = ref GS.empty in - Annotations.iter_global (fun _ g -> pool := GS.add g !pool); - GS.iter do_global !pool; - annots - -(* ------------------------------------------------------------------------ *) -(* --- Main functions to build the strategies --- *) -(* ------------------------------------------------------------------------ *) - -let string_of_active active = - Format.asprintf "%a" - (Pretty_utils.pp_list (fun fmt s -> Format.fprintf fmt "_%s" s)) - (Datatype.String.Set.elements active) - -let behavior_name_of_config config = - match config.cur_bhv with - | FunBhv None -> None - | FunBhv (Some b) when b.b_name = Cil.default_behavior_name -> None - | FunBhv (Some b) -> Some b.b_name - | StmtBhv (_, s, active, b) when b.b_name = Cil.default_behavior_name -> - Some( - "default_for_stmt_"^(string_of_int s.sid)^(string_of_active active)) - (*TODO better name ?*) - | StmtBhv (_, s, active, b) -> - Some (b.b_name^"_stmt_"^(string_of_int s.sid)^(string_of_active active)) - -let build_bhv_strategy config = - let annots = get_behavior_annots config in - let annots = add_global_annotations annots in - let desc = Format.asprintf "%a" pp_strategy_info config in - WpStrategy.mk_strategy desc config.cfg (behavior_name_of_config config) - WpStrategy.SKannots annots - -(* Visit the CFG to find all the internal statement specifications. - * (see [HdefAnnotBhv] documentation for information about this table). -*) -let internal_function_behaviors cfg = - let def_annot_bhv = HdefAnnotBhv.create 42 in - let get_stmt_bhv node stmt acc = - let add_bhv_info active acc b = - if is_empty_behavior b then acc else - begin - if b.b_name = Cil.default_behavior_name then - begin - let _, int_edges = Cil2cfg.get_internal_edges cfg node in - let n = Cil2cfg.Eset.cardinal int_edges in - let reg e = - try - let (_old_s, old_n) = HdefAnnotBhv.find def_annot_bhv e in - if n < old_n then - (* new spec is included in the old one : override. *) - raise Not_found - with Not_found -> - HdefAnnotBhv.replace def_annot_bhv e (stmt, n) - in - Cil2cfg.Eset.iter reg int_edges - end; - (node, stmt, active, b)::acc - end - in - let spec_bhv_names acc annot = match annot with - | {annot_content = AStmtSpec (active, spec)} -> - List.fold_left (add_bhv_info active) acc spec.spec_behavior - | _ -> Wp_parameters.fatal "filter on is_contract didn't work ?" - in - let annots = Annotations.code_annot ~filter:Logic_utils.is_contract stmt in - List.fold_left spec_bhv_names acc annots - in - let get_bhv n ((seen_stmts, bhvs) as l) = - match Cil2cfg.start_stmt_of_node n with None -> l - | Some s -> - if List.mem s.sid seen_stmts then l - else - let seen_stmts = s.sid::seen_stmts in - let bhvs = get_stmt_bhv n s bhvs in - (seen_stmts, bhvs) - in - let _, bhvs = Cil2cfg.fold_nodes get_bhv cfg ([], []) in - bhvs, def_annot_bhv - - -(** empty [bhv_names] means all (whatever [ki] is) *) -let find_behaviors kf cfg ki bhv_names = - let f_bhvs = Annotations.behaviors kf in - let s_bhvs, def_annot_bhv = internal_function_behaviors cfg in - - let add_fct_bhv (def, acc) b = - let add () = - let def = if Cil.is_default_behavior b then true else def in - def, (FunBhv (Some b))::acc - in - if bhv_names = [] then add() - else match ki with - | None (* not specified ki *) | Some Kglobal -> - if List.mem b.b_name bhv_names then add () else (def, acc) - | Some Kstmt _ -> def, acc - in - - let add_stmt_bhv acc (n,s,active,b) = - let active = Datatype.String.Set.of_list active in - if bhv_names = [] then (StmtBhv (n,s,active,b))::acc - else if List.mem b.b_name bhv_names then - let acc = match ki with - | None -> (* not specified ki *) (StmtBhv (n, s, active, b))::acc - | Some (Kstmt stmt) when stmt.sid = s.sid -> - (StmtBhv (n, s, active, b))::acc - | _ -> (* specified ki but not this one *) acc - in acc - else acc - in - - let f_bhvs = List.rev f_bhvs in (* for compatibility with previous version *) - let def, bhvs = List.fold_left add_fct_bhv (false, []) f_bhvs in - let bhvs = List.fold_left add_stmt_bhv bhvs s_bhvs in - let bhvs = - if def then (* fct default behavior already in *) bhvs - else if bhv_names = [] then (FunBhv None)::bhvs - else match ki with - | None (* not specified ki *) | Some Kglobal -> - if List.mem Cil.default_behavior_name bhv_names - then (FunBhv None)::bhvs - else bhvs - | Some Kstmt _ -> bhvs - in def_annot_bhv, bhvs - -(*----------------------------------------------------------------------------*) -(* Unreachable *) -(*----------------------------------------------------------------------------*) - -class vexit kf acc = - object - inherit Visitor.frama_c_inplace - val mutable ips = acc - method acc = ips - method! vstmt_aux stmt = - Annotations.iter_code_annot - (fun _e ca -> - match ca.annot_content with - | AStmtSpec(_,spec) -> - List.iter - (fun bhv -> - List.iter - (fun post -> - if fst post = Exits then - let ip = WpPropId.mk_stmt_post_id kf stmt bhv post in - ips <- ip :: ips - ) bhv.b_post_cond - ) spec.spec_behavior - | _ -> () - ) stmt ; - Cil.DoChildren - end - -let process_unreached_annots cfg reachability = - debug "collecting unreachable annotations@."; - let unreached = Cil2cfg.unreachable_nodes cfg in - let kf = Cil2cfg.cfg_kf cfg in - let spec = Annotations.funspec kf in - let add_id acc id = - if WpPropId.filter_status id then id::acc - else (* non-selected property : nothing to do *) acc - in - let do_post b tk acc (termk, _ as p) = - if tk = termk then add_id acc (WpPropId.mk_fct_post_id kf b p) else acc - in - let do_bhv termk acc b = List.fold_left (do_post b termk) acc b.b_post_cond in - let do_annot s _ a acc = - List.fold_left add_id acc (WpPropId.mk_code_annot_ids kf s a) - in - let do_stmt s acc = - let acc = - match reachability with - | None -> acc - | Some r -> - if WpReached.smoking r s then - WpPropId.mk_smoke kf ~id:"unreachable" ~unreachable:s () :: acc - else acc - in Annotations.fold_code_annot (do_annot s) s acc - in - let do_node acc n = - debug - "process annotations of unreachable node %a@." - Cil2cfg.pp_node_type n; - match n with - | Cil2cfg.Vstart -> Wp_parameters.fatal "Start must be reachable" - | Cil2cfg.VfctIn -> Wp_parameters.fatal "FctIn must be reachable" - | Cil2cfg.VfctOut -> List.fold_left (do_bhv Normal) acc spec.spec_behavior - | Cil2cfg.VfctErr -> - let acc = List.fold_left (do_bhv Exits) acc spec.spec_behavior in - let visitor = new vexit kf acc in - ignore Visitor.(visitFramacKf (visitor :> frama_c_visitor) kf) ; - visitor#acc - | Cil2cfg.Vcall (s, _, call, _) -> - do_stmt s acc @ - preconditions_at_call s call - | Cil2cfg.Vstmt s - | Cil2cfg.VblkIn (Cil2cfg.Bstmt s, _) - | Cil2cfg.VblkOut (Cil2cfg.Bstmt s, _) - | Cil2cfg.Vtest (true, s, _) | Cil2cfg.Vloop (_, s) | Cil2cfg.Vswitch (s,_) - -> do_stmt s acc - | Cil2cfg.Vtest (false, _, _) | Cil2cfg.Vloop2 _ - | Cil2cfg.VblkIn _ | Cil2cfg.VblkOut _ | Cil2cfg.Vend -> acc - in - let annots = List.fold_left do_node [] unreached in - debug "found %d unreachable annotations@." (List.length annots) ; - List.iter (fun pid -> set_unreachable pid) annots - -(*----------------------------------------------------------------------------*) -(* Everything must go through here. *) -(*----------------------------------------------------------------------------*) - -let build_configs assigns kf model behaviors ki property = - debug "[get_strategies] for behaviors names: %a@." - (Wp_error.pp_string_list ~sep:" " ~empty:"<none>") - (match behaviors with [] -> ["<all>"] | _ :: _ as l -> l) ; - let _ = match ki with - | None -> () - | Some Kglobal -> - debug - "[get_strategies] select in function properties@." - | Some (Kstmt s) -> - debug - "[get_strategies] select stmt %d properties@." s.sid - in - if Wp_parameters.RTE.get () then WpRTE.generate model kf ; - let cfg = Cil2cfg.get kf in - let reachability = - if Wp_parameters.SmokeTests.get () - && Wp_parameters.SmokeDeadcode.get () - then Some (WpReached.reachability kf) - else None in - process_unreached_annots cfg reachability ; - let def_annot_bhv, bhvs = find_behaviors kf cfg ki behaviors in - if bhvs <> [] then debug "[get_strategies] %d behaviors" (List.length bhvs); - let mk_bhv_config bhv = { - kf; reachability; cfg; - cur_bhv = bhv; - asked_prop = property; - assigns_filter = assigns; - def_annots_info = def_annot_bhv; - } in List.map mk_bhv_config bhvs - -let get_strategies assigns kf model behaviors ki property = - let configs = build_configs assigns kf model behaviors ki property in - let rec add_stgs l = match l with [] -> [] | config::tl -> - let stg = build_bhv_strategy config in - stg::(add_stgs tl) - in add_stgs configs - -(*----------------------------------------------------------------------------*) -(* Public functions to build the strategies *) -(*----------------------------------------------------------------------------*) - -let get_precond_strategies ~model p = - debug "[get_precond_strategies] %s@." - (Property.Names.get_prop_name_id p); - let open Property in - match p with - | IPPredicate {ip_kind = PKRequires b; ip_kf; ip_kinstr = Kglobal} -> - let strategies = - if WpStrategy.is_main_init ip_kf then - get_strategies NoAssigns ip_kf model [b.b_name] None (IdProp p) - else [] - in - let call_sites = Kernel_function.find_syntactic_callsites ip_kf in - let add_call_pre_strategy acc (kf_caller, stmt) = - let asked = CallPre (stmt, Some p) in - get_strategies NoAssigns kf_caller model [] None asked @ acc - in - if call_sites = [] then - (Wp_parameters.warning ~once:true - "No direct call sites for function '%a': cannot check pre-conditions" - Kernel_function.pretty ip_kf; - strategies) - else List.fold_left add_call_pre_strategy strategies call_sites - | _ -> - invalid_arg "[get_precond_strategies] not a function precondition" - -let get_call_pre_strategies ~model stmt = - debug - "[get_call_pre_strategies] on statement %a@." Stmt.pretty_sid stmt; - match stmt.skind with - | Instr(Call(_,f,_,_)) -> - let strategies = match Kernel_function.get_called f with - | None -> - Wp_parameters.warning - "Call through function pointer not implemented yet: \ - cannot check pre-conditions for statement %a" - Stmt.pretty_sid stmt; - [] - | Some _kf_called -> - let kf_caller = Kernel_function.find_englobing_kf stmt in - let asked = CallPre (stmt, None) in - get_strategies NoAssigns kf_caller model [] None asked - in strategies - | Instr(Local_init(_, ConsInit _, _)) -> - let kf_caller = Kernel_function.find_englobing_kf stmt in - let asked = CallPre(stmt, None) in - get_strategies NoAssigns kf_caller model [] None asked - | _ -> Wp_parameters.warning - "[get_call_pre_strategies] this is not a call statement"; [] - -let get_id_prop_strategies ~model ?(assigns=WithAssigns) p = - debug "[get_id_prop_strategies] %s@." - (Property.Names.get_prop_name_id p); - let open Property in match p with - | IPCodeAnnot {ica_kf; ica_ca} -> - let bhvs = match ica_ca.annot_content with - | AAssert (l, _) | AInvariant (l, _, _) | AAssigns (l, _) -> l - | _ -> [] - in get_strategies assigns ica_kf model bhvs None (IdProp p) - | IPAssigns {ias_kf = kf; ias_bhv = Id_loop _} - (*loop assigns: belongs to the default behavior *) - | IPDecrease {id_kf = kf} -> - (* any variant property is attached to the default behavior of - * the function, NOT to a statement behavior *) - let bhvs = [ Cil.default_behavior_name ] in - get_strategies assigns kf model bhvs None (IdProp p) - | IPPredicate {ip_kind = PKRequires _; ip_kinstr = Kglobal} -> - get_precond_strategies model p - | _ -> - let strategies = match get_kf p with - | None -> Wp_parameters.warning - "WP of property outside functions: ignore %s" - (Property.Names.get_prop_name_id p); [] - | Some kf -> - let ki = Some (get_kinstr p) in - let bhv = match get_behavior p with - | None -> Cil.default_behavior_name - | Some fb -> fb.b_name - in get_strategies assigns kf model [bhv] ki (IdProp p) - in strategies - -let get_function_strategies ~model - ?(assigns=WithAssigns) ?(bhv=[]) ?(prop=[]) kf = - let prop = match prop with [] -> AllProps | _ -> NamedProp prop in - get_strategies assigns kf model bhv None prop - -let get_property_strategies ~model ip = - let open Property in - match ip with - | IPBehavior {ib_kf; ib_bhv} -> - let bhv = [ib_bhv.Cil_types.b_name] in - let assigns = WithAssigns in - get_function_strategies ~model ~assigns ~bhv ib_kf - | IPComplete _ - | IPDisjoint _ - | IPCodeAnnot _ - | IPAllocation _ - | IPAssigns _ - | IPDecrease _ - | IPPredicate _ - -> - let assigns = WithAssigns in - get_id_prop_strategies ~model ~assigns ip - - | IPAxiomatic _ - | IPLemma _ - | IPFrom _ - | IPReachable _ - | IPPropertyInstance _ - | IPOther _ - | IPTypeInvariant _ - | IPGlobalInvariant _ - | IPExtended _ - -> - Wp_parameters.result "Nothing to compute for '%a'" pretty ip ; - [] diff --git a/src/plugins/wp/wpAnnot.mli b/src/plugins/wp/wpAnnot.mli deleted file mode 100644 index a2b4ebcf85106f1964d12a6273f8296d4ced158e..0000000000000000000000000000000000000000 --- a/src/plugins/wp/wpAnnot.mli +++ /dev/null @@ -1,57 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of WP plug-in of Frama-C. *) -(* *) -(* Copyright (C) 2007-2021 *) -(* CEA (Commissariat a l'energie atomique et aux energies *) -(* 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 - -(** Every access to annotations have to go through here, - * so this is the place where we decide what the computation - * is allowed to use. *) - -(* ########################################################################## *) -(* ### WARNING: DEPRECATED API ### *) -(* ########################################################################## *) - -(*----------------------------------------------------------------------------*) - -val unreachable_proved : int ref -val unreachable_failed : int ref -val trivial_terminates : int ref -val set_unreachable : WpPropId.prop_id -> unit -val set_trivially_terminates : WpPropId.prop_id -> Property.Set.t -> unit - -type asked_assigns = NoAssigns | OnlyAssigns | WithAssigns - -val get_call_pre_strategies : - model:WpContext.model -> - stmt -> WpStrategy.strategy list - -val get_function_strategies : - model:WpContext.model -> - ?assigns:asked_assigns -> - ?bhv:string list -> - ?prop:string list -> - Kernel_function.t -> WpStrategy.strategy list - -val get_property_strategies : - model:WpContext.model -> Property.t -> WpStrategy.strategy list - -(*----------------------------------------------------------------------------*) diff --git a/src/plugins/wp/wpGenerator.ml b/src/plugins/wp/wpGenerator.ml deleted file mode 100644 index 92b9414af253bb28460e95656fb13517a7b16d8c..0000000000000000000000000000000000000000 --- a/src/plugins/wp/wpGenerator.ml +++ /dev/null @@ -1,237 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of WP plug-in of Frama-C. *) -(* *) -(* Copyright (C) 2007-2021 *) -(* CEA (Commissariat a l'energie atomique et aux energies *) -(* 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). *) -(* *) -(**************************************************************************) - -(* -------------------------------------------------------------------------- *) -(* --- WP Computer (main entry points) --- *) -(* -------------------------------------------------------------------------- *) - -class type computer = - object - method model : WpContext.model - method add_strategy : WpStrategy.strategy -> unit - method add_lemma : LogicUsage.logic_lemma -> unit - method compute : Wpo.t Bag.t - end - -(* -------------------------------------------------------------------------- *) -(* --- Property Entry Point --- *) -(* -------------------------------------------------------------------------- *) - -let compute_ip cc ip = - let open Property in match ip with - | IPLemma _ - | IPAxiomatic _ - -> - let rec iter cc = function - | IPLemma {il_name} -> cc#add_lemma (LogicUsage.logic_lemma il_name) - | IPAxiomatic {iax_props} -> List.iter (iter cc) iax_props - | _ -> () - in iter cc ip ; - cc#compute - | _ -> - List.iter cc#add_strategy - (WpAnnot.get_property_strategies ~model:cc#model ip) ; - cc#compute - -(* -------------------------------------------------------------------------- *) -(* --- Annotations Entry Point --- *) -(* -------------------------------------------------------------------------- *) - -let add_kf cc ?bhv ?prop kf = - let model = cc#model in - let assigns = WpAnnot.WithAssigns in - List.iter cc#add_strategy - (WpAnnot.get_function_strategies ~model ~assigns ?bhv ?prop kf) - -let add_lemmas cc = function - | None | Some[] -> - LogicUsage.iter_lemmas - (fun lem -> - let idp = WpPropId.mk_lemma_id lem in - if WpPropId.filter_status idp then cc#add_lemma lem) - | Some ps -> - if List.mem "-@lemmas" ps then () - else LogicUsage.iter_lemmas - (fun lem -> - let idp = WpPropId.mk_lemma_id lem in - if WpPropId.filter_status idp && WpPropId.select_by_name ps idp - then cc#add_lemma lem) - -let compute_kf cc ?kf ?bhv ?prop () = - begin - Option.iter (add_kf cc ?bhv ?prop) kf ; - cc#compute - end - -let compute_selection cc ?(fct=Wp_parameters.Fct_all) ?bhv ?prop () = - begin - add_lemmas cc prop ; - Wp_parameters.iter_fct (add_kf cc ?bhv ?prop) fct ; - cc#compute - end - -let compute_call cc stmt = - let model = cc#model in - List.iter cc#add_strategy (WpAnnot.get_call_pre_strategies ~model stmt) ; - cc#compute - -(* -------------------------------------------------------------------------- *) -(* --- WPO Computer --- *) -(* -------------------------------------------------------------------------- *) - -module KFmap = Kernel_function.Map - -module Computer(VCG : CfgWP.VCgen) = -struct - - open LogicUsage - module WP = Calculus.Cfg(VCG) - - let prove_strategy collection model kf strategy = - let cfg = WpStrategy.cfg_of_strategy strategy in - let bhv = WpStrategy.get_bhv strategy in - let index = Wpo.Function( kf , bhv ) in - if WpRTE.missing_guards model kf then - Wp_parameters.warning ~current:false ~once:true - "Missing RTE guards" ; - try - let (results,_) = WP.compute cfg strategy in - List.iter - (fun wp -> - let wcs = VCG.compile_wp index wp in - collection := Bag.concat !collection wcs - ) results - with Warning.Error(source,reason) -> - Wp_parameters.failure - ~current:false "From %s: %s" source reason - - class wp (model:WpContext.model) = - object - val mutable lemmas : LogicUsage.logic_lemma Bag.t = Bag.empty - val mutable annots : WpStrategy.strategy Bag.t KFmap.t = KFmap.empty - - method model = model - - method add_lemma lemma = - if Logic_utils.verify_predicate lemma.lem_predicate.tp_kind then - lemmas <- Bag.append lemmas lemma - - method add_strategy strategy = - let kf = WpStrategy.get_kf strategy in - let sf = try KFmap.find kf annots with Not_found -> Bag.empty in - annots <- KFmap.add kf (Bag.append sf strategy) annots - - method compute : Wpo.t Bag.t = - begin - let collection = ref Bag.empty in - Lang.F.release () ; - WpContext.on_context (model,WpContext.Global) - begin fun () -> - LogicUsage.iter_lemmas - (fun (l : LogicUsage.logic_lemma) -> - if Logic_utils.use_predicate l.lem_predicate.tp_kind - then VCG.register_lemma l) ; - Bag.iter - (fun (l : LogicUsage.logic_lemma) -> - if Logic_utils.verify_predicate l.lem_predicate.tp_kind then - let vc = VCG.compile_lemma l in - collection := Bag.append !collection vc - ) lemmas ; - end () ; - KFmap.iter - (fun kf strategies -> - WpContext.on_context (model,WpContext.Kf kf) - begin fun () -> - LogicUsage.iter_lemmas - (fun (l : LogicUsage.logic_lemma) -> - if Logic_utils.use_predicate l.lem_predicate.tp_kind - then VCG.register_lemma l) ; - LogicUsage.iter_lemmas VCG.register_lemma ; - Bag.iter (prove_strategy collection model kf) strategies ; - end () - ) annots ; - lemmas <- Bag.empty ; - annots <- KFmap.empty ; - Lang.F.release () ; - !collection - end - end - -end - -(* -------------------------------------------------------------------------- *) -(* --- Dump Computer --- *) -(* -------------------------------------------------------------------------- *) - -module DUMPER = Calculus.Cfg(CfgDump) - -let dumper () = - let driver = Driver.load_driver () in - let model = Factory.(instance default driver) in - object - val mutable wptasks = [] - - method model = model - - method add_lemma (_ : LogicUsage.logic_lemma) = () - - method add_strategy strategy = - wptasks <- strategy :: wptasks - - method compute : Wpo.t Bag.t = - begin - (* Generates Wpos and accumulate exported goals *) - List.iter - (fun strategy -> - let cfg = WpStrategy.cfg_of_strategy strategy in - let kf = Cil2cfg.cfg_kf cfg in - let bhv = WpStrategy.behavior_name_of_strategy strategy in - CfgDump.fopen kf bhv ; - try - ignore (DUMPER.compute cfg strategy) ; - CfgDump.flush () - with err -> - CfgDump.flush () ; - raise err - ) wptasks ; - wptasks <- [] ; - Bag.empty - end - - end - -(* -------------------------------------------------------------------------- *) -(* --- Generators --- *) -(* -------------------------------------------------------------------------- *) - -let generators = WpContext.MINDEX.create 1 - -let computer setup driver = - let model = Factory.instance setup driver in - try WpContext.MINDEX.find generators model - with Not_found -> - let module VCG = (val CfgWP.vcgen setup driver) in - let module CC = Computer(VCG) in - let computer = (new CC.wp model :> computer) in - WpContext.MINDEX.add generators model computer ; computer - -(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/wpGenerator.mli b/src/plugins/wp/wpGenerator.mli deleted file mode 100644 index a2e0cec5c9cfec7b86469cca946fa0b15f2ab38d..0000000000000000000000000000000000000000 --- a/src/plugins/wp/wpGenerator.mli +++ /dev/null @@ -1,53 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of WP plug-in of Frama-C. *) -(* *) -(* Copyright (C) 2007-2021 *) -(* CEA (Commissariat a l'energie atomique et aux energies *) -(* 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). *) -(* *) -(**************************************************************************) - -(* -------------------------------------------------------------------------- *) -(* --- WP Computer (main entry points) --- *) -(* -------------------------------------------------------------------------- *) - -class type computer = - object - method model : WpContext.model - method add_strategy : WpStrategy.strategy -> unit - method add_lemma : LogicUsage.logic_lemma -> unit - method compute : Wpo.t Bag.t - end - -open Wp_parameters - -val compute_ip : computer -> Property.t -> Wpo.t Bag.t -val compute_call : computer -> Cil_types.stmt -> Wpo.t Bag.t - -val compute_kf : computer -> - ?kf:Kernel_function.t -> - ?bhv:string list -> - ?prop:string list -> - unit -> Wpo.t Bag.t - -val compute_selection : computer -> - ?fct:functions -> - ?bhv:string list -> - ?prop:string list -> - unit -> Wpo.t Bag.t - -val dumper : unit -> computer -val computer : Factory.setup -> Factory.driver -> computer diff --git a/src/plugins/wp/wpReached.ml b/src/plugins/wp/wpReached.ml index fa15b385d0ef17ea8666aa2e1fc8b969b6d1867b..ec9fb07b03b9cf03eeed070b87f8bcda4ccef40d 100644 --- a/src/plugins/wp/wpReached.ml +++ b/src/plugins/wp/wpReached.ml @@ -377,3 +377,52 @@ let set_doomed emitter pid = | Property.OLGlob _ | Property.OLContract _ -> () (* -------------------------------------------------------------------------- *) +(* --- Status of Unreachable Annotations --- *) +(* -------------------------------------------------------------------------- *) + +let dkey = Wp_parameters.register_category "reach" (* debugging key *) +let debug fmt = Wp_parameters.debug ~dkey fmt + +let unreachable_proved = ref 0 +let unreachable_failed = ref 0 + +let wp_unreachable = + Emitter.create + "Unreachable Annotations" + [ Emitter.Property_status ] + ~correctness:[] (* TBC *) + ~tuning:[] (* TBC *) + +let set_unreachable pid = + if WpPropId.is_smoke_test pid then + begin + let source = WpPropId.source_of_id pid in + set_doomed wp_unreachable pid ; + incr unreachable_failed ; + Wp_parameters.warning ~source "Failed smoke-test" + end + else + let open Property in + let emit = function + | IPPredicate {ip_kind = PKAssumes _} -> () + | p -> + debug "unreachable annotation %a@." Property.pretty p; + Property_status.emit wp_unreachable ~hyps:[] p Property_status.True + in + let pids = match WpPropId.property_of_id pid with + | IPPredicate {ip_kind = PKAssumes _} -> [] + | IPBehavior {ib_kf; ib_kinstr; ib_active; ib_bhv} -> + let active = Datatype.String.Set.elements ib_active in + (ip_post_cond_of_behavior ib_kf ib_kinstr active ib_bhv) @ + (ip_requires_of_behavior ib_kf ib_kinstr ib_bhv) + | IPExtended _ -> [] + (* Extended clauses might concern anything. Don't validate them + unless we know exactly what is going on. *) + | p -> + incr unreachable_proved ; + Wp_parameters.result "[CFG] Goal %a : Valid (Unreachable)" + WpPropId.pp_propid pid ; [p] + in + List.iter emit pids + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/wpReached.mli b/src/plugins/wp/wpReached.mli index 77a7bfa2297a43d2bb32e73a1ce849d905ef8905..68b6bd23f8b80fb1a2729792fae599bae8002f19 100644 --- a/src/plugins/wp/wpReached.mli +++ b/src/plugins/wp/wpReached.mli @@ -51,4 +51,9 @@ val dump : dir:Datatype.Filepath.t -> Kernel_function.t -> reachability -> unit val set_doomed : Emitter.t -> WpPropId.prop_id -> unit +val unreachable_proved : int ref +val unreachable_failed : int ref + +val set_unreachable : WpPropId.prop_id -> unit + (* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/wpStrategy.ml b/src/plugins/wp/wpStrategy.ml deleted file mode 100644 index b4a4080208a94c700335156e1bf9ff75c49dee55..0000000000000000000000000000000000000000 --- a/src/plugins/wp/wpStrategy.ml +++ /dev/null @@ -1,772 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of WP plug-in of Frama-C. *) -(* *) -(* Copyright (C) 2007-2021 *) -(* CEA (Commissariat a l'energie atomique et aux energies *) -(* 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). *) -(* *) -(**************************************************************************) - -let dkey = Wp_parameters.register_category "strategy" (* debugging key *) -let debug fmt = Wp_parameters.debug ~dkey fmt - -open Cil_types -open Logic_utils -open LogicUsage - -(* -------------------------------------------------------------------------- *) -(** An annotation can be used for different purpose. *) -type annot_kind = - | Ahyp (* annotation is an hypothesis, - but not a goal (see Aboth) : A => ...*) - | Agoal (* annotation is a goal, - but not an hypothesis (see Aboth): A /\ ...*) - | Aboth of bool - (* annotation can be used as both hypothesis and goal : - - with true : considered as both : A /\ A=>.. - - with false : we just want to use it as hyp right now. *) - | AcutB of bool - (* annotation is use as a cut : - - with true (A is also a goal) -> A (+ proof obligation A => ...) - - with false (A is an hyp only) -> True (+ proof obligation A => ...) *) - | AcallHyp of kernel_function - (* annotation is a called function property to consider as an Hyp. - * The pre are not here but in AcallPre since they can also - * be considered as goals. *) - | AcallPre of bool * kernel_function - (* annotation is a called function precondition : - to be considered as hyp, and goal if bool=true *) - | AcallCheck of kernel_function - (* annotation is a called function check-only precondition. - to be considered as goal only. *) - | AcallPost of kernel_function - (* annotation is a called function post check : - to be considered as goal only *) - -type call_pre_kind = CPhyp | CPgoal | CPboth - -(* -------------------------------------------------------------------------- *) -(* --- Annotations for one program point. --- *) -(* -------------------------------------------------------------------------- *) - -module ForCall = Kernel_function.Map - -(** Some elements can be used as both Hyp and Goal : because of the selection - * mechanism, we need to add a boolean [as_goal] to tell if the element is to be - * considered as a goal. If [false], the element can still be used as hypothesis. -*) -type annots = { - p_hyp : WpPropId.pred_info list; - p_goal : WpPropId.pred_info list; - p_both : (bool * WpPropId.pred_info) list; - p_cut : (bool * WpPropId.pred_info) list; - call_hyp : WpPropId.pred_info list ForCall.t; (* post and pre *) - call_pre : (call_pre_kind * WpPropId.pred_info) list ForCall.t; - call_post : WpPropId.pred_info list ForCall.t; (* post goals only (not hyp) *) - call_asgn : WpPropId.assigns_full_info ForCall.t; - a_goal : WpPropId.assigns_full_info; - a_hyp : WpPropId.assigns_full_info; - a_call : WpPropId.assigns_full_info; (* dynamic calls *) -} - -type t_annots = { has_asgn_goal : bool; has_prop_goal : bool; info: annots } - -(* --- Add annotations --- *) - -let empty_acc = - let a = { - p_hyp = []; p_goal = []; p_both = []; p_cut = []; - call_hyp = ForCall.empty; - call_pre = ForCall.empty; - call_asgn = ForCall.empty; - call_post = ForCall.empty; - a_goal = WpPropId.empty_assigns_info; - a_hyp = WpPropId.empty_assigns_info; - a_call = WpPropId.empty_assigns_info; - } in - { has_asgn_goal = false; has_prop_goal = false; info = a; } - -let normalize id ?assumes labels p = - try - let p = NormAtLabels.preproc_annot labels p in - match assumes with - | None -> Some p - | Some a -> - let a = Logic_const.pat(a,Logic_const.pre_label) in - let a = NormAtLabels.(preproc_annot labels_fct_pre a) in - Some(Logic_const.pimplies(a,p)) - with e -> - let pid = WpPropId.get_propid id in - NormAtLabels.catch_label_error e pid "annotation" ; - None - -let add_prop acc kind id p = - let get_p = match p with None -> None - | Some p -> Some(WpPropId.mk_pred_info id p) in - let add_hyp l = match get_p with None -> l | Some p -> p::l in - let add_goal l = match get_p with None -> l | Some p -> p::l in - let add_both_std goal l = - match get_p with None -> l | Some p -> (goal,p) :: l - in - let add_both goal l = - match get_p with - | None -> l - | Some p -> - let kind = if goal then CPboth else CPhyp in - (kind, p)::l - in - let add_pre l = - match get_p with - | None -> l - | Some p -> (CPgoal, p) :: l - in - let add_for_call fct calls = - let l = try ForCall.find fct calls with Not_found -> [] in - ForCall.add fct (add_hyp l) calls in - let add_both_call fct goal calls = - let l = try ForCall.find fct calls with Not_found -> [] in - ForCall.add fct (add_both goal l) calls in - let add_pre_call fct calls = - let l = try ForCall.find fct calls with Not_found -> [] in - ForCall.add fct (add_pre l) calls - in - let info = acc.info in - let goal, info = match kind with - | Ahyp -> - false, { info with p_hyp = add_hyp info.p_hyp } - | Agoal -> - true, { info with p_goal = add_goal info.p_goal } - | Aboth goal -> - goal, { info with p_both = add_both_std goal info.p_both } - | AcutB goal -> - goal, { info with p_cut = add_both_std goal info.p_cut } - | AcallHyp fct -> - false, { info with call_hyp = add_for_call fct info.call_hyp } - | AcallPre (goal,fct) -> - goal, { info with call_pre = add_both_call fct goal info.call_pre } - | AcallCheck fct -> - true, { info with call_pre = add_pre_call fct info.call_pre } - | AcallPost fct -> - true, { info with call_post = add_for_call fct info.call_post } - in let acc = { acc with info = info } in - if goal then { acc with has_prop_goal = true} else acc - -(* -------------------------------------------------------------------------- *) -(* adding some specific properties. *) -let add_prop_fct_pre_bhv acc kind kf bhv = - let norm_pred pred = - let p = Logic_const.pred_of_id_pred pred in - Logic_const.(pat (p,pre_label)) - in - let requires = - List.filter (fun x -> use_predicate x.ip_content.tp_kind) bhv.b_requires - in - let requires = Logic_const.pands (List.map norm_pred requires) in - let assumes = Logic_const.pands (List.map norm_pred bhv.b_assumes) in - let precond = Logic_const.pimplies (assumes, requires) in - let precond_id = Logic_const.new_predicate precond in - let id = WpPropId.mk_pre_id kf Kglobal bhv precond_id in - let labels = NormAtLabels.labels_fct_pre in - let p = normalize id labels precond in - add_prop acc kind id p - -let add_prop_fct_pre acc kind kf bhv ~assumes pre = - if use_predicate pre.ip_content.tp_kind then begin - let id = WpPropId.mk_pre_id kf Kglobal bhv pre in - let labels = NormAtLabels.labels_fct_pre in - let p = Logic_const.pred_of_id_pred pre in - let p = Logic_const.(pat (p,pre_label)) in - let p = normalize id ?assumes labels p in - add_prop acc kind id p - end else acc - -let add_prop_fct_post acc kind kf bhv tkind post = - if verify_predicate post.ip_content.tp_kind then begin - let id = WpPropId.mk_fct_post_id kf bhv (tkind, post) in - let labels = NormAtLabels.labels_fct_post ~exit:false in - let p = Logic_const.pred_of_id_pred post in - let p = normalize id labels p in - add_prop acc kind id p - end else acc - -let add_prop_fct_bhv_pre acc kind kf bhv = - let assumes = None in - let add acc p = add_prop_fct_pre acc kind kf bhv ~assumes p in - let acc = List.fold_left add acc bhv.b_requires in - List.fold_left add acc bhv.b_assumes - -let add_prop_stmt_pre acc kind kf s bhv ~assumes pre = - if use_predicate pre.ip_content.tp_kind then begin - let id = WpPropId.mk_pre_id kf (Kstmt s) bhv pre in - let labels = NormAtLabels.labels_stmt_pre ~kf s in - let p = Logic_const.pred_of_id_pred pre in - let p = normalize id labels ?assumes p in - add_prop acc kind id p - end else acc - -let add_prop_stmt_bhv_requires acc kind kf s bhv ~with_assumes = - let assumes = - if with_assumes then Some (Ast_info.behavior_assumes bhv) else None - in let add acc pre = - add_prop_stmt_pre acc kind kf s bhv ~assumes pre - in List.fold_left add acc bhv.b_requires - -(** Process the stmt spec precondition as an hypothesis for external properties. - * Add [assumes => requires] for all the behaviors. *) -let add_prop_stmt_spec_pre acc kind kf s spec = - let add_bhv_pre acc bhv = - add_prop_stmt_bhv_requires acc kind kf s bhv ~with_assumes:true - in List.fold_left add_bhv_pre acc spec.spec_behavior - -let add_prop_stmt_post acc kind kf s bhv tkind l_post ~assumes post = - if verify_predicate post.ip_content.tp_kind then begin - let id = WpPropId.mk_stmt_post_id kf s bhv (tkind, post) in - let labels = NormAtLabels.labels_stmt_post_l ~kf s l_post in - let p = Logic_const.pred_of_id_pred post in - let p = normalize id labels ?assumes p in - add_prop acc kind id p - end else acc - -let update_kind kind pre = - if pre.ip_content.tp_kind = Check then begin - match kind with - | AcallPre(false,_) -> None - | AcallPre(true, kf) -> Some (AcallCheck kf) - | _ -> Some kind - end else Some kind - -let add_prop_call_pre acc kind id ~assumes pre = - match update_kind kind pre with - | None -> acc - | Some kind -> - let labels = NormAtLabels.labels_fct_pre in - let p = Logic_const.pred_of_id_pred pre in - (* assumes can be normalized in the same time *) - let p = Logic_const.pimplies (assumes, p) in - let p = normalize id labels p in - add_prop acc kind id p - -let add_prop_call_post acc kind called_kf bhv tkind ~assumes post = - let id = WpPropId.mk_fct_post_id called_kf bhv (tkind, post) in - let labels = NormAtLabels.labels_fct_post ~exit:false in - let p = Logic_const.pred_of_id_pred post in - let p = normalize id labels ~assumes p in - add_prop acc kind id p - -let add_prop_assert acc kind kf s ca p = - let id = WpPropId.mk_assert_id kf s ca in - let labels = NormAtLabels.labels_assert ~kf s in - let p = normalize id labels p in - add_prop acc kind id p - -let add_prop_loop_inv acc kind s id p = - let labels = NormAtLabels.labels_loop s in - let p = normalize id labels p in - add_prop acc kind id p - -(** apply [f_normal] on the [Normal] postconditions, - * [f_exits] on the [Exits] postconditions, and warn on the others. *) -let fold_bhv_post_cond ~warn f_normal f_exits acc b = - let add (p_acc, e_acc) ((termination_kind, pe) as e) = - match termination_kind with - | Normal -> f_normal p_acc pe, e_acc - | Exits -> p_acc, f_exits e_acc pe - | Returns -> p_acc, e_acc (* HANDLED by an ASSERT from CIL *) - | (Breaks|Continues) -> (* TODO *) - begin - if warn then - Wp_parameters.warning - "Abrupt statement termination property ignored:@, %a" - Printer.pp_post_cond e; - p_acc, e_acc - end - in List.fold_left add acc b.b_post_cond - -(* -------------------------------------------------------------------------- *) -(* --- Smoke --- *) -(* -------------------------------------------------------------------------- *) - -let add_smoke acc kf ~id ?doomed ?unreachable () = - let id = WpPropId.mk_smoke kf ~id ?doomed ?unreachable () in - add_prop acc Agoal id (Some Logic_const.pfalse) - -let add_prop_fct_smoke acc kf bhv = - if bhv.b_requires = [] then acc else - let bname = - if Cil.is_default_behavior bhv then "default" else bhv.b_name in - let id = bname ^ "_requires" in - let doomed = Property.ip_requires_of_behavior kf Kglobal bhv in - add_smoke acc kf ~id ~doomed () - -let add_prop_dead_loop acc kf stmt = - add_smoke acc kf ~id:"dead_loop" ~unreachable:stmt () - -let add_prop_dead_code acc kf stmt = - add_smoke acc kf ~id:"dead_code" ~unreachable:stmt () - -let add_prop_dead_call kf stmt acc_posts acc_exits = - let id = WpPropId.mk_smoke kf ~id:"dead_call" ~unreachable:stmt () in - let kind = AcallPost kf in - let pred = Some Logic_const.pfalse in - add_prop acc_posts kind id pred , add_prop acc_exits kind id pred - -(* -------------------------------------------------------------------------- *) - -let from_has_deps = function _, FromAny -> false | _, From _ -> true -let assigns_has_deps = function - | WritesAny -> false - | Writes l -> List.exists from_has_deps l - -let add_assigns acc kind id a_desc = - let take_assigns () = - debug "take %a %a" WpPropId.pp_propid id WpPropId.pp_assigns_desc a_desc; - WpPropId.mk_assigns_info id a_desc - in - let take_assigns_call fct info = - let asgn = take_assigns () in - { info with call_asgn = ForCall.add fct asgn info.call_asgn } - in - let info = acc.info in - let goal, info = match kind with - | Ahyp -> false, {info with a_hyp = take_assigns ()} - | AcallHyp fct -> false, take_assigns_call fct info - | Agoal -> true, {info with a_goal = take_assigns ()} - | _ -> Wp_parameters.fatal "Assigns prop can only be Hyp or Goal" - in let acc = { acc with info = info } in - if goal && assigns_has_deps a_desc.a_assigns then - Wp_parameters.warning - ~once: true ~current:false ~wkey:AssignsCompleteness.wkey_pedantic - "WP uses \\from to generate precise hypotheses, however their proof is \ - not supported yet" ; - if goal then { acc with has_asgn_goal = true} else acc - -let add_assigns_any acc kind asgn = - let take_call fct asgn info = - { info with call_asgn = ForCall.add fct asgn info.call_asgn } in - match kind with - | Ahyp -> {acc with info = { acc.info with a_hyp = asgn}} - | AcallHyp fct -> {acc with info = take_call fct asgn acc.info} - | _ -> Wp_parameters.fatal "Assigns Any prop can only be Hyp" - -let assigns_upper_bound spec = - let bhvs = spec.spec_behavior in - let upper a b = - match a, b.b_assigns with - | None, Writes a when Cil.is_default_behavior b -> - Some (b,a) (* default behavior always applies. *) - | None, _ -> None (* WritesAny U X -> WritesAny *) - | Some (b,_), _ when Cil.is_default_behavior b -> - a (* default behavior prevails over other behaviors. *) - | Some _, WritesAny -> - None (* No default behavior and one behavior assigns everything. *) - | Some(b,a1), Writes a2 -> Some (b,a1 @ a2) - (* take the whole list of assigns. *) - in - match bhvs with - | [] -> None - | bhv::bhvs -> - (* [VP 2011-02-04] Note that if there is no default and each - behavior has a proper assigns clause we put dependencies only - to the assigns of a more or less randomly selected behavior, - but the datatypes above can't handle anything better. *) - let acc = - match bhv.b_assigns with - WritesAny -> None - | Writes a -> Some(bhv,a) - in - List.fold_left upper acc bhvs - -(* [VP 2011-02-04] These two functions below mix all the assigns of - a function regardless of the behavior. At least now that we take - WritesAny as soon as at least one behavior has no assigns clause, - this is correct, but still imprecise. Needs refactoring of t_annots to - go further, though. - [AP 2011-03-11] I think that the merge of all assigns properties - is intended because we are using it as an hypothesis to skip the statement - or the function call. -*) -let add_stmt_spec_assigns_hyp acc kf s l_post spec = - match assigns_upper_bound spec with - | None -> - add_assigns_any acc Ahyp - (WpPropId.mk_stmt_any_assigns_info s) - | Some(bhv, assigns) -> - (* We are always using the spec covering all possible parent behaviors. *) - let id = WpPropId.mk_stmt_assigns_id kf s [] bhv assigns in - match id with - | None -> add_assigns_any acc Ahyp - (WpPropId.mk_stmt_any_assigns_info s) - | Some id -> - let labels = NormAtLabels.labels_stmt_assigns_l ~kf s l_post in - let assigns = NormAtLabels.preproc_assigns labels assigns in - let a_desc = WpPropId.mk_stmt_assigns_desc s assigns in - add_assigns acc Ahyp id a_desc - -let add_call_assigns_any acc s = - let asgn = WpPropId.mk_stmt_any_assigns_info s in - {acc with info = { acc.info with a_call = asgn }} - -let add_call_assigns_hyp acc kf_caller s ~called_kf l_post spec_opt = - match spec_opt with - | None -> - let pid = WpPropId.mk_stmt_any_assigns_info s in - add_assigns_any acc (AcallHyp called_kf) pid - | Some spec -> - match assigns_upper_bound spec with - | None -> - let asgn = WpPropId.mk_stmt_any_assigns_info s in - add_assigns_any acc (AcallHyp called_kf) asgn - | Some(bhv, assigns) -> - (* we're taking assigns from a function contract. - They're not subject to any active behavior. *) - let id = WpPropId.mk_stmt_assigns_id kf_caller s [] bhv assigns in - match id with - | None -> - let asgn = WpPropId.mk_stmt_any_assigns_info s in - add_assigns_any acc (AcallHyp called_kf) asgn - | Some pid -> - ignore l_post ; - (* let kf = kf_caller in *) - let labels = NormAtLabels.labels_fct_assigns ~exit:false (* ~kf s l_post *) in - let assigns = NormAtLabels.preproc_assigns labels assigns in - let a_desc = WpPropId.mk_stmt_assigns_desc s assigns in - add_assigns acc (AcallHyp called_kf) pid a_desc - -(* [VP 2011-01-28] following old behavior, not sure it is correct: - why should we give to add_assigns the assigns with unnormalized labels? - [AP 2011-03-11] to answer VP question, the source assigns are only used to - build an identifier for the property which is use later to update its status - and dependencies so we need to have the original one. -*) -let add_loop_assigns_hyp acc kf s asgn_opt = match asgn_opt with - | None -> - let asgn = WpPropId.mk_loop_any_assigns_info s in - add_assigns_any acc Ahyp asgn - | Some (ca, assigns) -> - let id = WpPropId.mk_loop_assigns_id kf s ca assigns in - match id with - | None -> - let asgn = WpPropId.mk_loop_any_assigns_info s in - add_assigns_any acc Ahyp asgn - | Some id -> - let labels = NormAtLabels.labels_loop s in - let assigns' = NormAtLabels.preproc_assigns labels assigns in - let a_desc = WpPropId.mk_loop_assigns_desc s assigns' in - add_assigns acc Ahyp id a_desc - -let add_fct_bhv_assigns_hyp acc kf tkind b = match b.b_assigns with - | WritesAny -> - let id = WpPropId.mk_kf_any_assigns_info () in - add_assigns_any acc Ahyp id - | Writes assigns -> - let has_exit = Cil2cfg.has_exit (Cil2cfg.get kf) in - let id = WpPropId.mk_fct_assigns_id kf has_exit b tkind assigns in - match id with - | None -> - let id = WpPropId.mk_kf_any_assigns_info () in - add_assigns_any acc Ahyp id - | Some id -> - let labels = NormAtLabels.labels_fct_assigns ~exit:false in - let assigns' = NormAtLabels.preproc_assigns labels assigns in - let a_desc = WpPropId.mk_kf_assigns_desc assigns' in - add_assigns acc Ahyp id a_desc - -(* --- Get annotations --- *) - -let get_goal_only annots = annots.info.p_goal - -let get_hyp_only annots = annots.info.p_hyp - -let filter_both l = - let add (h_acc, g_acc) (goal, p) = - p::h_acc, if goal then p::g_acc else g_acc - in List.fold_left add ([], []) l - -let filter_both_call l = - let add (h_acc, g_acc) (goal, p) = - match goal with - | CPboth -> p :: h_acc, p :: g_acc - | CPhyp -> p :: h_acc, g_acc - | CPgoal -> h_acc, p :: g_acc - in List.fold_left add ([], []) l - -let get_both_hyp_goals annots = filter_both annots.info.p_both - -let get_call_hyp annots fct = - try ForCall.find fct annots.info.call_hyp - with Not_found -> [] - -let get_call_pre annots fct = - try filter_both_call (ForCall.find fct annots.info.call_pre) - with Not_found -> [],[] - -let get_call_post annots fct = - try ForCall.find fct annots.info.call_post - with Not_found -> [] - -let get_call_asgn annots = function - | None -> annots.info.a_call - | Some fct -> - try ForCall.find fct annots.info.call_asgn - with Not_found -> WpPropId.empty_assigns_info - -let get_cut annots = annots.info.p_cut - -let get_asgn_hyp annots = annots.info.a_hyp - -let get_asgn_goal annots = annots.info.a_goal - -(* --- Print annotations --- *) - -let pp_annots fmt acc = - let acc = acc.info in - let pp_pred k b p = - Format.fprintf fmt "%s%s: %a@." - k (if b then "" else " (h)") WpPropId.pp_pred_of_pred_info p - in - let pp_pred_c k c p = - let kind = match c with CPboth -> "(h+g)" | CPgoal -> "g" | CPhyp -> "h" in - Format.fprintf fmt "%s%s: %a@." - k kind WpPropId.pp_pred_of_pred_info p - in - let pp_pred_list k l = List.iter (fun p -> pp_pred k true p) l in - let pp_pred_b_list k l = List.iter (fun (b, p) -> pp_pred k b p) l in - let pp_pred_c_list k l = List.iter (fun (c, p) -> pp_pred_c k c p) l in - begin - pp_pred_list "H" acc.p_hyp; - pp_pred_list "G" acc.p_goal; - pp_pred_b_list "H+G" acc.p_both; - pp_pred_b_list "C" acc.p_cut; - ForCall.iter - (fun kf hs -> - let name = "CallHyp:" ^ (Kernel_function.get_name kf) in - pp_pred_list name hs) - acc.call_hyp; - ForCall.iter - (fun kf bhs -> - let name = "CallPre:" ^ (Kernel_function.get_name kf) in - pp_pred_c_list name bhs) - acc.call_pre; - ForCall.iter - (fun kf asgn -> - let name = "CallAsgn:" ^ (Kernel_function.get_name kf) in - WpPropId.pp_assign_info name fmt asgn) - acc.call_asgn; - WpPropId.pp_assign_info "DC" fmt acc.a_call; - WpPropId.pp_assign_info "HA" fmt acc.a_hyp; - WpPropId.pp_assign_info "GA" fmt acc.a_goal; - end - -let merge_calls f call1 call2 = - ForCall.merge - (fun _fct a b -> match a,b with - | None,c | c,None -> c - | Some a,Some b -> Some (f a b) - ) call1 call2 - -(* TODO: it should be possible to do without this, but needs a big refactoring*) -let merge_acc acc1 acc2 = - { - p_hyp = acc1.p_hyp @ acc2.p_hyp; - p_goal = acc1.p_goal @ acc2.p_goal; - p_both = acc1.p_both @ acc2.p_both; - p_cut = acc1.p_cut @ acc2.p_cut; - call_hyp = merge_calls (@) acc1.call_hyp acc2.call_hyp; - call_pre = merge_calls (@) acc1.call_pre acc2.call_pre; - call_post = merge_calls (@) acc1.call_post acc2.call_post; - call_asgn = merge_calls WpPropId.merge_assign_info acc1.call_asgn acc2.call_asgn; - a_goal = WpPropId.merge_assign_info acc1.a_goal acc2.a_goal; - a_hyp = WpPropId.merge_assign_info acc1.a_hyp acc2.a_hyp; - a_call = WpPropId.merge_assign_info acc1.a_call acc2.a_call; - } - -(* -------------------------------------------------------------------------- *) -(* --- Annotation table --- *) -(* -------------------------------------------------------------------------- *) - -(** This is an Hashtbl where some predicates are stored on CFG edges. - * On each edge, we store hypotheses and goals. -*) -module Hannots = Cil2cfg.HE (struct type t = annots end) - -type annots_tbl = { - tbl_annots : Hannots.t; - mutable tbl_axioms : WpPropId.axiom_info list; - mutable tbl_has_prop_goal : bool; - mutable tbl_has_asgn_goal : bool; -} - -let create_tbl () = { - tbl_annots = Hannots.create 7; - tbl_axioms = []; - tbl_has_prop_goal = false; - tbl_has_asgn_goal = false; -} - -let add_on_edges tbl new_acc edges = - if new_acc.has_prop_goal then tbl.tbl_has_prop_goal <- true; - if new_acc.has_asgn_goal then tbl.tbl_has_asgn_goal <- true; - let add_on_edge e = - let acc = - try - let acc = Hannots.find tbl.tbl_annots e in - merge_acc new_acc.info acc - with Not_found -> new_acc.info - in Hannots.replace tbl.tbl_annots e acc; - in List.iter add_on_edge edges - -let add_node_annots tbl cfg v (before, (post, exits)) = - debug "[add_node_annots] on %a@." Cil2cfg.pp_node v; - add_on_edges tbl before (Cil2cfg.get_pre_edges cfg v); - if post <> empty_acc then - begin - let edges_after = Cil2cfg.get_post_edges cfg v in - if edges_after = [] - then (* unreachable (see [process_unreached_annots]) *) () - else add_on_edges tbl post edges_after - end; - if exits <> empty_acc then - begin - let edges_exits = Cil2cfg.get_exit_edges cfg v in - if edges_exits = [] - then (* unreachable (see [process_unreached_annots]) *) () - else add_on_edges tbl exits edges_exits - end - -let add_loop_annots tbl cfg vloop ~entry ~back ~core = - debug "[add_loop_annots] on %a@."Cil2cfg.pp_node vloop; - let edges_to_head = Cil2cfg.succ_e cfg vloop in - debug "[add_loop_annots] %d edges_to_head" (List.length edges_to_head); - let edges_to_loop = Cil2cfg.pred_e cfg vloop in - debug "[add_loop_annots] %d edges_to_loop" (List.length edges_to_loop); - let back_edges, entry_edges = - List.partition Cil2cfg.is_back_edge edges_to_loop - in - debug "[add_loop_annots] %d back_edges + %d entry_edges" - (List.length back_edges) (List.length entry_edges); - add_on_edges tbl entry entry_edges; - debug "[add_loop_annots on entry_edges ok]@."; - add_on_edges tbl back back_edges; - debug "[add_loop_annots on back_edges ok]@."; - add_on_edges tbl core edges_to_head; - debug "[add_loop_annots on edges_to_head ok]@." - -let add_axiom tbl lemma = - try - (* Labels does not need normalization *) - let axiom = WpPropId.mk_axiom_info lemma in - debug "take %a@." WpPropId.pp_axiom_info axiom; - tbl.tbl_axioms <- axiom::tbl.tbl_axioms - with e -> - NormAtLabels.catch_label_error e ("axiom "^lemma.lem_name) "axiom" - -let add_all_axioms tbl = - let rec do_g g = - match g with - | Daxiomatic (_ax_name, globs,_,_) -> do_globs globs - | Dlemma (name,_,_,_,_,_) -> - let lem = LogicUsage.logic_lemma name in - add_axiom tbl lem - | _ -> () - and do_globs globs = List.iter do_g globs in - Annotations.iter_global (fun _ -> do_g) - -let get_annots tbl e = - try (* TODO clean : this is not very nice ! *) - let info = Hannots.find tbl.tbl_annots e in { empty_acc with info = info} - with Not_found -> empty_acc - -(* -------------------------------------------------------------------------- *) -(* --- Strategy --- *) -(* -------------------------------------------------------------------------- *) - -type strategy_for_froms = { - get_pre : unit -> t_annots; - more_vars : logic_var list -} - -type strategy_kind = - | SKannots (* normal mode for annotations *) - | SKfroms of strategy_for_froms - -(* an object of this type is the only access to annotations - * from the rest of the application. - * The idea is to be able to tune which properties to use for a computation. *) -type strategy = { - desc : string ; - cfg : Cil2cfg.t; - behavior_name : string option ; - strategy_kind : strategy_kind; - annots : annots_tbl; -} - - -let get_kf s = Cil2cfg.cfg_kf s.cfg -let get_bhv s = s.behavior_name - -let is_default_behavior s = - match s.behavior_name with None -> true | Some _ -> false - -let mk_strategy desc cfg bhv_name kind tbl = { - desc = desc; cfg = cfg; behavior_name = bhv_name; - strategy_kind = kind; annots = tbl; -} - -let cfg_of_strategy strat = strat.cfg -let behavior_name_of_strategy strat = strat.behavior_name -let global_axioms strat = strat.annots.tbl_axioms -let strategy_kind strat = strat.strategy_kind -let strategy_has_prop_goal strat = strat.annots.tbl_has_prop_goal -let strategy_has_asgn_goal strat = strat.annots.tbl_has_asgn_goal -let get_annots strat = get_annots strat.annots - -let pp_info_of_strategy fmt strat = - Format.fprintf fmt "@[%s@]" strat.desc - -(* -------------------------------------------------------------------------- *) -(* --- Helpers --- *) -(* -------------------------------------------------------------------------- *) - -let is_main_init kf = - if Kernel.LibEntry.get () then false - else - let is_main = - try - let main, _ = Globals.entry_point () in - Kernel_function.equal kf main - with Globals.No_such_entry_point _ -> false - in - debug "'%a' is %sthe main entry point@." - Kernel_function.pretty kf (if is_main then "" else "NOT "); - is_main - -let isInitConst () = Wp_parameters.Init.get () - -let isGlobalInitConst var = - var.vglob && var.vstorage <> Extern && Cil.typeHasQualifier "const" var.vtype - -let mk_variant_properties kf s ca v = - let vpos_id = WpPropId.mk_var_pos_id kf s ca in - let vdecr_id = WpPropId.mk_var_decr_id kf s ca in - let loc = v.term_loc in - let lcurr = Clabels.to_logic (Clabels.loop_current s) in - let vcurr = Logic_const.tat ~loc (v, lcurr) in - let zero = Cil.lzero ~loc () in - let vpos = Logic_const.prel ~loc (Rle, zero, vcurr) in - let vdecr = Logic_const.prel ~loc (Rlt, v, vcurr) in - (vpos_id, vpos), (vdecr_id, vdecr) - -(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/wpStrategy.mli b/src/plugins/wp/wpStrategy.mli deleted file mode 100644 index d69d701af37f6c607894f7282ee16ec479c7009c..0000000000000000000000000000000000000000 --- a/src/plugins/wp/wpStrategy.mli +++ /dev/null @@ -1,302 +0,0 @@ -(**************************************************************************) -(* *) -(* This file is part of WP plug-in of Frama-C. *) -(* *) -(* Copyright (C) 2007-2021 *) -(* CEA (Commissariat a l'energie atomique et aux energies *) -(* 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 Clabels - -(* -------------------------------------------------------------------------- *) -(** This file provide all the functions to build a strategy that can then - * be used by the main generic calculus. *) -(* -------------------------------------------------------------------------- *) - -(* -------------------------------------------------------------------------- *) -(** {2 Annotations} *) -(* -------------------------------------------------------------------------- *) - -(** a set of annotations to be added to a program point. *) -type t_annots - -val empty_acc : t_annots - -(** {3 How to use an annotation} *) - -(** An annotation can be used for different purpose. *) -type annot_kind = - | Ahyp (** annotation is an hypothesis, - but not a goal (see Aboth) : A => ...*) - | Agoal (** annotation is a goal, - but not an hypothesis (see Aboth): A /\ ...*) - | Aboth of bool (** annotation can be used as both hypothesis and goal : - - with true : considered as both : A /\ A=>.. - - with false : we just want to use it as hyp right now. *) - | AcutB of bool (** annotation is use as a cut : - - with true (A is also a goal) -> A (+ proof obligation A => ...) - - with false (A is an hyp only) -> True (+ proof obligation A => ...) *) - | AcallHyp of kernel_function - (** annotation is a called function property to consider as an Hyp. - * The pre are not here but in AcallPre since they can also - * be considered as goals. *) - | AcallPre of bool * kernel_function - (** annotation is a called function precondition : - to be considered as hyp, and goal if bool=true *) - | AcallCheck of kernel_function - (** annotation is check-only called function precondition. - handled internally by {!add_prop_call_pre} below. *) - | AcallPost of kernel_function - (** annotation is a called function post check : - to be considered as goal only (no hyp) *) - -(** {3 Adding properties (predicates)} *) - -val normalize : WpPropId.prop_id -> - ?assumes:predicate -> - NormAtLabels.label_mapping -> - predicate -> predicate option - -(** generic function to add a predicate property after normalisation. - * All the [add_prop_xxx] functions below use this one. *) -val add_prop : t_annots -> annot_kind -> WpPropId.prop_id -> predicate option -> t_annots - -val add_prop_fct_pre_bhv : - t_annots -> annot_kind -> Cil_types.kernel_function -> - Cil_types.funbehavior -> t_annots - -(** Add the predicate as a function precondition. - * Add [assumes => pre] if [assumes] is given. *) -val add_prop_fct_pre : t_annots -> annot_kind -> - kernel_function -> funbehavior -> - assumes: predicate option -> identified_predicate -> t_annots - -(** Add the preconditions of the behavior *) -val add_prop_fct_bhv_pre : t_annots -> annot_kind -> - kernel_function -> funbehavior -> t_annots - -(** Add Smoke Test behavior *) -val add_prop_fct_smoke : t_annots -> kernel_function -> funbehavior -> t_annots - -(** Add Smoke Test for loop *) -val add_prop_dead_loop : t_annots -> kernel_function -> stmt -> t_annots - -(** Add Smoke Test for possibly dead code *) -val add_prop_dead_code : t_annots -> kernel_function -> stmt -> t_annots - -(** Add Smoke Test for possibly dead call : (posts,exits) *) -val add_prop_dead_call : kernel_function -> stmt -> t_annots -> t_annots -> - t_annots * t_annots - -val add_prop_fct_post : t_annots -> annot_kind -> - kernel_function -> funbehavior -> termination_kind -> identified_predicate - -> t_annots - -(** Add the predicate as a stmt precondition. - * Add [assumes => pre] if [assumes] is given. *) -val add_prop_stmt_pre : t_annots -> annot_kind -> - kernel_function -> stmt -> funbehavior -> - assumes: predicate option -> identified_predicate -> t_annots - -(** Add the predicate as a stmt precondition. - * Add [\old (assumes) => post] if [assumes] is given. *) -val add_prop_stmt_post :t_annots -> annot_kind -> - kernel_function -> stmt -> funbehavior -> termination_kind -> - c_label option -> assumes:predicate option -> identified_predicate - -> t_annots - -(** Add all the [b_requires]. Add [b_assumes => b_requires] if [with_assumes] *) -val add_prop_stmt_bhv_requires : t_annots -> annot_kind -> - kernel_function -> stmt -> funbehavior -> with_assumes:bool -> t_annots - -(** Process the stmt spec precondition as an hypothesis for external properties. - * Add [assumes => requires] for all the behaviors. *) -val add_prop_stmt_spec_pre : t_annots -> annot_kind -> - kernel_function -> stmt -> funspec -> t_annots - -val add_prop_call_pre : t_annots -> annot_kind -> WpPropId.prop_id -> - assumes:predicate -> identified_predicate -> t_annots - -(** Add a postcondition of a called function. Beware that [kf] and [bhv] - * are the called one. *) -val add_prop_call_post : t_annots -> annot_kind -> - kernel_function -> funbehavior -> termination_kind -> - assumes:predicate -> identified_predicate -> t_annots - -val add_prop_assert : t_annots -> annot_kind -> - kernel_function -> stmt -> code_annotation -> predicate -> t_annots - -val add_prop_loop_inv : t_annots -> annot_kind -> - stmt -> WpPropId.prop_id -> predicate -> t_annots - -(** {3 Adding assigns properties} *) - -(** generic function to add an assigns property. *) -val add_assigns : t_annots -> annot_kind -> - WpPropId.prop_id -> WpPropId.assigns_desc -> t_annots - -(** generic function to add a WriteAny assigns property. *) -val add_assigns_any : t_annots -> annot_kind -> - WpPropId.assigns_full_info -> t_annots - -(** shortcut to add a stmt spec assigns property as an hypothesis. *) -val add_stmt_spec_assigns_hyp : t_annots -> kernel_function -> stmt -> - c_label option -> funspec -> t_annots - -(** short cut to add a dynamic call *) -val add_call_assigns_any : t_annots -> stmt -> t_annots - -(** shortcut to add a call assigns property as an hypothesis. *) -val add_call_assigns_hyp : t_annots -> kernel_function -> stmt -> - called_kf:kernel_function -> - c_label option -> funspec option -> t_annots - -(** shortcut to add a loop assigns property as an hypothesis. *) -val add_loop_assigns_hyp : t_annots -> kernel_function -> stmt -> - (code_annotation * from list) option -> t_annots - -val add_fct_bhv_assigns_hyp : t_annots -> kernel_function -> termination_kind -> - funbehavior -> t_annots - -val assigns_upper_bound : - funspec -> (funbehavior * from list) option - -(** {3 Getting information from annotations} *) - -val get_hyp_only : t_annots -> WpPropId.pred_info list -val get_goal_only : t_annots -> WpPropId.pred_info list -val get_both_hyp_goals : t_annots -> - WpPropId.pred_info list * WpPropId.pred_info list - -(** the [bool] in [get_cut] results says if the property has to be - * considered as a both goal and hyp ([goal=true], or hyp only ([goal=false]) *) -val get_cut : t_annots -> (bool * WpPropId.pred_info) list - -(** To be used as hypotheses around a call, (the pre are in - * [get_call_pre_goal]) *) -val get_call_hyp : t_annots -> kernel_function -> WpPropId.pred_info list - -(** Preconditions of a called function to be considered as hyp and goal - * (similar to [get_both_hyp_goals]). *) -val get_call_pre : t_annots -> kernel_function -> WpPropId.pred_info list * WpPropId.pred_info list - -(** Post-checks of a called function to be considered as goal only *) -val get_call_post : t_annots -> kernel_function -> WpPropId.pred_info list - -val get_call_asgn : t_annots -> kernel_function option -> WpPropId.assigns_full_info - - -val get_asgn_hyp : t_annots -> WpPropId.assigns_full_info -val get_asgn_goal : t_annots -> WpPropId.assigns_full_info - -(** {3 Printing} *) - -val pp_annots : Format.formatter -> t_annots -> unit - -(* -------------------------------------------------------------------------- *) -(** {2 Annotation table} *) -(* -------------------------------------------------------------------------- *) - -type annots_tbl - -val create_tbl : unit -> annots_tbl - -val add_on_edges : annots_tbl -> t_annots -> Cil2cfg.edge list -> unit - -(** [add_node_annots cfg annots v (before, (after, exits))] - * add the annotations for the node : - * @param before preconditions - * @param after postconditions - * @param exits \exits properties -*) -val add_node_annots : annots_tbl -> Cil2cfg.t -> Cil2cfg.node -> - (t_annots * (t_annots * t_annots)) -> unit - -val add_loop_annots : annots_tbl -> Cil2cfg.t -> Cil2cfg.node -> - entry:t_annots -> back:t_annots -> core:t_annots -> unit - -val add_axiom : annots_tbl -> LogicUsage.logic_lemma -> unit - -val add_all_axioms : annots_tbl -> unit - -(* -------------------------------------------------------------------------- *) -(** {2 Strategy} *) -(* -------------------------------------------------------------------------- *) - -type strategy - -type strategy_for_froms = { - get_pre : unit -> t_annots; - more_vars : logic_var list -} - -type strategy_kind = - | SKannots (** normal mode for annotations *) - | SKfroms of strategy_for_froms - -val mk_strategy : string -> Cil2cfg.t -> string option -> - strategy_kind -> annots_tbl -> strategy - -val get_annots : strategy -> Cil2cfg.edge -> t_annots -val strategy_has_asgn_goal : strategy -> bool -val strategy_has_prop_goal : strategy -> bool -val strategy_kind : strategy -> strategy_kind -val global_axioms : strategy -> WpPropId.axiom_info list -val behavior_name_of_strategy : strategy -> string option -val is_default_behavior : strategy -> bool - -val cfg_of_strategy : strategy -> Cil2cfg.t - -val get_kf : strategy -> kernel_function -val get_bhv : strategy -> string option - -val pp_info_of_strategy : Format.formatter -> strategy -> unit - -(* -------------------------------------------------------------------------- *) -(** {2 Other useful things} *) -(* -------------------------------------------------------------------------- *) - -(** The function is the main entry point AND it is not a lib entry *) -val is_main_init : Cil_types.kernel_function -> bool - - -(** True if both options [-const-readonly] and [-wp-init] are positioned, - and the variable is global, not extern, with a ["const"] type - (see [hasConstAttribute]). - @since Sodium-20150201 -*) -val isInitConst : unit -> bool - -(** True if the variable is global, not extern, with a ["const"] qualifier type. - {b Should} only apply when [isInitConst] is true. - @since Sodium-20150201 -*) -val isGlobalInitConst : varinfo -> bool - -(** apply [f_normal] on the [Normal] postconditions, - * [f_exits] on the [Exits] postconditions, and warn on the others. *) -val fold_bhv_post_cond : warn:bool -> - ('n_acc -> Cil_types.identified_predicate -> 'n_acc) -> - ('e_acc -> Cil_types.identified_predicate -> 'e_acc) -> - 'n_acc * 'e_acc -> funbehavior -> 'n_acc * 'e_acc - -val mk_variant_properties : - kernel_function -> stmt -> code_annotation -> term -> - (WpPropId.prop_id * predicate) - * (WpPropId.prop_id * predicate) -(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/wp_parameters.ml b/src/plugins/wp/wp_parameters.ml index d037da922d3448206ffcd2da3aea622730a853c8..f822f4567be925a02be6aa29e94a887128522a3c 100644 --- a/src/plugins/wp/wp_parameters.ml +++ b/src/plugins/wp/wp_parameters.ml @@ -55,13 +55,6 @@ module WP = end) let () = on_reset WP.clear -let () = Parameter_customize.set_group wp_generation -module Legacy = - False(struct - let option_name = "-wp-legacy" - let help = "Use legacy generator engine." - end) - let () = Parameter_customize.set_group wp_generation module Dump = Action(struct diff --git a/src/plugins/wp/wp_parameters.mli b/src/plugins/wp/wp_parameters.mli index 9c7961f3df2bda0a0235f294a216c67a41372501..1b2e995888b805f310e22c1cc9a346b314092a96 100644 --- a/src/plugins/wp/wp_parameters.mli +++ b/src/plugins/wp/wp_parameters.mli @@ -40,7 +40,6 @@ val iter_kf : (Kernel_function.t -> unit) -> unit module WP : Parameter_sig.Bool module Dump : Parameter_sig.Bool -module Legacy : Parameter_sig.Bool module Behaviors : Parameter_sig.String_list module Properties : Parameter_sig.String_list module StatusAll : Parameter_sig.Bool