| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft Corporation and contributors. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeLanguagePch.h"
- #include "RuntimeMathPch.h"
- #include "EHBailoutData.h"
- #include "Library/JavascriptRegularExpression.h"
- #if DBG_DUMP
- #include "ByteCode/OpCodeUtilAsmJs.h"
- #endif
- #include "Language/InterpreterStackFrame.h"
- #include "Library/JavascriptGeneratorFunction.h"
- #include "Library/ForInObjectEnumerator.h"
- #include "Library/AtomicsOperations.h"
- #include "../../WasmReader/WasmParseTree.h"
- ///----------------------------------------------------------------------------
- ///
- /// macros PROCESS_INtoOUT
- ///
- /// This set of macros defines standard patterns for processing OpCodes in
- /// RcInterpreter::Run(). Each macro is named for "in" - "out":
- /// - A: Var
- /// - I: Integer
- /// - R: Double
- /// - X: Nothing
- ///
- /// Examples:
- /// - "A2toA1" reads two registers, each storing an Var, and writes a single
- /// register with a new Var.
- /// - "A1I1toA2" reads two registers, first an Var and second an Int32, then
- /// writes two Var registers.
- ///
- /// Although these could use lookup tables to standard OpLayout types, this
- /// additional indirection would slow the main interpreter loop further by
- /// preventing the main 'switch' statement from using the OpCode to become a
- /// direct local-function jump.
- ///----------------------------------------------------------------------------
- #define PROCESS_FALLTHROUGH(name, func) \
- case OpCode::name:
- #define PROCESS_FALLTHROUGH_COMMON(name, func, suffix) \
- case OpCode::name:
- #define PROCESS_READ_LAYOUT(name, layout, suffix) \
- CompileAssert(OpCodeInfo<OpCode::name>::Layout == OpLayoutType::layout); \
- const unaligned OpLayout##layout##suffix * playout = m_reader.layout##suffix(ip); \
- Assert((playout != nullptr) == (Js::OpLayoutType::##layout != Js::OpLayoutType::Empty)); // Make sure playout is used
- #define PROCESS_NOP_COMMON(name, layout, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, layout, suffix); \
- break; \
- }
- #define PROCESS_NOP(name, layout) PROCESS_NOP_COMMON(name, layout,)
- #define PROCESS_CUSTOM_COMMON(name, func, layout, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, layout, suffix); \
- func(playout); \
- break; \
- }
- #define PROCESS_CUSTOM(name, func, layout) PROCESS_CUSTOM_COMMON(name, func, layout,)
- #define PROCESS_CUSTOM_L_COMMON(name, func, layout, regslot, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, layout, suffix); \
- func(playout); \
- break; \
- }
- #define PROCESS_CUSTOM_L(name, func, layout, regslot) PROCESS_CUSTOM_L_COMMON(name, func, layout, regslot,)
- #define PROCESS_CUSTOM_L_Arg_COMMON(name, func, suffix) PROCESS_CUSTOM_L_COMMON(name, func, Arg, Arg, suffix)
- #define PROCESS_CUSTOM_L_Arg2_COMMON(name, func, layout, suffix) PROCESS_CUSTOM_L_COMMON(name, func, layout, Arg, suffix)
- #define PROCESS_CUSTOM_L_Arg(name, func) PROCESS_CUSTOM_L_COMMON(name, func, Arg, Arg,)
- #define PROCESS_CUSTOM_ArgNoSrc_COMMON(name, func, suffix) PROCESS_CUSTOM_COMMON(name, func, ArgNoSrc, suffix)
- #define PROCESS_CUSTOM_ArgNoSrc(name, func) PROCESS_CUSTOM_COMMON(name, func, ArgNoSrc,)
- #define PROCESS_CUSTOM_L_R0_COMMON(name, func, layout, suffix) PROCESS_CUSTOM_L_COMMON(name, func, layout, R0, suffix)
- #define PROCESS_CUSTOM_L_R0(name, func, layout) PROCESS_CUSTOM_L_COMMON(name, func, layout, R0,)
- #define PROCESS_CUSTOM_L_Value_COMMON(name, func, layout, suffix) PROCESS_CUSTOM_L_COMMON(name, func, layout, Value, suffix)
- #define PROCESS_CUSTOM_L_Value(name, func, layout) PROCESS_CUSTOM_L_COMMON(name, func, layout, Value,)
- #define PROCESS_TRY(name, func) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Br,); \
- func(playout); \
- ip = m_reader.GetIP(); \
- break; \
- }
- #define PROCESS_EMPTY(name, func) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Empty, ); \
- func(); \
- ip = m_reader.GetIP(); \
- break; \
- }
- #define PROCESS_TRYBR2_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, BrReg2, suffix); \
- func((const byte*)(playout + 1), playout->RelativeJumpOffset, playout->R1, playout->R2); \
- ip = m_reader.GetIP(); \
- break; \
- }
- #define PROCESS_CALL_COMMON(name, func, layout, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, layout, suffix); \
- func(playout); \
- break; \
- }
- #define PROCESS_CALL(name, func, layout) PROCESS_CALL_COMMON(name, func, layout,)
- #define PROCESS_CALL_FLAGS_None_COMMON(name, func, layout, suffix) PROCESS_CALL_COMMON(name, func, layout, suffix)
- #define PROCESS_CALL_FLAGS_Value_COMMON(name, func, layout, suffix) PROCESS_CALL_COMMON(name, func, layout, suffix)
- #define PROCESS_A1toXX_ALLOW_STACK_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg1, suffix); \
- func(GetRegAllowStackVar(playout->R0)); \
- break; \
- }
- #define PROCESS_A1toXX_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg1, suffix); \
- func(GetReg(playout->R0)); \
- break; \
- }
- #define PROCESS_A1toXX(name, func) PROCESS_A1toXX_COMMON(name, func,)
- #define PROCESS_A1toXXMem_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg1, suffix); \
- func(GetReg(playout->R0), GetScriptContext()); \
- break; \
- }
- #define PROCESS_A1toXXMem(name, func) PROCESS_A1toXXMem_COMMON(name, func,)
- #define PROCESS_A1toXXMemNonVar_COMMON(name, func, type, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg1, suffix); \
- func((type)GetNonVarReg(playout->R0), GetScriptContext()); \
- break; \
- }
- #define PROCESS_A1toXXMemNonVar(name, func, type) PROCESS_A1toXXMemNonVar_COMMON(name, func, type,)
- #define PROCESS_XXtoA1_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg1, suffix); \
- SetReg(playout->R0, \
- func()); \
- break; \
- }
- #define PROCESS_XXtoA1(name, func) PROCESS_XXtoA1_COMMON(name, func,)
- #define PROCESS_XXtoA1NonVar_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg1, suffix); \
- SetNonVarReg(playout->R0, \
- func()); \
- break; \
- }
- #define PROCESS_XXtoA1NonVar(name, func) PROCESS_XXtoA1NonVar_COMMON(name, func,)
- #define PROCESS_XXtoA1Mem_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg1, suffix); \
- SetReg(playout->R0, \
- func(GetScriptContext())); \
- break; \
- }
- #define PROCESS_XXtoA1Mem(name, func) PROCESS_XXtoA1Mem_COMMON(name, func,)
- #define PROCESS_A1toA1_ALLOW_STACK_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg2, suffix); \
- SetRegAllowStackVar(playout->R0, \
- func(GetRegAllowStackVar(playout->R1))); \
- break; \
- }
- #define PROCESS_A1toA1_ALLOW_STACK(name, func) PROCESS_A1toA1_ALLOW_STACK_COMMON(name, func,)
- #define PROCESS_A1toA1_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg2, suffix); \
- SetReg(playout->R0, \
- func(GetReg(playout->R1))); \
- break; \
- }
- #define PROCESS_A1toA1(name, func) PROCESS_A1toA1_COMMON(name, func,)
- #define PROCESS_A1toA1Profiled_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ProfiledReg2, suffix); \
- SetReg(playout->R0, \
- func(GetReg(playout->R1), playout->profileId)); \
- break; \
- }
- #define PROCESS_A1toA1Profiled(name, func) PROCESS_A1toA1Profiled_COMMON(name, func,)
- #define PROCESS_A1toA1CallNoArg_COMMON(name, func, layout, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, layout, suffix); \
- SetReg(playout->R0, \
- func(playout)); \
- break; \
- }
- #define PROCESS_A1toA1CallNoArg(name, func, layout) PROCESS_A1toA1CallNoArg_COMMON(name, func, layout,)
- #define PROCESS_A1toA1Mem_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg2, suffix); \
- SetReg(playout->R0, \
- func(GetReg(playout->R1),GetScriptContext())); \
- break; \
- }
- #define PROCESS_A1toA1Mem(name, func) PROCESS_A1toA1Mem_COMMON(name, func,)
- #define PROCESS_A1toA1NonVar_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg2, suffix); \
- SetNonVarReg(playout->R0, \
- func(GetNonVarReg(playout->R1))); \
- break; \
- }
- #define PROCESS_A1toA1NonVar(name, func) PROCESS_A1toA1NonVar_COMMON(name, func,)
- #define PROCESS_A1toA1MemNonVar_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg2, suffix); \
- SetNonVarReg(playout->R0, \
- func(GetNonVarReg(playout->R1),GetScriptContext())); \
- break; \
- }
- #define PROCESS_A1toA1MemNonVar(name, func) PROCESS_A1toA1MemNonVar_COMMON(name, func,)
- #define PROCESS_INNERtoA1_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg1Unsigned1, suffix); \
- SetReg(playout->R0, InnerScopeFromIndex(playout->C1)); \
- break; \
- }
- #define PROCESS_INNERtoA1(name, fun) PROCESS_INNERtoA1_COMMON(name, func,)
- #define PROCESS_U1toINNERMemNonVar_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Unsigned1, suffix); \
- SetInnerScopeFromIndex(playout->C1, func(GetScriptContext())); \
- break; \
- }
- #define PROCESS_U1toINNERMemNonVar(name, func) PROCESS_U1toINNERMemNonVar_COMMON(name, func,)
- #define PROCESS_XXINNERtoA1MemNonVar_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg1Unsigned1, suffix); \
- SetNonVarReg(playout->R0, \
- func(InnerScopeFromIndex(playout->C1), GetScriptContext())); \
- break; \
- }
- #define PROCESS_XXINNERtoA1MemNonVar(name, func) PROCESS_XXINNERtoA1MemNonVar_COMMON(name, func,)
- #define PROCESS_A1INNERtoA1MemNonVar_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg2Int1, suffix); \
- SetNonVarReg(playout->R0, \
- func(InnerScopeFromIndex(playout->C1), GetNonVarReg(playout->R1), GetScriptContext())); \
- break; \
- }
- #define PROCESS_A1LOCALtoA1MemNonVar(name, func) PROCESS_A1LOCALtoA1MemNonVar_COMMON(name, func,)
- #define PROCESS_LOCALI1toA1_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg1Unsigned1, suffix); \
- SetReg(playout->R0, \
- func(this->localClosure, playout->C1)); \
- break; \
- }
- #define PROCESS_LOCALI1toA1(name, func) PROCESS_LOCALI1toA1_COMMON(name, func,)
- #define PROCESS_A1I1toA1_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg2Int1, suffix); \
- SetReg(playout->R0, \
- func(GetReg(playout->R1), playout->C1)); \
- break; \
- }
- #define PROCESS_A1I1toA1(name, func) PROCESS_A1I1toA1_COMMON(name, func,)
- #define PROCESS_A1I1toA1Mem_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg2Int1, suffix); \
- SetReg(playout->R0, \
- func(GetReg(playout->R1), playout->C1, GetScriptContext())); \
- break; \
- }
- #define PROCESS_A1I1toA1Mem(name, func) PROCESS_A1I1toA1Mem_COMMON(name, func,)
- #define PROCESS_RegextoA1_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg1Unsigned1, suffix); \
- SetReg(playout->R0, \
- func(this->m_functionBody->GetLiteralRegex(playout->C1), GetScriptContext())); \
- break; \
- }
- #define PROCESS_RegextoA1(name, func) PROCESS_RegextoA1_COMMON(name, func,)
- #define PROCESS_A2toXX_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg2, suffix); \
- func(GetReg(playout->R0), GetReg(playout->R1)); \
- break; \
- }
- #define PROCESS_A2toXX(name, func) PROCESS_A2toXX_COMMON(name, func,)
- #define PROCESS_A2toXXMemNonVar_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg2, suffix); \
- func(GetNonVarReg(playout->R0), GetNonVarReg(playout->R1), GetScriptContext()); \
- break; \
- }
- #define PROCESS_A2toXXMemNonVar(name, func) PROCESS_A2toXXMemNonVar_COMMON(name, func,)
- #define PROCESS_A1NonVarToA1_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg2, suffix); \
- SetReg(playout->R0, \
- func(GetNonVarReg(playout->R1))); \
- break; \
- }
- #define PROCESS_A2NonVarToA1Reg_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg3, suffix); \
- SetReg(playout->R0, \
- func(GetNonVarReg(playout->R1), playout->R2)); \
- break; \
- }
- #define PROCESS_A2toA1Mem_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg3, suffix); \
- SetReg(playout->R0, \
- func(GetReg(playout->R1), GetReg(playout->R2),GetScriptContext())); \
- break; \
- }
- #define PROCESS_A2toA1Mem(name, func) PROCESS_A2toA1Mem_COMMON(name, func,)
- #define PROCESS_A2toA1MemProfiled_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ProfiledReg3, suffix); \
- SetReg(playout->R0, \
- func(GetReg(playout->R1), GetReg(playout->R2),GetScriptContext(), playout->profileId)); \
- break; \
- }
- #define PROCESS_A2toA1MemProfiled(name, func) PROCESS_A2toA1MemProfiled_COMMON(name, func,)
- #define PROCESS_A2toA1NonVar_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg3, suffix); \
- SetNonVarReg(playout->R0, \
- func(GetNonVarReg(playout->R1), GetNonVarReg(playout->R2))); \
- break; \
- }
- #define PROCESS_A2toA1NonVar(name, func) PROCESS_A2toA1NonVar_COMMON(name, func,)
- #define PROCESS_A2toA1MemNonVar_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg3, suffix); \
- SetNonVarReg(playout->R0, \
- func(GetNonVarReg(playout->R1), GetNonVarReg(playout->R2),GetScriptContext())); \
- break; \
- }
- #define PROCESS_A2toA1MemNonVar(name, func) PROCESS_A2toA1MemNonVar_COMMON(name, func,)
- #define PROCESS_CMMem_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg3, suffix); \
- SetReg(playout->R0, \
- func(GetReg(playout->R1), GetReg(playout->R2), GetScriptContext()) ? JavascriptBoolean::OP_LdTrue(GetScriptContext()) : \
- JavascriptBoolean::OP_LdFalse(GetScriptContext())); \
- break; \
- }
- #define PROCESS_CMMem(name, func) PROCESS_CMMem_COMMON(name, func,)
- #define PROCESS_ELEM_RtU_to_XX_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ElementRootU, suffix); \
- func(playout->PropertyIdIndex); \
- break; \
- }
- #define PROCESS_ELEM_RtU_to_XX(name, func) PROCESS_ELEM_RtU_to_XX_COMMON(name, func,)
- #define PROCESS_ELEM_C2_to_XX_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ElementScopedC, suffix); \
- func(GetEnvForEvalCode(), playout->PropertyIdIndex, GetReg(playout->Value)); \
- break; \
- }
- #define PROCESS_ELEM_C2_to_XX(name, func) PROCESS_ELEM_C2_to_XX_COMMON(name, func,)
- #define PROCESS_GET_ELEM_SLOT_FB_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ElementSlot, suffix); \
- SetReg(playout->Value, \
- func((FrameDisplay*)GetNonVarReg(playout->Instance), this->m_functionBody->GetNestedFuncReference(playout->SlotIndex))); \
- break; \
- }
- #define PROCESS_GET_ELEM_SLOT_FB(name, func) PROCESS_GET_ELEM_SLOT_FB_COMMON(name, func,)
- #define PROCESS_GET_ELEM_SLOT_FB_HMO_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ElementSlotI3, suffix); \
- SetReg(playout->Value, \
- func((FrameDisplay*)GetNonVarReg(playout->Instance), this->m_functionBody->GetNestedFuncReference(playout->SlotIndex), GetReg(playout->HomeObj))); \
- break; \
- }
- #define PROCESS_GET_ELEM_SLOT_FB_HMO(name, func) PROCESS_GET_ELEM_SLOT_FB_HMO_COMMON(name, func,)
- #define PROCESS_GET_SLOT_FB_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ElementSlotI1, suffix); \
- SetReg(playout->Value, \
- func(this->GetFrameDisplayForNestedFunc(), this->m_functionBody->GetNestedFuncReference(playout->SlotIndex))); \
- break; \
- }
- #define PROCESS_GET_SLOT_FB(name, func) PROCESS_GET_SLOT_FB_COMMON(name, func,)
- #define PROCESS_GET_SLOT_FB_HMO_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ElementSlot, suffix); \
- SetReg(playout->Value, \
- func(this->GetFrameDisplayForNestedFunc(), this->m_functionBody->GetNestedFuncReference(playout->SlotIndex), GetReg(playout->Instance))); \
- break; \
- }
- #define PROCESS_GET_SLOT_FB_HMO(name, func) PROCESS_GET_SLOT_FB_HMO_COMMON(name, func,)
- #define PROCESS_GET_ELEM_IMem_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ElementI, suffix); \
- SetReg(playout->Value, \
- func(GetReg(playout->Instance), GetReg(playout->Element), GetScriptContext())); \
- break; \
- }
- #define PROCESS_GET_ELEM_IMem(name, func) PROCESS_GET_ELEM_IMem_COMMON(name, func,)
- #define PROCESS_GET_ELEM_IMem_Strict_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ElementI, suffix); \
- SetReg(playout->Value, \
- func(GetReg(playout->Instance), GetReg(playout->Element), GetScriptContext(), PropertyOperation_StrictMode)); \
- break; \
- }
- #define PROCESS_GET_ELEM_IMem_Strict(name, func) PROCESS_GET_ELEM_IMem_Strict_COMMON(name, func,)
- #define PROCESS_BR(name, func) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Br,); \
- ip = func(playout); \
- break; \
- }
- #ifdef BYTECODE_BRANCH_ISLAND
- #define PROCESS_BRLONG(name, func) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, BrLong,); \
- ip = func(playout); \
- break; \
- }
- #endif
- #define PROCESS_BRS(name,func) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, BrS,); \
- if (func(playout->val,GetScriptContext())) \
- { \
- ip = m_reader.SetCurrentRelativeOffset(ip, playout->RelativeJumpOffset); \
- } \
- break; \
- }
- #define PROCESS_BRB_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, BrReg1, suffix); \
- if (func(GetReg(playout->R1))) \
- { \
- ip = m_reader.SetCurrentRelativeOffset(ip, playout->RelativeJumpOffset); \
- } \
- break; \
- }
- #define PROCESS_BRB(name, func) PROCESS_BRB_COMMON(name, func,)
- #define PROCESS_BRB_ALLOW_STACK_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, BrReg1, suffix); \
- if (func(GetRegAllowStackVar(playout->R1))) \
- { \
- ip = m_reader.SetCurrentRelativeOffset(ip, playout->RelativeJumpOffset); \
- } \
- break; \
- }
- #define PROCESS_BRB_ALLOW_STACK(name, func) PROCESS_BRB_ALLOW_STACK_COMMON(name, func,)
- #define PROCESS_BRBS_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, BrReg1, suffix); \
- if (func(GetReg(playout->R1), GetScriptContext())) \
- { \
- ip = m_reader.SetCurrentRelativeOffset(ip, playout->RelativeJumpOffset); \
- } \
- break; \
- }
- #define PROCESS_BRBS(name, func) PROCESS_BRBS_COMMON(name, func,)
- #define PROCESS_BRBReturnP1toA1_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, BrReg1Unsigned1, suffix); \
- SetReg(playout->R1, func(GetForInEnumerator(playout->C2))); \
- if (!GetReg(playout->R1)) \
- { \
- ip = m_reader.SetCurrentRelativeOffset(ip, playout->RelativeJumpOffset); \
- } \
- break; \
- }
- #define PROCESS_BRBReturnP1toA1(name, func) PROCESS_BRBReturnP1toA1_COMMON(name, func,)
- #define PROCESS_BRBMem_ALLOW_STACK_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, BrReg1, suffix); \
- if (func(GetRegAllowStackVar(playout->R1),GetScriptContext())) \
- { \
- ip = m_reader.SetCurrentRelativeOffset(ip, playout->RelativeJumpOffset); \
- } \
- break; \
- }
- #define PROCESS_BRBMem_ALLOW_STACK(name, func) PROCESS_BRBMem_ALLOW_STACK_COMMON(name, func,)
- #define PROCESS_BRCMem_COMMON(name, func,suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, BrReg2, suffix); \
- if (func(GetReg(playout->R1), GetReg(playout->R2),GetScriptContext())) \
- { \
- ip = m_reader.SetCurrentRelativeOffset(ip, playout->RelativeJumpOffset); \
- } \
- break; \
- }
- #define PROCESS_BRCMem(name, func) PROCESS_BRCMem_COMMON(name, func,)
- #define PROCESS_BRPROP(name, func) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, BrProperty,); \
- if (func(GetReg(playout->Instance), playout->PropertyIdIndex, GetScriptContext())) \
- { \
- ip = m_reader.SetCurrentRelativeOffset(ip, playout->RelativeJumpOffset); \
- } \
- break; \
- }
- #define PROCESS_BRLOCALPROP(name, func) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, BrLocalProperty,); \
- if (func(this->localClosure, playout->PropertyIdIndex, GetScriptContext())) \
- { \
- ip = m_reader.SetCurrentRelativeOffset(ip, playout->RelativeJumpOffset); \
- } \
- break; \
- }
- #define PROCESS_BRENVPROP(name, func) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, BrEnvProperty,); \
- if (func(LdEnv(), playout->SlotIndex, playout->PropertyIdIndex, GetScriptContext())) \
- { \
- ip = m_reader.SetCurrentRelativeOffset(ip, playout->RelativeJumpOffset); \
- } \
- break; \
- }
- #define PROCESS_W1(name, func) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, W1,); \
- func(playout->C1, GetScriptContext()); \
- break; \
- }
- #define PROCESS_U1toA1_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg1Unsigned1, suffix); \
- SetReg(playout->R0, \
- func(playout->C1,GetScriptContext())); \
- break; \
- }
- #define PROCESS_U1toA1(name, func) PROCESS_U1toA1_COMMON(name, func,)
- #define PROCESS_U1toA1NonVar_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg1Unsigned1, suffix); \
- SetNonVarReg(playout->R0, \
- func(playout->C1)); \
- break; \
- }
- #define PROCESS_U1toA1NonVar(name, func) PROCESS_U1toA1NonVar_COMMON(name, func,)
- #define PROCESS_U1toA1NonVar_FuncBody_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg1Unsigned1, suffix); \
- SetNonVarReg(playout->R0, \
- func(playout->C1,GetScriptContext(), this->m_functionBody)); \
- break; \
- }
- #define PROCESS_U1toA1NonVar_FuncBody(name, func) PROCESS_U1toA1NonVar_FuncBody_COMMON(name, func,)
- #define PROCESS_A1I2toXXNonVar_FuncBody(name, func) PROCESS_A1I2toXXNonVar_FuncBody_COMMON(name, func,)
- #define PROCESS_A1I2toXXNonVar_FuncBody_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg3, suffix); \
- func(playout->R0, playout->R1, playout->R2, GetScriptContext(), this->m_functionBody); \
- break; \
- }
- #define PROCESS_A1U1toXX_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg1Unsigned1, suffix); \
- func(GetReg(playout->R0), playout->C1); \
- break; \
- }
- #define PROCESS_A1U1toXX(name, func) PROCESS_A1U1toXX_COMMON(name, func,)
- #define PROCESS_A1U1toXXWithCache_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ProfiledReg1Unsigned1, suffix); \
- func(GetReg(playout->R0), playout->C1, playout->profileId); \
- break; \
- }
- #define PROCESS_A1U1toXXWithCache(name, func) PROCESS_A1U1toXXWithCache_COMMON(name, func,)
- #define PROCESS_EnvU1toXX_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Unsigned1, suffix); \
- func(LdEnv(), playout->C1); \
- break; \
- }
- #define PROCESS_EnvU1toXX(name, func) PROCESS_EnvU1toXX_COMMON(name, func,)
- #define PROCESS_GET_ELEM_SLOTNonVar_COMMON(name, func, layout, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, layout, suffix); \
- SetNonVarReg(playout->Value, func(GetNonVarReg(playout->Instance), playout)); \
- break; \
- }
- #define PROCESS_GET_ELEM_SLOTNonVar(name, func, layout) PROCESS_GET_ELEM_SLOTNonVar_COMMON(name, func, layout,)
- #define PROCESS_GET_ELEM_LOCALSLOTNonVar_COMMON(name, func, layout, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, layout, suffix); \
- SetNonVarReg(playout->Value, func((Var*)GetLocalClosure(), playout)); \
- break; \
- }
- #define PROCESS_GET_ELEM_LOCALSLOTNonVar(name, func, layout) PROCESS_GET_ELEM_LOCALSLOTNonVar_COMMON(name, func, layout,)
- #define PROCESS_GET_ELEM_PARAMSLOTNonVar_COMMON(name, func, layout, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, layout, suffix); \
- SetNonVarReg(playout->Value, func((Var*)GetParamClosure(), playout)); \
- break; \
- }
- #define PROCESS_GET_ELEM_PARAMSLOTNonVar(name, func, layout) PROCESS_GET_ELEM_PARAMSLOTNonVar_COMMON(name, func, layout,)
- #define PROCESS_GET_ELEM_INNERSLOTNonVar_COMMON(name, func, layout, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, layout, suffix); \
- SetNonVarReg(playout->Value, func(InnerScopeFromIndex(playout->SlotIndex1), playout)); \
- break; \
- }
- #define PROCESS_GET_ELEM_INNERSLOTNonVar(name, func, layout) PROCESS_GET_ELEM_INNERSLOTNonVar_COMMON(name, func, layout,)
- #define PROCESS_GET_ELEM_ENVSLOTNonVar_COMMON(name, func, layout, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, layout, suffix); \
- SetNonVarReg(playout->Value, func(LdEnv(), playout)); \
- break; \
- }
- #define PROCESS_GET_ELEM_ENVSLOTNonVar(name, func, layout) PROCESS_GET_ELEM_ENVSLOTNonVar_COMMON(name, func, layout,)
- #define PROCESS_SET_ELEM_SLOTNonVar_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ElementSlot, suffix); \
- func(GetNonVarReg(playout->Instance), playout->SlotIndex, GetRegAllowStackVarEnableOnly(playout->Value)); \
- break; \
- }
- #define PROCESS_SET_ELEM_SLOTNonVar(name, func) PROCESS_SET_ELEM_SLOTNonVar_COMMON(name, func,)
- #define PROCESS_SET_ELEM_LOCALSLOTNonVar_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ElementSlotI1, suffix); \
- func((Var*)GetLocalClosure(), playout->SlotIndex, GetRegAllowStackVarEnableOnly(playout->Value)); \
- break; \
- }
- #define PROCESS_SET_ELEM_LOCALSLOTNonVar(name, func) PROCESS_SET_ELEM_LOCALSLOTNonVar_COMMON(name, func,)
- #define PROCESS_SET_ELEM_PARAMSLOTNonVar_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ElementSlotI1, suffix); \
- func((Var*)GetParamClosure(), playout->SlotIndex, GetRegAllowStackVarEnableOnly(playout->Value)); \
- break; \
- }
- #define PROCESS_SET_ELEM_PARAMSLOTNonVar(name, func) PROCESS_SET_ELEM_PARAMSLOTNonVar_COMMON(name, func,); \
- #define PROCESS_SET_ELEM_INNERSLOTNonVar_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ElementSlotI2, suffix); \
- func(InnerScopeFromIndex(playout->SlotIndex1), playout->SlotIndex2, GetRegAllowStackVarEnableOnly(playout->Value)); \
- break; \
- }
- #define PROCESS_SET_ELEM_INNERSLOTNonVar(name, func) PROCESS_SET_ELEM_INNERSLOTNonVar_COMMON(name, func,)
- #define PROCESS_SET_ELEM_ENVSLOTNonVar_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, ElementSlotI2, suffix); \
- func(LdEnv(), playout->SlotIndex1, playout->SlotIndex2, GetRegAllowStackVarEnableOnly(playout->Value)); \
- break; \
- }
- #define PROCESS_SET_ELEM_ENVSLOTNonVar(name, func) PROCESS_SET_ELEM_ENVSLOTNonVar_COMMON(name, func,)
- /*---------------------------------------------------------------------------------------------- */
- #define PROCESS_A3toA1Mem_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg4, suffix); \
- SetReg(playout->R0, \
- func(GetReg(playout->R1), GetReg(playout->R2), GetReg(playout->R3), GetScriptContext())); \
- break; \
- }
- #define PROCESS_A3toA1Mem(name, func) PROCESS_A3toA1Mem_COMMON(name, func,)
- /*---------------------------------------------------------------------------------------------- */
- #define PROCESS_A2I1toA1Mem_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg3B1, suffix); \
- SetReg(playout->R0, \
- func(GetReg(playout->R1), GetReg(playout->R2), playout->B3, GetScriptContext())); \
- break; \
- }
- #define PROCESS_A2I1toA1Mem(name, func) PROCESS_A2I1toA1Mem_COMMON(name, func,)
- /*---------------------------------------------------------------------------------------------- */
- #define PROCESS_A2I1toXXMem_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg2B1, suffix); \
- func(GetReg(playout->R0), GetReg(playout->R1), playout->B2, scriptContext); \
- break; \
- }
- #define PROCESS_A2I1toXXMem(name, func) PROCESS_A2I1toXXMem_COMMON(name, func,)
- /*---------------------------------------------------------------------------------------------- */
- #define PROCESS_A3I1toXXMem_COMMON(name, func, suffix) \
- case OpCode::name: \
- { \
- PROCESS_READ_LAYOUT(name, Reg3B1, suffix); \
- func(GetReg(playout->R0), GetReg(playout->R1), GetReg(playout->R2), playout->B3, scriptContext); \
- break; \
- }
- #define PROCESS_A3I1toXXMem(name, func) PROCESS_A3I1toXXMem_COMMON(name, func,)
- #if ENABLE_PROFILE_INFO
- #define PROCESS_IP_TARG_IMPL(name, func, layoutSize) \
- case OpCode::name: \
- { \
- Assert(!switchProfileMode); \
- ip = func<layoutSize, INTERPRETERPROFILE>(ip); \
- if(switchProfileMode) \
- { \
- m_reader.SetIP(ip); \
- return nullptr; \
- } \
- break; \
- }
- #else
- #define PROCESS_IP_TARG_IMPL(name, func, layoutSize) \
- case OpCode::name: \
- { \
- ip = func<layoutSize, INTERPRETERPROFILE>(ip); \
- break; \
- }
- #endif
- #define PROCESS_IP_TARG_COMMON(name, func, suffix) PROCESS_IP_TARG##suffix(name, func)
- #define PROCESS_IP_TARG_Large(name, func) PROCESS_IP_TARG_IMPL(name, func, Js::LargeLayout)
- #define PROCESS_IP_TARG_Medium(name, func) PROCESS_IP_TARG_IMPL(name, func, Js::MediumLayout)
- #define PROCESS_IP_TARG_Small(name, func) PROCESS_IP_TARG_IMPL(name, func, Js::SmallLayout)
- #if ENABLE_TTD
- #if ENABLE_TTD_DIAGNOSTICS_TRACING
- #define SHOULD_DO_TTD_STACK_STMT_OP(CTX) ((CTX)->ShouldPerformRecordOrReplayAction())
- #else
- #define SHOULD_DO_TTD_STACK_STMT_OP(CTX) ((CTX)->ShouldPerformRecordOrReplayDebuggerAction())
- #endif
- #endif
- namespace Js
- {
- Var InterpreterStackFrame::InnerScopeFromRegSlot(RegSlot reg) const
- {
- return InnerScopeFromIndex(reg - m_functionBody->GetFirstInnerScopeRegister());
- }
- Var InterpreterStackFrame::InnerScopeFromIndex(uint32 index) const
- {
- if (index >= m_functionBody->GetInnerScopeCount())
- {
- AssertMsg(false, "Illegal byte code: bad inner scope index");
- Js::Throw::FatalInternalError();
- }
- Assert(this->innerScopeArray != nullptr);
- return this->innerScopeArray[index];
- }
- void InterpreterStackFrame::SetInnerScopeFromIndex(uint32 index, Var scope)
- {
- if (index >= m_functionBody->GetInnerScopeCount())
- {
- AssertMsg(false, "Illegal byte code: bad inner scope index");
- Js::Throw::FatalInternalError();
- }
- Assert(this->innerScopeArray != nullptr);
- this->innerScopeArray[index] = scope;
- }
- const int k_stackFrameVarCount = (sizeof(InterpreterStackFrame) + sizeof(Var) - 1) / sizeof(Var);
- InterpreterStackFrame::Setup::Setup(Js::ScriptFunction * function, Js::Arguments& args, bool bailedOut, bool inlinee)
- : function(function), inParams(args.Values), inSlotsCount(args.Info.Count), executeFunction(function->GetFunctionBody()), callFlags(args.Info.Flags), bailedOutOfInlinee(inlinee), bailedOut(bailedOut)
- {
- SetupInternal();
- }
- InterpreterStackFrame::Setup::Setup(Js::ScriptFunction * function, Var * inParams, int inSlotsCount)
- : function(function), inParams(inParams), inSlotsCount(inSlotsCount), executeFunction(function->GetFunctionBody()), callFlags(CallFlags_None), bailedOutOfInlinee(false), bailedOut(false)
- {
- SetupInternal();
- }
- void InterpreterStackFrame::Setup::SetupInternal()
- {
- if (this->function->GetHasInlineCaches() && Js::ScriptFunctionWithInlineCache::Is(this->function))
- {
- this->inlineCaches = (void**)Js::ScriptFunctionWithInlineCache::FromVar(this->function)->GetInlineCaches();
- Assert(this->inlineCaches != nullptr);
- }
- else
- {
- this->inlineCaches = this->executeFunction->GetInlineCaches();
- }
- this->inlineCacheCount = this->executeFunction->GetInlineCacheCount();
- //
- // Compute the amount of memory needed on the stack:
- // - We compute this in 'Atoms' instead of 'bytes' to keep everything natural word aligned.
- //
- this->localCount = this->executeFunction->GetLocalsCount();
- uint extraVarCount = 0;
- #if ENABLE_PROFILE_INFO
- if (Js::DynamicProfileInfo::EnableImplicitCallFlags(this->executeFunction))
- {
- extraVarCount += (sizeof(ImplicitCallFlags) * this->executeFunction->GetLoopCount() + sizeof(Var) - 1) / sizeof(Var);
- }
- #endif
- // If we bailed out, we will use the JIT frame's for..in enumerators
- uint forInVarCount = bailedOut ? 0 : (this->executeFunction->GetForInLoopDepth() * (sizeof(Js::ForInObjectEnumerator) / sizeof(Var)));
- this->varAllocCount = k_stackFrameVarCount + localCount + this->executeFunction->GetOutParamMaxDepth() + forInVarCount +
- extraVarCount + this->executeFunction->GetInnerScopeCount();
- if (this->executeFunction->DoStackNestedFunc() && this->executeFunction->GetNestedCount() != 0)
- {
- // Track stack funcs...
- this->varAllocCount += (sizeof(StackScriptFunction) * this->executeFunction->GetNestedCount()) / sizeof(Var);
- if (!this->bailedOutOfInlinee)
- {
- // Frame display (if environment depth is statically known)...
- if (this->executeFunction->DoStackFrameDisplay())
- {
- uint16 envDepth = this->executeFunction->GetEnvDepth();
- Assert(envDepth != (uint16)-1);
- this->varAllocCount += sizeof(FrameDisplay) / sizeof(Var) + (envDepth + 1);
- }
- // ...and scope slots (if any)
- if (this->executeFunction->DoStackScopeSlots())
- {
- uint32 scopeSlots = this->executeFunction->scopeSlotArraySize;
- Assert(scopeSlots != 0);
- this->varAllocCount += scopeSlots + Js::ScopeSlots::FirstSlotIndex;
- }
- }
- }
- }
- InterpreterStackFrame *
- InterpreterStackFrame::Setup::InitializeAllocation(__in_ecount(varAllocCount) Var * allocation, bool initParams, bool profileParams, LoopHeader* loopHeaderArray, DWORD_PTR stackAddr
- #if DBG
- , Var invalidStackVar
- #endif
- )
- {
- //
- // Initialize the new InterpreterStackFrame instance on the program stack.
- //
- //This will fail if InterpreterStackFrame ever gets a non-empty ctor (you'll need to use
- //placement_new(allocation, InterpreterStackFrame) instead, though that will cause problems
- //if the placement_new is surrounded by a try/finally since this would mix C++/SEH exception
- //handling.
- __analysis_assume(varAllocCount >= k_stackFrameVarCount + localCount);
- InterpreterStackFrame* newInstance = (InterpreterStackFrame*)allocation;
- newInstance->scriptContext = this->executeFunction->GetScriptContext();
- newInstance->m_inSlotsCount = this->inSlotsCount;
- newInstance->m_inParams = this->inParams;
- newInstance->m_callFlags = this->callFlags;
- newInstance->m_outParams = newInstance->m_localSlots + localCount;
- newInstance->m_outSp = newInstance->m_outParams;
- newInstance->m_outSpCached = nullptr;
- newInstance->m_arguments = NULL;
- newInstance->function = this->function;
- newInstance->m_functionBody = this->executeFunction;
- newInstance->inlineCaches = this->inlineCaches;
- newInstance->inlineCacheCount = this->inlineCacheCount;
- newInstance->currentLoopNum = LoopHeader::NoLoop;
- newInstance->currentLoopCounter = 0;
- newInstance->m_flags = InterpreterStackFrameFlags_None;
- newInstance->closureInitDone = false;
- newInstance->isParamScopeDone = false;
- newInstance->shouldCacheSP = true;
- #if ENABLE_PROFILE_INFO
- newInstance->switchProfileMode = false;
- newInstance->isAutoProfiling = false;
- newInstance->switchProfileModeOnLoopEndNumber = 0u - 1;
- #endif
- newInstance->ehBailoutData = nullptr;
- newInstance->nestedTryDepth = -1;
- newInstance->nestedCatchDepth = -1;
- newInstance->nestedFinallyDepth = -1;
- newInstance->retOffset = 0;
- newInstance->localFrameDisplay = nullptr;
- newInstance->localClosure = nullptr;
- newInstance->paramClosure = nullptr;
- newInstance->innerScopeArray = nullptr;
- newInstance->m_asmJsBuffer = nullptr;
- #ifdef ENABLE_WASM
- newInstance->m_wasmMemory = nullptr;
- #endif
- Var* outparamsEnd = newInstance->m_outParams + this->executeFunction->GetOutParamMaxDepth();
- #if DBG
- newInstance->m_outParamsEnd = outparamsEnd;
- #endif
- bool doInterruptProbe = newInstance->scriptContext->GetThreadContext()->DoInterruptProbe(this->executeFunction);
- #if ENABLE_NATIVE_CODEGEN
- bool doJITLoopBody =
- !this->executeFunction->GetScriptContext()->GetConfig()->IsNoNative() &&
- !(this->executeFunction->GetHasTry() && (PHASE_OFF((Js::JITLoopBodyInTryCatchPhase), this->executeFunction))) &&
- !(this->executeFunction->GetHasFinally() && (PHASE_OFF((Js::JITLoopBodyInTryFinallyPhase), this->executeFunction))) &&
- (this->executeFunction->ForceJITLoopBody() || this->executeFunction->IsJitLoopBodyPhaseEnabled()) &&
- !this->executeFunction->IsInDebugMode();
- #endif
- // Pick a version of the LoopBodyStart OpCode handlers that is hardcoded to do loop body JIT and
- // interrupt probes as needed.
- if (doInterruptProbe)
- {
- #if ENABLE_NATIVE_CODEGEN
- if (doJITLoopBody)
- {
- newInstance->opProfiledLoopBodyStart = &InterpreterStackFrame::ProfiledLoopBodyStart<true, true>;
- newInstance->opLoopBodyStart = &InterpreterStackFrame::LoopBodyStart<true, true>;
- }
- else
- #endif
- {
- #if ENABLE_PROFILE_INFO
- newInstance->opProfiledLoopBodyStart = &InterpreterStackFrame::ProfiledLoopBodyStart<true, false>;
- #endif
- newInstance->opLoopBodyStart = &InterpreterStackFrame::LoopBodyStart<true, false>;
- }
- }
- else
- {
- #if ENABLE_NATIVE_CODEGEN
- if (doJITLoopBody)
- {
- newInstance->opProfiledLoopBodyStart = &InterpreterStackFrame::ProfiledLoopBodyStart<false, true>;
- newInstance->opLoopBodyStart = &InterpreterStackFrame::LoopBodyStart<false, true>;
- }
- else
- #endif
- {
- #if ENABLE_PROFILE_INFO
- newInstance->opProfiledLoopBodyStart = &InterpreterStackFrame::ProfiledLoopBodyStart<false, false>;
- #endif
- newInstance->opLoopBodyStart = &InterpreterStackFrame::LoopBodyStart<false, false>;
- }
- }
- newInstance->loopHeaderArray = loopHeaderArray;
- newInstance->m_stackAddress = stackAddr;
- #if ENABLE_PROFILE_INFO
- // the savedLoopImplicitCallFlags is allocated at the end of the out param array
- newInstance->savedLoopImplicitCallFlags = nullptr;
- #endif
- char * nextAllocBytes = (char *)(outparamsEnd);
- // If we bailed out, we will use the JIT frame's for..in enumerators
- if (bailedOut || this->executeFunction->GetForInLoopDepth() == 0)
- {
- newInstance->forInObjectEnumerators = nullptr;
- }
- else
- {
- newInstance->forInObjectEnumerators = (ForInObjectEnumerator *)nextAllocBytes;
- nextAllocBytes += sizeof(ForInObjectEnumerator) * this->executeFunction->GetForInLoopDepth();
- }
- if (this->executeFunction->GetInnerScopeCount())
- {
- newInstance->innerScopeArray = (Var*)nextAllocBytes;
- nextAllocBytes += this->executeFunction->GetInnerScopeCount() * sizeof(Var);
- }
- if (this->executeFunction->DoStackNestedFunc() && this->executeFunction->GetNestedCount() != 0)
- {
- newInstance->InitializeStackFunctions((StackScriptFunction *)nextAllocBytes);
- nextAllocBytes = nextAllocBytes + sizeof(StackScriptFunction) * this->executeFunction->GetNestedCount();
- if (!this->bailedOutOfInlinee)
- {
- if (this->executeFunction->DoStackFrameDisplay())
- {
- uint16 envDepth = this->executeFunction->GetEnvDepth();
- Assert(envDepth != (uint16)-1);
- newInstance->localFrameDisplay = (FrameDisplay*)nextAllocBytes;
- newInstance->localFrameDisplay->SetLength(0); // Start with no scopes. It will get set in NewFrameDisplay
- nextAllocBytes += sizeof(FrameDisplay) + (envDepth + 1) * sizeof(Var);
- }
- if (this->executeFunction->DoStackScopeSlots())
- {
- uint32 scopeSlots = this->executeFunction->scopeSlotArraySize;
- Assert(scopeSlots != 0);
- ScopeSlots((Field(Var)*)nextAllocBytes).SetCount(0); // Start with count as 0. It will get set in NewScopeSlots
- newInstance->localClosure = nextAllocBytes;
- nextAllocBytes += (scopeSlots + ScopeSlots::FirstSlotIndex) * sizeof(Var);
- }
- }
- }
- #if ENABLE_PROFILE_INFO
- if (Js::DynamicProfileInfo::EnableImplicitCallFlags(this->executeFunction))
- {
- /*
- __analysis_assume(varAllocCount == (k_stackFrameVarCount + localCount + executeFunction->GetOutParamMaxDepth()
- + ((sizeof(ImplicitCallFlags) * executeFunction->GetLoopCount() + sizeof(Var) - 1) / sizeof(Var))));
- */
- newInstance->savedLoopImplicitCallFlags = (ImplicitCallFlags *)nextAllocBytes;
- for (uint i = 0; i < this->executeFunction->GetLoopCount(); i++)
- {
- #pragma prefast(suppress:26015, "Above analysis assume doesn't work")
- newInstance->savedLoopImplicitCallFlags[i] = ImplicitCall_None;
- }
- }
- #endif
- #if DBG
- if (CONFIG_ISENABLED(InitializeInterpreterSlotsWithInvalidStackVarFlag))
- {
- // Fill the local slots with the invalid stack var so that we will crash deterministically if something goes wrong
- for (uint i = 0; i < localCount; ++i)
- {
- newInstance->m_localSlots[i] = invalidStackVar;
- }
- }
- else
- {
- memset(newInstance->m_localSlots, 0, sizeof(Js::Var) * localCount);
- }
- #else
- if (newInstance->m_functionBody->IsInDebugMode())
- {
- // In the debug mode zero out the local slot, so this could prevent locals being uninitialized in the case of setNextStatement.
- memset(newInstance->m_localSlots, 0, sizeof(Js::Var) * localCount);
- }
- else
- {
- Js::RegSlot varCount = function->GetFunctionBody()->GetVarCount();
- if (varCount)
- {
- // Zero out the non-constant var slots.
- Js::RegSlot constantCount = function->GetFunctionBody()->GetConstantCount();
- memset(newInstance->m_localSlots + constantCount, 0, varCount * sizeof(Js::Var));
- }
- // Zero out the return slot. This is not a user local, so the byte code will not initialize
- // it to "undefined". And it's not an expression temp, so, for instance, a jitted loop body may expect
- // it to be valid on entry to the loop, where "valid" means either a var or null.
- newInstance->SetNonVarReg(0, NULL);
- }
- #endif
- // Wasm doesn't use const table
- if (!executeFunction->IsWasmFunction())
- {
- // Initialize the low end of the local slots from the constant table.
- // Skip the slot for the return value register.
- this->executeFunction->InitConstantSlots(&newInstance->m_localSlots[FunctionBody::FirstRegSlot]);
- }
- // Set local FD/SS pointers to null until after we've successfully probed the stack in the process loop.
- // That way we avoid trying to box these structures before they've been initialized in the byte code.
- if (this->executeFunction->DoStackFrameDisplay())
- {
- newInstance->SetNonVarReg(executeFunction->GetLocalFrameDisplayRegister(), nullptr);
- }
- if (this->executeFunction->DoStackScopeSlots())
- {
- Assert(!executeFunction->HasScopeObject());
- newInstance->SetNonVarReg(executeFunction->GetLocalClosureRegister(), nullptr);
- }
- Var *prestDest = &newInstance->m_localSlots[this->executeFunction->GetConstantCount()];
- if (initParams)
- {
- #if ENABLE_PROFILE_INFO
- Assert(!this->executeFunction->NeedEnsureDynamicProfileInfo());
- #endif
- if (profileParams)
- {
- #if ENABLE_PROFILE_INFO
- Assert(this->executeFunction->HasExecutionDynamicProfileInfo());
- #endif
- FunctionBody* functionBody = this->executeFunction;
- InitializeParams(newInstance, [functionBody](Var param, ArgSlot index)
- {
- #if ENABLE_PROFILE_INFO
- functionBody->GetDynamicProfileInfo()->RecordParameterInfo(functionBody, index - 1, param);
- #endif
- }, &prestDest);
- }
- else
- {
- InitializeParams(newInstance, [](Var param, ArgSlot index) {}, &prestDest);
- }
- }
- if (this->executeFunction->GetHasRestParameter())
- {
- InitializeRestParam(newInstance, prestDest);
- }
- Js::RegSlot envReg = executeFunction->GetEnvRegister();
- if (envReg != Js::Constants::NoRegister && envReg < executeFunction->GetConstantCount())
- {
- Assert(this->executeFunction->GetThisRegisterForEventHandler() == Constants::NoRegister);
- // The correct FD (possibly distinct from the one on the function) is passed in the constant table.
- this->function->SetEnvironment((Js::FrameDisplay*)newInstance->GetNonVarReg(envReg));
- }
- return newInstance;
- }
- template <class Fn>
- void InterpreterStackFrame::Setup::InitializeParams(InterpreterStackFrame * newInstance, Fn callback, Var **pprestDest)
- {
- ArgSlot requiredInParamCount = executeFunction->GetInParamsCount();
- Assert(requiredInParamCount > 1);
- if (this->inSlotsCount >= requiredInParamCount)
- {
- Var * pArg = &newInstance->m_localSlots[executeFunction->GetConstantCount()];
- Var * paGivenSrc = this->inParams + 1;
- ArgSlot paramIndex = 1;
- do
- {
- Var src = *paGivenSrc++;
- callback(src, paramIndex);
- *pArg++ = src;
- paramIndex++;
- } while (paramIndex < requiredInParamCount);
- *pprestDest = pArg;
- }
- else
- {
- InitializeParamsAndUndef(newInstance, callback, pprestDest);
- }
- }
- template <class Fn>
- void InterpreterStackFrame::Setup::InitializeParamsAndUndef(InterpreterStackFrame * newInstance, Fn callback, Var **pprestDest)
- {
- Var * pArg = &newInstance->m_localSlots[executeFunction->GetConstantCount()];
- Var * paGivenSrc = this->inParams + 1;
- ArgSlot requiredInParamCount = executeFunction->GetInParamsCount();
- ArgSlot paramIndex = 1;
- while (paramIndex < this->inSlotsCount)
- {
- Var src = *paGivenSrc++;
- callback(src, paramIndex);
- *pArg++ = src;
- paramIndex++;
- }
- Var varUndef = executeFunction->GetScriptContext()->GetLibrary()->GetUndefined();
- do
- {
- callback(varUndef, paramIndex);
- *pArg++ = varUndef;
- paramIndex++;
- } while (paramIndex < requiredInParamCount);
- *pprestDest = pArg;
- }
- void InterpreterStackFrame::Setup::InitializeRestParam(InterpreterStackFrame * newInstance, Var *dest)
- {
- Var *src = this->inParams + executeFunction->GetInParamsCount();
- if (this->inSlotsCount > executeFunction->GetInParamsCount())
- {
- // Create the rest array and copy the args directly into the contiguous head segment.
- int excess = this->inSlotsCount - executeFunction->GetInParamsCount();
- *dest = JavascriptArray::OP_NewScArray(excess, executeFunction->GetScriptContext());
- JavascriptArray *array = static_cast<JavascriptArray *>(*dest);
- Field(Var)* elements = SparseArraySegment<Var>::From(array->GetHead())->elements;
- CopyArray(elements, excess, src, excess);
- }
- else
- {
- // Rest is an empty array when there are no excess parameters.
- *dest = JavascriptArray::OP_NewScArray(0, executeFunction->GetScriptContext());
- }
- }
- FrameDisplay * InterpreterStackFrame::GetEnvForEvalCode()
- {
- FrameDisplay *pScope;
- if (m_functionBody->GetIsStrictMode() && m_functionBody->GetIsGlobalFunc())
- {
- pScope = this->GetLocalFrameDisplay();
- }
- else
- {
- pScope = (FrameDisplay*)this->LdEnv();
- }
- return pScope;
- }
- void InterpreterStackFrame::InitializeClosures()
- {
- FunctionBody *executeFunction = this->function->GetFunctionBody();
- Var environment;
- if (executeFunction->IsParamAndBodyScopeMerged())
- {
- this->SetIsParamScopeDone(true);
- }
- RegSlot thisRegForEventHandler = executeFunction->GetThisRegisterForEventHandler();
- if (thisRegForEventHandler != Constants::NoRegister)
- {
- Var varThis = OP_ArgIn0();
- SetReg(thisRegForEventHandler, varThis);
- environment = JavascriptOperators::OP_LdHandlerScope(varThis, GetScriptContext());
- this->SetEnv((FrameDisplay*)environment);
- }
- else if (this->paramClosure != nullptr)
- {
- // When paramClosure is non-null we are calling this method to initialize the closure for body scope.
- // In this case we have to use the param scope's closure as the parent for the body scope's frame display.
- Assert(!executeFunction->IsParamAndBodyScopeMerged());
- environment = this->GetLocalFrameDisplay();
- }
- else
- {
- environment = this->LdEnv();
- }
- Var funcExprScope = nullptr;
- Js::RegSlot funcExprScopeReg = executeFunction->GetFuncExprScopeRegister();
- if (funcExprScopeReg != Constants::NoRegister && this->paramClosure == nullptr)
- {
- // t0 = NewPseudoScope
- // t1 = LdFrameDisplay t0 env
- funcExprScope = JavascriptOperators::OP_NewPseudoScope(GetScriptContext());
- SetReg(funcExprScopeReg, funcExprScope);
- }
- RegSlot closureReg = executeFunction->GetLocalClosureRegister();
- if (closureReg != Js::Constants::NoRegister)
- {
- Assert(closureReg >= executeFunction->GetConstantCount());
- if (executeFunction->HasScopeObject())
- {
- this->NewScopeObject();
- }
- else
- {
- this->NewScopeSlots();
- }
- this->SetNonVarReg(closureReg, nullptr);
- }
- Js::RegSlot frameDisplayReg = executeFunction->GetLocalFrameDisplayRegister();
- if (frameDisplayReg != Js::Constants::NoRegister)
- {
- Assert(frameDisplayReg >= executeFunction->GetConstantCount());
- if (funcExprScope != nullptr)
- {
- environment = OP_LdFrameDisplay(funcExprScope, environment, GetScriptContext());
- }
- if (closureReg != Js::Constants::NoRegister)
- {
- void *argHead = this->GetLocalClosure();
- environment = this->NewFrameDisplay(argHead, environment);
- }
- this->SetLocalFrameDisplay((Js::FrameDisplay*)environment);
- this->SetNonVarReg(frameDisplayReg, nullptr);
- }
- this->closureInitDone = true;
- }
- #ifdef _M_IX86
- #ifdef ASMJS_PLAT
- int InterpreterStackFrame::GetAsmJsArgSize(AsmJsCallStackLayout* stack)
- {
- JavascriptFunction * func = stack->functionObject;
- AsmJsFunctionInfo* asmInfo = func->GetFunctionBody()->GetAsmJsFunctionInfo();
- uint argSize = (uint)(asmInfo->GetArgByteSize());
- argSize = ::Math::Align<int32>(argSize, 8);
- // 2 * sizeof(Var) is for functionObject, and another push that DynamicInterpreterThunk does
- return argSize + 2 * sizeof(Var);
- }
- int InterpreterStackFrame::GetDynamicRetType(AsmJsCallStackLayout* stack)
- {
- return GetRetType(stack->functionObject);
- }
- int InterpreterStackFrame::GetRetType(JavascriptFunction* func)
- {
- AsmJsFunctionInfo* asmInfo = func->GetFunctionBody()->GetAsmJsFunctionInfo();
- return asmInfo->GetReturnType().which();
- }
- #ifdef ASMJS_PLAT
- /*
- AsmInterpreterThunk
- -------------------
- This is the entrypoint for all Asm Interpreter calls (external and internal)
- TODO - Make this a dynamic Interpreter thunk to support ETW
- Functionality:
- 1) Prolog
- 2) call AsmInterpreter passing the function object
- 3) Get The return type
- 4) Check for Double or Float return type
- 5) If true then retrieve the value stored at a constant offset from the ScriptContext
- 6) Get Argument Size for callee cleanup
- 7) EpiLog
- a) Retrieve the frame pointer
- b) Store the return address in register (edx)
- c) Clean the arguments based on the arguments size
- d) push the return address back into the stack
- */
- __declspec(naked)
- void InterpreterStackFrame::InterpreterAsmThunk(AsmJsCallStackLayout* layout)
- {
- __asm
- {
- //Prologue
- push ebp;
- mov ebp, esp;
- // Allocate space to put the return value
- sub esp, 16;
- and esp, -16;
- push esp;
- push layout;
- call InterpreterStackFrame::AsmJsInterpreter;
- // we need to move stack around in order to do callee cleanup
- // unfortunately, we don't really have enough registers to do this cleanly
- //
- // we are rearranging the stack from this:
- // 0x14 caller push scriptArg1
- // 0x10 caller push functionObject
- // 0x0C DynamicInterpreterThunk return address
- // 0x08 DynamicInterpreterThunk push ebp
- // 0x04 DynamicInterpreterThunk push functionObject
- // 0x00 InterpreterAsmThunk return address <- stack pointer
- // to this:
- // 0x14 DynamicInterpreterThunk return address
- // 0x10 DynamicInterpreterThunk push ebp
- // 0x0C InterpreterAsmThunk return address <- stack pointer
- // Read return value into possible registers
- movups xmm0, [esp];
- mov edx, [esp + 4];
- mov eax, [esp];
- mov ecx, layout;
- // Epilog, callee cleanup
- mov esp, ebp;
- pop ebp;
- push eax; // save eax
- push edx; // save edx
- // we have to do +0x8 on all stack addresses because we saved 2 registers
- push ecx; // ebp has been restored, we can't use parameters anymore
- call InterpreterStackFrame::GetAsmJsArgSize;
- mov ecx, eax;
- lea eax, [esp + ecx * 1 + 0x8 + 0x8]; // eax will be our stack destination
- mov edx, [esp + 0xC + 0x8];
- mov[eax], edx; // move the dynamic interpreter thunk return location
- mov edx, [esp + 0x8 + 0x8];
- mov[eax - 0x4], edx; // move the dynamic interpreter thunk "push ebp" location
- // skip "push functionObject"
- mov edx, [esp + 0x0 + 0x8];
- mov[eax - 0x8], edx; // move the return location
- pop edx; // restore possible int64 return value
- pop eax; // restore return value
- add esp, ecx; // cleanup arguments
- ret;
- }
- }
- #endif
- #endif
- #endif
- #if DYNAMIC_INTERPRETER_THUNK
- #ifdef _M_IX86
- __declspec(naked)
- Var InterpreterStackFrame::AsmJsDelayDynamicInterpreterThunk(RecyclableObject* function, CallInfo callInfo, ...)
- {
- __asm
- {
- push ebp;
- mov ebp, esp;
- push[esp + 8]; // push function object
- call WasmLibrary::EnsureWasmEntrypoint;
- test eax, eax;
- jne skipThunk;
- push[esp + 8]; // push function object
- call InterpreterStackFrame::EnsureDynamicInterpreterThunk;
- skipThunk:
- #ifdef _CONTROL_FLOW_GUARD
- // verify that the call target is valid
- mov ecx, eax;
- call[__guard_check_icall_fptr];
- mov eax, ecx;
- #endif
- pop ebp;
- jmp eax;
- }
- }
- __declspec(naked)
- Var InterpreterStackFrame::DelayDynamicInterpreterThunk(RecyclableObject* function, CallInfo callInfo, ...)
- {
- __asm
- {
- push ebp;
- mov ebp, esp;
- push[esp + 8]; // push function object
- call InterpreterStackFrame::EnsureDynamicInterpreterThunk;
- #ifdef _CONTROL_FLOW_GUARD
- // verify that the call target is valid
- mov ecx, eax;
- call[__guard_check_icall_fptr];
- mov eax, ecx;
- #endif
- pop ebp;
- jmp eax;
- }
- }
- #elif !defined(_M_AMD64)
- Var InterpreterStackFrame::AsmJsDelayDynamicInterpreterThunk(RecyclableObject* function, CallInfo callInfo, ...)
- {
- // Asm.js only supported on x64 and x86
- AssertOrFailFast(UNREACHED);
- return nullptr;
- }
- #endif
- #endif
- #if ENABLE_PROFILE_INFO
- JavascriptMethod InterpreterStackFrame::EnsureDynamicInterpreterThunk(Js::ScriptFunction * function)
- {
- #if DYNAMIC_INTERPRETER_THUNK
- Assert(function);
- Js::FunctionBody *functionBody = function->GetFunctionBody();
- JavascriptMethod entrypoint = functionBody->EnsureDynamicInterpreterThunk(function->GetFunctionEntryPointInfo());
- Assert(!IsDelayDynamicInterpreterThunk(functionBody->GetDirectEntryPoint(function->GetEntryPointInfo())));
- if (function->GetEntryPoint() == InterpreterStackFrame::DelayDynamicInterpreterThunk)
- {
- // If we are not profiling, or the function object is not cross site, this is the direct entry point.
- // Change the entry point on the object
- Assert(functionBody->GetDirectEntryPoint(function->GetEntryPointInfo()) == entrypoint);
- function->ChangeEntryPoint(function->GetEntryPointInfo(), entrypoint);
- }
- // Return the original entry point to be called
- return entrypoint;
- #else
- return function->GetEntryPoint();
- #endif
- }
- #endif
- bool InterpreterStackFrame::IsDelayDynamicInterpreterThunk(JavascriptMethod entryPoint)
- {
- return false
- #if DYNAMIC_INTERPRETER_THUNK
- || entryPoint == InterpreterStackFrame::DelayDynamicInterpreterThunk
- #ifdef ASMJS_PLAT
- || entryPoint == InterpreterStackFrame::AsmJsDelayDynamicInterpreterThunk
- #endif
- #endif
- ;
- }
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- THREAD_LOCAL int InterpreterThunkStackCountTracker::s_count = 0;
- #endif
- #if DYNAMIC_INTERPRETER_THUNK
- #pragma optimize("", off)
- #ifdef ASMJS_PLAT
- void InterpreterStackFrame::StaticInterpreterAsmThunk(RecyclableObject* function, ...)
- {
- InterpreterAsmThunk((AsmJsCallStackLayout*)&function);
- }
- #endif
- #if !defined(_M_ARM64)
- Var InterpreterStackFrame::StaticInterpreterThunk(RecyclableObject* function, CallInfo callInfo, ...)
- {
- return InterpreterThunk((JavascriptCallStackLayout*)&function);
- }
- #else
- // Language\arm64\arm64_Thunks.asm
- #endif
- #pragma optimize("", on)
- Var InterpreterStackFrame::InterpreterThunk(JavascriptCallStackLayout* layout)
- {
- Js::ScriptFunction * function = Js::ScriptFunction::UnsafeFromVar(layout->functionObject);
- Js::ArgumentReader args(&layout->callInfo, layout->args);
- void* localReturnAddress = _ReturnAddress();
- void* localAddressOfReturnAddress = _AddressOfReturnAddress();
- return InterpreterHelper(function, args, localReturnAddress, localAddressOfReturnAddress);
- }
- #else
- #pragma optimize("", off)
- Var InterpreterStackFrame::InterpreterThunk(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ARGUMENTS(args, callInfo);
- void* localReturnAddress = _ReturnAddress();
- void* localAddressOfReturnAddress = _AddressOfReturnAddress();
- Assert(ScriptFunction::Is(function));
- return InterpreterHelper(ScriptFunction::FromVar(function), args, localReturnAddress, localAddressOfReturnAddress);
- }
- #pragma optimize("", on)
- #endif
- const bool InterpreterStackFrame::ShouldDoProfile(FunctionBody* executeFunction)
- {
- #if ENABLE_PROFILE_INFO
- const bool doProfile = executeFunction->GetInterpreterExecutionMode(false) == ExecutionMode::ProfilingInterpreter ||
- (executeFunction->IsInDebugMode() && DynamicProfileInfo::IsEnabled(executeFunction));
- return doProfile;
- #else
- return false;
- #endif
- }
- InterpreterStackFrame* InterpreterStackFrame::CreateInterpreterStackFrameForGenerator(ScriptFunction* function, FunctionBody* executeFunction, JavascriptGenerator* generator, bool doProfile)
- {
- //
- // Allocate a new InterpreterStackFrame instance on the recycler heap.
- // It will live with the JavascriptGenerator object.
- //
- ScriptContext* functionScriptContext = function->GetScriptContext();
- Arguments generatorArgs = generator->GetArguments();
- InterpreterStackFrame::Setup setup(function, generatorArgs);
- size_t varAllocCount = setup.GetAllocationVarCount();
- size_t varSizeInBytes = varAllocCount * sizeof(Var);
- DWORD_PTR stackAddr = reinterpret_cast<DWORD_PTR>(&generator); // use any stack address from this frame to ensure correct debugging functionality
- LoopHeader* loopHeaderArray = executeFunction->GetHasAllocatedLoopHeaders() ? executeFunction->GetLoopHeaderArrayPtr() : nullptr;
- Var* allocation = RecyclerNewPlus(functionScriptContext->GetRecycler(), varSizeInBytes, Var);
- AnalysisAssert(allocation);
- InterpreterStackFrame* newInstance;
- #if DBG
- // Allocate invalidVar on GC instead of stack since this InterpreterStackFrame will out live the current real frame
- Js::RecyclableObject* invalidVar = (Js::RecyclableObject*)RecyclerNewPlusLeaf(functionScriptContext->GetRecycler(), sizeof(Js::RecyclableObject), Var);
- AnalysisAssert(invalidVar);
- memset(reinterpret_cast<void*>(invalidVar), 0xFE, sizeof(Js::RecyclableObject));
- newInstance = setup.InitializeAllocation(allocation, executeFunction->GetHasImplicitArgIns(), doProfile, loopHeaderArray, stackAddr, invalidVar);
- #else
- newInstance = setup.InitializeAllocation(allocation, executeFunction->GetHasImplicitArgIns(), doProfile, loopHeaderArray, stackAddr);
- #endif
- newInstance->m_reader.Create(executeFunction);
- generator->SetFrame(newInstance, varSizeInBytes);
- return newInstance;
- }
- Var InterpreterStackFrame::InterpreterHelper(ScriptFunction* function, ArgumentReader args, void* returnAddress, void* addressOfReturnAddress, AsmJsReturnStruct* asmJsReturn)
- {
- const bool isAsmJs = asmJsReturn != nullptr;
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- // Support for simulating partially initialized interpreter stack frame.
- InterpreterThunkStackCountTracker tracker;
- if (CONFIG_ISENABLED(InjectPartiallyInitializedInterpreterFrameErrorFlag) &&
- CONFIG_FLAG(InjectPartiallyInitializedInterpreterFrameError) == InterpreterThunkStackCountTracker::GetCount())
- {
- switch (CONFIG_FLAG(InjectPartiallyInitializedInterpreterFrameErrorType))
- {
- case 0:
- DebugBreak();
- break;
- case 1:
- Js::JavascriptError::MapAndThrowError(function->GetScriptContext(), VBSERR_InternalError);
- break;
- default:
- DebugBreak();
- }
- }
- #endif
- ScriptContext* functionScriptContext = function->GetScriptContext();
- ThreadContext * threadContext = functionScriptContext->GetThreadContext();
- Assert(!threadContext->IsDisableImplicitException());
- functionScriptContext->VerifyAlive(!function->IsExternal());
- Assert(threadContext->IsScriptActive());
- Assert(threadContext->IsInScript());
- FunctionBody* executeFunction = JavascriptFunction::UnsafeFromVar(function)->GetFunctionBody();
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (!isAsmJs && executeFunction->IsInDebugMode() != functionScriptContext->IsScriptContextInDebugMode()) // debug mode mismatch
- {
- if (executeFunction->GetUtf8SourceInfo()->GetIsLibraryCode())
- {
- Assert(!executeFunction->IsInDebugMode()); // Library script byteCode is never in debug mode
- }
- else
- {
- Throw::FatalInternalError();
- }
- }
- #endif
- if (executeFunction->GetInterpretedCount() == 0)
- {
- executeFunction->TraceInterpreterExecutionMode();
- }
- class AutoRestore
- {
- private:
- ThreadContext * const threadContext;
- const uint8 savedLoopDepth;
- public:
- AutoRestore(ThreadContext *const threadContext, FunctionBody *const executeFunction)
- : threadContext(threadContext),
- savedLoopDepth(threadContext->LoopDepth())
- {
- if (savedLoopDepth != 0 && !executeFunction->GetIsAsmJsFunction())
- {
- executeFunction->SetWasCalledFromLoop();
- }
- }
- ~AutoRestore()
- {
- threadContext->SetLoopDepth(savedLoopDepth);
- }
- } autoRestore(threadContext, executeFunction);
- #if ENABLE_PROFILE_INFO
- DynamicProfileInfo * dynamicProfileInfo = nullptr;
- const bool doProfile = ShouldDoProfile(executeFunction);
- if (doProfile)
- {
- #if !DYNAMIC_INTERPRETER_THUNK
- executeFunction->EnsureDynamicProfileInfo();
- #endif
- dynamicProfileInfo = executeFunction->GetDynamicProfileInfo();
- threadContext->ClearImplicitCallFlags();
- }
- #else
- const bool doProfile = false;
- #endif
- executeFunction->IncreaseInterpretedCount();
- #ifdef BGJIT_STATS
- functionScriptContext->interpretedCount++;
- functionScriptContext->maxFuncInterpret = max(functionScriptContext->maxFuncInterpret, executeFunction->GetInterpretedCount());
- #endif
- AssertMsg(!executeFunction->IsDeferredParseFunction(),
- "Non-intrinsic functions must provide byte-code to execute");
- executeFunction->BeginExecution();
- bool fReleaseAlloc = false;
- InterpreterStackFrame* newInstance = nullptr;
- if (!isAsmJs && executeFunction->IsCoroutine())
- {
- // If the FunctionBody is a generator then this call is being made by one of the three
- // generator resuming methods: next(), throw(), or return(). They all pass the generator
- // object as the first of two arguments. The real user arguments are obtained from the
- // generator object. The second argument is the ResumeYieldData which is only needed
- // when resuming a generator and so it only used here if a frame already exists on the
- // generator object.
- AssertOrFailFastMsg(args.Info.Count == 2 && ((args.Info.Flags & CallFlags_ExtraArg) == CallFlags_None), "Generator ScriptFunctions should only be invoked by generator APIs with the pair of arguments they pass in -- the generator object and a ResumeYieldData pointer");
- JavascriptGenerator* generator = JavascriptGenerator::FromVar(args[0]);
- newInstance = generator->GetFrame();
- if (newInstance != nullptr)
- {
- ResumeYieldData* resumeYieldData = static_cast<ResumeYieldData*>(args[1]);
- newInstance->SetNonVarReg(executeFunction->GetYieldRegister(), resumeYieldData);
- // The debugger relies on comparing stack addresses of frames to decide when a step_out is complete so
- // give the InterpreterStackFrame a legit enough stack address to make this comparison work.
- newInstance->m_stackAddress = reinterpret_cast<DWORD_PTR>(&generator);
- }
- else
- {
- newInstance = CreateInterpreterStackFrameForGenerator(function, executeFunction, generator, doProfile);
- }
- }
- else
- {
- InterpreterStackFrame::Setup setup(function, args);
- size_t varAllocCount = setup.GetAllocationVarCount();
- size_t varSizeInBytes = varAllocCount * sizeof(Var);
- //
- // Allocate a new InterpreterStackFrame instance on the interpreter's virtual stack.
- //
- DWORD_PTR stackAddr;
- Var* allocation;
- // If the locals area exceeds a certain limit, allocate it from a private arena rather than
- // this frame. The current limit is based on an old assert on the number of locals we would allow here.
- if (varAllocCount > InterpreterStackFrame::LocalsThreshold)
- {
- ArenaAllocator *tmpAlloc = nullptr;
- fReleaseAlloc = functionScriptContext->EnsureInterpreterArena(&tmpAlloc);
- allocation = (Var*)tmpAlloc->Alloc(varSizeInBytes);
- stackAddr = reinterpret_cast<DWORD_PTR>(&allocation); // use a stack address so the debugger stepping logic works (step-out, for example, compares stack depths to determine when to complete the step)
- }
- else
- {
- PROBE_STACK_PARTIAL_INITIALIZED_INTERPRETER_FRAME(functionScriptContext, Js::Constants::MinStackInterpreter + varSizeInBytes);
- allocation = (Var*)_alloca(varSizeInBytes);
- #if DBG
- memset(allocation, 0xFE, varSizeInBytes);
- #endif
- stackAddr = reinterpret_cast<DWORD_PTR>(allocation);
- }
- /*
- * If the function has any loop headers, we allocate an array for the loop headers wrappers, and
- * reference the wrappers in the array. We then push the pointer to the array onto the stack itself.
- * We do this so that while the function is being interpreted, we don't want the jitted loop
- * bodies to be collected, even if the loop body isn't being executed. The loop body will
- * get collected when the function has been JITted, and when the function exits the interpreter.
- * The array contains nulls if the loop body isn't jitted (or hasn't been jitted yet) but
- * it's cheaper to just copy them all into the recycler array rather than just the ones that
- * have been jitted.
- */
- LoopHeader* loopHeaderArray = nullptr;
- if (executeFunction->GetHasAllocatedLoopHeaders())
- {
- // Loop header array is recycler allocated, so we push it on the stack
- // When we scan the stack, we'll recognize it as a recycler allocated
- // object, and mark it's contents and keep the individual loop header
- // wrappers alive
- loopHeaderArray = executeFunction->GetLoopHeaderArrayPtr();
- }
- #if DBG
- Js::RecyclableObject * invalidStackVar = (Js::RecyclableObject*)_alloca(sizeof(Js::RecyclableObject));
- memset(reinterpret_cast<void*>(invalidStackVar), 0xFE, sizeof(Js::RecyclableObject));
- newInstance = setup.InitializeAllocation(allocation, executeFunction->GetHasImplicitArgIns() && !isAsmJs, doProfile, loopHeaderArray, stackAddr, invalidStackVar);
- #else
- newInstance = setup.InitializeAllocation(allocation, executeFunction->GetHasImplicitArgIns() && !isAsmJs, doProfile, loopHeaderArray, stackAddr);
- #endif
- newInstance->m_reader.Create(executeFunction);
- }
- //
- // Execute the function's byte-code, returning the return-value:
- // - Mark that the function is current executing and may not be modified.
- //
- #if ENABLE_TTD
- TTD::TTDExceptionFramePopper exceptionFramePopper;
- if (SHOULD_DO_TTD_STACK_STMT_OP(functionScriptContext))
- {
- bool isInFinally = newInstance->TestFlags(Js::InterpreterStackFrameFlags_WithinFinallyBlock);
- threadContext->TTDExecutionInfo->PushCallEvent(function, args.Info.Count, args.Values, isInFinally);
- exceptionFramePopper.PushInfo(threadContext->TTDExecutionInfo, function);
- }
- #endif
- Var aReturn = nullptr;
- {
- #ifdef ENABLE_SCRIPT_DEBUGGING
- if (!isAsmJs && executeFunction->IsInDebugMode())
- {
- #if DYNAMIC_INTERPRETER_THUNK
- PushPopFrameHelper pushPopFrameHelper(newInstance, returnAddress, addressOfReturnAddress);
- aReturn = newInstance->DebugProcess();
- #else
- aReturn = newInstance->DebugProcessThunk(_ReturnAddress(), _AddressOfReturnAddress());
- #endif
- }
- else
- #endif
- {
- #if DYNAMIC_INTERPRETER_THUNK
- PushPopFrameHelper pushPopFrameHelper(newInstance, returnAddress, addressOfReturnAddress);
- aReturn = newInstance->Process();
- #else
- aReturn = newInstance->ProcessThunk(_ReturnAddress(), _AddressOfReturnAddress());
- #endif
- }
- }
- executeFunction->EndExecution();
- #if ENABLE_TTD
- if (SHOULD_DO_TTD_STACK_STMT_OP(functionScriptContext))
- {
- exceptionFramePopper.PopInfo();
- threadContext->TTDExecutionInfo->PopCallEvent(function, aReturn);
- }
- #endif
- #ifdef ASMJS_PLAT
- if (isAsmJs)
- {
- asmJsReturn->i = newInstance->GetRegRawInt(0);
- asmJsReturn->l = newInstance->GetRegRawInt64(0);
- asmJsReturn->d = newInstance->GetRegRawDouble(0);
- asmJsReturn->f = newInstance->GetRegRawFloat(0);
- asmJsReturn->simd = newInstance->GetRegRawSimd(0);
- }
- #endif
- if (fReleaseAlloc)
- {
- functionScriptContext->ReleaseInterpreterArena();
- }
- #if ENABLE_PROFILE_INFO
- if (doProfile)
- {
- dynamicProfileInfo->RecordImplicitCallFlags(threadContext->GetImplicitCallFlags());
- }
- #endif
- return aReturn;
- }
- #ifdef ASMJS_PLAT
- #if _M_IX86
- void InterpreterStackFrame::AsmJsInterpreter(AsmJsCallStackLayout* stack, byte* retDst)
- {
- ScriptFunction * function = (ScriptFunction*)stack->functionObject;
- Var* paramsAddr = stack->args;
- int flags = CallFlags_Value;
- ArgSlot nbArgs = ArgSlotMath::Add(function->GetFunctionBody()->GetAsmJsFunctionInfo()->GetArgCount(), 1);
- CallInfo callInfo((CallFlags)flags, nbArgs);
- ArgumentReader args(&callInfo, paramsAddr);
- void* returnAddress = _ReturnAddress();
- void* addressOfReturnAddress = _AddressOfReturnAddress();
- #if ENABLE_PROFILE_INFO
- function->GetFunctionBody()->EnsureDynamicProfileInfo();
- #endif
- AsmJsReturnStruct asmJsReturn = { 0 };
- InterpreterHelper(function, args, returnAddress, addressOfReturnAddress, &asmJsReturn);
- //Handle return value
- AsmJsRetType::Which retType = (AsmJsRetType::Which) GetRetType(function);
- switch (retType)
- {
- #ifdef ENABLE_WASM_SIMD
- case AsmJsRetType::Int32x4:
- case AsmJsRetType::Bool32x4:
- case AsmJsRetType::Bool16x8:
- case AsmJsRetType::Bool8x16:
- case AsmJsRetType::Float32x4:
- case AsmJsRetType::Float64x2:
- case AsmJsRetType::Int16x8:
- case AsmJsRetType::Int8x16:
- case AsmJsRetType::Uint32x4:
- case AsmJsRetType::Uint16x8:
- case AsmJsRetType::Uint8x16:
- Assert(Wasm::Simd::IsEnabled());
- *(AsmJsSIMDValue*)retDst = asmJsReturn.simd;
- break;
- #endif
- // double return
- case AsmJsRetType::Double:
- *(double*)retDst = asmJsReturn.d;
- break;
- // float return
- case AsmJsRetType::Float:
- *(float*)retDst = asmJsReturn.f;
- break;
- // signed or void return
- case AsmJsRetType::Signed:
- case AsmJsRetType::Void:
- *(int*)retDst = asmJsReturn.i;
- break;
- case AsmJsRetType::Int64:
- *(int64*)retDst = asmJsReturn.l;
- break;
- default:
- Assume(false);
- }
- }
- #elif _M_X64
- typedef double(*AsmJsInterpreterDoubleEP)(AsmJsCallStackLayout*, void *);
- typedef float(*AsmJsInterpreterFloatEP)(AsmJsCallStackLayout*, void *);
- typedef int(*AsmJsInterpreterIntEP)(AsmJsCallStackLayout*, void *);
- typedef int64(*AsmJsInterpreterInt64EP)(AsmJsCallStackLayout*, void *);
- void * InterpreterStackFrame::GetAsmJsInterpreterEntryPoint(AsmJsCallStackLayout* stack)
- {
- JavascriptFunction * function = stack->functionObject;
- void * entryPoint = nullptr;
- switch (function->GetFunctionBody()->GetAsmJsFunctionInfo()->GetReturnType().which())
- {
- case Js::AsmJsRetType::Double:
- {
- entryPoint = (void*)(AsmJsInterpreterDoubleEP)Js::InterpreterStackFrame::AsmJsInterpreter < double >;
- break;
- }
- case Js::AsmJsRetType::Float:
- {
- entryPoint = (void*)(AsmJsInterpreterFloatEP)Js::InterpreterStackFrame::AsmJsInterpreter < float >;
- break;
- }
- case Js::AsmJsRetType::Signed:
- case Js::AsmJsRetType::Void:
- {
- entryPoint = (void*)(AsmJsInterpreterIntEP)Js::InterpreterStackFrame::AsmJsInterpreter < int >;
- break;
- }
- case Js::AsmJsRetType::Int64:
- {
- entryPoint = (void*)(AsmJsInterpreterInt64EP)Js::InterpreterStackFrame::AsmJsInterpreter < int64 >;
- break;
- }
- #ifdef ENABLE_WASM_SIMD
- case Js::AsmJsRetType::Int32x4:
- case Js::AsmJsRetType::Bool32x4:
- case Js::AsmJsRetType::Bool16x8:
- case Js::AsmJsRetType::Bool8x16:
- case Js::AsmJsRetType::Float32x4:
- case Js::AsmJsRetType::Float64x2:
- case Js::AsmJsRetType::Int16x8:
- case Js::AsmJsRetType::Int8x16:
- case Js::AsmJsRetType::Uint32x4:
- case Js::AsmJsRetType::Uint16x8:
- case Js::AsmJsRetType::Uint8x16:
- {
- entryPoint = (void*)Js::InterpreterStackFrame::AsmJsInterpreterSimdJs;
- break;
- }
- #endif
- default:
- Assume(UNREACHED);
- }
- return entryPoint;
- }
- template<typename T>
- T InterpreterStackFrame::AsmJsInterpreter(AsmJsCallStackLayout* layout)
- {
- Js::ScriptFunction * function = Js::ScriptFunction::FromVar(layout->functionObject);
- int flags = CallFlags_Value;
- ArgSlot nbArgs = ArgSlotMath::Add(function->GetFunctionBody()->GetAsmJsFunctionInfo()->GetArgCount(), 1);
- CallInfo callInfo((CallFlags)flags, nbArgs);
- ArgumentReader args(&callInfo, (Var*)layout->args);
- void* returnAddress = _ReturnAddress();
- void* addressOfReturnAddress = _AddressOfReturnAddress();
- function->GetFunctionBody()->EnsureDynamicProfileInfo();
- AsmJsReturnStruct asmJsReturn = { 0 };
- InterpreterHelper(function, args, returnAddress, addressOfReturnAddress, &asmJsReturn);
- return asmJsReturn.GetRetVal<T>();
- }
- __m128 InterpreterStackFrame::AsmJsInterpreterSimdJs(AsmJsCallStackLayout* layout)
- {
- return AsmJsInterpreter<X86SIMDValue>(layout).m128_value;
- }
- #endif
- #endif
- ///----------------------------------------------------------------------------
- ///
- /// InterpreterStackFrame::SetOut()
- ///
- /// SetOut() change the Var value stored in the specified "out parameter"
- /// register.
- ///
- ///----------------------------------------------------------------------------
- inline void InterpreterStackFrame::SetOut(ArgSlot outRegisterID, Var aValue)
- {
- //
- // The "out" parameter slots are located at the end of the local register range, counting
- // forwards. This results in the "in" parameter slots being disjoint from the rest of the
- // InterpreterStackFrame.
- // ..., InterpreterStackFrame A, Locals A[], ..., Out A:0, Out A:1, Out A:2, ...
- // | In B:0, In B:1, ..., InterpreterStackFrame B, Locals B[], ...
- // (current 'this') |
- // (new 'this' after call)
- //
- Assert(m_outParams + outRegisterID < m_outSp);
- m_outParams[outRegisterID] = aValue;
- }
- inline void InterpreterStackFrame::SetOut(ArgSlot_OneByte outRegisterID, Var aValue)
- {
- Assert(m_outParams + outRegisterID < m_outSp);
- m_outParams[outRegisterID] = aValue;
- }
- inline void InterpreterStackFrame::OP_SetOutAsmDb(RegSlot outRegisterID, double val)
- {
- Assert(m_outParams + outRegisterID < m_outSp);
- m_outParams[outRegisterID] = JavascriptNumber::NewWithCheck(val, scriptContext);
- }
- inline void InterpreterStackFrame::OP_SetOutAsmInt(RegSlot outRegisterID, int val)
- {
- Assert(m_outParams + outRegisterID < m_outSp);
- m_outParams[outRegisterID] = JavascriptNumber::ToVar(val, scriptContext);
- }
- void InterpreterStackFrame::OP_SetOutAsmFlt(RegSlot outRegisterID, float val)
- {
- OP_SetOutAsmDb(outRegisterID, (double)val);
- }
- inline void InterpreterStackFrame::OP_I_SetOutAsmFlt(RegSlot outRegisterID, float val)
- {
- Assert(m_outParams + outRegisterID < m_outSp);
- *(float*)(&(m_outParams[outRegisterID])) = val;
- }
- inline void InterpreterStackFrame::OP_I_SetOutAsmLong(RegSlot outRegisterID, int64 val)
- {
- Assert(m_outParams + outRegisterID < m_outSp);
- *(int64*)(&(m_outParams[outRegisterID])) = val;
- }
- inline void InterpreterStackFrame::OP_I_SetOutAsmInt(RegSlot outRegisterID, int val)
- {
- Assert(m_outParams + outRegisterID < m_outSp);
- *(int*)(&(m_outParams[outRegisterID])) = val;
- }
- inline void InterpreterStackFrame::OP_I_SetOutAsmDb(RegSlot outRegisterID, double val)
- {
- Assert(m_outParams + outRegisterID < m_outSp);
- *(double*)(&(m_outParams[outRegisterID])) = val;
- }
- inline void InterpreterStackFrame::OP_I_SetOutAsmSimd(RegSlot outRegisterID, AsmJsSIMDValue val)
- {
- Assert(m_outParams + outRegisterID < m_outSp);
- *(AsmJsSIMDValue*)(&(m_outParams[outRegisterID])) = val;
- }
- template<int type, bool toJs>
- void InterpreterStackFrame::OP_InvalidWasmTypeConversion(...)
- {
- #ifdef ENABLE_WASM
- CompileAssert(type < Wasm::WasmTypes::Limit);
- const char16* fromType = toJs ? Wasm::WasmTypes::GetTypeName(static_cast<Wasm::WasmTypes::WasmType>(type)) : _u("Javascript Variable");
- const char16* toType = toJs ? _u("Javascript Variable") : Wasm::WasmTypes::GetTypeName(static_cast<Wasm::WasmTypes::WasmType>(type));
- JavascriptError::ThrowTypeErrorVar(scriptContext, WASMERR_InvalidTypeConversion, fromType, toType);
- #else
- Assert(UNREACHED); //shouldn't get there
- JavascriptError::ThrowTypeErrorVar(scriptContext, WASMERR_InvalidTypeConversion, _u("unknown"), _u("unknown")); //throw for a release build
- #endif
- }
- // This will be called in the beginning of the try_finally.
- inline void InterpreterStackFrame::CacheSp()
- {
- // Before caching the current m_outSp, we will be storing the previous the previously stored value in the m_outSpCached.
- *m_outSp++ = (Var)m_outSpCached;
- *m_outSp++ = (Var)m_outParams;
- m_outSpCached = m_outSp - 2;
- }
- inline void InterpreterStackFrame::RestoreSp()
- {
- // This will be called in the Finally block to restore from the previous SP cached.
- // m_outSpCached can be null if the catch block is called.
- if (m_outSpCached != nullptr)
- {
- Assert(m_outSpCached < m_outSp);
- m_outSp = m_outSpCached;
- m_outSpCached = (Var*)*m_outSp;
- Assert(m_outSpCached == nullptr || m_outSpCached <= m_outSp);
- m_outParams = (Var*)*(m_outSp + 1);
- }
- else
- {
- ResetOut();
- }
- }
- inline void InterpreterStackFrame::PushOut(Var aValue)
- {
- *m_outSp++ = aValue;
- }
- inline void InterpreterStackFrame::PopOut(ArgSlot argCount)
- {
- ArgSlotMath::Inc(argCount);
- m_outSp -= argCount;
- m_outParams = (Var*)*m_outSp;
- AssertMsg(m_localSlots + this->m_functionBody->GetLocalsCount() <= m_outSp &&
- m_outSp < (m_localSlots + this->m_functionBody->GetLocalsCount() + this->m_functionBody->GetOutParamMaxDepth()),
- "out args Stack pointer not in range after Pop");
- }
- void InterpreterStackFrame::ResetOut()
- {
- //
- // Reset the m_outParams and m_outSp
- //
- m_outParams = m_localSlots + this->m_functionBody->GetLocalsCount();
- m_outSp = m_outParams;
- m_outSpCached = nullptr;
- }
- #ifdef ENABLE_SCRIPT_DEBUGGING
- _NOINLINE
- Var InterpreterStackFrame::DebugProcessThunk(void* returnAddress, void* addressOfReturnAddress)
- {
- PushPopFrameHelper pushPopFrameHelper(this, returnAddress, addressOfReturnAddress);
- return this->DebugProcess();
- }
- //
- // Under debug mode allow the exception to be swallowed and execution to continue
- // if the debugger has specified that behavior.
- //
- Var InterpreterStackFrame::DebugProcess()
- {
- Assert(this->returnAddress != nullptr);
- while (true)
- {
- JavascriptExceptionObject *exception = nullptr;
- try
- {
- #if ENABLE_TTD
- if (SHOULD_DO_TTD_STACK_STMT_OP(this->scriptContext))
- {
- return this->ProcessWithDebugging_PreviousStmtTracking();
- }
- else
- {
- return this->ProcessWithDebugging();
- }
- #else
- return this->ProcessWithDebugging();
- #endif
- }
- catch (const Js::JavascriptException& err)
- {
- JavascriptExceptionObject *exception_ = err.GetAndClear();
- Assert(exception_);
- exception = exception_;
- }
- if (exception)
- {
- bool skipException = false;
- if (!exception->IsGeneratorReturnException() &&
- exception != scriptContext->GetThreadContext()->GetPendingSOErrorObject() &&
- exception != scriptContext->GetThreadContext()->GetPendingOOMErrorObject())
- {
- skipException = exception->IsDebuggerSkip();
- }
- if (skipException)
- {
- // If we are going to swallow the exception then advance to the beginning of the next user statement
- if (exception->IsIgnoreAdvanceToNextStatement()
- || this->scriptContext->GetDebugContext()->GetProbeContainer()->AdvanceToNextUserStatement(this->m_functionBody, &this->m_reader))
- {
- // We must fix up the return value to at least be undefined:
- this->SetReg((RegSlot)0, this->scriptContext->GetLibrary()->GetUndefined());
- // If we recover from the exception, there may be a chance the out pointers in the InterpreterStackframe are not in a proper state.
- // Reset them to correct the stack.
- ResetOut();
- // If we can successfully advance then continuing processing
- continue;
- }
- }
- JavascriptExceptionOperators::DoThrowCheckClone(exception, scriptContext);
- }
- }
- }
- #endif
- template<typename OpCodeType, Js::OpCode(ReadOpFunc)(const byte*&), void (TracingFunc)(InterpreterStackFrame*, OpCodeType)>
- OpCodeType InterpreterStackFrame::ReadOp(const byte *& ip)
- {
- #if DBG || DBG_DUMP
- //
- // For debugging byte-code, store the current offset before the instruction is read:
- // - We convert this to "void *" to encourage the debugger to always display in hex,
- // which matches the displayed offsets used by ByteCodeDumper.
- //
- this->DEBUG_currentByteOffset = (void *)m_reader.GetCurrentOffset();
- #endif
- OpCodeType op = (OpCodeType)ReadOpFunc(ip);
- #if DBG_DUMP
- TracingFunc(this, op);
- #endif
- return op;
- }
- void InterpreterStackFrame::TraceOpCode(InterpreterStackFrame* that, Js::OpCode op)
- {
- #if DBG_DUMP
- that->scriptContext->byteCodeHistogram[(int)op]++;
- if (PHASE_TRACE(Js::InterpreterPhase, that->m_functionBody))
- {
- Output::Print(_u("%d.%d:Executing %s at offset 0x%X\n"), that->m_functionBody->GetSourceContextId(), that->m_functionBody->GetLocalFunctionId(), Js::OpCodeUtil::GetOpCodeName(op), that->DEBUG_currentByteOffset);
- }
- #endif
- }
- void InterpreterStackFrame::TraceAsmJsOpCode(InterpreterStackFrame* that, Js::OpCodeAsmJs op)
- {
- #if DBG_DUMP && defined(ASMJS_PLAT)
- if (PHASE_TRACE(Js::AsmjsInterpreterPhase, that->m_functionBody))
- {
- Output::Print(_u("%d.%d:Executing %s at offset 0x%X\n"), that->m_functionBody->GetSourceContextId(), that->m_functionBody->GetLocalFunctionId(), Js::OpCodeUtilAsmJs::GetOpCodeName(op), that->DEBUG_currentByteOffset);
- }
- #endif
- }
- #if ENABLE_TTD
- template<typename OpCodeType, Js::OpCode(ReadOpFunc)(const byte*&), void (TracingFunc)(InterpreterStackFrame*, OpCodeType)>
- OpCodeType InterpreterStackFrame::ReadOp_WPreviousStmtTracking(const byte *& ip)
- {
- #if DBG || DBG_DUMP
- //
- // For debugging byte-code, store the current offset before the instruction is read:
- // - We convert this to "void *" to encourage the debugger to always display in hex,
- // which matches the displayed offsets used by ByteCodeDumper.
- //
- this->DEBUG_currentByteOffset = (void *)m_reader.GetCurrentOffset();
- #endif
- #if ENABLE_TTD
- AssertMsg(this->scriptContext->GetThreadContext()->IsRuntimeInTTDMode(), "We never be fetching an opcode via this path if this is not true!!!");
- #endif
- if (SHOULD_DO_TTD_STACK_STMT_OP(this->scriptContext))
- {
- TTD::ExecutionInfoManager* executionMgr = this->scriptContext->GetThreadContext()->TTDExecutionInfo;
- executionMgr->UpdateCurrentStatementInfo(m_reader.GetCurrentOffset());
- }
- OpCodeType op = (OpCodeType)ReadOpFunc(ip);
- #if DBG_DUMP
- TracingFunc(this, op);
- #endif
- return op;
- }
- #endif
- _NOINLINE
- Var InterpreterStackFrame::ProcessThunk(void* address, void* addressOfReturnAddress)
- {
- PushPopFrameHelper pushPopFrameHelper(this, address, addressOfReturnAddress);
- return this->Process();
- }
- #ifdef ASMJS_PLAT
- Var InterpreterStackFrame::ProcessAsmJsModule()
- {
- FunctionBody* asmJsModuleFunctionBody = GetFunctionBody();
- AsmJsModuleInfo* info = asmJsModuleFunctionBody->GetAsmJsModuleInfo();
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (Configuration::Global.flags.ForceAsmJsLinkFail)
- {
- AsmJSCompiler::OutputError(this->scriptContext, _u("Asm.js Runtime Error : Forcing link failure"));
- return this->ProcessLinkFailedAsmJsModule();
- }
- #endif
- if (m_inSlotsCount != info->GetArgInCount() + 1)
- {
- // Error reparse without asm.js
- AsmJSCompiler::OutputError(this->scriptContext, _u("Asm.js Runtime Error : Invalid module argument count"));
- return this->ProcessLinkFailedAsmJsModule();
- }
- const AsmJsModuleMemory& moduleMemory = info->GetModuleMemory();
- Field(Var)* moduleMemoryPtr = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), moduleMemory.mMemorySize);
- Field(Var)* arrayBufferPtr = moduleMemoryPtr + moduleMemory.mArrayBufferOffset;
- Assert(moduleMemory.mArrayBufferOffset == AsmJsModuleMemory::MemoryTableBeginOffset);
- Field(Var)* stdLibPtr = moduleMemoryPtr + moduleMemory.mStdLibOffset;
- int* localIntSlots = (int*)(moduleMemoryPtr + moduleMemory.mIntOffset);
- float* localFloatSlots = (float*)(moduleMemoryPtr + moduleMemory.mFloatOffset);
- double* localDoubleSlots = (double*)(moduleMemoryPtr + moduleMemory.mDoubleOffset);
- Field(Var)* localFunctionImports = moduleMemoryPtr + moduleMemory.mFFIOffset;
- Field(Var)* localModuleFunctions = moduleMemoryPtr + moduleMemory.mFuncOffset;
- Field(Field(Var)*)* localFunctionTables = (Field(Field(Var)*)*)(moduleMemoryPtr + moduleMemory.mFuncPtrOffset);
- ThreadContext* threadContext = this->scriptContext->GetThreadContext();
- *stdLibPtr = (m_inSlotsCount > 1) ? m_inParams[1] : nullptr;
- Var foreign = (m_inSlotsCount > 2) ? m_inParams[2] : nullptr;
- *arrayBufferPtr = (m_inSlotsCount > 3) ? m_inParams[3] : nullptr;
- //cache the current state of the disable implicit call flag
- DisableImplicitFlags prevDisableImplicitFlags = threadContext->GetDisableImplicitFlags();
- ImplicitCallFlags saveImplicitcallFlags = threadContext->GetImplicitCallFlags();
- // Disable implicit calls to check if any of the VarImport or Function Import leads to implicit calls
- threadContext->DisableImplicitCall();
- threadContext->SetImplicitCallFlags(ImplicitCallFlags::ImplicitCall_None);
- bool checkParamResult = ASMLink::CheckParams(this->scriptContext, info, *stdLibPtr, foreign, *arrayBufferPtr);
- if (!checkParamResult)
- {
- // don't need to print, because checkParams will do it for us
- goto linkFailure;
- }
- else if (this->CheckAndResetImplicitCall(prevDisableImplicitFlags, saveImplicitcallFlags))
- {
- AsmJSCompiler::OutputError(this->scriptContext, _u("Asm.js Runtime Error : Params have side effects"));
- return this->ProcessLinkFailedAsmJsModule();
- }
- // Initialize Variables
- for (int i = 0; i < info->GetVarCount(); i++)
- {
- const auto& var = info->GetVar(i);
- const AsmJsVarType type(var.type);
- if (type.isInt())
- {
- localIntSlots[var.location] = var.initialiser.intInit;
- }
- else if (type.isFloat())
- {
- localFloatSlots[var.location] = var.initialiser.floatInit;
- }
- else if (type.isDouble())
- {
- localDoubleSlots[var.location] = var.initialiser.doubleInit;
- }
- else
- {
- Assert(UNREACHED);
- }
- }
- // Load constant variables
- for (int i = 0; i < info->GetVarImportCount(); i++)
- {
- const auto& import = info->GetVarImport(i);
- const AsmJsVarType type(import.type);
- // this might throw, but it would anyway in non-asm.js
- Var value = JavascriptOperators::OP_GetProperty(foreign, import.field, scriptContext);
- // check if there is implicit call and if there is implicit call then clear the disableimplicitcall flag
- if (this->CheckAndResetImplicitCall(prevDisableImplicitFlags, saveImplicitcallFlags))
- {
- AsmJSCompiler::OutputError(this->scriptContext, _u("Asm.js Runtime Error : Accessing var import %s has side effects"), this->scriptContext->GetPropertyName(import.field)->GetBuffer());
- return this->ProcessLinkFailedAsmJsModule();
- }
- if (CONFIG_FLAG(AsmJsEdge))
- {
- // emscripten had a bug which caused this check to fail in some circumstances, so this check fails for some demos
- if (!TaggedNumber::Is(value) && (!RecyclableObject::Is(value) || DynamicType::Is(RecyclableObject::FromVar(value)->GetTypeId())))
- {
- AsmJSCompiler::OutputError(this->scriptContext, _u("Asm.js Runtime Error : Var import %s must be primitive"), this->scriptContext->GetPropertyName(import.field)->GetBuffer());
- goto linkFailure;
- }
- }
- if (type.isInt())
- {
- int val = JavascriptMath::ToInt32(value, scriptContext);
- localIntSlots[import.location] = val;
- }
- else if (type.isFloat())
- {
- float val = (float)JavascriptConversion::ToNumber(value, scriptContext);
- localFloatSlots[import.location] = val;
- }
- else if (type.isDouble())
- {
- double val = JavascriptConversion::ToNumber(value, scriptContext);
- localDoubleSlots[import.location] = val;
- }
- // check for implicit call after converting to number
- if (this->CheckAndResetImplicitCall(prevDisableImplicitFlags, saveImplicitcallFlags))
- {
- // Runtime error
- AsmJSCompiler::OutputError(this->scriptContext, _u("Asm.js Runtime Error : Accessing var import %s has side effects"), this->scriptContext->GetPropertyName(import.field)->GetBuffer());
- return this->ProcessLinkFailedAsmJsModule();
- }
- }
- // Load external functions
- for (int i = 0; i < info->GetFunctionImportCount(); i++)
- {
- const auto& import = info->GetFunctionImport(i);
- // this might throw, but it would anyway in non-asm.js
- Var importFunc = JavascriptOperators::OP_GetProperty(foreign, import.field, scriptContext);
- // check if there is implicit call and if there is implicit call then clear the disableimplicitcall flag
- if (this->CheckAndResetImplicitCall(prevDisableImplicitFlags, saveImplicitcallFlags))
- {
- AsmJSCompiler::OutputError(this->scriptContext, _u("Asm.js Runtime Error : Accessing foreign function import %s has side effects"), this->scriptContext->GetPropertyName(import.field)->GetBuffer());
- return this->ProcessLinkFailedAsmJsModule();
- }
- if (!JavascriptFunction::Is(importFunc))
- {
- AsmJSCompiler::OutputError(this->scriptContext, _u("Asm.js Runtime Error : Foreign function import %s is not a function"), this->scriptContext->GetPropertyName(import.field)->GetBuffer());
- goto linkFailure;
- }
- localFunctionImports[import.location] = importFunc;
- }
- threadContext->SetDisableImplicitFlags(prevDisableImplicitFlags);
- threadContext->SetImplicitCallFlags(saveImplicitcallFlags);
- // scope
- {
- FrameDisplay* pDisplay = RecyclerNewPlus(scriptContext->GetRecycler(), sizeof(void*), FrameDisplay, 0);
- //DynamicObject* asmModule = scriptContext->GetLibrary()->CreateObject(false, 1);
- //JavascriptOperators::OP_SetProperty(asmModule, PropertyIds::module, moduleMemoryPtr, scriptContext);
- //pDisplay->SetItem(0, this->function);
- for (int i = 0; i < info->GetFunctionCount(); i++)
- {
- const auto& modFunc = info->GetFunction(i);
- // TODO: add more runtime checks here
- FunctionInfoPtrPtr functionInfo = m_functionBody->GetNestedFuncReference(i);
- AsmJsScriptFunction* scriptFuncObj = AsmJsScriptFunction::OP_NewAsmJsFunc(pDisplay, functionInfo);
- localModuleFunctions[modFunc.location] = scriptFuncObj;
- if (scriptFuncObj->GetDynamicType()->GetEntryPoint() == DefaultDeferredDeserializeThunk)
- {
- JavascriptFunction::DeferredDeserialize(scriptFuncObj);
- }
- scriptFuncObj->GetDynamicType()->SetEntryPoint(AsmJsExternalEntryPoint);
- scriptFuncObj->GetFunctionBody()->GetAsmJsFunctionInfo()->SetModuleFunctionBody(asmJsModuleFunctionBody);
- scriptFuncObj->SetModuleEnvironment(moduleMemoryPtr);
- if (!info->IsRuntimeProcessed())
- {
- // don't reset entrypoint upon relinking
- FunctionEntryPointInfo* entrypointInfo = (FunctionEntryPointInfo*)scriptFuncObj->GetEntryPointInfo();
- entrypointInfo->SetIsAsmJSFunction(true);
- #if DYNAMIC_INTERPRETER_THUNK
- if (!PHASE_ON1(AsmJsJITTemplatePhase))
- {
- entrypointInfo->jsMethod = AsmJsDefaultEntryThunk;
- }
- #endif
- }
- }
- }
- // Initialize function table arrays
- for (int i = 0; i < info->GetFunctionTableCount(); i++)
- {
- const auto& modFuncTable = info->GetFunctionTable(i);
- Field(Var)* funcTableArray = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), modFuncTable.size);
- for (uint j = 0; j < modFuncTable.size; j++)
- {
- // get the module function index
- const RegSlot index = modFuncTable.moduleFunctionIndex[j];
- // assign the module function pointer to the array
- Var functionPtr = localModuleFunctions[index];
- funcTableArray[j] = functionPtr;
- }
- localFunctionTables[i] = funcTableArray;
- }
- // Do MTJRC/MAIC:0 check
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (
- (PHASE_ON1(Js::AsmJsJITTemplatePhase) && CONFIG_FLAG(MaxTemplatizedJitRunCount) == 0) ||
- (!PHASE_ON1(Js::AsmJsJITTemplatePhase) && (CONFIG_FLAG(MaxAsmJsInterpreterRunCount) == 0 || CONFIG_FLAG(ForceNative)))
- )
- {
- if (PHASE_TRACE1(AsmjsEntryPointInfoPhase))
- {
- Output::Print(_u("%s Scheduling For Full JIT at callcount:%d\n"), asmJsModuleFunctionBody->GetDisplayName(), 0);
- Output::Flush();
- }
- for (int i = 0; i < info->GetFunctionCount(); i++)
- {
- ScriptFunction* functionObj = (ScriptFunction*)PointerValue(localModuleFunctions[i]);
- AnalysisAssert(functionObj != nullptr);
- // don't want to generate code for APIs like changeHeap
- if (functionObj->GetEntryPoint() == Js::AsmJsExternalEntryPoint)
- {
- WAsmJs::JitFunctionIfReady(functionObj);
- }
- }
- }
- #endif
- info->SetIsRuntimeProcessed(true);
- // create export object
- if (info->GetExportsCount())
- {
- Var newObj = JavascriptOperators::NewScObjectLiteral(GetScriptContext(), info->GetExportsIdArray(),
- this->GetFunctionBody()->GetObjectLiteralTypeRef(0));
- for (int i = 0; i < info->GetExportsCount(); i++)
- {
- auto ex = info->GetExport(i);
- Var func = localModuleFunctions[*ex.location];
- JavascriptOperators::OP_InitProperty(newObj, *ex.id, func);
- }
- SetReg((RegSlot)0, newObj);
- return newObj;
- }
- // export only 1 function
- {
- Var exportFunc = localModuleFunctions[info->GetExportFunctionIndex()];
- SetReg((RegSlot)0, exportFunc);
- return exportFunc;
- }
- linkFailure:
- threadContext->SetDisableImplicitFlags(prevDisableImplicitFlags);
- threadContext->SetImplicitCallFlags(saveImplicitcallFlags);
- return this->ProcessLinkFailedAsmJsModule();
- }
- Var InterpreterStackFrame::ProcessLinkFailedAsmJsModule()
- {
- AsmJSCompiler::OutputError(this->scriptContext, _u("asm.js linking failed."));
- Js::FunctionBody* asmJsModuleFunctionBody = GetFunctionBody();
- AsmJsModuleInfo* info = asmJsModuleFunctionBody->GetAsmJsModuleInfo();
- // do not support relinking with failed relink
- if (info->IsRuntimeProcessed())
- {
- Js::Throw::OutOfMemory();
- }
- ScriptFunction * funcObj = GetJavascriptFunction();
- ScriptFunction::ReparseAsmJsModule(&funcObj);
- const bool doProfile =
- funcObj->GetFunctionBody()->GetInterpreterExecutionMode(false) == ExecutionMode::ProfilingInterpreter ||
- (funcObj->GetFunctionBody()->IsInDebugMode() && DynamicProfileInfo::IsEnabled(funcObj->GetFunctionBody()));
- DynamicProfileInfo * dynamicProfileInfo = nullptr;
- if (doProfile)
- {
- dynamicProfileInfo = funcObj->GetFunctionBody()->GetDynamicProfileInfo();
- funcObj->GetScriptContext()->GetThreadContext()->ClearImplicitCallFlags();
- }
- // after reparsing, we want to also use a new interpreter stack frame, as it will have different characteristics than the asm.js version
- InterpreterStackFrame::Setup setup(funcObj, m_inParams, m_inSlotsCount);
- size_t varAllocCount = setup.GetAllocationVarCount();
- size_t varSizeInBytes = varAllocCount * sizeof(Var);
- Var* allocation = nullptr;
- DWORD_PTR stackAddr;
- bool fReleaseAlloc = false;
- if (varAllocCount > InterpreterStackFrame::LocalsThreshold)
- {
- ArenaAllocator *tmpAlloc = nullptr;
- fReleaseAlloc = GetScriptContext()->EnsureInterpreterArena(&tmpAlloc);
- allocation = (Var*)tmpAlloc->Alloc(varSizeInBytes);
- // use a stack address so the debugger stepping logic works (step-out, for example, compares stack depths to determine when to complete the step)
- // debugger stepping does not matter here, but it's worth being consistent with normal stack frame
- stackAddr = reinterpret_cast<DWORD_PTR>(&allocation);
- }
- else
- {
- PROBE_STACK_PARTIAL_INITIALIZED_INTERPRETER_FRAME(GetScriptContext(), Js::Constants::MinStackInterpreter + varSizeInBytes);
- allocation = (Var*)_alloca(varSizeInBytes);
- stackAddr = reinterpret_cast<DWORD_PTR>(allocation);
- }
- #if DBG
- Var invalidStackVar = (Js::RecyclableObject*)_alloca(sizeof(Js::RecyclableObject));
- memset(invalidStackVar, 0xFE, sizeof(Js::RecyclableObject));
- InterpreterStackFrame * newInstance = newInstance = setup.InitializeAllocation(allocation, funcObj->GetFunctionBody()->GetHasImplicitArgIns(), doProfile, nullptr, stackAddr, invalidStackVar);
- #else
- InterpreterStackFrame * newInstance = newInstance = setup.InitializeAllocation(allocation, funcObj->GetFunctionBody()->GetHasImplicitArgIns(), doProfile, nullptr, stackAddr);
- #endif
- newInstance->m_reader.Create(funcObj->GetFunctionBody());
- // now that we have set up the new frame, let's interpret it!
- funcObj->GetFunctionBody()->BeginExecution();
- PushPopFrameHelper pushPopFrameHelper(newInstance, this->returnAddress, this->addressOfReturnAddress);
- Var retVal = newInstance->ProcessUnprofiled();
- if (doProfile)
- {
- dynamicProfileInfo->RecordImplicitCallFlags(GetScriptContext()->GetThreadContext()->GetImplicitCallFlags());
- }
- if (fReleaseAlloc)
- {
- GetScriptContext()->ReleaseInterpreterArena();
- }
- return retVal;
- }
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- int AsmJsCallDepth = 0;
- #endif
- // Function memory allocation should be done the same way as
- // T AsmJsCommunEntryPoint(Js::ScriptFunction* func, ...) (AsmJSJitTemplate.cpp)
- // update any changes there
- /*
- This function does the following fixup
- Stack Before Stack After
- ============== ================
- | VarConstants | | VarConstants |
- |--------------| |-----------------
- | IntConstants | | IntConstants |
- |--------------| | ------------ |
- | FloatConst | | Int Vars+Tmps |
- |--------------| |----------------|
- | DoubleConst | | FloatConst |
- |--------------| | ---------- |
- | Var&Temps | | Flt Vars+tmps |
- |==============| |----------------|
- | DoubleConst |
- | ----------- |
- | Dbl Vars+Tmps |
- ================
- intSrc,FltSrc&DblSrc are pointers to the stack before the change
- m_localIntSlots,m_localFloatSlots,m_localDoubleSlots are pointers to the stack after the change
- */
- void InterpreterStackFrame::AlignMemoryForAsmJs()
- {
- FunctionBody *const functionBody = GetFunctionBody();
- ScriptFunction* func = GetJavascriptFunction();
- uint32& callCount = ((FunctionEntryPointInfo*)func->GetEntryPointInfo())->callsCount;
- WAsmJs::JitFunctionIfReady(func, ++callCount);
- AsmJsFunctionInfo* info = functionBody->GetAsmJsFunctionInfo();
- // The const table is copied after the FirstRegSlot
- byte* constTable = (byte*)(m_localSlots + FunctionBody::FirstRegSlot);
- byte* slotsStart = (byte*)m_localSlots;
- // Must do in reverse order to avoid overwriting const of other type as we move things around
- for (int i = WAsmJs::LIMIT - 1; i >= 0; --i)
- {
- WAsmJs::Types type = (WAsmJs::Types)i;
- auto typeInfo = info->GetTypedSlotInfo(type);
- byte* destination = slotsStart + typeInfo->byteOffset;
- switch (type)
- {
- case WAsmJs::INT32: m_localIntSlots = (int*)destination; break;
- case WAsmJs::INT64: m_localInt64Slots = (int64*)destination; break;
- case WAsmJs::FLOAT32: m_localFloatSlots = (float*)destination; break;
- case WAsmJs::FLOAT64: m_localDoubleSlots = (double*)destination; break;
- case WAsmJs::SIMD: m_localSimdSlots = (AsmJsSIMDValue*)destination; break;
- default:
- CompileAssert(WAsmJs::SIMD == WAsmJs::LastType);
- Assert(false);
- break;
- }
- byte* source = constTable + typeInfo->constSrcByteOffset;
- if (typeInfo->constCount > 0 && source != destination)
- {
- // Make sure slots are aligned for this type
- Assert(::Math::Align<intptr_t>((intptr_t)destination, (intptr_t)WAsmJs::GetTypeByteSize(type)) == (intptr_t)destination);
- Assert(typeInfo->constSrcByteOffset != Js::Constants::InvalidOffset);
- uint constByteSize = typeInfo->constCount * WAsmJs::GetTypeByteSize(type);
- memmove_s(destination, constByteSize, source, constByteSize);
- }
- }
- // Load module environment
- AsmJsScriptFunction* asmJsFunc = AsmJsScriptFunction::FromVar(this->function);
- m_localSlots[AsmJsFunctionMemory::ModuleEnvRegister] = asmJsFunc->GetModuleEnvironment();
- m_localSlots[AsmJsFunctionMemory::ArrayBufferRegister] = nullptr;
- #ifdef ENABLE_WASM
- if (WasmScriptFunction::Is(func))
- {
- WasmScriptFunction* wasmFunc = WasmScriptFunction::FromVar(func);
- m_wasmMemory = wasmFunc->GetWebAssemblyMemory();
- m_signatures = func->GetFunctionBody()->GetAsmJsFunctionInfo()->GetWebAssemblyModule()->GetSignatures();
- }
- else
- #endif
- {
- m_asmJsBuffer = asmJsFunc->GetAsmJsArrayBuffer();
- }
- m_localSlots[AsmJsFunctionMemory::ArraySizeRegister] = 0; // do not cache ArraySize in the interpreter
- m_localSlots[AsmJsFunctionMemory::ScriptContextBufferRegister] = functionBody->GetScriptContext();
- int* intArg = m_localIntSlots + info->GetTypedSlotInfo(WAsmJs::INT32)->constCount;
- int64* int64Arg = m_localInt64Slots + info->GetTypedSlotInfo(WAsmJs::INT64)->constCount;
- double* doubleArg = m_localDoubleSlots + info->GetTypedSlotInfo(WAsmJs::FLOAT64)->constCount;
- float* floatArg = m_localFloatSlots + info->GetTypedSlotInfo(WAsmJs::FLOAT32)->constCount;
- #ifdef ENABLE_WASM_SIMD
- AsmJsSIMDValue* simdArg = m_localSimdSlots + info->GetTypedSlotInfo(WAsmJs::SIMD)->constCount;
- #endif
- // Move the arguments to the right location
- ArgSlot argCount = info->GetArgCount();
- #if _M_X64 && _WIN32
- uint homingAreaSize = 0;
- #endif
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- const bool tracingFunc = PHASE_TRACE(AsmjsFunctionEntryPhase, functionBody);
- if (tracingFunc)
- {
- if (AsmJsCallDepth)
- {
- Output::Print(_u("%*c"), AsmJsCallDepth, ' ');
- }
- Output::Print(_u("Executing function %s("), functionBody->GetDisplayName());
- ++AsmJsCallDepth;
- }
- #endif
- uintptr_t argAddress = (uintptr_t)m_inParams;
- for (ArgSlot i = 0; i < argCount; i++)
- {
- #if _M_X64 && _WIN32
- // 3rd Argument should be at the end of the homing area.
- Assert(i != 3 || argAddress == (uintptr_t)m_inParams + homingAreaSize);
- if (i < 3)
- {
- // for x64 we spill the first 3 floating point args below the rest of the arguments on the stack
- // m_inParams will be from DynamicInterpreterThunk's frame. Floats are in InterpreterAsmThunk's frame. Stack will be set up like so:
- // DIT arg2 <- first scriptArg, m_inParams points here
- // DIT arg1
- // padding
- // IAT r9 home
- // IAT r8 home
- // IAT rdx home
- // IAT rcx home
- // IAT return address
- // IAT push rbp
- // IAT padding
- // IAT xmm3 spill
- // IAT xmm2 spill
- // IAT xmm1 spill <- floatSpillAddress for arg1
- #define FLOAT_SPILL_ADDRESS_OFFSET_WORDS 15
- // floats are spilled as xmmwords
- uintptr_t floatSpillAddress = (uintptr_t)m_inParams - MachPtr * (FLOAT_SPILL_ADDRESS_OFFSET_WORDS - 2 * i);
- if (info->GetArgType(i).isInt())
- {
- *intArg = *(int*)argAddress;
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (tracingFunc)
- {
- Output::Print(_u("%d, "), *intArg);
- }
- #endif
- ++intArg;
- homingAreaSize += MachPtr;
- }
- else if (info->GetArgType(i).isInt64())
- {
- *int64Arg = *(int64*)argAddress;
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (tracingFunc)
- {
- Output::Print(_u("%lld, "), *int64Arg);
- }
- #endif
- ++int64Arg;
- homingAreaSize += MachPtr;
- }
- else if (info->GetArgType(i).isFloat())
- {
- *floatArg = *(float*)floatSpillAddress;
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (tracingFunc)
- {
- Output::Print(_u("%.2f, "), *floatArg);
- }
- #endif
- ++floatArg;
- homingAreaSize += MachPtr;
- }
- else if (info->GetArgType(i).isDouble())
- {
- *doubleArg = *(double*)floatSpillAddress;
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (tracingFunc)
- {
- Output::Print(_u("%.2f, "), *doubleArg);
- }
- #endif
- ++doubleArg;
- homingAreaSize += MachPtr;
- }
- else
- {
- #ifdef ENABLE_WASM_SIMD
- Assert(info->GetArgType(i).isSIMD());
- *simdArg = *(AsmJsSIMDValue*)floatSpillAddress;
- ++simdArg;
- homingAreaSize += sizeof(AsmJsSIMDValue);
- #else
- Assert(UNREACHED);
- #endif
- }
- if (Wasm::Simd::IsEnabled() && i == 2) // last argument ?
- {
- // If we have simd arguments, the homing area in m_inParams can be larger than 3 64-bit slots. This is because SIMD values are unboxed there too.
- // After unboxing, the homing area is overwritten by rdx, r8 and r9, and we read/skip 64-bit slots from the homing area (argAddress += MachPtr).
- // After the last argument of the 3 is read, we need to advance argAddress to skip over the possible extra space and to the start of the rest of the arguments.
- argAddress = (uintptr_t)m_inParams + homingAreaSize;
- }
- else
- {
- argAddress += MachPtr;
- }
- }
- else
- #endif
- if (info->GetArgType(i).isInt())
- {
- *intArg = *(int*)argAddress;
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (tracingFunc)
- {
- Output::Print(_u("%d, "), *intArg);
- }
- #endif
- ++intArg;
- argAddress += MachPtr;
- }
- else if (info->GetArgType(i).isInt64())
- {
- *int64Arg = *(int64*)argAddress;
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (tracingFunc)
- {
- Output::Print(_u("%lld, "), *int64Arg);
- }
- #endif
- ++int64Arg;
- argAddress += sizeof(int64);
- }
- else if (info->GetArgType(i).isFloat())
- {
- *floatArg = *(float*)argAddress;
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (tracingFunc)
- {
- Output::Print(_u("%.2f, "), *floatArg);
- }
- #endif
- ++floatArg;
- argAddress += MachPtr;
- }
- else if (info->GetArgType(i).isDouble())
- {
- Assert(info->GetArgType(i).isDouble());
- *doubleArg = *(double*)argAddress;
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (tracingFunc)
- {
- Output::Print(_u("%.2f, "), *doubleArg);
- }
- #endif
- ++doubleArg;
- argAddress += sizeof(double);
- }
- #ifdef ENABLE_WASM_SIMD
- else if (Wasm::Simd::IsEnabled() && info->GetArgType(i).isSIMD())
- {
- *simdArg = *(AsmJsSIMDValue*)argAddress;
- ++simdArg;
- argAddress += sizeof(AsmJsSIMDValue);
- }
- #endif
- else
- {
- AssertMsg(UNREACHED, "Invalid function arg type.");
- }
- }
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (tracingFunc)
- {
- Output::Print(_u("){\n"));
- Output::Flush();
- }
- #endif
- }
- #endif
- ///----------------------------------------------------------------------------
- ///
- /// InterpreterStackFrame::Process
- ///
- /// Process() processes a single loop of execution for the current
- /// JavascriptFunction being executed:
- /// - Individual instructions are dispatched to specific handlers for different
- /// OpCodes.
- ///
- ///----------------------------------------------------------------------------
- #if ENABLE_PROFILE_INFO
- #define INTERPRETERLOOPNAME ProcessProfiled
- #define PROVIDE_INTERPRETERPROFILE
- #include "InterpreterLoop.inl"
- #undef PROVIDE_INTERPRETERPROFILE
- #undef INTERPRETERLOOPNAME
- #endif
- #define INTERPRETERLOOPNAME ProcessUnprofiled
- #include "InterpreterLoop.inl"
- #undef INTERPRETERLOOPNAME
- #if ENABLE_TTD_DIAGNOSTICS_TRACING
- #define PROVIDE_INTERPRETER_STMTS
- #define INTERPRETERLOOPNAME ProcessUnprofiled_PreviousStmtTracking
- #include "InterpreterLoop.inl"
- #undef INTERPRETERLOOPNAME
- #undef PROVIDE_INTERPRETER_STMTS
- #endif
- #ifdef ASMJS_PLAT
- #define INTERPRETERLOOPNAME ProcessAsmJs
- #define INTERPRETER_ASMJS
- #include "InterpreterProcessOpCodeAsmJs.h"
- #include "InterpreterLoop.inl"
- #undef INTERPRETER_ASMJS
- #undef INTERPRETERLOOPNAME
- #endif
- // For now, always collect profile data when debugging,
- // otherwise the backend will be confused if there's no profile data.
- #ifdef ENABLE_SCRIPT_DEBUGGING
- #define INTERPRETERLOOPNAME ProcessWithDebugging
- #define PROVIDE_DEBUGGING
- #if ENABLE_PROFILE_INFO
- #define PROVIDE_INTERPRETERPROFILE
- #endif
- #include "InterpreterLoop.inl"
- #if ENABLE_PROFILE_INFO
- #undef PROVIDE_INTERPRETERPROFILE
- #endif
- #undef PROVIDE_DEBUGGING
- #undef INTERPRETERLOOPNAME
- #endif
- #if ENABLE_TTD
- #define PROVIDE_INTERPRETER_STMTS
- #define INTERPRETERLOOPNAME ProcessWithDebugging_PreviousStmtTracking
- #define PROVIDE_DEBUGGING
- #if ENABLE_PROFILE_INFO
- #define PROVIDE_INTERPRETERPROFILE
- #endif
- #include "InterpreterLoop.inl"
- #if ENABLE_PROFILE_INFO
- #undef PROVIDE_INTERPRETERPROFILE
- #endif
- #undef PROVIDE_DEBUGGING
- #undef INTERPRETERLOOPNAME
- #undef PROVIDE_INTERPRETER_STMTS
- #endif
- Var InterpreterStackFrame::Process()
- {
- #if ENABLE_PROFILE_INFO
- class AutoRestore
- {
- private:
- InterpreterStackFrame * const interpreterStackFrame;
- const uint32 savedSwitchProfileModeOnLoopEndNumber;
- const bool savedIsAutoProfiling;
- const bool savedSwitchProfileMode;
- public:
- AutoRestore(InterpreterStackFrame *const interpreterStackFrame)
- : interpreterStackFrame(interpreterStackFrame),
- savedIsAutoProfiling(interpreterStackFrame->isAutoProfiling),
- savedSwitchProfileMode(interpreterStackFrame->switchProfileMode),
- savedSwitchProfileModeOnLoopEndNumber(interpreterStackFrame->switchProfileModeOnLoopEndNumber)
- {
- }
- ~AutoRestore()
- {
- interpreterStackFrame->isAutoProfiling = savedIsAutoProfiling;
- interpreterStackFrame->switchProfileMode = savedSwitchProfileMode;
- interpreterStackFrame->switchProfileModeOnLoopEndNumber = savedSwitchProfileModeOnLoopEndNumber;
- }
- } autoRestore(this);
- #endif
- if (this->ehBailoutData && !this->TestFlags(InterpreterStackFrameFlags_FromBailOutInInlinee))
- {
- if (this->TestFlags(Js::InterpreterStackFrameFlags_FromBailOut) && !this->TestFlags(InterpreterStackFrameFlags_ProcessingBailOutFromEHCode))
- {
- this->OrFlags(Js::InterpreterStackFrameFlags_ProcessingBailOutFromEHCode);
- EHBailoutData * topLevelEHBailoutData = this->ehBailoutData;
- while (topLevelEHBailoutData->parent->nestingDepth != -1)
- {
- topLevelEHBailoutData->parent->child = topLevelEHBailoutData;
- topLevelEHBailoutData = topLevelEHBailoutData->parent;
- }
- ProcessTryHandlerBailout(topLevelEHBailoutData, this->ehBailoutData->nestingDepth);
- this->ClearFlags(Js::InterpreterStackFrameFlags_ProcessingBailOutFromEHCode);
- this->ehBailoutData = nullptr;
- }
- }
- #ifdef ASMJS_PLAT
- if (GetFunctionBody()->GetIsAsmjsMode())
- {
- FunctionBody *const functionBody = GetFunctionBody();
- AsmJsFunctionInfo* asmInfo = functionBody->GetAsmJsFunctionInfo();
- if (asmInfo)
- {
- AlignMemoryForAsmJs();
- Var returnVar = ProcessAsmJs();
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (PHASE_TRACE(AsmjsFunctionEntryPhase, functionBody))
- {
- --AsmJsCallDepth;
- if (AsmJsCallDepth)
- {
- Output::Print(_u("%*c}"), AsmJsCallDepth, ' ');
- }
- else
- {
- Output::Print(_u("}"));
- }
- switch (asmInfo->GetReturnType().which())
- {
- case AsmJsRetType::Void:
- break;
- case AsmJsRetType::Signed:
- Output::Print(_u(" = %d"), m_localIntSlots[0]);
- break;
- case AsmJsRetType::Int64:
- Output::Print(_u(" = %lld"), m_localInt64Slots[0]);
- break;
- case AsmJsRetType::Float:
- Output::Print(_u(" = %.4f"), m_localFloatSlots[0]);
- break;
- case AsmJsRetType::Double:
- Output::Print(_u(" = %.4f"), m_localDoubleSlots[0]);
- break;
- default:
- break;
- }
- Output::Print(_u(";\n"));
- Output::Flush();
- }
- #endif
- return returnVar;
- }
- else
- {
- Assert(functionBody->GetAsmJsModuleInfo());
- return ProcessAsmJsModule();
- }
- }
- #endif
- #if ENABLE_PROFILE_INFO
- switchProfileMode = false;
- switchProfileModeOnLoopEndNumber = 0u - 1;
- #endif
- #if ENABLE_PROFILE_INFO
- FunctionBody *const functionBody = GetFunctionBody();
- const ExecutionMode interpreterExecutionMode =
- functionBody->GetInterpreterExecutionMode(TestFlags(InterpreterStackFrameFlags_FromBailOut));
- if (interpreterExecutionMode == ExecutionMode::ProfilingInterpreter)
- {
- #if ENABLE_TTD
- AssertMsg(!SHOULD_DO_TTD_STACK_STMT_OP(this->scriptContext), "We should have pinned into Interpreter mode in this case!!!");
- #endif
- isAutoProfiling = false;
- return ProcessProfiled();
- }
- Assert(
- interpreterExecutionMode == ExecutionMode::Interpreter ||
- interpreterExecutionMode == ExecutionMode::AutoProfilingInterpreter);
- isAutoProfiling = interpreterExecutionMode == ExecutionMode::AutoProfilingInterpreter;
- Var result;
- while (true)
- {
- Assert(!switchProfileMode);
- #if ENABLE_TTD_DIAGNOSTICS_TRACING
- if (this->scriptContext->ShouldPerformRecordOrReplayAction())
- {
- result = ProcessUnprofiled_PreviousStmtTracking();
- }
- else
- {
- result = ProcessUnprofiled();
- }
- #else
- result = ProcessUnprofiled();
- #endif
- Assert(!(switchProfileMode && result));
- if (switchProfileMode)
- {
- switchProfileMode = false;
- }
- else
- {
- break;
- }
- Assert(isAutoProfiling);
- #if DBG_DUMP
- if (PHASE_TRACE(InterpreterAutoProfilePhase, functionBody))
- {
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("InterpreterAutoProfile - Func %s - Started profiling\n"), functionBody->GetDebugNumberSet(debugStringBuffer));
- Output::Flush();
- }
- #endif
- Assert(!switchProfileMode);
- result = ProcessProfiled();
- Assert(!(switchProfileMode && result));
- if (switchProfileMode)
- {
- switchProfileMode = false;
- }
- else
- {
- break;
- }
- Assert(isAutoProfiling);
- #if DBG_DUMP
- if (PHASE_TRACE(InterpreterAutoProfilePhase, functionBody))
- {
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("InterpreterAutoProfile - Func %s - Stopped profiling\n"), functionBody->GetDebugNumberSet(debugStringBuffer));
- Output::Flush();
- }
- #endif
- }
- return result;
- #else
- #if ENABLE_TTD_DIAGNOSTICS_TRACING
- if (this->scriptContext->ShouldPerformRecordOrReplayAction())
- {
- return ProcessUnprofiled_PreviousStmtTracking();
- }
- else
- {
- return ProcessUnprofiled();
- }
- #else
- return ProcessUnprofiled();
- #endif
- #endif
- }
- template <class T>
- void InterpreterStackFrame::OP_GetMethodProperty(unaligned T *playout)
- {
- Var varInstance = GetReg(playout->Instance);
- OP_GetMethodProperty(varInstance, playout);
- }
- template <class T>
- void InterpreterStackFrame::OP_GetLocalMethodProperty(unaligned T *playout)
- {
- OP_GetMethodProperty(this->localClosure, playout);
- }
- template <class T>
- void InterpreterStackFrame::OP_GetMethodProperty(Var varInstance, unaligned T *playout)
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(varInstance);
- #endif
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- RecyclableObject* obj = JavascriptOperators::TryFromVar<RecyclableObject>(varInstance);
- if (obj)
- {
- ScriptFunction *fn = JavascriptOperators::TryFromVar<ScriptFunction>(obj);
- if ((propertyId == PropertyIds::apply || propertyId == PropertyIds::call) && fn)
- {
- // If the property being loaded is "apply"/"call", make an optimistic assumption that apply/call is not overridden and
- // undefer the function right here if it was defer parsed before. This is required so that the load of "apply"/"call"
- // happens from the same "type". Otherwise, we will have a polymorphic cache for load of "apply"/"call".
- if (fn->GetType()->GetEntryPoint() == JavascriptFunction::DeferredParsingThunk)
- {
- JavascriptFunction::DeferredParse(&fn);
- }
- }
- }
- InlineCache *inlineCache = this->GetInlineCache(playout->inlineCacheIndex);
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, GetFunctionBody(), inlineCache, playout->inlineCacheIndex, true);
- Var aValue;
- if (obj &&
- CacheOperators::TryGetProperty<true, true, false, false, false, false, true, false, false, false>(
- obj, false, obj, propertyId, &aValue, GetScriptContext(), nullptr, &info))
- {
- SetReg(playout->Value, aValue);
- return;
- }
- OP_GetMethodProperty_NoFastPath(varInstance, playout);
- }
- template <class T>
- _NOINLINE void InterpreterStackFrame::OP_GetMethodProperty_NoFastPath(Var instance, unaligned T *playout)
- {
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- Var value = JavascriptOperators::PatchGetMethod<false>(
- GetFunctionBody(),
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- instance,
- propertyId
- );
- #ifdef TELEMETRY_INTERPRETER
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- // `successful` will be true as PatchGetMethod throws an exception if not found.
- this->scriptContext->GetTelemetry().GetOpcodeTelemetry().GetMethodProperty(instance, propertyId, value, true);
- }
- #endif
- SetReg(playout->Value, value);
- }
- template <class T>
- void InterpreterStackFrame::OP_GetRootMethodProperty(unaligned T *playout)
- {
- Assert(playout->inlineCacheIndex >= this->m_functionBody->GetRootObjectLoadInlineCacheStart());
- Js::Var instance = this->GetRootObject();
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- InlineCache *inlineCache = this->GetInlineCache(playout->inlineCacheIndex);
- DynamicObject *obj = DynamicObject::UnsafeFromVar(instance);
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, GetFunctionBody(), inlineCache, playout->inlineCacheIndex, true);
- Var aValue;
- if (CacheOperators::TryGetProperty<true, true, false, false, false, false, true, false, false, false>(
- obj, true, obj, propertyId, &aValue, GetScriptContext(), nullptr, &info))
- {
- SetReg(playout->Value, aValue);
- return;
- }
- OP_GetRootMethodProperty_NoFastPath(playout);
- }
- template <class T>
- _NOINLINE void InterpreterStackFrame::OP_GetRootMethodProperty_NoFastPath(unaligned T *playout)
- {
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- Var rootInstance = this->GetRootObject();
- Var value = JavascriptOperators::PatchGetRootMethod<false>(
- GetFunctionBody(),
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- DynamicObject::UnsafeFromVar(rootInstance),
- propertyId
- );
- #ifdef TELEMETRY_INTERPRETER
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- // `successful` will be true as PatchGetMethod throws an exception if not found.
- this->scriptContext->GetTelemetry().GetOpcodeTelemetry().GetMethodProperty(rootInstance, propertyId, value, true);
- }
- #endif
- SetReg(playout->Value, value);
- }
- template <class T>
- void InterpreterStackFrame::OP_GetMethodPropertyScoped(unaligned T *playout)
- {
- ThreadContext* threadContext = this->GetScriptContext()->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- Var varInstance = GetReg(playout->Instance);
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- RecyclableObject* obj = NULL;
- if (RecyclableObject::Is(varInstance))
- {
- obj = RecyclableObject::FromVar(varInstance);
- }
- InlineCache *inlineCache = this->GetInlineCache(playout->inlineCacheIndex);
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, GetFunctionBody(), inlineCache, playout->inlineCacheIndex, true);
- Var aValue;
- if (obj &&
- CacheOperators::TryGetProperty<true, true, false, false, false, false, true, false, false, false>(
- obj, false, obj, propertyId, &aValue, GetScriptContext(), nullptr, &info))
- {
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- SetReg(playout->Value, aValue);
- return;
- }
- OP_GetMethodPropertyScoped_NoFastPath(playout);
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- }
- template <class T>
- _NOINLINE void InterpreterStackFrame::OP_GetMethodPropertyScoped_NoFastPath(unaligned T *playout)
- {
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- Js::Var instance = GetReg(playout->Instance);
- Js::Var value = JavascriptOperators::PatchScopedGetMethod<false>(
- GetFunctionBody(),
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- instance,
- propertyId
- );
- SetReg(playout->Value, value);
- #ifdef TELEMETRY_INTERPRETER
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- // `successful` will be true as PatchGetMethod throws an exception if not found.
- this->scriptContext->GetTelemetry().GetOpcodeTelemetry().GetMethodProperty(instance, propertyId, value, true);
- }
- #endif
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledGetMethodProperty(unaligned T *playout)
- {
- ProfiledGetProperty<T, false, true, false>(playout, GetReg(playout->Instance));
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledGetLocalMethodProperty(unaligned T *playout)
- {
- ProfiledGetProperty<T, false, true, false>(playout, this->localClosure);
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledGetRootMethodProperty(unaligned T *playout)
- {
- ProfiledGetProperty<T, true, true, false>(playout, GetRootObject());
- }
- RecyclableObject *
- InterpreterStackFrame::OP_CallGetFunc(Var target)
- {
- return JavascriptOperators::GetCallableObjectOrThrow(target, GetScriptContext());
- }
- void InterpreterStackFrame::OP_AsmStartCall(const unaligned OpLayoutStartCall * playout)
- {
- OP_StartCall(playout->ArgCount / sizeof(Var));
- m_outParams[0] = scriptContext->GetLibrary()->GetUndefined();
- }
- void InterpreterStackFrame::OP_StartCall(const unaligned OpLayoutStartCall * playout)
- {
- OP_StartCall(playout->ArgCount);
- }
- void InterpreterStackFrame::OP_StartCall(uint outParamCount)
- {
- // Save the outParams for the current callsite on the outparam stack
- PushOut(m_outParams);
- // Update outParams for the indicated callsite
- m_outParams = m_outSp;
- m_outSp += outParamCount;
- AssertMsg(m_localSlots + this->m_functionBody->GetLocalsCount() < m_outSp &&
- m_outSp <= (m_localSlots + this->m_functionBody->GetLocalsCount() + this->m_functionBody->GetOutParamMaxDepth()),
- "out args Stack pointer not in range after Push");
- }
- #ifdef ASMJS_PLAT
- void InterpreterStackFrame::OP_ProfiledCallAsmInternal(RegSlot funcReg, RegSlot returnReg, ProfileId profileId)
- {
- Var target = GetRegRawPtr(funcReg);
- ScriptFunction* function = (ScriptFunction*)OP_CallGetFunc(target);
- Js::FunctionBody * body = function->GetFunctionBody();
- DynamicProfileInfo * dynamicProfileInfo = GetFunctionBody()->GetDynamicProfileInfo();
- dynamicProfileInfo->RecordAsmJsCallSiteInfo(GetFunctionBody(), profileId, body);
- return OP_CallAsmInternalCommon(function, returnReg);
- }
- void InterpreterStackFrame::OP_CallAsmInternal(RegSlot funcReg, RegSlot returnReg)
- {
- Var target = GetRegRawPtr(funcReg);
- ScriptFunction* function = (ScriptFunction*)OP_CallGetFunc(target);
- return OP_CallAsmInternalCommon(function, returnReg);
- }
- void InterpreterStackFrame::OP_CallAsmInternalCommon(ScriptFunction* function, RegSlot returnReg)
- {
- AsmJsScriptFunction* scriptFunc = AsmJsScriptFunction::FromVar(function);
- AsmJsFunctionInfo* asmInfo = scriptFunc->GetFunctionBody()->GetAsmJsFunctionInfo();
- uint alignedArgsSize = ::Math::Align<uint32>(asmInfo->GetArgByteSize(), 16);
- #if _M_X64 && _WIN32
- // convention is to always allocate spill space for rcx,rdx,r8,r9
- if (alignedArgsSize < 0x20) alignedArgsSize = 0x20;
- uint* argSizes = asmInfo->GetArgsSizesArray();
- Assert(asmInfo->GetArgSizeArrayLength() >= 2);
- byte* curOutParams = (byte*)m_outParams + sizeof(Var);
- Assert(curOutParams + argSizes[0] + argSizes[1] + 16 <= (byte*)this->m_outParamsEnd);
- // Prepare in advance the possible arguments that will need to be put in register
- byte _declspec(align(16)) reg[3 * 16];
- CompileAssert((FunctionBody::MinAsmJsOutParams() * sizeof(Var)) == (sizeof(Var) * 2 + sizeof(reg)));
- js_memcpy_s(reg, 16, curOutParams, 16);
- js_memcpy_s(reg + 16, 16, curOutParams + argSizes[0], 16);
- js_memcpy_s(reg + 32, 16, curOutParams + argSizes[0] + argSizes[1], 16);
- #else
- byte* reg = nullptr;
- #endif
- ScriptContext * scriptContext = function->GetScriptContext();
- Js::FunctionEntryPointInfo* entrypointInfo = (Js::FunctionEntryPointInfo*)function->GetEntryPointInfo();
- PROBE_STACK_CALL(scriptContext, function, alignedArgsSize + Js::Constants::MinStackDefault);
- // Calling the jsMethod might change the entrypoint, adding the variable here
- // will save the method on the stack helping debug what really got called
- JavascriptMethod jsMethod = entrypointInfo->jsMethod;
- switch (asmInfo->GetReturnType().which())
- {
- case AsmJsRetType::Void:
- JavascriptFunction::CallAsmJsFunction<int>(function, jsMethod, m_outParams, alignedArgsSize, reg);
- break;
- case AsmJsRetType::Signed:
- m_localIntSlots[returnReg] = JavascriptFunction::CallAsmJsFunction<int>(function, jsMethod, m_outParams, alignedArgsSize, reg);
- break;
- case AsmJsRetType::Int64:
- m_localInt64Slots[returnReg] = JavascriptFunction::CallAsmJsFunction<int64>(function, jsMethod, m_outParams, alignedArgsSize, reg);
- break;
- case AsmJsRetType::Double:
- m_localDoubleSlots[returnReg] = JavascriptFunction::CallAsmJsFunction<double>(function, jsMethod, m_outParams, alignedArgsSize, reg);
- break;
- case AsmJsRetType::Float:
- m_localFloatSlots[returnReg] = JavascriptFunction::CallAsmJsFunction<float>(function, jsMethod, m_outParams, alignedArgsSize, reg);
- break;
- #ifdef ENABLE_WASM_SIMD
- case AsmJsRetType::Float32x4:
- case AsmJsRetType::Int32x4:
- case AsmJsRetType::Bool32x4:
- case AsmJsRetType::Bool16x8:
- case AsmJsRetType::Bool8x16:
- case AsmJsRetType::Float64x2:
- case AsmJsRetType::Int16x8:
- case AsmJsRetType::Int8x16:
- case AsmJsRetType::Uint32x4:
- case AsmJsRetType::Uint16x8:
- case AsmJsRetType::Uint8x16:
- #if _WIN32 //WASM.SIMD ToDo: Enable thunk for Xplat
- #if _M_X64
- X86SIMDValue simdVal;
- simdVal.m128_value = JavascriptFunction::CallAsmJsFunction<__m128>(function, jsMethod, m_outParams, alignedArgsSize, reg);
- m_localSimdSlots[returnReg] = X86SIMDValue::ToSIMDValue(simdVal);
- #else
- m_localSimdSlots[returnReg] = JavascriptFunction::CallAsmJsFunction<AsmJsSIMDValue>(function, jsMethod, m_outParams, alignedArgsSize, reg);
- #endif
- #endif
- break;
- #endif
- default:
- Assume(UNREACHED);
- }
- const ArgSlot nVarToPop = (asmInfo->GetArgByteSize() / sizeof(Var)) + 1;
- PopOut(nVarToPop);
- Assert(function);
- }
- #endif
- template <class T>
- void InterpreterStackFrame::OP_AsmCall(const unaligned T* playout)
- {
- Var target = GetRegRawPtr(playout->Function);
- RecyclableObject * function = OP_CallGetFunc(target);
- #if DBG
- if (this->IsInDebugMode())
- {
- JavascriptFunction::CheckValidDebugThunk(scriptContext, function);
- }
- #endif
- BEGIN_SAFE_REENTRANT_REGION(this->scriptContext->GetThreadContext())
- {
- if (playout->Return == Js::Constants::NoRegister)
- {
- Arguments args(CallInfo(CallFlags_NotUsed, playout->ArgCount), m_outParams);
- JavascriptFunction::CallFunction<true>(function, function->GetEntryPoint(), args);
- }
- else
- {
- Arguments args(CallInfo(CallFlags_Value, playout->ArgCount), m_outParams);
- Var retVal = JavascriptFunction::CallFunction<true>(function, function->GetEntryPoint(), args);
- SetRegRawPtr(playout->Return, retVal);
- }
- }
- END_SAFE_REENTRANT_REGION
- PopOut(playout->ArgCount);
- }
- template <class T>
- void InterpreterStackFrame::OP_EnsureHeapAttached(const unaligned T* playout)
- {
- AsmJsModuleInfo::EnsureHeapAttached(this->function);
- }
- template <class T>
- void InterpreterStackFrame::OP_CallCommon(const unaligned T * playout, RecyclableObject * function, unsigned flags, const Js::AuxArray<uint32> *spreadIndices)
- {
- // Always save and restore implicit call flags when calling out
- // REVIEW: Can we avoid it if we don't collect dynamic profile info?
- ThreadContext * threadContext = scriptContext->GetThreadContext();
- Js::ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- #ifdef ENABLE_SCRIPT_DEBUGGING
- #if DBG
- if (this->IsInDebugMode())
- {
- JavascriptFunction::CheckValidDebugThunk(scriptContext, function);
- }
- #endif
- #endif
- ArgSlot argCount = playout->ArgCount;
- BEGIN_SAFE_REENTRANT_REGION(threadContext)
- {
- if (playout->Return == Js::Constants::NoRegister)
- {
- flags |= CallFlags_NotUsed;
- Arguments args(CallInfo((CallFlags)flags, argCount), m_outParams);
- AssertMsg(static_cast<unsigned>(args.Info.Flags) == flags, "Flags don't fit into the CallInfo field?");
- argCount = args.GetArgCountWithExtraArgs();
- if (spreadIndices != nullptr)
- {
- JavascriptFunction::CallSpreadFunction(function, args, spreadIndices);
- }
- else
- {
- JavascriptFunction::CallFunction<true>(function, function->GetEntryPoint(), args);
- }
- }
- else
- {
- flags |= CallFlags_Value;
- Arguments args(CallInfo((CallFlags)flags, argCount), m_outParams);
- AssertMsg(static_cast<unsigned>(args.Info.Flags) == flags, "Flags don't fit into the CallInfo field?");
- argCount = args.GetArgCountWithExtraArgs();
- if (spreadIndices != nullptr)
- {
- SetReg((RegSlot)playout->Return, JavascriptFunction::CallSpreadFunction(function, args, spreadIndices));
- }
- else
- {
- SetReg((RegSlot)playout->Return, JavascriptFunction::CallFunction<true>(function, function->GetEntryPoint(), args));
- }
- }
- }
- END_SAFE_REENTRANT_REGION
- threadContext->SetImplicitCallFlags(savedImplicitCallFlags);
- PopOut(argCount);
- }
- template <class T>
- void InterpreterStackFrame::OP_CallCommonI(const unaligned T * playout, RecyclableObject * function, unsigned flags)
- {
- OP_CallCommon(playout, function, flags); // CallCommon doesn't do anything with Member
- }
- #if ENABLE_PROFILE_INFO
- template <class T>
- void InterpreterStackFrame::OP_ProfileCallCommon(const unaligned T * playout, RecyclableObject * function, unsigned flags, ProfileId profileId, InlineCacheIndex inlineCacheIndex, const Js::AuxArray<uint32> *spreadIndices)
- {
- FunctionBody* functionBody = this->m_functionBody;
- DynamicProfileInfo * dynamicProfileInfo = functionBody->GetDynamicProfileInfo();
- FunctionInfo* functionInfo = function->GetTypeId() == TypeIds_Function ?
- JavascriptFunction::FromVar(function)->GetFunctionInfo() : nullptr;
- bool isConstructorCall = (CallFlags_New & flags) == CallFlags_New;
- dynamicProfileInfo->RecordCallSiteInfo(functionBody, profileId, functionInfo, functionInfo ? static_cast<JavascriptFunction*>(function) : nullptr, playout->ArgCount, isConstructorCall, inlineCacheIndex);
- OP_CallCommon<T>(playout, function, flags, spreadIndices);
- if (playout->Return != Js::Constants::NoRegister)
- {
- dynamicProfileInfo->RecordReturnTypeOnCallSiteInfo(functionBody, profileId, GetReg((RegSlot)playout->Return));
- }
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfileReturnTypeCallCommon(const unaligned T * playout, RecyclableObject * function, unsigned flags, ProfileId profileId, const Js::AuxArray<uint32> *spreadIndices)
- {
- OP_CallCommon<T>(playout, function, flags, spreadIndices);
- FunctionBody* functionBody = this->m_functionBody;
- DynamicProfileInfo * dynamicProfileInfo = functionBody->GetDynamicProfileInfo();
- if (playout->Return != Js::Constants::NoRegister)
- {
- dynamicProfileInfo->RecordReturnType(functionBody, profileId, GetReg((RegSlot)playout->Return));
- }
- }
- #endif
- template <class T>
- void InterpreterStackFrame::OP_GetRootProperty(unaligned T* playout)
- {
- // Same fast path as in the backend.
- Assert(playout->inlineCacheIndex >= this->m_functionBody->GetRootObjectLoadInlineCacheStart());
- Js::Var instance = this->GetRootObject();
- InlineCache *inlineCache = this->GetInlineCache(playout->inlineCacheIndex);
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- DynamicObject * obj = DynamicObject::UnsafeFromVar(instance);
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, GetFunctionBody(), inlineCache, playout->inlineCacheIndex, true);
- Var value;
- if (CacheOperators::TryGetProperty<true, false, false, false, false, false, true, false, false, false>(
- obj, true, obj, propertyId, &value, GetScriptContext(), nullptr, &info))
- {
- SetReg(playout->Value, value);
- return;
- }
- OP_GetRootProperty_NoFastPath(playout);
- }
- template <class T>
- void InterpreterStackFrame::OP_GetRootPropertyForTypeOf(unaligned T* playout)
- {
- Var rootInstance = GetRootObject();
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- Var value = JavascriptOperators::PatchGetRootValueForTypeOf<false>(
- GetFunctionBody(),
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- DynamicObject::UnsafeFromVar(rootInstance),
- propertyId
- );
- SetReg(playout->Value, value);
- #ifdef TELEMETRY_INTERPRETER
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- // `successful` will be true as PatchGetRootValue throws an exception if not found.
- this->scriptContext->GetTelemetry().GetOpcodeTelemetry().GetProperty(rootInstance, propertyId, value, /*successful:*/true);
- }
- #endif
- }
- template <class T>
- _NOINLINE void InterpreterStackFrame::OP_GetRootProperty_NoFastPath(unaligned T* playout)
- {
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- Var rootInstance = this->GetRootObject();
- Var value = JavascriptOperators::PatchGetRootValue<false>(
- GetFunctionBody(),
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- DynamicObject::UnsafeFromVar(rootInstance),
- propertyId
- );
- SetReg(playout->Value, value);
- #ifdef TELEMETRY_INTERPRETER
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- // `successful` will be true as PatchGetRootValue throws an exception if not found.
- this->scriptContext->GetTelemetry().GetOpcodeTelemetry().GetProperty(rootInstance, propertyId, value, /*successful:*/true);
- }
- #endif
- }
- #if ENABLE_PROFILE_INFO
- template <class T>
- void InterpreterStackFrame::UpdateFldInfoFlagsForGetSetInlineCandidate(unaligned T* playout, FldInfoFlags& fldInfoFlags, CacheType cacheType,
- DynamicProfileInfo * dynamicProfileInfo, uint inlineCacheIndex, RecyclableObject * obj)
- {
- RecyclableObject *callee = nullptr;
- //TODO: Setter case once we stop sharing inline caches for these callsites.
- if ((cacheType & (CacheType_Getter | CacheType_Setter)) && GetInlineCache(inlineCacheIndex)->GetGetterSetter(obj->GetType(), &callee))
- {
- const auto functionBody = this->m_functionBody;
- bool canInline = dynamicProfileInfo->RecordLdFldCallSiteInfo(functionBody, callee, false /*callApplyTarget*/);
- if (canInline)
- {
- //updates this fldInfoFlags passed by reference.
- fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, FldInfo_InlineCandidate);
- }
- }
- }
- template <class T>
- void InterpreterStackFrame::UpdateFldInfoFlagsForCallApplyInlineCandidate(unaligned T* playout, FldInfoFlags& fldInfoFlags, CacheType cacheType,
- DynamicProfileInfo * dynamicProfileInfo, uint inlineCacheIndex, RecyclableObject * obj)
- {
- RecyclableObject *callee = nullptr;
- if (!(fldInfoFlags & FldInfo_Polymorphic) && GetInlineCache(inlineCacheIndex)->GetCallApplyTarget(obj, &callee))
- {
- const auto functionBody = this->m_functionBody;
- bool canInline = dynamicProfileInfo->RecordLdFldCallSiteInfo(functionBody, callee, true /*callApplyTarget*/);
- if (canInline)
- {
- //updates this fldInfoFlags passed by reference.
- fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, FldInfo_InlineCandidate);
- }
- }
- }
- template <class T, bool Root, bool Method, bool CallApplyTarget>
- void InterpreterStackFrame::ProfiledGetProperty(unaligned T* playout, const Var instance)
- {
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- Var value = ProfilingHelpers::ProfiledLdFld<Root, Method, CallApplyTarget>(
- instance,
- propertyId,
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- GetFunctionBody(),
- instance);
- SetReg(playout->Value, value);
- #ifdef TELEMETRY_INTERPRETER
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- // `successful` will be true as PatchGetRootValue throws an exception if not found.
- this->scriptContext->GetTelemetry().GetOpcodeTelemetry().GetProperty(instance, propertyId, value, /*successful:*/true);
- }
- #endif
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledGetRootProperty(unaligned T* playout)
- {
- ProfiledGetProperty<T, true, false, false>(playout, GetRootObject());
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledGetRootPropertyForTypeOf(unaligned T* playout)
- {
- Var rootInstance = GetRootObject();
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- Var value = ProfilingHelpers::ProfiledLdFldForTypeOf<true, false, false>(
- rootInstance,
- propertyId,
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- GetFunctionBody());
- SetReg(playout->Value, value);
- #ifdef TELEMETRY_INTERPRETER
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- // `successful` will be true as PatchGetRootValue throws an exception if not found.
- this->scriptContext->GetTelemetry().GetOpcodeTelemetry().GetProperty(rootInstance, propertyId, value, /*successful:*/true);
- }
- #endif
- }
- #endif
- template <class T>
- void InterpreterStackFrame::OP_GetPropertyForTypeOf(unaligned T* playout)
- {
- Var instance = GetReg(playout->Instance);
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- Var value = JavascriptOperators::PatchGetValueForTypeOf<false>(
- GetFunctionBody(),
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- instance,
- propertyId
- );
- SetReg(playout->Value, value);
- #ifdef TELEMETRY_INTERPRETER
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- // `successful` will be true as PatchGetRootValue throws an exception if not found.
- this->scriptContext->GetTelemetry().GetOpcodeTelemetry().GetProperty(instance, propertyId, value, /*successful:*/true);
- }
- #endif
- }
- template <class T>
- void InterpreterStackFrame::OP_GetProperty(unaligned T* playout)
- {
- // Same fast path as in the backend.
- Var instance = GetReg(playout->Instance);
- OP_GetProperty(instance, playout);
- }
- template <class T>
- void InterpreterStackFrame::OP_GetLocalProperty(unaligned T* playout)
- {
- // Same fast path as in the backend.
- Var instance = this->localClosure;
- OP_GetProperty(instance, playout);
- }
- template <class T>
- void InterpreterStackFrame::OP_GetProperty(Var instance, unaligned T* playout)
- {
- InlineCache *inlineCache = GetInlineCache(playout->inlineCacheIndex);
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- RecyclableObject* obj = JavascriptOperators::TryFromVar<RecyclableObject>(instance);
- if (obj)
- {
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, GetFunctionBody(), inlineCache, playout->inlineCacheIndex, true);
- Var value;
- if (CacheOperators::TryGetProperty<true, false, false, false, false, false, true, false, false, false>(
- obj, false, obj, propertyId, &value, GetScriptContext(), nullptr, &info))
- {
- SetReg(playout->Value, value);
- return;
- }
- }
- OP_GetProperty_NoFastPath(instance, playout);
- }
- template <class T>
- void InterpreterStackFrame::OP_GetSuperProperty(unaligned T* playout)
- {
- // Same fast path as in the backend.
- Var instance = GetReg(playout->Instance);
- Var thisInstance = GetReg(playout->Value2);
- InlineCache *inlineCache = GetInlineCache(playout->PropertyIdIndex);
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->PropertyIdIndex);
- if (RecyclableObject::Is(instance) && RecyclableObject::Is(thisInstance))
- {
- RecyclableObject* superObj = RecyclableObject::FromVar(instance);
- RecyclableObject* thisObj = RecyclableObject::FromVar(thisInstance);
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, GetFunctionBody(), inlineCache, playout->PropertyIdIndex, true);
- Var value;
- if (CacheOperators::TryGetProperty<true, false, false, false, false, false, true, false, false, false>(
- thisObj, false, superObj, propertyId, &value, GetScriptContext(), nullptr, &info))
- {
- SetReg(playout->Value, value);
- return;
- }
- }
- SetReg(
- playout->Value,
- JavascriptOperators::PatchGetValueWithThisPtr<false>(
- GetFunctionBody(),
- GetInlineCache(playout->PropertyIdIndex),
- playout->PropertyIdIndex,
- GetReg(playout->Instance),
- GetPropertyIdFromCacheId(playout->PropertyIdIndex),
- GetReg(playout->Value2)));
- }
- template <class T>
- _NOINLINE void InterpreterStackFrame::OP_GetProperty_NoFastPath(Var instance, unaligned T* playout)
- {
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- Var value = JavascriptOperators::PatchGetValue<false>(
- GetFunctionBody(),
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- instance,
- propertyId
- );
- #ifdef TELEMETRY_INTERPRETER
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- // `successful` will be true as PatchGetMethod throws an exception if not found.
- this->scriptContext->GetTelemetry().GetOpcodeTelemetry().GetProperty(instance, propertyId, value, true);
- }
- #endif
- SetReg(playout->Value, value);
- }
- #if ENABLE_PROFILE_INFO
- template <class T>
- void InterpreterStackFrame::OP_ProfiledGetProperty(unaligned T* playout)
- {
- ProfiledGetProperty<T, false, false, false>(playout, GetReg(playout->Instance));
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledGetLocalProperty(unaligned T* playout)
- {
- ProfiledGetProperty<T, false, false, false>(playout, this->localClosure);
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledGetSuperProperty(unaligned T* playout)
- {
- SetReg(
- playout->Value,
- ProfilingHelpers::ProfiledLdFld<false, false, false>(
- GetReg(playout->Instance),
- GetPropertyIdFromCacheId(playout->PropertyIdIndex),
- GetInlineCache(playout->PropertyIdIndex),
- playout->PropertyIdIndex,
- GetFunctionBody(),
- GetReg(playout->Value2)));
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledGetPropertyForTypeOf(unaligned T* playout)
- {
- Var instance = GetReg(playout->Instance);
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- Var value = ProfilingHelpers::ProfiledLdFldForTypeOf<false, false, false>(
- instance,
- propertyId,
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- GetFunctionBody()
- );
- SetReg(playout->Value, value);
- #ifdef TELEMETRY_INTERPRETER
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- // `successful` will be true as PatchGetMethod throws an exception if not found.
- this->scriptContext->GetTelemetry().GetOpcodeTelemetry().GetProperty(instance, propertyId, value, true);
- }
- #endif
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledGetPropertyCallApplyTarget(unaligned T* playout)
- {
- ProfiledGetProperty<T, false, false, true>(playout, GetReg(playout->Instance));
- }
- #endif
- template <typename T>
- void InterpreterStackFrame::OP_GetPropertyScoped(const unaligned OpLayoutT_ElementP<T>* playout)
- {
- ThreadContext* threadContext = this->GetScriptContext()->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- // Get the property, using a scope stack rather than an individual instance.
- // Use the cache
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- FrameDisplay *pScope = this->GetEnvForEvalCode();
- InlineCache *inlineCache = this->GetInlineCache(playout->inlineCacheIndex);
- ScriptContext* scriptContext = GetScriptContext();
- int length = pScope->GetLength();
- if (1 == length)
- {
- RecyclableObject *obj = RecyclableObject::FromVar(pScope->GetItem(0));
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, GetFunctionBody(), inlineCache, playout->inlineCacheIndex, true);
- Var value;
- if (CacheOperators::TryGetProperty<true, false, false, false, false, false, true, false, false, false>(
- obj, false, obj, propertyId, &value, scriptContext, nullptr, &info))
- {
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- SetReg(playout->Value, value);
- return;
- }
- }
- OP_GetPropertyScoped_NoFastPath(playout);
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- }
- template <typename T>
- void InterpreterStackFrame::OP_GetPropertyForTypeOfScoped(const unaligned OpLayoutT_ElementP<T>* playout)
- {
- ThreadContext* threadContext = this->GetScriptContext()->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- // Get the property, using a scope stack rather than an individual instance.
- // Use the cache
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- FrameDisplay *pScope = this->GetEnvForEvalCode();
- InlineCache *inlineCache = this->GetInlineCache(playout->inlineCacheIndex);
- ScriptContext* scriptContext = GetScriptContext();
- int length = pScope->GetLength();
- if (1 == length)
- {
- DynamicObject *obj = (DynamicObject*)pScope->GetItem(0);
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, GetFunctionBody(), inlineCache, playout->inlineCacheIndex, true);
- Var value;
- if (CacheOperators::TryGetProperty<true, false, false, false, false, false, true, false, false, false>(
- obj, false, obj, propertyId, &value, scriptContext, nullptr, &info))
- {
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- SetReg(playout->Value, value);
- return;
- }
- }
- SetReg(
- playout->Value,
- JavascriptOperators::PatchGetPropertyForTypeOfScoped<false>(
- GetFunctionBody(),
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- GetEnvForEvalCode(),
- GetPropertyIdFromCacheId(playout->inlineCacheIndex),
- GetReg(Js::FunctionBody::RootObjectRegSlot)));
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- }
- template <typename T>
- _NOINLINE void InterpreterStackFrame::OP_GetPropertyScoped_NoFastPath(const unaligned OpLayoutT_ElementP<T>* playout)
- {
- // Implicit root object as default instance
- Var defaultInstance = GetReg(Js::FunctionBody::RootObjectRegSlot);
- // PatchGetPropertyScoped doesn't update type and slotIndex if the scope is not an array of length 1.
- SetReg(
- playout->Value,
- JavascriptOperators::PatchGetPropertyScoped<false>(
- GetFunctionBody(),
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- GetEnvForEvalCode(),
- GetPropertyIdFromCacheId(playout->inlineCacheIndex),
- defaultInstance));
- }
- template <class T>
- void InterpreterStackFrame::OP_SetPropertyScoped(unaligned T* playout, PropertyOperationFlags flags)
- {
- ThreadContext* threadContext = this->GetScriptContext()->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- // Set the property, using a scope stack rather than an individual instance.
- // Use the cache
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- FrameDisplay *pScope = this->GetEnvForEvalCode();
- InlineCache *inlineCache = GetInlineCache(playout->inlineCacheIndex);
- ScriptContext* scriptContext = GetScriptContext();
- Var value = GetReg(playout->Value);
- int length = pScope->GetLength();
- if (1 == length)
- {
- RecyclableObject* obj = RecyclableObject::FromVar(pScope->GetItem(0));
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, GetFunctionBody(), inlineCache, playout->inlineCacheIndex, true);
- if (CacheOperators::TrySetProperty<true, false, false, false, false, true, false, false>(
- obj, false, propertyId, value, scriptContext, flags, nullptr, &info))
- {
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- return;
- }
- }
- OP_SetPropertyScoped_NoFastPath(playout, flags);
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- }
- template <class T>
- _NOINLINE void InterpreterStackFrame::OP_SetPropertyScoped_NoFastPath(unaligned T* playout, PropertyOperationFlags flags)
- {
- // Implicit root object as default instance
- Var defaultInstance = GetReg(Js::FunctionBody::RootObjectRegSlot);
- // PatchSetPropertyScoped doesn't update type and slotIndex if the scope is not an array of length 1.
- JavascriptOperators::PatchSetPropertyScoped<false>(
- GetFunctionBody(),
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- GetEnvForEvalCode(),
- GetPropertyIdFromCacheId(playout->inlineCacheIndex),
- GetReg(playout->Value),
- defaultInstance,
- flags);
- }
- template <class T>
- void InterpreterStackFrame::OP_SetPropertyScopedStrict(unaligned T* playout)
- {
- OP_SetPropertyScoped(playout, PropertyOperation_StrictMode);
- }
- template <class T>
- void InterpreterStackFrame::OP_ConsoleSetPropertyScoped(unaligned T* playout)
- {
- OP_SetPropertyScoped(playout, PropertyOperation_AllowUndeclInConsoleScope);
- }
- template <class T>
- void InterpreterStackFrame::OP_ConsoleSetPropertyScopedStrict(unaligned T* playout)
- {
- OP_SetPropertyScoped(playout, (PropertyOperationFlags)(PropertyOperation_StrictMode | PropertyOperation_AllowUndeclInConsoleScope));
- }
- template <class T>
- inline bool InterpreterStackFrame::TrySetPropertyLocalFastPath(unaligned T* playout, PropertyId pid, RecyclableObject* instance, InlineCache*& inlineCache, PropertyOperationFlags flags)
- {
- inlineCache = this->GetInlineCache(playout->inlineCacheIndex);
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, GetFunctionBody(), inlineCache, playout->inlineCacheIndex, true);
- return
- CacheOperators::TrySetProperty<true, false, false, false, false, true, false, false>(
- instance,
- !!(flags & PropertyOperation_Root),
- pid,
- GetReg(playout->Value),
- GetScriptContext(),
- flags,
- nullptr,
- &info);
- }
- template <class T>
- inline void InterpreterStackFrame::DoSetProperty(unaligned T* playout, Var instance, PropertyOperationFlags flags)
- {
- // Same fast path as in the backend.
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- InlineCache *inlineCache;
- if (!TaggedNumber::Is(instance)
- && TrySetPropertyLocalFastPath(playout, propertyId, RecyclableObject::UnsafeFromVar(instance), inlineCache, flags))
- {
- if (GetJavascriptFunction()->GetConstructorCache()->NeedsUpdateAfterCtor())
- {
- // This function has only 'this' statements and is being used as a constructor. When the constructor exits, the
- // function object's constructor cache will be updated with the type produced by the constructor. From that
- // point on, when the same function object is used as a constructor, the a new object with the final type will
- // be created. Whatever is stored in the inline cache currently will cause cache misses after the constructor
- // cache update. So, just clear it now so that the caches won't be flagged as polymorphic.
- inlineCache->RemoveFromInvalidationListAndClear(this->GetScriptContext()->GetThreadContext());
- }
- return;
- }
- DoSetProperty_NoFastPath(playout, instance, flags);
- }
- template <class T>
- inline void InterpreterStackFrame::DoSetSuperProperty(unaligned T* playout, Var instance, PropertyOperationFlags flags)
- {
- DoSetSuperProperty_NoFastPath(playout, instance, m_functionBody->GetIsStrictMode() ?
- (PropertyOperationFlags)(flags | PropertyOperation_StrictMode) : flags);
- }
- template <class T>
- _NOINLINE void InterpreterStackFrame::DoSetProperty_NoFastPath(unaligned T* playout, Var instance, PropertyOperationFlags flags)
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
- #endif
- InlineCache *const inlineCache = GetInlineCache(playout->inlineCacheIndex);
- const auto PatchPutRootValue = &JavascriptOperators::PatchPutRootValueNoLocalFastPath<false, InlineCache>;
- const auto PatchPutValue = &JavascriptOperators::PatchPutValueNoLocalFastPath<false, InlineCache>;
- const auto PatchPut = flags & PropertyOperation_Root ? PatchPutRootValue : PatchPutValue;
- PatchPut(
- GetFunctionBody(),
- inlineCache,
- playout->inlineCacheIndex,
- instance,
- GetPropertyIdFromCacheId(playout->inlineCacheIndex),
- GetReg(playout->Value),
- flags);
- if (!TaggedNumber::Is(instance) && GetJavascriptFunction()->GetConstructorCache()->NeedsUpdateAfterCtor())
- {
- // This function has only 'this' statements and is being used as a constructor. When the constructor exits, the
- // function object's constructor cache will be updated with the type produced by the constructor. From that
- // point on, when the same function object is used as a constructor, the a new object with the final type will
- // be created. Whatever is stored in the inline cache currently will cause cache misses after the constructor
- // cache update. So, just clear it now so that the caches won't be flagged as polymorphic.
- inlineCache->RemoveFromInvalidationListAndClear(this->GetScriptContext()->GetThreadContext());
- }
- }
- template <class T>
- _NOINLINE void InterpreterStackFrame::DoSetSuperProperty_NoFastPath(unaligned T* playout, Var instance, PropertyOperationFlags flags)
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
- #endif
- InlineCache *const inlineCache = GetInlineCache(playout->PropertyIdIndex);
- JavascriptOperators::PatchPutValueWithThisPtrNoLocalFastPath<false, InlineCache>(
- GetFunctionBody(),
- inlineCache,
- playout->PropertyIdIndex,
- instance,
- GetPropertyIdFromCacheId(playout->PropertyIdIndex),
- GetReg(playout->Value),
- GetReg(playout->Value2),
- flags);
- if (!TaggedNumber::Is(instance) && GetJavascriptFunction()->GetConstructorCache()->NeedsUpdateAfterCtor())
- {
- // This function has only 'this' statements and is being used as a constructor. When the constructor exits, the
- // function object's constructor cache will be updated with the type produced by the constructor. From that
- // point on, when the same function object is used as a constructor, the a new object with the final type will
- // be created. Whatever is stored in the inline cache currently will cause cache misses after the constructor
- // cache update. So, just clear it now so that the caches won't be flagged as polymorphic.
- inlineCache->RemoveFromInvalidationListAndClear(this->GetScriptContext()->GetThreadContext());
- }
- }
- #if ENABLE_PROFILE_INFO
- template <class T, bool Root>
- void InterpreterStackFrame::ProfiledSetProperty(unaligned T* playout, Var instance, PropertyOperationFlags flags)
- {
- Assert(!Root || flags & PropertyOperation_Root);
- ProfilingHelpers::ProfiledStFld<Root>(
- instance,
- GetPropertyIdFromCacheId(playout->inlineCacheIndex),
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- GetReg(playout->Value),
- flags,
- GetJavascriptFunction(),
- instance);
- }
- template <class T, bool Root>
- void InterpreterStackFrame::ProfiledSetSuperProperty(unaligned T* playout, Var instance, Var thisInstance, PropertyOperationFlags flags)
- {
- Assert(!Root || flags & PropertyOperation_Root);
- ProfilingHelpers::ProfiledStFld<Root>(
- instance,
- GetPropertyIdFromCacheId(playout->PropertyIdIndex),
- GetInlineCache(playout->PropertyIdIndex),
- playout->PropertyIdIndex,
- GetReg(playout->Value),
- m_functionBody->GetIsStrictMode() ?
- (PropertyOperationFlags)(flags | PropertyOperation_StrictMode) : flags,
- GetJavascriptFunction(),
- thisInstance);
- }
- #endif
- template <class T>
- void InterpreterStackFrame::OP_SetProperty(unaligned T* playout)
- {
- DoSetProperty(playout, GetReg(playout->Instance), PropertyOperation_None);
- }
- template <class T>
- void InterpreterStackFrame::OP_SetLocalProperty(unaligned T* playout)
- {
- DoSetProperty(playout, this->localClosure, PropertyOperation_None);
- }
- template <class T>
- void InterpreterStackFrame::OP_SetSuperProperty(unaligned T* playout)
- {
- DoSetSuperProperty(playout, GetReg(playout->Instance), PropertyOperation_None);
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledSetProperty(unaligned T* playout)
- {
- ProfiledSetProperty<T, false>(playout, GetReg(playout->Instance), PropertyOperation_None);
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledSetLocalProperty(unaligned T* playout)
- {
- ProfiledSetProperty<T, false>(playout, this->localClosure, PropertyOperation_None);
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledSetSuperProperty(unaligned T* playout)
- {
- ProfiledSetSuperProperty<T, false>(playout, GetReg(playout->Instance), GetReg(playout->Value2), PropertyOperation_None);
- }
- template <class T>
- void InterpreterStackFrame::OP_SetRootProperty(unaligned T* playout)
- {
- DoSetProperty(playout, this->GetRootObject(), PropertyOperation_Root);
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledSetRootProperty(unaligned T* playout)
- {
- ProfiledSetProperty<T, true>(playout, this->GetRootObject(), PropertyOperation_Root);
- }
- template <class T>
- void InterpreterStackFrame::OP_SetPropertyStrict(unaligned T* playout)
- {
- DoSetProperty(playout, GetReg(playout->Instance), PropertyOperation_StrictMode);
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledSetPropertyStrict(unaligned T* playout)
- {
- ProfiledSetProperty<T, false>(playout, GetReg(playout->Instance), PropertyOperation_StrictMode);
- }
- template <class T>
- void InterpreterStackFrame::OP_SetRootPropertyStrict(unaligned T* playout)
- {
- DoSetProperty(playout, this->GetRootObject(), PropertyOperation_StrictModeRoot);
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledSetRootPropertyStrict(unaligned T* playout)
- {
- ProfiledSetProperty<T, true>(playout, this->GetRootObject(), PropertyOperation_StrictModeRoot);
- }
- #if ENABLE_PROFILE_INFO
- template <bool doProfile>
- Var InterpreterStackFrame::ProfiledDivide(Var aLeft, Var aRight, ScriptContext* scriptContext, ProfileId profileId)
- {
- Var result = JavascriptMath::Divide(aLeft, aRight, scriptContext);
- if (doProfile)
- {
- Js::FunctionBody* body = this->m_functionBody;
- body->GetDynamicProfileInfo()->RecordDivideResultType(body, profileId, result);
- }
- return result;
- }
- template <bool doProfile>
- Var InterpreterStackFrame::ProfileModulus(Var aLeft, Var aRight, ScriptContext* scriptContext, ProfileId profileId)
- {
- // If both arguments are TaggedInt, then try to do integer division
- // This case is not handled by the lowerer.
- if (doProfile)
- {
- Js::FunctionBody* body = this->function->GetFunctionBody();
- if (TaggedInt::IsPair(aLeft, aRight))
- {
- int nLeft = TaggedInt::ToInt32(aLeft);
- int nRight = TaggedInt::ToInt32(aRight);
- // nLeft is positive and nRight is +2^i
- // Fast path for Power of 2 divisor
- if (nLeft > 0 && ::Math::IsPow2(nRight))
- {
- body->GetDynamicProfileInfo()->RecordModulusOpType(body, profileId, /*isModByPowerOf2*/ true);
- return TaggedInt::ToVarUnchecked(nLeft & (nRight - 1));
- }
- }
- body->GetDynamicProfileInfo()->RecordModulusOpType(body, profileId, /*isModByPowerOf2*/ false);
- }
- return JavascriptMath::Modulus(aLeft, aRight, scriptContext);
- }
- template <bool doProfile>
- Var InterpreterStackFrame::ProfiledSwitch(Var exp, ProfileId profileId)
- {
- if (doProfile)
- {
- Js::FunctionBody* body = this->m_functionBody;
- body->GetDynamicProfileInfo()->RecordSwitchType(body, profileId, exp);
- }
- return exp;
- }
- #else
- template <bool doProfile>
- Var InterpreterStackFrame::ProfiledDivide(Var aLeft, Var aRight, ScriptContext* scriptContext, ProfileId profileId)
- {
- Assert(!doProfile);
- return JavascriptMath::Divide(aLeft, aRight, scriptContext);
- }
- template <bool doProfile>
- Var InterpreterStackFrame::ProfileModulus(Var aLeft, Var aRight, ScriptContext* scriptContext, ProfileId profileId)
- {
- Assert(!doProfile);
- return JavascriptMath::Modulus(aLeft, aRight, scriptContext);
- }
- template <bool doProfile>
- Var InterpreterStackFrame::ProfiledSwitch(Var exp, ProfileId profileId)
- {
- Assert(!doProfile);
- return exp;
- }
- #endif
- template <class T>
- void InterpreterStackFrame::DoInitProperty(unaligned T* playout, Var instance)
- {
- // Same fast path as in the backend.
- InlineCache *inlineCache = nullptr;
- Assert(!TaggedNumber::Is(instance));
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- if (TrySetPropertyLocalFastPath(playout, propertyId, RecyclableObject::UnsafeFromVar(instance), inlineCache))
- {
- return;
- }
- DoInitProperty_NoFastPath(playout, instance);
- }
- template <class T>
- _NOINLINE void InterpreterStackFrame::DoInitProperty_NoFastPath(unaligned T* playout, Var instance)
- {
- JavascriptOperators::PatchInitValue<false>(
- GetFunctionBody(),
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- RecyclableObject::FromVar(instance),
- GetPropertyIdFromCacheId(playout->inlineCacheIndex),
- GetReg(playout->Value));
- }
- template <class T>
- void InterpreterStackFrame::OP_InitClassMember(const unaligned T * playout)
- {
- uint inlineCacheIndex = playout->inlineCacheIndex;
- InlineCache * inlineCache = this->GetInlineCache(inlineCacheIndex);
- Var instance = GetReg(playout->Instance);
- PropertyOperationFlags flags = PropertyOperation_None;
- Assert(!TaggedNumber::Is(instance));
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- if (!TrySetPropertyLocalFastPath(playout, propertyId, RecyclableObject::UnsafeFromVar(instance), inlineCache, flags))
- {
- JavascriptOperators::OP_InitClassMember(instance, propertyId, GetReg(playout->Value));
- }
- }
- template <class T>
- void InterpreterStackFrame::OP_InitClassMemberGet(const unaligned T * playout)
- {
- JavascriptOperators::OP_InitClassMemberGet(
- GetReg(playout->Instance),
- m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex),
- GetReg(playout->Value));
- }
- template <class T>
- void InterpreterStackFrame::OP_InitClassMemberSet(const unaligned T * playout)
- {
- JavascriptOperators::OP_InitClassMemberSet(
- GetReg(playout->Instance),
- m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex),
- GetReg(playout->Value));
- }
- template <class T>
- void InterpreterStackFrame::OP_InitClassMemberSetComputedName(const unaligned T * playout)
- {
- JavascriptOperators::OP_InitClassMemberSetComputedName(
- GetReg(playout->Instance),
- GetReg(playout->Element),
- GetReg(playout->Value),
- m_functionBody->GetScriptContext());
- }
- template <class T>
- void InterpreterStackFrame::OP_InitClassMemberGetComputedName(const unaligned T * playout)
- {
- JavascriptOperators::OP_InitClassMemberGetComputedName(
- GetReg(playout->Instance),
- GetReg(playout->Element),
- GetReg(playout->Value),
- m_functionBody->GetScriptContext());
- }
- template <class T>
- void InterpreterStackFrame::OP_InitClassMemberComputedName(const unaligned T * playout)
- {
- JavascriptOperators::OP_InitClassMemberComputedName(
- GetReg(playout->Instance),
- GetReg(playout->Element),
- GetReg(playout->Value),
- m_functionBody->GetScriptContext());
- }
- template <class T>
- void InterpreterStackFrame::DoInitLetFld(const unaligned T * playout, Var instance, PropertyOperationFlags flags)
- {
- uint inlineCacheIndex = playout->inlineCacheIndex;
- InlineCache * inlineCache = this->GetInlineCache(inlineCacheIndex);
- Assert(!TaggedNumber::Is(instance));
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- if (!TrySetPropertyLocalFastPath(playout, propertyId, RecyclableObject::UnsafeFromVar(instance), inlineCache, flags))
- {
- JavascriptOperators::OP_InitLetProperty(instance, propertyId, GetReg(playout->Value));
- }
- }
- template <class T>
- void InterpreterStackFrame::DoInitConstFld(const unaligned T * playout, Var instance, PropertyOperationFlags flags)
- {
- uint inlineCacheIndex = playout->inlineCacheIndex;
- InlineCache * inlineCache = this->GetInlineCache(inlineCacheIndex);
- Assert(!TaggedNumber::Is(instance));
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- if (!TrySetPropertyLocalFastPath(playout, propertyId, RecyclableObject::UnsafeFromVar(instance), inlineCache, flags))
- {
- JavascriptOperators::OP_InitConstProperty(instance, propertyId, GetReg(playout->Value));
- }
- }
- template <class T>
- void InterpreterStackFrame::OP_InitProperty(unaligned T* playout)
- {
- DoInitProperty(playout, GetReg(playout->Instance));
- }
- template <class T>
- void InterpreterStackFrame::OP_InitLocalProperty(unaligned T* playout)
- {
- DoInitProperty(playout, this->localClosure);
- }
- template <class T>
- void InterpreterStackFrame::OP_InitInnerFld(const unaligned T* playout)
- {
- DoInitProperty(playout, InnerScopeFromIndex(playout->scopeIndex));
- }
- template <class T>
- void InterpreterStackFrame::OP_InitLetFld(const unaligned T * playout)
- {
- DoInitLetFld(playout, GetReg(playout->Instance));
- }
- template <class T>
- void InterpreterStackFrame::OP_InitInnerLetFld(const unaligned T * playout)
- {
- DoInitLetFld(playout, InnerScopeFromIndex(playout->scopeIndex));
- }
- template <class T>
- void InterpreterStackFrame::OP_InitLocalLetFld(const unaligned T * playout)
- {
- DoInitLetFld(playout, this->localClosure);
- }
- template <class T>
- void InterpreterStackFrame::OP_InitConstFld(const unaligned T * playout)
- {
- DoInitConstFld(playout, GetReg(playout->Instance));
- }
- template <class T>
- void InterpreterStackFrame::OP_InitRootProperty(unaligned T* playout)
- {
- Assert(playout->inlineCacheIndex >= this->m_functionBody->GetRootObjectLoadInlineCacheStart());
- DoInitProperty(playout, this->GetRootObject());
- }
- template <class T>
- void InterpreterStackFrame::OP_InitRootLetFld(const unaligned T * playout)
- {
- Assert(playout->inlineCacheIndex >= this->m_functionBody->GetRootObjectLoadInlineCacheStart());
- DoInitLetFld(playout, this->GetRootObject(), PropertyOperation_Root);
- }
- template <class T>
- void InterpreterStackFrame::OP_InitRootConstFld(const unaligned T * playout)
- {
- Assert(playout->inlineCacheIndex >= this->m_functionBody->GetRootObjectLoadInlineCacheStart());
- DoInitConstFld(playout, this->GetRootObject(), PropertyOperation_Root);
- }
- template <class T>
- void InterpreterStackFrame::OP_InitUndeclLetProperty(unaligned T* playout)
- {
- Var instance = InnerScopeFromIndex(playout->scopeIndex);
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- JavascriptOperators::OP_InitLetProperty(instance, propertyId, this->scriptContext->GetLibrary()->GetUndeclBlockVar());
- }
- template <class T>
- void InterpreterStackFrame::OP_InitUndeclLocalLetProperty(unaligned T* playout)
- {
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- JavascriptOperators::OP_InitLetProperty(this->localClosure, propertyId, this->scriptContext->GetLibrary()->GetUndeclBlockVar());
- }
- void InterpreterStackFrame::OP_InitUndeclRootLetProperty(uint propertyIdIndex)
- {
- Var instance = this->GetRootObject();
- PropertyId propertyId = this->m_functionBody->GetReferencedPropertyId(propertyIdIndex);
- JavascriptOperators::OP_InitUndeclRootLetProperty(instance, propertyId);
- }
- template <class T>
- void InterpreterStackFrame::OP_InitUndeclConstProperty(unaligned T* playout)
- {
- Var instance = InnerScopeFromIndex(playout->scopeIndex);
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- JavascriptOperators::OP_InitConstProperty(instance, propertyId, this->scriptContext->GetLibrary()->GetUndeclBlockVar());
- }
- template <class T>
- void InterpreterStackFrame::OP_InitUndeclLocalConstProperty(unaligned T* playout)
- {
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- JavascriptOperators::OP_InitConstProperty(this->localClosure, propertyId, this->scriptContext->GetLibrary()->GetUndeclBlockVar());
- }
- void InterpreterStackFrame::OP_InitUndeclRootConstProperty(uint propertyIdIndex)
- {
- Var instance = this->GetRootObject();
- PropertyId propertyId = this->m_functionBody->GetReferencedPropertyId(propertyIdIndex);
- JavascriptOperators::OP_InitUndeclRootConstProperty(instance, propertyId);
- }
- template <class T>
- void InterpreterStackFrame::OP_InitUndeclConsoleLetProperty(unaligned T* playout)
- {
- FrameDisplay* pScope = (FrameDisplay*)this->LdEnv();
- AssertMsg(ConsoleScopeActivationObject::Is((DynamicObject*)pScope->GetItem(pScope->GetLength() - 1)), "How come we got this opcode without ConsoleScopeActivationObject?");
- PropertyId propertyId = m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex);
- JavascriptOperators::OP_InitLetProperty(pScope->GetItem(0), propertyId, this->scriptContext->GetLibrary()->GetUndeclBlockVar());
- }
- template <class T>
- void InterpreterStackFrame::OP_InitUndeclConsoleConstProperty(unaligned T* playout)
- {
- FrameDisplay* pScope = (FrameDisplay*)this->LdEnv();
- AssertMsg(ConsoleScopeActivationObject::Is((DynamicObject*)pScope->GetItem(pScope->GetLength() - 1)), "How come we got this opcode without ConsoleScopeActivationObject?");
- PropertyId propertyId = m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex);
- JavascriptOperators::OP_InitConstProperty(pScope->GetItem(0), propertyId, this->scriptContext->GetLibrary()->GetUndeclBlockVar());
- }
- #if ENABLE_PROFILE_INFO
- template <class T>
- void InterpreterStackFrame::ProfiledInitProperty(unaligned T* playout, Var instance)
- {
- ProfilingHelpers::ProfiledInitFld(
- RecyclableObject::FromVar(instance),
- GetPropertyIdFromCacheId(playout->inlineCacheIndex),
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- GetReg(playout->Value),
- GetFunctionBody());
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledInitProperty(unaligned T* playout)
- {
- ProfiledInitProperty(playout, GetReg(playout->Instance));
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledInitLocalProperty(unaligned T* playout)
- {
- ProfiledInitProperty(playout, this->localClosure);
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledInitRootProperty(unaligned T* playout)
- {
- ProfiledInitProperty(playout, this->GetRootObject());
- }
- template <class T>
- void InterpreterStackFrame::OP_ProfiledGetElementI(const unaligned OpLayoutDynamicProfile<T>* playout)
- {
- ThreadContext* threadContext = this->GetScriptContext()->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- SetReg(
- playout->Value,
- ProfilingHelpers::ProfiledLdElem(
- GetReg(playout->Instance),
- GetReg(playout->Element),
- m_functionBody,
- playout->profileId,
- this->TestFlags(InterpreterStackFrameFlags_ProcessingBailOutOnArrayAccessHelperCall),
- this->TestFlags(InterpreterStackFrameFlags_ProcessingBailOutOnArraySpecialization)));
- this->ClearFlags(InterpreterStackFrameFlags_ProcessingBailOutOnArrayAccessHelperCall | InterpreterStackFrameFlags_ProcessingBailOutOnArraySpecialization);
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- }
- #endif
- template <typename T>
- void InterpreterStackFrame::OP_GetElementI(const unaligned T* playout)
- {
- ThreadContext* threadContext = this->GetScriptContext()->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- // Same fast path as in the backend.
- Var instance = GetReg(playout->Instance);
- // Only enable fast path if the javascript array is not cross site
- Var element;
- #if ENABLE_PROFILE_INFO
- if (!TaggedNumber::Is(instance) && VirtualTableInfo<JavascriptArray>::HasVirtualTable(instance))
- {
- element =
- ProfilingHelpers::ProfiledLdElem_FastPath(
- JavascriptArray::UnsafeFromVar(instance),
- GetReg(playout->Element),
- GetScriptContext());
- }
- else
- #endif
- {
- element = JavascriptOperators::OP_GetElementI(instance, GetReg(playout->Element), GetScriptContext());
- }
- this->ClearFlags(InterpreterStackFrameFlags_ProcessingBailOutOnArrayAccessHelperCall);
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- SetReg(playout->Value, element);
- }
- template <typename T>
- void InterpreterStackFrame::OP_SetElementI(const unaligned T* playout, PropertyOperationFlags flags)
- {
- ThreadContext* threadContext = this->GetScriptContext()->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- // Same fast path as in the backend.
- Var instance = GetReg(playout->Instance);
- const Var varIndex = GetReg(playout->Element);
- const Var value = GetReg(playout->Value);
- #if ENABLE_PROFILE_INFO
- // Only enable fast path if the javascript array is not cross site
- if (!TaggedNumber::Is(instance) &&
- VirtualTableInfo<JavascriptArray>::HasVirtualTable(instance) &&
- !JavascriptOperators::SetElementMayHaveImplicitCalls(GetScriptContext()))
- {
- ProfilingHelpers::ProfiledStElem_FastPath(
- JavascriptArray::UnsafeFromVar(instance),
- varIndex,
- value,
- GetScriptContext(),
- flags);
- }
- else
- #endif
- {
- JavascriptOperators::OP_SetElementI(instance, varIndex, value, GetScriptContext(), flags);
- }
- this->ClearFlags(InterpreterStackFrameFlags_ProcessingBailOutOnArrayAccessHelperCall);
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- }
- #if ENABLE_PROFILE_INFO
- template <typename T>
- void InterpreterStackFrame::OP_ProfiledSetElementI(
- const unaligned OpLayoutDynamicProfile<T>* playout,
- PropertyOperationFlags flags)
- {
- ThreadContext* threadContext = this->GetScriptContext()->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- ProfilingHelpers::ProfiledStElem(
- GetReg(playout->Instance),
- GetReg(playout->Element),
- GetReg(playout->Value),
- m_functionBody,
- playout->profileId,
- flags,
- this->TestFlags(InterpreterStackFrameFlags_ProcessingBailOutOnArrayAccessHelperCall),
- this->TestFlags(InterpreterStackFrameFlags_ProcessingBailOutOnArraySpecialization));
- this->ClearFlags(InterpreterStackFrameFlags_ProcessingBailOutOnArrayAccessHelperCall | InterpreterStackFrameFlags_ProcessingBailOutOnArraySpecialization);
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- }
- #endif
- template <typename T>
- void InterpreterStackFrame::OP_SetElementIStrict(const unaligned T* playout)
- {
- ThreadContext* threadContext = this->GetScriptContext()->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- OP_SetElementI(playout, PropertyOperation_StrictMode);
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- }
- #if ENABLE_PROFILE_INFO
- template <typename T>
- void InterpreterStackFrame::OP_ProfiledSetElementIStrict(const unaligned OpLayoutDynamicProfile<T>* playout)
- {
- ThreadContext* threadContext = this->GetScriptContext()->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- OP_ProfiledSetElementI(playout, PropertyOperation_StrictMode);
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- }
- #endif
- template <class T>
- void InterpreterStackFrame::OP_LdArrayHeadSegment(const unaligned T* playout)
- {
- JavascriptArray* array = JavascriptArray::FromAnyArray(GetReg(playout->R1));
- // The array is create by the built-in on the same script context
- Assert(array->GetScriptContext() == GetScriptContext());
- SetNonVarReg(playout->R0, array->GetHead());
- }
- template <class T>
- void InterpreterStackFrame::OP_SetArraySegmentItem_CI4(const unaligned T* playout)
- {
- SparseArraySegment<Var> * segment = (SparseArraySegment<Var> *)GetNonVarReg(playout->Instance);
- uint32 index = playout->Element;
- Var value = GetReg(playout->Value);
- Assert(segment->left == 0);
- Assert(index < segment->length);
- segment->elements[index] = value;
- }
- template <class T>
- void InterpreterStackFrame::OP_NewScArray(const unaligned T * playout)
- {
- JavascriptArray *arr;
- arr = scriptContext->GetLibrary()->CreateArrayLiteral(playout->C1);
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- arr->CheckForceES5Array();
- #endif
- SetReg(playout->R0, arr);
- }
- #if ENABLE_PROFILE_INFO
- template <bool Profiled, class T>
- void InterpreterStackFrame::ProfiledNewScArray(const unaligned OpLayoutDynamicProfile<T> * playout)
- {
- if (!Profiled && !isAutoProfiling)
- {
- OP_NewScArray(playout);
- return;
- }
- SetReg(
- playout->R0,
- ProfilingHelpers::ProfiledNewScArray(
- playout->C1,
- m_functionBody,
- playout->profileId));
- }
- #else
- template <bool Profiled, class T>
- void InterpreterStackFrame::ProfiledNewScArray(const unaligned OpLayoutDynamicProfile<T> * playout)
- {
- Assert(!Profiled);
- OP_NewScArray(playout);
- }
- #endif
- void InterpreterStackFrame::OP_NewScIntArray(const unaligned OpLayoutAuxiliary * playout)
- {
- const Js::AuxArray<int32> *ints = Js::ByteCodeReader::ReadAuxArray<int32>(playout->Offset, this->GetFunctionBody());
- JavascriptNativeIntArray *arr = scriptContext->GetLibrary()->CreateNativeIntArrayLiteral(ints->count);
- SparseArraySegment<int32> * segment = (SparseArraySegment<int32>*)arr->GetHead();
- JavascriptOperators::AddIntsToArraySegment(segment, ints);
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- arr->CheckForceES5Array();
- #endif
- SetReg(playout->R0, arr);
- }
- #if ENABLE_PROFILE_INFO
- template <bool Profiled>
- void InterpreterStackFrame::ProfiledNewScIntArray(const unaligned OpLayoutDynamicProfile<OpLayoutAuxiliary> * playout)
- {
- if (!Profiled && !isAutoProfiling)
- {
- OP_NewScIntArray(playout);
- return;
- }
- const Js::AuxArray<int32> *ints = Js::ByteCodeReader::ReadAuxArray<int32>(playout->Offset, this->GetFunctionBody());
- Js::ProfileId profileId = playout->profileId;
- FunctionBody *functionBody = this->m_functionBody;
- ArrayCallSiteInfo *arrayInfo = functionBody->GetDynamicProfileInfo()->GetArrayCallSiteInfo(functionBody, profileId);
- Assert(arrayInfo);
- JavascriptArray *arr;
- if (arrayInfo && arrayInfo->IsNativeIntArray())
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary *lib = scriptContext->GetLibrary();
- if (JavascriptLibrary::IsCopyOnAccessArrayCallSite(lib, arrayInfo, ints->count))
- {
- Assert(lib->cacheForCopyOnAccessArraySegments);
- arr = scriptContext->GetLibrary()->CreateCopyOnAccessNativeIntArrayLiteral(arrayInfo, functionBody, ints);
- }
- else
- #endif
- {
- arr = scriptContext->GetLibrary()->CreateNativeIntArrayLiteral(ints->count);
- SparseArraySegment<int32> *segment = (SparseArraySegment<int32>*)arr->GetHead();
- JavascriptOperators::AddIntsToArraySegment(segment, ints);
- }
- JavascriptNativeIntArray *intArray = reinterpret_cast<JavascriptNativeIntArray*>(arr);
- Recycler *recycler = scriptContext->GetRecycler();
- intArray->SetArrayCallSite(profileId, recycler->CreateWeakReferenceHandle(functionBody));
- }
- else if (arrayInfo && arrayInfo->IsNativeFloatArray())
- {
- arr = scriptContext->GetLibrary()->CreateNativeFloatArrayLiteral(ints->count);
- SparseArraySegment<double> * segment = (SparseArraySegment<double>*)arr->GetHead();
- for (uint i = 0; i < ints->count; i++)
- {
- segment->elements[i] = (double)ints->elements[i];
- }
- JavascriptNativeFloatArray *floatArray = reinterpret_cast<JavascriptNativeFloatArray*>(arr);
- Recycler *recycler = scriptContext->GetRecycler();
- floatArray->SetArrayCallSite(profileId, recycler->CreateWeakReferenceHandle(functionBody));
- }
- else
- {
- arr = scriptContext->GetLibrary()->CreateArrayLiteral(ints->count);
- SparseArraySegment<Var> * segment = (SparseArraySegment<Var>*)arr->GetHead();
- for (uint i = 0; i < ints->count; i++)
- {
- segment->elements[i] = JavascriptNumber::ToVar(ints->elements[i], scriptContext);
- }
- }
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- arr->CheckForceES5Array();
- #endif
- SetReg(playout->R0, arr);
- }
- #else
- template <bool Profiled>
- void InterpreterStackFrame::ProfiledNewScIntArray(const unaligned OpLayoutDynamicProfile<OpLayoutAuxiliary> * playout)
- {
- OP_NewScIntArray(playout);
- }
- #endif
- void InterpreterStackFrame::OP_NewScFltArray(const unaligned OpLayoutAuxiliary * playout)
- {
- const Js::AuxArray<double> *doubles = Js::ByteCodeReader::ReadAuxArray<double>(playout->Offset, this->GetFunctionBody());
- JavascriptNativeFloatArray *arr = scriptContext->GetLibrary()->CreateNativeFloatArrayLiteral(doubles->count);
- SparseArraySegment<double> * segment = (SparseArraySegment<double>*)arr->GetHead();
- JavascriptOperators::AddFloatsToArraySegment(segment, doubles);
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- arr->CheckForceES5Array();
- #endif
- SetReg(playout->R0, arr);
- }
- #if ENABLE_PROFILE_INFO
- template <bool Profiled>
- void InterpreterStackFrame::ProfiledNewScFltArray(const unaligned OpLayoutDynamicProfile<OpLayoutAuxiliary> * playout)
- {
- if (!Profiled && !isAutoProfiling)
- {
- OP_NewScFltArray(playout);
- return;
- }
- const Js::AuxArray<double> *doubles = Js::ByteCodeReader::ReadAuxArray<double>(playout->Offset, this->GetFunctionBody());
- Js::ProfileId profileId = playout->profileId;
- FunctionBody *functionBody = this->m_functionBody;
- ArrayCallSiteInfo *arrayInfo = functionBody->GetDynamicProfileInfo()->GetArrayCallSiteInfo(functionBody, profileId);
- Assert(arrayInfo);
- JavascriptArray *arr;
- if (arrayInfo && arrayInfo->IsNativeFloatArray())
- {
- arrayInfo->SetIsNotNativeIntArray();
- arr = scriptContext->GetLibrary()->CreateNativeFloatArrayLiteral(doubles->count);
- SparseArraySegment<double> * segment = (SparseArraySegment<double>*)arr->GetHead();
- JavascriptOperators::AddFloatsToArraySegment(segment, doubles);
- JavascriptNativeFloatArray *floatArray = reinterpret_cast<JavascriptNativeFloatArray*>(arr);
- Recycler *recycler = scriptContext->GetRecycler();
- floatArray->SetArrayCallSite(profileId, recycler->CreateWeakReferenceHandle(functionBody));
- }
- else
- {
- arr = scriptContext->GetLibrary()->CreateArrayLiteral(doubles->count);
- SparseArraySegment<Var> * segment = (SparseArraySegment<Var>*)arr->GetHead();
- for (uint i = 0; i < doubles->count; i++)
- {
- segment->elements[i] = JavascriptNumber::ToVarNoCheck(doubles->elements[i], scriptContext);
- }
- }
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- arr->CheckForceES5Array();
- #endif
- SetReg(playout->R0, arr);
- }
- #else
- template <bool Profiled>
- void InterpreterStackFrame::ProfiledNewScFltArray(const unaligned OpLayoutDynamicProfile<OpLayoutAuxiliary> * playout)
- {
- OP_NewScFltArray(playout);
- }
- #endif
- void InterpreterStackFrame::OP_SetArraySegmentVars(const unaligned OpLayoutAuxiliary * playout)
- {
- const Js::VarArray *vars = Js::ByteCodeReader::ReadAuxArray<Var>(playout->Offset, this->GetFunctionBody());
- SparseArraySegment<Var> * segment = (SparseArraySegment<Var> *)GetNonVarReg(playout->R0);
- JavascriptOperators::AddVarsToArraySegment(segment, vars);
- }
- template <class T>
- void InterpreterStackFrame::OP_SetArrayItemC_CI4(const unaligned T* playout)
- {
- JavascriptArray* array = JavascriptArray::FromAnyArray(GetReg(playout->Instance));
- uint32 index = playout->Element;
- Var value = GetReg(playout->Value);
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(value);
- #endif
- // The array is create by the built-in on the same script context
- Assert(array->GetScriptContext() == GetScriptContext());
- TypeId typeId = array->GetTypeId();
- if (typeId == TypeIds_NativeIntArray)
- {
- JavascriptArray::OP_SetNativeIntElementC(reinterpret_cast<JavascriptNativeIntArray*>(array), index, value, array->GetScriptContext());
- }
- else if (typeId == TypeIds_NativeFloatArray)
- {
- JavascriptArray::OP_SetNativeFloatElementC(reinterpret_cast<JavascriptNativeFloatArray*>(array), index, value, array->GetScriptContext());
- }
- else
- {
- array->SetArrayLiteralItem(index, value);
- }
- }
- template <class T>
- void InterpreterStackFrame::OP_SetArrayItemI_CI4(const unaligned T* playout)
- {
- // Note that this code assumes that we only get here when we see an array literal,
- // so we know that the instance is truly an array, and the index is a uint32.
- // If/when we use this for cases like "a[0] = x", we'll at least have to check
- // whether "a" is really an array.
- JavascriptArray* array = JavascriptArray::FromAnyArray(GetReg(playout->Instance));
- // The array is create by the built-in on the same script context
- Assert(array->GetScriptContext() == GetScriptContext());
- uint32 index = playout->Element;
- Var value = GetReg(playout->Value);
- Assert(VirtualTableInfo<JavascriptArray>::HasVirtualTable(array));
- SparseArraySegment<Var>* lastUsedSeg = (SparseArraySegment<Var>*)array->GetLastUsedSegment();
- if (index >= lastUsedSeg->left)
- {
- uint32 index2 = index - lastUsedSeg->left;
- if (index2 < lastUsedSeg->size)
- {
- // Successful fastpath
- array->DirectSetItemInLastUsedSegmentAt(index2, value);
- return;
- }
- }
- ScriptContext* scriptContext = array->GetScriptContext();
- JavascriptOperators::SetItem(array, array, index, value, scriptContext);
- }
- #if ENABLE_PROFILE_INFO
- Var InterpreterStackFrame::OP_ProfiledLdThis(Var thisVar, int moduleID, ScriptContext *scriptContext)
- {
- FunctionBody * functionBody = this->m_functionBody;
- DynamicProfileInfo * dynamicProfileInfo = functionBody->GetDynamicProfileInfo();
- TypeId typeId = JavascriptOperators::GetTypeId(thisVar);
- if (JavascriptOperators::IsThisSelf(typeId))
- {
- Assert(typeId != TypeIds_GlobalObject || ((Js::GlobalObject*)thisVar)->ToThis() == thisVar);
- Assert(typeId != TypeIds_ModuleRoot || JavascriptOperators::GetThisFromModuleRoot(thisVar) == thisVar);
- // Record the fact that we saw a trivial LdThis.
- dynamicProfileInfo->RecordThisInfo(thisVar, ThisType_Simple);
- return thisVar;
- }
- thisVar = JavascriptOperators::OP_GetThis(thisVar, moduleID, scriptContext);
- // Record the fact that we saw a LdThis that had to map its source to something else, or at least
- // forced us to call a helper, e.g., a FastDOM object with an unrecognized type ID.
- dynamicProfileInfo->RecordThisInfo(thisVar, ThisType_Mapped);
- return thisVar;
- }
- Var InterpreterStackFrame::OP_ProfiledStrictLdThis(Var thisVar, ScriptContext* scriptContext)
- {
- FunctionBody * functionBody = this->m_functionBody;
- DynamicProfileInfo * dynamicProfileInfo = functionBody->GetDynamicProfileInfo();
- TypeId typeId = JavascriptOperators::GetTypeId(thisVar);
- if (typeId == TypeIds_ActivationObject)
- {
- thisVar = scriptContext->GetLibrary()->GetUndefined();
- dynamicProfileInfo->RecordThisInfo(thisVar, ThisType_Mapped);
- return thisVar;
- }
- dynamicProfileInfo->RecordThisInfo(thisVar, ThisType_Simple);
- return thisVar;
- }
- #endif
- void InterpreterStackFrame::OP_InitCachedFuncs(const unaligned OpLayoutAuxNoReg * playout)
- {
- const FuncInfoArray *info = Js::ByteCodeReader::ReadAuxArray<FuncInfoEntry>(playout->Offset, this->GetFunctionBody());
- JavascriptOperators::OP_InitCachedFuncs(this->localClosure, GetLocalFrameDisplay(), info, GetScriptContext());
- }
- Var InterpreterStackFrame::OP_GetCachedFunc(Var instance, int32 index)
- {
- ActivationObjectEx *obj = ActivationObjectEx::FromVar(instance);
- FuncCacheEntry *entry = obj->GetFuncCacheEntry((uint)index);
- return entry->func;
- }
- void InterpreterStackFrame::OP_CommitScope()
- {
- const Js::PropertyIdArray *propIds = this->m_functionBody->GetFormalsPropIdArray();
- this->OP_CommitScopeHelper(propIds);
- }
- void InterpreterStackFrame::OP_CommitScopeHelper(const PropertyIdArray *propIds)
- {
- ActivationObjectEx *obj = ActivationObjectEx::FromVar(this->localClosure);
- ScriptFunction *func = obj->GetParentFunc();
- Assert(obj->GetParentFunc() == func);
- if (func->GetCachedScope() == obj)
- {
- PropertyId firstVarSlot = ActivationObjectEx::GetFirstVarSlot(propIds);
- Var undef = scriptContext->GetLibrary()->GetUndefined();
- for (uint i = firstVarSlot; i < propIds->count; i++)
- {
- obj->SetSlot(SetSlotArguments(propIds->elements[i], i, undef));
- }
- obj->SetCommit(true);
- }
- }
- Var InterpreterStackFrame::OP_NewScObjectSimple()
- {
- Var object = scriptContext->GetLibrary()->CreateObject(true);
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(object));
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag))
- {
- object = JavascriptProxy::AutoProxyWrapper(object);
- }
- #endif
- return object;
- }
- void InterpreterStackFrame::OP_NewScObjectLiteral(const unaligned OpLayoutAuxiliary * playout)
- {
- const Js::PropertyIdArray *propIds = Js::ByteCodeReader::ReadPropertyIdArray(playout->Offset, this->GetFunctionBody());
- Var newObj = JavascriptOperators::NewScObjectLiteral(GetScriptContext(), propIds,
- this->GetFunctionBody()->GetObjectLiteralTypeRef(playout->C1));
- SetReg(playout->R0, newObj);
- }
- void InterpreterStackFrame::OP_NewScObjectLiteral_LS(const unaligned OpLayoutAuxiliary * playout, RegSlot& target)
- {
- const Js::PropertyIdArray *propIds = Js::ByteCodeReader::ReadPropertyIdArray(playout->Offset, this->GetFunctionBody());
- target = playout->R0;
- Var newObj = JavascriptOperators::NewScObjectLiteral(GetScriptContext(), propIds,
- this->GetFunctionBody()->GetObjectLiteralTypeRef(playout->C1));
- SetReg(playout->R0, newObj);
- target = Js::Constants::NoRegister;
- }
- void InterpreterStackFrame::OP_LdPropIds(const unaligned OpLayoutAuxiliary * playout)
- {
- const Js::PropertyIdArray *propIds = Js::ByteCodeReader::ReadPropertyIdArray(playout->Offset, this->GetFunctionBody());
- SetNonVarReg(playout->R0, (Var)propIds);
- }
- bool InterpreterStackFrame::IsCurrentLoopNativeAddr(void * codeAddr) const
- {
- if (this->GetCurrentLoopNum() == LoopHeader::NoLoop)
- {
- return false;
- }
- // TODO: Do more verification?
- return true;
- }
- template <LayoutSize layoutSize, bool profiled>
- const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
- {
- uint32 loopId = m_reader.GetLayout<OpLayoutT_Unsigned1<LayoutSizePolicy<layoutSize>>>(ip)->C1;
- return OP_ProfiledLoopBodyStart<layoutSize, profiled>(loopId);
- }
- #ifdef ASMJS_PLAT
- template <LayoutSize layoutSize, bool profiled>
- const byte * InterpreterStackFrame::OP_ProfiledWasmLoopBodyStart(const byte * ip)
- {
- uint32 loopId = m_reader.GetLayout<OpLayoutT_WasmLoopStart<LayoutSizePolicy<layoutSize>>>(ip)->loopId;
- return OP_ProfiledLoopBodyStart<layoutSize, profiled>(loopId);
- }
- #endif
- #if ENABLE_PROFILE_INFO
- void InterpreterStackFrame::OP_RecordImplicitCall(uint loopNumber)
- {
- Assert(Js::DynamicProfileInfo::EnableImplicitCallFlags(GetFunctionBody()));
- Assert(loopNumber < this->m_functionBody->GetLoopCount());
- FunctionBody* functionBody = this->m_functionBody;
- DynamicProfileInfo * dynamicProfileInfo = functionBody->GetDynamicProfileInfo();
- ThreadContext * threadContext = scriptContext->GetThreadContext();
- dynamicProfileInfo->RecordLoopImplicitCallFlags(functionBody, loopNumber, threadContext->GetImplicitCallFlags());
- }
- template <LayoutSize layoutSize, bool profiled>
- const byte * InterpreterStackFrame::OP_ProfiledLoopStart(const byte * ip)
- {
- const uint32 C1 = m_reader.GetLayout<OpLayoutT_Unsigned1<LayoutSizePolicy<layoutSize>>>(ip)->C1;
- if (!profiled && !isAutoProfiling)
- {
- return ip;
- }
- ThreadContext *const threadContext = GetScriptContext()->GetThreadContext();
- threadContext->IncrementLoopDepth();
- // Save the implicit call flags. The interpreter may switch to profiling mode during LoopBodyStart, so always do this.
- Assert(Js::DynamicProfileInfo::EnableImplicitCallFlags(GetFunctionBody()));
- this->savedLoopImplicitCallFlags[C1] = threadContext->GetImplicitCallFlags();
- threadContext->SetImplicitCallFlags(ImplicitCall_None);
- this->currentLoopCounter = 0;
- if (!profiled)
- {
- return ip;
- }
- LayoutSize localLayoutSize;
- OpCode peekOp = m_reader.PeekOp(ip, localLayoutSize);
- Assert(peekOp != OpCode::LoopBodyStart);
- if (peekOp == OpCode::ProfiledLoopBodyStart)
- {
- Assert(localLayoutSize == layoutSize);
- ip += Js::OpCodeUtil::EncodedSize(peekOp, layoutSize);
- // We are doing JIT loop body. Process the first ProfiledLoopBodyStart to avoid recording
- // the implicit call before the first iteration
- uint32 C2 = m_reader.GetLayout<OpLayoutT_Unsigned1<LayoutSizePolicy<layoutSize>>>(ip)->C1;
- Assert(C1 == C2);
- (this->*opProfiledLoopBodyStart)(C1, layoutSize, true /* isFirstIteration */);
- return m_reader.GetIP();
- }
- return ip;
- }
- template <LayoutSize layoutSize, bool profiled>
- const byte * InterpreterStackFrame::OP_ProfiledLoopEnd(const byte * ip)
- {
- uint32 loopNumber = m_reader.GetLayout<OpLayoutT_Unsigned1<LayoutSizePolicy<layoutSize>>>(ip)->C1;
- if (!profiled && !isAutoProfiling)
- {
- return ip;
- }
- this->CheckIfLoopIsHot(this->currentLoopCounter);
- Js::FunctionBody *fn = this->function->GetFunctionBody();
- if (fn->HasDynamicProfileInfo())
- {
- fn->GetAnyDynamicProfileInfo()->SetLoopInterpreted(loopNumber);
- // If the counter is 0, there is a high chance that some config disabled tracking that information. (ie: -off:jitloopbody)
- // Assume it is valid for memop in this case.
- if (this->currentLoopCounter >= (uint)CONFIG_FLAG(MinMemOpCount) ||
- (this->currentLoopCounter == 0 && !this->m_functionBody->DoJITLoopBody())
- )
- {
- // This flag becomes relevant only if the loop has been interpreted
- fn->GetAnyDynamicProfileInfo()->SetMemOpMinReached(loopNumber);
- }
- }
- this->currentLoopCounter = 0;
- if (profiled)
- {
- Assert(Js::DynamicProfileInfo::EnableImplicitCallFlags(GetFunctionBody()));
- OP_RecordImplicitCall(loopNumber);
- if (switchProfileModeOnLoopEndNumber == loopNumber)
- {
- // Stop profiling since the jitted loop body would be exiting the loop
- Assert(!switchProfileMode);
- switchProfileMode = true;
- switchProfileModeOnLoopEndNumber = 0u - 1;
- }
- }
- // Restore the implicit call flags state and add with flags in the loop as well
- ThreadContext *const threadContext = GetScriptContext()->GetThreadContext();
- threadContext->AddImplicitCallFlags(this->savedLoopImplicitCallFlags[loopNumber]);
- threadContext->DecrementLoopDepth();
- return ip;
- }
- template <LayoutSize layoutSize, bool profiled>
- const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(uint loopId)
- {
- if (profiled || isAutoProfiling)
- {
- this->currentLoopCounter++;
- }
- if (profiled)
- {
- OP_RecordImplicitCall(loopId);
- }
- (this->*(profiled ? opProfiledLoopBodyStart : opLoopBodyStart))(loopId, layoutSize, false /* isFirstIteration */);
- return m_reader.GetIP();
- }
- template<bool InterruptProbe, bool JITLoopBody>
- void InterpreterStackFrame::ProfiledLoopBodyStart(uint32 loopNumber, LayoutSize layoutSize, bool isFirstIteration)
- {
- Assert(Js::DynamicProfileInfo::EnableImplicitCallFlags(GetFunctionBody()));
- if (InterruptProbe)
- {
- this->DoInterruptProbe();
- }
- #if ENABLE_TTD
- if (SHOULD_DO_TTD_STACK_STMT_OP(this->scriptContext))
- {
- this->scriptContext->GetThreadContext()->TTDExecutionInfo->UpdateLoopCountInfo();
- }
- #endif
- if (!JITLoopBody || this->IsInCatchOrFinallyBlock())
- {
- // For functions having try-catch-finally, jit loop bodies for loops that are contained only in a try block,
- // not even indirect containment in a Catch or Finally.
- return;
- }
- LoopHeader const * loopHeader = DoLoopBodyStart(loopNumber, layoutSize, false, isFirstIteration);
- Assert(loopHeader == nullptr || this->m_functionBody->GetLoopNumber(loopHeader) == loopNumber);
- if (loopHeader != nullptr)
- {
- // We executed jitted loop body, no implicit call information available for this loop
- uint currentOffset = m_reader.GetCurrentOffset();
- if (!loopHeader->Contains(currentOffset) || (m_reader.PeekOp() == OpCode::ProfiledLoopEnd))
- {
- // Restore the outer loop's implicit call flags
- scriptContext->GetThreadContext()->SetImplicitCallFlags(this->savedLoopImplicitCallFlags[loopNumber]);
- }
- else
- {
- // We bailout from the loop, just continue collect implicit call flags for this loop
- }
- }
- }
- #else
- template <LayoutSize layoutSize, bool profiled>
- const byte * InterpreterStackFrame::OP_ProfiledLoopStart(const byte * ip)
- {
- Assert(!profiled);
- return ip;
- }
- template <LayoutSize layoutSize, bool profiled>
- const byte * InterpreterStackFrame::OP_ProfiledLoopEnd(const byte * ip)
- {
- Assert(!profiled);
- return ip;
- }
- template <LayoutSize layoutSize, bool profiled>
- const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(uint loopId)
- {
- Assert(!profiled);
- (this->*opLoopBodyStart)(loopId, layoutSize, false /* isFirstIteration */);
- return m_reader.GetIP();
- }
- #endif
- template<bool InterruptProbe, bool JITLoopBody>
- void InterpreterStackFrame::LoopBodyStart(uint32 loopNumber, LayoutSize layoutSize, bool isFirstIteration)
- {
- if (InterruptProbe)
- {
- this->DoInterruptProbe();
- }
- #if ENABLE_TTD
- if (SHOULD_DO_TTD_STACK_STMT_OP(this->scriptContext))
- {
- this->scriptContext->GetThreadContext()->TTDExecutionInfo->UpdateLoopCountInfo();
- }
- #endif
- if (!JITLoopBody || this->IsInCatchOrFinallyBlock())
- {
- // For functions having try-catch-finally, jit loop bodies for loops that are contained only in a try block,
- // not even indirect containment in a Catch or Finally.
- return;
- }
- DoLoopBodyStart(loopNumber, layoutSize, true, isFirstIteration);
- }
- LoopHeader const * InterpreterStackFrame::DoLoopBodyStart(uint32 loopNumber, LayoutSize layoutSize, const bool doProfileLoopCheck, const bool isFirstIteration)
- {
- #if ENABLE_PROFILE_INFO
- class AutoRestoreLoopNumbers
- {
- private:
- InterpreterStackFrame * const interpreterStackFrame;
- uint32 loopNumber;
- bool doProfileLoopCheck;
- public:
- AutoRestoreLoopNumbers(InterpreterStackFrame *const interpreterStackFrame, uint32 loopNumber, bool doProfileLoopCheck)
- : interpreterStackFrame(interpreterStackFrame), loopNumber(loopNumber), doProfileLoopCheck(doProfileLoopCheck)
- {
- Assert(interpreterStackFrame->currentLoopNum == LoopHeader::NoLoop);
- interpreterStackFrame->currentLoopNum = loopNumber;
- interpreterStackFrame->m_functionBody->SetRecentlyBailedOutOfJittedLoopBody(false);
- }
- ~AutoRestoreLoopNumbers()
- {
- interpreterStackFrame->currentLoopNum = LoopHeader::NoLoop;
- interpreterStackFrame->currentLoopCounter = 0;
- Js::FunctionBody* fn = interpreterStackFrame->m_functionBody;
- if (fn->RecentlyBailedOutOfJittedLoopBody())
- {
- if (doProfileLoopCheck && interpreterStackFrame->isAutoProfiling)
- {
- // Start profiling the loop after a bailout. Some bailouts require subsequent profile data collection such
- // that the rejitted loop body would not bail out again for the same reason.
- Assert(!interpreterStackFrame->switchProfileMode);
- interpreterStackFrame->switchProfileMode = true;
- Assert(interpreterStackFrame->switchProfileModeOnLoopEndNumber == 0u - 1);
- interpreterStackFrame->switchProfileModeOnLoopEndNumber = loopNumber;
- }
- }
- else
- {
- if (interpreterStackFrame->switchProfileModeOnLoopEndNumber == loopNumber)
- {
- // Stop profiling since the jitted loop body would be exiting the loop
- Assert(!interpreterStackFrame->switchProfileMode);
- interpreterStackFrame->switchProfileMode = true;
- interpreterStackFrame->switchProfileModeOnLoopEndNumber = 0u - 1;
- }
- interpreterStackFrame->scriptContext->GetThreadContext()->DecrementLoopDepth();
- }
- }
- };
- #endif
- Js::FunctionBody* fn = this->m_functionBody;
- Assert(loopNumber < fn->GetLoopCount());
- Assert(!this->IsInCatchOrFinallyBlock());
- Js::LoopHeader *loopHeader = fn->GetLoopHeader(loopNumber);
- loopHeader->isInTry = this->TestFlags(Js::InterpreterStackFrameFlags_WithinTryBlock);
- loopHeader->isInTryFinally = this->TestFlags(Js::InterpreterStackFrameFlags_WithinTryFinallyBlock);
- Js::LoopEntryPointInfo * entryPointInfo = loopHeader->GetCurrentEntryPointInfo();
- if (fn->ForceJITLoopBody() && loopHeader->interpretCount == 0 &&
- (entryPointInfo != NULL && entryPointInfo->IsNotScheduled()))
- {
- #if ENABLE_PROFILE_INFO
- if (Js::DynamicProfileInfo::EnableImplicitCallFlags(GetFunctionBody()))
- {
- scriptContext->GetThreadContext()->AddImplicitCallFlags(this->savedLoopImplicitCallFlags[loopNumber]);
- }
- #endif
- #if ENABLE_NATIVE_CODEGEN
- GenerateLoopBody(scriptContext->GetNativeCodeGenerator(), fn, loopHeader, entryPointInfo, fn->GetLocalsCount(), this->m_localSlots);
- #endif
- }
- #if ENABLE_NATIVE_CODEGEN
- // If we have JITted the loop, call the JITted code
- if (entryPointInfo != NULL && entryPointInfo->IsCodeGenDone())
- {
- #if DBG_DUMP
- if (PHASE_TRACE1(Js::JITLoopBodyPhase) && CONFIG_FLAG(Verbose))
- {
- fn->DumpFunctionId(true);
- Output::Print(_u(": %-20s LoopBody Execute Loop: %2d\n"), fn->GetDisplayName(), loopNumber);
- Output::Flush();
- }
- loopHeader->nativeCount++;
- #endif
- #ifdef BGJIT_STATS
- entryPointInfo->MarkAsUsed();
- #endif
- entryPointInfo->EnsureIsReadyToCall();
- entryPointInfo->SetNativeEntryPointProcessed();
- RegSlot envReg = this->m_functionBody->GetEnvRegister();
- if (envReg != Constants::NoRegister)
- {
- this->SetNonVarReg(envReg, this->LdEnv());
- }
- RegSlot localClosureReg = this->m_functionBody->GetLocalClosureRegister();
- RegSlot localFrameDisplayReg = this->m_functionBody->GetLocalFrameDisplayRegister();
- RegSlot paramClosureReg = this->m_functionBody->GetParamClosureRegister();
- if (entryPointInfo->HasJittedStackClosure())
- {
- // The jitted code is expecting the closure registers to point to known stack locations where
- // the closures can be found and possibly boxed.
- // In a jitted loop body, those locations are the local closure fields on the interpreter instance.
- if (localClosureReg != Constants::NoRegister)
- {
- this->SetNonVarReg(localClosureReg, &this->localClosure);
- }
- if (localFrameDisplayReg != Constants::NoRegister)
- {
- this->SetNonVarReg(localFrameDisplayReg, &this->localFrameDisplay);
- }
- if (paramClosureReg != Constants::NoRegister)
- {
- this->SetNonVarReg(paramClosureReg, &this->paramClosure);
- }
- }
- else
- {
- // In non-stack-closure jitted code, the closure registers are expected to hold the addresses
- // of the actual structures.
- if (localClosureReg != Constants::NoRegister)
- {
- this->SetNonVarReg(localClosureReg, this->localClosure);
- }
- if (localFrameDisplayReg != Constants::NoRegister)
- {
- this->SetNonVarReg(localFrameDisplayReg, this->localFrameDisplay);
- }
- if (paramClosureReg != Constants::NoRegister)
- {
- this->SetNonVarReg(paramClosureReg, this->paramClosure);
- }
- }
- uint32 innerScopeCount = this->m_functionBody->GetInnerScopeCount();
- for (uint32 i = 0; i < innerScopeCount; i++)
- {
- // As with the function-level scope, transfer the inner scopes from the interpreter's side storage
- // to their dedicated register slots.
- SetNonVarReg(this->m_functionBody->GetFirstInnerScopeRegister() + i, InnerScopeFromIndex(i));
- }
- uint newOffset = 0;
- if (fn->GetIsAsmJsFunction())
- {
- AutoRestoreLoopNumbers autoRestore(this, loopNumber, doProfileLoopCheck);
- newOffset = this->CallAsmJsLoopBody(entryPointInfo->jsMethod);
- }
- else
- {
- AutoRestoreLoopNumbers autoRestore(this, loopNumber, doProfileLoopCheck);
- newOffset = this->CallLoopBody(entryPointInfo->jsMethod);
- }
- if (envReg != Constants::NoRegister)
- {
- SetNonVarReg(envReg, nullptr);
- }
- if (localClosureReg != Constants::NoRegister)
- {
- SetNonVarReg(localClosureReg, nullptr);
- }
- if (localFrameDisplayReg != Constants::NoRegister)
- {
- SetNonVarReg(localFrameDisplayReg, nullptr);
- }
- if (paramClosureReg != Constants::NoRegister)
- {
- SetNonVarReg(paramClosureReg, nullptr);
- }
- for (uint32 i = 0; i < innerScopeCount; i++)
- {
- // Get the (possibly updated) scopes from their registers and put them back in side storage.
- // (Getting the updated values may not be necessary, actually, but it can't hurt.)
- // Then null out the registers.
- RegSlot reg = this->m_functionBody->GetFirstInnerScopeRegister() + i;
- SetInnerScopeFromIndex(i, GetNonVarReg(reg));
- SetNonVarReg(reg, nullptr);
- }
- Assert(Js::OpCodeUtil::GetOpCodeLayout(OpCode::ProfiledLoopBodyStart) == Js::OpLayoutType::Unsigned1);
- Assert(Js::OpCodeUtil::GetOpCodeLayout(OpCode::LoopBodyStart) == Js::OpLayoutType::Unsigned1);
- Assert(Js::OpCodeUtil::EncodedSize(Js::OpCode::LoopBodyStart, layoutSize) == Js::OpCodeUtil::EncodedSize(Js::OpCode::ProfiledLoopBodyStart, layoutSize));
- uint byteCodeSize = Js::OpCodeUtil::EncodedSize(Js::OpCode::LoopBodyStart, layoutSize);
- if (layoutSize == SmallLayout)
- {
- byteCodeSize += sizeof(OpLayoutUnsigned1_Small);
- }
- else if (layoutSize == MediumLayout)
- {
- byteCodeSize += sizeof(OpLayoutUnsigned1_Medium);
- }
- else
- {
- byteCodeSize += sizeof(OpLayoutUnsigned1_Large);
- }
- if (newOffset == loopHeader->startOffset || newOffset == m_reader.GetCurrentOffset() - byteCodeSize)
- {
- // If we bail out back the start of the loop, or start of this LoopBodyStart just skip and interpret the loop
- // instead of trying to start the loop body again
- // Increment the interpret count of the loop
- loopHeader->interpretCount++;
- }
- else
- {
- this->CheckIfLoopIsHot(loopHeader->profiledLoopCounter);
- if (newOffset >= loopHeader->endOffset)
- {
- // Reset the totalJittedLoopIterations for the next invocation of this loop entry point
- entryPointInfo->totalJittedLoopIterations =
- static_cast<uint8>(
- min(
- static_cast<uint>(static_cast<uint8>(CONFIG_FLAG(MinBailOutsBeforeRejit))) *
- (Js::LoopEntryPointInfo::GetDecrLoopCountPerBailout() - 1),
- entryPointInfo->totalJittedLoopIterations));
- entryPointInfo->jittedLoopIterationsSinceLastBailout = 0;
- }
- m_reader.SetCurrentOffset(newOffset);
- }
- return loopHeader;
- }
- #endif
- // Increment the interpret count of the loop
- loopHeader->interpretCount += !isFirstIteration;
- const uint loopInterpretCount = GetFunctionBody()->GetLoopInterpretCount(loopHeader);
- if (loopHeader->interpretCount > loopInterpretCount)
- {
- if (this->scriptContext->GetConfig()->IsNoNative())
- {
- return nullptr;
- }
- if (!fn->DoJITLoopBody())
- {
- return nullptr;
- }
- #if ENABLE_NATIVE_CODEGEN
- #if ENABLE_OOP_NATIVE_CODEGEN
- // If for some reason OOP JIT isn't connected (e.g. it crashed), don't attempt to JIT a loop body
- if (JITManager::GetJITManager()->IsOOPJITEnabled() && !JITManager::GetJITManager()->IsConnected())
- {
- return nullptr;
- }
- #endif
- // If the job is not scheduled then we need to schedule it now.
- // It is possible a job was scheduled earlier and we find ourselves looking at the same entry point
- // again. For example, if the function with the loop was JITed and bailed out then as we finish
- // the call in the interpreter we might encounter a loop for which we had scheduled a JIT job before
- // the function was initially scheduled. In such cases, that old JIT job will complete. If it completes
- // successfully then we can go ahead and use it. If it fails then it will eventually revert to the
- // NotScheduled state. Since transitions from NotScheduled can only occur on the main thread,
- // by checking the state we are safe from racing with the JIT thread when looking at the other fields
- // of the entry point.
- if (entryPointInfo != NULL && entryPointInfo->IsNotScheduled())
- {
- GenerateLoopBody(scriptContext->GetNativeCodeGenerator(), fn, loopHeader, entryPointInfo, fn->GetLocalsCount(), this->m_localSlots);
- }
- #endif
- }
- #if ENABLE_PROFILE_INFO
- else if (
- doProfileLoopCheck &&
- isAutoProfiling &&
- loopHeader->interpretCount > fn->GetLoopProfileThreshold(loopInterpretCount))
- {
- // Start profiling the loop so that the jitted loop body will have some profile data to use
- Assert(!switchProfileMode);
- switchProfileMode = true;
- Assert(switchProfileModeOnLoopEndNumber == 0u - 1);
- switchProfileModeOnLoopEndNumber = loopNumber;
- }
- #endif
- return nullptr;
- }
- void
- InterpreterStackFrame::CheckIfLoopIsHot(uint profiledLoopCounter)
- {
- Js::FunctionBody *fn = this->function->GetFunctionBody();
- if (!fn->GetHasHotLoop() && profiledLoopCounter > (uint)CONFIG_FLAG(JitLoopBodyHotLoopThreshold))
- {
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (PHASE_TRACE(Js::JITLoopBodyPhase, fn))
- {
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(
- _u("Speculate Jit set for this function with loopbody: function: %s (%s)\n"),
- fn->GetDisplayName(),
- fn->GetDebugNumberSet(debugStringBuffer));
- Output::Flush();
- }
- #endif
- fn->SetHasHotLoop();
- }
- }
- bool InterpreterStackFrame::CheckAndResetImplicitCall(DisableImplicitFlags prevDisableImplicitFlags, ImplicitCallFlags savedImplicitCallFlags)
- {
- ImplicitCallFlags curImplicitCallFlags = this->scriptContext->GetThreadContext()->GetImplicitCallFlags();
- if (curImplicitCallFlags > ImplicitCall_None)
- {
- //error implicit bit is set , reparse without asmjs
- this->scriptContext->GetThreadContext()->SetDisableImplicitFlags(prevDisableImplicitFlags);
- this->scriptContext->GetThreadContext()->SetImplicitCallFlags(savedImplicitCallFlags);
- return true;
- }
- return false;
- }
- uint
- InterpreterStackFrame::CallLoopBody(JavascriptMethod address)
- {
- #ifdef _M_IX86
- void *savedEsp = NULL;
- __asm
- {
- // Save ESP
- mov savedEsp, esp
- // 8-byte align frame to improve floating point perf of our JIT'd code.
- and esp, -8
- // Add an extra 4-bytes to the stack since we'll be pushing 3 arguments
- push eax
- }
- #endif
- #if defined(_M_ARM32_OR_ARM64)
- // For ARM we need to make sure that pipeline is synchronized with memory/cache for newly jitted code.
- // Note: this does not seem to affect perf, but if it was, we could add a boolean isCalled to EntryPointInfo
- // and do ISB only for 1st time this entry point is called (potential working set regression though).
- _InstructionSynchronizationBarrier();
- #endif
- uint newOffset = ::Math::PointerCastToIntegral<uint>(
- CALL_ENTRYPOINT_NOASSERT(address, function, CallInfo(CallFlags_InternalFrame, 1), this));
- #ifdef _M_IX86
- _asm
- {
- // Restore ESP
- mov esp, savedEsp
- }
- #endif
- return newOffset;
- }
- uint
- InterpreterStackFrame::CallAsmJsLoopBody(JavascriptMethod address)
- {
- #ifdef _M_IX86
- void *savedEsp = NULL;
- __asm
- {
- // Save ESP
- mov savedEsp, esp
- // Add an extra 4-bytes to the stack since we'll be pushing 3 arguments
- push eax
- }
- #endif
- #if defined(_M_ARM32_OR_ARM64)
- // For ARM we need to make sure that pipeline is synchronized with memory/cache for newly jitted code.
- // Note: this does not seem to affect perf, but if it was, we could add a boolean isCalled to EntryPointInfo
- // and do ISB only for 1st time this entry point is called (potential working set regression though).
- _InstructionSynchronizationBarrier();
- #endif
- uint newOffset = ::Math::PointerCastToIntegral<uint>(
- CALL_ENTRYPOINT_NOASSERT(address, function, CallInfo(CallFlags_InternalFrame, 1), this));
- #ifdef _M_IX86
- _asm
- {
- // Restore ESP
- mov esp, savedEsp
- }
- #endif
- return newOffset;
- }
- template <class T>
- void InterpreterStackFrame::OP_NewScObjectNoCtorFull(const unaligned T* playout)
- {
- Var function = GetReg(playout->R1);
- Var newObj = JavascriptOperators::NewScObjectNoCtorFull(function, GetScriptContext());
- SetReg(playout->R0, newObj);
- }
- ///----------------------------------------------------------------------------
- ///
- /// InterpreterStackFrame::OP_NewScObject
- ///
- /// OP_NewScObject() allocates a new DynamicObject and initializes it with an
- /// optional "constructor" function.
- ///
- /// NOTE: The return register must be carefully chosen to ensure proper
- /// behavior:
- /// 1. OpCode::NewInstance should never specify "R0" as the register to
- /// store the new instance, because it will get whacked from the
- /// "constructor" function's return value:
- ///
- /// var a1 = Date(); <-- a1 = string returned from Date() function
- /// var a2 = new Date(); <-- a2 = instance return from NewInstance.
- /// Date()'s return value is thrown away.
- ///
- /// 2. If an exception is thrown during construction, the destination
- /// variable / field should __not__ be modified. Therefore, the destination
- /// register should always be a temporary and never a valid local variable.
- /// After successfully returning from the constructor function, the new
- /// instance is valid and may be stored in its final destination variable /
- /// field.
- ///
- /// OPCODE NewObject:
- /// T1 = new DynamicObject(Function.Prototype)
- /// OutArg[0] = T1
- /// Call(Function, ArgCount)
- /// Local[Return] = T1
- ///
- /// - R0: Destination "local" register
- /// - R1: Optional constructor JavascriptFunction instance or 'null'
- ///
- ///----------------------------------------------------------------------------
- template <class T, bool Profiled, bool ICIndex>
- void InterpreterStackFrame::OP_NewScObject_Impl(const unaligned T* playout, InlineCacheIndex inlineCacheIndex, const Js::AuxArray<uint32> *spreadIndices)
- {
- if (ICIndex)
- {
- Assert(inlineCacheIndex != Js::Constants::NoInlineCacheIndex);
- }
- Var newVarInstance =
- #if ENABLE_PROFILE_INFO
- Profiled ?
- ProfiledNewScObject_Helper(
- GetReg(playout->Function),
- playout->ArgCount,
- static_cast<const unaligned OpLayoutDynamicProfile<T> *>(playout)->profileId,
- inlineCacheIndex,
- spreadIndices) :
- #endif
- NewScObject_Helper(GetReg(playout->Function), playout->ArgCount, spreadIndices);
- SetReg((RegSlot)playout->Return, newVarInstance);
- }
- template <class T, bool Profiled>
- void InterpreterStackFrame::OP_ProfiledNewScObjArray_Impl(const unaligned T* playout, const Js::AuxArray<uint32> *spreadIndices)
- {
- // Always profile this operation when auto-profiling so that array type changes are tracked
- #if ENABLE_PROFILE_INFO
- if (!Profiled && !isAutoProfiling)
- #else
- Assert(!Profiled);
- #endif
- {
- OP_NewScObjArray_Impl<T, Profiled>(playout, spreadIndices);
- return;
- }
- #if ENABLE_PROFILE_INFO
- Arguments args(CallInfo(CallFlags_New, playout->ArgCount), m_outParams);
- uint32 spreadSize = 0;
- if (spreadIndices != nullptr)
- {
- spreadSize = JavascriptFunction::GetSpreadSize(args, spreadIndices, scriptContext);
- // Allocate room on the stack for the spread args.
- Arguments outArgs(CallInfo(CallFlags_New, 0), nullptr);
- outArgs.Info.Count = spreadSize;
- const unsigned STACK_ARGS_ALLOCA_THRESHOLD = 8; // Number of stack args we allow before using _alloca
- Var stackArgs[STACK_ARGS_ALLOCA_THRESHOLD];
- size_t outArgsSize = 0;
- if (outArgs.Info.Count > STACK_ARGS_ALLOCA_THRESHOLD)
- {
- PROBE_STACK(scriptContext, outArgs.Info.Count * sizeof(Var) + Js::Constants::MinStackDefault); // args + function call
- outArgsSize = outArgs.Info.Count * sizeof(Var);
- outArgs.Values = (Var*)_alloca(outArgsSize);
- ZeroMemory(outArgs.Values, outArgsSize);
- }
- else
- {
- outArgs.Values = stackArgs;
- outArgsSize = STACK_ARGS_ALLOCA_THRESHOLD * sizeof(Var);
- ZeroMemory(outArgs.Values, outArgsSize); // We may not use all of the elements
- }
- JavascriptFunction::SpreadArgs(args, outArgs, spreadIndices, scriptContext);
- SetReg(
- (RegSlot)playout->Return,
- ProfilingHelpers::ProfiledNewScObjArray(
- GetReg(playout->Function),
- outArgs,
- function,
- static_cast<const unaligned OpLayoutDynamicProfile2<T> *>(playout)->profileId,
- static_cast<const unaligned OpLayoutDynamicProfile2<T> *>(playout)->profileId2));
- }
- else
- {
- SetReg(
- (RegSlot)playout->Return,
- ProfilingHelpers::ProfiledNewScObjArray(
- GetReg(playout->Function),
- args,
- function,
- static_cast<const unaligned OpLayoutDynamicProfile2<T> *>(playout)->profileId,
- static_cast<const unaligned OpLayoutDynamicProfile2<T> *>(playout)->profileId2));
- }
- PopOut(playout->ArgCount);
- #endif
- }
- void InterpreterStackFrame::OP_NewScObject_A_Impl(const unaligned OpLayoutAuxiliary * playout, RegSlot *target)
- {
- const Js::VarArrayVarCount * vars = Js::ByteCodeReader::ReadVarArrayVarCount(playout->Offset, this->GetFunctionBody());
- int count = Js::TaggedInt::ToInt32(vars->count);
- // Push the parameters to stack
- for (int i = 0; i < count; i++)
- {
- SetOut((ArgSlot)(i + 1), vars->elements[i]);
- }
- Var newVarInstance = NewScObject_Helper(GetReg((RegSlot)playout->C1), (ArgSlot)count + 1);
- SetReg((RegSlot)playout->R0, newVarInstance);
- }
- Var InterpreterStackFrame::NewScObject_Helper(Var target, ArgSlot ArgCount, const Js::AuxArray<uint32> *spreadIndices)
- {
- Arguments args(CallInfo(CallFlags_New, ArgCount), m_outParams);
- Var newVarInstance = nullptr;
- BEGIN_SAFE_REENTRANT_CALL(this->scriptContext->GetThreadContext())
- {
- newVarInstance = JavascriptOperators::NewScObject(target, args, GetScriptContext(), spreadIndices);
- }
- END_SAFE_REENTRANT_CALL
- PopOut(ArgCount);
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(newVarInstance));
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag))
- {
- newVarInstance = JavascriptProxy::AutoProxyWrapper(newVarInstance);
- // this might come from a different scriptcontext.
- newVarInstance = CrossSite::MarshalVar(GetScriptContext(), newVarInstance);
- }
- #endif
- #ifdef ENABLE_BASIC_TELEMETRY
- {
- this->scriptContext->GetTelemetry().GetOpcodeTelemetry().NewScriptObject(target, args, newVarInstance);
- }
- #endif
- return newVarInstance;
- }
- #if ENABLE_PROFILE_INFO
- Var InterpreterStackFrame::ProfiledNewScObject_Helper(Var target, ArgSlot ArgCount, ProfileId profileId, InlineCacheIndex inlineCacheIndex, const Js::AuxArray<uint32> *spreadIndices)
- {
- Arguments args(CallInfo(CallFlags_New, ArgCount), m_outParams);
- Var newVarInstance = nullptr;
- BEGIN_SAFE_REENTRANT_CALL(this->scriptContext->GetThreadContext())
- {
- newVarInstance = ProfilingHelpers::ProfiledNewScObject(
- target,
- args,
- GetFunctionBody(),
- profileId,
- inlineCacheIndex,
- spreadIndices);
- }
- END_SAFE_REENTRANT_CALL
- PopOut(ArgCount);
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(newVarInstance));
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag))
- {
- newVarInstance = JavascriptProxy::AutoProxyWrapper(newVarInstance);
- // this might come from a different scriptcontext.
- newVarInstance = CrossSite::MarshalVar(GetScriptContext(), newVarInstance);
- }
- #endif
- #ifdef TELEMETRY_PROFILED
- {
- this->scriptContext->GetTelemetry().GetOpcodeTelemetry().NewScriptObject(target, args, newVarInstance);
- }
- #endif
- return newVarInstance;
- }
- #endif
- template <typename T>
- void InterpreterStackFrame::OP_LdElementUndefined(const unaligned OpLayoutT_ElementU<T>* playout)
- {
- if (this->m_functionBody->IsEval())
- {
- JavascriptOperators::OP_LoadUndefinedToElementDynamic(GetReg(playout->Instance),
- this->m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex), GetScriptContext());
- }
- else
- {
- JavascriptOperators::OP_LoadUndefinedToElement(GetReg(playout->Instance),
- this->m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex));
- }
- }
- template <typename T>
- void InterpreterStackFrame::OP_LdLocalElementUndefined(const unaligned OpLayoutT_ElementRootU<T>* playout)
- {
- if (this->m_functionBody->IsEval())
- {
- JavascriptOperators::OP_LoadUndefinedToElementDynamic(this->localClosure,
- this->m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex), GetScriptContext());
- }
- else
- {
- JavascriptOperators::OP_LoadUndefinedToElement(this->localClosure,
- this->m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex));
- }
- }
- template <typename T>
- void InterpreterStackFrame::OP_LdElementUndefinedScoped(const unaligned OpLayoutT_ElementScopedU<T>* playout)
- {
- // Implicit root object as default instance
- JavascriptOperators::OP_LoadUndefinedToElementScoped(GetEnvForEvalCode(),
- this->m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex), GetReg(Js::FunctionBody::RootObjectRegSlot), GetScriptContext());
- }
- void InterpreterStackFrame::OP_ChkUndecl(Var aValue)
- {
- if (this->scriptContext->IsUndeclBlockVar(aValue))
- {
- JavascriptError::ThrowReferenceError(scriptContext, JSERR_UseBeforeDeclaration);
- }
- }
- void InterpreterStackFrame::OP_ChkNewCallFlag()
- {
- if (!(this->m_callFlags & CallFlags_New) && !this->TestFlags(InterpreterStackFrameFlags_FromBailOutInInlinee))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_ClassConstructorCannotBeCalledWithoutNew);
- }
- }
- void InterpreterStackFrame::OP_EnsureNoRootProperty(uint propertyIdIndex)
- {
- Var instance = this->GetRootObject();
- JavascriptOperators::OP_EnsureNoRootProperty(instance, this->m_functionBody->GetReferencedPropertyId(propertyIdIndex));
- }
- void InterpreterStackFrame::OP_EnsureNoRootRedeclProperty(uint propertyIdIndex)
- {
- Var instance = this->GetRootObject();
- JavascriptOperators::OP_EnsureNoRootRedeclProperty(instance, this->m_functionBody->GetReferencedPropertyId(propertyIdIndex));
- }
- void InterpreterStackFrame::OP_ScopedEnsureNoRedeclProperty(Var aValue, uint propertyIdIndex, Var aValue2)
- {
- Js::PropertyId propertyId = this->m_functionBody->GetReferencedPropertyId(propertyIdIndex);
- JavascriptOperators::OP_ScopedEnsureNoRedeclProperty((FrameDisplay*)aValue, propertyId, aValue2);
- }
- Var InterpreterStackFrame::OP_InitUndecl()
- {
- return this->scriptContext->GetLibrary()->GetUndeclBlockVar();
- }
- void InterpreterStackFrame::OP_InitUndeclSlot(Var aValue, int32 slot)
- {
- this->OP_StSlot(aValue, slot, this->scriptContext->GetLibrary()->GetUndeclBlockVar());
- }
- void InterpreterStackFrame::OP_TryCatch(const unaligned OpLayoutBr* playout)
- {
- Js::JavascriptExceptionObject* exception = NULL;
- try
- {
- this->nestedTryDepth++;
- // mark the stackFrame as 'in try block'
- this->OrFlags(InterpreterStackFrameFlags_WithinTryBlock);
- Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext);
- void * addrOfReturnAddr = _AddressOfReturnAddress();
- Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr);
- #ifdef ENABLE_SCRIPT_DEBUGGING
- if (this->IsInDebugMode())
- {
- #if ENABLE_TTD
- if (SHOULD_DO_TTD_STACK_STMT_OP(this->scriptContext))
- {
- this->ProcessWithDebugging_PreviousStmtTracking();
- }
- else
- {
- this->ProcessWithDebugging();
- }
- #else
- this->ProcessWithDebugging();
- #endif
- this->TrySetRetOffset();
- }
- else
- #endif
- {
- this->Process();
- this->TrySetRetOffset();
- }
- }
- catch (const Js::JavascriptException& err)
- {
- // We are using C++ exception handling which does not unwind the stack in the catch block.
- // For stack overflow and OOM exceptions, we cannot run user code here because the stack is not unwind.
- exception = err.GetAndClear();
- }
- if (--this->nestedTryDepth == -1)
- {
- // unmark the stackFrame as 'in try block'
- this->ClearFlags(InterpreterStackFrameFlags_WithinTryBlock);
- }
- // Now that the stack is unwound, let's run the catch block.
- if (exception)
- {
- if (exception->IsGeneratorReturnException())
- {
- // Generator return scenario, so no need to go into the catch block and we must rethrow to propagate the exception to down level
- JavascriptExceptionOperators::DoThrow(exception, scriptContext);
- }
- exception = exception->CloneIfStaticExceptionObject(scriptContext);
- // We've got a JS exception. Grab the exception object and assign it to the
- // catch object's location, then call the handler (i.e., we consume the Catch op here).
- Var catchObject = exception->GetThrownObject(scriptContext);
- m_reader.SetCurrentRelativeOffset((const byte *)(playout + 1), playout->RelativeJumpOffset);
- LayoutSize layoutSize;
- OpCode catchOp = m_reader.ReadOp(layoutSize);
- #ifdef BYTECODE_BRANCH_ISLAND
- if (catchOp == Js::OpCode::BrLong)
- {
- Assert(layoutSize == SmallLayout);
- auto playoutBrLong = m_reader.BrLong();
- m_reader.SetCurrentRelativeOffset((const byte *)(playoutBrLong + 1), playoutBrLong->RelativeJumpOffset);
- catchOp = m_reader.ReadOp(layoutSize);
- }
- #endif
- AssertMsg(catchOp == OpCode::Catch, "Catch op not found at catch offset");
- RegSlot reg = layoutSize == SmallLayout ? m_reader.Reg1_Small()->R0 :
- layoutSize == MediumLayout ? m_reader.Reg1_Medium()->R0 : m_reader.Reg1_Large()->R0;
- SetReg(reg, catchObject);
- ResetOut();
- this->nestedCatchDepth++;
- // mark the stackFrame as 'in catch block'
- this->OrFlags(InterpreterStackFrameFlags_WithinCatchBlock);
- this->ProcessCatch();
- if (--this->nestedCatchDepth == -1)
- {
- // unmark the stackFrame as 'in catch block'
- this->ClearFlags(InterpreterStackFrameFlags_WithinCatchBlock);
- }
- }
- }
- void InterpreterStackFrame::ProcessCatch()
- {
- #if ENABLE_TTD
- //Clear any previous Exception Info
- if (SHOULD_DO_TTD_STACK_STMT_OP(this->scriptContext))
- {
- this->scriptContext->GetThreadContext()->TTDExecutionInfo->ProcessCatchInfoForLastExecutedStatements();
- }
- #endif
- #ifdef ENABLE_SCRIPT_DEBUGGING
- if (this->IsInDebugMode())
- {
- this->DebugProcess();
- }
- else
- #endif
- {
- this->Process();
- }
- }
- int InterpreterStackFrame::ProcessFinally()
- {
- this->nestedFinallyDepth++;
- int newOffset = 0;
- #ifdef ENABLE_SCRIPT_DEBUGGING
- if (this->IsInDebugMode())
- {
- newOffset = ::Math::PointerCastToIntegral<int>(this->DebugProcess());
- }
- else
- #endif
- {
- newOffset = ::Math::PointerCastToIntegral<int>(this->Process());
- }
- return newOffset;
- }
- void InterpreterStackFrame::ProcessTryHandlerBailout(EHBailoutData * ehBailoutData, uint32 tryNestingDepth)
- {
- int catchOffset = ehBailoutData->catchOffset;
- int finallyOffset = ehBailoutData->finallyOffset;
- Js::JavascriptExceptionObject* exception = NULL;
- if (catchOffset != 0 || finallyOffset != 0)
- {
- try
- {
- this->nestedTryDepth++;
- // mark the stackFrame as 'in try block'
- this->OrFlags(InterpreterStackFrameFlags_WithinTryBlock);
- if (finallyOffset != 0)
- {
- this->OrFlags(InterpreterStackFrameFlags_WithinTryFinallyBlock);
- }
- if (tryNestingDepth != 0)
- {
- this->ProcessTryHandlerBailout(ehBailoutData->child, --tryNestingDepth);
- }
- if (catchOffset != 0)
- {
- Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext);
- }
- #ifdef ENABLE_SCRIPT_DEBUGGING
- if (this->IsInDebugMode())
- {
- #if ENABLE_TTD
- if (SHOULD_DO_TTD_STACK_STMT_OP(this->scriptContext))
- {
- this->ProcessWithDebugging_PreviousStmtTracking();
- }
- else
- {
- this->ProcessWithDebugging();
- }
- #else
- this->ProcessWithDebugging();
- #endif
- this->TrySetRetOffset();
- }
- else
- #endif
- {
- this->Process();
- this->TrySetRetOffset();
- }
- }
- catch (const Js::JavascriptException& err)
- {
- // We are using C++ exception handling which does not unwind the stack in the catch block.
- // For stack overflow and OOM exceptions, we cannot run user code here because the stack is not unwind.
- exception = err.GetAndClear();
- }
- }
- else if (ehBailoutData->ht == HandlerType::HT_Catch)
- {
- this->nestedCatchDepth++;
- // mark the stackFrame as 'in catch block'
- this->OrFlags(InterpreterStackFrameFlags_WithinCatchBlock);
- if (tryNestingDepth != 0)
- {
- this->ProcessTryHandlerBailout(ehBailoutData->child, --tryNestingDepth);
- }
- this->ProcessCatch();
- if (--this->nestedCatchDepth == -1)
- {
- // unmark the stackFrame as 'in catch block'
- this->ClearFlags(InterpreterStackFrameFlags_WithinCatchBlock);
- }
- return;
- }
- else
- {
- Assert(ehBailoutData->ht == HandlerType::HT_Finally);
- this->nestedFinallyDepth++;
- // mark the stackFrame as 'in finally block'
- this->OrFlags(InterpreterStackFrameFlags_WithinFinallyBlock);
- if (tryNestingDepth != 0)
- {
- this->ProcessTryHandlerBailout(ehBailoutData->child, --tryNestingDepth);
- }
- Js::JavascriptExceptionObject * exceptionObj = this->scriptContext->GetThreadContext()->GetPendingFinallyException();
- this->scriptContext->GetThreadContext()->SetPendingFinallyException(nullptr);
- int finallyEndOffset = this->ProcessFinally();
- if (--this->nestedFinallyDepth == -1)
- {
- // unmark the stackFrame as 'in finally block'
- this->ClearFlags(InterpreterStackFrameFlags_WithinFinallyBlock);
- }
- // Finally exited with LeaveNull, We don't throw for early returns
- if (finallyEndOffset == 0 && exceptionObj)
- {
- JavascriptExceptionOperators::DoThrow(const_cast<Js::JavascriptExceptionObject *>(exceptionObj), scriptContext);
- }
- if (finallyEndOffset != 0)
- {
- m_reader.SetCurrentOffset(finallyEndOffset);
- }
- return;
- }
- if (--this->nestedTryDepth == -1)
- {
- // unmark the stackFrame as 'in try block'
- this->ClearFlags(InterpreterStackFrameFlags_WithinTryBlock | InterpreterStackFrameFlags_WithinTryFinallyBlock);
- }
- // Now that the stack is unwound, let's run the catch block.
- if (exception)
- {
- if (exception->IsGeneratorReturnException())
- {
- // Generator return scenario, so no need to go into the catch block and we must rethrow to propagate the exception to down level
- JavascriptExceptionOperators::DoThrow(exception, scriptContext);
- }
- if (catchOffset != 0)
- {
- exception = exception->CloneIfStaticExceptionObject(scriptContext);
- // We've got a JS exception. Grab the exception object and assign it to the
- // catch object's location, then call the handler (i.e., we consume the Catch op here).
- Var catchObject = exception->GetThrownObject(scriptContext);
- m_reader.SetCurrentOffset(catchOffset);
- LayoutSize layoutSize;
- OpCode catchOp = m_reader.ReadOp(layoutSize);
- #ifdef BYTECODE_BRANCH_ISLAND
- if (catchOp == Js::OpCode::BrLong)
- {
- Assert(layoutSize == SmallLayout);
- auto playoutBrLong = m_reader.BrLong();
- m_reader.SetCurrentRelativeOffset((const byte *)(playoutBrLong + 1), playoutBrLong->RelativeJumpOffset);
- catchOp = m_reader.ReadOp(layoutSize);
- }
- #endif
- AssertMsg(catchOp == OpCode::Catch, "Catch op not found at catch offset");
- RegSlot reg = layoutSize == SmallLayout ? m_reader.Reg1_Small()->R0 :
- layoutSize == MediumLayout ? m_reader.Reg1_Medium()->R0 : m_reader.Reg1_Large()->R0;
- SetReg(reg, catchObject);
- ResetOut();
- this->nestedCatchDepth++;
- // mark the stackFrame as 'in catch block'
- this->OrFlags(InterpreterStackFrameFlags_WithinCatchBlock);
- this->ProcessCatch();
- if (--this->nestedCatchDepth == -1)
- {
- // unmark the stackFrame as 'in catch block'
- this->ClearFlags(InterpreterStackFrameFlags_WithinCatchBlock);
- }
- }
- else
- {
- Assert(finallyOffset != 0);
- exception = exception->CloneIfStaticExceptionObject(scriptContext);
- m_reader.SetCurrentOffset(finallyOffset);
- ResetOut();
- this->nestedFinallyDepth++;
- // mark the stackFrame as 'in finally block'
- this->OrFlags(InterpreterStackFrameFlags_WithinFinallyBlock);
- LayoutSize layoutSize;
- OpCode finallyOp = m_reader.ReadOp(layoutSize);
- #ifdef BYTECODE_BRANCH_ISLAND
- if (finallyOp == Js::OpCode::BrLong)
- {
- Assert(layoutSize == SmallLayout);
- auto playoutBrLong = m_reader.BrLong();
- m_reader.SetCurrentRelativeOffset((const byte *)(playoutBrLong + 1), playoutBrLong->RelativeJumpOffset);
- finallyOp = m_reader.ReadOp(layoutSize);
- }
- #endif
- Assert(finallyOp == Js::OpCode::Finally);
- int finallyEndOffset = this->ProcessFinally();
- if (--this->nestedFinallyDepth == -1)
- {
- // unmark the stackFrame as 'in finally block'
- this->ClearFlags(InterpreterStackFrameFlags_WithinFinallyBlock);
- }
- if (finallyEndOffset == 0)
- {
- JavascriptExceptionOperators::DoThrow(exception, scriptContext);
- }
- m_reader.SetCurrentOffset(finallyEndOffset);
- }
- }
- else
- {
- if (finallyOffset != 0)
- {
- int currOffset = m_reader.GetCurrentOffset();
- m_reader.SetCurrentOffset(finallyOffset);
- this->nestedFinallyDepth++;
- // mark the stackFrame as 'in finally block'
- this->OrFlags(InterpreterStackFrameFlags_WithinFinallyBlock);
- LayoutSize layoutSize;
- OpCode finallyOp = m_reader.ReadOp(layoutSize);
- #ifdef BYTECODE_BRANCH_ISLAND
- if (finallyOp == Js::OpCode::BrLong)
- {
- Assert(layoutSize == SmallLayout);
- auto playoutBrLong = m_reader.BrLong();
- m_reader.SetCurrentRelativeOffset((const byte *)(playoutBrLong + 1), playoutBrLong->RelativeJumpOffset);
- finallyOp = m_reader.ReadOp(layoutSize);
- }
- #endif
- Assert(finallyOp == Js::OpCode::Finally);
- int finallyEndOffset = this->ProcessFinally();
- if (--this->nestedFinallyDepth == -1)
- {
- // unmark the stackFrame as 'in finally block'
- this->ClearFlags(InterpreterStackFrameFlags_WithinFinallyBlock);
- }
- if (finallyEndOffset == 0)
- {
- m_reader.SetCurrentOffset(currOffset);
- }
- }
- }
- }
- void InterpreterStackFrame::TrySetRetOffset()
- {
- Assert(this->TestFlags(Js::InterpreterStackFrameFlags_WithinTryBlock));
- // It may happen that a JITted loop body returned the offset of RET. If the loop body was
- // called from a try, the interpreter "Process()" should also just return.
- if (this->retOffset != 0)
- {
- m_reader.SetCurrentOffset(this->retOffset);
- }
- }
- bool InterpreterStackFrame::IsInCatchOrFinallyBlock()
- {
- return this->TestFlags(Js::InterpreterStackFrameFlags_WithinCatchBlock) ||
- this->TestFlags(Js::InterpreterStackFrameFlags_WithinFinallyBlock);
- }
- void InterpreterStackFrame::OP_BeginBodyScope()
- {
- // Currently we are using the closures created for the param scope.
- // This marks the beginning of the body scope, so let's create new closures for the body scope.
- FunctionBody *executeFunction = this->function->GetFunctionBody();
- Assert(!this->IsParamScopeDone() && !executeFunction->IsParamAndBodyScopeMerged());
- // Save the current closure. We have to use this while copying the initial value of body symbols
- // from the corresponding symbols in the param.
- this->SetParamClosure(this->GetLocalClosure());
- this->SetNonVarReg(executeFunction->GetParamClosureRegister(), nullptr);
- this->SetIsParamScopeDone(true);
- // 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 (executeFunction->scopeSlotArraySize > 0 || executeFunction->HasScopeObject())
- {
- this->InitializeClosures();
- }
- }
- void InterpreterStackFrame::OP_ResumeCatch()
- {
- this->OrFlags(InterpreterStackFrameFlags_WithinCatchBlock);
- #ifdef ENABLE_SCRIPT_DEBUGGING
- if (this->IsInDebugMode())
- {
- this->DebugProcess();
- }
- else
- #endif
- {
- this->Process();
- }
- this->ClearFlags(InterpreterStackFrameFlags_WithinCatchBlock);
- }
- /// ---------------------------------------------------------------------------------------------------
- /// The behavior we want is the following:
- /// - If the control leaves the user's try without throwing, execute the finally and continue
- /// after the end of the try.
- /// - If the user code throws, catch this exception and then execute this finally while unwinding to
- /// the handler (if any).
- /// ---------------------------------------------------------------------------------------------------
- void InterpreterStackFrame::ProcessTryFinally(const byte* ip, Js::JumpOffset jumpOffset, Js::RegSlot regException, Js::RegSlot regOffset, bool hasYield)
- {
- Js::JavascriptExceptionObject* pExceptionObject = nullptr;
- bool skipFinallyBlock = false;
- try
- {
- Js::Var result = nullptr;
- this->nestedTryDepth++;
- // mark the stackFrame as 'in try block'
- this->OrFlags(InterpreterStackFrameFlags_WithinTryBlock | InterpreterStackFrameFlags_WithinTryFinallyBlock);
- if (shouldCacheSP)
- {
- CacheSp();
- }
- #ifdef ENABLE_SCRIPT_DEBUGGING
- if (this->IsInDebugMode())
- {
- #if ENABLE_TTD
- if (SHOULD_DO_TTD_STACK_STMT_OP(this->scriptContext))
- {
- result = this->ProcessWithDebugging_PreviousStmtTracking();
- }
- else
- {
- result = this->ProcessWithDebugging();
- }
- #else
- result = this->ProcessWithDebugging();
- #endif
- }
- else
- #endif
- {
- result = this->Process();
- }
- if (result == nullptr)
- {
- Assert(hasYield);
- skipFinallyBlock = true;
- }
- }
- catch (const Js::JavascriptException& err)
- {
- pExceptionObject = err.GetAndClear();
- }
- if (--this->nestedTryDepth == -1)
- {
- // unmark the stackFrame as 'in try block'
- this->ClearFlags(InterpreterStackFrameFlags_WithinTryBlock | InterpreterStackFrameFlags_WithinTryFinallyBlock);
- }
- shouldCacheSP = !skipFinallyBlock;
- if (skipFinallyBlock)
- {
- // A leave occurred due to a yield
- return;
- }
- // Save the current IP so execution can continue there if the finally doesn't
- // take control of the flow.
- int newOffset = 0;
- int currOffset = m_reader.GetCurrentOffset();
- if (hasYield)
- {
- // save the exception if there is one to a register in case we yield during the finally block
- // and need to get that exception object back upon resume in OP_ResumeFinally
- SetNonVarReg(regException, pExceptionObject);
- SetNonVarReg(regOffset, reinterpret_cast<Js::Var>(currOffset));
- }
- if (pExceptionObject && !pExceptionObject->IsGeneratorReturnException())
- {
- // Clone static exception object early in case finally block overwrites it
- pExceptionObject = pExceptionObject->CloneIfStaticExceptionObject(scriptContext);
- }
- #ifdef ENABLE_SCRIPT_DEBUGGING
- if (pExceptionObject && this->IsInDebugMode() &&
- pExceptionObject != scriptContext->GetThreadContext()->GetPendingSOErrorObject())
- {
- // Swallowing an exception that has triggered a finally is not implemented
- // (This appears to be the same behavior as ie8)
- pExceptionObject->SetDebuggerSkip(false);
- }
- #endif
- // Call into the finally by setting the IP, consuming the Finally, and letting the interpreter recurse.
- m_reader.SetCurrentRelativeOffset(ip, jumpOffset);
- RestoreSp();
- // mark the stackFrame as 'in finally block'
- this->OrFlags(InterpreterStackFrameFlags_WithinFinallyBlock);
- LayoutSize layoutSize;
- OpCode finallyOp = m_reader.ReadOp(layoutSize);
- #ifdef BYTECODE_BRANCH_ISLAND
- if (finallyOp == Js::OpCode::BrLong)
- {
- Assert(layoutSize == SmallLayout);
- auto playoutBrLong = m_reader.BrLong();
- m_reader.SetCurrentRelativeOffset((const byte *)(playoutBrLong + 1), playoutBrLong->RelativeJumpOffset);
- finallyOp = m_reader.ReadOp(layoutSize);
- }
- #endif
- AssertMsg(finallyOp == OpCode::Finally, "Finally op not found at catch offset");
- newOffset = this->ProcessFinally();
- if (--this->nestedFinallyDepth == -1)
- {
- // unmark the stackFrame as 'in finally block'
- this->ClearFlags(InterpreterStackFrameFlags_WithinFinallyBlock);
- }
- bool endOfFinallyBlock = newOffset == 0;
- if (endOfFinallyBlock)
- {
- // Finally completed without taking over the flow. Resume where we left off before calling it.
- m_reader.SetCurrentOffset(currOffset);
- }
- else
- {
- // Finally seized the flow with a jump out of its scope. Resume at the jump target and
- // force the runtime to return to this frame without executing the catch.
- m_reader.SetCurrentOffset(newOffset);
- return;
- }
- if (pExceptionObject && (endOfFinallyBlock || !pExceptionObject->IsGeneratorReturnException()))
- {
- JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext);
- }
- }
- void InterpreterStackFrame::OP_TryFinally(const unaligned OpLayoutBr* playout)
- {
- ProcessTryFinally((const byte*)(playout + 1), playout->RelativeJumpOffset);
- }
- void InterpreterStackFrame::OP_TryFinallyWithYield(const byte* ip, Js::JumpOffset jumpOffset, Js::RegSlot regException, Js::RegSlot regOffset)
- {
- ProcessTryFinally(ip, jumpOffset, regException, regOffset, true);
- }
- void InterpreterStackFrame::OP_ResumeFinally(const byte* ip, Js::JumpOffset jumpOffset, RegSlot exceptionRegSlot, RegSlot offsetRegSlot)
- {
- this->OrFlags(InterpreterStackFrameFlags_WithinFinallyBlock);
- int newOffset = 0;
- #ifdef ENABLE_SCRIPT_DEBUGGING
- if (this->IsInDebugMode())
- {
- newOffset = ::Math::PointerCastToIntegral<int>(this->DebugProcess());
- }
- else
- #endif
- {
- newOffset = ::Math::PointerCastToIntegral<int>(this->Process());
- }
- this->ClearFlags(InterpreterStackFrameFlags_WithinFinallyBlock);
- bool endOfFinallyBlock = newOffset == 0;
- if (endOfFinallyBlock)
- {
- // Finally completed without taking over the flow. Resume where we left off before calling it.
- int currOffset = ::Math::PointerCastToIntegral<int>(GetNonVarReg(offsetRegSlot));
- m_reader.SetCurrentOffset(currOffset);
- }
- else
- {
- // Finally seized the flow with a jump out of its scope. Resume at the jump target and
- // force the runtime to return to this frame without executing the catch.
- m_reader.SetCurrentOffset(newOffset);
- return;
- }
- Js::JavascriptExceptionObject* exceptionObj = (Js::JavascriptExceptionObject*)GetNonVarReg(exceptionRegSlot);
- if (exceptionObj && (endOfFinallyBlock || !exceptionObj->IsGeneratorReturnException()))
- {
- JavascriptExceptionOperators::DoThrow(exceptionObj, scriptContext);
- }
- }
- template <typename T>
- void InterpreterStackFrame::OP_IsInst(const unaligned T* playout)
- {
- Var instance = GetReg(playout->R1);
- Var function = GetReg(playout->R2);
- IsInstInlineCache *inlineCache = this->GetIsInstInlineCache(playout->inlineCacheIndex);
- ScriptContext* scriptContext = GetScriptContext();
- Var result = JavascriptOperators::OP_IsInst(instance, function, scriptContext, inlineCache);
- #ifdef ENABLE_BASIC_TELEMETRY
- {
- this->scriptContext->GetTelemetry().GetOpcodeTelemetry().IsInstanceOf(instance, function, result);
- }
- #endif
- SetReg(playout->R0, result);
- }
- template <typename T>
- void InterpreterStackFrame::OP_ApplyArgs(const unaligned OpLayoutT_Reg5<T> * playout)
- {
- // Always save and restore implicit call flags when calling out
- // REVIEW: Can we avoid it if we don't collect dynamic profile info?
- ThreadContext * threadContext = scriptContext->GetThreadContext();
- Js::ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- // Currently ApplyArgs is equivalent to CallFldVoid (where we don't use the return value)
- Var v = GetNonVarReg(playout->R4);
- JavascriptOperators::OP_ApplyArgs(GetReg(playout->R1), GetReg(playout->R2),
- (void**)GetNonVarReg(playout->R3), *((CallInfo*)&v), GetScriptContext());
- threadContext->SetImplicitCallFlags(savedImplicitCallFlags);
- }
- void InterpreterStackFrame::OP_SpreadArrayLiteral(const unaligned OpLayoutReg2Aux * playout)
- {
- ThreadContext* threadContext = this->GetScriptContext()->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- Var instance = GetReg(playout->R1);
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
- #endif
- const Js::AuxArray<uint32> *spreadIndices = m_reader.ReadAuxArray<uint32>(playout->Offset, this->GetFunctionBody());
- ScriptContext* scriptContext = GetScriptContext();
- Var result = JavascriptArray::SpreadArrayArgs(instance, spreadIndices, scriptContext);
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- SetReg(playout->R0, result);
- }
- FrameDisplay *
- InterpreterStackFrame::OP_LdInnerFrameDisplay(void *argHead, void *argEnv, ScriptContext *scriptContext)
- {
- JavascriptOperators::CheckInnerFrameDisplayArgument(argHead);
- return OP_LdFrameDisplay(argHead, argEnv, scriptContext);
- }
- FrameDisplay *
- InterpreterStackFrame::OP_LdInnerFrameDisplayNoParent(void *argHead, ScriptContext *scriptContext)
- {
- JavascriptOperators::CheckInnerFrameDisplayArgument(argHead);
- return OP_LdFrameDisplayNoParent<true>(argHead, scriptContext);
- }
- FrameDisplay *
- InterpreterStackFrame::OP_LdFrameDisplay(void *argHead, void *argEnv, ScriptContext *scriptContext)
- {
- FrameDisplay *frameDisplay;
- bool strict = this->m_functionBody->GetIsStrictMode();
- if (strict)
- {
- frameDisplay = JavascriptOperators::OP_LdStrictFrameDisplay(argHead, argEnv, scriptContext);
- }
- else
- {
- frameDisplay = JavascriptOperators::OP_LdFrameDisplay(argHead, argEnv, scriptContext);
- }
- return frameDisplay;
- }
- FrameDisplay *
- InterpreterStackFrame::OP_LdFrameDisplaySetLocal(void *argHead, void *argEnv, ScriptContext *scriptContext)
- {
- FrameDisplay *frameDisplay = OP_LdFrameDisplay(argHead, argEnv, scriptContext);
- this->SetLocalFrameDisplay(frameDisplay);
- return frameDisplay;
- }
- FrameDisplay *
- InterpreterStackFrame::NewFrameDisplay(void *argHead, void *argEnv)
- {
- FrameDisplay *frameDisplay;
- bool strict = this->m_functionBody->GetIsStrictMode();
- if (!this->m_functionBody->DoStackFrameDisplay() || !this->GetLocalFrameDisplay())
- {
- // Null local frame display probably indicates that we bailed out of an inlinee.
- // Once we support stack closures in inlined functions, we can just assert that this value
- // is never null if we should be allocating on the stack.
- return this->OP_LdFrameDisplaySetLocal(argHead, argEnv, this->GetScriptContext());
- }
- frameDisplay = this->GetLocalFrameDisplay();
- Assert(frameDisplay != nullptr);
- frameDisplay->SetTag(true);
- frameDisplay->SetStrictMode(strict);
- frameDisplay->SetLength(this->m_functionBody->GetEnvDepth() + 1);
- Assert(frameDisplay->GetLength() == ((FrameDisplay*)argEnv)->GetLength() + 1);
- for (uint i = 0; i < ((FrameDisplay*)argEnv)->GetLength(); i++)
- {
- frameDisplay->SetItem(i + 1, ((FrameDisplay*)argEnv)->GetItem(i));
- }
- frameDisplay->SetItem(0, argHead);
- return frameDisplay;
- }
- template<bool innerFD>
- FrameDisplay *
- InterpreterStackFrame::OP_LdFrameDisplayNoParent(void *argHead, ScriptContext *scriptContext)
- {
- FrameDisplay *frameDisplay;
- bool strict = this->m_functionBody->GetIsStrictMode();
- Var argEnv = nullptr;
- if (innerFD && this->m_functionBody->GetLocalFrameDisplayRegister() != Constants::NoRegister)
- {
- argEnv = this->GetLocalFrameDisplay();
- }
- if (argEnv == nullptr && this->m_functionBody->GetEnvRegister() != Constants::NoRegister)
- {
- argEnv = this->LdEnv();
- }
- if (argEnv == nullptr)
- {
- if (strict)
- {
- frameDisplay = JavascriptOperators::OP_LdStrictFrameDisplayNoParent(argHead, scriptContext);
- }
- else
- {
- frameDisplay = JavascriptOperators::OP_LdFrameDisplayNoParent(argHead, scriptContext);
- }
- }
- else
- {
- if (strict)
- {
- frameDisplay = JavascriptOperators::OP_LdStrictFrameDisplay(argHead, argEnv, scriptContext);
- }
- else
- {
- frameDisplay = JavascriptOperators::OP_LdFrameDisplay(argHead, argEnv, scriptContext);
- }
- }
- return frameDisplay;
- }
- FrameDisplay *
- InterpreterStackFrame::OP_LdFuncExprFrameDisplaySetLocal(void *argHead1, void *argHead2, ScriptContext *scriptContext)
- {
- FrameDisplay *frameDisplay = OP_LdFrameDisplayNoParent<false>(argHead2, scriptContext);
- frameDisplay = OP_LdFrameDisplay(argHead1, frameDisplay, scriptContext);
- this->SetLocalFrameDisplay(frameDisplay);
- return frameDisplay;
- }
- FrameDisplay* InterpreterStackFrame::GetLocalFrameDisplay() const
- {
- Assert(this->localFrameDisplay == nullptr || this->IsClosureInitDone() || this->localFrameDisplay->GetLength() == 0);
- return this->localFrameDisplay;
- }
- void InterpreterStackFrame::SetLocalFrameDisplay(FrameDisplay* frameDisplay)
- {
- this->localFrameDisplay = frameDisplay;
- }
- Var InterpreterStackFrame::GetLocalClosure() const
- {
- return this->localClosure;
- }
- void InterpreterStackFrame::SetLocalClosure(Var closure)
- {
- this->localClosure = closure;
- }
- Var InterpreterStackFrame::GetParamClosure() const
- {
- return this->paramClosure;
- }
- void InterpreterStackFrame::SetParamClosure(Var closure)
- {
- this->paramClosure = closure;
- }
- void
- InterpreterStackFrame::OP_NewInnerScopeSlots(uint innerScopeIndex, uint count, int scopeIndex, ScriptContext *scriptContext, FunctionBody *functionBody)
- {
- Field(Var)* slotArray =
- JavascriptOperators::OP_NewScopeSlotsWithoutPropIds(count, scopeIndex, scriptContext, functionBody);
- this->SetInnerScopeFromIndex(innerScopeIndex, slotArray);
- }
- template <typename T>
- void InterpreterStackFrame::OP_CloneInnerScopeSlots(const unaligned OpLayoutT_Unsigned1<T> *playout)
- {
- uint innerScopeIndex = playout->C1;
- Field(Var) * slotArray;
- slotArray = (Field(Var)*)this->InnerScopeFromIndex(innerScopeIndex);
- slotArray = JavascriptOperators::OP_CloneScopeSlots(slotArray, scriptContext);
- this->SetInnerScopeFromIndex(innerScopeIndex, slotArray);
- }
- template <typename T>
- void InterpreterStackFrame::OP_CloneBlockScope(const unaligned OpLayoutT_Unsigned1<T> *playout)
- {
- uint innerScopeIndex = playout->C1;
- Var scope = this->InnerScopeFromIndex(innerScopeIndex);
- BlockActivationObject* blockScope = BlockActivationObject::FromVar(scope);
- scope = JavascriptOperators::OP_CloneBlockScope(blockScope, scriptContext);
- this->SetInnerScopeFromIndex(innerScopeIndex, scope);
- }
- Field(Var)*
- InterpreterStackFrame::NewScopeSlots(unsigned int size, ScriptContext *scriptContext, Var scope)
- {
- Field(Var)* slotArray = JavascriptOperators::OP_NewScopeSlots(size, scriptContext, scope);
- this->SetLocalClosure(slotArray);
- return slotArray;
- }
- Field(Var)*
- InterpreterStackFrame::NewScopeSlots()
- {
- Field(Var)* slotArray;
- FunctionBody * functionBody = this->m_functionBody;
- uint scopeSlotCount = this->IsParamScopeDone() ? functionBody->scopeSlotArraySize : functionBody->paramScopeSlotArraySize;
- Assert(scopeSlotCount != 0);
- if (!functionBody->DoStackScopeSlots())
- {
- return this->NewScopeSlots(
- scopeSlotCount + ScopeSlots::FirstSlotIndex, this->GetScriptContext(), (Var)functionBody->GetFunctionInfo());
- }
- slotArray = (Field(Var)*)this->GetLocalClosure();
- Assert(slotArray != nullptr);
- ScopeSlots scopeSlots(slotArray);
- scopeSlots.SetCount(scopeSlotCount);
- scopeSlots.SetScopeMetadata((Var)functionBody->GetFunctionInfo());
- Var undef = functionBody->GetScriptContext()->GetLibrary()->GetUndefined();
- for (unsigned int i = 0; i < scopeSlotCount; i++)
- {
- scopeSlots.Set(i, undef);
- }
- return slotArray;
- }
- Var
- InterpreterStackFrame::NewScopeObject()
- {
- Var scopeObject;
- if (m_functionBody->HasCachedScopePropIds())
- {
- const Js::PropertyIdArray *propIds = this->m_functionBody->GetFormalsPropIdArray();
- JavascriptFunction* funcExpr = this->GetFunctionExpression();
- PropertyId objectId = ActivationObjectEx::GetLiteralObjectRef(propIds);
- scopeObject = JavascriptOperators::OP_InitCachedScope(funcExpr, propIds,
- this->GetFunctionBody()->GetObjectLiteralTypeRef(objectId),
- propIds->hasNonSimpleParams, GetScriptContext());
- }
- else
- {
- scopeObject = JavascriptOperators::OP_NewScopeObject(GetScriptContext());
- }
- this->SetLocalClosure(scopeObject);
- return scopeObject;
- }
- FrameDisplay *
- InterpreterStackFrame::GetFrameDisplayForNestedFunc() const
- {
- if (this->localFrameDisplay == nullptr)
- {
- return (FrameDisplay*)LdEnv();
- }
- return this->localFrameDisplay;
- }
- template <class T>
- void InterpreterStackFrame::OP_NewStackScFunc(const unaligned T * playout)
- {
- uint funcIndex = playout->SlotIndex;
- FrameDisplay *frameDisplay = this->GetFrameDisplayForNestedFunc();
- SetRegAllowStackVarEnableOnly(playout->Value,
- StackScriptFunction::OP_NewStackScFunc(frameDisplay,
- this->m_functionBody->GetNestedFuncReference(funcIndex),
- this->GetStackNestedFunction(funcIndex)));
- }
- template <class T>
- void InterpreterStackFrame::OP_NewInnerStackScFunc(const unaligned T * playout)
- {
- uint funcIndex = playout->SlotIndex;
- FrameDisplay *frameDisplay = (FrameDisplay*)GetNonVarReg(playout->Instance);
- SetRegAllowStackVarEnableOnly(playout->Value,
- StackScriptFunction::OP_NewStackScFunc(frameDisplay,
- this->m_functionBody->GetNestedFuncReference(funcIndex),
- this->GetStackNestedFunction(funcIndex)));
- }
- template <class T>
- void InterpreterStackFrame::OP_DeleteFld(const unaligned T * playout)
- {
- Var result = JavascriptOperators::OP_DeleteProperty(GetReg(playout->Instance), m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex), GetScriptContext());
- SetReg(playout->Value, result);
- }
- template <class T>
- void InterpreterStackFrame::OP_DeleteLocalFld(const unaligned T * playout)
- {
- Var result = JavascriptOperators::OP_DeleteProperty(this->localClosure, m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex), GetScriptContext());
- SetReg(playout->Instance, result);
- }
- template <class T>
- void InterpreterStackFrame::OP_DeleteRootFld(const unaligned T * playout)
- {
- Var result = JavascriptOperators::OP_DeleteRootProperty(GetReg(playout->Instance), m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex), GetScriptContext());
- SetReg(playout->Value, result);
- }
- template <class T>
- void InterpreterStackFrame::OP_DeleteFldStrict(const unaligned T * playout)
- {
- Var result = JavascriptOperators::OP_DeleteProperty(GetReg(playout->Instance), m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex), GetScriptContext(), PropertyOperation_StrictMode);
- SetReg(playout->Value, result);
- }
- template <class T>
- void InterpreterStackFrame::OP_DeleteRootFldStrict(const unaligned T * playout)
- {
- Var result = JavascriptOperators::OP_DeleteRootProperty(GetReg(playout->Instance), m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex), GetScriptContext(), PropertyOperation_StrictMode);
- SetReg(playout->Value, result);
- }
- template <typename T>
- void InterpreterStackFrame::OP_ScopedDeleteFld(const unaligned OpLayoutT_ElementScopedC<T> * playout)
- {
- // Implicit root object as default instance
- Var result = JavascriptOperators::OP_DeletePropertyScoped(GetEnvForEvalCode(),
- m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex),
- GetReg(Js::FunctionBody::RootObjectRegSlot), GetScriptContext());
- SetReg(playout->Value, result);
- }
- template <typename T>
- void InterpreterStackFrame::OP_ScopedDeleteFldStrict(const unaligned OpLayoutT_ElementScopedC<T> * playout)
- {
- // Implicit root object as default instance
- Var result = JavascriptOperators::OP_DeletePropertyScoped(GetEnvForEvalCode(),
- m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex),
- GetReg(Js::FunctionBody::RootObjectRegSlot), GetScriptContext(), PropertyOperation_StrictMode);
- SetReg(playout->Value, result);
- }
- template <class T>
- void InterpreterStackFrame::OP_ScopedLdInst(const unaligned T * playout)
- {
- Var thisVar = nullptr;
- Var rootObject = GetFunctionBody()->GetRootObject();
- Var result = JavascriptOperators::OP_GetInstanceScoped(GetEnvForEvalCode(),
- m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex), rootObject, &thisVar, GetScriptContext());
- SetReg(playout->Value, result);
- SetReg(playout->Value2, thisVar);
- }
- template <typename T>
- void InterpreterStackFrame::OP_ScopedInitFunc(const unaligned OpLayoutT_ElementScopedC<T> * playout)
- {
- JavascriptOperators::OP_InitFuncScoped(GetEnvForEvalCode(),
- m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex),
- GetReg(playout->Value), GetReg(Js::FunctionBody::RootObjectRegSlot), GetScriptContext());
- }
- template <class T>
- void InterpreterStackFrame::OP_ClearAttributes(const unaligned T * playout)
- {
- JavascriptOperators::OP_ClearAttributes(GetReg(playout->Instance), m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex));
- }
- template <class T>
- void InterpreterStackFrame::OP_InitGetFld(const unaligned T * playout)
- {
- JavascriptOperators::OP_InitGetter(GetReg(playout->Instance), m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex), GetReg(playout->Value));
- }
- template <class T>
- void InterpreterStackFrame::OP_InitSetFld(const unaligned T * playout)
- {
- JavascriptOperators::OP_InitSetter(GetReg(playout->Instance), m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex), GetReg(playout->Value));
- }
- template <class T>
- void InterpreterStackFrame::OP_InitSetElemI(const unaligned T * playout)
- {
- JavascriptOperators::OP_InitElemSetter(
- GetReg(playout->Instance),
- GetReg(playout->Element),
- GetReg(playout->Value),
- m_functionBody->GetScriptContext()
- );
- }
- template <class T>
- void InterpreterStackFrame::OP_InitGetElemI(const unaligned T * playout)
- {
- JavascriptOperators::OP_InitElemGetter(
- GetReg(playout->Instance),
- GetReg(playout->Element),
- GetReg(playout->Value),
- m_functionBody->GetScriptContext()
- );
- }
- template <class T>
- void InterpreterStackFrame::OP_InitComputedProperty(const unaligned T * playout)
- {
- JavascriptOperators::OP_InitComputedProperty(
- GetReg(playout->Instance),
- GetReg(playout->Element),
- GetReg(playout->Value),
- m_functionBody->GetScriptContext()
- );
- }
- template <class T>
- void InterpreterStackFrame::OP_InitProto(const unaligned T * playout)
- {
- JavascriptOperators::OP_InitProto(GetReg(playout->Instance), m_functionBody->GetReferencedPropertyId(playout->PropertyIdIndex), GetReg(playout->Value));
- }
- void InterpreterStackFrame::DoInterruptProbe()
- {
- PROBE_STACK(scriptContext, 0);
- }
- void InterpreterStackFrame::InitializeStackFunctions(StackScriptFunction * scriptFunctions)
- {
- this->stackNestedFunctions = scriptFunctions;
- FunctionBody * functionBody = this->m_functionBody;
- uint nestedCount = functionBody->GetNestedCount();
- for (uint i = 0; i < nestedCount; i++)
- {
- StackScriptFunction * stackScriptFunction = scriptFunctions + i;
- FunctionProxy* nestedProxy = functionBody->GetNestedFunctionProxy(i);
- ScriptFunctionType* type = nestedProxy->EnsureDeferredPrototypeType();
- new (stackScriptFunction)StackScriptFunction(nestedProxy, type);
- }
- }
- StackScriptFunction * InterpreterStackFrame::GetStackNestedFunction(uint index)
- {
- Assert(index < this->m_functionBody->GetNestedCount());
- // Re-check if we have disable stack nested function
- if (this->m_functionBody->DoStackNestedFunc())
- {
- return this->stackNestedFunctions + index;
- }
- return nullptr;
- }
- void InterpreterStackFrame::SetExecutingStackFunction(ScriptFunction * scriptFunction)
- {
- Assert(ThreadContext::IsOnStack(this->function));
- Assert(this->m_functionBody == scriptFunction->GetFunctionBody());
- this->function = scriptFunction;
- }
- DWORD_PTR InterpreterStackFrame::GetStackAddress() const
- {
- return m_stackAddress;
- }
- void* InterpreterStackFrame::GetAddressOfReturnAddress() const
- {
- return this->addressOfReturnAddress;
- }
- template <class T>
- const byte * InterpreterStackFrame::OP_Br(const unaligned T * playout)
- {
- return m_reader.SetCurrentRelativeOffset((const byte *)(playout + 1), playout->RelativeJumpOffset);
- }
- template <class T>
- void InterpreterStackFrame::OP_InitClass(const unaligned OpLayoutT_Class<T> * playout)
- {
- JavascriptOperators::OP_InitClass(GetReg(playout->Constructor), playout->Extends != Js::Constants::NoRegister ? GetReg(playout->Extends) : NULL, GetScriptContext());
- }
- #ifdef ENABLE_SCRIPT_DEBUGGING
- template <class T>
- void InterpreterStackFrame::OP_EmitTmpRegCount(const unaligned OpLayoutT_Unsigned1<T> * playout)
- {
- this->scriptContext->GetDebugContext()->GetProbeContainer()->SetCurrentTmpRegCount(playout->C1);
- }
- #endif
- Var InterpreterStackFrame::OP_LdHomeObj(ScriptContext * scriptContext)
- {
- return JavascriptOperators::OP_LdHomeObj(function, scriptContext);
- }
- Var InterpreterStackFrame::OP_LdFuncObj(ScriptContext * scriptContext)
- {
- return JavascriptOperators::OP_LdFuncObj(function, scriptContext);
- }
- Var InterpreterStackFrame::OP_ImportCall(Var specifier, ScriptContext *scriptContext)
- {
- return JavascriptOperators::OP_ImportCall(function, specifier, scriptContext);
- }
- void InterpreterStackFrame::ValidateRegValue(Var value, bool allowStackVar, bool allowStackVarOnDisabledStackNestedFunc) const
- {
- #if DBG
- if (value != nullptr && !TaggedNumber::Is(value))
- {
- if (!allowStackVar || !this->m_functionBody->DoStackNestedFunc())
- {
- Assert(!ThreadContext::IsOnStack(value)
- || (allowStackVar && allowStackVarOnDisabledStackNestedFunc && StackScriptFunction::IsBoxed(value)));
- }
- Assert(!CrossSite::NeedMarshalVar(value, GetScriptContext()));
- }
- #endif
- }
- template <typename RegSlotType>
- Var InterpreterStackFrame::GetReg(RegSlotType localRegisterID) const
- {
- Var value = m_localSlots[localRegisterID];
- ValidateRegValue(value);
- return value;
- }
- template <typename RegSlotType>
- void InterpreterStackFrame::SetReg(RegSlotType localRegisterID, Var value)
- {
- Assert(localRegisterID == 0 || localRegisterID >= m_functionBody->GetConstantCount());
- ValidateRegValue(value);
- m_localSlots[localRegisterID] = value;
- #if ENABLE_OBJECT_SOURCE_TRACKING
- if (value != nullptr && DynamicType::Is(Js::JavascriptOperators::GetTypeId(value)))
- {
- static_cast<DynamicObject*>(value)->SetDiagOriginInfoAsNeeded();
- }
- #endif
- #if ENABLE_VALUE_TRACE
- if (this->function->GetScriptContext()->ShouldPerformRecordOrReplayAction())
- {
- this->function->GetScriptContext()->GetThreadContext()->TTDExecutionInfo->GetTraceLogger()->WriteTraceValue(value);
- }
- #endif
- }
- template <typename T>
- T InterpreterStackFrame::GetRegRaw(RegSlot localRegisterID) const
- {
- return (T)m_localIntSlots[localRegisterID];
- }
- // specialized version for doubles
- template <>
- double VECTORCALL InterpreterStackFrame::GetRegRaw(RegSlot localRegisterID) const
- {
- return (double)m_localDoubleSlots[localRegisterID];
- }
- template <>
- float VECTORCALL InterpreterStackFrame::GetRegRaw(RegSlot localRegisterID) const
- {
- return (float)m_localFloatSlots[localRegisterID];
- }
- template <>
- int64 InterpreterStackFrame::GetRegRaw(RegSlot localRegisterID) const
- {
- return m_localInt64Slots[localRegisterID];
- }
- template <typename T>
- void InterpreterStackFrame::SetRegRaw(RegSlot localRegisterID, T bValue)
- {
- m_localIntSlots[localRegisterID] = (int)bValue;
- }
- template <>
- void InterpreterStackFrame::SetRegRaw(RegSlot localRegisterID, float bValue)
- {
- m_localFloatSlots[localRegisterID] = (float)bValue;
- }
- template <>
- void InterpreterStackFrame::SetRegRaw(RegSlot localRegisterID, double bValue)
- {
- m_localDoubleSlots[localRegisterID] = bValue;
- }
- template <>
- void InterpreterStackFrame::SetRegRaw(RegSlot localRegisterID, int64 bValue)
- {
- m_localInt64Slots[localRegisterID] = bValue;
- }
- template <typename RegSlotType>
- int InterpreterStackFrame::GetRegRawInt(RegSlotType localRegisterID) const
- {
- return m_localIntSlots[localRegisterID];
- }
- template <typename RegSlotType>
- double InterpreterStackFrame::GetRegRawDouble(RegSlotType localRegisterID) const
- {
- return m_localDoubleSlots[localRegisterID];
- }
- template <typename RegSlotType>
- float InterpreterStackFrame::GetRegRawFloat(RegSlotType localRegisterID) const
- {
- return m_localFloatSlots[localRegisterID];
- }
- template <typename RegSlotType>
- void InterpreterStackFrame::SetRegRawInt(RegSlotType localRegisterID, int bValue)
- {
- m_localIntSlots[localRegisterID] = bValue;
- }
- template <typename RegSlotType>
- int64 InterpreterStackFrame::GetRegRawInt64(RegSlotType localRegisterID) const
- {
- return m_localInt64Slots[localRegisterID];
- }
- template <typename RegSlotType>
- void* InterpreterStackFrame::GetRegRawPtr(RegSlotType localRegisterID) const
- {
- #if TARGET_32
- return (void*)m_localIntSlots[localRegisterID];
- #elif TARGET_64
- return (void*)m_localInt64Slots[localRegisterID];
- #endif
- }
- template <typename RegSlotType>
- void InterpreterStackFrame::SetRegRawPtr(RegSlotType localRegisterID, void* val)
- {
- #if TARGET_32
- m_localIntSlots[localRegisterID] = (int32)val;
- #elif TARGET_64
- m_localInt64Slots[localRegisterID] = (int64)val;
- #endif
- }
- template <typename RegSlotType>
- void InterpreterStackFrame::SetRegRawInt64(RegSlotType localRegisterID, int64 bValue)
- {
- m_localInt64Slots[localRegisterID] = bValue;
- }
- template <typename RegSlotType>
- void InterpreterStackFrame::SetRegRawDouble(RegSlotType localRegisterID, double bValue)
- {
- m_localDoubleSlots[localRegisterID] = bValue;
- }
- template <typename RegSlotType>
- void InterpreterStackFrame::SetRegRawFloat(RegSlotType localRegisterID, float bValue)
- {
- m_localFloatSlots[localRegisterID] = bValue;
- }
- template <typename RegSlotType>
- Var InterpreterStackFrame::GetRegAllowStackVar(RegSlotType localRegisterID) const
- {
- Var value = m_localSlots[localRegisterID];
- ValidateRegValue(value, true);
- return value;
- }
- template <typename RegSlotType>
- void InterpreterStackFrame::SetRegAllowStackVar(RegSlotType localRegisterID, Var value)
- {
- Assert(localRegisterID == 0 || localRegisterID >= m_functionBody->GetConstantCount());
- ValidateRegValue(value, true);
- m_localSlots[localRegisterID] = value;
- #if ENABLE_OBJECT_SOURCE_TRACKING
- if (value != nullptr && DynamicType::Is(Js::JavascriptOperators::GetTypeId(value)))
- {
- static_cast<DynamicObject*>(value)->SetDiagOriginInfoAsNeeded();
- }
- #endif
- #if ENABLE_VALUE_TRACE
- if (this->function->GetScriptContext()->ShouldPerformRecordOrReplayAction())
- {
- this->function->GetScriptContext()->GetThreadContext()->TTDExecutionInfo->GetTraceLogger()->WriteTraceValue(value);
- }
- #endif
- }
- template <typename RegSlotType>
- Var InterpreterStackFrame::GetRegAllowStackVarEnableOnly(RegSlotType localRegisterID) const
- {
- Var value = m_localSlots[localRegisterID];
- ValidateRegValue(value, true, false);
- return value;
- }
- template <typename RegSlotType>
- void InterpreterStackFrame::SetRegAllowStackVarEnableOnly(RegSlotType localRegisterID, Var value)
- {
- Assert(localRegisterID == 0 || localRegisterID >= m_functionBody->GetConstantCount());
- ValidateRegValue(value, true, false);
- m_localSlots[localRegisterID] = value;
- #if ENABLE_OBJECT_SOURCE_TRACKING
- if (value != nullptr && DynamicType::Is(Js::JavascriptOperators::GetTypeId(value)))
- {
- static_cast<DynamicObject*>(value)->SetDiagOriginInfoAsNeeded();
- }
- #endif
- #if ENABLE_VALUE_TRACE
- if (this->function->GetScriptContext()->ShouldPerformRecordOrReplayAction())
- {
- this->function->GetScriptContext()->GetThreadContext()->TTDExecutionInfo->GetTraceLogger()->WriteTraceValue(value);
- }
- #endif
- }
- template <>
- AsmJsSIMDValue InterpreterStackFrame::GetRegRaw(RegSlot localRegisterID) const
- {
- return (AsmJsSIMDValue)m_localSimdSlots[localRegisterID];
- }
- template<>
- void InterpreterStackFrame::SetRegRaw(RegSlot localRegisterID, AsmJsSIMDValue bValue)
- {
- m_localSimdSlots[localRegisterID] = bValue;
- }
- template <typename RegSlotType>
- AsmJsSIMDValue InterpreterStackFrame::GetRegRawSimd(RegSlotType localRegisterID) const
- {
- return m_localSimdSlots[localRegisterID];
- }
- template <typename RegSlotType>
- void InterpreterStackFrame::SetRegRawSimd(RegSlotType localRegisterID, AsmJsSIMDValue bValue)
- {
- m_localSimdSlots[localRegisterID] = bValue;
- }
- int InterpreterStackFrame::OP_GetMemorySize()
- {
- #ifdef ENABLE_WASM
- return (int)GetWebAssemblyMemory()->GetCurrentMemoryPages();
- #else
- Assert(UNREACHED);
- return 0;
- #endif
- }
- int InterpreterStackFrame::OP_GrowMemory(int32 delta)
- {
- #ifdef ENABLE_WASM
- return GetWebAssemblyMemory()->GrowInternal((uint32)delta);
- #else
- Assert(UNREACHED);
- return 0;
- #endif
- }
- template <typename T, InterpreterStackFrame::AsmJsMathPtr<T> func> T InterpreterStackFrame::OP_UnsignedDivRemCheck(T aLeft, T aRight, ScriptContext* scriptContext)
- {
- if (aRight == 0)
- {
- JavascriptError::ThrowWebAssemblyRuntimeError(scriptContext, WASMERR_DivideByZero);
- }
- return func(aLeft, aRight);
- }
- template <typename T, InterpreterStackFrame::AsmJsMathPtr<T> func> T InterpreterStackFrame::OP_DivOverflow(T aLeft, T aRight, ScriptContext* scriptContext)
- {
- if (aRight == 0)
- {
- JavascriptError::ThrowWebAssemblyRuntimeError(scriptContext, WASMERR_DivideByZero);
- }
- if (aLeft == SignedTypeTraits<T>::MinValue && aRight == -1)
- {
- JavascriptError::ThrowWebAssemblyRuntimeError(scriptContext, VBSERR_Overflow);
- }
- return func(aLeft, aRight);
- }
- template <typename T, InterpreterStackFrame::AsmJsMathPtr<T> func> T InterpreterStackFrame::OP_RemOverflow(T aLeft, T aRight, ScriptContext* scriptContext)
- {
- if (aRight == 0)
- {
- JavascriptError::ThrowWebAssemblyRuntimeError(scriptContext, WASMERR_DivideByZero);
- }
- if (aLeft == SignedTypeTraits<T>::MinValue && aRight == -1)
- {
- return 0;
- }
- return func(aLeft, aRight);
- }
- void InterpreterStackFrame::OP_Unreachable()
- {
- JavascriptError::ThrowUnreachable(scriptContext);
- }
- template <class T>
- void InterpreterStackFrame::OP_SimdLdArrGeneric(const unaligned T* playout)
- {
- Assert(playout->ViewType < Js::ArrayBufferView::TYPE_COUNT);
- const uint64 index = ((uint64)(uint32)GetRegRawInt(playout->SlotIndex) + playout->Offset /* WASM only */) & (int64)(int)ArrayBufferView::ViewMask[playout->ViewType];
- ArrayBufferBase* arr =
- #ifdef ENABLE_WASM_SIMD
- (m_functionBody->IsWasmFunction()) ?
- m_wasmMemory->GetBuffer() :
- #endif
- GetAsmJsBuffer();
- BYTE* buffer = arr->GetBuffer();
- uint8 dataWidth = playout->DataWidth;
- RegSlot dstReg = playout->Value;
- if (index + dataWidth > arr->GetByteLength())
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange, _u("Simd typed array access"));
- }
- AsmJsSIMDValue *data = (AsmJsSIMDValue*)(buffer + index);
- AsmJsSIMDValue value;
- value = SIMDUtils::SIMDLdData(data, dataWidth);
- SetRegRawSimd(dstReg, value);
- }
- template <class T>
- void InterpreterStackFrame::OP_SimdLdArrConstIndex(const unaligned T* playout)
- {
- Assert(playout->ViewType < Js::ArrayBufferView::TYPE_COUNT);
- const uint64 index = (uint32)playout->SlotIndex;
- JavascriptArrayBuffer* arr = GetAsmJsBuffer();
- BYTE* buffer = arr->GetBuffer();
- uint8 dataWidth = playout->DataWidth;
- RegSlot dstReg = playout->Value;
- if (index + dataWidth > arr->GetByteLength())
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange, _u("Simd typed array access"));
- }
- AsmJsSIMDValue *data = (AsmJsSIMDValue*)(buffer + index);
- AsmJsSIMDValue value;
- value = SIMDUtils::SIMDLdData(data, dataWidth);
- SetRegRawSimd(dstReg, value);
- }
- template <class T>
- void InterpreterStackFrame::OP_SimdStArrGeneric(const unaligned T* playout)
- {
- Assert(playout->ViewType < Js::ArrayBufferView::TYPE_COUNT);
- const uint64 index = ((uint64)(uint32)GetRegRawInt(playout->SlotIndex) + playout->Offset /* WASM only */) & (int64)(int)ArrayBufferView::ViewMask[playout->ViewType];
- ArrayBufferBase* arr =
- #ifdef ENABLE_WASM_SIMD
- (m_functionBody->IsWasmFunction()) ?
- m_wasmMemory->GetBuffer() :
- #endif
- GetAsmJsBuffer();
- BYTE* buffer = arr->GetBuffer();
- uint8 dataWidth = playout->DataWidth;
- RegSlot srcReg = playout->Value;
- if (index + dataWidth > arr->GetByteLength())
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange, _u("Simd typed array access"));
- }
- AsmJsSIMDValue *data = (AsmJsSIMDValue*)(buffer + index);
- AsmJsSIMDValue value = GetRegRawSimd(srcReg);
- SIMDUtils::SIMDStData(data, value, dataWidth);
- }
- template <class T>
- void InterpreterStackFrame::OP_SimdStArrConstIndex(const unaligned T* playout)
- {
- Assert(playout->ViewType < Js::ArrayBufferView::TYPE_COUNT);
- const uint64 index = (uint32)playout->SlotIndex;
- JavascriptArrayBuffer* arr = GetAsmJsBuffer();
- BYTE* buffer = arr->GetBuffer();
- uint8 dataWidth = playout->DataWidth;
- RegSlot srcReg = playout->Value;
- if (index + dataWidth > arr->GetByteLength())
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange, _u("Simd typed array access"));
- }
- AsmJsSIMDValue *data = (AsmJsSIMDValue*)(buffer + index);
- AsmJsSIMDValue value = GetRegRawSimd(srcReg);
- SIMDUtils::SIMDStData(data, value, dataWidth);
- }
- bool InterpreterStackFrame::SIMDAnyNaN(AsmJsSIMDValue& input)
- {
- if (!GetFunctionBody()->IsWasmFunction())
- {
- return false;
- }
- AsmJsSIMDValue compResult = SIMDFloat32x4Operation::OpEqual(input, input);
- return !SIMDBool32x4Operation::OpAllTrue(compResult);
- }
- // handler for SIMD.Int32x4.FromFloat32x4
- template <class T>
- void InterpreterStackFrame::OP_SimdInt32x4FromFloat32x4(const unaligned T* playout)
- {
- bool throws = false;
- AsmJsSIMDValue input = GetRegRawSimd(playout->F4_1);
- AsmJsSIMDValue result{ 0 };
- #ifdef ENABLE_WASM_SIMD
- throws = SIMDAnyNaN(input);
- if (!throws)
- #endif
- {
- result = SIMDInt32x4Operation::OpFromFloat32x4(input, throws);
- }
- // value is out of bound
- if (throws)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange, _u("SIMD.Int32x4.FromFloat32x4"));
- }
- SetRegRawSimd(playout->I4_0, result);
- }
- // handler for SIMD.Uint32x4.FromFloat32x4
- template <class T>
- void InterpreterStackFrame::OP_SimdUint32x4FromFloat32x4(const unaligned T* playout)
- {
- bool throws = false;
- AsmJsSIMDValue input = GetRegRawSimd(playout->F4_1);
- AsmJsSIMDValue result{ 0 };
- #ifdef ENABLE_WASM_SIMD
- throws = SIMDAnyNaN(input);
- if (!throws)
- #endif
- {
- result = SIMDUint32x4Operation::OpFromFloat32x4(input, throws);
- }
- if (throws)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange, _u("SIMD.Int32x4.FromFloat32x4"));
- }
- SetRegRawSimd(playout->U4_0, result);
- }
- template <class T>
- void InterpreterStackFrame::OP_WasmSimdConst(const unaligned T* playout)
- {
- AsmJsSIMDValue result{ playout->C1, playout->C2, playout->C3, playout->C4 };
- SetRegRawSimd(playout->F4_0, result);
- }
- template <class T>
- void InterpreterStackFrame::OP_SimdShuffleV8X16(const unaligned T* playout)
- {
- uint32 lanes[Wasm::Simd::MAX_LANES];
- for (uint32 i = 0; i < Wasm::Simd::MAX_LANES; i++)
- {
- Assert(playout->INDICES[i] < Wasm::Simd::MAX_LANES * 2);
- lanes[i] = playout->INDICES[i];
- }
- SetRegRawSimd(playout->R0, SIMDUtils::SIMD128InnerShuffle(GetRegRawSimd(playout->R1), GetRegRawSimd(playout->R2), Wasm::Simd::MAX_LANES, lanes));
- }
- template <class T>
- void InterpreterStackFrame::OP_SimdInt16x8(const unaligned T* playout)
- {
- int16 values[8];
- values[0] = (int16)GetRegRawInt(playout->I1);
- values[1] = (int16)GetRegRawInt(playout->I2);
- values[2] = (int16)GetRegRawInt(playout->I3);
- values[3] = (int16)GetRegRawInt(playout->I4);
- values[4] = (int16)GetRegRawInt(playout->I5);
- values[5] = (int16)GetRegRawInt(playout->I6);
- values[6] = (int16)GetRegRawInt(playout->I7);
- values[7] = (int16)GetRegRawInt(playout->I8);
- AsmJsSIMDValue result = SIMDInt16x8Operation::OpInt16x8(values);
- SetRegRawSimd(playout->I8_0, result);
- }
- template <class T>
- void InterpreterStackFrame::OP_SimdInt8x16(const unaligned T* playout)
- {
- int8 values[16];
- values[0] = (int8)GetRegRawInt(playout->I1);
- values[1] = (int8)GetRegRawInt(playout->I2);
- values[2] = (int8)GetRegRawInt(playout->I3);
- values[3] = (int8)GetRegRawInt(playout->I4);
- values[4] = (int8)GetRegRawInt(playout->I5);
- values[5] = (int8)GetRegRawInt(playout->I6);
- values[6] = (int8)GetRegRawInt(playout->I7);
- values[7] = (int8)GetRegRawInt(playout->I8);
- values[8] = (int8)GetRegRawInt(playout->I9);
- values[9] = (int8)GetRegRawInt(playout->I10);
- values[10] = (int8)GetRegRawInt(playout->I11);
- values[11] = (int8)GetRegRawInt(playout->I12);
- values[12] = (int8)GetRegRawInt(playout->I13);
- values[13] = (int8)GetRegRawInt(playout->I14);
- values[14] = (int8)GetRegRawInt(playout->I15);
- values[15] = (int8)GetRegRawInt(playout->I16);
- AsmJsSIMDValue result = SIMDInt8x16Operation::OpInt8x16(values);
- SetRegRawSimd(playout->I16_0, result);
- }
- template <class T>
- void InterpreterStackFrame::OP_SimdUint16x8(const unaligned T* playout)
- {
- uint16 values[8];
- values[0] = (uint16)GetRegRawInt(playout->I1);
- values[1] = (uint16)GetRegRawInt(playout->I2);
- values[2] = (uint16)GetRegRawInt(playout->I3);
- values[3] = (uint16)GetRegRawInt(playout->I4);
- values[4] = (uint16)GetRegRawInt(playout->I5);
- values[5] = (uint16)GetRegRawInt(playout->I6);
- values[6] = (uint16)GetRegRawInt(playout->I7);
- values[7] = (uint16)GetRegRawInt(playout->I8);
- AsmJsSIMDValue result = SIMDUint16x8Operation::OpUint16x8(values);
- SetRegRawSimd(playout->U8_0, result);
- }
- template <class T>
- void InterpreterStackFrame::OP_SimdUint8x16(const unaligned T* playout)
- {
- uint8 values[16];
- values[0] = (uint8)GetRegRawInt(playout->I1);
- values[1] = (uint8)GetRegRawInt(playout->I2);
- values[2] = (uint8)GetRegRawInt(playout->I3);
- values[3] = (uint8)GetRegRawInt(playout->I4);
- values[4] = (uint8)GetRegRawInt(playout->I5);
- values[5] = (uint8)GetRegRawInt(playout->I6);
- values[6] = (uint8)GetRegRawInt(playout->I7);
- values[7] = (uint8)GetRegRawInt(playout->I8);
- values[8] = (uint8)GetRegRawInt(playout->I9);
- values[9] = (uint8)GetRegRawInt(playout->I10);
- values[10] = (uint8)GetRegRawInt(playout->I11);
- values[11] = (uint8)GetRegRawInt(playout->I12);
- values[12] = (uint8)GetRegRawInt(playout->I13);
- values[13] = (uint8)GetRegRawInt(playout->I14);
- values[14] = (uint8)GetRegRawInt(playout->I15);
- values[15] = (uint8)GetRegRawInt(playout->I16);
- AsmJsSIMDValue result = SIMDUint8x16Operation::OpUint8x16(values);
- SetRegRawSimd(playout->U16_0, result);
- }
- // Bool constructors
- template <class T>
- void InterpreterStackFrame::OP_SimdBool32x4(const unaligned T* playout)
- {
- bool arg1 = GetRegRawInt(playout->I1) ? true : false;
- bool arg2 = GetRegRawInt(playout->I2) ? true : false;
- bool arg3 = GetRegRawInt(playout->I3) ? true : false;
- bool arg4 = GetRegRawInt(playout->I4) ? true : false;
- AsmJsSIMDValue result = SIMDBool32x4Operation::OpBool32x4(arg1, arg2, arg3, arg4);
- SetRegRawSimd(playout->B4_0, result);
- }
- template <class T>
- void InterpreterStackFrame::OP_SimdBool16x8(const unaligned T* playout)
- {
- bool values[8];
- values[0] = GetRegRawInt(playout->I1) ? true : false;
- values[1] = GetRegRawInt(playout->I2) ? true : false;
- values[2] = GetRegRawInt(playout->I3) ? true : false;
- values[3] = GetRegRawInt(playout->I4) ? true : false;
- values[4] = GetRegRawInt(playout->I5) ? true : false;
- values[5] = GetRegRawInt(playout->I6) ? true : false;
- values[6] = GetRegRawInt(playout->I7) ? true : false;
- values[7] = GetRegRawInt(playout->I8) ? true : false;
- AsmJsSIMDValue result = SIMDBool16x8Operation::OpBool16x8(values);
- SetRegRawSimd(playout->B8_0, result);
- }
- template <class T>
- void InterpreterStackFrame::OP_SimdBool8x16(const unaligned T* playout)
- {
- bool values[16];
- values[0] = GetRegRawInt(playout->I1) ? true : false;
- values[1] = GetRegRawInt(playout->I2) ? true : false;
- values[2] = GetRegRawInt(playout->I3) ? true : false;
- values[3] = GetRegRawInt(playout->I4) ? true : false;
- values[4] = GetRegRawInt(playout->I5) ? true : false;
- values[5] = GetRegRawInt(playout->I6) ? true : false;
- values[6] = GetRegRawInt(playout->I7) ? true : false;
- values[7] = GetRegRawInt(playout->I8) ? true : false;
- values[8] = GetRegRawInt(playout->I9) ? true : false;
- values[9] = GetRegRawInt(playout->I10) ? true : false;
- values[10] = GetRegRawInt(playout->I11) ? true : false;
- values[11] = GetRegRawInt(playout->I12) ? true : false;
- values[12] = GetRegRawInt(playout->I13) ? true : false;
- values[13] = GetRegRawInt(playout->I14) ? true : false;
- values[14] = GetRegRawInt(playout->I15) ? true : false;
- values[15] = GetRegRawInt(playout->I16) ? true : false;
- AsmJsSIMDValue result = SIMDBool8x16Operation::OpBool8x16(values);
- SetRegRawSimd(playout->B16_0, result);
- }
- Var InterpreterStackFrame::GetNonVarReg(RegSlot localRegisterID) const
- {
- return m_localSlots[localRegisterID];
- }
- void InterpreterStackFrame::SetNonVarReg(RegSlot localRegisterID, Var aValue)
- {
- m_localSlots[localRegisterID] = aValue;
- }
- Var InterpreterStackFrame::GetRootObject() const
- {
- Assert(!this->GetFunctionBody()->IsJsBuiltInCode());
- Var rootObject = GetReg(Js::FunctionBody::RootObjectRegSlot);
- Assert(rootObject == this->GetFunctionBody()->LoadRootObject());
- return rootObject;
- }
- Var InterpreterStackFrame::OP_ArgIn0()
- {
- return m_inParams[0];
- }
- #if ENABLE_PROFILE_INFO
- template <class T>
- void InterpreterStackFrame::OP_ProfiledArgOut_A(const unaligned T * playout)
- {
- FunctionBody* functionBody = this->m_functionBody;
- DynamicProfileInfo * dynamicProfileInfo = functionBody->GetDynamicProfileInfo();
- Assert(playout->Reg > FunctionBody::FirstRegSlot);
- Var value = GetReg(playout->Reg);
- dynamicProfileInfo->RecordParameterAtCallSite(functionBody, playout->profileId, value, playout->Arg, playout->Reg);
- SetOut(playout->Arg, value);
- }
- #endif
- template <class T>
- void InterpreterStackFrame::OP_ArgOut_A(const unaligned T * playout)
- {
- SetOut(playout->Arg, GetReg(playout->Reg));
- }
- #if DBG
- template <class T>
- void InterpreterStackFrame::OP_ArgOut_ANonVar(const unaligned T * playout)
- {
- SetOut(playout->Arg, GetNonVarReg(playout->Reg));
- }
- #endif
- template <class T>
- void InterpreterStackFrame::OP_ArgOut_Env(const unaligned T * playout)
- {
- Var argEnv;
- if (this->m_functionBody->GetLocalFrameDisplayRegister() != Constants::NoRegister)
- {
- argEnv = this->GetLocalFrameDisplay();
- }
- else
- {
- argEnv = this->LdEnv();
- }
- SetOut(playout->Arg, argEnv);
- }
- BOOL InterpreterStackFrame::OP_BrFalse_A(Var aValue, ScriptContext* scriptContext)
- {
- return !JavascriptConversion::ToBoolean(aValue, scriptContext);
- }
- BOOL InterpreterStackFrame::OP_BrTrue_A(Var aValue, ScriptContext* scriptContext)
- {
- return JavascriptConversion::ToBoolean(aValue, scriptContext);
- }
- BOOL InterpreterStackFrame::OP_BrNotNull_A(Var aValue)
- {
- return aValue != NULL;
- }
- BOOL InterpreterStackFrame::OP_BrUndecl_A(Var aValue)
- {
- return this->scriptContext->GetLibrary()->IsUndeclBlockVar(aValue);
- }
- BOOL InterpreterStackFrame::OP_BrNotUndecl_A(Var aValue)
- {
- return !this->scriptContext->GetLibrary()->IsUndeclBlockVar(aValue);
- }
- BOOL InterpreterStackFrame::OP_BrOnHasProperty(Var argInstance, uint propertyIdIndex, ScriptContext* scriptContext)
- {
- return JavascriptOperators::OP_HasProperty(argInstance,
- this->m_functionBody->GetReferencedPropertyId(propertyIdIndex), scriptContext);
- }
- BOOL InterpreterStackFrame::OP_BrOnNoProperty(Var argInstance, uint propertyIdIndex, ScriptContext* scriptContext)
- {
- return !JavascriptOperators::OP_HasProperty(argInstance,
- this->m_functionBody->GetReferencedPropertyId(propertyIdIndex), scriptContext);
- }
- BOOL InterpreterStackFrame::OP_BrOnNoEnvProperty(Var envInstance, int32 slotIndex, uint propertyIdIndex, ScriptContext* scriptContext)
- {
- Var instance = OP_LdFrameDisplaySlot(envInstance, slotIndex);
- return !JavascriptOperators::OP_HasProperty(instance,
- this->m_functionBody->GetReferencedPropertyId(propertyIdIndex), scriptContext);
- }
- BOOL InterpreterStackFrame::OP_BrOnClassConstructor(Var aValue)
- {
- return JavascriptOperators::IsClassConstructor(aValue);
- }
- BOOL InterpreterStackFrame::OP_BrOnBaseConstructorKind(Var aValue)
- {
- return JavascriptOperators::IsBaseConstructorKind(aValue);
- }
- template<class T>
- void InterpreterStackFrame::OP_LdLen(const unaligned T * const playout)
- {
- Assert(playout);
- ThreadContext* threadContext = this->GetScriptContext()->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- Var instance = GetReg(playout->Instance);
- Var length = JavascriptOperators::OP_GetLength(instance, GetScriptContext());
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- SetReg(playout->Value, length);
- }
- #if ENABLE_PROFILE_INFO
- template<class T>
- void InterpreterStackFrame::OP_ProfiledLdLen(const unaligned OpLayoutDynamicProfile<T> *const playout)
- {
- Assert(playout);
- FunctionBody * functionBody = m_functionBody;
- DynamicProfileInfo * profileData = functionBody->GetDynamicProfileInfo();
- Var instance = GetReg(playout->Instance);
- LdLenInfo ldLenInfo;
- ldLenInfo.arrayType = ValueType::Uninitialized.Merge(instance);
- if (this->TestFlags(InterpreterStackFrameFlags_ProcessingBailOutOnArraySpecialization))
- {
- ldLenInfo.disableAggressiveSpecialization = true;
- this->ClearFlags(InterpreterStackFrameFlags_ProcessingBailOutOnArraySpecialization);
- }
- profileData->RecordLengthLoad(functionBody, playout->profileId, ldLenInfo);
- ThreadContext* threadContext = this->GetScriptContext()->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- PropertyId propertyId = GetPropertyIdFromCacheId(playout->inlineCacheIndex);
- Var value = ProfilingHelpers::ProfiledLdFld<false, false, false>(
- instance,
- propertyId,
- GetInlineCache(playout->inlineCacheIndex),
- playout->inlineCacheIndex,
- GetFunctionBody(),
- instance);
- SetReg(playout->Value, value);
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- }
- #endif
- #if ENABLE_PROFILE_INFO
- template <bool doProfile>
- Var InterpreterStackFrame::ProfiledIsIn(Var argProperty, Var instance, ScriptContext* scriptContext, ProfileId profileId)
- {
- if (doProfile)
- {
- DynamicProfileInfo * profileData = m_functionBody->GetDynamicProfileInfo();
- LdElemInfo ldElemInfo;
- ldElemInfo.arrayType = ValueType::Uninitialized.Merge(instance);
- if (this->TestFlags(InterpreterStackFrameFlags_ProcessingBailOutOnArraySpecialization))
- {
- ldElemInfo.disableAggressiveSpecialization = true;
- }
- this->ClearFlags(InterpreterStackFrameFlags_ProcessingBailOutOnArraySpecialization);
- profileData->RecordElementLoad(m_functionBody, profileId, ldElemInfo);
- }
- return JavascriptOperators::IsIn(argProperty, instance, scriptContext);
- }
- #else
- template <bool doProfile>
- Var InterpreterStackFrame::ProfiledIsIn(Var argProperty, Var instance, ScriptContext* scriptContext, ProfileId profileId)
- {
- Assert(!doProfile);
- return JavascriptOperators::IsIn(argProperty, instance, scriptContext);
- }
- #endif
- JavascriptFunction* InterpreterStackFrame::GetFunctionExpression()
- {
- // Make sure we get the boxed function object if is there, (or the function itself)
- return StackScriptFunction::GetCurrentFunctionObject(this->function->GetRealFunctionObject());
- }
- template <class T>
- void InterpreterStackFrame::OP_LdFunctionExpression(const unaligned T * playout)
- {
- SetRegAllowStackVar(playout->R0, this->GetFunctionExpression());
- }
- template <class T>
- void InterpreterStackFrame::OP_StFunctionExpression(const unaligned T * playout)
- {
- OP_StFunctionExpression(GetReg(playout->Instance), GetReg(playout->Value), playout->PropertyIdIndex);
- }
- template <class T>
- void InterpreterStackFrame::OP_StLocalFunctionExpression(const unaligned T * playout)
- {
- OP_StFunctionExpression(this->localClosure, GetReg(playout->Instance), playout->PropertyIdIndex);
- }
- void InterpreterStackFrame::OP_StFunctionExpression(Var instance, Var value, PropertyIdIndexType index)
- {
- JavascriptOperators::OP_StFunctionExpression(instance,
- this->m_functionBody->GetReferencedPropertyId(index), value);
- }
- template <class T>
- void InterpreterStackFrame::OP_LdNewTarget(const unaligned T* playout)
- {
- if (Js::CallInfo::HasNewTarget(this->m_callFlags))
- {
- SetRegAllowStackVar(playout->R0, (Js::RecyclableObject*)Js::CallInfo::GetNewTarget(this->m_callFlags, this->m_inParams, (ArgSlot)this->m_inSlotsCount));
- }
- else if (this->m_callFlags & CallFlags_New)
- {
- SetRegAllowStackVar(playout->R0, this->GetFunctionExpression());
- }
- else
- {
- SetReg(playout->R0, this->GetScriptContext()->GetLibrary()->GetUndefined());
- }
- }
- Var InterpreterStackFrame::OP_Ld_A(Var aValue)
- {
- return aValue;
- }
- Var InterpreterStackFrame::LdEnv() const
- {
- return this->function->GetEnvironment();
- }
- void InterpreterStackFrame::SetEnv(FrameDisplay *frameDisplay)
- {
- this->function->SetEnvironment(frameDisplay);
- }
- Var InterpreterStackFrame::OP_LdLocalObj()
- {
- if (!VirtualTableInfo<ActivationObject>::HasVirtualTable(this->localClosure) &&
- !VirtualTableInfo<ActivationObjectEx>::HasVirtualTable(this->localClosure))
- {
- Js::Throw::FatalInternalError();
- }
- return this->localClosure;
- }
- Var InterpreterStackFrame::OP_LdParamObj()
- {
- if (!VirtualTableInfo<ActivationObject>::HasVirtualTable(this->paramClosure) &&
- !VirtualTableInfo<ActivationObjectEx>::HasVirtualTable(this->paramClosure))
- {
- Js::Throw::FatalInternalError();
- }
- return this->paramClosure;
- }
- #ifdef ASMJS_PLAT
- template <typename ArrayType, typename RegType>
- void InterpreterStackFrame::OP_StArr(uint32 index, RegSlot regSlot)
- {
- JavascriptArrayBuffer* arr = GetAsmJsBuffer();
- if (index < arr->GetByteLength())
- {
- BYTE* buffer = arr->GetBuffer();
- *(ArrayType*)(buffer + index) = (ArrayType)GetRegRaw<RegType>(regSlot);
- }
- }
- #endif
- template<> inline double InterpreterStackFrame::GetArrayViewOverflowVal()
- {
- return *(double*)&NumberConstants::k_Nan;
- }
- template<> inline float InterpreterStackFrame::GetArrayViewOverflowVal()
- {
- return (float)*(double*)&NumberConstants::k_Nan;
- }
- template<typename T> T InterpreterStackFrame::GetArrayViewOverflowVal()
- {
- return 0;
- }
- template <class T>
- void InterpreterStackFrame::OP_LdArrFunc(const unaligned T* playout)
- {
- Var* arr = (Var*)GetRegRawPtr(playout->Instance);
- const uint32 index = (uint32)GetRegRawInt(playout->SlotIndex);
- SetRegRawPtr(playout->Value, arr[index]);
- }
- template <class T>
- void InterpreterStackFrame::OP_LdArrWasmFunc(const unaligned T* playout)
- {
- #ifdef ENABLE_WASM
- WebAssemblyTable * table = WebAssemblyTable::FromVar(GetRegRawPtr(playout->Instance));
- const uint32 index = (uint32)GetRegRawInt(playout->SlotIndex);
- if (index >= table->GetCurrentLength())
- {
- JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_TableIndexOutOfRange);
- }
- Var func = table->DirectGetValue(index);
- if (!func)
- {
- JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_NeedWebAssemblyFunc);
- }
- SetRegRawPtr(playout->Value, func);
- #endif
- }
- template <class T>
- void InterpreterStackFrame::OP_CheckSignature(const unaligned T* playout)
- {
- #ifdef ENABLE_WASM
- ScriptFunction * func = ScriptFunction::FromVar(GetRegRawPtr(playout->R0));
- int sigIndex = playout->C1;
- Wasm::WasmSignature * expected = &m_signatures[sigIndex];
- if (func->GetFunctionInfo()->IsDeferredParseFunction())
- {
- // TODO: should be able to assert this once imports are converted to wasm functions
- JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_NeedWebAssemblyFunc);
- }
- AsmJsFunctionInfo * asmInfo = func->GetFunctionBody()->GetAsmJsFunctionInfo();
- if (!asmInfo)
- {
- // TODO: should be able to assert this once imports are converted to wasm functions
- JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_NeedWebAssemblyFunc);
- }
- if (!expected->IsEquivalent(asmInfo->GetWasmSignature()))
- {
- JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_SignatureMismatch);
- }
- #endif
- }
- #ifdef ASMJS_PLAT
- template <typename ArrayType, typename RegType>
- void InterpreterStackFrame::OP_LdArr(uint32 index, RegSlot regSlot)
- {
- JavascriptArrayBuffer* arr = GetAsmJsBuffer();
- BYTE* buffer = arr->GetBuffer();
- ArrayType val = index < (arr->GetByteLength()) ? *(ArrayType*)(buffer + index) : GetArrayViewOverflowVal<ArrayType>();
- SetRegRaw<RegType>(regSlot, (RegType)val);
- }
- #endif
- template <class T, typename T2>
- void InterpreterStackFrame::OP_StSlotPrimitive(const unaligned T* playout)
- {
- T2* buffer = (T2*)GetNonVarReg(playout->Instance);
- buffer[playout->SlotIndex] = GetRegRaw<T2>(playout->Value);
- }
- template <class T>
- void InterpreterStackFrame::OP_LdAsmJsSlot(const unaligned T* playout)
- {
- Var * slotArray = (Var*)GetNonVarReg(playout->Instance);
- SetRegRawPtr(playout->Value, slotArray[playout->SlotIndex]);
- }
- template <class T, typename T2>
- void InterpreterStackFrame::OP_LdSlotPrimitive(const unaligned T* playout)
- {
- T2* buffer = (T2*)GetNonVarReg(playout->Instance);
- SetRegRaw<T2>(playout->Value, buffer[playout->SlotIndex]);
- }
- #ifndef TEMP_DISABLE_ASMJS
- template <class T>
- void InterpreterStackFrame::OP_LdArrGeneric(const unaligned T* playout)
- {
- const uint32 index = (uint32)GetRegRawInt(playout->SlotIndex);
- switch (playout->ViewType)
- {
- #define ARRAYBUFFER_VIEW(name, align, RegType, MemType, ...) \
- case ArrayBufferView::ViewType::TYPE_##name: \
- OP_LdArr<MemType, RegType>(index & ARRAYBUFFER_VIEW_MASK(align), playout->Value); \
- return;
- #include "AsmJsArrayBufferViews.h"
- default:Assert(UNREACHED);
- }
- }
- template<typename MemType>
- void InterpreterStackFrame::WasmArrayBoundsCheck(uint64 index, uint32 byteLength)
- {
- if (index + sizeof(MemType) > byteLength)
- {
- JavascriptError::ThrowWebAssemblyRuntimeError(scriptContext, WASMERR_ArrayIndexOutOfRange);
- }
- }
- template<typename MemType>
- MemType* InterpreterStackFrame::WasmAtomicsArrayBoundsCheck(byte* buffer, uint64 index, uint32 byteLength)
- {
- MemType* readBuffer = (MemType*)(buffer + index);
- // Do alignment check to be coherent with the order the jit does the checks
- if (!::Math::IsAligned<intptr_t>((intptr_t)readBuffer, sizeof(MemType)))
- {
- JavascriptError::ThrowWebAssemblyRuntimeError(scriptContext, WASMERR_UnalignedAtomicAccess);
- }
- WasmArrayBoundsCheck<MemType>(index, byteLength);
- return readBuffer;
- }
- template <class T>
- void InterpreterStackFrame::OP_LdArrWasm(const unaligned T* playout)
- {
- #ifdef ENABLE_WASM
- Assert(playout->ViewType < Js::ArrayBufferView::TYPE_COUNT);
- const uint64 index = playout->Offset + (uint64)(uint32)GetRegRawInt(playout->SlotIndex);
- ArrayBufferBase* arr = GetWebAssemblyMemory()->GetBuffer();
- uint32 byteLength = arr->GetByteLength();
- BYTE* buffer = arr->GetBuffer();
- switch (playout->ViewType)
- {
- #define ARRAYBUFFER_VIEW(name, align, RegType, MemType, ...) \
- case ArrayBufferView::ViewType::TYPE_##name: \
- WasmArrayBoundsCheck<MemType>(index, byteLength); \
- SetRegRaw<RegType>(playout->Value, (RegType)*(MemType*)(buffer + index)); \
- return;
- #include "AsmJsArrayBufferViews.h"
- default:Assert(UNREACHED);
- }
- #else
- Assert(UNREACHED);
- #endif
- }
- template <class T>
- void InterpreterStackFrame::OP_LdArrAtomic(const unaligned T* playout)
- {
- #ifdef ENABLE_WASM
- Assert(Wasm::Threads::IsEnabled());
- Assert(playout->ViewType < Js::ArrayBufferView::TYPE_COUNT);
- const uint64 index = playout->Offset + (uint64)(uint32)GetRegRawInt(playout->SlotIndex);
- ArrayBufferBase* arr = GetWebAssemblyMemory()->GetBuffer();
- uint32 byteLength = arr->GetByteLength();
- BYTE* buffer = arr->GetBuffer();
- switch (playout->ViewType)
- {
- #define ARRAYBUFFER_VIEW_INT(name, align, RegType, MemType, ...) \
- case ArrayBufferView::ViewType::TYPE_##name: {\
- MemType* readBuffer = WasmAtomicsArrayBoundsCheck<MemType>(buffer, index, byteLength); \
- MemType value = AtomicsOperations::Load<MemType>(readBuffer); \
- SetRegRaw<RegType>(playout->Value, (RegType)value); \
- return; \
- }
- #include "AsmJsArrayBufferViews.h"
- default:Assert(UNREACHED);
- }
- #else
- Assert(UNREACHED);
- #endif
- }
- template <class T>
- void InterpreterStackFrame::OP_StArrAtomic(const unaligned T* playout)
- {
- #ifdef ENABLE_WASM
- Assert(Wasm::Threads::IsEnabled());
- Assert(playout->ViewType < Js::ArrayBufferView::TYPE_COUNT);
- const uint64 index = playout->Offset + (uint64)(uint32)GetRegRawInt(playout->SlotIndex);
- ArrayBufferBase* arr = GetWebAssemblyMemory()->GetBuffer();
- uint32 byteLength = arr->GetByteLength();
- BYTE* buffer = arr->GetBuffer();
- switch (playout->ViewType)
- {
- #define ARRAYBUFFER_VIEW_INT(name, align, RegType, MemType, ...) \
- case ArrayBufferView::ViewType::TYPE_##name: {\
- MemType* readBuffer = WasmAtomicsArrayBoundsCheck<MemType>(buffer, index, byteLength); \
- MemType value = (MemType)GetRegRaw<RegType>(playout->Value); \
- MemType storedValue = AtomicsOperations::Store<MemType>(readBuffer, value); \
- Assert(storedValue == value); \
- return; \
- }
- #include "AsmJsArrayBufferViews.h"
- default:Assert(UNREACHED);
- }
- #else
- Assert(UNREACHED);
- #endif
- }
- template <class T>
- void InterpreterStackFrame::OP_LdArrConstIndex(const unaligned T* playout)
- {
- switch (playout->ViewType)
- {
- #define ARRAYBUFFER_VIEW(name, align, RegType, MemType, ...) \
- case ArrayBufferView::ViewType::TYPE_##name: \
- OP_LdArr<MemType, RegType>(playout->SlotIndex, playout->Value); \
- return;
- #include "AsmJsArrayBufferViews.h"
- default:Assert(UNREACHED);
- }
- }
- template <class T>
- void InterpreterStackFrame::OP_StArrGeneric(const unaligned T* playout)
- {
- const uint32 index = (uint32)GetRegRawInt(playout->SlotIndex);
- switch (playout->ViewType)
- {
- #define ARRAYBUFFER_VIEW(name, align, RegType, MemType, ...) \
- case ArrayBufferView::ViewType::TYPE_##name: \
- OP_StArr<MemType, RegType>(index & ARRAYBUFFER_VIEW_MASK(align), playout->Value); \
- return;
- #include "AsmJsArrayBufferViews.h"
- default:Assert(UNREACHED);
- }
- }
- template <class T>
- void InterpreterStackFrame::OP_StArrWasm(const unaligned T* playout)
- {
- #ifdef ENABLE_WASM
- Assert(playout->ViewType < Js::ArrayBufferView::TYPE_COUNT);
- const uint64 index = playout->Offset + (uint64)(uint32)GetRegRawInt(playout->SlotIndex);
- ArrayBufferBase* arr = GetWebAssemblyMemory()->GetBuffer();
- uint32 byteLength = arr->GetByteLength();
- BYTE* buffer = arr->GetBuffer();
- switch (playout->ViewType)
- {
- #define ARRAYBUFFER_VIEW(name, align, RegType, MemType, ...) \
- case ArrayBufferView::ViewType::TYPE_##name: \
- WasmArrayBoundsCheck<MemType>(index, byteLength); \
- *(MemType*)(buffer + index) = (MemType)(GetRegRaw<RegType>(playout->Value)); \
- break;
- #include "AsmJsArrayBufferViews.h"
- default:Assert(UNREACHED);
- }
- CompileAssert(ArrayBufferView::ViewType::TYPE_COUNT == 15);
- #if DBG
- if (PHASE_TRACE(WasmMemWritesPhase, m_functionBody))
- {
- GetWebAssemblyMemory()->TraceMemWrite(GetWebAssemblyMemory(), (uint32)GetRegRawInt(playout->SlotIndex), playout->Offset, playout->ViewType, (uint32)(size_t)this->DEBUG_currentByteOffset, scriptContext);
- }
- #endif
- return;
- #else
- Assert(UNREACHED);
- #endif
- }
- template <class T>
- void InterpreterStackFrame::OP_StArrConstIndex(const unaligned T* playout)
- {
- switch (playout->ViewType)
- {
- #define ARRAYBUFFER_VIEW(name, align, RegType, MemType, ...) \
- case ArrayBufferView::ViewType::TYPE_##name: \
- OP_StArr<MemType, RegType>(playout->SlotIndex, playout->Value); \
- return;
- #include "AsmJsArrayBufferViews.h"
- default:Assert(UNREACHED);
- }
- }
- #endif
- Var InterpreterStackFrame::OP_LdSlot(Var instance, int32 slotIndex)
- {
- if (!PHASE_OFF(ClosureRangeCheckPhase, this->m_functionBody))
- {
- if ((uintptr_t)((Var*)instance)[ScopeSlots::EncodedSlotCountSlotIndex] <= (uintptr_t)(slotIndex - ScopeSlots::FirstSlotIndex))
- {
- Js::Throw::FatalInternalError();
- }
- }
- return ((Var*)(instance))[slotIndex];
- }
- template <class T>
- Var InterpreterStackFrame::OP_LdSlot(Var instance, const unaligned T* playout)
- {
- return OP_LdSlot(instance, playout->SlotIndex);
- }
- #if ENABLE_PROFILE_INFO
- template <class T>
- Var InterpreterStackFrame::OP_ProfiledLdSlot(Var instance, const unaligned T* playout)
- {
- Var value = OP_LdSlot(instance, playout->SlotIndex);
- ProfilingHelpers::ProfileLdSlot(value, GetFunctionBody(), playout->profileId);
- return value;
- }
- #endif
- template <class T>
- Var InterpreterStackFrame::OP_LdInnerSlot(Var slotArray, const unaligned T* playout)
- {
- return OP_LdSlot(slotArray, playout->SlotIndex2);
- }
- #if ENABLE_PROFILE_INFO
- template <class T>
- Var InterpreterStackFrame::OP_ProfiledLdInnerSlot(Var slotArray, const unaligned T* playout)
- {
- Var value = OP_LdInnerSlot(slotArray, playout);
- ProfilingHelpers::ProfileLdSlot(value, GetFunctionBody(), playout->profileId);
- return value;
- }
- #endif
- template <class T>
- Var InterpreterStackFrame::OP_LdInnerObjSlot(Var slotArray, const unaligned T* playout)
- {
- return OP_LdObjSlot(slotArray, playout->SlotIndex2);
- }
- #if ENABLE_PROFILE_INFO
- template <class T>
- Var InterpreterStackFrame::OP_ProfiledLdInnerObjSlot(Var slotArray, const unaligned T* playout)
- {
- Var value = OP_LdInnerObjSlot(slotArray, playout);
- ProfilingHelpers::ProfileLdSlot(value, GetFunctionBody(), playout->profileId);
- return value;
- }
- #endif
- Var InterpreterStackFrame::OP_LdFrameDisplaySlot(Var instance, int32 slotIndex)
- {
- if (!PHASE_OFF(ClosureRangeCheckPhase, this->m_functionBody))
- {
- if (((FrameDisplay*)instance)->GetLength() <= slotIndex - Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Var))
- {
- Js::Throw::FatalInternalError();
- }
- }
- return ((Var*)instance)[slotIndex];
- }
- template <class T>
- Var InterpreterStackFrame::OP_LdEnvObj(Var instance, const unaligned T* playout)
- {
- return OP_LdFrameDisplaySlot(instance, playout->SlotIndex);
- }
- template <class T>
- Var InterpreterStackFrame::OP_LdEnvSlot(Var instance, const unaligned T* playout)
- {
- Var slotArray = OP_LdFrameDisplaySlot(instance, playout->SlotIndex1);
- return OP_LdSlot(slotArray, playout->SlotIndex2);
- }
- #if ENABLE_PROFILE_INFO
- template <class T>
- Var InterpreterStackFrame::OP_ProfiledLdEnvSlot(Var instance, const unaligned T* playout)
- {
- Var value = OP_LdEnvSlot(instance, playout);
- ProfilingHelpers::ProfileLdSlot(value, GetFunctionBody(), playout->profileId);
- return value;
- }
- #endif
- Var InterpreterStackFrame::OP_LdObjSlot(Var instance, int32 slotIndex)
- {
- Var *slotArray = *(Var**)((char*)instance + DynamicObject::GetOffsetOfAuxSlots());
- return slotArray[slotIndex];
- }
- template <class T>
- Var InterpreterStackFrame::OP_LdObjSlot(Var instance, const unaligned T* playout)
- {
- return OP_LdObjSlot(instance, playout->SlotIndex);
- }
- #if ENABLE_PROFILE_INFO
- template <class T>
- Var InterpreterStackFrame::OP_ProfiledLdObjSlot(Var instance, const unaligned T* playout)
- {
- Var value = OP_LdObjSlot(instance, playout->SlotIndex);
- ProfilingHelpers::ProfileLdSlot(value, GetFunctionBody(), playout->profileId);
- return value;
- }
- #endif
- template <class T>
- Var InterpreterStackFrame::OP_LdEnvObjSlot(Var instance, const unaligned T* playout)
- {
- Var slotArray = OP_LdFrameDisplaySlot(instance, playout->SlotIndex1);
- return OP_LdObjSlot(slotArray, playout->SlotIndex2);
- }
- template <class T>
- Var InterpreterStackFrame::OP_LdModuleSlot(Var instance, const unaligned T* playout)
- {
- return JavascriptOperators::OP_LdModuleSlot(playout->SlotIndex1, playout->SlotIndex2, scriptContext);
- }
- inline void InterpreterStackFrame::OP_StModuleSlot(Var instance, uint32 slotIndex1, uint32 slotIndex2, Var value)
- {
- JavascriptOperators::OP_StModuleSlot(slotIndex1, slotIndex2, value, scriptContext);
- }
- #if ENABLE_PROFILE_INFO
- template <class T>
- Var InterpreterStackFrame::OP_ProfiledLdEnvObjSlot(Var instance, const unaligned T* playout)
- {
- Var value = OP_LdEnvObjSlot(instance, playout);
- ProfilingHelpers::ProfileLdSlot(value, GetFunctionBody(), playout->profileId);
- return value;
- }
- #endif
- void InterpreterStackFrame::OP_StSlot(Var instance, int32 slotIndex, Var value)
- {
- // We emit OpCode::StSlot in the bytecode only for scope slot arrays, which are not recyclable objects.
- if (!PHASE_OFF(ClosureRangeCheckPhase, this->m_functionBody))
- {
- if ((uintptr_t)((Var*)instance)[ScopeSlots::EncodedSlotCountSlotIndex] <= (uintptr_t)(slotIndex - ScopeSlots::FirstSlotIndex))
- {
- Js::Throw::FatalInternalError();
- }
- }
- ((Field(Var)*)(instance))[slotIndex] = value;
- }
- void InterpreterStackFrame::OP_StEnvSlot(Var instance, int32 slotIndex1, int32 slotIndex2, Var value)
- {
- Var slotArray = (Var*)OP_LdFrameDisplaySlot(instance, slotIndex1);
- OP_StSlot(slotArray, slotIndex2, value);
- }
- void InterpreterStackFrame::OP_StSlotChkUndecl(Var instance, int32 slotIndex, Var value)
- {
- // We emit OpCode::StSlot in the bytecode only for scope slot arrays, which are not recyclable objects.
- if (!PHASE_OFF(ClosureRangeCheckPhase, this->m_functionBody))
- {
- if ((uintptr_t)((Var*)instance)[ScopeSlots::EncodedSlotCountSlotIndex] <= (uintptr_t)(slotIndex - ScopeSlots::FirstSlotIndex))
- {
- Js::Throw::FatalInternalError();
- }
- }
- OP_ChkUndecl(((Field(Var)*)instance)[slotIndex]);
- ((Field(Var)*)(instance))[slotIndex] = value;
- }
- void InterpreterStackFrame::OP_StEnvSlotChkUndecl(Var instance, int32 slotIndex1, int32 slotIndex2, Var value)
- {
- Var slotArray = OP_LdFrameDisplaySlot(instance, slotIndex1);
- OP_StSlotChkUndecl(slotArray, slotIndex2, value);
- }
- void InterpreterStackFrame::OP_StObjSlot(Var instance, int32 slotIndex, Var value)
- {
- // It would be nice to assert that it's ok to store directly to slot, but we don't have the propertyId.
- Field(Var) *slotArray = *(Field(Var)**)((char*)instance + DynamicObject::GetOffsetOfAuxSlots());
- slotArray[slotIndex] = value;
- }
- void InterpreterStackFrame::OP_StObjSlotChkUndecl(Var instance, int32 slotIndex, Var value)
- {
- // It would be nice to assert that it's ok to store directly to slot, but we don't have the propertyId.
- Field(Var) *slotArray = *(Field(Var)**)((char*)instance + DynamicObject::GetOffsetOfAuxSlots());
- OP_ChkUndecl(slotArray[slotIndex]);
- slotArray[slotIndex] = value;
- }
- void InterpreterStackFrame::OP_StEnvObjSlot(Var instance, int32 slotIndex1, int32 slotIndex2, Var value)
- {
- // It would be nice to assert that it's ok to store directly to slot, but we don't have the propertyId.
- Var envInstance = (Var*)OP_LdFrameDisplaySlot(instance, slotIndex1);
- OP_StObjSlot(envInstance, slotIndex2, value);
- }
- void InterpreterStackFrame::OP_StEnvObjSlotChkUndecl(Var instance, int32 slotIndex1, int32 slotIndex2, Var value)
- {
- // It would be nice to assert that it's ok to store directly to slot, but we don't have the propertyId.
- Var envInstance = (Var*)OP_LdFrameDisplaySlot(instance, slotIndex1);
- OP_StObjSlotChkUndecl(envInstance, slotIndex2, value);
- }
- Var InterpreterStackFrame::OP_LdStackArgPtr(void)
- {
- // Return the address of the first param after "this".
- return m_inParams + 1;
- }
- ForInObjectEnumerator * InterpreterStackFrame::GetForInEnumerator(uint forInLoopLevel)
- {
- Assert(forInLoopLevel < this->m_functionBody->GetForInLoopDepth());
- return &this->forInObjectEnumerators[forInLoopLevel];
- }
- void InterpreterStackFrame::OP_InitForInEnumerator(Var object, uint forInLoopLevel)
- {
- JavascriptOperators::OP_InitForInEnumerator(object, GetForInEnumerator(forInLoopLevel), this->GetScriptContext());
- }
- void InterpreterStackFrame::OP_InitForInEnumeratorWithCache(Var object, uint forInLoopLevel, ProfileId profileId)
- {
- JavascriptOperators::OP_InitForInEnumerator(object, GetForInEnumerator(forInLoopLevel), this->GetScriptContext(),
- m_functionBody->GetForInCache(profileId));
- }
- // Called for the debug purpose, to create the arguments object explicitly even though script has not declared it.
- Var InterpreterStackFrame::CreateHeapArguments(ScriptContext* scriptContext)
- {
- return JavascriptOperators::LoadHeapArguments(this->function->GetRealFunctionObject(), this->m_inSlotsCount - 1, &this->m_inParams[1], scriptContext->GetLibrary()->GetNull(), scriptContext->GetLibrary()->GetNull(), scriptContext, false);
- }
- template <bool letArgs>
- Var InterpreterStackFrame::LdHeapArgumentsImpl(Var argsArray, ScriptContext* scriptContext)
- {
- Var frameObj;
- if (m_functionBody->HasScopeObject() && argsArray != scriptContext->GetLibrary()->GetNull())
- {
- frameObj = this->localClosure;
- Assert(frameObj);
- }
- else
- {
- frameObj = scriptContext->GetLibrary()->GetNull();
- }
- Var args = JavascriptOperators::LoadHeapArguments(this->function->GetRealFunctionObject(), this->m_inSlotsCount - 1, &this->m_inParams[1], frameObj, argsArray, scriptContext, letArgs);
- this->m_arguments = args;
- return args;
- }
- Var InterpreterStackFrame::OP_LdHeapArguments(ScriptContext* scriptContext)
- {
- Var argsArray = m_functionBody->GetFormalsPropIdArrayOrNullObj();
- return LdHeapArgumentsImpl<false>(argsArray, scriptContext);
- }
- Var InterpreterStackFrame::OP_LdLetHeapArguments(ScriptContext* scriptContext)
- {
- Var argsArray = m_functionBody->GetFormalsPropIdArrayOrNullObj();
- return LdHeapArgumentsImpl<true>(argsArray, scriptContext);
- }
- Var InterpreterStackFrame::OP_LdHeapArgsCached(ScriptContext* scriptContext)
- {
- uint32 formalsCount = this->m_functionBody->GetInParamsCount() - 1;
- Var args = JavascriptOperators::LoadHeapArgsCached(this->function->GetRealFunctionObject(), this->m_inSlotsCount - 1, formalsCount, &this->m_inParams[1], this->localClosure, scriptContext, false);
- this->m_arguments = args;
- return args;
- }
- Var InterpreterStackFrame::OP_LdLetHeapArgsCached(ScriptContext* scriptContext)
- {
- uint32 formalsCount = this->m_functionBody->GetInParamsCount() - 1;
- Var args = JavascriptOperators::LoadHeapArgsCached(this->function->GetRealFunctionObject(), this->m_inSlotsCount - 1, formalsCount, &this->m_inParams[1], this->localClosure, scriptContext, true);
- this->m_arguments = args;
- return args;
- }
- HeapArgumentsObject * InterpreterStackFrame::CreateEmptyHeapArgumentsObject(ScriptContext* scriptContext)
- {
- HeapArgumentsObject * args = JavascriptOperators::CreateHeapArguments(this->function->GetRealFunctionObject(), this->m_inSlotsCount - 1, 0, nullptr, scriptContext);
- this->m_arguments = args;
- return args;
- }
- void InterpreterStackFrame::TrySetFrameObjectInHeapArgObj(ScriptContext * scriptContext, bool hasNonSimpleParams, bool isScopeObjRestored)
- {
- Var frameObject = nullptr;
- uint32 formalsCount = this->m_functionBody->GetInParamsCount() - 1;
- Js::PropertyIdArray * propIds = nullptr;
- Js::HeapArgumentsObject* heapArgObj = nullptr;
- //We always set the Frame object to nullptr in BailOutRecord::EnsureArguments for stack args optimization.
- if (m_arguments != nullptr && ((Js::HeapArgumentsObject*)(m_arguments))->GetFrameObject() == nullptr)
- {
- heapArgObj = (Js::HeapArgumentsObject*)m_arguments;
- }
- bool isCachedScope = false;
- //For Non-simple params, we don't have a scope object created.
- if (this->m_functionBody->NeedScopeObjectForArguments(hasNonSimpleParams))
- {
- frameObject = GetLocalClosure();
- isCachedScope = m_functionBody->HasCachedScopePropIds();
- propIds = this->m_functionBody->GetFormalsPropIdArray();
- if (isScopeObjRestored && ActivationObject::Is(frameObject))
- {
- Assert(this->GetFunctionBody()->GetDoScopeObjectCreation());
- isCachedScope = true;
- if (PHASE_VERBOSE_TRACE1(Js::StackArgFormalsOptPhase) && m_functionBody->GetInParamsCount() > 1)
- {
- Output::Print(_u("StackArgFormals : %s (%d) :Using the restored scope object in the bail out path. \n"), m_functionBody->GetDisplayName(), m_functionBody->GetFunctionNumber());
- Output::Flush();
- }
- }
- else
- {
- if (isCachedScope)
- {
- Field(DynamicType*) literalType = nullptr;
- Assert(!propIds->hasNonSimpleParams && !hasNonSimpleParams);
- frameObject = JavascriptOperators::OP_InitCachedScope(this->GetJavascriptFunction(), propIds, &literalType, hasNonSimpleParams, scriptContext);
- }
- else
- {
- frameObject = JavascriptOperators::OP_NewScopeObject(GetScriptContext());
- }
- Assert(propIds != nullptr);
- SetLocalClosure(frameObject);
- if (PHASE_VERBOSE_TRACE1(Js::StackArgFormalsOptPhase) && m_functionBody->GetInParamsCount() > 1)
- {
- Output::Print(_u("StackArgFormals : %s (%d) :Creating scope object in the bail out path. \n"), m_functionBody->GetDisplayName(), m_functionBody->GetFunctionNumber());
- Output::Flush();
- }
- }
- }
- else
- {
- //We reached here because, either we don't have any formals or we don't have a scope object (it could be in strict mode or have non-simple param list)
- Assert(formalsCount == 0 || (m_functionBody->GetIsStrictMode() || hasNonSimpleParams));
- frameObject = nullptr;
- formalsCount = 0;
- if (PHASE_VERBOSE_TRACE1(Js::StackArgOptPhase))
- {
- Output::Print(_u("StackArgOpt : %s (%d) :Creating NULL scope object in the bail out path. \n"), m_functionBody->GetDisplayName(), m_functionBody->GetFunctionNumber());
- Output::Flush();
- }
- }
- if (heapArgObj)
- {
- Assert(frameObject == nullptr || ActivationObject::Is(frameObject));
- heapArgObj->SetFormalCount(formalsCount);
- heapArgObj->SetFrameObject(frameObject != nullptr ?
- static_cast<ActivationObject*>(frameObject) : nullptr);
- if (PHASE_TRACE1(Js::StackArgFormalsOptPhase) && formalsCount > 0)
- {
- Output::Print(_u("StackArgFormals : %s (%d) :Attaching the scope object with the heap arguments object in the bail out path. \n"), m_functionBody->GetDisplayName(), m_functionBody->GetFunctionNumber());
- Output::Flush();
- }
- }
- //Fill the Heap arguments and scope object with values
- // If there is no heap arguments object, then fill only the scope object with actuals.
- JavascriptOperators::FillScopeObject(this->function->GetRealFunctionObject(), this->m_inSlotsCount - 1, formalsCount, frameObject, &this->m_inParams[1], propIds, heapArgObj, scriptContext, hasNonSimpleParams, isCachedScope);
- }
- Var InterpreterStackFrame::OP_LdArgumentsFromFrame()
- {
- return this->m_arguments;
- }
- void* InterpreterStackFrame::OP_LdArgCnt()
- {
- return (void*)m_inSlotsCount;
- }
- Var InterpreterStackFrame::OP_ResumeYield(Var yieldDataVar, RegSlot yieldStarIterator)
- {
- ResumeYieldData* yieldData = static_cast<ResumeYieldData*>(yieldDataVar);
- RecyclableObject* iterator = yieldStarIterator != Constants::NoRegister ? RecyclableObject::FromVar(GetNonVarReg(yieldStarIterator)) : nullptr;
- return JavascriptOperators::OP_ResumeYield(yieldData, iterator);
- }
- void* InterpreterStackFrame::operator new(size_t byteSize, void* previousAllocation) throw()
- {
- //
- // Placement 'new' is used by InterpreterStackFrame to initialize the C++ object on the RcInterpreter's
- // program stack:
- // - Unlike most other allocations, the previously allocated memory will __not__ be
- // zero-initialized, as we do not want the overhead of zero-initializing the frame when
- // calling functions.
- //
- // NOTE: If we wanted to add C# semantics of all locals are automatically zero-initialized,
- // need to determine the most efficient mechanism for this.
- //
- return previousAllocation;
- }
- void __cdecl InterpreterStackFrame::operator delete(void * allocationToFree, void * previousAllocation) throw()
- {
- AssertMsg(allocationToFree == previousAllocation, "Memory locations should match");
- AssertMsg(false, "This function should never actually be called");
- }
- void InterpreterStackFrame::OP_WasmPrintFunc(int regIndex)
- {
- #if defined(ENABLE_DEBUG_CONFIG_OPTIONS) && defined(ENABLE_WASM)
- Assert(m_functionBody->IsWasmFunction());
- uint index = GetRegRawInt(regIndex);
- Wasm::WasmFunctionInfo* info = m_functionBody->GetAsmJsFunctionInfo()->GetWebAssemblyModule()->GetWasmFunctionInfo(index);
- int col = WAsmJs::Tracing::GetPrintCol();
- if (col > 0)
- {
- Output::SkipToColumn(col);
- }
- info->GetBody()->DumpFullFunctionName();
- Output::Print(_u("("));
- #endif
- }
- JavascriptArrayBuffer* InterpreterStackFrame::GetAsmJsBuffer() const
- {
- AssertMsg(!m_functionBody->IsWasmFunction(), "Do not use GetAsmJsBuffer for WebAssembly, Use GetWebAssemblyMemory instead");
- return m_asmJsBuffer;
- }
- #ifdef ENABLE_WASM
- WebAssemblyMemory* InterpreterStackFrame::GetWebAssemblyMemory() const
- {
- AssertMsg(m_functionBody->IsWasmFunction(), "Do not use GetWebAssemblyMemory for Asm.js, Use GetAsmJsBuffer instead");
- return m_wasmMemory;
- }
- #endif
- template void* Js::InterpreterStackFrame::GetReg<unsigned int>(unsigned int) const;
- template void Js::InterpreterStackFrame::SetReg<unsigned int>(unsigned int, void*);
- } // namespace Js
- // Make sure the macro and the layout for the op is consistent
- #define DEF2(x, op, ...) \
- CompileAssert(!Js::OpCodeInfo<Js::OpCode::op>::HasMultiSizeLayout); \
- CompileAssert(!Js::OpCodeInfo<Js::OpCode::op>::IsExtendedOpcode);
- #define DEF3(x, op, ...) DEF2(x, op)
- #define EXDEF2(x, op, ...) \
- CompileAssert(!Js::OpCodeInfo<Js::OpCode::op>::HasMultiSizeLayout); \
- CompileAssert(Js::OpCodeInfo<Js::OpCode::op>::IsExtendedOpcode);
- #define EXDEF3(x, op, ...) EXDEF2(x, op)
- #define DEF2_WMS(x, op, ...) \
- CompileAssert(Js::OpCodeInfo<Js::OpCode::op>::HasMultiSizeLayout); \
- CompileAssert(!Js::OpCodeInfo<Js::OpCode::op>::IsExtendedOpcode);
- #define DEF3_WMS(x, op, ...) DEF2_WMS(x, op)
- #define EXDEF2_WMS(x, op, ...) \
- CompileAssert(Js::OpCodeInfo<Js::OpCode::op>::HasMultiSizeLayout); \
- CompileAssert(Js::OpCodeInfo<Js::OpCode::op>::IsExtendedOpcode);
- #define EXDEF3_WMS(x, op, ...) EXDEF2_WMS(x, op)
- #include "InterpreterHandler.inl"
- // Make sure the macro and the layout for the op is consistent
- #define DEF2(x, op, ...) \
- CompileAssert(!Js::OpCodeInfoAsmJs<Js::OpCodeAsmJs::op>::HasMultiSizeLayout); \
- CompileAssert(!Js::OpCodeInfoAsmJs<Js::OpCodeAsmJs::op>::IsExtendedOpcode);
- #define DEF3(x, op, ...) DEF2(x, op)
- #define DEF4(x, op, ...) DEF2(x, op)
- #define EXDEF2(x, op, ...) \
- CompileAssert(!Js::OpCodeInfoAsmJs<Js::OpCodeAsmJs::op>::HasMultiSizeLayout); \
- CompileAssert(Js::OpCodeInfoAsmJs<Js::OpCodeAsmJs::op>::IsExtendedOpcode);
- #define EXDEF3(x, op, ...) EXDEF2(x, op)
- #define EXDEF4(x, op, ...) EXDEF2(x, op)
- #define DEF2_WMS(x, op, ...) \
- CompileAssert(Js::OpCodeInfoAsmJs<Js::OpCodeAsmJs::op>::HasMultiSizeLayout); \
- CompileAssert(!Js::OpCodeInfoAsmJs<Js::OpCodeAsmJs::op>::IsExtendedOpcode);
- #define DEF3_WMS(x, op, ...) DEF2_WMS(x, op)
- #define DEF4_WMS(x, op, ...) DEF2_WMS(x, op)
- #define EXDEF2_WMS(x, op, ...) \
- CompileAssert(Js::OpCodeInfoAsmJs<Js::OpCodeAsmJs::op>::HasMultiSizeLayout); \
- CompileAssert(Js::OpCodeInfoAsmJs<Js::OpCodeAsmJs::op>::IsExtendedOpcode);
- #define EXDEF3_WMS(x, op, ...) EXDEF2_WMS(x, op)
- #define EXDEF4_WMS(x, op, ...) EXDEF2_WMS(x, op)
- #include "InterpreterHandlerAsmJs.inl"
|