| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "Backend.h"
- #include "Library/ForInObjectEnumerator.h"
- #pragma prefast(push)
- #pragma prefast(disable:28652, "Prefast complains that the OR are causing the compiler to emit dynamic initializers and the variable to be allocated in read/write mem...")
- static const IR::BailOutKind c_debuggerBailOutKindForCall =
- IR::BailOutForceByFlag | IR::BailOutStackFrameBase | IR::BailOutBreakPointInFunction | IR::BailOutLocalValueChanged | IR::BailOutIgnoreException | IR::BailOutStep;
- static const IR::BailOutKind c_debuggerBaseBailOutKindForHelper = IR::BailOutIgnoreException | IR::BailOutForceByFlag;
- #pragma prefast(pop)
- uint
- IRBuilder::AddStatementBoundary(uint statementIndex, uint offset)
- {
- // Under debugger we use full stmt map, so that we know exactly start and end of each user stmt.
- // We insert additional instrs in between statements, such as ProfiledLoopStart, for these bytecode reader acts as
- // there is "unknown" stmt boundary with statementIndex == -1. Don't add stmt boundary for that as later
- // it may cause issues, e.g. see WinBlue 218307.
- if (!(statementIndex == Js::Constants::NoStatementIndex && this->m_func->IsJitInDebugMode()))
- {
- IR::PragmaInstr* pragmaInstr = IR::PragmaInstr::New(Js::OpCode::StatementBoundary, statementIndex, m_func);
- this->AddInstr(pragmaInstr, offset);
- }
- #ifdef BAILOUT_INJECTION
- if (!this->m_func->IsOOPJIT())
- {
- // Don't inject bailout if the function have trys
- if (!this->m_func->GetTopFunc()->HasTry() && (statementIndex != Js::Constants::NoStatementIndex))
- {
- if (Js::Configuration::Global.flags.IsEnabled(Js::BailOutFlag) && !this->m_func->GetJITFunctionBody()->IsLibraryCode())
- {
- ULONG line;
- LONG col;
- // Since we're on a separate thread, don't allow the line cache to be allocated in the Recycler.
- if (((Js::FunctionBody*)m_func->GetJITFunctionBody()->GetAddr())->GetLineCharOffset(this->m_jnReader.GetCurrentOffset(), &line, &col, false /*canAllocateLineCache*/))
- {
- line++;
- if (Js::Configuration::Global.flags.BailOut.Contains(line, (uint32)col) || Js::Configuration::Global.flags.BailOut.Contains(line, (uint32)-1))
- {
- this->InjectBailOut(offset);
- }
- }
- }
- else if (Js::Configuration::Global.flags.IsEnabled(Js::BailOutAtEveryLineFlag))
- {
- this->InjectBailOut(offset);
- }
- }
- }
- #endif
- return m_statementReader.MoveNextStatementBoundary();
- }
- // Add conditional bailout for breaking into interpreter debug thunk - for fast F12.
- void
- IRBuilder::InsertBailOutForDebugger(uint byteCodeOffset, IR::BailOutKind kind, IR::Instr* insertBeforeInstr /* default nullptr */)
- {
- Assert(m_func->IsJitInDebugMode());
- Assert(byteCodeOffset != Js::Constants::NoByteCodeOffset);
- BailOutInfo * bailOutInfo = JitAnew(m_func->m_alloc, BailOutInfo, byteCodeOffset, m_func);
- IR::BailOutInstr * instr = IR::BailOutInstr::New(Js::OpCode::BailForDebugger, kind, bailOutInfo, bailOutInfo->bailOutFunc);
- if (insertBeforeInstr)
- {
- InsertInstr(instr, insertBeforeInstr);
- }
- else
- {
- this->AddInstr(instr, m_lastInstr->GetByteCodeOffset());
- }
- }
- bool
- IRBuilder::DoBailOnNoProfile()
- {
- if (PHASE_OFF(Js::BailOnNoProfilePhase, this->m_func->GetTopFunc()))
- {
- return false;
- }
- Func *const topFunc = m_func->GetTopFunc();
- if(topFunc->GetWorkItem()->GetProfiledIterations() == 0)
- {
- // The top function has not been profiled yet. Some switch must have been used to force jitting. This is not a
- // real-world case, but for the purpose of testing the JIT, it's beneficial to generate code in unprofiled paths.
- return false;
- }
- if (m_func->HasProfileInfo() && m_func->GetReadOnlyProfileInfo()->IsNoProfileBailoutsDisabled())
- {
- return false;
- }
- if (!m_func->DoGlobOpt())
- {
- return false;
- }
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (this->m_func->GetTopFunc() != this->m_func && Js::Configuration::Global.flags.IsEnabled(Js::ForceJITLoopBodyFlag))
- {
- // No profile data for loop bodies with -force...
- return false;
- }
- #endif
- if (!this->m_func->HasProfileInfo())
- {
- return false;
- }
- if (m_func->GetTopFunc()->GetJITFunctionBody()->IsCoroutine())
- {
- return false;
- }
- return true;
- }
- void
- IRBuilder::InsertBailOnNoProfile(uint offset)
- {
- Assert(DoBailOnNoProfile());
- if (this->callTreeHasSomeProfileInfo)
- {
- return;
- }
- IR::Instr *startCall = nullptr;
- int count = 0;
- FOREACH_SLIST_ENTRY(IR::Instr *, argInstr, this->m_argStack)
- {
- if (argInstr->m_opcode == Js::OpCode::StartCall)
- {
- startCall = argInstr;
- count++;
- if (count > 1)
- {
- return;
- }
- }
- } NEXT_SLIST_ENTRY;
- AnalysisAssert(startCall);
- if (startCall->m_prev->m_opcode != Js::OpCode::BailOnNoProfile)
- {
- InsertBailOnNoProfile(startCall);
- }
- }
- void IRBuilder::InsertBailOnNoProfile(IR::Instr *const insertBeforeInstr)
- {
- Assert(DoBailOnNoProfile());
- IR::Instr *const bailOnNoProfileInstr = IR::Instr::New(Js::OpCode::BailOnNoProfile, m_func);
- InsertInstr(bailOnNoProfileInstr, insertBeforeInstr);
- }
- #ifdef BAILOUT_INJECTION
- void
- IRBuilder::InjectBailOut(uint offset)
- {
- if(m_func->IsSimpleJit())
- {
- return; // bailout injection is only applicable to full JIT
- }
- IR::IntConstOpnd * opnd = IR::IntConstOpnd::New(0, TyInt32, m_func);
- uint bailOutOffset = offset;
- if (bailOutOffset == Js::Constants::NoByteCodeOffset)
- {
- bailOutOffset = m_lastInstr->GetByteCodeOffset();
- }
- BailOutInfo * bailOutInfo = JitAnew(m_func->m_alloc, BailOutInfo, bailOutOffset, m_func);
- IR::BailOutInstr * instr = IR::BailOutInstr::New(Js::OpCode::BailOnEqual, IR::BailOutInjected, bailOutInfo, bailOutInfo->bailOutFunc);
- instr->SetSrc1(opnd);
- instr->SetSrc2(opnd);
- this->AddInstr(instr, offset);
- }
- void
- IRBuilder::CheckBailOutInjection(Js::OpCode opcode)
- {
- // Detect these sequence and disable Bailout injection between them:
- // LdStackArgPtr
- // LdArgCnt
- // ApplyArgs
- // This assumes that LdStackArgPtr, LdArgCnt and ApplyArgs can
- // only be emitted in these sequence. This will need to be modified if it changes.
- //
- // Also insert a single bailout before the beginning of a switch case block for all the case labels.
- switch(opcode)
- {
- case Js::OpCode::LdStackArgPtr:
- Assert(!seenLdStackArgPtr);
- Assert(!expectApplyArg);
- seenLdStackArgPtr = true;
- break;
- case Js::OpCode::LdArgCnt:
- Assert(seenLdStackArgPtr);
- Assert(!expectApplyArg);
- expectApplyArg = true;
- break;
- case Js::OpCode::ApplyArgs:
- Assert(seenLdStackArgPtr);
- Assert(expectApplyArg);
- seenLdStackArgPtr = false;
- expectApplyArg = false;
- break;
- case Js::OpCode::BeginSwitch:
- case Js::OpCode::ProfiledBeginSwitch:
- Assert(!seenProfiledBeginSwitch);
- seenProfiledBeginSwitch = true;
- break;
- case Js::OpCode::EndSwitch:
- Assert(seenProfiledBeginSwitch);
- seenProfiledBeginSwitch = false;
- break;
- default:
- Assert(!seenLdStackArgPtr);
- Assert(!expectApplyArg);
- break;
- }
- }
- #endif
- bool
- IRBuilder::IsLoopBody() const
- {
- return m_func->IsLoopBody();
- }
- bool
- IRBuilder::IsLoopBodyInTry() const
- {
- return m_func->IsLoopBodyInTry();
- }
- bool
- IRBuilder::IsLoopBodyReturnIPInstr(IR::Instr * instr) const
- {
- IR::Opnd * dst = instr->GetDst();
- return (dst && dst->IsRegOpnd() && dst->AsRegOpnd()->m_sym == m_loopBodyRetIPSym);
- }
- bool
- IRBuilder::IsLoopBodyOuterOffset(uint offset) const
- {
- if (!IsLoopBody())
- {
- return false;
- }
- return (offset >= m_func->m_workItem->GetLoopHeader()->endOffset || offset < m_func->m_workItem->GetLoopHeader()->startOffset);
- }
- uint
- IRBuilder::GetLoopBodyExitInstrOffset() const
- {
- // End of loop body, start of StSlot and Ret instruction at endOffset + 1
- return m_func->m_workItem->GetLoopHeader()->endOffset + 1;
- }
- Js::RegSlot
- IRBuilder::GetEnvReg() const
- {
- return m_func->GetJITFunctionBody()->GetEnvReg();
- }
- Js::RegSlot
- IRBuilder::GetEnvRegForInnerFrameDisplay() const
- {
- Js::RegSlot envReg = m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg();
- if (envReg == Js::Constants::NoRegister)
- {
- envReg = this->GetEnvReg();
- }
- return envReg;
- }
- void
- IRBuilder::AddEnvOpndForInnerFrameDisplay(IR::Instr *instr, uint offset)
- {
- Js::RegSlot envReg = this->GetEnvRegForInnerFrameDisplay();
- if (envReg != Js::Constants::NoRegister)
- {
- IR::RegOpnd *src2Opnd;
- if (envReg == m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg() &&
- m_func->DoStackFrameDisplay() &&
- m_func->IsTopFunc())
- {
- src2Opnd = IR::RegOpnd::New(TyVar, m_func);
- this->AddInstr(
- IR::Instr::New(
- Js::OpCode::LdSlot, src2Opnd,
- this->BuildFieldOpnd(Js::OpCode::LdSlot, m_func->GetLocalFrameDisplaySym()->m_id, 0, (Js::PropertyIdIndexType)-1, PropertyKindSlots),
- m_func),
- offset);
- }
- else
- {
- src2Opnd = this->BuildSrcOpnd(envReg);
- }
- instr->SetSrc2(src2Opnd);
- }
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::Build
- ///
- /// IRBuilder main entry point. Read the bytecode for this function and
- /// generate IR.
- ///
- ///----------------------------------------------------------------------------
- void
- IRBuilder::Build()
- {
- m_funcAlloc = m_func->m_alloc;
- NoRecoverMemoryJitArenaAllocator localAlloc(_u("BE-IRBuilder"), m_funcAlloc->GetPageAllocator(), Js::Throw::OutOfMemory);
- m_tempAlloc = &localAlloc;
- uint32 offset;
- uint32 statementIndex = m_statementReader.GetStatementIndex();
- m_argStack = JitAnew(m_tempAlloc, SList<IR::Instr *>, m_tempAlloc);
- this->branchRelocList = JitAnew(m_tempAlloc, SList<BranchReloc *>, m_tempAlloc);
- Func * topFunc = this->m_func->GetTopFunc();
- if (topFunc->HasTry() &&
- ((!topFunc->IsLoopBody() && !PHASE_OFF(Js::OptimizeTryCatchPhase, topFunc)) ||
- (topFunc->HasFinally() && !topFunc->IsLoopBody() && !PHASE_OFF(Js::OptimizeTryFinallyPhase, topFunc)) ||
- (topFunc->IsSimpleJit() && topFunc->GetJITFunctionBody()->DoJITLoopBody()) || // should be relaxed as more bailouts are added in Simple Jit
- topFunc->IsLoopBodyInTryFinally())) // We need accurate flow when we are full jitting loop bodies which have try finally
- {
- this->handlerOffsetStack = JitAnew(m_tempAlloc, SList<handlerStackElementType>, m_tempAlloc);
- }
- this->firstTemp = m_func->GetJITFunctionBody()->GetFirstTmpReg();
- Js::RegSlot tempCount = m_func->GetJITFunctionBody()->GetTempCount();
- if (tempCount > 0)
- {
- this->tempMap = AnewArrayZ(m_tempAlloc, SymID, tempCount);
- }
- else
- {
- this->tempMap = nullptr;
- }
- m_func->m_headInstr = IR::EntryInstr::New(Js::OpCode::FunctionEntry, m_func);
- m_func->m_exitInstr = IR::ExitInstr::New(Js::OpCode::FunctionExit, m_func);
- m_func->m_tailInstr = m_func->m_exitInstr;
- m_func->m_headInstr->InsertAfter(m_func->m_tailInstr);
- if (m_func->GetJITFunctionBody()->IsParamAndBodyScopeMerged() || this->IsLoopBody())
- {
- this->SetParamScopeDone();
- }
- if (m_func->GetJITFunctionBody()->GetLocalClosureReg() != Js::Constants::NoRegister)
- {
- m_func->InitLocalClosureSyms();
- }
- m_functionStartOffset = m_jnReader.GetCurrentOffset();
- m_lastInstr = m_func->m_headInstr;
- AssertMsg(sizeof(SymID) >= sizeof(Js::RegSlot), "sizeof(SymID) != sizeof(Js::RegSlot)!!");
- // Skip the last EndOfBlock opcode
- Assert(!OpCodeAttr::HasMultiSizeLayout(Js::OpCode::EndOfBlock));
- uint32 lastOffset = m_func->GetJITFunctionBody()->GetByteCodeLength() - Js::OpCodeUtil::EncodedSize(Js::OpCode::EndOfBlock, Js::SmallLayout);
- uint32 offsetToInstructionCount = lastOffset;
- if (this->IsLoopBody())
- {
- // LdSlot needs to cover all the register, including the temps, because we might treat
- // those as if they are local for the value of the with statement
- this->m_ldSlots = BVFixed::New<JitArenaAllocator>(m_func->GetJITFunctionBody()->GetLocalsCount(), m_tempAlloc);
- this->m_stSlots = BVFixed::New<JitArenaAllocator>(m_func->GetJITFunctionBody()->GetFirstTmpReg(), m_tempAlloc);
- this->m_loopBodyRetIPSym = StackSym::New(TyMachReg, this->m_func);
- #if DBG
- if (m_func->GetJITFunctionBody()->GetTempCount() != 0)
- {
- this->m_usedAsTemp = BVFixed::New<JitArenaAllocator>(m_func->GetJITFunctionBody()->GetTempCount(), m_tempAlloc);
- }
- #endif
- lastOffset = m_func->m_workItem->GetLoopHeader()->endOffset;
- AssertOrFailFast(lastOffset < m_func->GetJITFunctionBody()->GetByteCodeLength());
- // Ret is created at lastOffset + 1, so we need lastOffset + 2 entries
- offsetToInstructionCount = lastOffset + 2;
- // Compute the offset of the start of the locals array as a Var index.
- size_t localsOffset = Js::InterpreterStackFrame::GetOffsetOfLocals();
- Assert(localsOffset % sizeof(Js::Var) == 0);
- this->m_loopBodyLocalsStartSlot = (Js::PropertyId)(localsOffset / sizeof(Js::Var));
- }
- m_offsetToInstructionCount = offsetToInstructionCount;
- m_offsetToInstruction = JitAnewArrayZ(m_tempAlloc, IR::Instr *, offsetToInstructionCount);
- #ifdef BYTECODE_BRANCH_ISLAND
- longBranchMap = JitAnew(m_tempAlloc, LongBranchMap, m_tempAlloc);
- #endif
- m_switchBuilder.Init(m_func, m_tempAlloc, false);
- this->LoadNativeCodeData();
- this->BuildConstantLoads();
- if (!this->IsLoopBody() && m_func->GetJITFunctionBody()->HasImplicitArgIns())
- {
- this->BuildImplicitArgIns();
- }
- if (!this->IsLoopBody() && m_func->GetJITFunctionBody()->HasRestParameter())
- {
- this->BuildArgInRest();
- }
- // This is first bailout in the function, the locals at stack have not initialized to undefined, so do not restore them.
- // Note that for generators, we insert the bailout after the jump table to allow
- // the generator's execution to proceed before bailing out. Otherwise, we would always
- // bail to the beginning of the function in the interpreter, creating an infinite loop.
- if (m_func->IsJitInDebugMode() && !this->m_func->GetJITFunctionBody()->IsCoroutine())
- {
- this->InsertBailOutForDebugger(m_functionStartOffset, IR::BailOutForceByFlag | IR::BailOutBreakPointInFunction | IR::BailOutStep, nullptr);
- }
- #ifdef BAILOUT_INJECTION
- // Start bailout inject after the constant and arg load. We don't bailout before that
- IR::Instr * lastInstr = m_lastInstr;
- #endif
- offset = Js::Constants::NoByteCodeOffset;
- if (!this->IsLoopBody())
- {
- IR::Instr *instr;
- // Do the implicit operations LdEnv, NewScopeSlots, LdFrameDisplay, as indicated by function body attributes.
- Js::RegSlot envReg = m_func->GetJITFunctionBody()->GetEnvReg();
- if (envReg != Js::Constants::NoRegister && !this->RegIsConstant(envReg))
- {
- Js::OpCode newOpcode;
- Js::RegSlot thisReg = m_func->GetJITFunctionBody()->GetThisRegForEventHandler();
- IR::RegOpnd *srcOpnd = nullptr;
- IR::RegOpnd *dstOpnd = nullptr;
- if (thisReg != Js::Constants::NoRegister)
- {
- this->BuildArgIn0(offset, thisReg);
- srcOpnd = BuildSrcOpnd(thisReg);
- newOpcode = Js::OpCode::LdHandlerScope;
- }
- else
- {
- newOpcode = Js::OpCode::LdEnv;
- }
- dstOpnd = BuildDstOpnd(envReg);
- instr = IR::Instr::New(newOpcode, dstOpnd, m_func);
- if (srcOpnd)
- {
- instr->SetSrc1(srcOpnd);
- }
- if (dstOpnd->m_sym->m_isSingleDef)
- {
- dstOpnd->m_sym->m_isNotNumber = true;
- }
- this->AddInstr(instr, offset);
- }
- // The point at which we insert the generator resume jump table is important.
- // We want to insert it right *after* the environment and constants have
- // been loaded and *before* we create any other important objects
- // (e.g: FrameDisplay, LocalClosure) which will be passed on to the interpreter
- // frame when we bail out. Those values, if used when we resume, will be restored
- // by the bail-in code, therefore we don't want to unnecessarily create those new
- // objects every time we "resume" a generator
- //
- // Note: We need to make sure that all the values below are allocated on the heap.
- // so that they don't go away once this jit'd frame is popped off.
- #ifdef BAILOUT_INJECTION
- lastInstr = this->m_generatorJumpTable.BuildJumpTable();
- #else
- this->m_generatorJumpTable.BuildJumpTable();
- #endif
- // When debugging generators, insert bail-out after the jump table so that we can
- // get to the right point before going back to the interpreter.
- // This bailout is equivalent to the one inserted above for non-generator functions.
- // Additionally, we also need to insert bailouts on each resume point and right
- // after the bail-in code since this bailout is only for the very first time
- // we are in the generator.
- if (m_func->IsJitInDebugMode() && this->m_func->GetJITFunctionBody()->IsCoroutine())
- {
- this->InsertBailOutForDebugger(m_functionStartOffset, IR::BailOutForceByFlag | IR::BailOutBreakPointInFunction | IR::BailOutStep, nullptr);
- }
- Js::RegSlot funcExprScopeReg = m_func->GetJITFunctionBody()->GetFuncExprScopeReg();
- IR::RegOpnd *frameDisplayOpnd = nullptr;
- if (funcExprScopeReg != Js::Constants::NoRegister)
- {
- IR::RegOpnd *funcExprScopeOpnd = BuildDstOpnd(funcExprScopeReg);
- instr = IR::Instr::New(Js::OpCode::NewPseudoScope, funcExprScopeOpnd, m_func);
- this->AddInstr(instr, offset);
- }
- Js::RegSlot closureReg = m_func->GetJITFunctionBody()->GetLocalClosureReg();
- IR::RegOpnd *closureOpnd = nullptr;
- if (closureReg != Js::Constants::NoRegister)
- {
- Assert(!this->RegIsConstant(closureReg));
- if (m_func->DoStackScopeSlots())
- {
- closureOpnd = IR::RegOpnd::New(TyVar, m_func);
- }
- else
- {
- closureOpnd = this->BuildDstOpnd(closureReg);
- }
- if (m_func->GetJITFunctionBody()->HasScopeObject())
- {
- if (m_func->GetJITFunctionBody()->HasCachedScopePropIds())
- {
- this->BuildInitCachedScope(0, offset);
- }
- else
- {
- instr = IR::Instr::New(Js::OpCode::NewScopeObject, closureOpnd, m_func);
- this->AddInstr(instr, offset);
- }
- }
- else
- {
- Js::OpCode op =
- m_func->DoStackScopeSlots() ? Js::OpCode::NewStackScopeSlots : Js::OpCode::NewScopeSlots;
- uint size = m_func->GetJITFunctionBody()->IsParamAndBodyScopeMerged() ? m_func->GetJITFunctionBody()->GetScopeSlotArraySize() : m_func->GetJITFunctionBody()->GetParamScopeSlotArraySize();
- IR::Opnd * srcOpnd = IR::IntConstOpnd::New(size + Js::ScopeSlots::FirstSlotIndex, TyUint32, m_func);
- instr = IR::Instr::New(op, closureOpnd, srcOpnd, m_func);
- this->AddInstr(instr, offset);
- }
- if (closureOpnd->m_sym->m_isSingleDef)
- {
- closureOpnd->m_sym->m_isNotNumber = true;
- }
- if (m_func->DoStackScopeSlots())
- {
- // Init the stack closure sym and use it to save the scope slot pointer.
- this->AddInstr(
- IR::Instr::New(
- Js::OpCode::InitLocalClosure, this->BuildDstOpnd(m_func->GetLocalClosureSym()->m_id), m_func),
- offset);
- this->AddInstr(
- IR::Instr::New(
- Js::OpCode::StSlot,
- this->BuildFieldOpnd(
- Js::OpCode::StSlot, m_func->GetLocalClosureSym()->m_id, 0, (Js::PropertyIdIndexType)-1, PropertyKindSlots),
- closureOpnd, m_func),
- offset);
- }
- }
- Js::RegSlot frameDisplayReg = m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg();
- if (frameDisplayReg != Js::Constants::NoRegister)
- {
- Assert(!this->RegIsConstant(frameDisplayReg));
- Js::OpCode op = m_func->DoStackScopeSlots() ? Js::OpCode::NewStackFrameDisplay : Js::OpCode::LdFrameDisplay;
- if (funcExprScopeReg != Js::Constants::NoRegister)
- {
- // Insert the function expression scope ahead of any enclosing scopes.
- IR::RegOpnd * funcExprScopeOpnd = BuildSrcOpnd(funcExprScopeReg);
- frameDisplayOpnd = closureReg != Js::Constants::NoRegister ? IR::RegOpnd::New(TyVar, m_func) : BuildDstOpnd(frameDisplayReg);
- instr = IR::Instr::New(Js::OpCode::LdFrameDisplay, frameDisplayOpnd, funcExprScopeOpnd, m_func);
- if (envReg != Js::Constants::NoRegister)
- {
- instr->SetSrc2(this->BuildSrcOpnd(envReg));
- }
- this->AddInstr(instr, (uint)-1);
- }
- if (closureReg != Js::Constants::NoRegister)
- {
- IR::RegOpnd *dstOpnd;
- if (m_func->DoStackScopeSlots() && m_func->IsTopFunc())
- {
- dstOpnd = IR::RegOpnd::New(TyVar, m_func);
- }
- else
- {
- dstOpnd = this->BuildDstOpnd(frameDisplayReg);
- }
- instr = IR::Instr::New(op, dstOpnd, closureOpnd, m_func);
- if (frameDisplayOpnd != nullptr)
- {
- // We're building on an intermediate LdFrameDisplay result.
- instr->SetSrc2(frameDisplayOpnd);
- }
- else if (envReg != Js::Constants::NoRegister)
- {
- // We're building on the environment created by the enclosing function.
- instr->SetSrc2(this->BuildSrcOpnd(envReg));
- }
- this->AddInstr(instr, offset);
- if (dstOpnd->m_sym->m_isSingleDef)
- {
- dstOpnd->m_sym->m_isNotNumber = true;
- }
- if (m_func->DoStackFrameDisplay())
- {
- // Use the stack closure sym to save the frame display pointer.
- this->AddInstr(
- IR::Instr::New(
- Js::OpCode::InitLocalClosure, this->BuildDstOpnd(m_func->GetLocalFrameDisplaySym()->m_id), m_func),
- offset);
- this->AddInstr(
- IR::Instr::New(
- Js::OpCode::StSlot,
- this->BuildFieldOpnd(Js::OpCode::StSlot, m_func->GetLocalFrameDisplaySym()->m_id, 0, (Js::PropertyIdIndexType)-1, PropertyKindSlots),
- dstOpnd, m_func),
- offset);
- }
- }
- }
- }
- offset = m_functionStartOffset;
- if (m_statementReader.AtStatementBoundary(&m_jnReader))
- {
- statementIndex = this->AddStatementBoundary(statementIndex, offset);
- }
- // For label instr we can add bailout only after all labels were finalized. Put to list/add in the end.
- JsUtil::BaseDictionary<IR::Instr*, int, JitArenaAllocator> ignoreExBranchInstrToOffsetMap(m_tempAlloc);
- Js::LayoutSize layoutSize;
- IR::Instr* lastProcessedInstrForJITLoopBody = m_func->m_headInstr;
- for (Js::OpCode newOpcode = m_jnReader.ReadOp(layoutSize); (uint)m_jnReader.GetCurrentOffset() <= lastOffset; newOpcode = m_jnReader.ReadOp(layoutSize))
- {
- Assert(newOpcode != Js::OpCode::EndOfBlock);
- #ifdef BAILOUT_INJECTION
- if (!this->m_func->GetTopFunc()->HasTry()
- #ifdef BYTECODE_BRANCH_ISLAND
- && newOpcode != Js::OpCode::BrLong // Don't inject bailout on BrLong as they are just redirecting to a different offset anyways
- #endif
- )
- {
- if (!this->m_func->IsOOPJIT())
- {
- if (!seenLdStackArgPtr && !seenProfiledBeginSwitch)
- {
- if (Js::Configuration::Global.flags.IsEnabled(Js::BailOutByteCodeFlag))
- {
- ThreadContext * threadContext = this->m_func->GetScriptContext()->GetThreadContext();
- if (Js::Configuration::Global.flags.BailOutByteCode.Contains(threadContext->bailOutByteCodeLocationCount))
- {
- this->InjectBailOut(offset);
- }
- }
- else if (Js::Configuration::Global.flags.IsEnabled(Js::BailOutAtEveryByteCodeFlag))
- {
- this->InjectBailOut(offset);
- }
- }
- CheckBailOutInjection(newOpcode);
- }
- }
- #endif
- AssertOrFailFastMsg(Js::OpCodeUtil::IsValidByteCodeOpcode(newOpcode), "Error getting opcode from m_jnReader.Op()");
- uint layoutAndSize = layoutSize * Js::OpLayoutType::Count + Js::OpCodeUtil::GetOpCodeLayout(newOpcode);
- switch(layoutAndSize)
- {
- #define LAYOUT_TYPE(layout) \
- case Js::OpLayoutType::layout: \
- Assert(layoutSize == Js::SmallLayout); \
- this->Build##layout(newOpcode, offset); \
- break;
- #define LAYOUT_TYPE_WMS(layout) \
- case Js::SmallLayout * Js::OpLayoutType::Count + Js::OpLayoutType::layout: \
- this->Build##layout<Js::SmallLayoutSizePolicy>(newOpcode, offset); \
- break; \
- case Js::MediumLayout * Js::OpLayoutType::Count + Js::OpLayoutType::layout: \
- this->Build##layout<Js::MediumLayoutSizePolicy>(newOpcode, offset); \
- break; \
- case Js::LargeLayout * Js::OpLayoutType::Count + Js::OpLayoutType::layout: \
- this->Build##layout<Js::LargeLayoutSizePolicy>(newOpcode, offset); \
- break;
- #include "ByteCode/LayoutTypes.h"
- default:
- AssertMsg(0, "Unimplemented layout");
- break;
- }
- #ifdef BAILOUT_INJECTION
- if (!this->m_func->IsOOPJIT())
- {
- if (!this->m_func->GetTopFunc()->HasTry() && Js::Configuration::Global.flags.IsEnabled(Js::BailOutByteCodeFlag))
- {
- ThreadContext * threadContext = this->m_func->GetScriptContext()->GetThreadContext();
- if (lastInstr != m_lastInstr)
- {
- lastInstr = lastInstr->GetNextRealInstr();
- if (lastInstr->HasBailOutInfo())
- {
- lastInstr = lastInstr->m_next;
- }
- lastInstr->bailOutByteCodeLocation = threadContext->bailOutByteCodeLocationCount;
- lastInstr = m_lastInstr;
- }
- threadContext->bailOutByteCodeLocationCount++;
- }
- }
- #endif
- if (IsLoopBodyInTry() && lastProcessedInstrForJITLoopBody != m_lastInstr)
- {
- // traverse in backward so we get new/later value of given symId for storing instead of the earlier/stale
- // symId value. m_stSlots is used to prevent multiple stores to the same symId.
- FOREACH_INSTR_BACKWARD_EDITING_IN_RANGE(
- instr,
- instrPrev,
- m_lastInstr,
- lastProcessedInstrForJITLoopBody->m_next)
- {
- if (instr->GetDst() && instr->GetDst()->IsRegOpnd() && instr->GetDst()->GetStackSym()->HasByteCodeRegSlot())
- {
- StackSym * dstSym = instr->GetDst()->GetStackSym();
- Js::RegSlot dstRegSlot = dstSym->GetByteCodeRegSlot();
- if (!this->RegIsTemp(dstRegSlot) && !this->RegIsConstant(dstRegSlot))
- {
- SymID symId = dstSym->m_id;
- AssertOrFailFast(symId < m_stSlots->Length());
- if (this->m_stSlots->Test(symId))
- {
- // For jitted loop bodies that are in a try block, we consider any symbol that has a
- // non-temp bytecode reg slot, to be write-through. Hence, generating StSlots at all
- // defs for such symbols
- IR::Instr * stSlot = this->GenerateLoopBodyStSlot(dstRegSlot);
- AddInstr(stSlot, Js::Constants::NoByteCodeOffset);
- this->m_stSlots->Clear(symId);
- }
- else
- {
- Assert(dstSym->m_isCatchObjectSym);
- }
- }
- }
- } NEXT_INSTR_BACKWARD_EDITING_IN_RANGE;
- lastProcessedInstrForJITLoopBody = m_lastInstr;
- }
- offset = m_jnReader.GetCurrentOffset();
- if (m_func->IsJitInDebugMode())
- {
- bool needBailoutForHelper = CONFIG_FLAG(EnableContinueAfterExceptionWrappersForHelpers) &&
- (OpCodeAttr::NeedsPostOpDbgBailOut(newOpcode) ||
- (m_lastInstr->m_opcode == Js::OpCode::CallHelper && m_lastInstr->GetSrc1() &&
- HelperMethodAttributes::CanThrow(m_lastInstr->GetSrc1()->AsHelperCallOpnd()->m_fnHelper)));
- if (needBailoutForHelper)
- {
- // Insert bailout after return from a helper call.
- // For now use offset of next instr, when we get & ignore exception, we replace this with next statement offset.
- if (m_lastInstr->IsBranchInstr())
- {
- // Debugger bailout on branches goes to different block which can become dead. Keep bailout with real instr.
- // Can't convert to bailout at this time, can do that only after branches are finalized, remember for later.
- ignoreExBranchInstrToOffsetMap.Add(m_lastInstr, offset);
- }
- else if (
- m_lastInstr->m_opcode == Js::OpCode::Throw ||
- m_lastInstr->m_opcode == Js::OpCode::RuntimeReferenceError ||
- m_lastInstr->m_opcode == Js::OpCode::RuntimeTypeError)
- {
- uint32 lastInstrOffset = m_lastInstr->GetByteCodeOffset();
- AssertOrFailFast(lastInstrOffset < m_offsetToInstructionCount);
- #if DBG
- __analysis_assume(lastInstrOffset < this->m_offsetToInstructionCount);
- #endif
- bool isLastInstrUpdateNeeded = m_offsetToInstruction[lastInstrOffset] == m_lastInstr;
- BailOutInfo * bailOutInfo = JitAnew(this->m_func->m_alloc, BailOutInfo, offset, this->m_func);
- m_lastInstr = m_lastInstr->ConvertToBailOutInstr(bailOutInfo, c_debuggerBaseBailOutKindForHelper, true);
- if (isLastInstrUpdateNeeded)
- {
- m_offsetToInstruction[lastInstrOffset] = m_lastInstr;
- }
- }
- else
- {
- IR::BailOutKind bailOutKind = c_debuggerBaseBailOutKindForHelper;
- if (OpCodeAttr::HasImplicitCall(newOpcode) || OpCodeAttr::OpndHasImplicitCall(newOpcode))
- {
- // When we get out of e.g. valueOf called by a helper (e.g. Add_A) during stepping,
- // we need to bail out to continue debugging calling function in interpreter,
- // essentially this is similar to bail out on return from a method.
- bailOutKind |= c_debuggerBailOutKindForCall;
- }
- this->InsertBailOutForDebugger(offset, bailOutKind);
- }
- }
- }
- while (m_statementReader.AtStatementBoundary(&m_jnReader))
- {
- statementIndex = this->AddStatementBoundary(statementIndex, offset);
- }
- }
- if (Js::Constants::NoStatementIndex != statementIndex)
- {
- // If we are inside a user statement then create a trailing line pragma instruction
- statementIndex = this->AddStatementBoundary(statementIndex, Js::Constants::NoByteCodeOffset);
- }
- if (IsLoopBody())
- {
- // Insert the LdSlot/StSlot and Ret
- IR::Opnd * retOpnd = this->InsertLoopBodyReturnIPInstr(offset, offset);
- // Restore and Ret are at the last offset + 1
- GenerateLoopBodySlotAccesses(lastOffset + 1);
- InsertDoneLoopBodyLoopCounter(lastOffset);
- IR::Instr * retInstr = IR::Instr::New(Js::OpCode::Ret, m_func);
- retInstr->SetSrc1(retOpnd);
- this->AddInstr(retInstr, lastOffset + 1);
- }
- // Now fix up the targets for all the branches we've introduced.
- InsertLabels();
- Assert(!this->handlerOffsetStack || this->handlerOffsetStack->Empty());
- // Insert bailout for ignore exception for labels, after all labels were finalized.
- ignoreExBranchInstrToOffsetMap.Map([this](IR::Instr* instr, int byteCodeOffset) {
- BailOutInfo * bailOutInfo = JitAnew(this->m_func->m_alloc, BailOutInfo, byteCodeOffset, this->m_func);
- instr->ConvertToBailOutInstr(bailOutInfo, c_debuggerBaseBailOutKindForHelper, true);
- });
- // Now that we know whether the func is a leaf or not, decide whether we'll emit fast paths.
- // Do this once and for all, per-func, since the source size on the ThreadContext will be
- // changing while we JIT.
- if (this->m_func->IsTopFunc())
- {
- this->m_func->SetDoFastPaths();
- this->EmitClosureRangeChecks();
- }
- }
- void
- IRBuilder::EmitClosureRangeChecks()
- {
- if (m_func->frameDisplayCheckTable)
- {
- // Frame display checks. Again, chain to the instruction (LdEnv/LdSlot).
- FOREACH_HASHTABLE_ENTRY(FrameDisplayCheckRecord*, bucket, m_func->frameDisplayCheckTable)
- {
- StackSym *stackSym = m_func->m_symTable->FindStackSym(bucket.value);
- Assert(stackSym && stackSym->m_instrDef);
- IR::Instr *instrDef = stackSym->m_instrDef;
- IR::Instr *insertInstr = instrDef->m_next;
- IR::RegOpnd *dstOpnd = instrDef->UnlinkDst()->AsRegOpnd();
- IR::Instr *instr = IR::Instr::New(Js::OpCode::FrameDisplayCheck, dstOpnd, m_func);
- dstOpnd = IR::RegOpnd::New(TyVar, m_func);
- instrDef->SetDst(dstOpnd);
- instr->SetSrc1(dstOpnd);
- // Attach the two-dimensional check info.
- IR::AddrOpnd *recordOpnd = IR::AddrOpnd::New(bucket.element, IR::AddrOpndKindDynamicMisc, m_func, true);
- instr->SetSrc2(recordOpnd);
- insertInstr->InsertBefore(instr);
- }
- NEXT_HASHTABLE_ENTRY;
- }
- // If not a loop, but there are loops and trys, restore scope slot pointer and FD
- if (!m_func->IsLoopBody() && m_func->HasTry() && m_func->GetJITFunctionBody()->GetByteCodeInLoopCount() != 0)
- {
- BVSparse<JitArenaAllocator> * bv = nullptr;
- if (m_func->GetLocalClosureSym() && m_func->GetLocalClosureSym()->HasByteCodeRegSlot())
- {
- bv = JitAnew(m_func->m_alloc, BVSparse<JitArenaAllocator>, m_func->m_alloc);
- bv->Set(m_func->GetLocalClosureSym()->m_id);
- }
- if (m_func->GetLocalFrameDisplaySym() && m_func->GetLocalFrameDisplaySym()->HasByteCodeRegSlot())
- {
- if (!bv)
- {
- bv = JitAnew(m_func->m_alloc, BVSparse<JitArenaAllocator>, m_func->m_alloc);
- }
- bv->Set(m_func->GetLocalFrameDisplaySym()->m_id);
- }
- if (bv)
- {
- FOREACH_INSTR_IN_FUNC_BACKWARD(instr, m_func)
- {
- if (instr->m_opcode == Js::OpCode::Ret)
- {
- IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(instr);
- byteCodeUse->SetBV(bv);
- instr->InsertBefore(byteCodeUse);
- break;
- }
- }
- NEXT_INSTR_IN_FUNC_BACKWARD;
- }
- }
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::InsertLabels
- ///
- /// Insert label instructions at the offsets recorded in the branch reloc list.
- ///
- ///----------------------------------------------------------------------------
- void
- IRBuilder::InsertLabels()
- {
- AssertMsg(this->branchRelocList, "Malformed branch reloc list");
- SList<BranchReloc *>::Iterator iter(this->branchRelocList);
- while (iter.Next())
- {
- IR::LabelInstr * labelInstr = nullptr;
- BranchReloc * reloc = iter.Data();
- IR::BranchInstr * branchInstr = reloc->GetBranchInstr();
- uint offset = reloc->GetOffset();
- uint const branchOffset = reloc->GetBranchOffset();
- Assert(!IsLoopBody() || offset <= GetLoopBodyExitInstrOffset());
- if(branchInstr->m_opcode == Js::OpCode::MultiBr)
- {
- IR::MultiBranchInstr * multiBranchInstr = branchInstr->AsBranchInstr()->AsMultiBrInstr();
- multiBranchInstr->UpdateMultiBrTargetOffsets([&](uint32 offset) -> IR::LabelInstr *
- {
- labelInstr = this->CreateLabel(branchInstr, offset);
- multiBranchInstr->ChangeLabelRef(nullptr, labelInstr);
- return labelInstr;
- });
- }
- else
- {
- labelInstr = this->CreateLabel(branchInstr, offset);
- branchInstr->SetTarget(labelInstr);
- }
- if (!reloc->IsNotBackEdge() && branchOffset >= offset)
- {
- bool wasLoopTop = labelInstr->m_isLoopTop;
- labelInstr->m_isLoopTop = true;
- if (m_func->IsJitInDebugMode())
- {
- // Add bailout for Async Break.
- IR::BranchInstr* backEdgeBranchInstr = reloc->GetBranchInstr();
- this->InsertBailOutForDebugger(backEdgeBranchInstr->GetByteCodeOffset(), IR::BailOutForceByFlag | IR::BailOutBreakPointInFunction, backEdgeBranchInstr);
- }
- if (!wasLoopTop && m_loopCounterSym)
- {
- this->InsertIncrLoopBodyLoopCounter(labelInstr);
- }
- }
- }
- }
- IR::LabelInstr *
- IRBuilder::CreateLabel(IR::BranchInstr * branchInstr, uint& offset)
- {
- IR::LabelInstr * labelInstr;
- IR::Instr * targetInstr;
- for (;;)
- {
- AssertOrFailFast(offset < m_offsetToInstructionCount);
- targetInstr = this->m_offsetToInstruction[offset];
- if (targetInstr != nullptr)
- {
- #ifdef BYTECODE_BRANCH_ISLAND
- // If we have a long branch, remap it to the target offset
- if (targetInstr == VirtualLongBranchInstr)
- {
- offset = ResolveVirtualLongBranch(branchInstr, offset);
- continue;
- }
- #endif
- break;
- }
- offset++;
- }
- IR::Instr *instrPrev = targetInstr->m_prev;
- if (instrPrev)
- {
- instrPrev = targetInstr->GetPrevRealInstrOrLabel();
- }
- if (instrPrev && instrPrev->IsLabelInstr() && instrPrev->GetByteCodeOffset() == offset)
- {
- // Found an existing label at the right offset. Just reuse it.
- labelInstr = instrPrev->AsLabelInstr();
- }
- else
- {
- // No label at the desired offset. Create one.
- labelInstr = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
- labelInstr->SetByteCodeOffset(offset);
- if (instrPrev)
- {
- instrPrev->InsertAfter(labelInstr);
- }
- else
- {
- targetInstr->InsertBefore(labelInstr);
- }
- }
- return labelInstr;
- }
- void IRBuilder::InsertInstr(IR::Instr *instr, IR::Instr* insertBeforeInstr)
- {
- AssertOrFailFast(insertBeforeInstr->GetByteCodeOffset() < m_offsetToInstructionCount);
- instr->SetByteCodeOffset(insertBeforeInstr);
- uint32 offset = insertBeforeInstr->GetByteCodeOffset();
- if (m_offsetToInstruction[offset] == insertBeforeInstr)
- {
- m_offsetToInstruction[offset] = instr;
- }
- insertBeforeInstr->InsertBefore(instr);
- #if DBG_DUMP
- if (PHASE_TRACE(Js::IRBuilderPhase, m_func->GetTopFunc()))
- {
- instr->Dump();
- }
- #endif
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::AddInstr
- ///
- /// Add an instruction to the current instr list. Also add this instr to
- /// offsetToinstruction table to patch branches/labels afterwards.
- ///
- ///----------------------------------------------------------------------------
- void
- IRBuilder::AddInstr(IR::Instr *instr, uint32 offset)
- {
- m_lastInstr->InsertAfter(instr);
- if (offset != Js::Constants::NoByteCodeOffset)
- {
- AssertOrFailFast(offset < m_offsetToInstructionCount);
- if (m_offsetToInstruction[offset] == nullptr)
- {
- m_offsetToInstruction[offset] = instr;
- }
- else
- {
- Assert(m_lastInstr->GetByteCodeOffset() == offset);
- }
- if (instr->GetByteCodeOffset() == Js::Constants::NoByteCodeOffset)
- {
- instr->SetByteCodeOffset(offset);
- }
- }
- else
- {
- instr->SetByteCodeOffset(m_lastInstr->GetByteCodeOffset());
- }
- m_lastInstr = instr;
- Func *topFunc = this->m_func->GetTopFunc();
- if (!topFunc->GetHasTempObjectProducingInstr())
- {
- if (OpCodeAttr::TempObjectProducing(instr->m_opcode))
- {
- topFunc->SetHasTempObjectProducingInstr(true);
- }
- }
- #if DBG_DUMP
- if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::IRBuilderPhase, this->m_func->GetTopFunc()->GetSourceContextId(), this->m_func->GetTopFunc()->GetLocalFunctionId()))
- {
- instr->Dump();
- }
- #endif
- }
- IR::IndirOpnd *
- IRBuilder::BuildIndirOpnd(IR::RegOpnd *baseReg, IR::RegOpnd *indexReg)
- {
- IR::IndirOpnd *indirOpnd = IR::IndirOpnd::New(baseReg, indexReg, TyVar, m_func);
- return indirOpnd;
- }
- IR::IndirOpnd *
- IRBuilder::BuildIndirOpnd(IR::RegOpnd *baseReg, uint32 offset)
- {
- IR::IndirOpnd *indirOpnd = IR::IndirOpnd::New(baseReg, offset, TyVar, m_func);
- return indirOpnd;
- }
- #if DBG_DUMP || defined(ENABLE_IR_VIEWER)
- IR::IndirOpnd *
- IRBuilder::BuildIndirOpnd(IR::RegOpnd *baseReg, uint32 offset, const char16 *desc)
- {
- IR::IndirOpnd *indirOpnd = IR::IndirOpnd::New(baseReg, offset, TyVar, desc, m_func);
- return indirOpnd;
- }
- #endif
- IR::SymOpnd *
- IRBuilder::BuildFieldOpnd(Js::OpCode newOpcode, Js::RegSlot reg, Js::PropertyId propertyId, Js::PropertyIdIndexType propertyIdIndex, PropertyKind propertyKind, uint inlineCacheIndex)
- {
- AssertOrFailFast(inlineCacheIndex < m_func->GetJITFunctionBody()->GetInlineCacheCount() || inlineCacheIndex == Js::Constants::NoInlineCacheIndex);
- PropertySym * propertySym = BuildFieldSym(reg, propertyId, propertyIdIndex, inlineCacheIndex, propertyKind);
- IR::SymOpnd * symOpnd;
- // If we plan to apply object type optimization to this instruction or if we intend to emit a fast path using an inline
- // cache, we will need a property sym operand.
- if (OpCodeAttr::FastFldInstr(newOpcode) || inlineCacheIndex != (uint)-1)
- {
- Assert(propertyKind == PropertyKindData);
- symOpnd = IR::PropertySymOpnd::New(propertySym, inlineCacheIndex, TyVar, this->m_func);
- if (inlineCacheIndex != (uint)-1 && propertySym->m_loadInlineCacheIndex == (uint)-1)
- {
- if (GlobOpt::IsPREInstrCandidateLoad(newOpcode))
- {
- propertySym->m_loadInlineCacheIndex = inlineCacheIndex;
- propertySym->m_loadInlineCacheFunc = this->m_func;
- }
- }
- }
- else
- {
- symOpnd = IR::SymOpnd::New(propertySym, TyVar, this->m_func);
- }
- return symOpnd;
- }
- PropertySym *
- IRBuilder::BuildFieldSym(Js::RegSlot reg, Js::PropertyId propertyId, Js::PropertyIdIndexType propertyIdIndex, uint inlineCacheIndex, PropertyKind propertyKind)
- {
- PropertySym * propertySym;
- SymID symId = this->BuildSrcStackSymID(reg);
- AssertMsg(m_func->m_symTable->FindStackSym(symId), "Tried to use an undefined stacksym?");
- propertySym = PropertySym::FindOrCreate(symId, propertyId, propertyIdIndex, inlineCacheIndex, propertyKind, m_func);
- return propertySym;
- }
- SymID
- IRBuilder::BuildSrcStackSymID(Js::RegSlot regSlot)
- {
- SymID symID;
- if (this->RegIsTemp(regSlot))
- {
- // This is a use of a temp. Map the reg slot to its sym ID.
- // !!!NOTE: always process an instruction's temp uses before its temp defs!!!
- symID = this->GetMappedTemp(regSlot);
- if (symID == 0)
- {
- // We might have temps that are live through the loop body via "with" statement
- // We need to treat those as if they are locals and don't remap them
- Assert(this->IsLoopBody());
- Assert(!this->m_usedAsTemp->Test(regSlot - m_func->GetJITFunctionBody()->GetFirstTmpReg()));
- symID = static_cast<SymID>(regSlot);
- this->SetMappedTemp(regSlot, symID);
- this->EnsureLoopBodyLoadSlot(symID);
- }
- }
- else
- {
- symID = static_cast<SymID>(regSlot);
- if (IsLoopBody() && !this->RegIsConstant(regSlot))
- {
- this->EnsureLoopBodyLoadSlot(symID);
- }
- }
- return symID;
- }
- IR::RegOpnd *
- IRBuilder::EnsureLoopBodyForInEnumeratorArrayOpnd()
- {
- Assert(this->IsLoopBody());
- IR::RegOpnd * loopBodyForInEnumeratorArrayOpnd = this->m_loopBodyForInEnumeratorArrayOpnd;
- if (loopBodyForInEnumeratorArrayOpnd == nullptr)
- {
- loopBodyForInEnumeratorArrayOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
- this->m_loopBodyForInEnumeratorArrayOpnd = loopBodyForInEnumeratorArrayOpnd;
- StackSym *loopParamSym = m_func->EnsureLoopParamSym();
- IR::RegOpnd *loopParamOpnd = IR::RegOpnd::New(loopParamSym, TyMachPtr, m_func);
- IR::Instr * ldInstr = IR::Instr::New(Js::OpCode::Ld_A, loopBodyForInEnumeratorArrayOpnd,
- IR::IndirOpnd::New(loopParamOpnd, Js::InterpreterStackFrame::GetOffsetOfForInEnumerators(), TyMachPtr, this->m_func),
- this->m_func);
- m_func->m_headInstr->InsertAfter(ldInstr);
- }
- return loopBodyForInEnumeratorArrayOpnd;
- }
- IR::Opnd *
- IRBuilder::BuildForInEnumeratorOpnd(uint forInLoopLevel, uint32 offset)
- {
- Assert(forInLoopLevel < this->m_func->GetJITFunctionBody()->GetForInLoopDepth());
- if (this->IsLoopBody())
- {
- return IR::IndirOpnd::New(
- this->EnsureLoopBodyForInEnumeratorArrayOpnd(),
- forInLoopLevel * sizeof(Js::ForInObjectEnumerator),
- TyMachPtr,
- this->m_func
- );
- }
- else if (this->m_func->GetJITFunctionBody()->IsCoroutine())
- {
- return IR::IndirOpnd::New(
- this->m_generatorJumpTable.BuildForInEnumeratorArrayOpnd(offset),
- forInLoopLevel * sizeof(Js::ForInObjectEnumerator),
- TyMachPtr,
- this->m_func
- );
- }
- else
- {
- StackSym* stackSym = StackSym::New(TyMisc, this->m_func);
- stackSym->m_offset = forInLoopLevel;
- return IR::SymOpnd::New(stackSym, TyMachPtr, this->m_func);
- }
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildSrcOpnd
- ///
- /// Create a StackSym and return a RegOpnd for this RegSlot.
- ///
- ///----------------------------------------------------------------------------
- IR::RegOpnd *
- IRBuilder::BuildSrcOpnd(Js::RegSlot srcRegSlot, IRType type)
- {
- StackSym * symSrc = m_func->m_symTable->FindStackSym(BuildSrcStackSymID(srcRegSlot));
- AssertMsg(symSrc, "Tried to use an undefined stack slot?");
- IR::RegOpnd *regOpnd = IR::RegOpnd::New(symSrc, type, m_func);
- return regOpnd;
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildDstOpnd
- ///
- /// Create a StackSym and return a RegOpnd for this RegSlot.
- /// If the RegSlot is '0', it may have multiple defs, so use FindOrCreate.
- ///
- ///----------------------------------------------------------------------------
- IR::RegOpnd *
- IRBuilder::BuildDstOpnd(Js::RegSlot dstRegSlot, IRType type, bool isCatchObjectSym, bool reuseTemp)
- {
- StackSym * symDst;
- SymID symID;
- if (this->RegIsTemp(dstRegSlot))
- {
- #if DBG
- if (this->IsLoopBody())
- {
- // If we are doing loop body, and a temp reg slot is loaded via LdSlot
- // That means that we have detected that the slot is live coming in to the loop.
- // This would only happen for the value of a "with" statement, so there shouldn't
- // be any def for those
- Assert(!this->m_ldSlots->Test(dstRegSlot));
- this->m_usedAsTemp->Set(dstRegSlot - m_func->GetJITFunctionBody()->GetFirstTmpReg());
- }
- #endif
- // This is a def of a temp. Create a new sym ID for it if it's been used since its last def.
- // !!!NOTE: always process an instruction's temp uses before its temp defs!!!
- symID = this->GetMappedTemp(dstRegSlot);
- if (symID == 0)
- {
- // First time we've seen the temp. Just use the number that the front end gave it.
- symID = static_cast<SymID>(dstRegSlot);
- this->SetMappedTemp(dstRegSlot, symID);
- }
- else if (!reuseTemp)
- {
- // Byte code has not told us to reuse the mapped temp at this def, so don't. Make a new one.
- symID = m_func->m_symTable->NewID();
- this->SetMappedTemp(dstRegSlot, symID);
- }
- }
- else
- {
- symID = static_cast<SymID>(dstRegSlot);
- if (this->RegIsConstant(dstRegSlot))
- {
- // Don't need to track constant registers for bailout. Don't set the byte code register for constant.
- dstRegSlot = Js::Constants::NoRegister;
- }
- else if (IsLoopBody())
- {
- // Loop body and not constants
- this->SetLoopBodyStSlot(symID, isCatchObjectSym);
- // We need to make sure that the symbols is loaded as well
- // so that the sym will be defined on all path.
- this->EnsureLoopBodyLoadSlot(symID, isCatchObjectSym);
- }
- }
- symDst = StackSym::FindOrCreate(symID, dstRegSlot, m_func);
- // Always reset isSafeThis to false. We'll set it to true for singleDef cases,
- // but want to reset it to false if it is multi-def.
- // NOTE: We could handle the multiDef if they are all safe, but it probably isn't very common.
- symDst->m_isSafeThis = false;
- IR::RegOpnd *regOpnd = IR::RegOpnd::New(symDst, type, m_func);
- return regOpnd;
- }
- void
- IRBuilder::BuildImplicitArgIns()
- {
- Js::RegSlot startReg = m_func->GetJITFunctionBody()->GetConstCount() - 1;
- for (Js::ArgSlot i = 1; i < m_func->GetJITFunctionBody()->GetInParamsCount(); i++)
- {
- this->BuildArgIn((uint32)-1, startReg + i, i);
- }
- }
- void
- IRBuilder::LoadNativeCodeData()
- {
- if (m_func->IsOOPJIT() && m_func->IsTopFunc())
- {
- IR::RegOpnd * nativeDataOpnd = IR::RegOpnd::New(TyVar, m_func);
- IR::Instr * instr = IR::Instr::New(Js::OpCode::LdNativeCodeData, nativeDataOpnd, m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- m_func->SetNativeCodeDataSym(nativeDataOpnd->GetStackSym());
- }
- }
- void
- IRBuilder::BuildConstantLoads()
- {
- Js::RegSlot count = m_func->GetJITFunctionBody()->GetConstCount();
- for (Js::RegSlot reg = Js::FunctionBody::FirstRegSlot; reg < count; reg++)
- {
- intptr_t varConst = m_func->GetJITFunctionBody()->GetConstantVar(reg);
- Assert(varConst != 0);
- Js::TypeId type = m_func->GetJITFunctionBody()->GetConstantType(reg);
- IR::RegOpnd *dstOpnd = this->BuildDstOpnd(reg);
- Assert(this->RegIsConstant(reg));
- dstOpnd->m_sym->SetIsFromByteCodeConstantTable();
- // TODO: be more precise about this
- ValueType valueType;
- IR::Instr *instr = nullptr;
- switch (type)
- {
- case Js::TypeIds_Number:
- valueType = ValueType::Number;
- instr = IR::Instr::NewConstantLoad(dstOpnd, varConst, valueType, m_func
- #if !FLOATVAR
- , m_func->IsOOPJIT() ? m_func->GetJITFunctionBody()->GetConstAsT<Js::JavascriptNumber>(reg) : nullptr
- #endif
- );
- break;
- case Js::TypeIds_String:
- {
- valueType = ValueType::String;
- if (m_func->IsOOPJIT())
- {
- // must be either PropertyString or LiteralString
- JITRecyclableObject * jitObj = m_func->GetJITFunctionBody()->GetConstantContent(reg);
- JITJavascriptString * constStr = JITJavascriptString::FromVar(jitObj);
- instr = IR::Instr::NewConstantLoad(dstOpnd, varConst, valueType, m_func, constStr);
- }
- else
- {
- instr = IR::Instr::NewConstantLoad(dstOpnd, varConst, valueType, m_func);
- }
- break;
- }
- case Js::TypeIds_Limit:
- valueType = ValueType::FromTypeId(type, false);
- instr = IR::Instr::NewConstantLoad(dstOpnd, varConst, valueType, m_func);
- break;
- default:
- valueType = ValueType::FromTypeId(type, false);
- instr = IR::Instr::NewConstantLoad(dstOpnd, varConst, valueType, m_func,
- m_func->IsOOPJIT() ? m_func->GetJITFunctionBody()->GetConstAsT<Js::RecyclableObject>(reg) : nullptr);
- break;
- }
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- }
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildReg1
- ///
- /// Build IR instr for a Reg1 instruction.
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg1<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- }
- BuildReg1(newOpcode, offset, layout->R0);
- }
- void
- IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0)
- {
- if (newOpcode == Js::OpCode::ArgIn0)
- {
- this->BuildArgIn0(offset, R0);
- return;
- }
- IR::Instr * instr;
- Js::RegSlot srcRegOpnd, dstRegSlot;
- srcRegOpnd = dstRegSlot = R0;
- IR::Opnd * srcOpnd = nullptr;
- bool isNotInt = false;
- bool dstIsCatchObject = false;
- bool reuseLoc = false;
- ValueType dstValueType;
- switch (newOpcode)
- {
- case Js::OpCode::LdLetHeapArguments:
- {
- this->m_func->SetHasNonSimpleParams();
- //FallThrough to next case block!
- }
- case Js::OpCode::LdHeapArguments:
- {
- if (this->m_func->GetJITFunctionBody()->NeedScopeObjectForArguments(m_func->GetHasNonSimpleParams()))
- {
- Js::RegSlot regFrameObj = m_func->GetJITFunctionBody()->GetLocalClosureReg();
- Assert(regFrameObj != Js::Constants::NoRegister);
- srcOpnd = BuildSrcOpnd(regFrameObj);
- if (m_func->GetJITFunctionBody()->GetInParamsCount() > 1)
- {
- m_func->SetScopeObjSym(srcOpnd->GetStackSym());
- }
- }
- else
- {
- srcOpnd = IR::AddrOpnd::New(
- m_func->GetScriptContextInfo()->GetNullAddr(), IR::AddrOpndKindDynamicVar, m_func, true);
- }
- IR::RegOpnd * dstOpnd = BuildDstOpnd(R0);
- instr = IR::Instr::New(newOpcode, dstOpnd, srcOpnd, m_func);
- this->AddInstr(instr, offset);
- StackSym * dstSym = dstOpnd->m_sym;
- if (dstSym->m_isSingleDef)
- {
- dstSym->m_isSafeThis = true;
- dstSym->m_isNotNumber = true;
- }
- return;
- }
- case Js::OpCode::LdLetHeapArgsCached:
- {
- this->m_func->SetHasNonSimpleParams();
- //Fallthrough to next case block!
- }
- case Js::OpCode::LdHeapArgsCached:
- if (!m_func->GetJITFunctionBody()->HasScopeObject())
- {
- Js::Throw::FatalInternalError();
- }
- srcOpnd = BuildSrcOpnd(m_func->GetJITFunctionBody()->GetLocalClosureReg());
- if (m_func->GetJITFunctionBody()->GetInParamsCount() > 1)
- {
- m_func->SetScopeObjSym(srcOpnd->GetStackSym());
- }
- isNotInt = true;
- break;
- case Js::OpCode::LdLocalObj_ReuseLoc:
- reuseLoc = true;
- // fall through
- case Js::OpCode::LdLocalObj:
- if (!m_func->GetJITFunctionBody()->HasScopeObject())
- {
- Js::Throw::FatalInternalError();
- }
- srcOpnd = BuildSrcOpnd(m_func->GetJITFunctionBody()->GetLocalClosureReg());
- isNotInt = true;
- newOpcode = Js::OpCode::Ld_A;
- break;
- case Js::OpCode::LdParamObj:
- if (!m_func->GetJITFunctionBody()->HasScopeObject())
- {
- Js::Throw::FatalInternalError();
- }
- srcOpnd = BuildSrcOpnd(m_func->GetJITFunctionBody()->GetParamClosureReg());
- isNotInt = true;
- newOpcode = Js::OpCode::Ld_A;
- break;
- case Js::OpCode::Throw:
- {
- srcOpnd = this->BuildSrcOpnd(srcRegOpnd);
- if ((this->handlerOffsetStack && !this->handlerOffsetStack->Empty()) ||
- finallyBlockLevel > 0)
- {
- newOpcode = Js::OpCode::EHThrow;
- }
- instr = IR::Instr::New(newOpcode, m_func);
- instr->SetSrc1(srcOpnd);
- this->AddInstr(instr, offset);
- if(DoBailOnNoProfile())
- {
- //So optimistically assume it doesn't throw and introduce bailonnoprofile here.
- //If there are continuous bailout bailonnoprofile will be disabled.
- InsertBailOnNoProfile(instr);
- }
- return;
- }
- case Js::OpCode::LdC_A_Null:
- {
- const auto addrOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetNullAddr(), IR::AddrOpndKindDynamicVar, m_func, true);
- addrOpnd->SetValueType(ValueType::Null);
- srcOpnd = addrOpnd;
- newOpcode = Js::OpCode::Ld_A;
- break;
- }
- case Js::OpCode::LdUndef:
- {
- const auto addrOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndefinedAddr(), IR::AddrOpndKindDynamicVar, m_func, true);
- addrOpnd->SetValueType(ValueType::Undefined);
- srcOpnd = addrOpnd;
- newOpcode = Js::OpCode::Ld_A;
- break;
- }
- case Js::OpCode::LdInfinity:
- {
- const auto floatConstOpnd = IR::FloatConstOpnd::New(Js::JavascriptNumber::POSITIVE_INFINITY, TyFloat64, m_func);
- srcOpnd = floatConstOpnd;
- newOpcode = Js::OpCode::LdC_A_R8;
- break;
- }
- case Js::OpCode::LdNaN:
- {
- const auto floatConstOpnd = IR::FloatConstOpnd::New(Js::JavascriptNumber::NaN, TyFloat64, m_func);
- srcOpnd = floatConstOpnd;
- newOpcode = Js::OpCode::LdC_A_R8;
- break;
- }
- case Js::OpCode::LdBaseFncProto:
- {
- // reuseLoc set to true as this is only used when that is wanted - during class extension
- reuseLoc = true;
- srcOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetFunctionPrototypeAddr(), IR::AddrOpndKindDynamicVar, m_func, true);
- newOpcode = Js::OpCode::Ld_A;
- break;
- }
- case Js::OpCode::LdFalse_ReuseLoc:
- reuseLoc = true;
- // fall through
- case Js::OpCode::LdFalse:
- {
- const auto addrOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetFalseAddr(), IR::AddrOpndKindDynamicVar, m_func, true);
- addrOpnd->SetValueType(ValueType::Boolean);
- srcOpnd = addrOpnd;
- newOpcode = Js::OpCode::Ld_A;
- break;
- }
- case Js::OpCode::LdTrue_ReuseLoc:
- reuseLoc = true;
- // fall through
- case Js::OpCode::LdTrue:
- {
- const auto addrOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetTrueAddr(), IR::AddrOpndKindDynamicVar, m_func, true);
- addrOpnd->SetValueType(ValueType::Boolean);
- srcOpnd = addrOpnd;
- newOpcode = Js::OpCode::Ld_A;
- break;
- }
- case Js::OpCode::NewScObjectSimple:
- dstValueType = ValueType::GetObject(ObjectType::UninitializedObject);
- // fall-through
- case Js::OpCode::LdFuncExpr:
- m_func->DisableCanDoInlineArgOpt();
- break;
- case Js::OpCode::LdEnv:
- case Js::OpCode::LdHomeObj:
- case Js::OpCode::LdFuncObj:
- isNotInt = TRUE;
- break;
- case Js::OpCode::InitUndecl:
- srcOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndeclBlockVarAddr(), IR::AddrOpndKindDynamicVar, m_func, true);
- srcOpnd->SetValueType(ValueType::PrimitiveOrObject);
- newOpcode = Js::OpCode::Ld_A;
- break;
- case Js::OpCode::ChkUndecl:
- srcOpnd = BuildSrcOpnd(srcRegOpnd);
- instr = IR::Instr::New(Js::OpCode::ChkUndecl, m_func);
- instr->SetSrc1(srcOpnd);
- this->AddInstr(instr, offset);
- return;
- case Js::OpCode::Catch:
- if (this->handlerOffsetStack)
- {
- AssertOrFailFast(!this->handlerOffsetStack->Empty());
- AssertOrFailFast(this->handlerOffsetStack->Top().Second() == true);
- this->handlerOffsetStack->Pop();
- }
- dstIsCatchObject = true;
- break;
- case Js::OpCode::LdChakraLib:
- {
- const auto addrOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetChakraLibAddr(), IR::AddrOpndKindDynamicVar, m_func, true);
- addrOpnd->SetValueType(ValueType::PrimitiveOrObject);
- srcOpnd = addrOpnd;
- newOpcode = Js::OpCode::Ld_A;
- break;
- }
- }
- IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot, TyVar, dstIsCatchObject, reuseLoc);
- dstOpnd->SetValueType(dstValueType);
- StackSym * dstSym = dstOpnd->m_sym;
- dstSym->m_isCatchObjectSym = dstIsCatchObject;
- instr = IR::Instr::New(newOpcode, dstOpnd, m_func);
- if (srcOpnd)
- {
- instr->SetSrc1(srcOpnd);
- if (dstSym->m_isSingleDef)
- {
- if (srcOpnd->IsHelperCallOpnd())
- {
- // Don't do anything
- }
- else if (srcOpnd->IsIntConstOpnd())
- {
- dstSym->SetIsIntConst(srcOpnd->AsIntConstOpnd()->GetValue());
- }
- else if (srcOpnd->IsFloatConstOpnd())
- {
- dstSym->SetIsFloatConst();
- }
- else if (srcOpnd->IsAddrOpnd())
- {
- dstSym->m_isConst = true;
- dstSym->m_isNotNumber = true;
- }
- }
- }
- if (isNotInt && dstSym->m_isSingleDef)
- {
- dstSym->m_isNotNumber = true;
- }
- this->AddInstr(instr, offset);
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildReg2
- ///
- /// Build IR instr for a Reg2 instruction.
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildReg2(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg2<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- this->DoClosureRegCheck(layout->R1);
- }
- BuildReg2(newOpcode, offset, layout->R0, layout->R1, m_jnReader.GetCurrentOffset());
- }
- void
- IRBuilder::BuildReg2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, Js::RegSlot R1, uint32 nextOffset)
- {
- IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(R1);
- StackSym * symSrc1 = src1Opnd->m_sym;
- bool reuseLoc = false;
- switch (newOpcode)
- {
- case Js::OpCode::Ld_A_ReuseLoc:
- newOpcode = Js::OpCode::Ld_A;
- reuseLoc = true;
- break;
- case Js::OpCode::Typeof_ReuseLoc:
- newOpcode = Js::OpCode::Typeof;
- reuseLoc = true;
- break;
- case Js::OpCode::UnwrapWithObj_ReuseLoc:
- newOpcode = Js::OpCode::UnwrapWithObj;
- reuseLoc = true;
- break;
- case Js::OpCode::SpreadObjectLiteral:
- // fall through
- case Js::OpCode::SetComputedNameVar:
- {
- IR::Instr *instr = IR::Instr::New(newOpcode, m_func);
- instr->SetSrc1(this->BuildSrcOpnd(R0));
- instr->SetSrc2(src1Opnd);
- this->AddInstr(instr, offset);
- return;
- }
- case Js::OpCode::LdFuncExprFrameDisplay:
- {
- IR::RegOpnd *dstOpnd = IR::RegOpnd::New(TyVar, m_func);
- IR::Instr *instr = IR::Instr::New(Js::OpCode::LdFrameDisplay, dstOpnd, src1Opnd, m_func);
- Js::RegSlot envReg = this->GetEnvReg();
- if (envReg != Js::Constants::NoRegister)
- {
- instr->SetSrc2(BuildSrcOpnd(envReg));
- }
- this->AddInstr(instr, offset);
- IR::RegOpnd *src2Opnd = dstOpnd;
- src1Opnd = BuildSrcOpnd(R0);
- dstOpnd = BuildDstOpnd(m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg());
- instr = IR::Instr::New(Js::OpCode::LdFrameDisplay, dstOpnd, src1Opnd, src2Opnd, m_func);
- dstOpnd->m_sym->m_isNotNumber = true;
- this->AddInstr(instr, offset);
- return;
- }
- }
- IR::RegOpnd * dstOpnd = this->BuildDstOpnd(R0, TyVar, false, reuseLoc);
- StackSym * dstSym = dstOpnd->m_sym;
- IR::Instr * instr = nullptr;
- switch (newOpcode)
- {
- case Js::OpCode::Ld_A:
- if (symSrc1->m_builtInIndex != Js::BuiltinFunction::None)
- {
- // Note: don't set dstSym->m_builtInIndex to None here (see Win8 399972)
- dstSym->m_builtInIndex = symSrc1->m_builtInIndex;
- }
- break;
- case Js::OpCode::Delete_A:
- dstOpnd->SetValueType(ValueType::Boolean);
- break;
- case Js::OpCode::BeginSwitch:
- m_switchBuilder.BeginSwitch();
- newOpcode = Js::OpCode::Ld_A;
- break;
- case Js::OpCode::LdArrHead:
- src1Opnd->SetValueType(
- ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(false).SetArrayTypeId(Js::TypeIds_Array));
- src1Opnd->SetValueTypeFixed();
- break;
- case Js::OpCode::LdInnerFrameDisplayNoParent:
- {
- instr = IR::Instr::New(Js::OpCode::LdInnerFrameDisplay, dstOpnd, src1Opnd, m_func);
- this->AddEnvOpndForInnerFrameDisplay(instr, offset);
- if (dstSym->m_isSingleDef)
- {
- dstSym->m_isNotNumber = true;
- }
- this->AddInstr(instr, offset);
- return;
- }
- case Js::OpCode::Conv_Str:
- dstOpnd->SetValueType(ValueType::String);
- break;
- case Js::OpCode::Yield:
- instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, m_func);
- this->AddInstr(instr, offset);
- IR::Instr* yieldInstr = instr->ConvertToBailOutInstr(instr, IR::BailOutForGeneratorYield);
- this->m_lastInstr = yieldInstr;
- // This label indicates the bail-in section that we will jump to from the generator jump table
- auto* bailInLabel = IR::GeneratorBailInInstr::New(yieldInstr, m_func);
- bailInLabel->m_hasNonBranchRef = true; // set to true so that we don't move this label around
- LABELNAMESET(bailInLabel, "GeneratorBailInLabel");
- this->AddInstr(bailInLabel, offset);
- this->m_func->AddYieldOffsetResumeLabel(nextOffset, bailInLabel);
- yieldInstr->GetBailOutInfo()->bailInInstr = bailInLabel;
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (PHASE_TRACE(Js::Phase::BailInPhase, this->m_func))
- {
- IR::LabelInstr* traceBailInLabel = IR::LabelInstr::New(Js::OpCode::GeneratorOutputBailInTraceLabel, m_func);
- traceBailInLabel->m_hasNonBranchRef = true; // set to true so that we don't move this label around
- LABELNAMESET(traceBailInLabel, "OutputBailInTrace");
- this->AddInstr(traceBailInLabel, offset);
- IR::Instr* traceBailIn = IR::Instr::New(Js::OpCode::GeneratorOutputBailInTrace, m_func);
- this->AddInstr(traceBailIn, offset);
- }
- #endif
- IR::Instr* resumeYield = IR::Instr::New(Js::OpCode::GeneratorResumeYield, dstOpnd, m_func);
- this->AddInstr(resumeYield, offset);
- if (this->m_func->IsJitInDebugMode())
- {
- this->InsertBailOutForDebugger(offset, IR::BailOutForceByFlag | IR::BailOutBreakPointInFunction | IR::BailOutStep);
- }
- return;
- }
- if (instr == nullptr)
- {
- instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, m_func);
- }
- this->AddInstr(instr, offset);
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildProfiledReg2
- ///
- /// Build IR instr for a profiled Reg2 instruction.
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledReg2(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_Reg2<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- this->DoClosureRegCheck(layout->R1);
- }
- BuildProfiledReg2(newOpcode, offset, layout->R0, layout->R1, layout->profileId);
- }
- void
- IRBuilder::BuildProfiledReg2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot srcRegSlot, Js::ProfileId profileId)
- {
- bool switchFound = false;
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- Assert(newOpcode == Js::OpCode::BeginSwitch);
- IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(srcRegSlot);
- IR::RegOpnd * dstOpnd;
- if(srcRegSlot == dstRegSlot)
- {
- //if the operands are the same for BeginSwitch, don't build a new operand in IR.
- dstOpnd = src1Opnd;
- }
- else
- {
- dstOpnd = this->BuildDstOpnd(dstRegSlot);
- }
- m_switchBuilder.BeginSwitch();
- switchFound = true;
- newOpcode = Js::OpCode::Ld_A; // BeginSwitch is originally equivalent to Ld_A
- IR::Instr *instr;
- if (m_func->DoSimpleJitDynamicProfile())
- {
- // Since we're in simplejit, we want to keep track of the profileid:
- IR::JitProfilingInstr *profiledInstr = IR::JitProfilingInstr::New(newOpcode, dstOpnd, src1Opnd, m_func);
- profiledInstr->profileId = profileId;
- profiledInstr->isBeginSwitch = newOpcode == Js::OpCode::Ld_A;
- instr = profiledInstr;
- }
- else
- {
- IR::ProfiledInstr *profiledInstr = IR::ProfiledInstr::New(newOpcode, dstOpnd, src1Opnd, m_func);
- instr = profiledInstr;
- profiledInstr->u.FldInfo() = Js::FldInfo();
- }
- this->AddInstr(instr, offset);
- if(switchFound && instr->IsProfiledInstr())
- {
- m_switchBuilder.SetProfiledInstruction(instr, profileId);
- }
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildReg3
- ///
- /// Build IR instr for a Reg3 instruction.
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildReg3(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg3<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func) && newOpcode != Js::OpCode::NewInnerScopeSlots)
- {
- this->DoClosureRegCheck(layout->R0);
- this->DoClosureRegCheck(layout->R1);
- this->DoClosureRegCheck(layout->R2);
- }
- BuildReg3(newOpcode, offset, layout->R0, layout->R1, layout->R2, Js::Constants::NoProfileId);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledReg3(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_Reg3<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- this->DoClosureRegCheck(layout->R1);
- this->DoClosureRegCheck(layout->R2);
- }
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- BuildReg3(newOpcode, offset, layout->R0, layout->R1, layout->R2, layout->profileId);
- }
- void
- IRBuilder::BuildReg3(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot,
- Js::RegSlot src2RegSlot, Js::ProfileId profileId)
- {
- IR::Instr * instr;
- if (newOpcode == Js::OpCode::NewInnerScopeSlots)
- {
- if (dstRegSlot >= m_func->GetJITFunctionBody()->GetInnerScopeCount())
- {
- Js::Throw::FatalInternalError();
- }
- newOpcode = Js::OpCode::NewScopeSlotsWithoutPropIds;
- dstRegSlot += m_func->GetJITFunctionBody()->GetFirstInnerScopeReg();
- instr = IR::Instr::New(newOpcode, BuildDstOpnd(dstRegSlot),
- IR::IntConstOpnd::New(src1RegSlot, TyVar, m_func),
- IR::IntConstOpnd::New(src2RegSlot, TyVar, m_func),
- m_func);
- if (instr->GetDst()->AsRegOpnd()->m_sym->m_isSingleDef)
- {
- instr->GetDst()->AsRegOpnd()->m_sym->m_isNotNumber = true;
- }
- this->AddInstr(instr, offset);
- return;
- }
- IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(src1RegSlot);
- IR::RegOpnd * src2Opnd = this->BuildSrcOpnd(src2RegSlot);
- IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot);
- StackSym * dstSym = dstOpnd->m_sym;
- bool isProfiledInstr = (profileId != Js::Constants::NoProfileId);
- bool wasNotProfiled = false;
- const Js::LdElemInfo * ldElemInfo = nullptr;
- if (isProfiledInstr && newOpcode == Js::OpCode::IsIn)
- {
- if (!DoLoadInstructionArrayProfileInfo())
- {
- isProfiledInstr = false;
- }
- else
- {
- ldElemInfo = this->m_func->GetReadOnlyProfileInfo()->GetLdElemInfo(profileId);
- ValueType arrayType = ldElemInfo->GetArrayType();
- wasNotProfiled = !ldElemInfo->WasProfiled();
- if (arrayType.IsLikelyNativeArray() && !AllowNativeArrayProfileInfo())
- {
- arrayType = arrayType.SetArrayTypeId(Js::TypeIds_Array);
- // An opnd's value type will get replaced in the forward phase when it is not fixed. Store the array type in the ProfiledInstr.
- Js::LdElemInfo *const newLdElemInfo = JitAnew(m_func->m_alloc, Js::LdElemInfo, *ldElemInfo);
- newLdElemInfo->arrayType = arrayType;
- ldElemInfo = newLdElemInfo;
- }
- src2Opnd->SetValueType(arrayType);
- if (m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry())
- {
- isProfiledInstr = false;
- }
- }
- }
- if (isProfiledInstr)
- {
- if (m_func->DoSimpleJitDynamicProfile())
- {
- instr = IR::JitProfilingInstr::New(newOpcode, dstOpnd, src1Opnd, src2Opnd, m_func);
- instr->AsJitProfilingInstr()->profileId = profileId;
- }
- else
- {
- instr = IR::ProfiledInstr::New(newOpcode, dstOpnd, src1Opnd, src2Opnd, m_func);
- if (newOpcode == Js::OpCode::IsIn)
- {
- instr->AsProfiledInstr()->u.ldElemInfo = ldElemInfo;
- }
- else
- {
- instr->AsProfiledInstr()->u.profileId = profileId;
- }
- }
- }
- else
- {
- instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, src2Opnd, m_func);
- }
- this->AddInstr(instr, offset);
- if (wasNotProfiled && DoBailOnNoProfile())
- {
- InsertBailOnNoProfile(instr);
- }
-
- switch (newOpcode)
- {
- case Js::OpCode::LdHandlerScope:
- case Js::OpCode::NewScopeSlotsWithoutPropIds:
- if (dstSym->m_isSingleDef)
- {
- dstSym->m_isNotNumber = true;
- }
- break;
- case Js::OpCode::LdInnerFrameDisplay:
- if (dstSym->m_isSingleDef)
- {
- dstSym->m_isNotNumber = true;
- }
- break;
- }
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildReg3C
- ///
- /// Build IR instr for a Reg3C instruction.
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildReg3C(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg3C<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- this->DoClosureRegCheck(layout->R1);
- this->DoClosureRegCheck(layout->R2);
- }
- BuildReg3C(newOpcode, offset, layout->R0, layout->R1, layout->R2, layout->inlineCacheIndex);
- }
- void
- IRBuilder::BuildReg3C(Js::OpCode newOpCode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot,
- Js::RegSlot src2RegSlot, Js::CacheId inlineCacheIndex)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpCode));
- IR::Instr * instr;
- IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(src1RegSlot);
- IR::RegOpnd * src2Opnd = this->BuildSrcOpnd(src2RegSlot);
- IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot);
- instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src2Opnd, m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src1Opnd, instr->GetDst(), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- instr = IR::Instr::New(newOpCode, dstOpnd, IR::IntConstOpnd::New(inlineCacheIndex, TyUint32, m_func), instr->GetDst(), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- }
- void
- IRBuilder::BuildReg2U(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, Js::RegSlot R1, uint index)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- switch (newOpcode)
- {
- case Js::OpCode::InitBaseClass:
- {
- IR::Opnd * opndProtoParent = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetObjectPrototypeAddr(), IR::AddrOpndKindDynamicVar, m_func, true);
- IR::Opnd * opndCtorParent = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetFunctionPrototypeAddr(), IR::AddrOpndKindDynamicVar, m_func, true);
- BuildInitClass(offset, R0, R1, opndProtoParent, opndCtorParent, GetEnvironmentOperand(offset), index);
- break;
- }
- default:
- AssertMsg(false, "Unknown Reg2U op");
- break;
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildReg2U(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg2U<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- this->DoClosureRegCheck(layout->R1);
- }
- BuildReg2U(newOpcode, offset, layout->R0, layout->R1, layout->SlotIndex);
- }
- void
- IRBuilder::BuildReg3U(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, Js::RegSlot R1, Js::RegSlot R2, uint index)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- switch (newOpcode)
- {
- case Js::OpCode::InitInnerBaseClass:
- {
- IR::Opnd * opndProtoParent = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetObjectPrototypeAddr(), IR::AddrOpndKindDynamicVar, m_func, true);
- IR::Opnd * opndCtorParent = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetFunctionPrototypeAddr(), IR::AddrOpndKindDynamicVar, m_func, true);
- BuildInitClass(offset, R0, R1, opndProtoParent, opndCtorParent, BuildSrcOpnd(R2), index);
- break;
- }
- default:
- AssertMsg(false, "Unknown Reg3U op");
- break;
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildReg3U(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg3U<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- this->DoClosureRegCheck(layout->R1);
- this->DoClosureRegCheck(layout->R2);
- }
- BuildReg3U(newOpcode, offset, layout->R0, layout->R1, layout->R2, layout->SlotIndex);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildReg4U(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg4U<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- this->DoClosureRegCheck(layout->R1);
- this->DoClosureRegCheck(layout->R2);
- this->DoClosureRegCheck(layout->R3);
- }
- BuildReg4U(newOpcode, offset, layout->R0, layout->R1, layout->R2, layout->R3, layout->SlotIndex);
- }
- void
- IRBuilder::BuildReg4U(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, Js::RegSlot R1, Js::RegSlot R2, Js::RegSlot R3, uint slotIndex)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- switch (newOpcode)
- {
- case Js::OpCode::InitClass:
- {
- BuildInitClass(offset, R0, R1, BuildSrcOpnd(R3), BuildSrcOpnd(R2), GetEnvironmentOperand(offset), slotIndex);
- break;
- }
- default:
- AssertMsg(false, "Unknown Reg4U opcode");
- break;
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildReg5U(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg5U<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- this->DoClosureRegCheck(layout->R1);
- this->DoClosureRegCheck(layout->R2);
- this->DoClosureRegCheck(layout->R3);
- this->DoClosureRegCheck(layout->R4);
- }
- BuildReg5U(newOpcode, offset, layout->R0, layout->R1, layout->R2, layout->R3, layout->R4, layout->SlotIndex);
- }
- void
- IRBuilder::BuildReg5U(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, Js::RegSlot R1, Js::RegSlot R2, Js::RegSlot R3, Js::RegSlot R4, uint slotIndex)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- switch (newOpcode)
- {
- case Js::OpCode::InitInnerClass:
- {
- BuildInitClass(offset, R0, R1, BuildSrcOpnd(R3), BuildSrcOpnd(R2), BuildSrcOpnd(R4), slotIndex);
- break;
- }
- default:
- AssertMsg(false, "Unknown Reg5U opcode");
- break;
- }
- }
- void
- IRBuilder::BuildInitClass(uint32 offset, Js::RegSlot regConstructor, Js::RegSlot regProto, IR::Opnd * opndProtoParent, IR::Opnd * opndConstructorParent, IR::Opnd * opndEnvironment, uint index)
- {
- IR::RegOpnd * opndProto = BuildDstOpnd(regProto);
- opndProto->SetValueType(ValueType::GetObject(ObjectType::Object));
- IR::Instr * instr = IR::Instr::New(Js::OpCode::NewClassProto, opndProto, opndProtoParent, m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), opndConstructorParent, m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), opndProto, instr->GetDst(), m_func);
- this->AddInstr(instr, offset);
- Js::FunctionInfoPtrPtr infoRef = m_func->GetJITFunctionBody()->GetNestedFuncRef(index);
- IR::AddrOpnd * functionBodySlotOpnd = IR::AddrOpnd::New((Js::Var)infoRef, IR::AddrOpndKindDynamicMisc, m_func);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), functionBodySlotOpnd, instr->GetDst(), m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), opndEnvironment, instr->GetDst(), m_func);
- this->AddInstr(instr, offset);
- IR::RegOpnd * opndConstructor = BuildDstOpnd(regConstructor);
- instr = IR::Instr::New(Js::OpCode::NewClassConstructor, opndConstructor, instr->GetDst(), m_func);
- this->AddInstr(instr, offset);
- Assert(opndConstructor->m_sym->m_isSingleDef);
- opndConstructor->m_sym->m_isSafeThis = true;
- opndConstructor->m_sym->m_isNotNumber = true;
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildReg4
- ///
- /// Build IR instr for a Reg4 instruction.
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildReg4(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg4<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- this->DoClosureRegCheck(layout->R1);
- this->DoClosureRegCheck(layout->R2);
- this->DoClosureRegCheck(layout->R3);
- }
- BuildReg4(newOpcode, offset, layout->R0, layout->R1, layout->R2, layout->R3);
- }
- void
- IRBuilder::BuildReg4(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot,
- Js::RegSlot src2RegSlot, Js::RegSlot src3RegSlot)
- {
- IR::Instr * instr = nullptr;
- Assert(newOpcode == Js::OpCode::Concat3 || newOpcode == Js::OpCode::Restify);
- IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(src1RegSlot);
- IR::RegOpnd * src2Opnd = this->BuildSrcOpnd(src2RegSlot);
- IR::RegOpnd * src3Opnd = this->BuildSrcOpnd(src3RegSlot);
- if (newOpcode == Js::OpCode::Restify)
- {
- IR::RegOpnd * src0Opnd = this->BuildSrcOpnd(dstRegSlot);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src3Opnd, m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src2Opnd, instr->GetDst(), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src1Opnd, instr->GetDst(), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src0Opnd, instr->GetDst(), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- IR::Opnd *firstArg = instr->GetDst();
- instr = IR::Instr::New(newOpcode, m_func);
- instr->SetSrc1(firstArg);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- return;
- }
- IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot);
- IR::RegOpnd * str1Opnd = InsertConvPrimStr(src1Opnd, offset, true);
- IR::RegOpnd * str2Opnd = InsertConvPrimStr(src2Opnd, Js::Constants::NoByteCodeOffset, true);
- IR::RegOpnd * str3Opnd = InsertConvPrimStr(src3Opnd, Js::Constants::NoByteCodeOffset, true);
- // Need to insert a byte code use for src1/src2 that if ConvPrimStr of the src2/src3 bail out
- // we will restore it.
- bool src1HasByteCodeRegSlot = src1Opnd->m_sym->HasByteCodeRegSlot();
- bool src2HasByteCodeRegSlot = src2Opnd->m_sym->HasByteCodeRegSlot();
- if (src1HasByteCodeRegSlot || src2HasByteCodeRegSlot)
- {
- IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, Js::Constants::NoByteCodeOffset);
- if (src1HasByteCodeRegSlot)
- {
- byteCodeUse->Set(src1Opnd);
- }
- if (src2HasByteCodeRegSlot)
- {
- byteCodeUse->Set(src2Opnd);
- }
- this->AddInstr(byteCodeUse, Js::Constants::NoByteCodeOffset);
- }
- if (!PHASE_OFF(Js::BackendConcatExprOptPhase, this->m_func))
- {
- IR::RegOpnd* tmpDstOpnd1 = IR::RegOpnd::New(StackSym::New(this->m_func), TyVar, this->m_func);
- IR::RegOpnd* tmpDstOpnd2 = IR::RegOpnd::New(StackSym::New(this->m_func), TyVar, this->m_func);
- IR::RegOpnd* tmpDstOpnd3 = IR::RegOpnd::New(StackSym::New(this->m_func), TyVar, this->m_func);
- instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItemBE, tmpDstOpnd1, str1Opnd, m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItemBE, tmpDstOpnd2, str2Opnd, tmpDstOpnd1, m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItemBE, tmpDstOpnd3, str3Opnd, tmpDstOpnd2, m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- IR::IntConstOpnd * countIntConstOpnd = IR::IntConstOpnd::New(3, TyUint32, m_func, true);
- instr = IR::Instr::New(Js::OpCode::NewConcatStrMultiBE, dstOpnd, countIntConstOpnd, tmpDstOpnd3, m_func);
- dstOpnd->SetValueType(ValueType::String);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- }
- else
- {
- instr = IR::Instr::New(Js::OpCode::NewConcatStrMulti, dstOpnd, IR::IntConstOpnd::New(3, TyUint32, m_func, true), m_func);
- dstOpnd->SetValueType(ValueType::String);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItem, IR::IndirOpnd::New(dstOpnd, 0, TyVar, m_func), str1Opnd, m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItem, IR::IndirOpnd::New(dstOpnd, 1, TyVar, m_func), str2Opnd, m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItem, IR::IndirOpnd::New(dstOpnd, 2, TyVar, m_func), str3Opnd, m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- }
- }
- IR::RegOpnd *
- IRBuilder::InsertConvPrimStr(IR::RegOpnd * srcOpnd, uint offset, bool forcePreOpBailOutIfNeeded)
- {
- IR::RegOpnd * strOpnd = IR::RegOpnd::New(TyVar, this->m_func);
- IR::Instr * instr = IR::Instr::New(Js::OpCode::Conv_PrimStr, strOpnd, srcOpnd, m_func);
- instr->forcePreOpBailOutIfNeeded = forcePreOpBailOutIfNeeded;
- strOpnd->SetValueType(ValueType::String);
- strOpnd->SetValueTypeFixed();
- this->AddInstr(instr, offset);
- return strOpnd;
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildReg2B1(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg2B1<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- this->DoClosureRegCheck(layout->R1);
- }
- BuildReg2B1(newOpcode, offset, layout->R0, layout->R1, layout->B2);
- }
- void
- IRBuilder::BuildReg2B1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot srcRegSlot, byte index)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- Assert(newOpcode == Js::OpCode::SetConcatStrMultiItem);
- IR::Instr * instr;
- IR::RegOpnd * srcOpnd = this->BuildSrcOpnd(srcRegSlot);
- IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot, TyVar, false, true);
- IR::IndirOpnd * indir1Opnd = IR::IndirOpnd::New(dstOpnd, index, TyVar, m_func);
- dstOpnd->SetValueType(ValueType::String);
- instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItem, indir1Opnd, InsertConvPrimStr(srcOpnd, offset, true), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildReg3B1(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg3B1<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- this->DoClosureRegCheck(layout->R1);
- this->DoClosureRegCheck(layout->R2);
- }
- BuildReg3B1(newOpcode, offset, layout->R0, layout->R1, layout->R2, layout->B3);
- }
- void
- IRBuilder::BuildReg3B1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot,
- Js::RegSlot src2RegSlot, uint8 index)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- IR::Instr * instr;
- IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(src1RegSlot);
- IR::RegOpnd * src2Opnd = this->BuildSrcOpnd(src2RegSlot);
- IR::RegOpnd * dstOpnd = nullptr;
- IR::Instr * newConcatStrMulti = nullptr;
- switch (newOpcode)
- {
- case Js::OpCode::NewConcatStrMulti:
- dstOpnd = this->BuildDstOpnd(dstRegSlot);
- newConcatStrMulti = IR::Instr::New(Js::OpCode::NewConcatStrMulti, dstOpnd, IR::IntConstOpnd::New(index, TyUint32, m_func), m_func);
- index = 0;
- break;
- case Js::OpCode::SetConcatStrMultiItem2:
- dstOpnd = this->BuildDstOpnd(dstRegSlot, TyVar, false, true);
- break;
- default:
- Assert(false);
- };
- dstOpnd->SetValueType(ValueType::String);
- IR::IndirOpnd * indir1Opnd = IR::IndirOpnd::New(dstOpnd, index, TyVar, m_func);
- IR::IndirOpnd * indir2Opnd = IR::IndirOpnd::New(dstOpnd, index + 1, TyVar, m_func);
- // Need to do the to str first, as they may have side effects.
- IR::RegOpnd * str1Opnd = InsertConvPrimStr(src1Opnd, offset, true);
- IR::RegOpnd * str2Opnd = InsertConvPrimStr(src2Opnd, Js::Constants::NoByteCodeOffset, true);
- // Need to insert a byte code use for src1 so that if ConvPrimStr of the src2 bail out
- // we will restore it.
- if (src1Opnd->m_sym->HasByteCodeRegSlot())
- {
- IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, Js::Constants::NoByteCodeOffset);
- byteCodeUse->Set(src1Opnd);
- this->AddInstr(byteCodeUse, Js::Constants::NoByteCodeOffset);
- }
- if (newConcatStrMulti)
- {
- // Allocate the concat str after the ConvToStr
- this->AddInstr(newConcatStrMulti, Js::Constants::NoByteCodeOffset);
- }
- instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItem, indir1Opnd, str1Opnd, m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItem, indir2Opnd, str2Opnd, m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildReg5
- ///
- /// Build IR instr for a Reg5 instruction.
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildReg5(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg5<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- this->DoClosureRegCheck(layout->R1);
- this->DoClosureRegCheck(layout->R2);
- this->DoClosureRegCheck(layout->R3);
- this->DoClosureRegCheck(layout->R4);
- }
- BuildReg5(newOpcode, offset, layout->R0, layout->R1, layout->R2, layout->R3, layout->R4);
- }
- void
- IRBuilder::BuildReg5(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot,
- Js::RegSlot src2RegSlot, Js::RegSlot src3RegSlot, Js::RegSlot src4RegSlot)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- IR::Instr * instr;
- IR::RegOpnd * dstOpnd;
- IR::RegOpnd * src1Opnd;
- IR::RegOpnd * src2Opnd;
- IR::RegOpnd * src3Opnd;
- IR::RegOpnd * src4Opnd;
- // We can't support instructions with more than 2 srcs. Instead create a CallHelper instructions,
- // and pass the srcs as ArgOut_A instructions.
- src1Opnd = this->BuildSrcOpnd(src1RegSlot);
- src2Opnd = this->BuildSrcOpnd(src2RegSlot);
- src3Opnd = this->BuildSrcOpnd(src3RegSlot);
- src4Opnd = this->BuildSrcOpnd(src4RegSlot);
- dstOpnd = this->BuildDstOpnd(dstRegSlot);
-
- instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src4Opnd, m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src3Opnd, instr->GetDst(), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src2Opnd, instr->GetDst(), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src1Opnd, instr->GetDst(), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- IR::HelperCallOpnd *helperOpnd;
- switch (newOpcode) {
- case Js::OpCode::ApplyArgs:
- helperOpnd=IR::HelperCallOpnd::New(IR::HelperOp_OP_ApplyArgs, this->m_func);
- break;
- default:
- AssertMsg(UNREACHED, "Unknown Reg5 opcode");
- Fatal();
- }
- instr = IR::Instr::New(Js::OpCode::CallHelper, dstOpnd, helperOpnd, instr->GetDst(), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- }
- void
- IRBuilder::BuildW1(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode));
- unsigned short C1;
- const unaligned Js::OpLayoutW1 *regLayout = m_jnReader.W1();
- C1 = regLayout->C1;
- IR::Instr * instr;
- IntConstType value = C1;
- IR::IntConstOpnd * srcOpnd;
- srcOpnd = IR::IntConstOpnd::New(value, TyInt32, m_func);
- instr = IR::Instr::New(newOpcode, m_func);
- instr->SetSrc1(srcOpnd);
- this->AddInstr(instr, offset);
- if (newOpcode == Js::OpCode::RuntimeReferenceError || newOpcode == Js::OpCode::RuntimeTypeError)
- {
- if (DoBailOnNoProfile())
- {
- // RuntimeReferenceError are extremely rare as they are guaranteed to throw. Insert BailonNoProfile to optimize this code path.
- // If there are continues bailout bailonnoprofile will be disabled.
- InsertBailOnNoProfile(instr);
- }
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildUnsigned1(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Unsigned1<SizePolicy>>();
- BuildUnsigned1(newOpcode, offset, layout->C1);
- }
- void
- IRBuilder::BuildUnsigned1(Js::OpCode newOpcode, uint32 offset, uint32 num)
- {
- switch (newOpcode)
- {
- case Js::OpCode::EmitTmpRegCount:
- // Note: EmitTmpRegCount is inserted when debugging, not needed for jit.
- // It's only needed by the debugger to see how many tmp regs are active.
- Assert(m_func->IsJitInDebugMode());
- return;
- case Js::OpCode::NewBlockScope:
- case Js::OpCode::NewPseudoScope:
- {
- if (num >= m_func->GetJITFunctionBody()->GetInnerScopeCount())
- {
- Js::Throw::FatalInternalError();
- }
- Js::RegSlot dstRegSlot = num + m_func->GetJITFunctionBody()->GetFirstInnerScopeReg();
- IR::RegOpnd * dstOpnd = BuildDstOpnd(dstRegSlot);
- IR::Instr * instr = IR::Instr::New(newOpcode, dstOpnd, m_func);
- this->AddInstr(instr, offset);
- if (dstOpnd->m_sym->m_isSingleDef)
- {
- dstOpnd->m_sym->m_isNotNumber = true;
- }
- break;
- }
- case Js::OpCode::CloneInnerScopeSlots:
- case Js::OpCode::CloneBlockScope:
- {
- if (num >= m_func->GetJITFunctionBody()->GetInnerScopeCount())
- {
- Js::Throw::FatalInternalError();
- }
- Js::RegSlot srcRegSlot = num + m_func->GetJITFunctionBody()->GetFirstInnerScopeReg();
- IR::RegOpnd * srcOpnd = BuildSrcOpnd(srcRegSlot);
- IR::Instr * instr = IR::Instr::New(newOpcode, m_func);
- instr->SetSrc1(srcOpnd);
- this->AddInstr(instr, offset);
- break;
- }
- case Js::OpCode::ProfiledLoopBodyStart:
- {
- // This opcode is removed from the IR when we aren't doing Profiling SimpleJit or not jitting loop bodies
- if (m_func->DoSimpleJitDynamicProfile() && m_func->GetJITFunctionBody()->DoJITLoopBody())
- {
- // Attach a register to the dest of this instruction to communicate whether we should bail out (the deciding of this is done in lowering)
- IR::Opnd* fullJitExists = IR::RegOpnd::New(TyUint8, m_func);
- auto start = m_lastInstr;
- if (start->m_opcode == Js::OpCode::InitLoopBodyCount)
- {
- Assert(this->IsLoopBody());
- start = start->m_prev;
- }
- Assert(start->m_opcode == Js::OpCode::ProfiledLoopStart && start->GetDst());
- IR::JitProfilingInstr* instr = IR::JitProfilingInstr::New(Js::OpCode::ProfiledLoopBodyStart, fullJitExists, start->GetDst(), m_func);
- // profileId is used here to represent the loop number
- instr->loopNumber = num;
- this->AddInstr(instr, offset);
- // If fullJitExists isn't 0, bail out so that we can get the fulljitted version
- BailOutInfo * bailOutInfo = JitAnew(m_func->m_alloc, BailOutInfo, instr->GetByteCodeOffset(), m_func);
- IR::BailOutInstr * bailInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNotEqual, IR::BailOnSimpleJitToFullJitLoopBody, bailOutInfo, bailOutInfo->bailOutFunc);
- bailInstr->SetSrc1(fullJitExists);
- bailInstr->SetSrc2(IR::IntConstOpnd::New(0, TyUint8, m_func, true));
- this->AddInstr(bailInstr, offset);
- }
- Js::ImplicitCallFlags flags = Js::ImplicitCall_HasNoInfo;
- Js::LoopFlags loopFlags;
- if (this->m_func->HasProfileInfo())
- {
- flags = m_func->GetReadOnlyProfileInfo()->GetLoopImplicitCallFlags(num);
- loopFlags = m_func->GetReadOnlyProfileInfo()->GetLoopFlags(num);
- }
- // Put a label the instruction stream to carry the profile info
- IR::ProfiledLabelInstr * labelInstr = IR::ProfiledLabelInstr::New(Js::OpCode::Label, this->m_func, flags, loopFlags);
- #if DBG
- labelInstr->loopNum = num;
- #endif
- m_lastInstr->InsertAfter(labelInstr);
- m_lastInstr = labelInstr;
- // Set it to the offset to the start of the loop
- labelInstr->SetByteCodeOffset(m_jnReader.GetCurrentOffset());
- break;
- }
- case Js::OpCode::LoopBodyStart:
- break;
- case Js::OpCode::ProfiledLoopStart:
- {
- AssertOrFailFast(num < m_func->GetJITFunctionBody()->GetLoopCount());
- // If we're in profiling SimpleJit and jitting loop bodies, we need to keep this until lowering.
- if (m_func->DoSimpleJitDynamicProfile() && m_func->GetJITFunctionBody()->DoJITLoopBody())
- {
- // In order for the JIT engine to correctly allocate registers we need to have this set up before lowering.
- // There may be 0 to many LoopEnds, but there will only ever be one LoopStart
- Assert(!this->m_saveLoopImplicitCallFlags[num]);
- const auto ty = Lowerer::GetImplicitCallFlagsType();
- auto saveOpnd = IR::RegOpnd::New(ty, m_func);
- this->m_saveLoopImplicitCallFlags[num] = saveOpnd;
- // Note that we insert this instruction /before/ the actual ProfiledLoopStart opcode. This is because Lowering is backwards
- // and this is just a fake instruction which is only used to pass on the saveOpnd; this instruction will eventually be removed.
- auto instr = IR::JitProfilingInstr::New(Js::OpCode::Ld_A, saveOpnd, IR::MemRefOpnd::New((intptr_t)0, ty, m_func), m_func);
- instr->isLoopHelper = true;
- this->AddInstr(instr, offset);
- instr = IR::JitProfilingInstr::New(Js::OpCode::ProfiledLoopStart, IR::RegOpnd::New(TyMachPtr, m_func), nullptr, m_func);
- instr->loopNumber = num;
- this->AddInstr(instr, offset);
- }
- if (this->IsLoopBody() && !m_loopCounterSym)
- {
- InsertInitLoopBodyLoopCounter(num);
- }
- break;
- }
- case Js::OpCode::ProfiledLoopEnd:
- {
- AssertOrFailFast(num < m_func->GetJITFunctionBody()->GetLoopCount());
- // TODO: Decide whether we want the implicit loop call flags to be recorded in simplejitted loop bodies
- if (m_func->DoSimpleJitDynamicProfile() && m_func->GetJITFunctionBody()->DoJITLoopBody())
- {
- Assert(this->m_saveLoopImplicitCallFlags[num]);
- // In profiling simplejit we need this opcode in order to restore the implicit call flags
- auto instr = IR::JitProfilingInstr::New(Js::OpCode::ProfiledLoopEnd, nullptr, this->m_saveLoopImplicitCallFlags[num], m_func);
- this->AddInstr(instr, offset);
- instr->loopNumber = num;
- }
- if (!this->IsLoopBody())
- {
- break;
- }
- // In the early exit case (return), we generated ProfiledLoopEnd for all the outer loop.
- // If we see one of these profile loop, just load the IP of the immediate outer loop of the loop body being JIT'ed
- // and then skip all the other loops using the fact that we have already loaded the return IP
- if (IsLoopBodyReturnIPInstr(m_lastInstr))
- {
- // Already loaded the loop IP sym, skip
- break;
- }
- // See we are ending an outer loop and load the return IP to the ProfiledLoopEnd opcode
- // instead of following the normal branch
- const JITLoopHeaderIDL * loopHeader = m_func->GetJITFunctionBody()->GetLoopHeaderData(num);
- if (m_func->GetJITFunctionBody()->GetLoopHeaderAddr(num) != m_func->m_workItem->GetLoopHeaderAddr() &&
- JITTimeFunctionBody::LoopContains(loopHeader, m_func->m_workItem->GetLoopHeader()))
- {
- this->InsertLoopBodyReturnIPInstr(offset, offset);
- }
- else
- {
- Assert(JITTimeFunctionBody::LoopContains(m_func->m_workItem->GetLoopHeader(), loopHeader));
- }
- break;
- }
- case Js::OpCode::InvalCachedScope:
- {
- // The reg and constant are both src operands.
- IR::Instr* instr = IR::Instr::New(Js::OpCode::InvalCachedScope, m_func);
- IR::RegOpnd *envOpnd = this->BuildSrcOpnd(m_func->GetJITFunctionBody()->GetEnvReg());
- instr->SetSrc1(envOpnd);
- IR::IntConstOpnd *envIndex = IR::IntConstOpnd::New(num, TyInt32, m_func, true);
- instr->SetSrc2(envIndex);
- this->AddInstr(instr, offset);
- return;
- }
- default:
- Assert(false);
- __assume(false);
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledReg1Unsigned1(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_Reg1Unsigned1<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- }
- BuildProfiledReg1Unsigned1(newOpcode, offset, layout->R0, layout->C1, layout->profileId);
- }
- void
- IRBuilder::BuildProfiledReg1Unsigned1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, int32 C1, Js::ProfileId profileId)
- {
- Assert(newOpcode == Js::OpCode::ProfiledNewScArray || newOpcode == Js::OpCode::ProfiledInitForInEnumerator);
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- if (newOpcode == Js::OpCode::InitForInEnumerator)
- {
- IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(R0);
- IR::Opnd * src2Opnd = this->BuildForInEnumeratorOpnd(C1, offset);
- IR::Instr *instr = IR::ProfiledInstr::New(Js::OpCode::InitForInEnumerator, nullptr, src1Opnd, src2Opnd, m_func);
- instr->AsProfiledInstr()->u.profileId = profileId;
- this->AddInstr(instr, offset);
- return;
- }
- IR::Instr *instr;
- Js::RegSlot dstRegSlot = R0;
- IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot);
- StackSym * dstSym = dstOpnd->m_sym;
- int32 value = C1;
- IR::IntConstOpnd * srcOpnd;
- srcOpnd = IR::IntConstOpnd::New(value, TyInt32, m_func);
- if (m_func->DoSimpleJitDynamicProfile())
- {
- instr = IR::JitProfilingInstr::New(newOpcode, dstOpnd, srcOpnd, m_func);
- instr->AsJitProfilingInstr()->profileId = profileId;
- }
- else
- {
- instr = IR::ProfiledInstr::New(newOpcode, dstOpnd, srcOpnd, m_func);
- instr->AsProfiledInstr()->u.profileId = profileId;
- }
- this->AddInstr(instr, offset);
- if (dstSym->m_isSingleDef)
- {
- dstSym->m_isSafeThis = true;
- dstSym->m_isNotNumber = true;
- }
- // Undefined values in array literals ([0, undefined, 1]) are treated as missing values in some versions
- Js::ArrayCallSiteInfo *arrayInfo = nullptr;
- if (m_func->HasArrayInfo())
- {
- arrayInfo = m_func->GetReadOnlyProfileInfo()->GetArrayCallSiteInfo(profileId);
- }
- Js::TypeId arrayTypeId = Js::TypeIds_Array;
- if (arrayInfo && !m_func->IsJitInDebugMode() && Js::JavascriptArray::HasInlineHeadSegment(value))
- {
- if (arrayInfo->IsNativeIntArray())
- {
- arrayTypeId = Js::TypeIds_NativeIntArray;
- }
- else if (arrayInfo->IsNativeFloatArray())
- {
- arrayTypeId = Js::TypeIds_NativeFloatArray;
- }
- }
- dstOpnd->SetValueType(ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(true).SetArrayTypeId(arrayTypeId));
- if (dstOpnd->GetValueType().HasVarElements())
- {
- dstOpnd->SetValueTypeFixed();
- }
- else
- {
- dstOpnd->SetValueType(dstOpnd->GetValueType().ToLikely());
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildReg1Unsigned1(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg1Unsigned1<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- }
- BuildReg1Unsigned1(newOpcode, offset, layout->R0, layout->C1);
- }
- void
- IRBuilder::BuildReg1Unsigned1(Js::OpCode newOpcode, uint offset, Js::RegSlot R0, int32 C1)
- {
- switch (newOpcode)
- {
- case Js::OpCode::NewRegEx:
- this->BuildRegexFromPattern(R0, C1, offset);
- return;
- case Js::OpCode::LdInnerScope:
- {
- IR::RegOpnd * srcOpnd = BuildSrcOpnd(this->InnerScopeIndexToRegSlot(C1));
- IR::RegOpnd * dstOpnd = BuildDstOpnd(R0);
- IR::Instr * instr = IR::Instr::New(Js::OpCode::Ld_A, dstOpnd, srcOpnd, m_func);
- if (dstOpnd->m_sym->m_isSingleDef)
- {
- dstOpnd->m_sym->m_isNotNumber = true;
- }
- this->AddInstr(instr, offset);
- return;
- }
- case Js::OpCode::LdIndexedFrameDisplayNoParent:
- {
- newOpcode = Js::OpCode::LdFrameDisplay;
- IR::RegOpnd *srcOpnd = this->BuildSrcOpnd(this->InnerScopeIndexToRegSlot(C1));
- IR::RegOpnd *dstOpnd = this->BuildDstOpnd(R0);
- IR::Instr *instr = IR::Instr::New(newOpcode, dstOpnd, srcOpnd, m_func);
- this->AddEnvOpndForInnerFrameDisplay(instr, offset);
- if (dstOpnd->m_sym->m_isSingleDef)
- {
- dstOpnd->m_sym->m_isNotNumber = true;
- }
- this->AddInstr(instr, offset);
- return;
- }
- case Js::OpCode::GetCachedFunc:
- {
- IR::RegOpnd *src1Opnd = this->BuildSrcOpnd(m_func->GetJITFunctionBody()->GetLocalClosureReg());
- IR::Opnd *src2Opnd = IR::IntConstOpnd::New(C1, TyUint32, m_func);
- IR::RegOpnd *dstOpnd = this->BuildDstOpnd(R0);
- IR::Instr *instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, src2Opnd, m_func);
- if (dstOpnd->m_sym->m_isSingleDef)
- {
- dstOpnd->m_sym->m_isNotNumber = true;
- }
- this->AddInstr(instr, offset);
- return;
- }
- case Js::OpCode::InitForInEnumerator:
- {
- IR::Instr *instr = IR::Instr::New(Js::OpCode::InitForInEnumerator, m_func);
- instr->SetSrc1(this->BuildSrcOpnd(R0));
- instr->SetSrc2(this->BuildForInEnumeratorOpnd(C1, offset));
- this->AddInstr(instr, offset);
- return;
- }
- }
- IR::RegOpnd * dstOpnd = this->BuildDstOpnd(R0);
- StackSym * dstSym = dstOpnd->m_sym;
- IntConstType value = C1;
- IR::IntConstOpnd * srcOpnd = IR::IntConstOpnd::New(value, TyInt32, m_func);
- IR::Instr * instr = IR::Instr::New(newOpcode, dstOpnd, srcOpnd, m_func);
- this->AddInstr(instr, offset);
- if (newOpcode == Js::OpCode::NewScopeSlots)
- {
- this->AddInstr(
- IR::Instr::New(
- Js::OpCode::Ld_A, IR::RegOpnd::New(m_func->GetLocalClosureSym(), TyVar, m_func), dstOpnd, m_func),
- (uint32)-1);
- }
- if (dstSym->m_isSingleDef)
- {
- switch (newOpcode)
- {
- case Js::OpCode::NewScArray:
- case Js::OpCode::NewScArrayWithMissingValues:
- dstSym->m_isSafeThis = true;
- dstSym->m_isNotNumber = true;
- break;
- }
- }
- if (newOpcode == Js::OpCode::NewScArray || newOpcode == Js::OpCode::NewScArrayWithMissingValues)
- {
- // Undefined values in array literals ([0, undefined, 1]) are treated as missing values in some versions
- dstOpnd->SetValueType(
- ValueType::GetObject(ObjectType::Array)
- .SetHasNoMissingValues(newOpcode == Js::OpCode::NewScArray)
- .SetArrayTypeId(Js::TypeIds_Array));
- dstOpnd->SetValueTypeFixed();
- }
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildReg2Int1
- ///
- /// Build IR instr for a Reg2I4 instruction.
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildReg2Int1(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg2Int1<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R0);
- this->DoClosureRegCheck(layout->R1);
- }
- BuildReg2Int1(newOpcode, offset, layout->R0, layout->R1, layout->C1);
- }
- void
- IRBuilder::BuildReg2Int1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot srcRegSlot, int32 value)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- IR::Instr * instr;
- if (newOpcode == Js::OpCode::LdIndexedFrameDisplay)
- {
- newOpcode = Js::OpCode::LdFrameDisplay;
- if ((uint)value >= m_func->GetJITFunctionBody()->GetInnerScopeCount())
- {
- Js::Throw::FatalInternalError();
- }
- IR::RegOpnd *src1Opnd = this->BuildSrcOpnd(value + m_func->GetJITFunctionBody()->GetFirstInnerScopeReg());
- IR::RegOpnd *src2Opnd = this->BuildSrcOpnd(srcRegSlot);
- IR::RegOpnd *dstOpnd = this->BuildDstOpnd(dstRegSlot);
- instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, src2Opnd, m_func);
- if (dstOpnd->m_sym->m_isSingleDef)
- {
- dstOpnd->m_sym->m_isNotNumber = true;
- }
- this->AddInstr(instr, offset);
- return;
- }
- IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(srcRegSlot);
- IR::IntConstOpnd * src2Opnd = IR::IntConstOpnd::New(value, TyInt32, m_func);
- IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot);
- switch (newOpcode)
- {
- case Js::OpCode::ProfiledLdThis:
- newOpcode = Js::OpCode::LdThis;
- if(m_func->HasProfileInfo())
- {
- dstOpnd->SetValueType(m_func->GetReadOnlyProfileInfo()->GetThisInfo().valueType);
- }
- if(m_func->DoSimpleJitDynamicProfile())
- {
- instr = IR::JitProfilingInstr::New(newOpcode, dstOpnd, src1Opnd, src2Opnd, m_func);
- // Break out since we just made the instr
- break;
- }
- // fall-through
- default:
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, src2Opnd, m_func);
- break;
- }
- this->AddInstr(instr, offset);
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildElementC
- ///
- /// Build IR instr for an ElementC instruction.
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementScopedC(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementScopedC<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- }
- BuildElementScopedC(newOpcode, offset, layout->Value, layout->PropertyIdIndex);
- }
- void
- IRBuilder::BuildElementScopedC(Js::OpCode newOpcode, uint32 offset, Js::RegSlot regSlot, Js::PropertyIdIndexType propertyIdIndex)
- {
- IR::Instr * instr;
- Js::PropertyId propertyId = m_func->GetJITFunctionBody()->GetReferencedPropertyId(propertyIdIndex);
- PropertyKind propertyKind = PropertyKindData;
- IR::RegOpnd * regOpnd;
- Js::RegSlot fieldRegSlot = this->GetEnvRegForEvalCode();
- IR::SymOpnd * fieldSymOpnd = this->BuildFieldOpnd(newOpcode, fieldRegSlot, propertyId, propertyIdIndex, propertyKind);
- switch (newOpcode)
- {
- case Js::OpCode::ScopedEnsureNoRedeclFld:
- {
- regOpnd = this->BuildSrcOpnd(regSlot);
- instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, m_func);
- break;
- }
- case Js::OpCode::ScopedDeleteFld:
- case Js::OpCode::ScopedDeleteFldStrict:
- {
- Assert(this->m_func->GetScriptContextInfo()->GetAddr() == this->m_func->GetTopFunc()->GetScriptContextInfo()->GetAddr());
- regOpnd = this->BuildDstOpnd(regSlot);
- instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func);
- break;
- }
- case Js::OpCode::ScopedInitFunc:
- {
- // Implicit root object as default instance
- IR::Opnd * instance2Opnd = this->BuildSrcOpnd(Js::FunctionBody::RootObjectRegSlot);
- regOpnd = this->BuildSrcOpnd(regSlot);
- instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, instance2Opnd, m_func);
- break;
- }
- default:
- AssertMsg(UNREACHED, "Unknown ElementScopedC opcode");
- Fatal();
- }
- this->AddInstr(instr, offset);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementC(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementC<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- this->DoClosureRegCheck(layout->Instance);
- }
- BuildElementC(newOpcode, offset, layout->Instance, layout->Value, layout->PropertyIdIndex);
- }
- void
- IRBuilder::BuildElementC(Js::OpCode newOpcode, uint32 offset, Js::RegSlot fieldRegSlot, Js::RegSlot regSlot, Js::PropertyIdIndexType propertyIdIndex)
- {
- IR::Instr * instr;
- Js::PropertyId propertyId = m_func->GetJITFunctionBody()->GetReferencedPropertyId(propertyIdIndex);
- PropertyKind propertyKind = PropertyKindData;
- IR::SymOpnd * fieldSymOpnd = this->BuildFieldOpnd(newOpcode, fieldRegSlot, propertyId, propertyIdIndex, propertyKind);
- IR::RegOpnd * regOpnd;
- bool reuseLoc = false;
- switch (newOpcode)
- {
- case Js::OpCode::DeleteFld_ReuseLoc:
- newOpcode = Js::OpCode::DeleteFld;
- reuseLoc = true;
- // fall through
- case Js::OpCode::DeleteFld:
- case Js::OpCode::DeleteRootFld:
- case Js::OpCode::DeleteFldStrict:
- case Js::OpCode::DeleteRootFldStrict:
- // Load
- regOpnd = this->BuildDstOpnd(regSlot, TyVar, false, reuseLoc);
- instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func);
- break;
- case Js::OpCode::InitSetFld:
- case Js::OpCode::InitGetFld:
- case Js::OpCode::InitClassMemberGet:
- case Js::OpCode::InitClassMemberSet:
- case Js::OpCode::InitProto:
- case Js::OpCode::StFuncExpr:
- // Store
- regOpnd = this->BuildSrcOpnd(regSlot);
- instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, m_func);
- break;
- default:
- AssertMsg(UNREACHED, "Unknown ElementC opcode");
- Fatal();
- }
- this->AddInstr(instr, offset);
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildElementSlot
- ///
- /// Build IR instr for an ElementSlot instruction.
- ///
- ///----------------------------------------------------------------------------
- IR::Instr *
- IRBuilder::BuildProfiledSlotLoad(Js::OpCode loadOp, IR::RegOpnd *dstOpnd, IR::SymOpnd *srcOpnd, Js::ProfileId profileId, bool *pUnprofiled)
- {
- IR::Instr * instr = nullptr;
- if (m_func->DoSimpleJitDynamicProfile())
- {
- instr = IR::JitProfilingInstr::New(loadOp, dstOpnd, srcOpnd, m_func);
- instr->AsJitProfilingInstr()->profileId = profileId;
- }
- else if(this->m_func->HasProfileInfo())
- {
- instr = IR::ProfiledInstr::New(loadOp, dstOpnd, srcOpnd, m_func);
- instr->AsProfiledInstr()->u.FldInfo().valueType =
- this->m_func->GetReadOnlyProfileInfo()->GetSlotLoad(profileId);
- *pUnprofiled = instr->AsProfiledInstr()->u.FldInfo().valueType.IsUninitialized();
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if(Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::DynamicProfilePhase))
- {
- const ValueType valueType(instr->AsProfiledInstr()->u.FldInfo().valueType);
- char valueTypeStr[VALUE_TYPE_MAX_STRING_SIZE];
- valueType.ToString(valueTypeStr);
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("TestTrace function %s (#%s) ValueType = %S "), m_func->GetJITFunctionBody()->GetDisplayName(), m_func->GetDebugNumberSet(debugStringBuffer), valueTypeStr);
- instr->DumpTestTrace();
- }
- #endif
- }
- return instr;
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementSlot(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementSlot<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- this->DoClosureRegCheck(layout->Instance);
- }
- BuildElementSlot(newOpcode, offset, layout->Instance, layout->Value, layout->SlotIndex, Js::Constants::NoProfileId);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledElementSlot(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_ElementSlot<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- this->DoClosureRegCheck(layout->Instance);
- }
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- BuildElementSlot(newOpcode, offset, layout->Instance, layout->Value, layout->SlotIndex, layout->profileId);
- }
- void
- IRBuilder::BuildElementSlot(Js::OpCode newOpcode, uint32 offset, Js::RegSlot fieldRegSlot, Js::RegSlot regSlot,
- int32 slotId, Js::ProfileId profileId)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- IR::Instr * instr;
- IR::RegOpnd * regOpnd;
- IR::SymOpnd * fieldSymOpnd;
- PropertyKind propertyKind = PropertyKindSlots;
- PropertySym * fieldSym;
- StackSym * stackFuncPtrSym = nullptr;
- bool isLdSlotThatWasNotProfiled = false;
- switch (newOpcode)
- {
- case Js::OpCode::NewInnerStackScFunc:
- stackFuncPtrSym = this->EnsureStackFuncPtrSym();
- // fall through
- case Js::OpCode::NewInnerScFunc:
- newOpcode = Js::OpCode::NewScFunc;
- goto NewScFuncCommon;
- case Js::OpCode::NewInnerScGenFunc:
- newOpcode = Js::OpCode::NewScGenFunc;
- NewScFuncCommon:
- {
- IR::Opnd * functionBodySlotOpnd = IR::IntConstOpnd::New(slotId, TyInt32, m_func, true);
- IR::Opnd * environmentOpnd = this->BuildSrcOpnd(fieldRegSlot);
- regOpnd = this->BuildDstOpnd(regSlot);
- if (stackFuncPtrSym)
- {
- IR::RegOpnd * dataOpnd = IR::RegOpnd::New(TyVar, m_func);
- instr = IR::Instr::New(Js::OpCode::NewScFuncData, dataOpnd, environmentOpnd, IR::RegOpnd::New(stackFuncPtrSym, TyVar, m_func), m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(newOpcode, regOpnd, functionBodySlotOpnd, dataOpnd, m_func);
- }
- else
- {
- instr = IR::Instr::New(newOpcode, regOpnd, functionBodySlotOpnd, environmentOpnd, m_func);
- }
- if (regOpnd->m_sym->m_isSingleDef)
- {
- regOpnd->m_sym->m_isSafeThis = true;
- regOpnd->m_sym->m_isNotNumber = true;
- }
- this->AddInstr(instr, offset);
- return;
- }
- case Js::OpCode::NewScFuncHomeObj:
- case Js::OpCode::NewScGenFuncHomeObj:
- {
- Js::FunctionInfoPtrPtr infoRef = m_func->GetJITFunctionBody()->GetNestedFuncRef(slotId);
- IR::AddrOpnd * functionBodySlotOpnd = IR::AddrOpnd::New((Js::Var)infoRef, IR::AddrOpndKindDynamicMisc, m_func);
- IR::Opnd * environmentOpnd = GetEnvironmentOperand(offset);
- IR::Opnd * homeObjOpnd = this->BuildSrcOpnd(fieldRegSlot);
- regOpnd = this->BuildDstOpnd(regSlot);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), homeObjOpnd, m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), functionBodySlotOpnd, instr->GetDst(), m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), environmentOpnd, instr->GetDst(), m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(newOpcode, regOpnd, instr->GetDst(), m_func);
- if (regOpnd->m_sym->m_isSingleDef)
- {
- regOpnd->m_sym->m_isSafeThis = true;
- regOpnd->m_sym->m_isNotNumber = true;
- }
- this->AddInstr(instr, offset);
- return;
- }
- case Js::OpCode::LdObjSlot:
- newOpcode = Js::OpCode::LdSlot;
- goto ObjSlotCommon;
- case Js::OpCode::StObjSlot:
- newOpcode = Js::OpCode::StSlot;
- goto ObjSlotCommon;
- case Js::OpCode::StObjSlotChkUndecl:
- newOpcode = Js::OpCode::StSlotChkUndecl;
- ObjSlotCommon:
- regOpnd = IR::RegOpnd::New(TyVar, m_func);
- fieldSymOpnd = this->BuildFieldOpnd(newOpcode, fieldRegSlot, (Js::DynamicObject::GetOffsetOfAuxSlots())/sizeof(Js::Var), (Js::PropertyIdIndexType)-1, PropertyKindSlotArray);
- instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldSymOpnd, m_func);
- this->AddInstr(instr, offset);
- fieldSym = PropertySym::New(regOpnd->m_sym, slotId, (uint32)-1, (uint)-1, PropertyKindSlots, m_func);
- fieldSymOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func);
- if (newOpcode == Js::OpCode::StSlot || newOpcode == Js::OpCode::StSlotChkUndecl)
- {
- goto StSlotCommon;
- }
- goto LdSlotCommon;
- case Js::OpCode::LdSlotArr:
- propertyKind = PropertyKindSlotArray;
- case Js::OpCode::LdSlot:
- // Load
- fieldSymOpnd = this->BuildFieldOpnd(newOpcode, fieldRegSlot, slotId, (Js::PropertyIdIndexType)-1, propertyKind);
- LdSlotCommon:
- regOpnd = this->BuildDstOpnd(regSlot);
- instr = nullptr;
- if (profileId != Js::Constants::NoProfileId)
- {
- instr = this->BuildProfiledSlotLoad(newOpcode, regOpnd, fieldSymOpnd, profileId, &isLdSlotThatWasNotProfiled);
- }
- if (!instr)
- {
- instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func);
- }
- break;
- case Js::OpCode::StSlot:
- case Js::OpCode::StSlotChkUndecl:
- // Store
- fieldSymOpnd = this->BuildFieldOpnd(newOpcode, fieldRegSlot, slotId, (Js::PropertyIdIndexType)-1, propertyKind);
- StSlotCommon:
- regOpnd = this->BuildSrcOpnd(regSlot);
- instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, m_func);
- if (newOpcode == Js::OpCode::StSlotChkUndecl)
- {
- // ChkUndecl includes an implicit read of the destination. Communicate the liveness by using the destination in src2.
- instr->SetSrc2(fieldSymOpnd);
- }
- break;
- case Js::OpCode::StPropIdArrFromVar:
- {
- IR::RegOpnd * src0Opnd = this->BuildSrcOpnd(fieldRegSlot);
- IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(regSlot);
- IntConstType value = slotId;
- IR::IntConstOpnd * valOpnd = IR::IntConstOpnd::New(value, TyInt32, m_func);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src1Opnd, m_func);
- this->AddInstr(instr, offset);
- offset = Js::Constants::NoByteCodeOffset;
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), valOpnd, instr->GetDst(), m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src0Opnd, instr->GetDst(), m_func);
- this->AddInstr(instr, offset);
- IR::Opnd * firstArg = instr->GetDst();
- instr = IR::Instr::New(newOpcode, m_func);
- instr->SetSrc1(firstArg);
- break;
- }
- default:
- AssertMsg(UNREACHED, "Unknown ElementSlot opcode");
- Fatal();
- }
- this->AddInstr(instr, offset);
- if(isLdSlotThatWasNotProfiled && DoBailOnNoProfile())
- {
- InsertBailOnNoProfile(instr);
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementSlotI1(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementSlotI1<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- }
- BuildElementSlotI1(newOpcode, offset, layout->Value, layout->SlotIndex, Js::Constants::NoProfileId);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledElementSlotI1(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_ElementSlotI1<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- }
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- BuildElementSlotI1(newOpcode, offset, layout->Value, layout->SlotIndex, layout->profileId);
- }
- void
- IRBuilder::BuildElementSlotI1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot regSlot,
- int32 slotId, Js::ProfileId profileId)
- {
- IR::RegOpnd *regOpnd;
- IR::SymOpnd *fieldOpnd;
- IR::Instr *instr = nullptr;
- IR::ByteCodeUsesInstr *byteCodeUse;
- PropertySym *fieldSym = nullptr;
- StackSym * stackFuncPtrSym = nullptr;
- SymID symID = m_func->GetJITFunctionBody()->GetLocalClosureReg();
- bool isLdSlotThatWasNotProfiled = false;
- bool reuseLoc = false;
- StackSym* closureSym = m_func->GetLocalClosureSym();
- uint scopeSlotSize = this->IsParamScopeDone() ? m_func->GetJITFunctionBody()->GetScopeSlotArraySize() : m_func->GetJITFunctionBody()->GetParamScopeSlotArraySize();
- switch (newOpcode)
- {
- case Js::OpCode::LdParamSlot:
- scopeSlotSize = m_func->GetJITFunctionBody()->GetParamScopeSlotArraySize();
- closureSym = m_func->GetParamClosureSym();
- symID = m_func->GetJITFunctionBody()->GetParamClosureReg();
- // Fall through
- case Js::OpCode::LdLocalSlot:
- if (!PHASE_OFF(Js::ClosureRangeCheckPhase, m_func))
- {
- if ((uint32)slotId >= scopeSlotSize + Js::ScopeSlots::FirstSlotIndex)
- {
- Js::Throw::FatalInternalError();
- }
- }
- if (closureSym->HasByteCodeRegSlot())
- {
- byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset);
- byteCodeUse->SetNonOpndSymbol(closureSym->m_id);
- this->AddInstr(byteCodeUse, offset);
- }
- // Read the scope slot pointer back using the stack closure sym.
- newOpcode = Js::OpCode::LdSlot;
- if (m_func->DoStackFrameDisplay())
- {
- // Read the scope slot pointer back using the stack closure sym.
- fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, closureSym->m_id, 0, (Js::PropertyIdIndexType)-1, PropertyKindSlotArray);
- regOpnd = IR::RegOpnd::New(TyVar, m_func);
- instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func);
- this->AddInstr(instr, offset);
- symID = regOpnd->m_sym->m_id;
- if (IsLoopBody())
- {
- fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, closureSym->m_id, slotId, (Js::PropertyIdIndexType)-1, PropertyKindSlotArray);
- }
- }
- else if (IsLoopBody())
- {
- this->EnsureLoopBodyLoadSlot(symID);
- }
- fieldSym = PropertySym::FindOrCreate(symID, slotId, (uint32)-1, (uint)-1, PropertyKindSlots, m_func);
- fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func);
- regOpnd = this->BuildDstOpnd(regSlot);
- instr = nullptr;
- if (profileId != Js::Constants::NoProfileId)
- {
- instr = this->BuildProfiledSlotLoad(Js::OpCode::LdSlot, regOpnd, fieldOpnd, profileId, &isLdSlotThatWasNotProfiled);
- }
- if (!instr)
- {
- instr = IR::Instr::New(Js::OpCode::LdSlot, regOpnd, fieldOpnd, m_func);
- }
- this->AddInstr(instr, offset);
- break;
- case Js::OpCode::LdParamObjSlot:
- closureSym = m_func->GetParamClosureSym();
- symID = m_func->GetJITFunctionBody()->GetParamClosureReg();
- newOpcode = Js::OpCode::LdLocalObjSlot;
- // Fall through
- case Js::OpCode::LdLocalObjSlot:
- if (closureSym->HasByteCodeRegSlot())
- {
- byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset);
- byteCodeUse->SetNonOpndSymbol(closureSym->m_id);
- this->AddInstr(byteCodeUse, offset);
- }
- fieldOpnd = this->BuildFieldOpnd(newOpcode, symID, (Js::DynamicObject::GetOffsetOfAuxSlots()) / sizeof(Js::Var), (Js::PropertyIdIndexType) - 1, PropertyKindSlotArray);
- regOpnd = IR::RegOpnd::New(TyVar, m_func);
- instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func);
- this->AddInstr(instr, offset);
- fieldSym = PropertySym::New(regOpnd->m_sym, slotId, (uint32)-1, (uint)-1, PropertyKindSlots, m_func);
- fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func);
- regOpnd = this->BuildDstOpnd(regSlot);
- instr = nullptr;
- newOpcode = Js::OpCode::LdSlot;
- if (profileId != Js::Constants::NoProfileId)
- {
- instr = this->BuildProfiledSlotLoad(newOpcode, regOpnd, fieldOpnd, profileId, &isLdSlotThatWasNotProfiled);
- }
- if (!instr)
- {
- instr = IR::Instr::New(newOpcode, regOpnd, fieldOpnd, m_func);
- }
- this->AddInstr(instr, offset);
- break;
- case Js::OpCode::StParamSlot:
- case Js::OpCode::StParamSlotChkUndecl:
- scopeSlotSize = m_func->GetJITFunctionBody()->GetParamScopeSlotArraySize();
- closureSym = m_func->GetParamClosureSym();
- symID = m_func->GetJITFunctionBody()->GetParamClosureReg();
- newOpcode = newOpcode == Js::OpCode::StParamSlot ? Js::OpCode::StLocalSlot : Js::OpCode::StLocalSlotChkUndecl;
- // Fall through
- case Js::OpCode::StLocalSlot:
- case Js::OpCode::StLocalSlotChkUndecl:
- if (!PHASE_OFF(Js::ClosureRangeCheckPhase, m_func))
- {
- if ((uint32)slotId >= scopeSlotSize + Js::ScopeSlots::FirstSlotIndex)
- {
- Js::Throw::FatalInternalError();
- }
- }
- if (closureSym->HasByteCodeRegSlot())
- {
- byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset);
- byteCodeUse->SetNonOpndSymbol(closureSym->m_id);
- this->AddInstr(byteCodeUse, offset);
- }
- newOpcode = newOpcode == Js::OpCode::StLocalSlot ? Js::OpCode::StSlot : Js::OpCode::StSlotChkUndecl;
- if (m_func->DoStackFrameDisplay())
- {
- regOpnd = IR::RegOpnd::New(TyVar, m_func);
- // Read the scope slot pointer back using the stack closure sym.
- fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, closureSym->m_id, 0, (Js::PropertyIdIndexType)-1, PropertyKindSlotArray);
- instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func);
- this->AddInstr(instr, offset);
- symID = regOpnd->m_sym->m_id;
- if (IsLoopBody())
- {
- fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, closureSym->m_id, slotId, (Js::PropertyIdIndexType)-1, PropertyKindSlotArray);
- }
- }
- else
- {
- if (IsLoopBody())
- {
- this->EnsureLoopBodyLoadSlot(symID);
- }
- }
- fieldSym = PropertySym::FindOrCreate(symID, slotId, (uint32)-1, (uint)-1, PropertyKindSlots, m_func);
- fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func);
- regOpnd = this->BuildSrcOpnd(regSlot);
- instr = IR::Instr::New(newOpcode, fieldOpnd, regOpnd, m_func);
- this->AddInstr(instr, offset);
- if (newOpcode == Js::OpCode::StSlotChkUndecl)
- {
- instr->SetSrc2(fieldOpnd);
- }
- break;
- case Js::OpCode::StParamObjSlot:
- case Js::OpCode::StParamObjSlotChkUndecl:
- closureSym = m_func->GetParamClosureSym();
- symID = m_func->GetJITFunctionBody()->GetParamClosureReg();
- newOpcode = newOpcode == Js::OpCode::StParamObjSlot ? Js::OpCode::StLocalObjSlot : Js::OpCode::StLocalObjSlotChkUndecl;
- // Fall through
- case Js::OpCode::StLocalObjSlot:
- case Js::OpCode::StLocalObjSlotChkUndecl:
- if (closureSym->HasByteCodeRegSlot())
- {
- byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset);
- byteCodeUse->SetNonOpndSymbol(closureSym->m_id);
- this->AddInstr(byteCodeUse, offset);
- }
- regOpnd = IR::RegOpnd::New(TyVar, m_func);
- fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, symID, (Js::DynamicObject::GetOffsetOfAuxSlots())/sizeof(Js::Var), (Js::PropertyIdIndexType)-1, PropertyKindSlotArray);
- instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func);
- this->AddInstr(instr, offset);
- newOpcode = newOpcode == Js::OpCode::StLocalObjSlot ? Js::OpCode::StSlot : Js::OpCode::StSlotChkUndecl;
- fieldSym = PropertySym::New(regOpnd->m_sym, slotId, (uint32)-1, (uint)-1, PropertyKindSlots, m_func);
- fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func);
- regOpnd = this->BuildSrcOpnd(regSlot);
- instr = IR::Instr::New(newOpcode, fieldOpnd, regOpnd, m_func);
- if (newOpcode == Js::OpCode::StSlotChkUndecl)
- {
- // ChkUndecl includes an implicit read of the destination. Communicate the liveness by using the destination in src2.
- instr->SetSrc2(fieldOpnd);
- }
- this->AddInstr(instr, offset);
- break;
- case Js::OpCode::LdEnvObj_ReuseLoc:
- reuseLoc = true;
- // fall through
- case Js::OpCode::LdEnvObj:
- fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, this->GetEnvReg(), slotId, (Js::PropertyIdIndexType)-1, PropertyKindSlotArray);
- regOpnd = this->BuildDstOpnd(regSlot, TyVar, false, reuseLoc);
- instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func);
- this->AddInstr(instr, offset);
- m_func->GetTopFunc()->AddFrameDisplayCheck(fieldOpnd);
- break;
- case Js::OpCode::NewStackScFunc:
- stackFuncPtrSym = this->EnsureStackFuncPtrSym();
- newOpcode = Js::OpCode::NewScFunc;
- // fall through
- case Js::OpCode::NewScFunc:
- goto NewScFuncCommon;
- case Js::OpCode::NewScGenFunc:
- newOpcode = Js::OpCode::NewScGenFunc;
- NewScFuncCommon:
- {
- IR::Opnd * functionBodySlotOpnd = IR::IntConstOpnd::New(slotId, TyInt32, m_func, true);
- IR::Opnd *environmentOpnd = GetEnvironmentOperand(offset);
- regOpnd = this->BuildDstOpnd(regSlot);
- if (stackFuncPtrSym)
- {
- IR::RegOpnd * dataOpnd = IR::RegOpnd::New(TyVar, m_func);
- instr = IR::Instr::New(Js::OpCode::NewScFuncData, dataOpnd, environmentOpnd,
- IR::RegOpnd::New(stackFuncPtrSym, TyVar, m_func), m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(newOpcode, regOpnd, functionBodySlotOpnd, dataOpnd, m_func);
- }
- else
- {
- instr = IR::Instr::New(newOpcode, regOpnd, functionBodySlotOpnd, environmentOpnd, m_func);
- }
- if (regOpnd->m_sym->m_isSingleDef)
- {
- regOpnd->m_sym->m_isSafeThis = true;
- regOpnd->m_sym->m_isNotNumber = true;
- }
- this->AddInstr(instr, offset);
- return;
- }
- default:
- Assert(0);
- }
- if(isLdSlotThatWasNotProfiled && DoBailOnNoProfile())
- {
- InsertBailOnNoProfile(instr);
- }
- }
- IR::Opnd*
- IRBuilder::GetEnvironmentOperand(uint32 offset)
- {
- StackSym* sym = nullptr;
- // The byte code doesn't refer directly to a closure environment. Get the implicit one
- // that's pointed to by the function body.
- if (m_func->DoStackFrameDisplay() && m_func->GetLocalFrameDisplaySym())
- {
- // Read the scope slot pointer back using the stack closure sym.
- IR::Opnd *fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, m_func->GetLocalFrameDisplaySym()->m_id, 0, (Js::PropertyIdIndexType) - 1, PropertyKindSlotArray);
- IR::RegOpnd *regOpnd = IR::RegOpnd::New(TyVar, m_func);
- this->AddInstr(
- IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func),
- offset);
- sym = regOpnd->m_sym;
- }
- else
- {
- SymID symID;
- symID = this->GetEnvRegForInnerFrameDisplay();
- Assert(symID != Js::Constants::NoRegister);
- if (IsLoopBody() && !RegIsConstant(symID))
- {
- this->EnsureLoopBodyLoadSlot(symID);
- }
- if (m_func->DoStackNestedFunc() && symID == GetEnvReg())
- {
- // Environment is not guaranteed constant during this function because it could become boxed during execution,
- // so load the environment every time you need it.
- IR::RegOpnd *regOpnd = IR::RegOpnd::New(TyVar, m_func);
- this->AddInstr(
- IR::Instr::New(Js::OpCode::LdEnv, regOpnd, m_func),
- offset);
- sym = regOpnd->m_sym;
- }
- else
- {
- sym = StackSym::FindOrCreate(symID, (Js::RegSlot)symID, m_func);
- }
- }
- return IR::RegOpnd::New(sym, TyVar, m_func);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementSlotI2(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementSlotI2<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- }
- BuildElementSlotI2(newOpcode, offset, layout->Value, layout->SlotIndex1, layout->SlotIndex2, Js::Constants::NoProfileId);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledElementSlotI2(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_ElementSlotI2<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- }
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- BuildElementSlotI2(newOpcode, offset, layout->Value, layout->SlotIndex1, layout->SlotIndex2, layout->profileId);
- }
- void
- IRBuilder::BuildElementSlotI2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot regSlot,
- int32 slotId1, int32 slotId2, Js::ProfileId profileId)
- {
- IR::RegOpnd *regOpnd;
- IR::SymOpnd *fieldOpnd;
- IR::Instr *instr;
- PropertySym *fieldSym;
- bool isLdSlotThatWasNotProfiled = false;
- switch (newOpcode)
- {
- case Js::OpCode::LdModuleSlot:
- case Js::OpCode::StModuleSlot:
- {
- Field(Js::Var)* moduleExportVarArrayAddr = Js::JavascriptOperators::OP_GetModuleExportSlotArrayAddress(slotId1, slotId2, m_func->GetScriptContextInfo());
- IR::AddrOpnd* addrOpnd = IR::AddrOpnd::New(moduleExportVarArrayAddr, IR::AddrOpndKindConstantAddress, m_func, true);
- regOpnd = IR::RegOpnd::New(TyVar, m_func);
- instr = IR::Instr::New(Js::OpCode::Ld_A, regOpnd, addrOpnd, m_func);
- this->AddInstr(instr, offset);
- fieldSym = PropertySym::New(regOpnd->m_sym, slotId2, (uint32)-1, (uint)-1, PropertyKindSlots, m_func);
- fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func);
-
- if (newOpcode == Js::OpCode::LdModuleSlot)
- {
- newOpcode = Js::OpCode::LdSlot;
- regOpnd = this->BuildDstOpnd(regSlot);
- instr = IR::Instr::New(newOpcode, regOpnd, fieldOpnd, m_func);
- }
- else
- {
- Assert(newOpcode == Js::OpCode::StModuleSlot);
- newOpcode = Js::OpCode::StSlot;
- regOpnd = this->BuildSrcOpnd(regSlot);
- instr = IR::Instr::New(newOpcode, fieldOpnd, regOpnd, m_func);
- }
- this->AddInstr(instr, offset);
- break;
- }
- case Js::OpCode::LdEnvSlot:
- case Js::OpCode::LdEnvObjSlot:
- case Js::OpCode::StEnvSlot:
- case Js::OpCode::StEnvSlotChkUndecl:
- case Js::OpCode::StEnvObjSlot:
- case Js::OpCode::StEnvObjSlotChkUndecl:
- fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, this->GetEnvReg(), slotId1, (Js::PropertyIdIndexType)-1, PropertyKindSlotArray);
- regOpnd = IR::RegOpnd::New(TyVar, m_func);
- instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func);
- this->AddInstr(instr, offset);
- switch (newOpcode)
- {
- case Js::OpCode::LdEnvObjSlot:
- case Js::OpCode::StEnvObjSlot:
- case Js::OpCode::StEnvObjSlotChkUndecl:
- m_func->GetTopFunc()->AddFrameDisplayCheck(fieldOpnd, (uint32)-1);
- fieldSym = PropertySym::New(regOpnd->m_sym, (Js::DynamicObject::GetOffsetOfAuxSlots())/sizeof(Js::Var),
- (uint32)-1, (uint)-1, PropertyKindSlotArray, m_func);
- fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func);
- regOpnd = IR::RegOpnd::New(TyVar, m_func);
- instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func);
- this->AddInstr(instr, offset);
- break;
- default:
- m_func->GetTopFunc()->AddFrameDisplayCheck(fieldOpnd, slotId2);
- break;
- }
- fieldSym = PropertySym::New(regOpnd->m_sym, slotId2, (uint32)-1, (uint)-1, PropertyKindSlots, m_func);
- fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func);
- switch (newOpcode)
- {
- case Js::OpCode::LdEnvSlot:
- case Js::OpCode::LdEnvObjSlot:
- newOpcode = Js::OpCode::LdSlot;
- regOpnd = this->BuildDstOpnd(regSlot);
- instr = nullptr;
- if (profileId != Js::Constants::NoProfileId)
- {
- instr = this->BuildProfiledSlotLoad(newOpcode, regOpnd, fieldOpnd, profileId, &isLdSlotThatWasNotProfiled);
- }
- if (!instr)
- {
- instr = IR::Instr::New(newOpcode, regOpnd, fieldOpnd, m_func);
- }
- break;
- default:
- newOpcode =
- newOpcode == Js::OpCode::StEnvSlot || newOpcode == Js::OpCode::StEnvObjSlot ? Js::OpCode::StSlot : Js::OpCode::StSlotChkUndecl;
- regOpnd = this->BuildSrcOpnd(regSlot);
- instr = IR::Instr::New(newOpcode, fieldOpnd, regOpnd, m_func);
- if (newOpcode == Js::OpCode::StSlotChkUndecl)
- {
- // ChkUndecl includes an implicit read of the destination. Communicate the liveness by using the destination in src2.
- instr->SetSrc2(fieldOpnd);
- }
- break;
- }
- this->AddInstr(instr, offset);
- if(isLdSlotThatWasNotProfiled && DoBailOnNoProfile())
- {
- InsertBailOnNoProfile(instr);
- }
- break;
- case Js::OpCode::StInnerObjSlot:
- case Js::OpCode::StInnerObjSlotChkUndecl:
- case Js::OpCode::StInnerSlot:
- case Js::OpCode::StInnerSlotChkUndecl:
- if ((uint)slotId1 >= m_func->GetJITFunctionBody()->GetInnerScopeCount())
- {
- Js::Throw::FatalInternalError();
- }
- regOpnd = this->BuildSrcOpnd(regSlot);
- slotId1 += this->m_func->GetJITFunctionBody()->GetFirstInnerScopeReg();
- if ((uint)slotId1 >= this->m_func->GetJITFunctionBody()->GetLocalsCount())
- {
- Js::Throw::FatalInternalError();
- }
- if (newOpcode == Js::OpCode::StInnerObjSlot || newOpcode == Js::OpCode::StInnerObjSlotChkUndecl)
- {
- IR::RegOpnd * slotOpnd = IR::RegOpnd::New(TyVar, m_func);
- fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, slotId1, (Js::DynamicObject::GetOffsetOfAuxSlots())/sizeof(Js::Var), (Js::PropertyIdIndexType)-1, PropertyKindSlotArray);
- instr = IR::Instr::New(Js::OpCode::LdSlotArr, slotOpnd, fieldOpnd, m_func);
- this->AddInstr(instr, offset);
- PropertySym *propertySym = PropertySym::New(slotOpnd->m_sym, slotId2, (uint32)-1, (uint)-1, PropertyKindSlots, m_func);
- fieldOpnd = IR::PropertySymOpnd::New(propertySym, (Js::CacheId)-1, TyVar, m_func);
- }
- else
- {
- fieldOpnd = this->BuildFieldOpnd(Js::OpCode::StSlot, slotId1, slotId2, (Js::PropertyIdIndexType)-1, PropertyKindSlots);
- }
- newOpcode =
- newOpcode == Js::OpCode::StInnerObjSlot || newOpcode == Js::OpCode::StInnerSlot ?
- Js::OpCode::StSlot : Js::OpCode::StSlotChkUndecl;
- instr = IR::Instr::New(newOpcode, fieldOpnd, regOpnd, m_func);
- if (newOpcode == Js::OpCode::StSlotChkUndecl)
- {
- // ChkUndecl includes an implicit read of the destination. Communicate the liveness by using the destination in src2.
- instr->SetSrc2(fieldOpnd);
- }
- this->AddInstr(instr, offset);
- break;
- case Js::OpCode::LdInnerSlot:
- case Js::OpCode::LdInnerObjSlot:
- if ((uint)slotId1 >= m_func->GetJITFunctionBody()->GetInnerScopeCount())
- {
- Js::Throw::FatalInternalError();
- }
- slotId1 += this->m_func->GetJITFunctionBody()->GetFirstInnerScopeReg();
- if ((uint)slotId1 >= this->m_func->GetJITFunctionBody()->GetLocalsCount())
- {
- Js::Throw::FatalInternalError();
- }
- if (newOpcode == Js::OpCode::LdInnerObjSlot)
- {
- IR::RegOpnd * slotOpnd = IR::RegOpnd::New(TyVar, m_func);
- fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, slotId1, (Js::DynamicObject::GetOffsetOfAuxSlots())/sizeof(Js::Var), (Js::PropertyIdIndexType)-1, PropertyKindSlotArray);
- instr = IR::Instr::New(Js::OpCode::LdSlotArr, slotOpnd, fieldOpnd, m_func);
- this->AddInstr(instr, offset);
- PropertySym *propertySym = PropertySym::New(slotOpnd->m_sym, slotId2, (uint32)-1, (uint)-1, PropertyKindSlots, m_func);
- fieldOpnd = IR::PropertySymOpnd::New(propertySym, (Js::CacheId)-1, TyVar, m_func);
- }
- else
- {
- fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlot, slotId1, slotId2, (Js::PropertyIdIndexType)-1, PropertyKindSlots);
- }
- regOpnd = this->BuildDstOpnd(regSlot);
- instr = IR::Instr::New(Js::OpCode::LdSlot, regOpnd, fieldOpnd, m_func);
- this->AddInstr(instr, offset);
- break;
- default:
- AssertMsg(false, "Unsupported opcode in BuildElementSlotI2");
- break;
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementSlotI3(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementSlotI3<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- this->DoClosureRegCheck(layout->Instance);
- this->DoClosureRegCheck(layout->HomeObj);
- }
- BuildElementSlotI3(newOpcode, offset, layout->Instance, layout->Value, layout->SlotIndex, layout->HomeObj, Js::Constants::NoProfileId);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledElementSlotI3(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_ElementSlotI3<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- this->DoClosureRegCheck(layout->Instance);
- this->DoClosureRegCheck(layout->HomeObj);
- }
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- BuildElementSlotI3(newOpcode, offset, layout->Instance, layout->Value, layout->SlotIndex, layout->HomeObj, layout->profileId);
- }
- void
- IRBuilder::BuildElementSlotI3(Js::OpCode newOpcode, uint32 offset, Js::RegSlot fieldRegSlot, Js::RegSlot regSlot,
- int32 slotId, Js::RegSlot homeObj, Js::ProfileId profileId)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- IR::Instr * instr;
- IR::RegOpnd * regOpnd;
- switch (newOpcode)
- {
- case Js::OpCode::NewInnerScFuncHomeObj:
- newOpcode = Js::OpCode::NewScFuncHomeObj;
- goto NewScFuncCommon;
- case Js::OpCode::NewInnerScGenFuncHomeObj:
- newOpcode = Js::OpCode::NewScGenFuncHomeObj;
- NewScFuncCommon:
- {
- Js::FunctionInfoPtrPtr infoRef = m_func->GetJITFunctionBody()->GetNestedFuncRef(slotId);
- IR::AddrOpnd * functionBodySlotOpnd = IR::AddrOpnd::New((Js::Var)infoRef, IR::AddrOpndKindDynamicMisc, m_func);
- IR::Opnd * environmentOpnd = this->BuildSrcOpnd(fieldRegSlot);
- IR::Opnd * homeObjOpnd = this->BuildSrcOpnd(homeObj);
- regOpnd = this->BuildDstOpnd(regSlot);
-
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), homeObjOpnd, m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), functionBodySlotOpnd, instr->GetDst(), m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), environmentOpnd, instr->GetDst(), m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(newOpcode, regOpnd, instr->GetDst(), m_func);
-
- if (regOpnd->m_sym->m_isSingleDef)
- {
- regOpnd->m_sym->m_isSafeThis = true;
- regOpnd->m_sym->m_isNotNumber = true;
- }
- this->AddInstr(instr, offset);
- return;
- }
- default:
- AssertMsg(UNREACHED, "Unknown ElementSlotI3 opcode");
- Fatal();
- }
- }
- IR::SymOpnd *
- IRBuilder::BuildLoopBodySlotOpnd(SymID symId)
- {
- Assert(!this->RegIsConstant((Js::RegSlot)symId));
- // Get the interpreter frame instance that was passed in.
- StackSym *loopParamSym = m_func->EnsureLoopParamSym();
- PropertySym * fieldSym = PropertySym::FindOrCreate(loopParamSym->m_id, (Js::PropertyId)(symId + this->m_loopBodyLocalsStartSlot), (uint32)-1, (uint)-1, PropertyKindLocalSlots, m_func);
- return IR::SymOpnd::New(fieldSym, TyVar, m_func);
- }
- void
- IRBuilder::EnsureLoopBodyLoadSlot(SymID symId, bool isCatchObjectSym)
- {
- // No need to emit LdSlot for a catch object. In fact, if we do, we might be loading an uninitialized value from the slot.
- if (isCatchObjectSym)
- {
- return;
- }
- StackSym * symDst = StackSym::FindOrCreate(symId, (Js::RegSlot)symId, m_func);
- if (symDst->m_isCatchObjectSym)
- {
- return;
- }
- AssertOrFailFast(symId < m_ldSlots->Length());
- if (this->m_ldSlots->TestAndSet(symId))
- {
- return;
- }
- IR::SymOpnd * fieldSymOpnd = this->BuildLoopBodySlotOpnd(symId);
- IR::RegOpnd * dstOpnd = IR::RegOpnd::New(symDst, TyVar, m_func);
- IR::Instr * ldSlotInstr;
- ValueType symValueType;
- if(m_func->GetWorkItem()->HasSymIdToValueTypeMap() && m_func->GetWorkItem()->TryGetValueType(symId, &symValueType))
- {
- ldSlotInstr = IR::ProfiledInstr::New(Js::OpCode::LdSlot, dstOpnd, fieldSymOpnd, m_func);
- ldSlotInstr->AsProfiledInstr()->u.FldInfo().valueType = symValueType;
- }
- else
- {
- ldSlotInstr = IR::Instr::New(Js::OpCode::LdSlot, dstOpnd, fieldSymOpnd, m_func);
- }
- m_func->m_headInstr->InsertAfter(ldSlotInstr);
- if (m_lastInstr == m_func->m_headInstr)
- {
- m_lastInstr = ldSlotInstr;
- }
- }
- void
- IRBuilder::SetLoopBodyStSlot(SymID symID, bool isCatchObjectSym)
- {
- if (this->m_func->HasTry() && !PHASE_OFF(Js::JITLoopBodyInTryCatchPhase, this->m_func))
- {
- // No need to emit StSlot for a catch object. In fact, if we do, we might be storing an uninitialized value to the slot.
- if (isCatchObjectSym)
- {
- return;
- }
- StackSym * dstSym = StackSym::FindOrCreate(symID, (Js::RegSlot)symID, m_func);
- Assert(dstSym);
- if (dstSym->m_isCatchObjectSym)
- {
- return;
- }
- }
- AssertOrFailFast(symID < m_stSlots->Length());
- this->m_stSlots->Set(symID);
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildElementCP
- ///
- /// Build IR instr for an ElementCP or ElementRootCP instruction.
- ///
- ///----------------------------------------------------------------------------
- IR::Instr *
- IRBuilder::BuildProfiledFieldLoad(Js::OpCode loadOp, IR::RegOpnd *dstOpnd, IR::SymOpnd *srcOpnd, Js::CacheId inlineCacheIndex, bool *pUnprofiled)
- {
- IR::Instr * instr = nullptr;
- // Prefer JitProfilingInstr if we're in simplejit
- if (m_func->DoSimpleJitDynamicProfile())
- {
- instr = IR::JitProfilingInstr::New(loadOp, dstOpnd, srcOpnd, m_func);
- }
- else if (this->m_func->HasProfileInfo())
- {
- instr = IR::ProfiledInstr::New(loadOp, dstOpnd, srcOpnd, m_func);
- instr->AsProfiledInstr()->u.FldInfo() = *(m_func->GetReadOnlyProfileInfo()->GetFldInfo(inlineCacheIndex));
- *pUnprofiled = !instr->AsProfiledInstr()->u.FldInfo().WasLdFldProfiled();
- dstOpnd->SetValueType(instr->AsProfiledInstr()->u.FldInfo().valueType);
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if(Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::DynamicProfilePhase))
- {
- const ValueType valueType(instr->AsProfiledInstr()->u.FldInfo().valueType);
- char valueTypeStr[VALUE_TYPE_MAX_STRING_SIZE];
- valueType.ToString(valueTypeStr);
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("TestTrace function %s (%s) ValueType = %i "), m_func->GetJITFunctionBody()->GetDisplayName(), m_func->GetDebugNumberSet(debugStringBuffer), valueTypeStr);
- instr->DumpTestTrace();
- }
- #endif
- }
- return instr;
- }
- Js::RegSlot IRBuilder::GetEnvRegForEvalCode() const
- {
- if (m_func->GetJITFunctionBody()->IsStrictMode() && m_func->GetJITFunctionBody()->IsGlobalFunc())
- {
- return m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg();
- }
- else
- {
- return GetEnvReg();
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementP(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementP<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- }
- BuildElementP(newOpcode, offset, layout->Value, layout->inlineCacheIndex);
- }
- void
- IRBuilder::BuildElementP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot regSlot, Js::CacheId inlineCacheIndex)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- IR::Instr * instr;
- IR::RegOpnd * regOpnd;
- IR::Opnd * srcOpnd;
- IR::SymOpnd * fieldSymOpnd;
- Js::PropertyId propertyId;
- bool isProfiled = OpCodeAttr::IsProfiledOp(newOpcode);
- bool isLdFldThatWasNotProfiled = false;
- if (isProfiled)
- {
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- }
- propertyId = this->m_func->GetJITFunctionBody()->GetPropertyIdFromCacheId(inlineCacheIndex);
- Js::RegSlot instance = this->GetEnvRegForEvalCode();
- bool reuseLoc = false;
- switch (newOpcode)
- {
- case Js::OpCode::LdLocalFld_ReuseLoc:
- reuseLoc = true;
- newOpcode = Js::OpCode::LdLocalFld;
- // fall through
- case Js::OpCode::LdLocalFld:
- if (m_func->GetLocalClosureSym()->HasByteCodeRegSlot())
- {
- IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset);
- byteCodeUse->SetNonOpndSymbol(m_func->GetLocalClosureSym()->m_id);
- this->AddInstr(byteCodeUse, offset);
- }
- newOpcode = Js::OpCode::LdFld;
- fieldSymOpnd = this->BuildFieldOpnd(newOpcode, m_func->GetJITFunctionBody()->GetLocalClosureReg(), propertyId, (Js::PropertyIdIndexType)-1, PropertyKindData, inlineCacheIndex);
- if (fieldSymOpnd->IsPropertySymOpnd())
- {
- fieldSymOpnd->AsPropertySymOpnd()->TryDisableRuntimePolymorphicCache();
- }
- regOpnd = this->BuildDstOpnd(regSlot, TyVar, false, reuseLoc);
- instr = nullptr;
- if (isProfiled)
- {
- instr = this->BuildProfiledFieldLoad(newOpcode, regOpnd, fieldSymOpnd, inlineCacheIndex, &isLdFldThatWasNotProfiled);
- }
- // If it hasn't been set yet
- if (!instr)
- {
- instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func);
- }
- break;
- case Js::OpCode::StLocalFld:
- if (m_func->GetLocalClosureSym()->HasByteCodeRegSlot())
- {
- IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset);
- byteCodeUse->SetNonOpndSymbol(m_func->GetLocalClosureSym()->m_id);
- this->AddInstr(byteCodeUse, offset);
- }
- fieldSymOpnd = this->BuildFieldOpnd(newOpcode, m_func->GetJITFunctionBody()->GetLocalClosureReg(), propertyId, (Js::PropertyIdIndexType)-1, PropertyKindData, inlineCacheIndex);
- if (fieldSymOpnd->IsPropertySymOpnd())
- {
- fieldSymOpnd->AsPropertySymOpnd()->TryDisableRuntimePolymorphicCache();
- }
- srcOpnd = this->BuildSrcOpnd(regSlot);
- newOpcode = Js::OpCode::StFld;
- goto stCommon;
- case Js::OpCode::InitLocalFld:
- case Js::OpCode::InitLocalLetFld:
- case Js::OpCode::InitUndeclLocalLetFld:
- case Js::OpCode::InitUndeclLocalConstFld:
- {
- if (m_func->GetLocalClosureSym()->HasByteCodeRegSlot())
- {
- IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset);
- byteCodeUse->SetNonOpndSymbol(m_func->GetLocalClosureSym()->m_id);
- this->AddInstr(byteCodeUse, offset);
- }
- fieldSymOpnd = this->BuildFieldOpnd(newOpcode, m_func->GetJITFunctionBody()->GetLocalClosureReg(), propertyId, (Js::PropertyIdIndexType)-1, PropertyKindData, inlineCacheIndex);
- // Store
- if (newOpcode == Js::OpCode::InitUndeclLocalLetFld)
- {
- srcOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndeclBlockVarAddr(), IR::AddrOpndKindDynamicVar, this->m_func, true);
- srcOpnd->SetValueType(ValueType::PrimitiveOrObject);
- newOpcode = Js::OpCode::InitLetFld;
- }
- else if (newOpcode == Js::OpCode::InitUndeclLocalConstFld)
- {
- srcOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndeclBlockVarAddr(), IR::AddrOpndKindDynamicVar, this->m_func, true);
- srcOpnd->SetValueType(ValueType::PrimitiveOrObject);
- newOpcode = Js::OpCode::InitConstFld;
- }
- else
- {
- srcOpnd = this->BuildSrcOpnd(regSlot);
- newOpcode = newOpcode == Js::OpCode::InitLocalFld ? Js::OpCode::InitFld : Js::OpCode::InitLetFld;
- }
- stCommon:
- instr = nullptr;
- if (isProfiled)
- {
- if (m_func->DoSimpleJitDynamicProfile())
- {
- instr = IR::JitProfilingInstr::New(newOpcode, fieldSymOpnd, srcOpnd, m_func);
- }
- else if (this->m_func->HasProfileInfo())
- {
- instr = IR::ProfiledInstr::New(newOpcode, fieldSymOpnd, srcOpnd, m_func);
- instr->AsProfiledInstr()->u.FldInfo() = *(m_func->GetReadOnlyProfileInfo()->GetFldInfo(inlineCacheIndex));
- }
- }
- // If it hasn't been set yet
- if (!instr)
- {
- instr = IR::Instr::New(newOpcode, fieldSymOpnd, srcOpnd, m_func);
- }
- break;
- }
- case Js::OpCode::ScopedLdFld:
- case Js::OpCode::ScopedLdFldForTypeOf:
- {
- Assert(!isProfiled);
- Assert(this->m_func->GetScriptContextInfo()->GetAddr() == this->m_func->GetTopFunc()->GetScriptContextInfo()->GetAddr());
- fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instance, propertyId, (Js::PropertyIdIndexType)-1, PropertyKindData, inlineCacheIndex);
- regOpnd = this->BuildDstOpnd(regSlot);
- instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func);
- break;
- }
- case Js::OpCode::ScopedStFld:
- case Js::OpCode::ConsoleScopedStFld:
- case Js::OpCode::ScopedStFldStrict:
- case Js::OpCode::ConsoleScopedStFldStrict:
- {
- Assert(!isProfiled);
- fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instance, propertyId, (Js::PropertyIdIndexType)-1, PropertyKindData, inlineCacheIndex);
- // Implicit root object as default instance
- IR::Opnd * instance2Opnd = this->BuildSrcOpnd(Js::FunctionBody::RootObjectRegSlot);
- regOpnd = this->BuildSrcOpnd(regSlot);
- instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, instance2Opnd, m_func);
- break;
- }
- default:
- AssertMsg(UNREACHED, "Unknown ElementP opcode");
- Fatal();
- }
- this->AddInstr(instr, offset);
- if(isLdFldThatWasNotProfiled && DoBailOnNoProfile())
- {
- InsertBailOnNoProfile(instr);
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementPIndexed(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementPIndexed<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- }
- switch (newOpcode)
- {
- case Js::OpCode::InitInnerFld:
- newOpcode = Js::OpCode::InitFld;
- goto initinnerfldcommon;
- case Js::OpCode::InitInnerLetFld:
- newOpcode = Js::OpCode::InitLetFld;
- // fall through
- initinnerfldcommon:
- case Js::OpCode::InitUndeclLetFld:
- case Js::OpCode::InitUndeclConstFld:
- BuildElementCP(newOpcode, offset, InnerScopeIndexToRegSlot(layout->scopeIndex), layout->Value, layout->inlineCacheIndex);
- break;
- default:
- AssertMsg(false, "Unknown opcode for ElementPIndexed");
- break;
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementCP(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementCP<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- this->DoClosureRegCheck(layout->Instance);
- }
- BuildElementCP(newOpcode, offset, layout->Instance, layout->Value, layout->inlineCacheIndex);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementRootCP(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementRootCP<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- }
- BuildElementCP(newOpcode, offset, Js::FunctionBody::RootObjectRegSlot, layout->Value, layout->inlineCacheIndex);
- }
- void
- IRBuilder::BuildElementCP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instance, Js::RegSlot regSlot, Js::CacheId inlineCacheIndex)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- Js::PropertyId propertyId;
- bool isProfiled = OpCodeAttr::IsProfiledOp(newOpcode);
- if (isProfiled)
- {
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- }
- propertyId = m_func->GetJITFunctionBody()->GetPropertyIdFromCacheId(inlineCacheIndex);
- IR::SymOpnd * fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instance, propertyId, (Js::PropertyIdIndexType)-1, PropertyKindData, inlineCacheIndex);
- IR::RegOpnd * regOpnd;
- IR::Instr * instr = nullptr;
- bool isLdFldThatWasNotProfiled = false;
- bool reuseLoc = false;
- switch (newOpcode)
- {
- case Js::OpCode::LdFld_ReuseLoc:
- reuseLoc = true;
- newOpcode = Js::OpCode::LdFld;
- // fall through
- case Js::OpCode::LdFldForTypeOf:
- case Js::OpCode::LdFld:
- case Js::OpCode::LdLen_A:
- if (fieldSymOpnd->IsPropertySymOpnd())
- {
- fieldSymOpnd->AsPropertySymOpnd()->TryDisableRuntimePolymorphicCache();
- }
- case Js::OpCode::LdFldForCallApplyTarget:
- case Js::OpCode::LdRootFldForTypeOf:
- case Js::OpCode::LdRootFld:
- case Js::OpCode::LdMethodFld:
- case Js::OpCode::LdRootMethodFld:
- case Js::OpCode::ScopedLdMethodFld:
- // Load
- // LdMethodFromFlags is backend only. Don't need to be added here.
- regOpnd = this->BuildDstOpnd(regSlot, TyVar, false, reuseLoc);
- if (isProfiled)
- {
- instr = this->BuildProfiledFieldLoad(newOpcode, regOpnd, fieldSymOpnd, inlineCacheIndex, &isLdFldThatWasNotProfiled);
- }
- // If it hasn't been set yet
- if (!instr)
- {
- instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func);
- }
- if (newOpcode == Js::OpCode::LdFld ||
- newOpcode == Js::OpCode::LdFldForCallApplyTarget ||
- newOpcode == Js::OpCode::LdMethodFld ||
- newOpcode == Js::OpCode::LdRootMethodFld ||
- newOpcode == Js::OpCode::ScopedLdMethodFld)
- {
- // Check whether we're loading (what appears to be) a built-in method.
- Js::BuiltinFunction builtInIndex = Js::BuiltinFunction::None;
- PropertySym *fieldSym = fieldSymOpnd->m_sym->AsPropertySym();
- this->CheckBuiltIn(fieldSym, &builtInIndex);
- regOpnd->m_sym->m_builtInIndex = builtInIndex;
- }
- break;
- case Js::OpCode::StFld:
- if (fieldSymOpnd->IsPropertySymOpnd())
- {
- fieldSymOpnd->AsPropertySymOpnd()->TryDisableRuntimePolymorphicCache();
- }
- case Js::OpCode::InitFld:
- case Js::OpCode::InitRootFld:
- case Js::OpCode::InitLetFld:
- case Js::OpCode::InitRootLetFld:
- case Js::OpCode::InitConstFld:
- case Js::OpCode::InitRootConstFld:
- case Js::OpCode::InitUndeclLetFld:
- case Js::OpCode::InitUndeclConstFld:
- case Js::OpCode::InitClassMember:
- case Js::OpCode::StRootFld:
- case Js::OpCode::StFldStrict:
- case Js::OpCode::StRootFldStrict:
- {
- IR::Opnd *srcOpnd;
- // Store
- if (newOpcode == Js::OpCode::InitUndeclLetFld)
- {
- srcOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndeclBlockVarAddr(), IR::AddrOpndKindDynamicVar, this->m_func, true);
- srcOpnd->SetValueType(ValueType::PrimitiveOrObject);
- newOpcode = Js::OpCode::InitLetFld;
- }
- else if (newOpcode == Js::OpCode::InitUndeclConstFld)
- {
- srcOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndeclBlockVarAddr(), IR::AddrOpndKindDynamicVar, this->m_func, true);
- srcOpnd->SetValueType(ValueType::PrimitiveOrObject);
- newOpcode = Js::OpCode::InitConstFld;
- }
- else
- {
- srcOpnd = this->BuildSrcOpnd(regSlot);
- }
- if (isProfiled)
- {
- if (m_func->DoSimpleJitDynamicProfile())
- {
- instr = IR::JitProfilingInstr::New(newOpcode, fieldSymOpnd, srcOpnd, m_func);
- }
- else if (this->m_func->HasProfileInfo())
- {
- instr = IR::ProfiledInstr::New(newOpcode, fieldSymOpnd, srcOpnd, m_func);
- instr->AsProfiledInstr()->u.FldInfo() = *(m_func->GetReadOnlyProfileInfo()->GetFldInfo(inlineCacheIndex));
- }
- }
- // If it hasn't been set yet
- if (!instr)
- {
- instr = IR::Instr::New(newOpcode, fieldSymOpnd, srcOpnd, m_func);
- }
- break;
- }
- default:
- AssertMsg(UNREACHED, "Unknown ElementCP opcode");
- Fatal();
- }
- this->AddInstr(instr, offset);
- if(isLdFldThatWasNotProfiled && DoBailOnNoProfile())
- {
- InsertBailOnNoProfile(instr);
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledElementCP(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_ElementCP<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- this->DoClosureRegCheck(layout->Instance);
- }
- BuildProfiledElementCP(newOpcode, offset, layout->Instance, layout->Value, layout->inlineCacheIndex, layout->profileId);
- }
- void
- IRBuilder::BuildProfiledElementCP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instance, Js::RegSlot regSlot, Js::CacheId inlineCacheIndex, Js::ProfileId profileId)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- Assert(newOpcode == Js::OpCode::LdLen_A);
- Js::PropertyId propertyId = m_func->GetJITFunctionBody()->GetPropertyIdFromCacheId(inlineCacheIndex);
- IR::SymOpnd * fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instance, propertyId, (Js::PropertyIdIndexType) - 1, PropertyKindData, inlineCacheIndex);
- IR::RegOpnd * dstOpnd = this->BuildDstOpnd(regSlot);
- bool isProfiled = (profileId != Js::Constants::NoProfileId);
- ValueType arrayType = ValueType::Uninitialized;
- const Js::LdLenInfo * ldLenInfo = nullptr;
- if (m_func->HasProfileInfo())
- {
- ldLenInfo = m_func->GetReadOnlyProfileInfo()->GetLdLenInfo(profileId);
- arrayType = (ldLenInfo->GetArrayType());
- if (arrayType.IsLikelyNativeArray() && !AllowNativeArrayProfileInfo())
- {
- // An opnd's value type will get replaced in the forward phase when it is not fixed. Store the array type in the ProfiledInstr.
- arrayType = arrayType.SetArrayTypeId(Js::TypeIds_Array);
- }
- fieldSymOpnd->SetValueType(arrayType);
- if (m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry())
- {
- isProfiled = false;
- }
- }
- else
- {
- isProfiled = false;
- }
-
- bool wasNotProfiled = false;
- IR::Instr *instr = nullptr;
- if (isProfiled)
- {
- instr = this->BuildProfiledFieldLoad(newOpcode, dstOpnd, fieldSymOpnd, inlineCacheIndex, &wasNotProfiled);
- }
- if (instr == nullptr)
- {
- instr = IR::Instr::New(newOpcode, dstOpnd, fieldSymOpnd, m_func);
- }
- else if (instr->IsJitProfilingInstr())
- {
- instr->AsJitProfilingInstr()->profileId = profileId;
- }
- else if (instr->IsProfiledInstr())
- {
- instr->AsProfiledInstr()->u.LdLenInfo() = *ldLenInfo;
- instr->AsProfiledInstr()->u.LdLenInfo().arrayType = arrayType;
- }
- this->AddInstr(instr, offset);
- if (wasNotProfiled && DoBailOnNoProfile())
- {
- InsertBailOnNoProfile(instr);
- }
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildElementC2
- ///
- /// Build IR instr for an ElementC2 instruction.
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementScopedC2(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementScopedC2<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- this->DoClosureRegCheck(layout->Value2);
- }
- BuildElementScopedC2(newOpcode, offset, layout->Value2, layout->Value, layout->PropertyIdIndex);
- }
- void
- IRBuilder::BuildElementScopedC2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot value2Slot,
- Js::RegSlot regSlot, Js::PropertyIdIndexType propertyIdIndex)
- {
- IR::Instr * instr = nullptr;
- Js::PropertyId propertyId;
- IR::RegOpnd * regOpnd;
- IR::RegOpnd * value2Opnd;
- IR::SymOpnd * fieldSymOpnd;
- Js::RegSlot instanceSlot = this->GetEnvRegForEvalCode();
- switch (newOpcode)
- {
- case Js::OpCode::ScopedLdInst:
- {
- propertyId = m_func->GetJITFunctionBody()->GetReferencedPropertyId(propertyIdIndex);
- fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instanceSlot, propertyId, propertyIdIndex, PropertyKindData);
- regOpnd = this->BuildDstOpnd(regSlot);
- value2Opnd = this->BuildDstOpnd(value2Slot);
- IR::Instr *newInstr = IR::Instr::New(Js::OpCode::Unused, value2Opnd, m_func);
- this->AddInstr(newInstr, offset);
- instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, newInstr->GetDst(), m_func);
- this->AddInstr(instr, offset);
- }
- break;
- default:
- AssertMsg(UNREACHED, "Unknown ElementC2 opcode");
- Fatal();
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementC2(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementC2<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- this->DoClosureRegCheck(layout->Value2);
- this->DoClosureRegCheck(layout->Instance);
- }
- BuildElementC2(newOpcode, offset, layout->Instance, layout->Value2, layout->Value, layout->PropertyIdIndex);
- }
- void
- IRBuilder::BuildElementC2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instanceSlot, Js::RegSlot value2Slot,
- Js::RegSlot regSlot, Js::PropertyIdIndexType propertyIdIndex)
- {
- IR::Instr * instr = nullptr;
- Js::PropertyId propertyId;
- IR::RegOpnd * regOpnd;
- IR::RegOpnd * value2Opnd;
- IR::SymOpnd * fieldSymOpnd;
- switch (newOpcode)
- {
- case Js::OpCode::ProfiledLdSuperFld:
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- // fall-through
- case Js::OpCode::LdSuperFld:
- {
- propertyId = m_func->GetJITFunctionBody()->GetPropertyIdFromCacheId(propertyIdIndex);
- fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instanceSlot, propertyId, (Js::PropertyIdIndexType) - 1, PropertyKindData, propertyIdIndex);
- if (fieldSymOpnd->IsPropertySymOpnd())
- {
- fieldSymOpnd->AsPropertySymOpnd()->TryDisableRuntimePolymorphicCache();
- }
- value2Opnd = this->BuildSrcOpnd(value2Slot);
- regOpnd = this->BuildDstOpnd(regSlot);
- instr = IR::ProfiledInstr::New(newOpcode, regOpnd, fieldSymOpnd, value2Opnd, m_func);
- instr->AsProfiledInstr()->u.FldInfo() = *(m_func->GetReadOnlyProfileInfo()->GetFldInfo(propertyIdIndex));
- this->AddInstr(instr, offset);
- }
- break;
- case Js::OpCode::ProfiledStSuperFld:
- case Js::OpCode::ProfiledStSuperFldStrict:
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- // fall-through
- case Js::OpCode::StSuperFld:
- case Js::OpCode::StSuperFldStrict:
- {
- propertyId = m_func->GetJITFunctionBody()->GetPropertyIdFromCacheId(propertyIdIndex);
- fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instanceSlot, propertyId, (Js::PropertyIdIndexType) - 1, PropertyKindData, propertyIdIndex);
- if (fieldSymOpnd->IsPropertySymOpnd())
- {
- fieldSymOpnd->AsPropertySymOpnd()->TryDisableRuntimePolymorphicCache();
- }
- regOpnd = this->BuildSrcOpnd(regSlot);
- value2Opnd = this->BuildSrcOpnd(value2Slot);
- instr = IR::ProfiledInstr::New(newOpcode, fieldSymOpnd, regOpnd, value2Opnd, m_func);
- instr->AsProfiledInstr()->u.FldInfo() = *(m_func->GetReadOnlyProfileInfo()->GetFldInfo(propertyIdIndex));
- this->AddInstr(instr, offset);
- break;
- }
- default:
- AssertMsg(UNREACHED, "Unknown ElementC2 opcode");
- Fatal();
- }
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildElementU
- ///
- /// Build IR instr for an ElementU or ElementRootU instruction.
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementU(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementU<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Instance);
- }
- BuildElementU(newOpcode, offset, layout->Instance, layout->PropertyIdIndex);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementRootU(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementRootU<SizePolicy>>();
- BuildElementU(newOpcode, offset, Js::FunctionBody::RootObjectRegSlot, layout->PropertyIdIndex);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementScopedU(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementScopedU<SizePolicy>>();
- BuildElementU(newOpcode, offset, GetEnvReg(), layout->PropertyIdIndex);
- }
- void
- IRBuilder::BuildElementU(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instance, Js::PropertyIdIndexType propertyIdIndex)
- {
- IR::Instr * instr;
- IR::RegOpnd * regOpnd;
- IR::SymOpnd * fieldSymOpnd;
- Js::PropertyId propertyId = m_func->GetJITFunctionBody()->GetReferencedPropertyId(propertyIdIndex);
- bool reuseLoc = false;
- switch (newOpcode)
- {
- case Js::OpCode::LdLocalElemUndef:
- if (m_func->GetLocalClosureSym()->HasByteCodeRegSlot())
- {
- IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset);
- byteCodeUse->SetNonOpndSymbol(m_func->GetLocalClosureSym()->m_id);
- this->AddInstr(byteCodeUse, offset);
- }
- instance = m_func->GetJITFunctionBody()->GetLocalClosureReg();
- newOpcode = Js::OpCode::LdElemUndef;
- fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instance, propertyId, propertyIdIndex, PropertyKindData);
- instr = IR::Instr::New(newOpcode, fieldSymOpnd, m_func);
- break;
- // fall through
- case Js::OpCode::LdElemUndefScoped:
- {
- // Store
- PropertyKind propertyKind = PropertyKindData;
- fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instance, propertyId, propertyIdIndex, propertyKind);
- // Implicit root object as default instance
- regOpnd = this->BuildSrcOpnd(Js::FunctionBody::RootObjectRegSlot);
- instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, m_func);
- break;
- }
- case Js::OpCode::ClearAttributes:
- {
- instr = IR::Instr::New(newOpcode, m_func);
- IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(instance);
- IR::IntConstOpnd * src2Opnd = IR::IntConstOpnd::New(propertyId, TyInt32, m_func);
- instr->SetSrc1(src1Opnd);
- instr->SetSrc2(src2Opnd);
- break;
- }
- case Js::OpCode::StLocalFuncExpr:
- fieldSymOpnd = this->BuildFieldOpnd(newOpcode, m_func->GetJITFunctionBody()->GetLocalClosureReg(), propertyId, propertyIdIndex, PropertyKindData);
- regOpnd = this->BuildSrcOpnd(instance);
- newOpcode = Js::OpCode::StFuncExpr;
- instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, m_func);
- break;
- case Js::OpCode::DeleteLocalFld_ReuseLoc:
- newOpcode = Js::OpCode::DeleteLocalFld;
- reuseLoc = true;
- // fall through
- case Js::OpCode::DeleteLocalFld:
- newOpcode = Js::OpCode::DeleteFld;
- fieldSymOpnd = BuildFieldOpnd(newOpcode, m_func->GetJITFunctionBody()->GetLocalClosureReg(), propertyId, propertyIdIndex, PropertyKindData);
- regOpnd = BuildDstOpnd(instance, TyVar, false, reuseLoc);
- instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func);
- break;
- default:
- {
- fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instance, propertyId, propertyIdIndex, PropertyKindData);
- instr = IR::Instr::New(newOpcode, fieldSymOpnd, m_func);
- break;
- }
- }
- this->AddInstr(instr, offset);
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildAuxiliary
- ///
- /// Build IR instr for an Auxiliary instruction.
- ///
- ///----------------------------------------------------------------------------
- void
- IRBuilder::BuildAuxNoReg(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode));
- IR::Instr * instr;
- const unaligned Js::OpLayoutAuxNoReg *auxInsn = m_jnReader.AuxNoReg();
- switch (newOpcode)
- {
- case Js::OpCode::InitCachedFuncs:
- {
- IR::Opnd *src1Opnd = this->BuildSrcOpnd(m_func->GetJITFunctionBody()->GetLocalClosureReg());
- IR::Opnd *src2Opnd = this->BuildSrcOpnd(m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg());
- IR::Opnd *src3Opnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxFuncInfoArray, auxInsn->Offset);
- instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src3Opnd, m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src2Opnd, instr->GetDst(), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src1Opnd, instr->GetDst(), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- IR::HelperCallOpnd *helperOpnd;
- helperOpnd = IR::HelperCallOpnd::New(IR::HelperOP_InitCachedFuncs, this->m_func);
- src2Opnd = instr->GetDst();
- instr = IR::Instr::New(Js::OpCode::CallHelper, m_func);
- instr->SetSrc1(helperOpnd);
- instr->SetSrc2(src2Opnd);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- return;
- }
- default:
- {
- AssertMsg(UNREACHED, "Unknown AuxNoReg opcode");
- Fatal();
- break;
- }
- }
- }
- void
- IRBuilder::BuildAuxiliary(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode));
- const unaligned Js::OpLayoutAuxiliary *auxInsn = m_jnReader.Auxiliary();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(auxInsn->R0);
- }
- IR::Instr *instr;
- switch (newOpcode)
- {
- case Js::OpCode::NewScObjectLiteral:
- {
- int literalObjectId = auxInsn->C1;
- IR::RegOpnd * dstOpnd;
- IR::Opnd* srcOpnd;
- Js::RegSlot dstRegSlot = auxInsn->R0;
- // The property ID array needs to be both relocatable and available (so we can
- // get the slot capacity), so we need to just pass the offset to lower and let
- // lower take it from there...
- srcOpnd = IR::IntConstOpnd::New(auxInsn->Offset, TyUint32, m_func);
- dstOpnd = this->BuildDstOpnd(dstRegSlot);
- dstOpnd->SetValueType(ValueType::GetObject(ObjectType::UninitializedObject));
- instr = IR::Instr::New(newOpcode, dstOpnd, srcOpnd, m_func);
- // Because we're going to be making decisions based off the value, we have to defer
- // this until we get to lowering.
- instr->SetSrc2(IR::IntConstOpnd::New(literalObjectId, TyUint32, m_func));
- if (dstOpnd->m_sym->m_isSingleDef)
- {
- dstOpnd->m_sym->m_isSafeThis = true;
- }
- break;
- }
- case Js::OpCode::LdPropIds:
- {
- IR::RegOpnd * dstOpnd;
- IR::Opnd* srcOpnd;
- Js::RegSlot dstRegSlot = auxInsn->R0;
- srcOpnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxPropertyIdArray, auxInsn->Offset);
- dstOpnd = this->BuildDstOpnd(dstRegSlot);
- instr = IR::Instr::New(newOpcode, dstOpnd, srcOpnd, m_func);
- if (dstOpnd->m_sym->m_isSingleDef)
- {
- dstOpnd->m_sym->m_isNotNumber = true;
- }
- break;
- }
- case Js::OpCode::NewScIntArray:
- {
- IR::RegOpnd* dstOpnd;
- IR::Opnd* src1Opnd;
- src1Opnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxIntArray, auxInsn->Offset);
- dstOpnd = this->BuildDstOpnd(auxInsn->R0);
- instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, m_func);
- const Js::TypeId arrayTypeId = m_func->IsJitInDebugMode() ? Js::TypeIds_Array : Js::TypeIds_NativeIntArray;
- dstOpnd->SetValueType(
- ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(true).SetArrayTypeId(arrayTypeId));
- dstOpnd->SetValueTypeFixed();
- break;
- }
- case Js::OpCode::NewScFltArray:
- {
- IR::RegOpnd* dstOpnd;
- IR::Opnd* src1Opnd;
- src1Opnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxFloatArray, auxInsn->Offset);
- dstOpnd = this->BuildDstOpnd(auxInsn->R0);
- instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, m_func);
- const Js::TypeId arrayTypeId = m_func->IsJitInDebugMode() ? Js::TypeIds_Array : Js::TypeIds_NativeFloatArray;
- dstOpnd->SetValueType(
- ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(true).SetArrayTypeId(arrayTypeId));
- dstOpnd->SetValueTypeFixed();
- break;
- }
- case Js::OpCode::StArrSegItem_A:
- {
- IR::RegOpnd* src1Opnd;
- IR::Opnd* src2Opnd;
- src1Opnd = this->BuildSrcOpnd(auxInsn->R0);
- src2Opnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxVarsArray, auxInsn->Offset);
- instr = IR::Instr::New(newOpcode, m_func);
- instr->SetSrc1(src1Opnd);
- instr->SetSrc2(src2Opnd);
- break;
- }
- case Js::OpCode::NewScObject_A:
- {
- const Js::VarArrayVarCount *vars = (Js::VarArrayVarCount *)m_func->GetJITFunctionBody()->ReadFromAuxContextData(auxInsn->Offset);
- int count = Js::TaggedInt::ToInt32(vars->count);
- StackSym * symDst;
- IR::SymOpnd * dstOpnd;
- IR::Opnd * src1Opnd;
- //
- // PUSH all the parameters on the auxiliary context, to the stack
- //
- for (int i=0;i<count; i++)
- {
- m_argsOnStack++;
- symDst = m_func->m_symTable->GetArgSlotSym((uint16)(i + 2));
- if (symDst == nullptr || (uint16)(i + 2) != (i + 2))
- {
- AssertMsg(UNREACHED, "Arg count too big...");
- Fatal();
- }
- dstOpnd = IR::SymOpnd::New(symDst, TyVar, m_func);
- src1Opnd = IR::AddrOpnd::New(vars->elements[i], IR::AddrOpndKindDynamicVar, this->m_func, true);
- instr = IR::Instr::New(Js::OpCode::ArgOut_A, dstOpnd, src1Opnd, m_func);
- this->AddInstr(instr, offset);
- m_argStack->Push(instr);
- }
- BuildCallI_Helper(Js::OpCode::NewScObject, offset, (Js::RegSlot)auxInsn->R0, (Js::RegSlot)auxInsn->C1, (Js::ArgSlot)count+1, Js::Constants::NoProfileId);
- return;
- }
- default:
- {
- AssertMsg(UNREACHED, "Unknown Auxiliary opcode");
- Fatal();
- break;
- }
- }
- this->AddInstr(instr, offset);
- }
- void
- IRBuilder::BuildProfiledAuxiliary(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode));
- const unaligned Js::OpLayoutDynamicProfile<Js::OpLayoutAuxiliary> *auxInsn = m_jnReader.ProfiledAuxiliary();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(auxInsn->R0);
- }
- switch (newOpcode)
- {
- case Js::OpCode::ProfiledNewScIntArray:
- {
- Js::ProfileId profileId = static_cast<Js::ProfileId>(auxInsn->profileId);
- IR::RegOpnd* dstOpnd;
- IR::Opnd* src1Opnd;
- src1Opnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxIntArray, auxInsn->Offset);
- dstOpnd = this->BuildDstOpnd(auxInsn->R0);
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- IR::Instr *instr;
- Js::ArrayCallSiteInfo *arrayInfo = nullptr;
- Js::TypeId arrayTypeId = Js::TypeIds_Array;
- if (m_func->DoSimpleJitDynamicProfile())
- {
- instr = IR::JitProfilingInstr::New(newOpcode, dstOpnd, src1Opnd, m_func);
- instr->AsJitProfilingInstr()->profileId = profileId;
- }
- else if (m_func->HasArrayInfo())
- {
- instr = IR::ProfiledInstr::New(newOpcode, dstOpnd, src1Opnd, m_func);
- instr->AsProfiledInstr()->u.profileId = profileId;
- arrayInfo = m_func->GetReadOnlyProfileInfo()->GetArrayCallSiteInfo(profileId);
- if (arrayInfo && !m_func->IsJitInDebugMode())
- {
- if (arrayInfo->IsNativeIntArray())
- {
- arrayTypeId = Js::TypeIds_NativeIntArray;
- }
- else if (arrayInfo->IsNativeFloatArray())
- {
- arrayTypeId = Js::TypeIds_NativeFloatArray;
- }
- }
- }
- else
- {
- instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, m_func);
- }
- ValueType dstValueType(
- ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(true).SetArrayTypeId(arrayTypeId));
- if (dstValueType.IsLikelyNativeArray())
- {
- dstOpnd->SetValueType(dstValueType.ToLikely());
- }
- else
- {
- dstOpnd->SetValueType(dstValueType);
- dstOpnd->SetValueTypeFixed();
- }
- StackSym *dstSym = dstOpnd->AsRegOpnd()->m_sym;
- if (dstSym->m_isSingleDef)
- {
- dstSym->m_isSafeThis = true;
- dstSym->m_isNotNumber = true;
- }
- this->AddInstr(instr, offset);
- break;
- }
- case Js::OpCode::ProfiledNewScFltArray:
- {
- Js::ProfileId profileId = static_cast<Js::ProfileId>(auxInsn->profileId);
- IR::RegOpnd* dstOpnd;
- IR::Opnd* src1Opnd;
- src1Opnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxFloatArray, auxInsn->Offset);
- dstOpnd = this->BuildDstOpnd(auxInsn->R0);
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- IR::Instr *instr;
- Js::ArrayCallSiteInfo *arrayInfo = nullptr;
- if (m_func->DoSimpleJitDynamicProfile())
- {
- instr = IR::JitProfilingInstr::New(newOpcode, dstOpnd, src1Opnd, m_func);
- instr->AsJitProfilingInstr()->profileId = profileId;
- // Keep arrayInfo null because we aren't using profile data in profiling simplejit
- }
- else
- {
- instr = IR::ProfiledInstr::New(newOpcode, dstOpnd, src1Opnd, m_func);
- instr->AsProfiledInstr()->u.profileId = profileId;
- if (m_func->HasArrayInfo()) {
- arrayInfo = m_func->GetReadOnlyProfileInfo()->GetArrayCallSiteInfo(profileId);
- }
- }
- Js::TypeId arrayTypeId;
- if (arrayInfo && arrayInfo->IsNativeFloatArray())
- {
- arrayTypeId = Js::TypeIds_NativeFloatArray;
- }
- else
- {
- arrayTypeId = Js::TypeIds_Array;
- }
- ValueType dstValueType(
- ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(true).SetArrayTypeId(arrayTypeId));
- if (dstValueType.IsLikelyNativeArray())
- {
- dstOpnd->SetValueType(dstValueType.ToLikely());
- }
- else
- {
- dstOpnd->SetValueType(dstValueType);
- dstOpnd->SetValueTypeFixed();
- }
- StackSym *dstSym = dstOpnd->AsRegOpnd()->m_sym;
- if (dstSym->m_isSingleDef)
- {
- dstSym->m_isSafeThis = true;
- dstSym->m_isNotNumber = true;
- }
- this->AddInstr(instr, offset);
- break;
- }
- default:
- {
- AssertMsg(UNREACHED, "Unknown Auxiliary opcode");
- Fatal();
- break;
- }
- }
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildReg2Aux
- ///
- /// Build IR instr for a Reg2Aux instruction.
- ///
- ///----------------------------------------------------------------------------
- void IRBuilder::BuildInitCachedScope(int auxOffset, int offset)
- {
- IR::Instr * instr;
- IR::RegOpnd * dstOpnd;
- IR::RegOpnd * src1Opnd;
- IR::AddrOpnd * src2Opnd;
- IR::Opnd* src3Opnd;
- IR::Opnd* formalsAreLetDeclOpnd;
- src2Opnd = IR::AddrOpnd::New(m_func->GetJITFunctionBody()->GetFormalsPropIdArrayAddr(), IR::AddrOpndKindDynamicMisc, m_func);
- Js::PropertyIdArray * propIds = m_func->GetJITFunctionBody()->GetFormalsPropIdArray();
- src3Opnd = this->BuildAuxObjectLiteralTypeRefOpnd(Js::ActivationObjectEx::GetLiteralObjectRef(propIds));
- dstOpnd = this->BuildDstOpnd(m_func->GetJITFunctionBody()->GetLocalClosureReg());
- formalsAreLetDeclOpnd = IR::IntConstOpnd::New(propIds->hasNonSimpleParams, TyUint8, m_func);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), formalsAreLetDeclOpnd, m_func);
- this->AddInstr(instr, offset);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src3Opnd, instr->GetDst(), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src2Opnd, instr->GetDst(), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- // Disable opt that normally gets disabled when we see LdFuncExpr in the byte code.
- m_func->DisableCanDoInlineArgOpt();
- src1Opnd = IR::RegOpnd::New(TyVar, m_func);
- IR::Instr * instrLdFuncExpr = IR::Instr::New(Js::OpCode::LdFuncExpr, src1Opnd, m_func);
- this->AddInstr(instrLdFuncExpr, Js::Constants::NoByteCodeOffset);
- instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src1Opnd, instr->GetDst(), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- instr = IR::Instr::New(Js::OpCode::InitCachedScope, dstOpnd, instr->GetDst(), m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- }
- void
- IRBuilder::BuildReg2Aux(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode));
- const unaligned Js::OpLayoutReg2Aux *auxInsn = m_jnReader.Reg2Aux();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(auxInsn->R0);
- this->DoClosureRegCheck(auxInsn->R1);
- }
- IR::Instr *instr;
- switch (newOpcode)
- {
- case Js::OpCode::SpreadArrayLiteral:
- {
- IR::RegOpnd * dstOpnd;
- IR::RegOpnd * src1Opnd;
- IR::Opnd* src2Opnd;
- Js::RegSlot dstRegSlot = auxInsn->R0;
- Js::RegSlot srcRegSlot = auxInsn->R1;
- src1Opnd = this->BuildSrcOpnd(srcRegSlot);
- src2Opnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxIntArray, auxInsn->Offset);
- dstOpnd = this->BuildDstOpnd(dstRegSlot);
- instr = IR::Instr::New(Js::OpCode::SpreadArrayLiteral, dstOpnd, src1Opnd, src2Opnd, m_func);
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- if (dstOpnd->m_sym->m_isSingleDef)
- {
- dstOpnd->m_sym->m_isNotNumber = true;
- }
- break;
- }
- default:
- {
- AssertMsg(UNREACHED, "Unknown Reg2Aux opcode");
- Fatal();
- break;
- }
- }
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildElementI
- ///
- /// Build IR instr for an ElementI instruction.
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementI(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementI<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- this->DoClosureRegCheck(layout->Instance);
- this->DoClosureRegCheck(layout->Element);
- }
- BuildElementI(newOpcode, offset, layout->Instance, layout->Element, layout->Value, Js::Constants::NoProfileId);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledElementI(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_ElementI<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- this->DoClosureRegCheck(layout->Instance);
- this->DoClosureRegCheck(layout->Element);
- }
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- BuildElementI(newOpcode, offset, layout->Instance, layout->Element, layout->Value, layout->profileId);
- }
- void
- IRBuilder::BuildElementI(Js::OpCode newOpcode, uint32 offset, Js::RegSlot baseRegSlot, Js::RegSlot indexRegSlot,
- Js::RegSlot regSlot, Js::ProfileId profileId)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- ValueType arrayType;
- const Js::LdElemInfo *ldElemInfo = nullptr;
- const Js::StElemInfo *stElemInfo = nullptr;
- bool isProfiledLoad = false;
- bool isProfiledStore = false;
- bool isProfiledInstr = (profileId != Js::Constants::NoProfileId);
- bool isLdElemOrStElemThatWasNotProfiled = false;
- if (isProfiledInstr)
- {
- switch (newOpcode)
- {
- case Js::OpCode::LdElemI_A:
- if (!DoLoadInstructionArrayProfileInfo())
- {
- break;
- }
- ldElemInfo = this->m_func->GetReadOnlyProfileInfo()->GetLdElemInfo(profileId);
- arrayType = ldElemInfo->GetArrayType();
- isLdElemOrStElemThatWasNotProfiled = !ldElemInfo->WasProfiled();
- isProfiledLoad = true;
- break;
- case Js::OpCode::StElemI_A:
- case Js::OpCode::StElemI_A_Strict:
- if (!DoLoadInstructionArrayProfileInfo())
- {
- break;
- }
- isProfiledStore = true;
- stElemInfo = this->m_func->GetReadOnlyProfileInfo()->GetStElemInfo(profileId);
- arrayType = stElemInfo->GetArrayType();
- isLdElemOrStElemThatWasNotProfiled = !stElemInfo->WasProfiled();
- break;
- }
- }
- IR::Instr * instr;
- IR::RegOpnd * regOpnd;
- IR::IndirOpnd * indirOpnd;
- indirOpnd = this->BuildIndirOpnd(this->BuildSrcOpnd(baseRegSlot), this->BuildSrcOpnd(indexRegSlot));
- if (isProfiledLoad || isProfiledStore)
- {
- if(arrayType.IsLikelyNativeArray() && !AllowNativeArrayProfileInfo())
- {
- arrayType = arrayType.SetArrayTypeId(Js::TypeIds_Array);
- // An opnd's value type will get replaced in the forward phase when it is not fixed. Store the array type in the
- // ProfiledInstr.
- if(isProfiledLoad)
- {
- Js::LdElemInfo *const newLdElemInfo = JitAnew(m_func->m_alloc, Js::LdElemInfo, *ldElemInfo);
- newLdElemInfo->arrayType = arrayType;
- ldElemInfo = newLdElemInfo;
- }
- else
- {
- Js::StElemInfo *const newStElemInfo = JitAnew(m_func->m_alloc, Js::StElemInfo, *stElemInfo);
- newStElemInfo->arrayType = arrayType;
- stElemInfo = newStElemInfo;
- }
- }
- indirOpnd->GetBaseOpnd()->SetValueType(arrayType);
- if (m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry())
- {
- isProfiledLoad = false;
- isProfiledStore = false;
- }
- }
- switch (newOpcode)
- {
- case Js::OpCode::LdMethodElem:
- case Js::OpCode::LdElemI_A:
- case Js::OpCode::DeleteElemI_A:
- case Js::OpCode::DeleteElemIStrict_A:
- case Js::OpCode::TypeofElem:
- {
- // Evaluate to register
- regOpnd = this->BuildDstOpnd(regSlot);
- if (m_func->DoSimpleJitDynamicProfile() && isProfiledInstr)
- {
- instr = IR::JitProfilingInstr::New(newOpcode, regOpnd, indirOpnd, m_func);
- instr->AsJitProfilingInstr()->profileId = profileId;
- }
- else if (isProfiledLoad)
- {
- instr = IR::ProfiledInstr::New(newOpcode, regOpnd, indirOpnd, m_func);
- instr->AsProfiledInstr()->u.ldElemInfo = ldElemInfo;
- }
- else
- {
- instr = IR::Instr::New(newOpcode, regOpnd, indirOpnd, m_func);
- }
- break;
- }
- case Js::OpCode::StElemI_A:
- case Js::OpCode::StElemI_A_Strict:
- {
- // Store
- regOpnd = this->BuildSrcOpnd(regSlot);
- if (m_func->DoSimpleJitDynamicProfile() && isProfiledInstr)
- {
- instr = IR::JitProfilingInstr::New(newOpcode, indirOpnd, regOpnd, m_func);
- instr->AsJitProfilingInstr()->profileId = profileId;
- }
- else if (isProfiledStore)
- {
- instr = IR::ProfiledInstr::New(newOpcode, indirOpnd, regOpnd, m_func);
- instr->AsProfiledInstr()->u.stElemInfo = stElemInfo;
- }
- else
- {
- instr = IR::Instr::New(newOpcode, indirOpnd, regOpnd, m_func);
- }
- break;
- }
- case Js::OpCode::InitSetElemI:
- case Js::OpCode::InitGetElemI:
- case Js::OpCode::InitComputedProperty:
- case Js::OpCode::InitClassMemberComputedName:
- case Js::OpCode::InitClassMemberGetComputedName:
- case Js::OpCode::InitClassMemberSetComputedName:
- {
- regOpnd = this->BuildSrcOpnd(regSlot);
- instr = IR::Instr::New(newOpcode, indirOpnd, regOpnd, m_func);
- break;
- }
- default:
- AssertMsg(false, "Unknown ElementI opcode");
- return;
- }
- this->AddInstr(instr, offset);
- if(isLdElemOrStElemThatWasNotProfiled && DoBailOnNoProfile())
- {
- InsertBailOnNoProfile(instr);
- }
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildElementUnsigned1
- ///
- /// Build IR instr for an ElementUnsigned1 instruction.
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildElementUnsigned1(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementUnsigned1<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Value);
- this->DoClosureRegCheck(layout->Instance);
- }
- BuildElementUnsigned1(newOpcode, offset, layout->Instance, layout->Element, layout->Value);
- }
- void
- IRBuilder::BuildElementUnsigned1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot baseRegSlot, uint32 index, Js::RegSlot regSlot)
- {
- // This is an array-style access with a constant (integer) index.
- // Embed the index in the indir opnd as a constant offset.
- IR::Instr * instr;
- const bool simpleJit = m_func->DoSimpleJitDynamicProfile();
- IR::RegOpnd * regOpnd;
- IR::IndirOpnd * indirOpnd;
- IR::RegOpnd * baseOpnd;
- Js::OpCode opcode;
- switch (newOpcode)
- {
- case Js::OpCode::StArrItemI_CI4:
- {
- baseOpnd = this->BuildSrcOpnd(baseRegSlot);
- // This instruction must not create missing values in the array
- baseOpnd->SetValueType(
- ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(false).SetArrayTypeId(Js::TypeIds_Array));
- baseOpnd->SetValueTypeFixed();
- // In the case of simplejit, we won't know the exact type of array used until run time. Due to this,
- // we must use the specialized version of StElemC in Lowering.
- opcode = simpleJit ? Js::OpCode::StElemC : Js::OpCode::StElemI_A;
- break;
- }
- case Js::OpCode::StArrItemC_CI4:
- {
- baseOpnd = IR::RegOpnd::New(TyVar, m_func);
- // Insert LdArrHead as the next instr and clear the offset to avoid duplication.
- IR::RegOpnd *const arrayOpnd = this->BuildSrcOpnd(baseRegSlot);
- // This instruction must not create missing values in the array
- arrayOpnd->SetValueType(
- ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(false).SetArrayTypeId(Js::TypeIds_Array));
- arrayOpnd->SetValueTypeFixed();
- this->AddInstr(IR::Instr::New(Js::OpCode::LdArrHead, baseOpnd, arrayOpnd, m_func), offset);
- offset = Js::Constants::NoByteCodeOffset;
- opcode = Js::OpCode::StArrSegElemC;
- break;
- }
- case Js::OpCode::StArrSegItem_CI4:
- {
- baseOpnd = this->BuildSrcOpnd(baseRegSlot, TyVar);
- // This instruction must not create missing values in the array
- opcode = Js::OpCode::StArrSegElemC;
- break;
- }
- case Js::OpCode::StArrInlineItem_CI4:
- {
- baseOpnd = this->BuildSrcOpnd(baseRegSlot);
- IR::Opnd *defOpnd = baseOpnd->m_sym->m_instrDef ? baseOpnd->m_sym->m_instrDef->GetDst() : nullptr;
- if (!defOpnd)
- {
- // The array sym may be multi-def because of oddness in the renumbering of temps -- for instance,
- // if there's a loop increment expression whose result is unused (ExprGen only, probably).
- FOREACH_INSTR_BACKWARD(tmpInstr, m_func->m_exitInstr->m_prev)
- {
- if (tmpInstr->GetDst())
- {
- if (tmpInstr->GetDst()->IsEqual(baseOpnd))
- {
- defOpnd = tmpInstr->GetDst();
- break;
- }
- else if (tmpInstr->m_opcode == Js::OpCode::StElemC &&
- tmpInstr->GetDst()->AsIndirOpnd()->GetBaseOpnd()->IsEqual(baseOpnd))
- {
- defOpnd = tmpInstr->GetDst()->AsIndirOpnd()->GetBaseOpnd();
- break;
- }
- }
- }
- NEXT_INSTR_BACKWARD;
- }
- AnalysisAssert(defOpnd);
- // This instruction must not create missing values in the array
- baseOpnd->SetValueType(defOpnd->GetValueType());
- opcode = Js::OpCode::StElemC;
- break;
- }
- default:
- AssertMsg(false, "Unknown ElementUnsigned1 opcode");
- return;
- }
- indirOpnd = this->BuildIndirOpnd(baseOpnd, index);
- regOpnd = this->BuildSrcOpnd(regSlot);
- if (simpleJit)
- {
- instr = IR::JitProfilingInstr::New(opcode, indirOpnd, regOpnd, m_func);
- }
- else if(opcode == Js::OpCode::StElemC && !baseOpnd->GetValueType().IsUninitialized())
- {
- // An opnd's value type will get replaced in the forward phase when it is not fixed. Store the array type in the
- // ProfiledInstr.
- IR::ProfiledInstr *const profiledInstr = IR::ProfiledInstr::New(opcode, indirOpnd, regOpnd, m_func);
- Js::StElemInfo *const stElemInfo = JitAnew(m_func->m_alloc, Js::StElemInfo);
- stElemInfo->arrayType = baseOpnd->GetValueType();
- profiledInstr->u.stElemInfo = stElemInfo;
- instr = profiledInstr;
- }
- else
- {
- instr = IR::Instr::New(opcode, indirOpnd, regOpnd, m_func);
- }
- this->AddInstr(instr, offset);
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildArgIn
- ///
- /// Build IR instr for an ArgIn instruction.
- ///
- ///----------------------------------------------------------------------------
- void
- IRBuilder::BuildArgIn0(uint32 offset, Js::RegSlot dstRegSlot)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(Js::OpCode::ArgIn0));
- BuildArgIn(offset, dstRegSlot, 0);
- }
- void
- IRBuilder::BuildArgIn(uint32 offset, Js::RegSlot dstRegSlot, uint16 argument)
- {
- IR::Instr * instr;
- IR::SymOpnd * srcOpnd;
- IR::RegOpnd * dstOpnd;
- StackSym * symSrc = StackSym::NewParamSlotSym(argument + 1, m_func);
- this->m_func->SetArgOffset(symSrc, (argument + LowererMD::GetFormalParamOffset()) * MachPtr);
- srcOpnd = IR::SymOpnd::New(symSrc, TyVar, m_func);
- dstOpnd = this->BuildDstOpnd(dstRegSlot);
- if (!this->m_func->IsLoopBody() && this->m_func->HasProfileInfo())
- {
- // Skip "this" pointer; "this" profile data is captured by ProfiledLdThis.
- // Subtract 1 to skip "this" pointer, subtract 1 again to get the index to index into profileData->parameterInfo.
- int paramSlotIndex = symSrc->GetParamSlotNum() - 2;
- if (paramSlotIndex >= 0)
- {
- ValueType profiledValueType;
- profiledValueType = this->m_func->GetReadOnlyProfileInfo()->GetParameterInfo(static_cast<Js::ArgSlot>(paramSlotIndex));
- dstOpnd->SetValueType(profiledValueType);
- }
- }
- instr = IR::Instr::New(Js::OpCode::ArgIn_A, dstOpnd, srcOpnd, m_func);
- this->AddInstr(instr, offset);
- }
- void
- IRBuilder::BuildArgInRest()
- {
- IR::RegOpnd * dstOpnd = this->BuildDstOpnd(m_func->GetJITFunctionBody()->GetRestParamRegSlot());
- IR::Instr *instr = IR::Instr::New(Js::OpCode::ArgIn_Rest, dstOpnd, m_func);
- this->AddInstr(instr, (uint32)-1);
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildArg
- ///
- /// Build IR instr for an ArgOut instruction.
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildArgNoSrc(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ArgNoSrc<SizePolicy>>();
- BuildArg(Js::OpCode::ArgOut_A, offset, layout->Arg, this->GetEnvRegForInnerFrameDisplay());
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildArg(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Arg<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Reg);
- }
- BuildArg(newOpcode, offset, layout->Arg, layout->Reg);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledArg(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_Arg<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Reg);
- }
- newOpcode = Js::OpCode::ArgOut_A;
- BuildArg(newOpcode, offset, layout->Arg, layout->Reg);
- }
- void
- IRBuilder::BuildArg(Js::OpCode newOpcode, uint32 offset, Js::ArgSlot argument, Js::RegSlot srcRegSlot)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- IR::Instr * instr;
- IRType type = TyVar;
- if (newOpcode == Js::OpCode::ArgOut_ANonVar)
- {
- newOpcode = Js::OpCode::ArgOut_A;
- type = TyMachPtr;
- }
- m_argsOnStack++;
- StackSym * symDst;
- Assert(argument < USHRT_MAX);
- symDst = m_func->m_symTable->GetArgSlotSym((uint16)(argument+1));
- if (symDst == nullptr || (uint16)(argument + 1) != (argument + 1))
- {
- AssertMsg(UNREACHED, "Arg count too big...");
- Fatal();
- }
- IR::SymOpnd * dstOpnd = IR::SymOpnd::New(symDst, type, m_func);
- IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(srcRegSlot, type);
- instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, m_func);
- this->AddInstr(instr, offset);
- m_argStack->Push(instr);
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildStartCall
- ///
- /// Build IR instr for a StartCall instruction.
- ///
- ///----------------------------------------------------------------------------
- void
- IRBuilder::BuildStartCall(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(newOpcode == Js::OpCode::StartCall);
- const unaligned Js::OpLayoutStartCall * regLayout = m_jnReader.StartCall();
- Js::ArgSlot ArgCount = regLayout->ArgCount;
- IR::Instr * instr;
- IR::RegOpnd * dstOpnd;
- // Dst of StartCall is always r0... Let's give it a new dst such that it can
- // be singleDef.
- dstOpnd = IR::RegOpnd::New(TyVar, m_func);
- #if DBG
- m_callsOnStack++;
- #endif
- IntConstType value = ArgCount;
- IR::IntConstOpnd * srcOpnd;
- srcOpnd = IR::IntConstOpnd::New(value, TyInt32, m_func);
- instr = IR::Instr::New(newOpcode, dstOpnd, srcOpnd, m_func);
- this->AddInstr(instr, offset);
- // Keep a stack of arg instructions such that we can link them up once we see
- // the call that consumes them.
- m_argStack->Push(instr);
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildCallI
- ///
- /// Build IR instr for a CallI instruction.
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildCallI(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(Js::OpCodeUtil::IsCallOp(newOpcode) || newOpcode == Js::OpCode::NewScObject || newOpcode == Js::OpCode::NewScObjArray);
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_CallI<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Return);
- this->DoClosureRegCheck(layout->Function);
- }
- BuildCallI_Helper(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, Js::Constants::NoProfileId);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildCallIFlags(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(Js::OpCodeUtil::IsCallOp(newOpcode) || newOpcode == Js::OpCode::NewScObject || newOpcode == Js::OpCode::NewScObjArray);
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_CallIFlags<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Return);
- this->DoClosureRegCheck(layout->Function);
- }
- IR::Instr* instr = BuildCallI_Helper(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, Js::Constants::NoProfileId, layout->callFlags);
- Assert(instr->m_opcode == Js::OpCode::CallIFlags);
- if (instr->m_opcode == Js::OpCode::CallIFlags)
- {
- instr->m_opcode =
- layout->callFlags == (Js::CallFlags::CallFlags_NewTarget | Js::CallFlags::CallFlags_New | Js::CallFlags::CallFlags_ExtraArg) ? Js::OpCode::CallINewTargetNew :
- layout->callFlags == Js::CallFlags::CallFlags_New ? Js::OpCode::CallINew :
- instr->m_opcode;
- }
- }
- void IRBuilder::BuildLdSpreadIndices(uint32 offset, uint32 spreadAuxOffset)
- {
- // Link up the LdSpreadIndices instr to be the first in the arg chain. This will allow us to find it in Lowerer easier.
- IR::Opnd *auxArg = this->BuildAuxArrayOpnd(AuxArrayValue::AuxIntArray, spreadAuxOffset);
- IR::Instr *instr = IR::Instr::New(Js::OpCode::LdSpreadIndices, m_func);
- instr->SetSrc1(auxArg);
- // Create the link to the first arg.
- Js::RegSlot lastArg = m_argStack->Head()->GetDst()->AsSymOpnd()->GetStackSym()->GetArgSlotNum();
- instr->SetDst(IR::SymOpnd::New(m_func->m_symTable->GetArgSlotSym((uint16) (lastArg + 1)), TyVar, m_func));
- this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
- m_argStack->Push(instr);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildCallIExtended(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_CallIExtended<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Return);
- this->DoClosureRegCheck(layout->Function);
- }
- BuildCallIExtended(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->Options, layout->SpreadAuxOffset);
- }
- IR::Instr*
- IRBuilder::BuildCallIExtended(Js::OpCode newOpcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
- Js::ArgSlot argCount, Js::CallIExtendedOptions options, uint32 spreadAuxOffset, Js::CallFlags flags)
- {
- if (options & Js::CallIExtended_SpreadArgs)
- {
- BuildLdSpreadIndices(offset, spreadAuxOffset);
- }
- return BuildCallI_Helper(newOpcode, offset, returnValue, function, argCount, Js::Constants::NoProfileId, flags);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildCallIWithICIndex(Js::OpCode newOpcode, uint32 offset)
- {
- AssertMsg(false, "NYI");
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildCallIFlagsWithICIndex(Js::OpCode newOpcode, uint32 offset)
- {
- AssertMsg(false, "NYI");
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildCallIExtendedFlags(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_CallIExtendedFlags<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Return);
- this->DoClosureRegCheck(layout->Function);
- }
- IR::Instr* instr = BuildCallIExtended(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->Options, layout->SpreadAuxOffset, layout->callFlags);
- Assert(instr->m_opcode == Js::OpCode::CallIExtendedFlags);
- if (instr->m_opcode == Js::OpCode::CallIExtendedFlags)
- {
- instr->m_opcode =
- layout->callFlags == Js::CallFlags::CallFlags_ExtraArg ? Js::OpCode::CallIEval :
- layout->callFlags == Js::CallFlags::CallFlags_New ? Js::OpCode::CallIExtendedNew :
- layout->callFlags == (Js::CallFlags::CallFlags_New | Js::CallFlags::CallFlags_ExtraArg | Js::CallFlags::CallFlags_NewTarget) ? Js::OpCode::CallIExtendedNewTargetNew :
- instr->m_opcode;
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildCallIExtendedWithICIndex(Js::OpCode newOpcode, uint32 offset)
- {
- AssertMsg(false, "NYI");
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildCallIExtendedFlagsWithICIndex(Js::OpCode newOpcode, uint32 offset)
- {
- AssertMsg(false, "NYI");
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledCallIFlagsWithICIndex(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOpWithICIndex(newOpcode) || Js::OpCodeUtil::IsProfiledCallOpWithICIndex(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIFlagsWithICIndex<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Return);
- this->DoClosureRegCheck(layout->Function);
- }
- IR::Instr* instr = BuildProfiledCallIWithICIndex(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->inlineCacheIndex);
- Assert(instr->m_opcode == Js::OpCode::CallIFlags);
- if (instr->m_opcode == Js::OpCode::CallIFlags)
- {
- instr->m_opcode =
- layout->callFlags == (Js::CallFlags::CallFlags_NewTarget | Js::CallFlags::CallFlags_New | Js::CallFlags::CallFlags_ExtraArg) ? Js::OpCode::CallINewTargetNew :
- layout->callFlags == Js::CallFlags::CallFlags_New ? Js::OpCode::CallINew :
- instr->m_opcode;
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledCallIWithICIndex(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOpWithICIndex(newOpcode) || Js::OpCodeUtil::IsProfiledCallOpWithICIndex(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Return);
- this->DoClosureRegCheck(layout->Function);
- }
- BuildProfiledCallIWithICIndex(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->inlineCacheIndex);
- }
- IR::Instr*
- IRBuilder::BuildProfiledCallIWithICIndex(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
- Js::ArgSlot argCount, Js::ProfileId profileId, Js::InlineCacheIndex inlineCacheIndex)
- {
- return BuildProfiledCallI(opcode, offset, returnValue, function, argCount, profileId, Js::CallFlags_None, inlineCacheIndex);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledCallIExtendedFlags(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOp(newOpcode) || Js::OpCodeUtil::IsProfiledCallOp(newOpcode)
- || Js::OpCodeUtil::IsProfiledReturnTypeCallOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIExtendedFlags<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Return);
- this->DoClosureRegCheck(layout->Function);
- }
- IR::Instr* instr = BuildProfiledCallIExtended(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->Options, layout->SpreadAuxOffset, layout->callFlags);
- Assert(instr->m_opcode == Js::OpCode::CallIExtendedFlags);
- if (instr->m_opcode == Js::OpCode::CallIExtendedFlags)
- {
- instr->m_opcode =
- layout->callFlags == Js::CallFlags::CallFlags_ExtraArg ? Js::OpCode::CallIEval :
- layout->callFlags == Js::CallFlags::CallFlags_New ? Js::OpCode::CallIExtendedNew :
- layout->callFlags == (Js::CallFlags::CallFlags_New | Js::CallFlags::CallFlags_ExtraArg | Js::CallFlags::CallFlags_NewTarget) ? Js::OpCode::CallIExtendedNewTargetNew :
- instr->m_opcode;
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledCallIExtendedWithICIndex(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOpWithICIndex(newOpcode) || Js::OpCodeUtil::IsProfiledCallOpWithICIndex(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIExtendedWithICIndex<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Return);
- this->DoClosureRegCheck(layout->Function);
- }
- BuildProfiledCallIExtendedWithICIndex(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->Options, layout->SpreadAuxOffset);
- }
- void
- IRBuilder::BuildProfiledCallIExtendedWithICIndex(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
- Js::ArgSlot argCount, Js::ProfileId profileId, Js::CallIExtendedOptions options, uint32 spreadAuxOffset)
- {
- BuildProfiledCallIExtended(opcode, offset, returnValue, function, argCount, profileId, options, spreadAuxOffset);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledCallIExtendedFlagsWithICIndex(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOpWithICIndex(newOpcode) || Js::OpCodeUtil::IsProfiledCallOpWithICIndex(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIExtendedFlagsWithICIndex<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Return);
- this->DoClosureRegCheck(layout->Function);
- }
- IR::Instr* instr = BuildProfiledCallIExtended(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->Options, layout->SpreadAuxOffset);
- Assert(instr->m_opcode == Js::OpCode::CallIExtendedFlags);
- if (instr->m_opcode == Js::OpCode::CallIExtendedFlags)
- {
- instr->m_opcode =
- layout->callFlags == Js::CallFlags::CallFlags_ExtraArg ? Js::OpCode::CallIEval :
- layout->callFlags == Js::CallFlags::CallFlags_New ? Js::OpCode::CallIExtendedNew :
- layout->callFlags == (Js::CallFlags::CallFlags_New | Js::CallFlags::CallFlags_ExtraArg | Js::CallFlags::CallFlags_NewTarget) ? Js::OpCode::CallIExtendedNewTargetNew :
- instr->m_opcode;
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledCallI(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOp(newOpcode) || Js::OpCodeUtil::IsProfiledCallOp(newOpcode)
- || Js::OpCodeUtil::IsProfiledReturnTypeCallOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallI<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Return);
- this->DoClosureRegCheck(layout->Function);
- }
- BuildProfiledCallI(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledCallIFlags(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOp(newOpcode) || Js::OpCodeUtil::IsProfiledCallOp(newOpcode)
- || Js::OpCodeUtil::IsProfiledReturnTypeCallOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIFlags<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Return);
- this->DoClosureRegCheck(layout->Function);
- }
- IR::Instr* instr = BuildProfiledCallI(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->callFlags);
- Assert(instr->m_opcode == Js::OpCode::CallIFlags);
- if (instr->m_opcode == Js::OpCode::CallIFlags)
- {
- instr->m_opcode =
- layout->callFlags == (Js::CallFlags::CallFlags_NewTarget | Js::CallFlags::CallFlags_New | Js::CallFlags::CallFlags_ExtraArg) ? Js::OpCode::CallINewTargetNew :
- layout->callFlags == Js::CallFlags::CallFlags_New ? Js::OpCode::CallINew :
- instr->m_opcode;
- }
- }
- IR::Instr *
- IRBuilder::BuildProfiledCallI(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
- Js::ArgSlot argCount, Js::ProfileId profileId, Js::CallFlags flags, Js::InlineCacheIndex inlineCacheIndex)
- {
- Js::OpCode newOpcode;
- ValueType returnType;
- bool isProtectedByNoProfileBailout = false;
- if (opcode == Js::OpCode::ProfiledNewScObject || opcode == Js::OpCode::ProfiledNewScObjectWithICIndex
- || opcode == Js::OpCode::ProfiledNewScObjectSpread)
- {
- newOpcode = opcode;
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode);
- Assert(newOpcode == Js::OpCode::NewScObject || newOpcode == Js::OpCode::NewScObjectSpread);
- if (!this->m_func->HasProfileInfo())
- {
- returnType = ValueType::GetObject(ObjectType::UninitializedObject);
- }
- else
- {
- // If we have profile data, make use of it
- returnType = this->m_func->GetReadOnlyProfileInfo()->GetReturnType(opcode, profileId);
- }
- }
- else
- {
- if (this->m_func->HasProfileInfo())
- {
- returnType = this->m_func->GetReadOnlyProfileInfo()->GetReturnType(opcode, profileId);
- }
- if (opcode < Js::OpCode::ProfiledReturnTypeCallI)
- {
- newOpcode = Js::OpCodeUtil::ConvertProfiledCallOpToNonProfiled(opcode);
- if(DoBailOnNoProfile())
- {
- if(this->m_func->GetWorkItem()->GetJITTimeInfo())
- {
- const FunctionJITTimeInfo *inlinerData = this->m_func->GetWorkItem()->GetJITTimeInfo();
- if (!(this->IsLoopBody() && PHASE_OFF(Js::InlineInJitLoopBodyPhase, this->m_func))
- && inlinerData && inlinerData->GetInlineesBV())
- {
- AssertOrFailFast(profileId < inlinerData->GetInlineesBV()->Length());
- if (!inlinerData->GetInlineesBV()->Test(profileId)
- #if DBG
- || (PHASE_STRESS(Js::BailOnNoProfilePhase, this->m_func->GetTopFunc())
- && (CONFIG_FLAG(SkipFuncCountForBailOnNoProfile) < 0
- || this->m_func->m_callSiteCount >= (uint)CONFIG_FLAG(SkipFuncCountForBailOnNoProfile)))
- #endif
- )
- {
- this->InsertBailOnNoProfile(offset);
- isProtectedByNoProfileBailout = true;
- }
- }
- if (!isProtectedByNoProfileBailout)
- {
- this->callTreeHasSomeProfileInfo = true;
- }
- }
- #if DBG
- this->m_func->m_callSiteCount++;
- #endif
- }
- }
- else
- {
- // Changing this opcode into a non ReturnTypeCall* opcode is done in BuildCallI_Helper
- newOpcode = opcode;
- }
- }
- IR::Instr * callInstr = BuildCallI_Helper(newOpcode, offset, returnValue, function, argCount, profileId, flags, inlineCacheIndex);
- callInstr->isCallInstrProtectedByNoProfileBailout = isProtectedByNoProfileBailout;
- if (callInstr->GetDst() && (callInstr->GetDst()->GetValueType().IsUninitialized() || callInstr->GetDst()->GetValueType() == ValueType::UninitializedObject))
- {
- callInstr->GetDst()->SetValueType(returnType);
- }
- return callInstr;
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiledCallIExtended(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOp(newOpcode) || Js::OpCodeUtil::IsProfiledCallOp(newOpcode)
- || Js::OpCodeUtil::IsProfiledReturnTypeCallOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIExtended<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Return);
- this->DoClosureRegCheck(layout->Function);
- }
- BuildProfiledCallIExtended(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->Options, layout->SpreadAuxOffset);
- }
- IR::Instr *
- IRBuilder::BuildProfiledCallIExtended(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
- Js::ArgSlot argCount, Js::ProfileId profileId, Js::CallIExtendedOptions options,
- uint32 spreadAuxOffset, Js::CallFlags flags)
- {
- if (options & Js::CallIExtended_SpreadArgs)
- {
- BuildLdSpreadIndices(offset, spreadAuxOffset);
- }
- return BuildProfiledCallI(opcode, offset, returnValue, function, argCount, profileId, flags);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiled2CallI(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile2<Js::OpLayoutT_CallI<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Return);
- this->DoClosureRegCheck(layout->Function);
- }
- BuildProfiled2CallI(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->profileId2);
- }
- void
- IRBuilder::BuildProfiled2CallI(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
- Js::ArgSlot argCount, Js::ProfileId profileId, Js::ProfileId profileId2)
- {
- Assert(opcode == Js::OpCode::ProfiledNewScObjArray || opcode == Js::OpCode::ProfiledNewScObjArraySpread);
- Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(opcode);
- Js::OpCode useOpcode = opcode;
- // We either want to provide the array profile id (profileId2) to the native array creation or the call profileid (profileId)
- // to the call to NewScObject
- Js::ProfileId useProfileId = profileId2;
- Js::TypeId arrayTypeId = Js::TypeIds_Array;
- if (returnValue != Js::Constants::NoRegister)
- {
- Js::ArrayCallSiteInfo *arrayCallSiteInfo = nullptr;
- if (m_func->HasArrayInfo())
- {
- arrayCallSiteInfo = m_func->GetReadOnlyProfileInfo()->GetArrayCallSiteInfo(profileId2);
- }
- if (arrayCallSiteInfo && !m_func->IsJitInDebugMode())
- {
- if (arrayCallSiteInfo->IsNativeIntArray())
- {
- arrayTypeId = Js::TypeIds_NativeIntArray;
- }
- else if (arrayCallSiteInfo->IsNativeFloatArray())
- {
- arrayTypeId = Js::TypeIds_NativeFloatArray;
- }
- }
- else
- {
- useOpcode = (opcode == Js::OpCode::NewScObjArraySpread) ? Js::OpCode::NewScObjectSpread : Js::OpCode::NewScObject;
- useProfileId = profileId;
- }
- }
- else
- {
- useOpcode = (opcode == Js::OpCode::NewScObjArraySpread) ? Js::OpCode::NewScObjectSpread : Js::OpCode::NewScObject;
- useProfileId = profileId;
- }
- IR::Instr * callInstr = BuildCallI_Helper(useOpcode, offset, returnValue, function, argCount, useProfileId);
- if (callInstr->GetDst())
- {
- callInstr->GetDst()->SetValueType(
- ValueType::GetObject(ObjectType::Array).ToLikely().SetHasNoMissingValues(true).SetArrayTypeId(arrayTypeId));
- }
- if (callInstr->IsJitProfilingInstr())
- {
- // If we happened to decide in BuildCallI_Helper that this should be a jit profiling instr, then save the fact that it is
- // a "new Array(args, ...)" call and also save the array profile id (profileId2)
- callInstr->AsJitProfilingInstr()->isNewArray = true;
- callInstr->AsJitProfilingInstr()->arrayProfileId = profileId2;
- // Double check that this profileId made it to the JitProfilingInstr like we expect it to.
- Assert(callInstr->AsJitProfilingInstr()->profileId == profileId);
- }
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildProfiled2CallIExtended(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile2<Js::OpLayoutT_CallIExtended<SizePolicy>>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->Return);
- this->DoClosureRegCheck(layout->Function);
- }
- BuildProfiled2CallIExtended(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->profileId2, layout->Options, layout->SpreadAuxOffset);
- }
- void
- IRBuilder::BuildProfiled2CallIExtended(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
- Js::ArgSlot argCount, Js::ProfileId profileId, Js::ProfileId profileId2,
- Js::CallIExtendedOptions options, uint32 spreadAuxOffset)
- {
- if (options & Js::CallIExtended_SpreadArgs)
- {
- BuildLdSpreadIndices(offset, spreadAuxOffset);
- }
- BuildProfiled2CallI(opcode, offset, returnValue, function, argCount, profileId, profileId2);
- }
- IR::Instr *
- IRBuilder::BuildCallI_Helper(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot Src1RegSlot, Js::ArgSlot ArgCount, Js::ProfileId profileId, Js::CallFlags flags, Js::InlineCacheIndex inlineCacheIndex)
- {
- IR::Instr * instr;
- IR::RegOpnd * dstOpnd;
- IR::RegOpnd * src1Opnd;
- StackSym * symDst;
- src1Opnd = this->BuildSrcOpnd(Src1RegSlot);
- if (dstRegSlot == Js::Constants::NoRegister)
- {
- dstOpnd = nullptr;
- symDst = nullptr;
- }
- else
- {
- dstOpnd = this->BuildDstOpnd(dstRegSlot);
- symDst = dstOpnd->m_sym;
- }
- const bool jitProfiling = m_func->DoSimpleJitDynamicProfile();
- bool profiledReturn = false;
- if (Js::OpCodeUtil::IsProfiledReturnTypeCallOp(newOpcode))
- {
- profiledReturn = true;
- newOpcode = Js::OpCodeUtil::ConvertProfiledReturnTypeCallOpToNonProfiled(newOpcode);
- // If we're profiling in the jitted code we want to propagate the profileId
- // If we're using profile data instead of collecting it, we don't want to
- // use the profile data from a return type call (this was previously done in IRBuilder::BuildProfiledCallI)
- if (!jitProfiling)
- {
- profileId = Js::Constants::NoProfileId;
- }
- }
- if (profileId != Js::Constants::NoProfileId)
- {
- if (jitProfiling)
- {
- // In SimpleJit we want this call to be a profiled call after being jitted
- instr = IR::JitProfilingInstr::New(newOpcode, dstOpnd, src1Opnd, m_func);
- instr->AsJitProfilingInstr()->profileId = profileId;
- instr->AsJitProfilingInstr()->isProfiledReturnCall = profiledReturn;
- instr->AsJitProfilingInstr()->inlineCacheIndex = inlineCacheIndex;
- }
- else
- {
- instr = IR::ProfiledInstr::New(newOpcode, dstOpnd, src1Opnd, m_func);
- instr->AsProfiledInstr()->u.profileId = profileId;
- }
- }
- else
- {
- instr = IR::Instr::New(newOpcode, m_func);
- instr->SetSrc1(src1Opnd);
- if (dstOpnd != nullptr)
- {
- instr->SetDst(dstOpnd);
- }
- }
- if (dstOpnd && newOpcode == Js::OpCode::NewScObject)
- {
- dstOpnd->SetValueType(ValueType::GetObject(ObjectType::UninitializedObject));
- }
- if (symDst && symDst->m_isSingleDef)
- {
- switch (instr->m_opcode)
- {
- case Js::OpCode::NewScObject:
- case Js::OpCode::NewScObjectSpread:
- case Js::OpCode::NewScObjectLiteral:
- case Js::OpCode::NewScObjArray:
- case Js::OpCode::NewScObjArraySpread:
- symDst->m_isSafeThis = true;
- symDst->m_isNotNumber = true;
- break;
- }
- }
- this->AddInstr(instr, offset);
- this->BuildCallCommon(instr, symDst, ArgCount, flags);
- return instr;
- }
- void
- IRBuilder::BuildCallCommon(IR::Instr * instr, StackSym * symDst, Js::ArgSlot argCount, Js::CallFlags flags)
- {
- Js::OpCode newOpcode = instr->m_opcode;
- IR::Instr * argInstr = nullptr;
- IR::Instr * prevInstr = instr;
- #if DBG
- int count = 0;
- #endif
- // Link all the args of this call by creating a def/use chain through the src2.
- AssertOrFailFast(!m_argStack->Empty());
- for (argInstr = m_argStack->Pop();
- argInstr && !m_argStack->Empty() && argInstr->m_opcode != Js::OpCode::StartCall;
- argInstr = m_argStack->Pop())
- {
- prevInstr->SetSrc2(argInstr->GetDst());
- prevInstr = argInstr;
- #if DBG
- count++;
- #endif
- }
- AssertOrFailFast(argInstr == nullptr || argInstr->m_opcode == Js::OpCode::StartCall);
- if (m_argStack->Empty())
- {
- this->callTreeHasSomeProfileInfo = false;
- }
- if (newOpcode == Js::OpCode::NewScObject || newOpcode == Js::OpCode::NewScObjArray
- || newOpcode == Js::OpCode::NewScObjectSpread || newOpcode == Js::OpCode::NewScObjArraySpread)
- {
- #if DBG
- count++;
- #endif
- m_argsOnStack++;
- }
- argCount = Js::CallInfo::GetArgCountWithExtraArgs(flags, argCount);
- if (argInstr)
- {
- prevInstr->SetSrc2(argInstr->GetDst());
- AssertMsg(instr->m_prev->m_opcode == Js::OpCode::LdSpreadIndices
- // All non-spread calls need StartCall to have the same number of args
- || (argInstr->GetSrc1()->IsIntConstOpnd()
- && argInstr->GetSrc1()->AsIntConstOpnd()->GetValue() == count
- && count == argCount), "StartCall has wrong number of arguments...");
- }
- else
- {
- AssertMsg(false, "Expect StartCall on other opcodes...");
- }
- // Update Func if this is the highest amount of stack we've used so far
- // to push args.
- #if DBG
- m_callsOnStack--;
- #endif
- if (m_func->m_argSlotsForFunctionsCalled < m_argsOnStack)
- m_func->m_argSlotsForFunctionsCalled = m_argsOnStack;
- #if DBG
- if (m_callsOnStack == 0)
- Assert(m_argsOnStack == argCount);
- #endif
- m_argsOnStack -= argCount;
- if (m_func->IsJitInDebugMode())
- {
- // Insert bailout after return from a call, script or library function call.
- this->InsertBailOutForDebugger(
- m_jnReader.GetCurrentOffset(), // bailout will resume at the offset of next instr.
- c_debuggerBailOutKindForCall);
- }
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildBrReg1
- ///
- /// Build IR instr for a BrReg1 instruction.
- /// This is a conditional branch with a single source operand (e.g., "if (x)" ...)
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildBrReg1(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_BrReg1<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R1);
- }
- BuildBrReg1(newOpcode, offset, m_jnReader.GetCurrentOffset() + layout->RelativeJumpOffset, layout->R1);
- }
- void
- IRBuilder::BuildBrReg1(Js::OpCode newOpcode, uint32 offset, uint targetOffset, Js::RegSlot srcRegSlot)
- {
- IR::BranchInstr * branchInstr;
- IR::RegOpnd * srcOpnd;
- srcOpnd = this->BuildSrcOpnd(srcRegSlot);
- if (newOpcode == Js::OpCode::BrNotUndecl_A) {
- IR::AddrOpnd *srcOpnd2 = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndeclBlockVarAddr(),
- IR::AddrOpndKindDynamicVar, this->m_func);
- branchInstr = IR::BranchInstr::New(Js::OpCode::BrNotAddr_A, nullptr, srcOpnd, srcOpnd2, m_func);
- } else {
- branchInstr = IR::BranchInstr::New(newOpcode, nullptr, srcOpnd, m_func);
- }
- this->AddBranchInstr(branchInstr, offset, targetOffset);
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildBrReg2
- ///
- /// Build IR instr for a BrReg2 instruction.
- /// This is a conditional branch with a 2 source operands (e.g., "if (x == y)" ...)
- ///
- ///----------------------------------------------------------------------------
- template <typename SizePolicy>
- void
- IRBuilder::BuildBrReg2(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_BrReg2<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R1);
- this->DoClosureRegCheck(layout->R2);
- }
- BuildBrReg2(newOpcode, offset, m_jnReader.GetCurrentOffset() + layout->RelativeJumpOffset, layout->R1, layout->R2);
- }
- template <typename SizePolicy>
- void
- IRBuilder::BuildBrReg1Unsigned1(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(newOpcode == Js::OpCode::BrOnEmpty
- /* || newOpcode == Js::OpCode::BrOnNotEmpty */ // BrOnNotEmpty not generate by the byte code
- );
- Assert(!OpCodeAttr::IsProfiledOp(newOpcode));
- Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode));
- auto layout = m_jnReader.GetLayout<Js::OpLayoutT_BrReg1Unsigned1<SizePolicy>>();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(layout->R1);
- }
- BuildBrBReturn(newOpcode, offset, layout->R1, layout->C2, m_jnReader.GetCurrentOffset() + layout->RelativeJumpOffset);
- }
- void
- IRBuilder::BuildBrBReturn(Js::OpCode newOpcode, uint32 offset, Js::RegSlot DestRegSlot, uint32 forInLoopLevel, uint32 targetOffset)
- {
- IR::Opnd *srcOpnd = this->BuildForInEnumeratorOpnd(forInLoopLevel, offset);
- IR::RegOpnd * destOpnd = this->BuildDstOpnd(DestRegSlot);
- IR::BranchInstr * branchInstr = IR::BranchInstr::New(newOpcode, destOpnd, nullptr, srcOpnd, m_func);
- this->AddBranchInstr(branchInstr, offset, targetOffset);
- switch (newOpcode)
- {
- case Js::OpCode::BrOnEmpty:
- destOpnd->SetValueType(ValueType::String);
- break;
- default:
- Assert(false);
- break;
- };
- }
- void
- IRBuilder::BuildBrReg2(Js::OpCode newOpcode, uint32 offset, uint targetOffset, Js::RegSlot R1, Js::RegSlot R2)
- {
- IR::BranchInstr * branchInstr;
- if (newOpcode == Js::OpCode::BrOnEmpty
- /* || newOpcode == Js::OpCode::BrOnNotEmpty */ // BrOnNotEmpty not generate by the byte code
- )
- {
- BuildBrBReturn(newOpcode, offset, R1, R2, targetOffset);
- return;
- }
- IR::RegOpnd * src1Opnd;
- IR::RegOpnd * src2Opnd;
- src1Opnd = this->BuildSrcOpnd(R1);
- src2Opnd = this->BuildSrcOpnd(R2);
- if (newOpcode == Js::OpCode::Case)
- {
- // generating branches for Cases is entirely handled
- // by the SwitchIRBuilder
- m_switchBuilder.OnCase(src1Opnd, src2Opnd, offset, targetOffset);
- #ifdef BYTECODE_BRANCH_ISLAND
- // Make sure that if there are branch island between the cases, we consume it first
- EnsureConsumeBranchIsland();
- #endif
- // some instructions can't be optimized past, such as LdFld for objects. In these cases we have
- // to inform the SwitchBuilder to flush any optimized cases that it has stored up to this point
- // peeks the next opcode - to check if it is not a case statement (for example: the next instr can be a LdFld for objects)
- Js::OpCode peekOpcode = m_jnReader.PeekOp();
- if (peekOpcode != Js::OpCode::Case && peekOpcode != Js::OpCode::EndSwitch)
- {
- m_switchBuilder.FlushCases(m_jnReader.GetCurrentOffset());
- }
- }
- else
- {
- branchInstr = IR::BranchInstr::New(newOpcode, nullptr, src1Opnd, src2Opnd, m_func);
- this->AddBranchInstr(branchInstr, offset, targetOffset);
- }
- }
- void
- IRBuilder::BuildEmpty(Js::OpCode newOpcode, uint32 offset)
- {
- IR::Instr *instr;
- m_jnReader.Empty();
- instr = IR::Instr::New(newOpcode, m_func);
- switch (newOpcode)
- {
- case Js::OpCode::CommitScope:
- {
- IR::RegOpnd * src1Opnd;
- src1Opnd = this->BuildSrcOpnd(m_func->GetJITFunctionBody()->GetLocalClosureReg());
- IR::LabelInstr *labelNull = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
- IR::RegOpnd * funcExprOpnd = IR::RegOpnd::New(TyVar, m_func);
- instr = IR::Instr::New(Js::OpCode::LdFuncExpr, funcExprOpnd, m_func);
- this->AddInstr(instr, offset);
- IR::BranchInstr *branchInstr = IR::BranchInstr::New(Js::OpCode::BrFncCachedScopeNeq, labelNull,
- funcExprOpnd, src1Opnd, this->m_func);
- this->AddInstr(branchInstr, offset);
- instr = IR::Instr::New(newOpcode, this->m_func);
- instr->SetSrc1(src1Opnd);
- this->AddInstr(instr, offset);
- this->AddInstr(labelNull, Js::Constants::NoByteCodeOffset);
- return;
- }
- case Js::OpCode::Ret:
- {
- IR::RegOpnd *regOpnd = BuildDstOpnd(0);
- instr->SetSrc1(regOpnd);
- this->AddInstr(instr, offset);
- break;
- }
- case Js::OpCode::Leave:
- {
- IR::BranchInstr * branchInstr;
- IR::LabelInstr * labelInstr;
- if (this->handlerOffsetStack && !this->handlerOffsetStack->Empty() && this->handlerOffsetStack->Top().Second())
- {
- // If the try region has a break block, we don't want the Flowgraph to move all of that code out of the loop
- // because an exception will bring the control back into the loop. The branch out of the loop (which is the
- // reason for the code to be a break block) can still be moved out though.
- //
- // "BrOnException $catch" is inserted before Leave's in the try region to instrument flow from the try region
- // to the catch region (which is in the loop).
- IR::BranchInstr * brOnException = IR::BranchInstr::New(Js::OpCode::BrOnException, nullptr, this->m_func);
- this->AddBranchInstr(brOnException, offset, this->handlerOffsetStack->Top().First());
- }
- labelInstr = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
- branchInstr = IR::BranchInstr::New(newOpcode, labelInstr, this->m_func);
- this->AddInstr(branchInstr, offset);
- this->AddInstr(labelInstr, Js::Constants::NoByteCodeOffset);
- break;
- }
- case Js::OpCode::LeaveNull:
- finallyBlockLevel--;
- this->AddInstr(instr, offset);
- break;
- case Js::OpCode::Finally:
- if (this->handlerOffsetStack)
- {
- AssertOrFailFast(!this->handlerOffsetStack->Empty());
- AssertOrFailFast(this->handlerOffsetStack->Top().Second() == false);
- this->handlerOffsetStack->Pop();
- }
- finallyBlockLevel++;
- this->AddInstr(IR::Instr::New(Js::OpCode::Finally, this->m_func), offset);
- break;
- case Js::OpCode::Break:
- if (m_func->IsJitInDebugMode())
- {
- // Add explicit bailout.
- this->InsertBailOutForDebugger(offset, IR::BailOutExplicit);
- }
- else
- {
- // Default behavior, let's keep it for now, removed in lowerer.
- this->AddInstr(instr, offset);
- }
- break;
- case Js::OpCode::BeginBodyScope:
- {
- // This marks the end of a param scope which is not merged with body scope.
- // So we have to first cache the closure so that we can use it to copy the initial values for
- // body syms from corresponding param syms (LdParamSlot). Body should get its own scope slot.
- Assert(!this->IsParamScopeDone());
- this->SetParamScopeDone();
- IR::Opnd * localClosureOpnd;
- if (this->m_func->GetLocalClosureSym() != nullptr)
- {
- localClosureOpnd = IR::RegOpnd::New(this->m_func->GetLocalClosureSym(), TyVar, this->m_func);
- }
- else
- {
- AssertOrFailFast(this->m_func->GetJITFunctionBody()->GetScopeSlotArraySize() == 0 && !this->m_func->GetJITFunctionBody()->HasScopeObject());
- localClosureOpnd = IR::IntConstOpnd::New(0, TyVar, this->m_func);
- }
- this->AddInstr(
- IR::Instr::New(
- Js::OpCode::Ld_A,
- this->BuildDstOpnd(this->m_func->GetJITFunctionBody()->GetParamClosureReg()),
- localClosureOpnd,
- this->m_func),
- offset);
- // Create a new local closure for the body when either body scope has scope slots allocated or
- // eval is present which can leak declarations.
- if (this->m_func->GetJITFunctionBody()->GetScopeSlotArraySize() > 0 || this->m_func->GetJITFunctionBody()->HasScopeObject())
- {
- if (this->m_func->GetJITFunctionBody()->HasScopeObject())
- {
- if (this->m_func->GetJITFunctionBody()->HasCachedScopePropIds())
- {
- this->BuildInitCachedScope(0, Js::Constants::NoByteCodeOffset);
- }
- else
- {
- this->AddInstr(
- IR::Instr::New(
- Js::OpCode::NewScopeObject,
- this->BuildDstOpnd(this->m_func->GetJITFunctionBody()->GetLocalClosureReg()),
- m_func),
- Js::Constants::NoByteCodeOffset);
- }
- }
- else
- {
- this->AddInstr(
- IR::Instr::New(
- Js::OpCode::NewScopeSlots,
- this->BuildDstOpnd(this->m_func->GetJITFunctionBody()->GetLocalClosureReg()),
- IR::IntConstOpnd::New(this->m_func->GetJITFunctionBody()->GetScopeSlotArraySize() + Js::ScopeSlots::FirstSlotIndex, TyUint32, this->m_func),
- m_func),
- Js::Constants::NoByteCodeOffset);
- }
- IR::Instr* lfd = IR::Instr::New(
- Js::OpCode::LdFrameDisplay,
- this->BuildDstOpnd(this->m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg()),
- this->BuildDstOpnd(this->m_func->GetJITFunctionBody()->GetLocalClosureReg()),
- this->BuildDstOpnd(this->m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg()),
- this->m_func);
- this->AddInstr(
- lfd,
- Js::Constants::NoByteCodeOffset);
- lfd->isNonFastPathFrameDisplay = true;
- }
- break;
- }
- default:
- this->AddInstr(instr, offset);
- break;
- }
- }
- #ifdef BYTECODE_BRANCH_ISLAND
- void
- IRBuilder::EnsureConsumeBranchIsland()
- {
- if (m_jnReader.PeekOp() == Js::OpCode::Br)
- {
- // Save the old offset
- uint offset = m_jnReader.GetCurrentOffset();
- // Read the potentially a branch around
- Js::LayoutSize layoutSize;
- Js::OpCode opcode = m_jnReader.ReadOp(layoutSize);
- Assert(opcode == Js::OpCode::Br);
- Assert(layoutSize == Js::SmallLayout);
- const unaligned Js::OpLayoutBr * playout = m_jnReader.Br();
- unsigned int targetOffset = m_jnReader.GetCurrentOffset() + playout->RelativeJumpOffset;
- uint branchIslandOffset = m_jnReader.GetCurrentOffset();
- if (branchIslandOffset == targetOffset)
- {
- // branch to next, there is no long branch
- m_jnReader.SetCurrentOffset(offset);
- return;
- }
- // Ignore all the BrLong
- while (m_jnReader.PeekOp() == Js::OpCode::BrLong)
- {
- opcode = m_jnReader.ReadOp(layoutSize);
- Assert(opcode == Js::OpCode::BrLong);
- Assert(layoutSize == Js::SmallLayout);
- m_jnReader.BrLong();
- }
- // Confirm that is a branch around
- if ((uint)m_jnReader.GetCurrentOffset() == targetOffset)
- {
- // Really consume the branch island
- m_jnReader.SetCurrentOffset(branchIslandOffset);
- ConsumeBranchIsland();
- // Mark the virtual branch around as a redirect long branch as well
- // so that if it is the target of another branch, it will just keep pass
- // the branch island
- Assert(longBranchMap);
- Assert(offset < m_offsetToInstructionCount);
- Assert(m_offsetToInstruction[offset] == nullptr);
- m_offsetToInstruction[offset] = VirtualLongBranchInstr;
- longBranchMap->Add(offset, targetOffset);
- }
- else
- {
- // Reset the offset
- m_jnReader.SetCurrentOffset(offset);
- }
- }
- }
- IR::Instr * const IRBuilder::VirtualLongBranchInstr = (IR::Instr *)-1;
- void
- IRBuilder::ConsumeBranchIsland()
- {
- do
- {
- uint32 offset = m_jnReader.GetCurrentOffset();
- Js::LayoutSize layoutSize;
- Js::OpCode opcode = m_jnReader.ReadOp(layoutSize);
- Assert(opcode == Js::OpCode::BrLong);
- Assert(layoutSize == Js::SmallLayout);
- BuildBrLong(Js::OpCode::BrLong, offset);
- }
- while (m_jnReader.PeekOp() == Js::OpCode::BrLong);
- }
- void
- IRBuilder::BuildBrLong(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(newOpcode == Js::OpCode::BrLong);
- Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode));
- Assert(offset != Js::Constants::NoByteCodeOffset);
- const unaligned Js::OpLayoutBrLong *branchInsn = m_jnReader.BrLong();
- unsigned int targetOffset = m_jnReader.GetCurrentOffset() + branchInsn->RelativeJumpOffset;
- Assert(offset < m_offsetToInstructionCount);
- Assert(m_offsetToInstruction[offset] == nullptr);
- // BrLong are also just the target of another branch, just set a virtual long branch instr
- // and remap the original branch to the actual destination in ResolveVirtualLongBranch
- m_offsetToInstruction[offset] = VirtualLongBranchInstr;
- longBranchMap->Add(offset, targetOffset);
- }
- uint
- IRBuilder::ResolveVirtualLongBranch(IR::BranchInstr * branchInstr, uint offset)
- {
- Assert(longBranchMap);
- uint32 targetOffset;
- if (!longBranchMap->TryGetValue(offset, &targetOffset))
- {
- // If we see a VirtualLongBranchInstr, we must have a mapping to the real target offset
- Assert(false);
- Fatal();
- }
- // If this is a jump out of the loop body we need to load the return IP and jump to the loop exit instead
- if (!IsLoopBodyOuterOffset(targetOffset))
- {
- return targetOffset;
- }
- // Multi branch shouldn't be exiting a loop
- Assert(branchInstr->m_opcode != Js::OpCode::MultiBr);
- // Don't load the return IP if it is already loaded (for the case of early exit)
- if (!IsLoopBodyReturnIPInstr(branchInstr->m_prev))
- {
- IR::Instr * returnIPInstr = CreateLoopBodyReturnIPInstr(targetOffset, branchInstr->GetByteCodeOffset());
- // Any jump to this branch to jump to the return IP load instr first
- uint32 branchInstrByteCodeOffset = branchInstr->GetByteCodeOffset();
- Assert(this->m_offsetToInstruction[branchInstrByteCodeOffset] == branchInstr ||
- (this->m_offsetToInstruction[branchInstrByteCodeOffset]->HasBailOutInfo() &&
- this->m_offsetToInstruction[branchInstrByteCodeOffset]->GetBailOutKind() == IR::BailOutInjected));
- InsertInstr(returnIPInstr, branchInstr);
- }
- return GetLoopBodyExitInstrOffset();
- }
- #endif
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildBr
- ///
- /// Build IR instr for a Br (unconditional branch) instruction.
- /// or TryCatch/TryFinally
- ///
- ///----------------------------------------------------------------------------
- void
- IRBuilder::BuildBr(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode));
- IR::BranchInstr * branchInstr;
- const unaligned Js::OpLayoutBr *branchInsn = m_jnReader.Br();
- unsigned int targetOffset = m_jnReader.GetCurrentOffset() + branchInsn->RelativeJumpOffset;
- #ifdef BYTECODE_BRANCH_ISLAND
- bool isLongBranchIsland = (m_jnReader.PeekOp() == Js::OpCode::BrLong);
- if (isLongBranchIsland)
- {
- ConsumeBranchIsland();
- }
- #endif
- if(newOpcode == Js::OpCode::EndSwitch)
- {
- m_switchBuilder.EndSwitch(offset, targetOffset);
- return;
- }
- #ifdef PERF_HINT
- else if (PHASE_TRACE1(Js::PerfHintPhase) && (newOpcode == Js::OpCode::TryCatch || newOpcode == Js::OpCode::TryFinally) )
- {
- WritePerfHint(PerfHints::HasTryBlock, this->m_func, offset);
- }
- #endif
- #ifdef BYTECODE_BRANCH_ISLAND
- if (isLongBranchIsland && (targetOffset == (uint)m_jnReader.GetCurrentOffset()))
- {
- // Branch to next (probably after consume branch island), try to not emit the branch
- // Mark the jump around instruction as a virtual long branch as well so we can just
- // fall through instead of branch to exit
- Assert(offset < m_offsetToInstructionCount);
- if (m_offsetToInstruction[offset] == nullptr)
- {
- m_offsetToInstruction[offset] = VirtualLongBranchInstr;
- longBranchMap->Add(offset, targetOffset);
- return;
- }
- // We may have already create an instruction on this offset as a statement boundary
- // or in the bailout at every byte code case.
- // The statement boundary case only happens if we have emitted the long branch island
- // after an existing no fall through instruction, but that instruction also happen to be
- // branch to next. We will just generate an actual branch to next instruction.
- Assert(m_offsetToInstruction[offset]->m_opcode == Js::OpCode::StatementBoundary
- || (Js::Configuration::Global.flags.IsEnabled(Js::BailOutAtEveryByteCodeFlag)
- && m_offsetToInstruction[offset]->m_opcode == Js::OpCode::BailOnEqual));
- }
- #endif
- if ((newOpcode == Js::OpCode::TryCatch) && this->handlerOffsetStack)
- {
- this->handlerOffsetStack->Push(Pair<uint, bool>(targetOffset, true));
- }
- else if ((newOpcode == Js::OpCode::TryFinally) && this->handlerOffsetStack)
- {
- this->handlerOffsetStack->Push(Pair<uint, bool>(targetOffset, false));
- }
- branchInstr = IR::BranchInstr::New(newOpcode, nullptr, m_func);
- this->AddBranchInstr(branchInstr, offset, targetOffset);
- }
- void
- IRBuilder::BuildBrS(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode));
- IR::BranchInstr * branchInstr;
- const unaligned Js::OpLayoutBrS *branchInsn = m_jnReader.BrS();
- unsigned int targetOffset = m_jnReader.GetCurrentOffset() + branchInsn->RelativeJumpOffset;
- branchInstr = IR::BranchInstr::New(newOpcode, nullptr,
- IR::IntConstOpnd::New(branchInsn->val,
- TyInt32, m_func),m_func);
- this->AddBranchInstr(branchInstr, offset, targetOffset);
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildBrProperty
- ///
- /// Build IR instr for a BrProperty instruction.
- /// This is a conditional branch that tests whether the given property
- /// is present on the given instance.
- ///
- ///----------------------------------------------------------------------------
- void
- IRBuilder::BuildBrProperty(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode));
- const unaligned Js::OpLayoutBrProperty *branchInsn = m_jnReader.BrProperty();
- if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func))
- {
- this->DoClosureRegCheck(branchInsn->Instance);
- }
- IR::BranchInstr * branchInstr;
- Js::PropertyId propertyId =
- m_func->GetJITFunctionBody()->GetReferencedPropertyId(branchInsn->PropertyIdIndex);
- unsigned int targetOffset = m_jnReader.GetCurrentOffset() + branchInsn->RelativeJumpOffset;
- IR::SymOpnd * fieldSymOpnd = this->BuildFieldOpnd(newOpcode, branchInsn->Instance, propertyId, branchInsn->PropertyIdIndex, PropertyKindData);
- branchInstr = IR::BranchInstr::New(newOpcode, nullptr, fieldSymOpnd, m_func);
- this->AddBranchInstr(branchInstr, offset, targetOffset);
- }
- void
- IRBuilder::BuildBrLocalProperty(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode));
- Assert(newOpcode == Js::OpCode::BrOnHasLocalProperty);
- const unaligned Js::OpLayoutBrLocalProperty *branchInsn = m_jnReader.BrLocalProperty();
- if (m_func->GetLocalClosureSym()->HasByteCodeRegSlot())
- {
- IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset);
- byteCodeUse->SetNonOpndSymbol(m_func->GetLocalClosureSym()->m_id);
- this->AddInstr(byteCodeUse, offset);
- }
- IR::BranchInstr * branchInstr;
- Js::PropertyId propertyId =
- m_func->GetJITFunctionBody()->GetReferencedPropertyId(branchInsn->PropertyIdIndex);
- unsigned int targetOffset = m_jnReader.GetCurrentOffset() + branchInsn->RelativeJumpOffset;
- IR::SymOpnd * fieldSymOpnd = this->BuildFieldOpnd(newOpcode, m_func->GetJITFunctionBody()->GetLocalClosureReg(), propertyId, branchInsn->PropertyIdIndex, PropertyKindData);
- branchInstr = IR::BranchInstr::New(newOpcode, nullptr, fieldSymOpnd, m_func);
- this->AddBranchInstr(branchInstr, offset, targetOffset);
- }
- void
- IRBuilder::BuildBrEnvProperty(Js::OpCode newOpcode, uint32 offset)
- {
- Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode));
- const unaligned Js::OpLayoutBrEnvProperty *branchInsn = m_jnReader.BrEnvProperty();
- IR::Instr *instr;
- IR::BranchInstr * branchInstr;
- IR::RegOpnd *regOpnd;
- IR::SymOpnd *fieldOpnd;
- PropertySym *fieldSym;
- fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, this->GetEnvReg(), branchInsn->SlotIndex, (Js::PropertyIdIndexType)-1, PropertyKindSlotArray);
- regOpnd = IR::RegOpnd::New(TyVar, m_func);
- instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func);
- this->AddInstr(instr, offset);
- Js::PropertyId propertyId =
- m_func->GetJITFunctionBody()->GetReferencedPropertyId(branchInsn->PropertyIdIndex);
- unsigned int targetOffset = m_jnReader.GetCurrentOffset() + branchInsn->RelativeJumpOffset;\
- fieldSym = PropertySym::New(regOpnd->m_sym, propertyId, branchInsn->PropertyIdIndex, (uint)-1, PropertyKindData, m_func);
- fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func);
- Assert(newOpcode == Js::OpCode::BrOnHasEnvProperty || newOpcode == Js::OpCode::BrOnHasLocalEnvProperty);
- branchInstr = IR::BranchInstr::New(newOpcode == Js::OpCode::BrOnHasEnvProperty ? Js::OpCode::BrOnHasProperty : Js::OpCode::BrOnHasLocalProperty, nullptr, fieldOpnd, m_func);
- this->AddBranchInstr(branchInstr, offset, targetOffset);
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::AddBranchInstr
- ///
- /// Create a branch/offset pair which will be fixed up at the end of the
- /// IRBuilder phase and add the instruction
- ///
- ///----------------------------------------------------------------------------
- BranchReloc *
- IRBuilder::AddBranchInstr(IR::BranchInstr * branchInstr, uint32 offset, uint32 targetOffset)
- {
- AssertOrFailFast(targetOffset <= m_func->GetJITFunctionBody()->GetByteCodeLength());
- //
- // Loop jitting would be done only till the LoopEnd
- // Any branches beyond that offset are for the return stmt
- //
- if (IsLoopBodyOuterOffset(targetOffset))
- {
- // if we have loaded the loop IP sym from the ProfiledLoopEnd then don't add it here
- if (!IsLoopBodyReturnIPInstr(m_lastInstr))
- {
- this->InsertLoopBodyReturnIPInstr(targetOffset, offset);
- }
- // Jump the restore StSlot and Ret instruction
- targetOffset = GetLoopBodyExitInstrOffset();
- }
- BranchReloc * reloc = nullptr;
- reloc = this->CreateRelocRecord(branchInstr, offset, targetOffset);
- this->AddInstr(branchInstr, offset);
- return reloc;
- }
- BranchReloc *
- IRBuilder::CreateRelocRecord(IR::BranchInstr * branchInstr, uint32 offset, uint32 targetOffset)
- {
- BranchReloc * reloc = JitAnew(this->m_tempAlloc, BranchReloc, branchInstr, offset, targetOffset);
- this->branchRelocList->Prepend(reloc);
- return reloc;
- }
- ///----------------------------------------------------------------------------
- ///
- /// IRBuilder::BuildRegexFromPattern
- ///
- /// Build a new RegEx instruction. Simply construct a var to hold the regex
- /// and load it as an immediate into a register.
- ///
- ///----------------------------------------------------------------------------
- void
- IRBuilder::BuildRegexFromPattern(Js::RegSlot dstRegSlot, uint32 patternIndex, uint32 offset)
- {
- IR::Instr * instr;
- IR::RegOpnd* dstOpnd = this->BuildDstOpnd(dstRegSlot);
- dstOpnd->SetValueType(ValueType::GetObject(ObjectType::RegExp));
- IR::Opnd * regexOpnd = IR::AddrOpnd::New(m_func->GetJITFunctionBody()->GetLiteralRegexAddr(patternIndex), IR::AddrOpndKindDynamicMisc, this->m_func);
- instr = IR::Instr::New(Js::OpCode::NewRegEx, dstOpnd, regexOpnd, this->m_func);
- this->AddInstr(instr, offset);
- }
- bool
- IRBuilder::IsFloatFunctionCallsite(Js::BuiltinFunction index, size_t argc)
- {
- return Js::JavascriptLibrary::IsFloatFunctionCallsite(index, argc);
- }
- void
- IRBuilder::CheckBuiltIn(PropertySym * propertySym, Js::BuiltinFunction *puBuiltInIndex)
- {
- Js::BuiltinFunction index = Js::BuiltinFunction::None;
- // Check whether the propertySym appears to be a built-in.
- if (propertySym->m_fieldKind != PropertyKindData)
- {
- return;
- }
- index = Js::JavascriptLibrary::GetBuiltinFunctionForPropId(propertySym->m_propertyId);
- if (index == Js::BuiltinFunction::None)
- {
- return;
- }
- // If the target is one of the Math built-ins, see whether the stack sym is the
- // global "Math".
- if (Js::JavascriptLibrary::IsFltFunc(index))
- {
- if (!propertySym->m_stackSym->m_isSingleDef)
- {
- return;
- }
- IR::Instr *instr = propertySym->m_stackSym->m_instrDef;
- AssertMsg(instr != nullptr, "Single-def stack sym w/o def instr?");
- if (instr->m_opcode != Js::OpCode::LdRootFld && instr->m_opcode != Js::OpCode::LdRootFldForTypeOf)
- {
- return;
- }
- IR::Opnd * opnd = instr->GetSrc1();
- AssertMsg(opnd != nullptr && opnd->IsSymOpnd() && opnd->AsSymOpnd()->m_sym->IsPropertySym(),
- "LdRootFld w/o propertySym src?");
- if (opnd->AsSymOpnd()->m_sym->AsPropertySym()->m_propertyId != Js::PropertyIds::Math)
- {
- return;
- }
- }
- *puBuiltInIndex = index;
- }
- StackSym *
- IRBuilder::EnsureStackFuncPtrSym()
- {
- StackSym * sym = this->m_stackFuncPtrSym;
- if (sym)
- {
- return sym;
- }
- if (m_func->IsLoopBody() && m_func->DoStackNestedFunc())
- {
- Assert(m_func->IsTopFunc());
- sym = StackSym::New(TyVar, m_func);
- this->m_stackFuncPtrSym = sym;
- }
- return sym;
- }
- void
- IRBuilder::GenerateLoopBodySlotAccesses(uint offset)
- {
- //
- // The interpreter instance is passed as 0th argument to the JITted loop body function.
- // Always load the argument, then use it to generate any necessary store-slots.
- //
- uint16 argument = 0;
- StackSym *symSrc = StackSym::NewParamSlotSym(argument + 1, m_func);
- symSrc->m_offset = (argument + LowererMD::GetFormalParamOffset()) * MachPtr;
- symSrc->m_allocated = true;
- m_func->SetHasImplicitParamLoad();
- IR::SymOpnd *srcOpnd = IR::SymOpnd::New(symSrc, TyVar, m_func);
- StackSym *loopParamSym = m_func->EnsureLoopParamSym();
- IR::RegOpnd *loopParamOpnd = IR::RegOpnd::New(loopParamSym, TyMachPtr, m_func);
- IR::Instr *instrArgIn = IR::Instr::New(Js::OpCode::ArgIn_A, loopParamOpnd, srcOpnd, m_func);
- m_func->m_headInstr->InsertAfter(instrArgIn);
- StackSym *stackFuncPtrSym = this->m_stackFuncPtrSym;
- if (stackFuncPtrSym)
- {
- PropertySym * fieldSym = PropertySym::FindOrCreate(loopParamSym->m_id, (Js::PropertyId)(Js::InterpreterStackFrame::GetOffsetOfStackNestedFunctions() / sizeof(Js::Var)), (uint32)-1, (uint)-1, PropertyKindLocalSlots, m_func);
- IR::SymOpnd * opndPtrRef = IR::SymOpnd::New(fieldSym, TyVar, m_func);
- IR::Instr * instrPtrInit = IR::Instr::New(Js::OpCode::LdSlot, IR::RegOpnd::New(stackFuncPtrSym, TyVar, m_func), opndPtrRef, m_func);
- instrArgIn->InsertAfter(instrPtrInit);
- }
- GenerateLoopBodyStSlots(loopParamSym->m_id, offset);
- }
- void
- IRBuilder::GenerateLoopBodyStSlots(SymID loopParamSymId, uint offset)
- {
- if (this->m_stSlots->Count() == 0)
- {
- return;
- }
- FOREACH_BITSET_IN_FIXEDBV(regSlot, this->m_stSlots)
- {
- this->GenerateLoopBodyStSlot(regSlot, offset);
- }
- NEXT_BITSET_IN_FIXEDBV;
- }
- IR::Instr *
- IRBuilder::GenerateLoopBodyStSlot(Js::RegSlot regSlot, uint offset)
- {
- Assert(!this->RegIsConstant((Js::RegSlot)regSlot));
- StackSym *loopParamSym = m_func->EnsureLoopParamSym();
- PropertySym * fieldSym = PropertySym::FindOrCreate(loopParamSym->m_id, (Js::PropertyId)(regSlot + this->m_loopBodyLocalsStartSlot), (uint32)-1, (uint)-1, PropertyKindLocalSlots, m_func);
- IR::SymOpnd * fieldSymOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func);
- IR::RegOpnd * regOpnd = this->BuildSrcOpnd((Js::RegSlot)regSlot);
- #if !FLOATVAR
- Js::OpCode opcode = Js::OpCode::StSlotBoxTemp;
- #else
- Js::OpCode opcode = Js::OpCode::StSlot;
- #endif
- IR::Instr * stSlotInstr = IR::Instr::New(opcode, fieldSymOpnd, regOpnd, m_func);
- if (offset != Js::Constants::NoByteCodeOffset)
- {
- this->AddInstr(stSlotInstr, offset);
- return nullptr;
- }
- else
- {
- return stSlotInstr;
- }
- }
- IR::Instr *
- IRBuilder::CreateLoopBodyReturnIPInstr(uint targetOffset, uint offset)
- {
- IR::RegOpnd * retOpnd = IR::RegOpnd::New(m_loopBodyRetIPSym, TyMachReg, m_func);
- IR::IntConstOpnd * exitOffsetOpnd = IR::IntConstOpnd::New(targetOffset, TyMachReg, m_func);
- return IR::Instr::New(Js::OpCode::Ld_I4, retOpnd, exitOffsetOpnd, m_func);
- }
- IR::Opnd *
- IRBuilder::InsertLoopBodyReturnIPInstr(uint targetOffset, uint offset)
- {
- IR::Instr * setRetValueInstr = CreateLoopBodyReturnIPInstr(targetOffset, offset);
- this->AddInstr(setRetValueInstr, offset);
- return setRetValueInstr->GetDst();
- }
- void
- IRBuilder::InsertDoneLoopBodyLoopCounter(uint32 lastOffset)
- {
- if (m_loopCounterSym == nullptr)
- {
- return;
- }
- IR::Instr * loopCounterStoreInstr = IR::Instr::New(Js::OpCode::StLoopBodyCount, m_func);
- IR::RegOpnd *countRegOpnd = IR::RegOpnd::New(m_loopCounterSym, TyInt32, this->m_func);
- countRegOpnd->SetIsJITOptimizedReg(true);
- loopCounterStoreInstr->SetSrc1(countRegOpnd);
- this->AddInstr(loopCounterStoreInstr, lastOffset + 1);
- return;
- }
- void
- IRBuilder::InsertIncrLoopBodyLoopCounter(IR::LabelInstr *loopTopLabelInstr)
- {
- Assert(this->IsLoopBody());
- IR::RegOpnd *loopCounterOpnd = IR::RegOpnd::New(m_loopCounterSym, TyInt32, this->m_func);
- IR::Instr * incr = IR::Instr::New(Js::OpCode::IncrLoopBodyCount, loopCounterOpnd, loopCounterOpnd, this->m_func);
- loopCounterOpnd->SetIsJITOptimizedReg(true);
- IR::Instr* nextRealInstr = loopTopLabelInstr->GetNextRealInstr();
- InsertInstr(incr, nextRealInstr);
- }
- void
- IRBuilder::InsertInitLoopBodyLoopCounter(uint loopNum)
- {
- Assert(this->IsLoopBody());
- intptr_t loopHeader = m_func->GetJITFunctionBody()->GetLoopHeaderAddr(loopNum);
- Assert(m_func->GetWorkItem()->GetLoopHeaderAddr() == loopHeader); //Init only once
- m_loopCounterSym = StackSym::New(TyVar, this->m_func);
- IR::RegOpnd* loopCounterOpnd = IR::RegOpnd::New(m_loopCounterSym, TyVar, this->m_func);
- loopCounterOpnd->SetIsJITOptimizedReg(true);
- IR::Instr * initInstr = IR::Instr::New(Js::OpCode::InitLoopBodyCount, loopCounterOpnd, this->m_func);
- m_lastInstr->InsertAfter(initInstr);
- m_lastInstr = initInstr;
- initInstr->SetByteCodeOffset(m_jnReader.GetCurrentOffset());
- }
- IR::AddrOpnd *
- IRBuilder::BuildAuxArrayOpnd(AuxArrayValue auxArrayType, uint32 auxArrayOffset)
- {
- switch (auxArrayType)
- {
- case AuxArrayValue::AuxPropertyIdArray:
- case AuxArrayValue::AuxIntArray:
- case AuxArrayValue::AuxFloatArray:
- case AuxArrayValue::AuxVarsArray:
- case AuxArrayValue::AuxFuncInfoArray:
- case AuxArrayValue::AuxVarArrayVarCount:
- {
- IR::AddrOpnd * opnd = IR::AddrOpnd::New(m_func->GetJITFunctionBody()->GetAuxDataAddr(auxArrayOffset), IR::AddrOpndKindDynamicAuxBufferRef, m_func);
- opnd->m_metadata = m_func->GetJITFunctionBody()->ReadFromAuxData(auxArrayOffset);
- return opnd;
- }
- default:
- Assert(UNREACHED);
- return nullptr;
- }
- }
- IR::Opnd *
- IRBuilder::BuildAuxObjectLiteralTypeRefOpnd(int objectId)
- {
- return IR::AddrOpnd::New(m_func->GetJITFunctionBody()->GetObjectLiteralTypeRef(objectId), IR::AddrOpndKindDynamicMisc, this->m_func);
- }
- void
- IRBuilder::DoClosureRegCheck(Js::RegSlot reg)
- {
- if (reg == Js::Constants::NoRegister)
- {
- return;
- }
- if (reg == m_func->GetJITFunctionBody()->GetEnvReg() ||
- reg == m_func->GetJITFunctionBody()->GetLocalClosureReg() ||
- reg == m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg() ||
- reg == m_func->GetJITFunctionBody()->GetParamClosureReg())
- {
- Js::Throw::FatalInternalError();
- }
- }
- Js::RegSlot
- IRBuilder::InnerScopeIndexToRegSlot(uint32 index) const
- {
- if (index >= m_func->GetJITFunctionBody()->GetInnerScopeCount())
- {
- Js::Throw::FatalInternalError();
- }
- Js::RegSlot reg = m_func->GetJITFunctionBody()->GetFirstInnerScopeReg() + index;
- if (reg >= m_func->GetJITFunctionBody()->GetLocalsCount())
- {
- Js::Throw::FatalInternalError();
- }
- return reg;
- }
- bool
- IRBuilder::DoLoadInstructionArrayProfileInfo()
- {
- return !(!this->m_func->HasProfileInfo() ||
- (
- PHASE_OFF(Js::TypedArrayPhase, this->m_func->GetTopFunc()) &&
- PHASE_OFF(Js::ArrayCheckHoistPhase, this->m_func)
- ));
- }
- bool
- IRBuilder::AllowNativeArrayProfileInfo()
- {
- return !((!(m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry()) && m_func->GetWeakFuncRef() && !m_func->HasArrayInfo()) ||
- m_func->IsJitInDebugMode());
- }
- #if DBG_DUMP || defined(ENABLE_IR_VIEWER)
- #define POINTER_OFFSET(opnd, c, field) \
- m_irBuilder->BuildIndirOpnd((opnd), c, _u(#c) _u(".") _u(#field))
- #else
- #define POINTER_OFFSET(opnd, c, field) \
- m_irBuilder->BuildIndirOpnd((opnd), c)
- #endif
- IRBuilder::GeneratorJumpTable::GeneratorJumpTable(Func* func, IRBuilder* irBuilder) : m_func(func), m_irBuilder(irBuilder) {}
- IR::Instr*
- IRBuilder::GeneratorJumpTable::BuildJumpTable()
- {
- if (!this->m_func->GetJITFunctionBody()->IsCoroutine())
- {
- return this->m_irBuilder->m_lastInstr;
- }
- // Build code to check if the generator already has state and if it does then jump to the corresponding resume point.
- // Otherwise jump to the start of the function. The generator object is the first argument by convention established
- // in JavascriptGenerator::EntryNext/EntryReturn/EntryThrow.
- // We also create the interpreter stack frame for generator if it doesn't already exist.
- //
- // s1 = Ld_A prm1
- // s2 = Ld_A s1[offset of JavascriptGenerator::frame]
- // BrNotAddr_A s2 !nullptr $jumpTable
- //
- // $createInterpreterStackFrame:
- // call helper
- //
- // Br $startOfFunc
- //
- // $jumpTable:
- //
- // s3 = Ld_A s2[offset of InterpreterStackFrame::m_reader.m_currentLocation]
- // s4 = Ld_A s2[offset of InterpreterStackFrame::m_reader.m_startLocation]
- // s5 = Sub_I4 s3 s4
- // GeneratorResumeJumpTable s5
- //
- // $startOfFunc:
- //
- // s1 = Ld_A prm1
- StackSym* genParamSym = StackSym::NewParamSlotSym(1, this->m_func);
- this->m_func->SetArgOffset(genParamSym, LowererMD::GetFormalParamOffset() * MachPtr);
- IR::SymOpnd* genParamOpnd = IR::SymOpnd::New(genParamSym, TyMachPtr, this->m_func);
- IR::RegOpnd* genRegOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
- IR::Instr* instr = IR::Instr::New(Js::OpCode::Ld_A, genRegOpnd, genParamOpnd, this->m_func);
- this->m_irBuilder->AddInstr(instr, this->m_irBuilder->m_functionStartOffset);
- // s2 = Ld_A s1[offset of JavascriptGenerator::frame]
- IR::RegOpnd* genFrameOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
- instr = IR::Instr::New(
- Js::OpCode::Ld_A,
- genFrameOpnd,
- POINTER_OFFSET(genRegOpnd, Js::JavascriptGenerator::GetFrameOffset(), GeneratorFrame),
- this->m_func
- );
- this->m_irBuilder->AddInstr(instr, this->m_irBuilder->m_functionStartOffset);
- IR::LabelInstr* functionBegin = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
- LABELNAMESET(functionBegin, "GeneratorFunctionBegin");
- IR::LabelInstr* jumpTable = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
- LABELNAMESET(jumpTable, "GeneratorJumpTable");
- // If there is already a stack frame, generator function has previously begun execution - don't recreate, skip down to jump table
- // BrNotAddr_A s2 nullptr $jumpTable
- IR::BranchInstr* skipCreateInterpreterFrame = IR::BranchInstr::New(Js::OpCode::BrNotAddr_A, jumpTable, genFrameOpnd, IR::AddrOpnd::NewNull(this->m_func), this->m_func);
- this->m_irBuilder->AddInstr(skipCreateInterpreterFrame, this->m_irBuilder->m_functionStartOffset);
- // Create interpreter stack frame
- IR::Instr* createInterpreterFrame = IR::Instr::New(Js::OpCode::GeneratorCreateInterpreterStackFrame, genFrameOpnd /* dst */, genRegOpnd /* src */, this->m_func);
- this->m_irBuilder->AddInstr(createInterpreterFrame, this->m_irBuilder->m_functionStartOffset);
- // Having created the frame, skip over the jump table and start executing from the beginning of the function
- IR::BranchInstr* skipJumpTable = IR::BranchInstr::New(Js::OpCode::Br, functionBegin, this->m_func);
- this->m_irBuilder->AddInstr(skipJumpTable, this->m_irBuilder->m_functionStartOffset);
- // Label for start of jumpTable - where we look for the correct Yield resume point
- // $jumpTable:
- this->m_irBuilder->AddInstr(jumpTable, this->m_irBuilder->m_functionStartOffset);
- // s3 = Ld_A s2[offset of InterpreterStackFrame::m_reader.m_currentLocation]
- IR::RegOpnd* curLocOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
- instr = IR::Instr::New(
- Js::OpCode::Ld_A,
- curLocOpnd,
- POINTER_OFFSET(genFrameOpnd, Js::InterpreterStackFrame::GetCurrentLocationOffset(), InterpreterCurrentLocation),
- this->m_func
- );
- this->m_irBuilder->AddInstr(instr, this->m_irBuilder->m_functionStartOffset);
- // s4 = Ld_A s2[offset of InterpreterStackFrame::m_reader.m_startLocation]
- IR::RegOpnd* startLocOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
- instr = IR::Instr::New(
- Js::OpCode::Ld_A,
- startLocOpnd,
- POINTER_OFFSET(genFrameOpnd, Js::InterpreterStackFrame::GetStartLocationOffset(), InterpreterStartLocation),
- this->m_func
- );
- this->m_irBuilder->AddInstr(instr, this->m_irBuilder->m_functionStartOffset);
- // s5 = Sub_I4 s3 s4
- IR::RegOpnd* curOffsetOpnd = IR::RegOpnd::New(TyUint32, this->m_func);
- instr = IR::Instr::New(Js::OpCode::Sub_I4, curOffsetOpnd, curLocOpnd, startLocOpnd, this->m_func);
- this->m_irBuilder->AddInstr(instr, this->m_irBuilder->m_functionStartOffset);
- // GeneratorResumeJumpTable s5
- instr = IR::Instr::New(Js::OpCode::GeneratorResumeJumpTable, this->m_func);
- instr->SetSrc1(curOffsetOpnd);
- this->m_irBuilder->AddInstr(instr, this->m_irBuilder->m_functionStartOffset);
- this->m_func->m_bailOutForElidedYieldInsertionPoint = instr;
- this->m_irBuilder->AddInstr(functionBegin, this->m_irBuilder->m_functionStartOffset);
- // Save this value for later use
- this->m_generatorFrameOpnd = genFrameOpnd;
- this->m_func->SetGeneratorFrameSym(genFrameOpnd->GetStackSym());
- return this->m_irBuilder->m_lastInstr;
- }
- IR::RegOpnd*
- IRBuilder::GeneratorJumpTable::BuildForInEnumeratorArrayOpnd(uint32 offset)
- {
- Assert(this->m_generatorFrameOpnd != nullptr);
- IR::RegOpnd* forInEnumeratorArrayOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
- IR::Instr* instr = IR::Instr::New(Js::OpCode::Ld_A, forInEnumeratorArrayOpnd,
- POINTER_OFFSET(this->m_generatorFrameOpnd, Js::InterpreterStackFrame::GetOffsetOfForInEnumerators(), ForInEnumerators),
- this->m_func
- );
- this->m_irBuilder->AddInstr(instr, offset);
- return forInEnumeratorArrayOpnd;
- }
|