123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412(* 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!StdlibopenCodeletdebug_tc=Debug.find"gen_tc"typeclosure_info={f_name:Code.Var.t;args:Code.Var.tlist;cont:Code.cont;tc:Code.Addr.Set.tCode.Var.Map.t}type'aint_ext={int:'a;ext:'a}moduleSCC=Strongly_connected_components.Make(Var)letadd_multikvmap=letset=tryVar.Map.findkmapwithNot_found->Addr.Set.emptyinVar.Map.addk(Addr.Set.addvset)mapletrectailcallpcblocksvisitedtc=ifAddr.Set.mempcvisitedthenvisited,tcelseletvisited=Addr.Set.addpcvisitedinletblock=Addr.Map.findpcblocksinlettc_opt=matchblock.branchwith|Returnx->(matchList.lastblock.bodywith|Some(Let(y,Apply(z,_,true)))whenCode.Var.comparexy=0->Some(add_multizpctc)|None->None|Some_->None)|_->Noneinmatchtc_optwith|Sometc->visited,tc|None->Code.fold_childrenblockspc(funpc(visited,tc)->tailcallpcblocksvisitedtc)(visited,tc)letreccollect_closuresblocksl=matchlwith|Let(f_name,Closure(args,((pc,_)ascont)))::rem->lettc=snd(tailcallpcblocksAddr.Set.emptyVar.Map.empty)inletl,rem=collect_closuresblocksremin{f_name;args;cont;tc}::l,rem|rem->[],remletgroup_closuresclosures=letnames=List.fold_leftclosures~init:Var.Set.empty~f:(funnamesx->Var.Set.addx.f_namenames)inletclosures_map=List.fold_leftclosures~init:Var.Map.empty~f:(funclosures_mapx->Var.Map.addx.f_namexclosures_map)inletgraph=List.fold_leftclosures~init:Var.Map.empty~f:(fungraphx->lettc=Var.Map.fold(funx_tc->Var.Set.addxtc)x.tcVar.Set.emptyinlettc=Var.Set.internamestcinVar.Map.addx.f_nametcgraph)inclosures_map,SCC.connected_components_sorted_from_roots_to_leafgraphmoduleTrampoline=structletdirect_call_blockblock~counter~x~f~args=letcounter_plus_1=Code.Var.forkcounterinletreturn=Code.Var.forkxin{blockwithparams=[];body=[Let(counter_plus_1,Prim(Extern"%int_add",[Pvcounter;Pc(Int1l)]));Let(return,Apply(f,counter_plus_1::args,true))];branch=Returnreturn}letbounce_call_blockblock~x~f~args=letreturn=Code.Var.forkxinletnew_args=Code.Var.fresh()in{blockwithparams=[];body=[Let(new_args,Prim(Extern"%js_array",Pc(Int0l)::List.mapargs~f:(funx->Pvx)));Let(return,Prim(Extern"caml_trampoline_return",[Pvf;Pvnew_args]))];branch=Returnreturn}letwrapper_blockf~args~counter=letresult1=Code.Var.fresh()inletresult2=Code.Var.fresh()inletblock={params=[];handler=None;body=[Let(counter,Constant(Int0l));Let(result1,Apply(f,counter::args,true));Let(result2,Prim(Extern"caml_trampoline",[Pvresult1]))];branch=Returnresult2}inblockletwrapper_closurepcargs=Closure(args,(pc,[]))letffree_pcblocksclosures_mapcomponent=matchcomponentwith|SCC.No_loopid->letci=Var.Map.findidclosures_mapinletinstr=Let(ci.f_name,Closure(ci.args,ci.cont))infree_pc,blocks,{int=[];ext=[instr]}|SCC.Has_loopall->ifdebug_tc()then(Format.eprintf"Detect cycles of size (%d).\n%!"(List.lengthall);Format.eprintf"%s\n%!"(String.concat~sep:", "(List.mapall~f:(funx->Var.to_stringx))));letall=List.mapall~f:(funid->Code.Var.fresh_n"counter",Var.Map.findidclosures_map)inletblocks,free_pc,instrs,instrs_wrapper=List.fold_leftall~init:(blocks,free_pc,[],[])~f:(fun(blocks,free_pc,instrs,instrs_wrapper)(counter,ci)->ifdebug_tc()thenFormat.eprintf"Rewriting for %s\n%!"(Var.to_stringci.f_name);letnew_f=Code.Var.forkci.f_nameinletnew_args=List.mapci.args~f:Code.Var.forkinletwrapper_pc=free_pcinletfree_pc=free_pc+1inletnew_counter=Code.Var.forkcounterinletwrapper_block=wrapper_blocknew_f~args:new_args~counter:new_counterinletblocks=Addr.Map.addwrapper_pcwrapper_blockblocksinletinstr_wrapper=Let(ci.f_name,wrapper_closurewrapper_pcnew_args)inletinstr_real=Let(new_f,Closure(counter::ci.args,ci.cont))inletcounter_and_pc=List.fold_leftall~init:[]~f:(funacc(counter,ci2)->tryletpcs=Addr.Set.elements(Var.Map.findci.f_nameci2.tc)inList.mappcs~f:(funx->counter,x)@accwithNot_found->acc)inletblocks,free_pc=List.fold_leftcounter_and_pc~init:(blocks,free_pc)~f:(fun(blocks,free_pc)(counter,pc)->ifdebug_tc()thenFormat.eprintf"Rewriting tc in %d\n%!"pc;letblock=Addr.Map.findpcblocksinletdirect_call_pc=free_pcinletbounce_call_pc=free_pc+1inletfree_pc=free_pc+2inmatchList.revblock.bodywith|Let(x,Apply(f,args,true))::rem_rev->assert(Var.equalfci.f_name);letblocks=Addr.Map.adddirect_call_pc(direct_call_blockblock~counter~x~f:new_f~args)blocksinletblocks=Addr.Map.addbounce_call_pc(bounce_call_blockblock~x~f:new_f~args)blocksinletdirect=Code.Var.fresh()inletbranch=Cond(IsTrue,direct,(direct_call_pc,[]),(bounce_call_pc,[]))inletlast=Let(direct,Prim(Lt,[Pvcounter;Pc(Int(Int32.of_int(Config.Param.tailcall_max_depth())))]))inletblock={blockwithbody=List.rev(last::rem_rev);branch}inletblocks=Addr.Map.removepcblocksinAddr.Map.addpcblockblocks,free_pc|_->assertfalse)inblocks,free_pc,instr_real::instrs,instr_wrapper::instrs_wrapper)infree_pc,blocks,{int=instrs;ext=instrs_wrapper}endmoduleIdent=structletffree_pcblocksclosures_mapcomponent=matchcomponentwith|SCC.No_loopid->letci=Var.Map.findidclosures_mapinletinstr=Let(ci.f_name,Closure(ci.args,ci.cont))infree_pc,blocks,{int=[];ext=[instr]}|SCC.Has_loopids->letinstrs=List.mapids~f:(funid->letci=Var.Map.findidclosures_mapinletinstr=Let(ci.f_name,Closure(ci.args,ci.cont))ininstr)infree_pc,blocks,{int=[];ext=instrs}endletrewrite_tcfree_pcblocksclosures_mapcomponent=letopenConfig.Paraminmatchtailcall_optim()with|TcNone->Ident.ffree_pcblocksclosures_mapcomponent|TcTrampoline->Trampoline.ffree_pcblocksclosures_mapcomponentletrewrite_mutablefree_pcblocksmutated_varsrewrite_list{int=closures_intern;ext=closures_extern}=letinternal_and_external=closures_intern@closures_externinassert(not(List.is_emptyclosures_extern));letall_mut,names=List.fold_leftinternal_and_external~init:(Var.Set.empty,Var.Set.empty)~f:(fun(all_mut,names)i->matchiwith|Let(x,Closure(_,(pc,_)))->letall_mut=tryVar.Set.unionall_mut(Addr.Map.findpcmutated_vars)withNot_found->all_mutinletnames=Var.Set.addxnamesinall_mut,names|_->assertfalse)inletvars=Var.Set.elements(Var.Set.diffall_mutnames)inifList.is_emptyvarsthenfree_pc,blocks,internal_and_externalelsematchinternal_and_externalwith|[Let(x,Closure(params,(pc,pc_args)))]->letnew_pc=free_pcinletfree_pc=free_pc+1inletclosure=Code.Var.forkxinletargs=List.mapvars~f:Code.Var.forkinletnew_x=Code.Var.forkxinletmapping=Subst.from_map(Subst.build_mapping(x::vars)(new_x::args))inrewrite_list:=(mapping,pc)::!rewrite_list;letnew_block={params=[];handler=None;body=[Let(new_x,Closure(params,(pc,List.mappc_args~f:mapping)))];branch=Returnnew_x}inletblocks=Addr.Map.addnew_pcnew_blockblocksinletbody=[Let(closure,Closure(args,(new_pc,[])));Let(x,Apply(closure,vars,true))]infree_pc,blocks,body|_->letnew_pc=free_pcinletfree_pc=free_pc+1inletclosure=Code.Var.fresh_n"closures"inletclosure'=Code.Var.fresh_n"closures"inletb=Code.Var.fresh_n"block"inletargs=List.mapvars~f:Code.Var.forkinletpcs=List.mapinternal_and_external~f:(function|Let(_,Closure(_,(pc,_)))->pc|_->assertfalse)inletold_xs=List.mapclosures_extern~f:(function|Let(x,Closure_)->x|_->assertfalse)inletnew_xs=List.mapold_xs~f:Code.Var.forkinletmapping=Subst.from_map(Subst.build_mapping(old_xs@vars)(new_xs@args))inrewrite_list:=List.mappcs~f:(funpc->mapping,pc)@!rewrite_list;letnew_block=letproj=List.map2closures_externnew_xs~f:(funclnew_x->matchclwith|Let(_,Closure(params,(pc,pc_args)))->Let(new_x,Closure(params,(pc,List.mappc_args~f:mapping)))|_->assertfalse)in{params=[];handler=None;body=closures_intern@proj@[Let(b,Block(0,Array.of_listnew_xs,NotArray))];branch=Returnb}inletblocks=Addr.Map.addnew_pcnew_blockblocksinletbody=[Let(closure,Closure(args,(new_pc,[])));Let(closure',Apply(closure,vars,true))]@List.mapiclosures_extern~f:(funix->matchxwith|Let(x,Closure_)->Let(x,Field(closure',i))|_->assertfalse)infree_pc,blocks,bodyletrecrewrite_closuresmutated_varsrewrite_listfree_pcblocksbody:int*_*_list=matchbodywith|Let(_,Closure_)::_->letclosures,rem=collect_closuresblocksbodyinletclosures_map,components=group_closuresclosuresinletfree_pc,blocks,closures=List.fold_left(Array.to_listcomponents)~init:(free_pc,blocks,[])~f:(fun(free_pc,blocks,acc)component->letfree_pc,blocks,closures=rewrite_tcfree_pcblocksclosures_mapcomponentinletfree_pc,blocks,intrs=rewrite_mutablefree_pcblocksmutated_varsrewrite_listclosuresinfree_pc,blocks,intrs::acc)inletfree_pc,blocks,rem=rewrite_closuresmutated_varsrewrite_listfree_pcblocksreminfree_pc,blocks,List.flattenclosures@rem|i::rem->letfree_pc,blocks,rem=rewrite_closuresmutated_varsrewrite_listfree_pcblocksreminfree_pc,blocks,i::rem|[]->free_pc,blocks,[]letf((pc,blocks,free_pc)asp):Code.program=Code.invariantp;letmutated_vars=Freevars.fpinletrewrite_list=ref[]inletblocks,free_pc=Addr.Map.fold(funpc_(blocks,free_pc)->(* make sure we have the latest version *)letblock=Addr.Map.findpcblocksinletfree_pc,blocks,body=rewrite_closuresmutated_varsrewrite_listfree_pcblocksblock.bodyinAddr.Map.addpc{blockwithbody}blocks,free_pc)blocks(blocks,free_pc)in(* Code.invariant (pc, blocks, free_pc); *)letp=List.fold_left!rewrite_list~init:(pc,blocks,free_pc)~f:(funprogram(mapping,pc)->Subst.contmappingpcprogram)inCode.invariantp;p