123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2023 *)(* 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_datatypeopenSimplifiedmoduleVSet=Datatype.Int.SetmoduleVMap=Datatype.Int.MapmoduleLval=Simplified.LvalmoduleLSet=Cil_datatype.LvalStructEq.SetmoduleLMap=Cil_datatype.LvalStructEq.MapmoduleG=Graph.Persistent.Digraph.ConcreteBidirectional(Datatype.Int)moduleV=G.Vletvid(v:G.V.t):int=v(* like LMap, but organized with offset and specialized functions *)moduleLLMap=structmoduleOMap=Offset.Map(* each t is a map (lhost,NoOffset) -> offset -> V.t *)typet=(V.tOMap.t)LMap.tletempty:t=LMap.emptyletmem(lv:lval)(m:t)=letlv,off=Cil.removeOffsetLvallvintryOMap.memoff(LMap.findlvm)withNot_found->falseletfind(lv:lval)(m:t):V.t=letlv,off=Cil.removeOffsetLvallvinOMap.findoff(LMap.findlvm)letadd(lv:lval)(v:V.t)(m:t):t=letlv,off=Cil.removeOffsetLvallvinletmo=tryLMap.findlvmwithNot_found->OMap.emptyinLMap.addlv(OMap.addoffvmo)mletiterf=LMap.iter@@funlv->OMap.iter@@funo->f@@Cil.addOffsetLvalolvletmapf=LMap.map@@OMap.mapfletprettyfmt=letis_first=reftrueinLMap.iter(funlv->OMap.iter(funov->letlv=Cil.addOffsetLvalolvinif!is_firstthenis_first:=falseelseFormat.fprintffmt"@;<3>";Format.fprintffmt"@ @[%a:%d@]"Lval.prettylvv))(* left-biased *)letunion=LMap.union@@fun_lr->Some(OMap.union(fun_l_r->Somel)lr)letintersect=letintersect_omaplr=matchl,rwith|Somel,Somer->Some(l,r)|_->Noneinletintersect_lmaplr=matchl,rwith|Somel,Somer->letomap=OMap.merge(fun_->intersect_omap)lrinifOMap.is_emptyomapthenNoneelseSomeomap|_->NoneinLMap.merge@@fun_->intersect_lmapletto_seqm=LMap.fold(funlvomap->OMap.fold(funovs->Seq.cons(lv,o,v)s)omap)mSeq.empty(* specialized functions *)letrecis_sub_offseto1o2=match(o1,o2)withNoOffset,_->true|Index(e1,o1),Index(e2,o2)whenCil_datatype.ExpStructEq.equale1e2->is_sub_offseto1o2|Field(f1,o1),Field(f2,o2)whenFieldinfo.equalf1f2->is_sub_offseto1o2|_->false(* finds all the lval lv1 apearing in [m] such as there exists an offset o1 and lv1 + o1 = lv *)letfind_upper_offsets(lv:lval)(m:t):V.tLMap.t=letlv,off=Cil.removeOffsetLvallvinletmo=tryLMap.findlvmwithNot_found->OMap.emptyinletf_filtero_v=is_sub_offsetooffinletmo=OMap.filterf_filtermoinOMap.fold(funo->letlv=Cil.addOffsetLvalolvinLMap.addlv)moLMap.emptyendtypet={graph:G.t;lmap:LLMap.t;(* lmap(lv) is a table [offset->v] where the vertex v corresponding to lval (lv+offset), in other words (lv+offset) is in label(v) *)vmap:LSet.tVMap.t;(* reverse of lmap *)}letnode_counter=ref0letfresh_node_id()=letid=!node_counterinnode_counter:=!node_counter+1;id(* find functions *)letfind_lset(v:V.t)(x:t):LSet.t=tryVMap.findvx.vmapwithNot_found->LSet.emptyletfind_aliases(lv:lval)(x:t)=letlv=Lval.simplifylvintryletv=LLMap.findlvx.lmapinfind_lsetvxwithNot_found->LSet.emptyletrecget_points_to(v:V.t)(x:t):LSet.t=assert(G.mem_vertexx.graphv);letset_predecessors=List.fold_left(funaccv->LSet.unionacc(get_points_tovx))LSet.empty(G.predx.graphv)inLSet.union(find_lsetvx)(LSet.mapLval.points_toset_predecessors)letaliases_of_vertex(v:V.t)(x:t):LSet.t=assert(G.mem_vertexx.graphv);letlist_pred=G.predx.graphvinList.fold_left(funaccv->LSet.unionacc(get_points_tovx))LSet.emptylist_predletsucc_of_lval(lv:lval)(x:t):intoption=letlv=Lval.simplifylvintrybeginletv=LLMap.findlvx.lmapinmatchG.succx.graphvwith|[]->None|[succ_v]->Somesucc_v|_->Options.fatal"invariant violated (more than 1 successor)"endwithNot_found->Noneletfind_all_aliases(lv:lval)(x:t):LSet.t=matchsucc_of_lvallvxwith|None->LSet.empty|Somesucc_v->aliases_of_vertexsucc_vxletpoints_to_set(lv:lval)(x:t):LSet.t=matchsucc_of_lvallvxwith|None->LSet.empty|Somesucc_v->find_lsetsucc_vxletget_graph(x:t)=x.graph(* renamed for the interface *)letget_lval_set=find_lset(* printing functions *)letprint_debugfmt(x:t)=Format.fprintffmt"@[<v>";Format.fprintffmt"@[Edges:";G.iter_edges(funv1v2->Format.fprintffmt"@;<3 2>@[%d → %d@]"v1v2)x.graph;Format.fprintffmt"@]@;<6>";Format.fprintffmt"@[LMap:@;<3 2>";LLMap.prettyfmtx.lmap;Format.fprintffmt"@]@;<6>";Format.fprintffmt"@[VMap:@;<2>";VMap.iter(funvls->Format.fprintffmt"@;<2 2>@[%d:%a@]"vLSet.prettyls)x.vmap;Format.fprintffmt"@]";Format.fprintffmt"@]"letprint_graphfmt(x:t)=letis_first=reftrueinletprint_edgev1v2=if!is_firstthenis_first:=falseelseFormat.fprintffmt"@;<3>";letprint_nodevfmtlset=Format.fprintffmt"%d:%a"vLSet.prettylsetinFormat.fprintffmt"@[%a@] → @[%a@]"(print_nodev1)(VMap.findv1x.vmap)(print_nodev2)(VMap.findv2x.vmap)inifG.nb_edgesx.graph=0thenFormat.fprintffmt"<empty>"elseG.iter_edgesprint_edgex.graphletprint_aliasesfmt(x:t)=letis_first=reftrueinletprint_alias_set_set_lv=if!is_firstthenis_first:=falseelseFormat.fprintffmt"@;<2>";LSet.prettyfmtset_lvinletalias_set_of_vertexi_=letaliases=aliases_of_vertexixinifLSet.cardinalaliases>=2thenSomealiaseselseNoneinletalias_sets=VMap.filter_mapalias_set_of_vertexx.vmapinifVMap.is_emptyalias_setsthenFormat.fprintffmt"<none>"elseVMap.iterprint_alias_setalias_sets(** invariants of type t must be true before and after each functon call *)letassert_invariants(x:t):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_vertex(v:V.t)=assert(v>=0);assert(v<!node_counter);assert(VMap.memvx.vmap);assert(List.length(G.succx.graphv)<=1)inG.iter_vertexassert_vertexx.graph;letassert_edgev1v2=assert(v1<>v2);assert(G.mem_vertexx.graphv1);assert(G.mem_vertexx.graphv2)inG.iter_edgesassert_edgex.graph;letassert_lmap(lv:lval)(v:V.t)=assert(G.mem_vertexx.graphv);assert(LSet.memlv(VMap.findvx.vmap))inLLMap.iterassert_lmapx.lmap;letassert_vmap(v:V.t)(ls:LSet.t)=assert(G.mem_vertexx.graphv);(* TODO: we removed the invariant because of OSCS*)(* if not (LSet.is_empty ls)
* then
* begin
* let lv = LSet.choose ls in
* let is_ptr_lv = Lval.is_pointer lv in
* assert (LSet.for_all (fun x -> Lval.is_pointer x = is_ptr_lv) ls)
* end; *)assert(LSet.fold(funlvacc->acc&&V.equal(LLMap.findlvx.lmap)v)lstrue)inVMap.iterassert_vmapx.vmap(* Ensure that assert_invariants is not executed if the -noassert flag is supplied. *)letassert_invariantsx=assert(assert_invariantsx;true)letpretty?(debug=false)fmt(x:t)=ifdebugthentryassert_invariantsx;print_graphfmtxwithAssert_failure_->print_debugfmtxelseprint_aliasesfmtx(** .dot printing functions*)letfind_vertex_name_ref=Extlib.mk_fun"find_vertex_name"moduleDot=Graph.Graphviz.Dot(structincludeGletedge_attributes_=[]letdefault_edge_attributes_=[]letget_subgraph_=Noneletvertex_attributes_=[`Shape`Box]letvertex_name(v:V.t)=letlset=!find_vertex_name_refvinletfmt=Format.str_formatterinFormat.fprintffmt"\"%a\""LSet.prettylset;Format.flush_str_formatter()letdefault_vertex_attributes_=[]letgraph_attributes_=[]end)letprint_dotfilename(a:t)=letfile=open_outfilenameinfind_vertex_name_ref:=(funv->find_lsetva);Dot.output_graphfilea.graph;close_outfile(* find functions, part 2 *)letrecclosure_find_lset(v:V.t)(x:t):(V.t*LSet.t)list=matchG.succx.graphvwith[]->[v,find_lsetvx]|[v_next]->(v,find_lsetvx)::(closure_find_lsetv_nextx)|_->Options.fatal("this shall not happen (invariant broken)")letfind_transitive_closure(lv:lval)(x:t):(G.V.t*LSet.t)list=letlv=Lval.simplifylvinassert_invariantsx;tryclosure_find_lset(LLMap.findlvx.lmap)xwithNot_found->[](* TODO : what about offsets ? *)(* 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 LSet is
empty. By definition, constant vertex cannot be associated to a
lval in [lmap] *)letcreate_cst_vertex(x:t):V.t*t=letnew_v=fresh_node_id()innew_v,{graph=G.add_vertexx.graphnew_v;lmap=x.lmap;vmap=VMap.addnew_vLSet.emptyx.vmap}(* find all the aliases of lv1 in x, for create_vertex *)letfind_all_aliases_of_offset(lv1:lval)(x:t):LSet.t=letlvals_to_be_searched=decompose_lvallv1in(* for each lval, find the set of aliases *)letf_map(lv,o)=tryVMap.find(LLMap.findlvx.lmap)x.vmap,owithNot_found->LSet.empty,oinOptions.debug~level:9"decompose_lval %a : [@[<hov 2>"Lval.prettylv1;List.iter(fun(x,o)->Options.debug~level:9" (%a,%a) "Lval.prettyxOffset.prettyo)lvals_to_be_searched;Options.debug~level:9"@]]";letaliases=List.mapf_maplvals_to_be_searchedin(* for each lval of the Lset, add the offset and add it to the resulting set *)letf_fold_left(acc:LSet.t)(ls,o)=LSet.fold(funlv->LSet.add@@Cil.addOffsetLvalolv)lsaccinList.fold_leftf_fold_left(LSet.singletonlv1)aliases(* returns the new vertex and the new graph *)(* only for function find_or_create vertex *)letcreate_vertex_simple(lv:lval)(x:t):V.t*t=letnew_v=fresh_node_id()inletnew_g=G.add_vertexx.graphnew_vin(* find all the alias of lv (because of offset) *)letset_of_aliases:LSet.t=find_all_aliases_of_offsetlvxin(* add all these aliases *)Options.debug~level:9"all_aliases of %a : %a "Lval.prettylvLSet.prettyset_of_aliases;letnew_lmap=LSet.fold(funlvacc->assert(not(LLMap.memlvx.lmap));LLMap.addlvnew_vacc)set_of_aliasesx.lmapinletnew_vmap=VMap.addnew_vset_of_aliasesx.vmapinletnew_x={graph=new_g;lmap=new_lmap;vmap=new_vmap;}inassert_invariantsnew_x;matchlvwith|Varv,NoOffset->beginmatchv.vtypewithTPtr_->(* then add a constant vertex *)letanother_v,new_x=create_cst_vertexnew_xinletnew_g=G.add_edgenew_x.graphnew_vanother_vinnew_v,{new_xwithgraph=new_g}|_->new_v,new_xend|_->new_v,new_xletdiff_offset(lv1:lval)(lv2:lval)=letrecf_diff_offseto1o2=matcho1,o2withNoOffset,_->o2|Field(_,o1),Field(_,o2)->f_diff_offseto1o2|Index(_,o1),Index(_,o2)->f_diff_offseto1o2|_->Options.fatal"%s: unexpected case"__LOC__inlet_,o1=Cil.removeOffsetLvallv1and_,o2=Cil.removeOffsetLvallv2inassert(LLMap.is_sub_offseto1o2);f_diff_offseto1o2letreccreate_vertexlvx=Options.debug~level:9"creating a vertex for %a"Lval.prettylv;assert(not(LLMap.memlvx.lmap));beginmatchlvwith(Meme,NoOffset)->(* special case, when we also add another vertex and a points-to edge *)begin(* first find the vertex corresponding to e *)matchLvalOrRef.from_expewith|None->Options.fatal"unexpected result: Lval.from (%a) = None"Exp.prettye|Some(LvalOrRef.Reflv1)->find_or_create_vertex(LvalOrRef.Lvallv1)x|Some(LvalOrRef.Lvallv1)->(* find the vertex *)letv1,x=find_or_create_vertex(LvalOrRef.Lvallv1)xin(* then creates a vertex for bvl ONLY IF there is no successor *)beginmatchG.succx.graphv1with[]->letv2,x=create_vertex_simplelvxin(* finally add a points-to edge between v1 and v2 *)letnew_graph=G.add_edgex.graphv1v2inv2,{xwithgraph=new_graph}|[succ_v1]->(* if there is a successor, update lmap and vmap to add blv to that successor's set *)letnew_lmap=LLMap.addlvsucc_v1x.lmapinletnew_vmap=VMap.addsucc_v1(LSet.addlv(VMap.findsucc_v1x.vmap))x.vmapinsucc_v1,{xwithlmap=new_lmap;vmap=new_vmap}|_->Options.fatal" Invariant violated : more than 1 successor"endend|_->create_vertex_simplelvxendandfind_or_create_lval_vertex(lv:lval)(x:t):V.t*t=tryLLMap.findlvx.lmap,xwithNot_found->(* try to find if an alias already exists in x *)letmap_predecessors:V.tLMap.t=LLMap.find_upper_offsetslvx.lmapin(* for any predecessor, find all its aliases and then look for potential existing vertex *)letf_fold_lmaplvxvxacc=letset_aliases=VMap.findvxx.vmapinOptions.debug~level:9"looking for aliases of %a in set %a"Lval.prettylvLSet.prettyset_aliases;ifLSet.cardinalset_aliases<=1thenaccelseletoff=diff_offsetlvxlvinletf_fold_lsetlvsacc=tryletlvs=Cil.addOffsetLvalofflvsinVSet.add(LLMap.findlvsx.lmap)accwithNot_found->accinLSet.foldf_fold_lsetset_aliasesaccin(* set of all existing aliases *)letvset_res=LMap.foldf_fold_lmapmap_predecessorsVSet.emptyinOptions.debug~level:9"found aliases of %a : %a"Lval.prettylvVSet.prettyvset_res;ifVSet.is_emptyvset_resthencreate_vertexlvxelselet()=assert(VSet.cardinalvset_res=1)inletv_res=VSet.choosevset_resin(* vertex found, update the tables *)letnew_lmap=LLMap.addlvv_resx.lmapinletnew_vmap=VMap.addv_res(LSet.addlv(VMap.findv_resx.vmap))x.vmapinv_res,{xwithlmap=new_lmap;vmap=new_vmap}(* find the vertex of an lval *)andfind_or_create_vertex(lv:LvalOrRef.t)(x:t):V.t*t=matchlvwith|LvalOrRef.Lvallv->find_or_create_lval_vertexlvx|LvalOrRef.Reflv->Options.debug~level:9"creating a vertex for %a"LvalOrRef.pretty(LvalOrRef.Reflv);letv1,x=find_or_create_lval_vertexlvxinletva,x=create_cst_vertexxinva,{xwithgraph=G.add_edgex.graphvav1}(* TODO is there a better way to do it ? *)letfind_vertexlvx=letlv=Lval.simplifylvinletv,x1=find_or_create_lval_vertexlvxinifx==x1(* if x has not been modified, then the vertex was found, not created *)thenvelseraiseNot_found(* merge of two vertices; the first vertex carries both sets, the second is removed from the graph and from lmap and vmap *)letmergexv1v2=if(V.equalv1v2)||not(G.mem_vertexx.graphv1)||not(G.mem_vertexx.graphv2)thenxelseletset1=find_lsetv1xinletset2=find_lsetv2xinletnew_set=LSet.unionset1set2in(* update lmap : every lval in v2 must now be associated with v1*)letnew_lmap=LSet.fold(funlv2m->LLMap.addlv2v1m)set2x.lmapin(* update vmap *)letnew_vmap=VMap.addv1new_set(VMap.removev2x.vmap)in(* update the graph *)letf_fold_succv_succ(g:G.t):G.t=G.add_edgegv1v_succandf_fold_predv_pred(g:G.t):G.t=G.add_edgegv_predv1inletg=x.graphin(* adds all new edges *)letg=G.fold_succf_fold_succgv2ginletg=G.fold_predf_fold_predgv2gin(* remove v2 *)letg=G.remove_vertexgv2in{graph=g;lmap=new_lmap;vmap=new_vmap}(* functions join and unify-pointer of steensgaard's paper *)letrecjoin_without_check(x:t)(v1:V.t)(v2:V.t):t=if(V.equalv1v2)||not(G.mem_vertexx.graphv1&&G.mem_vertexx.graphv2)thenxelseletpt1=G.succx.graphv1in(* TODO ask frama-c type instead of looking in the graph *)letpt2=G.succx.graphv2inletx=mergexv1v2inassert(not(G.mem_vertexx.graphv2));match(pt1,pt2)with|[],_->x|_,[]->x|[succ_v1],[succ_v2]->assert(succ_v1<>v2);assert(succ_v2<>v1);join_without_checkxsucc_v1succ_v2|_,_->Options.fatal"invariant broken"(* since the recursive version of join, unify, unify2 and merge may break the invariants *)letjoin(x:t)(v1:V.t)(v2:V.t):t=Options.debug~level:7"graph before join(%d,%d):@;<2>@[%a@]"v1v2print_debugx;assert_invariantsx;letres=join_without_checkxv1v2inOptions.debug~level:7"graph after join(%d,%d):@;<2>@[%a@]"v1v2print_debugres;begintryassert_invariantsreswithAssert_failure_->Options.debug"join(%d,%d) failed"v1v2;Options.debug"graph before join(%d,%d):@;<2>@[%a@]"v1v2print_debugx;Options.debug"graph after join(%d,%d):@;<2>@[ %a@]"v1v2print_debugres;assert_invariantsresend;resletmerge_set(x:t)(vs:VSet.t):V.t*t=letv0=VSet.choosevsinifVSet.cardinalvs<2thenv0,xelsebeginOptions.debug~level:7"graph before merge_set %a:@;<2>@[%a@]"VSet.prettyvsprint_debugx;assert(G.mem_vertexx.graphv0);letresult=VSet.fold(funvacc->mergeaccv0v)vsxinOptions.debug~level:7"graph after merge_set %a:@;<2>@[%a@]"VSet.prettyvsprint_debugresult;v0,resultendletrecjoin_succs(x:t)v=Options.debug~level:8"joining successors of %d"v;ifnot@@G.mem_vertexx.graphvthenxelsematchG.succx.graphvwith|[]|[_]->x|succs->letv0,x=merge_setx@@VSet.of_listsuccsinjoin_succsxv0(* in Steensgard's paper, this is written settype(v1,ref(v2,bot)) *)letset_type(x:t)(v1:V.t)(v2:V.t):t=assert_invariantsx;(* if v1 points to another node, suppress current outgoing edge (and the node if it is a constant node) *)letg,new_vmap=matchG.succx.graphv1with[]->x.graph,x.vmap|[v2]->(* if v2 is a constant node supress it directly *)ifLSet.is_empty(VMap.findv2x.vmap)then(G.remove_vertexx.graphv2,VMap.removev2x.vmap)else(G.remove_edgex.graphv1v2,x.vmap)|_->Options.fatal"too many outgoing edges in set_type"inletnew_g=G.add_edgegv1v2inletnew_x={xwithgraph=new_g;vmap=new_vmap}inassert_invariantsnew_x;new_xletassignment(a:t)(lv:lval)(e:exp):t=assert_invariantsa;ifnot@@Cil.isPointerType(Cil.typeOfe)thenaelseletx=Lval.simplifylvinmatchLvalOrRef.from_expewith|None->a|Somey->let(v1,a)=find_or_create_lval_vertexxainlet(v2,a)=find_or_create_vertexyainifList.memv2(G.succa.graphv1)||List.memv1(G.succa.graphv2)thenlet()=Options.warning~source:(fste.eloc)"ignoring assignment of the form: %a = %a"Printer.pp_lvallvPrinter.pp_expe;inaelseleta=joinav1v2inlet()=assert_invariantsaina(* assignment x = allocate(y) *)letassignment_x_allocate_y(a:t)(lv:lval):t=assert_invariantsa;letx=Lval.simplifylvinlet(v1,a)=find_or_create_lval_vertexxainmatchG.succa.graphv1with|[]->let(v2,a)=create_cst_vertexainletnew_a:t=set_typeav1v2inlet()=assert_invariantsnew_ainnew_a|[_v2]->a|_->Options.fatal"this should not hapen (invariant broken)"letis_included(a1:t)(a2:t)=(* tests if a1 is included in a2, at least as the nodes with lval *)assert_invariantsa1;assert_invariantsa2;Options.debug~level:8"testing equal %a AND à.%a"(pretty~debug:true)a1(pretty~debug:true)a2;letexceptionNot_includedintryletiter_lmap(lv:lval)(v1:V.t):unit=letv2:V.t=tryLLMap.findlva2.lmapwithNot_found->raiseNot_includedinmatchG.succa1.graphv1,G.succa2.graphv2with[],_->()|[_],[]->raiseNot_included|[v1p],[v2p]->ifLSet.subset(VMap.findv1pa1.vmap)(VMap.findv2pa2.vmap)then()elseraiseNot_included|_->Options.fatal"this should not hapen (invariant broken)"inLLMap.iteriter_lmapa1.lmap;truewithNot_included->falseletempty:t={graph=G.empty;lmap=LLMap.empty;vmap=VMap.empty}letis_emptys=comparesempty=0(* add an int to all vertex values *)letshift(a:t):t=assert_invariantsa;ifis_emptyathenaelselet()=Options.debug~level:8"before shift: node_counter=%d@.%a"!node_counterprint_debugainletmax_idx=G.fold_vertexmaxa.graph0inletmin_idx=G.fold_vertexmina.graphmax_idxinletoffset=!node_counter-min_idxinletshiftx=x+offsetinletshift_vmapshift_elemvmap=VMap.of_seq@@Stdlib.Seq.mapshift_elem@@VMap.to_seqvmapinlet{graph;lmap;vmap}=ainnode_counter:=max_idx+offset+1;letresult={graph=G.map_vertexshiftgraph;lmap=LLMap.mapshiftlmap;vmap=shift_vmap(fun(key,l)->(shiftkey,l))vmap}inlet()=Options.debug~level:8"after shift: node_counter=%d@.%a"!node_counterprint_debugresultinassert_invariantsresult;resultletunion_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_joinedletunion(a1:t)(a2:t):t=(* naive algorithm :
1 merge the graph and the vmap (by doing union of sets)
2 for any node present in both a1.graph and a2.graph, merge/join them
3 for any lval [lv] that are has an entry in both a1.lmap and a2.lmap, merge the two vertex a1.lmap[lv]
and a2.lmap[lv]
I am not confident about this function, there are too many potential bugs and inefficiencies
*)assert_invariantsa1;assert_invariantsa2;Options.debug~level:4"Union: First graph:%a"print_grapha1;Options.debug~level:5"Union: First graph:%a"print_debuga1;Options.debug~level:4"Union: Second graph:%a"print_grapha2;Options.debug~level:5"Union: Second graph:%a"print_debuga2;letnew_graph=G.fold_vertex(funv2g->G.add_vertexgv2)a2.grapha1.graphinletnew_graph=G.fold_edges(funv2av2bg->G.add_edgegv2av2b)a2.graphnew_graphinletnew_vmap=VMap.union(fun_lset1lset2->Option.some@@LSet.unionlset1lset2)a2.vmapa1.vmapinletsets_to_be_joined=letintersections=LLMap.to_seq@@LLMap.intersecta1.lmapa2.lmapinunion_findnew_vmap@@Seq.map(fun(_,_,x)->x)intersectionsinletnew_lmap=LLMap.uniona1.lmapa2.lmapinOptions.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"@]";letnew_a={graph=new_graph;lmap=new_lmap;vmap=new_vmap}inletmerged_nodes,new_a=VMap.fold(fun_set(merged_nodes,x)->letv0,x=merge_setxsetin(v0::merged_nodes),x)sets_to_be_joined([],new_a)inletnew_a=List.fold_leftjoin_succsnew_amerged_nodesinOptions.debug~level:4"Union: Result graph:%a"print_graphnew_a;Options.debug~level:5"Union: Result graph:%a"print_debugnew_a;begintryassert_invariantsnew_awithAssert_failure_->Options.debug"union failed";Options.debug"Union: First graph:%a"print_grapha1;Options.debug"Union: First graph:%a"print_debuga1;Options.debug"Union: Second graph:%a"print_grapha2;Options.debug"Union: Second graph:%a"print_debuga2;Options.debug"Union: Result graph:%a"print_graphnew_a;Options.debug"Union: Result graph:%a"print_debugnew_a;assert_invariantsnew_aend;new_a(** a type for summaries of functions *)typesummary={state:toption;formals:lvallist;locals:lvallist;return:expoption}letmake_summary(s:t)(kf:kernel_function)=letexp_return:expoption=ifKernel_function.has_definitionkfthenletreturn_stmt=Kernel_function.find_returnkfinmatchreturn_stmt.skindwithReturn(e,_)->e|_->Options.fatal"this should not happen"elseNoneinlets=matchexp_returnwithNone->s|Somee->beginmatchs,LvalOrRef.from_expewith_,None->s|s,Somelv->let_,new_s=find_or_create_vertexlvsinnew_sendin{state=Somes;formals=List.map(funv->(Varv,NoOffset))(Kernel_function.get_formalskf);locals=List.map(funv->(Varv,NoOffset))(Kernel_function.get_localskf);return=exp_return}letpretty_summary?(debug=false)fmts=letprint_list_lval~statefmt(l:lvallist)=letis_first=reftrueinletprint_elemx=if!is_firstthenis_first:=falseelseFormat.fprintffmt"@ ";Format.fprintffmt"@[%a"Cil_datatype.Lval.prettyx;letpointees=points_to_setxstateinifnot@@LSet.is_emptypointeesthenFormat.fprintffmt"→%a"LSet.prettypointees;Format.fprintffmt"@]";inList.iterprint_elemlinletprint_optionppfmtx=matchxwith|Somex->ppfmtx|None->Format.fprintffmt"<none>"inmatchs.statewith|None->ifdebugthenFormat.fprintffmt"not found"|Someswhenis_emptys->ifdebugthenFormat.fprintffmt"empty"|Somestate->beginFormat.fprintffmt"@[formals: @[%a@]@;<4>locals: @[%a@]@;<4>returns: @[%a@]@;<4>state: @[%a@] "(print_list_lval~state)s.formals(print_list_lval~state)s.locals(print_optionExp.pretty)s.return(print_option@@pretty~debug)s.state;end(* 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
*)letcall(state:t)(res:lvaloption)(args:explist)(summary:summary):t=assert_invariantsstate;letformals=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 *)letstate,vertex_pairs=letstate=refstateinletfind_vertex(lv1,lv2)=tryletv2=LLMap.findlv2sum_state.lmapinletv1,new_state=find_or_create_vertexlv1!stateinstate:=new_state;Some(v1,v2)withNot_found->Nonein!state,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_succs(g:G.t)(v1,v2)=letv2_succs=G.succsum_state.graphv2inassert(List.lengthv2_succs<2);List.fold_left(fungsucc->G.add_edgegv1succ)gv2_succsinletg=state.graphinletg=G.fold_vertex(funig->G.add_vertexgi)sum_state.graphginletg=G.fold_edges(funijg->G.add_edgegij)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_degreesum_state.graphv=0thenlet()=g:=G.remove_vertex!gvinNoneelseSomeLSet.emptyinletremaining_vertices=VMap.filter_mapremove_if_leafsum_state.vmapinremaining_vertices,!ginletstate={graph=g;lmap=state.lmap;vmap=letleft_bias_l_=SomelinVMap.unionleft_biasstate.vmapvertices_to_add_to_g}inletstate=List.fold_leftjoin_succsstate(List.mapfstvertex_pairs)inassert_invariantsstate;state