123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367(* 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!StdlibopenCodetypeprop={size:int;optimizable:bool}typeclosure_info={cl_params:Var.tlist;cl_cont:int*Var.tlist;cl_prop:prop;cl_simpl:(Var.Set.t*intVar.Map.t*bool*Var.Set.t)option}letblock_size{branch;body;_}=List.fold_left~f:(funni->matchiwith|Event_->n|_->n+1)~init:0body+matchbranchwith|Cond_->2|Switch(_,a1)->Array.lengtha1|_->0letsimple_functionblockssizenameparamspc=letbound_vars=ref(List.fold_left~f:(funsx->Var.Set.addxs)~init:Var.Set.emptyparams)inletfree_vars=refVar.Map.emptyinlettc=refVar.Set.emptyintry(* Ignore large functions *)ifsize>10thenraiseExit;Code.preorder_traverse{fold=Code.fold_children}(funpc()->letblock=Addr.Map.findpcblocksin(matchblock.branchwith(* We currenly disable inlining when raising and catching exception *)|Poptrap_|Pushtrap_->raiseExit|Raise_->raiseExit|Stop->raiseExit|Returnx->(matchList.lastblock.bodywith|None->()|Some(Let(y,Apply{f;_}))->(* track if some params are called in tail position *)ifCode.Var.equalxy&&List.memf~set:paramsthentc:=Var.Set.addf!tc|Some_->())|Branch_|Cond_|Switch_->());List.iterblock.body~f:(funi->matchiwith(* We currenly don't want to duplicate Closure *)|Let(_,Closure_)->raiseExit|_->());Freevars.iter_block_bound_vars(funx->bound_vars:=Var.Set.addx!bound_vars)block;Freevars.iter_block_free_vars(funx->ifnot(Var.Set.memx!bound_vars)thenfree_vars:=Var.Map.updatex(function|None->Some1|Somen->Some(succn))!free_vars)block)pcblocks();Some(!bound_vars,!free_vars,Var.Map.memname!free_vars,!tc)withExit->None(****)letoptimizableblockspc=Code.traverse{fold=Code.fold_children}(funpc{size;optimizable}->letb=Addr.Map.findpcblocksinletthis_size=block_sizebinletoptimizable=optimizable&&List.for_allb.body~f:(function|Let(_,Prim(Extern"caml_js_eval_string",_))->false|Let(_,Prim(Extern"debugger",_))->false|Let(_,Prim(Extern("caml_js_var"|"caml_js_expr"|"caml_pure_js_expr"),_))->(* TODO: we should be smarter here and look the generated js *)(* let's consider it this opmiziable *)true|_->true)in{optimizable;size=size+this_size})pcblocks{optimizable=true;size=0}letget_closures{blocks;_}=Addr.Map.fold(fun_blockclosures->List.fold_leftblock.body~init:closures~f:(funclosuresi->matchiwith|Let(x,Closure(cl_params,cl_cont))->(* we can compute this once during the pass
as the property won't change with inlining *)letcl_prop=optimizableblocks(fstcl_cont)inletcl_simpl=simple_functionblockscl_prop.sizexcl_params(fstcl_cont)inVar.Map.addx{cl_params;cl_cont;cl_prop;cl_simpl}closures|_->closures))blocksVar.Map.empty(****)letrewrite_blockpc'pcblocks=letblock=Addr.Map.findpcblocksinletblock=matchblock.branch,pc'with|Returny,Somepc'->{blockwithbranch=Branch(pc',[y])}|_->blockinAddr.Map.addpcblockblocksletrewrite_closureblockscont_pcclos_pc=Code.traverse{fold=Code.fold_children_skip_try_body}(rewrite_blockcont_pc)clos_pcblocksblocks(****)letrecargs_equalxsys=matchxs,yswith|[],[]->true|x::xs,Pvy::ys->Code.Var.comparexy=0&&args_equalxsys|_->falseletinline~first_class_primitiveslive_varsclosuresnamepc(outer,p)=letblock=Addr.Map.findpcp.blocksinletbody,(outer,branch,p)=List.fold_rightblock.body~init:([],(outer,block.branch,p))~f:(funi(rem,state)->matchiwith|Let(x,Apply{f;args;exact=true;_})whenVar.Map.memfclosures->(letouter,branch,p=stateinlet{cl_params=params;cl_cont=clos_cont;cl_prop={size=f_size;optimizable=f_optimizable};cl_simpl}=Var.Map.findfclosuresinletmap_param_to_arg=List.fold_left2~f:(funmapab->Var.Map.addabmap)~init:Var.Map.emptyparamsargsiniflive_vars.(Var.idxf)=1&&Bool.equalouter.optimizablef_optimizable(* Inlining the code of an optimizable function could
make this code unoptimized. (wrt to Jit compilers) *)&&f_size<Config.Param.inlining_limit()thenletblocks,cont_pc,free_pc=matchrem,branchwith|[],ReturnywhenVar.comparexy=0->(* We do not need a continuation block for tail calls *)p.blocks,None,p.free_pc|_->letfresh_addr=p.free_pcinletfree_pc=fresh_addr+1in(Addr.Map.addfresh_addr{params=[x];body=rem;branch}p.blocks,Somefresh_addr,free_pc)inletblocks=rewrite_closureblockscont_pc(fstclos_cont)in(* We do not really need this intermediate block.
It just avoids the need to find which function
parameters are used in the function body. *)letfresh_addr=free_pcinletfree_pc=fresh_addr+1inletblocks=Addr.Map.addfresh_addr{params;body=[];branch=Branchclos_cont}blocksinletouter={outerwithsize=outer.size+f_size}in[],(outer,Branch(fresh_addr,args),{pwithblocks;free_pc})elsematchcl_simplwith|Some(bound_vars,free_vars,recursive,tc_params)(* We inline/duplicate
- single instruction functions (f_size = 1)
- small funtions that call one of their arguments in
tail position when the argument is a direct closure
used only once. *)when(Code.Var.Set.exists(funx->letfarg_tc=Var.Map.findxmap_param_to_arginVar.Map.memfarg_tcclosures&&live_vars.(Var.idxfarg_tc)=1)tc_params||f_size<=1)&&((notrecursive)||matchnamewith|None->true|Somef'->not(Var.equalff'))->let()=(* Update live_vars *)Var.Map.iter(funfvc->ifnot(Var.equalfvf)thenletidx=Var.idxfvinlive_vars.(idx)<-live_vars.(idx)+c)free_vars;live_vars.(Var.idxf)<-live_vars.(Var.idxf)-1inletp,f,params,clos_cont=letbound_vars=Var.Set.addfbound_varsinDuplicate.closurep~bound_vars~f~params~cont:clos_continifrecursivethen(Let(f,Closure(params,clos_cont))::Let(x,Apply{f;args;exact=true})::rem,(outer,branch,p))elseletblocks,cont_pc,free_pc=matchrem,branchwith|[],ReturnywhenVar.comparexy=0->(* We do not need a continuation block for tail calls *)p.blocks,None,p.free_pc|_->letfresh_addr=p.free_pcinletfree_pc=fresh_addr+1in(Addr.Map.addfresh_addr{params=[x];body=rem;branch}p.blocks,Somefresh_addr,free_pc)inletblocks=rewrite_closureblockscont_pc(fstclos_cont)in(* We do not really need this intermediate block.
It just avoids the need to find which function
parameters are used in the function body. *)letfresh_addr=free_pcinletfree_pc=fresh_addr+1inletblocks=Addr.Map.addfresh_addr{params;body=[];branch=Branchclos_cont}blocksinletouter={outerwithsize=outer.size+f_size}in[],(outer,Branch(fresh_addr,args),{pwithblocks;free_pc})|_->i::rem,state)|Let(x,Closure(l,(pc,[])))whenfirst_class_primitives->(letblock=Addr.Map.findpcp.blocksinmatchblockwith|{body=([Let(y,Prim(Externprim,args))]|[Event_;Let(y,Prim(Externprim,args))]|[Event_;Let(y,Prim(Externprim,args));Event_]);branch=Returny';params=[]}->letlen=List.lengthlinifCode.Var.compareyy'=0&&Primitive.has_arityprimlen&&args_equallargsthenLet(x,Special(Alias_primprim))::rem,stateelsei::rem,state|_->i::rem,state)|_->i::rem,state)inouter,{pwithblocks=Addr.Map.addpc{blockwithbody;branch}p.blocks}(****)lettimes=Debug.find"times"letfplive_vars=letfirst_class_primitives=matchConfig.target()with|`JavaScript->not(Config.Flag.effects())|`Wasm->falseinCode.invariantp;lett=Timer.make()inletclosures=get_closurespinlet_closures,p=Code.fold_closures_innermost_firstp(funnamecl_params(pc,_)(closures,p)->lettraverseouter=Code.traverse{fold=Code.fold_children}(inline~first_class_primitiveslive_varsclosuresname)pcp.blocks(outer,p)inmatchnamewith|None->let_,p=traverse(optimizablep.blockspc)inclosures,p|Somex->letinfo=Var.Map.findxclosuresinletouter,p=traverseinfo.cl_propinletcl_simpl=simple_functionp.blocksouter.sizexcl_paramspcinletclosures=Var.Map.addx{infowithcl_prop=outer;cl_simpl}closuresinclosures,p)(closures,p)iniftimes()thenFormat.eprintf" inlining: %a@."Timer.printt;Code.invariantp;p