From 32c9f5624b39f3e32ecfa4be034c1762096ecdff Mon Sep 17 00:00:00 2001 From: Allan Blanchard <allan.blanchard@cea.fr> Date: Wed, 2 Feb 2022 17:01:14 +0100 Subject: [PATCH] [kernel] First populate_spec version --- .gitattributes | 2 + .../typing/infer_annotations.ml | 2 - src/kernel_internals/typing/populate_spec.ml | 338 ++++++++++++++++++ src/kernel_internals/typing/populate_spec.mli | 23 ++ src/kernel_services/ast_queries/file.ml | 7 + 5 files changed, 370 insertions(+), 2 deletions(-) create mode 100644 src/kernel_internals/typing/populate_spec.ml create mode 100644 src/kernel_internals/typing/populate_spec.mli diff --git a/.gitattributes b/.gitattributes index 6b41e948311..9bc825fdf42 100644 --- a/.gitattributes +++ b/.gitattributes @@ -313,6 +313,8 @@ README* header_spec=.ignore /src/kernel_internals/typing/mergecil.mli header_spec=CIL /src/kernel_internals/typing/oneret.ml header_spec=CIL /src/kernel_internals/typing/oneret.mli header_spec=CIL +/src/kernel_internals/typing/populate_spec.ml header_spec=CEA_LGPL +/src/kernel_internals/typing/populate_spec.mli header_spec=CEA_LGPL /src/kernel_internals/typing/rmtmps.ml header_spec=CIL /src/kernel_internals/typing/rmtmps.mli header_spec=CIL /src/kernel_internals/typing/translate_lightweight.ml header_spec=CEA_INRIA_LGPL diff --git a/src/kernel_internals/typing/infer_annotations.ml b/src/kernel_internals/typing/infer_annotations.ml index b173d18b263..1053366953e 100644 --- a/src/kernel_internals/typing/infer_annotations.ml +++ b/src/kernel_internals/typing/infer_annotations.ml @@ -278,8 +278,6 @@ let populate_funspec kf spec = true ) -let () = Annotations.populate_spec_ref := populate_funspec - (* Local Variables: compile-command: "make -C ../../.." diff --git a/src/kernel_internals/typing/populate_spec.ml b/src/kernel_internals/typing/populate_spec.ml new file mode 100644 index 00000000000..96a87f76105 --- /dev/null +++ b/src/kernel_internals/typing/populate_spec.ml @@ -0,0 +1,338 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2022 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types + +type mode = ACSL | Safe | Frama_C | Other of string +type 'a result = Kept | Generated of 'a + +let default = Cil.default_behavior_name + +module type Generator = +sig + + type clause + type behaviors + + val has_behavior : string -> behaviors -> bool + val collect_behaviors : spec -> behaviors + val completes : string list list -> behaviors -> clause list option + + val acsl_default : unit -> clause + val safe_default : bool -> clause + val frama_c_default : unit -> clause + val combine_default : clause list -> clause + val custom_default : string -> kernel_function -> spec -> clause + +end + +module Make(G : Generator) = +struct + let get_default mode kf spec = + let table = G.collect_behaviors spec in + if G.has_behavior default table then + Kept + else if mode = ACSL then + Generated (G.acsl_default ()) + else + match G.completes spec.spec_complete_behaviors table with + | Some l -> + Generated (G.combine_default l) + | None when mode = Safe -> + Generated(G.safe_default @@ Kernel_function.has_definition kf) + | None when mode = Frama_C -> + Generated(G.frama_c_default ()) + | None -> + match mode with + | ACSL | Safe | Frama_C -> assert false + | Other mode -> + Generated(G.custom_default mode kf spec) +end + +module Terminates_generator = +struct + + type clause = identified_predicate option + type behaviors = bool + + let has_behavior name behaviors = + if name = default then behaviors else false + + let collect_behaviors spec = + None <> spec.spec_terminates + + let completes _ _ = None + + let acsl_default () = + Some(Logic_const.(new_predicate ptrue)) + + let safe_default has_body = + if has_body + then Some(Logic_const.(new_predicate ptrue)) + else Some(Logic_const.(new_predicate pfalse)) + + let frama_c_default () = + acsl_default () + + let combine_default _ = + acsl_default () + + let custom_default _mode _kf _spec = + acsl_default () + +end + +module Exits_generator = +struct + + type clause = (termination_kind * identified_predicate) list + type behaviors = (string, clause) Hashtbl.t + + let has_behavior name behaviors = + Hashtbl.mem behaviors name + + let collect_behaviors spec = + let table = Hashtbl.create 17 in + let iter b = + let exits = List.filter (fun (k, _ ) -> Exits = k) b.b_post_cond in + if exits <> [] then Hashtbl.add table b.b_name exits + in + List.iter iter spec.spec_behavior ; + table + + let completes completes table = + let exception Ok of clause list in + let collect l b = Hashtbl.find table b :: l in + let collect bhvs = + try let r = List.fold_left collect [] bhvs in raise (Ok r) + with Not_found -> () + in + try List.iter collect completes ; None with Ok l -> Some l + + let acsl_default () = [] + + let safe_default has_body = + if has_body + then [ Exits, Logic_const.(new_predicate pfalse) ] + else [] + + let frama_c_default () = + [ Exits, Logic_const.(new_predicate pfalse) ] + + let combine_default (clauses : clause list) = + let collect acc clauses = List.rev_append (List.rev clauses) acc in + let preds = + List.sort_uniq (Cil_datatype.Predicate.compare) @@ + List.map + (fun p -> p.ip_content.tp_statement) + (snd @@ List.split @@ List.fold_left collect [] clauses) + in + [ Exits, Logic_const.new_predicate @@ Logic_const.pors preds ] + + let custom_default _mode _kf _spec = + acsl_default () + +end + +module Assigns_generator = +struct + type clause = assigns + type behaviors = (string, assigns) Hashtbl.t + + let has_behavior name behaviors = + Hashtbl.mem behaviors name + + let collect_behaviors spec = + let table = Hashtbl.create 17 in + let iter { b_name ; b_assigns } = + if b_assigns <> WritesAny then Hashtbl.add table b_name b_assigns + in + List.iter iter spec.spec_behavior ; + table + + let completes (completes : string list list) (table : behaviors) = + let assigns = function + | WritesAny -> raise Not_found + | Writes l -> Writes l + in + let exception Ok of assigns list in + let collect l b = (assigns @@ Hashtbl.find table b) :: l in + let collect bhvs = + try let r = List.fold_left collect [] bhvs in raise (Ok r) + with Not_found -> () + in + try List.iter collect completes ; None with Ok l -> Some l + + let acsl_default () = + WritesAny + + let safe_default has_body = + if has_body + then Writes [] + else WritesAny + + let frama_c_default () = + acsl_default () (* todo : port infer annotations *) + + let compare_deps d1 d2 = + match d1, d2 with + | FromAny, FromAny -> 0 + | FromAny, _ -> 1 + | _, FromAny -> -1 + | From l1, From l2 -> + Extlib.list_compare Cil_datatype.Identified_term.compare l1 l2 + + let compare_from (f1, d1) (f2, d2) = + let r = Cil_datatype.Identified_term.compare f1 f2 in + if r <> 0 then compare_deps d1 d2 else r + + let combine_default clauses = + (* Note: combination is made on a set of complete behaviors in the sens of + [completes], thus here all clauses are [Writes ...] *) + let collect acc = function + | Writes l -> List.rev_append (List.rev l) acc + | _ -> assert false + in + let deps = function + | FromAny -> FromAny + | From l -> From (List.sort_uniq Cil_datatype.Identified_term.compare l) + in + let froms = + List.map (fun (e, ds) -> e, deps ds) @@ List.fold_left collect [] clauses + in + Writes (List.sort_uniq compare_from froms) + + let custom_default _mode _kf _spec = + acsl_default () + +end + +module Allocation_generator = +struct + type clause = allocation + type behaviors = (string, allocation) Hashtbl.t + + let has_behavior name behaviors = + Hashtbl.mem behaviors name + + let collect_behaviors spec = + let table = Hashtbl.create 17 in + let iter { b_name ; b_allocation } = + if b_allocation <> FreeAllocAny then Hashtbl.add table b_name b_allocation + in + List.iter iter spec.spec_behavior ; + table + + let completes (completes : string list list) (table : behaviors) = + let allocation = function + | FreeAllocAny -> raise Not_found + | alloc -> alloc + in + let exception Ok of allocation list in + let collect l b = (allocation @@ Hashtbl.find table b) :: l in + let collect bhvs = + try let r = List.fold_left collect [] bhvs in raise (Ok r) + with Not_found -> () + in + try List.iter collect completes ; None with Ok l -> Some l + + let acsl_default () = + FreeAlloc([],[]) + + let safe_default has_body = + if has_body + then FreeAlloc([],[]) + else FreeAllocAny + + let frama_c_default () = + acsl_default () + + let combine_default clauses = + (* Note: combination is made on a set of complete behaviors in the sens of + [completes], thus here all clauses are [FreeAlloc ...] *) + let collect (facc, aacc) = function + | FreeAlloc(f, a) -> + List.rev_append (List.rev f) facc, List.rev_append (List.rev a) aacc + | _ -> assert false + in + let f, a = List.fold_left collect ([],[]) clauses in + let f = List.sort_uniq Cil_datatype.Identified_term.compare f in + let a = List.sort_uniq Cil_datatype.Identified_term.compare a in + FreeAlloc(f, a) + + let custom_default _mode _kf _spec = + acsl_default () + +end + +module Terminates = Make(Terminates_generator) +module Exits = Make(Exits_generator) +module Assigns = Make(Assigns_generator) +module Allocation = Make(Allocation_generator) + +let emitter = Emitter.create "gen" [ Funspec ] ~correctness:[] ~tuning:[] + +let do_populate kf original_spec = + let mode = Frama_C in + + let exits = Exits.get_default mode kf original_spec in + let assigns = Assigns.get_default mode kf original_spec in + let allocation = Allocation.get_default mode kf original_spec in + let terminates = Terminates.get_default mode kf original_spec in + + let generated original = function + | Kept -> original + | Generated g -> g + in + + let bhv = Cil.mk_behavior () in + let bhv = { bhv with b_post_cond = generated bhv.b_post_cond exits } in + let bhv = { bhv with b_assigns = generated bhv.b_assigns assigns } in + let bhv = { bhv with b_allocation = generated bhv.b_allocation allocation } in + + let spec = Cil.empty_funspec () in + let spec = { spec with spec_behavior = [ bhv ] } in + let spec = + { spec with spec_terminates = generated spec.spec_terminates terminates } in + Annotations.add_spec emitter kf spec + +module Is_populated = + State_builder.Hashtbl + (Kernel_function.Hashtbl) + (Datatype.Unit) + (struct + let size = 17 + let dependencies = [ Annotations.funspec_state ] + let name = "Populate_spec.Is_populated" + end) + +let () = Ast.add_linked_state Is_populated.self + +let populate_funspec kf spec = + if Is_populated.mem kf then false + else begin + do_populate kf spec ; + Is_populated.add kf () ; + true + end + +let () = Annotations.populate_spec_ref := populate_funspec diff --git a/src/kernel_internals/typing/populate_spec.mli b/src/kernel_internals/typing/populate_spec.mli new file mode 100644 index 00000000000..90d98768a98 --- /dev/null +++ b/src/kernel_internals/typing/populate_spec.mli @@ -0,0 +1,23 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2022 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +val populate_funspec : Cil_types.kernel_function -> Cil_types.spec -> bool diff --git a/src/kernel_services/ast_queries/file.ml b/src/kernel_services/ast_queries/file.ml index ebd8cc54651..3b93f038bb9 100644 --- a/src/kernel_services/ast_queries/file.ml +++ b/src/kernel_services/ast_queries/file.ml @@ -1187,6 +1187,12 @@ let () = constglobfold syntactic_const_globals_substitution +let populate_spec () = + let add_spec kf = + ignore @@ Populate_spec.populate_funspec kf (Annotations.funspec kf) + in + Globals.Functions.iter add_spec + let prepare_cil_file ast = Kernel.feedback ~level:2 "preparing the AST"; computeCFG ~clear_id:true ast; @@ -1234,6 +1240,7 @@ let prepare_cil_file ast = Filecheck.check_ast ~ast "AST after annotation registration" end; Transform_after_cleanup.apply ast; + populate_spec () ; (* reset tables depending on AST in case they have been computed during the transformation. *) Ast.set_file ast -- GitLab