123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431(* Js_of_ocaml compiler
* http://www.ocsigen.org/js_of_ocaml/
* Copyright (C) 2010 Jérôme Vouillon
* Laboratoire PPS - CNRS Université Paris Diderot
*
* This program is free software; 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, with linking exception;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*)open!Stdlibletdebug=Debug.find"flow"lettimes=Debug.find"times"openCode(****)letadd_var=Var.ISet.addtypedef=|PhiofVar.Set.t|ExprofCode.expr|Paramtypeinfo={info_defs:defarray;info_known_origins:Code.Var.Set.tCode.Var.Tbl.t;info_maybe_unknown:boolCode.Var.Tbl.t;info_possibly_mutable:boolarray}letupdate_def{info_defs;_}xexp=letidx=Code.Var.idxxininfo_defs.(idx)<-Exprexpletundefined=PhiVar.Set.emptyletis_undefinedd=matchdwith|Phis->Var.Set.is_emptys|_->falseletadd_expr_defdefsxe=letidx=Var.idxxinassert(is_undefineddefs.(idx));defs.(idx)<-Expreletadd_assign_defvarsdefsxy=add_varvarsx;letidx=Var.idxxinmatchdefs.(idx)with|Expr_|Param->assertfalse|Phis->defs.(idx)<-Phi(Var.Set.addys)letadd_param_defvarsdefsx=add_varvarsx;letidx=Var.idxxinassert(is_undefineddefs.(idx)||Poly.(defs.(idx)=Param));defs.(idx)<-Param(* x depends on y *)letadd_depdepsxy=letidx=Var.idxyindeps.(idx)<-Var.Set.addxdeps.(idx)letrecarg_depsvarsdepsdefsparamsargs=matchparams,argswith|x::params,y::args->add_depdepsxy;add_assign_defvarsdefsxy;arg_depsvarsdepsdefsparamsargs|_->()letcont_depsblocksvarsdepsdefs(pc,args)=letblock=Addr.Map.findpcblocksinarg_depsvarsdepsdefsblock.paramsargsletexpr_depsblocksvarsdepsdefsxe=matchewith|Constant_|Apply_|Prim_->()|Closure(l,cont)->List.iterl~f:(funx->add_param_defvarsdefsx);cont_depsblocksvarsdepsdefscont|Block(_,a,_)->Array.itera~f:(funy->add_depdepsxy)|Field(y,_)->add_depdepsxyletprogram_deps{blocks;_}=letnv=Var.count()inletvars=Var.ISet.empty()inletdeps=Array.makenvVar.Set.emptyinletdefs=Array.makenvundefinedinAddr.Map.iter(fun_block->List.iterblock.body~f:(funi->matchiwith|Let(x,e)->add_varvarsx;add_expr_defdefsxe;expr_depsblocksvarsdepsdefsxe|Set_field_|Array_set_|Offset_ref_->());Option.iterblock.handler~f:(fun(x,cont)->add_param_defvarsdefsx;cont_depsblocksvarsdepsdefscont);matchblock.branchwith|Return_|Raise_|Stop->()|Branchcont|Poptrap(cont,_)->cont_depsblocksvarsdepsdefscont|Cond(_,cont1,cont2)->cont_depsblocksvarsdepsdefscont1;cont_depsblocksvarsdepsdefscont2|Switch(_,a1,a2)->Array.itera1~f:(funcont->cont_depsblocksvarsdepsdefscont);Array.itera2~f:(funcont->cont_depsblocksvarsdepsdefscont)|Pushtrap(cont,_,_,_)->cont_depsblocksvarsdepsdefscont)blocks;vars,deps,defsletvar_set_liftfs=Var.Set.fold(funys->Var.Set.union(fy)s)sVar.Set.emptyletpropagate1depsdefsstx=matchdefs.(Var.idxx)with|Param->Var.Set.singletonx|Phis->var_set_lift(funy->Var.Tbl.getsty)s|Expre->(matchewith|Constant_|Apply_|Prim_|Closure_|Block_->Var.Set.singletonx|Field(y,n)->var_set_lift(funz->matchdefs.(Var.idxz)with|Expr(Block(_,a,_))whenn<Array.lengtha->lett=a.(n)inadd_depdepsxt;Var.Tbl.getstt|Phi_|Param|Expr_->Var.Set.empty)(Var.Tbl.getsty))moduleG=Dgraph.Make_Imperative(Var)(Var.ISet)(Var.Tbl)moduleDomain1=structtypet=Var.Set.tletequal=Var.Set.equalletbot=Var.Set.emptyendmoduleSolver1=G.Solver(Domain1)letsolver1varsdepsdefs=letg={G.domain=vars;G.iter_children=(funfx->Var.Set.iterfdeps.(Var.idxx))}inSolver1.f()g(propagate1depsdefs)(****)typemutability_state={defs:defarray;known_origins:Code.Var.Set.tCode.Var.Tbl.t;may_escape:boolarray;possibly_mutable:boolarray}letrecblock_escapestx=Var.Set.iter(funy->letidx=Var.idxyinifnotst.may_escape.(idx)then(st.may_escape.(idx)<-true;st.possibly_mutable.(idx)<-true;matchst.defs.(Var.idxy)with|Expr(Block(_,l,_))->Array.iterl~f:(funz->block_escapestz)|_->()))(Var.Tbl.getst.known_originsx)letexpr_escapest_xe=matchewith|Constant_|Closure_|Block_|Field_->()|Apply(_,l,_)->List.iterl~f:(funx->block_escapestx)|Prim((Vectlength|Array_get|Not|IsInt|Eq|Neq|Lt|Le|Ult),_)->()|Prim(Externname,l)->letka=matchPrimitive.kind_argsnamewith|Somel->l|None->(matchPrimitive.kindnamewith|`Mutable|`Mutator->[]|`Pure->List.mapl~f:(fun_->`Const))inletrecloopargska=matchargs,kawith|[],_->()|Pc_::ax,[]->loopax[]|Pva::ax,[]->block_escapesta;loopax[]|a::ax,k::kx->(matcha,kwith|_,`Const|Pc_,_->()|Pvv,`Shallow_const->(matchst.defs.(Var.idxv)with|Expr(Block(_,a,_))->Array.itera~f:(funx->block_escapestx)|_->block_escapestv)|Pvv,`Object_literal->(matchst.defs.(Var.idxv)with|Expr(Block(_,a,_))->Array.itera~f:(funx->matchst.defs.(Var.idxx)with|Expr(Block(_,[|_k;v|],_))->block_escapestv|_->block_escapestx)|_->block_escapestv)|Pvv,`Mutable->block_escapestv);loopaxkxinlooplkaletprogram_escapedefsknown_origins{blocks;_}=letnv=Var.count()inletmay_escape=Array.makenvfalseinletpossibly_mutable=Array.makenvfalseinletst={defs;known_origins;may_escape;possibly_mutable}inAddr.Map.iter(fun_block->List.iterblock.body~f:(funi->matchiwith|Let(x,e)->expr_escapestxe|Set_field(x,_,y)|Array_set(x,_,y)->Var.Set.iter(funy->possibly_mutable.(Var.idxy)<-true)(Var.Tbl.getknown_originsx);block_escapesty|Offset_ref(x,_)->Var.Set.iter(funy->possibly_mutable.(Var.idxy)<-true)(Var.Tbl.getknown_originsx));matchblock.branchwith|Returnx|Raise(x,_)->block_escapestx|Stop|Branch_|Cond_|Switch_|Pushtrap_|Poptrap_->())blocks;possibly_mutable(****)letpropagate2?(skip_param=false)defsknown_originspossibly_mutablestx=matchdefs.(Var.idxx)with|Param->skip_param|Phis->Var.Set.exists(funy->Var.Tbl.getsty)s|Expre->(matchewith|Constant_|Closure_|Apply_|Prim_|Block_->false|Field(y,n)->Var.Tbl.getsty||Var.Set.exists(funz->matchdefs.(Var.idxz)with|Expr(Block(_,a,_))->n>=Array.lengtha||possibly_mutable.(Var.idxz)||Var.Tbl.getsta.(n)|Phi_|Param|Expr_->true)(Var.Tbl.getknown_originsy))moduleDomain2=structtypet=boolletequal=Bool.equalletbot=falseendmoduleSolver2=G.Solver(Domain2)letsolver2?skip_paramvarsdepsdefsknown_originspossibly_mutable=letg={G.domain=vars;G.iter_children=(funfx->Var.Set.iterfdeps.(Var.idxx))}inSolver2.f()g(propagate2?skip_paramdefsknown_originspossibly_mutable)letget_approx{info_defs=_;info_known_origins;info_maybe_unknown;_}ftopjoinx=lets=Var.Tbl.getinfo_known_originsxinifVar.Tbl.getinfo_maybe_unknownxthentopelsematchVar.Set.cardinalswith|0->top|1->f(Var.Set.chooses)|_->Var.Set.fold(funxu->join(fx)u)s(f(Var.Set.chooses))letthe_def_ofinfox=matchxwith|Pvx->get_approxinfo(funx->matchinfo.info_defs.(Var.idxx)with|Expr(Constant(Float_|Int_|IString_)ase)->Somee|Expr(Constant(String_)ase)whenConfig.Flag.safe_string()->Somee|Expre->ifinfo.info_possibly_mutable.(Var.idxx)thenNoneelseSomee|_->None)None(fun__->None)x|Pcc->Some(Constantc)letthe_const_ofinfox=matchxwith|Pvx->get_approxinfo(funx->matchinfo.info_defs.(Var.idxx)with|Expr(Constant((Float_|Int_|IString_)asc))->Somec|Expr(Constant(String_asc))whenConfig.Flag.safe_string()->Somec|Expr(Constantc)->ifinfo.info_possibly_mutable.(Var.idxx)thenNoneelseSomec|_->None)None(funuv->matchu,vwith|Somei,SomejwhenPoly.(Code.constant_equalij=Sometrue)->u|_->None)x|Pcc->Somecletthe_intinfox=matchthe_const_ofinfoxwith|Some(Inti)->Somei|_->Noneletthe_string_ofinfox=matchthe_const_ofinfoxwith|Some(Stringi|IStringi)->Somei|_->None(*XXX Maybe we could iterate? *)letdirect_approxinfox=matchinfo.info_defs.(Var.idxx)with|Expr(Field(y,n))->get_approxinfo(funz->ifinfo.info_possibly_mutable.(Var.idxz)thenNoneelsematchinfo.info_defs.(Var.idxz)with|Expr(Block(_,a,_))whenn<Array.lengtha->Somea.(n)|_->None)None(funuv->matchu,vwith|Somen,SomemwhenVar.comparenm=0->u|_->None)y|_->Noneletbuild_substinfovars=letnv=Var.count()inletsubst=Array.makenvNoneinVar.ISet.iter(funx->letu=Var.Tbl.getinfo.info_maybe_unknownxin(ifnotuthenlets=Var.Tbl.getinfo.info_known_originsxinifVar.Set.cardinals=1thensubst.(Var.idxx)<-Some(Var.Set.chooses));ifOption.is_nonesubst.(Var.idxx)thensubst.(Var.idxx)<-direct_approxinfox;matchsubst.(Var.idxx)with|None->()|Somey->Var.propagate_namexy)vars;subst(****)letf?skip_paramp=Code.invariantp;lett=Timer.make()inlett1=Timer.make()inletvars,deps,defs=program_depspiniftimes()thenFormat.eprintf" flow analysis 1: %a@."Timer.printt1;lett2=Timer.make()inletknown_origins=solver1varsdepsdefsiniftimes()thenFormat.eprintf" flow analysis 2: %a@."Timer.printt2;lett3=Timer.make()inletpossibly_mutable=program_escapedefsknown_originspiniftimes()thenFormat.eprintf" flow analysis 3: %a@."Timer.printt3;lett4=Timer.make()inletmaybe_unknown=solver2?skip_paramvarsdepsdefsknown_originspossibly_mutableiniftimes()thenFormat.eprintf" flow analysis 4: %a@."Timer.printt4;ifdebug()thenVar.ISet.iter(funx->lets=Var.Tbl.getknown_originsxinifnot(Var.Set.is_emptys)(*&& Var.Set.choose s <> x*)thenFormat.eprintf"%a: {%a} / %s@."Var.printxCode.Print.var_list(Var.Set.elementss)(ifVar.Tbl.getmaybe_unknownxthen"any"else"known"))vars;lett5=Timer.make()inletinfo={info_defs=defs;info_known_origins=known_origins;info_maybe_unknown=maybe_unknown;info_possibly_mutable=possibly_mutable}inlets=build_substinfovarsinletp=Subst.program(Subst.from_arrays)piniftimes()thenFormat.eprintf" flow analysis 5: %a@."Timer.printt5;iftimes()thenFormat.eprintf" flow analysis: %a@."Timer.printt;Code.invariantp;p,info