-
Allan Blanchard authored
- Info (expr_node) - IndexPI (PlusPI synonym)
Allan Blanchard authored- Info (expr_node) - IndexPI (PlusPI synonym)
data_for_aorai.ml 82.69 KiB
(**************************************************************************)
(* *)
(* This file is part of Aorai plug-in of Frama-C. *)
(* *)
(* Copyright (C) 2007-2021 *)
(* CEA (Commissariat à l'énergie atomique et aux énergies *)
(* alternatives) *)
(* INRIA (Institut National de Recherche en Informatique et en *)
(* Automatique) *)
(* INSA (Institut National des Sciences Appliquees) *)
(* *)
(* 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 Logic_ptree
open Cil
open Cil_types
open Promelaast
open Logic_simplification
exception Empty_automaton
module Aorai_state =
Datatype.Make_with_collections(
struct
type t = Promelaast.state
let structural_descr = Structural_descr.t_abstract
let reprs = [ { nums = -1; name = ""; multi_state = None;
acceptation = Bool3.False; init = Bool3.False
} ]
let name = "Aorai_state"
let equal x y = Datatype.Int.equal x.nums y.nums
let hash x = x.nums
let rehash = Datatype.identity
let compare x y = Datatype.Int.compare x.nums y.nums
let copy = Datatype.identity
let internal_pretty_code = Datatype.undefined
let pretty fmt x = Format.fprintf fmt "state_%d" x.nums
let varname _ =
assert false (* unused while internal_pretty_code is undefined *)
let mem_project = Datatype.never_any_project
end
)
module Aorai_typed_trans =
Datatype.Make_with_collections(
struct
let name = "Aorai_typed_trans"
type t = Promelaast.typed_trans
let structural_descr = Structural_descr.t_abstract
let reprs = [ { numt = -1; start = List.hd (Aorai_state.reprs);
stop = List.hd (Aorai_state.reprs);
cross = TTrue; actions=[]; } ]
let equal x y = Datatype.Int.equal x.numt y.numt
let hash x = x.numt
let rehash = Datatype.identity
let compare x y = Datatype.Int.compare x.numt y.numt
let copy = Datatype.identity
let internal_pretty_code = Datatype.undefined
let pretty = Promelaoutput.Typed.print_transition
let varname _ = assert false
let mem_project = Datatype.never_any_project
end)
module Aorai_automaton =
Datatype.Make(
struct
include Datatype.Serializable_undefined
let name = "Aorai_automaton"
type t = Promelaast.typed_automaton
let structural_descr = Structural_descr.t_abstract
let reprs = [ { states = Aorai_state.reprs;
trans = Aorai_typed_trans.reprs;
metavariables = Datatype.String.Map.empty;
observables = Some Datatype.String.Set.empty;
}]
end
)
module State_var =
State_builder.Hashtbl
(Aorai_state.Hashtbl)
(Cil_datatype.Varinfo)
(struct
let name = "Data_for_aorai.State_var"
let dependencies = [ Ast.self; Aorai_option.Ya.self ]
let size = 7
end)
let get_state_var =
let add_var state = Cil.makeVarinfo true false state.name Cil.intType in
State_var.memo add_var
let get_state_logic_var state = Cil.cvar_to_lvar (get_state_var state)
module Max_value_counter =
State_builder.Hashtbl
(Cil_datatype.Term.Hashtbl)
(Cil_datatype.Term)
(struct
let name = "Data_for_aorai.Max_value_counter"
let dependencies = [ Ast.self; Aorai_option.Ya.self ]
let size = 7
end)
let find_max_value t =
try Some (Max_value_counter.find t) with Not_found -> None
let raise_error msg =
Aorai_option.fatal "Aorai plugin internal error. \nStatus : %s.\n" msg;;
(* Format.printf "Aorai plugin internal error. \nStatus : %s.\n" msg; *)
(* assert false *)
let por t1 t2 =
match t1,t2 with
PTrue,_ | _,PTrue -> PTrue
| PFalse,t | t,PFalse -> t
| _,_ -> POr(t1,t2)
let pand t1 t2 =
match t1,t2 with
PTrue,t | t,PTrue -> t
| PFalse,_ | _,PFalse -> PFalse
| _,_ -> PAnd(t1,t2)
let pnot t =
match t with
PTrue -> PFalse
| PFalse -> PTrue
| PNot t -> t
| _ -> PNot t
let rec is_same_expression e1 e2 =
match e1,e2 with
| PVar x, PVar y -> x = y
| PVar _,_ | _,PVar _ -> false
| PCst cst1, PCst cst2 -> Logic_utils.is_same_pconstant cst1 cst2
| PCst _,_ | _,PCst _ -> false
| PPrm (f1,x1), PPrm(f2,x2) -> f1 = f2 && x1 = x2
| PPrm _,_ | _,PPrm _ -> false
| PMetavar x, PMetavar y -> x = y
| PMetavar _,_ | _,PMetavar _ -> false
| PBinop(b1,l1,r1), PBinop(b2,l2,r2) ->
b1 = b2 && is_same_expression l1 l2 && is_same_expression r1 r2
| PBinop _, _ | _, PBinop _ -> false
| PUnop(u1,e1), PUnop(u2,e2) -> u1 = u2 && is_same_expression e1 e2
| PUnop _,_ | _,PUnop _ -> false
| PArrget(a1,i1), PArrget(a2,i2) ->
is_same_expression a1 a2 && is_same_expression i1 i2
| PArrget _,_ | _,PArrget _ -> false
| PField(e1,f1), PField(e2,f2) -> f1 = f2 && is_same_expression e1 e2
| PField _,_ | _,PField _ -> false
| PArrow(e1,f1), PArrow(e2,f2) -> f1 = f2 && is_same_expression e1 e2
let declared_logics = Hashtbl.create 97
let add_logic name log_info = Hashtbl.replace declared_logics name log_info
let get_logic name =
try Hashtbl.find declared_logics name
with Not_found ->
raise_error ("Logic function '"^name^"' not declared in hashtbl")
let declared_predicates = Hashtbl.create 97
let add_predicate name pred_info =
Hashtbl.replace declared_predicates name pred_info
let get_predicate name =
try Hashtbl.find declared_predicates name
with Not_found -> raise_error ("Predicate '"^name^"' not declared in hashtbl")
(* ************************************************************************* *)
(* Some constant names used for generation *)
(* Logic variables *)
let transStart = "aorai_Trans_Start" (* OK *)
let transStop = "aorai_Trans_Stop" (* OK *)
let transCond = "aorai_Trans_Cond" (* OK *)
let transCondP = "aorai_Trans_Cond_param" (* OK *)
let loopInit = "aorai_Loop_Init" (* OK *)
(* C variables *)
let curState = "aorai_CurStates" (* OK *)
let history n = "aorai_StatesHistory_" ^ string_of_int n (* OK *)
let whole_history () =
let rec aux acc n =
if n > 0 then aux (history n :: acc) (n - 1) else acc
in
aux [] (Aorai_option.InstrumentationHistory.get ())
let curTrans = "aorai_CurTrans" (* OK *)
(*let curTransTmp = "aorai_CurTrans_tmp" (* OK *)*)
let curOp = "aorai_CurOperation" (* OK *)
let curOpStatus = "aorai_CurOpStatus" (* OK *)
let acceptSt = "aorai_AcceptStates" (* TODO *)
(* C constants #define *)
let nbOp = "aorai_NbOper" (* Deprecated ? *)
let nbAcceptSt = "aorai_NbAcceptStates" (* Deprecated ? *)
let nbTrans = "aorai_NbTrans" (* Deprecated ? *)
(* C Macros *)
let macro_ligth = "aorai_Macro_Prop_St_Tr_Without_Conds" (* Deprecated ? *)
let macro_full = "aorai_Macro_Prop_St_Tr" (* Deprecated ? *)
let macro_pure = "aorai_Macro_Op_without_sub_call" (* Deprecated ? *)
(* C enumeration *)
let listOp = "aorai_ListOper" (* OK *)
let listStatus = "aorai_OpStatusList" (* OK *)
let callStatus = "aorai_Called" (* OK *)
let termStatus = "aorai_Terminated" (* OK *)
let states = "aorai_States" (* OK *)
(* C function *)
let buch_sync = "Aorai_Sync" (* Deprecated ? *)
(* ************************************************************************* *)
(* Buchi automata as stored after parsing *)
module Automaton =
State_builder.Ref
(Datatype.Option(Aorai_automaton))
(struct
let name = "Data_for_aorai.Automaton"
let dependencies =
[ Aorai_option.Ltl_File.self; Aorai_option.Buchi.self;
Aorai_option.Ya.self ]
let default () = None
end)
(* Each transition with a parametrized cross condition (call param access or return value access) has its parametrized part stored in this array. *)
let cond_of_parametrizedTransitions = ref (Array.make (1) [[]])
(* List of functions defined in the C file *)
let defined_functions = ref []
(* List of functions without declaration *)
let ignored_functions = ref []
(** Return the buchi automaton as stored after parsing *)
let getAutomata () =
match Automaton.get() with
| Some auto -> auto
| None ->
Aorai_option.fatal "The automaton has not been compiled yet"
let getGraph () =
let auto = getAutomata () in
auto.states, auto.trans
(** Return the number of transitions of the automaton *)
let getNumberOfTransitions () =
List.length (getAutomata ()).trans
(** Return the number of states of the automaton *)
let getNumberOfStates () =
List.length (getAutomata ()).states
let is_c_global name =
try ignore (Globals.Vars.find_from_astinfo name VGlobal); true
with Not_found ->
try ignore (Globals.Functions.find_by_name name); true
with Not_found -> false
let get_fresh =
let used_names = Hashtbl.create 5 in
fun name ->
if Clexer.is_c_keyword name
|| Logic_lexer.is_acsl_keyword name || is_c_global name
|| Hashtbl.mem used_names name
then begin
let i = ref (try Hashtbl.find used_names name with Not_found -> 0) in
let proposed_name () = name ^ "_" ^ string_of_int !i in
while is_c_global (proposed_name()) do incr i done;
Hashtbl.replace used_names name (!i+1);
proposed_name ()
end
else begin
Hashtbl.add used_names name 0;
name
end
module AuxVariables =
State_builder.List_ref
(Cil_datatype.Varinfo)
(struct
let name = "Data_for_aorai.AuxVariables"
let dependencies =
[ Aorai_option.Ltl_File.self; Aorai_option.Buchi.self;
Aorai_option.Ya.self; Ast.self ]
end)
module AbstractLogicInfo =
State_builder.List_ref
(Cil_datatype.Logic_info)
(struct
let name = "Data_for_aorai.AbstractLogicInfo"
let dependencies =
[ Aorai_option.Ltl_File.self; Aorai_option.Buchi.self;
Aorai_option.Ya.self; Ast.self ]
end)
class change_var vi1 vi2 =
object
inherit Visitor.frama_c_copy (Project.current ())
method! vlogic_var_use vi =
if Cil_datatype.Logic_var.equal vi1 vi then ChangeTo vi2 else SkipChildren
end
let change_var_term vi1 vi2 t =
Visitor.visitFramacTerm (new change_var vi1 vi2) t
let update_condition vi1 vi2 cond =
let rec aux e =
match e with
| TOr (e1,e2) -> TOr(aux e1, aux e2)
| TAnd (e1,e2) -> TAnd(aux e1, aux e2)
| TNot e -> TNot (aux e)
| TCall _ | TReturn _ | TTrue | TFalse -> e
| TRel(rel,t1,t2) ->
TRel(rel,change_var_term vi1 vi2 t1,change_var_term vi1 vi2 t2)
in aux cond
let pebble_set_at li lab =
assert (li.l_profile = []);
let labels = List.map (fun _ -> lab) li.l_labels in
Logic_const.term (Tapp (li,labels,[])) (Option.get li.l_type)
let memo_multi_state st =
match st.multi_state with
| None ->
let aux = Cil.makeGlobalVar (get_fresh "aorai_aux") Cil.intType in
let laux = Cil.cvar_to_lvar aux in
let set = Cil_const.make_logic_info (get_fresh (st.name ^ "_pebble")) in
let typ = Logic_const.make_set_type (Ctype Cil.intType) in
set.l_var_info.lv_type <- typ;
set.l_labels <- [FormalLabel "L"];
set.l_type <- Some typ;
set.l_body <-
LBreads
[ Logic_const.new_identified_term (Logic_const.tvar laux) ];
let multi_state = set,laux in
st.multi_state <- Some multi_state;
multi_state
| Some multi_state -> multi_state
let change_bound_var st1 st2 cond =
if Option.is_some st1.multi_state then begin
let (_,idx1) = Option.get st1.multi_state in
let (_,idx2) = memo_multi_state st2 in
update_condition idx1 idx2 cond
end else cond
let add_aux_variable vi = AuxVariables.add vi
let aux_variables = AuxVariables.get
let abstract_logic_info = AbstractLogicInfo.get
module StateIndex =
State_builder.Counter(struct let name = "Data_for_aorai.StateIndex" end)
module TransIndex =
State_builder.Counter(struct let name = "Data_for_aorai.TransIndex" end)
let new_state name =
{ name = get_fresh name; acceptation = Bool3.False;
init = Bool3.False; nums = StateIndex.next();
multi_state = None
}
let new_intermediate_state () = new_state "aorai_intermediate_state"
let new_trans start stop cross actions =
{ start; stop; cross; actions; numt = TransIndex.next () }
let check_states s =
let {states;trans} = getAutomata() in
let max = getNumberOfStates () in
List.iter
(fun x -> if x.nums >= max then
Aorai_option.fatal "%s: State %d found while max id is supposed to be %d"
s x.nums max)
states;
List.iter
(fun x ->
try
let y = List.find (fun y -> x.nums = y.nums && not (x==y)) states in
Aorai_option.fatal "%s: State %s and %s share same id %d"
s x.name y.name x.nums
with Not_found -> ()
)
states;
List.iter
(fun x ->
if not (List.memq x.start states) then
Aorai_option.fatal
"%s: Start state %d of transition %d is not among known states"
s x.start.nums x.numt;
if not (List.memq x.stop states) then
Aorai_option.fatal
"%s: End state %d of transition %d is not among known states"
s x.start.nums x.numt;)
trans
let cst_one = PCst (Logic_ptree.IntConstant "1")
let cst_zero = PCst (Logic_ptree.IntConstant "0")
let is_cst_zero e =
match e with
| PCst(IntConstant "0") -> true
| _ -> false
let is_cst_one e =
match e with
PCst (IntConstant "1") -> true
| _ -> false
let is_single elt =
match elt.min_rep, elt.max_rep with
| Some min, Some max -> is_cst_one min && is_cst_one max
| _ -> false
(* Epsilon transitions will account for the possibility of
not entering a repeated sequence at all. They will be normalized after
the entire automaton is processed by adding direct transitions from the
starting state to the children of the end state.
*)
type 'a eps = Normal of 'a | Epsilon of 'a
let print_eps f fmt = function
| Normal x -> f fmt x
| Epsilon x -> Format.fprintf fmt "epsilon-trans:@\n%a" f x
let print_eps_trans fmt tr =
Format.fprintf fmt "%s -> %s:@[%a%a@]"
tr.start.name tr.stop.name
(print_eps Promelaoutput.Typed.print_condition) tr.cross
Promelaoutput.Typed.print_actionl tr.actions
type current_event =
| ECall of
kernel_function
* Cil_types.logic_var Cil_datatype.Varinfo.Hashtbl.t
* (typed_condition eps,typed_action) Promelaast.trans
| EReturn of kernel_function
| ECOR of kernel_function
| ENone (* None found yet *)
| EMulti (* multiple event possible.
repr of the stack does not take into account
this particular event. *)
let add_current_event event env cond =
let is_empty tbl = Cil_datatype.Varinfo.Hashtbl.length tbl = 0 in
match env with
[] -> assert false
| old_event :: tl ->
match event, old_event with
| ENone, _ -> env, cond
| _, ENone -> event::tl, cond
| ECall (kf1,_,_), ECall (kf2,_,_)
when Kernel_function.equal kf1 kf2 -> env, cond
| ECall (kf1,tbl1,_), ECall (kf2,tbl2,_)->
(* ltl2buchi generates such inconsistent guards, but luckily does
not speak about formals. In this case, we just return False with
an empty event. If this situation occurs in an handwritten
automaton that uses formals we simply reject it.
*)
if is_empty tbl1 && is_empty tbl2 then ENone::tl, TFalse
else
Aorai_option.abort
"specification is inconsistent: two call events for distinct \
functions %a and %a at the same time."
Kernel_function.pretty kf1 Kernel_function.pretty kf2
| ECall (_,_,_), EMulti -> event::tl, cond
| ECall (kf1,tbl1,_), EReturn kf2 ->
if is_empty tbl1 then ENone::tl, TFalse
else
Aorai_option.abort
"specification is inconsistent: trying to call %a and \
return from %a at the same time."
Kernel_function.pretty kf1 Kernel_function.pretty kf2
| ECall(kf1,_,_), ECOR kf2
when Kernel_function.equal kf1 kf2 ->
event::tl, cond
| ECall (kf1,tbl1,_), ECOR kf2 ->
if is_empty tbl1 then ENone::tl, TFalse
else
Aorai_option.abort
"specification is inconsistent: trying to call %a and \
call or return from %a at the same time."
Kernel_function.pretty kf1 Kernel_function.pretty kf2
| EReturn kf1, ECall(kf2,tbl2,_) ->
if is_empty tbl2 then ENone::tl, TFalse
else
Aorai_option.abort
"specification is inconsistent: trying to call %a and \
return from %a at the same time."
Kernel_function.pretty kf2 Kernel_function.pretty kf1
| EReturn kf1, (ECOR kf2 | EReturn kf2)
when Kernel_function.equal kf1 kf2 -> event::tl, cond
| EReturn _, EReturn _ -> ENone::tl, TFalse
| EReturn _, ECOR _ -> ENone::tl, TFalse
| EReturn _, EMulti -> ENone::tl, TFalse
| (EMulti | ECOR _), _ -> assert false
(* These are compound event. They cannot be found as individual ones*)
let merge_current_event env1 env2 cond1 cond2 =
assert (List.tl env1 == List.tl env2);
let old_env = List.tl env2 in
match List.hd env1, List.hd env2 with
| ENone, _ -> env2, tor cond1 cond2
| _, ENone -> env1, tor cond1 cond2
| ECall(kf1,_,_), ECall(kf2,_,_)
when Kernel_function.equal kf1 kf2 -> env2, tor cond1 cond2
| ECall _, ECall _ -> EMulti::old_env, tor cond1 cond2
| ECall _, EMulti -> env2, tor cond1 cond2
| ECall (kf1,_,_), ECOR kf2 when Kernel_function.equal kf1 kf2 ->
env2, tor cond1 cond2
| ECall (kf1,_,_), EReturn kf2 when Kernel_function.equal kf1 kf2 ->
ECOR kf1 :: old_env, tor cond1 cond2
| ECall _, (ECOR _ | EReturn _) -> EMulti :: old_env, tor cond1 cond2
| EReturn kf1, ECall (kf2,_,_) when Kernel_function.equal kf1 kf2 ->
ECOR kf1 :: old_env, tor cond1 cond2
| EReturn _, ECall _ -> EMulti :: old_env, tor cond1 cond2
| EReturn kf1, EReturn kf2 when Kernel_function.equal kf1 kf2 ->
env2, tor cond1 cond2
| EReturn _, EReturn _ -> EMulti :: old_env, tor cond1 cond2
| EReturn _, EMulti -> env2, tor cond1 cond2
| EReturn kf1, ECOR kf2 when Kernel_function.equal kf1 kf2 ->
env2, tor cond1 cond2
| EReturn _, ECOR _ ->
EMulti :: old_env, tor cond1 cond2
| ECOR kf1, (ECall(kf2,_,_) | EReturn kf2 | ECOR kf2)
when Kernel_function.equal kf1 kf2 -> env1, tor cond1 cond2
| ECOR _, (ECall _ | EReturn _ | ECOR _) ->
EMulti :: old_env, tor cond1 cond2
| ECOR _, EMulti -> env2, tor cond1 cond2
| EMulti, (ECall _ | EReturn _ | ECOR _) -> env1, tor cond1 cond2
| EMulti, EMulti -> EMulti::old_env, tor cond1 cond2
let get_bindings st my_var =
let my_lval = TVar my_var, TNoOffset in
match st with
None -> my_lval
| Some st ->
let (_,idx) = memo_multi_state st in
Logic_const.addTermOffsetLval (TIndex (Logic_const.tvar idx,TNoOffset)) my_lval
let get_bindings_term st my_var typ =
Logic_const.term (TLval (get_bindings st my_var)) typ
let memo_aux_variable tr counter used_prms vi =
try
let my_var = Cil_datatype.Varinfo.Hashtbl.find used_prms vi in
get_bindings_term counter my_var (Ctype vi.vtype)
with Not_found ->
let my_type =
match counter with
| None -> vi.vtype
| Some _ -> TArray(vi.vtype,None,[])
in
let my_var =
Cil.makeGlobalVar (get_fresh ("aorai_" ^ vi.vname)) my_type
in
add_aux_variable my_var;
let my_lvar = Cil.cvar_to_lvar my_var in
Cil_datatype.Varinfo.Hashtbl.add used_prms vi my_lvar;
(match tr.cross with
| Normal _ ->
let st = Option.map (fun _ -> tr.stop) counter in
let loc = get_bindings st my_lvar in
let copy = Copy_value (loc,Logic_const.tvar (Cil.cvar_to_lvar vi)) in
tr.actions <- copy :: tr.actions
| Epsilon _ ->
Aorai_option.fatal "Epsilon transition used as Call event"
);
get_bindings_term counter my_lvar (Ctype vi.vtype)
let check_one top info counter s =
match info with
| ECall (kf,used_prms,tr) ->
(try
let vi = Globals.Vars.find_from_astinfo s (VFormal kf) in
if top then Some (Logic_const.tvar (Cil.cvar_to_lvar vi))
else Some (memo_aux_variable tr counter used_prms vi)
with Not_found -> None)
| EReturn kf when top && ( Datatype.String.equal s "return"
|| Datatype.String.equal s "\\result") ->
let rt = Kernel_function.get_return_type kf in
if Cil.isVoidType rt then
Aorai_option.abort
"%a returns void. \\result is meaningless in this context"
Kernel_function.pretty kf;
Some (Logic_const.term (TLval (TResult rt,TNoOffset)) (Ctype rt))
| ECOR _ | EReturn _ | EMulti | ENone -> None
let find_metavar s metaenv =
try
Datatype.String.Map.find s metaenv
with Not_found ->
Aorai_option.abort "Metavariable %s not declared" s
let find_in_env env counter s =
let current, stack =
match env with
| current::stack -> current, stack
| [] -> Aorai_option.fatal "Empty type-checking environment"
in
match check_one true current counter s with
Some lv -> lv
| None ->
let module M = struct exception Found of term end in
(try
List.iter
(fun x ->
match check_one false x counter s with
None -> ()
| Some lv -> raise (M.Found lv))
stack;
let vi = Globals.Vars.find_from_astinfo s VGlobal in
Logic_const.tvar (Cil.cvar_to_lvar vi)
with
M.Found lv -> lv
| Not_found -> Aorai_option.abort "Unknown variable %s" s)
let find_prm_in_env env ?tr counter f x =
let kf =
try Globals.Functions.find_by_name f
with Not_found -> Aorai_option.abort "Unknown function %s" f
in
if Datatype.String.equal x "return" ||
Datatype.String.equal x "\\result" then begin
(* Return event *)
let rt = Kernel_function.get_return_type kf in
if Cil.isVoidType rt then
Aorai_option.abort
"%a returns void. %s().%s is meaningless in this context"
Kernel_function.pretty kf f x;
let env,cond = add_current_event (EReturn kf) env (TReturn kf) in
env,
Logic_const.term (TLval (TResult rt,TNoOffset)) (Ctype rt),
cond
end else begin (* Complete Call followed by Return event *)
let rec treat_env top =
function
| ECall(kf',_,_) as event :: _
when Kernel_function.equal kf kf'->
(match check_one top event counter x with
Some lv ->
env, lv, TTrue
| None ->
Aorai_option.abort "Function %s has no parameter %s" f x)
| (ENone | EReturn _ | EMulti | ECOR _ | ECall _ )
:: tl ->
treat_env false tl
| [] ->
let env, cond =
match tr with
None ->
Aorai_option.abort
"Function %s is not in the call stack. \
Cannot use its parameter %s here" f x
| Some tr ->
add_current_event
(ECall (kf, Cil_datatype.Varinfo.Hashtbl.create 3, tr))
env
(TCall (kf,None))
in
let vi =
try Globals.Vars.find_from_astinfo x (VFormal kf)
with Not_found ->
Aorai_option.abort "Function %s has no parameter %s" f x
in
(* By definition, we are at the call event: no need to store
it in an aux variable or array here.
*)
env, Logic_const.tvar (Cil.cvar_to_lvar vi), cond
in treat_env true env
end
module C_logic_env =
struct
let anonCompFieldName = Cabs2cil.anonCompFieldName
let conditionalConversion = Cabs2cil.logicConditionalConversion
let is_loop () = false
let find_macro _ = raise Not_found
let find_var ?label:_ _ = raise Not_found
let find_enum_tag _ = raise Not_found
(*let find_comp_type ~kind:_ _ = raise Not_found*)
let find_comp_field info s =
let field = Cil.getCompField info s in
Field(field,NoOffset)
let find_type _ = raise Not_found
let find_label _ = raise Not_found
include Logic_env
let add_logic_function =
add_logic_function_gen Logic_utils.is_same_logic_profile
let remove_logic_info =
remove_logic_info_gen Logic_utils.is_same_logic_profile
let integral_cast ty t =
Aorai_option.abort
"term %a has type %a, but %a is expected."
Printer.pp_term t Printer.pp_logic_type Linteger Printer.pp_typ ty
let error (source,_) msg = Aorai_option.abort ~source msg
(* we never attempt to recover on an error. *)
let on_error f _ x = f x
end
module LTyping = Logic_typing.Make(C_logic_env)
let type_expr metaenv env ?tr ?current e =
let loc = Cil_datatype.Location.unknown in
let rec aux env cond e =
match e with
| PVar s ->
let var = find_in_env env current s in
env, var, cond
| PPrm(f,x) -> find_prm_in_env env ?tr current f x
| PMetavar s ->
let var = Logic_const.tvar (Cil.cvar_to_lvar (find_metavar s metaenv)) in
env, var, cond
| PCst (Logic_ptree.IntConstant s) ->
let e = Cil.parseIntLogic ~loc s in
env, e, cond
| PCst (Logic_ptree.FloatConstant str) ->
env, Logic_utils.parse_float ~loc str, cond
| PCst (Logic_ptree.StringConstant s) ->
let t =
Logic_const.term
(TConst(LStr (Logic_typing.unescape s))) (Ctype Cil.charPtrType)
in
env,t,cond
| PCst (Logic_ptree.WStringConstant s) ->
let t =
Logic_const.term
(TConst (LWStr (Logic_typing.wcharlist_of_string s)))
(Ctype (TPtr(Cil.theMachine.wcharType,[])))
in env,t,cond
| PBinop(bop,e1,e2) ->
let op = Logic_typing.type_binop bop in
let env,e1,cond = aux env cond e1 in
let env,e2,cond = aux env cond e2 in
let t1 = e1.term_type in
let t2 = e2.term_type in
let t =
if Logic_typing.is_arithmetic_type t1
&& Logic_typing.is_arithmetic_type t2
then
let t = Logic_typing.arithmetic_conversion t1 t2 in
Logic_const.term
(TBinOp (op,LTyping.mk_cast e1 t,LTyping.mk_cast e2 t))
t
else
(match bop with
| Logic_ptree.Badd
when
Logic_typing.is_integral_type t2
&& Logic_utils.isLogicPointerType t1 ->
Logic_const.term (TBinOp (PlusPI,e1,e2)) t1
| Logic_ptree.Bsub
when
Logic_typing.is_integral_type t2
&& Logic_utils.isLogicPointerType t1 ->
Logic_const.term (TBinOp (MinusPI,e1,e2)) t1
| Logic_ptree.Badd
when
Logic_typing.is_integral_type t1
&& Logic_utils.isLogicPointerType t2 ->
Logic_const.term (TBinOp (PlusPI,e2,e1)) t2
| Logic_ptree.Bsub
when
Logic_typing.is_integral_type t1
&& Logic_utils.isLogicPointerType t2 ->
Logic_const.term (TBinOp (MinusPI,e2,e1)) t2
| Logic_ptree.Bsub
when
Logic_utils.isLogicPointerType t1
&& Logic_utils.isLogicPointerType t2 ->
Logic_const.term
(TBinOp (MinusPP,e1,LTyping.mk_cast e2 t1))
Linteger
| _ ->
Aorai_option.abort
"Invalid operands for binary operator %a: \
unexpected %a and %a"
Printer.pp_binop op
Printer.pp_term e1
Printer.pp_term e2)
in
env, t, cond
| PUnop(Logic_ptree.Uminus,e) ->
let env,t,cond = aux env cond e in
if Logic_typing.is_arithmetic_type t.term_type then
env,Logic_const.term (TUnOp (Neg,t)) Linteger,cond
else Aorai_option.abort
"Invalid operand for unary -: unexpected %a" Printer.pp_term t
| PUnop(Logic_ptree.Ubw_not,e) ->
let env,t,cond = aux env cond e in
if Logic_typing.is_arithmetic_type t.term_type then
env,Logic_const.term (TUnOp (BNot,t)) Linteger,cond
else Aorai_option.abort
"Invalid operand for bitwise not: unexpected %a" Printer.pp_term t
| PUnop(Logic_ptree.Uamp,e) ->
let env, t, cond = aux env cond e in
let ptr =
try Ctype (TPtr (Logic_utils.logicCType t.term_type,[]))
with Failure _ ->
Aorai_option.abort "Cannot take address: not a C type(%a): %a"
Printer.pp_logic_type t.term_type Printer.pp_term t
in
(match t.term_node with
| TLval v | TStartOf v -> env, Logic_const.taddrof v ptr, cond
| _ ->
Aorai_option.abort "Cannot take address: not an lvalue %a"
Printer.pp_term t
)
| PUnop (Logic_ptree.Ustar,e) ->
let env, t, cond = aux env cond e in
if Logic_utils.isLogicPointerType t.term_type then
env,
Logic_const.term
(TLval (TMem t, TNoOffset))
(Logic_typing.type_of_pointed t.term_type),
cond
else
Aorai_option.abort "Cannot dereference term %a" Printer.pp_term t
| PArrget(e1,e2) ->
let env, t1, cond = aux env cond e1 in
let env, t2, cond = aux env cond e2 in
let t =
if Logic_utils.isLogicPointerType t1.term_type
&& Logic_typing.is_integral_type t2.term_type
then
Logic_const.term
(TBinOp (PlusPI,t1,t2))
(Logic_typing.type_of_pointed t1.term_type)
else if Logic_utils.isLogicPointerType t2.term_type
&& Logic_typing.is_integral_type t1.term_type
then
Logic_const.term
(TBinOp (PlusPI,t2,t1))
(Logic_typing.type_of_pointed t2.term_type)
else if Logic_utils.isLogicArrayType t1.term_type
&& Logic_typing.is_integral_type t2.term_type
then
(match t1.term_node with
| TStartOf lv | TLval lv ->
Logic_const.term
(TLval
(Logic_const.addTermOffsetLval
(TIndex (t2, TNoOffset)) lv))
(Logic_typing.type_of_array_elem t1.term_type)
| _ ->
Aorai_option.fatal
"Unsupported operation: %a[%a]"
Printer.pp_term t1 Printer.pp_term t2)
else if Logic_utils.isLogicArrayType t2.term_type
&& Logic_typing.is_integral_type t1.term_type
then
(match t2.term_node with
| TStartOf lv | TLval lv ->
Logic_const.term
(TLval
(Logic_const.addTermOffsetLval (TIndex (t1, TNoOffset)) lv))
(Logic_typing.type_of_array_elem t2.term_type)
| _ ->
Aorai_option.fatal
"Unsupported operation: %a[%a]"
Printer.pp_term t1 Printer.pp_term t2)
else
Aorai_option.abort
"Subscripted value is neither array nor pointer: %a[%a]"
Printer.pp_term t1 Printer.pp_term t2
in
env, t, cond
| PField(e,s) ->
let env, t, cond = aux env cond e in
(match t.term_node with
| TLval lv ->
let off, ty = LTyping.type_of_field loc s t.term_type in
let lv = Logic_const.addTermOffsetLval off lv in
env, Logic_const.term (TLval lv) ty, cond
| _ ->
Aorai_option.fatal
"Unsupported operation: %a.%s" Printer.pp_term t s)
| PArrow(e,s) ->
let env, t, cond = aux env cond e in
if Logic_utils.isLogicPointerType t.term_type then begin
let off, ty =
LTyping.type_of_field loc s
(Logic_typing.type_of_pointed t.term_type)
in
let lv = Logic_const.addTermOffsetLval off (TMem t,TNoOffset) in
env, Logic_const.term (TLval lv) ty, cond
end else
Aorai_option.abort "base term is not a pointer in %a -> %s"
Printer.pp_term t s
in
aux env TTrue e
let type_cond needs_pebble metaenv env tr cond =
let current = if needs_pebble then Some tr.stop else None in
let rec aux pos env =
function
| PRel(rel,e1,e2) ->
let env, e1, c1 = type_expr metaenv env ~tr ?current e1 in
let env, e2, c2 = type_expr metaenv env ~tr ?current e2 in
let call_cond = if pos then tand c1 c2 else tor (tnot c1) (tnot c2) in
let rel = TRel(Logic_typing.type_rel rel,e1,e2) in
let cond = if pos then tand call_cond rel else tor call_cond rel in
env, cond
| PTrue -> env, TTrue
| PFalse -> env, TFalse
| POr(c1,c2) ->
let env1, c1 = aux pos env c1 in
let env2, c2 = aux pos env c2 in
let env, c = merge_current_event env1 env2 c1 c2 in
env, c
| PAnd(c1,c2) ->
let env, c1 = aux pos env c1 in
let env, c2 = aux pos env c2 in
env, TAnd (c1,c2)
| PNot c ->
let env, c = aux (not pos) env c in
env, TNot c
| PCall (s,b) ->
let kf =
try Globals.Functions.find_by_name s
with Not_found -> Aorai_option.abort "No such function: %s" s
in
let b =
Option.map
(fun b ->
let bhvs = Annotations.behaviors ~populate:false kf in
try List.find (fun x -> x.b_name = b) bhvs
with Not_found ->
Aorai_option.abort "Function %a has no behavior named %s"
Kernel_function.pretty kf b)
b
in
if pos then
let env, c = add_current_event
(ECall (kf, Cil_datatype.Varinfo.Hashtbl.create 3, tr)) env
(TCall (kf,b))
in
env, c
else
env, TCall (kf,b)
| PReturn s ->
let kf =
try
Globals.Functions.find_by_name s
with Not_found -> Aorai_option.abort "No such function %s" s
in
if pos then
let env,c = add_current_event (EReturn kf) env (TReturn kf) in
env, c
else
env, TReturn kf
in
aux true (ENone::env) cond
module Reject_state =
State_builder.Option_ref(Aorai_state)
(struct
let name = "Data_for_aorai.Reject_state"
let dependencies =
[ Aorai_option.Ltl_File.self; Aorai_option.Buchi.self;
Aorai_option.Ya.self]
end)
let get_reject_state () =
let create () = new_state "aorai_reject" in
Reject_state.memo create
let is_reject_state state =
match Reject_state.get_option () with
None -> false
| Some state' -> Aorai_state.equal state state'
let has_reject_state () =
match Reject_state.get_option () with None -> false | Some _ -> true
let add_if_needed states st =
if List.for_all (fun x -> not (Aorai_state.equal x st)) states
then st::states
else states
let rec type_seq default_state tr metaenv env needs_pebble curr_start curr_end seq =
let loc = Cil_datatype.Location.unknown in
match seq with
| [] -> (* We identify start and end. *)
(env, [], [], curr_end, curr_end)
| elt :: seq ->
let is_single_trans =
match elt.min_rep, elt.max_rep with
| Some min, Some max -> is_cst_one min && is_cst_one max
| None, _ | _, None -> false
in
let is_opt =
match elt.min_rep with
| Some min -> is_cst_zero min
| None-> true
in
let might_be_zero =
is_opt ||
(match Option.get elt.min_rep with PCst _ -> false | _ -> true)
in
let at_most_one =
is_opt &&
match elt.max_rep with
| None -> false
| Some max -> is_cst_one max
in
let has_loop = not at_most_one && not is_single_trans in
let needs_counter =
match elt.min_rep, elt.max_rep with
| None, None -> false
| Some min, None -> not (is_cst_zero min || is_cst_one min)
| None, Some max -> not (is_cst_one max)
| Some min, Some max ->
not (is_cst_zero min || is_cst_one min) || not (is_cst_one max)
in
let fixed_number_of_loop =
match elt.min_rep, elt.max_rep with
| _, None -> false
| None, Some max -> not (is_cst_zero max)
| Some min, Some max -> is_same_expression min max
in
let my_end =
match seq with
[] when not (curr_end.nums = tr.stop.nums)
|| is_single_trans || at_most_one -> curr_end
| _ -> new_intermediate_state ()
in
Aorai_option.debug "Examining single elt:@\n%s -> %s:@[%a@]"
curr_start.name my_end.name Promelaoutput.Parsed.print_seq_elt elt;
let guard_exit_loop env current counter =
if is_opt then TTrue
else
let e = Option.get elt.min_rep in
let _,e,_ = type_expr metaenv env ?current e in
(* If we have done at least the lower bound of cycles, we can exit
the loop. *)
TRel(Cil_types.Rle,e,counter)
in
let guard_loop env current counter =
match elt.max_rep with
| None ->
(* We're using an int: adds an (somewhat artificial) requirements
that the counter itself does not overflow...
*)
let i = Cil.max_signed_number (Cil.bitsSizeOf Cil.intType) in
let e = Logic_const.tint ~loc i in
TRel(Cil_types.Rlt, counter, e)
| Some e ->
let _,e,_ = type_expr metaenv env ?current e in
Max_value_counter.replace counter e;
(* The counter is incremented after the test: it
must be strictly less than the upper bound to enter
a new cycle.
*)
TRel(Cil_types.Rlt, counter, e)
in
let env,inner_states, inner_trans, inner_start, inner_end =
match elt.condition with
| None ->
assert (elt.nested <> []);
(* we don't have a completely empty condition. *)
type_seq
default_state tr metaenv env needs_pebble curr_start my_end elt.nested
| Some cond ->
let seq_start =
match elt.nested with
[] -> my_end
| _ -> new_intermediate_state ()
in
let trans_start = new_trans curr_start seq_start (Normal TTrue) []
in
let inner_env, cond =
type_cond needs_pebble metaenv env trans_start cond
in
let (env,states, seq_transitions, seq_end) =
match elt.nested with
| [] -> inner_env, [], [], my_end
| _ ->
let intermediate = new_intermediate_state () in
let (env, states, transitions, _, seq_end) =
type_seq
default_state tr metaenv
inner_env needs_pebble seq_start intermediate elt.nested
in env, states, transitions, seq_end
in
let states = add_if_needed states curr_start in
let transitions = trans_start :: seq_transitions in
(match trans_start.cross with
| Normal conds ->
trans_start.cross <- Normal (tand cond conds)
| Epsilon _ ->
Aorai_option.fatal
"Transition guard translated as epsilon transition");
let states = add_if_needed states seq_start in
(match env with
| [] | (ENone | ECall _) :: _ ->
(env, states, transitions, curr_start, seq_end)
| EReturn kf1 :: ECall (kf2,_,_) :: tl
when Kernel_function.equal kf1 kf2 ->
tl, states, transitions, curr_start, seq_end
| (EReturn _ | ECOR _ ) :: _ ->
(* If there is as mismatch (e.g. Call f; Return g), it will
be caught later. There are legitimate situations for
this pattern however (if the sequence itself occurs
in a non-empty context in particular)
*)
(env, states, transitions, curr_start, seq_end)
| EMulti :: env_tmp ->
env_tmp, states, transitions, curr_start, seq_end)
in
let loop_end = if has_loop then new_intermediate_state () else inner_end
in
let (_,oth_states,oth_trans,oth_start,_) =
type_seq default_state tr metaenv env needs_pebble loop_end curr_end seq
in
let trans = inner_trans @ oth_trans in
let states = List.fold_left add_if_needed oth_states inner_states in
let auto = (inner_states,inner_trans) in
if at_most_one then begin
(* Just adds an epsilon transition from start to end *)
let opt = new_trans curr_start oth_start (Epsilon TTrue) [] in
env, states, opt::trans, curr_start, curr_end
end
else if has_loop then begin
(* TODO: makes it an integer *)
let counter =
let ty = if needs_pebble then
Cil_types.TArray (Cil.intType,None,[])
else Cil.intType
in (* We won't always need a counter *)
lazy (
let vi = Cil.makeGlobalVar (get_fresh "aorai_counter") ty in
add_aux_variable vi;
vi
)
in
let make_counter st =
let vi = Lazy.force counter in
let base = TVar (Cil.cvar_to_lvar vi), TNoOffset in
if needs_pebble then
let (_,idx) = memo_multi_state st in
Logic_const.addTermOffsetLval
(TIndex (Logic_const.tvar idx,TNoOffset)) base
else base
in
let make_counter_term st =
Logic_const.term (TLval (make_counter st)) (Ctype Cil.intType)
in
Aorai_option.debug "Inner start is %s; Inner end is %s"
inner_start.name inner_end.name;
let treat_state (states, oth_trans) st =
let trans = Path_analysis.get_transitions_of_state st auto in
if st.nums = inner_start.nums then begin
let loop_trans =
if needs_counter then begin
List.fold_left
(fun acc tr ->
let init_action = Counter_init (make_counter tr.stop) in
let init_actions = init_action :: tr.actions in
let init_trans =
new_trans st tr.stop tr.cross init_actions
in
Aorai_option.debug "New init trans %a"
print_eps_trans init_trans;
if at_most_one then init_trans :: acc
else begin
let st =
if needs_pebble then Some curr_start else None
in
let loop_cond =
if needs_counter then
guard_loop env st
(make_counter_term curr_start)
else TTrue
in
let loop_actions =
if needs_counter then
let counter = make_counter curr_start in
Counter_incr counter :: tr.actions
else tr.actions
in
let loop_cross =
match tr.cross with
| Normal cond -> Normal (tand loop_cond cond)
| Epsilon cond -> Epsilon (tand loop_cond cond)
in
let loop_trans =
new_trans inner_end tr.stop loop_cross loop_actions
in
Aorai_option.debug "New loop trans %a"
print_eps_trans loop_trans;
init_trans :: loop_trans :: acc
end)
oth_trans trans
end else oth_trans
in
let trans =
if might_be_zero then begin
(* We can bypass the inner transition altogether *)
let zero_cond =
if is_opt then TTrue
else
let current =
if needs_pebble then Some curr_start else None
in
let _,t,_ =
type_expr metaenv env ?current (Option.get elt.min_rep)
in
TRel (Cil_types.Req, t, Logic_const.tinteger ~loc 0)
in
let no_seq = new_trans st oth_start (Epsilon zero_cond) [] in
no_seq :: loop_trans
end else loop_trans
in
states, trans
end
else if st.nums = inner_end.nums then begin
(* adds conditions on counter if needed *)
let st =
if needs_pebble then Some curr_end else None
in
let min_cond =
if needs_counter then
guard_exit_loop env st (make_counter_term curr_end)
else TTrue
in
let min_cond = Epsilon min_cond in
let exit_trans = new_trans inner_end oth_start min_cond [] in
Aorai_option.debug "New exit trans %a"
print_eps_trans exit_trans;
let trans = exit_trans :: trans @ oth_trans in
states, trans
end else begin
(* inner state: add a rejection state for consistency purposes
iff we don't have a constant number of repetition (i.e. cut
out branches where automaton wrongly start a new step) and
don't have an otherwise branch in the original automaton.
*)
if fixed_number_of_loop || default_state then
states, trans @ oth_trans
else begin
let cond =
List.fold_left
(fun acc tr ->
match tr.cross with
| Normal cond | Epsilon cond ->
let cond = change_bound_var tr.stop st cond in
tor acc cond)
TFalse trans
in
let (cond,_) = Logic_simplification.simplifyCond cond in
let cond = tnot cond in
(match cond with
TFalse -> states, trans @ oth_trans
| _ ->
let reject = get_reject_state () in
let states = add_if_needed states reject in
let trans = new_trans st reject (Normal cond) [] :: trans
in states, trans @ oth_trans
)
end
end
in
let states, trans =
List.fold_left treat_state
(* inner transition gets added in treat_state *)
(states, oth_trans)
inner_states
in
env, states, trans, curr_start, curr_end
end else
env, states, trans, curr_start, curr_end
let type_action metaenv env = function
| Metavar_assign (s, e) ->
let vi = find_metavar s metaenv in
let _, e, _ = type_expr metaenv env e in
(* TODO: check type assignability *)
Copy_value ((TVar (Cil.cvar_to_lvar vi), TNoOffset), e)
let single_path (states,transitions as auto) tr =
Aorai_option.Deterministic.get () ||
(let init = Path_analysis.get_init_states auto in
match init with
| [ st ] ->
let auto = (states,
List.filter (fun x -> x.numt <> tr.numt) transitions)
in
Path_analysis.at_most_one_path auto st tr.start
| _ -> false)
let find_otherwise_trans auto st =
let trans = Path_analysis.get_transitions_of_state st auto in
try let tr = List.find (fun x -> x.cross = Otherwise) trans in Some tr.stop
with Not_found -> None
let type_trans auto metaenv env tr =
let needs_pebble = not (single_path auto tr) in
let has_siblings =
match Path_analysis.get_transitions_of_state tr.start auto with
| [] -> Aorai_option.fatal "Ill-formed automaton"
(* at least tr should be there *)
| [ _ ] -> false (* We only have one sequence to exit from there anyway *)
| _::_::_ -> true
in
Aorai_option.debug
"Analyzing transition %s -> %s: %a (needs pebble: %B)"
tr.start.name tr.stop.name
Promelaoutput.Parsed.print_guard
tr.cross needs_pebble;
match tr.cross with
| Seq seq ->
let default_state = find_otherwise_trans auto tr.start in
let has_default_state = Option.is_some default_state in
let env,states, transitions,_,_ =
type_seq has_default_state tr metaenv env needs_pebble tr.start tr.stop seq
in
(* Insert metavariable assignments for transitions to tr.stop *)
let meta_actions = List.map (type_action metaenv env) tr.actions in
let add_meta_actions t =
if Aorai_state.equal t.stop tr.stop then
{ t with actions = t.actions @ meta_actions }
else
t
in
let transitions = List.map add_meta_actions transitions in
let transitions =
if List.exists (fun st -> st.multi_state <> None) states then begin
(* We have introduced some multi-state somewhere, we have to introduce
pebbles and propagate them from state to state. *)
let start = tr.start in
let count = (* TODO: make it an integer. *)
Cil.makeGlobalVar
(get_fresh ("aorai_cnt_" ^ start.name)) Cil.intType
in
add_aux_variable count;
let transitions =
List.map
(fun trans ->
match trans.cross with
| Epsilon _ -> trans
| Normal _ ->
let (dest,d_aux) = memo_multi_state tr.stop in
let actions =
if tr.start.nums <> start.nums then begin
let src,s_aux = memo_multi_state tr.start in
Pebble_move(dest,d_aux,src,s_aux) :: trans.actions
end else begin
let v = Cil.cvar_to_lvar count in
let incr = Counter_incr (TVar v, TNoOffset) in
let init = Pebble_init (dest, d_aux, v) in
init::incr::trans.actions
end
in
{ trans with actions })
transitions
in
transitions
end else
transitions
in
(* For each intermediate state, add a transition
to either the default state or a rejection state (in which we will
stay until the end of the execution, while another branch might
succeed in an acceptance state.
)*)
let needs_default =
has_siblings &&
match transitions with
| [] | [ _ ] -> false
| _::_::_ -> true
in
Aorai_option.debug "Resulting transitions:@\n%a"
(Pretty_utils.pp_list ~sep:"@\n"
(fun fmt tr -> Format.fprintf fmt "%a"
print_eps_trans tr))
transitions;
states, transitions, needs_default
| Otherwise -> [],[], false (* treated directly by type_seq *)
let add_reject_trans auto intermediate_states =
let treat_one_state (states, trans) st =
let my_trans = Path_analysis.get_transitions_of_state st auto in
let reject_state = get_reject_state () in
let states = add_if_needed states reject_state in
let cond =
List.fold_left
(fun acc tr ->
let cond = change_bound_var tr.stop st tr.cross in
tor cond acc)
TFalse my_trans
in
let cond = fst (Logic_simplification.simplifyCond (tnot cond)) in
match cond with
TFalse -> states,trans
| _ ->
Aorai_option.debug
"Adding default transition %s -> %s: %a"
st.name reject_state.name Promelaoutput.Typed.print_condition cond;
states, new_trans st reject_state cond [] :: trans
in
List.fold_left treat_one_state auto intermediate_states
let propagate_epsilon_transitions (states, _ as auto) =
let rec transitive_closure start conds actions known_states curr =
let known_states = curr :: known_states in
let trans = Path_analysis.get_transitions_of_state curr auto in
List.fold_left
(fun acc tr ->
match tr.cross with
| Epsilon cond ->
Aorai_option.debug "Treating epsilon trans %s -> %s"
curr.name tr.stop.name;
if List.exists (fun st -> st.nums = tr.stop.nums) known_states
then acc
else
transitive_closure
start (tand cond conds) (tr.actions @ actions)
known_states tr.stop @ acc
| Normal cond ->
Aorai_option.debug "Adding transition %s -> %s from epsilon trans"
start.name tr.stop.name;
let tr =
new_trans start tr.stop (tand cond conds) (tr.actions @ actions)
in
tr :: acc)
[] trans
in
let treat_one_state acc st =
acc @ transitive_closure st TTrue [] [] st
in
let trans = List.fold_left treat_one_state [] states in
(states, trans)
let add_default_trans (states, transitions as auto) otherwise =
let add_one_trans acc tr =
let st = tr.start in
let my_trans = Path_analysis.get_transitions_of_state st auto in
Aorai_option.debug "Considering new otherwise transition: %s -> %s"
st.name tr.stop.name;
let cond =
List.fold_left
(fun acc c ->
let cond = c.cross in
Aorai_option.debug "considering trans %s -> %s: %a"
c.start.name c.stop.name Promelaoutput.Typed.print_condition cond;
let neg = tnot cond in
Aorai_option.debug "negation: %a"
Promelaoutput.Typed.print_condition neg;
Aorai_option.debug "acc: %a"
Promelaoutput.Typed.print_condition acc;
let res = tand acc (tnot cond) in
Aorai_option.debug "partial result: %a"
Promelaoutput.Typed.print_condition res;
res
)
TTrue
my_trans
in
Aorai_option.debug "resulting transition: %a"
Promelaoutput.Typed.print_condition cond;
let cond,_ = Logic_simplification.simplifyCond cond in
let new_trans = new_trans st tr.stop cond [] in
new_trans::acc
in
let transitions = List.fold_left add_one_trans transitions otherwise in
states, transitions
let type_cond_auto auto =
let original_auto = auto in
let otherwise = List.filter (fun t -> t.cross = Otherwise) auto.trans in
let add_if_needed acc st =
if List.memq st acc then acc else st::acc
in
let type_trans (states,transitions,add_reject) tr =
let (intermediate_states, trans, needs_reject) =
type_trans (auto.states,auto.trans) auto.metavariables [] tr
in
Aorai_option.debug
"Considering parsed transition %s -> %s" tr.start.name tr.stop.name;
Aorai_option.debug
"Resulting transitions:@\n%a@\nEnd of transitions"
(Pretty_utils.pp_list ~sep:"@\n" print_eps_trans) trans;
let add_reject =
if needs_reject then
(List.filter
(fun x -> not (Aorai_state.equal tr.start x ||
Aorai_state.equal tr.stop x))
intermediate_states) @ add_reject
else add_reject
in
(List.fold_left add_if_needed states intermediate_states,
transitions @ trans,
add_reject)
in
let (states, trans, add_reject) =
List.fold_left type_trans (auto.states,[],[]) auto.trans
in
let auto = propagate_epsilon_transitions (states, trans) in
let auto = add_reject_trans auto add_reject in
let (states, transitions as auto) = add_default_trans auto otherwise in
(* nums (and in the past numt) are used as indices in arrays. Therefore, we
must ensure that we use consecutive numbers starting from 0, or we'll
have needlessly long arrays.
*)
let states, trans =
match Reject_state.get_option () with
| Some state ->
(states, new_trans state state TTrue [] :: transitions)
| None -> auto
in
let auto = { original_auto with states ; trans } in
if Aorai_option.debug_atleast 1 then
Promelaoutput.Typed.output_dot_automata auto "aorai_debug_typed.dot";
let (_,trans) =
List.fold_left
(fun (i,l as acc) t ->
let cond = fst (Logic_simplification.simplifyCond t.cross)
in match cond with
TFalse -> acc
| _ -> (i+1,{ t with cross = cond; numt = i } :: l))
(0,[]) trans
in
let states =
if Aorai_option.Deterministic.get () then
add_if_needed states (get_reject_state())
else states
in
let _, states =
List.fold_left
(fun (i,l as acc) s ->
if
is_reject_state s ||
List.exists
(fun t -> t.start.nums = s.nums || t.stop.nums = s.nums)
trans
then begin
s.nums <- i;
(i+1, s :: l)
end else acc)
(0,[]) states
in
{ original_auto with states = List.rev states; trans = List.rev trans }
(* Check Metavariable compatibility *)
let checkMetavariableCompatibility auto =
let is_extended_trans trans =
match trans.cross with
| Otherwise -> false
| Seq [ elt ] ->
elt.nested <> [] || not (is_single elt)
| Seq _ -> true
in
let has_metavariables = not (Datatype.String.Map.is_empty auto.metavariables)
and deterministic = Aorai_option.Deterministic.get ()
and uses_extended_guards = List.exists is_extended_trans auto.trans in
if has_metavariables && (not deterministic || uses_extended_guards) then
Aorai_option.abort
"The use of metavariables is incompatible with non-deterministic \
automata, such as automata using extended transitions."
let check_observables auto =
match auto.observables with
| None -> () (* No observable list set, everything is observable *)
| Some set ->
let is_relevant name =
try
let kf = Globals.Functions.find_by_name name in
if not (Kernel_function.is_definition kf) then
Aorai_option.warning
"Function %a is observable by the automaton but is not defined \
in the C code. It will be ignored in the instrumentation"
Printer.pp_varname (Kernel_function.get_name kf)
with Not_found ->
Aorai_option.abort "Observable %s doesn't match any function" name
in
let rec check = function
| TAnd (c1,c2) | TOr (c1,c2) -> check c1; check c2
| TNot (c) -> check c
| TRel _ | TTrue | TFalse -> ()
| TCall (kf,_) | TReturn kf ->
let name = Kernel_function.get_name kf in
if not (Datatype.String.Set.mem name set) then
Aorai_option.abort "Function %s is not observable" name
in
Datatype.String.Set.iter is_relevant set;
List.iter (fun tr -> check tr.cross) auto.trans
(** Stores the buchi automaton and its variables and
functions as it is returned by the parsing *)
let setAutomata auto =
checkMetavariableCompatibility auto;
let auto = type_cond_auto auto in
Automaton.set (Some auto);
check_states "typed automaton";
check_observables auto;
if Aorai_option.debug_atleast 1 then
Promelaoutput.Typed.output_dot_automata auto "aorai_debug_reduced.dot";
if (Array.length !cond_of_parametrizedTransitions) <
(getNumberOfTransitions ())
then
(* all transitions have a true parameterized guard, i.e. [[]] *)
cond_of_parametrizedTransitions :=
Array.make (getNumberOfTransitions ()) [[]] ;
Aorai_metavariables.checkInitialization auto ;
Aorai_metavariables.checkSingleAssignment auto
let getState num =
List.find (fun st -> st.nums = num) (getAutomata ()).states
let getStateName num = (getState num).name
let getTransition num =
List.find (fun trans -> trans.numt = num) (getAutomata ()).trans
(** Initializes some tables according to data from Cil AST. *)
let setCData () =
let (f_decl,f_def) =
Globals.Functions.fold
(fun f (lf_decl,lf_def) ->
match f.fundec with
| Definition _ -> (lf_decl, f :: lf_def)
| Declaration _ -> (f :: lf_decl, lf_def))
([],[])
in
defined_functions := f_def;
ignored_functions := f_decl
(** Return true if and only if the given string fname denotes an ignored function. *)
let isIgnoredFunction kf =
List.exists (Kernel_function.equal kf) !ignored_functions
let isDeclaredObservable kf =
let auto = getAutomata () in
let fname = Kernel_function.get_name kf in
match auto.observables with
| None -> true
| Some set ->
Datatype.String.Set.mem fname set
let isObservableFunction kf =
not (isIgnoredFunction kf) && isDeclaredObservable kf
(** Return the list of all function name observed in the C file, except ignored functions. *)
let getObservablesFunctions () =
List.filter isDeclaredObservable !defined_functions
(** Return the list of names of observable but ignored functions. A function is ignored if it is used in C file and if its declaration is unavailable. *)
let getIgnoredFunctions () =
List.filter isDeclaredObservable !ignored_functions
(* ************************************************************************* *)
(* Table giving the varinfo structure associated to a given variable name *)
(* In practice it contains all variables (from promela and globals from C file) and only variables *)
module Aux_varinfos =
State_builder.Hashtbl(Datatype.String.Hashtbl)(Cil_datatype.Varinfo)
(struct
let name = "Data_for_aorai.Aux_varinfos"
let dependencies =
[ Ast.self; Aorai_option.Ya.self; Aorai_option.Ltl_File.self;
Aorai_option.To_Buchi.self; Aorai_option.Deterministic.self ]
let size = 13
end)
let () = Ast.add_linked_state Aux_varinfos.self
module StringPair =
Datatype.Pair_with_collections
(Datatype.String)(Datatype.String)
(struct let module_name = "Data_for_aorai.StringPair" end)
module Paraminfos =
State_builder.Hashtbl(StringPair.Hashtbl)(Cil_datatype.Varinfo)
(struct
let name = "Data_for_aorai.Paraminfos"
let dependencies =
[ Ast.self; Aorai_option.Ya.self; Aorai_option.Ltl_File.self;
Aorai_option.To_Buchi.self; Aorai_option.Deterministic.self ]
let size = 13
end)
(* Add a new variable into the association table name -> varinfo *)
let set_varinfo = Aux_varinfos.add
(* Given a variable name, it returns its associated varinfo.
If the variable is not found then an error message is print and an assert false is raised. *)
let get_varinfo name =
try
Aux_varinfos.find name
with Not_found -> raise_error ("Variable not declared ("^name^")")
let get_logic_var name =
let vi = get_varinfo name in Cil.cvar_to_lvar vi
(* Same as get_varinfo, but the result is an option.
Hence, if the variable is not found then None is return. *)
let get_varinfo_option name =
try
Some(Aux_varinfos.find name)
with
| Not_found -> None
(* Add a new param into the association table (funcname,paramname) -> varinfo *)
let set_paraminfo funcname paramname vi =
(* Aorai_option.log "Adding %s(...,%s,...) " funcname paramname; *)
Paraminfos.add (funcname,paramname) vi
(* Given a function name and a param name, it returns the varinfo associated to the given param.
If the variable is not found then an error message is print and an assert false is raised. *)
let get_paraminfo funcname paramname =
try
Paraminfos.find (funcname,paramname)
with Not_found ->
raise_error
("Parameter '"^paramname^"' not declared for function '"^funcname^"'.")
(* Add a new param into the association table funcname -> varinfo *)
let set_returninfo funcname vi =
(* Aorai_option.log "Adding return %s(...) " funcname ; *)
Paraminfos.add (funcname,"\\return") vi
(* Given a function name, it returns the varinfo associated to the given param.
If the variable is not found then an error message is print and an assert false is raised. *)
let get_returninfo funcname =
try
Paraminfos.find (funcname,"\\return")
with Not_found ->
raise_error ("Return varinfo not declared for function '"^funcname^"'.")
type range =
| Fixed of int (** constant value *)
| Interval of int * int (** range of values *)
| Bounded of int * term (** range bounded by a logic term (depending on
program parameter).
*)
| Unbounded of int (** only the lower bound is known,
there is no upper bound *)
| Unknown (** completely unknown value. *)
module Range = Datatype.Make_with_collections
(struct
type t = range
let name = "Data_for_aorai.Range"
let rehash = Datatype.identity
let structural_descr = Structural_descr.t_abstract
let reprs =
Fixed 0 :: Interval (0,1) :: Unbounded 0 ::
List.map (fun x -> Bounded (0,x)) Cil_datatype.Term.reprs
let equal = Datatype.from_compare
let compare x y =
match x,y with
| Fixed c1, Fixed c2 -> Datatype.Int.compare c1 c2
| Fixed _, _ -> 1
| _, Fixed _ -> -1
| Interval (min1,max1), Interval(min2, max2) ->
let c1 = Datatype.Int.compare min1 min2 in
if c1 = 0 then Datatype.Int.compare max1 max2 else c1
| Interval _, _ -> 1
| _,Interval _ -> -1
| Bounded (min1,max1), Bounded(min2,max2) ->
let c1 = Datatype.Int.compare min1 min2 in
if c1 = 0 then Cil_datatype.Term.compare max1 max2 else c1
| Bounded _, _ -> 1
| _, Bounded _ -> -1
| Unbounded c1, Unbounded c2 -> Datatype.Int.compare c1 c2
| Unbounded _, _ -> 1
| _, Unbounded _ -> -1
| Unknown, Unknown -> 0
let hash = function
| Fixed c1 -> 2 * c1
| Interval(c1,c2) -> 3 * (c1 + c2)
| Bounded (c1,c2) -> 5 * (c1 + Cil_datatype.Term.hash c2)
| Unbounded c1 -> 7 * c1
| Unknown -> 11
let copy = function
| Fixed c1 ->
Fixed (Datatype.Int.copy c1)
| Interval(c1,c2) ->
Interval(Datatype.Int.copy c1, Datatype.Int.copy c2)
| Bounded(c1,c2) ->
Bounded(Datatype.Int.copy c1, Cil_datatype.Term.copy c2)
| Unbounded c1 -> Unbounded (Datatype.Int.copy c1)
| Unknown -> Unknown
let internal_pretty_code _ = Datatype.from_pretty_code
let pretty fmt = function
| Fixed c1 -> Format.fprintf fmt "%d" c1
| Interval (c1,c2) ->
Format.fprintf fmt "@[<2>[%d..@;%d]@]" c1 c2
| Bounded(c1,c2) ->
Format.fprintf fmt "@[<2>[%d..@;%a]@]" c1
Cil_datatype.Term.pretty c2
| Unbounded c1 -> Format.fprintf fmt "[%d..]" c1
| Unknown -> Format.fprintf fmt "[..]"
let varname _ = "r"
let mem_project = Datatype.never_any_project
end)
module Intervals = Cil_datatype.Term.Map.Make(Range)
module Vals = Cil_datatype.Term.Map.Make(Intervals)
(* If we have a bound for the number of iteration, the counter cannot grow
more than bound (we go to a rejection state otherwise).
*)
let absolute_range loc min =
let max = find_max_value loc in
match max with
| Some { term_node = TConst(Integer (t,_)) } ->
Interval(min,Integer.to_int_exn t)
| Some x ->
Bounded (min, Logic_const.term x.term_node x.term_type)
| None -> Unbounded min
let merge_range loc base r1 r2 =
match r1,r2 with
| Fixed c1, Fixed c2 when Datatype.Int.compare c1 c2 = 0 -> r1
| Fixed c1, Fixed c2 ->
let min, max =
if Datatype.Int.compare c1 c2 <= 0 then c1,c2 else c2,c1 in
Interval (min,max)
| Fixed c1, Interval(min,max) ->
let min = if Datatype.Int.compare c1 min <= 0 then c1 else min in
let max = if Datatype.Int.compare max c1 <= 0 then c1 else max in
Interval (min,max)
| Fixed c1, Bounded(min,_) ->
let min = if Datatype.Int.compare c1 min <= 0 then c1 else min in
Unbounded min
| Fixed c1, Unbounded min ->
let min = if Datatype.Int.compare c1 min <= 0 then c1 else min in
Unbounded min
| Interval(min,max), Fixed c ->
if Datatype.Int.compare c min < 0 || Datatype.Int.compare c max > 0 then
begin
let min = if Datatype.Int.compare c min < 0 then c else min in
if Cil.isLogicZero base then
absolute_range loc min
else Unbounded min
end else r1
| Interval(min1,max1), Interval(min2,max2) ->
if Datatype.Int.compare min2 min1 < 0
|| Datatype.Int.compare max2 max1 > 0 then
begin
let min =
if Datatype.Int.compare min2 min1 < 0 then min2 else min1
in
if Cil.isLogicZero base then
absolute_range loc min
else Unbounded min
end else r1
| Interval(min1,_), (Bounded(min2,_) | Unbounded min2)->
let min = if Datatype.Int.compare min1 min2 <= 0 then min1 else min2 in
Unbounded min
| Bounded(min1,max1), Bounded(min2,max2)
when Cil_datatype.Term.equal max1 max2 ->
let min =
if Datatype.Int.compare min2 min1 < 0 then min2 else min1
in
Bounded(min,max1)
| Bounded(min1,_),
(Fixed min2 | Interval(min2,_) | Bounded (min2,_) | Unbounded min2) ->
let min =
if Datatype.Int.compare min2 min1 < 0 then min2 else min1
in Unbounded min
| Unbounded min1,
(Fixed min2 | Interval(min2,_) | Bounded (min2,_) | Unbounded min2) ->
let min =
if Datatype.Int.compare min2 min1 < 0 then min2 else min1
in Unbounded min
| Unknown, _ | _, Unknown -> Unknown
let tlval lv = Logic_const.term (TLval lv) (Cil.typeOfTermLval lv)
let included_range range1 range2 =
match range1, range2 with
| Fixed c1, Fixed c2 -> Datatype.Int.equal c1 c2
| Fixed c, Interval(l,h) ->
Datatype.Int.compare l c <= 0 && Datatype.Int.compare c h <= 0
| Fixed _, Bounded _ -> false
| Fixed c1, Unbounded c2 -> Datatype.Int.compare c1 c2 >= 0
| Interval (l1,h1), Interval(l2,h2) ->
Datatype.Int.compare l1 l2 >= 0 && Datatype.Int.compare h1 h2 <= 0
| Interval (l1,_), Unbounded l2 ->
Datatype.Int.compare l1 l2 >= 0
| Interval _, (Fixed _ | Bounded _ ) -> false
| Bounded _, (Fixed _ | Interval _) -> false
| Bounded(l1,h1), Bounded(l2,h2) ->
Datatype.Int.compare l1 l2 >= 0 && Cil_datatype.Term.equal h1 h2
| Bounded(l1,_), Unbounded l2 -> Datatype.Int.compare l1 l2 <= 0
| Unbounded l1, Unbounded l2 -> Datatype.Int.compare l1 l2 <= 0
| Unbounded _, (Fixed _ | Interval _ | Bounded _) -> false
| _, Unknown -> true
| Unknown, _ -> false
let unchanged loc =
Cil_datatype.Term.Map.add loc (Fixed 0) Cil_datatype.Term.Map.empty
let merge_bindings tbl1 tbl2 =
let merge_range loc = Extlib.merge_opt (merge_range loc) in
let merge_vals loc tbl1 tbl2 =
match tbl1, tbl2 with
| None, None -> None
| Some tbl, None | None, Some tbl ->
Some
(Cil_datatype.Term.Map.merge
(merge_range loc) tbl (unchanged loc))
| Some tbl1, Some tbl2 ->
Some (Cil_datatype.Term.Map.merge (merge_range loc) tbl1 tbl2)
in
Cil_datatype.Term.Map.merge merge_vals tbl1 tbl2
module End_state =
Aorai_state.Map.Make(Datatype.Triple(Aorai_state.Set)(Aorai_state.Set)(Vals))
type end_state = End_state.t
(** The data associated to each statement: We have a mapping from each
possible state at the entrance to the function (before actual transition)
to the current state possibles, associated to any action that has occurred
on that path.
*)
module Case_state = Aorai_state.Map.Make(End_state)
type state = Case_state.t
let pretty_end_state start fmt tbl =
Aorai_state.Map.iter
(fun stop (fst,last, actions) ->
Format.fprintf fmt
"Possible path from %s to %s@\n Initial trans:@\n"
start.Promelaast.name stop.Promelaast.name;
Aorai_state.Set.iter
(fun state ->
Format.fprintf fmt " %s -> %s@\n"
start.Promelaast.name
state.Promelaast.name)
fst;
Format.fprintf fmt " Final trans:@\n";
Aorai_state.Set.iter
(fun state ->
Format.fprintf fmt " %s -> %s@\n"
state.Promelaast.name stop.Promelaast.name)
last;
Format.fprintf fmt " Related actions:@\n";
Cil_datatype.Term.Map.iter
(fun loc tbl ->
Cil_datatype.Term.Map.iter
(fun base itv ->
Format.fprintf fmt " %a <- %a + %a@\n"
Cil_datatype.Term.pretty loc
Cil_datatype.Term.pretty base
Range.pretty itv)
tbl)
actions)
tbl
let pretty_state fmt cases =
Aorai_state.Map.iter (fun start tbl -> pretty_end_state start fmt tbl) cases
let included_state tbl1 tbl2 =
try
Aorai_state.Map.iter
(fun s1 tbl1 ->
let tbl2 = Aorai_state.Map.find s1 tbl2 in
Aorai_state.Map.iter
(fun s2 (fst1, last1, tbl1) ->
let (fst2, last2, tbl2) = Aorai_state.Map.find s2 tbl2 in
if not (Aorai_state.Set.subset fst1 fst2)
|| not (Aorai_state.Set.subset last1 last2)
then raise Not_found;
Cil_datatype.Term.Map.iter
(fun base bindings1 ->
let bindings2 =
Cil_datatype.Term.Map.find base tbl2
in
Cil_datatype.Term.Map.iter
(fun loc range1 ->
let range2 = Cil_datatype.Term.Map.find loc bindings2 in
if not
(included_range range1 range2) then raise Not_found)
bindings1)
tbl1)
tbl1)
tbl1;
true
with Not_found -> false
let merge_end_state tbl1 tbl2 =
let merge_stop_state _ (fst1, last1, tbl1) (fst2, last2, tbl2) =
let fst = Aorai_state.Set.union fst1 fst2 in
let last = Aorai_state.Set.union last1 last2 in
let tbl = merge_bindings tbl1 tbl2 in
(fst, last, tbl)
in
Aorai_state.Map.merge (Extlib.merge_opt merge_stop_state) tbl1 tbl2
let merge_state tbl1 tbl2 =
let merge_state _ = merge_end_state in
Aorai_state.Map.merge (Extlib.merge_opt merge_state) tbl1 tbl2
module Pre_state =
Kernel_function.Make_Table
(Case_state)
(struct
let name = "Data_for_aorai.Pre_state"
let dependencies =
[ Ast.self; Aorai_option.Ya.self; Aorai_option.Ltl_File.self;
Aorai_option.To_Buchi.self; Aorai_option.Deterministic.self ]
let size = 17
end)
let set_kf_init_state kf state =
let change old_state = merge_state old_state state in
let set _ = state in
ignore (Pre_state.memo ~change set kf)
let dkey = Aorai_option.register_category "dataflow"
let replace_kf_init_state kf state =
Aorai_option.debug ~dkey
"Replacing pre-state of %a:@\n @[%a@]"
Kernel_function.pretty kf pretty_state state;
Pre_state.replace kf state
let get_kf_init_state kf =
try
Pre_state.find kf
with Not_found -> Aorai_state.Map.empty
module Post_state =
Kernel_function.Make_Table
(Case_state)
(struct
let name = "Data_for_aorai.Post_state"
let dependencies =
[ Ast.self; Aorai_option.Ya.self; Aorai_option.Ltl_File.self;
Aorai_option.To_Buchi.self; Aorai_option.Deterministic.self ]
let size = 17
end)
let set_kf_return_state kf state =
let change old_state = merge_state old_state state in
let set _ = state in
ignore (Post_state.memo ~change set kf)
let replace_kf_return_state = Post_state.replace
let get_kf_return_state kf =
try
Post_state.find kf
with Not_found -> Aorai_state.Map.empty
module Loop_init_state =
State_builder.Hashtbl
(Cil_datatype.Stmt.Hashtbl)
(Case_state)
(struct
let name = "Data_for_aorai.Loop_init_state"
let dependencies =
[ Ast.self; Aorai_option.Ya.self; Aorai_option.Ltl_File.self;
Aorai_option.To_Buchi.self; Aorai_option.Deterministic.self ]
let size = 17
end)
let set_loop_init_state stmt state =
let change old_state = merge_state old_state state in
let set _ = state in
ignore (Loop_init_state.memo ~change set stmt)
let replace_loop_init_state = Loop_init_state.replace
let get_loop_init_state stmt =
try
Loop_init_state.find stmt
with Not_found -> Aorai_state.Map.empty
module Loop_invariant_state =
State_builder.Hashtbl
(Cil_datatype.Stmt.Hashtbl)
(Case_state)
(struct
let name = "Data_for_aorai.Loop_invariant_state"
let dependencies =
[ Ast.self; Aorai_option.Ya.self; Aorai_option.Ltl_File.self;
Aorai_option.To_Buchi.self; Aorai_option.Deterministic.self ]
let size = 17
end)
let set_loop_invariant_state stmt state =
let change old_state = merge_state old_state state in
let set _ = state in
ignore (Loop_invariant_state.memo ~change set stmt)
let replace_loop_invariant_state = Loop_invariant_state.replace
let get_loop_invariant_state stmt =
try Loop_invariant_state.find stmt
with Not_found -> Aorai_state.Map.empty
let pretty_pre_state fmt =
Pre_state.iter
(fun kf state ->
Format.fprintf fmt "Function %a:@\n @[%a@]@\n"
Kernel_function.pretty kf pretty_state state)
let pretty_post_state fmt =
Post_state.iter
(fun kf state ->
Format.fprintf fmt "Function %a:@\n @[%a@]@\n"
Kernel_function.pretty kf pretty_state state)
let pretty_loop_init fmt =
Loop_init_state.iter
(fun stmt state ->
let kf = Kernel_function.find_englobing_kf stmt in
Format.fprintf fmt "Function %a, sid %d:@\n @[%a@]@\n"
Kernel_function.pretty kf stmt.sid pretty_state state)
let pretty_loop_invariant fmt =
Loop_invariant_state.iter
(fun stmt state ->
let kf = Kernel_function.find_englobing_kf stmt in
Format.fprintf fmt "Function %a, sid %d:@\n @[%a@]@\n"
Kernel_function.pretty kf stmt.sid pretty_state state)
let debug_computed_state ?(dkey=dkey) () =
Aorai_option.debug ~dkey
"Computed state:@\nPre-states:@\n @[%t@]@\nPost-states:@\n @[%t@]@\n\
Loop init:@\n @[%t@]@\nLoop invariants:@\n @[%t@]"
pretty_pre_state pretty_post_state pretty_loop_init pretty_loop_invariant
(* ************************************************************************* *)
let removeUnusedTransitionsAndStates () =
let auto = getAutomata () in
(* Step 1 : computation of reached states and crossed transitions *)
let treat_one_state state map set =
Aorai_state.Map.fold
(fun state (fst, last, _) set ->
Aorai_state.Set.add state
(Aorai_state.Set.union last
(Aorai_state.Set.union fst set)))
map
(Aorai_state.Set.add state set)
in
let reached _ state set = Aorai_state.Map.fold treat_one_state state set in
let init = Path_analysis.get_init_states (getGraph ()) in
let reached_states = Pre_state.fold reached (Aorai_state.Set.of_list init) in
let reached_states = Post_state.fold reached reached_states in
let reached_states = Loop_init_state.fold reached reached_states in
let reached_states = Loop_invariant_state.fold reached reached_states in
if Aorai_state.Set.is_empty reached_states then
raise Empty_automaton;
let reached_states =
if Aorai_option.Deterministic.get() then
(* keep the rejecting state anyways. *)
Aorai_state.Set.add (get_reject_state()) reached_states
else reached_states
in
(* Step 2 : computation of translation tables *)
let state_list =
List.sort
(fun x y -> Datatype.String.compare x.Promelaast.name y.Promelaast.name)
(Aorai_state.Set.elements reached_states)
in
let (_, translate_table) =
List.fold_left
(fun (i,map) x ->
let map = Aorai_state.Map.add x { x with nums = i } map in (i+1,map))
(0,Aorai_state.Map.empty) state_list
in
let new_state s = Aorai_state.Map.find s translate_table in
let (_, trans_list) =
List.fold_left
(fun (i,list as acc) trans ->
try
let new_start = new_state trans.start in
let new_stop = new_state trans.stop in
(i+1,
{ trans with start = new_start; stop = new_stop; numt = i } :: list)
with Not_found -> acc)
(0,[]) auto.trans
in
let state_list = List.map new_state state_list in
Reject_state.may
(fun reject_state ->
try
let new_reject = Aorai_state.Map.find reject_state translate_table in
Reject_state.set new_reject
with Not_found -> Reject_state.clear ());
(* Step 3 : rewriting stored information *)
Automaton.set (Some { auto with states =state_list; trans = trans_list });
check_states "reduced automaton";
let rewrite_state state =
let rewrite_set set =
Aorai_state.Set.fold
(fun s set -> Aorai_state.Set.add (new_state s) set)
set Aorai_state.Set.empty
in
let rewrite_bindings (fst_states, last_states, bindings) =
(rewrite_set fst_states, rewrite_set last_states, bindings)
in
let rewrite_curr_state s bindings acc =
let new_s = new_state s in
let bindings = rewrite_bindings bindings in
Aorai_state.Map.add new_s bindings acc
in
let rewrite_one_state s map acc =
let new_s = new_state s in
let new_map =
Aorai_state.Map.fold rewrite_curr_state map Aorai_state.Map.empty
in
Aorai_state.Map.add new_s new_map acc
in
Aorai_state.Map.fold rewrite_one_state state Aorai_state.Map.empty
in
Pre_state.iter (fun kf state -> Pre_state.replace kf (rewrite_state state));
Post_state.iter (fun kf state -> Post_state.replace kf (rewrite_state state));
Loop_init_state.iter
(fun s state -> Loop_init_state.replace s (rewrite_state state));
Loop_invariant_state.iter
(fun s state -> Loop_invariant_state.replace s (rewrite_state state))
(* ************************************************************************* *)
(* Given the name of a function, it return the name of the associated element in the operation list. *)
let func_to_op_func f = "op_"^f
let used_enuminfo = Hashtbl.create 2
let set_usedinfo name einfo =
Hashtbl.add used_enuminfo name einfo
let get_usedinfo name =
try Hashtbl.find used_enuminfo name
with Not_found -> raise_error ("Incomplete enum information.")
let get_cenum_option name =
let opnamed = func_to_op_func name in
Hashtbl.fold
(fun _ ei value ->
match value with
| Some(_) as r -> r (* Already found *)
| None ->
let rec search = function
| {einame = n} as ei ::_ when n=name -> Some(CEnum ei)
| {einame = n} as ei ::_ when n=opnamed -> Some(CEnum ei)
| _::l -> search l
| [] -> None
in
search ei.eitems
)
used_enuminfo
None
let func_enum_type () =
try TEnum(Hashtbl.find used_enuminfo listOp,[])
with Not_found ->
Aorai_option.fatal
"Enum type indicating current function (%s) is unknown" listOp
let status_enum_type () =
try TEnum(Hashtbl.find used_enuminfo listStatus,[])
with Not_found ->
Aorai_option.fatal
"Enum type indicating current event (%s) is unknown" listStatus
let func_to_cenum func =
try
let ei = Hashtbl.find used_enuminfo listOp in
let name = func_to_op_func func in
let rec search = function
| {einame = n} as ei ::_ when n=name -> CEnum ei
| _::l -> search l
| [] -> raise_error
("Operation '"^name^"' not found in operations enumeration")
in
search ei.eitems
(* CEnum(ex,s,ei)*)
with Not_found -> raise_error ("Operation not found")
let op_status_to_cenum status =
try
let ei = Hashtbl.find used_enuminfo listStatus in
let name = if status = Promelaast.Call then callStatus else termStatus in
let rec search = function
| {einame=n} as ei ::_ when n=name -> CEnum ei
| _::l -> search l
| [] -> raise_error ("Status not found")
in
search ei.eitems
with Not_found -> raise_error ("Status not found")
(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)