Skip to content
Snippets Groups Projects
slicingCmds.ml 29.33 KiB
(**************************************************************************)
(*                                                                        *)
(*  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).            *)
(*                                                                        *)
(**************************************************************************)

(** Those functions were previously outside the slicing module to show how to
 * use the slicing API. So, they are supposed to use the slicing module through
 * Db.Slicing only. There are mainly high level functions which make easier
 * to achieve simple tasks. *)

open Cil_types

type set = SlicingTypes.Fct_user_crit.t Cil_datatype.Varinfo.Map.t

let apply_all_actions () =
  SlicingParameters.debug ~level:1 "[Api.apply_all_internal]";
  SlicingParameters.feedback ~level:1 "applying all slicing requests...";
  SlicingParameters.debug ~level:2 "pending requests:@\n %t@\n"
    SlicingProject.print_proj_worklist;
  let r = SlicingProject.apply_all_actions () in
  SlicingParameters.feedback ~level:2 "done (applying all slicing requests).";
  r

let apply_next_action () =
  SlicingParameters.debug ~level:1 "[Api.apply_next_internal]";
  SlicingProject.apply_next_action ()

let apply_all ~propagate_to_callers =
  SlicingParameters.debug ~level:1 "[Api.apply_all]";
  assert (not propagate_to_callers) ;
  try
    while (true)
    do
      (* Format.printf "@\napply_next_internal@."; *)
      apply_next_action ()
    done
  with Not_found -> ()

let get_select_kf (fvar, _select) = Globals.Functions.get fvar

let get_lval_zone ?(for_writing=false) stmt lval =
  let open Eva.Results in
  before stmt |> eval_address ~for_writing lval |> as_zone

(** Utilities for [kinstr]. *)
module Kinstr: sig
  val iter_from_func : (stmt -> unit) -> kernel_function -> unit
  val is_rw_zone : (Locations.Zone.t option * Locations.Zone.t option) -> stmt -> Locations.Zone.t option * Locations.Zone.t option
end
=
struct
  (** Iter on statements of a kernel function *)
  let iter_from_func f kf =
    let definition = Kernel_function.get_definition kf in
    List.iter f definition.sallstmts

  (** Get directly read/written [Zone.t] by the statement.
    * i.e. directly means when [ki] is a call,
      it doesn't don't look at the assigns clause of the called function. *)
  let get_rw_zone stmt = (* returns [Zone.t read],[Zone.t written] *)
    assert (Eva.Analysis.is_computed ());
    let lval_process read_zone stmt lv =
      (* returns [read_zone] joined to [Zone.t read] by [lv], [Zone.t written] by [lv] *)
      (* The modified locations are [looking_for], those address are
         function of [deps]. *)
      let zloc = get_lval_zone ~for_writing:true stmt lv in
      let deps = Eva.Results.(before stmt |> address_deps lv) in
      Locations.Zone.join read_zone deps, zloc
    in
    let call_process lv f args _loc =
      (* returns  [Zone.t read] by [lv, f, args], [Zone.t written] by [lv] *)
      let read_zone = !Db.From.find_deps_no_transitivity stmt f in
      let add_args arg inputs =
        Locations.Zone.join inputs
          (!Db.From.find_deps_no_transitivity stmt arg) in
      let read_zone = List.fold_right add_args args read_zone in
      let read_zone,write_zone =
        match lv with
        | None -> read_zone , Locations.Zone.bottom
        | Some lv -> lval_process read_zone stmt lv
      in read_zone,write_zone
    in
    match stmt.skind with
    | Switch (exp,_,_,_)
    | If (exp,_,_,_) ->
      (* returns  [Zone.t read] by condition [exp], [Zone.bottom] *)
      !Db.From.find_deps_no_transitivity stmt exp, Locations.Zone.bottom
    | Instr (Set (lv,exp,_)) ->
      (* returns  [Zone.t read] by [exp, lv], [Zone.t written] by [lv] *)
      let read_zone = !Db.From.find_deps_no_transitivity stmt exp in
      lval_process read_zone stmt lv
    | Instr (Local_init (v, AssignInit i, _)) ->
      let rec collect zone i =
        match i with
        | SingleInit e ->
          Locations.Zone.join zone (!Db.From.find_deps_no_transitivity stmt e)
        | CompoundInit (_,l) ->
          List.fold_left
            (fun acc (_,i) -> collect acc i) zone l
      in
      let read_zone = collect Locations.Zone.bottom i in
      lval_process read_zone stmt (Cil.var v)
    | Instr (Call (lvaloption,funcexp,argl,l)) ->
      call_process lvaloption funcexp argl l
    | Instr (Local_init(v, ConsInit(f, args, k),l)) ->
      Cil.treat_constructor_as_func call_process v f args k l
    | _ -> Locations.Zone.bottom, Locations.Zone.bottom

  (** Look at intersection of [rd_zone_opt]/[wr_zone_opt] with the
      directly read/written [Zone.t] by the statement.
    * i.e. directly means when [ki] is a call,
      it doesn't don't look at the assigns clause of the called function. *)
  let is_rw_zone (rd_zone_opt, wr_zone_opt) stmt =
    let rd_zone, wr_zone = get_rw_zone stmt in
    let inter_zone zone_opt zone =
      match zone_opt with
      | None -> zone_opt
      | Some zone_requested ->
        if Locations.Zone.intersects zone_requested zone
        then let inter = Locations.Zone.narrow zone_requested zone
          in Some inter
        else None
    in inter_zone rd_zone_opt rd_zone, inter_zone wr_zone_opt wr_zone
end

(** Topologically propagate user marks to callers in whole project *)
let topologic_propagation project =
  apply_all_actions project;
  Callgraph.Uses.iter_in_rev_order
    (fun kf ->
       SlicingParameters.debug ~level:3
         "doing topologic propagation for function: %a"
         Kernel_function.pretty kf;
       apply_all_actions project)

let add_to_selection set selection =
  SlicingSelect.Selections.add_to_selects selection set

(** Registered as a slicing selection function:
    Add a selection of the pdg nodes. *)
let select_pdg_nodes set mark nodes kf =
  let selection = SlicingSelect.select_pdg_nodes kf nodes mark
  in add_to_selection set selection

(** Registered as a slicing selection function:
    Add a selection of the statement. *)
let select_stmt set ~spare stmt kf =
  let stmt_mark = SlicingMarks.mk_user_mark
      ~data:(not spare) ~addr:(not spare) ~ctrl:(not spare) in
  let selection = SlicingSelect.select_stmt_computation kf stmt stmt_mark
  in add_to_selection set selection

(** Add a selection to the entrance of the function [kf]
    and add a selection to its return if [~return] is true
    and add a selection to [~inputs] parts of its inputs
    and add a selection to [~outputs] parts of its outputs*)
let select_entry_point_and_some_inputs_outputs set ~mark kf ~return ~outputs ~inputs =
  SlicingParameters.debug ~level:3
    "select_entry_point_and_some_inputs_outputs %a"
    Kernel_function.pretty kf ;
  let set = let selection = SlicingSelect.select_entry_point kf mark in
    add_to_selection set selection
  in
  let set =
    if (Locations.Zone.equal Locations.Zone.bottom inputs)
    then set
    else let selection = SlicingSelect.select_zone_at_entry kf inputs mark in
      add_to_selection set selection
  in if ((Locations.Zone.equal Locations.Zone.bottom outputs) && not return) ||
        (try
           let stmt = Kernel_function.find_return kf
           in if Eva.Results.is_reachable stmt then
             false
           else
             begin
               SlicingParameters.feedback
                 "@[Nothing to select for unreachable return stmt of %a@]"
                 Kernel_function.pretty kf;
               true
             end
         with Kernel_function.No_Statement -> false)
  then set
  else
    let set =
      if (Locations.Zone.equal Locations.Zone.bottom outputs)
      then set
      else let selection = SlicingSelect.select_modified_output_zone kf outputs mark in
        add_to_selection set selection
    in if return
    then let selection = SlicingSelect.select_return kf mark in
      add_to_selection set selection
    else set
(* apply [select ~spare] on each callsite of [kf] and add the returned selection
   to [set]. *)
let generic_select_func_calls select_stmt set ~spare kf =
  assert (Eva.Analysis.is_computed ());
  let callers = Eva.Results.callsites kf in
  let select_calls acc (caller, stmts) =
    List.fold_left (fun acc s -> select_stmt acc ~spare s caller) acc stmts
  in
  List.fold_left select_calls set callers

(** Registered as a slicing selection function:
    Add a selection of calls to a [kf]. *)
let select_func_calls_into set ~spare kf =
  let add_to_select set ~spare select =
    let mark =
      let nspare = not spare in
      SlicingMarks.mk_user_mark ~data:nspare ~addr:nspare ~ctrl:nspare
    in add_to_selection set (select mark)
  in
  let kf_entry, _library = Globals.entry_point () in
  if Kernel_function.equal kf_entry kf then
    add_to_select set ~spare (SlicingSelect.select_entry_point kf)
  else
    let select_min_call set ~spare ki kf =
      add_to_select set ~spare (SlicingSelect.select_minimal_call kf ki)
    in
    generic_select_func_calls select_min_call set ~spare kf

(** Registered as a slicing selection function:
    Add a selection of calls to a [kf]. *)
let select_func_calls_to set ~spare kf =
  let kf_entry, _library = Globals.entry_point () in
  if Kernel_function.equal kf_entry kf then
    begin
      let mark =
        let nspare = not spare in
        SlicingMarks.mk_user_mark ~data:nspare ~addr:nspare ~ctrl:nspare
      in
      assert (Eva.Analysis.is_computed ());
      let outputs = !Db.Outputs.get_external kf in
      select_entry_point_and_some_inputs_outputs set ~mark kf
        ~return:true
        ~outputs
        ~inputs:Locations.Zone.bottom
    end
  else
    generic_select_func_calls select_stmt set ~spare kf

(** Registered as a slicing selection function:
    Add selection of function outputs. *)
let select_func_zone set mark zone kf =
  let selection = SlicingSelect.select_zone_at_end kf zone mark
  in add_to_selection set selection

(** Registered as a slicing selection function:
    Add a selection of the [kf] return statement. *)
let select_func_return set ~spare kf =
  try
    let ki = Kernel_function.find_return kf
    in select_stmt set ~spare ki kf
  with Kernel_function.No_Statement ->
    let mark =
      SlicingMarks.mk_user_mark
        ~data:(not spare) ~addr:(not spare) ~ctrl:(not spare)
    in
    select_entry_point_and_some_inputs_outputs
      set
      ~mark
      kf
      ~return:true
      ~outputs:Locations.Zone.bottom
      ~inputs:Locations.Zone.bottom

(** Registered as a slicing selection function:
    Add a selection of the statement reachability.
    Note: add also a transparent selection on the whole statement. *)
let select_stmt_ctrl set ~spare ki kf =
  let ctrl_mark =
    SlicingMarks.mk_user_mark ~data:false ~addr:false ~ctrl:(not spare) in
  let selection = SlicingSelect.select_stmt_computation kf ki ctrl_mark
  in add_to_selection set selection

(** Registered as a slicing selection function:
    Add a selection of data relative to a statement.
    Note: add also a transparent selection on the whole statement. *)
let select_stmt_zone set mark zone ~before ki kf =
  let selection =
    SlicingSelect.select_stmt_zone kf ki ~before zone mark
  in let set = add_to_selection set selection
  in select_stmt_ctrl set ~spare:true ki kf

(** Registered as a slicing selection function:
    Add a selection of data relative to a statement.
    Variables of [lval_str] string are bounded
    relatively to the whole scope of the function [kf].
    The interpretation of the address of the lvalues is
    done just before the execution of the statement [~eval].
    The selection preserve the value of these lvalues before
    or after (c.f. boolean [~before]) the statement [ki].
    Note: add also a transparent selection on the whole statement. *)
let select_stmt_lval set mark lval_str ~before ki ~eval kf =
  assert (Eva.Analysis.is_computed ());
  if Datatype.String.Set.is_empty lval_str
  then set
  else
    let zone =
      Datatype.String.Set.fold
        (fun lval_str acc ->
           let lval_term = !Db.Properties.Interp.term_lval kf lval_str in
           let lval =
             !Db.Properties.Interp.term_lval_to_lval ~result:None lval_term
           in
           let zone = get_lval_zone eval lval in
           Locations.Zone.join zone acc)
        lval_str
        Locations.Zone.bottom
    in
    select_stmt_zone set mark zone ~before ki kf

(** Add a selection of data relative to read/write accesses.
    Interpret the [~rd] lvalues and the [~wr] lvalues from [~eval]
    statements of [kf]:
    - Variables of [lval_str] string are bounded
      relatively to the whole scope of the function [kf].
    - The interpretation of the address of the lvalues is
      done just before the execution of the statement [~eval].
      Find read/write accesses from the whole project if [ki_opt]=None.
      Otherwise, restrict the research among the direct effect of [ki_opt] statement.
      i.e. when [ki_opt] is a call, the selection doesn't look at the assigns clause
      of a call. *)
let select_lval_rw set mark ~rd ~wr ~eval kf ki_opt=
  assert (Eva.Analysis.is_computed ());
  let zone_option ~for_writing lval_str =
    if Datatype.String.Set.is_empty lval_str
    then None
    else
      let zone =
        Datatype.String.Set.fold
          (fun lval_str acc ->
             let lval_term = !Db.Properties.Interp.term_lval kf lval_str in
             let lval = !Db.Properties.Interp.term_lval_to_lval ~result:None lval_term in
             let zone = get_lval_zone ~for_writing eval lval in
             Locations.Zone.join zone acc)
          lval_str Locations.Zone.bottom
      in SlicingParameters.debug ~level:3
        "select_lval_rw %a zone=%a"
        Kernel_function.pretty kf
        Locations.Zone.pretty zone;
      Some zone
  in
  let zone_rd_opt = zone_option ~for_writing:false rd in
  let zone_wr_opt = zone_option ~for_writing:true wr in
  match zone_rd_opt, zone_wr_opt with
  | None, None -> set
  | (_, _) as zone_option_rw ->
    let ac = ref set in
    let select_rw_from_stmt kf ki =
      let rd_zone_opt, wr_zone_opt = Kinstr.is_rw_zone zone_option_rw ki in
      let select_zone ~before zone_opt =
        match zone_opt with
        | None -> !ac
        | Some zone ->
          SlicingParameters.debug ~level:3
            "select_lval_rw sid=%d before=%b zone=%a"
            ki.sid before Locations.Zone.pretty zone;
          select_stmt_zone !ac mark zone ~before ki kf ;
      in
      ac := select_zone ~before:true rd_zone_opt ;
      ac := select_zone ~before:false wr_zone_opt
    in (match ki_opt with
        | Some ki -> select_rw_from_stmt kf ki
        | None ->
          Globals.Functions.iter
            (fun kf ->
               if Eva.Results.is_called kf then
                 if not (!Db.Value.use_spec_instead_of_definition kf)
                 then (* Called function with source code: just looks at its stmt *)
                   Kinstr.iter_from_func (select_rw_from_stmt kf) kf
                 else begin (* Called function without source code: looks at its effect *)
                   let select_inter_zone fsel zone_opt zone =
                     match zone_opt with
                     | None -> ()
                     | Some zone_requested ->
                       (* Format.printf "@\nselect_lval_rw zone_req=%a zone=%a@."
                          Locations.Zone.pretty zone_requested
                          Locations.Zone.pretty zone; *)
                       if Locations.Zone.intersects zone_requested zone
                       then let inter = Locations.Zone.narrow zone_requested zone
                         in fsel inter
                       else () in
                   let select_wr outputs =
                     ac := select_entry_point_and_some_inputs_outputs !ac ~mark kf
                         ~return:false ~outputs ~inputs:Locations.Zone.bottom
                   and select_rd inputs =
                     ac := select_entry_point_and_some_inputs_outputs !ac ~mark kf
                         ~return:false ~inputs ~outputs:Locations.Zone.bottom

                   in
                   assert (Eva.Results.is_called kf) ; (* otherwise [!Db.Outputs.get_external kf] gives weird results *)
                   select_inter_zone select_wr zone_wr_opt (!Db.Outputs.get_external kf) ;
                   select_inter_zone select_rd zone_rd_opt (!Db.Inputs.get_external kf)
                 end
            ));
    !ac

(** Registered as a slicing selection function:
    Add a selection of rw accesses to lvalues relative to a statement.
    Variables of [~rd] and [~wr] string are bounded
    relatively to the whole scope of the function [kf].
    The interpretation of the address of the lvalues is
    done just before the execution of the statement [~eval].
    The selection preserve the [~rd] and ~[wr] accesses
    directly contained into the statement [ki].
    i.e. when [ki] is a call, the selection doesn't look at
    the assigns clause of the called function.
    Note: add also a transparent selection on the whole statement.*)
let select_stmt_lval_rw set mark ~rd ~wr ki ~eval kf =
  select_lval_rw set mark ~rd ~wr ~eval kf (Some ki)

(** Add a selection of the declaration of [vi]. *)
let select_decl_var set mark vi kf =
  let selection = SlicingSelect.select_decl_var kf vi mark in
  add_to_selection set selection

let select_ZoneAnnot_pragmas set ~spare pragmas kf =
  let set =
    Cil_datatype.Stmt.Set.fold
      (* selection related to statement assign and //@ slice pragma stmt *)
      (fun ki' acc -> select_stmt acc ~spare ki' kf)
      pragmas.Db.Properties.Interp.To_zone.stmt set
  in
  Cil_datatype.Stmt.Set.fold
    (* selection related to //@ slice pragma ctrl/expr *)
    (fun ki' acc -> select_stmt_ctrl acc ~spare ki' kf)
    pragmas.Db.Properties.Interp.To_zone.ctrl
    set

let select_ZoneAnnot_zones_decl_vars set mark (zones,decl_vars) kf =
  let set =
    Cil_datatype.Varinfo.Set.fold
      (fun vi acc -> select_decl_var acc mark vi kf)
      decl_vars.Db.Properties.Interp.To_zone.var
      set
  in
  let set =
    Cil_datatype.Logic_label.Set.fold
      (fun l acc ->
         let selection = SlicingSelect.select_label kf l mark
         in add_to_selection acc selection)
      decl_vars.Db.Properties.Interp.To_zone.lbl
      set
  in
  List.fold_right
    (fun z acc ->
       (* selection related to the parsing/compilation of the annotation *)
       select_stmt_zone acc mark
         z.Db.Properties.Interp.To_zone.zone
         ~before:z.Db.Properties.Interp.To_zone.before
         z.Db.Properties.Interp.To_zone.ki
         kf)
    zones set

let get_or_raise (info_data_opt, info_decl) = match info_data_opt with
  | None ->
    (* TODO: maybe we can know how to use [info_decl] ? *)
    SlicingParameters.not_yet_implemented
      "%s" !Logic_interp.To_zone.not_yet_implemented
  | Some info_data -> info_data, info_decl

(** Registered as a slicing selection function:
    Add selection of the annotations related to a statement.
    Note: add also a transparent selection on the whole statement. *)
let select_stmt_pred set mark pred ki kf =
  let zones_decl_vars =
    !Db.Properties.Interp.To_zone.from_pred pred
      (!Db.Properties.Interp.To_zone.mk_ctx_stmt_annot kf ki)
  in
  select_ZoneAnnot_zones_decl_vars set mark (get_or_raise zones_decl_vars) kf

(** Registered as a slicing selection function:
    Add selection of the annotations related to a statement.
    Note: add also a transparent selection on the whole statement. *)
let select_stmt_term set mark term ki kf =
  let zones_decl_vars =
    !Db.Properties.Interp.To_zone.from_term term
      (!Db.Properties.Interp.To_zone.mk_ctx_stmt_annot kf ki)
  in
  select_ZoneAnnot_zones_decl_vars set mark (get_or_raise zones_decl_vars) kf

(** Registered as a slicing selection function:
    Add selection of the annotations related to a statement.
    Note: add also a transparent selection on the whole statement. *)
let select_stmt_annot set mark ~spare annot ki kf =
  let zones_decl_vars,pragmas =
    !Db.Properties.Interp.To_zone.from_stmt_annot annot (ki, kf)
  in let set = select_ZoneAnnot_pragmas set ~spare pragmas kf
  in select_ZoneAnnot_zones_decl_vars set mark (get_or_raise zones_decl_vars) kf

(** Registered as a slicing selection function:
    Add selection of the annotations related to a statement.
    Note: add also a transparent selection on the whole statement. *)
let select_stmt_annots set mark ~spare  ~threat ~user_assert ~slicing_pragma ~loop_inv ~loop_var ki kf =
  let zones_decl_vars,pragmas =
    !Db.Properties.Interp.To_zone.from_stmt_annots
      (Some (!Db.Properties.Interp.To_zone.code_annot_filter
               ~threat ~user_assert ~slicing_pragma
               ~loop_inv ~loop_var ~others:false))
      (ki, kf)
  in let set = select_ZoneAnnot_pragmas set ~spare pragmas kf
  in select_ZoneAnnot_zones_decl_vars set mark (get_or_raise zones_decl_vars) kf

(** Registered as a slicing selection function:
    Add a selection of the annotations related to a function. *)
let select_func_annots set mark ~spare ~threat ~user_assert ~slicing_pragma ~loop_inv ~loop_var kf =
  try
    let zones_decl_vars,pragmas =
      !Db.Properties.Interp.To_zone.from_func_annots Kinstr.iter_from_func
        (Some
           (!Db.Properties.Interp.To_zone.code_annot_filter
              ~threat ~user_assert ~slicing_pragma ~loop_inv
              ~loop_var ~others:false))
        kf
    in let set = select_ZoneAnnot_pragmas set ~spare pragmas kf
    in select_ZoneAnnot_zones_decl_vars set mark (get_or_raise zones_decl_vars) kf
  with Kernel_function.No_Definition ->
    SlicingParameters.warning ~wkey:SlicingParameters.wkey_cmdline
      "No definition for function '%a'. \
       Slicing requests from the command line are ignored."
      Kernel_function.pretty kf;
    Cil_datatype.Varinfo.Map.empty


(** Registered as a slicing selection function:
    Add selection of function outputs.
    Variables of [lval_str] string are bounded
    relatively to the whole scope of the function [kf].
    The interpretation of the address of the lvalues is
    done just before the execution of the first statement [kf].
    The selection preserve the value of these lvalues before
    execution of the return statement.  *)
let select_func_lval set mark lval_str kf =
  if Datatype.String.Set.is_empty lval_str
  then set
  else
    let ki_scope_eval = Kernel_function.find_first_stmt kf in
    select_stmt_lval
      set mark lval_str
      ~before:false
      (Kernel_function.find_return kf)
      ~eval:ki_scope_eval
      kf

(** Registered as a slicing selection function:
    Add a selection of data relative to read/write accesses.
    Interpret the [~rd] lvalues and the [~wr] lvalues from [~eval]
    statements of [kf]:
    - Variables of [lval_str] string are bounded
      relatively to the whole scope of the function [kf].
    - The interpretation of the address of the lvalues is
      done just before the execution of the statement [~eval].
      Find read/write accesses from the whole project if [ki_opt]=None. *)
let select_func_lval_rw set mark ~rd ~wr ~eval kf =
  if Datatype.String.Set.is_empty rd && Datatype.String.Set.is_empty wr
  then set
  else select_lval_rw set mark ~rd ~wr ~eval kf None

(** Registered as a slicing request function:
    Add selections to all concerned slices, as slicing requests and apply them,
    kernel function by kernel function.
    Note:
    - the function begins by applying the remaining internal requests.
    - the requests added for the last kernel function are not applied. *)
let add_selection set =
  let add_selection prev selection =
    let kf = get_select_kf selection in
    let r = match prev with
        None -> apply_all_actions () ; Some (kf)
      | Some prev_kf -> if prev_kf == kf then prev else None
    and make_request slice =
      SlicingSelect.add_ff_selection slice selection
    and slices =
      let slices = SlicingProject.get_slices kf
      in if slices = [] then [SlicingProject.create_slice kf] else slices
    in List.iter make_request slices ;
    r
  in ignore (SlicingSelect.Selections.fold_selects_internal add_selection None set)

(** Registered as a slicing request function:
    Add selections that will be applied to all the slices of the function
    (already existing or created later)
    Note:
    - the function begins by applying the remaining internal requests.
    - the requests added for the last kernel function are not applied. *)
let add_persistent_selection set =
  (* Format.printf "@\nadd_persistent_selection@."; *)
  let add_selection prev selection =
    let kf = get_select_kf selection in
    let r = match prev with
        None -> apply_all_actions () ; Some (kf)
      | Some prev_kf -> if prev_kf == kf then prev else None
    in SlicingSelect.add_fi_selection selection; r
  in ignore (SlicingSelect.Selections.fold_selects_internal add_selection None set)

(** Registered as a slicing request function:
    Add selections that will be applied to all the slices of the function
    (already existing or created later)
    Note:
    - the function begins by applying the remaining internal requests.
    - the requests added for the last kernel function are not applied. *)
let add_persistent_cmdline () =
  SlicingParameters.feedback ~level:1
    "interpreting slicing requests from the command line...";
  begin try
      let selection = ref Cil_datatype.Varinfo.Map.empty in
      let top_mark = SlicingMarks.mk_user_mark ~addr:true ~ctrl:true ~data:true in
      Globals.Functions.iter
        (fun kf ->
           let add_selection opt select  =
             if Kernel_function.Set.mem kf (opt ()) then
               selection := select !selection ~spare:false kf
           in
           add_selection
             SlicingParameters.Select.Return.get
             select_func_return;
           add_selection
             SlicingParameters.Select.Calls.get
             select_func_calls_to;
           add_selection
             SlicingParameters.Select.Pragma.get
             (fun s -> select_func_annots s top_mark
                 ~threat:false ~user_assert:false ~slicing_pragma:true
                 ~loop_inv:false ~loop_var:false);
           add_selection
             SlicingParameters.Select.Threat.get
             (fun s -> select_func_annots s top_mark
                 ~threat:true ~user_assert:false ~slicing_pragma:false
                 ~loop_inv:false ~loop_var:false);
           add_selection
             SlicingParameters.Select.Assert.get
             (fun s -> select_func_annots s top_mark
                 ~threat:false ~user_assert:true ~slicing_pragma:false
                 ~loop_inv:false ~loop_var:false);
           add_selection
             SlicingParameters.Select.LoopInv.get
             (fun s -> select_func_annots s top_mark
                 ~threat:false ~user_assert:false ~slicing_pragma:false
                 ~loop_inv:true ~loop_var:false);
           add_selection
             SlicingParameters.Select.LoopVar.get
             (fun s -> select_func_annots s top_mark
                 ~threat:false ~user_assert:false ~slicing_pragma:false
                 ~loop_inv:false ~loop_var:true);
        );
      if not (Datatype.String.Set.is_empty
                (SlicingParameters.Select.Value.get ()))
         ||
         not (Datatype.String.Set.is_empty
                (SlicingParameters.Select.RdAccess.get ()))
         ||
         not (Datatype.String.Set.is_empty
                (SlicingParameters.Select.WrAccess.get ()))
      then begin
        (* fprintf fmt "@\n[-slice-value] Select %s at end of the entry point %a@."
           lval_str Db.pretty_name kf; *)
        let kf = fst (Globals.entry_point ()) in
        let ki_scope_eval = Kernel_function.find_first_stmt kf in
        selection := select_func_lval !selection top_mark
            (SlicingParameters.Select.Value.get ()) kf;
        selection := select_func_lval_rw !selection top_mark
            ~rd:(SlicingParameters.Select.RdAccess.get ())
            ~wr:(SlicingParameters.Select.WrAccess.get ())
            ~eval:ki_scope_eval kf ;
        SlicingParameters.Select.Value.clear () ;
        SlicingParameters.Select.RdAccess.clear () ;
        SlicingParameters.Select.WrAccess.clear () ;
      end;
      add_persistent_selection !selection;
    with Logic_interp.Error(_loc,msg) ->
      SlicingParameters.warning ~wkey:SlicingParameters.wkey_cmdline
        "%s. Slicing requests from the command line are ignored." msg
  end;
  SlicingParameters.feedback ~level:2
    "done (interpreting slicing requests from the command line)."

(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)