123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020(**************************************************************************)(* *)(* SPDX-License-Identifier LGPL-2.1 *)(* Copyright (C) *)(* CEA (Commissariat à l'énergie atomique et aux énergies alternatives) *)(* *)(**************************************************************************)(* -------------------------------------------------------------------------- *)(* Tailrec List.map *)lettailrec_list_mapfl=List.rev(List.rev_mapfl)moduleFILE=FileopenCil_typesopenCil_datatypeopenOptions.KeysmoduleStringTbl=Datatype.String.Hashtblletdkey_binding=Options.register_category~help:"Prints debug messages related to volatile operations""binding"letdkey_binding_table=Options.register_category~help:"Prints debug messages related to volatile operations on internal tables""binding-table"letdkey_volatile_table=Options.register_category~help:"Prints Volatile internal tables""volatile-table"letdkey_transformation_action=Options.register_category~help:"Prints information on generated code""transformation-action"letdkey_transformation_visit=Options.register_category~help:"Prints visitor information during the transformation""transformation-visit"lethas_volatile_attrt=Ast_types.get_attributest|>Ast_attributes.contains"volatile"letadd_volatile_attr=Ast_types.add_attributes[("volatile",[])](* This function replaces spaces in type names.
Note: A previous version also made sure all characters were either a-z, A-Z or
0-9 (or raised Not_found), it does not seem to be necessary so it was removed
to simplify the code, but maybe it'll break something (?).
*)lettypename(t:typ)=lettypename=Pretty_utils.to_stringTyp.prettytinString.map(function' '->'_'|c->c)typename(* -------------------------------------------------------------------------- *)(* --- Global Volatile Annotation tables --- *)(* -------------------------------------------------------------------------- *)(* normalized l-path
`p+idx` is normalized to `p` and by the way `p[idx]` is normalised to `*p`.
Due to CIL decomposition, it is impossible to find the final volatile lvalue
when the path to that volatile lvalue contents a volatile pointer.
In a simplest way, volatile of volatile are not handle by volatile clauses.
From that limitation and type constraints, there is no needs to distinguish
`*p` from `p` (only one of these lvalue is volatile).
*)moduleL_PATH=structexceptionUnsupportedtypet=|LramofInteger.t(* absolute addr *)|Lvarofvarinfo(* [x] *)|Lfieldoft*fieldinfo(* [e.f] *)letrecprettyfmt=function|Lvarx->Varinfo.prettyfmtx|Lramp->Format.fprintffmt"&%s"(Integer.to_stringp)|Lfield(e,fd)->Format.fprintffmt"%a:%a"prettyeFieldinfo.prettyfdletreccompareab=matcha,bwith|Lramp,Lramq->Integer.comparepq|Lram_,_->-1|_,Lram_->1|Lvarx,Lvary->Varinfo.comparexy|Lvar_,_->-1|_,Lvar_->1|Lfield(x,f),Lfield(y,g)->letcmp=Fieldinfo.comparefginifcmp<>0thencmpelsecomparexyletrecof_expre=matche.enodewith|Lvallv->of_lvallv|AddrOflv|StartOflv->of_lvallv|BinOp((PlusPI|MinusPI),e,_,_)|CastE(_,e)->of_expre|_->matchCil.constFoldToIntewith|Somep->Lramp|None->raiseUnsupportedandof_lval(host,offset)=of_offset(of_hosthost)offsetandof_host=function|Varx->Lvarx|Meme->of_expreandof_offsetp=function|NoOffset->p|Index(_,ofs)->of_offsetpofs|Field(fd,ofs)->of_offset(Lfield(p,fd))ofsletrecto_constt=matcht.term_nodewith|TCast(false,_,e)->to_conste|Tnull->Integer.zero|TConst(Integer(i,_))->i|TBinOp(PlusA,a,b)->Integer.add(to_consta)(to_constb)|_->raiseUnsupportedletrecof_termt=matcht.term_nodewith|TLvallv->of_tlvallv|TBinOp((PlusPI|MinusPI),e,_)|TCast(_,_,e)|Tat(e,_)->of_terme|_->Lram(to_constt)andof_tlval(host,offset)=of_toffset(of_thosthost)offsetandof_thost=function|TVar{lv_origin=Somex}->Lvarx|TMeme->of_terme|_->raiseUnsupportedandof_toffsetp=function|TNoOffset->p|TIndex(_,ofs)->of_toffsetpofs|TField(fd,ofs)->of_toffset(Lfield(p,fd))ofs|TModel_->raiseUnsupportedendmoduleL_MAP=Map.Make(L_PATH)(* -------------------------------------------------------------------------- *)(* --- Automatic binding of volatile accesses --- *)(* -------------------------------------------------------------------------- *)(* Table management for [-volatile-binding-auto] option *)moduleBA_TBL=structletget_tbl_access~is_wr_accesskf_tbl=ifis_wr_accessthensndkf_tblelsefstkf_tbl(** Looking for a kernel function name starting with prefix^("Wr_"|"Rd_") *)letfilter_kf_namekf_name=letprefix=Options.BindingPrefix.get()inletrd_prefix=prefix^"Rd_"inletwr_prefix=prefix^"Wr_"inifString.starts_with~prefix:rd_prefixkf_namethenSomefalseelseifString.starts_with~prefix:wr_prefixkf_namethenSometrueelseNoneletfilter_kf_prototype~is_wr_accessfct=(* Verifying the prototype within the kind of access. *)letty=fct.vtypeinletret_type,args,is_varg_arg,_attrib=Cil.splitFunctionTypetyinletvolatile_ret_type=add_volatile_attrret_typeinOptions.debug~level:2~dkey:dkey_binding"Verifying prototype of function %s: %a@."fct.vorig_nameTyp.prettyty;letnot_void_or_varg=not(Ast_types.is_voidret_type||is_varg_arg)inmatchis_wr_access,argswith|false,Some[_,arg1,_]whennot_void_or_varg&&Ast_types.is_ptrarg1&&Typ.equal(Ast_types.direct_pointed_typearg1)volatile_ret_type->true(* matching prototype: T fct (volatile T *arg1) *)|false,Some[_,arg1,_]whennot_void_or_varg&&Ast_types.is_ptrarg1&&Typ.equal(Ast_types.direct_pointed_typearg1)ret_type&&Ast_types.is_volatileret_type->true(* matching prototype: T fct (T *arg1) when T has some volatile attr. *)|true,Some((_,arg1,_)::[_,arg2,_])whennot_void_or_varg&&Ast_types.is_ptrarg1&&Typ.equalarg2ret_type&&Typ.equal(Ast_types.direct_pointed_typearg1)volatile_ret_type->true(* matching prototype: T fct (volatile T *arg1, T arg2) *)|true,Some((_,arg1,_)::[_,arg2,_])whennot_void_or_varg&&Ast_types.is_ptrarg1&&Typ.equalarg2ret_type&&Typ.equal(Ast_types.direct_pointed_typearg1)ret_type&&Ast_types.is_volatileret_type->true(* matching prototype: T fct (T *arg1, T arg2) when T has some volatile attr. *)|_,_->Options.debug~level:2~dkey:dkey_binding"Invalid prototype of function %s@."fct.vorig_name;falseletbuild_kf_tablekf_tbl=match!kf_tblwith|Somekf_tbl->kf_tbl|None->letkf_tbl=lettbl_rd=StringTbl.create40inlettbl_wr=StringTbl.create40inlettbl_rd_wr=tbl_rd,tbl_wrinkf_tbl:=Sometbl_rd_wr;tbl_rd_wrinletmay_add_vi~is_wr_accesskf_tblkf_namevi_kf=iffilter_kf_prototype~is_wr_accessvi_kfthenbeginOptions.debug~level:2~dkey:dkey_binding_table"Adding function into the default binding table: %s@."kf_name;StringTbl.add(get_tbl_access~is_wr_accesskf_tbl)kf_namevi_kfendinletmay_add_kfkf=letvi_kf=Kernel_function.get_vikfinletkf_name=vi_kf.vorig_nameinmatchfilter_kf_namekf_namewith|None->()|Someis_wr_access->may_add_vi~is_wr_accesskf_tblkf_namevi_kf;inifOptions.BindingAuto.get()then(Options.feedback~level:2"Building default binding table...@.";Globals.Functions.itermay_add_kf);kf_tblletclear_kf_tablekf_tbl=match!kf_tblwith|None->()|Some(rd_tbl,wr_tbl)->StringTbl.clearrd_tbl;StringTbl.clearwr_tblend(* -------------------------------------------------------------------------- *)(* --- Specific binding of volatile accesses --- *)(* -------------------------------------------------------------------------- *)moduleT_MAP=structmoduleM=Typ.Mapletempty=M.emptyletbasetypet=lett=Ast_types.unroll_deeptinifOptions.Base.get()thenletrecbaset'=matcht'.tnodewith|TInt_|TFloat_whent'.tattr=[]->t'|TInti->Cil_const.mk_tinti|TFloatf->Cil_const.mk_tfloatf|TEnume->Cil_const.mk_tinte.ekind|TPtrbt->Cil_const.mk_tptr(basebt)|_->Ast_types.remove_attributes_for_c_castt'inbasetelsetletaddtdm=M.add(basetypet)dmletfind_opttm=M.find_opt(basetypet)mend(* Table management for [-volatile-binding] option *)moduleB_MAP=structletchecks_prototype_kindfct=(* Verifying the prototype within the kind of access. *)letty=fct.vtypeinletret_type,args,is_varg_arg,_attrib=Cil.splitFunctionTypetyinletvolatile_ret_type=add_volatile_attrret_typeinOptions.debug~level:2~dkey:dkey_binding"Verifying prototype of function %s: %a@."fct.vorig_nameTyp.prettyty;letresultis_wr_accessarg1=Some(is_wr_access,(Ast_types.direct_pointed_typearg1))inmatchargswith|Some[_,arg1,_]when(not(Ast_types.is_voidret_type||is_varg_arg))&&Ast_types.is_ptrarg1&&Typ.equal(Ast_types.direct_pointed_typearg1)volatile_ret_type->resultfalsearg1(* matching prototype: T fct (volatile T *arg1) *)|Some[_,arg1,_]when(not(Ast_types.is_voidret_type||is_varg_arg))&&Ast_types.is_ptrarg1&&Typ.equal(Ast_types.direct_pointed_typearg1)ret_type&&Ast_types.is_volatileret_type->resultfalsearg1(* matching prototype: T fct (T *arg1) when T has some volatile attr*)|Some((_,arg1,_)::[_,arg2,_])when(not(Ast_types.is_voidret_type||is_varg_arg))&&Ast_types.is_ptrarg1&&Typ.equalarg2ret_type&&Typ.equal(Ast_types.direct_pointed_typearg1)volatile_ret_type->resulttruearg1(* matching prototype: T fct (volatile T *arg1, T arg2) *)|Some((_,arg1,_)::[_,arg2,_])when(not(Ast_types.is_voidret_type||is_varg_arg))&&Ast_types.is_ptrarg1&&Typ.equalarg2ret_type&&Typ.equal(Ast_types.direct_pointed_typearg1)ret_type&&Ast_types.is_volatileret_type->resulttruearg1(* matching prototype: T fct (T *arg1, T arg2) when T has some volatile attr *)|_->Options.warning~once:true~wkey:wkey_invalid_binding_function"Binding function '%s' has an invalid prototype"fct.vorig_name;Noneletbuild_binding_map()=List.fold_left(fun((map_rd,map_wr)asmaps)f->tryletkf=Globals.Functions.find_by_namefinletvf=Kernel_function.get_vikfinmatchchecks_prototype_kindvfwith|None->maps|Some(is_wr_access,volatile_object)->letmap=ifis_wr_accessthenmap_wrelsemap_rdinletmap=matchT_MAP.find_optvolatile_objectmapwith|None->Some(T_MAP.addvolatile_objectvfmap)|Somevf0->Options.warning~once:true~wkey:wkey_invalid_binding_function"Functions -volatile-binding '%s' and '%s' %s"vf0.vorig_namevf.vorig_name(ifOptions.Base.get()then"apply to the same base type"else"has same signature");Noneinmatchmapwith|None->maps|Somemap->Options.feedback"Register binding function '%s' for '%s' accesses to type '%a'"vf.vorig_name(ifis_wr_accessthen"write"else"read")Typ.pretty(T_MAP.basetypevolatile_object);ifis_wr_accessthen(map_rd,map)else(map,map_wr)withNot_found->Options.warning~once:true~wkey:wkey_invalid_binding_function"Unknown function related to -volatile-binding '%s'"f;maps)(T_MAP.empty,T_MAP.empty)(Options.Binding.get())letfind_binding~is_wr_accessmaptyp=T_MAP.find_opttyp(ifis_wr_accessthensndmapelsefstmap)end(* -------------------------------------------------------------------------- *)(* --- Pointer Call Annotations --- *)(* -------------------------------------------------------------------------- *)moduleSIG=structmoduleCT=Wp.Ctypestypet=CT.c_objectoption*CT.c_objectlist*boolletprettyfmt(r,ts,va)=Format.fprintffmt"@[<hov 2>%a("CT.prettyr;Pretty_utils.pp_list~sep:",@ "CT.prettyfmtts;ifvathenFormat.fprintffmt",@,...";Format.fprintffmt")@]"[@@warning"-32"]letof_returnr=ifAst_types.is_voidrthenNoneelseSome(CT.object_ofr)letof_typet:t=letr,args,va,_=Cil.splitFunctionTypetinof_returnr,List.map(fun(_,ty,_)->CT.object_ofty)(Cil.argsToListargs),valetof_vivi=of_typevi.vtypeletof_kfkf=of_vi(Kernel_function.get_vikf)[@@warning"-32"]letreccompare_listxsys=matchxs,yswith|[],[]->0|[],_->(-1)|_,[]->1|p::ps,q::qs->letcmp=CT.comparepqinifcmp<>0thencmpelsecompare_listpsqsletcompare_optionxy=matchx,ywith|None,None->0|None,Some_->(-1)|Some_,None->1|Somep,Someq->CT.comparepqletcompare(r1,p1,v1)(r2,p2,v2)=matchv1,v2with|true,false->(-1)|false,true->1|true,true|false,false->letcmp=compare_optionr1r2inifcmp<>0thencmpelsecompare_listp1p2letstubvf=ifAst_types.is_funvf.vtypethenletr,args,va,_=Cil.splitFunctionTypeVIvfinmatchCil.argsToListargswith|(_,tf,_)::ps->letr=of_returnrinletts=List.map(fun(_,ty,_)->CT.object_ofty)psinletsp=of_type(Ast_types.direct_pointed_typetf)inletsf=(r,ts,va)inifcomparespsf<>0thenNoneelseSomesf|_->NoneelseNoneendmoduleINDEX=Map.Make(SIG)letbuild_call_index()=List.fold_left(funidxf->letkf=tryGlobals.Functions.find_by_namefwithNot_found->Options.abort"Unknown function -volatile-call-pointer '%s'"finletvf=Kernel_function.get_vikfinmatchSIG.stubvfwith|None->Options.abort"Function '%s' can not be used as call-pointer stub"f|Somes->matchINDEX.find_optsidxwith|Somevf0->Options.abort"Functions -volatile-call-pointer '%s' and '%s' has same signature"vf0.vorig_namevf.vorig_name|None->INDEX.addsvfidx)INDEX.empty(Options.CallPtr.get())(* -------------------------------------------------------------------------- *)(* --- Pointer Calls --- *)(* -------------------------------------------------------------------------- *)letget_called_ptr=function|Meme->Somee|_->Noneletget_canonical_call~sourceftf=letname=matchtf.tnodewith|TNamedtiwhenAst_types.is_funtf->ti.torig_name|TFun(r,args,va)->letbuffer=Buffer.create80inBuffer.add_stringbuffer(Options.BindingPrefix.get());Buffer.add_stringbuffer"Call_";Buffer.add_stringbuffer(typenamer);List.iter(fun(_,ty,_)->Buffer.add_charbuffer'_';Buffer.add_stringbuffer(typenamety);)(Cil.argsToListargs);ifvathenBuffer.add_stringbuffer"_va";Buffer.contentsbuffer|_->Options.abort~source"@[<hov 0>Call to @[<hov 2>(%a)@]@ with non-function type @[<hov 2>(%a)@]@]"Exp.prettyfTyp.prettytfintryletkf=Globals.Functions.find_by_namenameinSome(Kernel_function.get_vikf)withNot_found->Options.warning~source~once:true~wkey:wkey_untransformed_call_function_not_found"@[<hov 0>Call to (%a) with type @[<hov 2>(%a):@]@ Function '%s' not found@]"Exp.prettyfTyp.pretty(Cil.typeOff)name;Noneletget_pointer_call~index~sourcef=lettf=Ast_types.direct_pointed_type(Cil.typeOff)inletres=INDEX.find_opt(SIG.of_typetf)indexinmatchreswith|Some_->res|None->ifOptions.BindingCall.get()thenget_canonical_call~sourceftfelseNoneletadd_eventual_cast_to_expressionlval_type=letnewt=Ast_types.remove_attributes_for_c_castlval_typinlete'=Cil.mkCast~force:false~newteinife'!=ethenOptions.warning~source:(fste.eloc)~once:true~wkey:wkey_cast_insertion"@[<hov 0>Cast to (%a) inserted@ for expression (%a)@ of type (%a)@]"Typ.prettynewtExp.prettyeTyp.pretty(Cil.typeOfe);e'letadd_eventual_cast_to_paramarg_typparam=letnewt=Ast_types.remove_attributes_for_c_castarg_typinletparam'=Cil.mkCast~force:false~newtparaminifparam'!=paramthenOptions.warning~source:(fstparam.eloc)~once:true~wkey:wkey_cast_insertion"@[<hov 0>Cast to (%a) inserted@ for parameter (%a)@ of type (%a)@]"Typ.prettynewtExp.prettyparamTyp.pretty(Cil.typeOfparam);param'letdo_pointer_call~loc~index~transformfes=letsource=fstlocinmatchget_pointer_call~index~sourcefwith|None->None|Somevf->letfn=vf.vorig_nameinletrecwraptsvaes:explist=matchts,eswith|[],[]->[]|(_,t,_)::ts,e::es->(add_eventual_cast_to_paramte)::wraptsvaes|[],eswhenva->es|[],es->Options.warning~source~once:true~wkey:wkey_transformed_call_skipped_parameters"Using '%s': %d last parameters skipped"fn(List.lengthes);[]|ts,[]->Options.warning~source~once:true~wkey:wkey_transformed_call_missing_parameters"Using '%s': missing %d parameters"fn(List.lengthts);[]inletvf=transformvfinlet(_,args,va,_)=Cil.splitFunctionTypeVIvfinSome(fn,Varvf,wrap(Cil.argsToListargs)va(f::es))(*-------------------------------------------------------------------------*)lettypename_access~is_wr_access(t:typ)=lettypename=typenametinletr=(Options.BindingPrefix.get())^(ifis_wr_accessthen"Wr_"else"Rd_")^typenameinOptions.debug~dkey:dkey_binding"Looking for function %s@."r;rletfind_typename~is_wr_accesskf_tbltyp=letopenOption.Operatorsinletkf_tbl=Option.value~default:(BA_TBL.build_kf_tablekf_tbl)!kf_tblinletrecfind_fcttyp=lettyp=Ast_types.remove_attributes_for_c_casttypinlettbl=BA_TBL.get_tbl_access~is_wr_accesskf_tblinlettyp_name=typename_access~is_wr_accesstypinmatchStringTbl.find_opttbltyp_namewith|Somevi->Somevi|None->(* Unroll the typedef until finding one function into the kf table. *)matchtyp.tnodewith|TNamedr->find_fctr.ttype|_->NoneinOptions.debug~level:2~dkey:dkey_binding"Looking for a default binding from the type name: %a@."Typ.prettytyp;(* Verifying the protyping within the type of the volatile access. *)let*fct=find_fcttypinletty=fct.vtypeinletret,_args,_is_varg_arg,_attrib=Cil.splitFunctionTypetyinletvolatile_ret_type=add_volatile_attrretinOptions.debug~level:2~dkey:dkey_binding"Verifying the type of the lvalue within the prototype of function %s: %a@."fct.vorig_nameTyp.prettyty;ifnot(Typ.equaltypvolatile_ret_type)thenNoneelseSomefct(*-------------------------------------------------------------------------*)typevmap={mutablerd:varinfoL_MAP.t;mutablewr:varinfoL_MAP.t;}(** Builds a table of volatile clauses.
This table can be viewed as a map from term_lhost to a map from term_lval
to (reads, writes) functions. *)letbuild_volatile_tablevmap=letadd_fctkindlocmappath=function|None->map|Somefct->matchL_MAP.find_optpathmapwith|Someold->ifnot(Varinfo.equaloldfct)thenOptions.warning~source:(fstloc)~once:true~wkey:wkey_duplicated_access_function"%s access function already defined for %a"kindL_PATH.prettypath;map|None->L_MAP.addpathfctmapinletadd_clause_emitter=function|Dvolatile(tset,fct_rd,fct_wr,_attr,loc)->List.iter(funl->tryletp=L_PATH.of_terml.it_contentinvmap.rd<-add_fct"read"locvmap.rdpfct_rd;vmap.wr<-add_fct"write"locvmap.wrpfct_wr;withL_PATH.Unsupported->Options.error~source:(fstloc)"Unsupported l-value in volatile clause: %a@."Identified_term.prettyl)tset|_->()inOptions.feedback~level:2"Building volatile table...@.";Annotations.iter_globaladd_clause;ifOptions.is_debug_key_enableddkey_volatile_tablethenbeginletdumpkindmapfmt=L_MAP.iter(funpf->Format.fprintffmt"@\n@[<hov 2>volatile %a@ %s %a@]"L_PATH.prettypkindVarinfo.prettyf)mapinOptions.debug~dkey:dkey_volatile_table"Volatile table:%t%t@."(dump"reads"vmap.rd)(dump"writes"vmap.wr)end(*-------------------------------------------------------------------------*)(* Returne a tuple 'transformed_key, untransformed_key) *)letget_wkeys~is_complete=ifis_completethenwkey_transformed_access_lvalue_volatile,wkey_untransformed_access_lvalue_volatileelsewkey_transformed_access_lvalue_partially_volatile,wkey_untransformed_access_lvalue_partially_volatileletget_volatile_access~is_wr_accessfct_namebinding_mapkf_tblvol_tbllval=lettyp=Cil.typeOfLvallvalinletsource=fst(Current_loc.get())inletwarn_access=ifis_wr_accessthen"write"else"read"in(* Can raise L_PATH.Unsupported via L_PATH.of_lval *)letget_volatile_access~is_complete=lettransformed_key,untransformed_key=get_wkeys~is_completeinletwarn_complete=ifis_completethen""else"partially "inletpath=L_PATH.of_lvallvalinletfoundfct=Options.debug~level:2~dkey:dkey_binding"Function found: %s@."fct.vname;Options.warning~source~once:true~wkey:transformed_key"%s function: Introducing a call to '%s' for %s access to %svolatile left-value: %a"fct_namefct.vorig_namewarn_accesswarn_completeLval.prettylval;Some(fct,(Ast_types.remove_attributes_for_c_casttyp))in(* Looking for a volatile function relative to the [lval] access. *)Options.debug~level:2~dkey:dkey_binding"Looking for a function relative to %s access to volatile left-value: %a@."warn_accessL_PATH.prettypath;(* 1 - Looking into the volatile table [vol_tbl]. *)letvmap=ifis_wr_accessthenvol_tbl.wrelsevol_tbl.rdinmatchL_MAP.find_optpathvmapwith|Somefct->foundfct|None->(* 2 - Looking into binding functions from the type value. *)matchB_MAP.find_binding~is_wr_accessbinding_maptypwith|Somef->foundf|None->(* 3 - Looking into kernel functions for a name inferred from the type value. *)matchfind_typename~is_wr_accesskf_tbltypwith|Somefct->foundfct|None->lett=Ast_types.remove_attributes_for_c_cast(ifOptions.Base.get()thenT_MAP.basetypetypelsetyp)inOptions.warning~source~once:true~wkey:untransformed_key"Undefined %s access function for %svolatile left-value: (volatile %a) %a"warn_accesswarn_completeTyp.prettytLval.prettylval;Noneintryifhas_volatile_attrtypthenget_volatile_access~is_complete:trueelseifAst_types.is_volatiletypthenget_volatile_access~is_complete:falseelseNonewithL_PATH.Unsupported->Options.warning~source"Unsupported volatile l-value: %a"Lval.prettylval;Noneletget_rd_typesfct=letty=fct.vtypeinletret,args,_is_varg_arg,_attrib=Cil.splitFunctionTypetyinmatchargswith|Some[_,arg1,_]->ret,arg1|_->Options.abort"Invalid prototype of function %s@."fct.vorig_nameletget_wr_typesfct=letty=fct.vtypeinletret,args,_is_varg_arg,_attrib=Cil.splitFunctionTypetyinmatchargswith|Some((_,arg1,_)::[_,arg2,_])->ret,arg1,arg2|_->Options.abort"Invalid prototype of function %s@."fct.vorig_nameletget_cast_type_needed_for_assignation~ret_typ~lv=lettlv=Cil.typeOfLvallvinlettlv=Ast_types.remove_qualifierstlvinifCabs2cil.allow_return_collapse~tlv~tf:ret_typthenNoneelseSometlvmoduleScopingBlock=structletstack=ref[]letreset()=stack:=[]letpushb=stack:=b::!stackletpop()=match!stackwith|[]->assertfalse|_::tail->stack:=taillettop()=match!stackwith|[]->assertfalse|top::_->topendletnew_blk()=Cil.mkBlockNonScoping[]classprocess_volatile_accessprojectbinding_mapkf_tblvol_tblindex=letcallptr=not(INDEX.is_emptyindex)||Options.BindingCall.get()inobject(self)inheritVisitor.frama_c_copyprojectmethodprivateget_volatile_access~is_wr_accesslv=letkf_name=matchself#current_kfwith|None->Options.fatal"get_volatile_access: this method should always be \
called inside a function (i.e. at a point where \
current_kf is set)."|Somekf->Kernel_function.get_namekfinget_volatile_access~is_wr_accesskf_namebinding_mapkf_tblvol_tbllvvalmutabletop_eid=-1methodprivateset_top_eid=function|Set(lv,{enode=Lval_;eid},_loc)->beginmatchself#get_volatile_access~is_wr_access:truelvwith|None->top_eid<-eid|Some_->()end|_->()methodprivatereset_top_eid()=top_eid<--1valmutableblk=new_blk()methodprivateadd_instri=Options.debug~level:2~dkey:dkey_transformation_action"Add new stmt to block";blk<-{blkwithbstmts=(Cil.mkStmt(Instri))::blk.bstmts}methodprivatemakeTempLvaltyp=Options.debug~level:2~dkey:dkey_transformation_action"Add tmp variable to block";letdefinition=Visitor_behavior.Get.fundecself#behavior(Option.getself#current_func)inVar(Cil.makeLocalVardefinition~scope:(ScopingBlock.top())"__volatile_tmp"typ),NoOffsetmethod!vblockb=Options.debug~level:2~dkey:dkey_transformation_visit"Visit DO blk@.";ifb.bscopingthenScopingBlock.pushb;letpopb=ifb.bscopingthenScopingBlock.pop();binletr=Cil.ChangeDoChildrenPost(b,pop)inOptions.debug~level:2~dkey:dkey_transformation_visit"Visit DONE blk@.";rmethod!vstmt_auxs=Options.debug~level:2~dkey:dkey_transformation_visit"Visit DO stmt: sid=%d, volatile block:@.%a@."s.sidBlock.prettyblk;letprevious_blk=ifblk.bstmts=[]thenNoneelseletprevious_blk=blkinblk<-new_blk();Someprevious_blkinletdo_vstmtst=letcurrent_blk=blkinblk<-(matchprevious_blkwith|None->new_blk()|Someprevious_blk->previous_blk);ifcurrent_blk.bstmts=[]thenbeginOptions.debug~level:3~dkey:dkey_transformation_visit"Do not Transform stmt:@.sid=%d %a@."s.sidStmt.prettyst;stendelsebeginOptions.debug~level:2~dkey:dkey_transformation_visit"Transform DO stmt: sid=%d, volatile block:@.%a@."s.sidBlock.prettycurrent_blk;letstmt=Cil.mkStmtst.skindinletstmts={current_blkwithbstmts=List.rev(stmt::current_blk.bstmts)}inst.skind<-Blockstmts;Options.debug~level:2~dkey:dkey_transformation_visit"Transform Done stmt: sid=%d, new block:@.%a@."st.sidStmt.prettyst;stendinletr=Cil.ChangeDoChildrenPost(s,do_vstmt)inOptions.debug~level:2~dkey:dkey_transformation_visit"Visit Done stmt: sid=%d@."s.sid;rmethod!vinstinstr=letdo_volatile=function|Set(lv,e,loc)asi->beginmatchself#get_volatile_access~is_wr_access:truelvwith|None->leti=matche.enodewith|Lvallv2whene.eid=top_eid->beginmatchself#get_volatile_access~is_wr_access:falselv2with|None->i|Some(rd_fct,_typ)->begin(* lv=lv2; -> lv=rd_fct(&lv2); *)(* To get the varinfo of the new project *)letrd_fct=Visitor_behavior.Memo.varinfoself#behaviorrd_fctinletret_typ,arg1_typ=get_rd_typesrd_fctinletrd_fct=Varrd_fctinletaddr=add_eventual_cast_to_paramarg1_typ(Cil.mkAddrOf~loclv2)inmatchget_cast_type_needed_for_assignation~ret_typ~lvwith|None->Call(Somelv,rd_fct,[addr],loc)|Somenewt->(* In fact a cast has to be added
lv=lv2; -> vtmp=rd_fct(&lv2); lv=(newt) vtmp *)Options.debug~level:2~dkey:dkey_transformation_visit"@[<hov 0> Cast Needed: Lval-type(%a) Return-type (%a)@]"Typ.pretty(Cil.typeOfLvallv)Typ.prettyret_typ;letlvtmp=self#makeTempLvalret_typinletinstr=Call(Somelvtmp,rd_fct,[addr],loc)inself#add_instrinstr;letetmp=Cil.new_exp~loc(Lvallvtmp)inSet(lv,(add_eventual_cast_to_expressionnewtetmp),loc)endend|_->iinself#reset_top_eid();i|Some(wr_fct,_typ)->(* lv=e; -> wr_fct(&lv, e); *)(* To get the varinfo of the new project *)letwr_fct=Visitor_behavior.Memo.varinfoself#behaviorwr_fctinlet_,arg1_typ,arg2_typ=get_wr_typeswr_fctinletwr_fct=Varwr_fctinletaddr=add_eventual_cast_to_paramarg1_typ(Cil.mkAddrOf~loclv)inlete=add_eventual_cast_to_paramarg2_typeinCall(None,wr_fct,[addr;e],loc)end|Call(Somelv,f,a,loc)asi->beginmatchself#get_volatile_access~is_wr_access:truelvwith|None->i|Some(wr_fct,typ)->(* lv=f(a); -> vtmp = f(a); wr_fct(&lv, vtmp); *)(* To get the varinfo of the new project *)letwr_fct=Visitor_behavior.Memo.varinfoself#behaviorwr_fctinlet_,arg1_typ,arg2_typ=get_wr_typeswr_fctinletwr_fct=Varwr_fctinletaddr=add_eventual_cast_to_paramarg1_typ(Cil.mkAddrOf~loclv)inletlvtmp=self#makeTempLval(Ast_types.remove_attributes_for_c_casttyp)inletinstr=Call(Somelvtmp,f,a,loc)inletetmp=add_eventual_cast_to_paramarg2_typ(Cil.new_exp~loc(Lvallvtmp))inself#add_instrinstr;Call(None,wr_fct,[addr;etmp],loc)end|i->iinletdo_call=function|Call(result,ef,xs,loc)asi->beginmatchget_called_ptrefwith|None->i|Somef->lettransform=Visitor_behavior.Memo.varinfoself#behaviorinmatchdo_pointer_call~loc~index~transformfxswith|Some(fn,g,ys)->Options.warning~source:(fstloc)~once:true~wkey:wkey_transformed_call"%a: use pointer function '%s'"Location.prettylocfn;Call(result,g,ys,loc)|None->Options.warning~source:(fstloc)~once:true~wkey:wkey_untransformed_call"Original pointer function kept";iend|i->iinletdo_vinsti=do_volatile(ifcallptrthendo_callielsei)inself#set_top_eidinstr;Cil.ChangeDoChildrenPost([instr],tailrec_list_mapdo_vinst)method!vexpre=letdo_vexpre=matche.enodewith|Lval(lv)whene.eid<>top_eid->begin(* ...lv..;. -> vtmp=rd_fct(&lv); ...vtmp...; *)matchself#get_volatile_access~is_wr_access:falselvwith|None->e|Some(rd_fct,_typ)->(* To get the varinfo of the new project *)letrd_fct=Visitor_behavior.Memo.varinfoself#behaviorrd_fctinletret_typ,arg1_typ=get_rd_typesrd_fctinletloc=matchself#current_kinstrwith|Kstmtstmt->Stmt.locstmt|_->assertfalse(* impossible *)inletrd_fct=Varrd_fctinletaddr=add_eventual_cast_to_paramarg1_typ(Cil.mkAddrOf~loclv)inletret_typ=Ast_types.remove_attributes_for_c_castret_typinletlvtmp=self#makeTempLvalret_typinletinstr=Call(Somelvtmp,rd_fct,[addr],loc)inself#add_instrinstr;add_eventual_cast_to_expression(Cil.typeOfe)(Cil.new_exp~loc(Lvallvtmp))end|CastE(typ,exp)when(matchAst_types.unroll_nodetypwith|TPtrtyp_pointed->not(has_volatile_attrtyp_pointed)|_->false)->beginlettyp_exp=Cil.typeOfexpinmatchAst_types.unroll_nodetyp_expwith|TPtrtyp_pointedwhenAst_types.is_volatiletyp_pointed->Options.warning~source:(fst(Current_loc.get()))~once:true~wkey:wkey_volatile_cast"Cast from type with volatile attribute (%a) to %a. Detection of \
volatile access may fail."Typ.prettytyp_expTyp.prettytyp|_->()end;e|_->einmatche.enodewith|SizeOfE_|AlignOfE_->Cil.JustCopy|_->Cil.ChangeDoChildrenPost(e,do_vexpr)method!vglob_aux=function|GFun(decl,_)->letf=decl.svar.vnameinletfs=Options.Process.get()inifDatatype.String.Set.is_emptyfs||Datatype.String.Set.memffsthenbeginOptions.debug~level:2~dkey:dkey_transformation_visit"Visit DO fun %s@."f;ScopingBlock.reset();Cil.DoChildrenendelsebeginOptions.debug~level:2~dkey:dkey_transformation_visit"Visit COPY fun %s@."f;Cil.JustCopyend|_->Options.debug~level:3~dkey:dkey_transformation_visit"Visit COPY glob@.";Cil.JustCopyendletfind_volatile_accessvol_tbl=Options.feedback~level:2"Building new project with volatile access transformed...@.";letkf_tbl=refNoneinletindex=build_call_index()inletbinding_map=B_MAP.build_binding_map()inlet_fresh_project=FILE.create_project_from_visitor~reorder:trueOptions.plugin_name(funprj->newprocess_volatile_accessprjbinding_mapkf_tblvol_tblindex)inBA_TBL.clear_kf_tablekf_tblletprocess_volatile()=Options.feedback~level:1"Running volatile plugin...@.";let_ast=Ast.get()inletvol_tbl={rd=L_MAP.empty;wr=L_MAP.empty}inOptions.feedback~level:1"Processing volatile clauses...@.";build_volatile_tablevol_tbl;find_volatile_accessvol_tblletprocess_volatile_once,_=State_builder.apply_once"Volatile.process_volatile"[]process_volatileletrun()=ifOptions.Enabled.get()thenprocess_volatile_once()let()=Boot.Main.extendrun