123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922(**************************************************************************)(* *)(* This file is part of WP plug-in of Frama-C. *)(* *)(* Copyright (C) 2007-2023 *)(* CEA (Commissariat a l'energie atomique et aux energies *)(* alternatives) *)(* *)(* you can redistribute it and/or modify it under the terms of the GNU *)(* Lesser General Public License as published by the Free Software *)(* Foundation, version 2.1. *)(* *)(* It is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU Lesser General Public License for more details. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file licenses/LGPLv2.1). *)(* *)(**************************************************************************)(* ---------------------------------------------------------------------- *)(* --- Variable Analysis --- *)(* ---------------------------------------------------------------------- *)openCtypesopenCil_typesopenCil_datatype(* ---------------------------------------------------------------------- *)(* --- Varinfo Accesses --- *)(* ---------------------------------------------------------------------- *)(** By lattice order of usage *)typeaccess=|NoAccess(** Never used *)|ByRef(** Only used as ["*x"], equals to [load(shift(load(&x),0))] *)|ByArray(** Only used as ["x[_]"], equals to [load(shift(load(&x),_))] *)|ByValue(** Only used as ["x"], equals to [load(&x)] *)|ByAddr(** Widely used, potentially up to ["&x"] *)moduleAccess:sigtypet=accessvalis_bot:t->boolvalcup:t->t->tvalpretty:varinfo->Format.formatter->t->unitend=structtypet=accessletis_bot=functionNoAccess->true|_->falseletprettyxfmt=function|NoAccess->Format.fprintffmt"-%a"Varinfo.prettyx|ByRef->Format.fprintffmt"*%a"Varinfo.prettyx|ByArray->Format.fprintffmt"%a[]"Varinfo.prettyx|ByValue->Format.fprintffmt"%a"Varinfo.prettyx|ByAddr->Format.fprintffmt"&%a"Varinfo.prettyxletrank=function|NoAccess->0|ByRef->1|ByArray->2|ByValue->3|ByAddr->4letcupab=ifranka<rankbthenbelseaend(* ---------------------------------------------------------------------- *)(* --- Expressions & Memory Model --- *)(* ---------------------------------------------------------------------- *)moduleE:sigtypetvalbot:tvalis_bot:t->boolvalcup:t->t->tvalcup_differ:t->t->t*bool(* val leq : t -> t -> bool *)(* unused for now *)vallcup:tlist->tvalfcup:('a->t)->'alist->tvalget:varinfo->t->accessvalaccess:varinfo->access->t->tvalpartition_formals_vs_others:t->t*tvalpretty:Format.formatter->t->unitvaliter:(varinfo->access->unit)->t->unitend=structmoduleXmap=Qed.Mergemap.Make(Varinfo)typet=accessXmap.tletprettyfmtm=beginFormat.fprintffmt"@[<hov 2>{";Xmap.iter(funxe->ife<>NoAccessthen(Format.pp_print_spacefmt();Access.prettyxfmte))m;Format.fprintffmt" }@]";endletbot=Xmap.emptyletis_bot=Xmap.is_emptyletcup=Xmap.union(fun_->Access.cup)letcup_differe1e2=letr=cupe1e2inletis_modified=not(r==e1)in(* Format.printf "cup_differ %a %a = %a,%b@."
pretty e1 pretty e2 pretty r is_modified; *)r,is_modified(* unused for now *)(* let leq = Xmap.subset (fun _ -> Access.leq) *)(* unused for now *)letreclcup=function[]->bot|[x]->x|x::xs->cupx(lcupxs)letrecfcupf=function[]->bot|[x]->fx|x::xs->cup(fx)(fcupfxs)letgetvie=tryXmap.findviewithNot_found->NoAccessletaccessviue=ifAccess.is_botutheneelseXmap.insert(fun_uold->Access.cupoldu)viueletpartition_formals_vs_otherse=Xmap.partition(funvi_a->vi.vformal)eletiter=Xmap.iterendtypevalue=E.ttypemodel=|Eofvalue(* E *)|Loc_varofvarinfo(* &x *)|Loc_shiftofvarinfo*value(* &x.[...] *)|Val_varofvarinfo(* x *)|Val_compofvarinfo*value(* x.f[_].g... *)|Val_shiftofvarinfo*value(* (x + E) *)[@@@warning"-32"]letpp_modelfmt=function|Ev->E.prettyfmtv|Loc_varx->Format.fprintffmt"&%a"Varinfo.prettyx|Loc_shift(x,v)->Format.fprintffmt"&%a.(%a)"Varinfo.prettyxE.prettyv|Val_varx->Varinfo.prettyfmtx|Val_comp(x,v)->Format.fprintffmt"%a.(%a)"Varinfo.prettyxE.prettyv|Val_shift(x,v)->Format.fprintffmt"%a+(%a)"Varinfo.prettyxE.prettyv[@@@warning"+32"]letnothing=EE.botletv_modelv=ifE.is_botvthennothingelseEvletvcup(a:value)(b:value):model=v_model(E.cupab)letshare_vcup(m:model)~old(b:value):model=(* requires m = E(old) *)(* ensures \result == vcup old b *)lete=E.cupoldbinife==oldthenmelsev_modele(* let lcup xs = m_value (E.lcup xs) *)(* unused for now *)(* let fcup f xs = m_value (E.fcup f xs) *)(* unused for now *)lete_value=function|Loc_varx->E.accessxByAddrE.bot|Loc_shift(x,e)->E.accessxByAddre|Val_varx->E.accessxByValueE.bot|Val_comp(x,e)|Val_shift(x,e)->E.accessxByValuee|Ee->eletm_value=function|E_asm->m|m->E(e_valuem)letm_vcup=letm_vcupm=vcup(e_valuem)infunction|Eoldasm->(* better sharing than vcup (e_value m) b *)share_vcupm~old|_asm->m_vcupmletm_fcupf=letm_fcupf=E.fcup(funx->e_value(fx))infunction(* better sharing than E.fcup (fun x -> e_value (f x)) *)|[]->nothing|[x]->m_value(fx)|x::xs->m_vcup(fx)(m_fcupfxs)letcvalx=Val_varxletcvarx=Loc_varxletshift(m:model)(k:value)=letshare~oldmke=(* for a better sharing between maps *)ife==oldthenmelsemkeinmatchmwith|Loc_varx->Loc_shift(x,k)|Loc_shift(x,e)->share~old:e(funk->Loc_shift(x,k))(E.cupek)|Val_varx->Val_shift(x,k)|Val_comp(x,e)->share~old:e(funk->Val_comp(x,k))(E.cupek)|Val_shift(x,e)->share~old:e(funk->Val_shift(x,k))(E.cupek)|Eold->share_vcupm~oldkletfield=function|Val_varx->Val_comp(x,E.bot)|(Val_comp_|Val_shift_)asm->m|m->shiftmE.botletload=function|Loc_varx->Val_varx(* E.access x ByValue E.bot *)|Loc_shift(x,e)->ifCil.isArithmeticOrPointerTypex.vtypethenE(E.accessxByAddre)elseE(E.accessxByValuee)|Val_varx->E(E.accessxByRefE.bot)|Val_comp(x,e)->E(E.accessxByRefe)|Val_shift(x,e)->E(E.accessxByArraye)|E_asm->m(* for \\valid, \\separated, \\block_length: no variable escape,
excepts for shifts *)letunescape=function(* better than e_value (load m) *)|Loc_varx->E.accessxByValueE.bot|Loc_shift(x,e)->E.accessxByValuee|Val_varx->E.accessxByRefE.bot|Val_comp(x,e)->E.accessxByRefe|Val_shift(x,e)->E.accessxByArraye|Ee->e(* ---------------------------------------------------------------------- *)(* --- Casts --- *)(* ---------------------------------------------------------------------- *)typecast=|Identity|Convert|Castletcastcvm=matchcvwith|Identity->m|Convert|Cast->m_valuemletcast_objtgtsrc=matchtgt,srcwith|(C_int_|C_float_),(C_int_|C_float_)->Convert|C_pointertr,C_pointerte->letobj_r=Ctypes.object_oftrinletobj_e=Ctypes.object_ofteinifCtypes.compareobj_robj_e=0thenIdentityelseCast|_->ifCtypes.equaltgtsrcthenIdentityelseCastletcast_ctyptgtsrc=cast_obj(Ctypes.object_oftgt)(Ctypes.object_ofsrc)letcast_ltyptgtsrc=matchLogic_utils.unroll_type~unroll_typedef:falsesrcwith|Ctypesrc->cast_ctyptgtsrc|_->Cast(* ---------------------------------------------------------------------- *)(* --- Environment --- *)(* ---------------------------------------------------------------------- *)moduleKFmap=Qed.Mergemap.Make(Kernel_function)moduleKFset=Qed.Mergeset.Make(Kernel_function)moduleLVmap=Qed.Mergemap.Make(Logic_var)moduleLFset=Qed.Mergeset.Make(Logic_info)typeglobal_ctx={mutablecode:value;(** Variable accesses from C code and code annotations *)mutablespec_formals:value;(** Accesses of formal variables from function specs *)mutablespec_globals:value;(** Accesses of global variables from function specs *)mutablecphi:(modellistlist)KFmap.t;(** A map to a list (since a same kf can be called more than ones)
to a list of models for each arg_exp of the call to the kf. *)mutablelphi:LFset.t;(** Logical function/predicate used directly and indirectly by
specs/annots of a C function *)}letmk_global_ctx()={code=E.bot;spec_formals=E.bot;spec_globals=E.bot;cphi=KFmap.empty;lphi=LFset.empty}(* Temporary local context *)typelocal_ctx={mutabletlet:modelLVmap.t;(* for \\let var bound to a term *)mutableplet:valueLVmap.t;(* for \\let var bound to a predicate *)mutablespec:value;(* for formals and globals of of spec,
before partitioning the result *)}letmk_local_ctx()={tlet=LVmap.empty;plet=LVmap.empty;spec=E.bot}typectx={local:local_ctx;global:global_ctx}letmk_ctx()={global=mk_global_ctx();local=mk_local_ctx()}(* ---------------------------------------------------------------------- *)(* --- Tlet --- *)(* ---------------------------------------------------------------------- *)(* For \\let binding a predicate *)letget_tlet(env:local_ctx)(lv:logic_var)=tryLVmap.findlvenv.tletwithNot_found->assert(false)(* nothing *)letadd_tlet(env:local_ctx)(lv:logic_var)(m:model)=env.tlet<-LVmap.insert(fun___old->assertfalse)lvmenv.tletletrem_tlet(env:local_ctx)(lv:logic_var)=env.tlet<-LVmap.removelvenv.tlet(* ---------------------------------------------------------------------- *)(* --- Plet --- *)(* ---------------------------------------------------------------------- *)(* For \\let binding a predicate *)letget_plet(env:local_ctx)(lv:logic_var)=tryLVmap.findlvenv.pletwithNot_found->e_value(get_tletenvlv)letadd_plet(env:local_ctx)(lv:logic_var)(e:value)=env.plet<-LVmap.insert(fun___old->assertfalse)lveenv.pletletrem_plet(env:local_ctx)(lv:logic_var)=env.plet<-LVmap.removelvenv.plet(* ---------------------------------------------------------------------- *)(* --- Compilation of C-Expressions --- *)(* ---------------------------------------------------------------------- *)letrecvexpr(e:Cil_types.exp):value=e_value(expre)andmexpr(e:Cil_types.exp):model=(* better sharing than E (vexpr e) *)m_value(expre)andexpr(e:Cil_types.exp):model=matche.enodewith(* Logics *)|Const_|SizeOf_|SizeOfE_|SizeOfStr_|AlignOf_|AlignOfE_->nothing(* Unary *)|UnOp((Neg|BNot|LNot),e,_)->mexpre(* Binary *)|BinOp((MinusPP|PlusA|MinusA|Mult|Div|Mod|Shiftlt|Shiftrt|BAnd|BXor|BOr|LAnd|LOr|Lt|Gt|Le|Ge|Eq|Ne),a,b,_)->m_vcup(expra)(vexprb)(* Shifts *)|BinOp((PlusPI|MinusPI),a,b,_)->shift(expra)(vexprb)(* Casts *)|CastE(ty_tgt,e)->cast(cast_ctypty_tgt(Cil.typeOfe))(expre)(* Address *)|AddrOflval->lvaluelval|StartOflval->startof(lvaluelval)(Cil.typeOfLvallval)(* Load *)|Lvallval->load(lvaluelval)andlvalue(h,ofs)=offset(hosth)ofsandhost=function|Varx->cvarx|Meme->expreandoffset(m:model)=function|NoOffset->m|Field(_,ofs)->offset(fieldm)ofs|Index(e,ofs)->offset(shiftm(vexpre))ofsandstartof(m:model)typ=ifCil.isArrayTypetypthenshiftmE.botelsem(* ---------------------------------------------------------------------- *)(* --- Compilation of ACSL-Terms --- *)(* ---------------------------------------------------------------------- *)letrecvterm(env:ctx)(t:term):value=e_value(termenvt)andmterm(env:ctx)(t:term):model=m_value(termenvt)(* better sharing than E (vterm env e) *)andtermopt(env:ctx)=functionNone->nothing|Somet->termenvt(* --- Expr --- *)andterm(env:ctx)(t:term):model=matcht.term_nodewith(* Logics *)|TConst_|TSizeOf_|TSizeOfE_|TSizeOfStr_|TAlignOf_|TAlignOfE_|Ttypeof_|Ttype_->nothing(* Unary *)|TUnOp((Neg|BNot|LNot),t)->mtermenvt(* Binary *)|TBinOp((MinusPP|PlusA|MinusA|Mult|Div|Mod|Shiftlt|Shiftrt|BAnd|BXor|BOr|LAnd|LOr|Lt|Gt|Le|Ge|Eq|Ne),a,b)->m_vcup(termenva)(vtermenvb)(* Shifts *)|TBinOp((PlusPI|MinusPI),a,b)->shift(termenva)(vtermenvb)(* Casts *)|TCastE(ty_tgt,t)->cast(cast_ltypty_tgtt.term_type)(termenvt)|TLogic_coerce(_lt,t)->termenvt(* Term L-Values *)|TLvaltlv->term_lvalenvtlv|TAddrOftlv|TStartOftlv->addr_lvalenvtlv|TUpdate(s,ofs,t)->letv=termenvsinletk=term_indicesenvE.botofsinlete=vtermenvtinm_vcup(m_vcupvk)e(* Operators *)|Tat(t,_)->termenvt|Tunionts|Tinterts|TDataCons(_,ts)->m_fcup(termenv)ts|Tif(e,a,b)->m_fcup(termenv)[e;a;b]|Trange(a,b)->m_fcup(termoptenv)[a;b]|Toffset(_,t)|Tbase_addr(_,t)->mtermenvt|Tnull|Tempty_set->nothing(* Binders *)|Tlambda(_xs,b)->mtermenvb|Tcomprehension(t,_xs,None)->mtermenvt|Tcomprehension(t,_xs,Somep)->m_vcup(termenvt)(predenvp)|Tlet({l_var_info;l_body=LBtermdef},t)->letm_def=termenvdefinadd_tletenv.locall_var_infom_def;letm=termenvtinrem_tletenv.locall_var_info;m|Tlet(_,_t)->Wp_parameters.not_yet_implemented"unknown \\let construct"(* No escape *)|Tblock_length(_,t)->E(unescape((termenv)t))(* Call *)|Tapp({l_var_info=({lv_origin=None;lv_kind=LVLocal}aslvar)},[],[])->(* var bound by a \\let *)get_tletenv.locallvar|Tapp(phi,_,ts)->v_model(v_lphienvphits)(* --- Lvalues --- *)andterm_lvalenv(h,ofs)=matchhwith|TResult_|TVar{lv_name="\\exit_status"}->nothing|TVar({lv_origin=None;lv_kind=LVLocal}aslvar)->(* var bound by a \\let *)load(term_offsetenv(get_tletenv.locallvar)ofs)|TVar({lv_origin=None})->(* logic variable *)nothing|TVar({lv_origin=Somex})->load(term_offsetenv(Loc_varx)ofs)|TMemt->load(term_offsetenv(load(termenvt))ofs)andterm_indicesenvv=function|TNoOffset->v|TModel(_,ofs)|TField(_,ofs)->term_indicesenvvofs|TIndex(e,ofs)->term_indicesenv(E.cupv(vtermenve))ofsandterm_offsetenv(l:model)=function|TNoOffset->l|TField(_,ofs)->term_offsetenv(fieldl)ofs|TIndex(e,ofs)->term_offsetenv(shiftl(vtermenve))ofs|TModel_->Wp_parameters.not_yet_implemented"Model fields"andaddr_lvalenv(h,ofs)=matchhwith|TResult_->Wp_parameters.abort~current:true"Address of \\result"|TVar{lv_name="\\exit_status"}->Wp_parameters.abort~current:true"Address of \\exit_status"|TMemt->term_offsetenv(termenvt)ofs|TVar({lv_origin=Somex})->term_offsetenv(Loc_varx)ofs|TVar({lv_origin=None}asx)->Wp_parameters.abort~current:true"Address of logic variable (%a)"Logic_var.prettyx(* --- Predicates --- *)andpred(env:ctx)p:value=matchp.pred_contentwith|Pfalse|Ptrue->E.bot(* Unary *)|Pat(p,_)|Pnotp->(predenv)p(* Binary *)|Pand(p1,p2)|Por(p1,p2)|Pxor(p1,p2)|Piff(p1,p2)|Pimplies(p1,p2)->E.fcup(predenv)[p1;p2]|Pif(t,p1,p2)->E.cup((vtermenv)t)(E.fcup(predenv)[p1;p2])|Prel(_,t1,t2)->E.fcup(vtermenv)[t1;t2](* Binders *)|Pforall(_,p)|Pexists(_,p)->(predenv)p|Plet({l_var_info;l_body=LBtermdef},p)->letm_def=termenvdefinadd_tletenv.locall_var_infom_def;lete=predenvpinrem_tletenv.locall_var_info;e|Plet({l_var_info;l_body=LBpreddef},p)->lete_def=predenvdefinadd_pletenv.locall_var_infoe_def;lete=predenvpinrem_pletenv.locall_var_info;e|Plet(_,_t)->Wp_parameters.not_yet_implemented"unknown \\let construct"(* Call *)|Papp({l_var_info=({lv_origin=None;lv_kind=LVLocal}aslvar)},[],[])->(* var bound by a \\let *)get_pletenv.locallvar|Papp(phi,_,ts)->v_lphienvphits(* No escape *)|Pinitialized(_,t)|Pdangling(_,t)|Pallocable(_,t)|Pfreeable(_,t)|Pvalid(_,t)|Pvalid_read(_,t)|Pobject_pointer(_,t)|Pvalid_functiont->unescape((termenv)t)|Pseparatedts->E.fcup(funt->unescape((termenv)t))ts|Pfresh(_,_,t1,t2)->E.fcup(funt->unescape((termenv)t))[t1;t2](* --- Call to Logical functions/Predicates --- *)andv_lphi(env:ctx)(lphi:logic_info)ts:value=letnot_yet_implementeds=Wp_parameters.not_yet_implemented"unknown construct with %s"sinmatchlphi.l_var_info.lv_kindwith|LVC->not_yet_implemented"LVC"|LVFormal->not_yet_implemented"LVFormal"|LVQuant->not_yet_implemented"LVQuant"|LVLocal->not_yet_implemented"LVLocal"|LVGlobal->letv_body=(* get the accesses the globals *)ifnot(LFset.memlphienv.global.lphi)thenbeginenv.global.lphi<-LFset.addlphienv.global.lphi;v_bodyenvlphi.l_bodyendelseE.botandv_param=E.fcup(vtermenv)ts(* usage of the parameter of the application *)inE.cupv_paramv_bodyandv_body(env:ctx)=(* locals of the logical function are removed *)letvglobv=snd(E.partition_formals_vs_othersv)infunction|LBnone->E.bot|LBreads(its)->E.fcup(funit->vglob((vtermenv)it.it_content))its|LBterm(t)->vglob(vtermenvt)|LBpred(p)->vglob(predenvp)|LBinductive(inds)->E.fcup(fun(_,_,_,p)->vglob(predenvp))inds(* ---------------------------------------------------------------------- *)(* --- Compilation of C Function --- *)(* ---------------------------------------------------------------------- *)letcinitviinit=letupdate_code_envav=E.cupavinleteinit(m:model)aexp=update_code_enva(E.cup(e_valuem)(vexprexp))inletrecaux(m:model)a=function|SingleInit(exp)->einitmaexp|CompoundInit(_,loi)->List.fold_left(funa(ofs,init)->aux(offsetmofs)ainit)aloiinaux(cvalvi)E.botinitletcfun_codeenvkf=(* Visits term/pred of code annotations and C exp *)letupdate_code_envv=env.global.code<-E.cupenv.global.codevinletdo_termt=update_code_env(vtermenvt)inletdo_predp=update_code_env(predenvp)inletdo_code=letdo_argarg=(* normalizing model: taking out access map *)matchexprargwith(* in order to put it code_env *)|(Loc_var_|Val_var_)asm->m|(Loc_shift(_,e)|Val_shift(_,e))asmwhenE.is_bote->m|Loc_shift(x,e)->update_code_enve;Loc_shift(x,E.bot)|Val_shift(x,e)->update_code_enve;Val_shift(x,E.bot)|Val_comp(x,e)->update_code_enve;Val_comp(x,E.bot)|mwhenm==nothing->m|Ee->update_code_enve;nothinginletdo_argskfargs=env.global.cphi<-KFmap.insert(fun_uold->u@old)kf[(List.mapdo_argargs)]env.global.cphiinletdo_expexp=update_code_env(vexprexp)inletdo_lvallval=update_code_env(e_value(load(lvaluelval)))inletdo_lval_opt=function|None->()|Somelval->do_lvallvalinfunction|Block_|Break_|Continue_|Goto_|Loop_|UnspecifiedSequence_|TryFinally_|Return(None,_)|Instr(Asm_)|Instr(Skip_)|Instr(Code_annot_)->()|Throw_|TryCatch_|TryExcept_->Wp_parameters.warning"RefUsage: throw/try-catch not implemented"|Instr(Set(lval,exp,_))->do_lvallval;do_expexp|Instr(Call(lval_opt,fun_exp,args_list,_))->begindo_lval_optlval_opt;matchKernel_function.get_calledfun_expwith|None->List.iterdo_exp(fun_exp::args_list)|Somecalled_kf->do_argscalled_kfargs_listend|Instr(Local_init(v,AssignIniti,_))->update_code_env(cinitvi)|Instr(Local_init(v,ConsInit(f,args,kind),_))->letkf=Globals.Functions.getfin(matchkindwith|Constructor->do_argskf(Cil.mkAddrOfViv::args)|Plain_func->update_code_env(e_value(cvalv));do_argskfargs)|Return(Someexp,_)|If(exp,_,_,_)|Switch(exp,_,_,_)->do_expexpinletvisitor=objectinheritVisitor.frama_c_inplaceassupermethod!vstmtstmt=do_codestmt.skind;super#vstmtstmt(* vpredicate and vterm are called from vcode_annot *)method!vpredicatep=do_predp;Cil.SkipChildrenmethod!vtermt=do_termt;Cil.SkipChildren(* speed up: skip non interesting subtrees *)method!vloop_pragma_=Cil.SkipChildren(* no need *)method!vvdec_=Cil.SkipChildren(* done via stmt *)method!vexpr_=Cil.SkipChildren(* done via stmt *)method!vlval_=Cil.SkipChildren(* done via stmt *)method!vattr_=Cil.SkipChildren(* done via stmt *)method!vinst_=Cil.SkipChildren(* done via stmt *)endintryletdefinition=Kernel_function.get_definitionkfinignore(Cil.visitCilFunction(visitor:>Cil.cilVisitor)definition)withNot_found->()letcfun_specenvkf=letupdate_spec_envv=env.local.spec<-E.cupenv.local.specv;Cil.SkipChildreninletvisitor=objectinheritCil.nopCilVisitormethod!vpredicatep=update_spec_env(predenvp)method!vtermt=update_spec_env(vtermenvt)endinAssignsCompleteness.computekf;letspec=Annotations.funspeckfinignore(Cil.visitCilFunspec(visitor:>Cil.cilVisitor)spec);(* Partitioning the accesses of the spec for formals vs globals *)letformals,globals=E.partition_formals_vs_othersenv.local.specinenv.global.spec_formals<-formals;env.global.spec_globals<-globalsletcfunkf=letenv=mk_ctx()in(* Skipping frama-c builtins?
if not (Cil_builtins.is_builtin (Kernel_function.get_vi kf)) then *)beginifKernel_function.is_definitionkfthencfun_codeenvkf;cfun_specenvkfend;env.globalletcvarinitviinitinfoenv=matchinitinfo.initwith|None->env|Someinit->E.cupenv(cinitviinit)(* ---------------------------------------------------------------------- *)(* --- Compilation --- *)(* ---------------------------------------------------------------------- *)letmk_context()=KFmap.emptyletparamam=matchawith|NoAccess|ByAddr->E.bot(* should never arise *)|ByValue->e_valuem|ByRef->e_value(loadm)|ByArray->e_value(load(shiftmE.bot))letupdate_call_env(env:global_ctx)v=letr,differ=E.cup_differenv.codevinenv.code<-r;differletcall_kf(env:global_ctx)(formals:accesslist)(models:modellist)(reached:bool)=letunmodified=refreachedinletreccallxsms=matchxs,mswith|x::xs,m::ms->letactual=paramxminifupdate_call_envenvactualthenunmodified:=false;callxsms|_->()incallformalsmodels;!unmodifiedtypecallee=KFset.ttypecallees=calleeKFmap.ttypefp_t={mutabletodo:unitKFmap.t;mutableredo:unitKFmap.t}letcompute_usage()=Wp_parameters.feedback~ontty:`Transient"Collecting variable usage";(* initial state from variable initializers *)letu_init=Globals.Vars.foldcvarinitE.botin(* Usage in lemmas *)letu_lemmas=LogicUsage.fold_lemmas(funl->E.cup(pred(mk_ctx())l.lem_predicate.tp_statement))E.botin(* initial state by kf *)letusage=Globals.Functions.fold(funkfenv->KFmap.insert(fun__u_old->assertfalse)kf(cfunkf)env)(mk_context())in(* inverse table of function calls *)letcallees=KFmap.fold(funkfv(a:callees)->KFmap.fold(funcalled_kf_(a:callees)->KFmap.insert(fun_v(old:callee)->KFset.unionoldv)called_kf(KFset.addkfKFset.empty)a)v.cphia)usageKFmap.emptyin(* extract kf map to be fixed (the callers). *)letcallers=KFmap.mapq(fun_kfv->ifKFmap.is_emptyv.cphithenNoneelseSomev)usagein(* extract it as a working list to be fixed *)lettodo=KFmap.map(fun_->())callersin(* the todo map is used to intersect the callers map *)letkf_fpstate_fpkfenv_=letkf_callscalled_kfcalls(reached:bool)=letcalled=tryKFmap.findcalled_kfusagewithNot_found->assertfalsein(* update from accesses of globals of the called spec *)letreached=ifupdate_call_envenvcalled.spec_globalsthenfalseelsereachedin(* update from accesses of formals of the called spec for each calls*)letspecs_formals=called.spec_formalsinletparams=Kernel_function.get_formalscalled_kfinletformals=List.map(funvi->E.getvispecs_formals)paramsinletkf_callreachedcall=call_kfenvformalscallreachedinList.fold_leftkf_callreachedcallsinstate_fp.todo<-KFmap.removekfstate_fp.todo;letcphi=env.cphiinletreached=KFmap.foldkf_callscphitrueinifnotreachedthenbeginletcallers=tryKFmap.findkfcalleeswithNot_found->KFset.emptyinKFset.iter(funkf_caller->tryignore(KFmap.findkf_callertodo)(* kf_caller is still into the current remaining working list *)withNot_found->(* kf_caller must be added to the next working list. *)state_fp.redo<-KFmap.addkf_caller()state_fp.redo)callersend;(* the intersect result is not used *)Noneinletrecfixpointtodo=ifnot(KFmap.is_emptytodo)thenletstate_fp={redo=KFmap.empty;todo}inignore(KFmap.interf(kf_fpstate_fp)callerstodo);fixpointstate_fp.todoinfixpointtodo;letu_init=E.cupu_initu_lemmasin(* TODO[LC]: prendre en compte la compilation des fonctions logiques et predicats ; Cf. add_lphi *)letusage=KFmap.map(functx->E.lcup[u_lemmas;ctx.code;ctx.spec_globals;ctx.spec_formals])usageinu_init,usage(* ---------------------------------------------------------------------- *)(* --- Projectified Analysis Result --- *)(* ---------------------------------------------------------------------- *)moduleD=Datatype.Make(structtypet=E.t*E.tKFmap.tincludeDatatype.Serializable_undefinedletreprs=[E.bot,KFmap.empty]letname="RefUsage.usage"end)moduleS=State_builder.Option_ref(D)(structletname="RefUsage.Analysis"letdependencies=[Ast.self]end)(* compute_usage is called once per project *)letusage()=S.memocompute_usageletis_computed()=S.is_computed()(* ---------------------------------------------------------------------- *)(* --- Nullable variables --- *)(* ---------------------------------------------------------------------- *)moduleNullable=structletattribute_name="wp_nullable"letis_nullablevi=vi.vformal&&Cil.hasAttributeattribute_namevi.vattrletmake_nullablevi=vi.vattr<-Cil.addAttribute(AttrAnnotattribute_name)vi.vattrmoduleNullable_extension=structlettype_termctxtloce=matchctxt.Logic_typing.type_termctxtctxt.pre_stateewith|{term_node=TLval(TVar{lv_origin=Somevi},TNoOffset)}astermwhenCil.isPointerTypevi.vtype&&vi.vformal->make_nullablevi;term|t->ctxt.errorloc"Not a formal pointer: %a"Cil_printer.pp_termtlettyperctxtlocl=Ext_terms(List.map(type_termctxtloc)l)endlet()=Acsl_extension.register_behavior"wp_nullable_args"Nullable_extension.typerfalsemoduleHasNullable=State_builder.Option_ref(Datatype.Bool)(structletname="Wp.RefUsage.HasNullable"letdependencies=[Ast.self]end)letcompute_nullable()=letmoduleF=Globals.FunctionsinF.fold(funfb->b||List.fold_left(funbv->b||is_nullablev)b(F.get_paramsf))falselethas_nullable()=HasNullable.memocompute_nullableend(* ---------------------------------------------------------------------- *)(* --- API --- *)(* ---------------------------------------------------------------------- *)letiter?kf?(init=false)f=letu_init,usage=usage()inletkf_access=matchkfwith|None->E.bot|Somekf->(tryKFmap.findkfusagewithNot_found->E.bot)inletaccess=ifinitthenE.cupkf_accessu_initelsekf_accessinE.iterfaccessletget?kf?(init=false)vi=letu_init,usage=usage()inletkf_access=matchkfwith|None->NoAccess|Somekf->(tryE.getvi(KFmap.findkfusage)withNot_found->NoAccess)inifinitthenAccess.cupkf_access(E.getviu_init)elsekf_accessletcompute()=ignore(usage())letis_nullable=Nullable.is_nullablelethas_nullable=Nullable.has_nullableletprintxmfmt=Access.prettyxfmtmletdump()=Log.print_on_outputbeginfunfmt->Format.fprintffmt".................................................@\n";Format.fprintffmt"... Ref Usage@\n";Format.fprintffmt".................................................@\n";leta_init,a_usage=usage()inFormat.fprintffmt"@[<hv 0>Init:@ %a@]@."E.prettya_init;KFmap.iter(funkfm->(* Do not dump results for frama-c builtins *)ifnot(Cil_builtins.is_builtin(Kernel_function.get_vikf))thenFormat.fprintffmt"@[<hv 0>Function %a:@ %a@]@."Kernel_function.prettykfE.prettym;)a_usage;Format.fprintffmt".................................................@\n";end