123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2025 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* alternatives) *)(* *)(* you can redistribute it and/or modify it under the terms of the GNU *)(* Lesser General Public License as published by the Free Software *)(* Foundation, version 2.1. *)(* *)(* It is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU Lesser General Public License for more details. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file licenses/LGPLv2.1). *)(* *)(**************************************************************************)openCil_typesopenCil_datatypeopenVisitoropenLocationsopenMt_cilopenMt_memory.TypesopenMt_typesopenMt_shared_vars_typesopenMt_cfg_typesopenMt_thread(* -------------------------------------------------------------------------- *)(* --- Collecting accesses to variables --- *)(* -------------------------------------------------------------------------- *)(* The beginning of this module is essentially a variation on an old version
of Inputs/Outputs, in which we keep a bit more information (typically
locations for writes, or the statements at which the operation took place
for reads *)let()=Ast_attributes.register~ignore:true(AttrNamefalse)"FRAMA_C_MODEL"(* FIXME: Frama-C has removed the attribute FRAMA_C_MODEL from its libc. This
should also probably be removed here. *)(* Skip variables such as __fc_heap_status, __fc_random_counter, etc. *)letis_model_baseb=tryletvi=Base.to_varinfobinAst_attributes.contains"FRAMA_C_MODEL"vi.vattrwithBase.Not_a_C_variable->falseletkeep_baseb=not(Base.is_any_formal_or_localb||Mt_memory.is_frama_c_baseb||is_model_baseb||(Mt_options.IgnoreNull.get()&&Base.(equalbnull)))(* We are only interested in globals, and remove locals, formals, and special
frama-c variables on the fly *)letremove_uninteresting_variables_zonez=Zone.filter_basekeep_basezletremove_uninteresting_variables_locloc=Locations.filter_basekeep_baselocleterror_io_whole_memoryop=letsource=fst(RW.locop)inMt_options.error~source~once:true"@[%a of the whole memory.@ Ignoring to allow Mthread to continue, \
but the analysis will not be correct.@]"RW.prettyopletfilter_inout_memory=letis_mthread_sharedbase=try(* Skip variable "__fc_mthread_shared", as it is only used to prevent
Memexec from caching some functions *)letvi=Base.to_varinfobaseinString.equalvi.vorig_name"__fc_mthread_shared"withBase.Not_a_C_variable->falseinletfilter_basebase=Base.is_globalbase&¬(is_mthread_sharedbase)inInout_memory.mk_filter~filter_baseletread_written_by_thread?(watch_only=Locations.Zone.top)smth=letopenCurrent_loc.Operatorsinletaddstmtopzoneacc=ifLocations.Zone.is_bottomzonethen(* Do nothing *)accelseifLocations.Zone.is_topzonethenlet()=error_io_whole_memoryopinaccelseletzone=remove_uninteresting_variables_zonezoneinletzone=Locations.Zone.narrowzonewatch_onlyinletstate=AccessesByZone.Mapaccinletv=SetStmtIdAccess.inject_singleton(op,stmt,th)inmatchAccessesByZone.add_bindingstate~exact:falsezonevwith|AccessesByZone.Bottom->assertfalse(* state is not Bottom *)|AccessesByZone.Top->assertfalse(* Top is checked above *)|AccessesByZone.Mapm->minInout_memory.fold~filter:filter_inout_memory(funalocmemoryacc->matchalocwith|Global_->(* A Global analysis location represents the initialization state and
is never multithreaded. *)acc|Local(stmt,_)->let<>UpdatedCurrentLoc=Stmt.locstmtinifsmstmtthenacc|>addstmt(ReadAlocaloc)memory.read|>addstmt(WriteAlocaloc)memory.writtenelseacc)AccessesByZone.empty_map(** In global mode, we do a rough analysis using the synthetic results of
Value. In local mode, we supply precise states for each statement of the
function. *)typemode=VLocal|VGlobaltypecollect_params={stmt_multithread:stmt->bool;thread:thread;mode:mode;iter_requests:stmt->(Results.request->unit)->unit;watch_only:Locations.Zone.t;}(* Visitor that collects all reads and assignments done by the functions.
Strongly inspired by inout/{inputs,outputs}.ml. There are some
differences, eg. Outputs.get_internal, which is accessed through Db in
outputs.ml, is here in the class. Moreover we always use the same visitor:
all results are accumulated in the variable [result], instead of being
returned functionally *)classdo_itcp=object(self)inheritVisitor.frama_c_inplaceassupervalmutableresult=AccessesByZone.empty_mapmethodaccesses=result(* Functions already visited. Used to avoid recursion and to prevent adding
results multiple times in [result] *)valvisited=Cil_datatype.Kf.Hashtbl.create17methodprivatemark_visitedkf=Cil_datatype.Kf.Hashtbl.addvisitedkf()methodprivatealready_visitedkf=tryCil_datatype.Kf.Hashtbl.findvisitedkf;truewithNot_found->falsemethodprivateadd_accessopz=ifnot(Locations.Zone.equalLocations.Zone.topz)then(letstmt=self#cur_stmtinifcp.stmt_multithreadstmtthenletinteresting=remove_uninteresting_variables_zonezinletconcurrent=Locations.Zone.narrowinterestingcp.watch_onlyinletstate=AccessesByZone.Mapresultinletv=SetStmtIdAccess.inject_singleton(op,stmt,cp.thread)inmatchAccessesByZone.add_bindingstate~exact:falseconcurrentvwith|AccessesByZone.Bottom->assertfalse(* state is not Bottom *)|AccessesByZone.Top->assertfalse(* Top is checked above *)|AccessesByZone.Mapm->result<-m)elseMt_options.error~current:true~once:true"@[%a@ of@ the@ whole@ memory.@ Ignoring@ to@ allow@ Mthread@ to@ \
continue,@ but@ the@ analysis@ will@ not@ be@ correct.@]"RW.prettyopmethodprivatecur_stmt=matchsuper#current_stmtwith|None->Mt_options.abort"visiting without current statement"|Somes->smethod!vstmt_auxs=ifcp.stmt_multithreadsthenmatchs.skindwith|UnspecifiedSequenceseq->List.iter(fun(stmt,_,_,_,_)->ignore(visitFramacStmt(self:>frama_c_visitor)stmt))seq;Cil.SkipChildren|_->super#vstmt_auxselseCil.DoChildren(* Not an interesting statement, we do not analyse it
deeply *)method!vlvallv=cp.iter_requestsself#cur_stmt(funreq->self#add_accessReadResults.(lval_depslvreq));Cil.SkipChildrenmethodprivatedo_assignlv=cp.iter_requestsself#cur_stmt(funrequest->letdeps=Results.(address_depslvrequest)inself#add_accessReaddeps;letloc=Results.(eval_addresslvrequest|>as_location)inifLocation_Bits.(equalloc.loctop)thenMt_options.warning~current:true~once:true"Problem with %a: its writing location is completely unknown."Printer.pp_lvallv;letloc=remove_uninteresting_variables_loclocinletloc=Locations.(valid_partWriteloc)inletbits_loc=Locations.(enumerate_valid_bitsWriteloc)inself#add_access(Writeloc)bits_loc)methodprivatedo_initvi=letrecauxlv=function|SingleInite->self#do_assignlv;ignore(visitFramacExpr(self:>frama_c_visitor)e)|CompoundInit(ct,initl)->letdoinitoi_()=aux(Cil.addOffsetLvalolv)iinCil.foldLeftCompound~implicit:true~doinit~ct~initl~acc:()inaux(Cil.varv)imethodprivatedo_callexp=cp.iter_requestsself#cur_stmt(funrequest->letdeps=matchexp.enodewith|Lvallv->Results.address_depslvrequest|_->assertfalseinself#add_accessReaddeps;(* In global mode, we treat the recursive calls. In precise
mode, they are done elsewhere in the construction of the cfg *)ifcp.mode=VGlobalthenletcallees=Results.(eval_calleeexprequest|>default[])inList.iterself#rw_funcallees)method!vinsti=letvisit_expre=ignore(visitFramacExpr(self:>frama_c_visitor)e)inifnot(Results.is_reachableself#cur_stmt)thenCil.SkipChildrenelsematchiwith|Set(lv,exp,_)->self#do_assignlv;visit_exprexp;Cil.SkipChildren|Local_init(v,AssignIniti,_)->self#do_initvi;Cil.SkipChildren|Local_init(v,ConsInit(f,args,_),_)->self#do_assign(Cil.varv);self#do_call(Cil.evarf);List.itervisit_exprargs;Cil.SkipChildren|Call(lv_opt,exp,args,_)->Option.iterself#do_assignlv_opt;self#do_callexp;List.itervisit_exprargs;Cil.SkipChildren|_->Cil.DoChildrenmethod!vexprexp=matchexp.enodewith|AddrOflv|StartOflv->cp.iter_requestsself#cur_stmt(funrequest->letdeps=Results.address_depslvrequestinself#add_accessReaddeps;);Cil.SkipChildren|_->Cil.DoChildrenmethodrw_stmtstmt=ignore(visitFramacStmt(self:>frama_c_visitor)stmt)(* Skip assigns to "__fc_mthread_shared" variable, as this variable is
only used to prevent Memexec from caching some functions *)methodprivateassigns_not_mthread=function|WritesAny->WritesAny|Writesl->letaux(t,_)=matcht.it_content.term_nodewith|TLval(TVar{lv_name=name},_)->name<>"__fc_mthread_shared"|_->trueinWrites(List.filterauxl)methodprivatecompute_for_funspeckf=letauxrequest=letstate=Results.get_cvalue_modelrequestinletbehaviors=Logic_inout.valid_behaviorskfstateinletassigns=Ast_info.merge_assignsbehaviorsinletassigns=self#assigns_not_mthreadassignsin(* Compute the zones written by the assigns *)(matchassignswith|WritesAny->lettop=Locations.make_locLocation_Bits.topInt_Base.topinself#add_access(Writetop)Zone.top;|Writesassigns'->letauxl=tryletloc=Eva_results.eval_tlval_as_locationstatelinletloc=remove_uninteresting_variables_loclocinletloc=Locations.(valid_partWriteloc)inletz=Locations.(enumerate_valid_bitsWriteloc)inself#add_access(Writeloc)zwithLogic_to_c.No_conversion->Mt_options.warning~once:true"unsupported@ assigns@ clause@ for@ function %a;@ Ignoring it."Kernel_function.prettykf;inList.iter(fun({it_content=loc},_)->ifnot(Logic_utils.is_resultloc)thenauxloc)assigns');(* Compute the zone read by the assigns *)letread=Logic_inout.assigns_inputs_to_zonestateassignsinself#add_accessReadreadinmatchcp.modewith|VGlobal->letrequests=Results.(at_start_ofkf|>by_callstack|>List.mapsnd)inList.iterauxrequests|VLocal->cp.iter_requestsself#cur_stmtauxmethodrw_funkf=ifnot(self#already_visitedkf)then(self#mark_visitedkf;matchAnalysis.use_spec_instead_of_definitionkf,kf.fundecwith|false,Definition(f,_)->ignore(visitFramacFunction(self:>frama_c_visitor)f)|true,_|_,Declaration_->self#compute_for_funspeckf)endletaux_visitorsmthsawatch_only=letcp={stmt_multithread=sm;thread=th;mode=(matchsawithGlobal->VGlobal|Local_->VLocal);iter_requests=iter_requestssa;watch_only=watch_only;}innewdo_itcpletread_written_by_functionsmthsa?(watch_only=Locations.Zone.top)kfki=letcomp=aux_visitorsmthsawatch_onlyin(* We position the current statement for calls to leaf functions *)(matchkiwith|Kglobal->()|Kstmts->comp#push_stmts);comp#rw_funkf;comp#accesses;;;letvar_thread_created=Mt_cil.mthread_global_var"__fc_mthread_threads_running"(* Ad-hoc function that disregards accesses to variables that
occurs before any thread is created. This simplifies the cfg of threads,
and increases convergence speed *)exceptionStmt_is_multithreadedletstmt_is_multithreadedanalysissa=letiter_requests=iter_requestssainletth=analysis.curr_threadinifThread.is_mainth.th_eva_threadthenletv=var_thread_created()in(funstmt->tryiter_requestsstmt(funrequest->letvalue=Results.(eval_varvrequest|>as_cvalue)inmatchMt_memory.extract_intvaluewith|`Success0->()|_->raiseStmt_is_multithreaded);falsewithStmt_is_multithreaded->true)else(fun_->true)(* -------------------------------------------------------------------------- *)(* --- Computation of variables accessed concurrently by two threads --- *)(* -------------------------------------------------------------------------- *)moduletypeComputer=sigmoduleAccess:Datatype.SmoduleSet:Lattice_type.Lattice_SetwithtypeO.elt=Access.tmoduleZoneMap:Lmap_bitwise.Location_map_bitwisewithtypev=Set.ttypelist_accesses=(Locations.Zone.t*Set.t)listvalpretty_concurrent_accesses:?f:Access.tPretty_utils.formatter->unit->Format.formatter->list_accesses->unitvalall_zones_accessed:list_accesses->Locations.Zone.tvalconcurrent_accesses_all_threads:Mt_thread.ThreadState.tlist->(list_accesses*list_accesses)*ZoneMap.mapend(* All our computations are parameterized by the structure on which we
act: either the information is at the level of the statement (obtained
by the class [do_it] above), or at the level of the cfg node. In the
second case, we use the dataflow information to determine when
two threads are live simultaneously *)moduleAux(X:sigtypeinfomoduleAccess:Datatype.Swithtypet=rw*info*Thread.tmoduleSet:sigincludeLattice_type.Lattice_SetwithtypeO.elt=Access.tvalpretty_aux:Access.tPretty_utils.formatter->tPretty_utils.formatterendmoduleZoneMap:Lmap_bitwise.Location_map_bitwisewithtypev=Set.tvalthread_data:thread_state->ZoneMap.mapvalrunning_concurrently:thp:thread_state->ths:thread_state->infop:info->boolend)=structincludeXopenAbstract_interp(* YYY: this is not the good approach, as a write t[i] = foo with i imprecise
will result in a huge location, instead of a unique location with many
offsets. However, extracting the real location require changes at many
places. Ideally, those locations should be stocked directly the RW
constructor itself. This has been done for W, but not for R. *)letfold_locationfmacc=letmoduleH=Datatype.Integer.Hashtblinletauxbitvsvacc=tryletl=Int_Intervals.project_setitvsinletby_size=H.create4inletaux_itv(ib,ie)=letloc=Location_Bits.injectb(Ival.inject_singletonib)inletsize=Int.succ(Int.subieib)intryletprev=H.findby_sizesizeinletloc=Location_Bits.joinprevlocinH.replaceby_sizesizelocwithNot_found->H.addby_sizesizelocinList.iteraux_itvl;H.fold(funsizelocacc->letloc=Locations.make_locloc(Int_Base.injectsize)inflocvacc)by_sizeaccwithAbstract_interp.Error_Top->letlocb=Location_Bits.injectbIval.zeroinletsize=Int_Base.top(* TODO : use validity *)inletloc=Locations.make_loclocbsizeinflocvaccinX.ZoneMap.fold_base(funbase->X.ZoneMap.LOffset.fold_fuse_same(auxbase))macc(* Given two threads, return a function that tells if two possible concurrent
accesses to a variable need to be considered (ie. if they are really
concurrent wrt. the calling structure of the threads). *)letconsider_vars_accessesth1th2=matchThreadState.one_creates_otherth1th2with|`Unrelated->(* The two threads are independent, so we have no better choice
than to assume that all their variable accesses are concurrent *)(fun__->true)|`Creates(thp,ths)->(* thp creates ths. We should only consider accesses of [thp] that
can occur after [ths] is created, but we do not necessarily
have this information available *)letbeforeinfo=X.running_concurrently~thp~ths~infop:infoinifThreadState.equalthpth1then(fun(_,info,_:X.Access.t)_->beforeinfo)else(fun_(_,info,_:X.Access.t)->beforeinfo);;(* Join two sets of accesses to a same location for two given threads.
The [consider] function must return true when the accesses are possibly
concurrent, ie when the two threads can be live. *)letconcurrent_accesses_setsconsiders1s2=(* We basically do a cartesian product, only removing accesses that
are guaranteed to be non concurrent *)X.Set.fold(funo1acc->X.Set.fold(funo2s->Mt_options.debug~level:2"@[<hov>Possible concurrent accesss@ %a@ and %a@]"X.Access.prettyo1X.Access.prettyo2;letis_concurrent=considero1o2inifis_concurrentthen(Mt_options.debug~level:2"@[Above access is concurrent@]";X.Set.joins(X.Set.join(X.Set.inject_singletono1)(X.Set.inject_singletono2)))else(Mt_options.debug~level:2"@[Above access is not concurrent@]";s))s2acc)s1X.Set.bottom;;(* Compute the concurrent accesses between two threads, by considering
all accesses to the same variable by the two threads, and by
removing those that are not really concurrent (using
[concurrent_accesses_sets] above) *)letconcurrent_accesses_two_threadsth1th2=Mt_options.debug~level:2"Concurrent accessses in threads %a and %a"ThreadState.prettyth1ThreadState.prettyth2;letconsider=consider_vars_accessesth1th2in(* not a global cache: we have a dependency on [Thread.one_creates_other],
which is not a pure function. *)letcache=Hptmap_sig.TemporaryCache"Mt_shared_vars.concurrent_accesses_two_threads"in(* NOT [empty_neutral]: this operation is akin to an intersection. *)letempty_neutral=falsein(* NOT [idempotent]: two accesses at the same statement may fail to interact
if one thread is not yet created. *)letidempotent=falseinletsymmetric=falseinletdecide_fast__=X.ZoneMap.LOffset.Recurseinletmap2=X.ZoneMap.map2~cache~symmetric~idempotent~empty_neutraldecide_fastinmap2(funs1s2->concurrent_accesses_setsconsiders1s2)(X.thread_datath1)(X.thread_datath2);;(* Basic union of two sets accesses to the same variable. We simply
merge the sets *)letbasic_merge_events=letcache=Hptmap_sig.PersistentCache"Mt_shared_vars.basic_merge_events"inletempty_neutral=trueinletidempotent=trueinletsymmetric=trueinletdecide_fast__=X.ZoneMap.LOffset.Recursein(* Partial application is important *)X.ZoneMap.map2~cache~symmetric~idempotent~empty_neutraldecide_fastX.Set.jointypelist_accesses=(Locations.Zone.t*X.Set.t)list(* Compute all the concurrent accesses to all the variables. For each
thread, we consider its possible concurrent accesses with all
the other threads. Algorithmically, there is no need to consider
the accesses between (th1, th2) and (th2, th1), as the relation
is symmetric. Hence we consider only half the cases. *)letconcurrent_accesses_all_threadsall_threads:(list_accesses*list_accesses)*_=letrecauxacc=function|[]->acc|th::thq->letrecaux'acc=function|[]->acc|th'::thq'->letm=concurrent_accesses_two_threadsthth'inaux'(basic_merge_eventsmacc)thq'inaux(aux'accthq)thqinletall=auxX.ZoneMap.empty_mapall_threadsin(* Gather possible data races into two different lists. At this write/write
dataraces are separated from read/write dataraces.
*)X.ZoneMap.fold_fuse_same(funzs((write_write_races,read_write_races)asacc)->letread_access,write_access=X.Set.fold(fun(op,_,_)(read,write)->matchopwith|Read->(true,write)|Write_->(read,true)|ReadAloc_->(true,write)|WriteAloc_->(read,true))s(false,false)inmatchread_access,write_accesswith|false,false->acc(* no access at all, [s] is empty *)|true,false->(* not a race condition *)acc|false,true->(* write/write race *)ifMt_options.WriteWriteRaces.get()then(z,s)::write_write_races,read_write_raceselseacc|true,true->(* read/write race *)write_write_races,(z,s)::read_write_races)all([],[]),allletpretty_concurrent_accesses?(f=(fun_fmt_->()))()fmt(l:list_accesses)=ifl=[]thenFormat.fprintffmt"none"elseFormat.fprintffmt"@[<v 1>%a@]"(Pretty_utils.pp_list~sep:"@ "(funfmt(z,s)->Format.fprintffmt"@[<v 0>%a:@ @[<hov>%a@]@]"Locations.Zone.prettyz(X.Set.pretty_auxf)s))lletall_zones_accessed(l:list_accesses)=letauxacc(z,_)=Locations.Zone.joinzaccinList.fold_leftauxLocations.Zone.bottomlendmoduleGlobal=Aux(structtypeinfo=stmtletthread_datath=th.th_read_writtenmoduleAccess=StmtIdAccessmoduleSet=SetStmtIdAccessmoduleZoneMap=AccessesByZone(* For this analysis, we do not try to find if the two threads run
concurrently. This will be done later through the cfg *)letrunning_concurrently~thp:_~ths:_~infop:_=trueend)modulePrecise=structincludeAux(structtypeinfo=nodeletthread_datath=th.th_read_written_cfgmoduleAccess=NodeIdAccessmoduleSet=SetNodeIdAccessmoduleZoneMap=AccessesByZoneNodeletrunning_concurrently~thp:_~ths~infop=letcontext=infop.cfgn_contextinmatchThreadPresence.findcontext.started_threadsths.th_eva_threadwith|NotPresent->false|MaybePresent|Present->trueend)(* validity should not be [Invalid] *)letdefault_offsetmapvalidity=letsize=Cvalue.V_Offsetmap.size_from_validityvalidityinletsize=Lattice_bounds.Bottom.non_bottomsizeinCvalue.V_Offsetmap.create_isotropic~sizeCvalue.V_Or_Uninitialized.bottomletextract_shared_valuenodeoplocstate=matchloc.sizewith|Int_Base.Top->Mt_options.warning?source:(CfgNode.node_first_locnode)"Ignoring imprecise %a at %a"Mt_types.RW.prettyopLocations.prettyloc;[]|Int_Base.Valuesize->Location_Bits.fold_topset_ok(funbaseoffsacc->letvalidity=Base.validitybaseinifBase.Validity.equalBase.Invalidvaliditythenaccelseletdefault=default_offsetmapvalidityinletv=Cvalue.Model.find~conflate_bottom:falsestatelocinletr=Cvalue.V_Offsetmap.update~validity:(Base.validitybase)~exact:true~offsets:offs~size(Cvalue.V_Or_Uninitialized.C_init_noescv)defaultinmatchrwith|`Bottom->acc|`Valueoffsm->(base,offsm)::acc)loc.loc[]letpp_stackfmtnode=Format.fprintffmt"@ // %a"CfgNode.pretty_stmtsnode;ifMt_options.DumpSharedVarsValues.get()>1thenFormat.fprintffmt"@ %a"Callstack.prettynode.cfgn_stackletpp_access(op,node,th)baseoffsm=ifMt_options.DumpSharedVarsValues.get()>0thenMt_options.result~once:true"@[%a %as @ @[%a%a@]@ %a@]"Thread.prettythMt_types.RW.prettyopBase.prettybase(Cvalue.V_Offsetmap.pretty_generic?typ:(Base.typeofbase)())offsmpp_stacknodeletdisplay_shared_vars_valuem=fold_location(funlocs()->SetNodeIdAccess.fold(fun(op,node,_thidasaccess)()->matchopwith|ReadAloc_|WriteAloc_->Mt_options.not_yet_implemented~current:true~once:true"MtSharedVars.Precise.display_shared_vars_value for ALoc"|Write_->()|Read->letstate=node.cfgn_value_state.state_beforeinletshared=extract_shared_valuenodeoplocstateinList.iter(fun(base,offsm)->pp_accessaccessbaseoffsm)shared)s())m()moduleWriteSeen=Datatype.Triple_with_collections(CfgNode)(Thread)(Locations.Location)letenumerate_written_vars_valuem=letaux_b_itvssacc=letaux_nodes(op,node,thasaccess)(seen,_wrasacc)=matchopwith|ReadAloc_|WriteAloc_->Mt_options.not_yet_implemented~current:true~once:true"MtSharedVars.Precise.enumerate_written_vars_value for ALoc"|Read->acc|Writeloc->ifnot(WriteSeen.Set.mem(node,th,loc)seen)thenletstate=node.cfgn_value_state.state_afterinletshared=extract_shared_valuenodeoplocstateinList.fold_left(fun(seen,wr)(base,offsm)->pp_accessaccessbaseoffsm;letseen=WriteSeen.Set.add(node,th,loc)seenin(seen,(th,base,offsm)::wr))accsharedelseaccinSetNodeIdAccess.foldaux_nodessaccinlet_seen,wr=AccessesByZoneNode.fold_base(funbase->AccessesByZoneNode.LOffset.fold_fuse_same(auxbase))m(WriteSeen.Set.empty,[])inwrletjoin_shared_valuesl=letauxm(_id,base,offsm)=tryletoffsm'=Cvalue.Model.find_basebaseminmatchoffsm'with|`Top->Mt_options.fatal"Top state"|`Bottom->m(* base invalid. Probably impossible case *)|`Valueoffsm'->letjoin=Cvalue.V_Offsetmap.joinoffsmoffsm'inCvalue.Model.add_basebasejoinmwithNot_found->(* from Cvalue.Model.find_base *)Cvalue.Model.add_basebaseoffsmminList.fold_leftauxCvalue.Model.empty_mapl(* Remove from the field [concur_accesses] of cfg nodes the zones
that are not really concurrent. Then flag the node either as
[NotReallySharedVar] or [SharedVarNonConcurrentAccess], depending
on whether some zones remain. *)letremove_non_concur_zones_from_cfgall_zonescfg=letupdate_zonesn=letfiltered=SetZoneAccess.filter(fun(_,z)->not(Locations.Zone.equalLocations.Zone.bottom(Locations.Zone.narrowall_zonesz)))n.cfgn_var_access.concur_accessesinletkind=ifSetZoneAccess.equalfilteredSetZoneAccess.emptythenNotReallySharedVarelseSharedVarNonConcurrentAccess(* for now *)inn.cfgn_var_access<-{concur_accesses=filtered;var_access_kind=kind}inCfgNode.iter~f_before:update_zonescfg;;(* Given a list of zone of accesses that are supposed to be the really
concurrent ones (typically obtained by the function
[concurrent_accesses_all_threads] of this module), mark all the relevant
nodes as containing a really concurrent access *)letmark_concur_access_in_cfgl=letmark_useful(_z,s)=SetNodeIdAccess.iter(fun(_rw,n,_id)->n.cfgn_var_access<-{n.cfgn_var_accesswithvar_access_kind=ConcurrentAccess})sinList.itermark_usefullendletregister_concurrent_var_accessesanalysisstates=letkf=current_funanalysisin(* In the precise computation of shared vars, we prefer to have all accesses
to a shared variable , even if the access itself is not concurrent. Hence
we set [is_multithread] to a function that always return false *)letis_multithreaded=fun_->trueinletki=calling_kianalysisinletsa=matchstateswith|`Finalh->h|`Leafstate->matchkiwith|Kglobal->assertfalse|Kstmts->leth=Stmt.Hashtbl.create1inStmt.Hashtbl.addhsstate;hinletaccesses=read_written_by_functionis_multithreadedanalysis.curr_thread.th_eva_thread(Localsa)~watch_only:analysis.concurrent_accesseskfkiin(* We transform the various accesses into mthread events *)AccessesByZone.fold(funzset()->SetStmtIdAccess.iter(fun(rw,stmt,_id)->lettop=Stack.access_to_varstmtinMt_thread.register_eventanalysis~top(VarAccess(rw,z)))set)accesses()