123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277(* Iain Proctor, Yoann Padioleau, Jiao Li
*
* Copyright (C) 2009-2010 Facebook
* Copyright (C) 2019 r2c
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation, with the
* special exception on linking described in file license.txt.
*
* This library 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 file
* license.txt for more details.
*)openCommonmoduleF=Controlflow(*****************************************************************************)(* Prelude *)(*****************************************************************************)(* Dataflow analysis "framework".
*
* The goal of a dataflow analysis is to store information about each
* variable at each program point, that is each node in a CFG
* (e.g. whether a variable is "live" at a program point).
* As you may want different kinds of information, the types below
* are polymorphic. But each take as a key a variable name.
*
* todo:
* - could use a functor, so would not have all those 'a?
* - do we need other kind of information than variable environment?
* Dataflow analysis talks only about variables? for the belief analysis
* we actually want expressions instead.
*)(*****************************************************************************)(* Types *)(*****************************************************************************)(* The comparison function uses only the name of a variable (a string), so
* two variables at different positions in the code will be agglomerated
* correctly in the Set or Map.
*)typevar=string(* convenient aliases *)moduleVarMap=Map.Make(String)moduleVarSet=Set.Make(String)moduleNodeiSet=Set.Make(Int)(* The final dataflow result; a map from each program point to a map containing
* information for each variables.
*
* opti: this used to be a 'NodeiMap.t' instead of an 'array' but 'nodei'
* are always int and array gives a 6x speedup according to Iain
* so let's use array.
*)type'amapping=('ainout)array(* the In and Out sets, as in Appel Modern Compiler in ML book *)and'ainout={in_env:'aenv;out_env:'aenv;}and'aenv='aVarMap.tletempty_env()=VarMap.emptyletempty_inout()={in_env=empty_env();out_env=empty_env()}(*****************************************************************************)(* Equality *)(*****************************************************************************)(* the environment is polymorphic, so we require to pass an eq for 'a *)leteq_enveqenv1env2=VarMap.equaleqenv1env2leteq_inouteqio1io2=leteqe=eq_enveqin(eqeio1.in_envio2.in_env)&&(eqeio1.out_envio2.out_env)(*****************************************************************************)(* Env manipulation *)(*****************************************************************************)let(varmap_union:('a->'a->'a)->'aenv->'aenv->'aenv)=fununion_openv1env2->letacc=env1inVarMap.fold(funvarxacc->letx'=tryunion_opx(VarMap.findvaracc)withNot_found->xinVarMap.addvarx'acc)env2acclet(varmap_diff:('a->'a->'a)->('a->bool)->'aenv->'aenv->'aenv)=fundiff_opis_emptyenv1env2->letacc=env1inVarMap.fold(funvarxacc->tryletdiff=diff_op(VarMap.findvaracc)xinifis_emptydiffthenVarMap.removevaraccelseVarMap.addvardiffaccwithNot_found->acc)env2acc(* useful helpers when the environment maps to a set of Nodes, e.g.,
* for reaching definitions.
*)let(union_env:NodeiSet.tenv->NodeiSet.tenv->NodeiSet.tenv)=funenv1env2->letacc=env1inVarMap.fold(funvarsetacc->letset2=tryNodeiSet.unionset(VarMap.findvaracc)withNot_found->setinVarMap.addvarset2acc)env2acclet(diff_env:NodeiSet.tenv->NodeiSet.tenv->NodeiSet.tenv)=funenv1env2->letacc=env1inVarMap.fold(funvarsetacc->tryletdiff=NodeiSet.diff(VarMap.findvaracc)setinifNodeiSet.is_emptydiffthenVarMap.removevaraccelseVarMap.addvardiffaccwithNot_found->acc)env2acclet(add_var_and_nodei_to_env:var->F.nodei->NodeiSet.tenv->NodeiSet.tenv)=funvarnienv->letset=tryNodeiSet.addni(VarMap.findvarenv)withNot_found->NodeiSet.singletonniinVarMap.addvarsetenvlet(add_vars_and_nodei_to_env:VarSet.t->F.nodei->NodeiSet.tenv->NodeiSet.tenv)=funvarsetnienv->letacc=envinVarSet.fold(funvaracc->add_var_and_nodei_to_envvarniacc)varsetacc(*****************************************************************************)(* Debugging support *)(*****************************************************************************)letcsv_appendsv=ifString.lengths==0thenvelses^","^vletarray_fold_left_idxf=letidx=ref0inArray.fold_left(funve->letr=fv!idxeinincridx;r)letns_to_strns="{"^NodeiSet.fold(funns->csv_appends(string_of_intn))ns""^"}"let(env_to_str:('a->string)->'aenv->string)=funval2strenv->VarMap.fold(fundnvs->s^dn^":"^val2strv^" ")env""let(inout_to_str:('a->string)->'ainout->string)=funval2strinout->spf"IN= %15s OUT = %15s"(env_to_strval2strinout.in_env)(env_to_strval2strinout.out_env)letmapping_to_str(fl:F.flow)val2strmapping=array_fold_left_idx(funsniv->s^(spf"%2d <- %7s: %15s %s\n"ni((fl#predecessorsni)#fold(funs(ni,_)->csv_appends(string_of_intni))"")(F.short_string_of_node(fl#nodes#findni))(inout_to_strval2strv)))""mappinglet(display_mapping:F.flow->'amapping->('a->string)->unit)=funflowmappingstring_of_val->pr(mapping_to_strflowstring_of_valmapping)(*****************************************************************************)(* Main generic entry point *)(*****************************************************************************)(* The transition/transfer function. It is usually made from the
* gens and kills.
*
* todo? having only a transfer function is enough ? do we need to pass
* extra information to it ? maybe only the mapping is not enough. For
* instance if in the code there is $x = &$g, a reference, then
* we may want later to have access to this information. Maybe we
* should pass an extra env argument ? Or maybe can encode this
* sharing of reference in the 'a, so that when one update the
* value associated to a var, its reference variable get also
* the update.
*)type'atransfn='amapping->F.nodei->'ainoutletrecfixpoint_workereqmappingtransflowsuccsworkset=ifNodeiSet.is_emptyworksetthenmappingelseletni=NodeiSet.chooseworksetinletwork'=NodeiSet.removeniworksetinletold=mapping.(ni)inletnew_=transmappingniinletwork''=ifeq_inouteqoldnew_thenwork'elsebegin(mapping.(ni)<-new_;NodeiSet.unionwork'(succsflowni))endinfixpoint_workereqmappingtransflowsuccswork''letforward_succs(f:F.flow)n=(f#successorsn)#fold(funs(ni,_)->NodeiSet.addnis)NodeiSet.emptyletbackward_succs(f:F.flow)n=(f#predecessorsn)#fold(funs(ni,_)->NodeiSet.addnis)NodeiSet.emptylet(fixpoint:eq:('a->'a->bool)->init:'amapping->trans:'atransfn->flow:F.flow->forward:bool->'amapping)=fun~eq~init~trans~flow~forward->letsuccs=ifforwardthenforward_succselsebackward_succsinletwork=flow#nodes#fold(funs(ni,_)->NodeiSet.addnis)NodeiSet.emptyinfixpoint_workereqinittransflowsuccswork(*****************************************************************************)(* Helpers *)(*****************************************************************************)letnew_node_array(f:F.flow)v=letnb_nodes=f#nb_nodesinletmax_nodei=ref(-1)inf#nodes#tolist|>List.iter(fun(ni,_nod)->(* actually there are some del_node done in cfg_build, for
* switch, so sometimes ni is >= len
*
* old:
* if ni >= nb_nodes
* then pr2 "the CFG nodei is bigger than the number of nodes"
*)ifni>!max_nodeithenmax_nodei:=ni;);assert(!max_nodei+1>=nb_nodes);Array.make(!max_nodei+1)v