123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283(* 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"gen"lettimes=Debug.find"times"letcps_transform()=matchConfig.effects()with|`Cps|`Double_translation->true|`Disabled->false|`Jspi->assertfalseopenCodemoduleJ=Javascript(****)letstring_of_sets=String.concat~sep:", "(List.map~f:Addr.to_string(Addr.Set.elementss))letreclist_group_rec~equalfglbmn=matchlwith|[]->List.rev((b,List.revm)::n)|a::r->letfa=fainifequalfabthenlist_group_rec~equalfgrb(ga::m)nelselist_group_rec~equalfgrfa[ga]((b,List.revm)::n)letlist_group~equalfgl=matchlwith|[]->[]|a::r->list_group_rec~equalfgr(fa)[ga][](****)typefall_through=|BlockofAddr.t|Returntypeapplication_description={arity:int;exact:bool;trampolined:bool;in_cps:bool}moduleShare=structmoduleAppMap=Map.Make(structtypet=application_descriptionletcompare=Poly.compareend)type'aaux={byte_strings:'aStringMap.t;utf_strings:'aStringMap.t;applies:'aAppMap.t;prims:'aStringMap.t}letempty_aux={prims=StringMap.empty;byte_strings=StringMap.empty;utf_strings=StringMap.empty;applies=AppMap.empty}typet={count:intaux;mutablevars:J.identaux;alias_prims:bool;alias_strings:bool;alias_apply:bool}letadd_byte_stringst=letn=tryStringMap.findst.byte_stringswithNot_found->0in{twithbyte_strings=StringMap.adds(n+1)t.byte_strings}letadd_utf_stringst=letn=tryStringMap.findst.utf_stringswithNot_found->0in{twithutf_strings=StringMap.adds(n+1)t.utf_strings}letadd_primst=letn=tryStringMap.findst.primswithNot_found->0inifn<0thentelse{twithprims=StringMap.adds(n+1)t.prims}letadd_special_prim_if_existsst=ifPrimitive.existssthen{twithprims=StringMap.adds(-1)t.prims}elsetletadd_applyit=letn=tryAppMap.findit.applieswithNot_found->0in{twithapplies=AppMap.addi(n+1)t.applies}letadd_code_stringsshare=letshare=ifString.is_asciisthenadd_utf_stringsshareelseadd_byte_stringsshareinifConfig.Flag.use_js_string()thenshareelseadd_prim"caml_string_of_jsbytes"shareletadd_code_native_string(s:Code.Native_string.t)share=matchswith|Utf(Utf8s)->add_utf_stringsshare|Bytes->add_byte_stringsshareletrecget_constantct=matchcwith|Strings->add_code_stringst|NativeStrings->add_code_native_stringst|Tuple(_,args,_)->Array.fold_leftargs~init:t~f:(funtc->get_constantct)|_->tletadd_argsargst=List.fold_leftargs~init:t~f:(funta->matchawith|Pcc->get_constantct|_->t)letget~trampolined_calls~in_cps?alias_strings?(alias_prims=false)?(alias_apply=true){blocks;_}:t=letalias_strings=matchalias_stringswith|None->Config.Flag.use_js_string()&¬(Config.Flag.share_constant())|Somex->xinletcount=Addr.Map.fold(fun_blockshare->List.fold_leftblock.body~init:share~f:(funsharei->matchiwith|Let(_,Constantc)->get_constantcshare|Let(x,Apply{args;exact;_})->lettrampolined=Var.Set.memxtrampolined_callsinletin_cps=Var.Set.memxin_cpsinif(notexact)||trampolinedthenadd_apply{arity=List.lengthargs;exact;trampolined;in_cps}shareelseshare|Let(_,Special(Alias_primname))->letname=Primitive.resolvenameinletshare=ifPrimitive.existsnamethenadd_primnameshareelseshareinshare|Let(_,Prim(Externname,args))->letname=Primitive.resolvenameinletshare=ifPrimitive.existsnamethenadd_primnameshareelseshareinadd_argsargsshare|Let(_,Prim(_,args))->add_argsargsshare|_->share))blocksempty_auxinletcount=List.fold_left["caml_trampoline";"caml_trampoline_return";"caml_wrap_exception";"caml_list_of_js_array";"caml_maybe_attach_backtrace";"jsoo_effect_not_supported"]~init:count~f:(funaccx->add_special_prim_if_existsxacc)in{count;vars=empty_aux;alias_strings;alias_prims;alias_apply}letget_byte_stringgenst=ifnott.alias_stringsthengenselsetryletc=StringMap.findst.count.byte_stringsinifc>1then(tryJ.EVar(StringMap.findst.vars.byte_strings)withNot_found->letx=Var.fresh_n(Printf.sprintf"cst_%s"s)inletv=J.Vxint.vars<-{t.varswithbyte_strings=StringMap.addsvt.vars.byte_strings};J.EVarv)elsegenswithNot_found->gensletget_utf_stringgenst=ifnott.alias_stringsthengenselsetryletc=StringMap.findst.count.utf_stringsinifc>1then(tryJ.EVar(StringMap.findst.vars.utf_strings)withNot_found->letx=Var.fresh_n(Printf.sprintf"cst_%s"s)inletv=J.Vxint.vars<-{t.varswithutf_strings=StringMap.addsvt.vars.utf_strings};J.EVarv)elsegenswithNot_found->gensletget_primgenst=lets=Primitive.resolvesinifnott.alias_primsthengenselsetryletc=StringMap.findst.count.primsinifc>1||c=-1then(tryJ.EVar(StringMap.findst.vars.prims)withNot_found->letx=Var.fresh_nsinletv=J.Vxint.vars<-{t.varswithprims=StringMap.addsvt.vars.prims};J.EVarv)elsegenswithNot_found->gensletget_applygendesct=ifnott.alias_applythengendescelsetryJ.EVar(AppMap.finddesct.vars.applies)withNot_found->letx=let{arity;exact;trampolined;in_cps}=descinVar.fresh_n(Printf.sprintf"caml_%scall%d"(matchexact,trampolined,in_cpswith|true,false,false->assertfalse(* inlined *)|true,false,true->"exact_cps_"|true,true,false->"exact_trampoline_"|false,false,true->assertfalse(* CPS functions are always trampolined *)|false,false,false->""|false,true,false->"trampoline_"|false,true,true->"trampoline_cps_"|true,true,true->"exact_trampoline_cps_")arity)inletv=J.Vxint.vars<-{t.varswithapplies=AppMap.adddescvt.vars.applies};J.EVarvendmoduleCtx=structtypet={blocks:blockAddr.Map.t;live:Deadcode.variable_uses;share:Share.t;debug:Parse_bytecode.Debug.t;exported_runtime:(Code.Var.t*boolref)option;should_export:bool;effect_warning:boolref;trampolined_calls:Effects.trampolined_calls;deadcode_sentinal:Var.t;mutated_vars:Code.Var.Set.tCode.Addr.Map.t;freevars:Code.Var.Set.tCode.Addr.Map.t;in_cps:Effects.in_cps}letinitial~warn_on_unhandled_effect~exported_runtime~should_export~deadcode_sentinal~mutated_vars~freevars~in_cpsblockslivetrampolined_callssharedebug={blocks;live;share;debug;exported_runtime;should_export;effect_warning=ref(notwarn_on_unhandled_effect);trampolined_calls;deadcode_sentinal;mutated_vars;freevars;in_cps}endtypeedge_kind=|Loop|Exit_loopofboolref|Exit_switchofboolref|Forwardletvarx=J.EVar(J.Vx)letintn=J.ENum(J.Num.of_targetint(Targetint.of_int_exnn))lettargetintn=J.ENum(J.Num.of_targetintn)letto_intcx=J.EBin(J.Bor,cx,int0)letunsigned'x=J.EBin(J.Lsr,x,int0)letunsignedx=letx=matchxwith|J.EBin(J.Bor,x,J.ENummaybe_zero)whenJ.Num.is_zeromaybe_zero->x|_->xinletpos_int32=matchxwith|J.ENumnum->(tryTargetint.(J.Num.to_targetintnum>=zero)with_->false)|_->falseinifpos_int32thenxelseunsigned'xletone=J.ENum(J.Num.of_targetintTargetint.one)letzero=J.ENum(J.Num.of_targetintTargetint.zero)letplus_intxy=matchx,ywith|J.ENumy,xwhenJ.Num.is_zeroy->x|x,J.ENumywhenJ.Num.is_zeroy->x|J.ENumx,J.ENumy->J.ENum(J.Num.addxy)|x,y->J.EBin(J.Plus,x,y)letboole=J.ECond(e,one,zero)(****)letsource_locationctxpositionpc=matchParse_bytecode.Debug.find_locctx.Ctx.debug~positionpcwith|Somepi->J.Pipi|None->J.N(****)letfloat_constf=J.ENum(J.Num.of_floatf)lets_varname=J.EVar(J.ident(Utf8_string.of_string_exnname))letruntime_functxname=matchctx.Ctx.exported_runtimewith|Some(runtime,runtime_needed)->runtime_needed:=true;letname=Utf8_string.of_string_exnnameinJ.dot(J.EVar(J.Vruntime))name|None->s_varnameletstr_js_bytes=letb=Buffer.create(String.lengths)inString.iters~f:(function|'\\'->Buffer.add_stringb"\\\\"|'\128'..'\255'asc->Buffer.add_stringb"\\x";Buffer.add_char_hexbc|c->Buffer.add_charbc);lets=Buffer.contentsbinJ.EStr(Utf8_string.of_string_exns)letstr_js_utf8s=letb=Buffer.create(String.lengths)inString.iters~f:(function|'\\'->Buffer.add_stringb"\\\\"|c->Buffer.add_charbc);lets=Buffer.contentsbinJ.EStr(Utf8_string.of_string_exns)(****)(*
Some variables are constant: x = 1
Some may change after effectful operations : x = y[z]
There can be at most one effectful operations in the queue at once
let (e, expr_queue) = ... in
flush_queue expr_queue e
*)letconst_p=0,Var.Set.emptyletmutable_p=1,Var.Set.emptyletmutator_p=2,Var.Set.emptyletflush_p=3,Var.Set.emptyletor_p(p,s1)(q,s2)=maxpq,Var.Set.unions1s2letis_mutable(p,_)=p>=fstmutable_pletkindk=matchkwith|`Pure->const_p|`Mutable->mutable_p|`Mutator->mutator_pletocaml_string~ctx~locs=ifConfig.Flag.use_js_string()thenselseletp=Share.get_prim(runtime_functx)"caml_string_of_jsbytes"ctx.Ctx.shareinJ.callp[s]locletrecconstant_rec~ctxxlevelinstrs=matchxwith|Strings->lete=ifString.is_asciisthenShare.get_utf_stringstr_js_bytesctx.Ctx.shareelseShare.get_byte_stringstr_js_bytesctx.Ctx.shareinlete=ocaml_string~ctx~loc:J.Neine,instrs|NativeStrings->(matchswith|Bytex->Share.get_byte_stringstr_js_bytexctx.Ctx.share,instrs|Utf(Utf8x)->Share.get_utf_stringstr_js_utf8xctx.Ctx.share,instrs)|Floatf->float_constf,instrs|Float_arraya->(Mlvalue.Array.make~tag:Obj.double_array_tag~args:(Array.to_list(Array.mapa~f:(funx->J.Element(float_constx)))),instrs)|Int64i->letp=Share.get_prim(runtime_functx)"caml_int64_create_lo_mi_hi"ctx.Ctx.shareinletlo=int(Int64.to_intiland0xffffff)andmi=int(Int64.to_int(Int64.shift_righti24)land0xffffff)andhi=int(Int64.to_int(Int64.shift_righti48)land0xffff)inJ.callp[lo;mi;hi]J.N,instrs|Tuple(tag,a,_)->(letconstant_max_depth=Config.Param.constant_max_depth()inletrecdetect_listnacc=function|Tuple(0,[|x;l|],_)->detect_list(succn)(x::acc)l|Intmaybe_zerowhenTargetint.is_zeromaybe_zero->ifn>constant_max_depththenSomeaccelseNone|_->Noneinmatchdetect_list0[]xwith|Someelts_rev->letelements,instrs=List.fold_leftelts_rev~init:([],instrs)~f:(fun(arr,instrs)elt->letjs,instrs=constant_rec~ctxeltlevelinstrsinjs::arr,instrs)inletp=Share.get_prim(runtime_functx)"caml_list_of_js_array"ctx.Ctx.shareinJ.callp[J.arrayelements]J.N,instrs|None->letsplit=level=constant_max_depthinletlevel=ifsplitthen0elselevel+1inletl,instrs=List.fold_left(Array.to_lista)~init:([],instrs)~f:(fun(l,instrs)cc->letjs,instrs=constant_rec~ctxcclevelinstrsinjs::l,instrs)inletl,instrs=ifsplitthenList.fold_leftl~init:([],instrs)~f:(fun(acc,instrs)js->matchjswith|J.EArr_->letv=Code.Var.fresh_n"partial"inletinstrs=(J.variable_declaration[J.Vv,(js,J.N)],J.N)::instrsinJ.Element(J.EVar(J.Vv))::acc,instrs|_->J.Elementjs::acc,instrs)elseList.map~f:(funx->J.Elementx)(List.revl),instrsinMlvalue.Block.make~tag~args:l,instrs)|Inti->targetinti,instrs|Int32_|NativeInt_->assertfalse(* Should not be produced when compiling to Javascript *)letconstant~ctxxlevel=letexpr,instr=constant_rec~ctxxlevel[]inexpr,List.revinstrtypequeue_elt={prop:int;ce:J.expression;loc:J.locationoption;deps:Code.Var.Set.t}letaccess_queuequeuex=tryletelt=List.assocxqueuein((elt.prop,elt.deps),elt.ce,elt.loc),List.remove_assocxqueuewithNot_found->((fstconst_p,Code.Var.Set.singletonx),varx,None),queueletaccess_queue_locqueueloc'x=let(prop,c,loc),queue=access_queuequeuexin(prop,c,Option.value~default:loc'loc),queueletshould_flush(cond,_)prop=cond<>fstconst_p&&cond+prop>=fstflush_pletflush_queueexpr_queueproploc(l:J.statement_list)=letinstrs,expr_queue=iffstprop>=fstflush_pthenexpr_queue,[]elseList.partition~f:(fun(_,elt)->should_flushpropelt.prop)expr_queueinletinstrs=List.mapinstrs~f:(fun(x,elt)->letloc=Option.value~default:locelt.locinJ.variable_declaration[J.Vx,(elt.ce,loc)],loc)inList.rev_appendinstrsl,expr_queueletflush_allexpr_queuelocl=fst(flush_queueexpr_queueflush_plocl)letenqueueexpr_queuepropxceflush_locexpr_locacc=letinstrs,expr_queue=ifConfig.Flag.compact()thenifis_mutablepropthenflush_queueexpr_queuepropflush_locaccelseacc,expr_queueelseflush_queueexpr_queueflush_pflush_locaccinletprop,deps=propininstrs,(x,{prop;deps;ce;loc=expr_loc})::expr_queuetypequeue=(Var.t*queue_elt)listtypeprop=int*Code.Var.Set.tmoduleExpr_builder:sigtype'atval(let*):'at->('a->'bt)->'btvalreturn:'a->'atvalaccess:Var.t->J.expressiontvalaccess':ctx:Ctx.t->prim_arg->J.expressiontvalinfo:?need_loc:bool->prop->unittvalstatement_loc:J.location->J.locationtvalflush_all:queue->J.location->J.statement_listt->J.statement_listvalflush_queue:queue->J.location->J.statement_listt->J.statement_list*queuevalenqueue:queue->Var.t->J.location->(J.expression*J.statement_list)t->J.statement_list*queuevalget:queue->J.location->'at->'a*J.location*queuevallist_map:('a->'bt)->'alist->'blisttend=structtypestate={queue:queue;prop:prop;need_loc:bool;loc:J.locationoption}type'at=state->'a*statelet(let*)(typeab)(e:at)(f:a->bt):bt=funst->letv,st=estinfvstletreturnxst=x,stletinfo?(need_loc=false)propst=(),{stwithprop=or_pst.propprop;need_loc=need_loc||st.need_loc}letaccessxst=let(prop,c,loc),queue=access_queuest.queuexin(c,{stwithprop=or_pst.propprop;queue;loc=(matchst.locwith|None->loc|_->st.loc)})letaccess'~ctxx=matchxwith|Pcc->letjs,instrs=constant~ctxc(Config.Param.constant_max_depth())inassert(List.is_emptyinstrs);(* We only have simple constants here *)funst->js,st|Pvx->accessxletstatement_loclocst=((matchst.locwith|None->loc|Someloc->loc),st)letinitial_statequeue={queue;prop=const_p;loc=None;need_loc=false}letflush_queuequeuelocinstrs=letv,{queue;prop;_}=instrs(initial_statequeue)inflush_queuequeueproplocvletflush_allqueuelocinstrs=letv,{queue;_}=instrs(initial_statequeue)inflush_allqueuelocvletenqueuequeuexflush_locexpr=let(ce,instrs),{queue;prop;loc;need_loc}=expr(initial_statequeue)inletexpr_loc=matchlocwith|Nonewhenneed_loc->Someflush_loc|_->locinenqueuequeuepropxceflush_locexpr_locinstrsletgetqueueloc'x=letx,{queue;loc;_}=x(initial_statequeue)inletloc=matchlocwith|None->loc'|Someloc->locinx,loc,queueletreclist_mapflst=matchlwith|[]->[],st|x::r->letx',st=fxstinletr',st=list_mapfrstinx'::r',stend(****)typestate={structure:Structure.t;dom:Structure.graph;visited_blocks:Addr.Set.tref;ctx:Ctx.t;pc:Addr.t}moduleDTree=struct(* This has to be kept in sync with the way we build conditionals
and switches! *)typecond=|IsTrue|CEqofTargetint.t|CLtofTargetint.t|CLeofTargetint.ttype'abranch=intlist*'atype'at=|Ifofcond*'at*'at|Switchof'abrancharray|Branchof'abranchletnormalizea=a|>Array.to_list|>List.sort~cmp:(fun(cont1,_)(cont2,_)->Poly.comparecont1cont2)|>list_group~equal:Poly.equalfstsnd|>List.map~f:(fun(cont1,l1)->cont1,List.flattenl1)|>List.sort~cmp:(fun(_,l1)(_,l2)->compare(List.lengthl1)(List.lengthl2))|>Array.of_listletbuild_ifb1b2=If(IsTrue,Branch([1],b1),Branch([0],b2))letbuild_switch(a:contarray):contt=letm=Config.Param.switch_max_case()inletai=Array.mapia~f:(funix->x,i)in(* group the contiguous cases with the same continuation *)letai:(Code.cont*intlist)array=Array.of_list(list_group~equal:Poly.equalfstsnd(Array.to_listai))inletreclooplowup=letarray_norm:(Code.cont*intlist)array=normalize(Array.subai~pos:low~len:(up-low+1))inletarray_len=Array.lengtharray_norminifarray_len=1(* remaining cases all jump to the same branch *)thenBranch(sndarray_norm.(0),fstarray_norm.(0))elsetry(* try to optimize when there are only 2 branch *)matcharray_normwith|[|(b1,([i1]asl1));(b2,l2)|]->If(CEq(Targetint.of_int_exni1),Branch(l1,b1),Branch(l2,b2))|[|(b1,l1);(b2,([i2]asl2))|]->If(CEq(Targetint.of_int_exni2),Branch(l2,b2),Branch(l1,b1))|[|(b1,l1);(b2,l2)|]->letboundl1=matchl1,List.revl1with|min::_,max::_->min,max|_->assertfalseinletmin1,max1=boundl1inletmin2,max2=boundl2inifmax1<min2thenIf(CLt(Targetint.of_int_exnmax1),Branch(l2,b2),Branch(l1,b1))elseifmax2<min1thenIf(CLt(Targetint.of_int_exnmax2),Branch(l1,b1),Branch(l2,b2))elseraiseNot_found|_->raiseNot_foundwithNot_found->((* do we have to split again ? *)(* we count the number of cases, default/last case count for one *)letnbcases=ref1(* default case *)infori=0toarray_len-2donbcases:=!nbcases+List.length(sndarray_norm.(i))done;if!nbcases<=mthenSwitch(Array.maparray_norm~f:(fun(x,l)->l,x))elseleth=(up+low)/2inletb1=looplowhandb2=loop(succh)upinletrange1=sndai.(h)andrange2=sndai.(succh)inmatchrange1,range2with|[],_|_,[]->assertfalse|_,lower_bound2::_->If(CLe(Targetint.of_int_exnlower_bound2),b2,b1))inletlen=Array.lengthaiinassert(len>0);loop0(len-1)letnbbranch(a:contt)pc=letrecloopc:contt->int=function|Branch(_,(pc',_))->ifpc'=pcthensucccelsec|If(_,a,b)->letc=loopcainletc=loopcbinc|Switcha->Array.fold_lefta~init:c~f:(funacc(_,(pc',_))->ifpc'=pcthensuccaccelseacc)inloop0aletnbcompa=letrecloopc=function|Branch_->c|If(_,a,b)->letc=succcinletc=loopcainletc=loopcbinc|Switch_->letc=succcincinloop0aendletbuild_graphctxpc=letvisited_blocks=refAddr.Set.emptyinletstructure=Structure.build_graphctx.Ctx.blockspcinletdom=Structure.dominator_treestructurein{visited_blocks;structure;dom;ctx;pc}(****)letrecvisitvisitedprevsmxl=ifnot(Var.Set.memxvisited)thenletvisited=Var.Set.addxvisitedinlety=Var.Map.findxminifCode.Var.comparexy=0thenvisited,None,lelseifVar.Set.memyprevthenlett=Code.Var.fresh()invisited,Some(y,t),(x,t)::lelseifVar.Set.memysthenletvisited,aliases,l=visitvisited(Var.Set.addxprev)smylinmatchaliaseswith|Some(a,b)whenCode.Var.compareax=0->visited,None,(b,a)::(x,y)::l|_->visited,aliases,(x,y)::lelsevisited,None,(x,y)::lelsevisited,None,lletvisit_allparamsargs=letm=Subst.build_mappingparamsargsinlets=List.fold_leftparams~init:Var.Set.empty~f:(funsx->Var.Set.addxs)inlet_,l=Var.Set.fold(funx(visited,l)->letvisited,_,l=visitvisitedVar.Set.emptysmxlinvisited,l)s(Var.Set.empty,[])inlletparallel_renaminglocback_edgeparamsargscontinuationqueue=ifback_edge&&Config.Flag.es6()(* This is likely slower than using explicit temp variable
but let's experiment with es6 a bit *)thenletargs,params=List.map2argsparams~f:(funap->ifVar.equalapthenNoneelseSome(a,p))|>List.filter_map~f:(funx->x)|>List.splitinletopenExpr_builderinletargs,loc,queue=getqueueloc(List.fold_leftargs~init:(return[])~f:(funacca->let*acc=accinlet*cx=accessainreturn(cx::acc)))inletnever,code=continuationqueueinmatchparams,argswith|[p],[a]->never,(J.Expression_statement(J.EBin(J.Eq,J.EVar(J.Vp),a)),loc)::code|params,args->letlhs=J.EAssignTarget(J.ArrayTarget(List.mapparams~f:(funp->J.TargetElementId(J.Vp,None))))inletrhs=J.EArr(List.rev_mapargs~f:(funx->J.Elementx))innever,(J.Expression_statement(J.EBin(J.Eq,lhs,rhs)),loc)::codeelseletl=visit_allparamsargsin(* if not back_edge
* then assert (Poly.( = ) l (List.rev_map2 params args ~f:(fun a b -> a, b))); *)letqueue,before,renaming,_=List.fold_leftl~init:(queue,[],[],Code.Var.Set.empty)~f:(fun(queue,before,renaming,seen)(y,x)->let((_,deps_x),cx,locx),queue=access_queue_locqueuelocxinletseen'=Code.Var.Set.addyseeninifnotCode.Var.Set.(is_empty(interseendeps_x))thenlet()=assertback_edgeinletbefore=(J.variable_declaration[J.Vx,(cx,locx)],locx)::beforeinletrenaming=(y,J.EVar(J.Vx))::renaminginqueue,before,renaming,seen'elseletrenaming=(y,cx)::renaminginqueue,before,renaming,seen')inletrenaming=ifback_edgethenList.maprenaming~f:(fun(t,e)->J.Expression_statement(J.EBin(J.Eq,J.EVar(J.Vt),e)),loc)elseList.maprenaming~f:(fun(t,e)->J.variable_declaration[J.Vt,(e,loc)],loc)inletnever,code=continuationqueueinnever,List.rev_appendbefore(List.rev_appendrenamingcode)(****)letapply_fun_raw=letcps_field=Utf8_string.of_string_exn"cps"infunctxfparamsexacttrampolinedcpsloc->letapply_directlyfparams=(* Make sure we are performing a regular call, not a (slower)
method call *)matchfwith|J.EAccess_|J.EDot_->J.call(J.dotf(Utf8_string.of_string_exn"call"))(s_var"null"::params)loc|_->J.callfparamslocinletapply~cpsfparams=(* Adapt if [f] is a (direct-style, CPS) closure pair *)letreal_closure=matchConfig.effects()with|`Double_translationwhencps->(* Effects enabled, CPS version, not single-version *)J.EDot(f,J.ANormal,cps_field)|`Cps|`Double_translation|`Disabled->f|`Jspi->assertfalsein(* We skip the arity check when we know that we have the right
number of parameters, since this test is expensive. *)ifexactthenapply_directlyreal_closureparamselseletl=Utf8_string.of_string_exn"l"inJ.ECond(J.EBin(J.EqEqEq,J.ECond(J.EBin(J.Ge,J.dotreal_closurel,int0),J.dotreal_closurel,J.EBin(J.Eq,J.dotreal_closurel,J.dotreal_closure(Utf8_string.of_string_exn"length"))),int(List.lengthparams)),apply_directlyreal_closureparams,J.call(* Note: when double translation is enabled, [caml_call_gen*] functions takes a two-version function *)(runtime_functx(matchConfig.effects()with|`Double_translationwhencps->"caml_call_gen_cps"|`Double_translation|`Cps|`Disabled->"caml_call_gen"|`Jspi->assertfalse))[f;J.arrayparams]J.N)inletapply=matchConfig.effects()with|`Double_translationwhencps->letn=List.lengthparamsinJ.ECond(J.EDot(f,J.ANormal,cps_field),apply~cps:truefparams,J.call(List.nthparams(n-1))[apply~cps:falsef(fst(List.take(n-1)params))]J.N)|`Double_translation|`Cps|`Disabled->apply~cpsfparams|`Jspi->assertfalseiniftrampolinedthen(assert(cps_transform());(* When supporting effect, we systematically perform tailcall
optimization. To implement it, we check the stack depth and
bounce to a trampoline if needed, to avoid a stack overflow.
The trampoline then performs the call in an shorter stack. *)J.ECond(J.call(runtime_functx"caml_stack_check_depth")[]loc,apply,J.call(runtime_functx"caml_trampoline_return")[f;J.arrayparams;(ifcpsthenzeroelseone)]loc))elseapplyletgenerate_apply_functx{arity;exact;trampolined;in_cps}=letf'=Var.fresh_n"f"inletf=J.Vf'inletparams=Array.to_list(Array.initarity~f:(funi->leta=Var.fresh_n(Printf.sprintf"a%d"i)inJ.Va))inletf'=J.EVarfinletparams'=List.mapparams~f:(funx->J.EVarx)inJ.EFun(None,J.fun_(f::params)[(J.Return_statement(Some(apply_fun_rawctxf'params'exacttrampolinedin_cpsJ.N),J.N),J.N)]J.N)letapply_functxfparamsexacttrampolinedin_cpsloc=(* We always go through an intermediate function when doing CPS
calls. This function first checks the stack depth to prevent
a stack overflow. This makes the code smaller than inlining
the test, and we expect the performance impact to be low
since the function should get inlined by the JavaScript
engines. *)ifConfig.Flag.inline_callgen()||(exact&¬trampolined)thenapply_fun_rawctxfparamsexacttrampolinedin_cpslocelselety=Share.get_apply(generate_apply_functx){arity=List.lengthparams;exact;trampolined;in_cps}ctx.Ctx.shareinJ.cally(f::params)loc(****)letinternal_primitives=Hashtbl.create31letinternal_primname=trylet_,f=Hashtbl.findinternal_primitivesnameinSomefwithNot_found->Noneletregister_primnamekf=Hashtbl.addinternal_primitivesname(k,f)letinvalid_aritynamel~loc~expected=failwith(Printf.sprintf"%sInvalid arity for primitive %s. Expecting %d but used with %d."(match(loc:J.location)with|Pi{name=Somename;col;line;_}->Printf.sprintf"%s:%d:%d: "namelinecol|Pi_|N|U->"")nameexpected(List.lengthl))letregister_un_primname?(need_loc=false)kf=register_primnamek(funlctxloc->matchlwith|[x]->letopenExpr_builderinlet*cx=access'~ctxxinlet*()=info~need_loc(kindk)inreturn(fcxloc)|l->invalid_aritynamel~loc~expected:1)letregister_un_prim_ctxnamekf=register_primnamek(funlctxloc->matchlwith|[x]->letopenExpr_builderinlet*cx=access'~ctxxinlet*()=info(kindk)inreturn(fctxcxloc)|_->invalid_aritynamel~loc~expected:1)letregister_bin_primnamekf=register_primnamek(funlctxloc->matchlwith|[x;y]->letopenExpr_builderinlet*cx=access'~ctxxinlet*cy=access'~ctxyinlet*()=info(kindk)inreturn(fcxcyloc)|_->invalid_aritynamel~loc~expected:2)letregister_tern_primnamef=register_primname`Mutator(funlctxloc->matchlwith|[x;y;z]->letopenExpr_builderinlet*cx=access'~ctxxinlet*cy=access'~ctxyinlet*cz=access'~ctxzinlet*()=infomutator_pinreturn(fcxcyczloc)|_->invalid_aritynamel~loc~expected:3)letregister_un_math_primnameprim=letprim=Utf8_string.of_string_exnpriminregister_un_primname`Pure(funcxloc->J.call(J.dot(s_var"Math")prim)[cx]loc)letregister_bin_math_primnameprim=letprim=Utf8_string.of_string_exnpriminregister_bin_primname`Pure(funcxcyloc->J.call(J.dot(s_var"Math")prim)[cx;cy]loc)let_=register_un_prim_ctx"%caml_format_int_special"`Pure(functxcxloc->lets=J.EBin(J.Plus,str_js_utf8"",cx)inocaml_string~ctx~locs);register_un_prim"%direct_obj_tag"`Mutator(funcx_loc->Mlvalue.Block.tagcx);register_bin_prim"caml_array_unsafe_get"`Mutable(funcxcy_->Mlvalue.Array.fieldcxcy);register_bin_prim"%int_add"`Pure(funcxcy_->matchcx,cywith|J.EBin(J.Minus,cz,J.ENumn),J.ENumm->to_int(J.EBin(J.Plus,cz,J.ENum(J.Num.addm(J.Num.negn))))|_->to_int(plus_intcxcy));register_bin_prim"%int_sub"`Pure(funcxcy_->matchcx,cywith|J.EBin(J.Minus,cz,J.ENumn),J.ENumm->to_int(J.EBin(J.Minus,cz,J.ENum(J.Num.addnm)))|_->to_int(J.EBin(J.Minus,cx,cy)));register_bin_prim"%direct_int_mul"`Pure(funcxcy_->to_int(J.EBin(J.Mul,cx,cy)));register_bin_prim"%direct_int_div"`Pure(funcxcy_->to_int(J.EBin(J.Div,cx,cy)));register_bin_prim"%direct_int_mod"`Pure(funcxcy_->to_int(J.EBin(J.Mod,cx,cy)));register_bin_prim"%int_and"`Pure(funcxcy_->J.EBin(J.Band,cx,cy));register_bin_prim"%int_or"`Pure(funcxcy_->J.EBin(J.Bor,cx,cy));register_bin_prim"%int_xor"`Pure(funcxcy_->J.EBin(J.Bxor,cx,cy));register_bin_prim"%int_lsl"`Pure(funcxcy_->J.EBin(J.Lsl,cx,cy));register_bin_prim"%int_lsr"`Pure(funcxcy_->to_int(J.EBin(J.Lsr,cx,cy)));register_bin_prim"%int_asr"`Pure(funcxcy_->J.EBin(J.Asr,cx,cy));register_un_prim"%int_neg"`Pure(funcx_->to_int(J.EUn(J.Neg,cx)));register_bin_prim"caml_eq_float"`Pure(funcxcy_->bool(J.EBin(J.EqEqEq,cx,cy)));register_bin_prim"caml_neq_float"`Pure(funcxcy_->bool(J.EBin(J.NotEqEq,cx,cy)));register_bin_prim"caml_ge_float"`Pure(funcxcy_->bool(J.EBin(J.Le,cy,cx)));register_bin_prim"caml_le_float"`Pure(funcxcy_->bool(J.EBin(J.Le,cx,cy)));register_bin_prim"caml_gt_float"`Pure(funcxcy_->bool(J.EBin(J.Lt,cy,cx)));register_bin_prim"caml_lt_float"`Pure(funcxcy_->bool(J.EBin(J.Lt,cx,cy)));register_bin_prim"caml_add_float"`Pure(funcxcy_->J.EBin(J.Plus,cx,cy));register_bin_prim"caml_sub_float"`Pure(funcxcy_->J.EBin(J.Minus,cx,cy));register_bin_prim"caml_mul_float"`Pure(funcxcy_->J.EBin(J.Mul,cx,cy));register_bin_prim"caml_div_float"`Pure(funcxcy_->J.EBin(J.Div,cx,cy));register_un_prim"caml_neg_float"`Pure(funcx_->J.EUn(J.Neg,cx));register_bin_prim"caml_fmod_float"`Pure(funcxcy_->J.EBin(J.Mod,cx,cy));register_tern_prim"caml_array_unsafe_set"(funcxcycz_->J.EBin(J.Eq,Mlvalue.Array.fieldcxcy,cz));register_un_prim"caml_alloc_dummy"`Pure(fun__->J.array[]);register_un_prim"caml_obj_dup"~need_loc:true`Mutable(funcxloc->J.call(J.dotcx(Utf8_string.of_string_exn"slice"))[]loc);register_un_prim"caml_int_of_float"`Pure(funcx_loc->to_intcx);register_un_math_prim"caml_abs_float""abs";register_un_math_prim"caml_acos_float""acos";register_un_math_prim"caml_asin_float""asin";register_un_math_prim"caml_atan_float""atan";register_bin_math_prim"caml_atan2_float""atan2";register_un_math_prim"caml_ceil_float""ceil";register_un_math_prim"caml_cos_float""cos";register_un_math_prim"caml_exp_float""exp";register_un_math_prim"caml_floor_float""floor";register_un_math_prim"caml_log_float""log";register_bin_math_prim"caml_power_float""pow";register_un_math_prim"caml_sin_float""sin";register_un_math_prim"caml_sqrt_float""sqrt";register_un_math_prim"caml_tan_float""tan";register_un_prim"caml_js_from_bool"`Pure(funcx_->J.EUn(J.Not,J.EUn(J.Not,cx)));register_un_prim"caml_js_to_bool"`Pure(funcx_->to_intcx);register_tern_prim"caml_js_set"(funcxcycz_->J.EBin(J.Eq,J.EAccess(cx,ANormal,cy),cz));(* [caml_js_get] can have side effect, we declare it as mutator.
see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get *)register_bin_prim"caml_js_get"`Mutator(funcxcy_->J.EAccess(cx,ANormal,cy));register_bin_prim"caml_js_delete"`Mutator(funcxcy_->J.EUn(J.Delete,J.EAccess(cx,ANormal,cy)));register_bin_prim"caml_js_equals"`Mutable(funcxcy_->bool(J.EBin(J.EqEq,cx,cy)));register_bin_prim"caml_js_strict_equals"`Mutable(funcxcy_->bool(J.EBin(J.EqEqEq,cx,cy)));register_bin_prim"caml_js_instanceof"`Mutator(funcxcy_->bool(J.EBin(J.InstanceOf,cx,cy)));register_un_prim"caml_js_typeof"`Mutator(funcx_->J.EUn(J.Typeof,cx))(****)(* when raising ocaml exception and [improved_stacktrace] is enabled,
tag the ocaml exception with a Javascript error (that contain js stacktrace).
{[ throw e ]}
becomes
{[ throw (caml_exn_with_js_backtrace(e,false)) ]}
*)letthrow_statementctxcxkloc=match(k:[`Normal|`Reraise|`Notrace])with|`Notrace->[J.Throw_statementcx,loc]|(`Normal|`Reraise)asm->letforce=matchmwith|`Normal->true|`Reraise->falsein[(J.Throw_statement(J.call(Share.get_prim(runtime_functx)"caml_maybe_attach_backtrace"ctx.share)[cx;(ifforcethenint1elseint0)]loc),loc)]letremove_unused_tail_argsctxexacttrampolinedargs=ifexact&¬trampolinedthenlethas_unused_tail_args=List.fold_left~f:(fun_x->Var.equalxctx.Ctx.deadcode_sentinal)~init:falseargsinifhas_unused_tail_argsthenList.fold_right~f:(funxargs->matchargswith|[]whenVar.equalxctx.Ctx.deadcode_sentinal->[]|_->x::args)~init:[]argselseargselseargsletrectranslate_exprctxlocxelevel:(_*J.statement_list)Expr_builder.t=letopenExpr_builderinmatchewith|Apply{f;args;exact}->lettrampolined=Var.Set.memxctx.Ctx.trampolined_callsinletargs=remove_unused_tail_argsctxexacttrampolinedargsinlet*()=info~need_loc:truemutator_pinletin_cps=Var.Set.memxctx.Ctx.in_cpsinlet*args=list_mapaccessargsinlet*f=accessfinreturn(apply_functxfargsexacttrampolinedin_cpsloc,[])|Block(tag,a,array_or_not,_mut)->let*contents=list_map(funx->let*cx=accessxinletcx=matchcxwith|J.EVar(J.Vv)->ifVar.equalvctx.deadcode_sentinalthenJ.ElementHoleelseJ.Elementcx|_->J.Elementcxinreturncx)(Array.to_lista)inletx=matcharray_or_notwith|Array->Mlvalue.Array.make~tag~args:contents|NotArray|Unknown->Mlvalue.Block.make~tag~args:contentsinreturn(x,[])|Field(x,n,_)->let*cx=accessxinlet*()=infomutable_pinreturn(Mlvalue.Block.fieldcxn,[])|Closure(args,((pc,_)ascont))->letloc=source_locationctxAfterpcinletfv=Addr.Map.findpcctx.freevarsinletclo=compile_closurectxcontinletclo=J.EFun(None,J.fun_(List.mapargs~f:(funv->J.Vv))(Js_simpl.function_bodyclo)loc)inlet*()=info(fstconst_p,fv)inreturn(clo,[])|Constantc->return(constant~ctxclevel)|Special(Alias_primname)->letprim=Share.get_prim(runtime_functx)namectx.Ctx.shareinreturn(prim,[])|Prim(Extern"debugger",_)->letins=ifConfig.Flag.debugger()thenJ.Debugger_statementelseJ.Empty_statementinreturn(int0,[ins,loc])|Prim(p,l)->let*res=matchp,lwith|Vectlength,[x]->let*cx=access'~ctxxinreturn(Mlvalue.Array.lengthcx)|Array_get,[x;y]->let*cx=access'~ctxxinlet*cy=access'~ctxyinlet*()=infomutable_pinreturn(Mlvalue.Array.fieldcxcy)|Extern"caml_js_var",[Pc(Stringnm)]|Extern("caml_js_expr"|"caml_pure_js_expr"),[Pc(Stringnm)]->(tryletpos=matchlocwith|J.N|J.U->None|J.Pipi->((* [pi] is the position of the call, not the
string. We don't have enough information to
recover the start column *)matchpi.srcwith|Somepos_fname->Some{Lexing.pos_fname;pos_lnum=pi.line;pos_cnum=pi.idx;pos_bol=pi.idx}|None->None)inletlex=Parse_js.Lexer.of_string?posnminreturn(Parse_js.parse_exprlex)withParse_js.Parsing_errorpi->failwith(Printf.sprintf"Parsing error %S%s at l:%d col:%d"nm(matchpi.Parse_info.srcwith|None->""|Somes->Printf.sprintf", file %S"s)pi.Parse_info.linepi.Parse_info.col))|Extern"%js_array",l->let*args=list_map(funx->access'~ctxx)linreturn(J.arrayargs)|Extern"%caml_js_opt_call",f::o::l->let*()=info~need_loc:truemutator_pinlet*cf=access'~ctxfinlet*co=access'~ctxoinlet*args=list_map(funx->access'~ctxx)linreturn(J.call(J.dotcf(Utf8_string.of_string_exn"call"))(co::args)loc)|Extern"%caml_js_opt_fun_call",f::l->let*()=info~need_loc:truemutator_pinlet*cf=access'~ctxfinlet*args=list_map(funx->access'~ctxx)linreturn(J.callcfargsloc)|Extern"%caml_js_opt_meth_call",o::Pc(NativeString(Utfm))::l->let*()=info~need_loc:truemutator_pinlet*co=access'~ctxoinlet*args=list_map(funx->access'~ctxx)linreturn(J.call(J.dotcom)argsloc)|Extern"%caml_js_opt_meth_call",_->assertfalse|Extern"%caml_js_opt_new",c::l->let*()=info~need_loc:truemutator_pinlet*cc=access'~ctxcinlet*args=list_map(funx->let*cx=access'~ctxxinreturn(J.Argcx))linreturn(J.ENew(cc,(ifList.is_emptyargsthenNoneelseSomeargs),loc))|Extern"caml_js_get",[Pvo;Pc(NativeString(Utff))]whenJ.is_ident'f->let*co=accessoinlet*()=infomutable_pinreturn(J.dotcof)|Extern"caml_js_set",[Pvo;Pc(NativeString(Utff));v]whenJ.is_ident'f->let*co=accessoinlet*cv=access'~ctxvinlet*()=infomutator_pinreturn(J.EBin(J.Eq,J.dotcof,cv))|Extern"caml_js_delete",[Pvo;Pc(NativeString(Utff))]whenJ.is_ident'f->let*co=accessoinlet*()=infomutator_pinreturn(J.EUn(J.Delete,J.dotcof))(*
This is only useful for debugging:
{[
| Extern "caml_js_get", [ _; Pc (String _) ] -> assert false
| Extern "caml_js_set", [ _; Pc (String s); _ ] -> assert false
| Extern "caml_js_delete", [ _; Pc (String _) ] -> assert false
]}
*)|Extern"%caml_js_opt_object",fields->letrecbuild_fieldsl=matchlwith|[]->return[]|Pc(NativeString(Utfnm))::x::r->let*cx=access'~ctxxinlet*r'=build_fieldsrinletp_name=ifJ.is_ident'nmthenJ.PNInmelseJ.PNSnminreturn(J.Property(p_name,cx)::r')|_->assertfalseinlet*fields=build_fieldsfieldsinreturn(J.EObjfields)|Extern"caml_alloc_dummy_function",[_;size]->let*i=let*cx=access'~ctxsizeinreturn(matchcxwith|J.ENumi->Targetint.to_int_exn(J.Num.to_targetinti)|_->assertfalse)inletargs=Array.to_list(Array.initi~f:(fun_->J.V(Var.fresh())))inletf=J.V(Var.fresh())inletcall=J.call(J.dot(J.EVarf)(Utf8_string.of_string_exn"fun"))(List.mapargs~f:(funv->J.EVarv))locinlete=J.EFun(Somef,J.fun_args[J.Return_statement(Somecall,J.N),J.N]J.N)inreturne|Extern"caml_alloc_dummy_function",_->assertfalse|Extern("%resume"|"%perform"|"%reperform"),_->assert(not(cps_transform()));ifnot!(ctx.effect_warning)then(warn"Warning: your program contains effect handlers; you should probably run \
js_of_ocaml with option '--effects=cps'@.";ctx.effect_warning:=true);letname="jsoo_effect_not_supported"inletprim=Share.get_prim(runtime_functx)namectx.Ctx.shareinlet*()=info~need_loc:true(kind(Primitive.kindname))inreturn(J.callprim[]loc)|Extern"caml_string_notequal",[a;b]whenConfig.Flag.use_js_string()->let*cx=access'~ctxainlet*cy=access'~ctxbinreturn(bool(J.EBin(J.NotEqEq,cx,cy)))|Extern"caml_string_equal",[a;b]whenConfig.Flag.use_js_string()->let*cx=access'~ctxainlet*cy=access'~ctxbinreturn(bool(J.EBin(J.EqEqEq,cx,cy)))|Extern"caml_string_concat",[a;b]whenConfig.Flag.use_js_string()->let*ca=access'~ctxainlet*cb=access'~ctxbinletrecaddcacb=matchcbwith|J.EBin(J.Plus,cb1,cb2)->J.EBin(J.Plus,addcacb1,cb2)|_->J.EBin(J.Plus,ca,cb)inreturn(addcacb)|Externname,l->(letname=Primitive.resolvenameinmatchinternal_primnamewith|Somef->flctxloc|None->ifString.is_prefixname~prefix:"%"thenfailwith(Printf.sprintf"Unresolved internal primitive: %s"name);letprim=Share.get_prim(runtime_functx)namectx.Ctx.shareinlet*()=info~need_loc:true(kind(Primitive.kindname))inlet*args=list_map(funx->access'~ctxx)linreturn(J.callprimargsloc))|Not,[x]->let*cx=access'~ctxxinreturn(J.EBin(J.Minus,one,cx))|Lt,[x;y]->let*cx=access'~ctxxinlet*cy=access'~ctxyinreturn(bool(J.EBin(J.LtInt,cx,cy)))|Le,[x;y]->let*cx=access'~ctxxinlet*cy=access'~ctxyinreturn(bool(J.EBin(J.LeInt,cx,cy)))|Eq,[x;y]->let*cx=access'~ctxxinlet*cy=access'~ctxyinreturn(bool(J.EBin(J.EqEqEq,cx,cy)))|Neq,[x;y]->let*cx=access'~ctxxinlet*cy=access'~ctxyinreturn(bool(J.EBin(J.NotEqEq,cx,cy)))|IsInt,[x]->let*cx=access'~ctxxinreturn(bool(Mlvalue.is_immediatecx))|Ult,[x;y]->let*cx=access'~ctxxinlet*cy=access'~ctxyinreturn(bool(J.EBin(J.LtInt,unsignedcx,unsignedcy)))|(Vectlength|Array_get|Not|IsInt|Eq|Neq|Lt|Le|Ult),_->assertfalseinreturn(res,[])andtranslate_instrctxexpr_queuelocinstr=letopenExpr_builderinmatchinstrwith|Assign(x,y)->flush_queueexpr_queueloc(let*cy=accessyinlet*()=infomutator_pinlet*loc=statement_loclocinreturn[J.Expression_statement(J.EBin(J.Eq,J.EVar(J.Vx),cy)),loc])|Let(x,e)->(lete'=translate_exprctxlocxe0inletkeep_namex=matchCode.Var.get_namexwith|None->false|Some""->false|Somes->(* "switcher" is emitted by the OCaml compiler when compiling
pattern matching, it does not help much to keep it in the
generated js, let's drop it *)(not(generated_names))&¬(String.is_prefixs~prefix:"jsoo_")inmatchctx.Ctx.live.(Var.idxx),ewith|0,_->(* deadcode is off *)flush_queueexpr_queueloc(let*ce,instrs=e'inlet*loc=statement_loclocinreturn(instrs@[J.Expression_statementce,loc]))|1,_whenConfig.Flag.compact()&&((not(Config.Flag.pretty()))||not(keep_namex))->enqueueexpr_queuexloce'|1,Constant(Int_|Float_)->enqueueexpr_queuexloce'|_->flush_queueexpr_queueloc(let*ce,instrs=e'inlet*loc=statement_loclocinreturn(instrs@[J.variable_declaration[J.Vx,(ce,loc)],loc])))|Set_field(x,n,_,y)->flush_queueexpr_queueloc(let*cx=accessxinlet*cy=accessyinlet*()=infomutator_pinlet*loc=statement_loclocinreturn[J.Expression_statement(J.EBin(J.Eq,Mlvalue.Block.fieldcxn,cy)),loc])|Offset_ref(x,n)->(* FIX: may overflow.. *)flush_queueexpr_queueloc(let*cx=accessxinletexpr=Mlvalue.Block.fieldcx0inletexpr'=matchnwith|1->J.EUn(J.IncrA,expr)|-1->J.EUn(J.DecrA,expr)|nwhenn<0(* *)->J.EBin(J.MinusEq,expr,int(-n))|n(* n > 0 *)->J.EBin(J.PlusEq,expr,intn)inlet*()=infomutator_pinlet*loc=statement_loclocinreturn[J.Expression_statementexpr',loc])|Array_set(x,y,z)->flush_queueexpr_queueloc(let*cx=accessxinlet*cy=accessyinlet*cz=accesszinlet*()=infomutator_pinlet*loc=statement_loclocinreturn[J.Expression_statement(J.EBin(J.Eq,Mlvalue.Array.fieldcxcy,cz)),loc])|Event_->[],expr_queueandtranslate_instrs_rev(ctx:Ctx.t)locexpr_queueinstrsacc_revmuts_map=matchinstrswith|[]->loc,acc_rev,expr_queue|Let(_,Closure_)::_->letnames,pcs,all,rem,loc=collect_closureslocinstrsinletfvs=List.fold_leftpcs~init:Code.Var.Set.empty~f:(funaccpc->Code.Var.Set.unionacc(Addr.Map.findpcctx.freevars))inletmuts=List.fold_leftpcs~init:Code.Var.Set.empty~f:(funaccpc->Code.Var.Set.unionacc(Code.Addr.Map.findpcctx.Ctx.mutated_vars))inletnames=List.fold_leftnames~init:Code.Var.Set.empty~f:(funaccname->Code.Var.Set.addnameacc)inassert(Code.Var.Set.cardinalnames=List.lengthall);assert(Code.Var.Set.(is_empty(diffmutsfvs)));letold_muts_map=muts_mapinletmuts_map_l=Code.Var.Set.elementsmuts|>List.map~f:(funx->(x,matchCode.Var.Map.find_optxold_muts_mapwith|None->Code.Var.forkx|Somex'->x'))inletmuts_map=List.fold_leftmuts_map_l~init:old_muts_map~f:(funacc(x,x')->Var.Map.addxx'acc)in(* Rewrite blocks using well-scoped closure variables *)letctx=ifList.is_emptymuts_map_lthenctxelseletsubst=Subst.from_mapmuts_mapinletp,_visited=List.fold_leftpcs~init:(ctx.blocks,Addr.Set.empty)~f:(fun(blocks,visited)pc->Subst.Excluding_Binders.cont'substpcblocksvisited)in{ctxwithblocks=p}inletvdkind=function|[]->[]|l->[J.variable_declaration~kind(List.revl),J.N]in(* flush variables part of closures env from the queue *)letbind_fvs,bind_fvs_muts,expr_queue=letexpr_queue,vars,lets=Code.Var.Set.fold(funv(expr_queue,vars,lets)->assert(not(Code.Var.Set.memvnames));let(px,cx,locx),expr_queue=access_queue_locexpr_queuelocvinletflushed=Code.Var.Set.(equal(sndpx)(singletonv))inmatch(flushed,Code.Var.Map.find_optvmuts_map,Code.Var.Map.find_optvold_muts_map)with|true,None,_->expr_queue,vars,lets|(true|false),Some_,Some_->expr_queue,vars,lets|(true|false),Somev',None->letlets=(J.Vv',(cx,locx))::letsinexpr_queue,vars,lets|false,None,_->letvars=(J.Vv,(cx,locx))::varsinexpr_queue,vars,lets)(Code.Var.Set.difffvsnames)(expr_queue,[],[])invars,lets,expr_queuein(* Mutually recursive functions need to be properly scoped. *)letbind_fvs_rec,funs_rev,expr_queue=List.fold_leftall~init:([],[],expr_queue)~f:(fun(mut_rec,st_rev,expr_queue)(i,loc)->letx'=matchiwith|Let(x',_)->x'|_->assertfalseinletl,expr_queue=translate_instrctxexpr_queuelociinifCode.Var.Set.memx'fvsthenletmut_rec=matchCode.Var.Map.find_optx'muts_mapwith|None->mut_rec|Somev'->(J.Vv',(J.EVar(J.Vx'),J.N))::mut_recinmatchlwith|[i]->mut_rec,i::st_rev,expr_queue|[]->let(_px,cx,locx),expr_queue=access_queue_locexpr_queuelocx'in(mut_rec,(J.variable_declaration[J.Vx',(cx,locx)],locx)::st_rev,expr_queue)|_::_::_->assertfalseelsemut_rec,List.rev_appendlst_rev,expr_queue)inletacc_rev=vdVarbind_fvs@acc_revinletacc_rev=vdLetbind_fvs_muts@acc_revinletacc_rev=funs_rev@acc_revinletacc_rev=vdLetbind_fvs_rec@acc_revintranslate_instrs_revctxlocexpr_queueremacc_revmuts_map|Eventloc::rem->translate_instrs_revctx(J.Piloc)expr_queueremacc_revmuts_map|instr::rem->letst,expr_queue=translate_instrctxexpr_queuelocinstrinletacc_rev=List.rev_appendstacc_revintranslate_instrs_revctxlocexpr_queueremacc_revmuts_mapandtranslate_instrs(ctx:Ctx.t)locexpr_queueinstrs=letloc,st_rev,expr_queue=translate_instrs_rev(ctx:Ctx.t)locexpr_queueinstrs[]Var.Map.emptyinloc,List.revst_rev,expr_queue(* Compile loops. *)andcompile_blockstlocqueue(pc:Addr.t)scope_stack~fall_through=if(not(List.is_emptyqueue))&&(Structure.is_loop_headerst.structurepc||(* Do not inline expressions across block boundaries when --no-inline is used
Single-stepping in the debugger should work better this way (fixes #290). *)not(Config.Flag.inline()))thenletnever,code=compile_blockstloc[]pcscope_stack~fall_throughinnever,flush_allqueueloccodeelsematchStructure.is_loop_headerst.structurepcwith|false->compile_block_no_loopstlocqueuepcscope_stack~fall_through|true->ifdebug()thenFormat.eprintf"@[<hv 2>for(;;) {@,";letnever_body,body=letlab=J.Label.fresh()inletlab_used=reffalseinletexit_branch_used=reffalseinletscope_stack=(pc,(lab,lab_used,Loop))::scope_stackinletscope_stack=matchfall_throughwith|Blockfall_through->(fall_through,(lab,lab_used,Exit_loopexit_branch_used))::scope_stack|Return->scope_stackinletnever_body,body=compile_block_no_loopstlocqueuepcscope_stack~fall_through:(Blockpc)inifdebug()thenFormat.eprintf"}@]@,";letfor_loop=J.For_statement(J.LeftNone,None,None,Js_simpl.blockbody),locinletlabel=if!lab_usedthenSomelabelseNoneinletfor_loop=matchlabelwith|None->for_loop|Somelabel->J.Labelled_statement(label,for_loop),J.Nin(not!exit_branch_used)&&never_body,[for_loop]innever_body,body(* Compile block. Loops have already been handled. *)andcompile_block_no_loopstlocqueue(pc:Addr.t)~fall_throughscope_stack=ifpc<0thenassertfalse;ifAddr.Set.mempc!(st.visited_blocks)then(Format.eprintf"Trying to compile a block twice !!!! %d@."pc;assertfalse);ifdebug()thenFormat.eprintf"Compiling block %d@;"pc;st.visited_blocks:=Addr.Set.addpc!(st.visited_blocks);letblock=Addr.Map.findpcst.ctx.blocksinletloc,seq,queue=translate_instrsst.ctxlocqueueblock.bodyinletnbbranch=matchblock.branchwith|Switch(_,a)->(* Build an artifical dtree with the correct layout so that
[Dtree.nbbranch dtree pc] is correct *)letdtree=DTree.build_switchainfunpc->DTree.nbbranchdtreepc|Cond(_,a,b)->letdtree=DTree.build_ifabinfunpc->DTree.nbbranchdtreepc|_->fun_pc->0inletnew_scopes=Structure.get_edgesst.dompc|>Addr.Set.elements|>List.filter~f:(funpc'->nbbranchpc'>=2||Structure.is_merge_nodest.structurepc')|>Structure.sort_in_post_orderst.structureinletrecloop~scope_stack~fall_throughl=matchlwith|[]->compile_conditionalstqueue~fall_throughlocblock.branchscope_stack|x::xs->(letl=J.Label.fresh()inletused=reffalseinletscope_stack=(x,(l,used,Forward))::scope_stackinlet_never_inner,inner=loop~scope_stack~fall_through:(Blockx)xsinletnever,code=compile_blockstloc[]xscope_stack~fall_throughinmatch!usedwith|true->never,[J.Labelled_statement(l,(J.Blockinner,J.N)),J.N]@code|false->never,inner@code)inletnever_after,after=loop~scope_stack~fall_throughnew_scopesinnever_after,seq@afterandcompile_decision_treekindstscope_stackloc_beforecxloc_afterdtree~fall_through=(* Some changes here may require corresponding changes
in function [DTree.fold_cont] above. *)letreclooploccxscope_stack:_->bool*_=function|DTree.Branch(l,cont)->ifdebug()thenFormat.eprintf"@[<hv 2>case %s(%a) {@;"kindFormat.(pp_print_list~pp_sep:(funfmt()->Format.pp_print_stringfmt", ")(funfmtpc->Format.fprintffmt"%d"pc))l;letnever,code=compile_branchstloc_after[]contscope_stack~fall_throughinifdebug()thenFormat.eprintf"}@]@;";never,code|DTree.If(cond,cont1,cont2)->letnever1,iftrue=looploc_aftercxscope_stackcont1inletnever2,iffalse=looploc_aftercxscope_stackcont2inlete'=matchcondwith|IsTrue->cx|CEqn->J.EBin(J.EqEqEq,targetintn,cx)|CLtn->J.EBin(J.LtInt,targetintn,cx)|CLen->J.EBin(J.LeInt,targetintn,cx)in(never1&&never2,Js_simpl.if_statement~function_end:(fun()->source_locationst.ctxAfterst.pc)e'loc(Js_simpl.blockiftrue)never1(Js_simpl.blockiffalse)never2)|DTree.Switcha->letall_never=reftrueinletlen=Array.lengthainletlast_index=len-1inletlab=J.Label.fresh()inletlabel_used=reffalseinletexit_branch_used=reffalseinletscope_stack=matchfall_throughwith|Blockfall_through->(fall_through,(lab,label_used,Exit_switchexit_branch_used))::scope_stack|Return->scope_stackinletlast=letcase=a.(last_index)inletnever,code=looploc_aftercxscope_stack(Branchcase)inifnotneverthenall_never:=false;codeinletrecloop_casesposacc=let((ints,_cont)ascase)=a.(pos)inletnever,code=looploc_aftercxscope_stack(Branchcase)inifnotneverthenall_never:=false;let_,acc=List.fold_rightints~init:(true,acc)~f:(funi(first,acc)->(false,(inti,iffirstthenifnotneverthencode@[Break_statementNone,J.N]elsecodeelse[])::acc))inifpos=0thenaccelseloop_cases(predpos)accinletl=loop_cases(last_index-1)[]inletcode=if!label_usedthen[(J.Labelled_statement(lab,(J.Switch_statement(cx,l,Somelast,[]),loc)),loc)]else[J.Switch_statement(cx,l,Somelast,[]),loc]in(not!exit_branch_used)&&!all_never,codeinletcx,binds,loc=matchcxwith|(J.EVar_|_)whenDTree.nbcompdtree<=1->cx,[],loc_before|_->letv=J.V(Code.Var.fresh())inJ.EVarv,[J.variable_declaration[v,(cx,loc_before)],J.N],loc_afterinletnever,code=looploccxscope_stackdtreeinnever,binds@codeandcompile_conditionalstqueue~fall_throughloclastscope_stack:_*_=(ifdebug()thenmatchlastwith|Branch_|Poptrap_->()|Pushtrap_->Format.eprintf"@[<hv 2>try {@;"|Return_->Format.eprintf"ret;@;"|Raise_->Format.eprintf"raise;@;"|Stop->Format.eprintf"stop;@;"|Cond(x,_,_)->Format.eprintf"@[<hv 2>cond(%a){@;"Code.Var.printx|Switch(x,_)->Format.eprintf"@[<hv 2>switch(%a){@;"Code.Var.printx);letres=matchlastwith|Returnx->letopenExpr_builderinletinstrs=let*cx=accessxinletreturn_expr=ifVar.equalst.ctx.deadcode_sentinalxthenNoneelseSomecxinletloc'=matchcxwith|ECall_->((* We usually don't have a good locations for tail
calls, so use the end of the function instead *)matchsource_locationst.ctxAfterst.pcwith|J.N->loc|loc->loc)|_->locinlet*loc=statement_loclocinreturn[J.Return_statement(return_expr,loc'),loc]intrue,flush_allqueuelocinstrs|Raise(x,k)->letopenExpr_builderinletinstrs=let*cx=accessxinlet*loc=statement_loclocinreturn(throw_statementst.ctxcxkloc)intrue,flush_allqueuelocinstrs|Stop->lete_opt=ifst.ctx.Ctx.should_exportthenSome(s_varGlobal_constant.exports)elseNoneintrue,flush_allqueueloc[J.Return_statement(e_opt,loc),loc]|Branchcont->compile_branchstlocqueuecontscope_stack~fall_through|Pushtrap(c1,x,e1)->letnever_body,body=compile_branchstJ.N[]c1scope_stack~fall_throughinifdebug()thenFormat.eprintf"@,}@]@,@[<hv 2>catch {@;";letnever_handler,handler=compile_branchstJ.U[]e1scope_stack~fall_throughinletexn_var,handler=assert(not(List.memx~set:(snde1)));letwrap_exnx=J.call(Share.get_prim(runtime_funst.ctx)"caml_wrap_exception"st.ctx.Ctx.share)[J.EVar(J.Vx)]J.Ninmatchst.ctx.Ctx.live.(Var.idxx)with|0->x,handler|_->lethandler_var=Code.Var.forkxin(handler_var,(J.variable_declaration[J.Vx,(wrap_exnhandler_var,J.U)],J.N)::handler)in(never_body&&never_handler,flush_allqueueloc[(J.Try_statement(body,Some(Some(J.param'(J.Vexn_var)),handler),None),loc)])|Poptrapcont->letnever,code=compile_branchstJ.N[]contscope_stack~fall_throughinnever,flush_allqueueloccode|Cond(x,c1,c2)->letcx,loc_before,queue=Expr_builder.getqueueloc(Expr_builder.accessx)in(* We keep track of the location [loc_before] before the
expression is evaluated and of the location after [loc]. *)letnever,b=compile_decision_tree"Bool"stscope_stack~fall_throughloc_beforecxloc(DTree.build_ifc1c2)innever,flush_allqueueloc_beforeb|Switch(x,a1)->letcx,loc_before,queue=Expr_builder.getqueueloc(Expr_builder.accessx)in(* We keep track of the location [loc_before] before the
expression is evaluated and of the location after [loc]. *)letnever,code=compile_decision_tree"Int"stscope_stack~fall_throughloc_beforecxloc(DTree.build_switcha1)innever,flush_allqueueloc_beforecodein(ifdebug()thenmatchlastwith|Branch_|Poptrap_|Return_|Raise_|Stop->()|Switch_|Cond_|Pushtrap_->Format.eprintf"}@]@;");resandcompile_argument_passingctxlocqueue(pc,args)back_edgecontinuation=ifList.is_emptyargsthencontinuationqueueelseletblock=Addr.Map.findpcctx.Ctx.blocksinparallel_renaminglocback_edgeblock.paramsargscontinuationqueueandcompile_branchstlocqueue((pc,_)ascont)scope_stack~fall_through:bool*_=letscope=List.assoc_optpcscope_stackinletback_edge=List.exists~f:(function|pc',(_,_,Loop)whenpc'=pc->true|_->false)scope_stackincompile_argument_passingst.ctxlocqueuecontback_edge(funqueue->ifmatchfall_throughwith|Blockpc'->pc'=pc|Return->falsethenfalse,flush_allqueueloc[]elsematchscopewith|Some(l,used,Loop)->(* Loop back to the beginning of the loop using continue.
We can skip the label if we're not inside a nested loop. *)letreccan_skip_labelscope_stack=matchscope_stackwith|[]->assertfalse|(_,(_,_,(Forward|Exit_switch_)))::rem->can_skip_labelrem|(pc',(l',_,(Loop|Exit_loop_)))::rem->Poly.(l'=l)&&(pc=pc'||can_skip_labelrem)inletlabel=ifcan_skip_labelscope_stackthenNoneelse(used:=true;Somel)inifdebug()thenifOption.is_nonelabelthenFormat.eprintf"continue;@,"elseFormat.eprintf"continue (%d);@,"pc;true,flush_allqueueloc[J.Continue_statementlabel,J.N]|Some(l,used,(Exit_loopbranch_used|Exit_switchbranch_used))->(* Break out of a loop or switch (using Break)
We can skip the label if we're not inside a nested loop or switch.
*)branch_used:=true;letreccan_skip_labelscope_stack=matchscope_stackwith|[]->assertfalse|(_,(_,_,Forward))::rem->can_skip_labelrem|(pc',(l',_,(Loop|Exit_loop_|Exit_switch_)))::rem->Poly.(l'=l)&&(pc=pc'||can_skip_labelrem)inletlabel=ifcan_skip_labelscope_stackthenNoneelse(used:=true;Somel)inifdebug()thenifOption.is_nonelabelthenFormat.eprintf"break;@,"elseFormat.eprintf"break (%d);@,"pc;true,flush_allqueueloc[J.Break_statementlabel,J.N]|Some(l,used,Forward)->(* break outside a labelled statement. The label is mandatory in this case. *)ifdebug()thenFormat.eprintf"(br %d)@;"pc;used:=true;true,flush_allqueueloc[J.Break_statement(Somel),J.N]|None->compile_blockstlocqueuepcscope_stack~fall_through)andcompile_closurectx(pc,args)=letst=build_graphctxpcinletcurrent_blocks=Structure.get_nodesst.structureinifdebug()thenFormat.eprintf"@[<hv 2>closure {@;";letscope_stack=[]inletstart_loc=letblock=Addr.Map.findpcctx.Ctx.blocksinmatchblock.bodywith|Eventloc::_->J.Piloc|_->J.Uinlet_never,res=compile_branchststart_loc[](pc,args)scope_stack~fall_through:ReturninifAddr.Set.cardinal!(st.visited_blocks)<>Addr.Set.cardinalcurrent_blocksthen(letmissing=Addr.Set.diffcurrent_blocks!(st.visited_blocks)inFormat.eprintf"Some blocks not compiled %s!@."(string_of_setmissing);assertfalse);ifdebug()thenFormat.eprintf"}@]@;";resandcollect_closureslocl=matchlwith|Eventloc::(Let(_,Closure_)::_asrem)->collect_closures(J.Piloc)rem|(Let(x,Closure(_,(pc,_)))asi)::rem->letnames',pcs',i',rem',loc'=collect_closureslocreminx::names',pc::pcs',(i,loc)::i',rem',loc'|_->[],[],[],l,locletgenerate_shared_valuectx=letstrings=(J.variable_declaration((matchctx.Ctx.exported_runtimewith|None->[]|Some(_,{contents=false})->[]|Some(v,_)->[(J.Vv,(J.dot(s_varGlobal_constant.global_object)(Utf8_string.of_string_exn"jsoo_runtime"),J.U))])@List.map(StringMap.bindingsctx.Ctx.share.Share.vars.Share.byte_strings)~f:(fun(s,v)->v,(str_js_bytes,J.U))@List.map(StringMap.bindingsctx.Ctx.share.Share.vars.Share.utf_strings)~f:(fun(s,v)->v,(str_js_utf8s,J.U))@List.map(StringMap.bindingsctx.Ctx.share.Share.vars.Share.prims)~f:(fun(s,v)->v,(runtime_functxs,J.U))),J.U)inifnot(Config.Flag.inline_callgen())thenletapplies=List.map(Share.AppMap.bindingsctx.Ctx.share.Share.vars.Share.applies)~f:(fun(desc,v)->matchgenerate_apply_functxdescwith|J.EFun(_,decl)->J.Function_declaration(v,decl),J.U|_->assertfalse)instrings::applieselse[strings]letcompile_programctxpc=ifdebug()thenFormat.eprintf"@[<v 2>";letres=compile_closurectx(pc,[])inletres=generate_shared_valuectx@resinifdebug()thenFormat.eprintf"@]@.";resletf(p:Code.program)~exported_runtime~live_vars~trampolined_calls~in_cps~should_export~warn_on_unhandled_effect~deadcode_sentinaldebug=lett'=Timer.make()inletshare=Share.get~trampolined_calls~in_cps~alias_prims:exported_runtimepinletexported_runtime=ifexported_runtimethenSome(Code.Var.fresh_n"runtime",reffalse)elseNoneinletmutated_vars=Freevars.f_mutablepinletfreevars=Freevars.fpinletctx=Ctx.initial~warn_on_unhandled_effect~exported_runtime~should_export~deadcode_sentinal~mutated_vars~freevars~in_cpsp.blockslive_varstrampolined_callssharedebuginletp=compile_programctxp.startiniftimes()thenFormat.eprintf" code gen.: %a@."Timer.printt';pletinit()=List.iter~f:(fun(nm,nm')->Primitive.aliasnmnm')["%int_mul","caml_mul";"%int_div","caml_div";"%int_mod","caml_mod";"caml_int32_neg","%int_neg";"caml_int32_add","%int_add";"caml_int32_sub","%int_sub";"caml_int32_mul","%int_mul";"caml_int32_div","%int_div";"caml_int32_mod","%int_mod";"caml_int32_and","%int_and";"caml_int32_or","%int_or";"caml_int32_xor","%int_xor";"caml_int32_shift_left","%int_lsl";"caml_int32_shift_right","%int_asr";"caml_int32_shift_right_unsigned","%int_lsr";"caml_int32_of_int","%identity";"caml_int32_to_int","%identity";"caml_int32_of_float","caml_int_of_float";"caml_int32_to_float","%identity";"caml_int32_format","caml_format_int";"caml_int32_of_string","caml_int_of_string";"caml_int32_compare","caml_int_compare";"caml_nativeint_neg","%int_neg";"caml_nativeint_add","%int_add";"caml_nativeint_sub","%int_sub";"caml_nativeint_mul","%int_mul";"caml_nativeint_div","%int_div";"caml_nativeint_mod","%int_mod";"caml_nativeint_and","%int_and";"caml_nativeint_or","%int_or";"caml_nativeint_xor","%int_xor";"caml_nativeint_shift_left","%int_lsl";"caml_nativeint_shift_right","%int_asr";"caml_nativeint_shift_right_unsigned","%int_lsr";"caml_nativeint_of_int","%identity";"caml_nativeint_to_int","%identity";"caml_nativeint_of_float","caml_int_of_float";"caml_nativeint_to_float","%identity";"caml_nativeint_of_int32","%identity";"caml_nativeint_to_int32","%identity";"caml_nativeint_format","caml_format_int";"caml_nativeint_of_string","caml_int_of_string";"caml_nativeint_compare","caml_int_compare";"caml_nativeint_bswap","caml_int32_bswap";"caml_int64_of_int","caml_int64_of_int32";"caml_int64_to_int","caml_int64_to_int32";"caml_int64_of_nativeint","caml_int64_of_int32";"caml_int64_to_nativeint","caml_int64_to_int32";"caml_float_of_int","%identity";"caml_array_get_float","caml_array_get";"caml_floatarray_get","caml_array_get";"caml_array_get_addr","caml_array_get";"caml_array_set_float","caml_array_set";"caml_floatarray_set","caml_array_set";"caml_array_set_addr","caml_array_set";"caml_array_unsafe_get_float","caml_array_unsafe_get";"caml_floatarray_unsafe_get","caml_array_unsafe_get";"caml_array_unsafe_set_float","caml_array_unsafe_set";"caml_array_unsafe_set_addr","caml_array_unsafe_set";"caml_floatarray_unsafe_set","caml_array_unsafe_set";"caml_check_bound_gen","caml_check_bound";"caml_check_bound_float","caml_check_bound";"caml_alloc_dummy_float","caml_alloc_dummy";"caml_make_array","%identity";"caml_array_of_uniform_array","%identity";"caml_ensure_stack_capacity","%identity";"caml_js_from_float","%identity";"caml_js_to_float","%identity";"caml_js_from_int32","%identity";"caml_js_from_nativeint","%identity";"caml_js_to_int32","caml_int_of_float";"caml_js_to_nativeint","caml_int_of_float"];Hashtbl.iter(funname(k,_)->Primitive.registernamekNoneNone)internal_primitives