123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174(* 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=ControlflowmoduleD=DataflowmoduleNodeiSet=Dataflow.NodeiSetmoduleVarMap=Dataflow.VarMapmoduleVarSet=Dataflow.VarSet(*****************************************************************************)(* Prelude *)(*****************************************************************************)(* Example of dataflow analysis: reaching definitions *)(*****************************************************************************)(* Types *)(*****************************************************************************)(* For a reaching definitions analysis, the dataflow result is
* a map from each program point (as usual), to a map from each
* variable (as usual), to a set of nodes that define this variable
* that are visible at this program point.
*
* For instance on:
*
* 1: $a = 1;
* 2: if(...) {
* 3: $a = 2;
* 4: } else {
* 5: $a = 3;
* 6: }
* 7: echo $a;
*
* then at the program point (node index) 7, then for $a the nodei set
* is {3, 5}, but not '1'.
*)typereaching_mapping=Dataflow.NodeiSet.tDataflow.mapping(*****************************************************************************)(* Helpers *)(*****************************************************************************)(*****************************************************************************)(* Gen/Kill *)(*****************************************************************************)let(reaching_defs:F.flow->NodeiSet.tDataflow.env)=funflow->Dataflow_visitor.flow_fold_lv(funnivaracc->Dataflow.add_var_and_nodei_to_envvarniacc)VarMap.emptyflowlet(reaching_gens:F.flow->VarSet.tarray)=funflow->letarr=Dataflow.new_node_arrayflowVarSet.emptyinDataflow_visitor.flow_fold_lv(funnivararr->arr.(ni)<-VarSet.addvararr.(ni);arr)arrflowlet(reaching_kills:NodeiSet.tDataflow.env->F.flow->(NodeiSet.tDataflow.env)array)=fundefsflow->letarr=Dataflow.new_node_arrayflow(Dataflow.empty_env())inDataflow_visitor.flow_fold_lv(funnivararr->letset=NodeiSet.removeni(VarMap.findvardefs)inarr.(ni)<-VarMap.addvarsetarr.(ni);arr)arrflow(*****************************************************************************)(* Transfer *)(*****************************************************************************)(*
* This algorithm is taken from Modern Compiler Implementation in ML, Appel,
* 1998, pp. 382.
*
* The transfer is setting:
* - in'[n] = U_{p in pred[n]} out[p]
* - out'[n] = gen[n] U (in[n] - kill[n])
*)let(reaching_transfer:gen:VarSet.tarray->kill:(NodeiSet.tDataflow.env)array->flow:F.flow->NodeiSet.tDataflow.transfn)=fun~gen~kill~flow->(* the transfer function to update the mapping at node index ni *)funmappingni->letin'=(flow#predecessorsni)#fold(funacc(ni_pred,_)->Dataflow.union_envaccmapping.(ni_pred).D.out_env)VarMap.emptyinletin_minus_kill=Dataflow.diff_envin'kill.(ni)inletout'=Dataflow.add_vars_and_nodei_to_envgen.(ni)niin_minus_killin{D.in_env=in';out_env=out'}(*****************************************************************************)(* Entry point *)(*****************************************************************************)let(reaching_fixpoint:F.flow->reaching_mapping)=funflow->letdefs=reaching_defsflowinletgen=reaching_gensflowinletkill=reaching_killsdefsflowinDataflow.fixpoint~eq:NodeiSet.equal~init:(Dataflow.new_node_arrayflow(Dataflow.empty_inout()))~trans:(reaching_transfer~gen~kill~flow)~forward:true~flow(*****************************************************************************)(* Dataflow pretty printing *)(*****************************************************************************)letstring_of_niflowni=letnode=flow#nodes#assocniinmatchnode.F.iwith|None->"Unknown location"|Some(info)->letinfo=Parse_info.token_location_of_infoinfoinspf"%s:%d:%d: "info.Parse_info.fileinfo.Parse_info.lineinfo.Parse_info.columnletdisplay_reaching_dflowflowmapping=letarr=Dataflow.new_node_arrayflowtruein(* Set the flag to false if the node has defined anything *)letflow_lv_fn=funni_vararr->letnode=flow#nodes#assocniin(matchnode.F.nwith|F.SimpleStmt(F.ExprStmt_)->arr.(ni)<-false|_->());arrinletarr=Dataflow_visitor.flow_fold_lvflow_lv_fnarrflowin(* Now flag the def if it is ever used on rhs *)letflow_rv_fn=funnivararr->letin_env=mapping.(ni).D.in_envin(tryletns=VarMap.findvarin_envinNodeiSet.iter(funn->arr.(n)<-true)nswithNot_found->pr(spf"%s: Undefined variable %s"(string_of_niflowni)var));arrinletarr=Dataflow_visitor.flow_fold_rvflow_rv_fnarrflowinarr|>Array.iteri(funix->if(notx)thenpr(spf"%s: Dead Assignment"(string_of_niflowi));)