123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032(*
Copyright (c) 2001-2002,
John Kodumal <jkodumal@eecs.berkeley.edu>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)openGoblintCil(***********************************************************************)(* *)(* Exceptions *)(* *)(***********************************************************************)exceptionInconsistent(* raised if constraint system is inconsistent *)exceptionWellFormed(* raised if types are not well-formed *)exceptionNoContentsexceptionReachedTop(* raised if top (from an undefined function)
flows to a c_absloc during the flow step *)exceptionUnknownLocationmoduleU=UrefmoduleS=SetpmoduleH=HashtblmoduleQ=Queue(** Generic bounds *)type'abound={info:'aU.uref}moduleBound=structtype'at='aboundletcompare(x:'at)(y:'at)=Stdlib.compare(U.derefx.info)(U.derefy.info)endmoduleB=S.Make(Bound)type'aboundset='aB.t(** Abslocs, which identify elements in points-to sets *)(* jk : I'd prefer to make this an 'a absloc and specialize it to
varinfo for use with the Cil frontend, but for now, this will do *)typeabsloc=int*string*Cil.varinfooptionmoduleAbsloc=structtypet=abslocletcompare(xid,_,_)(yid,_,_)=xid-yidendmoduleC=Set.Make(Absloc)(** Sets of abslocs. Set union is used when two c_abslocs containing
absloc sets are unified *)typeabslocset=C.tletd_absloc()(a:absloc):Pretty.doc=leti,s,_=ainPretty.dprintf"<%d, %s>"istypec_abslocinfo={mutablel_name:string;(** name of the location *)loc:absloc;l_stamp:int;mutablel_top:bool;mutablealiases:abslocset;mutablelbounds:c_abslocinfoboundset;mutableubounds:c_abslocinfoboundset;mutableflow_computed:bool}andc_absloc=c_abslocinfoU.uref(** The type of lvalues. *)typelvalue={l:c_absloc;contents:tau}andvinfo={v_stamp:int;v_name:string;mutablev_top:bool;mutablev_lbounds:tinfoboundset;mutablev_ubounds:tinfoboundset}andrinfo={r_stamp:int;rl:c_absloc;points_to:tau}andfinfo={f_stamp:int;fl:c_absloc;ret:tau;mutableargs:taulist}andpinfo={p_stamp:int;ptr:tau;lam:tau}andtinfo=Varofvinfo|Refofrinfo|Funoffinfo|Pairofpinfoandtau=tinfoU.ureftypetconstraint=Unificationoftau*tau|Leqoftau*tau(** Association lists, used for printing recursive types. The first
element is a type that has been visited. The second element is the
string representation of that type (so far). If the string option is
set, then this type occurs within itself, and is associated with the
recursive var name stored in the option. When walking a type, add it
to an association list.
Example: suppose we have the constraint 'a = ref('a). The type is
unified via cyclic unification, and would loop infinitely if we
attempted to print it. What we want to do is print the type u
rv. ref(rv). This is accomplished in the following manner:
-- ref('a) is visited. It is not in the association list, so it is
added and the string "ref(" is stored in the second element. We
recurse to print the first argument of the constructor.
-- In the recursive call, we see that 'a (or ref('a)) is already
in the association list, so the type is recursive. We check the
string option, which is None, meaning that this is the first
recurrence of the type. We create a new recursive variable, rv and
set the string option to 'rv. Next, we prepend u rv. to the string
representation we have seen before, "ref(", and return "rv" as the
string representation of this type.
-- The string so far is "u rv.ref(". The recursive call returns,
and we complete the type by printing the result of the call, "rv",
and ")"
In a type where the recursive variable appears twice, e.g. 'a =
pair('a,'a), the second time we hit 'a, the string option will be
set, so we know to reuse the same recursive variable name.
*)typeassociation=tau*stringref*stringoptionref(** The current state of the solver engine either adding more
constraints, or finished adding constraints and querying graph *)typestate=AddingConstraints|FinishedConstraints(***********************************************************************)(* *)(* Global Variables *)(* *)(***********************************************************************)(** A count of the constraints introduced from the AST. Used for
debugging. *)lettoplev_count=ref0letsolver_state:stateref=refAddingConstraints(** Print the instantiations constraints. *)letprint_constraints:boolref=reffalse(** If true, print all constraints (including induced) and show
additional debug output. *)letdebug=reffalse(** Just debug all the constraints (including induced) *)letdebug_constraints=reffalse(** Compatibility with GOLF *)letdebug_aliases=reffalseletsmart_aliases=reffalseletno_flow=reffalseletanalyze_mono=reffalse(** If true, disable subtyping (unification at all levels) *)letno_sub=reffalse(** A list of equality constraints. *)leteq_worklist:tconstraintQ.t=Q.create()(** A list of leq constraints. *)letleq_worklist:tconstraintQ.t=Q.create()(** A hashtable mapping pairs of tau's to their join node. *)letjoin_cache:(int*int,tau)H.t=H.create64(** *)letlabel_prefix="l_"(***********************************************************************)(* *)(* Utility Functions *)(* *)(***********************************************************************)letstarts_withsp=letn=String.lengthpinifString.lengths<nthenfalseelseString.subs0n=pletdies=Printf.printf"*******\nAssertion failed: %s\n*******\n"s;assertfalseletinsistbs=ifnotbthendieselse()letcan_add_constraints()=!solver_state=AddingConstraintsletcan_query_graph()=!solver_state=FinishedConstraintsletfinished_constraints()=insist(!solver_state=AddingConstraints)"inconsistent states";solver_state:=FinishedConstraintsletfind=U.deref(** Generate a unique integer. *)letfresh_index:(unit->int)=letcounter=ref0infun()->incrcounter;!counterletfresh_stamp:(unit->int)=letstamp=ref0infun()->incrstamp;!stamp(** Return a unique integer representation of a tau *)letget_stamp(t:tau):int=matchfindtwithVarv->v.v_stamp|Refr->r.r_stamp|Pairp->p.p_stamp|Funf->f.f_stamp(** Consistency checks for inferred types *)letpair_or_var(t:tau)=matchfindtwithPair_->true|Var_->true|_->falseletref_or_var(t:tau)=matchfindtwithRef_->true|Var_->true|_->falseletfun_or_var(t:tau)=matchfindtwithFun_->true|Var_->true|_->false(** Apply [f] structurally down [t]. Guaranteed to terminate, even if [t]
is recursive *)letiter_tauft=letvisited:(int,tau)H.t=H.create4inletreciter_tau't=ifH.memvisited(get_stampt)then()elsebeginft;H.addvisited(get_stampt)t;matchfindtwithPairp->iter_tau'p.ptr;iter_tau'p.lam|Funf->List.iteriter_tau'f.args;iter_tau'f.ret;|Refr->iter_tau'r.points_to|_->()endiniter_tau'tletequal_absloc=function(i,_,_),(i',_,_)->i=i'letequal_c_abslocll'=(findl).l_stamp=(findl').l_stampletequal_tau(t:tau)(t':tau)=get_stampt=get_stampt'lettop_c_abslocl=(findl).l_topletget_flow_computedl=(findl).flow_computedletset_flow_computedl=(findl).flow_computed<-trueletrectop_tau(t:tau)=matchfindtwithPairp->top_taup.ptr||top_taup.lam|Refr->top_c_abslocr.rl|Funf->top_c_abslocf.fl|Varv->v.v_topletget_c_absloc_stamp(l:c_absloc):int=(findl).l_stampletset_top_c_absloc(l:c_absloc)(b:bool):unit=(findl).l_top<-bletget_aliases(l:c_absloc)=iftop_c_absloclthenraiseReachedTopelse(findl).aliases(***********************************************************************)(* *)(* Printing Functions *)(* *)(***********************************************************************)(** Convert a c_absloc to a string, short representation *)letstring_of_c_absloc(l:c_absloc):string="\""^(findl).l_name^iftop_c_absloclthen"(top)"else""^"\""(** Return true if the element [e] is present in the association list,
according to uref equality *)letrecassoc_list_mem(e:tau)(l:associationlist)=matchlwith[]->None|(h,s,so)::t->ifU.equal(h,e)thenSome(s,so)elseassoc_list_memet(** Given a tau, create a unique recursive variable name. This should
always return the same name for a given tau *)letfresh_recvar_name(t:tau):string=matchfindtwithPairp->"rvp"^string_of_intp.p_stamp|Refr->"rvr"^string_of_intr.r_stamp|Funf->"rvf"^string_of_intf.f_stamp|_->die"fresh_recvar_name"(** Return a string representation of a tau, using association lists. *)letstring_of_tau(t:tau):string=lettau_map:associationlistref=ref[]inletrecstring_of_tau't=matchassoc_list_memt!tau_mapwithSome(s,so)->(* recursive type. see if a var name has been set *)beginmatch!sowithNone->letrv=fresh_recvar_nametins:="u "^rv^"."^!s;so:=Somerv;rv|Somerv->rvend|None->(* type's not recursive. Add it to the assoc list and cont. *)lets=ref""andso:stringoptionref=refNoneintau_map:=(t,s,so)::!tau_map;beginmatchfindtwithVarv->s:=v.v_name|Pairp->insist(ref_or_varp.ptr)"wellformed";insist(fun_or_varp.lam)"wellformed";s:="{";s:=!s^string_of_tau'p.ptr;s:=!s^",";s:=!s^string_of_tau'p.lam;s:=!s^"}"|Refr->insist(pair_or_varr.points_to)"wellformed";s:="ref(|";s:=!s^string_of_c_abslocr.rl;s:=!s^"|,";s:=!s^string_of_tau'r.points_to;s:=!s^")"|Funf->letrecstring_of_args=function[]->()|h::[]->insist(pair_or_varh)"wellformed";s:=!s^string_of_tau'h|h::t->insist(pair_or_varh)"wellformed";s:=!s^string_of_tau'h^",";string_of_argstininsist(pair_or_varf.ret)"wellformed";s:="fun(|";s:=!s^string_of_c_abslocf.fl;s:=!s^"|,";s:=!s^"<";ifList.lengthf.args>0thenstring_of_argsf.argselses:=!s^"void";s:=!s^">,";s:=!s^string_of_tau'f.ret;s:=!s^")"end;tau_map:=List.tl!tau_map;!sinstring_of_tau't(** Convert an lvalue to a string *)letstring_of_lvalue(lv:lvalue):string=letcontents=string_of_taulv.contentsandl=string_of_c_absloclv.lininsist(pair_or_varlv.contents)"inconsistency at string_of_lvalue";(* do a consistency check *)Printf.sprintf"[%s]^(%s)"contentslletprint_constraint(c:tconstraint)=matchcwithUnification(t,t')->letlhs=string_of_tautinletrhs=string_of_taut'inPrintf.printf"%s == %s\n"lhsrhs|Leq(t,t')->letlhs=string_of_tautinletrhs=string_of_taut'inPrintf.printf"%s <= %s\n"lhsrhs(***********************************************************************)(* *)(* Type Operations -- these do not create any constraints *)(* *)(***********************************************************************)(** Create an lvalue with c_absloc [lbl] and tau contents [t]. *)letmake_lval(loc,t:c_absloc*tau):lvalue={l=loc;contents=t}letmake_c_absloc_int(is_top:bool)(name:string)(vio:Cil.varinfooption):c_absloc=letmy_absloc=(fresh_index(),name,vio)inletlocc=C.addmy_abslocC.emptyinU.uref{l_name=name;l_top=is_top;l_stamp=fresh_stamp();loc=my_absloc;aliases=locc;ubounds=B.empty;lbounds=B.empty;flow_computed=false}(** Create a new c_absloc with name [name]. Also adds a fresh absloc
with name [name] to this c_absloc's aliases set. *)letmake_c_absloc(is_top:bool)(name:string)(vio:Cil.varinfooption)=make_c_absloc_intis_topnamevioletfresh_c_absloc(is_top:bool):c_absloc=letindex=fresh_index()inmake_c_absloc_intis_top(label_prefix^string_of_intindex)None(** Create a fresh bound (edge in the constraint graph). *)letmake_bound(a:c_absloc):c_abslocinfobound={info=a}letmake_tau_bound(t:tau):tinfobound={info=t}(** Create a fresh named variable with name '[name]. *)letmake_var(is_top:bool)(name:string):tau=U.uref(Var{v_name=("'"^name);v_top=is_top;v_stamp=fresh_index();v_lbounds=B.empty;v_ubounds=B.empty})letfresh_var(is_top:bool):tau=make_varis_top("fi"^string_of_int(fresh_index()))(** Create a fresh unnamed variable (name will be 'fi). *)letfresh_var_i(is_top:bool):tau=make_varis_top("fi"^string_of_int(fresh_index()))(** Create a Fun constructor. *)letmake_fun(lbl,a,r:c_absloc*(taulist)*tau):tau=U.uref(Fun{fl=lbl;f_stamp=fresh_index();args=a;ret=r})(** Create a Ref constructor. *)letmake_ref(lbl,pt:c_absloc*tau):tau=U.uref(Ref{rl=lbl;r_stamp=fresh_index();points_to=pt})(** Create a Pair constructor. *)letmake_pair(p,f:tau*tau):tau=U.uref(Pair{ptr=p;p_stamp=fresh_index();lam=f})(** Copy the toplevel constructor of [t], putting fresh variables in each
argument of the constructor. *)letcopy_toplevel(t:tau):tau=matchfindtwithPair_->make_pair(fresh_var_ifalse,fresh_var_ifalse)|Ref_->make_ref(fresh_c_abslocfalse,fresh_var_ifalse)|Funf->make_fun(fresh_c_abslocfalse,Util.list_map(fun_->fresh_var_ifalse)f.args,fresh_var_ifalse)|_->die"copy_toplevel"letpad_args(fi,tlr:finfo*taulistref):unit=letpadding=List.lengthfi.args-List.length!tlrinifpadding==0then()elseifpadding>0thenfor_=1topaddingdotlr:=!tlr@[fresh_varfalse]doneelsefor_=1to-paddingdofi.args<-fi.args@[fresh_varfalse]done(***********************************************************************)(* *)(* Constraint Generation/ Resolution *)(* *)(***********************************************************************)letset_top(b:bool)(t:tau):unit=letset_top_downt=matchfindtwithVarv->v.v_top<-b|Refr->set_top_c_abslocr.rlb|Funf->set_top_c_abslocf.flb|Pairp->()initer_tauset_top_downtletrecunify_int(t,t':tau*tau):unit=ifequal_tautt'then()elseletti,ti'=findt,findt'inU.unifycombine(t,t');matchti,ti'withVarv,Varv'->set_top(v.v_top||v'.v_top)t';merge_v_lbounds(v,v');merge_v_ubounds(v,v')|Varv,_->set_top(v.v_top||top_taut')t';notify_vlboundstv;notify_vuboundstv|_,Varv->set_top(v.v_top||top_taut)t;notify_vlboundst'v;notify_vuboundst'v|Refr,Refr'->unify_ref(r,r')|Funf,Funf'->unify_fun(f,f')|Pairp,Pairp'->unify_pair(p,p')|_->raiseInconsistentandnotify_vlbounds(t:tau)(vi:vinfo):unit=letnotifybounds=List.iter(funb->add_constraint(Unification(b.info,copy_toplevelt));add_constraint(Leq(b.info,t)))boundsinnotify(B.elementsvi.v_lbounds)andnotify_vubounds(t:tau)(vi:vinfo):unit=letnotifybounds=List.iter(funb->add_constraint(Unification(b.info,copy_toplevelt));add_constraint(Leq(t,b.info)))boundsinnotify(B.elementsvi.v_ubounds)andunify_ref(ri,ri':rinfo*rinfo):unit=unify_c_abslocs(ri.rl,ri'.rl);add_constraint(Unification(ri.points_to,ri'.points_to))andunify_fun(fi,fi':finfo*finfo):unit=letrecunion_args=function_,[]->false|[],_->true|h::t,h'::t'->add_constraint(Unification(h,h'));union_args(t,t')inunify_c_abslocs(fi.fl,fi'.fl);add_constraint(Unification(fi.ret,fi'.ret));ifunion_args(fi.args,fi'.args)thenfi.args<-fi'.argsandunify_pair(pi,pi':pinfo*pinfo):unit=add_constraint(Unification(pi.ptr,pi'.ptr));add_constraint(Unification(pi.lam,pi'.lam))andunify_c_abslocs(l,l':c_absloc*c_absloc):unit=letpick_name(li,li':c_abslocinfo*c_abslocinfo)=ifstarts_withli.l_namelabel_prefixthenli.l_name<-li'.l_nameelse()inletcombine_c_absloc(li,li':c_abslocinfo*c_abslocinfo):c_abslocinfo=pick_name(li,li');li.l_top<-li.l_top||li'.l_top;li.aliases<-C.unionli.aliasesli'.aliases;li.ubounds<-B.unionli.uboundsli'.ubounds;li.lbounds<-B.unionli.lboundsli'.lbounds;liinif!debug_constraintsthenPrintf.printf"%s == %s\n"(string_of_c_abslocl)(string_of_c_abslocl');U.unifycombine_c_absloc(l,l')andmerge_v_lbounds(vi,vi':vinfo*vinfo):unit=vi'.v_lbounds<-B.unionvi.v_lboundsvi'.v_lbounds;andmerge_v_ubounds(vi,vi':vinfo*vinfo):unit=vi'.v_ubounds<-B.unionvi.v_uboundsvi'.v_ubounds(** Pick the representative info for two tinfo's. This function
prefers the first argument when both arguments are the same
structure, but when one type is a structure and the other is a
var, it picks the structure. All other actions (e.g., updating
the info) is done in unify_int *)andcombine(ti,ti':tinfo*tinfo):tinfo=matchti,ti'withVar_,_->ti'|_,_->tiandleq_int(t,t'):unit=ifequal_tautt'then()elseletti,ti'=findt,findt'inmatchti,ti'withVarv,Varv'->v.v_ubounds<-B.add(make_tau_boundt')v.v_ubounds;v'.v_lbounds<-B.add(make_tau_boundt)v'.v_lbounds|Varv,_->add_constraint(Unification(t,copy_toplevelt'));add_constraint(Leq(t,t'))|_,Varv->add_constraint(Unification(t',copy_toplevelt));add_constraint(Leq(t,t'))|Refr,Refr'->leq_ref(r,r')|Funf,Funf'->(* TODO: check, why not do subtyping here? *)add_constraint(Unification(t,t'))|Pairpr,Pairpr'->add_constraint(Leq(pr.ptr,pr'.ptr));add_constraint(Leq(pr.lam,pr'.lam))|_->raiseInconsistentandleq_ref(ri,ri'):unit=leq_c_absloc(ri.rl,ri'.rl);add_constraint(Unification(ri.points_to,ri'.points_to))andleq_c_absloc(l,l'):unit=letli,li'=findl,findl'inif!debug_constraintsthenPrintf.printf"%s <= %s\n"(string_of_c_abslocl)(string_of_c_abslocl');ifU.equal(l,l')then()elsebeginli.ubounds<-B.add(make_boundl')li.ubounds;li'.lbounds<-B.add(make_boundl)li'.lboundsendandadd_constraint_int(c:tconstraint)(toplev:bool)=if!debug_constraints&&toplevthenbeginPrintf.printf"%d:>"!toplev_count;print_constraintc;incrtoplev_countendelseif!debug_constraintsthenprint_constraintcelse();insist(can_add_constraints())"can't add constraints after compute_results is called";beginmatchcwithUnification_->Q.addceq_worklist|Leq_->Q.addcleq_worklistend;solve_constraints()(* solve online *)andadd_constraint(c:tconstraint)=add_constraint_intcfalseandadd_toplev_constraint(c:tconstraint)=if!print_constraints&¬!debug_constraintsthenbeginPrintf.printf"%d:>"!toplev_count;incrtoplev_count;print_constraintcendelse();add_constraint_intctrueandfetch_constraint():tconstraintoption=trySome(Q.takeeq_worklist)withQ.Empty->begintrySome(Q.takeleq_worklist)withQ.Empty->Noneend(** The main solver loop. *)andsolve_constraints():unit=matchfetch_constraint()withNone->()|Somec->beginmatchcwithUnification(t,t')->unify_int(t,t')|Leq(t,t')->if!no_subthenunify_int(t,t')elseleq_int(t,t')end;solve_constraints()(***********************************************************************)(* *)(* Interface Functions *)(* *)(***********************************************************************)(** Return the contents of the lvalue. *)letrvalue(lv:lvalue):tau=lv.contents(** Dereference the rvalue. If it does not have enough structure to
support the operation, then the correct structure is added via new
unification constraints. *)letrecderef(t:tau):lvalue=matchfindtwithPairp->beginmatchfindp.ptrwith|Var_->letis_top=top_taup.ptrinletpoints_to=fresh_varis_topinletl=fresh_c_abslocis_topinletr=make_ref(l,points_to)inadd_toplev_constraint(Unification(p.ptr,r));make_lval(l,points_to)|Refr->make_lval(r.rl,r.points_to)|_->raiseWellFormedend|Varv->letis_top=top_tautinadd_toplev_constraint(Unification(t,make_pair(fresh_varis_top,fresh_varis_top)));dereft|_->raiseWellFormed(** Form the union of [t] and [t'], if it doesn't exist already. *)letjoin(t:tau)(t':tau):tau=lets,s'=get_stampt,get_stampt'intryH.findjoin_cache(s,s')withNot_found->lett''=fresh_varfalseinadd_toplev_constraint(Leq(t,t''));add_toplev_constraint(Leq(t',t''));H.addjoin_cache(s,s')t'';t''(** Form the union of a list [tl], expected to be the initializers of some
structure or array type. *)letjoin_inits(tl:taulist):tau=lett'=fresh_varfalseinList.iter(functiont->add_toplev_constraint(Leq(t,t')))tl;t'(** Take the address of an lvalue. Does not add constraints. *)letaddress(lv:lvalue):tau=make_pair(make_ref(lv.l,lv.contents),fresh_varfalse)(** No instantiation in this analysis *)letinstantiate(lv:lvalue)(i:int):lvalue=lv(** Constraint generated from assigning [t] to [lv]. *)letassign(lv:lvalue)(t:tau):unit=add_toplev_constraint(Leq(t,lv.contents))letassign_ret(i:int)(lv:lvalue)(t:tau):unit=add_toplev_constraint(Leq(t,lv.contents))(** Project out the first (ref) component or a pair. If the argument
[t] has no discovered structure, raise NoContents. *)letproj_ref(t:tau):tau=matchfindtwithPairp->p.ptr|Varv->raiseNoContents|_->raiseWellFormed(* Project out the second (fun) component of a pair. If the argument
[t] has no discovered structure, create it on the fly by adding
constraints. *)letproj_fun(t:tau):tau=matchfindtwithPairp->p.lam|Varv->letp,f=fresh_varfalse,fresh_varfalseinadd_toplev_constraint(Unification(t,make_pair(p,f)));f|_->raiseWellFormedletget_finfo(t:tau):finfo=matchfindtwithFunf->f|_->raiseWellFormed(** Function type [t] is applied to the arguments [actuals]. Unifies
the actuals with the formals of [t]. If no functions have been
discovered for [t] yet, create a fresh one and unify it with
t. The result is the return value of the function plus the index
of this application site.
For this analysis, the application site is always 0 *)letapply(t:tau)(al:taulist):(tau*int)=letf=proj_funtinletactuals=refalinletfi,ret=matchfindfwithFunfi->fi,fi.ret|Varv->letnew_l,new_ret,new_args=fresh_c_abslocfalse,fresh_varfalse,Util.list_map(function_->fresh_varfalse)!actualsinletnew_fun=make_fun(new_l,new_args,new_ret)inadd_toplev_constraint(Unification(new_fun,f));(get_finfonew_fun,new_ret)|_->raiseWellFormedinpad_args(fi,actuals);List.iter2(funactual->funformal->add_toplev_constraint(Leq(actual,formal)))!actualsfi.args;(ret,0)letmake_undefined_lvalue()=make_lval(make_c_abslocfalse"undefined"None,make_vartrue"undefined")letmake_undefined_rvalue()=make_vartrue"undefined"letassign_undefined(lv:lvalue):unit=assignlv(make_undefined_rvalue())letapply_undefined(al:taulist):(tau*int)=List.iter(funactual->assign(make_undefined_lvalue())actual)al;(fresh_vartrue,0)(** Create a new function type with name [name], list of formal
arguments [formals], and return value [ret]. Adds no constraints. *)letmake_function(name:string)(formals:lvaluelist)(ret:tau):tau=letf=make_fun(make_c_abslocfalsenameNone,Util.list_map(funx->rvaluex)formals,ret)inmake_pair(fresh_varfalse,f)(** Create an lvalue. *)letmake_lvalue(b:bool)(name:string)(vio:Cil.varinfooption)=make_lval(make_c_abslocfalsenamevio,make_varfalsename)(** Create a fresh named variable. *)letmake_fresh(name:string):tau=make_varfalsename(** The default type for abslocs. *)letbottom():tau=make_varfalse"bottom"(** Unify the result of a function with its return value. *)letreturn(t:tau)(t':tau)=add_toplev_constraint(Leq(t',t))(***********************************************************************)(* *)(* Query/Extract Solutions *)(* *)(***********************************************************************)moduleIntHash=Hashtbl.Make(structtypet=intletequalxy=x=ylethashx=xend)(** this is a quadratic flow step. keep it for debugging the fast
version above. *)letcollect_ptset_slow(l:c_absloc):abslocset=letonpath:unitIntHash.t=IntHash.create101inletrecflow_step(l:c_absloc):abslocset=iftop_c_absloclthenraiseReachedTopelseletstamp=get_c_absloc_stamplinifIntHash.memonpathstampthenC.emptyelseletli=findlinIntHash.addonpathstamp();B.iter(funlb->li.aliases<-C.unionli.aliases(flow_steplb.info))li.lbounds;li.aliasesininsist(can_query_graph())"collect_ptset_slow can't query graph";ifget_flow_computedlthenget_aliaseslelsebeginset_flow_computedl;flow_steplendletcollect_ptset=collect_ptset_slow(* if !debug_flow_step then collect_ptset_slow
else collect_ptset_fast *)letmay_alias(t1:tau)(t2:tau):bool=letget_l(t:tau):c_absloc=matchfind(proj_reft)withRefr->r.rl|Varv->raiseNoContents|_->raiseWellFormedintryletl1=get_lt1andl2=get_lt2inequal_c_abslocl1l2||not(C.is_empty(C.inter(collect_ptsetl1)(collect_ptsetl2)))withNoContents->false|ReachedTop->raiseUnknownLocationletpoints_to_aux(t:tau):absloclist=trymatchfind(proj_reft)withVarv->[]|Refr->C.elements(collect_ptsetr.rl)|_->raiseWellFormedwithNoContents->[]|ReachedTop->raiseUnknownLocationletpoints_to(lv:lvalue):Cil.varinfolist=letrecget_vinfosl:Cil.varinfolist=matchlwith[]->[]|(_,_,Someh)::t->h::get_vinfost|(_,_,None)::t->get_vinfostinget_vinfos(points_to_auxlv.contents)letepoints_to(t:tau):Cil.varinfolist=letrecget_vinfosl:Cil.varinfolist=matchlwith[]->[]|(_,_,Someh)::t->h::get_vinfost|(_,_,None)::t->get_vinfostinget_vinfos(points_to_auxt)letpoints_to_names(lv:lvalue):stringlist=Util.list_map(funv->v.vname)(points_tolv)letabsloc_points_to(lv:lvalue):absloclist=points_to_auxlv.contentsletabsloc_epoints_to(t:tau):absloclist=points_to_auxtletabsloc_of_lvalue(lv:lvalue):absloc=(findlv.l).locletabsloc_eq=equal_absloc