Inline.cpp 250 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "Backend.h"
  6. void
  7. Inline::Optimize()
  8. {
  9. this->Optimize(this->topFunc);
  10. }
  11. void
  12. Inline::Optimize(Func *func, __in_ecount_opt(callerArgOutCount) IR::Instr *callerArgOuts[], Js::ArgSlot callerArgOutCount, uint recursiveInlineDepth)
  13. {
  14. if (!func->DoInline() || !topFunc->DoInline() || func->GetJnFunction()->GetIsAsmjsMode()) // disable inlining for asm
  15. {
  16. return;
  17. }
  18. bool doFixedMethods = !PHASE_OFF(Js::FixedMethodsPhase, func->GetJnFunction());
  19. const auto inlinerData = func->m_workItem->RecyclableData()->JitTimeData();
  20. bool doInline = (inlinerData->InlineeCount() > 0 || inlinerData->IsLdFldInlineePresent());
  21. if (PHASE_OFF(Js::InlinePhase, this->topFunc) ||
  22. PHASE_OFF(Js::InlinePhase, func->GetJnFunction()) ||
  23. func->IsJitInDebugMode())
  24. {
  25. doInline = false;
  26. }
  27. func->actualCount = callerArgOutCount;
  28. // Keep the caller's "this" symbol (if any).
  29. StackSym *symThis = nullptr;
  30. lastStatementBoundary = nullptr;
  31. IR::LabelInstr* loopTop = nullptr;
  32. int32 backEdgeCount = 0;
  33. // Profile data already filter call site outside of loops if the function has loops, so we don't need to detect that here.
  34. FOREACH_INSTR_EDITING(instr, instrNext, func->m_headInstr)
  35. {
  36. bool isInlined = false;
  37. bool isPolymorphic = false;
  38. bool isBuiltIn = false;
  39. bool isCtor = false;
  40. if (doInline)
  41. {
  42. switch (instr->m_opcode)
  43. {
  44. case Js::OpCode::StatementBoundary:
  45. lastStatementBoundary = instr->AsPragmaInstr();
  46. break;
  47. case Js::OpCode::Label:
  48. {
  49. if (!loopTop && instr->AsLabelInstr()->m_isLoopTop)
  50. {
  51. // We only need to know if we are inside loop or not, it doesn't matter how many nested levels we are in.
  52. // This is the cheap way of doing so.
  53. loopTop = instr->AsLabelInstr();
  54. AnalysisAssert(loopTop);
  55. this->isInLoop++;
  56. backEdgeCount = loopTop->labelRefs.Count();
  57. }
  58. }
  59. break;
  60. case Js::OpCode::StFld:
  61. case Js::OpCode::LdFld:
  62. case Js::OpCode::LdFldForCallApplyTarget:
  63. {
  64. // Try inlining of getter setter
  65. if (!inlinerData->IsLdFldInlineePresent())
  66. {
  67. break;
  68. }
  69. if (!instr->IsProfiledInstr())
  70. {
  71. break;
  72. }
  73. if (!(instr->AsProfiledInstr()->u.FldInfo().flags & Js::FldInfoFlags::FldInfo_FromAccessor))
  74. {
  75. break;
  76. }
  77. bool getter = instr->m_opcode != Js::OpCode::StFld;
  78. IR::Opnd *opnd = getter ? instr->GetSrc1() : instr->GetDst();
  79. if (!(opnd && opnd->IsSymOpnd()))
  80. {
  81. break;
  82. }
  83. IR::SymOpnd* symOpnd = opnd->AsSymOpnd();
  84. if (!symOpnd->m_sym->IsPropertySym())
  85. {
  86. break;
  87. }
  88. Assert(symOpnd->AsSymOpnd()->IsPropertySymOpnd());
  89. const auto inlineCacheIndex = symOpnd->AsPropertySymOpnd()->m_inlineCacheIndex;
  90. const auto inlineeData = inlinerData->GetLdFldInlinee(inlineCacheIndex);
  91. if (!inlineeData)
  92. {
  93. break;
  94. }
  95. Js::FunctionInfo* functionInfo = inlineeData->GetFunctionInfo();
  96. if (!functionInfo->GetFunctionBody())
  97. {
  98. #ifdef ENABLE_DOM_FAST_PATH
  99. Assert(functionInfo->GetLocalFunctionId() == Js::JavascriptBuiltInFunction::DOMFastPathGetter ||
  100. functionInfo->GetLocalFunctionId() == Js::JavascriptBuiltInFunction::DOMFastPathSetter);
  101. if (PHASE_OFF1(Js::InlineHostCandidatePhase))
  102. {
  103. break;
  104. }
  105. this->InlineDOMGetterSetterFunction(instr, inlineeData, inlinerData);
  106. #endif
  107. break;
  108. }
  109. bool isInlinePhaseOff = PHASE_OFF(Js::InlineCandidatePhase, functionInfo->GetFunctionBody()) ||
  110. PHASE_OFF(Js::InlineAccessorsPhase, functionInfo->GetFunctionBody()) ||
  111. (getter && PHASE_OFF(Js::InlineGettersPhase, functionInfo->GetFunctionBody())) ||
  112. (!getter && PHASE_OFF(Js::InlineSettersPhase, functionInfo->GetFunctionBody()));
  113. if (isInlinePhaseOff)
  114. {
  115. break;
  116. }
  117. this->InlineGetterSetterFunction(instr, inlineeData, symThis, inlineCacheIndex, getter /*isGetter*/, &isInlined, recursiveInlineDepth);
  118. break;
  119. }
  120. case Js::OpCode::NewScObjArray:
  121. // We know we're not going to inline these. Just break out and try to do a fixed function check.
  122. isCtor = true;
  123. isBuiltIn = true;
  124. break;
  125. case Js::OpCode::NewScObject:
  126. isCtor = true;
  127. if (PHASE_OFF(Js::InlineConstructorsPhase, this->topFunc))
  128. {
  129. break;
  130. }
  131. // fall-through
  132. case Js::OpCode::CallI:
  133. {
  134. IR::PropertySymOpnd* methodValueOpnd = GetMethodLdOpndForCallInstr(instr);
  135. if (this->inlineesProcessed == inlinerData->InlineeCount())
  136. {
  137. TryResetObjTypeSpecFldInfoOn(methodValueOpnd);
  138. TryDisableRuntimePolymorphicCacheOn(methodValueOpnd);
  139. break;
  140. }
  141. if(!instr->IsProfiledInstr())
  142. {
  143. TryResetObjTypeSpecFldInfoOn(methodValueOpnd);
  144. TryDisableRuntimePolymorphicCacheOn(methodValueOpnd);
  145. break;
  146. }
  147. const auto profileId = static_cast<Js::ProfileId>(instr->AsProfiledInstr()->u.profileId);
  148. if(profileId >= func->GetJnFunction()->GetProfiledCallSiteCount())
  149. {
  150. TryResetObjTypeSpecFldInfoOn(methodValueOpnd);
  151. TryDisableRuntimePolymorphicCacheOn(methodValueOpnd);
  152. break;
  153. }
  154. const auto inlineeData = inlinerData->GetInlinee(profileId);
  155. if(!inlineeData)
  156. {
  157. TryResetObjTypeSpecFldInfoOn(methodValueOpnd);
  158. TryDisableRuntimePolymorphicCacheOn(methodValueOpnd);
  159. break;
  160. }
  161. if(inlinerData->IsPolymorphicCallSite(profileId))
  162. {
  163. isPolymorphic = true;
  164. if (isCtor ||
  165. (PHASE_OFF(Js::PolymorphicInlinePhase, this->topFunc) || PHASE_OFF(Js::PolymorphicInlinePhase, func->GetJnFunction())) ||
  166. (this->IsInliningOutSideLoops() && !PHASE_FORCE(Js::InlinePhase, this->topFunc) && !PHASE_FORCE(Js::InlinePhase, func->GetJnFunction())))
  167. {
  168. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  169. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  170. #endif
  171. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic): Skip Inline: Inlining polymorphic call site outside loop\tIsConstructorCall: %s \tisTopFunc: %s\tCaller: %s (%s)\n"),
  172. (isCtor? _u("true"): _u("false")), (this->topFunc != func? _u("true"):_u("false")),
  173. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer));
  174. // TODO: Constructor polymorphic inlining
  175. TryResetObjTypeSpecFldInfoOn(methodValueOpnd);
  176. TryDisableRuntimePolymorphicCacheOn(methodValueOpnd);
  177. break;
  178. }
  179. if (!PHASE_OFF(Js::FixedMethodsPhase, this->topFunc) && !PHASE_OFF(Js::PolymorphicInlineFixedMethodsPhase, this->topFunc))
  180. {
  181. instrNext = InlinePolymorphicFunctionUsingFixedMethods(instr, inlinerData, symThis, profileId, methodValueOpnd, &isInlined, recursiveInlineDepth);
  182. }
  183. else
  184. {
  185. TryResetObjTypeSpecFldInfoOn(methodValueOpnd);
  186. TryDisableRuntimePolymorphicCacheOn(methodValueOpnd);
  187. instrNext = InlinePolymorphicFunction(instr, inlinerData, symThis, profileId, &isInlined, recursiveInlineDepth);
  188. }
  189. }
  190. else
  191. {
  192. TryResetObjTypeSpecFldInfoOn(methodValueOpnd);
  193. TryDisableRuntimePolymorphicCacheOn(methodValueOpnd);
  194. Js::FunctionInfo* functionInfo = inlineeData->GetFunctionInfo();
  195. Js::OpCode builtInInlineCandidateOpCode;
  196. ValueType builtInReturnType;
  197. // If the inlinee info is the array constructor, just change the opcode to NewScObjArray
  198. // so that we will inline the array allocation in lower
  199. if (isCtor && functionInfo == &Js::JavascriptArray::EntryInfo::NewInstance)
  200. {
  201. isBuiltIn = true;
  202. instr->m_opcode = Js::OpCode::NewScObjArray;
  203. instr->AsProfiledInstr()->u.profileId = Js::Constants::NoProfileId;
  204. break;
  205. }
  206. isBuiltIn = InliningDecider::GetBuiltInInfo(functionInfo, &builtInInlineCandidateOpCode, &builtInReturnType, func->GetScriptContext());
  207. if(!builtInReturnType.IsUninitialized() && instr->GetDst())
  208. {
  209. Assert(!functionInfo->HasBody());
  210. AssertMsg(instr->m_opcode != Js::OpCode::NewScObjArray, "We should have broken out of the switch statement earlier on this opcode.");
  211. // Value types for the array built-in calls are pulled from the profile; don't change them here.
  212. if ((instr->m_opcode != Js::OpCode::NewScObjArray) ||
  213. !instr->GetDst()->GetValueType().IsLikelyNativeArray())
  214. {
  215. // Assume that this built-in function is not going to be inlined, so the return type cannot be definite
  216. instr->GetDst()->SetValueType(builtInReturnType.ToLikely());
  217. }
  218. }
  219. bool isInlinePhaseOff = functionInfo->HasBody() ?
  220. PHASE_OFF(Js::InlineCandidatePhase, functionInfo->GetFunctionBody()) :
  221. PHASE_OFF1(Js::InlineBuiltInPhase);
  222. if (isInlinePhaseOff)
  223. {
  224. break;
  225. }
  226. if(!functionInfo->HasBody() && builtInInlineCandidateOpCode == 0)
  227. {
  228. // This built-in function is not going to be inlined
  229. break;
  230. }
  231. if(!functionInfo->HasBody())
  232. {
  233. Assert(builtInInlineCandidateOpCode != 0);
  234. if(isCtor)
  235. {
  236. // Inlining a built-in function called as a constructor is currently not supported. Although InliningDecider
  237. // already checks for this, profile data matching with a function does not take into account the difference
  238. // between a constructor call and a regular function call, so need to check it again.
  239. break;
  240. }
  241. // This built-in function is going to be inlined, so reset the destination's value type
  242. if(!builtInReturnType.IsUninitialized())
  243. {
  244. if(instr->GetDst())
  245. {
  246. instr->GetDst()->SetValueType(builtInReturnType);
  247. if(builtInReturnType.IsDefinite())
  248. {
  249. instr->GetDst()->SetValueTypeFixed();
  250. }
  251. }
  252. }
  253. }
  254. else
  255. {
  256. if (!inlineeData->GetFunctionBody()->HasDynamicProfileInfo()) // Don't try to inline a function if it doesn't have profile data
  257. {
  258. break;
  259. }
  260. uint16 constantArguments = 0;
  261. if (!PHASE_OFF(Js::InlineRecursivePhase, func->GetJnFunction()))
  262. {
  263. instr->IterateArgInstrs([&](IR::Instr* argInstr) {
  264. IR::Opnd *src1 = argInstr->GetSrc1();
  265. if (!src1->IsRegOpnd())
  266. {
  267. return false;
  268. }
  269. StackSym *sym = src1->AsRegOpnd()->m_sym;
  270. if (sym->IsIntConst())
  271. {
  272. if (argInstr->GetSrc2() && argInstr->GetSrc2()->IsSymOpnd())
  273. {
  274. StackSym *dstSym = argInstr->GetDst()->AsSymOpnd()->m_sym->AsStackSym();
  275. Assert(dstSym->IsSingleDef());
  276. Assert(dstSym->IsArgSlotSym());
  277. Js::ArgSlot argCount = dstSym->GetArgSlotNum() - 1;
  278. if (argCount == Js::Constants::MaximumArgumentCountForConstantArgumentInlining)
  279. {
  280. return true;
  281. }
  282. constantArguments |= (1 << argCount);
  283. }
  284. }
  285. return false;
  286. });
  287. }
  288. if (!inliningHeuristics.BackendInlineIntoInliner(inlineeData->GetFunctionBody(),
  289. func->GetJnFunction(), this->topFunc, profileId, isCtor, true /*isFixedMethodCall*/,
  290. this->IsInliningOutSideLoops(), this->isInLoop != 0, recursiveInlineDepth, constantArguments))
  291. {
  292. break;
  293. }
  294. }
  295. instrNext = builtInInlineCandidateOpCode != 0 ?
  296. this->InlineBuiltInFunction(instr, functionInfo, builtInInlineCandidateOpCode, inlinerData, symThis, &isInlined, profileId, recursiveInlineDepth) :
  297. this->InlineScriptFunction(instr, inlineeData, symThis, profileId, &isInlined, recursiveInlineDepth);
  298. }
  299. if(++this->inlineesProcessed == inlinerData->InlineeCount())
  300. {
  301. // getterSetter inline caches are shared and we have no way of knowing how many more are present
  302. if (!inlinerData->IsLdFldInlineePresent() && !doFixedMethods)
  303. {
  304. return ;
  305. }
  306. }
  307. break;
  308. }
  309. case Js::OpCode::CallIExtended:
  310. {
  311. if (this->inlineesProcessed == inlinerData->InlineeCount())
  312. {
  313. break;
  314. }
  315. if (!instr->IsProfiledInstr())
  316. {
  317. break;
  318. }
  319. const auto profileId = static_cast<Js::ProfileId>(instr->AsProfiledInstr()->u.profileId);
  320. if (profileId >= func->GetJnFunction()->GetProfiledCallSiteCount())
  321. {
  322. break;
  323. }
  324. const auto inlineeData = inlinerData->GetInlinee(profileId);
  325. if (!inlineeData)
  326. {
  327. break;
  328. }
  329. if (Lowerer::IsSpreadCall(instr))
  330. {
  331. InlineSpread(instr);
  332. }
  333. break;
  334. }
  335. case Js::OpCode::ArgOut_A:
  336. InlConstFoldArg(instr, callerArgOuts, callerArgOutCount);
  337. break;
  338. case Js::OpCode::LdThis:
  339. Assert(instr->GetDst() && instr->GetDst()->IsRegOpnd());
  340. Assert(symThis == nullptr);
  341. symThis = instr->GetDst()->AsRegOpnd()->m_sym;
  342. break;
  343. case Js::OpCode::CheckThis:
  344. // Is this possible? Can we be walking an inlinee here? Doesn't hurt to support this case...
  345. Assert(instr->GetSrc1() && instr->GetSrc1()->IsRegOpnd());
  346. Assert(symThis == nullptr);
  347. symThis = instr->GetSrc1()->AsRegOpnd()->m_sym;
  348. break;
  349. default:
  350. {
  351. if (loopTop && instr->IsBranchInstr())
  352. {
  353. // Look for the back edge to loopTop.
  354. IR::BranchInstr *branch = instr->AsBranchInstr();
  355. IR::LabelInstr *labelDestination = branch->GetTarget();
  356. if (labelDestination == loopTop) // We found the back edge
  357. {
  358. backEdgeCount--;
  359. if (backEdgeCount == 0) // We have seen all the back edges, hence we are outside loop now.
  360. {
  361. Assert(this->isInLoop > 0);
  362. --this->isInLoop;
  363. loopTop = nullptr;
  364. }
  365. }
  366. }
  367. }
  368. }
  369. }
  370. // If we chose not to inline, let's try to optimize this call if it uses a fixed method
  371. if (!isInlined)
  372. {
  373. switch (instr->m_opcode)
  374. {
  375. case Js::OpCode::NewScObject:
  376. case Js::OpCode::NewScObjArray:
  377. isCtor = true;
  378. // intentionally fall through.
  379. case Js::OpCode::CallI:
  380. {
  381. IR::PropertySymOpnd* methodValueOpnd = GetMethodLdOpndForCallInstr(instr);
  382. TryResetObjTypeSpecFldInfoOn(methodValueOpnd);
  383. TryDisableRuntimePolymorphicCacheOn(methodValueOpnd);
  384. StackSym* originalCallTargetStackSym = instr->GetSrc1()->GetStackSym();
  385. bool safeThis = false;
  386. if (TryOptimizeCallInstrWithFixedMethod(instr, nullptr, isPolymorphic /*isPolymorphic*/, isBuiltIn /*isBuiltIn*/, isCtor /*isCtor*/, false /*isInlined*/, safeThis /*unused here*/))
  387. {
  388. Assert(originalCallTargetStackSym != nullptr);
  389. // Insert a ByteCodeUsesInstr to make sure the methodValueDstOpnd's constant value is captured by any
  390. // bailout that occurs between CheckFixedMethodField and CallI.
  391. IR::ByteCodeUsesInstr * useCallTargetInstr = IR::ByteCodeUsesInstr::New(instr, originalCallTargetStackSym->m_id);
  392. instr->InsertBefore(useCallTargetInstr);
  393. // Split NewScObject into NewScObjectNoCtor and CallI, but don't touch NewScObjectArray.
  394. if (instr->m_opcode == Js::OpCode::NewScObject && !PHASE_OFF(Js::SplitNewScObjectPhase, this->topFunc))
  395. {
  396. SplitConstructorCall(instr, false, true);
  397. }
  398. }
  399. else if (instr->m_opcode == Js::OpCode::NewScObjArray)
  400. {
  401. if (instr->GetDst() && instr->GetDst()->GetValueType().IsLikelyNativeArray())
  402. {
  403. // We expect to create a native array here, so we'll insert a check against the
  404. // expected call target, which requires a bailout.
  405. instr = instr->ConvertToBailOutInstr(instr, IR::BailOutOnNotNativeArray);
  406. }
  407. }
  408. }
  409. break;
  410. }
  411. }
  412. } NEXT_INSTR_EDITING;
  413. INLINE_FLUSH();
  414. }
  415. uint Inline::FillInlineesDataArray(
  416. const Js::FunctionCodeGenJitTimeData* inlineeJitTimeData,
  417. const Js::FunctionCodeGenRuntimeData* inlineeRuntimeData,
  418. _Out_writes_to_(inlineesDataArrayLength, (return >= inlineesDataArrayLength ? inlineesDataArrayLength : return)) InlineeData *inlineesDataArray,
  419. uint inlineesDataArrayLength
  420. )
  421. {
  422. uint inlineeCount = 0;
  423. while(inlineeJitTimeData)
  424. {
  425. if (inlineeCount >= inlineesDataArrayLength)
  426. {
  427. // Count the actual number of inlinees for logging.
  428. while (inlineeJitTimeData)
  429. {
  430. inlineeCount++;
  431. inlineeJitTimeData = inlineeJitTimeData->GetNext();
  432. }
  433. return inlineeCount;
  434. }
  435. Js::FunctionBody *inlineeFunctionBody = inlineeJitTimeData->GetFunctionBody();
  436. if (!PHASE_OFF(Js::PolymorphicInlinePhase, inlineeFunctionBody))
  437. {
  438. const Js::FunctionCodeGenJitTimeData* rightInlineeJitTimeData = inlineeJitTimeData->GetJitTimeDataFromFunctionInfo(inlineeFunctionBody);
  439. const Js::FunctionCodeGenRuntimeData* rightInlineeRuntimeData = inlineeRuntimeData->GetRuntimeDataFromFunctionInfo(inlineeFunctionBody);
  440. if (rightInlineeJitTimeData)
  441. {
  442. inlineesDataArray[inlineeCount].inlineeJitTimeData = rightInlineeJitTimeData;
  443. inlineesDataArray[inlineeCount].inlineeRuntimeData = rightInlineeRuntimeData;
  444. inlineesDataArray[inlineeCount].functionBody = inlineeFunctionBody;
  445. Assert(rightInlineeJitTimeData->GetFunctionBody() == inlineeFunctionBody);
  446. #ifdef DBG
  447. for (uint k = 0; k < inlineeCount; k++)
  448. {
  449. if (inlineesDataArray[k].functionBody == inlineeFunctionBody)
  450. {
  451. AssertMsg(false, "We should never see duplicate function body here");
  452. }
  453. }
  454. #endif
  455. inlineeCount++;
  456. }
  457. else
  458. {
  459. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  460. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  461. #endif
  462. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic): Missing jit time data skipped inlinee\tInlinee: %s (%s)\n"),
  463. inlineeFunctionBody->GetDisplayName(), inlineeFunctionBody->GetDebugNumberSet(debugStringBuffer));
  464. }
  465. }
  466. inlineeJitTimeData = inlineeJitTimeData->GetNext();
  467. }
  468. return inlineeCount;
  469. }
  470. void Inline::FillInlineesDataArrayUsingFixedMethods(
  471. const Js::FunctionCodeGenJitTimeData* inlineeJitTimeData,
  472. const Js::FunctionCodeGenRuntimeData* inlineeRuntimeData,
  473. __inout_ecount(inlineesDataArrayLength) InlineeData *inlineesDataArray,
  474. uint inlineesDataArrayLength,
  475. __inout_ecount(cachedFixedInlineeCount) Js::FixedFieldInfo* fixedFieldInfoArray,
  476. uint16 cachedFixedInlineeCount
  477. )
  478. {
  479. AnalysisAssert(cachedFixedInlineeCount <= inlineesDataArrayLength);
  480. Js::FunctionBody* inlineeFuncBody = nullptr;
  481. while (inlineeJitTimeData)
  482. {
  483. inlineeFuncBody = inlineeJitTimeData->GetFunctionBody();
  484. if (!PHASE_OFF(Js::PolymorphicInlinePhase, inlineeFuncBody) && !PHASE_OFF(Js::PolymorphicInlineFixedMethodsPhase, inlineeFuncBody))
  485. {
  486. const Js::FunctionCodeGenJitTimeData* jitTimeData = inlineeJitTimeData->GetJitTimeDataFromFunctionInfo(inlineeFuncBody);
  487. if (jitTimeData)
  488. {
  489. for (uint16 i = 0; i < cachedFixedInlineeCount; i++)
  490. {
  491. if (inlineeFuncBody == ((Js::JavascriptFunction*)(fixedFieldInfoArray[i].fieldValue))->GetFunctionBody())
  492. {
  493. inlineesDataArray[i].inlineeJitTimeData = inlineeJitTimeData->GetJitTimeDataFromFunctionInfo(inlineeFuncBody);
  494. inlineesDataArray[i].inlineeRuntimeData = inlineeRuntimeData->GetRuntimeDataFromFunctionInfo(inlineeFuncBody);
  495. inlineesDataArray[i].functionBody = inlineeFuncBody;
  496. break;
  497. }
  498. }
  499. }
  500. else
  501. {
  502. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  503. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  504. #endif
  505. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic): Missing jit time data skipped inlinee\tInlinee: %s (%s)\n"),
  506. inlineeFuncBody->GetDisplayName(), inlineeFuncBody->GetDebugNumberSet(debugStringBuffer));
  507. }
  508. }
  509. inlineeJitTimeData = inlineeJitTimeData->GetNext();
  510. }
  511. }
  512. IR::Instr *
  513. Inline::InlinePolymorphicFunctionUsingFixedMethods(IR::Instr *callInstr, const Js::FunctionCodeGenJitTimeData* inlinerData, const StackSym *symCallerThis, const Js::ProfileId profileId, IR::PropertySymOpnd* methodValueOpnd, bool* pIsInlined, uint recursiveInlineDepth)
  514. {
  515. IR::Instr* instrNext = callInstr->m_next;
  516. *pIsInlined = false;
  517. const Js::FunctionCodeGenJitTimeData* inlineeJitTimeData = inlinerData->GetInlinee(profileId);
  518. AnalysisAssert(inlineeJitTimeData);
  519. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  520. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  521. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  522. #endif
  523. // Abort conditions:
  524. if(!inlineeJitTimeData->GetNext())
  525. {
  526. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic): Skip Inline: Missing JitTime data \tInlinee: %s (%s):\tCaller: %s (%s)\n"),
  527. inlineeJitTimeData->GetFunctionBody()->GetDisplayName(), inlineeJitTimeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  528. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  529. // There are no multiple codegen jit-time data allocated for this call site, not sure how is this possible, abort
  530. TryResetObjTypeSpecFldInfoOn(methodValueOpnd);
  531. TryDisableRuntimePolymorphicCacheOn(methodValueOpnd);
  532. return instrNext;
  533. }
  534. // arguments exceed MaxInlineeArgoutCount
  535. if (callInstr->GetSrc2() &&
  536. callInstr->GetSrc2()->IsSymOpnd() &&
  537. callInstr->GetSrc2()->AsSymOpnd()->m_sym->AsStackSym()->GetArgSlotNum() > Js::InlineeCallInfo::MaxInlineeArgoutCount)
  538. {
  539. // This is a hard limit as we only use 4 bits to encode the actual count in the InlineeCallInfo. Although
  540. // InliningDecider already checks for this, the check is against profile data that may not be accurate since profile
  541. // data matching does not take into account some types of changes to source code. Need to check this again with current
  542. // information.
  543. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic): Skip Inline: ArgSlot > MaxInlineeArgoutCount\tInlinee: %s (%s)\tArgSlotNum: %d\tMaxInlineeArgoutCount: %d\tCaller: %s (%s)\n"),
  544. inlineeJitTimeData->GetFunctionBody()->GetDisplayName(), inlineeJitTimeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer) , callInstr->GetSrc2()->AsSymOpnd()->m_sym->AsStackSym()->GetArgSlotNum(),
  545. Js::InlineeCallInfo::MaxInlineeArgoutCount, inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  546. TryResetObjTypeSpecFldInfoOn(methodValueOpnd);
  547. TryDisableRuntimePolymorphicCacheOn(methodValueOpnd);
  548. return instrNext;
  549. }
  550. uint inlineeCount = 0;
  551. const Js::FunctionCodeGenJitTimeData* tmpInlineeJitTimeData = inlineeJitTimeData;
  552. while(tmpInlineeJitTimeData)
  553. {
  554. inlineeCount++;
  555. tmpInlineeJitTimeData = tmpInlineeJitTimeData->GetNext();
  556. }
  557. // Inlinee count too small (<2) or too large (>4)
  558. if (inlineeCount < 2 || inlineeCount > Js::DynamicProfileInfo::maxPolymorphicInliningSize)
  559. {
  560. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic): Skip Inline: Inlinee count either too small or too large: InlineeCount %d (Max: %d)\tInlinee: %s (%s):\tCaller: %s (%s)\n"),
  561. inlineeCount, Js::DynamicProfileInfo::maxPolymorphicInliningSize,
  562. inlineeJitTimeData->GetFunctionBody()->GetDisplayName(), inlineeJitTimeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  563. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  564. TryResetObjTypeSpecFldInfoOn(methodValueOpnd);
  565. TryDisableRuntimePolymorphicCacheOn(methodValueOpnd);
  566. return instrNext;
  567. }
  568. *pIsInlined = true;
  569. IR::Instr* tmpInstr = callInstr->m_prev;
  570. while (tmpInstr->m_opcode != Js::OpCode::StartCall)
  571. {
  572. if ((tmpInstr->m_opcode != Js::OpCode::ArgOut_A) && (tmpInstr->m_opcode != Js::OpCode::Ld_A))
  573. {
  574. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic; Using Fixed Methods): Skip Inline: ArgOuts may have side effects Inlinee: %s (%s):\tCaller: %s (%s)\n"),
  575. inlineeJitTimeData->GetFunctionBody()->GetDisplayName(), inlineeJitTimeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  576. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  577. return InlinePolymorphicFunction(callInstr, inlinerData, symCallerThis, profileId, pIsInlined, recursiveInlineDepth, true);
  578. }
  579. tmpInstr = tmpInstr->m_prev;
  580. }
  581. StackSym* methodValueSym = callInstr->GetSrc1()->AsRegOpnd()->m_sym->AsStackSym();
  582. if (!methodValueSym->IsSingleDef())
  583. {
  584. return InlinePolymorphicFunction(callInstr, inlinerData, symCallerThis, profileId, pIsInlined, recursiveInlineDepth, true);
  585. }
  586. IR::Instr* ldMethodFldInstr = methodValueSym->GetInstrDef();
  587. if (!(ldMethodFldInstr->GetSrc1()->IsSymOpnd() && ldMethodFldInstr->GetSrc1()->AsSymOpnd()->IsPropertySymOpnd()))
  588. {
  589. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic; Using Fixed Methods): Skip Inline: Did not find property sym operand for the method load Inlinee: %s (%s):\tCaller: %s (%s)\n"),
  590. inlineeJitTimeData->GetFunctionBody()->GetDisplayName(), inlineeJitTimeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  591. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  592. return InlinePolymorphicFunction(callInstr, inlinerData, symCallerThis, profileId, pIsInlined, recursiveInlineDepth, true);
  593. }
  594. IR::PropertySymOpnd* methodPropertyOpnd = ldMethodFldInstr->GetSrc1()->AsPropertySymOpnd();
  595. if (!methodPropertyOpnd->HasObjTypeSpecFldInfo())
  596. {
  597. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic; Using Fixed Methods): Skip Inline: no ObjTypeSpecFldInfo to get Fixed Methods from Inlinee: %s (%s):\tCaller: %s (%s)\n"),
  598. inlineeJitTimeData->GetFunctionBody()->GetDisplayName(), inlineeJitTimeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  599. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  600. return InlinePolymorphicFunction(callInstr, inlinerData, symCallerThis, profileId, pIsInlined, recursiveInlineDepth, true);
  601. }
  602. if (!methodPropertyOpnd->HasFixedValue())
  603. {
  604. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic; Using Fixed Methods): Skip Inline: ObjTypeSpecFldInfo doesn't have Fixed Methods for one or some of the inlinees Inlinee: %s (%s):\tCaller: %s (%s)\n"),
  605. inlineeJitTimeData->GetFunctionBody()->GetDisplayName(), inlineeJitTimeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  606. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  607. return InlinePolymorphicFunction(callInstr, inlinerData, symCallerThis, profileId, pIsInlined, recursiveInlineDepth, true);
  608. }
  609. uint16 cachedFixedInlineeCount = methodPropertyOpnd->GetFixedFieldCount();
  610. if (cachedFixedInlineeCount < 2)
  611. {
  612. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic; Using Fixed Methods): Skip Inline: fixed function count too less %d (Max: %d)\tInlinee: %s (%s):\tCaller: %s (%s)\n"),
  613. cachedFixedInlineeCount, Js::DynamicProfileInfo::maxPolymorphicInliningSize,
  614. inlineeJitTimeData->GetFunctionBody()->GetDisplayName(), inlineeJitTimeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  615. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  616. return InlinePolymorphicFunction(callInstr, inlinerData, symCallerThis, profileId, pIsInlined, recursiveInlineDepth, true);
  617. }
  618. Js::FixedFieldInfo* fixedFunctionInfoArray = methodPropertyOpnd->GetFixedFieldInfoArray();
  619. // It might so be the case that two objects of different types call the same function (body), for e.g., if they share the prototype on which the function is defined.
  620. uint uniqueFixedFunctionCount = HandleDifferentTypesSameFunction(fixedFunctionInfoArray, cachedFixedInlineeCount);
  621. if (uniqueFixedFunctionCount != inlineeCount)
  622. {
  623. // inlineeCount obtained from the inlineeJitTimeData is more accurate than cached number of fixed methods for inlinees.
  624. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic; Using Fixed Methods): Skip Inline: cached fixed function count (%d) doesn't match inlinee count (%d); (Max: %d)\tInlinee: %s (%s):\tCaller: %s (%s)\n"),
  625. uniqueFixedFunctionCount, inlineeCount, Js::DynamicProfileInfo::maxPolymorphicInliningSize,
  626. inlineeJitTimeData->GetFunctionBody()->GetDisplayName(), inlineeJitTimeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  627. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  628. return InlinePolymorphicFunction(callInstr, inlinerData, symCallerThis, profileId, pIsInlined, recursiveInlineDepth, true);
  629. }
  630. Assert(cachedFixedInlineeCount <= Js::DynamicProfileInfo::maxPolymorphicInliningSize);
  631. InlineeData inlineesDataArray[Js::DynamicProfileInfo::maxPolymorphicInliningSize] = {};
  632. const Js::FunctionCodeGenRuntimeData* inlineeRuntimeData = callInstr->m_func->m_runtimeData ?
  633. callInstr->m_func->m_runtimeData->GetInlinee(profileId) :
  634. this->topFunc->GetJnFunction()->GetInlineeCodeGenRuntimeData(profileId);
  635. FillInlineesDataArrayUsingFixedMethods(inlineeJitTimeData, inlineeRuntimeData, inlineesDataArray, Js::DynamicProfileInfo::maxPolymorphicInliningSize, fixedFunctionInfoArray, cachedFixedInlineeCount);
  636. for (uint i = 0; i < cachedFixedInlineeCount; i++)
  637. {
  638. if(!inlineesDataArray[i].functionBody)
  639. {
  640. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic; Using Fixed Methods): Skip Inline: One of the inlinees doesn't have the corresponding object/prototype's type cached\tCaller: %s (%s)\n"),
  641. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer));
  642. return InlinePolymorphicFunction(callInstr, inlinerData, symCallerThis, profileId, pIsInlined, recursiveInlineDepth, true);
  643. }
  644. #if DBG
  645. if(inlineesDataArray[i].functionBody && inlineesDataArray[i].functionBody != methodPropertyOpnd->GetFieldValueAsFixedFunction(i)->GetFunctionBody())
  646. {
  647. AssertMsg(false, "inlineesDataArray and fixedfunctionInfoArray should be aligned with each other at this point");
  648. }
  649. #endif
  650. while (fixedFunctionInfoArray[i].nextHasSameFixedField)
  651. {
  652. i++;
  653. }
  654. }
  655. bool safeThis = true; // Eliminate CheckThis for inlining.
  656. for (uint i = 0; i < cachedFixedInlineeCount; i++)
  657. {
  658. if (!methodPropertyOpnd->GetFieldValue(i))
  659. {
  660. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic; Using Fixed Methods): Skip Inline: no fixed method for one of the inlinees; Inlinee: %s (%s):\tCaller: %s (%s)\n"),
  661. inlineesDataArray[i].functionBody->GetDisplayName(), inlineesDataArray[i].functionBody->GetDebugNumberSet(debugStringBuffer),
  662. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  663. return InlinePolymorphicFunction(callInstr, inlinerData, symCallerThis, profileId, pIsInlined, recursiveInlineDepth, true);
  664. }
  665. if (i == 0)
  666. {
  667. // Do all the general, non-function-object-specific checks just once.
  668. if (!TryOptimizeCallInstrWithFixedMethod(callInstr, (Js::FunctionInfo*)(inlineesDataArray[i].functionBody), true, false, false, true /*isInlined*/, safeThis, true /*dontOptimizeJustCheck*/, i))
  669. {
  670. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic; Using Fixed Methods): Skip Inline: can't optimize using Fixed Methods %d (Max: %d)\tInlinee: %s (%s):\tCaller: %s (%s)\n"),
  671. inlineeCount, Js::DynamicProfileInfo::maxPolymorphicInliningSize,
  672. inlineeJitTimeData->GetFunctionBody()->GetDisplayName(), inlineeJitTimeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  673. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  674. return InlinePolymorphicFunction(callInstr, inlinerData, symCallerThis, profileId, pIsInlined, recursiveInlineDepth, true);
  675. }
  676. }
  677. else
  678. {
  679. if (methodPropertyOpnd->GetFieldValueAsFixedFunction(i) &&
  680. methodPropertyOpnd->GetFieldValueAsFixedFunction(i)->GetFunctionInfo() != (Js::FunctionInfo*)(inlineesDataArray[i].functionBody))
  681. {
  682. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic; Using Fixed Methods): Skip Inline: can't optimize using Fixed Methods %d (Max: %d)\tInlinee: %s (%s):\tCaller: %s (%s)\n"),
  683. inlineeCount, Js::DynamicProfileInfo::maxPolymorphicInliningSize,
  684. inlineeJitTimeData->GetFunctionBody()->GetDisplayName(), inlineeJitTimeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  685. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  686. return InlinePolymorphicFunction(callInstr, inlinerData, symCallerThis, profileId, pIsInlined, recursiveInlineDepth, true);
  687. }
  688. }
  689. Js::TypeId typeId = methodPropertyOpnd->GetTypeId(i);
  690. if(!(typeId > Js::TypeIds_LastJavascriptPrimitiveType && typeId <= Js::TypeIds_LastTrueJavascriptObjectType))
  691. {
  692. // Don't eliminate CheckThis if it cannot be done for any one of the inlinees
  693. safeThis = false;
  694. }
  695. while (fixedFunctionInfoArray[i].nextHasSameFixedField)
  696. {
  697. i++;
  698. }
  699. }
  700. Assert(methodPropertyOpnd->IsPoly());
  701. // emit property guard check for the method load, and load type
  702. IR::RegOpnd *typeOpnd = IR::RegOpnd::New(TyVar, callInstr->m_func);
  703. IR::Instr* propertyGuardCheckInstr = IR::Instr::New(Js::OpCode::CheckPropertyGuardAndLoadType, typeOpnd, ldMethodFldInstr->GetSrc1(), callInstr->m_func);
  704. ldMethodFldInstr->InsertBefore(propertyGuardCheckInstr);
  705. propertyGuardCheckInstr->SetByteCodeOffset(ldMethodFldInstr);
  706. propertyGuardCheckInstr = propertyGuardCheckInstr->ConvertToBailOutInstr(ldMethodFldInstr, IR::BailOutFailedFixedFieldCheck);
  707. POLYMORPHIC_INLINE_TESTTRACE(_u("------------------------------------------------\n"));
  708. for (uint i = 0; i < cachedFixedInlineeCount; i++)
  709. {
  710. Js::FunctionBody *inlineeFunctionBody = inlineesDataArray[i].functionBody;
  711. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic; Using Fixed Methods): Start inlining: \tInlinee: %s (%s):\tCaller: %s (%s)\n"),
  712. inlineeFunctionBody->GetDisplayName(), inlineeFunctionBody->GetDebugNumberSet(debugStringBuffer),
  713. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  714. while (fixedFunctionInfoArray[i].nextHasSameFixedField)
  715. {
  716. i++;
  717. }
  718. }
  719. POLYMORPHIC_INLINE_TESTTRACE(_u("------------------------------------------------\n"));
  720. IR::RegOpnd * returnValueOpnd;
  721. if (callInstr->GetDst())
  722. {
  723. returnValueOpnd = callInstr->UnlinkDst()->AsRegOpnd();
  724. }
  725. else
  726. {
  727. returnValueOpnd = nullptr;
  728. }
  729. callInstr->MoveArgs(/*generateByteCodeCapture*/ true);
  730. callInstr->m_opcode = Js::OpCode::CallIFixed;
  731. // iterate over inlineesDataArray to emit each inlinee
  732. IR::LabelInstr * doneLabel = IR::LabelInstr::New(Js::OpCode::Label, callInstr->m_func, false);
  733. IR::Instr* dispatchStartLabel = IR::LabelInstr::New(Js::OpCode::Label, callInstr->m_func, false);
  734. callInstr->InsertBefore(dispatchStartLabel);
  735. for(uint i=0; i < cachedFixedInlineeCount; i++)
  736. {
  737. IR::LabelInstr* inlineeStartLabel = IR::LabelInstr::New(Js::OpCode::Label, callInstr->m_func);
  738. callInstr->InsertBefore(inlineeStartLabel);
  739. IR::AddrOpnd * constMethodValueOpnd = IR::AddrOpnd::New(methodPropertyOpnd->GetFieldValue(i), IR::AddrOpndKind::AddrOpndKindDynamicVar, callInstr->m_func);
  740. constMethodValueOpnd->m_isFunction = true;
  741. InsertOneInlinee(callInstr, returnValueOpnd, constMethodValueOpnd, inlineesDataArray[i], doneLabel, symCallerThis, safeThis, recursiveInlineDepth);
  742. while (fixedFunctionInfoArray[i].nextHasSameFixedField)
  743. {
  744. dispatchStartLabel->InsertBefore(IR::BranchInstr::New(Js::OpCode::BrAddr_A, inlineeStartLabel, typeOpnd, IR::AddrOpnd::New(methodPropertyOpnd->GetType(i),
  745. IR::AddrOpndKindDynamicType, dispatchStartLabel->m_func), dispatchStartLabel->m_func));
  746. this->topFunc->PinTypeRef(methodPropertyOpnd->GetType(i)); // Keep the types alive as the types may not be equivalent and, hence, won't be kept alive by EquivalentTypeCache
  747. i++;
  748. }
  749. dispatchStartLabel->InsertBefore(IR::BranchInstr::New(Js::OpCode::BrAddr_A, inlineeStartLabel,
  750. typeOpnd, IR::AddrOpnd::New(methodPropertyOpnd->GetType(i), IR::AddrOpndKindDynamicType, dispatchStartLabel->m_func), dispatchStartLabel->m_func));
  751. this->topFunc->PinTypeRef(methodPropertyOpnd->GetType(i)); // Keep the types alive as the types may not be equivalent and, hence, won't be kept alive by EquivalentTypeCache
  752. }
  753. ldMethodFldInstr->Unlink();
  754. ldMethodFldInstr->m_opcode = Js::OpCode::LdMethodFldPolyInlineMiss;
  755. Assert(cachedFixedInlineeCount > 0);
  756. CompletePolymorphicInlining(callInstr, returnValueOpnd, doneLabel, dispatchStartLabel, ldMethodFldInstr, IR::BailOutOnFailedPolymorphicInlineTypeCheck);
  757. this->topFunc->SetHasInlinee();
  758. InsertStatementBoundary(instrNext);
  759. InsertStatementBoundary(ldMethodFldInstr);
  760. return instrNext;
  761. }
  762. void Inline::CloneCallSequence(IR::Instr* callInstr, IR::Instr* clonedCallInstr)
  763. {
  764. IR::Instr* previousArg = nullptr;
  765. IR::Instr* previousClonedArg = clonedCallInstr;
  766. callInstr->IterateArgInstrs([&](IR::Instr* argInstr){
  767. IR::Instr* cloneArg = IR::Instr::New(argInstr->m_opcode,
  768. IR::SymOpnd::New(callInstr->m_func->m_symTable->GetArgSlotSym(argInstr->GetDst()->GetStackSym()->GetArgSlotNum()), 0, TyMachPtr, callInstr->m_func),
  769. argInstr->GetSrc1(), callInstr->m_func);
  770. cloneArg->SetByteCodeOffset(callInstr);
  771. cloneArg->GetDst()->GetStackSym()->m_isArgCaptured = true;
  772. previousClonedArg->SetSrc2(cloneArg->GetDst());
  773. previousClonedArg->InsertBefore(cloneArg);
  774. previousArg = argInstr;
  775. previousClonedArg = cloneArg;
  776. return false;
  777. });
  778. IR::Instr* startCall = previousArg->GetSrc2()->GetStackSym()->GetInstrDef();
  779. previousClonedArg->SetSrc2(startCall->GetDst());
  780. }
  781. IR::Instr *
  782. Inline::InlinePolymorphicFunction(IR::Instr *callInstr, const Js::FunctionCodeGenJitTimeData* inlinerData, const StackSym *symCallerThis, const Js::ProfileId profileId, bool* pIsInlined, uint recursiveInlineDepth, bool triedUsingFixedMethods)
  783. {
  784. IR::Instr* instrNext = callInstr->m_next;
  785. *pIsInlined = false;
  786. if (triedUsingFixedMethods)
  787. {
  788. if (callInstr->GetSrc1()->AsRegOpnd()->m_sym->AsStackSym()->IsSingleDef())
  789. {
  790. IR::Instr* ldMethodFldInstr = callInstr->GetSrc1()->AsRegOpnd()->m_sym->AsStackSym()->GetInstrDef();
  791. if (ldMethodFldInstr->GetSrc1()->IsSymOpnd() && ldMethodFldInstr->GetSrc1()->AsSymOpnd()->IsPropertySymOpnd())
  792. {
  793. TryResetObjTypeSpecFldInfoOn(ldMethodFldInstr->GetSrc1()->AsPropertySymOpnd());
  794. TryDisableRuntimePolymorphicCacheOn(ldMethodFldInstr->GetSrc1()->AsPropertySymOpnd());
  795. }
  796. }
  797. }
  798. const Js::FunctionCodeGenJitTimeData* inlineeJitTimeData = inlinerData->GetInlinee(profileId);
  799. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  800. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  801. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  802. #endif
  803. if (!triedUsingFixedMethods) // We would have done the following two checks when we tried to inline using fixed methods
  804. {
  805. if(!inlineeJitTimeData->GetNext())
  806. {
  807. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic): Skip Inline: Missing JitTime data \tInlinee: %s (%s):\tCaller: %s (%s)\n"),
  808. inlineeJitTimeData->GetFunctionBody()->GetDisplayName(), inlineeJitTimeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  809. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  810. //There are no multiple codegen jit-time data allocated for this call site, not sure how is this possible, abort
  811. return instrNext;
  812. }
  813. if (callInstr->GetSrc2() &&
  814. callInstr->GetSrc2()->IsSymOpnd() &&
  815. callInstr->GetSrc2()->AsSymOpnd()->m_sym->AsStackSym()->GetArgSlotNum() > Js::InlineeCallInfo::MaxInlineeArgoutCount)
  816. {
  817. // This is a hard limit as we only use 4 bits to encode the actual count in the InlineeCallInfo. Although
  818. // InliningDecider already checks for this, the check is against profile data that may not be accurate since profile
  819. // data matching does not take into account some types of changes to source code. Need to check this again with current
  820. // information.
  821. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic): Skip Inline: ArgSlot > MaxInlineeArgoutCount\tInlinee: %s (%s)\tArgSlotNum: %d\tMaxInlineeArgoutCount: %d\tCaller: %s (%s)\n"),
  822. inlineeJitTimeData->GetFunctionBody()->GetDisplayName(), inlineeJitTimeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer) , callInstr->GetSrc2()->AsSymOpnd()->m_sym->AsStackSym()->GetArgSlotNum(),
  823. Js::InlineeCallInfo::MaxInlineeArgoutCount, inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  824. return instrNext;
  825. }
  826. }
  827. InlineeData inlineesDataArray[Js::DynamicProfileInfo::maxPolymorphicInliningSize];
  828. const Js::FunctionCodeGenRuntimeData* inlineeRuntimeData = callInstr->m_func->m_runtimeData ?
  829. callInstr->m_func->m_runtimeData->GetInlinee(profileId) :
  830. this->topFunc->GetJnFunction()->GetInlineeCodeGenRuntimeData(profileId);
  831. uint inlineeCount = FillInlineesDataArray(inlineeJitTimeData, inlineeRuntimeData, inlineesDataArray, Js::DynamicProfileInfo::maxPolymorphicInliningSize);
  832. if (inlineeCount < 2 || inlineeCount > Js::DynamicProfileInfo::maxPolymorphicInliningSize)
  833. {
  834. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic): Skip Inline: Inlinee count either too small or too large %d (Max: %d)\tInlinee: %s (%s):\tCaller: %s (%s)\n"),
  835. inlineeCount, Js::DynamicProfileInfo::maxPolymorphicInliningSize,
  836. inlineeJitTimeData->GetFunctionBody()->GetDisplayName(), inlineeJitTimeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  837. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  838. return instrNext;
  839. }
  840. // Begin inlining.
  841. POLYMORPHIC_INLINE_TESTTRACE(_u("------------------------------------------------\n"));
  842. for (uint i = 0; i < inlineeCount; i++)
  843. {
  844. Js::FunctionBody *inlineeFunctionBody = inlineesDataArray[i].functionBody;
  845. POLYMORPHIC_INLINE_TESTTRACE(_u("INLINING (Polymorphic): Start inlining: \tInlinee: %s (%s):\tCaller: %s (%s)\n"),
  846. inlineeFunctionBody->GetDisplayName(), inlineeFunctionBody->GetDebugNumberSet(debugStringBuffer),
  847. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2));
  848. }
  849. POLYMORPHIC_INLINE_TESTTRACE(_u("------------------------------------------------\n"));
  850. *pIsInlined = true;
  851. // This function is recursive, so when jitting in the foreground, probe the stack
  852. if (!this->topFunc->IsBackgroundJIT())
  853. {
  854. PROBE_STACK(this->topFunc->GetScriptContext(), Js::Constants::MinStackDefault);
  855. }
  856. IR::RegOpnd * returnValueOpnd;
  857. Js::RegSlot returnRegSlot;
  858. if (callInstr->GetDst())
  859. {
  860. returnValueOpnd = callInstr->UnlinkDst()->AsRegOpnd();
  861. returnRegSlot = returnValueOpnd->m_sym->GetByteCodeRegSlot();
  862. }
  863. else
  864. {
  865. returnValueOpnd = nullptr;
  866. returnRegSlot = Js::Constants::NoRegister;
  867. }
  868. Assert(inlineeCount >= 2);
  869. // Shared bailout point for all the guard check bailouts.
  870. InsertJsFunctionCheck(callInstr, callInstr, IR::BailOutOnPolymorphicInlineFunction);
  871. callInstr->MoveArgs(/*generateByteCodeCapture*/ true);
  872. IR::LabelInstr * doneLabel = IR::LabelInstr::New(Js::OpCode::Label, callInstr->m_func, false);
  873. IR::Instr* dispatchStartLabel = IR::LabelInstr::New(Js::OpCode::Label, callInstr->m_func, false);
  874. callInstr->InsertBefore(dispatchStartLabel);
  875. for (uint i = 0; i < inlineeCount; i++)
  876. {
  877. IR::LabelInstr* inlineeStartLabel = IR::LabelInstr::New(Js::OpCode::Label, callInstr->m_func);
  878. callInstr->InsertBefore(inlineeStartLabel);
  879. InsertOneInlinee(callInstr, returnValueOpnd, callInstr->GetSrc1(), inlineesDataArray[i], doneLabel, symCallerThis, /*fixedFunctionSafeThis*/ false, recursiveInlineDepth);
  880. IR::RegOpnd* functionObject = callInstr->GetSrc1()->AsRegOpnd();
  881. dispatchStartLabel->InsertBefore(IR::BranchInstr::New(Js::OpCode::BrAddr_A, inlineeStartLabel,
  882. IR::IndirOpnd::New(functionObject, Js::JavascriptFunction::GetOffsetOfFunctionInfo(), TyMachPtr, dispatchStartLabel->m_func),
  883. IR::AddrOpnd::New(inlineesDataArray[i].functionBody, IR::AddrOpndKindDynamicFunctionBody, dispatchStartLabel->m_func), dispatchStartLabel->m_func));
  884. }
  885. CompletePolymorphicInlining(callInstr, returnValueOpnd, doneLabel, dispatchStartLabel, /*ldMethodFldInstr*/nullptr, IR::BailOutOnPolymorphicInlineFunction);
  886. this->topFunc->SetHasInlinee();
  887. InsertStatementBoundary(instrNext);
  888. return instrNext;
  889. }
  890. void Inline::CompletePolymorphicInlining(IR::Instr* callInstr, IR::RegOpnd* returnValueOpnd, IR::LabelInstr* doneLabel, IR::Instr* dispatchStartLabel, IR::Instr* ldMethodFldInstr, IR::BailOutKind bailoutKind)
  891. {
  892. // Label $bailout:
  893. // LdMethodFldPolyInlineMiss
  894. // BailOnNotPolymorphicInlinee $callOutBytecodeOffset - BailOutOnFailedPolymorphicInlineTypeCheck
  895. // ByteCoudeUses
  896. // BytecodeArgoutUses
  897. // returnValueOpnd = EndCallForPolymorphicInlinee actualsCount
  898. IR::LabelInstr* bailOutLabel = IR::LabelInstr::New(Js::OpCode::Label, callInstr->m_func, /*helperLabel*/ true);
  899. callInstr->InsertBefore(bailOutLabel);
  900. dispatchStartLabel->InsertBefore(IR::BranchInstr::New(Js::OpCode::Br, bailOutLabel, callInstr->m_func));
  901. // Only fixed function inlining requires a ldMethodFldInstr
  902. if (ldMethodFldInstr)
  903. {
  904. callInstr->InsertBefore(ldMethodFldInstr);
  905. }
  906. callInstr->InsertBefore(IR::BailOutInstr::New(Js::OpCode::BailOnNotPolymorphicInlinee, bailoutKind, callInstr, callInstr->m_func));
  907. uint actualsCount = 0;
  908. callInstr->IterateArgInstrs([&](IR::Instr* argInstr) {
  909. IR::Instr* bytecodeArgOutUse = IR::Instr::New(Js::OpCode::BytecodeArgOutUse, callInstr->m_func);
  910. bytecodeArgOutUse->SetByteCodeOffset(callInstr);
  911. bytecodeArgOutUse->SetSrc1(argInstr->GetSrc1());
  912. callInstr->InsertBefore(bytecodeArgOutUse);
  913. actualsCount++;
  914. // Remove the original args
  915. argInstr->Remove();
  916. return false;
  917. });
  918. callInstr->InsertBefore(IR::ByteCodeUsesInstr::New(callInstr, callInstr->GetSrc1()->GetStackSym()->m_id));
  919. IR::Instr* endCallInstr = IR::Instr::New(Js::OpCode::EndCallForPolymorphicInlinee, callInstr->m_func);
  920. endCallInstr->SetSrc1(IR::IntConstOpnd::New(actualsCount + Js::Constants::InlineeMetaArgCount, TyInt32, callInstr->m_func, /*dontEncode*/ true));
  921. if (returnValueOpnd)
  922. {
  923. StackSym* returnValueSym = returnValueOpnd->m_sym->AsStackSym();
  924. IR::Opnd* dstOpnd = IR::RegOpnd::New(returnValueSym, returnValueSym->GetType(), callInstr->m_func);
  925. dstOpnd->SetValueType(returnValueOpnd->GetValueType());
  926. endCallInstr->SetDst(dstOpnd);
  927. }
  928. callInstr->InsertBefore(endCallInstr);
  929. callInstr->InsertBefore(doneLabel);
  930. callInstr->Remove(); // We don't need callInstr anymore.
  931. }
  932. //
  933. // Inlines a function if it is a polymorphic inlining candidate.
  934. // otherwise introduces a call to it.
  935. // The IR for the args & calls is cloned to do this
  936. //
  937. void Inline::InsertOneInlinee(IR::Instr* callInstr, IR::RegOpnd* returnValueOpnd, IR::Opnd* methodOpnd,
  938. const InlineeData& inlineeData, IR::LabelInstr* doneLabel, const StackSym* symCallerThis, bool fixedFunctionSafeThis, uint recursiveInlineDepth)
  939. {
  940. bool isInlined = inlineeData.inlineeJitTimeData->GetIsInlined();
  941. IR::Instr* currentCallInstr;
  942. if (isInlined)
  943. {
  944. currentCallInstr = IR::Instr::New(Js::OpCode::InlineeStart, IR::RegOpnd::New(TyVar, callInstr->m_func), methodOpnd, callInstr->m_func);
  945. }
  946. else
  947. {
  948. currentCallInstr = IR::Instr::New(callInstr->m_opcode, callInstr->m_func);
  949. currentCallInstr->SetSrc1(methodOpnd);
  950. if (returnValueOpnd)
  951. {
  952. currentCallInstr->SetDst(returnValueOpnd);
  953. }
  954. }
  955. currentCallInstr->SetIsCloned(true);
  956. callInstr->InsertBefore(currentCallInstr);
  957. this->CloneCallSequence(callInstr, currentCallInstr);
  958. if (isInlined)
  959. {
  960. Js::FunctionBody *funcBody = inlineeData.functionBody;
  961. Func *inlinee = BuildInlinee(funcBody, inlineeData, returnValueOpnd ? returnValueOpnd->m_sym->GetByteCodeRegSlot() : Js::Constants::NoRegister, callInstr, recursiveInlineDepth);
  962. IR::Instr *argOuts[Js::InlineeCallInfo::MaxInlineeArgoutCount];
  963. #if DBG
  964. memset(argOuts, 0xFE, sizeof(argOuts));
  965. #endif
  966. bool stackArgsArgOutExpanded = false;
  967. Js::ArgSlot actualCount = MapActuals(currentCallInstr, argOuts, Js::InlineeCallInfo::MaxInlineeArgoutCount, inlinee, (Js::ProfileId)callInstr->AsProfiledInstr()->u.profileId, &stackArgsArgOutExpanded);
  968. Assert(actualCount > 0);
  969. MapFormals(inlinee, argOuts, funcBody->GetInParamsCount(), actualCount, returnValueOpnd, currentCallInstr->GetSrc1(), symCallerThis, stackArgsArgOutExpanded, fixedFunctionSafeThis, argOuts);
  970. currentCallInstr->m_func = inlinee;
  971. // Put the meta arguments that the stack walker expects to find on the stack.
  972. // As all the argouts are shared among the inlinees, do this only once.
  973. SetupInlineeFrame(inlinee, currentCallInstr, actualCount, currentCallInstr->GetSrc1());
  974. IR::Instr* inlineeEndInstr = IR::Instr::New(Js::OpCode::InlineeEnd, inlinee);
  975. inlineeEndInstr->SetByteCodeOffset(inlinee->m_tailInstr->GetPrevRealInstr());
  976. inlineeEndInstr->SetSrc1(IR::IntConstOpnd::New(actualCount + Js::Constants::InlineeMetaArgCount, TyInt32, inlinee));
  977. inlineeEndInstr->SetSrc2(currentCallInstr->GetDst());
  978. inlinee->m_tailInstr->InsertBefore(inlineeEndInstr);
  979. // JMP to done at the end
  980. IR::Instr* doneInstr = IR::BranchInstr::New(Js::OpCode::Br, doneLabel, currentCallInstr->m_func);
  981. inlinee->m_tailInstr->InsertBefore(doneInstr);
  982. currentCallInstr->InsertRangeAfter(inlinee->m_headInstr->m_next, inlinee->m_tailInstr->m_prev);
  983. inlinee->m_headInstr->Free();
  984. inlinee->m_tailInstr->Free();
  985. }
  986. else
  987. {
  988. callInstr->InsertBefore(IR::BranchInstr::New(Js::OpCode::Br, doneLabel, callInstr->m_func));
  989. }
  990. }
  991. uint
  992. Inline::HandleDifferentTypesSameFunction(__inout_ecount(cachedFixedInlineeCount) Js::FixedFieldInfo* fixedFunctionInfoArray, uint16 cachedFixedInlineeCount)
  993. {
  994. uint16 uniqueCount = cachedFixedInlineeCount;
  995. uint16 swapIndex;
  996. for (uint16 i = 0; i < cachedFixedInlineeCount; i++)
  997. {
  998. swapIndex = i+1;
  999. for (uint16 j = i+1; j < cachedFixedInlineeCount; j++)
  1000. {
  1001. if (fixedFunctionInfoArray[i].fieldValue == fixedFunctionInfoArray[j].fieldValue)
  1002. {
  1003. Js::FixedFieldInfo tmpInfo = fixedFunctionInfoArray[j];
  1004. fixedFunctionInfoArray[j] = fixedFunctionInfoArray[swapIndex];
  1005. fixedFunctionInfoArray[swapIndex] = tmpInfo;
  1006. fixedFunctionInfoArray[swapIndex - 1].nextHasSameFixedField = true;
  1007. swapIndex++;
  1008. uniqueCount--;
  1009. }
  1010. }
  1011. i = swapIndex-1;
  1012. }
  1013. return uniqueCount;
  1014. }
  1015. void
  1016. Inline::SetInlineeFrameStartSym(Func *inlinee, uint actualCount)
  1017. {
  1018. StackSym *stackSym = inlinee->m_symTable->GetArgSlotSym((Js::ArgSlot)actualCount + 1);
  1019. stackSym->m_isInlinedArgSlot = true;
  1020. this->topFunc->SetArgOffset(stackSym, (currentInlineeFrameSlot) * MachPtr);
  1021. inlinee->SetInlineeFrameStartSym(stackSym);
  1022. }
  1023. Func *
  1024. Inline::BuildInlinee(Js::FunctionBody* funcBody, const InlineeData& inlineeData, Js::RegSlot returnRegSlot, IR::Instr *callInstr, uint recursiveInlineDepth)
  1025. {
  1026. Assert(callInstr->IsProfiledInstr());
  1027. Js::ProfileId callSiteId = static_cast<Js::ProfileId>(callInstr->AsProfiledInstr()->u.profileId);
  1028. Js::ProxyEntryPointInfo *defaultEntryPointInfo = funcBody->GetDefaultEntryPointInfo();
  1029. Assert(defaultEntryPointInfo->IsFunctionEntryPointInfo());
  1030. Js::FunctionEntryPointInfo *functionEntryPointInfo = static_cast<Js::FunctionEntryPointInfo*>(defaultEntryPointInfo);
  1031. JsFunctionCodeGen *workItem = JitAnew(this->topFunc->m_alloc, JsFunctionCodeGen,
  1032. funcBody->GetScriptContext()->GetNativeCodeGenerator(), funcBody, functionEntryPointInfo, this->topFunc->IsJitInDebugMode());
  1033. workItem->SetRecyclableData(JitAnew(this->topFunc->m_alloc, Js::CodeGenRecyclableData, inlineeData.inlineeJitTimeData));
  1034. workItem->SetJitMode(this->topFunc->m_workItem->GetJitMode());
  1035. const auto profileInfo =
  1036. JitAnew(
  1037. this->topFunc->m_alloc,
  1038. Js::ReadOnlyDynamicProfileInfo,
  1039. funcBody->HasDynamicProfileInfo() ? funcBody->GetAnyDynamicProfileInfo() : nullptr,
  1040. this->topFunc->IsBackgroundJIT() ? this->topFunc->m_alloc : nullptr);
  1041. Js::EntryPointPolymorphicInlineCacheInfo * entryPointPolymorphicInlineCacheInfo = this->topFunc->m_workItem->GetEntryPoint()->GetPolymorphicInlineCacheInfo();
  1042. Func *inlinee = JitAnew(this->topFunc->m_alloc,
  1043. Func,
  1044. this->topFunc->m_alloc,
  1045. workItem,
  1046. inlineeData.inlineeRuntimeData,
  1047. entryPointPolymorphicInlineCacheInfo ? entryPointPolymorphicInlineCacheInfo->GetInlineeInfo(funcBody) : nullptr,
  1048. this->topFunc->GetCodeGenAllocators(),
  1049. this->topFunc->GetNumberAllocator(),
  1050. profileInfo,
  1051. this->topFunc->GetCodeGenProfiler(),
  1052. this->topFunc->IsBackgroundJIT(),
  1053. callInstr->m_func,
  1054. callInstr->m_next->GetByteCodeOffset(),
  1055. returnRegSlot,
  1056. false,
  1057. callSiteId,
  1058. false);
  1059. BuildIRForInlinee(inlinee, funcBody, callInstr, false, recursiveInlineDepth);
  1060. return inlinee;
  1061. }
  1062. void
  1063. Inline::BuildIRForInlinee(Func *inlinee, Js::FunctionBody *funcBody, IR::Instr *callInstr, bool isApplyTarget, uint recursiveInlineDepth)
  1064. {
  1065. Js::ArgSlot actualsCount = 0;
  1066. IR::Instr *argOuts[Js::InlineeCallInfo::MaxInlineeArgoutCount];
  1067. #if DBG
  1068. memset(argOuts, 0xFE, sizeof(argOuts));
  1069. #endif
  1070. callInstr->IterateArgInstrs([&](IR::Instr* argInstr){
  1071. StackSym *argSym = argInstr->GetDst()->AsSymOpnd()->m_sym->AsStackSym();
  1072. argOuts[argSym->GetArgSlotNum() - 1] = argInstr;
  1073. actualsCount++;
  1074. return false;
  1075. });
  1076. inlinee->actualCount = actualsCount;
  1077. inlinee->m_symTable = this->topFunc->m_symTable;
  1078. inlinee->m_symTable->SetIDAdjustment();
  1079. inlinee->m_symTable->IncreaseStartingID(funcBody->GetLocalsCount());
  1080. BEGIN_CODEGEN_PHASE(this->topFunc, Js::IRBuilderPhase);
  1081. IRBuilder irBuilder(inlinee);
  1082. irBuilder.Build();
  1083. END_CODEGEN_PHASE_NO_DUMP(this->topFunc, Js::IRBuilderPhase);
  1084. inlinee->m_symTable->ClearIDAdjustment();
  1085. Inline recursiveInliner(this->topFunc, this->inliningHeuristics, this->isInLoop, currentInlineeFrameSlot + Js::Constants::InlineeMetaArgCount + actualsCount, isApplyTarget);
  1086. recursiveInliner.Optimize(inlinee, argOuts, actualsCount, inlinee->GetJnFunction() == callInstr->m_func->GetJnFunction() ? recursiveInlineDepth + 1 : 0);
  1087. #ifdef DBG
  1088. Js::ArgSlot formalCount = funcBody->GetInParamsCount();
  1089. if (formalCount > Js::InlineeCallInfo::MaxInlineeArgoutCount)
  1090. {
  1091. Fatal();
  1092. }
  1093. #endif
  1094. }
  1095. bool
  1096. Inline::TryOptimizeCallInstrWithFixedMethod(IR::Instr *callInstr, Js::FunctionInfo* functionInfo, bool isPolymorphic, bool isBuiltIn, bool isCtor, bool isInlined, bool &safeThis,
  1097. bool dontOptimizeJustCheck, uint i /*i-th inlinee at a polymorphic call site*/)
  1098. {
  1099. Assert(!callInstr->m_func->GetJnFunction()->GetHasTry());
  1100. if (PHASE_OFF(Js::FixedMethodsPhase, callInstr->m_func->GetJnFunction()))
  1101. {
  1102. return false;
  1103. }
  1104. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  1105. #define TRACE_FIXED_FIELDS 1
  1106. #endif
  1107. #if TRACE_FIXED_FIELDS
  1108. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  1109. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  1110. bool printFixedFieldsTrace =
  1111. ((PHASE_TRACE(Js::FixedMethodsPhase, callInstr->m_func->GetJnFunction()) || PHASE_TESTTRACE(Js::FixedMethodsPhase, callInstr->m_func->GetJnFunction()) ||
  1112. (isCtor && PHASE_TRACE(Js::FixedNewObjPhase, callInstr->m_func->GetJnFunction()) || PHASE_TESTTRACE(Js::FixedNewObjPhase, callInstr->m_func->GetJnFunction()))) && !dontOptimizeJustCheck);
  1113. if (printFixedFieldsTrace)
  1114. {
  1115. Js::FunctionBody* callerFunctionBody = callInstr->m_func->GetJnFunction();
  1116. Js::FunctionBody* calleeFunctionBody = functionInfo != nullptr && functionInfo->HasBody() ? functionInfo->GetFunctionBody() : nullptr;
  1117. const char16* calleeName = calleeFunctionBody != nullptr ? calleeFunctionBody->GetDisplayName() : _u("<unknown>");
  1118. Output::Print(_u("FixedFields: function %s (%s): considering method <unknown> (%s %s): polymorphic = %d, built-in = %d, ctor = %d, inlined = %d, functionInfo = %p.\n"),
  1119. callerFunctionBody->GetDisplayName(), callerFunctionBody->GetDebugNumberSet(debugStringBuffer), calleeName,
  1120. calleeFunctionBody ? calleeFunctionBody->GetDebugNumberSet(debugStringBuffer2) : _u("(null)"),
  1121. isPolymorphic, isBuiltIn, isCtor, isInlined, functionInfo);
  1122. Output::Flush();
  1123. }
  1124. #endif
  1125. if (isPolymorphic && isInlined)
  1126. {
  1127. Assert(dontOptimizeJustCheck);
  1128. }
  1129. StackSym* methodValueSym = callInstr->GetSrc1()->AsRegOpnd()->m_sym->AsStackSym();
  1130. if (!methodValueSym->IsSingleDef())
  1131. {
  1132. #if TRACE_FIXED_FIELDS
  1133. if (printFixedFieldsTrace)
  1134. {
  1135. Js::FunctionBody* callerFunctionBody = callInstr->m_func->GetJnFunction();
  1136. Js::FunctionBody* calleeFunctionBody = functionInfo != nullptr && functionInfo->HasBody() ? functionInfo->GetFunctionBody() : nullptr;
  1137. const char16* calleeName = calleeFunctionBody != nullptr ? calleeFunctionBody->GetDisplayName() : _u("<unknown>");
  1138. Output::Print(_u("FixedFields: function %s (%s): %s non-fixed method <unknown> (%s %s), because callee is not single def.\n"),
  1139. callerFunctionBody->GetDisplayName(), callerFunctionBody->GetDebugNumberSet(debugStringBuffer),
  1140. functionInfo != nullptr ? _u("inlining") : _u("calling"), calleeName,
  1141. calleeFunctionBody ? calleeFunctionBody->GetDebugNumberSet(debugStringBuffer2) : _u("(null)"));
  1142. Output::Flush();
  1143. }
  1144. #endif
  1145. return false;
  1146. }
  1147. IR::Instr* ldMethodFldInstr = methodValueSym->GetInstrDef();
  1148. if (ldMethodFldInstr->m_opcode != Js::OpCode::ScopedLdMethodFld
  1149. && ldMethodFldInstr->m_opcode != Js::OpCode::LdRootMethodFld
  1150. && ldMethodFldInstr->m_opcode != Js::OpCode::LdMethodFld
  1151. && ldMethodFldInstr->m_opcode != Js::OpCode::LdRootFld
  1152. && ldMethodFldInstr->m_opcode != Js::OpCode::LdFld
  1153. && ldMethodFldInstr->m_opcode != Js::OpCode::LdFldForCallApplyTarget
  1154. && ldMethodFldInstr->m_opcode != Js::OpCode::LdMethodFromFlags)
  1155. {
  1156. #if TRACE_FIXED_FIELDS
  1157. if (printFixedFieldsTrace)
  1158. {
  1159. Js::FunctionBody* callerFunctionBody = callInstr->m_func->GetJnFunction();
  1160. Js::FunctionBody* calleeFunctionBody = functionInfo != nullptr && functionInfo->HasBody() ? functionInfo->GetFunctionBody() : nullptr;
  1161. const char16* calleeName = calleeFunctionBody != nullptr ? calleeFunctionBody->GetDisplayName() : _u("<unknown>");
  1162. Output::Print(_u("FixedFields: function %s (%s): %s non-fixed method <unknown> (%s %s), because callee does not come from LdMethodFld.\n"),
  1163. callerFunctionBody->GetDisplayName(), callerFunctionBody->GetDebugNumberSet(debugStringBuffer),
  1164. functionInfo != nullptr ? _u("inlining") : _u("calling"), calleeName,
  1165. calleeFunctionBody ? calleeFunctionBody->GetDebugNumberSet(debugStringBuffer2) : _u("(null)"));
  1166. Output::Flush();
  1167. }
  1168. #endif
  1169. return false;
  1170. }
  1171. IR::PropertySymOpnd* methodPropertyOpnd = ldMethodFldInstr->GetSrc1()->AsPropertySymOpnd();
  1172. if ((isCtor &&
  1173. ((isInlined && PHASE_OFF(Js::FixedCtorInliningPhase, callInstr->m_func->GetJnFunction())) ||
  1174. (!isInlined && PHASE_OFF(Js::FixedCtorCallsPhase, callInstr->m_func->GetJnFunction())) ||
  1175. (methodPropertyOpnd->UsesAccessor()))) ||
  1176. (!isCtor &&
  1177. ((isBuiltIn &&
  1178. ((isInlined && PHASE_OFF(Js::FixedBuiltInMethodInliningPhase, callInstr->m_func->GetJnFunction())) ||
  1179. (!isInlined && PHASE_OFF(Js::FixedBuiltInMethodCallsPhase, callInstr->m_func->GetJnFunction())))) ||
  1180. (!isBuiltIn &&
  1181. ((isInlined && PHASE_OFF(Js::FixedScriptMethodInliningPhase, callInstr->m_func->GetJnFunction())) ||
  1182. (!isInlined && !PHASE_ON(Js::FixedScriptMethodCallsPhase, callInstr->m_func->GetJnFunction()))))))
  1183. )
  1184. {
  1185. #if TRACE_FIXED_FIELDS
  1186. if (printFixedFieldsTrace)
  1187. {
  1188. Js::FunctionBody* callerFunctionBody = callInstr->m_func->GetJnFunction();
  1189. Js::FunctionBody* calleeFunctionBody = functionInfo != nullptr && functionInfo->HasBody() ? functionInfo->GetFunctionBody() : nullptr;
  1190. const char16* calleeName = calleeFunctionBody != nullptr ? calleeFunctionBody->GetDisplayName() : _u("<unknown>");
  1191. Js::PropertyId methodPropertyId = callerFunctionBody->GetPropertyIdFromCacheId(methodPropertyOpnd->m_inlineCacheIndex);
  1192. Js::PropertyRecord const * const methodPropertyRecord = callerFunctionBody->GetScriptContext()->GetPropertyNameLocked(methodPropertyId);
  1193. Output::Print(_u("FixedFields: function %s (#%u): %s non-fixed method %s (%s #%u) (cache id: %d), because %s fixed %s %s is disabled.\n"),
  1194. callerFunctionBody->GetDisplayName(), callerFunctionBody->GetDebugNumberSet(debugStringBuffer),
  1195. functionInfo != nullptr ? _u("inlining") : _u("calling"), methodPropertyRecord->GetBuffer(), calleeName,
  1196. calleeFunctionBody ? calleeFunctionBody->GetDebugNumberSet(debugStringBuffer2) : _u("(null)"),
  1197. methodPropertyOpnd->m_inlineCacheIndex, isInlined ? _u("inlining") : _u("calling"), isBuiltIn ? _u("built-in") : _u("script"),
  1198. isCtor ? _u("ctors") : _u("methods"));
  1199. Output::Flush();
  1200. }
  1201. #endif
  1202. return false;
  1203. }
  1204. if (!methodPropertyOpnd->IsObjTypeSpecCandidate() && !methodPropertyOpnd->IsRootObjectNonConfigurableFieldLoad())
  1205. {
  1206. #if TRACE_FIXED_FIELDS
  1207. if (printFixedFieldsTrace)
  1208. {
  1209. Js::FunctionBody* callerFunctionBody = callInstr->m_func->GetJnFunction();
  1210. Js::FunctionBody* calleeFunctionBody = functionInfo != nullptr && functionInfo->HasBody() ? functionInfo->GetFunctionBody() : nullptr;
  1211. const char16* calleeName = calleeFunctionBody != nullptr ? calleeFunctionBody->GetDisplayName() : _u("<unknown>");
  1212. Js::PropertyId methodPropertyId = callerFunctionBody->GetPropertyIdFromCacheId(methodPropertyOpnd->m_inlineCacheIndex);
  1213. Js::PropertyRecord const * const methodPropertyRecord = callerFunctionBody->GetScriptContext()->GetPropertyNameLocked(methodPropertyId);
  1214. Output::Print(_u("FixedFields: function %s (%s): %s non-fixed method %s (%s %s) (cache id: %d), because inline cache has no cached type.\n"),
  1215. callerFunctionBody->GetDisplayName(), callerFunctionBody->GetDebugNumberSet(debugStringBuffer),
  1216. functionInfo != nullptr ? _u("inlining") : _u("calling"), methodPropertyRecord->GetBuffer(), calleeName,
  1217. calleeFunctionBody ? calleeFunctionBody->GetDebugNumberSet(debugStringBuffer2) : _u("(null)"),
  1218. methodPropertyOpnd->m_inlineCacheIndex);
  1219. Output::Flush();
  1220. }
  1221. #endif
  1222. return false;
  1223. }
  1224. Js::JavascriptFunction const * functionObject = nullptr;
  1225. if (!isPolymorphic)
  1226. {
  1227. functionObject = methodPropertyOpnd->HasFixedValue() ? methodPropertyOpnd->GetFieldValueAsFixedFunction() : nullptr;
  1228. }
  1229. else if (isPolymorphic && isInlined)
  1230. {
  1231. functionObject = methodPropertyOpnd->HasFixedValue() ? methodPropertyOpnd->GetFieldValueAsFixedFunction(i) : nullptr;
  1232. }
  1233. if (!functionObject)
  1234. {
  1235. #if TRACE_FIXED_FIELDS
  1236. if (printFixedFieldsTrace)
  1237. {
  1238. Js::FunctionBody* callerFunctionBody = callInstr->m_func->GetJnFunction();
  1239. Js::FunctionBody* calleeFunctionBody = functionInfo != nullptr && functionInfo->HasBody() ? functionInfo->GetFunctionBody() : nullptr;
  1240. const char16* calleeName = calleeFunctionBody != nullptr ? calleeFunctionBody->GetDisplayName() : _u("<unknown>");
  1241. Js::PropertyId methodPropertyId = callerFunctionBody->GetPropertyIdFromCacheId(methodPropertyOpnd->m_inlineCacheIndex);
  1242. Js::PropertyRecord const * const methodPropertyRecord = callerFunctionBody->GetScriptContext()->GetPropertyNameLocked(methodPropertyId);
  1243. Output::Print(_u("FixedFields: function %s (%s): %s non-fixed method %s (%s %s) (cache id: %d, layout: %s), because inline cache has no fixed function object.\n"),
  1244. callerFunctionBody->GetDisplayName(), callerFunctionBody->GetDebugNumberSet(debugStringBuffer),
  1245. functionInfo != nullptr ? _u("inlining") : _u("calling"), methodPropertyRecord->GetBuffer(), calleeName,
  1246. calleeFunctionBody ? calleeFunctionBody->GetDebugNumberSet(debugStringBuffer2) : _u("(null)"),
  1247. methodPropertyOpnd->m_inlineCacheIndex,
  1248. methodPropertyOpnd->IsLoadedFromProto() ? _u("proto") : methodPropertyOpnd->UsesAccessor() ? _u("accessor") : _u("local"));
  1249. Output::Flush();
  1250. }
  1251. #endif
  1252. return false;
  1253. }
  1254. // Certain built-ins that we decide not to inline will get a fast path emitted by the lowerer.
  1255. // The lowering code cannot handle a call with a fixed function target, because it needs access to
  1256. // the original property sym. Turn off fixed method calls for these cases.
  1257. if (functionInfo == nullptr && Func::IsBuiltInInlinedInLowerer(callInstr->GetSrc1()))
  1258. {
  1259. #if TRACE_FIXED_FIELDS
  1260. if (printFixedFieldsTrace)
  1261. {
  1262. Js::FunctionBody* callerFunctionBody = callInstr->m_func->GetJnFunction();
  1263. Js::FunctionBody* calleeFunctionBody = functionInfo != nullptr && functionInfo->HasBody() ? functionInfo->GetFunctionBody() : nullptr;
  1264. const char16* calleeName = calleeFunctionBody != nullptr ? calleeFunctionBody->GetDisplayName() : _u("<unknown>");
  1265. Js::PropertyId methodPropertyId = callerFunctionBody->GetPropertyIdFromCacheId(methodPropertyOpnd->m_inlineCacheIndex);
  1266. Js::PropertyRecord const * const methodPropertyRecord = callerFunctionBody->GetScriptContext()->GetPropertyNameLocked(methodPropertyId);
  1267. Output::Print(_u("FixedFields: function %s (%s): %s non-fixed method %s (%s %s) (cache id: %d, layout: %s), because callee is a built-in with fast path in lowerer.\n"),
  1268. callerFunctionBody->GetDisplayName(), callerFunctionBody->GetDebugNumberSet(debugStringBuffer),
  1269. functionInfo != nullptr ? _u("inlining") : _u("calling"), methodPropertyRecord->GetBuffer(), calleeName,
  1270. calleeFunctionBody ? calleeFunctionBody->GetDebugNumberSet(debugStringBuffer2) : _u("(null)"),
  1271. methodPropertyOpnd->m_inlineCacheIndex,
  1272. methodPropertyOpnd->IsLoadedFromProto() ? _u("proto") : methodPropertyOpnd->UsesAccessor() ? _u("accessor") : _u("local"));
  1273. Output::Flush();
  1274. }
  1275. #endif
  1276. return false;
  1277. }
  1278. if (functionInfo != nullptr && functionObject->GetFunctionInfo() != functionInfo)
  1279. {
  1280. #if TRACE_FIXED_FIELDS
  1281. if (printFixedFieldsTrace)
  1282. {
  1283. char16 debugStringBuffer3[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  1284. Js::FunctionBody* callerFunctionBody = callInstr->m_func->GetJnFunction();
  1285. Js::PropertyId methodPropertyId = callerFunctionBody->GetPropertyIdFromCacheId(methodPropertyOpnd->m_inlineCacheIndex);
  1286. Js::PropertyRecord const * const methodPropertyRecord = callerFunctionBody->GetScriptContext()->GetPropertyNameLocked(methodPropertyId);
  1287. bool isProto = methodPropertyOpnd->IsLoadedFromProto();
  1288. bool isAccessor = methodPropertyOpnd->UsesAccessor();
  1289. Js::DynamicObject* protoObject = isProto ? methodPropertyOpnd->GetProtoObject() : nullptr;
  1290. Js::FunctionBody* fixedFunctionBody = functionObject->GetFunctionInfo()->GetFunctionBody();
  1291. const char16* fixedFunctionNumbers = fixedFunctionBody ? fixedFunctionBody->GetDebugNumberSet(debugStringBuffer2) : _u("(null)");
  1292. Js::FunctionBody* profileFunctionBody = functionInfo->GetFunctionBody();
  1293. const char16* profileFunctionName = profileFunctionBody != nullptr ? profileFunctionBody->GetDisplayName() : _u("<unknown>");
  1294. const char16* profileFunctionNumbers = profileFunctionBody ? profileFunctionBody->GetDebugNumberSet(debugStringBuffer3) : _u("(null)");
  1295. if (PHASE_TRACE(Js::FixedMethodsPhase, callInstr->m_func->GetJnFunction()))
  1296. {
  1297. Output::Print(_u("FixedFields: function %s (#%s): function body mismatch for inlinee: %s (%s) 0x%p->0x%p != %s (%s) 0x%p (cache id: %d, layout: %s, type: 0x%p, proto: 0x%p, proto type: 0x%p).\n"),
  1298. callerFunctionBody->GetDisplayName(), callerFunctionBody->GetDebugNumberSet(debugStringBuffer),
  1299. methodPropertyRecord->GetBuffer(), fixedFunctionNumbers, functionObject, functionObject->GetFunctionInfo(),
  1300. profileFunctionName, profileFunctionNumbers, functionInfo,
  1301. methodPropertyOpnd->m_inlineCacheIndex, isProto ? _u("proto") : isAccessor ? _u("accessor") : _u("local"),
  1302. methodPropertyOpnd->GetType(), protoObject, protoObject != nullptr ? protoObject->GetType() : nullptr);
  1303. }
  1304. if (PHASE_TESTTRACE(Js::FixedMethodsPhase, callInstr->m_func->GetJnFunction()))
  1305. {
  1306. Output::Print(_u("FixedFields: function %s (%s): function body mismatch for inlinee: %s (%s) != %s (%s) (cache id: %d, layout: %s).\n"),
  1307. callerFunctionBody->GetDisplayName(), callerFunctionBody->GetDebugNumberSet(debugStringBuffer),
  1308. methodPropertyRecord->GetBuffer(), fixedFunctionNumbers, profileFunctionName, profileFunctionNumbers,
  1309. methodPropertyOpnd->m_inlineCacheIndex, isProto ? _u("proto") : isAccessor ? _u("accessor") : _u("local"));
  1310. }
  1311. Output::Flush();
  1312. }
  1313. #endif
  1314. // It appears that under certain bailout and re-JIT conditions we may end up with an updated
  1315. // inline cache pointing to a new function object, while the call site profile info still
  1316. // holds the old function body. If the two don't match, let's fall back on the regular LdMethodFld.
  1317. return false;
  1318. }
  1319. else
  1320. {
  1321. #if TRACE_FIXED_FIELDS
  1322. if (printFixedFieldsTrace)
  1323. {
  1324. Js::FunctionBody* callerFunctionBody = callInstr->m_func->GetJnFunction();
  1325. Js::FunctionBody* calleeFunctionBody = functionInfo != nullptr && functionInfo->HasBody() ? functionInfo->GetFunctionBody() : nullptr;
  1326. Js::PropertyId methodPropertyId = callerFunctionBody->GetPropertyIdFromCacheId(methodPropertyOpnd->m_inlineCacheIndex);
  1327. Js::PropertyRecord const * const methodPropertyRecord = callerFunctionBody->GetScriptContext()->GetPropertyNameLocked(methodPropertyId);
  1328. const char16* fixedFunctionName = calleeFunctionBody != nullptr ? calleeFunctionBody->GetDisplayName() : _u("<unknown>");
  1329. Js::FunctionBody* fixedFunctionBody = functionObject->GetFunctionInfo()->GetFunctionBody();
  1330. const char16* fixedFunctionNumbers = fixedFunctionBody ? fixedFunctionBody->GetDebugNumberSet(debugStringBuffer2) : _u("(null)");
  1331. Output::Print(_u("FixedFields: function %s (%s): %s fixed method %s (%s %s) (cache id: %d, layout: %s).\n"),
  1332. callerFunctionBody->GetDisplayName(), callerFunctionBody->GetDebugNumberSet(debugStringBuffer),
  1333. functionInfo != nullptr ? _u("inlining") : _u("calling"),
  1334. methodPropertyRecord->GetBuffer(), fixedFunctionName, fixedFunctionNumbers,
  1335. methodPropertyOpnd->m_inlineCacheIndex,
  1336. methodPropertyOpnd->IsLoadedFromProto() ? _u("proto") : methodPropertyOpnd->UsesAccessor() ? _u("accessor") : _u("local"));
  1337. Output::Flush();
  1338. }
  1339. #endif
  1340. }
  1341. #undef TRACE_FIXED_FIELDS
  1342. if (dontOptimizeJustCheck)
  1343. {
  1344. return true;
  1345. }
  1346. // Change Ld[Root]MethodFld, LdMethodFromFlags to CheckFixedFld, which doesn't need a dst.
  1347. if(ldMethodFldInstr->m_opcode == Js::OpCode::LdMethodFromFlags)
  1348. {
  1349. Assert(ldMethodFldInstr->HasBailOutInfo());
  1350. ldMethodFldInstr->ClearBailOutInfo();
  1351. }
  1352. ldMethodFldInstr->m_opcode = Js::OpCode::CheckFixedFld;
  1353. IR::Opnd * methodValueDstOpnd = ldMethodFldInstr->UnlinkDst();
  1354. IR::Instr * chkMethodFldInstr = ldMethodFldInstr->ConvertToBailOutInstr(ldMethodFldInstr,
  1355. !methodPropertyOpnd->HasEquivalentTypeSet() ? IR::BailOutFailedFixedFieldTypeCheck : IR::BailOutFailedEquivalentFixedFieldTypeCheck);
  1356. chkMethodFldInstr->GetBailOutInfo()->polymorphicCacheIndex = methodPropertyOpnd->m_inlineCacheIndex;
  1357. Assert(chkMethodFldInstr->GetSrc1()->IsSymOpnd());
  1358. if (chkMethodFldInstr->GetSrc1()->AsSymOpnd()->IsPropertySymOpnd())
  1359. {
  1360. Assert(chkMethodFldInstr->m_opcode == Js::OpCode::CheckFixedFld);
  1361. IR::PropertySymOpnd* chkMethodFldOpnd = chkMethodFldInstr->GetSrc1()->AsPropertySymOpnd();
  1362. // For polymorphic field loads we only support fixed functions on prototypes. This helps keep the equivalence check helper simple.
  1363. Assert(chkMethodFldOpnd->IsMono() || chkMethodFldOpnd->IsLoadedFromProto() || chkMethodFldOpnd->UsesAccessor());
  1364. chkMethodFldOpnd->SetUsesFixedValue(true);
  1365. }
  1366. if (isCtor)
  1367. {
  1368. Js::JitTimeConstructorCache* constructorCache = methodPropertyOpnd->GetCtorCache();
  1369. if (constructorCache != nullptr && callInstr->IsProfiledInstr())
  1370. {
  1371. #if ENABLE_DEBUG_CONFIG_OPTIONS
  1372. if (PHASE_TRACE(Js::FixedNewObjPhase, callInstr->m_func->GetJnFunction()) || PHASE_TESTTRACE(Js::FixedNewObjPhase, callInstr->m_func->GetJnFunction()))
  1373. {
  1374. Js::FunctionBody* callerFunctionBody = callInstr->m_func->GetJnFunction();
  1375. Js::FunctionBody* calleeFunctionBody = functionInfo != nullptr && functionInfo->HasBody() ? functionInfo->GetFunctionBody() : nullptr;
  1376. Js::PropertyId methodPropertyId = callerFunctionBody->GetPropertyIdFromCacheId(methodPropertyOpnd->m_inlineCacheIndex);
  1377. Js::PropertyRecord const * const methodPropertyRecord = callerFunctionBody->GetScriptContext()->GetPropertyNameLocked(methodPropertyId);
  1378. const char16* fixedFunctionName = calleeFunctionBody != nullptr ? calleeFunctionBody->GetDisplayName() : _u("<unknown>");
  1379. Js::FunctionBody* fixedFunctionBody = functionObject->GetFunctionInfo()->GetFunctionBody();
  1380. const char16* fixedFunctionNumbers = fixedFunctionBody ? fixedFunctionBody->GetDebugNumberSet(debugStringBuffer2) : _u("(null)");
  1381. Output::Print(_u("FixedNewObj: function %s (%s): fixed new object for %s with %s ctor %s (%s %s)%s\n"),
  1382. callerFunctionBody->GetDisplayName(), callerFunctionBody->GetDebugNumberSet(debugStringBuffer), Js::OpCodeUtil::GetOpCodeName(callInstr->m_opcode),
  1383. functionInfo != nullptr ? _u("inlined") : _u("called"),
  1384. methodPropertyRecord->GetBuffer(), fixedFunctionName, fixedFunctionNumbers,
  1385. constructorCache->skipNewScObject ? _u(" skip default object") : _u(""));
  1386. Output::Flush();
  1387. }
  1388. #endif
  1389. // The profile ID's hung from array ctor opcodes don't match up with normal profiled call sites.
  1390. if (callInstr->m_opcode != Js::OpCode::NewScObjArray)
  1391. {
  1392. // Because we are storing flow sensitive info in the cache (guarded property operations),
  1393. // we must make sure the same cache cannot be used multiple times in the flow.
  1394. if (constructorCache->isUsed)
  1395. {
  1396. // It's okay to allocate a JitTimeConstructorCache from the func's allocator (rather than recycler),
  1397. // because we only use these during JIT. We use the underlying runtime cache as a guard that must
  1398. // live after JIT, and these are added to the EntryPointInfo during work item creation and thus kept alive.
  1399. constructorCache = constructorCache->Clone(this->topFunc->m_alloc);
  1400. }
  1401. Assert(!constructorCache->isUsed);
  1402. constructorCache->isUsed = true;
  1403. callInstr->m_func->SetConstructorCache(static_cast<Js::ProfileId>(callInstr->AsProfiledInstr()->u.profileId), constructorCache);
  1404. }
  1405. }
  1406. else
  1407. {
  1408. #if ENABLE_DEBUG_CONFIG_OPTIONS
  1409. if (PHASE_TRACE(Js::FixedNewObjPhase, callInstr->m_func->GetJnFunction()) || PHASE_TESTTRACE(Js::FixedNewObjPhase, callInstr->m_func->GetJnFunction()))
  1410. {
  1411. Js::FunctionBody* callerFunctionBody = callInstr->m_func->GetJnFunction();
  1412. Js::FunctionBody* calleeFunctionBody = functionInfo != nullptr && functionInfo->HasBody() ? functionInfo->GetFunctionBody() : nullptr;
  1413. Js::PropertyId methodPropertyId = callerFunctionBody->GetPropertyIdFromCacheId(methodPropertyOpnd->m_inlineCacheIndex);
  1414. Js::PropertyRecord const * const methodPropertyRecord = callerFunctionBody->GetScriptContext()->GetPropertyNameLocked(methodPropertyId);
  1415. const char16* fixedFunctionName = calleeFunctionBody != nullptr ? calleeFunctionBody->GetDisplayName() : _u("<unknown>");
  1416. Js::FunctionBody* fixedFunctionBody = functionObject->GetFunctionInfo()->GetFunctionBody();
  1417. const char16* fixedFunctionNumbers = fixedFunctionBody ? fixedFunctionBody->GetDebugNumberSet(debugStringBuffer2) : _u("(null)");
  1418. Output::Print(_u("FixedNewObj: function %s (%s): non-fixed new object for %s with %s ctor %s (%s %s), because %s.\n"),
  1419. callerFunctionBody->GetDisplayName(), callerFunctionBody->GetDebugNumberSet(debugStringBuffer), Js::OpCodeUtil::GetOpCodeName(callInstr->m_opcode),
  1420. functionInfo != nullptr ? _u("inlined") : _u("called"),
  1421. methodPropertyRecord->GetBuffer(), fixedFunctionName, fixedFunctionNumbers,
  1422. constructorCache == nullptr ? _u("constructor cache hasn't been cloned") : _u("instruction isn't profiled"));
  1423. Output::Flush();
  1424. }
  1425. #endif
  1426. }
  1427. }
  1428. // Insert a load instruction to place the constant address in methodOpnd (the Ld[Root]MethodFld's original dst).
  1429. IR::AddrOpnd * constMethodValueOpnd = IR::AddrOpnd::New((Js::Var)functionObject, IR::AddrOpndKind::AddrOpndKindDynamicVar, callInstr->m_func);
  1430. constMethodValueOpnd->m_isFunction = true;
  1431. IR::Instr * ldMethodValueInstr = IR::Instr::New(Js::OpCode::Ld_A, methodValueDstOpnd, constMethodValueOpnd, callInstr->m_func);
  1432. StackSym* methodSym = methodValueDstOpnd->AsRegOpnd()->m_sym;
  1433. if (methodSym->IsSingleDef())
  1434. {
  1435. methodSym->SetIsConst();
  1436. }
  1437. methodValueDstOpnd->SetValueType(ValueType::FromObject((Js::RecyclableObject* const)functionObject));
  1438. chkMethodFldInstr->InsertAfter(ldMethodValueInstr);
  1439. callInstr->ReplaceSrc1(constMethodValueOpnd);
  1440. if (callInstr->m_opcode == Js::OpCode::CallI || callInstr->CallsAccessor(methodPropertyOpnd))
  1441. {
  1442. callInstr->m_opcode = Js::OpCode::CallIFixed;
  1443. }
  1444. else
  1445. {
  1446. // We patch later for constructor inlining.
  1447. Assert(
  1448. callInstr->m_opcode == Js::OpCode::NewScObject ||
  1449. callInstr->m_opcode == Js::OpCode::NewScObjArray);
  1450. }
  1451. if (!isBuiltIn && isInlined)
  1452. {
  1453. // We eliminate CheckThis for fixed method inlining. Assert here that our assumption is true.
  1454. Js::TypeId typeId = methodPropertyOpnd->IsRootObjectNonConfigurableField() ?
  1455. Js::TypeIds_GlobalObject : methodPropertyOpnd->GetTypeId();
  1456. if(typeId > Js::TypeIds_LastJavascriptPrimitiveType && typeId <= Js::TypeIds_LastTrueJavascriptObjectType)
  1457. {
  1458. // Eliminate CheckThis for inlining.
  1459. safeThis = true;
  1460. }
  1461. }
  1462. return true;
  1463. }
  1464. Js::Var
  1465. Inline::TryOptimizeInstrWithFixedDataProperty(IR::Instr *&instr)
  1466. {
  1467. if (PHASE_OFF(Js::UseFixedDataPropsPhase, instr->m_func->GetJnFunction()) ||
  1468. PHASE_OFF(Js::UseFixedDataPropsInInlinerPhase, instr->m_func->GetJnFunction()))
  1469. {
  1470. return nullptr;
  1471. }
  1472. if (!instr->IsProfiledInstr() ||
  1473. !instr->GetSrc1()->IsSymOpnd() || !instr->GetSrc1()->AsSymOpnd()->IsPropertySymOpnd())
  1474. {
  1475. return nullptr;
  1476. }
  1477. if (!OpCodeAttr::CanLoadFixedFields(instr->m_opcode))
  1478. {
  1479. return nullptr;
  1480. }
  1481. return instr->TryOptimizeInstrWithFixedDataProperty(&instr, nullptr);
  1482. }
  1483. // Inline a built-in/math function call, such as Math.sin(x).
  1484. // Main idea on what happens with IR during different stages.
  1485. // 1) Copy args from ArgOuts into inline instr.
  1486. // 2) Change opcode: ArgOut_A -> ArgOut_A_InlineBuiltIn (aka BIA).
  1487. // 3) Notes:
  1488. // - General logic is similar to inlining regular functions, except that:
  1489. // - There are no inner instructions to inline.
  1490. // - We don't need to support arguments object inside the inlinee - don't need inlinee meta frame, etc.
  1491. // - ArgOuts are linked through src2->m_sym->m_instrDef.
  1492. // - ArgOuts are not needed for the inlined call itself, but we can't remove them because they are needed for bailout.
  1493. // We convert them to ArgOut_A_InlineBuiltIn.
  1494. // Example for Math.pow(x, y), x86 case.
  1495. // Original:
  1496. // instrS: dstS = StartCall <N=count>, NULL -- N is actual number of parameters, including "this".
  1497. // instr0: arg0 = ArgOut t, link(->instrS) -- "this" arg
  1498. // instr1: arg1 = ArgOut x, link(->instr0) -- src1
  1499. // instr2: arg2 = ArgOut y, link(->instr1) -- src2
  1500. // instr3: dstC = CallI fn, link(->instr2) -- links to instr2, etc.
  1501. // After Inline:
  1502. // instrS: dstS = StartCall <N=count>, NULL -- N is actual number of parameters, including "this".
  1503. // tmpt = BytecodeArgOutCapture t -- create assigns to temps to snapshot argout values in case they are modified later before the call
  1504. // tmpx = BytecodeArgOutCapture x
  1505. // tmpy = BytecodeArgOutCapture y
  1506. // instr1: arg1 = ArgOut_InlineBuiltIn tmpx, link(->instr0) -- src1
  1507. // instr0: arg0 = ArgOut_InlineBuiltIn tmpt, link(->instrS) -- "this" arg -- Change ArgOut_a to ArgOut_A_InlineBuiltIn
  1508. // instr2: arg2 = ArgOut_InlineBuiltIn tmpy, link(->instr1) -- src2
  1509. // NULL = InlineBuiltInStart fn, link(->instr2)
  1510. // dstC = InlineMathPow, tmpx, tmpy -- actual native math call.
  1511. // NULL = InlineBuiltInEnd <N=count>, link(->instr2)
  1512. // After Globopt:
  1513. // instrS: dstS = StartCall <N=count>, NULL -- N is actual number of parameters, including "this".
  1514. // tmpt = BytecodeArgOutCapture t -- create assigns to temps to snapshot argout values in case they are modified later before the call
  1515. // tmpx = BytecodeArgOutCapture x
  1516. // Bailout 1
  1517. // tmpy = BytecodeArgOutCapture y
  1518. // Bailout 2
  1519. // instr1: arg1 = ArgOut_InlineBuiltIn tmpx, link(->instr0) -- src1
  1520. // instr0: arg0 = ArgOut_InlineBuiltIn tmpt, link(->instrS) -- "this" arg -- Change ArgOut_a to ArgOut_A_InlineBuiltIn
  1521. // instr2: arg2 = ArgOut_InlineBuiltIn tmpy, link(->instr1) -- src2
  1522. // ...
  1523. // NULL = InlineBuiltInStart fn, link(->instr2) -- Note that InlineBuiltInStart is after last bailout.
  1524. // This is important so that fn used for bailout is after last bailout.
  1525. // dstC = InlineMathPow, tmpx, tmpy -- actual native math call.
  1526. // NULL = InlineBuiltInEnd <N=count>, link(->instr2)
  1527. // After Lowerer:
  1528. // ...
  1529. // s1(XMM0) = MOVSD tmpx
  1530. // s2(XMM1) = MOVSD tmpy
  1531. // s1(XMM0) = CALL pow -- actual native math call.
  1532. // dstC = MOVSD s1(XMM0)
  1533. IR::Instr *
  1534. Inline::InlineBuiltInFunction(IR::Instr *callInstr, Js::FunctionInfo *funcInfo, Js::OpCode inlineCallOpCode, const Js::FunctionCodeGenJitTimeData* inlinerData, const StackSym *symCallerThis, bool* pIsInlined, uint profileId, uint recursiveInlineDepth)
  1535. {
  1536. Assert(callInstr);
  1537. Assert(funcInfo);
  1538. Assert(inlinerData);
  1539. Assert(inlineCallOpCode != 0);
  1540. // We may still decide not to inline.
  1541. *pIsInlined = false;
  1542. // Inlining is profile-based, so get the built-in function from profile rather than from the callInstr's opnd.
  1543. Js::BuiltinFunction builtInId = Js::JavascriptLibrary::GetBuiltInForFuncInfo(funcInfo, callInstr->m_func->GetScriptContext());
  1544. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  1545. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  1546. #endif
  1547. if(inlineCallOpCode == Js::OpCode::InlineMathFloor || inlineCallOpCode == Js::OpCode::InlineMathCeil || inlineCallOpCode == Js::OpCode::InlineMathRound)
  1548. {
  1549. #if defined(_M_IX86) || defined(_M_X64)
  1550. if (!AutoSystemInfo::Data.SSE4_1Available())
  1551. {
  1552. INLINE_TESTTRACE(_u("INLINING: Skip Inline: SSE4.1 not available\tInlinee: %s (#%d)\tCaller: %s\n"), Js::JavascriptLibrary::GetNameForBuiltIn(builtInId), (int)builtInId, inlinerData->GetFunctionBody()->GetDisplayName());
  1553. return callInstr->m_next;
  1554. }
  1555. #endif
  1556. if(callInstr->m_func->GetTopFunc()->GetProfileInfo()->IsFloorInliningDisabled())
  1557. {
  1558. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Floor Inlining Disabled\tInlinee: %s (#%d)\tCaller: %s\n"), Js::JavascriptLibrary::GetNameForBuiltIn(builtInId), (int)builtInId, inlinerData->GetFunctionBody()->GetDisplayName());
  1559. return callInstr->m_next;
  1560. }
  1561. }
  1562. if (callInstr->GetSrc2() &&
  1563. callInstr->GetSrc2()->IsSymOpnd() &&
  1564. callInstr->GetSrc2()->AsSymOpnd()->m_sym->AsStackSym()->GetArgSlotNum() > Js::InlineeCallInfo::MaxInlineeArgoutCount)
  1565. {
  1566. // This is a hard limit as we only use 4 bits to encode the actual count in the InlineeCallInfo. Although
  1567. // InliningDecider already checks for this, the check is against profile data that may not be accurate since profile
  1568. // data matching does not take into account some types of changes to source code. Need to check this again with current
  1569. // information.
  1570. INLINE_TESTTRACE(_u("INLINING: Skip Inline: ArgSlot > MaxInlineeArgoutCount\tInlinee: %s (#%d)\tArgSlotNum: %d\tMaxInlineeArgoutCount: %d\tCaller: %s (#%d)\n"),
  1571. Js::JavascriptLibrary::GetNameForBuiltIn(builtInId), (int)builtInId, callInstr->GetSrc2()->AsSymOpnd()->m_sym->AsStackSym()->GetArgSlotNum(),
  1572. Js::InlineeCallInfo::MaxInlineeArgoutCount, inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer));
  1573. return callInstr->m_next;
  1574. }
  1575. Js::BuiltInFlags builtInFlags = Js::JavascriptLibrary::GetFlagsForBuiltIn(builtInId);
  1576. bool isAnyArgFloat = (builtInFlags & Js::BuiltInFlags::BIF_TypeSpecAllToFloat) != 0;
  1577. if (isAnyArgFloat && !GlobOpt::DoFloatTypeSpec(this->topFunc))
  1578. {
  1579. INLINE_TESTTRACE(_u("INLINING: Skip Inline: float type spec is off\tInlinee: %s (#%d)\tCaller: %s (%s)\n"),
  1580. Js::JavascriptLibrary::GetNameForBuiltIn(builtInId), (int)builtInId,
  1581. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer));
  1582. return callInstr->m_next;
  1583. }
  1584. bool canDstBeFloat = (builtInFlags & Js::BuiltInFlags::BIF_TypeSpecDstToFloat) != 0;
  1585. if (canDstBeFloat && !Js::JavascriptLibrary::CanFloatPreferenceFunc(builtInId) && inlineCallOpCode != Js::OpCode::InlineArrayPop)
  1586. {
  1587. // Note that for Math.abs that means that even though it can potentially be type-spec'd to int, we won't inline it.
  1588. // Some built-in functions, such as atan2, are disabled for float-pref.
  1589. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Cannot float-type-spec the inlinee\tInlinee: %s (#%d)\tCaller: %s (%s)\n"),
  1590. Js::JavascriptLibrary::GetNameForBuiltIn(builtInId), (int)builtInId, // Get the _value (cause operator _E) to avoid using struct directly.
  1591. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer));
  1592. return callInstr->m_next;
  1593. }
  1594. bool isAnyArgInt = (builtInFlags & (Js::BuiltInFlags::BIF_TypeSpecDstToInt | Js::BuiltInFlags::BIF_TypeSpecSrc1ToInt | Js::BuiltInFlags::BIF_TypeSpecSrc2ToInt)) != 0;
  1595. if (isAnyArgInt && !GlobOpt::DoAggressiveIntTypeSpec(this->topFunc))
  1596. {
  1597. // Note that for Math.abs that means that even though it can potentially be type-spec'd to float, we won't inline it.
  1598. INLINE_TESTTRACE(_u("INLINING: Skip Inline: int type spec is off\tInlinee: %s (#%d)\tCaller: %s (%s)\n"),
  1599. Js::JavascriptLibrary::GetNameForBuiltIn(builtInId), (int)builtInId,
  1600. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer));
  1601. return callInstr->m_next;
  1602. }
  1603. if(inlineCallOpCode == Js::OpCode::InlineMathImul && !GlobOpt::DoLossyIntTypeSpec(topFunc))
  1604. {
  1605. INLINE_TESTTRACE(_u("INLINING: Skip Inline: lossy int type spec is off, it's required for Math.imul to do | 0 on src opnds\tInlinee: %s (#%d)\tCaller: %s (%s)\n"),
  1606. Js::JavascriptLibrary::GetNameForBuiltIn(builtInId), (int)builtInId,
  1607. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer));
  1608. return callInstr->m_next;
  1609. }
  1610. if(inlineCallOpCode == Js::OpCode::InlineMathClz32 && !GlobOpt::DoLossyIntTypeSpec(topFunc))
  1611. {
  1612. INLINE_TESTTRACE(_u("INLINING: Skip Inline: lossy int type spec is off, it's required for Math.clz32 to do | 0 on src opnds\tInlinee: %s (#%d)\tCaller: %s (%s)\n"),
  1613. Js::JavascriptLibrary::GetNameForBuiltIn(builtInId), (int)builtInId,
  1614. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer));
  1615. return callInstr->m_next;
  1616. }
  1617. if (inlineCallOpCode == Js::OpCode::InlineFunctionApply && (!callInstr->m_func->IsStackArgsEnabled() || this->topFunc->GetJnFunction()->IsInlineApplyDisabled()))
  1618. {
  1619. INLINE_TESTTRACE(_u("INLINING: Skip Inline: stack args of inlining is off\tInlinee: %s (#%d)\tCaller: %s (%s)\n"),
  1620. Js::JavascriptLibrary::GetNameForBuiltIn(builtInId), (int)builtInId,
  1621. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer));
  1622. return callInstr->m_next;
  1623. }
  1624. // TODO: when adding support for other type spec args (array, string) do appropriate check as well.
  1625. Assert(callInstr->GetSrc1());
  1626. Assert(callInstr->GetSrc1()->IsRegOpnd());
  1627. Assert(callInstr->GetSrc1()->AsRegOpnd()->m_sym);
  1628. if (!(builtInFlags & Js::BuiltInFlags::BIF_IgnoreDst) && callInstr->GetDst() == nullptr && inlineCallOpCode != Js::OpCode::InlineArrayPop)
  1629. {
  1630. // Is seems that it's not worth optimizing odd cases where the result is unused.
  1631. INLINE_TESTTRACE(_u("INLINING: Skip Inline: inlinee's return value is not assigned to anything\tInlinee: %s (#%d)\tCaller: %s (%s)\n"),
  1632. Js::JavascriptLibrary::GetNameForBuiltIn(builtInId), (int)builtInId,
  1633. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer));
  1634. return callInstr->m_next;
  1635. }
  1636. // Number of arguments, not including "this".
  1637. IntConstType requiredInlineCallArgCount = (IntConstType)Js::JavascriptLibrary::GetArgCForBuiltIn(builtInId);
  1638. IR::Opnd* linkOpnd = callInstr->GetSrc2();
  1639. Js::ArgSlot actualCount = linkOpnd->AsSymOpnd()->m_sym->AsStackSym()->GetArgSlotNum();
  1640. // Check for missing actuals:
  1641. // if number of passed params to built-in function is not what it needs, don't inline.
  1642. int inlineCallArgCount = (int)((builtInFlags & Js::BuiltInFlags::BIF_UseSrc0) != 0 ? actualCount : actualCount - 1);
  1643. Assert(inlineCallArgCount >= 0);
  1644. if (linkOpnd->IsSymOpnd())
  1645. {
  1646. if((builtInFlags & Js::BuiltInFlags::BIF_VariableArgsNumber) != 0)
  1647. {
  1648. if(inlineCallArgCount > requiredInlineCallArgCount)
  1649. {
  1650. INLINE_TESTTRACE(_u("INLINING: Skip Inline: parameter count exceeds the maximum number of parameters allowed\tInlinee: %s (#%d)\tCaller: %s (%s)\n"),
  1651. Js::JavascriptLibrary::GetNameForBuiltIn(builtInId), (int)builtInId,
  1652. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer));
  1653. return callInstr->m_next;
  1654. }
  1655. }
  1656. else if(inlineCallArgCount != requiredInlineCallArgCount)
  1657. {
  1658. INLINE_TESTTRACE(_u("INLINING: Skip Inline: parameter count doesn't match dynamic profile\tInlinee: %s (#%d)\tCaller: %s (%s)\n"),
  1659. Js::JavascriptLibrary::GetNameForBuiltIn(builtInId), (int)builtInId,
  1660. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer));
  1661. return callInstr->m_next;
  1662. }
  1663. }
  1664. IR::Instr *inlineBuiltInEndInstr = nullptr;
  1665. if (inlineCallOpCode == Js::OpCode::InlineFunctionApply)
  1666. {
  1667. inlineBuiltInEndInstr = InlineApply(callInstr, funcInfo, inlinerData, symCallerThis, pIsInlined, profileId, recursiveInlineDepth);
  1668. return inlineBuiltInEndInstr->m_next;
  1669. }
  1670. if (inlineCallOpCode == Js::OpCode::InlineFunctionCall)
  1671. {
  1672. inlineBuiltInEndInstr = InlineCall(callInstr, funcInfo, inlinerData, symCallerThis, pIsInlined, profileId, recursiveInlineDepth);
  1673. return inlineBuiltInEndInstr->m_next;
  1674. }
  1675. #if defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  1676. InliningDecider::TraceInlining(inlinerData->GetFunctionBody(), Js::JavascriptLibrary::GetNameForBuiltIn(builtInId),
  1677. nullptr, 0, this->topFunc->m_workItem->GetFunctionBody(), 0, nullptr, profileId, callInstr->m_func->GetTopFunc()->IsLoopBody(), builtInId);
  1678. #endif
  1679. // From now on we are committed to inlining.
  1680. *pIsInlined = true;
  1681. // Save off the call target operand (function object) so we can extend its lifetime as needed, even if
  1682. // the call instruction gets transformed to CallIFixed.
  1683. StackSym* originalCallTargetStackSym = callInstr->GetSrc1()->GetStackSym();
  1684. // We are committed to inlining, optimize the call instruction for fixed fields now and don't attempt it later.
  1685. bool safeThis = false;
  1686. if (TryOptimizeCallInstrWithFixedMethod(callInstr, funcInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/, safeThis /*unused here*/))
  1687. {
  1688. Assert(callInstr->m_opcode == Js::OpCode::CallIFixed);
  1689. Assert(callInstr->GetFixedFunction()->GetFunctionInfo() == funcInfo);
  1690. }
  1691. else
  1692. {
  1693. // FunctionObject check for built-ins
  1694. IR::BailOutInstr * bailOutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNotBuiltIn, IR::BailOutOnInlineFunction, callInstr, callInstr->m_func);
  1695. InsertFunctionObjectCheck(callInstr, callInstr, bailOutInstr, funcInfo);
  1696. }
  1697. // To push function object for cases when we have to make calls to helper method to assist in inlining
  1698. if(inlineCallOpCode == Js::OpCode::CallDirect)
  1699. {
  1700. IR::Instr* argoutInstr;
  1701. StackSym *dstSym = callInstr->m_func->m_symTable->GetArgSlotSym((uint16)(1));
  1702. argoutInstr = IR::Instr::New(Js::OpCode::ArgOut_A_InlineSpecialized, IR::SymOpnd::New(dstSym, 0, TyMachPtr, callInstr->m_func), callInstr->UnlinkSrc1(), callInstr->UnlinkSrc2(), callInstr->m_func);
  1703. argoutInstr->SetByteCodeOffset(callInstr);
  1704. callInstr->GetInsertBeforeByteCodeUsesInstr()->InsertBefore(argoutInstr);
  1705. Js::BuiltinFunction builtInFunctionId = Js::JavascriptLibrary::GetBuiltInForFuncInfo(funcInfo, callInstr->m_func->GetScriptContext());
  1706. callInstr->m_opcode = inlineCallOpCode;
  1707. SetupInlineInstrForCallDirect(builtInFunctionId, callInstr, argoutInstr);
  1708. // Generate ByteCodeArgOutCaptures and move the ArgOut_A/ArgOut_A_Inline close to the call instruction
  1709. callInstr->MoveArgs(/*generateByteCodeCapture*/ true);
  1710. WrapArgsOutWithCoerse(builtInFunctionId, callInstr);
  1711. inlineBuiltInEndInstr = callInstr;
  1712. }
  1713. else
  1714. {
  1715. inlineBuiltInEndInstr = InsertInlineeBuiltInStartEndTags(callInstr, actualCount);
  1716. // InlineArrayPop - TrackCalls Need to be done at InlineArrayPop and not at the InlineBuiltInEnd
  1717. // Hence we use a new opcode, to detect that it is an InlineArrayPop and we don't track the call during End of inlineBuiltInCall sequence
  1718. if(inlineCallOpCode == Js::OpCode::InlineArrayPop)
  1719. {
  1720. inlineBuiltInEndInstr->m_opcode = Js::OpCode::InlineNonTrackingBuiltInEnd;
  1721. }
  1722. }
  1723. // Insert a byteCodeUsesInstr to make sure the function object's lifetime is extended beyond the last bailout point
  1724. // at which we may need to call the inlinee again in the interpreter.
  1725. {
  1726. IR::ByteCodeUsesInstr * useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr, originalCallTargetStackSym->m_id);
  1727. callInstr->InsertBefore(useCallTargetInstr);
  1728. }
  1729. if(Js::JavascriptLibrary::IsTypeSpecRequired(builtInFlags)
  1730. // SIMD_JS
  1731. || IsSimd128Opcode(inlineCallOpCode)
  1732. //
  1733. )
  1734. {
  1735. // Emit byteCodeUses for function object
  1736. IR::Instr * inlineBuiltInStartInstr = inlineBuiltInEndInstr;
  1737. while(inlineBuiltInStartInstr->m_opcode != Js::OpCode::InlineBuiltInStart)
  1738. {
  1739. inlineBuiltInStartInstr = inlineBuiltInStartInstr->m_prev;
  1740. }
  1741. IR::Opnd * tmpDst = nullptr;
  1742. IR::Opnd * callInstrDst = callInstr->GetDst();
  1743. if(callInstrDst && inlineCallOpCode != Js::OpCode::InlineArrayPop)
  1744. {
  1745. StackSym * tmpSym = StackSym::New(callInstr->GetDst()->GetType(), callInstr->m_func);
  1746. tmpDst = IR::RegOpnd::New(tmpSym, tmpSym->GetType(), callInstr->m_func);
  1747. callInstrDst = callInstr->UnlinkDst();
  1748. callInstr->SetDst(tmpDst);
  1749. }
  1750. else
  1751. {
  1752. AssertMsg(inlineCallOpCode == Js::OpCode::InlineArrayPush || inlineCallOpCode == Js::OpCode::InlineArrayPop || Js::IsSimd128Opcode(inlineCallOpCode),
  1753. "Currently Dst can be null only for InlineArrayPush/InlineArrayPop");
  1754. }
  1755. // Insert a byteCodeUsesInstr to make sure the function object's lifetime is extended beyond the last bailout point
  1756. // at which we may need to call the inlinee again in the interpreter.
  1757. IR::ByteCodeUsesInstr * useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr->GetPrevRealInstrOrLabel(), originalCallTargetStackSym->m_id);
  1758. if(inlineCallOpCode == Js::OpCode::InlineArrayPop)
  1759. {
  1760. callInstr->InsertBefore(useCallTargetInstr);
  1761. }
  1762. else
  1763. {
  1764. inlineBuiltInEndInstr->InsertBefore(useCallTargetInstr);
  1765. }
  1766. if(tmpDst)
  1767. {
  1768. IR::Instr * ldInstr = IR::Instr::New(Js::OpCode::Ld_A, callInstrDst, tmpDst, callInstr->m_func);
  1769. inlineBuiltInEndInstr->InsertBefore(ldInstr);
  1770. }
  1771. // Set srcs of the callInstr, and process ArgOuts.
  1772. callInstr->UnlinkSrc1();
  1773. callInstr->UnlinkSrc2();
  1774. callInstr->m_opcode = inlineCallOpCode;
  1775. int argIndex = inlineCallArgCount; // We'll use it to fill call instr srcs from upper to lower.
  1776. IR::ByteCodeUsesInstr * byteCodeUsesInstr = IR::ByteCodeUsesInstr::New(callInstr->m_func);
  1777. byteCodeUsesInstr->SetByteCodeOffset(callInstr);
  1778. byteCodeUsesInstr->byteCodeUpwardExposedUsed = JitAnew(callInstr->m_func->m_alloc, BVSparse<JitArenaAllocator>, callInstr->m_func->m_alloc);
  1779. IR::Instr *argInsertInstr = inlineBuiltInStartInstr;
  1780. #ifdef ENABLE_SIMDJS
  1781. // SIMD_JS
  1782. IR::Instr *eaInsertInstr = callInstr;
  1783. IR::Opnd *eaLinkOpnd = nullptr;
  1784. ThreadContext::SimdFuncSignature simdFuncSignature;
  1785. if (IsSimd128Opcode(callInstr->m_opcode))
  1786. {
  1787. callInstr->m_func->GetScriptContext()->GetThreadContext()->GetSimdFuncSignatureFromOpcode(callInstr->m_opcode, simdFuncSignature);
  1788. Assert(simdFuncSignature.valid);
  1789. // if we have decided to inline, then actual arg count == signature arg count == required arg count from inlinee list (LibraryFunction.h)
  1790. Assert(simdFuncSignature.argCount == (uint)inlineCallArgCount);
  1791. Assert(simdFuncSignature.argCount == (uint)requiredInlineCallArgCount);
  1792. }
  1793. #endif
  1794. inlineBuiltInEndInstr->IterateArgInstrs([&](IR::Instr* argInstr) {
  1795. StackSym *linkSym = linkOpnd->GetStackSym();
  1796. linkSym->m_isInlinedArgSlot = true;
  1797. linkSym->m_allocated = true;
  1798. // We are going to replace the use on the call (below), insert byte code use if necessary
  1799. if (OpCodeAttr::BailOutRec(inlineCallOpCode) || Js::IsSimd128Opcode(inlineCallOpCode))
  1800. {
  1801. StackSym * sym = argInstr->GetSrc1()->GetStackSym();
  1802. if (!sym->m_isSingleDef || !sym->m_instrDef->GetSrc1() || !sym->m_instrDef->GetSrc1()->IsConstOpnd())
  1803. {
  1804. if (!sym->IsFromByteCodeConstantTable())
  1805. {
  1806. byteCodeUsesInstr->byteCodeUpwardExposedUsed->Set(sym->m_id);
  1807. }
  1808. }
  1809. }
  1810. // Convert the arg out to built in arg out, and get the src of the arg out
  1811. IR::Opnd * argOpnd = ConvertToInlineBuiltInArgOut(argInstr);
  1812. #ifdef ENABLE_SIMDJS
  1813. // SIMD_JS
  1814. if (inlineCallArgCount > 2 && argIndex != 0 /* don't include 'this' */)
  1815. {
  1816. Assert(IsSimd128Opcode(callInstr->m_opcode));
  1817. // Insert ExtendedArgs
  1818. IR::Instr *eaInstr;
  1819. // inliner sets the dst type of the ExtendedArg to the expected arg type for the operation. The globOpt uses this info to know the type-spec target for each ExtendedArg.
  1820. eaInstr = IR::Instr::New(Js::OpCode::ExtendArg_A, callInstr->m_func);
  1821. eaInstr->SetByteCodeOffset(callInstr);
  1822. if (argIndex == inlineCallArgCount)
  1823. {
  1824. // fix callInstr
  1825. eaLinkOpnd = IR::RegOpnd::New(TyVar, callInstr->m_func);
  1826. eaLinkOpnd->GetStackSym()->m_isInlinedArgSlot = true;
  1827. eaLinkOpnd->GetStackSym()->m_allocated = true;
  1828. Assert(callInstr->GetSrc1() == nullptr && callInstr->GetSrc2() == nullptr);
  1829. callInstr->SetSrc1(eaLinkOpnd);
  1830. }
  1831. Assert(eaLinkOpnd);
  1832. eaInstr->SetDst(eaLinkOpnd);
  1833. eaInstr->SetSrc1(argInstr->GetSrc1());
  1834. // insert link opnd, except for first ExtendedArg
  1835. if (argIndex > 1)
  1836. {
  1837. eaInstr->SetSrc2(IR::RegOpnd::New(TyVar, callInstr->m_func));
  1838. eaLinkOpnd = eaInstr->GetSrc2();
  1839. eaLinkOpnd->GetStackSym()->m_isInlinedArgSlot = true;
  1840. eaLinkOpnd->GetStackSym()->m_allocated = true;
  1841. }
  1842. eaInstr->GetDst()->SetValueType(simdFuncSignature.args[argIndex - 1]);
  1843. eaInsertInstr->InsertBefore(eaInstr);
  1844. eaInsertInstr = eaInstr;
  1845. }
  1846. else
  1847. #endif
  1848. {
  1849. // Use parameter to the inline call to tempDst.
  1850. if (argIndex == 2)
  1851. {
  1852. callInstr->SetSrc2(argOpnd);
  1853. // Prevent inserting ByteCodeUses instr during globopt, as we already track the src in ArgOut.
  1854. callInstr->GetSrc2()->SetIsJITOptimizedReg(true);
  1855. }
  1856. else if (argIndex == 1)
  1857. {
  1858. callInstr->SetSrc1(argOpnd);
  1859. // Prevent inserting ByteCodeUses instr during globopt, as we already track the src in ArgOut.
  1860. callInstr->GetSrc1()->SetIsJITOptimizedReg(true);
  1861. }
  1862. }
  1863. argIndex--;
  1864. linkOpnd = argInstr->GetSrc2();
  1865. // Move the arguments next to the call.
  1866. argInstr->Move(argInsertInstr);
  1867. argInsertInstr = argInstr;
  1868. return false;
  1869. });
  1870. #ifdef ENABLE_SIMDJS
  1871. //SIMD_JS
  1872. Simd128FixLoadStoreInstr(builtInId, callInstr);
  1873. #endif
  1874. if(inlineCallOpCode == Js::OpCode::InlineMathImul || inlineCallOpCode == Js::OpCode::InlineMathClz32)
  1875. {
  1876. // Convert:
  1877. // s1 = InlineMathImul s2, s3
  1878. // Into:
  1879. // s4 = Or_A s2, 0
  1880. // s5 = Or_A s3, 0
  1881. // s1 = InlineMathImul s4, s5
  1882. Func *const func = callInstr->m_func;
  1883. IR::AddrOpnd *const zeroOpnd = IR::AddrOpnd::NewFromNumber(0, func, true);
  1884. IR::RegOpnd *const s4 = IR::RegOpnd::New(TyVar, func);
  1885. s4->SetIsJITOptimizedReg(true);
  1886. IR::Instr *orInstr = IR::Instr::New(Js::OpCode::Or_A, s4, callInstr->UnlinkSrc1(), zeroOpnd, func);
  1887. orInstr->SetByteCodeOffset(callInstr);
  1888. callInstr->InsertBefore(orInstr);
  1889. callInstr->SetSrc1(s4);
  1890. if (inlineCallOpCode == Js::OpCode::InlineMathImul)
  1891. {
  1892. if (callInstr->GetSrc2()->IsEqual(callInstr->GetSrc1()))
  1893. {
  1894. callInstr->ReplaceSrc2(s4);
  1895. }
  1896. else
  1897. {
  1898. IR::RegOpnd *const s5 = IR::RegOpnd::New(TyVar, func);
  1899. s5->SetIsJITOptimizedReg(true);
  1900. orInstr = IR::Instr::New(Js::OpCode::Or_A, s5, callInstr->UnlinkSrc2(), zeroOpnd, func);
  1901. orInstr->SetByteCodeOffset(callInstr);
  1902. callInstr->InsertBefore(orInstr);
  1903. callInstr->SetSrc2(s5);
  1904. }
  1905. }
  1906. }
  1907. if(OpCodeAttr::BailOutRec(inlineCallOpCode))
  1908. {
  1909. inlineBuiltInEndInstr->InsertBefore(byteCodeUsesInstr);
  1910. }
  1911. Assert(linkOpnd->AsRegOpnd()->m_sym->GetInstrDef()->m_opcode == Js::OpCode::StartCall);
  1912. Assert(linkOpnd->AsRegOpnd()->m_sym->GetInstrDef()->GetArgOutCount(/*getInterpreterArgOutCount*/ false) == actualCount);
  1913. // Mark the StartCall's dst as an inlined arg slot as well so we know this is an inlined start call
  1914. // and not adjust the stack height on x86
  1915. linkOpnd->AsRegOpnd()->m_sym->m_isInlinedArgSlot = true;
  1916. if(OpCodeAttr::BailOutRec(inlineCallOpCode))
  1917. {
  1918. callInstr = callInstr->ConvertToBailOutInstr(callInstr, IR::BailOutOnFloor);
  1919. }
  1920. }
  1921. return inlineBuiltInEndInstr->m_next;
  1922. }
  1923. IR::Instr* Inline::InsertInlineeBuiltInStartEndTags(IR::Instr* callInstr, uint actualCount, IR::Instr** builtinStartInstr)
  1924. {
  1925. IR::Instr* inlineBuiltInStartInstr = IR::Instr::New(Js::OpCode::InlineBuiltInStart, callInstr->m_func);
  1926. inlineBuiltInStartInstr->SetSrc1(callInstr->GetSrc1());
  1927. inlineBuiltInStartInstr->SetSrc2(callInstr->GetSrc2());
  1928. inlineBuiltInStartInstr->SetByteCodeOffset(callInstr);
  1929. callInstr->InsertBefore(inlineBuiltInStartInstr);
  1930. if (builtinStartInstr)
  1931. {
  1932. *builtinStartInstr = inlineBuiltInStartInstr;
  1933. }
  1934. IR::Instr* inlineBuiltInEndInstr = IR::Instr::New(Js::OpCode::InlineBuiltInEnd, callInstr->m_func);
  1935. inlineBuiltInEndInstr->SetSrc1(IR::IntConstOpnd::New(actualCount, TyInt32, callInstr->m_func));
  1936. inlineBuiltInEndInstr->SetSrc2(callInstr->GetSrc2());
  1937. inlineBuiltInEndInstr->SetByteCodeOffset(callInstr->GetNextRealInstrOrLabel());
  1938. callInstr->InsertAfter(inlineBuiltInEndInstr);
  1939. return inlineBuiltInEndInstr;
  1940. }
  1941. IR::Instr* Inline::GetDefInstr(IR::Opnd* linkOpnd)
  1942. {
  1943. StackSym *linkSym = linkOpnd->AsSymOpnd()->m_sym->AsStackSym();
  1944. Assert(linkSym->m_isSingleDef);
  1945. Assert(linkSym->IsArgSlotSym());
  1946. return linkSym->m_instrDef;
  1947. }
  1948. IR::Instr* Inline::InlineApply(IR::Instr *callInstr, Js::FunctionInfo *funcInfo, const Js::FunctionCodeGenJitTimeData* inlinerData, const StackSym *symCallerThis, bool* pIsInlined, uint callSiteId, uint recursiveInlineDepth)
  1949. {
  1950. // We may still decide not to inline.
  1951. *pIsInlined = false;
  1952. Js::BuiltinFunction builtInId = Js::JavascriptLibrary::GetBuiltInForFuncInfo(funcInfo, callInstr->m_func->GetScriptContext());
  1953. const Js::FunctionCodeGenJitTimeData * inlineeData = nullptr;
  1954. IR::SymOpnd* linkOpnd = callInstr->GetSrc2()->AsSymOpnd();
  1955. StackSym *arrayArgsym = linkOpnd->AsSymOpnd()->m_sym->AsStackSym();
  1956. Assert(arrayArgsym->m_isSingleDef);
  1957. Assert(arrayArgsym->IsArgSlotSym());
  1958. IR::Instr* arrayArgInstr = arrayArgsym->m_instrDef;
  1959. IR::Opnd *arrayArgOpnd = arrayArgInstr->GetSrc1();
  1960. // if isArrayOpndArgumentsObject == false, the array opnd can still be the arguments object; we just can't say that for sure
  1961. bool isArrayOpndArgumentsObject = arrayArgOpnd->IsArgumentsObject();
  1962. IR::Instr * returnInstr = nullptr;
  1963. if (!PHASE_OFF(Js::InlineApplyTargetPhase, this->topFunc))
  1964. {
  1965. if (InlineApplyTarget(callInstr, inlinerData, &inlineeData, funcInfo, symCallerThis, &returnInstr, recursiveInlineDepth, isArrayOpndArgumentsObject))
  1966. {
  1967. *pIsInlined = true;
  1968. Assert(returnInstr);
  1969. return returnInstr;
  1970. }
  1971. }
  1972. #if defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  1973. InliningDecider::TraceInlining(inlinerData->GetFunctionBody(), Js::JavascriptLibrary::GetNameForBuiltIn(builtInId),
  1974. nullptr, 0, this->topFunc->m_workItem->GetFunctionBody(), 0, nullptr, callSiteId, callInstr->m_func->GetTopFunc()->IsLoopBody(), builtInId);
  1975. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  1976. #endif
  1977. if (!isArrayOpndArgumentsObject)
  1978. {
  1979. if (inlineeData && inlineeData->GetFunctionBody() == nullptr)
  1980. {
  1981. *pIsInlined = true;
  1982. Assert((inlineeData->GetFunctionInfo()->GetAttributes() & Js::FunctionInfo::Attributes::BuiltInInlinableAsLdFldInlinee) != 0);
  1983. return InlineApplyWithArray(callInstr, funcInfo, Js::JavascriptLibrary::GetBuiltInForFuncInfo(inlineeData->GetFunctionInfo(), callInstr->m_func->GetScriptContext()));
  1984. }
  1985. else
  1986. {
  1987. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Supporting inlining func.apply(this, array) or func.apply(this, arguments) with formals in the parent function only when func is a built-in inlinable as apply target \tCaller: %s (%s)\n"),
  1988. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer));
  1989. return callInstr;
  1990. }
  1991. }
  1992. *pIsInlined = true;
  1993. return InlineApplyWithArgumentsObject(callInstr, arrayArgInstr, funcInfo);
  1994. }
  1995. IR::Instr * Inline::InlineApplyWithArgumentsObject(IR::Instr * callInstr, IR::Instr * argsObjectArgInstr, Js::FunctionInfo * funcInfo)
  1996. {
  1997. IR::Instr* ldHeapArguments = argsObjectArgInstr->GetSrc1()->GetStackSym()->GetInstrDef();
  1998. argsObjectArgInstr->ReplaceSrc1(ldHeapArguments->GetDst());
  1999. IR::Opnd * linkOpnd = callInstr->GetSrc2()->AsSymOpnd();
  2000. IR::Instr * explicitThisArgOut = nullptr;
  2001. IR::Instr * implicitThisArgOut = nullptr;
  2002. callInstr->IterateArgInstrs([&](IR::Instr* argInstr) {
  2003. explicitThisArgOut = implicitThisArgOut;
  2004. implicitThisArgOut = argInstr;
  2005. linkOpnd->AsSymOpnd()->m_sym->AsStackSym()->m_isInlinedArgSlot = true;
  2006. linkOpnd->AsSymOpnd()->m_sym->AsStackSym()->m_allocated = true;
  2007. ConvertToInlineBuiltInArgOut(argInstr);
  2008. linkOpnd = argInstr->GetSrc2();
  2009. return false;
  2010. });
  2011. // BailOnNotEqual s4.var ---------------New additional BAILOUT if not stack args or actuals exceed 16 at runtime.
  2012. // Bailout: #004e (BailOutOnInlineFunction)
  2013. // linkOpnd Argout_FromStackArgs s4.var
  2014. // linkOpnd1 ArgOut_A_Dynamic s3.var, linkOpnd
  2015. // CallI_Dynamic s6.var, linkOpnd1
  2016. IR::Instr* bailOutOnNotStackArgs;
  2017. IR::Instr* bailOutOnNotStackArgsInsertionPoint = callInstr;
  2018. // Save off the call target operand (function object) so we can extend its lifetime as needed, even if
  2019. // the call instruction gets transformed to CallIFixed.
  2020. StackSym* originalCallTargetStackSym = callInstr->GetSrc1()->GetStackSym();
  2021. // If we optimized the call instruction for a fixed function we will have bailed out earlier if the function
  2022. // wasn't what we expected or was not a function at all. However, we must still check and bail out on heap arguments.
  2023. bool safeThis = false;
  2024. if (TryOptimizeCallInstrWithFixedMethod(callInstr, funcInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/, safeThis /*unused here*/))
  2025. {
  2026. Assert(callInstr->m_opcode == Js::OpCode::CallIFixed);
  2027. bailOutOnNotStackArgs = IR::BailOutInstr::New(Js::OpCode::BailOnNotStackArgs, IR::BailOutOnInlineFunction, callInstr, callInstr->m_func);
  2028. }
  2029. else
  2030. {
  2031. IR::Instr *primaryBailoutInstr = PrepareInsertionPoint(callInstr, funcInfo, callInstr);
  2032. bailOutOnNotStackArgs = IR::BailOutInstr::New(Js::OpCode::BailOnNotStackArgs, IR::BailOutOnInlineFunction, primaryBailoutInstr->GetBailOutInfo(), callInstr->m_func);
  2033. bailOutOnNotStackArgsInsertionPoint = primaryBailoutInstr;
  2034. }
  2035. bailOutOnNotStackArgsInsertionPoint->InsertBefore(bailOutOnNotStackArgs);
  2036. // If we optimized the call instruction for a fixed function, we must extend the function object's lifetime until after
  2037. // the bailout on non-stack arguments.
  2038. if (callInstr->m_opcode == Js::OpCode::CallIFixed)
  2039. {
  2040. IR::ByteCodeUsesInstr * useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr, originalCallTargetStackSym->m_id);
  2041. callInstr->InsertBefore(useCallTargetInstr);
  2042. }
  2043. // Optimize .init.apply(this, arguments);
  2044. IR::Instr* builtInStartInstr;
  2045. InsertInlineeBuiltInStartEndTags(callInstr, 3, &builtInStartInstr); //3 args (implicit this + explicit this + arguments = 3)
  2046. // Move argouts close to call
  2047. IR::Instr* argInsertInstr = builtInStartInstr;
  2048. builtInStartInstr->IterateArgInstrs([&](IR::Instr* argInstr) {
  2049. argInstr->Move(argInsertInstr);
  2050. argInsertInstr = argInstr;
  2051. return false;
  2052. });
  2053. IR::Instr *startCall = IR::Instr::New(Js::OpCode::StartCall, callInstr->m_func);
  2054. startCall->SetDst(IR::RegOpnd::New(TyVar, callInstr->m_func));
  2055. startCall->SetSrc1(IR::IntConstOpnd::New(2, TyInt32, callInstr->m_func)); //2 args (this pointer & ArgOut_A_From_StackArgs for this direct call to init
  2056. callInstr->InsertBefore(startCall);
  2057. StackSym *symDst = callInstr->m_func->m_symTable->GetArgSlotSym((uint16)(2));
  2058. IR::SymOpnd* linkOpnd1 = IR::SymOpnd::New(symDst, 0, TyMachPtr, callInstr->m_func);
  2059. symDst = callInstr->m_func->m_symTable->GetArgSlotSym((uint16)(1));
  2060. IR::Opnd *linkOpnd2 = IR::SymOpnd::New(symDst, 0, TyMachPtr, callInstr->m_func);
  2061. // This keeps the stack args alive for bailout to recover
  2062. IR::Instr* argout = IR::Instr::New(Js::OpCode::ArgOut_A_FromStackArgs, linkOpnd1, ldHeapArguments->GetDst(), startCall->GetDst(), callInstr->m_func);
  2063. callInstr->InsertBefore(argout);
  2064. callInstr->ReplaceSrc1(implicitThisArgOut->GetSrc1());
  2065. callInstr->ReplaceSrc2(linkOpnd2);
  2066. callInstr->m_opcode = Js::OpCode::CallIDynamic;
  2067. argout = IR::Instr::New(Js::OpCode::ArgOut_A_Dynamic, linkOpnd2, explicitThisArgOut->GetSrc1(), linkOpnd1, callInstr->m_func); // push explicit this as this pointer
  2068. callInstr->InsertBefore(argout);
  2069. return callInstr;
  2070. }
  2071. IR::Instr * Inline::InlineApplyWithArray(IR::Instr * callInstr, Js::FunctionInfo * funcInfo, Js::BuiltinFunction builtInId)
  2072. {
  2073. IR::Opnd * linkOpnd = callInstr->GetSrc2()->AsSymOpnd();
  2074. IR::Instr * argInsertInstr = callInstr;
  2075. IR::Instr * arrayArgOut = nullptr;
  2076. IR::Instr * explicitThisArgOut = nullptr;
  2077. IR::Instr * implicitThisArgOut = nullptr;
  2078. callInstr->IterateArgInstrs([&](IR::Instr* argInstr) {
  2079. arrayArgOut = explicitThisArgOut;
  2080. explicitThisArgOut = implicitThisArgOut;
  2081. implicitThisArgOut = argInstr;
  2082. linkOpnd->AsSymOpnd()->m_sym->AsStackSym()->m_isInlinedArgSlot = true;
  2083. linkOpnd->AsSymOpnd()->m_sym->AsStackSym()->m_allocated = true;
  2084. ConvertToInlineBuiltInArgOut(argInstr);
  2085. argInstr->Move(argInsertInstr);
  2086. argInsertInstr = argInstr;
  2087. linkOpnd = argInstr->GetSrc2();
  2088. return false;
  2089. });
  2090. StackSym* originalCallTargetStackSym = callInstr->GetSrc1()->GetStackSym();
  2091. // If we optimized the call instruction for a fixed function we will have bailed out earlier if the function
  2092. // wasn't what we expected or was not a function at all. However, we must still check and bail out on heap arguments.
  2093. bool safeThis = false;
  2094. if (TryOptimizeCallInstrWithFixedMethod(callInstr, funcInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/, safeThis /*unused here*/))
  2095. {
  2096. Assert(callInstr->m_opcode == Js::OpCode::CallIFixed);
  2097. }
  2098. else
  2099. {
  2100. PrepareInsertionPoint(callInstr, funcInfo, callInstr);
  2101. }
  2102. // If we optimized the call instruction for a fixed function, we must extend the function object's lifetime until after the last bailout before the call.
  2103. if (callInstr->m_opcode == Js::OpCode::CallIFixed)
  2104. {
  2105. IR::ByteCodeUsesInstr * useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr, originalCallTargetStackSym->m_id);
  2106. callInstr->InsertBefore(useCallTargetInstr);
  2107. }
  2108. IR::Instr* builtInEndInstr = InsertInlineeBuiltInStartEndTags(callInstr, 3); // 3 args (implicit this + explicit this + array = 3)
  2109. builtInEndInstr->m_opcode = Js::OpCode::InlineNonTrackingBuiltInEnd; // We will call EndTrackCall when we see CallDirect for reasons explained in GlobOpt::TrackCalls
  2110. IR::Instr * startCall = IR::Instr::New(Js::OpCode::StartCall,
  2111. IR::RegOpnd::New(TyVar, callInstr->m_func),
  2112. IR::IntConstOpnd::New(2, TyInt32, callInstr->m_func),
  2113. callInstr->m_func);
  2114. callInstr->InsertBefore(startCall);
  2115. StackSym * sym = callInstr->m_func->m_symTable->GetArgSlotSym((uint16)(1));
  2116. linkOpnd = IR::SymOpnd::New(sym, 0, TyMachPtr, callInstr->m_func);
  2117. IR::Instr * argOut = IR::Instr::New(Js::OpCode::ArgOut_A, linkOpnd, explicitThisArgOut->GetSrc1(), startCall->GetDst(), callInstr->m_func);
  2118. callInstr->InsertBefore(argOut);
  2119. sym = callInstr->m_func->m_symTable->GetArgSlotSym((uint16)(2));
  2120. linkOpnd = IR::SymOpnd::New(sym, 0, TyMachPtr, callInstr->m_func);
  2121. argOut = IR::Instr::New(Js::OpCode::ArgOut_A, linkOpnd, arrayArgOut->GetSrc1(), argOut->GetDst(), callInstr->m_func);
  2122. callInstr->InsertBefore(argOut);
  2123. linkOpnd = IR::SymOpnd::New(callInstr->m_func->m_symTable->GetArgSlotSym((uint16)(1)), 0, TyMachPtr, callInstr->m_func);
  2124. argOut = IR::Instr::New(Js::OpCode::ArgOut_A_InlineSpecialized, linkOpnd, implicitThisArgOut->GetSrc1(), argOut->GetDst(), callInstr->m_func);
  2125. callInstr->InsertBefore(argOut);
  2126. IR::HelperCallOpnd * helperCallOpnd = nullptr;
  2127. switch (builtInId)
  2128. {
  2129. case Js::BuiltinFunction::Math_Max:
  2130. helperCallOpnd = IR::HelperCallOpnd::New(IR::HelperOp_MaxInAnArray, callInstr->m_func);
  2131. break;
  2132. case Js::BuiltinFunction::Math_Min:
  2133. helperCallOpnd = IR::HelperCallOpnd::New(IR::HelperOp_MinInAnArray, callInstr->m_func);
  2134. break;
  2135. default:
  2136. Assert(false);
  2137. __assume(UNREACHED);
  2138. }
  2139. callInstr->m_opcode = Js::OpCode::CallDirect;
  2140. callInstr->ReplaceSrc1(helperCallOpnd);
  2141. callInstr->ReplaceSrc2(argOut->GetDst());
  2142. return callInstr;
  2143. }
  2144. bool Inline::InlineApplyTarget(IR::Instr *callInstr, const Js::FunctionCodeGenJitTimeData* inlinerData, const Js::FunctionCodeGenJitTimeData** pInlineeData, Js::FunctionInfo *applyFuncInfo,
  2145. const StackSym *symCallerThis, IR::Instr ** returnInstr, uint recursiveInlineDepth, bool isArrayOpndArgumentsObject)
  2146. {
  2147. #if ENABLE_DEBUG_CONFIG_OPTIONS
  2148. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2149. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2150. #endif
  2151. if (this->isApplyTargetInliningInProgress)
  2152. {
  2153. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Skipping apply target inlining, Recursive apply inlining is not supported \tCaller: %s\t(%s) \tTop Func:%s\t(%s)\n"), inlinerData->GetFunctionBody()->GetDisplayName(),
  2154. inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer), this->topFunc->GetJnFunction()->GetDisplayName(), this->topFunc->GetJnFunction()->GetDebugNumberSet(debugStringBuffer2));
  2155. return false;
  2156. }
  2157. // Begin inlining apply target
  2158. IR::Opnd* applyOpnd = callInstr->GetSrc1();
  2159. Assert(applyOpnd->IsRegOpnd());
  2160. StackSym* applySym = applyOpnd->AsRegOpnd()->m_sym->AsStackSym();
  2161. if (!applySym->IsSingleDef())
  2162. {
  2163. return false;
  2164. }
  2165. IR::Instr* applyLdInstr = applySym->GetInstrDef();
  2166. IR::Instr* applyTargetLdInstr = applyLdInstr->m_prev;
  2167. if(applyTargetLdInstr->m_opcode != Js::OpCode::LdFldForCallApplyTarget ||
  2168. ((applyTargetLdInstr->AsProfiledInstr()->u.FldInfo().flags & Js::FldInfo_FromAccessor) != 0))
  2169. {
  2170. return false;
  2171. }
  2172. IR::Opnd *applyTargetLdOpnd = applyTargetLdInstr->GetSrc1();
  2173. if (!applyTargetLdOpnd->IsSymOpnd() || !applyTargetLdOpnd->AsSymOpnd()->IsPropertySymOpnd())
  2174. {
  2175. return false;
  2176. }
  2177. const auto inlineCacheIndex = applyTargetLdOpnd->AsPropertySymOpnd()->m_inlineCacheIndex;
  2178. const auto inlineeData = inlinerData->GetLdFldInlinee(inlineCacheIndex);
  2179. if (!isArrayOpndArgumentsObject || SkipCallApplyTargetInlining_Shared(callInstr, inlinerData, inlineeData, /*isApplyTarget*/ true, /*isCallTarget*/ false))
  2180. {
  2181. *pInlineeData = inlineeData;
  2182. return false;
  2183. }
  2184. if (callInstr->m_func->IsTopFunc())
  2185. {
  2186. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Skipping apply target inlining in top func\tCaller: %s\t(%s) \tTop Func:%s\t(%s)\n"), inlinerData->GetFunctionBody()->GetDisplayName(),
  2187. inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer), this->topFunc->GetJnFunction()->GetDisplayName(), this->topFunc->GetJnFunction()->GetDebugNumberSet(debugStringBuffer2));
  2188. return false;
  2189. }
  2190. StackSym* originalCallTargetStackSym = callInstr->GetSrc1()->GetStackSym();
  2191. bool safeThis = false;
  2192. if (!TryGetFixedMethodsForBuiltInAndTarget(callInstr, inlinerData, inlineeData, applyFuncInfo, applyLdInstr, applyTargetLdInstr, safeThis, /*isApplyTarget*/ true))
  2193. {
  2194. return false;
  2195. }
  2196. // o.foo.apply(obj, arguments)
  2197. //
  2198. // StartCall
  2199. // ArgOut_A <-- implicit "this" (foo) argout
  2200. // ArgOut_A <-- explicit "this" (obj) argout
  2201. // ArgOut_A <-- arguments object argout
  2202. // CallIFixed
  2203. IR::Instr* implicitThisArgOut = nullptr;
  2204. IR::Instr* explicitThisArgOut = nullptr;
  2205. IR::Instr* argumentsObjArgOut = nullptr;
  2206. callInstr->IterateArgInstrs([&](IR::Instr* argInstr)
  2207. {
  2208. argumentsObjArgOut = explicitThisArgOut;
  2209. explicitThisArgOut = implicitThisArgOut;
  2210. implicitThisArgOut = argInstr;
  2211. argInstr->GenerateBytecodeArgOutCapture(); // Generate BytecodeArgOutCapture here to capture the implicit "this" (to be removed) and arguments object (to be expanded) argouts,
  2212. // so that any bailout in the call sequence restores the argouts stack as the interpreter would expect it to be.
  2213. argInstr->GetDst()->AsSymOpnd()->GetStackSym()->DecrementArgSlotNum(); // We will be removing implicit "this" argout
  2214. return false;
  2215. });
  2216. if (safeThis)
  2217. {
  2218. IR::Instr * byteCodeArgOutCapture = explicitThisArgOut->GetBytecodeArgOutCapture();
  2219. Assert(byteCodeArgOutCapture->GetSrc1()->IsRegOpnd());
  2220. if (byteCodeArgOutCapture->GetSrc1()->AsRegOpnd()->GetStackSym() != symCallerThis)
  2221. {
  2222. safeThis = false;
  2223. }
  2224. }
  2225. IR::Instr* argObjByteCodeArgoutCapture = argumentsObjArgOut->GetBytecodeArgOutCapture();
  2226. argObjByteCodeArgoutCapture->GetDst()->GetStackSym()->m_nonEscapingArgObjAlias = true;
  2227. argumentsObjArgOut->m_opcode = Js::OpCode::ArgOut_A_FromStackArgs;
  2228. Assert(implicitThisArgOut->GetSrc2()->IsRegOpnd());
  2229. IR::Instr * startCall = implicitThisArgOut->GetSrc2()->AsRegOpnd()->m_sym->AsStackSym()->GetInstrDef();
  2230. Assert(startCall->m_opcode == Js::OpCode::StartCall);
  2231. IR::Instr * bailOutOnNotStackArgs = IR::BailOutInstr::New(Js::OpCode::BailOnNotStackArgs, IR::BailOutOnInlineFunction,
  2232. callInstr, callInstr->m_func);
  2233. argumentsObjArgOut->InsertBefore(bailOutOnNotStackArgs);
  2234. IR::Instr* byteCodeArgOutUse = IR::Instr::New(Js::OpCode::BytecodeArgOutUse, callInstr->m_func);
  2235. byteCodeArgOutUse->SetSrc1(implicitThisArgOut->GetSrc1());
  2236. byteCodeArgOutUse->SetSrc2(argumentsObjArgOut->GetSrc1());
  2237. callInstr->InsertBefore(byteCodeArgOutUse);
  2238. // don't need the implicit "this" anymore
  2239. explicitThisArgOut->ReplaceSrc2(startCall->GetDst());
  2240. implicitThisArgOut->Remove();
  2241. startCall->SetSrc2(IR::IntConstOpnd::New(startCall->GetArgOutCount(/*getInterpreterArgOutCount*/ false), TyUint32, startCall->m_func));
  2242. startCall->GetSrc1()->AsIntConstOpnd()->IncrValue(-1); // update the count of argouts as seen by JIT, in the start call instruction
  2243. *returnInstr = InlineCallApplyTarget_Shared(callInstr, originalCallTargetStackSym, inlineeData->GetFunctionInfo(), inlineeData, inlineCacheIndex,
  2244. safeThis, /*isApplyTarget*/ true, /*isCallTarget*/ false, recursiveInlineDepth);
  2245. return true;
  2246. }
  2247. IR::Instr *
  2248. Inline::InlineCallApplyTarget_Shared(IR::Instr *callInstr, StackSym* originalCallTargetStackSym, Js::FunctionInfo *funcInfo, const Js::FunctionCodeGenJitTimeData *const inlineeData,
  2249. uint inlineCacheIndex, bool safeThis, bool isApplyTarget, bool isCallTarget, uint recursiveInlineDepth)
  2250. {
  2251. Assert(isApplyTarget ^ isCallTarget);
  2252. // function body
  2253. Js::FunctionBody* funcBody = funcInfo->GetFunctionBody();
  2254. // returnValueOpnd
  2255. IR::RegOpnd * returnValueOpnd;
  2256. Js::RegSlot returnRegSlot;
  2257. if (callInstr->GetDst())
  2258. {
  2259. returnValueOpnd = callInstr->UnlinkDst()->AsRegOpnd();
  2260. returnRegSlot = returnValueOpnd->m_sym->GetByteCodeRegSlot();
  2261. }
  2262. else
  2263. {
  2264. returnValueOpnd = nullptr;
  2265. returnRegSlot = Js::Constants::NoRegister;
  2266. }
  2267. Assert(callInstr->IsProfiledInstr());
  2268. Js::ProfileId callSiteId = static_cast<Js::ProfileId>(callInstr->AsProfiledInstr()->u.profileId);
  2269. // inlinee
  2270. Js::ProxyEntryPointInfo *defaultEntryPointInfo = funcBody->GetDefaultEntryPointInfo();
  2271. Assert(defaultEntryPointInfo->IsFunctionEntryPointInfo());
  2272. Js::FunctionEntryPointInfo *functionEntryPointInfo = static_cast<Js::FunctionEntryPointInfo*>(defaultEntryPointInfo);
  2273. JsFunctionCodeGen *workItem = JitAnew(this->topFunc->m_alloc, JsFunctionCodeGen,
  2274. funcBody->GetScriptContext()->GetNativeCodeGenerator(), funcBody, functionEntryPointInfo, this->topFunc->IsJitInDebugMode());
  2275. workItem->SetRecyclableData(JitAnew(this->topFunc->m_alloc, Js::CodeGenRecyclableData, inlineeData));
  2276. workItem->SetJitMode(this->topFunc->m_workItem->GetJitMode());
  2277. const auto profileInfo =
  2278. JitAnew(
  2279. this->topFunc->m_alloc,
  2280. Js::ReadOnlyDynamicProfileInfo,
  2281. funcBody->HasDynamicProfileInfo() ? funcBody->GetAnyDynamicProfileInfo() : nullptr,
  2282. this->topFunc->IsBackgroundJIT() ? this->topFunc->m_alloc : nullptr);
  2283. Js::EntryPointPolymorphicInlineCacheInfo * entryPointPolymorphicInlineCacheInfo = this->topFunc->m_workItem->GetEntryPoint()->GetPolymorphicInlineCacheInfo();
  2284. Func *inlinee = JitAnew(this->topFunc->m_alloc,
  2285. Func,
  2286. this->topFunc->m_alloc,
  2287. workItem,
  2288. callInstr->m_func->m_runtimeData ?
  2289. callInstr->m_func->m_runtimeData->GetLdFldInlinee(inlineCacheIndex) :
  2290. this->topFunc->GetJnFunction()->GetLdFldInlineeCodeGenRuntimeData(inlineCacheIndex),
  2291. entryPointPolymorphicInlineCacheInfo ? entryPointPolymorphicInlineCacheInfo->GetInlineeInfo(funcBody) : nullptr,
  2292. this->topFunc->GetCodeGenAllocators(),
  2293. this->topFunc->GetNumberAllocator(),
  2294. profileInfo,
  2295. this->topFunc->GetCodeGenProfiler(),
  2296. this->topFunc->IsBackgroundJIT(),
  2297. callInstr->m_func,
  2298. callInstr->m_next->GetByteCodeOffset(),
  2299. returnRegSlot,
  2300. false,
  2301. callSiteId,
  2302. false);
  2303. // instrNext
  2304. IR::Instr* instrNext = callInstr->m_next;
  2305. return InlineFunctionCommon(callInstr, originalCallTargetStackSym, funcBody, inlinee, instrNext, returnValueOpnd, callInstr, nullptr, recursiveInlineDepth, safeThis, isApplyTarget);
  2306. }
  2307. IR::Opnd *
  2308. Inline::ConvertToInlineBuiltInArgOut(IR::Instr * argInstr)
  2309. {
  2310. argInstr->m_opcode = Js::OpCode::ArgOut_A_InlineBuiltIn;
  2311. argInstr->GenerateBytecodeArgOutCapture();
  2312. return argInstr->GetSrc1();
  2313. }
  2314. IR::Instr*
  2315. Inline::InlineCall(IR::Instr *callInstr, Js::FunctionInfo *funcInfo, const Js::FunctionCodeGenJitTimeData* inlinerData, const StackSym *symCallerThis, bool* pIsInlined, uint callSiteId, uint recursiveInlineDepth)
  2316. {
  2317. Js::BuiltinFunction builtInId = Js::JavascriptLibrary::GetBuiltInForFuncInfo(funcInfo, callInstr->m_func->GetScriptContext());
  2318. Func *func = callInstr->m_func;
  2319. *pIsInlined = false;
  2320. if (PHASE_OFF(Js::InlineCallPhase, this->topFunc) || PHASE_OFF(Js::InlineCallPhase, func->GetJnFunction())
  2321. || !this->topFunc->GetJnFunction()->GetInParamsCount())
  2322. {
  2323. return callInstr;
  2324. }
  2325. // Convert all the current ARG_OUT to ArgOut_A_InlineBuiltIn
  2326. IR::Opnd *linkOpnd = callInstr->GetSrc2();
  2327. if (!GetDefInstr(linkOpnd)->GetSrc2()->IsSymOpnd())
  2328. {
  2329. // There is no benefit of inlining.call() with no arguments.
  2330. return callInstr;
  2331. }
  2332. *pIsInlined = true;
  2333. const Js::FunctionCodeGenJitTimeData * inlineeData = nullptr;
  2334. IR::Instr * returnInstr = nullptr;
  2335. if (!PHASE_OFF(Js::InlineCallTargetPhase, this->topFunc))
  2336. {
  2337. if (InlineCallTarget(callInstr, inlinerData, &inlineeData, funcInfo, symCallerThis, &returnInstr, recursiveInlineDepth))
  2338. {
  2339. Assert(returnInstr);
  2340. return returnInstr;
  2341. }
  2342. }
  2343. #if defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  2344. InliningDecider::TraceInlining(inlinerData->GetFunctionBody(), Js::JavascriptLibrary::GetNameForBuiltIn(builtInId),
  2345. nullptr, 0, this->topFunc->m_workItem->GetFunctionBody(), 0, nullptr, callSiteId, callInstr->m_func->GetTopFunc()->IsLoopBody(), builtInId);
  2346. #endif
  2347. uint actualCount = 0;
  2348. Assert(linkOpnd->IsSymOpnd());
  2349. // We are trying to optimize this.superConstructor.call(this, a, b,c);
  2350. // argImplicitInstr represents this.superConstructor which we need to call directly.
  2351. IR::Instr *argImplicitInstr;
  2352. IR::Instr* argInsertInstr = callInstr;
  2353. callInstr->IterateArgInstrs([&](IR::Instr* argInstr) {
  2354. argImplicitInstr = argInstr;
  2355. ++actualCount;
  2356. linkOpnd->AsSymOpnd()->m_sym->AsStackSym()->m_isInlinedArgSlot = true;
  2357. linkOpnd->AsSymOpnd()->m_sym->AsStackSym()->m_allocated = true;
  2358. ConvertToInlineBuiltInArgOut(argInstr);
  2359. // Move the arguments next to the call.
  2360. argInstr->Move(argInsertInstr);
  2361. argInsertInstr = argInstr;
  2362. linkOpnd = argInstr->GetSrc2();
  2363. return false;
  2364. });
  2365. linkOpnd->AsRegOpnd()->m_sym->m_isInlinedArgSlot = true;
  2366. IR::SymOpnd* orgLinkOpnd = callInstr->GetSrc2()->AsSymOpnd();
  2367. // Save off the call target operand (function object) so we can extend its lifetime as needed, even if
  2368. // the call instruction gets transformed to CallIFixed.
  2369. StackSym* originalCallTargetStackSym = callInstr->GetSrc1()->GetStackSym();
  2370. bool safeThis = false;
  2371. if (!TryOptimizeCallInstrWithFixedMethod(callInstr, funcInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/, safeThis))
  2372. {
  2373. PrepareInsertionPoint(callInstr, funcInfo, callInstr);
  2374. }
  2375. else
  2376. {
  2377. Assert(callInstr->m_opcode == Js::OpCode::CallIFixed);
  2378. // If we optimized the call instruction for a fixed function, we must extend the function object's lifetime until after
  2379. // the bailout on non-stack arguments.
  2380. IR::ByteCodeUsesInstr * useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr, originalCallTargetStackSym->m_id);
  2381. callInstr->InsertBefore(useCallTargetInstr);
  2382. }
  2383. InsertInlineeBuiltInStartEndTags(callInstr, actualCount);
  2384. uint actualCountToInlinedCall = actualCount - 1;
  2385. IR::Instr *startCall = IR::Instr::New(Js::OpCode::StartCall, func);
  2386. startCall->SetDst(IR::RegOpnd::New(TyVar, func));
  2387. startCall->SetSrc1(IR::IntConstOpnd::New(actualCountToInlinedCall, TyInt32, func)); // New call will have one less parameter.
  2388. callInstr->InsertBefore(startCall);
  2389. callInstr->ReplaceSrc1(argImplicitInstr->GetSrc1());
  2390. callInstr->UnlinkSrc2();
  2391. callInstr->m_opcode = Js::OpCode::CallI;
  2392. IR::Instr* insertBeforeInstr = callInstr;
  2393. IR::Instr* clonedArgout = nullptr;
  2394. IR::Instr* orgArgout = nullptr;
  2395. for (uint i = actualCountToInlinedCall ; i > 0; i--)
  2396. {
  2397. orgArgout = GetDefInstr(orgLinkOpnd);
  2398. orgLinkOpnd = orgArgout->GetSrc2()->AsSymOpnd();
  2399. IR::Opnd *orgSrc1 = orgArgout->GetSrc1();
  2400. // Change ArgOut to use temp as src1.
  2401. StackSym * stackSym = StackSym::New(orgSrc1->GetStackSym()->GetType(), argImplicitInstr->m_func);
  2402. IR::Opnd* tempDst = IR::RegOpnd::New(stackSym, orgSrc1->GetType(), argImplicitInstr->m_func);
  2403. IR::Instr *assignInstr = IR::Instr::New(Js::OpCode::Ld_A, tempDst, orgSrc1, argImplicitInstr->m_func);
  2404. assignInstr->SetByteCodeOffset(orgArgout);
  2405. tempDst->SetIsJITOptimizedReg(true);
  2406. orgArgout->InsertBefore(assignInstr);
  2407. StackSym *symDst = callInstr->m_func->m_symTable->GetArgSlotSym((uint16)(i));
  2408. IR::SymOpnd* newLinkOpnd = IR::SymOpnd::New(symDst, 0, TyMachPtr, func);
  2409. clonedArgout = IR::Instr::New(Js::OpCode::ArgOut_A, newLinkOpnd, tempDst, func);
  2410. insertBeforeInstr->SetSrc2(newLinkOpnd);
  2411. insertBeforeInstr->InsertBefore(clonedArgout);
  2412. insertBeforeInstr = clonedArgout;
  2413. }
  2414. clonedArgout->SetSrc2(startCall->GetDst());
  2415. Assert(GetDefInstr(orgLinkOpnd) == argImplicitInstr);
  2416. return callInstr;
  2417. }
  2418. bool
  2419. Inline::InlineCallTarget(IR::Instr *callInstr, const Js::FunctionCodeGenJitTimeData* inlinerData, const Js::FunctionCodeGenJitTimeData** pInlineeData, Js::FunctionInfo *callFuncInfo,
  2420. const StackSym *symCallerThis, IR::Instr ** returnInstr, uint recursiveInlineDepth)
  2421. {
  2422. IR::Opnd* src1 = callInstr->GetSrc1();
  2423. Assert(src1->IsRegOpnd());
  2424. StackSym* sym = src1->AsRegOpnd()->GetStackSym();
  2425. if (!sym->IsSingleDef())
  2426. {
  2427. return false;
  2428. }
  2429. IR::Instr* callLdInstr = sym->GetInstrDef();
  2430. Assert(callLdInstr);
  2431. IR::Instr* callTargetLdInstr = callLdInstr->m_prev;
  2432. if (callTargetLdInstr->m_opcode != Js::OpCode::LdFldForCallApplyTarget ||
  2433. ((callTargetLdInstr->AsProfiledInstr()->u.FldInfo().flags & Js::FldInfoFlags::FldInfo_FromAccessor) != 0))
  2434. {
  2435. return false;
  2436. }
  2437. IR::Opnd* callTargetLdOpnd = callTargetLdInstr->GetSrc1();
  2438. if (!callTargetLdOpnd->IsSymOpnd() || !callTargetLdOpnd->AsSymOpnd()->IsPropertySymOpnd())
  2439. {
  2440. return false;
  2441. }
  2442. const auto inlineCacheIndex = callTargetLdOpnd->AsPropertySymOpnd()->m_inlineCacheIndex;
  2443. const auto inlineeData = inlinerData->GetLdFldInlinee(inlineCacheIndex);
  2444. if (SkipCallApplyTargetInlining_Shared(callInstr, inlinerData, inlineeData, /*isApplyTarget*/ false, /*isCallTarget*/ true))
  2445. {
  2446. *pInlineeData = inlineeData;
  2447. return false;
  2448. }
  2449. StackSym* originalCallTargetStackSym = callInstr->GetSrc1()->GetStackSym();
  2450. bool safeThis = false;
  2451. if (!TryGetFixedMethodsForBuiltInAndTarget(callInstr, inlinerData, inlineeData, callFuncInfo, callLdInstr, callTargetLdInstr, safeThis, /*isApplyTarget*/ false))
  2452. {
  2453. return false;
  2454. }
  2455. IR::Instr* implicitThisArgOut = nullptr;
  2456. IR::Instr* explicitThisArgOut = nullptr;
  2457. callInstr->IterateArgInstrs([&] (IR::Instr* argInstr)
  2458. {
  2459. explicitThisArgOut = implicitThisArgOut;
  2460. implicitThisArgOut = argInstr;
  2461. argInstr->GenerateBytecodeArgOutCapture(); // Generate BytecodeArgOutCapture here to capture the implicit "this" argout (which will be removed) as well,
  2462. // so that any bailout in the call sequence restores the argouts stack as the interpreter would expect it to be.
  2463. argInstr->GetDst()->AsSymOpnd()->GetStackSym()->DecrementArgSlotNum(); // We will be removing implicit "this" argout
  2464. return false;
  2465. });
  2466. Assert(explicitThisArgOut);
  2467. Assert(explicitThisArgOut->HasByteCodeArgOutCapture());
  2468. if (safeThis)
  2469. {
  2470. IR::Instr * byteCodeArgOutCapture = explicitThisArgOut->GetBytecodeArgOutCapture();
  2471. Assert(byteCodeArgOutCapture->GetSrc1()->IsRegOpnd());
  2472. if (byteCodeArgOutCapture->GetSrc1()->AsRegOpnd()->GetStackSym() != symCallerThis)
  2473. {
  2474. safeThis = false;
  2475. }
  2476. }
  2477. IR::Opnd* linkOpnd = implicitThisArgOut->GetSrc2();
  2478. Assert(linkOpnd->IsRegOpnd() && linkOpnd->AsRegOpnd()->GetStackSym()->IsSingleDef());
  2479. Assert(linkOpnd->AsRegOpnd()->GetStackSym()->GetInstrDef()->m_opcode == Js::OpCode::StartCall);
  2480. IR::Instr* startCall = linkOpnd->AsRegOpnd()->GetStackSym()->GetInstrDef();
  2481. explicitThisArgOut->ReplaceSrc2(startCall->GetDst());
  2482. IR::Instr * bytecodeArgOutUse = IR::Instr::New(Js::OpCode::BytecodeArgOutUse, callInstr->m_func);
  2483. bytecodeArgOutUse->SetSrc1(implicitThisArgOut->GetSrc1());
  2484. callInstr->InsertBefore(bytecodeArgOutUse); // Need to keep the implicit "this" argout live till the call instruction for it to be captured by any bailout in the call sequence.
  2485. implicitThisArgOut->Remove();
  2486. startCall->SetSrc2(IR::IntConstOpnd::New(startCall->GetArgOutCount(/*getInterpreterArgOutCount*/ false), TyUint32, startCall->m_func));
  2487. startCall->GetSrc1()->AsIntConstOpnd()->SetValue(startCall->GetSrc1()->AsIntConstOpnd()->GetValue() - 1);
  2488. *returnInstr = InlineCallApplyTarget_Shared(callInstr, originalCallTargetStackSym, inlineeData->GetFunctionInfo(), inlineeData, inlineCacheIndex,
  2489. safeThis, /*isApplyTarget*/ false, /*isCallTarget*/ true, recursiveInlineDepth);
  2490. return true;
  2491. }
  2492. bool
  2493. Inline::SkipCallApplyTargetInlining_Shared(IR::Instr *callInstr, const Js::FunctionCodeGenJitTimeData* inlinerData, const Js::FunctionCodeGenJitTimeData* inlineeData, bool isApplyTarget, bool isCallTarget)
  2494. {
  2495. #if ENABLE_DEBUG_CONFIG_OPTIONS
  2496. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2497. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2498. char16 debugStringBuffer3[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2499. #endif
  2500. Assert(isApplyTarget ^ isCallTarget);
  2501. if (PHASE_OFF(Js::FixedMethodsPhase, callInstr->m_func->GetJnFunction()))
  2502. {
  2503. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Skipping %s target inlining, Fixed Methods turned off\tCaller: %s\t(#%d) \tTop Func:%s\t(#%d)\n"), isApplyTarget ? _u("apply") : _u("call") ,
  2504. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  2505. this->topFunc->GetJnFunction()->GetDisplayName(), this->topFunc->GetJnFunction()->GetDebugNumberSet(debugStringBuffer2));
  2506. return true;
  2507. }
  2508. if (!inlineeData)
  2509. {
  2510. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Skipping %s target inlining, inlineeData not present\tCaller: %s\t(#%d) \tTop Func:%s\t(#%d)\n"), isApplyTarget ? _u("apply") : _u("call"),
  2511. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  2512. this->topFunc->GetJnFunction()->GetDisplayName(), this->topFunc->GetJnFunction()->GetDebugNumberSet(debugStringBuffer2));
  2513. return true;
  2514. }
  2515. if (!inlineeData->GetFunctionBody())
  2516. {
  2517. if (isCallTarget)
  2518. {
  2519. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Skipping .call inlining, target is a built-in\tCaller: %s\t(#%d) \tTop Func:%s\t(#%d)\n"),
  2520. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  2521. this->topFunc->GetJnFunction()->GetDisplayName(), this->topFunc->GetJnFunction()->GetDebugNumberSet(debugStringBuffer2));
  2522. }
  2523. return true;
  2524. }
  2525. if (!inlinerData->IsLdFldInlineePresent())
  2526. {
  2527. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Skipping %s target inlining, not registered as a LdFld inlinee \tInlinee: %s (#%d)\tCaller: %s\t(#%d) \tTop Func:%s\t(#%d)\n"), isApplyTarget ? _u("apply") : _u("call"),
  2528. inlineeData->GetFunctionBody()->GetDisplayName(), inlineeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  2529. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2),
  2530. this->topFunc->GetJnFunction()->GetDisplayName(), this->topFunc->GetJnFunction()->GetDebugNumberSet(debugStringBuffer3));
  2531. return true;
  2532. }
  2533. return false;
  2534. }
  2535. bool
  2536. Inline::TryGetFixedMethodsForBuiltInAndTarget(IR::Instr *callInstr, const Js::FunctionCodeGenJitTimeData* inlinerData, const Js::FunctionCodeGenJitTimeData* inlineeData, Js::FunctionInfo *builtInFuncInfo,
  2537. IR::Instr* builtInLdInstr, IR::Instr* targetLdInstr, bool& safeThis, bool isApplyTarget)
  2538. {
  2539. #if ENABLE_DEBUG_CONFIG_OPTIONS
  2540. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2541. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2542. char16 debugStringBuffer3[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2543. #endif
  2544. Assert(isApplyTarget || (Js::JavascriptLibrary::GetBuiltInForFuncInfo(builtInFuncInfo, callInstr->m_func->GetScriptContext()) == Js::BuiltinFunction::Function_Call));
  2545. Js::OpCode originalCallOpCode = callInstr->m_opcode;
  2546. StackSym* originalCallTargetStackSym = callInstr->GetSrc1()->GetStackSym();
  2547. IR::ByteCodeUsesInstr * useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr->m_func);
  2548. useCallTargetInstr->SetByteCodeOffset(callInstr);
  2549. useCallTargetInstr->byteCodeUpwardExposedUsed = JitAnew(callInstr->m_func->m_alloc, BVSparse<JitArenaAllocator>, callInstr->m_func->m_alloc);
  2550. Js::FunctionInfo* targetFunctionInfo = inlineeData->GetFunctionInfo();
  2551. safeThis = false;
  2552. // Check if we can get fixed method for call
  2553. if (TryOptimizeCallInstrWithFixedMethod(callInstr, builtInFuncInfo/*funcinfo for call*/, false /*isPolymorphic*/, false /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/,
  2554. safeThis /*unused here*/, true /*dontOptimizeJustCheck*/))
  2555. {
  2556. Assert(callInstr->m_opcode == originalCallOpCode); // check that we didn't change the opcode to CallIFixed.
  2557. callInstr->ReplaceSrc1(targetLdInstr->GetDst());
  2558. safeThis = false;
  2559. // Check if we can get fixed method for call target
  2560. if (!TryOptimizeCallInstrWithFixedMethod(callInstr, targetFunctionInfo, false /*isPolymorphic*/, false /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/,
  2561. safeThis /*unused here*/, true /*dontOptimizeJustCheck*/))
  2562. {
  2563. callInstr->ReplaceSrc1(builtInLdInstr->GetDst());
  2564. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Skipping %s target inlining, did not get fixed method for %s target \tInlinee: %s (#%d)\tCaller: %s\t(#%d) \tTop Func:%s\t(#%d)\n"), isApplyTarget ? _u("apply") : _u("call"), isApplyTarget ? _u("apply") : _u("call"),
  2565. inlineeData->GetFunctionBody()->GetDisplayName(), inlineeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  2566. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2),
  2567. this->topFunc->GetJnFunction()->GetDisplayName(), this->topFunc->GetJnFunction()->GetDebugNumberSet(debugStringBuffer3));
  2568. return false;
  2569. }
  2570. }
  2571. else
  2572. {
  2573. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Skipping %s target inlining, did not get fixed method for %s \tInlinee: %s (#%d)\tCaller: %s\t(#%d) \tTop Func:%s\t(#%d)\n"), isApplyTarget ? _u("apply") : _u("call"), isApplyTarget ? _u("apply") : _u("call"),
  2574. inlineeData->GetFunctionBody()->GetDisplayName(), inlineeData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer),
  2575. inlinerData->GetFunctionBody()->GetDisplayName(), inlinerData->GetFunctionBody()->GetDebugNumberSet(debugStringBuffer2),
  2576. this->topFunc->GetJnFunction()->GetDisplayName(), this->topFunc->GetJnFunction()->GetDebugNumberSet(debugStringBuffer3));
  2577. return false;
  2578. }
  2579. if (isApplyTarget)
  2580. {
  2581. callInstr->m_func->SetHasApplyTargetInlining();
  2582. }
  2583. Assert(callInstr->m_opcode == originalCallOpCode);
  2584. callInstr->ReplaceSrc1(builtInLdInstr->GetDst());
  2585. // Emit Fixed Method check for apply/call
  2586. safeThis = false;
  2587. TryOptimizeCallInstrWithFixedMethod(callInstr, builtInFuncInfo/*funcinfo for apply/call */, false /*isPolymorphic*/, false /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/, safeThis /*unused here*/);
  2588. // If we optimized the call instruction for a fixed function, we must extend the function object's lifetime until after
  2589. // the bailout on non-stack arguments.
  2590. Assert(callInstr->m_opcode == Js::OpCode::CallIFixed);
  2591. useCallTargetInstr->byteCodeUpwardExposedUsed->Set(originalCallTargetStackSym->m_id);
  2592. // Make the target of apply/call as the target of the call instruction
  2593. callInstr->ReplaceSrc1(targetLdInstr->GetDst());
  2594. callInstr->m_opcode = originalCallOpCode;
  2595. //Emit Fixed Method check for apply/call target
  2596. originalCallTargetStackSym = callInstr->GetSrc1()->GetStackSym();
  2597. safeThis = false;
  2598. TryOptimizeCallInstrWithFixedMethod(callInstr, targetFunctionInfo, false /*isPolymorphic*/, false /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/, safeThis /*unused here*/);
  2599. // If we optimized the call instruction for a fixed function, we must extend the function object's lifetime until after
  2600. // the bailout on non-stack arguments.
  2601. Assert(callInstr->m_opcode == Js::OpCode::CallIFixed);
  2602. useCallTargetInstr->byteCodeUpwardExposedUsed->Set(originalCallTargetStackSym->m_id);
  2603. callInstr->InsertBefore(useCallTargetInstr);
  2604. return true;
  2605. }
  2606. void
  2607. Inline::SetupInlineInstrForCallDirect(Js::BuiltinFunction builtInId, IR::Instr* callInstr, IR::Instr* argoutInstr)
  2608. {
  2609. switch(builtInId)
  2610. {
  2611. case Js::BuiltinFunction::Array_Concat:
  2612. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperArray_Concat, callInstr->m_func));
  2613. break;
  2614. case Js::BuiltinFunction::Array_IndexOf:
  2615. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperArray_IndexOf, callInstr->m_func));
  2616. break;
  2617. case Js::BuiltinFunction::Array_Includes:
  2618. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperArray_Includes, callInstr->m_func));
  2619. break;
  2620. case Js::BuiltinFunction::Array_Join:
  2621. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperArray_Join, callInstr->m_func));
  2622. break;
  2623. case Js::BuiltinFunction::Array_LastIndexOf:
  2624. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperArray_LastIndexOf, callInstr->m_func));
  2625. break;
  2626. case Js::BuiltinFunction::Array_Reverse:
  2627. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperArray_Reverse, callInstr->m_func));
  2628. break;
  2629. case Js::BuiltinFunction::Array_Shift:
  2630. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperArray_Shift, callInstr->m_func));
  2631. break;
  2632. case Js::BuiltinFunction::Array_Slice:
  2633. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperArray_Slice, callInstr->m_func));
  2634. break;
  2635. case Js::BuiltinFunction::Array_Splice:
  2636. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperArray_Splice, callInstr->m_func));
  2637. break;
  2638. case Js::BuiltinFunction::Array_Unshift:
  2639. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperArray_Unshift, callInstr->m_func));
  2640. break;
  2641. case Js::BuiltinFunction::String_Concat:
  2642. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_Concat, callInstr->m_func));
  2643. break;
  2644. case Js::BuiltinFunction::String_CharCodeAt:
  2645. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_CharCodeAt, callInstr->m_func));
  2646. break;
  2647. case Js::BuiltinFunction::String_CharAt:
  2648. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_CharAt, callInstr->m_func));
  2649. break;
  2650. case Js::BuiltinFunction::String_FromCharCode:
  2651. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_FromCharCode, callInstr->m_func));
  2652. break;
  2653. case Js::BuiltinFunction::String_FromCodePoint:
  2654. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_FromCodePoint, callInstr->m_func));
  2655. break;
  2656. case Js::BuiltinFunction::String_IndexOf:
  2657. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_IndexOf, callInstr->m_func));
  2658. break;
  2659. case Js::BuiltinFunction::String_LastIndexOf:
  2660. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_LastIndexOf, callInstr->m_func));
  2661. break;
  2662. case Js::BuiltinFunction::String_Link:
  2663. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_Link, callInstr->m_func));
  2664. break;
  2665. case Js::BuiltinFunction::String_LocaleCompare:
  2666. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_LocaleCompare, callInstr->m_func));
  2667. break;
  2668. case Js::BuiltinFunction::String_Match:
  2669. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_Match, callInstr->m_func));
  2670. break;
  2671. case Js::BuiltinFunction::String_Replace:
  2672. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_Replace, callInstr->m_func));
  2673. break;
  2674. case Js::BuiltinFunction::String_Search:
  2675. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_Search, callInstr->m_func));
  2676. break;
  2677. case Js::BuiltinFunction::String_Slice:
  2678. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_Slice, callInstr->m_func));
  2679. break;
  2680. case Js::BuiltinFunction::String_Split:
  2681. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_Split, callInstr->m_func));
  2682. break;
  2683. case Js::BuiltinFunction::String_Substr:
  2684. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_Substr, callInstr->m_func));
  2685. break;
  2686. case Js::BuiltinFunction::String_Substring:
  2687. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_Substring, callInstr->m_func));
  2688. break;
  2689. case Js::BuiltinFunction::String_ToLocaleLowerCase:
  2690. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_ToLocaleLowerCase, callInstr->m_func));
  2691. break;
  2692. case Js::BuiltinFunction::String_ToLocaleUpperCase:
  2693. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_ToLocaleUpperCase, callInstr->m_func));
  2694. break;
  2695. case Js::BuiltinFunction::String_ToLowerCase:
  2696. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_ToLowerCase, callInstr->m_func));
  2697. break;
  2698. case Js::BuiltinFunction::String_ToUpperCase:
  2699. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_ToUpperCase, callInstr->m_func));
  2700. break;
  2701. case Js::BuiltinFunction::String_Trim:
  2702. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_Trim, callInstr->m_func));
  2703. break;
  2704. case Js::BuiltinFunction::String_TrimLeft:
  2705. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_TrimLeft, callInstr->m_func));
  2706. break;
  2707. case Js::BuiltinFunction::String_TrimRight:
  2708. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_TrimRight, callInstr->m_func));
  2709. break;
  2710. case Js::BuiltinFunction::String_PadStart:
  2711. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_PadStart, callInstr->m_func));
  2712. break;
  2713. case Js::BuiltinFunction::String_PadEnd:
  2714. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_PadEnd, callInstr->m_func));
  2715. break;
  2716. case Js::BuiltinFunction::GlobalObject_ParseInt:
  2717. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperGlobalObject_ParseInt, callInstr->m_func));
  2718. break;
  2719. case Js::BuiltinFunction::RegExp_Exec:
  2720. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperRegExp_Exec, callInstr->m_func));
  2721. break;
  2722. case Js::BuiltinFunction::RegExp_SymbolSearch:
  2723. callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperRegExp_SymbolSearch, callInstr->m_func));
  2724. break;
  2725. };
  2726. callInstr->SetSrc2(argoutInstr->GetDst());
  2727. return;
  2728. }
  2729. void
  2730. Inline::WrapArgsOutWithCoerse(Js::BuiltinFunction builtInId, IR::Instr* callInstr)
  2731. {
  2732. switch (builtInId)
  2733. {
  2734. case Js::BuiltinFunction::String_Match:
  2735. callInstr->ForEachCallDirectArgOutInstrBackward([&](IR::Instr *argOutInstr, uint argNum)
  2736. {
  2737. IR::Instr * newInstr = nullptr;
  2738. bool isPreOpBailOutNeeded = false;
  2739. if (argNum == 0)
  2740. {
  2741. newInstr = argOutInstr->HoistSrc1(Js::OpCode::Coerce_Str);
  2742. isPreOpBailOutNeeded = true;
  2743. newInstr->GetDst()->SetValueType(ValueType::String);
  2744. newInstr->SetSrc2(IR::AddrOpnd::New((Js::Var)_u("String.prototype.match"), IR::AddrOpndKindSz, newInstr->m_func));
  2745. argOutInstr->GetSrc1()->SetValueType(ValueType::String);
  2746. }
  2747. else if (argNum == 1)
  2748. {
  2749. newInstr = argOutInstr->HoistSrc1(Js::OpCode::Coerce_Regex);
  2750. isPreOpBailOutNeeded = true;
  2751. }
  2752. if (isPreOpBailOutNeeded)
  2753. {
  2754. newInstr->SetByteCodeOffset(argOutInstr);
  2755. newInstr->forcePreOpBailOutIfNeeded = true;
  2756. }
  2757. return false;
  2758. }, 2);
  2759. break;
  2760. case Js::BuiltinFunction::String_Replace:
  2761. callInstr->ForEachCallDirectArgOutInstrBackward([&](IR::Instr *argOutInstr, uint argNum)
  2762. {
  2763. IR::Instr * newInstr = nullptr;
  2764. bool isPreOpBailOutNeeded = false;
  2765. if (argNum == 0)
  2766. {
  2767. newInstr = argOutInstr->HoistSrc1(Js::OpCode::Coerce_Str);
  2768. isPreOpBailOutNeeded = true;
  2769. newInstr->GetDst()->SetValueType(ValueType::String);
  2770. newInstr->SetSrc2(IR::AddrOpnd::New((Js::Var)_u("String.prototype.replace"), IR::AddrOpndKindSz, newInstr->m_func));
  2771. argOutInstr->GetSrc1()->SetValueType(ValueType::String);
  2772. }
  2773. if (argNum == 1)
  2774. {
  2775. newInstr = argOutInstr->HoistSrc1(Js::OpCode::Coerce_StrOrRegex);
  2776. isPreOpBailOutNeeded = true;
  2777. }
  2778. if (isPreOpBailOutNeeded)
  2779. {
  2780. newInstr->SetByteCodeOffset(argOutInstr);
  2781. newInstr->forcePreOpBailOutIfNeeded = true;
  2782. }
  2783. return false;
  2784. }, 3);
  2785. break;
  2786. case Js::BuiltinFunction::RegExp_Exec:
  2787. callInstr->ForEachCallDirectArgOutInstrBackward([&](IR::Instr *argOutInstr, uint argNum)
  2788. {
  2789. IR::Instr * newInstr = nullptr;
  2790. bool isPreOpBailOutNeeded = false;
  2791. if (argNum == 0)
  2792. {
  2793. newInstr = argOutInstr->HoistSrc1(Js::OpCode::Coerce_Regex);
  2794. isPreOpBailOutNeeded = true;
  2795. }
  2796. else if (argNum == 1)
  2797. {
  2798. newInstr = argOutInstr->HoistSrc1(Js::OpCode::Conv_Str);
  2799. newInstr->GetDst()->SetValueType(ValueType::String);
  2800. argOutInstr->GetSrc1()->SetValueType(ValueType::String);
  2801. isPreOpBailOutNeeded = true;
  2802. }
  2803. if (isPreOpBailOutNeeded)
  2804. {
  2805. newInstr->SetByteCodeOffset(argOutInstr);
  2806. newInstr->forcePreOpBailOutIfNeeded = true;
  2807. }
  2808. return false;
  2809. }, 2);
  2810. break;
  2811. }
  2812. }
  2813. IR::Instr *
  2814. Inline::SimulateCallForGetterSetter(IR::Instr *accessorInstr, IR::Instr* insertInstr, IR::PropertySymOpnd* methodOpnd, bool isGetter)
  2815. {
  2816. Assert(methodOpnd->UsesAccessor());
  2817. IntConstType argOutCount = isGetter ? 1 : 2; // A setter would have an additional ArgOut in the form of the value being set.
  2818. IR::Instr *ldMethodFld = IR::Instr::New(Js::OpCode::LdMethodFromFlags, IR::RegOpnd::New(TyVar, accessorInstr->m_func), methodOpnd, accessorInstr->m_func);
  2819. insertInstr->InsertBefore(ldMethodFld);
  2820. ldMethodFld = ldMethodFld->ConvertToBailOutInstr(accessorInstr, IR::BailOutFailedInlineTypeCheck);
  2821. ldMethodFld->SetByteCodeOffset(accessorInstr);
  2822. IR::Instr *startCall = IR::Instr::New(Js::OpCode::StartCall, accessorInstr->m_func);
  2823. startCall->SetDst(IR::RegOpnd::New(TyVar, accessorInstr->m_func));
  2824. startCall->SetSrc1(IR::IntConstOpnd::New(argOutCount, TyInt32, accessorInstr->m_func));
  2825. insertInstr->InsertBefore(startCall);
  2826. startCall->SetByteCodeOffset(accessorInstr);
  2827. PropertySym * fieldSym = methodOpnd->AsSymOpnd()->m_sym->AsPropertySym();
  2828. IR::RegOpnd * instanceOpnd = IR::RegOpnd::New(fieldSym->m_stackSym, TyVar, accessorInstr->m_func);
  2829. IR::Instr *argOutThis = IR::Instr::New(Js::OpCode::ArgOut_A, accessorInstr->m_func);
  2830. StackSym *symDst = accessorInstr->m_func->m_symTable->GetArgSlotSym((uint16)(1));
  2831. argOutThis->SetDst(IR::SymOpnd::New(symDst, 0, TyVar, accessorInstr->m_func));
  2832. argOutThis->SetSrc1(instanceOpnd);
  2833. argOutThis->SetSrc2(startCall->GetDst());
  2834. insertInstr->InsertBefore(argOutThis);
  2835. IR::Instr * argOut = nullptr;
  2836. if(!isGetter)
  2837. {
  2838. // Set the src1 of the StFld to be the second ArgOut.
  2839. argOut = IR::Instr::New(Js::OpCode::ArgOut_A, accessorInstr->m_func);
  2840. symDst = accessorInstr->m_func->m_symTable->GetArgSlotSym((uint16)(2));
  2841. argOut->SetDst(IR::SymOpnd::New(symDst, 0, TyVar, accessorInstr->m_func));
  2842. argOut->SetSrc1(accessorInstr->GetSrc1());
  2843. argOut->SetSrc2(argOutThis->GetDst());
  2844. insertInstr->InsertBefore(argOut);
  2845. }
  2846. accessorInstr->ReplaceSrc1(ldMethodFld->GetDst());
  2847. isGetter ? accessorInstr->SetSrc2(argOutThis->GetDst()) : accessorInstr->SetSrc2(argOut->GetDst());
  2848. if(!isGetter)
  2849. {
  2850. accessorInstr->UnlinkDst();
  2851. }
  2852. return startCall;
  2853. }
  2854. IR::Instr *
  2855. Inline::InlineGetterSetterFunction(IR::Instr *accessorInstr, const Js::FunctionCodeGenJitTimeData *const inlineeData, const StackSym *symCallerThis, const uint inlineCacheIndex, bool isGetter, bool *pIsInlined, uint recursiveInlineDepth)
  2856. {
  2857. // This function is recursive, so when jitting in the foreground, probe the stack
  2858. if (!this->topFunc->IsBackgroundJIT())
  2859. {
  2860. PROBE_STACK(this->topFunc->GetScriptContext(), Js::Constants::MinStackDefault);
  2861. }
  2862. *pIsInlined = true;
  2863. IR::Instr *instrNext = accessorInstr->m_next;
  2864. Js::FunctionBody *funcCaller = accessorInstr->m_func->GetJnFunction();
  2865. Js::FunctionBody *funcBody = inlineeData->GetFunctionBody();
  2866. Assert(!accessorInstr->GetSrc2());
  2867. JS_ETW(EventWriteJSCRIPT_BACKEND_INLINE(
  2868. funcCaller->GetFunctionNumber(), funcBody->GetFunctionNumber(),
  2869. funcCaller->GetExternalDisplayName(), funcBody->GetExternalDisplayName()));
  2870. IR::Instr *inlineBailoutChecksBeforeInstr = accessorInstr;
  2871. Js::ProxyEntryPointInfo *defaultEntryPointInfo = funcBody->GetDefaultEntryPointInfo();
  2872. Assert(defaultEntryPointInfo->IsFunctionEntryPointInfo());
  2873. Js::FunctionEntryPointInfo *functionEntryPointInfo = static_cast<Js::FunctionEntryPointInfo*>(defaultEntryPointInfo);
  2874. JsFunctionCodeGen *workItem = JitAnew(this->topFunc->m_alloc, JsFunctionCodeGen,
  2875. funcBody->GetScriptContext()->GetNativeCodeGenerator(), funcBody, functionEntryPointInfo, this->topFunc->IsJitInDebugMode());
  2876. workItem->SetRecyclableData(JitAnew(this->topFunc->m_alloc, Js::CodeGenRecyclableData, inlineeData));
  2877. workItem->SetJitMode(this->topFunc->m_workItem->GetJitMode());
  2878. IR::RegOpnd * returnValueOpnd;
  2879. Js::RegSlot returnRegSlot;
  2880. if (isGetter && accessorInstr->GetDst())
  2881. {
  2882. returnValueOpnd = accessorInstr->UnlinkDst()->AsRegOpnd();
  2883. returnRegSlot = returnValueOpnd->m_sym->GetByteCodeRegSlot();
  2884. }
  2885. else
  2886. {
  2887. returnValueOpnd = nullptr;
  2888. returnRegSlot = Js::Constants::NoRegister;
  2889. }
  2890. const auto profileInfo =
  2891. JitAnew(
  2892. this->topFunc->m_alloc,
  2893. Js::ReadOnlyDynamicProfileInfo,
  2894. funcBody->HasDynamicProfileInfo() ? funcBody->GetAnyDynamicProfileInfo() : nullptr,
  2895. this->topFunc->IsBackgroundJIT() ? this->topFunc->m_alloc : nullptr);
  2896. Js::EntryPointPolymorphicInlineCacheInfo * entryPointPolymorphicInlineCacheInfo = this->topFunc->m_workItem->GetEntryPoint()->GetPolymorphicInlineCacheInfo();
  2897. Func *inlinee = JitAnew(this->topFunc->m_alloc,
  2898. Func,
  2899. this->topFunc->m_alloc,
  2900. workItem,
  2901. accessorInstr->m_func->m_runtimeData ?
  2902. accessorInstr->m_func->m_runtimeData->GetLdFldInlinee(inlineCacheIndex) :
  2903. this->topFunc->GetJnFunction()->GetLdFldInlineeCodeGenRuntimeData(inlineCacheIndex),
  2904. entryPointPolymorphicInlineCacheInfo ? entryPointPolymorphicInlineCacheInfo->GetInlineeInfo(funcBody) : nullptr,
  2905. this->topFunc->GetCodeGenAllocators(),
  2906. this->topFunc->GetNumberAllocator(),
  2907. profileInfo,
  2908. this->topFunc->GetCodeGenProfiler(),
  2909. this->topFunc->IsBackgroundJIT(),
  2910. accessorInstr->m_func,
  2911. accessorInstr->m_next->GetByteCodeOffset(),
  2912. returnRegSlot,
  2913. false,
  2914. UINT16_MAX,
  2915. true);
  2916. // funcBody->GetInParamsCount() can be greater than one even if it is all undefined. Example defineProperty(a,"foo", {get:function(a,b,c){}});
  2917. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  2918. if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::InlinePhase) ||
  2919. Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::InlineAccessorsPhase) || Js::Configuration::Global.flags.Trace.IsEnabled(Js::InlineAccessorsPhase))
  2920. {
  2921. char16 debugStringBuffer [MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2922. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2923. PropertySym *propertySym = isGetter ? accessorInstr->GetSrc1()->AsSymOpnd()->m_sym->AsPropertySym() : accessorInstr->GetDst()->AsSymOpnd()->m_sym->AsPropertySym();
  2924. Js::ScriptContext* scriptContext = propertySym->GetFunc()->GetScriptContext();
  2925. Output::Print(_u("INLINING: %s: \tInlinee: %s (%s)\tCaller: %s (%s)\t fieldName: %s\n"), isGetter ? _u("Getter") : _u("Setter"),
  2926. funcBody->GetDisplayName(), funcBody->GetDebugNumberSet(debugStringBuffer), funcCaller->GetDisplayName(), funcCaller->GetDebugNumberSet(debugStringBuffer2),
  2927. scriptContext->GetPropertyNameLocked(propertySym->m_propertyId)->GetBuffer());
  2928. Output::Flush();
  2929. }
  2930. #endif
  2931. IR::Opnd * methodOpnd = isGetter ? accessorInstr->GetSrc1() : accessorInstr->GetDst();
  2932. Assert(methodOpnd->IsSymOpnd() && methodOpnd->AsSymOpnd()->IsPropertySymOpnd());
  2933. inlineBailoutChecksBeforeInstr = SimulateCallForGetterSetter(accessorInstr, accessorInstr, methodOpnd->AsPropertySymOpnd(), isGetter);
  2934. bool safeThis = false;
  2935. TryOptimizeCallInstrWithFixedMethod(accessorInstr, inlineeData->GetFunctionInfo(), false, false, false, true, safeThis);
  2936. return InlineFunctionCommon(accessorInstr, nullptr, funcBody, inlinee, instrNext, returnValueOpnd, inlineBailoutChecksBeforeInstr, symCallerThis, recursiveInlineDepth, safeThis);
  2937. }
  2938. IR::Instr *
  2939. Inline::InlineFunctionCommon(IR::Instr *callInstr, StackSym* originalCallTargetStackSym, Js::FunctionBody *funcBody, Func *inlinee, IR::Instr *instrNext,
  2940. IR::RegOpnd * returnValueOpnd, IR::Instr *inlineBailoutChecksBeforeInstr, const StackSym *symCallerThis, uint recursiveInlineDepth, bool safeThis, bool isApplyTarget)
  2941. {
  2942. BuildIRForInlinee(inlinee, funcBody, callInstr, isApplyTarget, recursiveInlineDepth);
  2943. Js::ArgSlot formalCount = funcBody->GetInParamsCount();
  2944. IR::Instr *argOuts[Js::InlineeCallInfo::MaxInlineeArgoutCount];
  2945. #if DBG
  2946. memset(argOuts, 0xFE, sizeof(argOuts));
  2947. #endif
  2948. if (callInstr->m_opcode == Js::OpCode::CallIFixed)
  2949. {
  2950. Assert(callInstr->GetFixedFunction()->GetFunctionInfo() == funcBody);
  2951. }
  2952. else
  2953. {
  2954. PrepareInsertionPoint(callInstr, funcBody, inlineBailoutChecksBeforeInstr);
  2955. }
  2956. Assert(formalCount <= Js::InlineeCallInfo::MaxInlineeArgoutCount);
  2957. __analysis_assume(formalCount <= Js::InlineeCallInfo::MaxInlineeArgoutCount);
  2958. IR::Instr *argOutsExtra[Js::InlineeCallInfo::MaxInlineeArgoutCount];
  2959. #if DBG
  2960. memset(argOutsExtra, 0xFE, sizeof(argOutsExtra));
  2961. #endif
  2962. bool stackArgsArgOutExpanded = false;
  2963. Js::ArgSlot actualCount = MapActuals(callInstr, argOuts, formalCount, inlinee, (Js::ProfileId)callInstr->AsProfiledInstr()->u.profileId, &stackArgsArgOutExpanded, argOutsExtra);
  2964. inlinee->actualCount = actualCount;
  2965. Assert(actualCount > 0);
  2966. #if DBG
  2967. if(safeThis)
  2968. {
  2969. Assert(callInstr->m_opcode == Js::OpCode::CallIFixed);
  2970. }
  2971. #endif
  2972. MapFormals(inlinee, argOuts, formalCount, actualCount, returnValueOpnd, callInstr->GetSrc1(), symCallerThis, stackArgsArgOutExpanded, safeThis, argOutsExtra);
  2973. if (callInstr->m_opcode == Js::OpCode::CallIFixed && !inlinee->isGetterSetter)
  2974. {
  2975. Assert(originalCallTargetStackSym != nullptr);
  2976. // Insert a ByteCodeUsesInstr to make sure the function object's lifetimes is extended beyond the last bailout point
  2977. // at which we may have to call the function again in the interpreter.
  2978. // Don't need to do this for a getter/setter inlinee as, upon bailout, the execution will start in the interpreter at the LdFld/StFld itself.
  2979. callInstr->InsertBefore(IR::ByteCodeUsesInstr::New(callInstr, originalCallTargetStackSym->m_id));
  2980. }
  2981. // InlineeStart indicate the beginning of the inlinee, and we need the stack arg for the inlinee until InlineeEnd
  2982. callInstr->m_opcode = Js::OpCode::InlineeStart;
  2983. // Set it to belong to the inlinee, so that we can use the actual count when lowering InlineeStart
  2984. callInstr->m_func = inlinee;
  2985. callInstr->SetDst(IR::RegOpnd::New(TyVar, inlinee));
  2986. // Put the meta arguments that the stack walker expects to find on the stack.
  2987. SetupInlineeFrame(inlinee, callInstr, actualCount, callInstr->GetSrc1());
  2988. // actualCount + MetaArgCount to include the meta arguments to pop from the inlinee argout stack.
  2989. IR::Instr *inlineeEndInstr = IR::Instr::New(Js::OpCode::InlineeEnd, inlinee);
  2990. inlineeEndInstr->SetByteCodeOffset(inlinee->m_tailInstr->GetPrevRealInstr());
  2991. inlineeEndInstr->SetSrc1(IR::IntConstOpnd::New(actualCount + Js::Constants::InlineeMetaArgCount, TyInt32, callInstr->m_func));
  2992. inlineeEndInstr->SetSrc2(callInstr->GetDst()); // Link the inlinee end to the inlinee Start
  2993. callInstr->InsertAfter(inlineeEndInstr);
  2994. // Move the ArgOut_A_Inlines close to the InlineeStart
  2995. callInstr->MoveArgs();
  2996. inlineeEndInstr->InsertRangeBefore(inlinee->m_headInstr->m_next, inlinee->m_tailInstr->m_prev);
  2997. inlinee->m_headInstr->Free();
  2998. inlinee->m_tailInstr->Free();
  2999. this->topFunc->SetHasInlinee();
  3000. InsertStatementBoundary(instrNext);
  3001. return instrNext;
  3002. }
  3003. #ifdef ENABLE_DOM_FAST_PATH
  3004. // we have LdFld, src1 obj, src2: null; dest: return value
  3005. // We need to convert it to inlined method call.
  3006. // We cannot do CallDirect as it requires ArgOut and that cannot be hoisted/copyprop'd
  3007. // Create a new OpCode, DOMFastPathGetter. The OpCode takes three arguments:
  3008. // The function object, the "this" instance object, and the helper routine as we have one for each index
  3009. // A functionInfo->Index# table is created in scriptContext (and potentially movable to threadContext if WS is not a concern).
  3010. // we use the table to identify the helper that needs to be lowered.
  3011. // At lower time we create the call to helper, which is function entrypoint at this time.
  3012. IR::Instr * Inline::InlineDOMGetterSetterFunction(IR::Instr *ldFldInstr, const Js::FunctionCodeGenJitTimeData *const inlineeData, const Js::FunctionCodeGenJitTimeData *const inlinerData)
  3013. {
  3014. Js::FunctionInfo* functionInfo = inlineeData->GetFunctionInfo();
  3015. Assert(ldFldInstr->GetSrc1()->IsSymOpnd() && ldFldInstr->GetSrc1()->AsSymOpnd()->IsPropertySymOpnd());
  3016. Assert(ldFldInstr->GetSrc1()->AsPropertySymOpnd()->HasObjTypeSpecFldInfo());
  3017. Assert(ldFldInstr->GetSrc1()->AsPropertySymOpnd()->GetObjTypeSpecInfo()->UsesAccessor());
  3018. // Find the helper routine for this functionInfo.
  3019. Js::ScriptContext* scriptContext = this->topFunc->GetScriptContext();
  3020. IR::JnHelperMethod helperMethod;
  3021. bool found = scriptContext->EnsureDOMFastPathIRHelperMap()->TryGetValue(functionInfo, &helperMethod);
  3022. Assert(found);
  3023. // Find the instance object (External object).
  3024. PropertySym * fieldSym = ldFldInstr->GetSrc1()->AsSymOpnd()->m_sym->AsPropertySym();
  3025. IR::RegOpnd * instanceOpnd = IR::RegOpnd::New(fieldSym->m_stackSym, TyMachPtr, ldFldInstr->m_func);
  3026. // Find the function object from getter inline cache. Need bailout to verify.
  3027. IR::Instr *ldMethodFld = IR::Instr::New(Js::OpCode::LdMethodFromFlags, IR::RegOpnd::New(TyVar, ldFldInstr->m_func), ldFldInstr->GetSrc1(), ldFldInstr->m_func);
  3028. ldFldInstr->InsertBefore(ldMethodFld);
  3029. ldMethodFld = ldMethodFld->ConvertToBailOutInstr(ldFldInstr, IR::BailOutFailedInlineTypeCheck);
  3030. ldFldInstr->ReplaceSrc1(ldMethodFld->GetDst());
  3031. ldMethodFld->SetByteCodeOffset(ldFldInstr);
  3032. // generate further object/type bailout
  3033. PrepareInsertionPoint(ldFldInstr, functionInfo, ldFldInstr);
  3034. // We have three arguments to pass to the OpCode. Create a new ExtendArg_A opcode to chain up the argument. It is similar to ArgOut chain
  3035. // except that it is not argout.
  3036. // The Opcode sequence is like:
  3037. // (dst)helpArg1: ExtendArg_A (src1)thisObject (src2)null
  3038. // (dst)helpArg2: ExtendArg_A (src1)funcObject (src2)helpArg1
  3039. // method: DOMFastPathGetter (src1)HelperCall (src2)helpArg2
  3040. IR::Instr* extendArg0 = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, ldFldInstr->m_func), instanceOpnd, ldFldInstr->m_func);
  3041. ldFldInstr->InsertBefore(extendArg0);
  3042. IR::Instr* extendArg1 = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, ldFldInstr->m_func), ldMethodFld->GetDst(), extendArg0->GetDst(), ldFldInstr->m_func);
  3043. ldFldInstr->InsertBefore(extendArg1);
  3044. ldFldInstr->ReplaceSrc1(IR::HelperCallOpnd::New(helperMethod, ldFldInstr->m_func));
  3045. ldFldInstr->SetSrc2(extendArg1->GetDst());
  3046. ldFldInstr->m_opcode = Js::OpCode::DOMFastPathGetter;
  3047. StackSym * tmpSym = StackSym::New(ldFldInstr->GetDst()->GetType(), ldFldInstr->m_func);
  3048. IR::Opnd * tmpDst = IR::RegOpnd::New(tmpSym, tmpSym->GetType(), ldFldInstr->m_func);
  3049. IR::Opnd * callInstrDst = ldFldInstr->UnlinkDst();
  3050. ldFldInstr->SetDst(tmpDst);
  3051. IR::Instr * ldInstr = IR::Instr::New(Js::OpCode::Ld_A, callInstrDst, tmpDst, ldFldInstr->m_func);
  3052. ldFldInstr->InsertAfter(ldInstr);
  3053. this->topFunc->SetHasInlinee();
  3054. InsertStatementBoundary(ldInstr->m_next);
  3055. return ldInstr->m_next;
  3056. }
  3057. #endif
  3058. void
  3059. Inline::InsertStatementBoundary(IR::Instr * instrNext)
  3060. {
  3061. if (lastStatementBoundary)
  3062. {
  3063. Assert(lastStatementBoundary->m_func == instrNext->m_func);
  3064. IR::PragmaInstr * pragmaInstr = IR::PragmaInstr::New(Js::OpCode::StatementBoundary,
  3065. lastStatementBoundary->m_statementIndex,
  3066. lastStatementBoundary->m_func);
  3067. pragmaInstr->SetByteCodeOffset(instrNext);
  3068. instrNext->InsertBefore(pragmaInstr);
  3069. }
  3070. }
  3071. IR::Instr *
  3072. Inline::InlineScriptFunction(IR::Instr *callInstr, const Js::FunctionCodeGenJitTimeData *const inlineeData, const StackSym *symCallerThis, const Js::ProfileId profileId, bool* pIsInlined, uint recursiveInlineDepth)
  3073. {
  3074. *pIsInlined = false;
  3075. // This function is recursive, so when jitting in the foreground, probe the stack
  3076. if (!this->topFunc->IsBackgroundJIT())
  3077. {
  3078. PROBE_STACK(this->topFunc->GetScriptContext(), Js::Constants::MinStackDefault);
  3079. }
  3080. IR::Instr *instrNext = callInstr->m_next;
  3081. Js::FunctionBody *funcCaller = callInstr->m_func->GetJnFunction();
  3082. Js::FunctionBody *funcBody = inlineeData->GetFunctionBody();
  3083. // We don't do stack args optimization in jitted loop body (because of lack of information about the code before and after the loop)
  3084. // and we turn off stack arg optimization for the whole inline chain if we can't do it for one of the functionss.
  3085. // Inlining a function that uses arguments object could potentially hurt perf because we'll have to create arguments object on the
  3086. // heap for that function (versus otherwise the function will be jitted and have its arguments object creation optimized).
  3087. // TODO: Allow arguments object creation to be optimized on a function level instead of an all-or-nothing approach.
  3088. if (callInstr->m_func->IsLoopBody() && funcBody->GetUsesArgumentsObject())
  3089. {
  3090. return instrNext;
  3091. }
  3092. if (callInstr->GetSrc2() &&
  3093. callInstr->GetSrc2()->IsSymOpnd() &&
  3094. callInstr->GetSrc2()->AsSymOpnd()->m_sym->AsStackSym()->GetArgSlotNum() > Js::InlineeCallInfo::MaxInlineeArgoutCount)
  3095. {
  3096. #if ENABLE_DEBUG_CONFIG_OPTIONS
  3097. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  3098. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  3099. #endif
  3100. // This is a hard limit as we only use 4 bits to encode the actual count in the InlineeCallInfo. Although
  3101. // InliningDecider already checks for this, the check is against profile data that may not be accurate since profile
  3102. // data matching does not take into account some types of changes to source code. Need to check this again with current
  3103. // information.
  3104. INLINE_TESTTRACE(_u("INLINING: Skip Inline: ArgSlot > MaxInlineeArgoutCount\tInlinee: %s (%s)\tArgSlotNum: %d\tMaxInlineeArgoutCount: %d\tCaller: %s (%s)\n"),
  3105. funcBody->GetDisplayName(), funcBody->GetDebugNumberSet(debugStringBuffer), callInstr->GetSrc2()->AsSymOpnd()->m_sym->AsStackSym()->GetArgSlotNum(),
  3106. Js::InlineeCallInfo::MaxInlineeArgoutCount, funcCaller->GetDisplayName(), funcCaller->GetDebugNumberSet(debugStringBuffer2));
  3107. return instrNext;
  3108. }
  3109. *pIsInlined = true;
  3110. // Save off the call target operand (function object) so we can extend its lifetime as needed, even if
  3111. // the call instruction gets transformed to CallIFixed.
  3112. StackSym* originalCallTargetStackSym = callInstr->GetSrc1()->GetStackSym();
  3113. // We are committed to inlining, optimize the call instruction for fixed fields now and don't attempt it later.
  3114. bool isFixed = false;
  3115. JS_ETW(EventWriteJSCRIPT_BACKEND_INLINE(
  3116. funcCaller->GetFunctionNumber(), funcBody->GetFunctionNumber(),
  3117. funcCaller->GetExternalDisplayName(), funcBody->GetExternalDisplayName()));
  3118. bool isCtor = false;
  3119. bool safeThis = false;
  3120. IR::Instr *inlineBailoutChecksBeforeInstr;
  3121. if (callInstr->m_opcode == Js::OpCode::NewScObject || callInstr->m_opcode == Js::OpCode::NewScObjArray)
  3122. {
  3123. isCtor = true;
  3124. isFixed = TryOptimizeCallInstrWithFixedMethod(callInstr, inlineeData->GetFunctionInfo(),
  3125. false /*isPolymorphic*/, false /*isBuiltIn*/, isCtor /*isCtor*/, true /*isInlined*/, safeThis /*&safeThis*/);
  3126. bool split = SplitConstructorCall(callInstr, true, isFixed, &inlineBailoutChecksBeforeInstr);
  3127. Assert(split && inlineBailoutChecksBeforeInstr != nullptr);
  3128. }
  3129. else
  3130. {
  3131. isFixed = TryOptimizeCallInstrWithFixedMethod(callInstr, inlineeData->GetFunctionInfo(),
  3132. false /*isPolymorphic*/, false /*isBuiltIn*/, isCtor /*isCtor*/, true /*isInlined*/, safeThis /*&safeThis*/);
  3133. inlineBailoutChecksBeforeInstr = callInstr;
  3134. }
  3135. Assert(callInstr->IsProfiledInstr());
  3136. Js::ProfileId callSiteId = static_cast<Js::ProfileId>(callInstr->AsProfiledInstr()->u.profileId);
  3137. Js::ProxyEntryPointInfo *defaultEntryPointInfo = funcBody->GetDefaultEntryPointInfo();
  3138. Assert(defaultEntryPointInfo->IsFunctionEntryPointInfo());
  3139. Js::FunctionEntryPointInfo *functionEntryPointInfo = static_cast<Js::FunctionEntryPointInfo*>(defaultEntryPointInfo);
  3140. JsFunctionCodeGen *workItem = JitAnew(this->topFunc->m_alloc, JsFunctionCodeGen,
  3141. funcBody->GetScriptContext()->GetNativeCodeGenerator(), funcBody, functionEntryPointInfo, this->topFunc->IsJitInDebugMode());
  3142. workItem->SetRecyclableData(JitAnew(this->topFunc->m_alloc, Js::CodeGenRecyclableData, inlineeData));
  3143. workItem->SetJitMode(this->topFunc->m_workItem->GetJitMode());
  3144. IR::RegOpnd * returnValueOpnd;
  3145. Js::RegSlot returnRegSlot;
  3146. if (callInstr->GetDst())
  3147. {
  3148. returnValueOpnd = callInstr->UnlinkDst()->AsRegOpnd();
  3149. returnRegSlot = returnValueOpnd->m_sym->GetByteCodeRegSlot();
  3150. }
  3151. else
  3152. {
  3153. returnValueOpnd = nullptr;
  3154. returnRegSlot = Js::Constants::NoRegister;
  3155. }
  3156. const auto profileInfo =
  3157. JitAnew(
  3158. this->topFunc->m_alloc,
  3159. Js::ReadOnlyDynamicProfileInfo,
  3160. funcBody->HasDynamicProfileInfo() ? funcBody->GetAnyDynamicProfileInfo() : nullptr,
  3161. this->topFunc->IsBackgroundJIT() ? this->topFunc->m_alloc : nullptr);
  3162. Js::EntryPointPolymorphicInlineCacheInfo * entryPointPolymorphicInlineCacheInfo = this->topFunc->m_workItem->GetEntryPoint()->GetPolymorphicInlineCacheInfo();
  3163. Func *inlinee = JitAnew(this->topFunc->m_alloc,
  3164. Func,
  3165. this->topFunc->m_alloc,
  3166. workItem,
  3167. callInstr->m_func->m_runtimeData ?
  3168. callInstr->m_func->m_runtimeData->GetInlineeForTargetInlinee(profileId, funcBody) :
  3169. this->topFunc->GetJnFunction()->GetInlineeCodeGenRuntimeDataForTargetInlinee(profileId, funcBody),
  3170. entryPointPolymorphicInlineCacheInfo ? entryPointPolymorphicInlineCacheInfo->GetInlineeInfo(funcBody) : nullptr,
  3171. this->topFunc->GetCodeGenAllocators(),
  3172. this->topFunc->GetNumberAllocator(),
  3173. profileInfo,
  3174. this->topFunc->GetCodeGenProfiler(),
  3175. this->topFunc->IsBackgroundJIT(),
  3176. callInstr->m_func,
  3177. callInstr->GetNextRealInstr()->GetByteCodeOffset(),
  3178. returnRegSlot,
  3179. isCtor,
  3180. callSiteId,
  3181. false);
  3182. return InlineFunctionCommon(callInstr, originalCallTargetStackSym, funcBody, inlinee, instrNext, returnValueOpnd, inlineBailoutChecksBeforeInstr, symCallerThis, recursiveInlineDepth, safeThis);
  3183. }
  3184. bool
  3185. Inline::SplitConstructorCall(IR::Instr *const newObjInstr, const bool isInlined, const bool isFixed, IR::Instr** createObjInstrOut, IR::Instr** callCtorInstrOut) const
  3186. {
  3187. Assert(newObjInstr);
  3188. Assert(newObjInstr->m_opcode == Js::OpCode::NewScObject);
  3189. Assert(newObjInstr->GetSrc1());
  3190. Assert(newObjInstr->GetSrc2());
  3191. this->topFunc->SetHasTempObjectProducingInstr(true);
  3192. return
  3193. SplitConstructorCallCommon(
  3194. newObjInstr,
  3195. newObjInstr->GetSrc2(),
  3196. Js::OpCode::NewScObjectNoCtor,
  3197. isInlined,
  3198. isFixed,
  3199. createObjInstrOut,
  3200. callCtorInstrOut);
  3201. }
  3202. bool
  3203. Inline::SplitConstructorCallCommon(
  3204. IR::Instr *const newObjInstr,
  3205. IR::Opnd *const lastArgOpnd,
  3206. const Js::OpCode newObjOpCode,
  3207. const bool isInlined,
  3208. const bool isFixed,
  3209. IR::Instr** createObjInstrOut,
  3210. IR::Instr** callCtorInstrOut) const
  3211. {
  3212. Assert(newObjInstr);
  3213. Assert(newObjInstr->GetSrc1());
  3214. Assert(lastArgOpnd);
  3215. Assert(isInlined || isFixed);
  3216. const auto callerFunc = newObjInstr->m_func;
  3217. // Call the NoCtor version of NewScObject
  3218. // Use a temporary register for the newly allocated object (before the call to ctor) - even if we know we'll return this
  3219. // object from the whole operation. That's so that we don't trash the bytecode register if we need to bail out at
  3220. // object allocation (bytecode instruction has the form [Profiled]NewScObject R6 = R6).
  3221. IR::RegOpnd* createObjDst = nullptr;
  3222. IR::Instr* createObjInstr = nullptr;
  3223. const Js::JitTimeConstructorCache* constructorCache;
  3224. bool returnCreatedObject = false;
  3225. bool skipNewScObj = false;
  3226. if (newObjInstr->IsProfiledInstr())
  3227. {
  3228. Js::ProfileId profiledCallSiteId = static_cast<Js::ProfileId>(newObjInstr->AsProfiledInstr()->u.profileId);
  3229. constructorCache = newObjInstr->m_func->GetConstructorCache(profiledCallSiteId);
  3230. returnCreatedObject = constructorCache != nullptr && constructorCache->ctorHasNoExplicitReturnValue;
  3231. skipNewScObj = constructorCache != nullptr && constructorCache->skipNewScObject;
  3232. if (!skipNewScObj)
  3233. {
  3234. createObjDst = IR::RegOpnd::New(TyVar, callerFunc);
  3235. createObjInstr = IR::ProfiledInstr::New(newObjOpCode, createObjDst, newObjInstr->GetSrc1(), callerFunc);
  3236. createObjInstr->AsProfiledInstr()->u.profileId = profiledCallSiteId;
  3237. }
  3238. }
  3239. else
  3240. {
  3241. constructorCache = nullptr;
  3242. createObjDst = IR::RegOpnd::New(TyVar, callerFunc);
  3243. createObjInstr = IR::Instr::New(newObjOpCode, createObjDst, newObjInstr->GetSrc1(), callerFunc);
  3244. }
  3245. Assert(!isInlined || !skipNewScObj);
  3246. Assert(isFixed || !skipNewScObj);
  3247. // For new Object() and new Array() we have special fast helpers. We'll let the lowerer convert this instruction directly
  3248. // into a call to one of these helpers.
  3249. if (skipNewScObj)
  3250. {
  3251. Js::JavascriptFunction* ctor = newObjInstr->GetFixedFunction();
  3252. Js::FunctionInfo* ctorInfo = ctor->GetFunctionInfo();
  3253. if ((ctorInfo == &Js::JavascriptObject::EntryInfo::NewInstance || ctorInfo == &Js::JavascriptArray::EntryInfo::NewInstance) &&
  3254. newObjInstr->HasEmptyArgOutChain())
  3255. {
  3256. return false;
  3257. }
  3258. }
  3259. IR::Opnd* thisPtrOpnd;
  3260. if (createObjInstr != nullptr)
  3261. {
  3262. createObjInstr->SetByteCodeOffset(newObjInstr);
  3263. createObjInstr->GetSrc1()->SetIsJITOptimizedReg(true);
  3264. newObjInstr->InsertBefore(createObjInstr);
  3265. createObjDst->SetValueType(ValueType::GetObject(ObjectType::UninitializedObject));
  3266. thisPtrOpnd = createObjDst;
  3267. }
  3268. else
  3269. {
  3270. thisPtrOpnd = IR::AddrOpnd::NewNull(newObjInstr->m_func);
  3271. }
  3272. // Pass the new object to the constructor function with an ArgOut
  3273. const auto thisArgOpnd = IR::SymOpnd::New(callerFunc->m_symTable->GetArgSlotSym(1), TyVar, callerFunc);
  3274. auto instr = IR::Instr::New(Js::OpCode::ArgOut_A, thisArgOpnd, thisPtrOpnd, lastArgOpnd, callerFunc);
  3275. instr->SetByteCodeOffset(newObjInstr);
  3276. instr->GetDst()->SetIsJITOptimizedReg(true);
  3277. instr->GetSrc2()->SetIsJITOptimizedReg(true);
  3278. newObjInstr->InsertBefore(instr);
  3279. // Call the constructor using CallI with isCtorCall set. If we inline the constructor, and the inlined constructor
  3280. // bails out, the interpreter would be entered with CallFlags_Value as well. If the interpreter starts using the
  3281. // call flags, the proper call flags will need to be specified here by using a different op code specific to constructors.
  3282. if (isFixed)
  3283. {
  3284. newObjInstr->m_opcode = Js::OpCode::CallIFixed;
  3285. }
  3286. else
  3287. {
  3288. newObjInstr->m_opcode = Js::OpCode::CallI;
  3289. }
  3290. newObjInstr->isCtorCall = true;
  3291. if(newObjInstr->GetSrc2())
  3292. {
  3293. newObjInstr->FreeSrc2();
  3294. }
  3295. newObjInstr->SetSrc2(thisArgOpnd);
  3296. const auto insertBeforeInstr = newObjInstr->m_next;
  3297. Assert(insertBeforeInstr);
  3298. const auto nextByteCodeOffsetInstr = newObjInstr->GetNextRealInstrOrLabel();
  3299. // Determine which object to use as the final result of NewScObject, the object passed into the constructor as 'this', or
  3300. // the object returned by the constructor. We only need this if we don't have a hard-coded constructor cache, or if the
  3301. // constructor returns something explicitly. Otherwise, we simply return the object we allocated and passed to the constructor.
  3302. if (returnCreatedObject)
  3303. {
  3304. instr = IR::Instr::New(Js::OpCode::Ld_A, newObjInstr->GetDst(), createObjDst, callerFunc);
  3305. instr->SetByteCodeOffset(nextByteCodeOffsetInstr);
  3306. instr->GetDst()->SetIsJITOptimizedReg(true);
  3307. instr->GetSrc1()->SetIsJITOptimizedReg(true);
  3308. insertBeforeInstr->InsertBefore(instr);
  3309. }
  3310. else if (!skipNewScObj)
  3311. {
  3312. Assert(createObjDst != newObjInstr->GetDst());
  3313. // Since we're not returning the default new object, the constructor must be returning something explicitly. We don't
  3314. // know at this point whether it's an object or not. If the constructor is later inlined, the value type will be determined
  3315. // from the flow in glob opt. Otherwise, we'll need to emit an object check.
  3316. newObjInstr->GetDst()->SetValueType(ValueType::Uninitialized);
  3317. instr = IR::Instr::New(Js::OpCode::GetNewScObject, newObjInstr->GetDst(), newObjInstr->GetDst(), createObjDst, callerFunc);
  3318. instr->SetByteCodeOffset(nextByteCodeOffsetInstr);
  3319. instr->GetDst()->SetIsJITOptimizedReg(true);
  3320. instr->GetSrc1()->SetIsJITOptimizedReg(true);
  3321. insertBeforeInstr->InsertBefore(instr);
  3322. }
  3323. // Update the NewScObject cache, but only if we don't have a hard-coded constructor cache. We only clone caches that
  3324. // don't require update, and once updated a cache never requires an update again.
  3325. if (constructorCache == nullptr)
  3326. {
  3327. instr = IR::Instr::New(Js::OpCode::UpdateNewScObjectCache, callerFunc);
  3328. instr->SetSrc1(newObjInstr->GetSrc1()); // constructor function
  3329. instr->SetSrc2(newObjInstr->GetDst()); // the new object
  3330. instr->SetByteCodeOffset(nextByteCodeOffsetInstr);
  3331. instr->GetSrc1()->SetIsJITOptimizedReg(true);
  3332. instr->GetSrc2()->SetIsJITOptimizedReg(true);
  3333. insertBeforeInstr->InsertBefore(instr);
  3334. }
  3335. if (createObjInstrOut != nullptr)
  3336. {
  3337. *createObjInstrOut = createObjInstr;
  3338. }
  3339. if (callCtorInstrOut != nullptr)
  3340. {
  3341. *callCtorInstrOut = newObjInstr;
  3342. }
  3343. return true;
  3344. }
  3345. void
  3346. Inline::InsertObjectCheck(IR::Instr *callInstr, IR::Instr* insertBeforeInstr, IR::Instr*bailOutIfNotObject)
  3347. {
  3348. // Bailout if 'functionRegOpnd' is not an object.
  3349. bailOutIfNotObject->SetSrc1(callInstr->GetSrc1()->AsRegOpnd());
  3350. bailOutIfNotObject->SetByteCodeOffset(insertBeforeInstr);
  3351. insertBeforeInstr->InsertBefore(bailOutIfNotObject);
  3352. }
  3353. void
  3354. Inline::InsertFunctionTypeIdCheck(IR::Instr *callInstr, IR::Instr* insertBeforeInstr, IR::Instr* bailOutIfNotJsFunction)
  3355. {
  3356. // functionTypeRegOpnd = Ld functionRegOpnd->type
  3357. IR::IndirOpnd *functionTypeIndirOpnd = IR::IndirOpnd::New(callInstr->GetSrc1()->AsRegOpnd(), Js::RecyclableObject::GetOffsetOfType(), TyMachPtr, callInstr->m_func);
  3358. IR::RegOpnd *functionTypeRegOpnd = IR::RegOpnd::New(TyVar, this->topFunc);
  3359. IR::Instr *instr = IR::Instr::New(Js::OpCode::Ld_A, functionTypeRegOpnd, functionTypeIndirOpnd, callInstr->m_func);
  3360. if(instr->m_func->HasByteCodeOffset())
  3361. {
  3362. instr->SetByteCodeOffset(insertBeforeInstr);
  3363. }
  3364. insertBeforeInstr->InsertBefore(instr);
  3365. CompileAssert(sizeof(Js::TypeId) == sizeof(int32));
  3366. // if (functionTypeRegOpnd->typeId != TypeIds_Function) goto $noInlineLabel
  3367. // BrNeq_I4 $noInlineLabel, functionTypeRegOpnd->typeId, TypeIds_Function
  3368. IR::IndirOpnd *functionTypeIdIndirOpnd = IR::IndirOpnd::New(functionTypeRegOpnd, Js::Type::GetOffsetOfTypeId(), TyInt32, callInstr->m_func);
  3369. IR::IntConstOpnd *typeIdFunctionConstOpnd = IR::IntConstOpnd::New(Js::TypeIds_Function, TyInt32, callInstr->m_func);
  3370. bailOutIfNotJsFunction->SetSrc1(functionTypeIdIndirOpnd);
  3371. bailOutIfNotJsFunction->SetSrc2(typeIdFunctionConstOpnd);
  3372. insertBeforeInstr->InsertBefore(bailOutIfNotJsFunction);
  3373. }
  3374. void
  3375. Inline::InsertJsFunctionCheck(IR::Instr *callInstr, IR::Instr *insertBeforeInstr, IR::BailOutKind bailOutKind)
  3376. {
  3377. // This function only inserts bailout for tagged int & TypeIds_Function.
  3378. // As of now this is only used for polymorphic inlining.
  3379. Assert(bailOutKind == IR::BailOutOnPolymorphicInlineFunction);
  3380. Assert(insertBeforeInstr);
  3381. Assert(insertBeforeInstr->m_func == callInstr->m_func);
  3382. // bailOutIfNotFunction is primary bailout instruction
  3383. IR::Instr* bailOutIfNotFunction = IR::BailOutInstr::New(Js::OpCode::BailOnNotEqual, bailOutKind, insertBeforeInstr, callInstr->m_func);
  3384. IR::Instr *bailOutIfNotObject = IR::BailOutInstr::New(Js::OpCode::BailOnNotObject, bailOutKind, bailOutIfNotFunction->GetBailOutInfo(),callInstr->m_func);
  3385. InsertObjectCheck(callInstr, insertBeforeInstr, bailOutIfNotObject);
  3386. InsertFunctionTypeIdCheck(callInstr, insertBeforeInstr, bailOutIfNotFunction);
  3387. }
  3388. void
  3389. Inline::InsertFunctionBodyCheck(IR::Instr *callInstr, IR::Instr *insertBeforeInstr, IR::Instr* bailoutInstr, Js::FunctionInfo *funcInfo)
  3390. {
  3391. // if (JavascriptFunction::FromVar(r1)->functionInfo != funcInfo) goto noInlineLabel
  3392. // BrNeq_I4 noInlineLabel, r1->functionInfo, funcInfo
  3393. IR::IndirOpnd* funcBody = IR::IndirOpnd::New(callInstr->GetSrc1()->AsRegOpnd(), Js::JavascriptFunction::GetOffsetOfFunctionInfo(), TyMachPtr, callInstr->m_func);
  3394. IR::AddrOpnd* inlinedFuncBody = IR::AddrOpnd::New(funcInfo, IR::AddrOpndKindDynamicFunctionBody, callInstr->m_func);
  3395. bailoutInstr->SetSrc1(funcBody);
  3396. bailoutInstr->SetSrc2(inlinedFuncBody);
  3397. insertBeforeInstr->InsertBefore(bailoutInstr);
  3398. }
  3399. void
  3400. Inline::InsertFunctionObjectCheck(IR::Instr *callInstr, IR::Instr *insertBeforeInstr, IR::Instr *bailOutInstr, Js::FunctionInfo *funcInfo)
  3401. {
  3402. Js::BuiltinFunction index = Js::JavascriptLibrary::GetBuiltInForFuncInfo(funcInfo, callInstr->m_func->GetScriptContext());
  3403. AssertMsg(index < Js::BuiltinFunction::Count, "Invalid built-in index on a call target marked as built-in");
  3404. bailOutInstr->SetSrc1(callInstr->GetSrc1()->AsRegOpnd());
  3405. bailOutInstr->SetSrc2(IR::IntConstOpnd::New(index, TyInt32, callInstr->m_func));
  3406. insertBeforeInstr->InsertBefore(bailOutInstr);
  3407. }
  3408. IR::Instr *
  3409. Inline::PrepareInsertionPoint(IR::Instr *callInstr, Js::FunctionInfo *funcInfo, IR::Instr *insertBeforeInstr, IR::BailOutKind bailOutKind)
  3410. {
  3411. Assert(insertBeforeInstr);
  3412. Assert(insertBeforeInstr->m_func == callInstr->m_func);
  3413. Assert(bailOutKind == IR::BailOutOnInlineFunction);
  3414. // FunctionBody check is the primary bailout instruction, create it first
  3415. IR::BailOutInstr* primaryBailOutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNotEqual, bailOutKind, insertBeforeInstr, callInstr->m_func);
  3416. // 1. Bailout if function object is not an object.
  3417. IR::Instr *bailOutIfNotObject = IR::BailOutInstr::New(Js::OpCode::BailOnNotObject,
  3418. bailOutKind,
  3419. primaryBailOutInstr->GetBailOutInfo(),
  3420. callInstr->m_func);
  3421. InsertObjectCheck(callInstr, insertBeforeInstr, bailOutIfNotObject);
  3422. // 2. Bailout if function object is not a TypeId_Function
  3423. IR::Instr* bailOutIfNotJsFunction = IR::BailOutInstr::New(Js::OpCode::BailOnNotEqual, bailOutKind, primaryBailOutInstr->GetBailOutInfo(), callInstr->m_func);
  3424. InsertFunctionTypeIdCheck(callInstr, insertBeforeInstr, bailOutIfNotJsFunction);
  3425. // 3. Bailout if function body doesn't match funcInfo
  3426. InsertFunctionBodyCheck(callInstr, insertBeforeInstr, primaryBailOutInstr, funcInfo);
  3427. return primaryBailOutInstr;
  3428. }
  3429. uint Inline::CountActuals(IR::Instr *callInstr)
  3430. {
  3431. IR::Opnd *linkOpnd = callInstr->GetSrc2();
  3432. uint actualCount = 0;
  3433. if (linkOpnd->IsSymOpnd())
  3434. {
  3435. IR::Instr *argInstr;
  3436. do
  3437. {
  3438. Assert(linkOpnd->IsSymOpnd());
  3439. StackSym *sym = linkOpnd->AsSymOpnd()->m_sym->AsStackSym();
  3440. Assert(sym->m_isSingleDef);
  3441. Assert(sym->IsArgSlotSym());
  3442. argInstr = sym->m_instrDef;
  3443. ++actualCount;
  3444. linkOpnd = argInstr->GetSrc2();
  3445. }
  3446. while (linkOpnd->IsSymOpnd());
  3447. }
  3448. return actualCount;
  3449. }
  3450. bool Inline::InlConstFoldArg(IR::Instr *instr, __in_ecount_opt(callerArgOutCount) IR::Instr *callerArgOuts[], Js::ArgSlot callerArgOutCount)
  3451. {
  3452. Assert(instr->m_opcode == Js::OpCode::ArgOut_A);
  3453. if (PHASE_OFF(Js::InlinerConstFoldPhase, instr->m_func->GetTopFunc()))
  3454. {
  3455. return false;
  3456. }
  3457. IR::Opnd *src1 = instr->GetSrc1();
  3458. IntConstType value;
  3459. if (!src1->IsRegOpnd())
  3460. {
  3461. return false;
  3462. }
  3463. StackSym *sym = instr->GetSrc1()->AsRegOpnd()->m_sym;
  3464. if (!sym->IsSingleDef())
  3465. {
  3466. return false;
  3467. }
  3468. IR::Instr *instrDef = sym->GetInstrDef();
  3469. if (!this->InlConstFold(instrDef, &value, callerArgOuts, callerArgOutCount))
  3470. {
  3471. return false;
  3472. }
  3473. return true;
  3474. }
  3475. bool Inline::InlConstFold(IR::Instr *instr, IntConstType *pValue, __in_ecount_opt(callerArgOutCount) IR::Instr *callerArgOuts[], Js::ArgSlot callerArgOutCount)
  3476. {
  3477. IR::Opnd *src1 = instr->GetSrc1();
  3478. if (!src1)
  3479. {
  3480. return false;
  3481. }
  3482. switch (src1->GetKind())
  3483. {
  3484. case IR::OpndKindReg:
  3485. // Walk the tree below
  3486. break;
  3487. case IR::OpndKindIntConst:
  3488. if (instr->m_opcode == Js::OpCode::LdC_A_I4)
  3489. {
  3490. // Found a constant
  3491. *pValue = src1->AsIntConstOpnd()->GetValue();
  3492. return true;
  3493. }
  3494. return false;
  3495. case IR::OpndKindSym:
  3496. if (callerArgOuts && instr->m_opcode == Js::OpCode::ArgIn_A)
  3497. {
  3498. // We have an ArgIn. Walk the caller's ArgOut tree to see if a constant
  3499. // is passed in to the inlinee.
  3500. Assert(callerArgOuts && callerArgOutCount != (Js::ArgSlot) - 1);
  3501. Assert(src1->AsSymOpnd()->m_sym->AsStackSym()->IsParamSlotSym());
  3502. Js::ArgSlot paramSlot = src1->AsSymOpnd()->m_sym->AsStackSym()->GetParamSlotNum();
  3503. if (paramSlot <= callerArgOutCount)
  3504. {
  3505. IR::Instr *argOut = callerArgOuts[paramSlot - 1];
  3506. IR::Opnd *argOutSrc1 = argOut->GetSrc1();
  3507. if (!argOutSrc1->IsRegOpnd())
  3508. {
  3509. return false;
  3510. }
  3511. StackSym *sym = argOutSrc1->AsRegOpnd()->m_sym;
  3512. if (!sym->IsSingleDef())
  3513. {
  3514. return false;
  3515. }
  3516. IR::Instr *instrDef = sym->GetInstrDef();
  3517. // Walk the caller
  3518. return InlConstFold(instrDef, pValue, nullptr, (Js::ArgSlot) - 1);
  3519. }
  3520. }
  3521. else if (src1->AsSymOpnd()->IsPropertySymOpnd())
  3522. {
  3523. // See if we have a LdFld of a fixed field.
  3524. Js::Var var = TryOptimizeInstrWithFixedDataProperty(instr);
  3525. if (!Js::TaggedInt::Is(var))
  3526. {
  3527. return false;
  3528. }
  3529. else
  3530. {
  3531. *pValue = Js::TaggedInt::ToInt32(var);
  3532. return true;
  3533. }
  3534. }
  3535. return false;
  3536. default:
  3537. return false;
  3538. }
  3539. // All that is left is RegOpnds
  3540. Assert(src1->IsRegOpnd());
  3541. StackSym *sym = instr->GetSrc1()->AsRegOpnd()->m_sym;
  3542. if (!sym->IsSingleDef())
  3543. {
  3544. return false;
  3545. }
  3546. if (!src1 || !src1->IsRegOpnd() || !src1->AsRegOpnd()->m_sym->IsSingleDef())
  3547. {
  3548. return false;
  3549. }
  3550. IR::Opnd *src2 = instr->GetSrc2();
  3551. if (src2)
  3552. {
  3553. if (!src2->IsRegOpnd() || !src2->AsRegOpnd()->m_sym->IsSingleDef())
  3554. {
  3555. return false;
  3556. }
  3557. }
  3558. // See if src1 can be folded to a constant
  3559. if (!InlConstFold(src1->AsRegOpnd()->m_sym->GetInstrDef(), pValue, callerArgOuts, callerArgOutCount))
  3560. {
  3561. return false;
  3562. }
  3563. IntConstType src1Constant = *pValue;
  3564. // See if src2 (unless it is unary) can be folded to a constant
  3565. if (src2 && !InlConstFold(src2->AsRegOpnd()->m_sym->GetInstrDef(), pValue, callerArgOuts, callerArgOutCount))
  3566. {
  3567. return false;
  3568. }
  3569. // Now let's try to constant fold the current instruction
  3570. if (src2)
  3571. {
  3572. IntConstType src2Constant = *pValue;
  3573. if (!instr->BinaryCalculator(src1Constant, src2Constant, pValue)
  3574. || !Math::FitsInDWord(*pValue))
  3575. {
  3576. return false;
  3577. }
  3578. // Success
  3579. IR::ByteCodeUsesInstr * byteCodeInstr = IR::ByteCodeUsesInstr::New(instr->m_func);
  3580. byteCodeInstr->SetByteCodeOffset(instr);
  3581. StackSym *src1Sym = src1->AsRegOpnd()->m_sym;
  3582. StackSym *src2Sym = src2->AsRegOpnd()->m_sym;
  3583. if (src1Sym->HasByteCodeRegSlot() || src2Sym->HasByteCodeRegSlot())
  3584. {
  3585. if (src1Sym->HasByteCodeRegSlot())
  3586. {
  3587. byteCodeInstr->Set(src1Sym->m_id);
  3588. }
  3589. if (src2Sym->HasByteCodeRegSlot())
  3590. {
  3591. byteCodeInstr->Set(src2Sym->m_id);
  3592. }
  3593. instr->InsertBefore(byteCodeInstr);
  3594. }
  3595. #if DBG_DUMP
  3596. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::InlinerConstFoldPhase, this->topFunc->GetSourceContextId(), this->topFunc->GetLocalFunctionId()))
  3597. {
  3598. Output::Print(_u("Constant folding to %d\n"), *pValue);
  3599. instr->Dump();
  3600. }
  3601. #endif
  3602. instr->m_opcode = Js::OpCode::LdC_A_I4;
  3603. instr->ReplaceSrc1(IR::IntConstOpnd::New(*pValue, TyInt32, instr->m_func));
  3604. instr->GetDst()->AsRegOpnd()->m_sym->SetIsConst();
  3605. instr->FreeSrc2();
  3606. }
  3607. else
  3608. {
  3609. if (!instr->UnaryCalculator(src1Constant, pValue)
  3610. || !Math::FitsInDWord(*pValue))
  3611. {
  3612. // Skip over BytecodeArgOutCapture
  3613. if (instr->m_opcode == Js::OpCode::BytecodeArgOutCapture)
  3614. {
  3615. return true;
  3616. }
  3617. return false;
  3618. }
  3619. // Success
  3620. StackSym *src1Sym = src1->AsRegOpnd()->m_sym;
  3621. if (src1Sym->HasByteCodeRegSlot())
  3622. {
  3623. IR::ByteCodeUsesInstr * byteCodeInstr = IR::ByteCodeUsesInstr::New(instr->m_func);
  3624. byteCodeInstr->SetByteCodeOffset(instr);
  3625. byteCodeInstr->Set(src1Sym->m_id);
  3626. instr->InsertBefore(byteCodeInstr);
  3627. }
  3628. #if DBG_DUMP
  3629. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::InlinerConstFoldPhase, this->topFunc->GetSourceContextId(), this->topFunc->GetLocalFunctionId()))
  3630. {
  3631. Output::Print(_u("Constant folding to %d\n"), *pValue);
  3632. instr->Dump();
  3633. }
  3634. #endif
  3635. instr->m_opcode = Js::OpCode::LdC_A_I4;
  3636. instr->ReplaceSrc1(IR::IntConstOpnd::New(*pValue, TyInt32, instr->m_func));
  3637. instr->GetDst()->AsRegOpnd()->m_sym->SetIsConst();
  3638. }
  3639. return true;
  3640. }
  3641. Js::ArgSlot
  3642. Inline::MapActuals(IR::Instr *callInstr, __out_ecount(maxParamCount) IR::Instr *argOuts[],
  3643. Js::ArgSlot formalCount,
  3644. Func* inlinee,
  3645. Js::ProfileId callSiteId,
  3646. bool *stackArgsArgOutExpanded,
  3647. IR::Instr *argOutsExtra[],
  3648. Js::ArgSlot maxParamCount /* = Js::InlineeCallInfo::MaxInlineeArgoutCount*/)
  3649. {
  3650. AnalysisAssert(formalCount <= maxParamCount);
  3651. IR::Opnd *linkOpnd = callInstr->GetSrc2();
  3652. Js::ArgSlot actualCount = 0;
  3653. *stackArgsArgOutExpanded = false;
  3654. uint inlineeFrameSlot = currentInlineeFrameSlot + (Js::Constants::InlineeMetaArgCount - 1);
  3655. uint fixupArgoutCount = 0;
  3656. if (inlinee)
  3657. {
  3658. bool hasArgumentsAccess = this->GetInlineeHasArgumentObject(inlinee);
  3659. inlinee->SetHasUnoptimizedArgumentsAccess(hasArgumentsAccess);
  3660. }
  3661. if (linkOpnd->IsSymOpnd())
  3662. {
  3663. IR::Instr *argInstr;
  3664. do
  3665. {
  3666. Assert(linkOpnd->IsSymOpnd());
  3667. StackSym *sym = linkOpnd->AsSymOpnd()->m_sym->AsStackSym();
  3668. Assert(sym->m_isSingleDef);
  3669. Assert(sym->IsArgSlotSym());
  3670. sym->m_isInlinedArgSlot = true;
  3671. this->topFunc->SetArgOffset(sym, (inlineeFrameSlot + sym->GetArgSlotNum()) * MachPtr);
  3672. argInstr = sym->m_instrDef;
  3673. if (argInstr->m_opcode == Js::OpCode::ArgOut_A)
  3674. {
  3675. if(inlinee)
  3676. {
  3677. if (!inlinee->GetHasUnoptimizedArgumentsAcccess())
  3678. {
  3679. // This allows us to markTemp the argOut source.
  3680. argInstr->m_opcode = Js::OpCode::ArgOut_A_Inline;
  3681. }
  3682. }
  3683. argInstr->GenerateBytecodeArgOutCapture();
  3684. }
  3685. // Expand
  3686. //
  3687. // s31 ArgOut_A s32
  3688. // s30 ArgOut_A_FromStackArgs s31
  3689. //
  3690. // to
  3691. //
  3692. // s31 ArgOut_A(_Inline) s32
  3693. // sXX ArgOut_A_FixupForStackArgs s31
  3694. // .
  3695. // .
  3696. // s34 ArgOut_A_FixupForStackArgs sXX
  3697. // s30 ArgOut_A_FromStackArgs s34
  3698. if (inlinee && argInstr->m_opcode == Js::OpCode::ArgOut_A_FromStackArgs)
  3699. {
  3700. IR::Instr * argFixupInstr;
  3701. for(uint currentFormal = 1; currentFormal < formalCount; currentFormal++)
  3702. {
  3703. StackSym* newStackSym = StackSym::NewArgSlotSym(sym->GetArgSlotNum(), argInstr->m_func);
  3704. newStackSym->m_isInlinedArgSlot = true;
  3705. IR::SymOpnd * newLinkOpnd = IR::SymOpnd::New(newStackSym, sym->GetType(), argInstr->m_func);
  3706. IR::Opnd * undefined = IR::AddrOpnd::New(this->topFunc->GetScriptContext()->GetLibrary()->GetUndefined(),
  3707. IR::AddrOpndKindDynamicVar, this->topFunc, true);
  3708. undefined->SetValueType(ValueType::Undefined);
  3709. argFixupInstr = IR::Instr::New(Js::OpCode::ArgOut_A_FixupForStackArgs, newLinkOpnd, undefined, argInstr->GetSrc2(), argInstr->m_func);
  3710. argInstr->InsertBefore(argFixupInstr);
  3711. argInstr->ReplaceSrc2(argFixupInstr->GetDst());
  3712. sym->IncrementArgSlotNum();
  3713. argInstr->m_func->SetArgOffset(sym, (inlineeFrameSlot + sym->GetArgSlotNum()) * MachPtr);
  3714. argFixupInstr->GenerateArgOutSnapshot();
  3715. fixupArgoutCount++;
  3716. }
  3717. // Now that the arguments object has been expanded, we don't require the sym corresponding to it.
  3718. IR::IntConstOpnd* callSiteIdOpnd = IR::IntConstOpnd::New(callSiteId, TyUint16, argInstr->m_func);
  3719. argInstr->ReplaceSrc1(callSiteIdOpnd);
  3720. // Don't count ArgOut_A_FromStackArgs as an actual, when it has been expanded
  3721. --actualCount;
  3722. *stackArgsArgOutExpanded = true;
  3723. }
  3724. ++actualCount;
  3725. const Js::ArgSlot currentActual = sym->GetArgSlotNum() - 1;
  3726. if (currentActual < formalCount)
  3727. {
  3728. Assert(currentActual < Js::InlineeCallInfo::MaxInlineeArgoutCount);
  3729. argOuts[currentActual] = argInstr;
  3730. }
  3731. // We don't want to treat ArgOut_A_FromStackArgs as an actual arg.
  3732. else if (argInstr->m_opcode != Js::OpCode::ArgOut_A_FromStackArgs)
  3733. {
  3734. Assert(currentActual <= Js::InlineeCallInfo::MaxInlineeArgoutCount);
  3735. if(argOutsExtra)
  3736. {
  3737. argOutsExtra[currentActual] = argInstr;
  3738. if (currentActual < maxParamCount)
  3739. {
  3740. __analysis_assume(currentActual < Js::InlineeCallInfo::MaxInlineeArgoutCount);
  3741. argOuts[currentActual] = nullptr;
  3742. }
  3743. }
  3744. }
  3745. linkOpnd = argInstr->GetSrc2();
  3746. }
  3747. while (linkOpnd->IsSymOpnd());
  3748. #if DBG
  3749. Assert(actualCount <= Js::InlineeCallInfo::MaxInlineeArgoutCount);
  3750. for(Js::ArgSlot i = 0; i < min(actualCount, formalCount); ++i)
  3751. {
  3752. #pragma prefast(suppress:6001)
  3753. Assert(argOuts[i]);
  3754. }
  3755. #endif
  3756. }
  3757. Assert(linkOpnd->IsRegOpnd());
  3758. Assert(linkOpnd->AsRegOpnd()->m_sym->m_isSingleDef);
  3759. Js::OpCode startCallOpCode = linkOpnd->AsRegOpnd()->m_sym->m_instrDef->m_opcode;
  3760. Assert(startCallOpCode == Js::OpCode::StartCall);
  3761. // Update the count in StartCall to reflect
  3762. // 1. ArgOut_A_FromStackArgs is not an actual once it has been expanded.
  3763. // 2. The expanded argouts (from ArgOut_A_FromStackArgs).
  3764. //
  3765. // Note that the StartCall will reflect the formal count only as of now; the actual count would be set during MapFormals
  3766. if(*stackArgsArgOutExpanded)
  3767. {
  3768. // TODO: Is an underflow here intended, it triggers on test\inlining\OS_2733280.js
  3769. IR::IntConstOpnd * countOpnd = linkOpnd->AsRegOpnd()->m_sym->m_instrDef->GetSrc1()->AsIntConstOpnd();
  3770. int32 count = countOpnd->AsInt32();
  3771. count += fixupArgoutCount - 1;
  3772. countOpnd->SetValue(count);
  3773. callInstr->m_func->EnsureCallSiteToArgumentsOffsetFixupMap();
  3774. Assert(!(callInstr->m_func->callSiteToArgumentsOffsetFixupMap->ContainsKey(callSiteId)));
  3775. callInstr->m_func->callSiteToArgumentsOffsetFixupMap->Add(callSiteId, fixupArgoutCount - 1);
  3776. }
  3777. Assert(linkOpnd->AsRegOpnd()->m_sym->m_instrDef->GetArgOutCount(/*getInterpreterArgOutCount*/ false) == actualCount);
  3778. // Mark the StartCall's dst as an inlined arg slot as well so we know this is an inlined start call
  3779. // and not adjust the stack height on x86
  3780. linkOpnd->AsRegOpnd()->m_sym->m_isInlinedArgSlot = true;
  3781. // Missing arguments...
  3782. for (Js::ArgSlot i = actualCount; i < formalCount; i++)
  3783. {
  3784. argOuts[i] = nullptr;
  3785. }
  3786. // We may not know the exact number of actuals that "b" gets in a.b.apply just yet, since we have expanded the ArgOut_A_FromStackArgs based on the number of formals "b" accepts.
  3787. // So, return the actualCount stored on the func if the ArgOut_A_FromStackArgs was expanded (and thus, the expanded argouts were accounted for in calculating the local actualCount)
  3788. return *stackArgsArgOutExpanded ? callInstr->m_func->actualCount : actualCount;
  3789. }
  3790. void
  3791. Inline::MapFormals(Func *inlinee,
  3792. __in_ecount(formalCount) IR::Instr *argOuts[],
  3793. uint formalCount,
  3794. uint actualCount,
  3795. IR::RegOpnd *retOpnd,
  3796. IR::Opnd * funcObjOpnd,
  3797. const StackSym *symCallerThis,
  3798. bool stackArgsArgOutExpanded,
  3799. bool fixedFunctionSafeThis,
  3800. IR::Instr *argOutsExtra[])
  3801. {
  3802. IR::SymOpnd *formalOpnd;
  3803. uint argIndex;
  3804. uint formalCountForInlinee;
  3805. IR::Instr * argInstr;
  3806. IR::Opnd * linkOpnd;
  3807. bool fUsesSafeThis = false;
  3808. bool fUsesConstThis = false;
  3809. StackSym *symThis = nullptr;
  3810. Js::Var thisConstVar = nullptr;
  3811. FOREACH_INSTR_EDITING(instr, instrNext, inlinee->m_headInstr)
  3812. {
  3813. switch (instr->m_opcode)
  3814. {
  3815. case Js::OpCode::ArgIn_Rest:
  3816. {
  3817. // We only currently support a statically known number of actuals.
  3818. if (stackArgsArgOutExpanded)
  3819. {
  3820. break;
  3821. }
  3822. IR::Opnd *restDst = instr->GetDst();
  3823. Assert(actualCount < 1 << 24 && formalCount < 1 << 24); // 24 bits for arg count (see CallInfo.h)
  3824. int excess = actualCount - formalCount;
  3825. if (excess < 0)
  3826. {
  3827. excess = 0;
  3828. }
  3829. // Set the type info about the destination so the array offsets get calculated properly.
  3830. restDst->SetValueType(
  3831. ValueType::GetObject(ObjectType::Array)
  3832. .SetHasNoMissingValues(true)
  3833. .SetArrayTypeId(Js::TypeIds_Array));
  3834. restDst->SetValueTypeFixed();
  3835. // Create the array and assign the elements.
  3836. IR::Instr *newArrInstr = IR::Instr::New(Js::OpCode::NewScArray, restDst, IR::IntConstOpnd::New(excess, TyUint32, inlinee), inlinee);
  3837. instr->InsertBefore(newArrInstr);
  3838. for (uint i = formalCount; i < actualCount; ++i)
  3839. {
  3840. IR::IndirOpnd *arrayLocOpnd = IR::IndirOpnd::New(restDst->AsRegOpnd(), i - formalCount, TyVar, inlinee);
  3841. IR::Instr *stElemInstr = IR::Instr::New(Js::OpCode::StElemC, arrayLocOpnd, argOutsExtra[i]->GetBytecodeArgOutCapture()->GetDst(), inlinee);
  3842. instr->InsertBefore(stElemInstr);
  3843. }
  3844. instr->Remove();
  3845. break;
  3846. }
  3847. case Js::OpCode::ArgIn_A:
  3848. formalOpnd = instr->UnlinkSrc1()->AsSymOpnd();
  3849. argIndex = formalOpnd->m_sym->AsStackSym()->GetParamSlotNum() - 1;
  3850. if (argIndex >= formalCount)
  3851. {
  3852. Fatal();
  3853. }
  3854. formalOpnd->Free(this->topFunc);
  3855. if (argOuts[argIndex])
  3856. {
  3857. IR::Instr *argOut = argOuts[argIndex];
  3858. IR::Instr* instrDef;
  3859. if (argOut->HasByteCodeArgOutCapture())
  3860. {
  3861. instrDef = argOut->GetBytecodeArgOutCapture();
  3862. }
  3863. else
  3864. {
  3865. Assert(argOut->m_opcode == Js::OpCode::ArgOut_A_FixupForStackArgs);
  3866. instrDef = argOut->GetArgOutSnapshot();
  3867. }
  3868. instr->SetSrc1(instrDef->GetDst());
  3869. instr->m_opcode = Js::OpCode::Ld_A;
  3870. IR::Opnd* dst = instr->GetDst();
  3871. IR::Opnd* src = instrDef->GetSrc1();
  3872. if (argIndex == 0)
  3873. {
  3874. // Look at the "this" argument source.
  3875. // If it's known to be a normal object (the caller has already guaranteed that, or
  3876. // it was defined by an instruction that produces normal objects), we'll omit CheckThis.
  3877. // If it's a constant value, we'll do the mapping at jit time and copy the final value.
  3878. if (src->IsRegOpnd())
  3879. {
  3880. symThis = dst->AsRegOpnd()->m_sym;
  3881. StackSym *symSrc = src->AsRegOpnd()->m_sym;
  3882. if (symSrc == symCallerThis ||
  3883. symSrc->m_isSafeThis ||
  3884. inlinee->IsInlinedConstructor())
  3885. {
  3886. fUsesSafeThis = true;
  3887. }
  3888. else if (symSrc->m_isSingleDef && symSrc->IsConst() && !symSrc->IsIntConst() && !symSrc->IsFloatConst())
  3889. {
  3890. thisConstVar = symSrc->GetConstAddress();
  3891. fUsesConstThis = true;
  3892. }
  3893. else if(fixedFunctionSafeThis)
  3894. {
  3895. // Note this need to come after we determined that this pointer is not const (undefined/null)
  3896. fUsesSafeThis = true;
  3897. }
  3898. }
  3899. }
  3900. }
  3901. else
  3902. {
  3903. instr->SetSrc1(IR::AddrOpnd::New(this->topFunc->GetScriptContext()->GetLibrary()->GetUndefined(),
  3904. IR::AddrOpndKindDynamicVar, this->topFunc, true));
  3905. instr->GetSrc1()->SetValueType(ValueType::Undefined);
  3906. instr->m_opcode = Js::OpCode::Ld_A;
  3907. }
  3908. break;
  3909. case Js::OpCode::ArgOut_A_FromStackArgs:
  3910. {
  3911. linkOpnd = instr->GetSrc2();
  3912. if(!linkOpnd->IsSymOpnd())
  3913. {
  3914. break;
  3915. }
  3916. Assert(instr->GetSrc1()->IsIntConstOpnd());
  3917. Js::ProfileId callSiteId = static_cast<Js::ProfileId>(instr->GetSrc1()->AsIntConstOpnd()->GetValue());
  3918. argInstr = linkOpnd->AsSymOpnd()->m_sym->AsStackSym()->GetInstrDef();
  3919. while(linkOpnd->IsSymOpnd())
  3920. {
  3921. argInstr = linkOpnd->AsSymOpnd()->m_sym->AsStackSym()->GetInstrDef();
  3922. linkOpnd = argInstr->GetSrc2();
  3923. }
  3924. Assert(linkOpnd->IsRegOpnd());
  3925. Js::OpCode startCallOpCode = linkOpnd->AsRegOpnd()->m_sym->AsStackSym()->GetInstrDef()->m_opcode;
  3926. Assert(startCallOpCode == Js::OpCode::StartCall);
  3927. IR::Instr* startCallForInlinee = linkOpnd->AsRegOpnd()->m_sym->AsStackSym()->GetInstrDef();
  3928. formalCountForInlinee = startCallForInlinee->GetArgOutCount(false); // As of now, StartCall has the formal count
  3929. if(actualCount < formalCountForInlinee)
  3930. {
  3931. RemoveExtraFixupArgouts(instr, formalCountForInlinee - actualCount, callSiteId);
  3932. startCallForInlinee->GetSrc1()->AsIntConstOpnd()->DecrValue(formalCountForInlinee - actualCount); //account for the extra formals
  3933. }
  3934. linkOpnd = instr->GetSrc2();
  3935. argInstr = linkOpnd->AsSymOpnd()->m_sym->AsStackSym()->GetInstrDef();
  3936. argIndex = ((actualCount < formalCountForInlinee) ? actualCount : formalCountForInlinee) - 1;
  3937. for ( ; argIndex > 0; argIndex--)
  3938. {
  3939. if(argInstr->m_opcode != Js::OpCode::ArgOut_A_FixupForStackArgs)
  3940. {
  3941. break;
  3942. }
  3943. Assert(!argInstr->HasByteCodeArgOutCapture()); // ArgOut_A_FixupForStackArgs should not be restored on bailout, so we don't generate ByteCodeArgOutCapture for these argouts.
  3944. IR::Instr* currentArgOutInstr = nullptr;
  3945. if(argOuts[argIndex])
  3946. {
  3947. currentArgOutInstr = argOuts[argIndex];
  3948. }
  3949. else if(argOutsExtra && argOutsExtra[argIndex])
  3950. {
  3951. currentArgOutInstr = argOutsExtra[argIndex];
  3952. }
  3953. if(currentArgOutInstr)
  3954. {
  3955. Assert(currentArgOutInstr->m_opcode == Js::OpCode::ArgOut_A || currentArgOutInstr->m_opcode == Js::OpCode::ArgOut_A_Inline);
  3956. IR::Instr* bytecodeArgoutCapture = currentArgOutInstr->GetBytecodeArgOutCapture();
  3957. IR::Instr* formalArgOutUse = argInstr->GetArgOutSnapshot();
  3958. Assert(formalArgOutUse->m_opcode == Js::OpCode::Ld_A);
  3959. Assert(formalArgOutUse->GetSrc1()->AsAddrOpnd()->m_address == this->topFunc->GetScriptContext()->GetLibrary()->GetUndefined());
  3960. formalArgOutUse->ReplaceSrc1(bytecodeArgoutCapture->GetDst());
  3961. linkOpnd = argInstr->GetSrc2();
  3962. argInstr = linkOpnd->AsSymOpnd()->m_sym->AsStackSym()->GetInstrDef();
  3963. }
  3964. }
  3965. if (formalCountForInlinee < actualCount)
  3966. {
  3967. FixupExtraActualParams(instr, argOuts, argOutsExtra, formalCountForInlinee, actualCount, callSiteId);
  3968. startCallForInlinee->GetSrc1()->AsIntConstOpnd()->IncrValue(actualCount - formalCountForInlinee); //account for the extra actuals
  3969. }
  3970. break;
  3971. }
  3972. case Js::OpCode::InlineeStart:
  3973. {
  3974. linkOpnd = instr->GetSrc2();
  3975. if(!linkOpnd->IsSymOpnd())
  3976. {
  3977. break;
  3978. }
  3979. IR::Instr* stackArgsInstr = linkOpnd->AsSymOpnd()->m_sym->AsStackSym()->GetInstrDef();
  3980. if (stackArgsInstr->m_opcode == Js::OpCode::ArgOut_A_FromStackArgs)
  3981. {
  3982. linkOpnd = stackArgsInstr->GetSrc2();
  3983. argInstr = linkOpnd->AsSymOpnd()->m_sym->AsStackSym()->GetInstrDef();
  3984. Assert(argInstr->m_opcode == Js::OpCode::ArgOut_A_Inline || argInstr->m_opcode == Js::OpCode::ArgOut_A_FixupForStackArgs || argInstr->m_opcode == Js::OpCode::ArgOut_A);
  3985. stackArgsInstr->Remove();
  3986. Assert(argInstr->GetDst()->IsSymOpnd());
  3987. instr->ReplaceSrc2(argInstr->GetDst());
  3988. }
  3989. break;
  3990. }
  3991. case Js::OpCode::LdEnv:
  3992. if (instr->m_func == inlinee)
  3993. {
  3994. // Need to give the inlinee's function to load the environment
  3995. if (funcObjOpnd->IsAddrOpnd())
  3996. {
  3997. instr->m_opcode = Js::OpCode::Ld_A;
  3998. instr->SetSrc1(IR::AddrOpnd::New(Js::ScriptFunction::FromVar(funcObjOpnd->AsAddrOpnd()->m_address)->GetEnvironment(),
  3999. IR::AddrOpndKindDynamicFrameDisplay, instr->m_func));
  4000. }
  4001. else
  4002. {
  4003. instr->SetSrc1(funcObjOpnd);
  4004. }
  4005. }
  4006. else
  4007. {
  4008. Assert(instr->GetSrc1() != nullptr);
  4009. }
  4010. break;
  4011. case Js::OpCode::LdNewTarget:
  4012. if (instr->m_func == inlinee)
  4013. {
  4014. if (instr->m_func->IsInlinedConstructor())
  4015. {
  4016. instr->SetSrc1(funcObjOpnd);
  4017. }
  4018. else
  4019. {
  4020. instr->SetSrc1(IR::AddrOpnd::New(this->topFunc->GetScriptContext()->GetLibrary()->GetUndefined(),
  4021. IR::AddrOpndKindDynamicVar, this->topFunc, true));
  4022. instr->GetSrc1()->SetValueType(ValueType::Undefined);
  4023. }
  4024. instr->m_opcode = Js::OpCode::Ld_A;
  4025. }
  4026. break;
  4027. case Js::OpCode::ChkNewCallFlag:
  4028. if (instr->m_func == inlinee)
  4029. {
  4030. if (instr->m_func->IsInlinedConstructor())
  4031. {
  4032. instr->Remove();
  4033. }
  4034. else
  4035. {
  4036. // InliningDecider::Inline should have decided not to inline this since we are going to end up throwing anyway
  4037. Assert(false);
  4038. }
  4039. }
  4040. break;
  4041. case Js::OpCode::LdSuper:
  4042. case Js::OpCode::LdSuperCtor:
  4043. if (instr->m_func == inlinee)
  4044. {
  4045. instr->SetSrc1(funcObjOpnd);
  4046. }
  4047. else
  4048. {
  4049. Assert(instr->GetSrc1() != nullptr);
  4050. }
  4051. break;
  4052. case Js::OpCode::LdThis:
  4053. case Js::OpCode::StrictLdThis:
  4054. // Optimization of LdThis may be possible.
  4055. // Verify that this is a use of the "this" passed by the caller (not a nested function).
  4056. if (instr->GetSrc1()->AsRegOpnd()->m_sym == symThis)
  4057. {
  4058. if (fUsesSafeThis)
  4059. {
  4060. // No need for any "this" mapping.
  4061. instrNext = this->RemoveLdThis(instr);
  4062. break;
  4063. }
  4064. else if (fUsesConstThis)
  4065. {
  4066. // "this" is a constant, so map it now.
  4067. // Don't bother mapping if it's not an object, though, since we'd have to create a
  4068. // boxed value at JIT time, and that case doesn't seem worth it.
  4069. Js::TypeId typeId = Js::JavascriptOperators::GetTypeId(thisConstVar);
  4070. if (Js::JavascriptOperators::IsObjectType(typeId) ||
  4071. Js::JavascriptOperators::IsUndefinedOrNullType(typeId))
  4072. {
  4073. Js::ScriptContext *scriptContext = inlinee->GetScriptContext();
  4074. if (instr->m_opcode == Js::OpCode::LdThis)
  4075. {
  4076. thisConstVar = Js::JavascriptOperators::OP_GetThis(
  4077. thisConstVar, instr->GetSrc2()->AsIntConstOpnd()->AsInt32(), scriptContext);
  4078. instr->FreeSrc2();
  4079. }
  4080. else
  4081. {
  4082. thisConstVar = Js::JavascriptOperators::OP_StrictGetThis(thisConstVar, scriptContext);
  4083. }
  4084. IR::Opnd *thisOpnd = IR::AddrOpnd::New(thisConstVar, IR::AddrOpndKindDynamicVar, inlinee, true);
  4085. instr->m_opcode = Js::OpCode::Ld_A;
  4086. instr->ReplaceSrc1(thisOpnd);
  4087. break;
  4088. }
  4089. }
  4090. }
  4091. // Couldn't eliminate the execution-time "this" mapping. Try to change it to a check.
  4092. instrNext = this->DoCheckThisOpt(instr);
  4093. break;
  4094. case Js::OpCode::Throw:
  4095. instr->m_opcode = Js::OpCode::InlineThrow;
  4096. instr->m_func->SetHasImplicitCallsOnSelfAndParents();
  4097. break;
  4098. case Js::OpCode::RuntimeTypeError:
  4099. instr->m_opcode = Js::OpCode::InlineRuntimeTypeError;
  4100. instr->m_func->SetHasImplicitCallsOnSelfAndParents();
  4101. break;
  4102. case Js::OpCode::RuntimeReferenceError:
  4103. instr->m_opcode = Js::OpCode::InlineRuntimeReferenceError;
  4104. instr->m_func->SetHasImplicitCallsOnSelfAndParents();
  4105. break;
  4106. case Js::OpCode::Ret:
  4107. if (!retOpnd)
  4108. {
  4109. instr->Remove();
  4110. }
  4111. else
  4112. {
  4113. instr->m_opcode = Js::OpCode::Ld_A;
  4114. instr->SetDst(retOpnd);
  4115. }
  4116. break;
  4117. }
  4118. } NEXT_INSTR_EDITING;
  4119. }
  4120. void
  4121. Inline::SetupInlineeFrame(Func *inlinee, IR::Instr *inlineeStart, Js::ArgSlot actualCount, IR::Opnd *functionObject)
  4122. {
  4123. Js::ArgSlot argSlots[Js::Constants::InlineeMetaArgCount] = {
  4124. actualCount + 1u, /* argc */
  4125. actualCount + 2u, /* function object */
  4126. actualCount + 3u /* arguments object slot */
  4127. };
  4128. IR::Opnd *srcs[Js::Constants::InlineeMetaArgCount] = {
  4129. IR::AddrOpnd::New((Js::Var)actualCount, IR::AddrOpndKindConstant, inlinee, true /*dontEncode*/),
  4130. /*
  4131. * Don't initialize this slot with the function object yet. In compat mode we evaluate
  4132. * the target only after evaluating all arguments. Having this SymOpnd here ensures it gets
  4133. * the correct slot in the frame. Lowerer fills this slot with the function object just
  4134. * before entering the inlinee when we're sure we've evaluated the target in all modes.
  4135. */
  4136. nullptr,
  4137. IR::AddrOpnd::NewNull(inlinee)
  4138. };
  4139. const IRType types[Js::Constants::InlineeMetaArgCount] = {
  4140. TyMachReg,
  4141. TyVar,
  4142. TyMachReg
  4143. };
  4144. for (unsigned instrIndex = 0; instrIndex < Js::Constants::InlineeMetaArgCount; instrIndex++)
  4145. {
  4146. StackSym *stackSym = inlinee->m_symTable->GetArgSlotSym(argSlots[instrIndex]);
  4147. stackSym->m_isInlinedArgSlot = true;
  4148. this->topFunc->SetArgOffset(stackSym, (currentInlineeFrameSlot + instrIndex) * MachPtr);
  4149. IR::SymOpnd *symOpnd = IR::SymOpnd::New(stackSym, 0, types[instrIndex], inlinee);
  4150. IR::Instr *instr = IR::Instr::New(Js::OpCode::InlineeMetaArg, inlinee);
  4151. instr->SetDst(symOpnd);
  4152. if (srcs[instrIndex])
  4153. {
  4154. instr->SetSrc1(srcs[instrIndex]);
  4155. }
  4156. inlineeStart->InsertBefore(instr);
  4157. if (instrIndex == 0)
  4158. {
  4159. inlinee->SetInlineeFrameStartSym(stackSym);
  4160. }
  4161. }
  4162. }
  4163. void
  4164. Inline::FixupExtraActualParams(IR::Instr * instr, IR::Instr *argOuts[], IR::Instr *argOutsExtra[], uint index, uint actualCount, Js::ProfileId callSiteId)
  4165. {
  4166. Assert(instr->m_opcode == Js::OpCode::ArgOut_A_FromStackArgs);
  4167. int offsetFixup;
  4168. Assert(instr->m_func->callSiteToArgumentsOffsetFixupMap->ContainsKey(callSiteId));
  4169. instr->m_func->callSiteToArgumentsOffsetFixupMap->TryGetValue(callSiteId, &offsetFixup);
  4170. StackSym *sym = instr->GetDst()->AsSymOpnd()->m_sym->AsStackSym();
  4171. while (index < actualCount)
  4172. {
  4173. IR::Instr* argOutToMapTo = argOuts[index] ? argOuts[index] : argOutsExtra[index];
  4174. StackSym* newStackSym = StackSym::NewArgSlotSym(sym->GetArgSlotNum(), instr->m_func);
  4175. newStackSym->m_isInlinedArgSlot = true;
  4176. this->topFunc->SetArgOffset(newStackSym, sym->m_offset);
  4177. sym->IncrementArgSlotNum();
  4178. this->topFunc->SetArgOffset(sym, sym->m_offset + MachPtr);
  4179. IR::SymOpnd * linkOpnd = IR::SymOpnd::New(newStackSym, sym->GetType(), instr->m_func);
  4180. IR::Instr * extraActualParamInstr = IR::Instr::New(Js::OpCode::ArgOut_A_FixupForStackArgs, linkOpnd, argOutToMapTo->GetSrc1(), instr->GetSrc2(), instr->m_func);
  4181. instr->InsertBefore(extraActualParamInstr);
  4182. extraActualParamInstr->GenerateArgOutSnapshot();
  4183. instr->m_func->callSiteToArgumentsOffsetFixupMap->Item(callSiteId, ++offsetFixup);
  4184. instr->ReplaceSrc2(extraActualParamInstr->GetDst());
  4185. index++;
  4186. }
  4187. }
  4188. void
  4189. Inline::RemoveExtraFixupArgouts(IR::Instr* instr, uint argoutRemoveCount, Js::ProfileId callSiteId)
  4190. {
  4191. Assert(instr->m_opcode == Js::OpCode::ArgOut_A_FromStackArgs);
  4192. int offsetFixup;
  4193. Assert(instr->m_func->callSiteToArgumentsOffsetFixupMap->ContainsKey(callSiteId));
  4194. instr->m_func->callSiteToArgumentsOffsetFixupMap->TryGetValue(callSiteId, &offsetFixup);
  4195. StackSym* argSym = instr->GetDst()->AsSymOpnd()->m_sym->AsStackSym();
  4196. IR::Instr* argInstr = instr->GetSrc2()->AsSymOpnd()->m_sym->AsStackSym()->GetInstrDef();
  4197. for(uint argIndex = 0; argIndex < argoutRemoveCount; argIndex++)
  4198. {
  4199. Assert(argInstr->m_opcode == Js::OpCode::ArgOut_A_FixupForStackArgs);
  4200. Assert(!argInstr->HasByteCodeArgOutCapture()); // ArgOut_A_FixupForStackArgs should not be restored on bailout, so we don't generate ByteCodeArgOutCapture for these argouts.
  4201. instr->ReplaceSrc2(argInstr->GetSrc2());
  4202. argSym->DecrementArgSlotNum();
  4203. argSym->m_offset -= MachPtr;
  4204. argSym->m_allocated = true;
  4205. argInstr->Remove();
  4206. instr->m_func->callSiteToArgumentsOffsetFixupMap->Item(callSiteId, --offsetFixup);
  4207. argInstr = instr->GetSrc2()->AsSymOpnd()->m_sym->AsStackSym()->GetInstrDef();
  4208. }
  4209. }
  4210. IR::Instr *
  4211. Inline::DoCheckThisOpt(IR::Instr * instr)
  4212. {
  4213. IR::Instr * instrNext = instr->m_next;
  4214. if (PHASE_OFF(Js::CheckThisPhase, instr->m_func->GetTopFunc()))
  4215. {
  4216. return instrNext;
  4217. }
  4218. if (!PHASE_FORCE(Js::CheckThisPhase, instr->m_func->GetTopFunc()))
  4219. {
  4220. if (!instr->m_func->HasProfileInfo())
  4221. {
  4222. return instrNext;
  4223. }
  4224. if (instr->m_func->GetProfileInfo()->GetThisInfo().thisType != Js::ThisType_Simple)
  4225. {
  4226. return instrNext;
  4227. }
  4228. if (instr->m_func->GetProfileInfo()->IsCheckThisDisabled())
  4229. {
  4230. return instrNext;
  4231. }
  4232. }
  4233. // If the instr is an inlined LdThis, try to replace it with a CheckThis
  4234. // that will bail out if a helper call is required to get the real "this" pointer.
  4235. Assert(instr->m_opcode == Js::OpCode::LdThis || instr->m_opcode == Js::OpCode::StrictLdThis);
  4236. Assert(instr->IsInlined());
  4237. // Create the CheckThis. The target is the original offset, i.e., the LdThis still has to be executed.
  4238. if(instr->m_opcode == Js::OpCode::LdThis)
  4239. {
  4240. instr->FreeSrc2();
  4241. }
  4242. IR::Instr *newInstr =
  4243. IR::BailOutInstr::New( instr->m_opcode == Js::OpCode::LdThis ? Js::OpCode::CheckThis : Js::OpCode::StrictCheckThis, IR::BailOutCheckThis, instr, instr->m_func);
  4244. // Just re-use the original src1 since the LdThis will usually be deleted.
  4245. newInstr->SetSrc1(instr->GetSrc1());
  4246. newInstr->SetByteCodeOffset(instr);
  4247. instr->InsertBefore(newInstr);
  4248. return this->RemoveLdThis(instr);
  4249. }
  4250. IR::Instr *
  4251. Inline::RemoveLdThis(IR::Instr *instr)
  4252. {
  4253. // Replace the original instr with a copy, if needed.
  4254. if (instr->GetDst()->IsEqual(instr->GetSrc1()))
  4255. {
  4256. // The copy would be a nop, so just delete.
  4257. IR::Instr *instrNext = instr->m_next;
  4258. instr->Remove();
  4259. return instrNext;
  4260. }
  4261. else
  4262. {
  4263. instr->m_opcode = Js::OpCode::Ld_A;
  4264. return instr;
  4265. }
  4266. }
  4267. bool
  4268. Inline::IsArgumentsOpnd(IR::Opnd* opnd, SymID argumentsSymId)
  4269. {
  4270. if (opnd->IsRegOpnd())
  4271. {
  4272. return argumentsSymId == opnd->AsRegOpnd()->m_sym->m_id;
  4273. }
  4274. else if (opnd->IsSymOpnd())
  4275. {
  4276. Sym *sym = opnd->AsSymOpnd()->m_sym;
  4277. if (sym && sym->IsPropertySym())
  4278. {
  4279. PropertySym *propertySym = sym->AsPropertySym();
  4280. return argumentsSymId == propertySym->m_stackSym->m_id;
  4281. }
  4282. return false;
  4283. }
  4284. else if (opnd->IsIndirOpnd())
  4285. {
  4286. IR::RegOpnd *indexOpnd = opnd->AsIndirOpnd()->GetIndexOpnd();
  4287. IR::RegOpnd *baseOpnd = opnd->AsIndirOpnd()->GetBaseOpnd();
  4288. return (argumentsSymId == baseOpnd->m_sym->m_id) || (indexOpnd && indexOpnd->m_sym->m_id == argumentsSymId);
  4289. }
  4290. AssertMsg(false, "Unknown type");
  4291. return false;
  4292. }
  4293. bool
  4294. Inline::HasArgumentsAccess(IR::Opnd *opnd, SymID argumentsSymId)
  4295. {
  4296. // We should look at dst last to correctly handle cases where it's the same as one of the src operands.
  4297. if (opnd)
  4298. {
  4299. if (opnd->IsRegOpnd() || opnd->IsSymOpnd() || opnd->IsIndirOpnd())
  4300. {
  4301. if (IsArgumentsOpnd(opnd, argumentsSymId))
  4302. {
  4303. return true;
  4304. }
  4305. }
  4306. }
  4307. return false;
  4308. }
  4309. bool
  4310. Inline::HasArgumentsAccess(IR::Instr * instr, SymID argumentsSymId)
  4311. {
  4312. IR::Opnd* dst = instr->GetDst();
  4313. IR::Opnd* src1 = instr->GetSrc1();
  4314. IR::Opnd* src2 = instr->GetSrc2();
  4315. // Super conservative here, if we see the arguments or any of its alias being used in any
  4316. // other opcode just don't do this optimization.
  4317. if (HasArgumentsAccess(src1, argumentsSymId) || HasArgumentsAccess(src2, argumentsSymId))
  4318. {
  4319. return true;
  4320. }
  4321. if (dst)
  4322. {
  4323. // For dst no need to check for RegOpnd
  4324. if (dst->IsSymOpnd() || dst->IsIndirOpnd())
  4325. {
  4326. if (IsArgumentsOpnd(dst, argumentsSymId))
  4327. {
  4328. return true;
  4329. }
  4330. }
  4331. }
  4332. return false;
  4333. }
  4334. bool
  4335. Inline::GetInlineeHasArgumentObject(Func * inlinee)
  4336. {
  4337. if (!inlinee->GetJnFunction()->GetUsesArgumentsObject())
  4338. {
  4339. // If inlinee has no arguments access return false
  4340. return false;
  4341. }
  4342. // Inlinee has arguments access
  4343. if (!inlinee->GetHasApplyTargetInlining())
  4344. {
  4345. // There is no apply target inlining (this.init.apply(this, arguments))
  4346. // So arguments access continues to exist
  4347. return true;
  4348. }
  4349. // Its possible there is no more arguments access after we inline apply target validate the same.
  4350. // This sounds expensive, but we are only walking inlinee which has apply target inlining optimization enabled.
  4351. // Also we walk only instruction in that inlinee and not nested inlinees. So it is not expensive.
  4352. SymID argumentsSymId = 0;
  4353. FOREACH_INSTR_IN_FUNC(instr, inlinee)
  4354. {
  4355. if (instr->m_func != inlinee)
  4356. {
  4357. // Skip nested inlinees
  4358. continue;
  4359. }
  4360. if (instr->m_opcode == Js::OpCode::LdHeapArguments || instr->m_opcode == Js::OpCode::LdLetHeapArguments)
  4361. {
  4362. argumentsSymId = instr->GetDst()->AsRegOpnd()->m_sym->m_id;
  4363. }
  4364. else if (argumentsSymId != 0)
  4365. {
  4366. // Once we find the arguments object i.e. argumentsSymId is set
  4367. // Make sure no one refers to it.
  4368. switch (instr->m_opcode)
  4369. {
  4370. case Js::OpCode::InlineBuiltInStart:
  4371. {
  4372. IR::Opnd* builtInOpnd = instr->GetSrc1();
  4373. if (builtInOpnd->IsAddrOpnd())
  4374. {
  4375. Assert(builtInOpnd->AsAddrOpnd()->m_isFunction);
  4376. Js::BuiltinFunction builtinFunction = Js::JavascriptLibrary::GetBuiltInForFuncInfo(((Js::JavascriptFunction*)builtInOpnd->AsAddrOpnd()->m_address)->GetFunctionInfo(), inlinee->GetScriptContext());
  4377. if (builtinFunction == Js::BuiltinFunction::Function_Apply)
  4378. {
  4379. this->SetIsInInlinedApplyCall(true);
  4380. }
  4381. }
  4382. else if (builtInOpnd->IsRegOpnd())
  4383. {
  4384. if (builtInOpnd->AsRegOpnd()->m_sym->m_builtInIndex == Js::BuiltinFunction::Function_Apply)
  4385. {
  4386. this->SetIsInInlinedApplyCall(true);
  4387. }
  4388. }
  4389. break;
  4390. }
  4391. case Js::OpCode::InlineBuiltInEnd:
  4392. {
  4393. if(this->GetIsInInlinedApplyCall())
  4394. {
  4395. this->SetIsInInlinedApplyCall(false);
  4396. }
  4397. break;
  4398. }
  4399. case Js::OpCode::BailOnNotStackArgs:
  4400. case Js::OpCode::ArgOut_A_InlineBuiltIn:
  4401. case Js::OpCode::BytecodeArgOutCapture:
  4402. case Js::OpCode::BytecodeArgOutUse:
  4403. // These are part of arguments optimization and we are fine if they access stack args.
  4404. break;
  4405. case Js::OpCode::ArgOut_A_FromStackArgs:
  4406. {
  4407. // If ArgOut_A_FromStackArgs is part of the call sequence for apply built-in inlining (as opposed to apply target inlining),
  4408. // then arguments access continues to exist.
  4409. if (this->GetIsInInlinedApplyCall() && HasArgumentsAccess(instr, argumentsSymId))
  4410. {
  4411. return true;
  4412. }
  4413. break;
  4414. }
  4415. default:
  4416. {
  4417. if (HasArgumentsAccess(instr, argumentsSymId))
  4418. {
  4419. return true;
  4420. }
  4421. }
  4422. }
  4423. }
  4424. }
  4425. NEXT_INSTR_IN_FUNC;
  4426. return false;
  4427. }
  4428. IR::Instr *
  4429. Inline::InlineSpread(IR::Instr *spreadCall)
  4430. {
  4431. Assert(Lowerer::IsSpreadCall(spreadCall));
  4432. if (spreadCall->m_func->GetJnFunction()->IsInlineSpreadDisabled()
  4433. || this->topFunc->GetJnFunction()->IsInlineSpreadDisabled())
  4434. {
  4435. return spreadCall;
  4436. }
  4437. IR::Instr *spreadIndicesInstr = Lowerer::GetLdSpreadIndicesInstr(spreadCall);
  4438. IR::Opnd *spreadIndicesOpnd = spreadIndicesInstr->GetSrc1();
  4439. Js::AuxArray<uint32>* spreadIndices = static_cast<Js::AuxArray<uint32>*>(spreadIndicesOpnd->AsAddrOpnd()->m_address);
  4440. Assert(spreadIndices->count > 0);
  4441. IR::Instr *argInstr = spreadIndicesInstr;
  4442. IR::SymOpnd *argLinkOpnd = argInstr->GetSrc2()->AsSymOpnd();
  4443. StackSym *argLinkSym = argLinkOpnd->m_sym->AsStackSym();
  4444. argInstr = argLinkSym->m_instrDef;
  4445. // We only support one spread argument for inlining.
  4446. if (argLinkSym->GetArgSlotNum() > 2)
  4447. {
  4448. return spreadCall;
  4449. }
  4450. // We are now committed to inlining spread. Remove the LdSpreadIndices instr
  4451. // and convert the spread and 'this' ArgOuts.
  4452. spreadCall->ReplaceSrc2(argLinkOpnd);
  4453. spreadIndicesInstr->Remove();
  4454. // Insert the bailout before the array ArgOut
  4455. IR::Opnd *arrayOpnd = argInstr->GetSrc1();
  4456. argInstr->m_opcode = Js::OpCode::ArgOut_A_SpreadArg;
  4457. IR::Instr *bailoutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNotSpreadable, IR::BailOutOnInlineFunction, argInstr, argInstr->m_func);
  4458. bailoutInstr->SetSrc1(arrayOpnd);
  4459. argInstr->InsertBefore(bailoutInstr);
  4460. argLinkOpnd = argInstr->GetSrc2()->AsSymOpnd();
  4461. argLinkSym = argLinkOpnd->m_sym->AsStackSym();
  4462. argInstr = argLinkSym->m_instrDef;
  4463. argInstr->m_opcode = Js::OpCode::ArgOut_A_Dynamic;
  4464. IR::RegOpnd *startCallDstOpnd = argInstr->GetSrc2()->AsRegOpnd();
  4465. argLinkSym = startCallDstOpnd->m_sym->AsStackSym();
  4466. argInstr = argLinkSym->m_instrDef;
  4467. Assert(argInstr->m_opcode == Js::OpCode::StartCall);
  4468. spreadCall->m_opcode = Js::OpCode::CallIDynamicSpread;
  4469. return spreadCall;
  4470. }
  4471. void
  4472. Inline::TryResetObjTypeSpecFldInfoOn(IR::PropertySymOpnd* propertySymOpnd)
  4473. {
  4474. // if an objTypeSpecFldInfo was created just for the purpose of polymorphic inlining but didn't get used for the same (for some reason or the other), and the polymorphic cache it was created from, wasn't equivalent,
  4475. // we should null out this info on the propertySymOpnd so that assumptions downstream around equivalent object type spec still hold.
  4476. if (propertySymOpnd)
  4477. {
  4478. propertySymOpnd->TryResetObjTypeSpecFldInfo();
  4479. }
  4480. }
  4481. void
  4482. Inline::TryDisableRuntimePolymorphicCacheOn(IR::PropertySymOpnd* propertySymOpnd)
  4483. {
  4484. if (propertySymOpnd)
  4485. {
  4486. propertySymOpnd->TryDisableRuntimePolymorphicCache();
  4487. }
  4488. }
  4489. IR::PropertySymOpnd*
  4490. Inline::GetMethodLdOpndForCallInstr(IR::Instr* callInstr)
  4491. {
  4492. IR::Opnd* methodOpnd = callInstr->GetSrc1();
  4493. if (methodOpnd->IsRegOpnd())
  4494. {
  4495. if (methodOpnd->AsRegOpnd()->m_sym->IsStackSym())
  4496. {
  4497. if (methodOpnd->AsRegOpnd()->m_sym->AsStackSym()->IsSingleDef())
  4498. {
  4499. IR::Instr* defInstr = methodOpnd->AsRegOpnd()->m_sym->AsStackSym()->GetInstrDef();
  4500. if (defInstr->GetSrc1() && defInstr->GetSrc1()->IsSymOpnd() && defInstr->GetSrc1()->AsSymOpnd()->IsPropertySymOpnd())
  4501. {
  4502. return defInstr->GetSrc1()->AsSymOpnd()->AsPropertySymOpnd();
  4503. }
  4504. return nullptr;
  4505. }
  4506. return nullptr;
  4507. }
  4508. return nullptr;
  4509. }
  4510. return nullptr;
  4511. }
  4512. #ifdef ENABLE_SIMDJS
  4513. // SIMD_JS
  4514. /*
  4515. Fixes the format of a SIMD load/store to match format expected by globOpt. Namely:
  4516. Load:
  4517. dst = Simd128LdArr arr, index
  4518. becomes
  4519. dst = Simd128LdArr [arr, indx]
  4520. Store:
  4521. t3 = EA arr
  4522. t2 = EA index, t3
  4523. t1 = EA value, t2
  4524. Simd128StArr t1
  4525. becomes
  4526. [arr, index] = Simd128StArr value
  4527. It also sets width in bytes of data to be loaded. Needed for bound check generation in GlobOpt.
  4528. */
  4529. void
  4530. Inline::Simd128FixLoadStoreInstr(Js::BuiltinFunction builtInId, IR::Instr * callInstr)
  4531. {
  4532. bool isStore = false;
  4533. callInstr->dataWidth = 0;
  4534. switch (builtInId)
  4535. {
  4536. case Js::BuiltinFunction::SIMD_Float32x4_Store:
  4537. case Js::BuiltinFunction::SIMD_Int32x4_Store:
  4538. isStore = true;
  4539. // fall through
  4540. case Js::BuiltinFunction::SIMD_Float32x4_Load:
  4541. case Js::BuiltinFunction::SIMD_Int32x4_Load:
  4542. callInstr->dataWidth = 16;
  4543. break;
  4544. case Js::BuiltinFunction::SIMD_Float32x4_Store3:
  4545. case Js::BuiltinFunction::SIMD_Int32x4_Store3:
  4546. isStore = true;
  4547. // fall through
  4548. case Js::BuiltinFunction::SIMD_Float32x4_Load3:
  4549. case Js::BuiltinFunction::SIMD_Int32x4_Load3:
  4550. callInstr->dataWidth = 12;
  4551. break;
  4552. case Js::BuiltinFunction::SIMD_Float32x4_Store2:
  4553. case Js::BuiltinFunction::SIMD_Int32x4_Store2:
  4554. isStore = true;
  4555. // fall through
  4556. case Js::BuiltinFunction::SIMD_Float32x4_Load2:
  4557. case Js::BuiltinFunction::SIMD_Int32x4_Load2:
  4558. callInstr->dataWidth = 8;
  4559. break;
  4560. case Js::BuiltinFunction::SIMD_Float32x4_Store1:
  4561. case Js::BuiltinFunction::SIMD_Int32x4_Store1:
  4562. isStore = true;
  4563. // fall through
  4564. case Js::BuiltinFunction::SIMD_Float32x4_Load1:
  4565. case Js::BuiltinFunction::SIMD_Int32x4_Load1:
  4566. callInstr->dataWidth = 4;
  4567. break;
  4568. default:
  4569. // nothing to do
  4570. return;
  4571. }
  4572. IR::IndirOpnd *indirOpnd;
  4573. if (!isStore)
  4574. {
  4575. // load
  4576. indirOpnd = IR::IndirOpnd::New(callInstr->GetSrc1()->AsRegOpnd(), callInstr->GetSrc2()->AsRegOpnd(), TyVar, callInstr->m_func);
  4577. callInstr->ReplaceSrc1(indirOpnd);
  4578. callInstr->FreeSrc2();
  4579. }
  4580. else
  4581. {
  4582. IR::Opnd *linkOpnd = callInstr->GetSrc1();
  4583. IR::Instr *eaInstr1, *eaInstr2, *eaInstr3;
  4584. IR::Opnd *value, *index, *arr;
  4585. IR::Opnd *dst = callInstr->GetDst();
  4586. eaInstr1 = linkOpnd->GetStackSym()->m_instrDef;
  4587. value = eaInstr1->GetSrc1();
  4588. linkOpnd = eaInstr1->GetSrc2();
  4589. eaInstr2 = linkOpnd->GetStackSym()->m_instrDef;
  4590. index = eaInstr2->GetSrc1();
  4591. linkOpnd = eaInstr2->GetSrc2();
  4592. eaInstr3 = linkOpnd->GetStackSym()->m_instrDef;
  4593. Assert(!eaInstr3->GetSrc2()); // end of args list
  4594. arr = eaInstr3->GetSrc1();
  4595. indirOpnd = IR::IndirOpnd::New(arr->AsRegOpnd(), index->AsRegOpnd(), TyVar, callInstr->m_func);
  4596. if (dst)
  4597. {
  4598. //Load value to be stored to dst. Store returns the value being stored.
  4599. IR::Instr * ldInstr = IR::Instr::New(Js::OpCode::Ld_A, dst, value, callInstr->m_func);
  4600. callInstr->InsertBefore(ldInstr);
  4601. //Replace dst
  4602. callInstr->ReplaceDst(indirOpnd);
  4603. }
  4604. else
  4605. {
  4606. callInstr->SetDst(indirOpnd);
  4607. }
  4608. callInstr->ReplaceSrc1(value);
  4609. // remove ea instructions
  4610. eaInstr1->Remove(); eaInstr2->Remove(); eaInstr3->Remove();
  4611. }
  4612. }
  4613. #endif