123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2024 *)(* 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_datatypeopenSimplifiedmoduleEdgeLabel=structtypet=|Pointer(* represents dereferentiation of pointers as well as arrays *)|Fieldoffieldinfoletcomparelr=matchl,rwith|Pointer,Pointer->0|Pointer,Field_->-1|Fieldlv,Fieldrv->Fieldinfo.comparelvrv|Field_,Pointer->1letdefault=Pointerletis_pointer=functionPointer->true|_->falseletis_field=functionField_->true|_->falseletprettyfmt=function|Pointer->()|Fieldf->Format.fprintffmt"-%s"f.fnameendmoduleG=structincludeGraph.Persistent.Digraph.ConcreteBidirectionalLabeled(Datatype.Int)(EdgeLabel)letpsuccgv=letonly_pointer_succe=ifEdgeLabel.is_pointer(E.labele)thenSome(E.dste)elseNoneinList.filter_maponly_pointer_succ(succ_egv)letppredgv=letpe=matchE.labelewithPointer->Some(E.srce)|_->NoneinList.filter_mapp(pred_egv)letfsucc_optgvf=assert(List.for_all(fune->EdgeLabel.is_field@@E.labele)@@succ_egv);letis_field_fe=matchE.labelewith|Fieldf'->Fieldinfo.equalff'|_->falseinletedges=succ_egvinassert(List.length(List.filteris_field_fedges)<=1);Option.mapE.dst@@List.find_optis_field_fedgesletpsucc_optgv=matchpsuccgvwith|[]->None|[v]->Somev|_->Options.fatal"Invariant violated: more than one successor"endtypev=G.V.tmoduleV=G.VmoduleE=structincludeG.EincludeEdgeLabelmoduleMap=Stdlib.Map.Make(structtypet=EdgeLabel.tletcompare=EdgeLabel.compareend)endmoduleVMap=Datatype.Int.MapmoduleVSet=Datatype.Int.SetmoduleLSet=Cil_datatype.LvalStructEq.SetmoduleVarSet=Cil_datatype.Varinfo.SetmoduleVarMap=structincludeCil_datatype.Varinfo.Mapletintersect=merge@@fun_lr->matchl,rwith|Somel,Somer->Some(l,r)|_->Noneletpretty=letmoduleM=Make(Datatype.Int)inM.prettyendtypestate={graph:G.t;vmap:VarSet.tVMap.t;(* associate with each node a set of variables *)varmap:V.tVarMap.t(* reverse of varmap *)}letnode_counter=ref0letfresh_node_id()=letid=!node_counterinnode_counter:=!node_counter+1;idletget_varsvs:VarSet.t=tryVMap.findvs.vmapwithNot_found->VarSet.empty(* raises Not_found *)letrecfind_lval_vertex((lhost,offset):lval)s:V.t=letfind_psuccv=matchG.psucc_opts.graphvwithSomev->v|_->raiseNot_foundinletfind_fsuccvfname=matchG.fsucc_opts.graphvfnamewithSomev->v|_->raiseNot_foundinletfind_lhost=function|Varvar->VarMap.findvars.varmap|Meme->matchLvalOrRef.from_expewith|None->Options.fatal"unexpected result: Lval.from (%a) = None"Exp.prettye|Some(LvalOrRef.Reflv1)->find_lval_vertexlv1s|Some(LvalOrRef.Lvallv1)->letv1=find_lval_vertexlv1sinfind_psuccv1inletrecfind_offsetv=function|NoOffset->v|Index(_,o)->letv'=find_psuccvinfind_offsetv'o|Field(f,o)->letv'=find_fsuccvfinfind_offsetv'oinlethv=find_lhostlhostinfind_offsethvoffsetmoduleReadout=struct(* Reconstruct all lvals that are represented by the given node.
Nodes only carry varinfos. In order to obtain lvals we recursively walk
backwards in the graph inductively constructing lvals from scratch.
- The lvals of the current node are the stored varinfos themselves.
- After gathering the lvals for the predecessor, the lvals are
modified according to the edge type used:
* Pointer: add a star (x → *x)
* Field f: add an offset (x -> x.f) *)letget_lval_setvs:LSet.t=assert(G.mem_vertexs.graphv);(* cycles can occur with unsafe casts such as: x->f = (int* ) x; *)letrecchecking_for_cyclessvisitedv=ifVSet.memvvisitedthenlet()=Options.warning~once:true~wkey:Options.Warn.incoherent"cycle during readout of vertex %d, \
(following unsafe cast?); analysis may be unsound"vinLSet.emptyelseletvisited=VSet.addvvisitedinletmodified_predecessors=List.map(fune->letpred_lvals=checking_for_cyclessvisited@@E.srceinletmodify_lvallv=matchE.labelewith|Fieldf->Cil.addOffsetLval(Field(f,NoOffset))lv|Pointer->(* TODO: This Cil.typeOfLval may crash with a fatal kernel
error for certain reconstructed lvals involving a union
type. See tests/known_bugs/union_readback.c *)letty=Cil.typeOfLvallvinifCil.isArrayTypetythenCil.addOffsetLval(Index(Simplified.nul_exp,NoOffset))lvelselet()=ifnot@@Cil.isPointerTypetythenOptions.debug"unexpected type: %a"Printer.pp_typtyinMem(Cil.dummy_exp@@Lvallv),NoOffsetinLSet.mapmodify_lvalpred_lvals)(G.pred_es.graphv)inletlvals_of_v=letmk_lvalvar=(Varvar),NoOffsetinLSet.of_seq@@Seq.mapmk_lval@@VarSet.to_seq@@get_varsvsinList.fold_leftLSet.unionlvals_of_vmodified_predecessorsinchecking_for_cyclessVSet.emptyvletlvals_pointing_to_vertexvs:LSet.t=assert(G.mem_vertexs.graphv);letlist_pred=List.map(funb->get_lval_setbs)(G.ppreds.graphv)inList.fold_leftLSet.unionLSet.emptylist_predletvars_pointing_to_vertexvs:VarSet.t=letpreds=G.ppreds.graphvinletpred_vars=List.map(funp->get_varsps)predsinList.fold_leftVarSet.unionVarSet.emptypred_varsletfind_varslvs=letlv=Lval.simplifylvintryletv=find_lval_vertexlvsinget_varsvswithNot_found->VarSet.emptyletfind_synonymslvs=letlv=Lval.simplifylvintryletv=find_lval_vertexlvsinget_lval_setvswithNot_found->LSet.emptyletalias_varslvs:VarSet.t=tryletv=find_lval_vertexlvsinList.fold_left(funaccsucc->VarSet.unionacc@@vars_pointing_to_vertexsuccs)VarSet.empty(G.psuccs.graphv)withNot_found->VarSet.emptyletalias_lvalslvs:LSet.t=letv_opt=trySome(find_lval_vertexlvs)withNot_found->NoneinmatchOption.bindv_opt@@G.psucc_opts.graphwith|None->LSet.empty|Somesucc->lvals_pointing_to_vertexsuccsletpoints_to_varslvs:VarSet.t=letsucc=tryG.psucc_opts.graph@@find_lval_vertexlvswithNot_found->Noneinmatchsuccwith|None->VarSet.empty|Somesucc_v->get_varssucc_vsletpoints_to_lvalslvs:LSet.t=letsucc=tryG.psucc_opts.graph@@find_lval_vertexlvswithNot_found->Noneinmatchsuccwith|None->LSet.empty|Somesucc_v->get_lval_setsucc_vsletalias_sets_varss=letalias_set_of_vertex(i,_)=letaliases=vars_pointing_to_vertexisinifVarSet.cardinalaliases>=2thenSomealiaseselseNoneinList.filter_mapalias_set_of_vertex@@VMap.bindingss.vmapletalias_sets_lvalss=letalias_set_of_vertex(i,_)=letaliases=lvals_pointing_to_vertexisinifLSet.cardinalaliases>=2thenSomealiaseselseNoneinList.filter_mapalias_set_of_vertex@@VMap.bindingss.vmapendmodulePretty=structletpp_debugfmts=Format.fprintffmt"@[<v>";Format.fprintffmt"@[Edges:";G.iter_edges_e(fune->Format.fprintffmt"@;<3 2>@[%d@ @[%a→@]@ %d@]"(E.srce)E.pretty(E.labele)(E.dste))s.graph;Format.fprintffmt"@]@;<6>";Format.fprintffmt"@[VarMap:@;<3 2>";VarMap.prettyfmts.varmap;Format.fprintffmt"@]@;<6>";Format.fprintffmt"@[VMap:@;<2>";VMap.iter(funvls->Format.fprintffmt"@;<2 2>@[%d:%a@]"vVarSet.prettyls)s.vmap;Format.fprintffmt"@]";Format.fprintffmt"@]"letpp_graphfmts=letis_first=reftrueinletpp_nodevfmtlset=Format.fprintffmt"%d:%a"vVarSet.prettylsetinletpp_edgee=letv1=E.srceandv2=E.dsteinif!is_firstthenis_first:=falseelseFormat.fprintffmt"@;<3>";Format.fprintffmt"@[%a@] %a→ @[%a@]"(pp_nodev1)(VMap.findv1s.vmap)E.pretty(E.labele)(pp_nodev2)(VMap.findv2s.vmap)inletpp_unconnected_vertexv=ifG.in_degrees.graphv=0&&G.out_degrees.graphv=0thenbeginif!is_firstthenis_first:=falseelseFormat.fprintffmt"@;<3>";pp_nodevfmt(VMap.findvs.vmap)endinifG.nb_vertexs.graph=0thenFormat.fprintffmt"<empty>"else(G.iter_edges_epp_edges.graph;G.iter_vertexpp_unconnected_vertexs.graph)letpp_aliasesfmts=letalias_sets=Readout.alias_sets_lvalssinPretty_utils.pp_list~empty:"<none>"~sep:"@;<2>"LSet.prettyfmtalias_setsend(* invariants of type t must be true before and after each functon call *)letassert_invariantss:unit=(* check that all vertex of the graph have entries in vmap,
and are integer between 0 and node_counter, and have at most 1 successor *)assert(!node_counter>=0);letassert_vertexv=Options.debug~level:11"checking coherence of vertex %d"v;assert(v>=0);assert(v<!node_counter);assert(VMap.memvs.vmap);letsucc_e=G.succ_es.graphvinletis_pointer_vertex=List.exists(fune->E.is_pointer@@E.labele)succ_eandis_struct_vertex=List.exists(fune->E.is_field@@E.labele)succ_einassert(not(is_pointer_vertex&&is_struct_vertex));assert(notis_pointer_vertex||List.length(G.succs.graphv)<=1);inG.iter_vertexassert_vertexs.graph;letassert_edgev1v2=Options.debug~level:11"checking coherence of edge %d → %d"v1v2;ifv1=v2thenOptions.warning~once:true~wkey:Options.Warn.incoherent"loop on vertex %d (following unsafe cast?); analysis may be unsound"v1;assert(G.mem_vertexs.graphv1);assert(G.mem_vertexs.graphv2)inG.iter_edgesassert_edges.graph;letassert_varmap(var:varinfo)v=assert(G.mem_vertexs.graphv);assert(VarSet.memvar(VMap.findvs.vmap))inVarMap.iterassert_varmaps.varmap;letassert_vmapv(ls:VarSet.t)=assert(G.mem_vertexs.graphv);(* TODO: we removed the invariant because of OSCS*)(* if not (VarSet.is_empty ls)
* then
* begin
* let lv = VarSet.choose ls in
* let is_ptr_lv = Lval.is_pointer lv in
* assert (VarSet.for_all (fun x -> Lval.is_pointer x = is_ptr_lv) ls)
* end; *)assert(VarSet.fold(funlvacc->acc&&V.equal(VarMap.findlvs.varmap)v)lstrue)inVMap.iterassert_vmaps.vmap(* Ensure that assert_invariants is not executed if the -noassert flag is supplied. *)letassert_invariantss=tryassert(assert_invariantss;true)withAssert_failure_asexn->letbt=Printexc.get_raw_backtrace()inOptions.debug"incoherent graph:@ @[%a@]"Pretty.pp_debugs;Options.debug"incoherent graph:@ @[%a@]"Pretty.pp_graphs;Printexc.raise_with_backtraceexnbtletasserting_invariantss=assert_invariantss;sletpretty?(debug=false)fmts=assert_invariantss;ifdebugthenPretty.pp_graphfmtselsePretty.pp_aliasesfmts(* NOTE on "constant vertex": a constant vertex represents an unamed
scalar value (type bottom in steensgaard's paper), or the address
of a variable. It means that in [vmap], its associated VarSet is
empty. By definition, constant vertex cannot be associated to a
lval in [varmap] *)letcreate_empty_vertexs:V.t*state=letnew_v=fresh_node_id()innew_v,{graph=G.add_vertexs.graphnew_v;varmap=s.varmap;vmap=VMap.addnew_vVarSet.emptys.vmap}letcreate_var_vertexvars=assert(not@@VarMap.memvars.varmap);letv=fresh_node_id()inlets={graph=G.add_vertexs.graphv;varmap=VarMap.addvarvs.varmap;vmap=VMap.addv(VarSet.singletonvar)s.vmap}inletreccreate_typ_vertexsvty=matchtywith|TArray(ty,_,_)|TPtr(ty,_)->(* create more vertices for each level of dereferentiation *)letv',s=create_empty_vertexsinlets={swithgraph=G.add_edges.graphvv'}increate_typ_vertexsv'ty|_->s(* until the type becomes scalar *)inv,create_typ_vertexsvvar.vtypeletfind_or_create_var_vertex(var:varinfo)s=tryVarMap.findvars.varmap,swithNot_found->create_var_vertexvarsletrecfind_or_create_lval_vertex((lhost,offset):lval)s:V.t*state=letfind_or_create_psuccvs=matchG.psucc_opts.graphvwith|None->letv',s=create_empty_vertexsin(* finally add a points-to edge between v and v' *)letnew_graph=G.add_edges.graphvv'inv',{swithgraph=new_graph}|Somev'->v',sinletfind_or_create_fsuccvsf=matchG.fsucc_opts.graphvfwith|None->letv',s=create_empty_vertexsin(* finally add a points-to edge between v and v' *)letnew_graph=G.add_edge_es.graph@@E.createv(Fieldf)v'inv',{swithgraph=new_graph}|Somev'->v',sinletfind_or_create_lhosts=function|Varvar->find_or_create_var_vertexvars|Meme->matchLvalOrRef.from_expewith|None->Options.fatal"unexpected result: Lval.from (%a) = None"Exp.prettye|Some(LvalOrRef.Reflv1)->find_or_create_lval_vertexlv1s|Some(LvalOrRef.Lvallv1)->letv1,s=find_or_create_lval_vertexlv1sinfind_or_create_psuccv1sinletrecfind_or_create_offsetvs=function|NoOffset->v,s|Index(_,o)->letv',s=find_or_create_psuccvsinfind_or_create_offsetv'so|Field(f,o)->letv',s=find_or_create_fsuccvsfinfind_or_create_offsetv'soinlethv,s=find_or_create_lhostslhostinletv,s=find_or_create_offsethvsoffsetinOptions.debug~level:7"graph after find_or_create_lval_vertex @[%a@] (%d):@ %a"Printer.pp_lval(lhost,offset)vPretty.pp_graphs;v,sandfind_or_create_ref_vertexlvs:V.t*state=letv1,s=find_or_create_lval_vertexlvsinletva,s=create_empty_vertexsinlets={swithgraph=G.add_edges.graphvav1}inOptions.debug~level:7"graph after find_or_create_ref_vertex @[%a@] (%d):@ %a"LvalOrRef.pretty(LvalOrRef.Reflv)vaPretty.pp_graphs;va,sandfind_or_create_lval_or_ref_vertex(lv:LvalOrRef.t)s:V.t*state=matchlvwith|LvalOrRef.Lvallv->find_or_create_lval_vertexlvs|LvalOrRef.Reflv->find_or_create_ref_vertexlvs(* TODO is there a better way to do it ? *)letfind_vertexlvs=letlv=Lval.simplifylvinletv,x1=find_or_create_lval_vertexlvsinifs==x1thenv(* if s has not been modified, then the vertex was found, not created *)elseraiseNot_found(* merge of two vertices; the first vertex carries both sets, the second is
removed from the graph and from varmap and vmap *)letmergesv1v2=ifV.equalv1v2||not(G.mem_vertexs.graphv1)||not(G.mem_vertexs.graphv2)thenselse(* update varmap : every lval in v2 must now be associated with v1 *)letnew_varmap=VarSet.fold(funlv2->VarMap.addlv2v1)(get_varsv2s)s.varmapinletnew_vmap=letnew_set=VarSet.union(get_varsv1s)(get_varsv2s)inVMap.addv1new_set@@VMap.removev2s.vmapinletnew_graph=(* update the graph *)letf_fold_succeg:G.t=G.add_edge_eg@@E.createv1(E.labele)(E.dste)andf_fold_predeg:G.t=G.add_edge_eg@@E.create(E.srce)(E.labele)v1inletg=s.graphin(* add all new edges *)letg=G.fold_succ_ef_fold_succgv2ginletg=G.fold_pred_ef_fold_predgv2ginG.remove_vertexgv2(* remove v2 *)in{graph=new_graph;varmap=new_varmap;vmap=new_vmap}(* functions join and unify-pointer of steensgaard's paper *)(* join_without_check may break the invariants *)letrecjoin_without_checksv1v2:state=ifV.equalv1v2||not(G.mem_vertexs.graphv1&&G.mem_vertexs.graphv2)thenselseletmk_edge_mapsuccs=letmk_succe=E.labele,E.dsteinE.Map.of_seq@@Seq.mapmk_succ@@List.to_seqsuccsinletsuccs1=mk_edge_map@@G.succ_es.graphv1inletsuccs2=mk_edge_map@@G.succ_es.graphv2inletsucc_pairs=letmk_pair_succ1succ2=matchsucc1,succ2with|Somes1,Somes2->Some(s1,s2)|_->NoneinE.Map.mergemk_pairsuccs1succs2inlets=mergesv1v2inassert(not(G.mem_vertexs.graphv2));letmerge_succs_(succ1,succ2)s=assert(succ1<>v2);assert(succ2<>v1);join_without_checkssucc1succ2inE.Map.foldmerge_succssucc_pairssletjoinsv1v2:state=Options.debug~level:6"graph before join(%d,%d):@;<2>@[%a@]"v1v2Pretty.pp_graphs;assert_invariantss;letres=join_without_checksv1v2inOptions.debug~level:6"graph after join(%d,%d):@;<2>@[%a@]"v1v2Pretty.pp_graphres;begintryassert_invariantsreswithAssert_failure_->Options.debug"join(%d,%d) failed"v1v2;Options.debug"graph before join(%d,%d):@;<2>@[%a@]"v1v2Pretty.pp_debugs;Options.debug"graph after join(%d,%d):@;<2>@[ %a@]"v1v2Pretty.pp_debugres;assert_invariantsresend;resletmerge_sets(vs:VSet.t):V.t*state=letv0=VSet.choosevsinifVSet.cardinalvs<2thenv0,selsebeginOptions.debug~level:6"graph before merge_set %a:@;<2>@[%a@]"VSet.prettyvsPretty.pp_debugs;assert(G.mem_vertexs.graphv0);letresult=VSet.fold(funvacc->mergeaccv0v)vssinOptions.debug~level:6"graph after merge_set %a:@;<2>@[%a@]"VSet.prettyvsPretty.pp_debugresult;v0,resultend(* may operate on an unsound state, where nodes may have multiple successors
of the same edge type *)letrecjoin_succssv=Options.debug~level:8"joining successors of %d"v;ifnot@@G.mem_vertexs.graphvthenselseletedge_map=List.fold_left(funme->letadd_dst=function|None->Some(VSet.singleton@@E.dste)|Somevs->Some(VSet.add(E.dste)vs)inE.Map.update(E.labele)add_dstm)E.Map.empty(G.succ_es.graphv)inletmerge_vset_evss=ifVSet.cardinalvs<2thenselseletv0,s=merge_setsvsinjoin_succssv0inE.Map.foldmerge_vsetedge_maps(* in Steensgard's paper, this is written settype(v1,ref(v2,bot)) *)letset_typesv1v2:state=assert_invariantss;(* if v1 points to another node, suppress current outgoing edge (and the node if it is a constant node) *)letg,new_vmap=matchG.psucc_opts.graphv1with|None->s.graph,s.vmap|Somev2->(* if v2 is a constant node supress it directly *)ifVarSet.is_empty(VMap.findv2s.vmap)thenG.remove_vertexs.graphv2,VMap.removev2s.vmapelseG.remove_edges.graphv1v2,s.vmapinletnew_g=G.add_edgegv1v2inasserting_invariants{swithgraph=new_g;vmap=new_vmap}letassignmentslv(e:exp):state=assert_invariantss;matchCil.isPointerType(Cil.typeOfe),LvalOrRef.from_expewith|false,_|_,None->s|true,Somey->letv1,s=find_or_create_lval_vertex(Lval.simplifylv)sinletv2,s=find_or_create_lval_or_ref_vertexysinifList.memv2(G.psuccs.graphv1)||List.memv1(G.psuccs.graphv2)thenlet()=Options.warning~source:(fste.eloc)"ignoring assignment of the form: %a = %a"Printer.pp_lvallvPrinter.pp_expe;inselseasserting_invariants@@joinsv1v2(* assignment x = allocate(y) *)letassignment_x_allocate_yslv:state=assert_invariantss;letv1,s=find_or_create_lval_vertex(Lval.simplifylv)sinmatchG.psucc_opts.graphv1with|None->letv2,s=create_empty_vertexsinset_typesv1v2|Some_->sletis_includedss'=(* tests if s is included in s', at least as the nodes with lval *)assert_invariantss;assert_invariantss';Options.debug~level:8"testing equal %a AND à.%a"Pretty.pp_graphs(pretty~debug:true)s';letexceptionNot_includedintryletiter_varmap(var:varinfo)v:unit=letv'=tryVarMap.findvars'.varmapwithNot_found->raiseNot_includedin(* TODO: render correct for structs *)letsuccs=E.Map.of_seq@@Seq.map(fune->E.labele,E.dste)@@List.to_seq@@G.succ_es.graphvandsuccs'=E.Map.of_seq@@Seq.map(fune->E.labele,E.dste)@@List.to_seq@@G.succ_es'.graphv'inletcheck_succs_succ1succ2=matchsucc1,succ2with|None,_->None|Some_,None->raiseNot_included|Somev1p,Somev2p->ifVarSet.subset(VMap.findv1ps.vmap)(VMap.findv2ps'.vmap)thenNoneelseraiseNot_includedinignore@@E.Map.mergecheck_succssuccssuccs'inVarMap.iteriter_varmaps.varmap;truewithNot_included->falseletempty:state={graph=G.empty;varmap=VarMap.empty;vmap=VMap.empty}letis_emptys=comparesempty=0(* add an int to all vertex values *)letshifts:state=assert_invariantss;ifis_emptysthenselsebeginOptions.debug~level:8"before shift: node_counter=%d@.%a"!node_counterPretty.pp_debugs;letmax_idx=G.fold_vertexmaxs.graph0inletmin_idx=G.fold_vertexmins.graphmax_idxinletoffset=!node_counter-min_idxinletshiftx=x+offsetinletshift_vmapshift_elemvmap=VMap.of_seq@@Stdlib.Seq.mapshift_elem@@VMap.to_seqvmapinlet{graph;varmap;vmap}=sinnode_counter:=max_idx+offset+1;letresult={graph=G.map_vertexshiftgraph;varmap=VarMap.mapshiftvarmap;vmap=shift_vmap(fun(key,l)->shiftkey,l)vmap}inOptions.debug~level:8"after shift: node_counter=%d@.%a"!node_counterPretty.pp_debugresult;asserting_invariantsresultendletunion_findvmapintersections=letmoduleStore:UnionFind.STORE=UnionFind.StoreMap.Make(VMap)inletmoduleUF=UnionFind.Make(Store)inletuf=UF.new_store()inletrefs=VMap.mapi(funi_->UF.makeufi)vmapinletput_into_uf(v1,v2)=letr1=VMap.findv1refsinletr2=VMap.findv2refsinignore@@UF.unionufr1r2inlet_vs=Seq.iterput_into_ufintersectionsinletsets_to_be_joined=letadd_to_mapirsets=letrepr=UF.findufrinletadd_to_set=function|None->Some(VSet.singletoni)|Someset->Some(VSet.addiset)inVMap.update(UF.getufrepr)add_to_setsetsinVMap.foldadd_to_maprefsVMap.emptyinsets_to_be_joinedletunions1s2:state=assert_invariantss1;assert_invariantss2;Options.debug~level:4"Union: First graph:%a"Pretty.pp_graphs1;Options.debug~level:5"Union: First graph:%a"Pretty.pp_debugs1;Options.debug~level:4"Union: Second graph:%a"Pretty.pp_graphs2;Options.debug~level:5"Union: Second graph:%a"Pretty.pp_debugs2;letnew_graph=G.fold_vertex(funv2g->G.add_vertexgv2)s2.graphs1.graphinletnew_graph=G.fold_edges_e(funeg->G.add_edge_ege)s2.graphnew_graphinletnew_vmap=VMap.union(fun_lset1lset2->Option.some@@VarSet.unionlset1lset2)s2.vmaps1.vmapinletsets_to_be_joined=letintersections=VarMap.to_seq@@VarMap.intersects1.varmaps2.varmapinunion_findnew_vmap@@Seq.mapsndintersectionsinletnew_varmap=VarMap.union(fun_l_r->Somel)s1.varmaps2.varmapinOptions.debug~level:7"Union: sets to be joined:@[";VMap.iter(fun_set->Options.debug~level:7"%a"VSet.prettyset)sets_to_be_joined;Options.debug~level:7"@]";lets={graph=new_graph;varmap=new_varmap;vmap=new_vmap}inletmerged_nodes,s=VMap.fold(fun_set(merged_nodes,s)->letv0,s=merge_setssetin(v0::merged_nodes),s)sets_to_be_joined([],s)inlets=List.fold_leftjoin_succssmerged_nodesinOptions.debug~level:4"Union: Result graph:%a"Pretty.pp_graphs;Options.debug~level:5"Union: Result graph:%a"Pretty.pp_debugs;begintryassert_invariantsswithAssert_failure_->Options.debug"union failed";Options.debug"Union: First graph:%a"Pretty.pp_graphs1;Options.debug"Union: First graph:%a"Pretty.pp_debugs1;Options.debug"Union: Second graph:%a"Pretty.pp_graphs2;Options.debug"Union: Second graph:%a"Pretty.pp_debugs2;Options.debug"Union: Result graph:%a"Pretty.pp_graphs;Options.debug"Union: Result graph:%a"Pretty.pp_debugs;assert_invariantssend;smoduleSummary=struct(* a type for summaries of functions *)typet={state:stateoption;formals:lvallist;return:expoption}letmakes(kf:kernel_function)=letexp_return:expoption=ifKernel_function.has_definitionkfthenletreturn_stmt=Kernel_function.find_returnkfinmatchreturn_stmt.skindwith|Return(e,_)->e|_->Options.fatal"this should not happen"elseNoneinlets=matchexp_returnwith|None->s|Somee->beginmatchs,LvalOrRef.from_expewith|_,None->s|s,Somelv->let_,new_s=find_or_create_lval_or_ref_vertexlvsinnew_sendin{state=Somes;formals=List.map(funv->(Varv,NoOffset))(Kernel_function.get_formalskf);return=exp_return}letpretty?(debug=false)fmtsummary=letpp_list_lvalsfmt(l:lvallist)=letis_first=reftrueinletpp_elemlv=if!is_firstthenis_first:=falseelseFormat.fprintffmt"@ ";Format.fprintffmt"@[%a"Cil_datatype.Lval.prettylv;letpointees=Readout.points_to_varslvsinifnot@@VarSet.is_emptypointeesthenFormat.fprintffmt"→%a"VarSet.prettypointees;Format.fprintffmt"@]";inList.iterpp_elemlinletpp_optionppfmt=function|Somex->ppfmtx|None->Format.fprintffmt"<none>"inmatchsummary.statewith|None->ifdebugthenFormat.fprintffmt"not found"|Someswhenis_emptys->ifdebugthenFormat.fprintffmt"empty"|Somes->Format.fprintffmt"@[formals: @[%a@]@;<4>returns: @[%a@]@;<4>state: @[%a@] "(pp_list_lvals)summary.formals(pp_optionExp.pretty)summary.return(pp_option@@pretty~debug)summary.stateend(* the algorithm:
- unify the two graphs dropping all the variables from the summary
- pair arguments with formals assigning the formal's successor as the argument's successor
*)letcalls(res:lvaloption)(args:explist)(summary:Summary.t):state=assert_invariantss;letformals=summary.Summary.formalsinassert(List.lengthargs=List.lengthformals);letsum_state=shift@@Option.getsummary.statein(* pair up formals and their corresponding arguments,
as well as the bound result with the returned value *)letarg_formal_pairs=letres_ret=matchres,summary.returnwith|None,None->[]|Someres,Someret->letsimplify_retx=matchLvalOrRef.from_expxwith|Some(LvalOrRef.Lvallval)->lval|_->Options.fatal"unexpected form of return statement"in[LvalOrRef.Lval(Lval.simplifyres),simplify_retret]|None,Some_->[]|Some_,None->(* Shouldn't happen: Frama-C adds missing returns *)Options.fatal"unexpected case: result without return"inletsimplify_both(arg,formal)=trymatchLvalOrRef.from_expargwith|None->None|Somelv->Some(lv,Lval.simplifyformal)withExplicit_pointer_addressloc->Options.warning~source:(fstloc)~wkey:Options.Warn.unsupported_address"unsupported feature: explicit pointer address: %a; analysis may be unsound"Printer.pp_exparg;Noneinres_ret@List.filter_mapsimplify_both@@List.combineargsformalsin(* for each pair (lv1,lv2) find (or create) the corresponding vertices *)lets,vertex_pairs=lets=refsinletfind_vertex(lv1,lv2)=tryletv2=find_lval_vertexlv2sum_stateinletv1,new_state=find_or_create_lval_or_ref_vertexlv1!sins:=new_state;Some(v1,v2)withNot_found->Nonein!s,List.filter_mapfind_vertexarg_formal_pairsin(* merge the function graph;
for every arg/formal vertex pair (v1,v2) and every edge v2→v create edge v1→v. *)letg=lettransfer_succsg(v1,v2)=List.fold_left(funge->G.add_edge_eg@@E.createv1(E.labele)(E.dste))g(G.succ_esum_state.graphv2)inletg=s.graphinletg=G.fold_vertex(funig->G.add_vertexgi)sum_state.graphginletg=G.fold_edges_e(funeg->G.add_edge_ege)sum_state.graphginList.fold_lefttransfer_succsgvertex_pairsin(* garbage collect: remove leaf vertices from g that originate from sum_state *)letvertices_to_add_to_g,g=letg=refginletremove_if_leafv_=ifG.in_degree!gv=0thenlet()=g:=G.remove_vertex!gvinNoneelseSomeVarSet.emptyinletremaining_vertices=VMap.filter_mapremove_if_leafsum_state.vmapinremaining_vertices,!ginlets={graph=g;varmap=s.varmap;vmap=letleft_bias_l_=SomelinVMap.unionleft_biass.vmapvertices_to_add_to_g}inasserting_invariants(List.fold_leftjoin_succss@@List.mapfstvertex_pairs)moduleDot=structletfind_vars_ref=Extlib.mk_fun"find_vars"includeGraph.Graphviz.Dot(structincludeGletedge_attributes_=[]letdefault_edge_attributes_=[]letget_subgraph_=Noneletvertex_attributesv=letlset=!find_vars_refvinletlabel=VarSet.prettyFormat.str_formatterlset;Format.flush_str_formatter()in[`Labellabel]letvertex_namev=string_of_intvletdefault_vertex_attributes_=[`Shape`Box]letgraph_attributes_=[]end)endmoduleAPI=structtypet=statetypesummary=Summary.tletpretty_summary=Summary.prettyletmake_summary=Summary.makeletvidv:int=vletrecclosure_find_lsetvs:(V.t*LSet.t)list=matchG.psucc_opts.graphvwith|None->[v,Readout.get_lval_setvs]|Somev_next->(v,Readout.get_lval_setvs)::closure_find_lsetv_nextsletfind_transitive_closurelvs:(V.t*LSet.t)list=letlv=Lval.simplifylvinassert_invariantss;tryclosure_find_lset(find_lval_vertexlvs)swithNot_found->[](* TODO : what about offsets ? *)letget_lval_set=Readout.get_lval_setletfind_vars=Readout.find_varsletfind_synonyms=Readout.find_synonymsletfind_aliases=Readout.find_synonymsletalias_vars=Readout.alias_varsletalias_lvals=Readout.alias_lvalsletfind_all_aliases=Readout.alias_lvals(* deprecated *)letpoints_to_vars=Readout.points_to_varsletpoints_to_set=Readout.points_to_lvalsletpoints_to_lvals=Readout.points_to_lvalsletalias_sets_vars=Readout.alias_sets_varsletalias_sets_lvals=Readout.alias_sets_lvalsletget_graphs=s.graphletprint_dotfilenames=letfile=open_outfilenameinDot.find_vars_ref:=(funv->get_varsvs);Dot.output_graphfiles.graph;close_outfileendincludeAPI