| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "BackEnd.h"
- #include "SCCLiveness.h"
- #if DBG_DUMP || ENABLE_DEBUG_CONFIG_OPTIONS
- char const * const RegNames[RegNumCount] =
- {
- #define REGDAT(Name, ListName, ...) "" STRINGIZE(ListName) "",
- #include "RegList.h"
- #undef REGDAT
- };
- wchar_t const * const RegNamesW[RegNumCount] =
- {
- #define REGDAT(Name, ListName, ...) L"" STRINGIZEW(ListName) L"",
- #include "RegList.h"
- #undef REGDAT
- };
- #endif
- static const uint8 RegAttribs[RegNumCount] =
- {
- #define REGDAT(Name, ListName, Encode, Type, Attribs) Attribs,
- #include "RegList.h"
- #undef REGDAT
- };
- extern const IRType RegTypes[RegNumCount] =
- {
- #define REGDAT(Name, ListName, Encode, Type, Attribs) Type,
- #include "RegList.h"
- #undef REGDAT
- };
- LoweredBasicBlock* LoweredBasicBlock::New(JitArenaAllocator* allocator)
- {
- return JitAnew(allocator, LoweredBasicBlock, allocator);
- }
- void LoweredBasicBlock::Copy(LoweredBasicBlock* block)
- {
- this->inlineeFrameLifetimes.Copy(&block->inlineeFrameLifetimes);
- this->inlineeStack.Copy(&block->inlineeStack);
- this->inlineeFrameSyms.Copy(&block->inlineeFrameSyms);
- }
- bool LoweredBasicBlock::HasData()
- {
- return this->inlineeFrameLifetimes.Count() > 0 || this->inlineeStack.Count() > 0;
- }
- LoweredBasicBlock* LoweredBasicBlock::Clone(JitArenaAllocator* allocator)
- {
- if (this->HasData())
- {
- LoweredBasicBlock* clone = LoweredBasicBlock::New(allocator);
- clone->Copy(this);
- return clone;
- }
- return nullptr;
- }
- bool LoweredBasicBlock::Equals(LoweredBasicBlock* otherBlock)
- {
- if(this->HasData() != otherBlock->HasData())
- {
- return false;
- }
- if (!this->inlineeFrameLifetimes.Equals(&otherBlock->inlineeFrameLifetimes))
- {
- return false;
- }
- if (!this->inlineeStack.Equals(&otherBlock->inlineeStack))
- {
- return false;
- }
- return true;
- }
- // LinearScan::RegAlloc
- // This register allocator is based on the 1999 linear scan register allocation paper
- // by Poletto and Sarkar. This code however walks the IR while doing the lifetime
- // allocations, and assigns the regs to all the RegOpnd as it goes. It assumes
- // the IR is in R-DFO, and that the lifetime list is sorted in starting order.
- // Lifetimes are allocated as they become live, and retired as they go dead. RegOpnd
- // are assigned their register. If a lifetime becomes active and there are no free
- // registers left, a lifetime is picked to be spilled.
- // When we spill, the whole lifetime is spilled. All the loads and stores are done
- // through memory for that lifetime, even the ones allocated before the current instruction.
- // We do optimize this slightly by not reloading the previous loads that were not in loops.
- void
- LinearScan::RegAlloc()
- {
- NoRecoverMemoryJitArenaAllocator tempAlloc(L"BE-LinearScan", this->func->m_alloc->GetPageAllocator(), Js::Throw::OutOfMemory);
- this->tempAlloc = &tempAlloc;
- this->opHelperSpilledLiveranges = JitAnew(&tempAlloc, SList<Lifetime *>, &tempAlloc);
- this->activeLiveranges = JitAnew(&tempAlloc, SList<Lifetime *>, &tempAlloc);
- this->liveOnBackEdgeSyms = JitAnew(&tempAlloc, BVSparse<JitArenaAllocator>, &tempAlloc);
- this->stackPackInUseLiveRanges = JitAnew(&tempAlloc, SList<Lifetime *>, &tempAlloc);
- this->stackSlotsFreeList = JitAnew(&tempAlloc, SList<StackSlot *>, &tempAlloc);
- this->currentBlock = LoweredBasicBlock::New(&tempAlloc);
- IR::Instr *currentInstr = this->func->m_headInstr;
- SCCLiveness liveness(this->func, this->tempAlloc);
- BEGIN_CODEGEN_PHASE(this->func, Js::LivenessPhase);
- // Build the lifetime list
- liveness.Build();
- END_CODEGEN_PHASE(this->func, Js::LivenessPhase);
- this->lifetimeList = &liveness.lifetimeList;
- this->opHelperBlockList = &liveness.opHelperBlockList;
- this->opHelperBlockIter = SList<OpHelperBlock>::Iterator(this->opHelperBlockList);
- this->opHelperBlockIter.Next();
- this->Init();
- NativeCodeData::Allocator * nativeAllocator = this->func->GetNativeCodeDataAllocator();
- if (func->hasBailout)
- {
- this->globalBailOutRecordTables = NativeCodeDataNewArrayZ(nativeAllocator, GlobalBailOutRecordDataTable *, func->m_inlineeId + 1);
- this->lastUpdatedRowIndices = JitAnewArrayZ(this->tempAlloc, uint *, func->m_inlineeId + 1);
- #ifdef PROFILE_BAILOUT_RECORD_MEMORY
- if (Js::Configuration::Global.flags.ProfileBailOutRecordMemory)
- {
- this->func->GetScriptContext()->bailOutOffsetBytes += (sizeof(GlobalBailOutRecordDataTable *) * (func->m_inlineeId + 1));
- this->func->GetScriptContext()->bailOutRecordBytes += (sizeof(GlobalBailOutRecordDataTable *) * (func->m_inlineeId + 1));
- }
- #endif
- }
- m_bailOutRecordCount = 0;
- IR::Instr * insertBailInAfter = nullptr;
- BailOutInfo * bailOutInfoForBailIn = nullptr;
- bool endOfBasicBlock = true;
- FOREACH_INSTR_EDITING(instr, instrNext, currentInstr)
- {
- if (instr->GetNumber() == 0)
- {
- AssertMsg(LowererMD::IsAssign(instr), "Only expect spill code here");
- continue;
- }
- #if DBG_DUMP && defined(ENABLE_DEBUG_CONFIG_OPTIONS)
- if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::LinearScanPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
- {
- instr->Dump();
- }
- #endif // DBG
- this->currentInstr = instr;
- if(instr->StartsBasicBlock() || endOfBasicBlock)
- {
- endOfBasicBlock = false;
- ++currentBlockNumber;
- }
- if (instr->IsLabelInstr())
- {
- this->lastLabel = instr->AsLabelInstr();
- if (this->lastLabel->m_loweredBasicBlock)
- {
- this->currentBlock = this->lastLabel->m_loweredBasicBlock;
- }
- else if(currentBlock->HasData())
- {
- // Check if the previous block has fall-through. If so, retain the block info. If not, create empty info.
- IR::Instr *const prevInstr = instr->GetPrevRealInstrOrLabel();
- Assert(prevInstr);
- if(!prevInstr->HasFallThrough())
- {
- currentBlock = LoweredBasicBlock::New(&tempAlloc);
- }
- }
- this->currentRegion = this->lastLabel->GetRegion();
- }
- else if (instr->IsBranchInstr())
- {
- if (this->func->HasTry() && this->func->DoOptimizeTryCatch())
- {
- this->ProcessEHRegionBoundary(instr);
- }
- this->ProcessSecondChanceBoundary(instr->AsBranchInstr());
- }
- this->CheckIfInLoop(instr);
- if (this->RemoveDeadStores(instr))
- {
- continue;
- }
- if (instr->HasBailOutInfo())
- {
- if (this->currentRegion)
- {
- RegionType curRegType = this->currentRegion->GetType();
- Assert(curRegType != RegionTypeFinally); //Finally regions are not optimized yet
- if (curRegType == RegionTypeTry || curRegType == RegionTypeCatch)
- {
- this->func->hasBailoutInEHRegion = true;
- }
- }
- this->FillBailOutRecord(instr);
- if (instr->GetBailOutKind() == IR::BailOutForGeneratorYield)
- {
- Assert(instr->m_next->IsLabelInstr());
- insertBailInAfter = instr->m_next;
- bailOutInfoForBailIn = instr->GetBailOutInfo();
- }
- }
- this->SetSrcRegs(instr);
- this->EndDeadLifetimes(instr);
- this->CheckOpHelper(instr);
- this->KillImplicitRegs(instr);
- this->AllocateNewLifetimes(instr);
- this->SetDstReg(instr);
- this->EndDeadOpHelperLifetimes(instr);
- if (instr->IsLabelInstr())
- {
- this->ProcessSecondChanceBoundary(instr->AsLabelInstr());
- }
- #if DBG
- this->CheckInvariants();
- #endif // DBG
- if(instr->EndsBasicBlock())
- {
- endOfBasicBlock = true;
- }
- if (insertBailInAfter == instr)
- {
- instrNext = linearScanMD.GenerateBailInForGeneratorYield(instr, bailOutInfoForBailIn);
- insertBailInAfter = nullptr;
- bailOutInfoForBailIn = nullptr;
- }
- }NEXT_INSTR_EDITING;
- if (func->hasBailout)
- {
- for (uint i = 0; i <= func->m_inlineeId; i++)
- {
- if (globalBailOutRecordTables[i] != nullptr)
- {
- globalBailOutRecordTables[i]->Finalize(nativeAllocator, &tempAlloc);
- #ifdef PROFILE_BAILOUT_RECORD_MEMORY
- if (Js::Configuration::Global.flags.ProfileBailOutRecordMemory)
- {
- func->GetScriptContext()->bailOutOffsetBytes += sizeof(GlobalBailOutRecordDataRow) * globalBailOutRecordTables[i]->length;
- func->GetScriptContext()->bailOutRecordBytes += sizeof(GlobalBailOutRecordDataRow) * globalBailOutRecordTables[i]->length;
- }
- #endif
- }
- }
- }
- AssertMsg((this->intRegUsedCount + this->floatRegUsedCount) == this->linearScanMD.UnAllocatableRegCount(this->func) , "RegUsedCount is wrong");
- AssertMsg(this->activeLiveranges->Empty(), "Active list not empty");
- AssertMsg(this->stackPackInUseLiveRanges->Empty(), "Spilled list not empty");
- AssertMsg(!this->opHelperBlockIter.IsValid(), "Got to the end with a helper block still on the list?");
- Assert(this->currentBlock->inlineeStack.Count() == 0);
- this->InsertOpHelperSpillAndRestores();
- #if _M_IX86
- # if ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.Instrument.IsEnabled(Js::LinearScanPhase, this->func->GetJnFunction()->GetSourceContextId(),this->func->GetJnFunction()->GetLocalFunctionId()))
- {
- this->DynamicStatsInstrument();
- }
- # endif
- #endif
- #if DBG_DUMP
- if (PHASE_STATS(Js::LinearScanPhase, this->func))
- {
- this->PrintStats();
- }
- if (PHASE_TRACE(Js::StackPackPhase, this->func))
- {
- Output::Print(L"---------------------------\n");
- }
- #endif // DBG_DUMP
- DebugOnly(this->func->allowRemoveBailOutArgInstr = true);
- }
- JitArenaAllocator *
- LinearScan::GetTempAlloc()
- {
- Assert(tempAlloc);
- return tempAlloc;
- }
- #if DBG
- void
- LinearScan::CheckInvariants() const
- {
- BitVector bv = this->nonAllocatableRegs;
- uint32 lastend = 0;
- FOREACH_SLIST_ENTRY(Lifetime *, lifetime, this->activeLiveranges)
- {
- // Make sure there are only one lifetime per reg
- Assert(!bv.Test(lifetime->reg));
- bv.Set(lifetime->reg);
- Assert(!lifetime->isOpHelperSpilled);
- Assert(!lifetime->isSpilled);
- Assert(lifetime->end >= lastend);
- lastend = lifetime->end;
- }
- NEXT_SLIST_ENTRY;
- // Make sure the active reg bit vector is correct
- Assert(bv.Equal(this->activeRegs));
- uint ints = 0, floats = 0;
- FOREACH_BITSET_IN_UNITBV(index, this->activeRegs, BitVector)
- {
- if (IRType_IsFloat(RegTypes[index]))
- {
- floats++;
- }
- else
- {
- ints++;
- }
- }
- NEXT_BITSET_IN_UNITBV;
- Assert(ints == this->intRegUsedCount);
- Assert(floats == this->floatRegUsedCount);
- Assert((this->intRegUsedCount + this->floatRegUsedCount) == this->activeRegs.Count());
- bv.ClearAll();
- lastend = 0;
- FOREACH_SLIST_ENTRY(Lifetime *, lifetime, this->opHelperSpilledLiveranges)
- {
- // Make sure there are only one lifetime per reg in the op helper spilled liveranges
- Assert(!bv.Test(lifetime->reg));
- if (!lifetime->cantOpHelperSpill)
- {
- bv.Set(lifetime->reg);
- Assert(lifetime->isOpHelperSpilled);
- Assert(!lifetime->isSpilled);
- }
- Assert(lifetime->end >= lastend);
- lastend = lifetime->end;
- }
- NEXT_SLIST_ENTRY;
- // Make sure the opHelperSpilledRegs bit vector is correct
- Assert(bv.Equal(this->opHelperSpilledRegs));
- for (int i = 0; i < RegNumCount; i++)
- {
- if (this->tempRegs.Test(i))
- {
- Assert(this->tempRegLifetimes[i]->reg == i);
- }
- }
- FOREACH_BITSET_IN_UNITBV(reg, this->secondChanceRegs, BitVector)
- {
- Lifetime *lifetime = this->regContent[reg];
- Assert(lifetime);
- StackSym *sym = lifetime->sym;
- Assert(lifetime->isSecondChanceAllocated);
- Assert(sym->IsConst() || sym->IsAllocated()); // Should have been spilled already.
- } NEXT_BITSET_IN_UNITBV;
- }
- #endif // DBG
- // LinearScan::Init
- // Initialize bit vectors
- void
- LinearScan::Init()
- {
- FOREACH_REG(reg)
- {
- // Registers that can't be used are set to active, and will remain this way
- if (!LinearScan::IsAllocatable(reg))
- {
- this->activeRegs.Set(reg);
- if (IRType_IsFloat(RegTypes[reg]))
- {
- this->floatRegUsedCount++;
- }
- else
- {
- this->intRegUsedCount++;
- }
- }
- if (RegTypes[reg] == TyMachReg)
- {
- // JIT64_TODO: Rename int32Regs to machIntRegs.
- this->int32Regs.Set(reg);
- numInt32Regs++;
- }
- else if (RegTypes[reg] == TyFloat64)
- {
- this->floatRegs.Set(reg);
- numFloatRegs++;
- }
- if (LinearScan::IsCallerSaved(reg))
- {
- this->callerSavedRegs.Set(reg);
- }
- if (LinearScan::IsCalleeSaved(reg))
- {
- this->calleeSavedRegs.Set(reg);
- }
- this->regContent[reg] = nullptr;
- } NEXT_REG;
- this->instrUseRegs.ClearAll();
- this->secondChanceRegs.ClearAll();
- this->linearScanMD.Init(this);
- #if DBG
- this->nonAllocatableRegs = this->activeRegs;
- #endif
- #if DBG_DUMP
- if (PHASE_TRACE(Js::LinearScanPhase, this->func))
- {
- this->func->DumpHeader();
- }
- #endif
- }
- // LinearScan::CheckIfInLoop
- // Track whether the current instruction is in a loop or not.
- bool
- LinearScan::CheckIfInLoop(IR::Instr *instr)
- {
- if (this->IsInLoop())
- {
- // Look for end of loop
- AssertMsg(this->curLoop->regAlloc.loopEnd != 0, "Something is wrong here....");
- if (instr->GetNumber() >= this->curLoop->regAlloc.loopEnd)
- {
- AssertMsg(instr->IsBranchInstr(), "Loop tail should be a branchInstr");
- while (this->IsInLoop() && instr->GetNumber() >= this->curLoop->regAlloc.loopEnd)
- {
- this->loopNest--;
- this->curLoop->isProcessed = true;
- this->curLoop = this->curLoop->parent;
- if (this->loopNest == 0)
- {
- this->liveOnBackEdgeSyms->ClearAll();
- }
- }
- }
- }
- if (instr->IsLabelInstr() && instr->AsLabelInstr()->m_isLoopTop)
- {
- IR::LabelInstr * labelInstr = instr->AsLabelInstr();
- Loop *parentLoop = this->curLoop;
- if (parentLoop)
- {
- parentLoop->isLeaf = false;
- }
- this->curLoop = labelInstr->GetLoop();
- this->curLoop->isProcessed = false;
- // Lexically nested may not always nest in a flow based way:
- // while(i--) {
- // if (cond) {
- // while(j--) {
- // }
- // break;
- // }
- // }
- // These look nested, but they are not...
- // So update the flow based parent to be lexical or we won't be able to figure out when we get back
- // to the outer loop.
- // REVIEW: This isn't necessary anymore now that break blocks are moved out of the loops.
- this->curLoop->parent = parentLoop;
- this->curLoop->regAlloc.defdInLoopBv = JitAnew(this->tempAlloc, BVSparse<JitArenaAllocator>, this->tempAlloc);
- this->curLoop->regAlloc.symRegUseBv = JitAnew(this->tempAlloc, BVSparse<JitArenaAllocator>, this->tempAlloc);
- this->curLoop->regAlloc.loopStart = labelInstr->GetNumber();
- this->curLoop->regAlloc.exitRegContentList = JitAnew(this->tempAlloc, SList<Lifetime **>, this->tempAlloc);
- this->curLoop->regAlloc.regUseBv = 0;
- this->liveOnBackEdgeSyms->Or(this->curLoop->regAlloc.liveOnBackEdgeSyms);
- this->loopNest++;
- }
- return this->IsInLoop();
- }
- void
- LinearScan::InsertOpHelperSpillAndRestores()
- {
- linearScanMD.InsertOpHelperSpillAndRestores(opHelperBlockList);
- }
- void
- LinearScan::CheckOpHelper(IR::Instr *instr)
- {
- if (this->IsInHelperBlock())
- {
- if (this->currentOpHelperBlock->opHelperEndInstr == instr)
- {
- // Get targetInstr if we can.
- // We can deterministically get it only for unconditional branches, as conditional branch may fall through.
- IR::Instr * targetInstr = nullptr;
- if (instr->IsBranchInstr() && instr->AsBranchInstr()->IsUnconditional())
- {
- AssertMsg(!instr->AsBranchInstr()->IsMultiBranch(), "Not supported for Multibranch");
- targetInstr = instr->AsBranchInstr()->GetTarget();
- }
- /*
- * Keep track of the number of registers we've had to
- * store and restore around a helper block for LinearScanMD (on ARM
- * and X64). We need this to be able to allocate space in the frame.
- * We can't emit a PUSH/POP sequence around the block like IA32 because
- * the stack pointer can't move outside the prolog.
- */
- uint32 helperSpilledLiverangeCount = 0;
- // Exiting a helper block. We are going to insert
- // the restore here after linear scan. So put all the restored
- // lifetime back to active
- while (!this->opHelperSpilledLiveranges->Empty())
- {
- Lifetime * lifetime = this->opHelperSpilledLiveranges->Pop();
- lifetime->isOpHelperSpilled = false;
- if (!lifetime->cantOpHelperSpill)
- {
- // Put the life time back to active
- this->AssignActiveReg(lifetime, lifetime->reg);
- bool reload = true;
- // Lifetime ends before the target after helper block, don't need to save and restore helper spilled lifetime.
- if (targetInstr && lifetime->end < targetInstr->GetNumber())
- {
- // However, if lifetime is spilled as arg - we still need to spill it because the helper assumes the value
- // to be available in the stack
- if (lifetime->isOpHelperSpillAsArg)
- {
- // we should not attempt to restore it as it is dead on return from the helper.
- reload = false;
- }
- else
- {
- Assert(!instr->AsBranchInstr()->IsLoopTail(this->func));
- continue;
- }
- }
- // Save all the lifetime that needs to be restored
- OpHelperSpilledLifetime spilledLifetime;
- spilledLifetime.lifetime = lifetime;
- spilledLifetime.spillAsArg = lifetime->isOpHelperSpillAsArg;
- spilledLifetime.reload = reload;
- /*
- * Can't unfortunately move this into the else block above because we don't know if this
- * lifetime will actually get spilled until register allocation completes.
- * Instead we allocate a slot to this StackSym in LinearScanMD iff
- * !(lifetime.isSpilled && lifetime.noReloadsIfSpilled).
- */
- helperSpilledLiverangeCount++;
- // save the reg in case it is spilled later. We still need to save and restore
- // for the non-loop case.
- spilledLifetime.reg = lifetime->reg;
- this->currentOpHelperBlock->spilledLifetime.Prepend(spilledLifetime);
- }
- else
- {
- // Clear it for the next helper block
- lifetime->cantOpHelperSpill = false;
- }
- lifetime->isOpHelperSpillAsArg = false;
- }
- this->totalOpHelperFullVisitedLength += this->currentOpHelperBlock->Length();
- // Use a dummy label as the insertion point of the reloads, as second-chance-allocation
- // may insert compensation code right before the branch
- IR::PragmaInstr *dummyLabel = IR::PragmaInstr::New(Js::OpCode::Nop, 0, this->func);
- this->currentOpHelperBlock->opHelperEndInstr->InsertBefore(dummyLabel);
- dummyLabel->CopyNumber(this->currentOpHelperBlock->opHelperEndInstr);
- this->currentOpHelperBlock->opHelperEndInstr = dummyLabel;
- this->opHelperSpilledRegs.ClearAll();
- this->currentOpHelperBlock = nullptr;
- linearScanMD.EndOfHelperBlock(helperSpilledLiverangeCount);
- }
- }
- if (this->opHelperBlockIter.IsValid())
- {
- AssertMsg(
- !instr->IsLabelInstr() ||
- !instr->AsLabelInstr()->isOpHelper ||
- this->opHelperBlockIter.Data().opHelperLabel == instr,
- "Found a helper label that doesn't begin the next helper block in the list?");
- if (this->opHelperBlockIter.Data().opHelperLabel == instr)
- {
- this->currentOpHelperBlock = &this->opHelperBlockIter.Data();
- this->opHelperBlockIter.Next();
- }
- }
- }
- uint
- LinearScan::HelperBlockStartInstrNumber() const
- {
- Assert(IsInHelperBlock());
- return this->currentOpHelperBlock->opHelperLabel->GetNumber();
- }
- uint
- LinearScan::HelperBlockEndInstrNumber() const
- {
- Assert(IsInHelperBlock());
- return this->currentOpHelperBlock->opHelperEndInstr->GetNumber();
- }
- // LinearScan::AddToActive
- // Add a lifetime to the active list. The list is kept sorted in order lifetime end.
- // This makes it easier to pick the lifetimes to retire.
- void
- LinearScan::AddToActive(Lifetime * lifetime)
- {
- LinearScan::AddLiveRange(this->activeLiveranges, lifetime);
- this->regContent[lifetime->reg] = lifetime;
- if (lifetime->isSecondChanceAllocated)
- {
- this->secondChanceRegs.Set(lifetime->reg);
- }
- else
- {
- Assert(!this->secondChanceRegs.Test(lifetime->reg));
- }
- }
- void
- LinearScan::AddOpHelperSpilled(Lifetime * lifetime)
- {
- RegNum reg = lifetime->reg;
- Assert(this->IsInHelperBlock());
- Assert(!this->opHelperSpilledRegs.Test(reg));
- Assert(lifetime->isOpHelperSpilled == false);
- Assert(lifetime->cantOpHelperSpill == false);
- this->opHelperSpilledRegs.Set(reg);
- lifetime->isOpHelperSpilled = true;
- this->regContent[reg] = nullptr;
- this->secondChanceRegs.Clear(reg);
- // If a lifetime is being OpHelper spilled and it's an inlinee arg sym
- // we need to make sure its spilled to the sym offset spill space, i.e. isOpHelperSpillAsArg
- // is set. Otherwise, it's value will not be available on inline frame reconstruction.
- if (this->currentBlock->inlineeFrameSyms.Count() > 0 &&
- this->currentBlock->inlineeFrameSyms.ContainsKey(lifetime->sym->m_id) &&
- (lifetime->sym->m_isSingleDef || !lifetime->defList.Empty()))
- {
- lifetime->isOpHelperSpillAsArg = true;
- if (!lifetime->sym->IsAllocated())
- {
- this->AllocateStackSpace(lifetime);
- }
- this->RecordLoopUse(lifetime, lifetime->reg);
- }
- LinearScan::AddLiveRange(this->opHelperSpilledLiveranges, lifetime);
- }
- void
- LinearScan::RemoveOpHelperSpilled(Lifetime * lifetime)
- {
- Assert(this->IsInHelperBlock());
- Assert(lifetime->isOpHelperSpilled);
- Assert(lifetime->cantOpHelperSpill == false);
- Assert(this->opHelperSpilledRegs.Test(lifetime->reg));
- this->opHelperSpilledRegs.Clear(lifetime->reg);
- lifetime->isOpHelperSpilled = false;
- lifetime->cantOpHelperSpill = false;
- lifetime->isOpHelperSpillAsArg = false;
- this->opHelperSpilledLiveranges->Remove(lifetime);
- }
- void
- LinearScan::SetCantOpHelperSpill(Lifetime * lifetime)
- {
- Assert(this->IsInHelperBlock());
- Assert(lifetime->isOpHelperSpilled);
- Assert(lifetime->cantOpHelperSpill == false);
- this->opHelperSpilledRegs.Clear(lifetime->reg);
- lifetime->isOpHelperSpilled = false;
- lifetime->cantOpHelperSpill = true;
- }
- void
- LinearScan::AddLiveRange(SList<Lifetime *> * list, Lifetime * newLifetime)
- {
- FOREACH_SLIST_ENTRY_EDITING(Lifetime *, lifetime, list, iter)
- {
- if (newLifetime->end < lifetime->end)
- {
- break;
- }
- }
- NEXT_SLIST_ENTRY_EDITING;
- iter.InsertBefore(newLifetime);
- }
- Lifetime *
- LinearScan::RemoveRegLiveRange(SList<Lifetime *> * list, RegNum reg)
- {
- // Find the register in the active set
- FOREACH_SLIST_ENTRY_EDITING(Lifetime *, lifetime, list, iter)
- {
- if (lifetime->reg == reg)
- {
- Lifetime * lifetimeReturn = lifetime;
- iter.RemoveCurrent();
- return lifetimeReturn;
- }
- } NEXT_SLIST_ENTRY_EDITING;
- AssertMsg(false, "Can't find life range for a reg");
- return nullptr;
- }
- // LinearScan::SetDstReg
- // Set the reg on each RegOpnd def.
- void
- LinearScan::SetDstReg(IR::Instr *instr)
- {
- //
- // Enregister dst
- //
- IR::Opnd *dst = instr->GetDst();
- if (dst == nullptr)
- {
- return;
- }
- if (!dst->IsRegOpnd())
- {
- // This could be, for instance, a store to a sym with a large offset
- // that was just assigned when we saw the use.
- this->linearScanMD.LegalizeDef(instr);
- return;
- }
- IR::RegOpnd * regOpnd = dst->AsRegOpnd();
- /*
- * If this is a register used to setup a callsite per
- * a calling convention then mark it unavailable to allocate
- * until we see a CALL.
- */
- if (regOpnd->m_isCallArg)
- {
- RegNum callSetupReg = regOpnd->GetReg();
- callSetupRegs.Set(callSetupReg);
- }
- StackSym * stackSym = regOpnd->m_sym;
- // Arg slot sym can be in an RegOpnd for param passed via registers
- // Just use the assigned register
- if (stackSym == nullptr || stackSym->IsArgSlotSym())
- {
- //
- // Already allocated register. just spill the destination
- //
- RegNum reg = regOpnd->GetReg();
- if(LinearScan::IsAllocatable(reg))
- {
- this->SpillReg(reg);
- }
- this->tempRegs.Clear(reg);
- }
- else
- {
- if (regOpnd->GetReg() != RegNOREG)
- {
- this->RecordLoopUse(nullptr, regOpnd->GetReg());
- // Nothing to do
- return;
- }
- Lifetime * lifetime = stackSym->scratch.linearScan.lifetime;
- uint32 useCountCost = LinearScan::GetUseSpillCost(this->loopNest, (this->currentOpHelperBlock != nullptr));
- // Optimistically decrease the useCount. We'll undo this if we put it on the defList.
- lifetime->SubFromUseCount(useCountCost, this->curLoop);
- if (lifetime->isSpilled)
- {
- if (stackSym->IsConst() && !IsSymNonTempLocalVar(stackSym))
- {
- // We will reload the constant (but in debug mode, we still need to process this if this is a user var).
- return;
- }
- RegNum reg = regOpnd->GetReg();
- if (reg != RegNOREG)
- {
- // It is already assigned, just record it as a temp reg
- this->AssignTempReg(lifetime, reg);
- }
- else
- {
- IR::Opnd *src1 = instr->GetSrc1();
- IR::Opnd *src2 = instr->GetSrc2();
- if ((src1 && src1->IsRegOpnd() && src1->AsRegOpnd()->m_sym == stackSym) ||
- (src2 && src2->IsRegOpnd() && src2->AsRegOpnd()->m_sym == stackSym))
- {
- // OpEQ: src1 should have a valid reg (put src2 for other targets)
- reg = this->GetAssignedTempReg(lifetime, dst->GetType());
- Assert(reg != RegNOREG);
- RecordDef(lifetime, instr, 0);
- }
- else
- {
- // Try second chance
- reg = this->SecondChanceAllocation(lifetime, false);
- if (reg != RegNOREG)
- {
- Assert(!stackSym->m_isSingleDef);
- this->SetReg(regOpnd);
- // Keep track of defs for this lifetime, in case it gets spilled.
- RecordDef(lifetime, instr, useCountCost);
- return;
- }
- else
- {
- reg = this->GetAssignedTempReg(lifetime, dst->GetType());
- RecordDef(lifetime, instr, 0);
- }
- }
- if (LowererMD::IsAssign(instr) && instr->GetSrc1()->IsRegOpnd())
- {
- // Fold the spilled store
- if (reg != RegNOREG)
- {
- // If the value is in a temp reg, it's not valid any more.
- this->tempRegs.Clear(reg);
- }
- IRType srcType = instr->GetSrc1()->GetType();
- instr->ReplaceDst(IR::SymOpnd::New(stackSym, srcType, this->func));
- this->linearScanMD.LegalizeDef(instr);
- return;
- }
- if (reg == RegNOREG)
- {
- IR::Opnd *src = instr->GetSrc1();
- if (src && src->IsRegOpnd() && src->AsRegOpnd()->m_sym == stackSym)
- {
- // Handle OPEQ's for x86/x64
- reg = src->AsRegOpnd()->GetReg();
- AssertMsg(!this->activeRegs.Test(reg), "Shouldn't be active");
- }
- else
- {
- // The lifetime was spilled, but we still need a reg for this operand.
- reg = this->FindReg(nullptr, regOpnd);
- }
- this->AssignTempReg(lifetime, reg);
- }
- }
- if (!lifetime->isDeadStore && !lifetime->isSecondChanceAllocated)
- {
- // Insert a store since the lifetime is spilled
- IR::Opnd *nextDst = instr->m_next->GetDst();
- // Don't need the store however if the next instruction has the same dst
- if (nextDst == nullptr || !nextDst->IsEqual(regOpnd))
- {
- this->InsertStore(instr, regOpnd->m_sym, reg);
- }
- }
- }
- else
- {
- if (lifetime->isOpHelperSpilled)
- {
- // We must be in a helper block and the lifetime must
- // start before the helper block
- Assert(this->IsInHelperBlock());
- Assert(lifetime->start < this->HelperBlockStartInstrNumber());
- RegNum reg = lifetime->reg;
- Assert(this->opHelperSpilledRegs.Test(reg));
- if (this->activeRegs.Test(reg))
- {
- // The reg must have been used locally in the helper block
- // by some other lifetime. Just spill it
- this->SpillReg(reg);
- }
- // We can't save/restore this reg across the helper call because the restore would overwrite
- // this def, but the def means we don't need to spill at all. Mark the lifetime as cantOpHelperSpill
- // however in case another helper call in this block tries to spill it.
- this->SetCantOpHelperSpill(lifetime);
- this->AddToActive(lifetime);
- this->tempRegs.Clear(reg);
- this->activeRegs.Set(reg);
- if (RegTypes[reg] == TyMachReg)
- {
- this->intRegUsedCount++;
- }
- else
- {
- Assert(RegTypes[reg] == TyFloat64);
- this->floatRegUsedCount++;
- }
- }
- // Keep track of defs for this lifetime, in case it gets spilled.
- RecordDef(lifetime, instr, useCountCost);
- }
- this->SetReg(regOpnd);
- }
- }
- // Get the stack offset of the non temp locals from the stack.
- int32 LinearScan::GetStackOffset(Js::RegSlot regSlotId)
- {
- Assert(this->func->GetJnFunction());
- int32 stackSlotId = regSlotId - this->func->GetJnFunction()->GetFirstNonTempLocalIndex();
- Assert(stackSlotId >= 0);
- return this->func->GetLocalVarSlotOffset(stackSlotId);
- }
- //
- // This helper function is used for saving bytecode stack sym value to memory / local slots on stack so that we can read it for the locals inspection.
- void
- LinearScan::WriteThroughForLocal(IR::RegOpnd* regOpnd, Lifetime* lifetime, IR::Instr* instrInsertAfter)
- {
- Assert(regOpnd);
- Assert(lifetime);
- Assert(instrInsertAfter);
- StackSym* sym = regOpnd->m_sym;
- Assert(IsSymNonTempLocalVar(sym));
- Js::RegSlot slotIndex = sym->GetByteCodeRegSlot();
- // First we insert the write through moves
- sym->m_offset = GetStackOffset(slotIndex);
- sym->m_allocated = true;
- // Save the value on reg to local var slot.
- this->InsertStore(instrInsertAfter, sym, lifetime->reg);
- }
- bool
- LinearScan::NeedsWriteThrough(StackSym * sym)
- {
- return this->NeedsWriteThroughForEH(sym) || this->IsSymNonTempLocalVar(sym);
- }
- bool
- LinearScan::NeedsWriteThroughForEH(StackSym * sym)
- {
- if (!this->func->HasTry() || !this->func->DoOptimizeTryCatch() || !sym->HasByteCodeRegSlot())
- {
- return false;
- }
- Assert(this->currentRegion);
- return this->currentRegion->writeThroughSymbolsSet && this->currentRegion->writeThroughSymbolsSet->Test(sym->m_id);
- }
- // Helper routine to check if current sym belongs to non temp bytecodereg
- bool
- LinearScan::IsSymNonTempLocalVar(StackSym *sym)
- {
- Assert(sym);
- if (this->func->IsJitInDebugMode() && sym->HasByteCodeRegSlot())
- {
- Js::RegSlot slotIndex = sym->GetByteCodeRegSlot();
- return this->func->IsNonTempLocalVar(slotIndex);
- }
- return false;
- }
- // LinearScan::SetSrcRegs
- // Set the reg on each RegOpnd use.
- // Note that this includes regOpnd of indir dsts...
- void
- LinearScan::SetSrcRegs(IR::Instr *instr)
- {
- //
- // Enregister srcs
- //
- IR::Opnd *src1 = instr->GetSrc1();
- if (src1 != nullptr)
- {
- // Capture src2 now as folding in SetUses could swab the srcs...
- IR::Opnd *src2 = instr->GetSrc2();
- this->SetUses(instr, src1);
- if (src2 != nullptr)
- {
- this->SetUses(instr, src2);
- }
- }
- IR::Opnd *dst = instr->GetDst();
- if (dst && dst->IsIndirOpnd())
- {
- this->SetUses(instr, dst);
- }
- this->instrUseRegs.ClearAll();
- }
- // LinearScan::SetUses
- void
- LinearScan::SetUses(IR::Instr *instr, IR::Opnd *opnd)
- {
- switch (opnd->GetKind())
- {
- case IR::OpndKindReg:
- this->SetUse(instr, opnd->AsRegOpnd());
- break;
- case IR::OpndKindSym:
- {
- Sym * sym = opnd->AsSymOpnd()->m_sym;
- if (sym->IsStackSym())
- {
- StackSym* stackSym = sym->AsStackSym();
- if (!stackSym->IsAllocated())
- {
- func->StackAllocate(stackSym, opnd->GetSize());
- // StackSym's lifetime is allocated during SCCLiveness::ProcessDst
- // we might not need to set the flag if the sym is not a dst.
- if (stackSym->scratch.linearScan.lifetime)
- {
- stackSym->scratch.linearScan.lifetime->cantStackPack = true;
- }
- }
- this->linearScanMD.LegalizeUse(instr, opnd);
- }
- }
- break;
- case IR::OpndKindIndir:
- {
- IR::IndirOpnd * indirOpnd = opnd->AsIndirOpnd();
- this->SetUse(instr, indirOpnd->GetBaseOpnd());
- if (indirOpnd->GetIndexOpnd())
- {
- this->SetUse(instr, indirOpnd->GetIndexOpnd());
- }
- }
- break;
- case IR::OpndKindIntConst:
- case IR::OpndKindAddr:
- this->linearScanMD.LegalizeConstantUse(instr, opnd);
- break;
- };
- }
- struct FillBailOutState
- {
- SListCounted<Js::Var> constantList;
- uint registerSaveCount;
- StackSym * registerSaveSyms[RegNumCount - 1];
- FillBailOutState(JitArenaAllocator * allocator) : constantList(allocator) {}
- };
- void
- LinearScan::FillBailOutOffset(int * offset, StackSym * stackSym, FillBailOutState * state, IR::Instr * instr)
- {
- AssertMsg(*offset == 0, "Can't have two active lifetime for the same byte code register");
- if (stackSym->IsConst())
- {
- state->constantList.Prepend(reinterpret_cast<Js::Var>(stackSym->GetLiteralConstValue_PostGlobOpt()));
- // Constant offset are offset by the number of register save slots
- *offset = state->constantList.Count() + GetBailOutRegisterSaveSlotCount() + GetBailOutReserveSlotCount();
- }
- else if (stackSym->m_isEncodedConstant)
- {
- Assert(!stackSym->m_isSingleDef);
- state->constantList.Prepend((Js::Var)stackSym->constantValue);
- // Constant offset are offset by the number of register save slots
- *offset = state->constantList.Count() + GetBailOutRegisterSaveSlotCount() + GetBailOutReserveSlotCount();
- }
- else
- {
- Lifetime * lifetime = stackSym->scratch.linearScan.lifetime;
- Assert(lifetime && lifetime->start < instr->GetNumber() && instr->GetNumber() <= lifetime->end);
- if (instr->GetBailOutKind() == IR::BailOutOnException)
- {
- // Apart from the exception object sym, lifetimes for all other syms that need to be restored at this bailout,
- // must have been spilled at least once (at the TryCatch, or at the Leave, or both)
- // Post spilling, a lifetime could have been second chance allocated. But, it should still have stack allocated for its sym
- Assert(stackSym->IsAllocated() || (stackSym == this->currentRegion->GetExceptionObjectSym()));
- }
- this->PrepareForUse(lifetime);
- if (lifetime->isSpilled ||
- ((instr->GetBailOutKind() == IR::BailOutOnException) && (stackSym != this->currentRegion->GetExceptionObjectSym()))) // BailOutOnException must restore from memory
- {
- Assert(stackSym->IsAllocated());
- #ifdef MD_GROW_LOCALS_AREA_UP
- *offset = -((int)stackSym->m_offset + BailOutInfo::StackSymBias);
- #else
- // Stack offset are negative, includes the PUSH EBP and return address
- *offset = stackSym->m_offset - (2 * MachPtr);
- #endif
- }
- else
- {
- Assert(lifetime->reg != RegNOREG);
- Assert(state->registerSaveSyms[lifetime->reg - 1] == nullptr ||
- state->registerSaveSyms[lifetime->reg - 1] == stackSym);
- AssertMsg((stackSym->IsFloat64() || stackSym->IsSimd128()) && RegTypes[lifetime->reg] == TyFloat64 ||
- !(stackSym->IsFloat64() || stackSym->IsSimd128()) && RegTypes[lifetime->reg] != TyFloat64,
- "Trying to save float64 sym into non-float64 reg or non-float64 sym into float64 reg");
- // Save the register value to the register save space using the reg enum value as index
- state->registerSaveSyms[lifetime->reg - 1] = stackSym;
- *offset = LinearScanMD::GetRegisterSaveIndex(lifetime->reg);
- state->registerSaveCount++;
- }
- }
- }
- struct FuncBailOutData
- {
- Func * func;
- BailOutRecord * bailOutRecord;
- int * localOffsets;
- BVFixed * losslessInt32Syms;
- BVFixed * float64Syms;
- // SIMD_JS
- BVFixed * simd128F4Syms;
- BVFixed * simd128I4Syms;
- void Initialize(Func * func, JitArenaAllocator * tempAllocator);
- void FinalizeLocalOffsets(JitArenaAllocator *allocator, GlobalBailOutRecordDataTable *table, uint **lastUpdatedRowIndices);
- void Clear(JitArenaAllocator * tempAllocator);
- };
- void
- FuncBailOutData::Initialize(Func * func, JitArenaAllocator * tempAllocator)
- {
- Js::RegSlot localsCount = func->GetJnFunction()->GetLocalsCount();
- this->func = func;
- this->localOffsets = AnewArrayZ(tempAllocator, int, localsCount);
- this->losslessInt32Syms = BVFixed::New(localsCount, tempAllocator);
- this->float64Syms = BVFixed::New(localsCount, tempAllocator);
- // SIMD_JS
- this->simd128F4Syms = BVFixed::New(localsCount, tempAllocator);
- this->simd128I4Syms = BVFixed::New(localsCount, tempAllocator);
- }
- void
- FuncBailOutData::FinalizeLocalOffsets(JitArenaAllocator *allocator, GlobalBailOutRecordDataTable *globalBailOutRecordDataTable, uint **lastUpdatedRowIndices)
- {
- Js::RegSlot localsCount = func->GetJnFunction()->GetLocalsCount();
- Assert(globalBailOutRecordDataTable != nullptr);
- Assert(lastUpdatedRowIndices != nullptr);
- if (*lastUpdatedRowIndices == nullptr)
- {
- *lastUpdatedRowIndices = JitAnewArrayZ(allocator, uint, localsCount);
- memset(*lastUpdatedRowIndices, -1, sizeof(uint)*localsCount);
- }
- uint32 bailOutRecordId = bailOutRecord->m_bailOutRecordId;
- bailOutRecord->localOffsetsCount = 0;
- for (uint32 i = 0; i < localsCount; i++)
- {
- // if the sym is live
- if (localOffsets[i] != 0)
- {
- bool isFloat = float64Syms->Test(i) != 0;
- bool isInt = losslessInt32Syms->Test(i) != 0;
- // SIMD_JS
- bool isSimd128F4 = simd128F4Syms->Test(i) != 0;
- bool isSimd128I4 = simd128I4Syms->Test(i) != 0;
- globalBailOutRecordDataTable->AddOrUpdateRow(allocator, bailOutRecordId, i, isFloat, isInt, isSimd128F4, isSimd128I4, localOffsets[i], &((*lastUpdatedRowIndices)[i]));
- Assert(globalBailOutRecordDataTable->globalBailOutRecordDataRows[(*lastUpdatedRowIndices)[i]].regSlot == i);
- bailOutRecord->localOffsetsCount++;
- }
- }
- }
- void
- FuncBailOutData::Clear(JitArenaAllocator * tempAllocator)
- {
- Js::RegSlot localsCount = func->GetJnFunction()->GetLocalsCount();
- JitAdeleteArray(tempAllocator, localsCount, localOffsets);
- losslessInt32Syms->Delete(tempAllocator);
- float64Syms->Delete(tempAllocator);
- // SIMD_JS
- simd128F4Syms->Delete(tempAllocator);
- simd128I4Syms->Delete(tempAllocator);
- }
- GlobalBailOutRecordDataTable *
- LinearScan::EnsureGlobalBailOutRecordTable(Func *func)
- {
- Assert(globalBailOutRecordTables != nullptr);
- Func *topFunc = func->GetTopFunc();
- bool isTopFunc = (func == topFunc);
- uint32 inlineeID = isTopFunc ? 0 : func->m_inlineeId;
- NativeCodeData::Allocator * allocator = this->func->GetNativeCodeDataAllocator();
- GlobalBailOutRecordDataTable *globalBailOutRecordDataTable = globalBailOutRecordTables[inlineeID];
- if (globalBailOutRecordDataTable == nullptr)
- {
- globalBailOutRecordDataTable = globalBailOutRecordTables[inlineeID] = NativeCodeDataNew(allocator, GlobalBailOutRecordDataTable);
- globalBailOutRecordDataTable->length = globalBailOutRecordDataTable->size = 0;
- globalBailOutRecordDataTable->isInlinedFunction = !isTopFunc;
- globalBailOutRecordDataTable->isInlinedConstructor = func->IsInlinedConstructor();
- globalBailOutRecordDataTable->isLoopBody = topFunc->IsLoopBody();
- globalBailOutRecordDataTable->returnValueRegSlot = func->returnValueRegSlot;
- globalBailOutRecordDataTable->firstActualStackOffset = -1;
- globalBailOutRecordDataTable->registerSaveSpace = func->GetScriptContext()->GetThreadContext()->GetBailOutRegisterSaveSpace();
- globalBailOutRecordDataTable->globalBailOutRecordDataRows = nullptr;
- #ifdef PROFILE_BAILOUT_RECORD_MEMORY
- if (Js::Configuration::Global.flags.ProfileBailOutRecordMemory)
- {
- topFunc->GetScriptContext()->bailOutOffsetBytes += sizeof(GlobalBailOutRecordDataTable);
- topFunc->GetScriptContext()->bailOutRecordBytes += sizeof(GlobalBailOutRecordDataTable);
- }
- #endif
- }
- return globalBailOutRecordDataTable;
- }
- void
- LinearScan::FillBailOutRecord(IR::Instr * instr)
- {
- BailOutInfo * bailOutInfo = instr->GetBailOutInfo();
- if (this->func->HasTry())
- {
- RegionType currentRegionType = this->currentRegion->GetType();
- if (currentRegionType == RegionTypeTry || currentRegionType == RegionTypeCatch)
- {
- bailOutInfo->bailOutRecord->ehBailoutData = this->currentRegion->ehBailoutData;
- }
- }
- BVSparse<JitArenaAllocator> * byteCodeUpwardExposedUsed = bailOutInfo->byteCodeUpwardExposedUsed;
- Func * bailOutFunc = bailOutInfo->bailOutFunc;
- uint funcCount = bailOutFunc->inlineDepth + 1;
- FuncBailOutData * funcBailOutData = AnewArray(this->tempAlloc, FuncBailOutData, funcCount);
- uint index = funcCount - 1;
- funcBailOutData[index].Initialize(bailOutFunc, this->tempAlloc);
- funcBailOutData[index].bailOutRecord = bailOutInfo->bailOutRecord;
- bailOutInfo->bailOutRecord->m_bailOutRecordId = m_bailOutRecordCount++;
- bailOutInfo->bailOutRecord->globalBailOutRecordTable = EnsureGlobalBailOutRecordTable(bailOutFunc);
- NativeCodeData::Allocator * allocator = this->func->GetNativeCodeDataAllocator();
- #if DBG_DUMP
- if(PHASE_DUMP(Js::BailOutPhase, this->func))
- {
- Output::Print(L"-------------------Bailout dump -------------------------\n");
- instr->Dump();
- }
- #endif
- // Generate chained bailout record for inlined functions
- Func * currentFunc = bailOutFunc->GetParentFunc();
- uint bailOutOffset = bailOutFunc->postCallByteCodeOffset;
- while (currentFunc != nullptr)
- {
- Assert(index > 0);
- Assert(bailOutOffset != Js::Constants::NoByteCodeOffset);
- BailOutRecord * bailOutRecord = NativeCodeDataNewZ(allocator, BailOutRecord, bailOutOffset, (uint)-1, IR::BailOutInvalid, currentFunc);
- bailOutRecord->m_bailOutRecordId = m_bailOutRecordCount++;
- bailOutRecord->globalBailOutRecordTable = EnsureGlobalBailOutRecordTable(currentFunc);
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- // To indicate this is a subsequent bailout from an inlinee
- bailOutRecord->bailOutOpcode = Js::OpCode::InlineeEnd;
- #endif
- funcBailOutData[index].bailOutRecord->parent = bailOutRecord;
- index--;
- funcBailOutData[index].bailOutRecord = bailOutRecord;
- funcBailOutData[index].Initialize(currentFunc, this->tempAlloc);
- bailOutOffset = currentFunc->postCallByteCodeOffset;
- currentFunc = currentFunc->GetParentFunc();
- }
- Assert(index == 0);
- Assert(bailOutOffset == Js::Constants::NoByteCodeOffset);
- FillBailOutState state(this->tempAlloc);
- state.registerSaveCount = 0;
- memset(state.registerSaveSyms, 0, sizeof(state.registerSaveSyms));
- // Fill in the constants
- FOREACH_SLISTBASE_ENTRY_EDITING(ConstantStackSymValue, value, &bailOutInfo->usedCapturedValues.constantValues, iterator)
- {
- AssertMsg(bailOutInfo->bailOutRecord->bailOutKind != IR::BailOutForGeneratorYield, "constant prop syms unexpected for bail-in for generator yield");
- StackSym * stackSym = value.Key();
- if(stackSym->HasArgSlotNum())
- {
- continue;
- }
- Assert(stackSym->HasByteCodeRegSlot());
- Js::RegSlot i = stackSym->GetByteCodeRegSlot();
- Func * stackSymFunc = stackSym->GetByteCodeFunc();
- uint index = stackSymFunc->inlineDepth;
- Assert(i != Js::Constants::NoRegister);
- Assert(i < stackSymFunc->GetJnFunction()->GetLocalsCount());
- Assert(index < funcCount);
- __analysis_assume(index < funcCount);
- Assert(funcBailOutData[index].func == stackSymFunc);
- Assert(!byteCodeUpwardExposedUsed->Test(stackSym->m_id));
- BailoutConstantValue constValue = value.Value();
- Js::Var varValue = constValue.ToVar(this->func, stackSymFunc->GetScriptContext());
- state.constantList.Prepend(varValue);
- AssertMsg(funcBailOutData[index].localOffsets[i] == 0, "Can't have two active lifetime for the same byte code register");
- // Constant offset are offset by the number of register save slots
- funcBailOutData[index].localOffsets[i] = state.constantList.Count() + GetBailOutRegisterSaveSlotCount() + GetBailOutReserveSlotCount();
- #if DBG_DUMP
- if(PHASE_DUMP(Js::BailOutPhase, this->func))
- {
- Output::Print(L"Constant stack sym #%d (argOut:%s): ", i, IsTrueOrFalse(stackSym->HasArgSlotNum()));
- stackSym->Dump();
- Output::Print(L" (0x%p (Var) Offset: %d)\n", varValue, funcBailOutData[index].localOffsets[i]);
- }
- #endif
- iterator.RemoveCurrent(this->func->m_alloc);
- }
- NEXT_SLISTBASE_ENTRY_EDITING;
- // Fill in the copy prop syms
- FOREACH_SLISTBASE_ENTRY_EDITING(CopyPropSyms, copyPropSyms, &bailOutInfo->usedCapturedValues.copyPropSyms, iter)
- {
- AssertMsg(bailOutInfo->bailOutRecord->bailOutKind != IR::BailOutForGeneratorYield, "copy prop syms unexpected for bail-in for generator yield");
- StackSym * stackSym = copyPropSyms.Key();
- if(stackSym->HasArgSlotNum())
- {
- continue;
- }
- Js::RegSlot i = stackSym->GetByteCodeRegSlot();
- Func * stackSymFunc = stackSym->GetByteCodeFunc();
- uint index = stackSymFunc->inlineDepth;
- Assert(i != Js::Constants::NoRegister);
- Assert(i < stackSymFunc->GetJnFunction()->GetLocalsCount());
- Assert(index < funcCount);
- __analysis_assume(index < funcCount);
- Assert(funcBailOutData[index].func == stackSymFunc);
- AssertMsg(funcBailOutData[index].localOffsets[i] == 0, "Can't have two active lifetime for the same byte code register");
- Assert(!byteCodeUpwardExposedUsed->Test(stackSym->m_id));
- StackSym * copyStackSym = copyPropSyms.Value();
- this->FillBailOutOffset(&funcBailOutData[index].localOffsets[i], copyStackSym, &state, instr);
- if (copyStackSym->IsInt32())
- {
- funcBailOutData[index].losslessInt32Syms->Set(i);
- }
- else if (copyStackSym->IsFloat64())
- {
- funcBailOutData[index].float64Syms->Set(i);
- }
- // SIMD_JS
- else if (copyStackSym->IsSimd128F4())
- {
- funcBailOutData[index].simd128F4Syms->Set(i);
- }
- else if (copyStackSym->IsSimd128I4())
- {
- funcBailOutData[index].simd128I4Syms->Set(i);
- }
- iter.RemoveCurrent(this->func->m_alloc);
- }
- NEXT_SLISTBASE_ENTRY_EDITING;
- // Fill in the upward exposed syms
- FOREACH_BITSET_IN_SPARSEBV(id, byteCodeUpwardExposedUsed)
- {
- StackSym * stackSym = this->func->m_symTable->FindStackSym(id);
- Assert(stackSym != nullptr);
- Js::RegSlot i = stackSym->GetByteCodeRegSlot();
- Func * stackSymFunc = stackSym->GetByteCodeFunc();
- uint index = stackSymFunc->inlineDepth;
- Assert(i != Js::Constants::NoRegister);
- Assert(i < stackSymFunc->GetJnFunction()->GetLocalsCount());
- Assert(index < funcCount);
- __analysis_assume(index < funcCount);
- Assert(funcBailOutData[index].func == stackSymFunc);
- AssertMsg(funcBailOutData[index].localOffsets[i] == 0, "Can't have two active lifetime for the same byte code register");
- this->FillBailOutOffset(&funcBailOutData[index].localOffsets[i], stackSym, &state, instr);
- if (stackSym->IsInt32())
- {
- funcBailOutData[index].losslessInt32Syms->Set(i);
- }
- else if (stackSym->IsFloat64())
- {
- funcBailOutData[index].float64Syms->Set(i);
- }
- // SIMD_JS
- else if (stackSym->IsSimd128F4())
- {
- funcBailOutData[index].simd128F4Syms->Set(i);
- }
- else if (stackSym->IsSimd128I4())
- {
- funcBailOutData[index].simd128I4Syms->Set(i);
- }
- }
- NEXT_BITSET_IN_SPARSEBV;
- if (bailOutInfo->usedCapturedValues.argObjSyms)
- {
- FOREACH_BITSET_IN_SPARSEBV(id, bailOutInfo->usedCapturedValues.argObjSyms)
- {
- StackSym * stackSym = this->func->m_symTable->FindStackSym(id);
- Assert(stackSym != nullptr);
- Js::RegSlot i = stackSym->GetByteCodeRegSlot();
- Func * stackSymFunc = stackSym->GetByteCodeFunc();
- uint index = stackSymFunc->inlineDepth;
- Assert(i != Js::Constants::NoRegister);
- Assert(i < stackSymFunc->GetJnFunction()->GetLocalsCount());
- Assert(index < funcCount);
- __analysis_assume(index < funcCount);
- Assert(funcBailOutData[index].func == stackSymFunc);
- AssertMsg(funcBailOutData[index].localOffsets[i] == 0, "Can't have two active lifetime for the same byte code register");
- funcBailOutData[index].localOffsets[i] = BailOutRecord::GetArgumentsObjectOffset();
- }
- NEXT_BITSET_IN_SPARSEBV;
- }
- // In the debug mode, fill in the rest of non temp locals as well in the records so that the restore stub will just get it automatically.
- if (this->func->IsJitInDebugMode())
- {
- Js::FunctionBody* functionBody = this->func->GetJnFunction();
- Assert(functionBody);
- // Need to allow filling the formal args slots.
- Js::PropertyIdOnRegSlotsContainer *propertyIdContainer = functionBody->GetPropertyIdOnRegSlotsContainer();
- bool hasFormalArgs = propertyIdContainer != nullptr && propertyIdContainer->propertyIdsForFormalArgs != nullptr;
- if (hasFormalArgs)
- {
- for (uint32 index = functionBody->GetFirstNonTempLocalIndex(); index < functionBody->GetEndNonTempLocalIndex(); index++)
- {
- StackSym * stackSym = this->func->m_symTable->FindStackSym(index);
- if (stackSym != nullptr)
- {
- Func * stackSymFunc = stackSym->GetByteCodeFunc();
- Js::RegSlot regSlotId = stackSym->GetByteCodeRegSlot();
- if (functionBody->IsNonTempLocalVar(regSlotId))
- {
- if (!propertyIdContainer->IsRegSlotFormal(regSlotId - functionBody->GetFirstNonTempLocalIndex()))
- {
- continue;
- }
- uint dataIndex = stackSymFunc->inlineDepth;
- Assert(dataIndex == 0); // There is no inlining while in debug mode
- // Filling in which are not filled already.
- __analysis_assume(dataIndex == 0);
- if (funcBailOutData[dataIndex].localOffsets[regSlotId] == 0)
- {
- int32 offset = GetStackOffset(regSlotId);
- #ifdef MD_GROW_LOCALS_AREA_UP
- Assert(offset >= 0);
- #else
- Assert(offset < 0);
- #endif
- funcBailOutData[dataIndex].localOffsets[regSlotId] = this->func->AdjustOffsetValue(offset);
- // We don't support typespec for debug, rework on the bellow assert once we start support them.
- Assert(!stackSym->IsInt32() && !stackSym->IsFloat64() && !stackSym->IsSimd128());
- }
- }
- }
- }
- }
- }
- // fill in the out params
- uint startCallCount = bailOutInfo->startCallCount;
- if (bailOutInfo->totalOutParamCount != 0)
- {
- Assert(startCallCount != 0);
- uint argOutSlot = 0;
- uint * startCallOutParamCounts = NativeCodeDataNewArray(allocator, uint, startCallCount);
- #ifdef _M_IX86
- uint * startCallArgRestoreAdjustCounts = NativeCodeDataNewArray(allocator, uint, startCallCount);
- #endif
- BVFixed * argOutFloat64Syms = BVFixed::New(bailOutInfo->totalOutParamCount, allocator);
- BVFixed * argOutLosslessInt32Syms = BVFixed::New(bailOutInfo->totalOutParamCount, allocator);
- // SIMD_JS
- BVFixed * argOutSimd128F4Syms = BVFixed::New(bailOutInfo->totalOutParamCount, allocator);
- BVFixed * argOutSimd128I4Syms = BVFixed::New(bailOutInfo->totalOutParamCount, allocator);
- int* outParamOffsets = bailOutInfo->outParamOffsets = NativeCodeDataNewArrayZ(allocator, int, bailOutInfo->totalOutParamCount);
- #ifdef _M_IX86
- int currentStackOffset = 0;
- bailOutInfo->outParamFrameAdjustArgSlot = JitAnew(this->func->m_alloc, BVSparse<JitArenaAllocator>, this->func->m_alloc);
- #endif
- if (this->func->HasInlinee())
- {
- bailOutInfo->outParamInlinedArgSlot = JitAnew(this->func->m_alloc, BVSparse<JitArenaAllocator>, this->func->m_alloc);
- }
- #if DBG
- uint lastFuncIndex = 0;
- #endif
- for (uint i = 0; i < startCallCount; i++)
- {
- uint outParamStart = argOutSlot; // Start of the out param offset for the current start call
- // Number of out param for the current start call
- uint outParamCount = bailOutInfo->GetStartCallOutParamCount(i);
- startCallOutParamCounts[i] = outParamCount;
- #ifdef _M_IX86
- startCallArgRestoreAdjustCounts[i] = bailOutInfo->startCallInfo[i].argRestoreAdjustCount;
- // Only x86 has a progression of pushes of out args, with stack alignment.
- bool fDoStackAdjust = false;
- if (!bailOutInfo->inlinedStartCall->Test(i))
- {
- // Only do the stack adjustment if the StartCall has not been moved down past the bailout.
- fDoStackAdjust = bailOutInfo->NeedsStartCallAdjust(i, instr);
- if (fDoStackAdjust)
- {
- currentStackOffset -= Math::Align<int>(outParamCount * MachPtr, MachStackAlignment);
- }
- }
- #endif
- Func * currentStartCallFunc = bailOutInfo->startCallFunc[i];
- #if DBG
- Assert(lastFuncIndex <= currentStartCallFunc->inlineDepth);
- lastFuncIndex = currentStartCallFunc->inlineDepth;
- #endif
- FuncBailOutData& currentFuncBailOutData = funcBailOutData[currentStartCallFunc->inlineDepth];
- BailOutRecord * currentBailOutRecord = currentFuncBailOutData.bailOutRecord;
- if (currentBailOutRecord->argOutOffsetInfo == nullptr)
- {
- currentBailOutRecord->argOutOffsetInfo = NativeCodeDataNew(allocator, BailOutRecord::ArgOutOffsetInfo);
- currentBailOutRecord->argOutOffsetInfo->argOutFloat64Syms = nullptr;
- currentBailOutRecord->argOutOffsetInfo->argOutLosslessInt32Syms = nullptr;
- // SIMD_JS
- currentBailOutRecord->argOutOffsetInfo->argOutSimd128F4Syms = nullptr;
- currentBailOutRecord->argOutOffsetInfo->argOutSimd128I4Syms = nullptr;
- currentBailOutRecord->argOutOffsetInfo->argOutSymStart = 0;
- currentBailOutRecord->argOutOffsetInfo->outParamOffsets = nullptr;
- currentBailOutRecord->argOutOffsetInfo->startCallOutParamCounts = nullptr;
- #ifdef PROFILE_BAILOUT_RECORD_MEMORY
- if (Js::Configuration::Global.flags.ProfileBailOutRecordMemory)
- {
- this->func->GetScriptContext()->bailOutRecordBytes += sizeof(BailOutRecord::ArgOutOffsetInfo);
- }
- #endif
- }
- currentBailOutRecord->argOutOffsetInfo->startCallCount++;
- if (currentBailOutRecord->argOutOffsetInfo->outParamOffsets == nullptr)
- {
- Assert(currentBailOutRecord->argOutOffsetInfo->startCallOutParamCounts == nullptr);
- currentBailOutRecord->argOutOffsetInfo->startCallOutParamCounts = &startCallOutParamCounts[i];
- #ifdef _M_IX86
- currentBailOutRecord->startCallArgRestoreAdjustCounts = &startCallArgRestoreAdjustCounts[i];
- #endif
- currentBailOutRecord->argOutOffsetInfo->outParamOffsets = &outParamOffsets[outParamStart];
- currentBailOutRecord->argOutOffsetInfo->argOutSymStart = outParamStart;
- currentBailOutRecord->argOutOffsetInfo->argOutFloat64Syms = argOutFloat64Syms;
- currentBailOutRecord->argOutOffsetInfo->argOutLosslessInt32Syms = argOutLosslessInt32Syms;
- // SIMD_JS
- currentBailOutRecord->argOutOffsetInfo->argOutSimd128F4Syms = argOutSimd128F4Syms;
- currentBailOutRecord->argOutOffsetInfo->argOutSimd128I4Syms = argOutSimd128I4Syms;
- }
- #if DBG_DUMP
- if (PHASE_DUMP(Js::BailOutPhase, this->func))
- {
- Output::Print(L"Bailout function: %s [#%d] \n", currentStartCallFunc->GetJnFunction()->GetDisplayName(),
- currentStartCallFunc->GetJnFunction()->GetFunctionNumber());
- }
- #endif
- for (uint j = 0; j < outParamCount; j++, argOutSlot++)
- {
- StackSym * sym = bailOutInfo->argOutSyms[argOutSlot];
- if (sym == nullptr)
- {
- // This can happen when instr with bailout occurs before all ArgOuts for current call instr are processed.
- continue;
- }
- Assert(sym->GetArgSlotNum() > 0 && sym->GetArgSlotNum() <= outParamCount);
- uint argSlot = sym->GetArgSlotNum() - 1;
- uint outParamOffsetIndex = outParamStart + argSlot;
- if (!sym->m_isBailOutReferenced && !sym->IsArgSlotSym())
- {
- FOREACH_SLISTBASE_ENTRY_EDITING(ConstantStackSymValue, constantValue, &bailOutInfo->usedCapturedValues.constantValues, iterator)
- {
- if (constantValue.Key()->m_id == sym->m_id)
- {
- Js::Var varValue = constantValue.Value().ToVar(func, currentStartCallFunc->GetScriptContext());
- state.constantList.Prepend(varValue);
- outParamOffsets[outParamOffsetIndex] = state.constantList.Count() + GetBailOutRegisterSaveSlotCount() + GetBailOutReserveSlotCount();
- #if DBG_DUMP
- if (PHASE_DUMP(Js::BailOutPhase, this->func))
- {
- Output::Print(L"OutParam #%d: ", argSlot);
- sym->Dump();
- Output::Print(L" (0x%p (Var)))\n", varValue);
- }
- #endif
- iterator.RemoveCurrent(func->m_alloc);
- break;
- }
- }
- NEXT_SLISTBASE_ENTRY_EDITING;
- if (outParamOffsets[outParamOffsetIndex])
- {
- continue;
- }
- FOREACH_SLISTBASE_ENTRY_EDITING(CopyPropSyms, copyPropSym, &bailOutInfo->usedCapturedValues.copyPropSyms, iter)
- {
- if (copyPropSym.Key()->m_id == sym->m_id)
- {
- StackSym * copyStackSym = copyPropSym.Value();
- BVSparse<JitArenaAllocator>* argObjSyms = bailOutInfo->usedCapturedValues.argObjSyms;
- if (argObjSyms && argObjSyms->Test(copyStackSym->m_id))
- {
- outParamOffsets[outParamOffsetIndex] = BailOutRecord::GetArgumentsObjectOffset();
- }
- else
- {
- this->FillBailOutOffset(&outParamOffsets[outParamOffsetIndex], copyStackSym, &state, instr);
- if (copyStackSym->IsInt32())
- {
- argOutLosslessInt32Syms->Set(outParamOffsetIndex);
- }
- else if (copyStackSym->IsFloat64())
- {
- argOutFloat64Syms->Set(outParamOffsetIndex);
- }
- // SIMD_JS
- else if (copyStackSym->IsSimd128F4())
- {
- argOutSimd128F4Syms->Set(outParamOffsetIndex);
- }
- else if (copyStackSym->IsSimd128I4())
- {
- argOutSimd128I4Syms->Set(outParamOffsetIndex);
- }
- }
- #if DBG_DUMP
- if (PHASE_DUMP(Js::BailOutPhase, this->func))
- {
- Output::Print(L"OutParam #%d: ", argSlot);
- sym->Dump();
- Output::Print(L" Copy Prop sym:");
- copyStackSym->Dump();
- Output::Print(L"\n");
- }
- #endif
- iter.RemoveCurrent(func->m_alloc);
- break;
- }
- }
- NEXT_SLISTBASE_ENTRY_EDITING;
- Assert(outParamOffsets[outParamOffsetIndex] != 0);
- }
- else
- {
- if (sym->IsArgSlotSym())
- {
- if (sym->m_isSingleDef)
- {
- Assert(sym->m_instrDef->m_func == currentStartCallFunc);
- IR::Instr * instrDef = sym->m_instrDef;
- Assert(LowererMD::IsAssign(instrDef));
- if (instrDef->GetNumber() < instr->GetNumber())
- {
- // The ArgOut instr is above current bailout instr.
- AssertMsg(sym->IsVar(), "Arg out slot can only be var.");
- if (sym->m_isInlinedArgSlot)
- {
- Assert(this->func->HasInlinee());
- #ifdef MD_GROW_LOCALS_AREA_UP
- outParamOffsets[outParamOffsetIndex] = -((int)sym->m_offset + BailOutInfo::StackSymBias);
- #else
- outParamOffsets[outParamOffsetIndex] = sym->m_offset;
- #endif
- bailOutInfo->outParamInlinedArgSlot->Set(outParamOffsetIndex);
- }
- else if (sym->m_isOrphanedArg)
- {
- #ifdef MD_GROW_LOCALS_AREA_UP
- outParamOffsets[outParamOffsetIndex] = -((int)sym->m_offset + BailOutInfo::StackSymBias);
- #else
- // Stack offset are negative, includes the PUSH EBP and return address
- outParamOffsets[outParamOffsetIndex] = sym->m_offset - (2 * MachPtr);
- #endif
- }
- #ifdef _M_IX86
- else if (fDoStackAdjust)
- {
- // If we've got args on the stack, then we must have seen (and adjusted for) the StartCall.
- // The values is already on the stack
- // On AMD64/ARM, ArgOut should have been moved next to the call, and shouldn't have bailout between them
- // Except for inlined arg outs
- outParamOffsets[outParamOffsetIndex] = currentStackOffset + argSlot * MachPtr;
- bailOutInfo->outParamFrameAdjustArgSlot->Set(outParamOffsetIndex);
- }
- #endif
- else
- {
- this->FillBailOutOffset(&outParamOffsets[outParamOffsetIndex], sym, &state, instr);
- }
- }
- else
- {
- // The ArgOut instruction might have moved down right next to the call,
- // because of a register calling convention, cloning, etc. This loop walks the chain
- // of assignments to try to find the original location of the assignment where
- // the value is available.
- while (!sym->IsConst())
- {
- // the value is in the register
- IR::RegOpnd * regOpnd = instrDef->GetSrc1()->AsRegOpnd();
- sym = regOpnd->m_sym;
- if (sym->scratch.linearScan.lifetime->start < instr->GetNumber())
- {
- break;
- }
- if (sym->m_isEncodedConstant)
- {
- break;
- }
- // For out parameter we might need to follow multiple assignments
- Assert(sym->m_isSingleDef);
- instrDef = sym->m_instrDef;
- Assert(LowererMD::IsAssign(instrDef));
- }
- if (bailOutInfo->usedCapturedValues.argObjSyms && bailOutInfo->usedCapturedValues.argObjSyms->Test(sym->m_id))
- {
- //foo.apply(this,arguments) case and we bailout when the apply is overridden. We need to restore the arguments object.
- outParamOffsets[outParamOffsetIndex] = BailOutRecord::GetArgumentsObjectOffset();
- }
- else
- {
- this->FillBailOutOffset(&outParamOffsets[outParamOffsetIndex], sym, &state, instr);
- }
- }
- }
- }
- else
- {
- this->FillBailOutOffset(&outParamOffsets[outParamOffsetIndex], sym, &state, instr);
- }
- if (sym->IsFloat64())
- {
- argOutFloat64Syms->Set(outParamOffsetIndex);
- }
- else if (sym->IsInt32())
- {
- argOutLosslessInt32Syms->Set(outParamOffsetIndex);
- }
- // SIMD_JS
- else if (sym->IsSimd128F4())
- {
- argOutSimd128F4Syms->Set(outParamOffsetIndex);
- }
- else if (sym->IsSimd128I4())
- {
- argOutSimd128I4Syms->Set(outParamOffsetIndex);
- }
- #if DBG_DUMP
- if (PHASE_DUMP(Js::BailOutPhase, this->func))
- {
- Output::Print(L"OutParam #%d: ", argSlot);
- sym->Dump();
- Output::Print(L"\n");
- }
- #endif
- }
- }
- }
- }
- else
- {
- Assert(bailOutInfo->argOutSyms == nullptr);
- Assert(bailOutInfo->startCallCount == 0);
- }
- if (this->currentBlock->inlineeStack.Count() > 0)
- {
- this->SpillInlineeArgs(instr);
- }
- else
- {
- // There is a chance that the instruction was hoisting from an inlinee func
- // but if there are no inlinee frames - make sure the instr belongs to the outer func
- // to ensure encoder does not encode an inline frame here - which does not really exist
- instr->m_func = this->func;
- }
- linearScanMD.GenerateBailOut(instr, state.registerSaveSyms, _countof(state.registerSaveSyms));
- // generate the constant table
- Js::Var * constants = NativeCodeDataNewArray(allocator, Js::Var, state.constantList.Count());
- uint constantCount = state.constantList.Count();
- while (!state.constantList.Empty())
- {
- Js::Var value = state.constantList.Head();
- state.constantList.RemoveHead();
- constants[state.constantList.Count()] = value;
- }
- // Generate the stack literal bail out info
- FillStackLiteralBailOutRecord(instr, bailOutInfo, funcBailOutData, funcCount);
- for (uint i = 0; i < funcCount; i++)
- {
- funcBailOutData[i].bailOutRecord->constants = constants;
- #if DBG
- funcBailOutData[i].bailOutRecord->inlineDepth = funcBailOutData[i].func->inlineDepth;
- funcBailOutData[i].bailOutRecord->constantCount = constantCount;
- #endif
- uint32 tableIndex = funcBailOutData[i].func->IsTopFunc() ? 0 : funcBailOutData[i].func->m_inlineeId;
- funcBailOutData[i].FinalizeLocalOffsets(tempAlloc, this->globalBailOutRecordTables[tableIndex], &(this->lastUpdatedRowIndices[tableIndex]));
- #if DBG_DUMP
- if(PHASE_DUMP(Js::BailOutPhase, this->func))
- {
- wchar_t debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(L"Bailout function: %s [%s]\n", funcBailOutData[i].func->GetJnFunction()->GetDisplayName(), funcBailOutData[i].func->GetJnFunction()->GetDebugNumberSet(debugStringBuffer), i);
- funcBailOutData[i].bailOutRecord->Dump();
- }
- #endif
- funcBailOutData[i].Clear(this->tempAlloc);
- #ifdef PROFILE_BAILOUT_RECORD_MEMORY
- if (Js::Configuration::Global.flags.ProfileBailOutRecordMemory)
- {
- this->func->GetScriptContext()->bailOutRecordBytes += sizeof(BailOutRecord);
- }
- #endif
- }
- JitAdeleteArray(this->tempAlloc, funcCount, funcBailOutData);
- }
- template <typename Fn>
- void
- LinearScan::ForEachStackLiteralBailOutInfo(IR::Instr * instr, BailOutInfo * bailOutInfo, FuncBailOutData * funcBailOutData, uint funcCount, Fn fn)
- {
- for (uint i = 0; i < bailOutInfo->stackLiteralBailOutInfoCount; i++)
- {
- BailOutInfo::StackLiteralBailOutInfo& stackLiteralBailOutInfo = bailOutInfo->stackLiteralBailOutInfo[i];
- StackSym * stackSym = stackLiteralBailOutInfo.stackSym;
- Assert(stackSym->scratch.linearScan.lifetime->start < instr->GetNumber());
- Assert(stackSym->scratch.linearScan.lifetime->end >= instr->GetNumber());
- Js::RegSlot regSlot = stackSym->GetByteCodeRegSlot();
- Func * stackSymFunc = stackSym->GetByteCodeFunc();
- uint index = stackSymFunc->inlineDepth;
- Assert(regSlot != Js::Constants::NoRegister);
- Assert(regSlot < stackSymFunc->GetJnFunction()->GetLocalsCount());
- Assert(index < funcCount);
- Assert(funcBailOutData[index].func == stackSymFunc);
- Assert(funcBailOutData[index].localOffsets[regSlot] != 0);
- fn(index, stackLiteralBailOutInfo, regSlot);
- }
- }
- void
- LinearScan::FillStackLiteralBailOutRecord(IR::Instr * instr, BailOutInfo * bailOutInfo, FuncBailOutData * funcBailOutData, uint funcCount)
- {
- if (bailOutInfo->stackLiteralBailOutInfoCount)
- {
- // Count the data
- ForEachStackLiteralBailOutInfo(instr, bailOutInfo, funcBailOutData, funcCount,
- [=](uint funcIndex, BailOutInfo::StackLiteralBailOutInfo& stackLiteralBailOutInfo, Js::RegSlot regSlot)
- {
- funcBailOutData[funcIndex].bailOutRecord->stackLiteralBailOutRecordCount++;
- });
- // Allocate the data
- NativeCodeData::Allocator * allocator = this->func->GetNativeCodeDataAllocator();
- for (uint i = 0; i < funcCount; i++)
- {
- uint stackLiteralBailOutRecordCount = funcBailOutData[i].bailOutRecord->stackLiteralBailOutRecordCount;
- if (stackLiteralBailOutRecordCount)
- {
- funcBailOutData[i].bailOutRecord->stackLiteralBailOutRecord =
- NativeCodeDataNewArray(allocator, BailOutRecord::StackLiteralBailOutRecord, stackLiteralBailOutRecordCount);
- // reset the count so we can track how much we have filled below
- funcBailOutData[i].bailOutRecord->stackLiteralBailOutRecordCount = 0;
- }
- }
- // Fill out the data
- ForEachStackLiteralBailOutInfo(instr, bailOutInfo, funcBailOutData, funcCount,
- [=](uint funcIndex, BailOutInfo::StackLiteralBailOutInfo& stackLiteralBailOutInfo, Js::RegSlot regSlot)
- {
- uint& recordIndex = funcBailOutData[funcIndex].bailOutRecord->stackLiteralBailOutRecordCount;
- BailOutRecord::StackLiteralBailOutRecord& stackLiteralBailOutRecord =
- funcBailOutData[funcIndex].bailOutRecord->stackLiteralBailOutRecord[recordIndex++];
- stackLiteralBailOutRecord.regSlot = regSlot;
- stackLiteralBailOutRecord.initFldCount = stackLiteralBailOutInfo.initFldCount;
- });
- }
- }
- void
- LinearScan::PrepareForUse(Lifetime * lifetime)
- {
- if (lifetime->isOpHelperSpilled)
- {
- // using a value in a helper that has been spilled in the helper block.
- // Just spill it for real
- // We must be in a helper block and the lifetime must
- // start before the helper block
- Assert(this->IsInHelperBlock());
- Assert(lifetime->start < this->HelperBlockStartInstrNumber());
- IR::Instr *insertionInstr = this->currentOpHelperBlock->opHelperLabel;
- this->RemoveOpHelperSpilled(lifetime);
- this->SpillLiveRange(lifetime, insertionInstr);
- }
- }
- void
- LinearScan::RecordUse(Lifetime * lifetime, IR::Instr * instr, IR::RegOpnd * regOpnd, bool isFromBailout)
- {
- uint32 useCountCost = LinearScan::GetUseSpillCost(this->loopNest, (this->currentOpHelperBlock != nullptr || isFromBailout));
- // We only spill at the use for constants (i.e. reload) or for function with try blocks. We don't
- // have real accurate flow info for the later.
- if ((regOpnd && regOpnd->m_sym->IsConst())
- || (
- (this->func->HasTry() && !this->func->DoOptimizeTryCatch()) &&
- this->IsInLoop() &&
- lifetime->lastUseLabel != this->lastLabel &&
- this->liveOnBackEdgeSyms->Test(lifetime->sym->m_id) &&
- !(lifetime->previousDefBlockNumber == currentBlockNumber && !lifetime->defList.Empty())
- ))
- {
- // Keep track of all the uses of this lifetime in case we decide to spill it.
- // Note that we won't need to insert reloads if the use are not in a loop,
- // unless it is a const. We always reload const instead of spilling to the stack.
- //
- // We also don't need to insert reloads if the previous use was in the same basic block (the first use in the block
- // would have done the reload), or the previous def is in the same basic block and the value is still live. Furthermore,
- // if the previous def is in the same basic block, the value is still live, and there's another def after this use in
- // the same basic block, the previous def may not do a spill store, so we must not reload the value from the stack.
- lifetime->useList.Prepend(instr);
- lifetime->lastUseLabel = this->lastLabel;
- lifetime->AddToUseCountAdjust(useCountCost, this->curLoop, this->func);
- }
- else
- {
- if (!isFromBailout)
- {
- // Since we won't reload this use if the lifetime gets spilled, adjust the spill cost to reflect this.
- lifetime->SubFromUseCount(useCountCost, this->curLoop);
- }
- }
- if (this->IsInLoop())
- {
- this->RecordLoopUse(lifetime, lifetime->reg);
- }
- }
- void LinearScan::RecordLoopUse(Lifetime *lifetime, RegNum reg)
- {
- if (!this->IsInLoop())
- {
- return;
- }
- if (this->func->HasTry() && !this->func->DoOptimizeTryCatch())
- {
- return;
- }
- // Record on each loop which register live into the loop ended up being used.
- // We are trying to avoid the need for compensation at the bottom of the loop if
- // the reg ends up being spilled before it is actually used.
- Loop *curLoop = this->curLoop;
- SymID symId = (SymID)-1;
- if (lifetime)
- {
- symId = lifetime->sym->m_id;
- }
- while (curLoop)
- {
- // Note that if the lifetime is spilled and reallocated to the same register,
- // will mark it as used when we shouldn't. However, it is hard at this point to handle
- // the case were a flow edge from the previous allocation merges in with the new allocation.
- // No compensation is inserted to let us know with previous lifetime needs reloading at the bottom of the loop...
- if (lifetime && curLoop->regAlloc.loopTopRegContent[reg] == lifetime)
- {
- curLoop->regAlloc.symRegUseBv->Set(symId);
- }
- curLoop->regAlloc.regUseBv.Set(reg);
- curLoop = curLoop->parent;
- }
- }
- void
- LinearScan::RecordDef(Lifetime *const lifetime, IR::Instr *const instr, const uint32 useCountCost)
- {
- Assert(lifetime);
- Assert(instr);
- Assert(instr->GetDst());
- IR::RegOpnd * regOpnd = instr->GetDst()->AsRegOpnd();
- Assert(regOpnd);
- StackSym *const sym = regOpnd->m_sym;
- if (this->IsInLoop())
- {
- Loop *curLoop = this->curLoop;
- while (curLoop)
- {
- curLoop->regAlloc.defdInLoopBv->Set(lifetime->sym->m_id);
- curLoop->regAlloc.regUseBv.Set(lifetime->reg);
- curLoop = curLoop->parent;
- }
- }
- if (lifetime->isSpilled)
- {
- return;
- }
- if (this->NeedsWriteThrough(sym))
- {
- if (this->IsSymNonTempLocalVar(sym))
- {
- // In the debug mode, we will write through on the stack location.
- WriteThroughForLocal(regOpnd, lifetime, instr);
- }
- else
- {
- // If this is a write-through sym, it should be live on the entry to 'try' and should have already
- // been allocated when we spilled all active lifetimes there.
- // If it was not part of the active lifetimes on entry to the 'try' then it must have been spilled
- // earlier and should have stack allocated for it.
- Assert(this->NeedsWriteThroughForEH(sym) && sym->IsAllocated());
- this->InsertStore(instr, sym, lifetime->reg);
- }
- // No need to record-def further as we already have stack allocated for it.
- return;
- }
- if (sym->m_isSingleDef)
- {
- lifetime->AddToUseCount(useCountCost, this->curLoop, this->func);
- // the def of a single-def sym is already on the sym
- return;
- }
- if(lifetime->previousDefBlockNumber == currentBlockNumber && !lifetime->defList.Empty())
- {
- // Only keep track of the last def in each basic block. When there are multiple defs of a sym in a basic block, upon
- // spill of that sym, a store needs to be inserted only after the last def of the sym.
- Assert(lifetime->defList.Head()->GetDst()->AsRegOpnd()->m_sym == sym);
- lifetime->defList.Head() = instr;
- }
- else
- {
- // First def of this sym in the current basic block
- lifetime->previousDefBlockNumber = currentBlockNumber;
- lifetime->defList.Prepend(instr);
- // Keep track of the cost of reinserting all the defs if we choose to spill this way.
- lifetime->allDefsCost += useCountCost;
- }
- }
- // LinearScan::SetUse
- void
- LinearScan::SetUse(IR::Instr *instr, IR::RegOpnd *regOpnd)
- {
- if (regOpnd->GetReg() != RegNOREG)
- {
- this->RecordLoopUse(nullptr, regOpnd->GetReg());
- return;
- }
- StackSym *sym = regOpnd->m_sym;
- Lifetime * lifetime = sym->scratch.linearScan.lifetime;
- this->PrepareForUse(lifetime);
- if (lifetime->isSpilled)
- {
- // See if it has been loaded in this basic block
- RegNum reg = this->GetAssignedTempReg(lifetime, regOpnd->GetType());
- if (reg == RegNOREG)
- {
- if (sym->IsConst() && EncoderMD::TryConstFold(instr, regOpnd))
- {
- return;
- }
- reg = this->SecondChanceAllocation(lifetime, false);
- if (reg != RegNOREG)
- {
- IR::Instr *insertInstr = this->TryHoistLoad(instr, lifetime);
- this->InsertLoad(insertInstr, sym, reg);
- }
- else
- {
- // Try folding if there are no registers available
- if (!sym->IsConst() && !this->RegsAvailable(regOpnd->GetType()) && EncoderMD::TryFold(instr, regOpnd))
- {
- return;
- }
- // We need a reg no matter what. Try to force second chance to re-allocate this.
- reg = this->SecondChanceAllocation(lifetime, true);
- if (reg == RegNOREG)
- {
- // Forcing second chance didn't work.
- // Allocate a new temp reg for it
- reg = this->FindReg(nullptr, regOpnd);
- this->AssignTempReg(lifetime, reg);
- }
- this->InsertLoad(instr, sym, reg);
- }
- }
- }
- if (!lifetime->isSpilled && instr->GetNumber() < lifetime->end)
- {
- // Don't border to record the use if this is the last use of the lifetime.
- this->RecordUse(lifetime, instr, regOpnd);
- }
- else
- {
- lifetime->SubFromUseCount(LinearScan::GetUseSpillCost(this->loopNest, (this->currentOpHelperBlock != nullptr)), this->curLoop);
- }
- this->instrUseRegs.Set(lifetime->reg);
- this->SetReg(regOpnd);
- }
- // LinearScan::SetReg
- void
- LinearScan::SetReg(IR::RegOpnd *regOpnd)
- {
- if (regOpnd->GetReg() == RegNOREG)
- {
- RegNum reg = regOpnd->m_sym->scratch.linearScan.lifetime->reg;
- AssertMsg(reg != RegNOREG, "Reg should be allocated here...");
- regOpnd->SetReg(reg);
- }
- }
- bool
- LinearScan::SkipNumberedInstr(IR::Instr *instr)
- {
- if (instr->IsLabelInstr())
- {
- if (instr->AsLabelInstr()->m_isLoopTop)
- {
- Assert(instr->GetNumber() != instr->m_next->GetNumber()
- && (instr->GetNumber() != instr->m_prev->GetNumber() || instr->m_prev->m_opcode == Js::OpCode::Nop));
- }
- else
- {
- return true;
- }
- }
- return false;
- }
- // LinearScan::EndDeadLifetimes
- // Look for lifetimes that are ending here, and retire them.
- void
- LinearScan::EndDeadLifetimes(IR::Instr *instr)
- {
- Lifetime * deadLifetime;
- if (this->SkipNumberedInstr(instr))
- {
- return;
- }
- // Retire all active lifetime ending at this instruction
- while (!this->activeLiveranges->Empty() && this->activeLiveranges->Head()->end <= instr->GetNumber())
- {
- deadLifetime = this->activeLiveranges->Head();
- deadLifetime->defList.Clear();
- deadLifetime->useList.Clear();
- this->activeLiveranges->RemoveHead();
- RegNum reg = deadLifetime->reg;
- this->activeRegs.Clear(reg);
- this->regContent[reg] = nullptr;
- this->secondChanceRegs.Clear(reg);
- if (RegTypes[reg] == TyMachReg)
- {
- this->intRegUsedCount--;
- }
- else
- {
- Assert(RegTypes[reg] == TyFloat64);
- this->floatRegUsedCount--;
- }
- }
- // Look for spilled lifetimes which end here such that we can make their stack slot
- // available for stack-packing.
- while (!this->stackPackInUseLiveRanges->Empty() && this->stackPackInUseLiveRanges->Head()->end <= instr->GetNumber())
- {
- deadLifetime = this->stackPackInUseLiveRanges->Head();
- deadLifetime->defList.Clear();
- deadLifetime->useList.Clear();
- this->stackPackInUseLiveRanges->RemoveHead();
- if (!deadLifetime->cantStackPack)
- {
- Assert(deadLifetime->spillStackSlot);
- deadLifetime->spillStackSlot->lastUse = deadLifetime->end;
- this->stackSlotsFreeList->Push(deadLifetime->spillStackSlot);
- }
- }
- }
- void
- LinearScan::EndDeadOpHelperLifetimes(IR::Instr * instr)
- {
- if (this->SkipNumberedInstr(instr))
- {
- return;
- }
- while (!this->opHelperSpilledLiveranges->Empty() &&
- this->opHelperSpilledLiveranges->Head()->end <= instr->GetNumber())
- {
- Lifetime * deadLifetime;
- // The lifetime doesn't extend beyond the helper block
- // No need to save and restore around the helper block
- Assert(this->IsInHelperBlock());
- deadLifetime = this->opHelperSpilledLiveranges->Head();
- this->opHelperSpilledLiveranges->RemoveHead();
- if (!deadLifetime->cantOpHelperSpill)
- {
- this->opHelperSpilledRegs.Clear(deadLifetime->reg);
- }
- deadLifetime->isOpHelperSpilled = false;
- deadLifetime->cantOpHelperSpill = false;
- deadLifetime->isOpHelperSpillAsArg = false;
- }
- }
- // LinearScan::AllocateNewLifetimes
- // Look for lifetimes coming live, and allocate a register for them.
- void
- LinearScan::AllocateNewLifetimes(IR::Instr *instr)
- {
- if (this->SkipNumberedInstr(instr))
- {
- return;
- }
- // Try to catch:
- // x = MOV y(r1)
- // where y's lifetime just ended and x's lifetime is starting.
- // If so, set r1 as a preferred register for x, which may allow peeps to remove the MOV
- if (instr->GetSrc1() && instr->GetSrc1()->IsRegOpnd() && LowererMD::IsAssign(instr) && instr->GetDst() && instr->GetDst()->IsRegOpnd() && instr->GetDst()->AsRegOpnd()->m_sym)
- {
- IR::RegOpnd *src = instr->GetSrc1()->AsRegOpnd();
- StackSym *srcSym = src->m_sym;
- // If src is a physReg ref, or src's lifetime ends here.
- if (!srcSym || srcSym->scratch.linearScan.lifetime->end == instr->GetNumber())
- {
- Lifetime *dstLifetime = instr->GetDst()->AsRegOpnd()->m_sym->scratch.linearScan.lifetime;
- if (dstLifetime)
- {
- dstLifetime->regPreference.Set(src->GetReg());
- }
- }
- }
- // Look for starting lifetimes
- while (!this->lifetimeList->Empty() && this->lifetimeList->Head()->start <= instr->GetNumber())
- {
- // We're at the start of a new live range
- Lifetime * newLifetime = this->lifetimeList->Head();
- newLifetime->lastAllocationStart = instr->GetNumber();
- this->lifetimeList->RemoveHead();
- if (newLifetime->dontAllocate)
- {
- // Lifetime spilled before beginning allocation (e.g., a lifetime known to span
- // multiple EH regions.) Do the work of spilling it now without adding it to the list.
- this->SpillLiveRange(newLifetime);
- continue;
- }
- RegNum reg;
- if (newLifetime->reg == RegNOREG)
- {
- if (newLifetime->isDeadStore)
- {
- // No uses, let's not waste a reg.
- newLifetime->isSpilled = true;
- continue;
- }
- reg = this->FindReg(newLifetime, nullptr);
- }
- else
- {
- // This lifetime is already assigned a physical register. Make
- // sure that register is available by calling SpillReg
- reg = newLifetime->reg;
- // If we're in a helper block, the physical register we're trying to ensure is available might get helper
- // spilled. Don't allow that if this lifetime's end lies beyond the end of the helper block because
- // spill code assumes that this physical register isn't active at the end of the helper block when it tries
- // to restore it. So we'd have to really spill the lifetime then anyway.
- this->SpillReg(reg, IsInHelperBlock() ? (newLifetime->end > currentOpHelperBlock->opHelperEndInstr->GetNumber()) : false);
- newLifetime->cantSpill = true;
- }
- // If we did get a register for this lifetime, add it to the active set.
- if (newLifetime->isSpilled == false)
- {
- this->AssignActiveReg(newLifetime, reg);
- }
- }
- }
- // LinearScan::FindReg
- // Look for an available register. If one isn't available, spill something.
- // Note that the newLifetime passed in could be the one we end up spilling.
- RegNum
- LinearScan::FindReg(Lifetime *newLifetime, IR::RegOpnd *regOpnd, bool force)
- {
- BVIndex regIndex = BVInvalidIndex;
- IRType type;
- bool tryCallerSavedRegs = false;
- BitVector callerSavedAvailableBv;
- if (newLifetime)
- {
- if (newLifetime->isFloat)
- {
- type = TyFloat64;
- }
- else if (newLifetime->isSimd128F4)
- {
- type = TySimd128F4;
- }
- else if (newLifetime->isSimd128I4)
- {
- type = TySimd128I4;
- }
- else if (newLifetime->isSimd128D2)
- {
- type = TySimd128D2;
- }
- else
- {
- type = TyMachReg;
- }
- }
- else
- {
- Assert(regOpnd);
- type = regOpnd->GetType();
- }
- if (this->RegsAvailable(type))
- {
- BitVector regsBv;
- regsBv.Copy(this->activeRegs);
- regsBv.Or(this->instrUseRegs);
- regsBv.Or(this->callSetupRegs);
- regsBv.ComplimentAll();
- if (newLifetime)
- {
- if (this->IsInHelperBlock())
- {
- if (newLifetime->end >= this->HelperBlockEndInstrNumber())
- {
- // this lifetime goes beyond the helper function
- // We need to exclude the helper spilled register as well.
- regsBv.Minus(this->opHelperSpilledRegs);
- }
- }
- if (newLifetime->isFloat || newLifetime->isSimd128())
- {
- #ifdef _M_IX86
- Assert(AutoSystemInfo::Data.SSE2Available());
- #endif
- regsBv.And(this->floatRegs);
- }
- else
- {
- regsBv.And(this->int32Regs);
- regsBv = this->linearScanMD.FilterRegIntSizeConstraints(regsBv, newLifetime->intUsageBv);
- }
- if (newLifetime->isLiveAcrossCalls)
- {
- // Try to find a callee saved regs
- BitVector regsBvTemp = regsBv;
- regsBvTemp.And(this->calleeSavedRegs);
- regIndex = GetPreferrencedRegIndex(newLifetime, regsBvTemp);
- if (regIndex == BVInvalidIndex)
- {
- if (!newLifetime->isLiveAcrossUserCalls)
- {
- // No callee saved regs is found and the lifetime only across helper
- // calls, we can also use a caller saved regs to make use of the
- // save and restore around helper blocks
- regIndex = GetPreferrencedRegIndex(newLifetime, regsBv);
- }
- else
- {
- // If we can't find a callee-saved reg, we can try using a caller-saved reg instead.
- // We'll hopefully get a few loads enregistered that way before we get to the call.
- tryCallerSavedRegs = true;
- callerSavedAvailableBv = regsBv;
- }
- }
- }
- else
- {
- regIndex = GetPreferrencedRegIndex(newLifetime, regsBv);
- }
- }
- else
- {
- AssertMsg(regOpnd, "Need a lifetime or a regOpnd passed in");
- if (regOpnd->IsFloat() || regOpnd->IsSimd128())
- {
- #ifdef _M_IX86
- Assert(AutoSystemInfo::Data.SSE2Available());
- #endif
- regsBv.And(this->floatRegs);
- }
- else
- {
- regsBv.And(this->int32Regs);
- BitVector regSizeBv;
- regSizeBv.ClearAll();
- regSizeBv.Set(TySize[regOpnd->GetType()]);
- regsBv = this->linearScanMD.FilterRegIntSizeConstraints(regsBv, regSizeBv);
- }
- if (!this->tempRegs.IsEmpty())
- {
- // avoid the temp reg that we have loaded in this basic block
- BitVector regsBvTemp = regsBv;
- regsBvTemp.Minus(this->tempRegs);
- regIndex = regsBvTemp.GetPrevBit();
- }
- if (regIndex == BVInvalidIndex)
- {
- // allocate a temp reg from the other end of the bit vector so that it can
- // keep live for longer.
- regIndex = regsBv.GetPrevBit();
- }
- }
- }
- RegNum reg;
- if (BVInvalidIndex != regIndex)
- {
- Assert(regIndex < RegNumCount);
- reg = (RegNum)regIndex;
- }
- else
- {
- if (tryCallerSavedRegs)
- {
- Assert(newLifetime);
- regIndex = GetPreferrencedRegIndex(newLifetime, callerSavedAvailableBv);
- if (BVInvalidIndex == regIndex)
- {
- tryCallerSavedRegs = false;
- }
- }
- bool dontSpillCurrent = tryCallerSavedRegs;
- if (newLifetime && newLifetime->isSpilled)
- {
- // Second chance allocation
- dontSpillCurrent = true;
- }
- // Can't find reg, spill some lifetime.
- reg = this->Spill(newLifetime, regOpnd, dontSpillCurrent, force);
- if (reg == RegNOREG && tryCallerSavedRegs)
- {
- Assert(BVInvalidIndex != regIndex);
- reg = (RegNum)regIndex;
- // This lifetime will get spilled once we get to the call it overlaps with (note: this may not be true
- // for second chance allocation as we may be beyond the call). Mark it as a cheap spill to give up the register
- // if some lifetime not overlapping with a call needs it.
- newLifetime->isCheapSpill = true;
- }
- }
- // We always have to return a reg if we are allocate temp reg.
- // If we are allocating for a new lifetime, we return RegNOREG, if we
- // spill the new lifetime
- Assert(newLifetime != nullptr || (reg != RegNOREG && reg < RegNumCount));
- return reg;
- }
- BVIndex
- LinearScan::GetPreferrencedRegIndex(Lifetime *lifetime, BitVector freeRegs)
- {
- BitVector freePreferencedRegs = freeRegs;
- freePreferencedRegs.And(lifetime->regPreference);
- // If one of the preferred register (if any) is available, use it. Otherwise, just pick one of free register.
- if (!freePreferencedRegs.IsEmpty())
- {
- return freePreferencedRegs.GetNextBit();
- }
- else
- {
- return freeRegs.GetNextBit();
- }
- }
- // LinearScan::Spill
- // We need to spill something to free up a reg. If the newLifetime
- // past in isn't NULL, we can spill this one instead of an active one.
- RegNum
- LinearScan::Spill(Lifetime *newLifetime, IR::RegOpnd *regOpnd, bool dontSpillCurrent, bool force)
- {
- uint minSpillCost = (uint)-1;
- Assert(!newLifetime || !regOpnd || newLifetime->isFloat == (regOpnd->GetType() == TyMachDouble) || newLifetime->isSimd128() == (regOpnd->IsSimd128()));
- bool isFloatReg;
- BitVector intUsageBV;
- bool needCalleeSaved;
- // For now, we just spill the lifetime with the lowest spill cost.
- if (newLifetime)
- {
- isFloatReg = newLifetime->isFloat || newLifetime->isSimd128();
- if (!force)
- {
- minSpillCost = this->GetSpillCost(newLifetime);
- }
- intUsageBV = newLifetime->intUsageBv;
- needCalleeSaved = newLifetime->isLiveAcrossUserCalls;
- }
- else
- {
- needCalleeSaved = false;
- if (regOpnd->IsFloat() || regOpnd->IsSimd128())
- {
- isFloatReg = true;
- }
- else
- {
- // Filter for int reg size constraints
- isFloatReg = false;
- intUsageBV.ClearAll();
- intUsageBV.Set(TySize[regOpnd->GetType()]);
- }
- }
- SList<Lifetime *>::EditingIterator candidate;
- FOREACH_SLIST_ENTRY_EDITING(Lifetime *, lifetime, this->activeLiveranges, iter)
- {
- uint spillCost = this->GetSpillCost(lifetime);
- if (spillCost < minSpillCost &&
- this->instrUseRegs.Test(lifetime->reg) == false &&
- (lifetime->isFloat || lifetime->isSimd128()) == isFloatReg &&
- !lifetime->cantSpill &&
- (!needCalleeSaved || this->calleeSavedRegs.Test(lifetime->reg)) &&
- this->linearScanMD.FitRegIntSizeConstraints(lifetime->reg, intUsageBV))
- {
- minSpillCost = spillCost;
- candidate = iter;
- }
- } NEXT_SLIST_ENTRY_EDITING;
- AssertMsg(newLifetime || candidate.IsValid(), "Didn't find anything to spill?!?");
- Lifetime * spilledRange;
- if (candidate.IsValid())
- {
- spilledRange = candidate.Data();
- candidate.RemoveCurrent();
- this->activeRegs.Clear(spilledRange->reg);
- if (spilledRange->isFloat || spilledRange->isSimd128())
- {
- this->floatRegUsedCount--;
- }
- else
- {
- this->intRegUsedCount--;
- }
- }
- else if (dontSpillCurrent)
- {
- return RegNOREG;
- }
- else
- {
- spilledRange = newLifetime;
- }
- return this->SpillLiveRange(spilledRange);
- }
- // LinearScan::SpillLiveRange
- RegNum
- LinearScan::SpillLiveRange(Lifetime * spilledRange, IR::Instr *insertionInstr)
- {
- Assert(!spilledRange->isSpilled);
- RegNum reg = spilledRange->reg;
- StackSym *sym = spilledRange->sym;
- spilledRange->isSpilled = true;
- spilledRange->isCheapSpill = false;
- spilledRange->reg = RegNOREG;
- // Don't allocate stack space for const, we always reload them. (For debugm mode, allocate on the stack)
- if (!sym->IsAllocated() && (!sym->IsConst() || IsSymNonTempLocalVar(sym)))
- {
- this->AllocateStackSpace(spilledRange);
- }
- // No need to insert loads or stores if there are no uses.
- if (!spilledRange->isDeadStore)
- {
- // In the debug mode, don't do insertstore for this stacksym, as we want to retain the IsConst for the sym,
- // and later we are going to find the reg for it.
- if (!IsSymNonTempLocalVar(sym))
- {
- this->InsertStores(spilledRange, reg, insertionInstr);
- }
- if (this->IsInLoop() || sym->IsConst())
- {
- this->InsertLoads(sym, reg);
- }
- else
- {
- sym->scratch.linearScan.lifetime->useList.Clear();
- }
- // Adjust useCount in case of second chance allocation
- spilledRange->ApplyUseCountAdjust(this->curLoop);
- }
- Assert(reg == RegNOREG || spilledRange->reg == RegNOREG || this->regContent[reg] == spilledRange);
- if (spilledRange->isSecondChanceAllocated)
- {
- Assert(reg == RegNOREG || spilledRange->reg == RegNOREG
- || (this->regContent[reg] == spilledRange && this->secondChanceRegs.Test(reg)));
- this->secondChanceRegs.Clear(reg);
- spilledRange->isSecondChanceAllocated = false;
- }
- else
- {
- Assert(!this->secondChanceRegs.Test(reg));
- }
- this->regContent[reg] = nullptr;
- #if DBG_DUMP
- if (PHASE_TRACE(Js::LinearScanPhase, this->func))
- {
- Output::Print(L"**** Spill: ");
- sym->Dump();
- Output::Print(L"(%S)", RegNames[reg]);
- Output::Print(L" SpillCount:%d Length:%d Cost:%d\n",
- spilledRange->useCount, spilledRange->end - spilledRange->start, this->GetSpillCost(spilledRange));
- }
- #endif
- return reg;
- }
- // LinearScan::SpillReg
- // Spill a given register.
- void
- LinearScan::SpillReg(RegNum reg, bool forceSpill /* = false */)
- {
- Lifetime *spilledRange = nullptr;
- if (activeRegs.Test(reg))
- {
- spilledRange = LinearScan::RemoveRegLiveRange(activeLiveranges, reg);
- }
- else if (opHelperSpilledRegs.Test(reg) && forceSpill)
- {
- // If a lifetime that was assigned this register was helper spilled,
- // really spill it now.
- Assert(IsInHelperBlock());
- // Look for the liverange in opHelperSpilledLiveranges instead of
- // activeLiveranges.
- FOREACH_SLIST_ENTRY(Lifetime *, lifetime, opHelperSpilledLiveranges)
- {
- if (lifetime->reg == reg)
- {
- spilledRange = lifetime;
- break;
- }
- } NEXT_SLIST_ENTRY;
- Assert(spilledRange);
- Assert(!spilledRange->cantSpill);
- RemoveOpHelperSpilled(spilledRange);
- // Really spill this liverange below.
- }
- else
- {
- return;
- }
- AnalysisAssert(spilledRange);
- Assert(!spilledRange->cantSpill);
- if ((!forceSpill) && this->IsInHelperBlock() && spilledRange->start < this->HelperBlockStartInstrNumber() && !spilledRange->cantOpHelperSpill)
- {
- // if the lifetime starts before the helper block, we can do save and restore
- // around the helper block instead.
- this->AddOpHelperSpilled(spilledRange);
- }
- else
- {
- if (spilledRange->cantOpHelperSpill)
- {
- // We're really spilling this liverange, so take it out of the helper-spilled liveranges
- // to avoid confusion (see Win8 313433).
- Assert(!spilledRange->isOpHelperSpilled);
- spilledRange->cantOpHelperSpill = false;
- this->opHelperSpilledLiveranges->Remove(spilledRange);
- }
- this->SpillLiveRange(spilledRange);
- }
- if (this->activeRegs.Test(reg))
- {
- this->activeRegs.Clear(reg);
- if (RegTypes[reg] == TyMachReg)
- {
- this->intRegUsedCount--;
- }
- else
- {
- Assert(RegTypes[reg] == TyFloat64);
- this->floatRegUsedCount--;
- }
- }
- }
- void
- LinearScan::ProcessEHRegionBoundary(IR::Instr * instr)
- {
- Assert(instr->IsBranchInstr());
- Assert(instr->m_opcode != Js::OpCode::TryFinally); // finallys are not supported for optimization yet.
- if (instr->m_opcode != Js::OpCode::TryCatch && instr->m_opcode != Js::OpCode::Leave)
- {
- return;
- }
- // Spill everything upon entry to the try region and upon a Leave.
- IR::Instr* insertionInstr = instr->m_opcode != Js::OpCode::Leave ? instr : instr->m_prev;
- FOREACH_SLIST_ENTRY_EDITING(Lifetime *, lifetime, this->activeLiveranges, iter)
- {
- this->activeRegs.Clear(lifetime->reg);
- if (lifetime->isFloat || lifetime->isSimd128())
- {
- this->floatRegUsedCount--;
- }
- else
- {
- this->intRegUsedCount--;
- }
- this->SpillLiveRange(lifetime, insertionInstr);
- iter.RemoveCurrent();
- }
- NEXT_SLIST_ENTRY_EDITING;
- }
- void
- LinearScan::AllocateStackSpace(Lifetime *spilledRange)
- {
- if (spilledRange->sym->IsAllocated())
- {
- return;
- }
- uint32 size = TySize[spilledRange->sym->GetType()];
- // For the bytecodereg syms instead of spilling to the any other location lets re-use the already created slot.
- if (IsSymNonTempLocalVar(spilledRange->sym))
- {
- Js::RegSlot slotIndex = spilledRange->sym->GetByteCodeRegSlot();
- // Get the offset which is already allocated from this local, and always spill on that location.
- spilledRange->sym->m_offset = GetStackOffset(slotIndex);
- spilledRange->sym->m_allocated = true;
- return;
- }
- StackSlot * newStackSlot = nullptr;
- if (!PHASE_OFF(Js::StackPackPhase, this->func) && !this->func->IsJitInDebugMode() && !spilledRange->cantStackPack)
- {
- // Search for a free stack slot to re-use
- FOREACH_SLIST_ENTRY_EDITING(StackSlot *, slot, this->stackSlotsFreeList, iter)
- {
- // Heuristic: should we use '==' or '>=' for the size?
- if (slot->lastUse <= spilledRange->start && slot->size >= size)
- {
- StackSym *spilledSym = spilledRange->sym;
- Assert(!spilledSym->IsArgSlotSym() && !spilledSym->IsParamSlotSym());
- Assert(!spilledSym->IsAllocated());
- spilledRange->spillStackSlot = slot;
- spilledSym->m_offset = slot->offset;
- spilledSym->m_allocated = true;
- iter.RemoveCurrent();
- #if DBG_DUMP
- if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::StackPackPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
- {
- spilledSym->Dump();
- Output::Print(L" *** stack packed at offset %3d (%4d - %4d)\n", spilledSym->m_offset, spilledRange->start, spilledRange->end);
- }
- #endif
- break;
- }
- } NEXT_SLIST_ENTRY_EDITING;
- if (spilledRange->spillStackSlot == nullptr)
- {
- newStackSlot = JitAnewStruct(this->tempAlloc, StackSlot);
- newStackSlot->size = size;
- spilledRange->spillStackSlot = newStackSlot;
- }
- this->AddLiveRange(this->stackPackInUseLiveRanges, spilledRange);
- }
- if (!spilledRange->sym->IsAllocated())
- {
- // Can't stack pack, allocate new stack slot.
- StackSym *spilledSym = spilledRange->sym;
- this->func->StackAllocate(spilledSym, size);
- #if DBG_DUMP
- if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::StackPackPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
- {
- spilledSym->Dump();
- Output::Print(L" at offset %3d (%4d - %4d)\n", spilledSym->m_offset, spilledRange->start, spilledRange->end);
- }
- #endif
- if (newStackSlot != nullptr)
- {
- newStackSlot->offset = spilledSym->m_offset;
- }
- }
- }
- // LinearScan::InsertLoads
- void
- LinearScan::InsertLoads(StackSym *sym, RegNum reg)
- {
- Lifetime *lifetime = sym->scratch.linearScan.lifetime;
- FOREACH_SLIST_ENTRY(IR::Instr *, instr, &lifetime->useList)
- {
- this->InsertLoad(instr, sym, reg);
- } NEXT_SLIST_ENTRY;
- lifetime->useList.Clear();
- }
- // LinearScan::InsertStores
- void
- LinearScan::InsertStores(Lifetime *lifetime, RegNum reg, IR::Instr *insertionInstr)
- {
- StackSym *sym = lifetime->sym;
- // If single def, use instrDef on the symbol
- if (sym->m_isSingleDef)
- {
- IR::Instr * defInstr = sym->m_instrDef;
- if (!sym->IsConst() && defInstr->GetDst()->AsRegOpnd()->GetReg() == RegNOREG
- || this->secondChanceRegs.Test(reg))
- {
- // This can happen if we were trying to allocate this lifetime,
- // and it is getting spilled right away.
- // For second chance allocations, this should have already been handled.
- return;
- }
- this->InsertStore(defInstr, defInstr->FindRegDef(sym)->m_sym, reg);
- return;
- }
- if (reg == RegNOREG)
- {
- return;
- }
- uint localStoreCost = LinearScan::GetUseSpillCost(this->loopNest, (this->currentOpHelperBlock != nullptr));
- // Is it cheaper to spill all the defs we've seen so far or just insert a store at the current point?
- if ((this->func->HasTry() && !this->func->DoOptimizeTryCatch()) || localStoreCost >= lifetime->allDefsCost)
- {
- // Insert a store for each def point we've seen so far
- FOREACH_SLIST_ENTRY(IR::Instr *, instr, &(lifetime->defList))
- {
- if (instr->GetDst()->AsRegOpnd()->GetReg() != RegNOREG)
- {
- IR::RegOpnd *regOpnd = instr->FindRegDef(sym);
- // Note that reg may not be equal to regOpnd->GetReg() if the lifetime has been re-allocated since we've seen this def
- this->InsertStore(instr, regOpnd->m_sym, regOpnd->GetReg());
- }
- } NEXT_SLIST_ENTRY;
- lifetime->defList.Clear();
- lifetime->allDefsCost = 0;
- lifetime->needsStoreCompensation = false;
- }
- else if (!lifetime->defList.Empty())
- {
- // Insert a def right here at the current instr, and then we'll use compensation code for paths not covered by this def.
- if (!insertionInstr)
- {
- insertionInstr = this->currentInstr->m_prev;
- }
- this->InsertStore(insertionInstr, sym, reg);
- if (this->IsInLoop())
- {
- RecordLoopUse(lifetime, reg);
- }
- // We now need to insert all store compensations when needed, unless we spill all the defs later on.
- lifetime->needsStoreCompensation = true;
- }
- }
- // LinearScan::InsertStore
- void
- LinearScan::InsertStore(IR::Instr *instr, StackSym *sym, RegNum reg)
- {
- // Win8 Bug 391484: We cannot use regOpnd->GetType() here because it
- // can lead to truncation as downstream usage of the register might be of a size
- // greater than the current use. Using RegTypes[reg] works only if the stack slot size
- // is always at least of size MachPtr
- // In the debug mode, if the current sym belongs to the byte code locals, then do not unlink this instruction, as we need to have this instruction to be there
- // to produce the write-through instruction.
- if (sym->IsConst() && !IsSymNonTempLocalVar(sym))
- {
- // Let's just delete the def. We'll reload the constant.
- // We can't just delete the instruction however since the
- // uses will look at the def to get the value.
- // Make sure it wasn't already deleted.
- if (sym->m_instrDef->m_next)
- {
- sym->m_instrDef->Unlink();
- sym->m_instrDef->m_next = nullptr;
- }
- return;
- }
- Assert(reg != RegNOREG);
- IRType type = sym->GetType();
- if (sym->IsSimd128())
- {
- type = sym->GetType();
- }
- IR::Instr *store = IR::Instr::New(LowererMD::GetStoreOp(type),
- IR::SymOpnd::New(sym, type, this->func),
- IR::RegOpnd::New(sym, reg, type, this->func), this->func);
- instr->InsertAfter(store);
- store->CopyNumber(instr);
- this->linearScanMD.LegalizeDef(store);
- #if DBG_DUMP
- if (PHASE_TRACE(Js::LinearScanPhase, this->func))
- {
- Output::Print(L"...Inserting store for ");
- sym->Dump();
- Output::Print(L" Cost:%d\n", this->GetSpillCost(sym->scratch.linearScan.lifetime));
- }
- #endif
- }
- // LinearScan::InsertLoad
- void
- LinearScan::InsertLoad(IR::Instr *instr, StackSym *sym, RegNum reg)
- {
- IR::Opnd *src;
- // The size of loads and stores to memory need to match. See the comment
- // around type in InsertStore above.
- IRType type = sym->GetType();
- if (sym->IsSimd128())
- {
- type = sym->GetType();
- }
- bool isMovSDZero = false;
- if (sym->IsConst())
- {
- Assert(!sym->IsAllocated() || IsSymNonTempLocalVar(sym));
- // For an intConst, reload the constant instead of using the stack.
- // Create a new StackSym to make sure the old sym remains singleDef
- src = sym->GetConstOpnd();
- if (!src)
- {
- isMovSDZero = true;
- sym = StackSym::New(sym->GetType(), this->func);
- sym->m_isConst = true;
- sym->m_isFltConst = true;
- }
- else
- {
- StackSym * oldSym = sym;
- sym = StackSym::New(TyVar, this->func);
- sym->m_isConst = true;
- sym->m_isIntConst = oldSym->m_isIntConst;
- sym->m_isTaggableIntConst = sym->m_isTaggableIntConst;
- }
- }
- else
- {
- src = IR::SymOpnd::New(sym, type, this->func);
- }
- IR::Instr * load;
- #if defined(_M_IX86) || defined(_M_X64)
- if (isMovSDZero)
- {
- load = IR::Instr::New(Js::OpCode::MOVSD_ZERO,
- IR::RegOpnd::New(sym, reg, type, this->func), this->func);
- instr->InsertBefore(load);
- }
- else
- #endif
- {
- load = Lowerer::InsertMove(IR::RegOpnd::New(sym, reg, type, this->func), src, instr);
- }
- load->CopyNumber(instr);
- if (!isMovSDZero)
- {
- this->linearScanMD.LegalizeUse(load, src);
- }
- this->RecordLoopUse(nullptr, reg);
- #if DBG_DUMP
- if (PHASE_TRACE(Js::LinearScanPhase, this->func))
- {
- Output::Print(L"...Inserting load for ");
- sym->Dump();
- if (sym->scratch.linearScan.lifetime)
- {
- Output::Print(L" Cost:%d\n", this->GetSpillCost(sym->scratch.linearScan.lifetime));
- }
- else
- {
- Output::Print(L"\n");
- }
- }
- #endif
- }
- uint8
- LinearScan::GetRegAttribs(RegNum reg)
- {
- return RegAttribs[reg];
- }
- IRType
- LinearScan::GetRegType(RegNum reg)
- {
- return RegTypes[reg];
- }
- bool
- LinearScan::IsCalleeSaved(RegNum reg)
- {
- return (RegAttribs[reg] & RA_CALLEESAVE) != 0;
- }
- bool
- LinearScan::IsCallerSaved(RegNum reg) const
- {
- return !LinearScan::IsCalleeSaved(reg) && LinearScan::IsAllocatable(reg);
- }
- bool
- LinearScan::IsAllocatable(RegNum reg) const
- {
- return !(RegAttribs[reg] & RA_DONTALLOCATE) && this->linearScanMD.IsAllocatable(reg, this->func);
- }
- void
- LinearScan::KillImplicitRegs(IR::Instr *instr)
- {
- if (instr->IsLabelInstr() || instr->IsBranchInstr())
- {
- // Note: need to clear these for branch as well because this info isn't recorded for second chance
- // allocation on branch boundaries
- this->tempRegs.ClearAll();
- }
- #if defined(_M_IX86) || defined(_M_X64)
- if (instr->m_opcode == Js::OpCode::IMUL)
- {
- this->SpillReg(LowererMDArch::GetRegIMulHighDestLower());
- this->tempRegs.Clear(LowererMDArch::GetRegIMulHighDestLower());
- this->RecordLoopUse(nullptr, LowererMDArch::GetRegIMulHighDestLower());
- return;
- }
- #endif
- this->TrackInlineeArgLifetimes(instr);
- // Don't care about kills on bailout calls as we are going to exit anyways
- // Also, for bailout scenarios we have already handled the inlinee frame spills
- Assert(LowererMD::IsCall(instr) || !instr->HasBailOutInfo());
- if (!LowererMD::IsCall(instr) || instr->HasBailOutInfo())
- {
- return;
- }
- if (this->currentBlock->inlineeStack.Count() > 0)
- {
- this->SpillInlineeArgs(instr);
- }
- else
- {
- instr->m_func = this->func;
- }
- //
- // Spill caller-saved registers that are active.
- //
- BitVector deadRegs;
- deadRegs.Copy(this->activeRegs);
- deadRegs.And(this->callerSavedRegs);
- FOREACH_BITSET_IN_UNITBV(reg, deadRegs, BitVector)
- {
- this->SpillReg((RegNum)reg);
- }
- NEXT_BITSET_IN_UNITBV;
- this->tempRegs.And(this->calleeSavedRegs);
- if (callSetupRegs.Count())
- {
- callSetupRegs.ClearAll();
- }
- Loop *loop = this->curLoop;
- while (loop)
- {
- loop->regAlloc.regUseBv.Or(this->callerSavedRegs);
- loop = loop->parent;
- }
- }
- //
- // Before a call, all inlinee frame syms need to be spilled to a pre-defined location
- //
- void LinearScan::SpillInlineeArgs(IR::Instr* instr)
- {
- Assert(this->currentBlock->inlineeStack.Count() > 0);
- // Ensure the call instruction is tied to the current inlinee
- // This is used in the encoder to encode mapping or return offset and InlineeFrameRecord
- instr->m_func = this->currentBlock->inlineeStack.Last();
- BitVector spilledRegs;
- this->currentBlock->inlineeFrameLifetimes.Map([&](uint i, Lifetime* lifetime){
- Assert(lifetime->start < instr->GetNumber() && lifetime->end >= instr->GetNumber());
- Assert(!lifetime->sym->IsConst());
- Assert(this->currentBlock->inlineeFrameSyms.ContainsKey(lifetime->sym->m_id));
- if (lifetime->reg == RegNOREG)
- {
- return;
- }
- StackSym* sym = lifetime->sym;
- if (!lifetime->isSpilled && !lifetime->isOpHelperSpilled &&
- (!lifetime->isDeadStore && (lifetime->sym->m_isSingleDef || !lifetime->defList.Empty()))) // if deflist is empty - we have already spilled at all defs - and the value is current
- {
- if (!spilledRegs.Test(lifetime->reg))
- {
- spilledRegs.Set(lifetime->reg);
- if (!sym->IsAllocated())
- {
- this->AllocateStackSpace(lifetime);
- }
- this->RecordLoopUse(lifetime, lifetime->reg);
- Assert(this->regContent[lifetime->reg] != nullptr);
- if (sym->m_isSingleDef)
- {
- // For a single def - we do not track the deflist - the def below will remove the single def on the sym
- // hence, we need to track the original def.
- Assert(lifetime->defList.Empty());
- lifetime->defList.Prepend(sym->m_instrDef);
- }
- this->InsertStore(instr->m_prev, sym, lifetime->reg);
- }
- }
- });
- }
- void LinearScan::TrackInlineeArgLifetimes(IR::Instr* instr)
- {
- if (instr->m_opcode == Js::OpCode::InlineeStart)
- {
- if (instr->m_func->m_hasInlineArgsOpt)
- {
- instr->m_func->frameInfo->IterateSyms([=](StackSym* sym){
- Lifetime* lifetime = sym->scratch.linearScan.lifetime;
- this->currentBlock->inlineeFrameLifetimes.Add(lifetime);
- // We need to maintain as count because the same sym can be used for multiple arguments
- uint* value;
- if (this->currentBlock->inlineeFrameSyms.TryGetReference(sym->m_id, &value))
- {
- *value = *value + 1;
- }
- else
- {
- this->currentBlock->inlineeFrameSyms.Add(sym->m_id, 1);
- }
- });
- if (this->currentBlock->inlineeStack.Count() > 0)
- {
- Assert(instr->m_func->inlineDepth == this->currentBlock->inlineeStack.Last()->inlineDepth + 1);
- }
- this->currentBlock->inlineeStack.Add(instr->m_func);
- }
- else
- {
- Assert(this->currentBlock->inlineeStack.Count() == 0);
- }
- }
- else if (instr->m_opcode == Js::OpCode::InlineeEnd)
- {
- if (instr->m_func->m_hasInlineArgsOpt)
- {
- instr->m_func->frameInfo->AllocateRecord(this->func, instr->m_func->GetJnFunction());
- if(this->currentBlock->inlineeStack.Count() == 0)
- {
- // Block is unreachable
- Assert(this->currentBlock->inlineeFrameLifetimes.Count() == 0);
- Assert(this->currentBlock->inlineeFrameSyms.Count() == 0);
- }
- else
- {
- Func* func = this->currentBlock->inlineeStack.RemoveAtEnd();
- Assert(func == instr->m_func);
- instr->m_func->frameInfo->IterateSyms([=](StackSym* sym){
- Lifetime* lifetime = this->currentBlock->inlineeFrameLifetimes.RemoveAtEnd();
- uint* value;
- if (this->currentBlock->inlineeFrameSyms.TryGetReference(sym->m_id, &value))
- {
- *value = *value - 1;
- if (*value == 0)
- {
- bool removed = this->currentBlock->inlineeFrameSyms.Remove(sym->m_id);
- Assert(removed);
- }
- }
- else
- {
- Assert(UNREACHED);
- }
- Assert(sym->scratch.linearScan.lifetime == lifetime);
- }, /*reverse*/ true);
- }
- }
- }
- }
- // GetSpillCost
- // The spill cost is trying to estimate the usage density of the lifetime,
- // by dividing the useCount by the lifetime length.
- uint
- LinearScan::GetSpillCost(Lifetime *lifetime)
- {
- uint useCount = lifetime->GetRegionUseCount(this->curLoop);
- uint spillCost;
- // Get local spill cost. Ignore helper blocks as we'll also need compensation on the main path.
- uint localUseCost = LinearScan::GetUseSpillCost(this->loopNest, false);
- if (lifetime->reg && !lifetime->isSpilled)
- {
- // If it is in a reg, we'll need a store
- if (localUseCost >= lifetime->allDefsCost)
- {
- useCount += lifetime->allDefsCost;
- }
- else
- {
- useCount += localUseCost;
- }
- if (this->curLoop && !lifetime->sym->IsConst()
- && this->curLoop->regAlloc.liveOnBackEdgeSyms->Test(lifetime->sym->m_id))
- {
- // If we spill here, we'll need to insert a load at the bottom of the loop
- // (it would be nice to be able to check is was in a reg at the top of the loop)...
- useCount += localUseCost;
- }
- }
- // When comparing 2 lifetimes, we don't really care about the actual length of the lifetimes.
- // What matters is how much longer will they use the register.
- const uint start = currentInstr->GetNumber();
- uint end = max(start, lifetime->end);
- uint lifetimeTotalOpHelperFullVisitedLength = lifetime->totalOpHelperLengthByEnd;
- if (this->curLoop && this->curLoop->regAlloc.loopEnd < end && !PHASE_OFF(Js::RegionUseCountPhase, this->func))
- {
- end = this->curLoop->regAlloc.loopEnd;
- lifetimeTotalOpHelperFullVisitedLength = this->curLoop->regAlloc.helperLength;
- }
- uint length = end - start + 1;
- // Exclude helper block length since helper block paths are typically infrequently taken paths and not as important
- const uint totalOpHelperVisitedLength = this->totalOpHelperFullVisitedLength + CurrentOpHelperVisitedLength(currentInstr);
- Assert(lifetimeTotalOpHelperFullVisitedLength >= totalOpHelperVisitedLength);
- const uint lifetimeHelperLength = lifetimeTotalOpHelperFullVisitedLength - totalOpHelperVisitedLength;
- Assert(length >= lifetimeHelperLength);
- length -= lifetimeHelperLength;
- if(length == 0)
- {
- length = 1;
- }
- // Add a base length so that the difference between a length of 1 and a length of 2 is not so large
- #ifdef _M_X64
- length += 64;
- #else
- length += 16;
- #endif
- spillCost = (useCount << 13) / length;
- if (lifetime->isSecondChanceAllocated)
- {
- // Second chance allocation have additional overhead, so de-prioritize them
- // Note: could use more tuning...
- spillCost = spillCost * 4/5;
- }
- if (lifetime->isCheapSpill)
- {
- // This lifetime will get spilled eventually, so lower the spill cost to favor other lifetimes
- // Note: could use more tuning...
- spillCost /= 2;
- }
- if (lifetime->sym->IsConst())
- {
- spillCost = spillCost / 16;
- }
- return spillCost;
- }
- bool
- LinearScan::RemoveDeadStores(IR::Instr *instr)
- {
- IR::Opnd *dst = instr->GetDst();
- if (dst && dst->IsRegOpnd() && dst->AsRegOpnd()->m_sym && !dst->AsRegOpnd()->m_isCallArg)
- {
- IR::RegOpnd *regOpnd = dst->AsRegOpnd();
- Lifetime * lifetime = regOpnd->m_sym->scratch.linearScan.lifetime;
- if (lifetime->isDeadStore)
- {
- if (Lowerer::HasSideEffects(instr) == false)
- {
- // If all the bailouts referencing this arg are removed (which can happen in some scenarios)
- //- then it's OK to remove this def of the arg
- DebugOnly(this->func->allowRemoveBailOutArgInstr = true);
- // We are removing this instruction, end dead life time now
- this->EndDeadLifetimes(instr);
- instr->Remove();
- DebugOnly(this->func->allowRemoveBailOutArgInstr = false);
- return true;
- }
- }
- }
- return false;
- }
- void
- LinearScan::AssignActiveReg(Lifetime * lifetime, RegNum reg)
- {
- Assert(!this->activeRegs.Test(reg));
- Assert(!lifetime->isSpilled);
- Assert(lifetime->reg == RegNOREG || lifetime->reg == reg);
- this->func->m_regsUsed.Set(reg);
- lifetime->reg = reg;
- this->activeRegs.Set(reg);
- if (lifetime->isFloat || lifetime->isSimd128())
- {
- this->floatRegUsedCount++;
- }
- else
- {
- this->intRegUsedCount++;
- }
- this->AddToActive(lifetime);
- this->tempRegs.Clear(reg);
- }
- void
- LinearScan::AssignTempReg(Lifetime * lifetime, RegNum reg)
- {
- Assert(reg > RegNOREG && reg < RegNumCount);
- Assert(!this->activeRegs.Test(reg));
- Assert(lifetime->isSpilled);
- this->func->m_regsUsed.Set(reg);
- lifetime->reg = reg;
- this->tempRegs.Set(reg);
- __analysis_assume(reg > 0 && reg < RegNumCount);
- this->tempRegLifetimes[reg] = lifetime;
- this->RecordLoopUse(nullptr, reg);
- }
- RegNum
- LinearScan::GetAssignedTempReg(Lifetime * lifetime, IRType type)
- {
- if (this->tempRegs.Test(lifetime->reg) && this->tempRegLifetimes[lifetime->reg] == lifetime)
- {
- if (this->linearScanMD.FitRegIntSizeConstraints(lifetime->reg, type))
- {
- this->RecordLoopUse(nullptr, lifetime->reg);
- return lifetime->reg;
- }
- else
- {
- // Free this temp, we'll need to find another one.
- this->tempRegs.Clear(lifetime->reg);
- lifetime->reg = RegNOREG;
- }
- }
- return RegNOREG;
- }
- uint
- LinearScan::GetUseSpillCost(uint loopNest, BOOL isInHelperBlock)
- {
- if (isInHelperBlock)
- {
- // Helper block uses are not as important.
- return 0;
- }
- else if (loopNest < 6)
- {
- return (1 << (loopNest * 3));
- }
- else
- {
- // Slow growth for deep nest to avoid overflow
- return (1 << (5 * 3)) * (loopNest-5);
- }
- }
- void
- LinearScan::ProcessSecondChanceBoundary(IR::BranchInstr *branchInstr)
- {
- if (this->func->HasTry() && !this->func->DoOptimizeTryCatch())
- {
- return;
- }
- if (this->currentOpHelperBlock && this->currentOpHelperBlock->opHelperEndInstr == branchInstr)
- {
- // Lifetimes opHelperSpilled won't get recorded by SaveRegContent(). Do it here.
- FOREACH_SLIST_ENTRY(Lifetime *, lifetime, this->opHelperSpilledLiveranges)
- {
- if (!lifetime->cantOpHelperSpill)
- {
- if (lifetime->isSecondChanceAllocated)
- {
- this->secondChanceRegs.Set(lifetime->reg);
- }
- this->regContent[lifetime->reg] = lifetime;
- }
- } NEXT_SLIST_ENTRY;
- }
- if(branchInstr->IsMultiBranch())
- {
- IR::MultiBranchInstr * multiBranchInstr = branchInstr->AsMultiBrInstr();
- multiBranchInstr->MapUniqueMultiBrLabels([=](IR::LabelInstr * branchLabel) -> void
- {
- this->ProcessSecondChanceBoundaryHelper(branchInstr, branchLabel);
- });
- }
- else
- {
- IR::LabelInstr *branchLabel = branchInstr->GetTarget();
- this->ProcessSecondChanceBoundaryHelper(branchInstr, branchLabel);
- }
- this->SaveRegContent(branchInstr);
- }
- void
- LinearScan::ProcessSecondChanceBoundaryHelper(IR::BranchInstr *branchInstr, IR::LabelInstr *branchLabel)
- {
- if (branchInstr->GetNumber() > branchLabel->GetNumber())
- {
- // Loop back-edge
- Assert(branchLabel->m_isLoopTop);
- branchInstr->m_regContent = nullptr;
- this->InsertSecondChanceCompensation(this->regContent, branchLabel->m_regContent, branchInstr, branchLabel);
- }
- else
- {
- // Forward branch
- this->SaveRegContent(branchInstr);
- if (this->curLoop)
- {
- this->curLoop->regAlloc.exitRegContentList->Prepend(branchInstr->m_regContent);
- }
- if (!branchLabel->m_loweredBasicBlock)
- {
- if (branchInstr->IsConditional() || branchInstr->IsMultiBranch())
- {
- // Clone with deep copy
- branchLabel->m_loweredBasicBlock = this->currentBlock->Clone(this->tempAlloc);
- }
- else
- {
- // If the unconditional branch leads to the end of the function for the scenario of a bailout - we do not want to
- // copy the lowered inlinee info.
- IR::Instr* nextInstr = branchLabel->GetNextRealInstr();
- if (nextInstr->m_opcode != Js::OpCode::FunctionExit &&
- nextInstr->m_opcode != Js::OpCode::BailOutStackRestore &&
- this->currentBlock->HasData())
- {
- // Clone with shallow copy
- branchLabel->m_loweredBasicBlock = this->currentBlock;
- }
- }
- }
- else
- {
- // The lowerer sometimes generates unreachable blocks that would have empty data.
- Assert(!currentBlock->HasData() || branchLabel->m_loweredBasicBlock->Equals(this->currentBlock));
- }
- }
- }
- void
- LinearScan::ProcessSecondChanceBoundary(IR::LabelInstr *labelInstr)
- {
- if (this->func->HasTry() && !this->func->DoOptimizeTryCatch())
- {
- return;
- }
- if (labelInstr->m_isLoopTop)
- {
- this->SaveRegContent(labelInstr);
- Lifetime ** regContent = AnewArrayZ(this->tempAlloc, Lifetime *, RegNumCount);
- js_memcpy_s(regContent, (RegNumCount * sizeof(Lifetime *)), this->regContent, sizeof(this->regContent));
- this->curLoop->regAlloc.loopTopRegContent = regContent;
- }
- FOREACH_SLISTCOUNTED_ENTRY_EDITING(IR::BranchInstr *, branchInstr, &labelInstr->labelRefs, iter)
- {
- if (branchInstr->m_isAirlock)
- {
- // This branch was just inserted... Skip it.
- continue;
- }
- Assert(branchInstr->GetNumber() && labelInstr->GetNumber());
- if (branchInstr->GetNumber() < labelInstr->GetNumber())
- {
- // Normal branch
- this->InsertSecondChanceCompensation(branchInstr->m_regContent, this->regContent, branchInstr, labelInstr);
- }
- else
- {
- // Loop back-edge
- Assert(labelInstr->m_isLoopTop);
- }
- } NEXT_SLISTCOUNTED_ENTRY_EDITING;
- }
- IR::Instr * LinearScan::EnsureAirlock(bool needsAirlock, bool *pHasAirlock, IR::Instr *insertionInstr,
- IR::Instr **pInsertionStartInstr, IR::BranchInstr *branchInstr, IR::LabelInstr *labelInstr)
- {
- if (needsAirlock && !(*pHasAirlock))
- {
- // We need an extra block for the compensation code.
- insertionInstr = this->InsertAirlock(branchInstr, labelInstr);
- *pInsertionStartInstr = insertionInstr->m_prev;
- *pHasAirlock = true;
- }
- return insertionInstr;
- }
- bool LinearScan::NeedsLoopBackEdgeCompensation(Lifetime *lifetime, IR::LabelInstr *loopTopLabel)
- {
- if (!lifetime)
- {
- return false;
- }
- if (lifetime->sym->IsConst())
- {
- return false;
- }
- // No need if lifetime begins in the loop
- if (lifetime->start > loopTopLabel->GetNumber())
- {
- return false;
- }
- // Only needed if lifetime is live on the back-edge, and the register is used inside the loop, or the lifetime extends
- // beyond the loop (and compensation out of the loop may use this reg)...
- if (!loopTopLabel->GetLoop()->regAlloc.liveOnBackEdgeSyms->Test(lifetime->sym->m_id)
- || (this->currentInstr->GetNumber() >= lifetime->end && !this->curLoop->regAlloc.symRegUseBv->Test(lifetime->sym->m_id)))
- {
- return false;
- }
- return true;
- }
- void
- LinearScan::InsertSecondChanceCompensation(Lifetime ** branchRegContent, Lifetime **labelRegContent,
- IR::BranchInstr *branchInstr, IR::LabelInstr *labelInstr)
- {
- IR::Instr *prevInstr = branchInstr->GetPrevRealInstrOrLabel();
- bool needsAirlock = branchInstr->IsConditional() || (prevInstr->IsBranchInstr() && prevInstr->AsBranchInstr()->IsConditional() || branchInstr->IsMultiBranch());
- bool hasAirlock = false;
- IR::Instr *insertionInstr = branchInstr;
- IR::Instr *insertionStartInstr = branchInstr->m_prev;
- // For loop back-edge, we want to keep the insertionStartInstr before the branch as spill need to happen on all paths
- // Pass a dummy instr address to airLockBlock insertion code.
- BitVector thrashedRegs(0);
- bool isLoopBackEdge = (this->regContent == branchRegContent);
- Lifetime * tmpRegContent[RegNumCount];
- Lifetime **regContent = this->regContent;
- if (isLoopBackEdge)
- {
- Loop *loop = labelInstr->GetLoop();
- js_memcpy_s(&tmpRegContent, (RegNumCount * sizeof(Lifetime *)), this->regContent, sizeof(this->regContent));
- branchRegContent = tmpRegContent;
- regContent = tmpRegContent;
- #if defined(_M_IX86) || defined(_M_X64)
- // Insert XCHG to avoid some conflicts for int regs
- // Note: no XCHG on ARM or SSE2. We could however use 3 XOR on ARM...
- this->AvoidCompensationConflicts(labelInstr, branchInstr, labelRegContent, branchRegContent,
- &insertionInstr, &insertionStartInstr, needsAirlock, &hasAirlock);
- #endif
- FOREACH_BITSET_IN_UNITBV(reg, this->secondChanceRegs, BitVector)
- {
- Lifetime *labelLifetime = labelRegContent[reg];
- Lifetime *lifetime = branchRegContent[reg];
- // 1. Insert Stores
- // Lifetime starts before the loop
- // Lifetime was re-allocated within the loop (i.e.: a load was most likely inserted)
- // Lifetime is live on back-edge and has unsaved defs.
- if (lifetime && lifetime->start < labelInstr->GetNumber() && lifetime->lastAllocationStart > labelInstr->GetNumber()
- && (labelInstr->GetLoop()->regAlloc.liveOnBackEdgeSyms->Test(lifetime->sym->m_id))
- && !lifetime->defList.Empty())
- {
- insertionInstr = this->EnsureAirlock(needsAirlock, &hasAirlock, insertionInstr, &insertionStartInstr, branchInstr, labelInstr);
- // If the lifetime was second chance allocated inside the loop, there might
- // be spilled loads of this symbol in the loop. Insert the stores.
- // We don't need to do this if the lifetime was re-allocated before the loop.
- //
- // Note that reg may not be equal to lifetime->reg because of inserted XCHG...
- this->InsertStores(lifetime, lifetime->reg, insertionStartInstr);
- }
- if (lifetime == labelLifetime)
- {
- continue;
- }
- // 2. MOV labelReg/MEM, branchReg
- // Move current register to match content at the top of the loop
- if (this->NeedsLoopBackEdgeCompensation(lifetime, labelInstr))
- {
- // Mismatch, we need to insert compensation code
- insertionInstr = this->EnsureAirlock(needsAirlock, &hasAirlock, insertionInstr, &insertionStartInstr, branchInstr, labelInstr);
- // MOV ESI, EAX
- // MOV EDI, ECX
- // MOV ECX, ESI
- // MOV EAX, EDI <<<
- this->ReconcileRegContent(branchRegContent, labelRegContent, branchInstr, labelInstr,
- lifetime, (RegNum)reg, &thrashedRegs, insertionInstr, insertionStartInstr);
- }
- // 2. MOV labelReg, MEM
- // Lifetime was in a reg at the top of the loop but is spilled right now.
- if (labelLifetime && labelLifetime->isSpilled && !labelLifetime->sym->IsConst() && labelLifetime->end >= branchInstr->GetNumber())
- {
- if (!loop->regAlloc.liveOnBackEdgeSyms->Test(labelLifetime->sym->m_id))
- {
- continue;
- }
- if (this->ClearLoopExitIfRegUnused(labelLifetime, (RegNum)reg, branchInstr, loop))
- {
- continue;
- }
- insertionInstr = this->EnsureAirlock(needsAirlock, &hasAirlock, insertionInstr, &insertionStartInstr, branchInstr, labelInstr);
- this->ReconcileRegContent(branchRegContent, labelRegContent, branchInstr, labelInstr,
- labelLifetime, (RegNum)reg, &thrashedRegs, insertionInstr, insertionStartInstr);
- }
- } NEXT_BITSET_IN_UNITBV;
- // 3. MOV labelReg, MEM
- // Finish up reloading lifetimes needed at the top. #2 only handled secondChanceRegs.
- FOREACH_REG(reg)
- {
- // Handle lifetimes in a register at the top of the loop, but not currently.
- Lifetime *labelLifetime = labelRegContent[reg];
- if (labelLifetime && !labelLifetime->sym->IsConst() && labelLifetime != branchRegContent[reg] && !thrashedRegs.Test(reg)
- && (loop->regAlloc.liveOnBackEdgeSyms->Test(labelLifetime->sym->m_id)))
- {
- if (this->ClearLoopExitIfRegUnused(labelLifetime, (RegNum)reg, branchInstr, loop))
- {
- continue;
- }
- // Mismatch, we need to insert compensation code
- insertionInstr = this->EnsureAirlock(needsAirlock, &hasAirlock, insertionInstr, &insertionStartInstr, branchInstr, labelInstr);
- this->ReconcileRegContent(branchRegContent, labelRegContent, branchInstr, labelInstr,
- labelLifetime, (RegNum)reg, &thrashedRegs, insertionInstr, insertionStartInstr);
- }
- } NEXT_REG;
- if (hasAirlock)
- {
- loop->regAlloc.hasAirLock = true;
- }
- }
- else
- {
- //
- // Non-loop-back-edge merge
- //
- FOREACH_REG(reg)
- {
- Lifetime *branchLifetime = branchRegContent[reg];
- Lifetime *lifetime = regContent[reg];
- if (lifetime == branchLifetime)
- {
- continue;
- }
- if (branchLifetime && branchLifetime->isSpilled && !branchLifetime->sym->IsConst() && branchLifetime->end > labelInstr->GetNumber())
- {
- // The lifetime was in a reg at the branch and is now spilled. We need a store on this path.
- //
- // MOV MEM, branch_REG
- insertionInstr = this->EnsureAirlock(needsAirlock, &hasAirlock, insertionInstr, &insertionStartInstr, branchInstr, labelInstr);
- this->ReconcileRegContent(branchRegContent, labelRegContent, branchInstr, labelInstr,
- branchLifetime, (RegNum)reg, &thrashedRegs, insertionInstr, insertionStartInstr);
- }
- if (lifetime && !lifetime->sym->IsConst() && lifetime->start <= branchInstr->GetNumber())
- {
- // MOV label_REG, branch_REG / MEM
- insertionInstr = this->EnsureAirlock(needsAirlock, &hasAirlock, insertionInstr, &insertionStartInstr, branchInstr, labelInstr);
- this->ReconcileRegContent(branchRegContent, labelRegContent, branchInstr, labelInstr,
- lifetime, (RegNum)reg, &thrashedRegs, insertionInstr, insertionStartInstr);
- }
- } NEXT_REG;
- }
- if (hasAirlock)
- {
- // Fix opHelper on airlock label.
- if (insertionInstr->m_prev->IsLabelInstr() && insertionInstr->IsLabelInstr())
- {
- if (insertionInstr->m_prev->AsLabelInstr()->isOpHelper && !insertionInstr->AsLabelInstr()->isOpHelper)
- {
- insertionInstr->m_prev->AsLabelInstr()->isOpHelper = false;
- }
- }
- }
- }
- void
- LinearScan::ReconcileRegContent(Lifetime ** branchRegContent, Lifetime **labelRegContent,
- IR::BranchInstr *branchInstr, IR::LabelInstr *labelInstr,
- Lifetime *lifetime, RegNum reg, BitVector *thrashedRegs, IR::Instr *insertionInstr, IR::Instr *insertionStartInstr)
- {
- RegNum originalReg = RegNOREG;
- IRType type = RegTypes[reg];
- Assert(labelRegContent[reg] != branchRegContent[reg]);
- bool matchBranchReg = (branchRegContent[reg] == lifetime);
- Lifetime **originalRegContent = (matchBranchReg ? labelRegContent : branchRegContent);
- bool isLoopBackEdge = (branchInstr->GetNumber() > labelInstr->GetNumber());
- if (lifetime->sym->IsConst())
- {
- return;
- }
- // Look if this lifetime was in a different register in the previous block.
- // Split the search in 2 to speed this up.
- if (type == TyMachReg)
- {
- FOREACH_INT_REG(regIter)
- {
- if (originalRegContent[regIter] == lifetime)
- {
- originalReg = regIter;
- break;
- }
- } NEXT_INT_REG;
- }
- else
- {
- Assert(type == TyFloat64 || IRType_IsSimd128(type));
- FOREACH_FLOAT_REG(regIter)
- {
- if (originalRegContent[regIter] == lifetime)
- {
- originalReg = regIter;
- break;
- }
- } NEXT_FLOAT_REG;
- }
- RegNum branchReg, labelReg;
- if (matchBranchReg)
- {
- branchReg = reg;
- labelReg = originalReg;
- }
- else
- {
- branchReg = originalReg;
- labelReg = reg;
- }
- if (branchReg != RegNOREG && !thrashedRegs->Test(branchReg) && !lifetime->sym->IsConst())
- {
- Assert(branchRegContent[branchReg] == lifetime);
- if (labelReg != RegNOREG)
- {
- // MOV labelReg, branchReg
- Assert(labelRegContent[labelReg] == lifetime);
- IR::Instr *load = IR::Instr::New(LowererMD::GetLoadOp(type),
- IR::RegOpnd::New(lifetime->sym, labelReg, type, this->func),
- IR::RegOpnd::New(lifetime->sym, branchReg, type, this->func), this->func);
- insertionInstr->InsertBefore(load);
- load->CopyNumber(insertionInstr);
- // symRegUseBv needs to be set properly. Unfortunately, we need to go conservative as we don't know
- // which allocation it was at the source of the branch.
- if (this->IsInLoop())
- {
- this->RecordLoopUse(lifetime, branchReg);
- }
- thrashedRegs->Set(labelReg);
- }
- else if (!lifetime->sym->IsSingleDef() && lifetime->needsStoreCompensation && !isLoopBackEdge)
- {
- Assert(!lifetime->sym->IsConst());
- Assert(matchBranchReg);
- Assert(branchRegContent[branchReg] == lifetime);
- // MOV mem, branchReg
- this->InsertStores(lifetime, branchReg, insertionInstr->m_prev);
- // symRegUseBv needs to be set properly. Unfortunately, we need to go conservative as we don't know
- // which allocation it was at the source of the branch.
- if (this->IsInLoop())
- {
- this->RecordLoopUse(lifetime, branchReg);
- }
- }
- }
- else if (labelReg != RegNOREG)
- {
- Assert(labelRegContent[labelReg] == lifetime);
- Assert(lifetime->sym->IsConst() || lifetime->sym->IsAllocated());
- if (branchReg != RegNOREG && !lifetime->sym->IsSingleDef())
- {
- Assert(thrashedRegs->Test(branchReg));
- // We can't insert a "MOV labelReg, branchReg" at the insertion point
- // because branchReg was thrashed by a previous reload.
- // Look for that reload to see if we can insert before it.
- IR::Instr *newInsertionInstr = insertionInstr->m_prev;
- bool foundIt = false;
- while (LowererMD::IsAssign(newInsertionInstr))
- {
- IR::Opnd *dst = newInsertionInstr->GetDst();
- IR::Opnd *src = newInsertionInstr->GetSrc1();
- if (src->IsRegOpnd() && src->AsRegOpnd()->GetReg() == labelReg)
- {
- // This uses labelReg, give up...
- break;
- }
- if (dst->IsRegOpnd() && dst->AsRegOpnd()->GetReg() == branchReg)
- {
- // Success!
- foundIt = true;
- break;
- }
- newInsertionInstr = newInsertionInstr->m_prev;
- }
- if (foundIt)
- {
- // MOV labelReg, branchReg
- Assert(labelRegContent[labelReg] == lifetime);
- IR::Instr *load = IR::Instr::New(LowererMD::GetLoadOp(type),
- IR::RegOpnd::New(lifetime->sym, labelReg, type, this->func),
- IR::RegOpnd::New(lifetime->sym, branchReg, type, this->func), this->func);
- newInsertionInstr->InsertBefore(load);
- load->CopyNumber(newInsertionInstr);
- // symRegUseBv needs to be set properly. Unfortunately, we need to go conservative as we don't know
- // which allocation it was at the source of the branch.
- if (this->IsInLoop())
- {
- this->RecordLoopUse(lifetime, branchReg);
- }
- thrashedRegs->Set(labelReg);
- return;
- }
- Assert(thrashedRegs->Test(branchReg));
- this->InsertStores(lifetime, branchReg, insertionStartInstr);
- // symRegUseBv needs to be set properly. Unfortunately, we need to go conservative as we don't know
- // which allocation it was at the source of the branch.
- if (this->IsInLoop())
- {
- this->RecordLoopUse(lifetime, branchReg);
- }
- }
- // MOV labelReg, mem
- this->InsertLoad(insertionInstr, lifetime->sym, labelReg);
- thrashedRegs->Set(labelReg);
- }
- else if (!lifetime->sym->IsConst())
- {
- Assert(matchBranchReg);
- Assert(branchReg != RegNOREG);
- // The lifetime was in a register at the top of the loop, but we thrashed it with a previous reload...
- if (!lifetime->sym->IsSingleDef())
- {
- this->InsertStores(lifetime, branchReg, insertionStartInstr);
- }
- #if DBG_DUMP
- if (PHASE_TRACE(Js::SecondChancePhase, this->func))
- {
- Output::Print(L"****** Spilling reg because of bad compensation code order: ");
- lifetime->sym->Dump();
- Output::Print(L"\n");
- }
- #endif
- }
- }
- bool LinearScan::ClearLoopExitIfRegUnused(Lifetime *lifetime, RegNum reg, IR::BranchInstr *branchInstr, Loop *loop)
- {
- // If a lifetime was enregistered into the loop and then spilled, we need compensation at the bottom
- // of the loop to reload the lifetime into that register.
- // If that lifetime was spilled before it was ever used, we don't need the compensation code.
- // We do however need to clear the regContent on any loop exit as the register will not
- // be available anymore on that path.
- // Note: If the lifetime was reloaded into the same register, we might clear the regContent unnecessarily...
- if (!PHASE_OFF(Js::ClearRegLoopExitPhase, this->func))
- {
- return false;
- }
- if (!loop->regAlloc.symRegUseBv->Test(lifetime->sym->m_id) && !lifetime->needsStoreCompensation)
- {
- if (lifetime->end > branchInstr->GetNumber())
- {
- FOREACH_SLIST_ENTRY(Lifetime **, regContent, loop->regAlloc.exitRegContentList)
- {
- if (regContent[reg] == lifetime)
- {
- regContent[reg] = nullptr;
- }
- } NEXT_SLIST_ENTRY;
- }
- return true;
- }
- return false;
- }
- #if defined(_M_IX86) || defined(_M_X64)
- void LinearScan::AvoidCompensationConflicts(IR::LabelInstr *labelInstr, IR::BranchInstr *branchInstr,
- Lifetime *labelRegContent[], Lifetime *branchRegContent[],
- IR::Instr **pInsertionInstr, IR::Instr **pInsertionStartInstr, bool needsAirlock, bool *pHasAirlock)
- {
- bool changed = true;
- // Look for conflicts in the incoming compensation code:
- // MOV ESI, EAX
- // MOV ECX, ESI << ESI was lost...
- // Using XCHG:
- // XCHG ESI, EAX
- // MOV ECX, EAX
- //
- // Note that we need to iterate while(changed) to catch all conflicts
- while(changed) {
- RegNum conflictRegs[RegNumCount] = {RegNOREG};
- changed = false;
- FOREACH_BITSET_IN_UNITBV(reg, this->secondChanceRegs, BitVector)
- {
- Lifetime *labelLifetime = labelRegContent[reg];
- Lifetime *lifetime = branchRegContent[reg];
- // We don't have an XCHG for SSE2 regs
- if (lifetime == labelLifetime || IRType_IsFloat(RegTypes[reg]))
- {
- continue;
- }
- if (this->NeedsLoopBackEdgeCompensation(lifetime, labelInstr))
- {
- // Mismatch, we need to insert compensation code
- *pInsertionInstr = this->EnsureAirlock(needsAirlock, pHasAirlock, *pInsertionInstr, pInsertionStartInstr, branchInstr, labelInstr);
- if (conflictRegs[reg] != RegNOREG)
- {
- // Eliminate conflict with an XCHG
- IR::RegOpnd *reg1 = IR::RegOpnd::New(branchRegContent[reg]->sym, (RegNum)reg, RegTypes[reg], this->func);
- IR::RegOpnd *reg2 = IR::RegOpnd::New(branchRegContent[reg]->sym, conflictRegs[reg], RegTypes[reg], this->func);
- IR::Instr *instrXchg = IR::Instr::New(Js::OpCode::XCHG, reg1, reg1, reg2, this->func);
- (*pInsertionInstr)->InsertBefore(instrXchg);
- instrXchg->CopyNumber(*pInsertionInstr);
- Lifetime *tmpLifetime = branchRegContent[reg];
- branchRegContent[reg] = branchRegContent[conflictRegs[reg]];
- branchRegContent[conflictRegs[reg]] = tmpLifetime;
- reg = conflictRegs[reg];
- changed = true;
- }
- RegNum labelReg = RegNOREG;
- FOREACH_INT_REG(regIter)
- {
- if (labelRegContent[regIter] == branchRegContent[reg])
- {
- labelReg = regIter;
- break;
- }
- } NEXT_INT_REG;
- if (labelReg != RegNOREG)
- {
- conflictRegs[labelReg] = (RegNum)reg;
- }
- }
- } NEXT_BITSET_IN_UNITBV;
- }
- }
- #endif
- RegNum
- LinearScan::SecondChanceAllocation(Lifetime *lifetime, bool force)
- {
- if (PHASE_OFF(Js::SecondChancePhase, this->func) || this->func->HasTry())
- {
- return RegNOREG;
- }
- // Don't start a second chance allocation from a helper block
- if (lifetime->dontAllocate || this->IsInHelperBlock() || lifetime->isDeadStore)
- {
- return RegNOREG;
- }
- Assert(lifetime->isSpilled);
- Assert(lifetime->sym->IsConst() || lifetime->sym->IsAllocated());
- RegNum oldReg = lifetime->reg;
- RegNum reg;
- if (lifetime->start == this->currentInstr->GetNumber() || lifetime->end == this->currentInstr->GetNumber())
- {
- // No point doing second chance if the lifetime ends here, or starts here (normal allocation would
- // have found a register if one is available).
- return RegNOREG;
- }
- if (lifetime->sym->IsConst())
- {
- // Can't second-chance allocate because we might have deleted the initial def instr, after
- // having set the reg content on a forward branch...
- return RegNOREG;
- }
- lifetime->reg = RegNOREG;
- lifetime->isSecondChanceAllocated = true;
- reg = this->FindReg(lifetime, nullptr, force);
- lifetime->reg = oldReg;
- if (reg == RegNOREG)
- {
- lifetime->isSecondChanceAllocated = false;
- return reg;
- }
- // Success!! We're re-allocating this lifetime...
- this->SecondChanceAllocateToReg(lifetime, reg);
- return reg;
- }
- void LinearScan::SecondChanceAllocateToReg(Lifetime *lifetime, RegNum reg)
- {
- RegNum oldReg = lifetime->reg;
- if (oldReg != RegNOREG && this->tempRegLifetimes[oldReg] == lifetime)
- {
- this->tempRegs.Clear(oldReg);
- }
- lifetime->isSpilled = false;
- lifetime->isSecondChanceAllocated = true;
- lifetime->lastAllocationStart = this->currentInstr->GetNumber();
- lifetime->reg = RegNOREG;
- this->AssignActiveReg(lifetime, reg);
- this->secondChanceRegs.Set(reg);
- lifetime->sym->scratch.linearScan.lifetime->useList.Clear();
- #if DBG_DUMP
- if (PHASE_TRACE(Js::SecondChancePhase, this->func))
- {
- Output::Print(L"**** Second chance: ");
- lifetime->sym->Dump();
- Output::Print(L"\t Reg: %S ", RegNames[reg]);
- Output::Print(L" SpillCount:%d Length:%d Cost:%d %S\n",
- lifetime->useCount, lifetime->end - lifetime->start, this->GetSpillCost(lifetime),
- lifetime->isLiveAcrossCalls ? "LiveAcrossCalls" : "");
- }
- #endif
- }
- IR::Instr *
- LinearScan::InsertAirlock(IR::BranchInstr *branchInstr, IR::LabelInstr *labelInstr)
- {
- // Insert a new block on a flow arc:
- // JEQ L1 JEQ L2
- // ... => ...
- // <fallthrough> JMP L1
- // L1: L2:
- // <new block>
- // L1:
- // An airlock is needed when we need to add code on a flow arc, and the code can't
- // be added directly at the source or sink of that flow arc without impacting other
- // code paths.
- bool isOpHelper = labelInstr->isOpHelper;
- if (!isOpHelper)
- {
- // Check if branch is coming from helper block.
- IR::Instr *prevLabel = branchInstr->m_prev;
- while (prevLabel && !prevLabel->IsLabelInstr())
- {
- prevLabel = prevLabel->m_prev;
- }
- if (prevLabel && prevLabel->AsLabelInstr()->isOpHelper)
- {
- isOpHelper = true;
- }
- }
- IR::LabelInstr *airlockLabel = IR::LabelInstr::New(Js::OpCode::Label, this->func, isOpHelper);
- airlockLabel->SetRegion(this->currentRegion);
- #if DBG
- if (isOpHelper)
- {
- if (branchInstr->m_isHelperToNonHelperBranch)
- {
- labelInstr->m_noHelperAssert = true;
- }
- if (labelInstr->isOpHelper && labelInstr->m_noHelperAssert)
- {
- airlockLabel->m_noHelperAssert = true;
- }
- }
- #endif
- bool replaced = branchInstr->ReplaceTarget(labelInstr, airlockLabel);
- Assert(replaced);
- IR::Instr * prevInstr = labelInstr->GetPrevRealInstrOrLabel();
- if (prevInstr->HasFallThrough())
- {
- IR::BranchInstr *branchOverAirlock = IR::BranchInstr::New(LowererMD::MDUncondBranchOpcode, labelInstr, this->func);
- prevInstr->InsertAfter(branchOverAirlock);
- branchOverAirlock->CopyNumber(prevInstr);
- prevInstr = branchOverAirlock;
- branchOverAirlock->m_isAirlock = true;
- branchOverAirlock->m_regContent = nullptr;
- }
- prevInstr->InsertAfter(airlockLabel);
- airlockLabel->CopyNumber(prevInstr);
- prevInstr = labelInstr->GetPrevRealInstrOrLabel();
- return labelInstr;
- }
- void
- LinearScan::SaveRegContent(IR::Instr *instr)
- {
- bool isLabelLoopTop = false;
- Lifetime ** regContent = AnewArrayZ(this->tempAlloc, Lifetime *, RegNumCount);
- if (instr->IsBranchInstr())
- {
- instr->AsBranchInstr()->m_regContent = regContent;
- }
- else
- {
- Assert(instr->IsLabelInstr());
- Assert(instr->AsLabelInstr()->m_isLoopTop);
- instr->AsLabelInstr()->m_regContent = regContent;
- isLabelLoopTop = true;
- }
- js_memcpy_s(regContent, (RegNumCount * sizeof(Lifetime *)), this->regContent, sizeof(this->regContent));
- #if DBG
- FOREACH_SLIST_ENTRY(Lifetime *, lifetime, this->activeLiveranges)
- {
- Assert(regContent[lifetime->reg] == lifetime);
- } NEXT_SLIST_ENTRY;
- #endif
- }
- bool LinearScan::RegsAvailable(IRType type)
- {
- if (IRType_IsFloat(type) || IRType_IsSimd128(type))
- {
- return (this->floatRegUsedCount < FLOAT_REG_COUNT);
- }
- else
- {
- return (this->intRegUsedCount < INT_REG_COUNT);
- }
- }
- uint LinearScan::GetRemainingHelperLength(Lifetime *const lifetime)
- {
- // Walk the helper block linked list starting from the next helper block until the end of the lifetime
- uint helperLength = 0;
- SList<OpHelperBlock>::Iterator it(opHelperBlockIter);
- Assert(it.IsValid());
- const uint end = max(currentInstr->GetNumber(), lifetime->end);
- do
- {
- const OpHelperBlock &helper = it.Data();
- const uint helperStart = helper.opHelperLabel->GetNumber();
- if(helperStart > end)
- {
- break;
- }
- const uint helperEnd = min(end, helper.opHelperEndInstr->GetNumber());
- helperLength += helperEnd - helperStart;
- if(helperEnd != helper.opHelperEndInstr->GetNumber() || !helper.opHelperEndInstr->IsLabelInstr())
- {
- // A helper block that ends at a label does not return to the function. Since this helper block does not end
- // at a label, include the end instruction as well.
- ++helperLength;
- }
- } while(it.Next());
- return helperLength;
- }
- uint LinearScan::CurrentOpHelperVisitedLength(IR::Instr *const currentInstr) const
- {
- Assert(currentInstr);
- if(!currentOpHelperBlock)
- {
- return 0;
- }
- // Consider the current instruction to have not yet been visited
- Assert(currentInstr->GetNumber() >= currentOpHelperBlock->opHelperLabel->GetNumber());
- return currentInstr->GetNumber() - currentOpHelperBlock->opHelperLabel->GetNumber();
- }
- IR::Instr * LinearScan::TryHoistLoad(IR::Instr *instr, Lifetime *lifetime)
- {
- // If we are loading a lifetime into a register inside a loop, try to hoist that load outside the loop
- // if that register hasn't been used yet.
- RegNum reg = lifetime->reg;
- IR::Instr *insertInstr = instr;
- if (PHASE_OFF(Js::RegHoistLoadsPhase, this->func))
- {
- return insertInstr;
- }
- if ((this->func->HasTry() && !this->func->DoOptimizeTryCatch()) || (this->currentRegion && this->currentRegion->GetType() != RegionTypeRoot))
- {
- return insertInstr;
- }
- // Register unused, and lifetime unused yet.
- if (this->IsInLoop() && !this->curLoop->regAlloc.regUseBv.Test(reg)
- && !this->curLoop->regAlloc.defdInLoopBv->Test(lifetime->sym->m_id)
- && !this->curLoop->regAlloc.symRegUseBv->Test(lifetime->sym->m_id)
- && !this->curLoop->regAlloc.hasAirLock)
- {
- // Let's hoist!
- insertInstr = insertInstr->m_prev;
- // Walk each instructions until the top of the loop looking for branches
- while (!insertInstr->IsLabelInstr() || !insertInstr->AsLabelInstr()->m_isLoopTop || !insertInstr->AsLabelInstr()->GetLoop()->IsDescendentOrSelf(this->curLoop))
- {
- if (insertInstr->IsBranchInstr() && insertInstr->AsBranchInstr()->m_regContent)
- {
- IR::BranchInstr *branchInstr = insertInstr->AsBranchInstr();
- // That lifetime might have been in another register coming into the loop, and spilled before used.
- // Clear the reg content.
- FOREACH_REG(regIter)
- {
- if (branchInstr->m_regContent[regIter] == lifetime)
- {
- branchInstr->m_regContent[regIter] = nullptr;
- }
- } NEXT_REG;
- // Set the regContent for that reg to the lifetime on this branch
- branchInstr->m_regContent[reg] = lifetime;
- }
- insertInstr = insertInstr->m_prev;
- }
- IR::LabelInstr *loopTopLabel = insertInstr->AsLabelInstr();
- // Set the reg content for the loop top correctly as well
- FOREACH_REG(regIter)
- {
- if (loopTopLabel->m_regContent[regIter] == lifetime)
- {
- loopTopLabel->m_regContent[regIter] = nullptr;
- this->curLoop->regAlloc.loopTopRegContent[regIter] = nullptr;
- }
- } NEXT_REG;
- Assert(loopTopLabel->GetLoop() == this->curLoop);
- loopTopLabel->m_regContent[reg] = lifetime;
- this->curLoop->regAlloc.loopTopRegContent[reg] = lifetime;
- this->RecordLoopUse(lifetime, reg);
- IR::LabelInstr *loopLandingPad = nullptr;
- Assert(loopTopLabel->GetNumber() != Js::Constants::NoByteCodeOffset);
- // Insert load in landing pad.
- // Redirect branches to new landing pad.
- FOREACH_SLISTCOUNTED_ENTRY_EDITING(IR::BranchInstr *, branchInstr, &loopTopLabel->labelRefs, iter)
- {
- Assert(branchInstr->GetNumber() != Js::Constants::NoByteCodeOffset);
- // <= because the branch may be newly inserted and have the same instr number as the loop top...
- if (branchInstr->GetNumber() <= loopTopLabel->GetNumber())
- {
- if (!loopLandingPad)
- {
- loopLandingPad = IR::LabelInstr::New(Js::OpCode::Label, this->func);
- loopLandingPad->SetRegion(this->currentRegion);
- loopTopLabel->InsertBefore(loopLandingPad);
- loopLandingPad->CopyNumber(loopTopLabel);
- }
- branchInstr->ReplaceTarget(loopTopLabel, loopLandingPad);
- }
- } NEXT_SLISTCOUNTED_ENTRY_EDITING;
- }
- return insertInstr;
- }
- #if DBG_DUMP
- void LinearScan::PrintStats() const
- {
- uint loopNest = 0;
- uint storeCount = 0;
- uint loadCount = 0;
- uint wStoreCount = 0;
- uint wLoadCount = 0;
- uint instrCount = 0;
- bool isInHelper = false;
- FOREACH_INSTR_IN_FUNC_BACKWARD(instr, this->func)
- {
- switch (instr->GetKind())
- {
- case IR::InstrKindPragma:
- continue;
- case IR::InstrKindBranch:
- if (instr->AsBranchInstr()->IsLoopTail(this->func))
- {
- loopNest++;
- }
- instrCount++;
- break;
- case IR::InstrKindLabel:
- case IR::InstrKindProfiledLabel:
- if (instr->AsLabelInstr()->m_isLoopTop)
- {
- Assert(loopNest);
- loopNest--;
- }
- isInHelper = instr->AsLabelInstr()->isOpHelper;
- break;
- default:
- {
- Assert(instr->IsRealInstr());
- if (isInHelper)
- {
- continue;
- }
- IR::Opnd *dst = instr->GetDst();
- if (dst && dst->IsSymOpnd() && dst->AsSymOpnd()->m_sym->IsStackSym() && dst->AsSymOpnd()->m_sym->AsStackSym()->IsAllocated())
- {
- storeCount++;
- wStoreCount += LinearScan::GetUseSpillCost(loopNest, false);
- }
- IR::Opnd *src1 = instr->GetSrc1();
- if (src1)
- {
- if (src1->IsSymOpnd() && src1->AsSymOpnd()->m_sym->IsStackSym() && src1->AsSymOpnd()->m_sym->AsStackSym()->IsAllocated())
- {
- loadCount++;
- wLoadCount += LinearScan::GetUseSpillCost(loopNest, false);
- }
- IR::Opnd *src2 = instr->GetSrc2();
- if (src2 && src2->IsSymOpnd() && src2->AsSymOpnd()->m_sym->IsStackSym() && src2->AsSymOpnd()->m_sym->AsStackSym()->IsAllocated())
- {
- loadCount++;
- wLoadCount += LinearScan::GetUseSpillCost(loopNest, false);
- }
- }
- }
- break;
- }
- } NEXT_INSTR_IN_FUNC_BACKWARD;
- Assert(loopNest == 0);
- this->func->GetJnFunction()->DumpFullFunctionName();
- Output::SkipToColumn(45);
- Output::Print(L"Instrs:%5d, Lds:%4d, Strs:%4d, WLds: %4d, WStrs: %4d, WRefs: %4d\n",
- instrCount, loadCount, storeCount, wLoadCount, wStoreCount, wLoadCount+wStoreCount);
- }
- #endif
- #ifdef _M_IX86
- # if ENABLE_DEBUG_CONFIG_OPTIONS
- IR::Instr * LinearScan::GetIncInsertionPoint(IR::Instr *instr)
- {
- // Make sure we don't insert an INC between an instr setting the condition code, and one using it.
- IR::Instr *instrNext = instr;
- while(!EncoderMD::UsesConditionCode(instrNext) && !EncoderMD::SetsConditionCode(instrNext))
- {
- if (instrNext->IsLabelInstr() || instrNext->IsExitInstr() || instrNext->IsBranchInstr())
- {
- break;
- }
- instrNext = instrNext->GetNextRealInstrOrLabel();
- }
- if (instrNext->IsLowered() && EncoderMD::UsesConditionCode(instrNext))
- {
- IR::Instr *instrPrev = instr->GetPrevRealInstrOrLabel();
- while(!EncoderMD::SetsConditionCode(instrPrev))
- {
- instrPrev = instrPrev->GetPrevRealInstrOrLabel();
- Assert(!instrPrev->IsLabelInstr());
- }
- return instrPrev;
- }
- return instr;
- }
- void LinearScan::DynamicStatsInstrument()
- {
- IR::Instr *firstInstr = this->func->m_headInstr;
- IR::MemRefOpnd *memRefOpnd = IR::MemRefOpnd::New(&(this->func->GetJnFunction()->callCountStats), TyUint32, this->func);
- firstInstr->InsertAfter(IR::Instr::New(Js::OpCode::INC, memRefOpnd, memRefOpnd, this->func));
- FOREACH_INSTR_IN_FUNC(instr, this->func)
- {
- if (!instr->IsRealInstr() || !instr->IsLowered())
- {
- continue;
- }
- if (EncoderMD::UsesConditionCode(instr) && instr->GetPrevRealInstrOrLabel()->IsLabelInstr())
- {
- continue;
- }
- IR::Opnd *dst = instr->GetDst();
- if (dst && dst->IsSymOpnd() && dst->AsSymOpnd()->m_sym->IsStackSym() && dst->AsSymOpnd()->m_sym->AsStackSym()->IsAllocated())
- {
- IR::Instr *insertionInstr = this->GetIncInsertionPoint(instr);
- IR::MemRefOpnd *memRefOpnd = IR::MemRefOpnd::New(&(this->func->GetJnFunction()->regAllocStoreCount), TyUint32, this->func);
- insertionInstr->InsertBefore(IR::Instr::New(Js::OpCode::INC, memRefOpnd, memRefOpnd, this->func));
- }
- IR::Opnd *src1 = instr->GetSrc1();
- if (src1)
- {
- if (src1->IsSymOpnd() && src1->AsSymOpnd()->m_sym->IsStackSym() && src1->AsSymOpnd()->m_sym->AsStackSym()->IsAllocated())
- {
- IR::Instr *insertionInstr = this->GetIncInsertionPoint(instr);
- IR::MemRefOpnd *memRefOpnd = IR::MemRefOpnd::New(&(this->func->GetJnFunction()->regAllocLoadCount), TyUint32, this->func);
- insertionInstr->InsertBefore(IR::Instr::New(Js::OpCode::INC, memRefOpnd, memRefOpnd, this->func));
- }
- IR::Opnd *src2 = instr->GetSrc2();
- if (src2 && src2->IsSymOpnd() && src2->AsSymOpnd()->m_sym->IsStackSym() && src2->AsSymOpnd()->m_sym->AsStackSym()->IsAllocated())
- {
- IR::Instr *insertionInstr = this->GetIncInsertionPoint(instr);
- IR::MemRefOpnd *memRefOpnd = IR::MemRefOpnd::New(&(this->func->GetJnFunction()->regAllocLoadCount), TyUint32, this->func);
- insertionInstr->InsertBefore(IR::Instr::New(Js::OpCode::INC, memRefOpnd, memRefOpnd, this->func));
- }
- }
- } NEXT_INSTR_IN_FUNC;
- }
- # endif //ENABLE_DEBUG_CONFIG_OPTIONS
- #endif // _M_IX86
- IR::Instr* LinearScan::InsertMove(IR::Opnd *dst, IR::Opnd *src, IR::Instr *const insertBeforeInstr)
- {
- IR::Instr *instrPrev = insertBeforeInstr->m_prev;
- IR::Instr *instrRet = Lowerer::InsertMove(dst, src, insertBeforeInstr);
- for (IR::Instr *instr = instrPrev->m_next; instr != insertBeforeInstr; instr = instr->m_next)
- {
- instr->CopyNumber(insertBeforeInstr);
- }
- return instrRet;
- }
|