-
Virgile Prevosto authored
[kernel] merging admit lemmas & axioms See merge request frama-c/frama-c!3058
Virgile Prevosto authored[kernel] merging admit lemmas & axioms See merge request frama-c/frama-c!3058
aorai_utils.ml 82.58 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 Cil
open Logic_const
open Logic_utils
open Data_for_aorai
open Cil_types
open Cil_datatype
open Promelaast
open Bool3
let func_body_dkey = Aorai_option.register_category "func-body"
let action_dkey = Aorai_option.register_category "action"
let rename_pred v1 v2 p =
let r =
object
inherit Visitor.frama_c_copy (Project.current())
method! vlogic_var_use v =
if Cil_datatype.Logic_var.equal v v1 then Cil.ChangeTo v2
else Cil.JustCopy
end
in
Visitor.visitFramacPredicate r p
(** Given a transition a function name and a function status (call or
return) it returns if the cross condition can be satisfied with
only function status.
*)
let isCrossable tr func st =
let rec isCross p =
match p with
| TOr (c1, c2) -> bool3or (isCross c1) (isCross c2)
| TAnd (c1, c2) -> bool3and (isCross c1) (isCross c2)
| TNot c1 -> bool3not (isCross c1)
| TCall (kf,None) when Kernel_function.equal func kf && st=Call -> True
| TCall (kf, Some _) when Kernel_function.equal func kf && st=Call ->
Undefined
| TCall _ -> False
| TReturn kf when Kernel_function.equal func kf && st=Return -> True
| TReturn _ -> False
| TTrue -> True
| TFalse -> False
| TRel _ -> Undefined
in
let res = isCross tr.cross <> False in
Aorai_option.debug ~level:2 "Function %a %s-state, \
transition %s -> %s is%s possible" Kernel_function.pretty func
(if st=Call then "pre" else "post")
tr.start.Promelaast.name
tr.stop.Promelaast.name
(if res then "" else " NOT");
res
(** Returns the lval associated to the curState generated variable *)
let state_lval () =
Cil.var (get_varinfo curState)
(* ************************************************************************* *)
let find_enum, set_enum =
let module H =
State_builder.Int_hashtbl
(Cil_datatype.Enumitem)
(struct
let name = "ltl_states_enum"
let size = 17
let dependencies = (* TODO: projectify the automata
and depend on it.
*)
[ Ast.self;
Aorai_option.Ltl_File.self;
Aorai_option.Buchi.self;
Aorai_option.Ya.self
]
end)
in
(fun n ->
try H.find n
with Not_found ->
Aorai_option.fatal
"Could not find the enum item corresponding to a state"),
(List.iter (fun (n,item) -> H.add n item))
(* ************************************************************************* *)
(** Given a transition a function name and a function status (call or return)
it returns if the cross condition can be satisfied with only
function status. *)
let isCrossableAtInit tr func =
(* When in doubt, return true anyway. More clever plug-ins will take care
of analysing the instrumented code if needed. *)
let eval_term_at_init t =
if Kernel.LibEntry.get() then t
else begin
let bool_res test =
if test then Cil.lconstant Integer.one else Cil.lzero ()
in
let bool3_res dft test =
match test with
| True -> bool_res true
| False -> bool_res false
| Undefined -> dft
in
let is_true t =
match t with
| TConst(Integer(i,_)) ->
Bool3.bool3_of_bool (not (Integer.is_zero i))
| TConst(LChr c) -> Bool3.bool3_of_bool (not (Char.code c <> 0))
| TConst(LReal r) -> Bool3.bool3_of_bool (not (r.r_nearest <> 0.))
| TConst(LStr _ | LWStr _) -> Bool3.True
| _ -> Bool3.Undefined
in
let rec aux t =
match t.term_node with
| TConst (LEnum ei) ->
aux (Logic_utils.expr_to_term ei.eival)
| TLval lv ->
(match aux_lv lv with
| Some t -> t
| None -> t)
| TUnOp(op,t1) ->
let t1 = aux t1 in
(match op,t1.term_node with
| Neg, TConst(Integer(i,_)) ->
{ t with term_node = TConst(Integer(Integer.neg i,None)) }
| Neg, TConst(LReal r) ->
let f = ~-. (r.r_nearest) in
let r = {
r_literal = string_of_float f ;
r_nearest = f ;
r_upper = ~-. (r.r_lower) ;
r_lower = ~-. (r.r_upper) ;
} in
{ t with term_node = TConst(LReal r) }
| LNot, t1 -> bool3_res t (is_true t1)
| _ -> t)
| TBinOp(op,t1,t2) ->
let t1 = aux t1 in
let t2 = aux t2 in
let rec comparison comp t1 t2 =
match t1.term_node,t2.term_node with
| TConst (Integer(i1,_)), TConst (Integer(i2,_)) ->
bool_res (comp (Integer.compare i1 i2))
| TConst (LChr c1), TConst (LChr c2) ->
bool_res (comp (Char.compare c1 c2))
| TConst(LReal r1), TConst (LReal r2) ->
bool_res (comp (compare r1.r_nearest r2.r_nearest))
| TCastE(ty1,t1), TCastE(ty2,t2)
when Cil_datatype.Typ.equal ty1 ty2 ->
comparison comp t1 t2
| _ -> t
in
(match op, t1.term_node, t2.term_node with
| PlusA, TConst(Integer(i1,_)), TConst(Integer(i2,_)) ->
{ t with term_node =
TConst(Integer(Integer.add i1 i2,None))}
| MinusA, TConst(Integer(i1,_)), TConst(Integer(i2,_)) ->
{ t with term_node =
TConst(Integer(Integer.sub i1 i2,None)) }
| Mult, TConst(Integer(i1,_)), TConst(Integer(i2,_)) ->
{ t with term_node =
TConst(Integer(Integer.mul i1 i2,None)) }
| Div, TConst(Integer(i1,_)), TConst(Integer(i2,_)) ->
(try
{ t with term_node =
TConst(Integer(Integer.c_div i1 i2,None)) }
with Division_by_zero -> t)
| Mod, TConst(Integer(i1,_)), TConst(Integer(i2,_)) ->
(try
{ t with term_node =
TConst(Integer(Integer.c_rem i1 i2,None)) }
with Division_by_zero -> t)
| Shiftlt, TConst(Integer(i1,_)), TConst(Integer(i2,_)) ->
{ t with term_node =
TConst(Integer(Integer.shift_left i1 i2,None)) }
| Shiftrt, TConst(Integer(i1,_)), TConst(Integer(i2,_)) ->
{ t with term_node =
TConst(Integer(Integer.shift_right i1 i2,None)) }
| Lt, _, _ -> comparison ((<) 0) t1 t2
| Gt, _, _ -> comparison ((>) 0) t1 t2
| Le, _, _ -> comparison ((<=) 0) t1 t2
| Ge, _, _ -> comparison ((>=) 0) t1 t2
| Eq, _, _ -> comparison ((=) 0) t1 t2
| Ne, _, _ -> comparison ((<>) 0) t1 t2
| LAnd, t1, t2 ->
bool3_res t (Bool3.bool3and (is_true t1) (is_true t2))
| LOr, t1, t2 ->
bool3_res t (Bool3.bool3or (is_true t1) (is_true t2))
| _ -> t)
| TCastE(ty,t1) ->
let t1 = aux t1 in
(match t1.term_type with
Ctype ty1 when Cil_datatype.Typ.equal ty ty1 -> t1
| _ -> { t with term_node = TCastE(ty,t1) })
| _ -> t
and aux_lv (base,off) =
match base with
| TVar v ->
(try
Option.bind
v.lv_origin
(fun v ->
let init = Globals.Vars.find v in
let init = match init.Cil_types.init with
None -> Cil.makeZeroInit ~loc:v.vdecl v.vtype
| Some i -> i
in
aux_init off init)
with Not_found -> None)
| TMem t ->
(match (aux t).term_node with
| TAddrOf lv -> aux_lv (Logic_const.addTermOffsetLval off lv)
| _ -> None)
| TResult _ -> None
and aux_init off initinfo =
match off, initinfo with
| TNoOffset, SingleInit e ->
Some (aux (Logic_utils.expr_to_term e))
| TIndex(t,oth), CompoundInit (ct,initl) ->
(match (aux t).term_node with
| TConst(Integer(i1,_)) ->
Cil.foldLeftCompound ~implicit:true
~doinit:
(fun o i _ t ->
match o with
| Index({ enode = Const(CInt64(i2,_,_))},_)
when Integer.equal i1 i2 -> aux_init oth i
| _ -> t)
~ct ~initl ~acc:None
| _ -> None)
| TField(f1,oth), CompoundInit(ct,initl) ->
Cil.foldLeftCompound ~implicit:true
~doinit:
(fun o i _ t ->
match o with
| Field(f2,_) when Cil_datatype.Fieldinfo.equal f1 f2 ->
aux_init oth i
| _ -> t)
~ct ~initl ~acc:None
| _ -> None
in
aux t
end
in
let eval_rel_at_init rel t1 t2 =
let t1 = eval_term_at_init (Cil.constFoldTerm true t1) in
let t2 = eval_term_at_init (Cil.constFoldTerm true t2) in
let comp =
match rel with
| Req -> ((=) 0)
| Rneq -> ((<>) 0)
| Rge -> ((>=) 0)
| Rgt -> ((>) 0)
| Rle -> ((<=) 0)
| Rlt -> ((<) 0)
in
let rec comparison t1 t2 =
match t1.term_node,t2.term_node with
| TConst (Integer(i1,_)), TConst (Integer(i2,_)) ->
Bool3.bool3_of_bool (comp (Integer.compare i1 i2))
| TConst (LChr c1), TConst (LChr c2) ->
Bool3.bool3_of_bool (comp (Char.compare c1 c2))
| TConst(LReal r1), TConst (LReal r2) ->
Bool3.bool3_of_bool (comp (compare r1.r_nearest r2.r_nearest))
| TCastE(ty1,t1), TCastE(ty2,t2) when Cil_datatype.Typ.equal ty1 ty2 ->
comparison t1 t2
| _ -> Bool3.Undefined
in
comparison t1 t2
in
let rec isCross = function
| TOr (c1, c2) -> Bool3.bool3or (isCross c1) (isCross c2)
| TAnd (c1, c2) -> Bool3.bool3and (isCross c1) (isCross c2)
| TNot (c1) -> Bool3.bool3not (isCross c1)
| TCall (s,None) -> Bool3.bool3_of_bool (Kernel_function.equal s func)
| TCall (s, Some _) when Kernel_function.equal s func -> Undefined
| TCall _ -> Bool3.False
| TReturn _ -> Bool3.False
| TTrue -> Bool3.True
| TFalse -> Bool3.False
| TRel(rel,t1,t2) -> eval_rel_at_init rel t1 t2
in
match isCross tr.cross with
| Bool3.True | Bool3.Undefined -> true
| Bool3.False -> false
(* ************************************************************************* *)
(** {b Expressions management} *)
(** Returns an int constant expression which represents the given int value. *)
let mk_int_exp value =
new_exp ~loc:Cil_datatype.Location.unknown
(Const(CInt64(Integer.of_int value,IInt,Some(string_of_int value))))
(** This function rewrites a cross condition into an ACSL expression.
Moreover, by giving current operation name and its status (call or
return) the generation simplifies the generated expression.
*)
let crosscond_to_pred cross curr_f curr_status =
let check_current_event f status pred =
if Kernel_function.equal curr_f f && curr_status = status then pred
else (Bool3.False, pfalse)
in
let rec convert =
function
(* Lazy evaluation of logic operators if the result can be statically
computed *)
| TOr (c1, c2) -> (*BinOp(LOr,convert c1,convert c2,Cil.intType)*)
begin
let (c1_val,c1_pred) = convert c1 in
match c1_val with
| Bool3.True -> (c1_val,c1_pred)
| Bool3.False -> convert c2
| Undefined ->
let (c2_val,c2_pred) = convert c2 in
match c2_val with
| Bool3.True -> (c2_val,c2_pred)
| Bool3.False -> (c1_val,c1_pred)
| Undefined -> (Undefined,Logic_const.por(c1_pred, c2_pred))
end
| TAnd (c1, c2) -> (*BinOp(LAnd,convert c1,convert c2,Cil.intType)*)
begin
let (c1_val,c1_pred) = convert c1 in
match c1_val with
| Bool3.True -> convert c2
| Bool3.False -> (c1_val,c1_pred)
| Undefined ->
let (c2_val,c2_pred) = convert c2 in
match c2_val with
| Bool3.True -> (c1_val,c1_pred)
| Bool3.False -> (c2_val,c2_pred)
| Undefined -> (Undefined,Logic_const.pand(c1_pred, c2_pred))
end
| TNot (c1) -> (*UnOp(LNot,convert c1,Cil.intType)*)
begin
let (c1_val,c1_pred) = convert c1 in
match c1_val with
| Bool3.True -> (Bool3.False,pfalse)
| Bool3.False -> (Bool3.True,ptrue)
| Undefined -> (c1_val,Logic_const.pnot(c1_pred))
end
| TCall (f,b) ->
let pred = match b with
None -> Bool3.True, ptrue
| Some b ->
(Bool3.Undefined,
Logic_const.pands
(List.map Logic_const.pred_of_id_pred b.b_assumes))
in
check_current_event f Promelaast.Call pred
| TReturn f ->
check_current_event f Promelaast.Return (Bool3.True, ptrue)
(* Other expressions are left unchanged *)
| TTrue -> (Bool3.True, ptrue)
| TFalse -> (Bool3.False, pfalse)
| TRel(rel,t1,t2) ->
(Bool3.Undefined, Logic_const.prel (rel,t1,t2))
in
snd (convert cross)
(* Translate a term into the correct expression at the location in argument.
Be careful if you wish to re-use this function elsewhere, some cases are
not treated generically.
Used in crosscond_to_exp. *)
let rec term_to_exp t res =
let loc = t.term_loc in
match t.term_node with
| TConst (Integer (value,repr)) -> Cil.kinteger64 ~loc ?repr value
| TConst (LStr str) -> new_exp loc (Const (CStr str))
| TConst (LWStr l) -> new_exp loc (Const (CWStr l))
| TConst (LChr c) -> new_exp loc (Const (CChr c))
| TConst (LReal l_real) ->
(* r_nearest is by definition in double precision. *)
new_exp loc (Const (CReal (l_real.r_nearest, FDouble, None)))
| TConst (LEnum e) -> new_exp loc (Const (CEnum e))
| TLval tlval -> new_exp loc (Lval (tlval_to_lval tlval res))
| TSizeOf ty -> new_exp loc (SizeOf ty)
| TSizeOfE t -> new_exp loc (SizeOfE(term_to_exp t res))
| TSizeOfStr s -> new_exp loc (SizeOfStr s)
| TAlignOf ty -> new_exp loc (AlignOf ty)
| TAlignOfE t -> new_exp loc (AlignOfE (term_to_exp t res))
| TUnOp (unop, t) ->
new_exp loc (UnOp (unop, term_to_exp t res, Cil.intType))
| TBinOp (binop, t1, t2)->
new_exp loc
(BinOp(binop, term_to_exp t1 res, term_to_exp t2 res, Cil.intType))
| TCastE (ty, t) -> new_exp loc (CastE (ty, term_to_exp t res))
| TAddrOf tlval -> new_exp loc (AddrOf (tlval_to_lval tlval res))
| TStartOf tlval -> new_exp loc (StartOf (tlval_to_lval tlval res))
| TLogic_coerce (_,t) -> term_to_exp t res
| _ ->
Aorai_option.fatal
"Term %a cannot be transformed into exp."
Printer.pp_term t
and tlval_to_lval (tlhost, toffset) res =
let rec t_to_loffset t_offset = match t_offset with
TNoOffset -> NoOffset
| TField (f_i,t_off) -> Field(f_i, t_to_loffset t_off)
| TIndex (t, t_off) -> Index (term_to_exp t res, t_to_loffset t_off)
| TModel _ -> Aorai_option.fatal "TModel cannot be treated as exp."
in
match tlhost with
| TVar l_var ->
let v_info =
begin
match l_var.lv_origin with
| Some vinfo -> vinfo
| None -> Aorai_option.fatal "TVar not coming from a C Variable"
end
in
(Var v_info, t_to_loffset toffset)
|TMem t -> mkMem (term_to_exp t res) (t_to_loffset toffset)
|TResult _ ->
(match res with
| Some res -> Var res, t_to_loffset toffset
(* This should not happen, as we always pass a real variable when
generating body for a post-function when the original function
has a non-void result. pre-functions and functions that return void
should not see \result. *)
| None -> Aorai_option.fatal "Unexpected \\result")
module Kf_bhv_cache =
Datatype.Pair_with_collections(Cil_datatype.Kf)(Datatype.String)
(struct let module_name = "Aorai_utils.Kf_bhv_cache" end)
let bhv_aux_functions_table = Kf_bhv_cache.Hashtbl.create 7
let get_bhv_aux_fct kf bhv =
match
Kf_bhv_cache.Hashtbl.find_opt bhv_aux_functions_table (kf,bhv.b_name)
with
| Some vi -> vi, false
| None ->
let loc = Cil_datatype.Location.unknown in
let ovi = Kernel_function.get_vi kf in
let vi = Cil_const.copy_with_new_vid ovi in
vi.vname <- Data_for_aorai.get_fresh (ovi.vname ^ "_bhv_" ^ bhv.b_name);
vi.vdefined <- false;
vi.vghost <- true;
let (_,args,varargs,_) = Cil.splitFunctionTypeVI ovi in
let typ = TFun(Cil.intType, args, varargs,[]) in
Cil.update_var_type vi typ;
Cil.setFormalsDecl vi typ;
vi.vattr <- [];
let assoc =
List.combine (Kernel_function.get_formals kf) (Cil.getFormalsDecl vi)
in
let vis = object
inherit Visitor.frama_c_copy (Project.current())
method! vlogic_var_use lv =
match lv.lv_origin with
| None -> JustCopy
| Some vi ->
(match
List.find_opt (fun (x,_) -> Cil_datatype.Varinfo.equal vi x) assoc
with
| None -> JustCopy
| Some (_,nvi) -> ChangeTo (Cil.cvar_to_lvar nvi))
end
in
let assumes = Visitor.visitFramacPredicates vis bhv.b_assumes in
let assumes = List.map Logic_const.refresh_predicate assumes in
let assigns = Writes [] in
let post_cond =
[Normal,
Logic_const.(
new_predicate
(prel (Req,tlogic_coerce (tresult Cil.intType) Linteger,lone())))]
in
let bhv_in =
Cil.mk_behavior ~name:bhv.b_name ~assumes ~assigns ~post_cond ()
in
let name = bhv.b_name ^ "_out" in
let assumes =
[ Logic_const.(
new_predicate (pnot (pands (List.map pred_of_id_pred assumes))))]
in
let assigns = Writes [] in
let post_cond =
[ Normal,
Logic_const.(
new_predicate
(prel
(Req, tlogic_coerce (tresult Cil.intType) Linteger, lzero())))]
in
let bhv_out = Cil.mk_behavior ~name ~assumes ~assigns ~post_cond () in
Globals.Functions.replace_by_declaration (Cil.empty_funspec()) vi loc;
let my_kf = Globals.Functions.get vi in
Annotations.add_behaviors
~register_children:true Aorai_option.emitter my_kf [bhv_in; bhv_out];
Annotations.add_assigns
~keep_empty:false Aorai_option.emitter my_kf (Writes []);
Annotations.add_complete Aorai_option.emitter my_kf
[bhv_in.b_name; bhv_out.b_name];
Annotations.add_disjoint Aorai_option.emitter my_kf
[bhv_in.b_name; bhv_out.b_name];
vi, true
(** create a new abstract function call to decide whether we are in the
corresponding behavior or not. *)
let mk_behavior_call generated_kf kf bhv =
let aux,generated = get_bhv_aux_fct kf bhv in
let res =
Cil.makeLocalVar
(Kernel_function.get_definition generated_kf)
~ghost:true ~referenced:true ~insert:false
(get_fresh "bhv_aux") Cil.intType
in
let stmt =
Cil.mkStmtOneInstr
~ghost:true
~valid_sid:true
(Cil_types.Call (
Some (Var res, NoOffset),
Cil.evar aux,
List.map (fun x -> Cil.evar x) (Kernel_function.get_formals kf),
Cil_datatype.Location.unknown))
in
(res, stmt,
if generated then Cil_datatype.Varinfo.Set.singleton aux
else Cil_datatype.Varinfo.Set.empty)
(* Translate the cross condition of an automaton edge to an expression.
Used in mk_stmt. This might generate calls to auxiliary functions, to
take into account a guard that uses a function behavior. *)
let crosscond_to_exp generated_kf curr_f curr_status loc cond res =
let check_current_event f status =
Kernel_function.equal curr_f f && curr_status = status
in
let rel_convert = function
| Rlt -> Lt
| Rgt -> Gt
| Rle -> Le
| Rge -> Ge
| Req -> Eq
| Rneq -> Ne
in
let rec expnode_convert =
function
| TOr (c1, c2) ->
let stmts1, vars1, defs1, e1 = expnode_convert c1 in
(match Cil.isInteger e1 with
| None ->
let stmts2, vars2, defs2, e2 = expnode_convert c2 in
stmts1 @ stmts2, vars1 @ vars2,
Cil_datatype.Varinfo.Set.union defs1 defs2,
Cil.mkBinOp loc LOr e1 e2
| Some i when Integer.is_zero i -> expnode_convert c2
| Some _ -> [], [], Cil_datatype.Varinfo.Set.empty,e1)
| TAnd (c1, c2) ->
let stmts1, vars1, defs1, e1 = expnode_convert c1 in
(match Cil.isInteger e1 with
| None ->
let stmts2, vars2, defs2, e2 = expnode_convert c2 in
stmts1 @ stmts2, vars1 @vars2,
Cil_datatype.Varinfo.Set.union defs1 defs2,
Cil.mkBinOp loc LAnd e1 e2
| Some i when Integer.is_zero i ->
[], [], Cil_datatype.Varinfo.Set.empty, e1
| Some _ -> expnode_convert c2)
| TNot (c1) ->
let stmts1, vars1, defs1, e1 = expnode_convert c1 in
(match Cil.isInteger e1 with
| None ->
stmts1, vars1, defs1, Cil.new_exp loc (UnOp(LNot, e1,Cil.intType))
| Some i when Integer.is_zero i ->
[], [], Cil_datatype.Varinfo.Set.empty, Cil.one loc
| Some _ -> [], [], Cil_datatype.Varinfo.Set.empty, Cil.zero loc)
| TCall (f,None) ->
if check_current_event f Promelaast.Call then
[], [], Cil_datatype.Varinfo.Set.empty, Cil.one loc
else
[], [], Cil_datatype.Varinfo.Set.empty, Cil.zero loc
| TCall (f, Some bhv) ->
if check_current_event f Promelaast.Call then begin
let res, stmt, new_kf = mk_behavior_call generated_kf f bhv in
[ stmt ], [res], new_kf, Cil.evar res
end else
[], [], Cil_datatype.Varinfo.Set.empty, Cil.zero loc
| TReturn f ->
if check_current_event f Promelaast.Return then
[], [], Cil_datatype.Varinfo.Set.empty, Cil.one loc
else
[], [], Cil_datatype.Varinfo.Set.empty, Cil.zero loc
| TTrue -> [], [], Cil_datatype.Varinfo.Set.empty, Cil.one loc
| TFalse -> [], [], Cil_datatype.Varinfo.Set.empty, Cil.zero loc
| TRel(rel,t1,t2) ->
[], [], Cil_datatype.Varinfo.Set.empty,
Cil.mkBinOp
loc (rel_convert rel) (term_to_exp t1 res) (term_to_exp t2 res)
in
expnode_convert cond
(* ************************************************************************* *)
(** {b Globals management} *)
(** Local copy of the file pointer *)
let file = ref Cil.dummyFile
let initFunction kf =
let fname = Kernel_function.get_name kf in
List.iter
(fun vi -> set_paraminfo fname vi.vname vi)
(Kernel_function.get_formals kf);
match (Kernel_function.find_return kf).skind with
| Cil_types.Return (Some { enode = Lval (Var vi,NoOffset) },_) ->
set_returninfo fname vi (* Add the vi of return stmt *)
| exception Kernel_function.No_Statement | _ -> () (* function without returned value *)
(** Copy the file pointer locally in the class in order to ease globals
management and initializes some tables. *)
let initFile f =
file := f;
Data_for_aorai.setCData ();
(* Adding C variables into our hashtable *)
Globals.Vars.iter (fun vi _ -> set_varinfo vi.vname vi);
Globals.Functions.iter initFunction
(** List of globals awaiting for adding into C file globals *)
let globals_queue = ref []
(** Flush all queued globals declarations into C file globals. *)
let flush_globals () =
let before, after =
List.fold_left
(fun (b,a) elem ->
match elem with
| GFun(f,loc) as func ->
(* [VP] if address of function is taken, it might be
used in a global initializer: keep a declaration at this point
to ensure ending up with a compilable C file in the end... *)
let b =
if f.svar.vaddrof then GFunDecl(Cil.empty_funspec(),f.svar,loc) :: b
else b
in
b, func :: a
| other -> other :: b, a)
([], [])
!file.globals
in
!file.globals <- List.rev before @ List.rev !globals_queue @ List.rev after;
Kernel_function.clear_sid_info ();
globals_queue := []
let add_global glob = globals_queue := glob :: !globals_queue
(* Utilities for global variables *)
let add_gvar ?init vi =
let initinfo = {Cil_types.init} in
vi.vghost <- true;
vi.vstorage <- NoStorage;
add_global (GVar(vi,initinfo,vi.vdecl));
Globals.Vars.add vi initinfo;
set_varinfo vi.vname vi
let add_gvar_zeroinit vi =
add_gvar ~init:(Cil.makeZeroInit ~loc:(CurrentLoc.get()) vi.vtype) vi
let mk_gvar ?init ~ty name =
(* See if the variable is already declared *)
let vi =
try
let ty' = typeAddAttributes [Attr ("ghost", [])] ty in
let vi = Globals.Vars.find_from_astinfo name VGlobal in
if not (Cil_datatype.Typ.equal vi.vtype ty') then
Aorai_option.abort "Global %s is declared with type %a instead of %a"
name Cil_printer.pp_typ vi.vtype Cil_printer.pp_typ ty';
Globals.Vars.remove vi;
vi
with Not_found ->
Cil.makeGlobalVar name ty
in
add_gvar ?init vi
let mk_gvar_scalar ~init ?(ty = Cil.typeOf init) name =
mk_gvar ~init:(SingleInit init) ~ty name
let mk_integer value =
Cil.integer ~loc:(CurrentLoc.get()) value
(* Utilities for global enumerations *)
let mk_global_c_enum_type_tagged name elements_l =
let einfo =
{ eorig_name = name;
ename = name;
eitems = [];
eattr = [];
ereferenced = true;
ekind = IInt; }
in
let l =
List.map
(fun (e,i) ->
{ eiorig_name = e;
einame = e;
eival = mk_integer i;
eiloc = Location.unknown;
eihost = einfo})
elements_l
in
einfo.eitems <- l;
set_usedinfo name einfo;
add_global (GEnumTag(einfo, Location.unknown));
einfo
let mk_global_c_enum_type name elements =
let _,elements =
List.fold_left (fun (i,l) x -> (i+1,(x,i)::l)) (0,[]) elements
in
(* no need to rev the list, as the elements got their value already *)
ignore (mk_global_c_enum_type_tagged name elements)
let mk_gvar_enum ?init name name_enuminfo =
mk_gvar ?init ~ty:(TEnum(get_usedinfo name_enuminfo,[])) name
(* ************************************************************************* *)
(** {b Terms management / computation} *)
(** Return an integer constant term from the given value. *)
let mk_int_term value = Cil.lconstant (Integer.of_int value)
(** Return an integer constant term with the 0 value.
@deprecated use directly Cil.lzero
*)
let zero_term() = Cil.lzero ()
let one_term () = Cil.lconstant Integer.one
(** Returns a term representing the variable associated to the given varinfo *)
let mk_term_from_vi vi =
Logic_const.term
(TLval((Logic_utils.lval_to_term_lval (Cil.var vi))))
(Ctype Cil.intType)
(** Given an lval term 'host' and an integer value 'off', it returns a lval term host[off]. *)
let mk_offseted_array host off =
Logic_const.term
(TLval(Logic_const.addTermOffsetLval (TIndex(mk_int_term (off),TNoOffset)) host))
(Ctype Cil.intType)
let int2enumstate nums =
let enum = find_enum nums in
Logic_const.term (TConst (LEnum enum)) (Ctype (TEnum (enum.eihost,[])))
let int2enumstate_exp loc nums = new_exp loc (Const (CEnum (find_enum nums)))
(** Given an lval term 'host' and an integer value 'off', it returns a lval term host[off]. *)
let mk_offseted_array_states_as_enum host off =
let enum = find_enum off in
Logic_const.term
(TLval
(Logic_const.addTermOffsetLval
(TIndex(Logic_const.term
(TConst(LEnum enum)) (Ctype (TEnum (enum.eihost,[]))),
TNoOffset))
host))
(Ctype Cil.intType)
(** Returns a lval term associated to the curState generated variable. *)
let host_state_term() = lval_to_term_lval (state_lval())
(*
(** Returns a lval term associated to the curStateOld generated variable. *)
let host_stateOld_term () =
lval_to_term_lval ~cast:true (Cil.var (get_varinfo curStateOld))
(** Returns a lval term associated to the curTrans generated variable. *)
let host_trans_term () =
lval_to_term_lval ~cast:true (Cil.var (get_varinfo curTrans))
*)
let state_term () =
Logic_const.tvar (Cil.cvar_to_lvar (get_varinfo curState))
(*
let stateOld_term () =
Logic_const.tvar (Cil.cvar_to_lvar (get_varinfo curStateOld))
let trans_term () =
Logic_const.tvar (Cil.cvar_to_lvar (get_varinfo curTrans))
*)
(* Utilities for generation of predicates / statements / expression
describing states' status. *)
let is_state_pred state =
if Aorai_option.Deterministic.get () then
Logic_const.prel (Req,state_term(),int2enumstate state.nums)
else
Logic_const.prel
(Req,one_term(),
Logic_const.tvar (Data_for_aorai.get_state_logic_var state))
let is_state_non_det_stmt (_,copy) loc =
mkStmtOneInstr ~ghost:true (Set (Cil.var copy, Cil.one loc, loc))
let is_state_det_stmt state loc =
let var = Data_for_aorai.get_varinfo curState in
mkStmtOneInstr
~ghost:true (Set (Cil.var var, int2enumstate_exp loc state.nums, loc))
let is_state_exp state loc =
if Aorai_option.Deterministic.get ()
then
Cil.mkBinOp
loc Eq
(int2enumstate_exp loc state.nums)
(Cil.evar ~loc (Data_for_aorai.get_varinfo curState))
else
Cil.mkBinOp
loc Eq (Cil.evar (Data_for_aorai.get_state_var state)) (Cil.one loc)
let is_out_of_state_pred state =
if Aorai_option.Deterministic.get () then
Logic_const.prel (Rneq,state_term(),int2enumstate state.nums)
else
Logic_const.prel
(Req,zero_term(),
Logic_const.tvar (Data_for_aorai.get_state_logic_var state))
(* In the deterministic case, we only assign the unique state variable
to a specific enumerated constant. Non-deterministic automata on the other
hand, need to have the corresponding state variable explicitly set to 0. *)
let is_out_of_state_stmt (_,copy) loc =
if Aorai_option.Deterministic.get ()
then
Aorai_option.fatal
"Deterministic automaton sync functions can't have out-of-state stmt. \
Maybe this should use `is_out_of_state_exp' instead."
else mkStmtOneInstr ~ghost:true (Set(Cil.var copy , mk_int_exp 0 , loc ))
let is_out_of_state_exp state loc =
if Aorai_option.Deterministic.get ()
then
Cil.mkBinOp
loc Ne
(int2enumstate_exp loc state.nums)
(evar ~loc (Data_for_aorai.get_varinfo curState))
else
Cil.mkBinOp
loc Eq
(Cil.evar (Data_for_aorai.get_state_var state))
(mk_int_exp 0)
let assert_alive_automaton kf stmt =
let pred =
if Aorai_option.Deterministic.get() then
let reject_state = Data_for_aorai.get_reject_state() in
is_out_of_state_pred reject_state
else begin
let valid_states =
List.filter
(fun x -> not (Data_for_aorai.is_reject_state x))
(fst (Data_for_aorai.getGraph ()))
in
let valid_preds = List.map is_state_pred valid_states in
Logic_const.pors valid_preds
end
in
let pred = { pred with pred_name = "aorai_smoke_test" :: pred.pred_name } in
Annotations.add_assert Aorai_option.emitter ~kf stmt pred
(* Utilities for other globals *)
let mk_global_comment txt = add_global (GText (txt))
(* ************************************************************************* *)
(** {b Initialization management / computation} *)
let mk_global_states_init root =
let (states,_ as auto) = Data_for_aorai.getGraph () in
let states = List.sort Data_for_aorai.Aorai_state.compare states in
let is_possible_init state =
state.Promelaast.init = Bool3.True &&
(let trans = Path_analysis.get_transitions_of_state state auto in
List.exists (fun tr -> isCrossableAtInit tr root) trans)
in
List.iter
(fun state ->
let init =
if is_possible_init state then mk_int_exp 1 else mk_int_exp 0
in
let init = SingleInit init in
let var = Data_for_aorai.get_state_var state in
add_gvar ~init var)
states
class visit_decl_loops_init () =
object(self)
inherit Visitor.frama_c_inplace
method! vstmt_aux stmt =
begin
match stmt.skind with
| Loop _ ->
let scope = Kernel_function.find_enclosing_block stmt in
let f = Option.get self#current_func in
let name = Data_for_aorai.loopInit ^ "_" ^ (string_of_int stmt.sid) in
let typ =
Cil.typeAddAttributes
[Attr (Cil.frama_c_ghost_formal,[])] Cil.intType
in
let var = Cil.makeLocalVar ~ghost:true f ~scope name typ in
Data_for_aorai.set_varinfo name var
| _ -> ()
end;
Cil.DoChildren
end
let mk_decl_loops_init () =
let visitor = new visit_decl_loops_init () in
Cil.visitCilFile (visitor :> Cil.cilVisitor) !file
let change_vars subst subst_res kf label pred =
let add_label t = ChangeDoChildrenPost(t,fun t -> tat(t,label)) in
let visitor =
object
inherit Visitor.frama_c_copy (Project.current())
method! vterm t =
match t.term_node with
TLval (TVar { lv_origin = Some v},_) when v.vglob -> add_label t
| TLval (TMem _,_) -> add_label t
| _ -> DoChildren
method! vterm_lhost = function
| TResult ty ->
(match kf with
None -> Aorai_option.fatal
"found \\result without being at a Return event"
| Some kf ->
(try
ChangeTo (TVar (Kernel_function.Hashtbl.find subst_res kf))
with Not_found ->
let new_lv =
Cil_const.make_logic_var_quant
("__retres_" ^ (Kernel_function.get_name kf)) (Ctype ty)
in
Kernel_function.Hashtbl.add subst_res kf new_lv;
ChangeTo (TVar new_lv)))
| TMem _ | TVar _ -> DoChildren
method! vlogic_var_use lv =
match lv.lv_origin with
| Some v when not v.vglob ->
(try
ChangeTo (Cil_datatype.Logic_var.Hashtbl.find subst lv)
with Not_found ->
let new_lv =
Cil_const.make_logic_var_quant lv.lv_name lv.lv_type
in
Cil_datatype.Logic_var.Hashtbl.add subst lv new_lv;
ChangeTo new_lv)
| Some _ | None -> DoChildren
end
in Visitor.visitFramacPredicateNode visitor pred
let pred_of_condition subst subst_res label cond =
let mk_func_event f =
let op = tat (mk_term_from_vi (get_varinfo curOp),label) in
(* [VP] TODO: change int to appropriate enum type. Also true
elsewhere.
*)
let f =
term
(TConst (constant_to_lconstant (func_to_cenum f)))
(Ctype (func_enum_type ()))
in
prel (Req,op,f)
in
let mk_func_status f status =
let curr = tat (mk_term_from_vi (get_varinfo curOpStatus),label) in
let call =
term
(TConst (constant_to_lconstant (op_status_to_cenum status)))
(Ctype (status_enum_type()))
in
Logic_const.pand (mk_func_event f, prel(Req,curr,call))
in
let mk_func_start f = mk_func_status f Promelaast.Call in
let mk_func_return f = mk_func_status f Promelaast.Return in
let rec aux kf is_or = function
| TOr(c1,c2) ->
let kf, c1 = aux kf true c1 in
let kf, c2 = aux kf true c2 in
kf, Logic_const.por (c1, c2)
| TAnd(c1,c2) ->
let kf, c1 = aux kf false c1 in
let kf, c2 = aux kf false c2 in
kf, Logic_const.pand (c1, c2)
| TNot c ->
let kf, c = aux kf (not is_or) c in kf, Logic_const.pnot c
| TCall (s,b) ->
let pred = mk_func_start (Kernel_function.get_name s) in
let pred =
match b with
| None -> pred
| Some b ->
Logic_const.pands
(pred :: (List.map Logic_const.pred_of_id_pred b.b_assumes))
in
kf, pred
| TReturn s ->
let kf = if is_or then kf else Some s in
kf, mk_func_return (Kernel_function.get_name s)
| TTrue -> kf, ptrue
| TFalse -> kf, pfalse
| TRel(rel,t1,t2) ->
kf,
unamed (change_vars subst subst_res kf label (prel (rel,t1,t2)).pred_content)
in
snd (aux None true cond)
let mk_deterministic_lemma () =
let automaton = Data_for_aorai.getGraph () in
let make_one_lemma state =
let label = Cil_types.FormalLabel "L" in
let disjoint_guards acc trans1 trans2 =
if trans1.numt <= trans2.numt then acc
(* don't need to repeat the same condition twice*)
else
let subst = Cil_datatype.Logic_var.Hashtbl.create 5 in
let subst_res = Kernel_function.Hashtbl.create 5 in
let guard1 =
pred_of_condition subst subst_res label trans1.cross
in
let guard2 =
pred_of_condition subst subst_res label trans2.cross
in
let pred = Logic_const.pnot (Logic_const.pand (guard1, guard2)) in
let quants =
Cil_datatype.Logic_var.Hashtbl.fold
(fun _ lv acc -> lv :: acc) subst []
in
let quants = Kernel_function.Hashtbl.fold
(fun _ lv acc -> lv :: acc) subst_res quants
in
(* [VP] far from perfect, but should give oracles for
regression tests that stay relatively stable across vid
changes. *)
let quants =
List.sort (fun v1 v2 -> String.compare v1.lv_name v2.lv_name) quants
in
Logic_const.pand (acc, (pforall (quants, pred)))
in
let trans = Path_analysis.get_transitions_of_state state automaton in
let prop = Extlib.product_fold disjoint_guards ptrue trans trans in
let prop = Logic_const.toplevel_predicate ~kind:Check prop in
let name = state.Promelaast.name ^ "_deterministic_trans" in
let lemma =
Dlemma (name, [label],[],prop,[],Cil_datatype.Location.unknown)
in
Annotations.add_global Aorai_option.emitter lemma
in
List.iter make_one_lemma (fst automaton)
let make_enum_states () =
let state_list =fst (Data_for_aorai.getGraph()) in
let state_list =
List.map (fun x -> (x.Promelaast.name, x.Promelaast.nums)) state_list
in
let enum = mk_global_c_enum_type_tagged states state_list in
let mapping =
List.map
(fun (name,id) ->
let item =
List.find (fun y -> y.einame = name) enum.eitems
in
(id, item))
state_list
in
set_enum mapping
let getInitialState () =
let loc = Cil_datatype.Location.unknown in
let states = fst (Data_for_aorai.getGraph()) in
let s = List.find (fun x -> x.Promelaast.init = Bool3.True) states in
Cil.new_exp ~loc (Const (CEnum (find_enum s.nums)))
(** This function computes all newly introduced globals (variables, enumeration structure, invariants, etc. *)
let initGlobals root complete =
mk_global_comment "//****************";
mk_global_comment "//* BEGIN Primitives generated for LTL verification";
mk_global_comment "//* ";
mk_global_comment "//* ";
mk_global_comment "//* Some constants";
if Aorai_option.Deterministic.get () then make_enum_states ();
(* non deterministic mode uses one variable for each possible state *)
mk_global_c_enum_type
listOp
(List.map
(fun kf -> func_to_op_func (Kernel_function.get_name kf))
(getObservablesFunctions() @ getIgnoredFunctions()));
mk_gvar_enum curOp listOp;
mk_global_c_enum_type listStatus (callStatus::[termStatus]);
mk_gvar_enum curOpStatus listStatus;
mk_global_comment "//* ";
mk_global_comment "//* States and Trans Variables";
if Aorai_option.Deterministic.get () then begin
mk_gvar_scalar ~init:(getInitialState()) curState;
let init = getInitialState() (* TODO a distinct initial value for history *)
and history = Data_for_aorai.whole_history () in
List.iter (fun name -> mk_gvar_scalar ~init name) history
end else
mk_global_states_init root;
if complete then begin
mk_global_comment "//* ";
mk_global_comment "//* Loops management";
mk_decl_loops_init ();
end;
mk_global_comment "//* ";
mk_global_comment "//****************** ";
mk_global_comment "//* Auxiliary variables used in transition conditions";
mk_global_comment "//*";
List.iter add_gvar_zeroinit (Data_for_aorai.aux_variables());
let auto = Data_for_aorai.getAutomata () in
mk_global_comment "//* ";
mk_global_comment "//****************** ";
mk_global_comment "//* Metavariables";
mk_global_comment "//*";
Datatype.String.Map.iter (fun _ -> add_gvar_zeroinit) auto.metavariables;
if Aorai_option.Deterministic.get () &&
Aorai_option.GenerateDeterministicLemmas.get () then begin
(* must flush now previous globals which are used in the lemmas in order to
be able to put these last ones in the right places in the AST. *)
flush_globals ();
mk_deterministic_lemma ();
end;
(match Data_for_aorai.abstract_logic_info () with
| [] -> ()
| l ->
let annot =
Daxiomatic
("Aorai_pebble_axiomatic",
List.map
(fun li -> Dfun_or_pred(li,Cil_datatype.Location.unknown)) l,
[],
Cil_datatype.Location.unknown)
in
Annotations.add_global Aorai_option.emitter annot);
mk_global_comment "//* ";
mk_global_comment "//* END Primitives generated for LTL verification";
mk_global_comment "//****************";
flush_globals ()
(* ************************************************************************* *)
(** {b Pre/post management} *)
let automaton_locations loc =
let auto_state =
if Aorai_option.Deterministic.get () then
[ Logic_const.new_identified_term (state_term()), FromAny ]
else
List.map
(fun state ->
Logic_const.new_identified_term
(Logic_const.tvar
(Data_for_aorai.get_state_logic_var state)), FromAny)
(fst (Data_for_aorai.getGraph()))
in
(Logic_const.new_identified_term
(Logic_const.tvar ~loc
(Data_for_aorai.get_logic_var Data_for_aorai.curOpStatus)),
FromAny) ::
(Logic_const.new_identified_term
(Logic_const.tvar ~loc
(Data_for_aorai.get_logic_var Data_for_aorai.curOp)),
FromAny) ::
auto_state
let automaton_assigns loc = Writes (automaton_locations loc)
let aorai_assigns state loc =
let merged_states =
Aorai_state.Map.fold
(fun _ state acc -> Data_for_aorai.merge_end_state state acc)
state Aorai_state.Map.empty
in
let bindings =
Aorai_state.Map.fold
(fun _ (_,_,b) acc -> Data_for_aorai.merge_bindings b acc)
merged_states Cil_datatype.Term.Map.empty
in
let elements =
Cil_datatype.Term.Map.fold
(fun t _ acc -> (Logic_const.new_identified_term t,FromAny)::acc)
bindings []
in
Writes (automaton_locations loc @ elements)
let action_assigns trans =
let add_if_needed v lv (known_vars, assigns as acc) =
if Cil_datatype.Varinfo.Set.mem v known_vars then acc
else
Cil_datatype.Varinfo.Set.add v known_vars,
(Logic_const.new_identified_term lv, FromAny)::assigns
in
let treat_one_action acc =
function
| Counter_init (host,off) | Counter_incr (host,off)
| Copy_value ((host,off),_) ->
let my_var =
match host with
| TVar ({ lv_origin = Some v}) -> v
| _ -> Aorai_option.fatal "Auxiliary variable is not a C global"
in
let my_off =
match off with
| TNoOffset -> TNoOffset
| TIndex _ -> TIndex(Logic_const.trange (None,None), TNoOffset)
| TField _ | TModel _ ->
Aorai_option.fatal "Unexpected offset in auxiliary variable"
in
add_if_needed my_var
(Logic_const.term (TLval(host,my_off))
(Cil.typeOfTermLval (host,my_off)))
acc
| Pebble_init(_,v,c) ->
let cc = Option.get c.lv_origin in
let cv = Option.get v.lv_origin in
add_if_needed cv (Logic_const.tvar v)
(add_if_needed cc (Logic_const.tvar c) acc)
| Pebble_move(_,v1,_,v2) ->
let cv1 = Option.get v1.lv_origin in
let cv2 = Option.get v2.lv_origin in
add_if_needed cv1 (Logic_const.tvar v1)
(add_if_needed cv2 (Logic_const.tvar v2) acc)
in
let empty = (Cil_datatype.Varinfo.Set.empty,[]) in
let empty_pebble =
match trans.start.multi_state, trans.stop.multi_state with
| Some(_,aux), None ->
let caux = Option.get aux.lv_origin in
add_if_needed caux (Logic_const.tvar aux) empty
| _ -> empty
in
let _,res = List.fold_left treat_one_action empty_pebble trans.actions in
Writes res
let get_reachable_trans state st auto current_state =
match st with
| Promelaast.Call ->
(try
let reach = Data_for_aorai.Aorai_state.Map.find state current_state in
let treat_one_state end_state _ l =
Path_analysis.get_edges state end_state auto @ l
in
Data_for_aorai.Aorai_state.Map.fold treat_one_state reach []
with Not_found -> [])
| Promelaast.Return ->
let treat_one_state end_state (_,last,_) l =
if Data_for_aorai.Aorai_state.Set.mem state last then
Path_analysis.get_edges state end_state auto @ l
else l
in
let treat_one_start _ map l =
Data_for_aorai.Aorai_state.Map.fold treat_one_state map l
in
Data_for_aorai.Aorai_state.Map.fold treat_one_start current_state []
let get_reachable_trans_to state st auto current_state =
match st with
| Promelaast.Call ->
let treat_one_start start map acc =
if Data_for_aorai.Aorai_state.Map.mem state map then
Path_analysis.get_edges start state auto @ acc
else acc
in
Data_for_aorai.Aorai_state.Map.fold treat_one_start current_state []
| Promelaast.Return ->
let treat_one_state _ map acc =
try
let (_,last,_) = Data_for_aorai.Aorai_state.Map.find state map in
Data_for_aorai.Aorai_state.Set.fold
(fun start acc -> Path_analysis.get_edges start state auto @ acc)
last acc
with Not_found -> acc
in Data_for_aorai.Aorai_state.Map.fold treat_one_state current_state []
(* force that we have a crossable transition for each state in which the
automaton might be at current event. *)
let force_transition loc f st current_state =
let (states, _ as auto) = Data_for_aorai.getGraph () in
(* We iterate aux on all the states, to get
- the predicate indicating in which states the automaton cannot possibly
be before the transition (because we can't fire a transition from there).
- the predicate indicating in which states the automaton might be, outside
of the reject state
- a list of predicate indicating for each possible state which condition
must hold to have at least one possible transition.
*)
let aux (impossible_states,possible_states,has_crossable_trans) state =
let reachable_trans = get_reachable_trans state st auto current_state in
(* we inspect each transition originating from state, and maintain the
following information:
- a typed condition indicating under which condition a transition
can be crossed from the current state
- a flag indicating whether a transition that
does not lead to a reject state can be crossed.
*)
let add_one_trans (has_crossable_trans, crossable_non_reject) trans =
let has_crossable_trans =
Logic_simplification.tor has_crossable_trans trans.cross
in
let crossable_non_reject =
crossable_non_reject ||
(isCrossable trans f st
&& not (Data_for_aorai.is_reject_state trans.stop))
in has_crossable_trans, crossable_non_reject
in
let cond, crossable_non_reject =
List.fold_left add_one_trans (Promelaast.TFalse, false) reachable_trans
in
let cond = fst (Logic_simplification.simplifyCond cond) in
let cond = crosscond_to_pred cond f st in
let start = is_state_pred state in
if Logic_utils.is_trivially_false cond then begin
(* no transition can be crossed. *)
let not_start = is_out_of_state_pred state in
Logic_const.pand ~loc (impossible_states,not_start),
possible_states,
has_crossable_trans
end else begin
(* we may cross a transition. Now check whether we have some
condition to check for that. *)
let has_crossable_trans =
if Logic_utils.is_trivially_true cond then has_crossable_trans
else
Logic_const.new_predicate
(pimplies ~loc (start,cond)) :: has_crossable_trans
in
let possible_states =
(* reject_state must not be the only possible state *)
match st with
| Promelaast.Return ->
if Data_for_aorai.is_reject_state state then possible_states
else Logic_const.por ~loc (possible_states,start)
| Promelaast.Call ->
if crossable_non_reject then
Logic_const.por ~loc (possible_states, start)
else possible_states
in
impossible_states, possible_states, has_crossable_trans
end
in
let impossible_states, possible_states, crossable_trans =
List.fold_left aux (ptrue, pfalse,[]) states
in
let states =
if Aorai_option.Deterministic.get() then
possible_states (* We're always in exactly one state, among the possible
ones, no need to list the impossible ones.
*)
else (* requires that the cells for impossible states be '0' *)
Logic_const.pand ~loc (possible_states, impossible_states)
in
Logic_const.new_predicate states :: (List.rev crossable_trans)
let partition_action trans =
let add_state t st map =
let old =
try Cil_datatype.Term_lval.Map.find t map
with Not_found -> Data_for_aorai.Aorai_state.Set.empty
in
let new_set = Data_for_aorai.Aorai_state.Set.add st old in
Cil_datatype.Term_lval.Map.add t new_set map
in
let treat_one_action st acc =
function
| Counter_init t | Counter_incr t | Copy_value (t,_) -> add_state t st acc
| Pebble_init _ | Pebble_move _ -> acc (* moving pebbles can occur at
the same time (but not for
same pebbles)
*)
in
let treat_one_trans acc tr =
List.fold_left (treat_one_action tr.start) acc tr.actions
in
List.fold_left treat_one_trans Cil_datatype.Term_lval.Map.empty trans
(* TODO: this must be refined to take pebbles into account: in that
case, disjointedness condition is on pebble set for each state. *)
let disjoint_states loc _ states precond =
let states = Data_for_aorai.Aorai_state.Set.elements states in
let rec product acc l =
match l with
| [] -> acc
| hd::tl ->
let pairs = List.map (fun x -> (hd,x)) tl in
product (pairs @ acc) tl
in
let disjoint = product [] states in
List.fold_left
(fun acc (st1, st2) ->
Logic_const.new_predicate
(Logic_const.por ~loc
(is_out_of_state_pred st1,is_out_of_state_pred st2)) :: acc)
precond
disjoint
(*
forces that parent states of a state with action are mutually exclusive,
at least at pebble level.
*)
let incompatible_states loc st current_state =
let (states,_ as auto) = Data_for_aorai.getGraph () in
let aux precond state =
let trans = get_reachable_trans_to state st auto current_state in
let actions = partition_action trans in
Cil_datatype.Term_lval.Map.fold (disjoint_states loc) actions precond
in
List.fold_left aux [] states
let auto_func_preconditions loc f st current_state =
force_transition loc f st current_state @
incompatible_states loc st current_state
let find_pebble_origin lab actions =
let rec aux = function
| [] -> Aorai_option.fatal "Transition to multi-state has no pebble action"
| Pebble_init (_,_,count) :: _ ->
Logic_const.term
(TLval (TVar count, TNoOffset))
(Logic_const.make_set_type count.lv_type)
| Pebble_move (_,_,set,_) :: _-> Data_for_aorai.pebble_set_at set lab
| _ :: tl -> aux tl
in aux actions
let mk_sub ~loc pebble_set v =
let sub = List.hd (Logic_env.find_all_logic_functions "\\subset") in
Logic_const.papp ~loc
(sub,[],
[Logic_const.term ~loc (TLval (TVar v,TNoOffset)) pebble_set.term_type;
pebble_set])
let pebble_guard ~loc pebble_set aux_var guard =
let v = Cil_const.make_logic_var_quant aux_var.lv_name aux_var.lv_type in
let g = rename_pred aux_var v guard in
let g = Logic_const.pand ~loc (mk_sub ~loc pebble_set v, g) in
Logic_const.pexists ~loc ([v], g)
let pebble_guard_neg ~loc pebble_set aux_var guard =
let v = Cil_const.make_logic_var_quant aux_var.lv_name aux_var.lv_type in
let g = rename_pred aux_var v guard in
let g =
Logic_const.pimplies ~loc
(mk_sub ~loc pebble_set v, Logic_const.pnot ~loc g)
in
Logic_const.pforall ~loc ([v], g)
let pebble_post ~loc pebble_set aux_var guard =
let v = Cil_const.make_logic_var_quant aux_var.lv_name aux_var.lv_type in
let g = rename_pred aux_var v guard in
let g = Logic_const.pimplies ~loc (mk_sub ~loc pebble_set v, g) in
Logic_const.pforall ~loc ([v], g)
(* behavior is the list of all behaviors related to the given state, trans
the list of potentially active transitions ending in this state.
If the state is a multi-state, we have one behavior
whose assumes is the disjunction of these assumes
*)
let add_behavior_pebble_actions ~loc f st behaviors state trans =
match state.multi_state with
| None -> behaviors
| Some (set,aux) ->
let name = Printf.sprintf "pebble_%s" state.name in
let assumes =
List.fold_left
(fun acc b ->
let assumes = List.map pred_of_id_pred b.b_assumes in
Logic_const.por ~loc (acc, Logic_const.pands assumes))
pfalse behaviors
in
let assumes = [ Logic_const.new_predicate assumes ] in
let set = Data_for_aorai.pebble_set_at set Logic_const.here_label in
let treat_action guard res action =
match action with
| Copy_value _ | Counter_incr _ | Counter_init _ ->
res
| Pebble_init (_,_,v) ->
let a = Cil_const.make_logic_var_quant aux.lv_name aux.lv_type in
let guard = rename_pred aux a guard in
let guard =
Logic_const.pand ~loc
(Logic_const.prel
~loc (Req,Logic_const.tvar a,Logic_const.tvar v),
guard)
in
Logic_const.term ~loc
(Tcomprehension (Logic_const.tvar a,[a], Some guard))
set.term_type
:: res
| Pebble_move(_,_,s1,_) ->
let a = Cil_const.make_logic_var_quant aux.lv_name aux.lv_type in
let guard = rename_pred aux a guard in
let in_s =
mk_sub ~loc
(Data_for_aorai.pebble_set_at s1 Logic_const.pre_label) a
in
let guard = Logic_const.pand ~loc (in_s,guard) in
Logic_const.term ~loc
(Tcomprehension (Logic_const.tvar a,[a], Some guard))
set.term_type
:: res
in
let treat_one_trans acc tr =
let guard = crosscond_to_pred tr.cross f st in
let guard = Logic_const.pold guard in
List.fold_left (treat_action guard) acc tr.actions
in
let res = List.fold_left treat_one_trans [] trans in
let res = Logic_const.term (Tunion res) set.term_type in
let post_cond =
[ Normal, Logic_const.new_predicate (Logic_const.prel (Req,set,res))]
in
Cil.mk_behavior ~name ~assumes ~post_cond () :: behaviors
(* NB: we assume that the terms coming from YA automata keep quite simple.
Notably that they do not introduce themselves any \at. *)
let make_old loc init t =
let vis =
object(self)
inherit Visitor.frama_c_inplace
val is_old = Stack.create ()
method private is_old =
if Stack.is_empty is_old then false else Stack.top is_old
method! vterm t =
match t.term_node with
| TLval lv ->
if Cil_datatype.Term_lval.Set.mem lv init then begin
if self#is_old then begin
Stack.push false is_old;
DoChildrenPost
(fun t ->
ignore (Stack.pop is_old);
Logic_const.(tat ~loc (t,here_label)))
end else DoChildren
end
else begin
if not self#is_old then begin
Stack.push true is_old;
DoChildrenPost
(fun t ->
ignore (Stack.pop is_old);
Logic_const.told ~loc t)
end else DoChildren
end
| _ -> DoChildren
end
in Visitor.visitFramacTerm vis t
let mk_action ~loc init a =
let term_lval lv =
Logic_const.term ~loc (TLval lv) (Cil.typeOfTermLval lv)
in
let add_lv lv = Cil_datatype.Term_lval.Set.add lv init in
match a with
| Counter_init lv ->
[Logic_const.prel ~loc
(Req, term_lval lv, Logic_const.tinteger ~loc 1)],
add_lv lv
| Counter_incr lv ->
[Logic_const.prel ~loc
(Req, term_lval lv,
Logic_const.term ~loc
(TBinOp (PlusA,
Logic_const.told ~loc (term_lval lv),
Logic_const.tinteger ~loc 1))
(Cil.typeOfTermLval lv))],
add_lv lv
| Pebble_init _ | Pebble_move _ -> [],init (* Treated elsewhere *)
| Copy_value (lv,t) ->
[Logic_const.prel ~loc
(Req, term_lval lv, make_old loc init t)],
add_lv lv
let is_reachable state status =
let treat_one_state _ map = Data_for_aorai.Aorai_state.Map.mem state map in
Data_for_aorai.Aorai_state.Map.exists treat_one_state status
let concat_assigns a1 a2 =
match a1,a2 with
| WritesAny, _ -> a2
| _, WritesAny -> a1
| Writes l1, Writes l2 ->
Writes
(List.fold_left
(fun acc (loc,_ as elt) ->
if List.exists
(fun (x,_) ->
Cil_datatype.Term.equal x.it_content loc.it_content)
l2
then
acc
else
elt :: acc)
l2 l1)
let get_accessible_transitions auto state status =
let treat_one_state curr_state (_,last,_) acc =
if Data_for_aorai.Aorai_state.equal curr_state state then
Data_for_aorai.Aorai_state.Set.union last acc
else acc
in
let treat_start_state _ map acc =
Data_for_aorai.Aorai_state.Map.fold treat_one_state map acc
in
let previous_set =
Data_for_aorai.Aorai_state.Map.fold
treat_start_state status Data_for_aorai.Aorai_state.Set.empty
in
Data_for_aorai.Aorai_state.Set.fold
(fun s acc -> Path_analysis.get_edges s state auto @ acc) previous_set []
let get_aux_var_bhv_name = function
| TVar v, _ ->
Data_for_aorai.get_fresh (v.lv_name ^ "_unchanged")
| lv ->
Aorai_option.fatal "unexpected lval for action variable: %a"
Printer.pp_term_lval lv
(* Assumes that we don't have a multi-state here.
pebbles are handled elsewhere
*)
let mk_unchanged_aux_vars_bhvs loc f st status =
let (states,_ as auto) = Data_for_aorai.getGraph() in
let add_state_trans acc state =
let trans = get_reachable_trans state st auto status in
List.rev_append trans acc
in
let crossable_trans =
List.fold_left add_state_trans [] states
in
let add_trans_aux_var trans map = function
| Counter_init lv | Counter_incr lv | Copy_value (lv,_) ->
let other_trans =
match Cil_datatype.Term_lval.Map.find_opt lv map with
| Some l -> l
| None -> []
in
Cil_datatype.Term_lval.Map.add lv (trans :: other_trans) map
| Pebble_init _ | Pebble_move _ -> map
in
let add_trans_aux_vars map trans =
List.fold_left (add_trans_aux_var trans) map trans.actions
in
let possible_actions =
List.fold_left add_trans_aux_vars
Cil_datatype.Term_lval.Map.empty
crossable_trans
in
let out_trans trans =
Logic_const.new_predicate
(Logic_const.por ~loc
(is_out_of_state_pred trans.start,
Logic_const.pnot (crosscond_to_pred trans.cross f st)))
in
let mk_behavior lv trans acc =
let name = get_aux_var_bhv_name lv in
let assumes = List.map out_trans trans in
let t = Data_for_aorai.tlval lv in
let ot = Logic_const.told t in
let p = Logic_const.prel (Req,t,ot) in
let post_cond = [Normal, Logic_const.new_predicate p] in
Cil.mk_behavior ~name ~assumes ~post_cond () :: acc
in
Cil_datatype.Term_lval.Map.fold mk_behavior possible_actions []
let mk_behavior ~loc auto kf e status state =
Aorai_option.debug "analysis of state %s (%d)"
state.Promelaast.name state.nums;
if is_reachable state status then begin
Aorai_option.debug "state %s is reachable" state.Promelaast.name;
let my_trans = get_accessible_transitions auto state status in
let rec treat_trans
((in_assumes, out_assumes, assigns, action_bhvs) as acc) l =
match l with
| [] -> acc
| trans :: tl ->
let consider, others =
List.partition (fun x -> x.start.nums = trans.start.nums) tl
in
let start = is_state_pred trans.start in
let not_start = is_out_of_state_pred trans.start in
let in_guard, out_guard, assigns, my_action_bhvs =
List.fold_left
(fun (in_guard, out_guard, all_assigns, action_bhvs) trans ->
Aorai_option.debug "examining transition %d" trans.numt;
Aorai_option.debug "transition %d is active" trans.numt;
let guard = crosscond_to_pred trans.cross kf e in
let my_in_guard,my_out_guard =
match state.multi_state with
| None -> guard, Logic_const.pnot ~loc guard
| Some (_,aux) ->
let set =
find_pebble_origin Logic_const.here_label trans.actions
in
pebble_guard ~loc set aux guard,
pebble_guard_neg ~loc set aux guard
in
let out_guard =
Logic_const.pand ~loc (out_guard, my_out_guard)
in
let in_guard, all_assigns, action_bhvs =
match trans.actions with
| [] ->
(Logic_const.por ~loc (in_guard,my_in_guard),
all_assigns,
action_bhvs)
| _ ->
let name =
Printf.sprintf "buch_state_%s_in_%d"
state.name (List.length action_bhvs)
in
Aorai_option.debug "Name is %s" name;
let assumes = [
Logic_const.new_predicate
(Logic_const.pand ~loc (start,my_in_guard))
]
in
let post_cond =
Normal,
Logic_const.new_predicate (is_state_pred state)
in
let treat_one_action (other_posts, init) a =
let posts, init = mk_action ~loc init a in
match state.multi_state with
| None ->
other_posts @
List.map
(fun x ->
(Normal, Logic_const.new_predicate x))
posts,
init
| Some (_,aux) ->
let set =
find_pebble_origin
Logic_const.pre_label trans.actions
in
other_posts @
List.map
(fun x ->
(Normal,
Logic_const.new_predicate
(pebble_post ~loc set aux x)))
posts,
init
in
let post_cond,_ =
List.fold_left treat_one_action
([post_cond], Cil_datatype.Term_lval.Set.empty)
trans.actions
in
let assigns = action_assigns trans in
let all_assigns = concat_assigns assigns all_assigns in
let bhv =
Cil.mk_behavior ~name ~assumes ~post_cond ()
in
in_guard, all_assigns, bhv :: action_bhvs
in
in_guard, out_guard, all_assigns, action_bhvs)
(pfalse,ptrue,assigns, action_bhvs) (trans::consider)
in
treat_trans
(Logic_const.por ~loc
(in_assumes, (Logic_const.pand ~loc (start, in_guard))),
Logic_const.pand ~loc
(out_assumes,
(Logic_const.por ~loc (not_start, out_guard))),
assigns,
my_action_bhvs
)
others
in
let my_trans = List.filter (fun x -> isCrossable x kf e) my_trans in
let in_assumes, out_assumes, assigns, action_behaviors =
treat_trans (pfalse, ptrue, WritesAny, []) my_trans
in
let behaviors =
if Logic_utils.is_trivially_false in_assumes then action_behaviors
else begin
let behavior_in =
Cil.mk_behavior
~name:(Printf.sprintf "buch_state_%s_in" state.Promelaast.name)
~assumes:[Logic_const.new_predicate in_assumes]
~post_cond:
[Normal, Logic_const.new_predicate (is_state_pred state)]
()
in behavior_in :: action_behaviors
end
in
let behaviors =
add_behavior_pebble_actions ~loc kf e behaviors state my_trans
in
let behaviors =
if Logic_utils.is_trivially_false out_assumes then behaviors
else begin
let post_cond =
match state.multi_state with
| None -> [] (* Done elsewhere *)
| Some (set,_) ->
let set =
Data_for_aorai.pebble_set_at set Logic_const.here_label
in
[Normal,
Logic_const.new_predicate
(Logic_const.prel ~loc
(Req,set,
Logic_const.term ~loc Tempty_set set.term_type))]
in
let post_cond =
(Normal, (Logic_const.new_predicate (is_out_of_state_pred state)))
:: post_cond
in
let behavior_out =
Cil.mk_behavior
~name:(Printf.sprintf "buch_state_%s_out" state.Promelaast.name)
~assumes:[Logic_const.new_predicate out_assumes]
~post_cond ()
in behavior_out :: behaviors
end
in
assigns, behaviors
end else begin
Aorai_option.debug "state %s is not reachable" state.Promelaast.name;
(* We know that we'll never end up in this state. *)
let name = Printf.sprintf "buch_state_%s_out" state.Promelaast.name in
let post_cond =
match state.multi_state with
| None -> []
| Some (set,_) ->
let set =
Data_for_aorai.pebble_set_at set Logic_const.here_label
in [Normal,
Logic_const.new_predicate
(Logic_const.prel ~loc
(Req,set,
Logic_const.term ~loc Tempty_set set.term_type))]
in
let post_cond =
(Normal, Logic_const.new_predicate (is_out_of_state_pred state))
::post_cond
in
WritesAny,[mk_behavior ~name ~post_cond ()]
end
let auto_func_behaviors loc f st state =
let call_or_ret =
match st with
| Promelaast.Call -> "call"
| Promelaast.Return -> "return"
in
Aorai_option.debug
"func behavior for %a (%s)" Kernel_function.pretty f call_or_ret;
let (states, _) as auto = Data_for_aorai.getGraph() in
let requires = auto_func_preconditions loc f st state in
let post_cond =
let called_pre =
Logic_const.new_predicate
(Logic_const.prel ~loc
(Req,
Logic_const.tvar ~loc
(Data_for_aorai.get_logic_var Data_for_aorai.curOpStatus),
(Logic_const.term
(TConst (constant_to_lconstant
(Data_for_aorai.op_status_to_cenum st)))
(Ctype Cil.intType))))
in
let called_pre_2 =
Logic_const.new_predicate
(Logic_const.prel ~loc
(Req,
Logic_const.tvar ~loc
(Data_for_aorai.get_logic_var Data_for_aorai.curOp),
(Logic_const.term
(TConst((constant_to_lconstant
(Data_for_aorai.func_to_cenum
(Kernel_function.get_name f)))))
(Ctype Cil.intType))))
in
(* let old_pred = Aorai_utils.mk_old_state_pred loc in *)
[(Normal, called_pre); (Normal, called_pre_2)]
in
let mk_behavior (assigns, behaviors) status =
let new_assigns, new_behaviors =
mk_behavior ~loc auto f st state status
in
concat_assigns new_assigns assigns, new_behaviors @ behaviors
in
let assigns = automaton_assigns loc in
let assigns, behaviors = (List.fold_left mk_behavior (assigns,[]) states) in
let global_behavior =
Cil.mk_behavior ~requires ~post_cond ~assigns ()
in
let non_action_behaviors =
mk_unchanged_aux_vars_bhvs loc f st state
in
(* Keep behaviors ordered according to the states they describe *)
global_behavior :: (List.rev_append behaviors non_action_behaviors)
let act_convert loc act res =
let treat_one_act =
function
| Counter_init t_lval ->
Cil.mkStmtOneInstr
~ghost:true (Set (tlval_to_lval t_lval res, Cil.one loc, loc))
| Counter_incr t_lval ->
let my_lval = tlval_to_lval t_lval res in
Cil.mkStmtOneInstr ~ghost:true
(Set
(my_lval,
(Cil.mkBinOp
loc
PlusA
(Cil.new_exp loc (Lval my_lval))
(Cil.one loc)),
loc))
| Copy_value (t_lval, t) ->
Cil.mkStmtOneInstr ~ghost:true
(Set (tlval_to_lval t_lval res, term_to_exp t res, loc))
| _ ->
Aorai_option.fatal "Peebles not treated yet." (* TODO : Treat peebles. *)
in
List.map treat_one_act act
let mk_transitions_stmt generated_kf loc f st res trans =
List.fold_right
(fun trans
(aux_stmts, aux_vars, new_funcs, exp_from_trans, stmt_from_action) ->
let (tr_stmts, tr_vars, tr_funcs, exp) =
crosscond_to_exp generated_kf f st loc trans.cross res
in
let cond = Cil.mkBinOp loc LAnd (is_state_exp trans.start loc) exp in
(tr_stmts @ aux_stmts,
tr_vars @ aux_vars,
Cil_datatype.Varinfo.Set.union tr_funcs new_funcs,
Cil.mkBinOp loc LOr exp_from_trans cond,
(Cil.copy_exp cond, act_convert loc trans.actions res)
:: stmt_from_action))
trans
([],[],Cil_datatype.Varinfo.Set.empty, Cil.zero ~loc, [])
let mk_goto loc b =
let ghost = true in
match b.bstmts with
| [] -> Cil.mkBlock []
| [ { skind = Instr i } ] ->
let s = mkStmtOneInstr ~ghost i in
Cil.mkBlock [s]
| [ { skind = Goto (s,_) }] ->
let s' = mkStmt ~ghost (Goto (ref !s,loc)) in
Cil.mkBlock [s']
| s::_ ->
s.labels <-
(Label(Data_for_aorai.get_fresh "__aorai_label",loc,false)):: s.labels;
let s' = mkStmt ~ghost (Goto (ref s,loc)) in
Cil.mkBlock [s']
let normalize_condition loc cond block1 block2 =
let rec aux cond b1 b2 =
match cond.enode with
| UnOp(LNot,e,_) -> aux e b2 b1
| BinOp(LAnd,e1,e2,_) ->
let b2' = mk_goto loc b2 in
let b1'= Cil.mkBlock [aux e2 b1 b2'] in
aux e1 b1' b2
| BinOp(LOr,e1,e2,_) ->
let b1' = mk_goto loc b1 in
let b2' = Cil.mkBlock [aux e2 b1' b2] in
aux e1 b1 b2'
| _ ->
Cil.mkStmt ~ghost:true (If(cond,b1,b2,loc))
in
aux cond block1 block2
let mkIfStmt loc exp1 block1 block2 =
if Kernel.LogicalOperators.get() then
Cil.mkStmt ~ghost:true (If (exp1, block1, block2, loc))
else
normalize_condition loc exp1 block1 block2
let mk_deterministic_stmt
generated_kf loc auto f fst status ret state
(other_stmts, other_funcs, other_vars, trans_stmts as res) =
if is_reachable state status then begin
let trans = get_accessible_transitions auto state status in
let aux_stmts, aux_vars, aux_funcs, _, stmt_from_action =
mk_transitions_stmt generated_kf loc f fst ret trans
in
let stmts =
List.fold_left
(fun acc (cond, stmt_act) ->
[mkIfStmt loc cond
(mkBlock (is_state_det_stmt state loc :: stmt_act))
(mkBlock acc)])
trans_stmts
(List.rev stmt_from_action)
in
aux_stmts @ other_stmts,
Cil_datatype.Varinfo.Set.union aux_funcs other_funcs,
aux_vars @ other_vars,
stmts
end else res
(* mk_non_deterministic_stmt loc (states, tr) f fst status state
Generates the statement updating the variable representing
the state argument.
If state is reachable, generates a "If then else" statement, else it is
just an assignment.
Used in auto_func_block. *)
let mk_non_deterministic_stmt
generated_kf loc (states, tr) f fst status ((st,_) as state) res =
if is_reachable st status then begin
let useful_trans = get_accessible_transitions (states,tr) st status in
let aux_stmts, new_vars, new_funcs, cond,stmt_from_action =
mk_transitions_stmt generated_kf loc f fst res useful_trans
in
let then_stmt = is_state_non_det_stmt state loc in
let else_stmt = [is_out_of_state_stmt state loc] in
let trans_stmts =
let actions =
List.fold_left
(fun acc (cond, stmt_act) ->
if stmt_act = [] then acc
else
(mkIfStmt loc cond (mkBlock stmt_act) (mkBlock []))::acc)
[]
(List.rev stmt_from_action)
in
mkIfStmt loc cond (mkBlock [then_stmt]) (mkBlock else_stmt) :: actions
in
new_funcs, new_vars, aux_stmts @ trans_stmts
end else
Cil_datatype.Varinfo.Set.empty, [], [is_out_of_state_stmt state loc]
let equalsStmt lval exp loc = (* assignment *)
Cil.mkStmtOneInstr ~ghost:true (Set (lval, exp, loc))
let mk_deterministic_body generated_kf loc f st status res =
let (states, _ as auto) = Data_for_aorai.getGraph() in
let aux_stmts, aux_funcs, aux_vars, trans_stmts =
List.fold_right
(mk_deterministic_stmt generated_kf loc auto f st status res)
states
([], Cil_datatype.Varinfo.Set.empty, [],
(* if all else fails, go to reject state. *)
[is_state_det_stmt (Data_for_aorai.get_reject_state()) loc])
in
aux_funcs, aux_vars, aux_stmts @ trans_stmts
let mk_non_deterministic_body generated_kf loc f st status res =
(* For the following tests, we need a copy of every state. *)
let (states, _) as auto = Data_for_aorai.getGraph() in
let copies, local_var =
let bindings =
List.map
(fun st ->
let state_var = Data_for_aorai.get_state_var st in
let copy = Cil.copyVarinfo state_var (state_var.vname ^ "_tmp") in
copy.vglob <- false;
(st,copy))
states
in bindings, snd (List.split bindings)
in
let copies_update =
List.map
(fun (st,copy) ->
equalsStmt (Cil.var copy)
(Cil.evar ~loc (Data_for_aorai.get_state_var st)) loc)
copies
in
let new_funcs, local_var, main_stmt =
List.fold_left
(fun (new_funcs, aux_vars, stmts) state ->
let my_funcs, my_vars, my_stmts =
mk_non_deterministic_stmt generated_kf loc auto f st status state res
in
Cil_datatype.Varinfo.Set.union my_funcs new_funcs,
my_vars @ aux_vars,
my_stmts@stmts )
(Cil_datatype.Varinfo.Set.empty, local_var, [])
copies
in
(* Finally, we replace the state var values by the ones computed in copies. *)
let stvar_update =
List.map
(fun (state,copy) ->
equalsStmt
(Cil.var (Data_for_aorai.get_state_var state))
(Cil.evar ~loc copy) loc)
copies
in
new_funcs, local_var, copies_update @ main_stmt @ stvar_update
let auto_func_block generated_kf loc f st status res =
let dkey = func_body_dkey in
let call_or_ret =
match st with
| Promelaast.Call -> "call"
| Promelaast.Return -> "return"
in
Aorai_option.debug
~dkey "func code for %a (%s)" Kernel_function.pretty f call_or_ret;
let stmt_begin_list =
[
(* First statement : what is the current status : called or return ? *)
equalsStmt
(Cil.var (Data_for_aorai.get_varinfo Data_for_aorai.curOpStatus))
(Cil.new_exp loc (Const (Data_for_aorai.op_status_to_cenum st))) loc;
(* Second statement : what is the current operation,
i.e. which function ? *)
equalsStmt
(Cil.var (Data_for_aorai.get_varinfo Data_for_aorai.curOp))
(Cil.new_exp loc
(Const (Data_for_aorai.func_to_cenum (Kernel_function.get_name f))))
loc
]
and stmt_history_update =
if Aorai_option.Deterministic.get () then
let history = Data_for_aorai.whole_history ()
and cur_state = Data_for_aorai.(get_varinfo curState) in
let add_stmt (src,acc) dst_name =
let dst = Data_for_aorai.get_varinfo dst_name in
let stmt = equalsStmt (Cil.var dst) (Cil.evar ~loc src) loc in
dst, stmt :: acc
in
snd (List.fold_left add_stmt (cur_state,[]) history)
else if Aorai_option.InstrumentationHistory.get () > 0 then
Aorai_option.fatal "history is not implemented for non-deterministic \
automaton"
else []
in
let new_funcs, local_var, main_stmt =
if Aorai_option.Deterministic.get() then
mk_deterministic_body generated_kf loc f st status res
else
mk_non_deterministic_body generated_kf loc f st status res
in
let ret =
Cil.mkStmt ~ghost:true ~valid_sid:true (Cil_types.Return(None,loc))
in
if Aorai_option.SmokeTests.get () then begin
assert_alive_automaton generated_kf ret;
end;
let res_block =
(Cil.mkBlock
( stmt_begin_list @ stmt_history_update @ main_stmt @ [ret]))
in
res_block.blocals <- local_var;
Aorai_option.debug ~dkey "Generated body is:@\n%a"
Printer.pp_block res_block;
new_funcs,res_block,local_var
let get_preds_wrt_params_reachable_states state f status =
let auto = Data_for_aorai.getGraph () in
let treat_one_trans acc tr = Logic_simplification.tor acc tr.cross in
let find_trans state prev tr =
Path_analysis.get_edges prev state auto @ tr
in
let treat_one_state state (_,last,_) acc =
let my_trans =
Data_for_aorai.Aorai_state.Set.fold (find_trans state) last []
in
let cond = List.fold_left treat_one_trans TFalse my_trans in
let (_,dnf) = Logic_simplification.simplifyCond cond in
let cond = Logic_simplification.simplifyDNFwrtCtx dnf f status in
let pred = crosscond_to_pred cond f status in
Logic_const.pand (acc, pimplies (is_state_pred state, pred))
in
Data_for_aorai.Aorai_state.Map.fold treat_one_state state ptrue
let get_preds_wrt_params_reachable_states state f status =
let merge_reachable_state _ = Data_for_aorai.merge_end_state in
let reachable_states =
Data_for_aorai.Aorai_state.Map.fold
merge_reachable_state state Data_for_aorai.Aorai_state.Map.empty
in
get_preds_wrt_params_reachable_states reachable_states f status
let get_preds_pre_wrt_params f =
let pre = Data_for_aorai.get_kf_init_state f in
get_preds_wrt_params_reachable_states pre f Promelaast.Call
let get_preds_post_bc_wrt_params f =
let post = Data_for_aorai.get_kf_return_state f in
get_preds_wrt_params_reachable_states post f Promelaast.Return
let treat_val loc base range pred =
let add term =
if Cil.isLogicZero base then term
else Logic_const.term
(TBinOp (PlusA, Logic_const.tat (base,Logic_const.pre_label), term))
Linteger
in
let add_cst i = add (Logic_const.tinteger i) in
let res =
match range with
| Fixed i -> Logic_const.prel (Req,loc, add_cst i)
| Interval(min,max) ->
let min = Logic_const.prel (Rle, add_cst min, loc) in
let max = Logic_const.prel (Rle, loc, add_cst max) in
Logic_const.pand (min,max)
| Bounded (min,max) ->
let min = Logic_const.prel (Rle, add_cst min, loc) in
let max = Logic_const.prel (Rle, loc, add max) in
Logic_const.pand (min,max)
| Unbounded min -> Logic_const.prel (Rle, add_cst min, loc)
| Unknown -> Logic_const.ptrue (* nothing is known: the loc can
take any value from then on. *)
in
Aorai_option.debug ~dkey:action_dkey "Action predicate: %a"
Printer.pp_predicate res;
Logic_const.por(pred,res)
let possible_states_preds state =
let treat_one_state start map acc =
let make_possible_state state _ acc =
Logic_const.por (acc,is_state_pred state)
in
let possible_states =
Data_for_aorai.Aorai_state.Map.fold make_possible_state map pfalse
in
Logic_const.pimplies
(Logic_const.pat (is_state_pred start,Logic_const.pre_label),
possible_states)
:: acc
in
Data_for_aorai.Aorai_state.Map.fold treat_one_state state []
let update_to_pred ~start ~pre_state ~post_state location bindings =
let loc = Cil_datatype.Location.unknown in
let intv =
Cil_datatype.Term.Map.fold
(treat_val location) bindings Logic_const.pfalse
in
let pred =
match post_state.multi_state with
| None -> intv
| Some(set,aux) ->
(* [VP 2011-09-05] In fact, not all the pebble come from the considered
pre-state. Will this lead to too strong post-conditions?
*)
let set = Data_for_aorai.pebble_set_at set Logic_const.here_label in
pebble_post ~loc set aux intv
in
let guard =
Logic_const.pand ~loc
(Logic_const.pat ~loc (is_state_pred pre_state, start),
is_state_pred post_state)
in
Logic_const.pimplies ~loc (guard, pred)
let action_to_pred ~start ~pre_state ~post_state bindings =
let treat_one_loc loc vals acc =
update_to_pred ~start ~pre_state ~post_state loc vals :: acc
in
Cil_datatype.Term.Map.fold treat_one_loc bindings []
let all_actions_preds start state =
let treat_current_state pre_state post_state (_,_,bindings) acc =
let my_bindings =
action_to_pred ~start ~pre_state ~post_state bindings
in
my_bindings @ acc
in
let treat_start_state pre_state map acc =
Data_for_aorai.Aorai_state.Map.fold
(treat_current_state pre_state) map acc
in
Data_for_aorai.Aorai_state.Map.fold treat_start_state state []
(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)