123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207(* Js_of_ocaml compiler
* http://www.ocsigen.org/js_of_ocaml/
*
* 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.
*)(* We compute which functions and which call points needs to be in CPS. *)open!Stdliblettimes=Debug.find"times"letdouble_translate()=matchConfig.effects()with|`Disabled|`Jspi->assertfalse|`Cps->false|`Double_translation->trueopenCodeletadd_var=Var.ISet.add(* x depends on y *)letadd_depdepsxy=letidx=Var.idxyindeps.(idx)<-Var.Set.addxdeps.(idx)letadd_tail_depdepsxy=ifnot(Var.Map.memx!deps)thendeps:=Var.Map.addxVar.Set.empty!deps;deps:=Var.Map.updatey(funs->Some(Var.Set.addx(Option.value~default:Var.Set.emptys)))!depsletrecblock_iter_last~fl=matchlwith|[]->()|[i]->ftruei|[i;Event_]->ftruei|i::l->ffalsei;block_iter_last~flletblock_deps~info~vars~tail_deps~deps~blocks~fun_namepc=letblock=Addr.Map.findpcblocksinblock_iter_lastblock.body~f:(funis_lasti->matchiwith|Let(x,Apply{f;_})->(add_varvarsx;(matchfun_namewith|None->()|Someg->add_varvarsg;(* If a call point is in CPS, then the englobing
function should be in CPS *)add_depdepsgx);matchVar.Tbl.getinfo.Global_flow.info_approximationfwith|Top->()|Values{known;others}->letknown_tail_call=(notothers)&&is_last&&matchblock.branchwith|Returnx'->Var.equalxx'|_->falseinVar.Set.iter(fung->add_varvarsg;(ifknown_tail_callthenmatchfun_namewith|None->()|Somef->add_tail_deptail_depsfg);(* If a called function is in CPS, then the call
point is in CPS *)add_depdepsxg;(* Conversally, if a call point is in CPS then all
called functions must be in CPS *)ifnot(double_translate())thenadd_depdepsgx)known)|Let(x,Prim(Extern("%perform"|"%reperform"|"%resume"),_))->(add_varvarsx;matchfun_namewith|None->()|Somef->add_varvarsf;(* If a function contains effect primitives, it must be
in CPS *)add_depdepsfx)|Let(x,Closure_)->add_varvarsx|Let(_,(Prim_|Block_|Constant_|Field_|Special_))|Event_|Assign_|Set_field_|Offset_ref_|Array_set_->())letprogram_deps~info~vars~tail_deps~depsp=fold_closuresp(funfun_name_(pc,_)_->traverse{fold=Code.fold_children}(funpc()->block_deps~info~vars~tail_deps~deps~blocks:p.blocks~fun_namepc)pcp.blocks())()moduleDomain=structtypet=boolletequal=Bool.equalletbot=falseendmoduleG=Dgraph.Make_Imperative(Var)(Var.ISet)(Var.Tbl)moduleSolver=G.Solver(Domain)letfold_childrengfxacc=letacc=refaccing.G.iter_children(funy->acc:=fy!acc)x;!accletcps_needed~info~in_mutual_recursion~rev_depsstx=(* Mutually recursive functions are turned into CPS for tail
optimization *)Var.Set.memxin_mutual_recursion||letidx=Var.idxxinfold_childrenrev_deps(funyacc->acc||Var.Tbl.getsty)xfalse||matchinfo.Global_flow.info_defs.(idx)with|Expr(Apply{f;_})->((* If we don't know all possible functions at a call point, it
must be in CPS *)matchVar.Tbl.getinfo.Global_flow.info_approximationfwith|Top->true|Values{others;_}->others)|Expr(Closure_)->(not(double_translate()))&&(* If a function escapes, it must be in CPS *)Var.ISet.meminfo.Global_flow.info_may_escapex|Expr(Prim(Extern("%perform"|"%reperform"|"%resume"),_))->(* Effects primitives are in CPS *)true|Expr(Prim_|Block_|Constant_|Field_|Special_)|Phi_->falsemoduleSCC=Strongly_connected_components.Make(structtypet=Var.tmoduleSet=Var.SetmoduleMap=Var.Mapend)letfind_mutually_recursive_callstail_deps=letscc=SCC.component_graph!tail_depsinArray.fold_left~f:(funs(c,_)->matchcwith|SCC.No_loop_->s|Has_loopl->List.fold_left~f:(funsx->Var.Set.addxs)l~init:s)~init:Var.Set.emptysccletannotstxi=match(xi:Print.xinstr)with|Instr(Let(x,_))whenVar.Set.memxst->"*"|_->" "letfpinfo=lett=Timer.make()inlett1=Timer.make()inletnv=Var.count()inletvars=Var.ISet.empty()inletdeps=Array.makenvVar.Set.emptyinlettail_deps=refVar.Map.emptyinprogram_deps~info~vars~tail_deps~depsp;iftimes()thenFormat.eprintf" fun analysis (initialize): %a@."Timer.printt1;lett2=Timer.make()inletin_mutual_recursion=find_mutually_recursive_callstail_depsiniftimes()thenFormat.eprintf" fun analysis (tail calls): %a@."Timer.printt2;lett3=Timer.make()inletg={G.domain=vars;iter_children=(funfx->Var.Set.iterfdeps.(Var.idxx))}inletrev_deps=G.invert()ginletres=Solver.f()g(cps_needed~info~in_mutual_recursion~rev_deps)iniftimes()thenFormat.eprintf" fun analysis (solve): %a@."Timer.printt3;lets=refVar.Set.emptyinVar.Tbl.iter(funxv->ifvthens:=Var.Set.addx!s)res;iftimes()thenFormat.eprintf" fun analysis: %a@."Timer.printt;!s