LowerMD.cpp 344 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361
  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. #include "Language\JavascriptFunctionArgIndex.h"
  7. #include "Types\DynamicObjectEnumerator.h"
  8. #include "Types\DynamicObjectSnapshotEnumerator.h"
  9. #include "Types\DynamicObjectSnapshotEnumeratorWPCache.h"
  10. #include "Library\ForInObjectEnumerator.h"
  11. const Js::OpCode LowererMD::MDUncondBranchOpcode = Js::OpCode::B;
  12. const Js::OpCode LowererMD::MDTestOpcode = Js::OpCode::TST;
  13. const Js::OpCode LowererMD::MDOrOpcode = Js::OpCode::ORR;
  14. const Js::OpCode LowererMD::MDXorOpcode = Js::OpCode::EOR;
  15. const Js::OpCode LowererMD::MDOverflowBranchOpcode = Js::OpCode::BVS;
  16. const Js::OpCode LowererMD::MDNotOverflowBranchOpcode = Js::OpCode::BVC;
  17. const Js::OpCode LowererMD::MDConvertFloat32ToFloat64Opcode = Js::OpCode::VCVTF64F32;
  18. const Js::OpCode LowererMD::MDConvertFloat64ToFloat32Opcode = Js::OpCode::VCVTF32F64;
  19. const Js::OpCode LowererMD::MDCallOpcode = Js::OpCode::Call;
  20. const Js::OpCode LowererMD::MDImulOpcode = Js::OpCode::MUL;
  21. template<typename T>
  22. inline void Swap(T& x, T& y)
  23. {
  24. T temp = x;
  25. x = y;
  26. y = temp;
  27. }
  28. // Static utility fn()
  29. //
  30. bool
  31. LowererMD::IsAssign(const IR::Instr *instr)
  32. {
  33. return (instr->m_opcode == Js::OpCode::MOV ||
  34. instr->m_opcode == Js::OpCode::VMOV ||
  35. instr->m_opcode == Js::OpCode::LDIMM ||
  36. instr->m_opcode == Js::OpCode::LDR ||
  37. instr->m_opcode == Js::OpCode::VLDR ||
  38. instr->m_opcode == Js::OpCode::VLDR32 ||
  39. instr->m_opcode == Js::OpCode::STR ||
  40. instr->m_opcode == Js::OpCode::VSTR ||
  41. instr->m_opcode == Js::OpCode::VSTR32);
  42. }
  43. ///----------------------------------------------------------------------------
  44. ///
  45. /// LowererMD::IsCall
  46. ///
  47. ///----------------------------------------------------------------------------
  48. bool
  49. LowererMD::IsCall(const IR::Instr *instr)
  50. {
  51. return (instr->m_opcode == Js::OpCode::BL ||
  52. instr->m_opcode == Js::OpCode::BLX);
  53. }
  54. ///----------------------------------------------------------------------------
  55. ///
  56. /// LowererMD::IsIndirectBranch
  57. ///
  58. ///----------------------------------------------------------------------------
  59. bool
  60. LowererMD::IsIndirectBranch(const IR::Instr *instr)
  61. {
  62. return (instr->m_opcode == Js::OpCode::BX);
  63. }
  64. ///----------------------------------------------------------------------------
  65. ///
  66. /// LowererMD::IsUnconditionalBranch
  67. ///
  68. ///----------------------------------------------------------------------------
  69. bool
  70. LowererMD::IsUnconditionalBranch(const IR::Instr *instr)
  71. {
  72. return instr->m_opcode == Js::OpCode::B;
  73. }
  74. bool
  75. LowererMD::IsReturnInstr(const IR::Instr *instr)
  76. {
  77. return instr->m_opcode == Js::OpCode::LDRRET || instr->m_opcode == Js::OpCode::RET;
  78. }
  79. ///----------------------------------------------------------------------------
  80. ///
  81. /// LowererMD::InvertBranch
  82. ///
  83. ///----------------------------------------------------------------------------
  84. void
  85. LowererMD::InvertBranch(IR::BranchInstr *branchInstr)
  86. {
  87. switch (branchInstr->m_opcode)
  88. {
  89. case Js::OpCode::BEQ:
  90. branchInstr->m_opcode = Js::OpCode::BNE;
  91. break;
  92. case Js::OpCode::BNE:
  93. branchInstr->m_opcode = Js::OpCode::BEQ;
  94. break;
  95. case Js::OpCode::BGE:
  96. branchInstr->m_opcode = Js::OpCode::BLT;
  97. break;
  98. case Js::OpCode::BGT:
  99. branchInstr->m_opcode = Js::OpCode::BLE;
  100. break;
  101. case Js::OpCode::BLT:
  102. branchInstr->m_opcode = Js::OpCode::BGE;
  103. break;
  104. case Js::OpCode::BLE:
  105. branchInstr->m_opcode = Js::OpCode::BGT;
  106. break;
  107. case Js::OpCode::BCS:
  108. branchInstr->m_opcode = Js::OpCode::BCC;
  109. break;
  110. case Js::OpCode::BCC:
  111. branchInstr->m_opcode = Js::OpCode::BCS;
  112. break;
  113. case Js::OpCode::BMI:
  114. branchInstr->m_opcode = Js::OpCode::BPL;
  115. break;
  116. case Js::OpCode::BPL:
  117. branchInstr->m_opcode = Js::OpCode::BMI;
  118. break;
  119. case Js::OpCode::BVS:
  120. branchInstr->m_opcode = Js::OpCode::BVC;
  121. break;
  122. case Js::OpCode::BVC:
  123. branchInstr->m_opcode = Js::OpCode::BVS;
  124. break;
  125. case Js::OpCode::BLS:
  126. branchInstr->m_opcode = Js::OpCode::BHI;
  127. break;
  128. case Js::OpCode::BHI:
  129. branchInstr->m_opcode = Js::OpCode::BLS;
  130. break;
  131. default:
  132. AssertMsg(UNREACHED, "B missing in InvertBranch()");
  133. }
  134. }
  135. Js::OpCode
  136. LowererMD::MDConvertFloat64ToInt32Opcode(const RoundMode roundMode)
  137. {
  138. switch (roundMode)
  139. {
  140. case RoundModeTowardZero:
  141. return Js::OpCode::VCVTS32F64;
  142. case RoundModeTowardInteger:
  143. return Js::OpCode::Nop;
  144. case RoundModeHalfToEven:
  145. return Js::OpCode::VCVTRS32F64;
  146. default:
  147. AssertMsg(0, "RoundMode has no MD mapping.");
  148. return Js::OpCode::Nop;
  149. }
  150. }
  151. // GenerateMemRef: Return an opnd that can be used to access the given address.
  152. // ARM can't encode direct accesses to physical addresses, so put the address in a register
  153. // and return an indir. (This facilitates re-use of the loaded address without having to re-load it.)
  154. IR::Opnd *
  155. LowererMD::GenerateMemRef(void *addr, IRType type, IR::Instr *instr, bool dontEncode)
  156. {
  157. IR::RegOpnd *baseOpnd = IR::RegOpnd::New(TyMachReg, this->m_func);
  158. IR::AddrOpnd *addrOpnd = IR::AddrOpnd::New(addr, IR::AddrOpndKindDynamicMisc, this->m_func, dontEncode);
  159. LowererMD::CreateAssign(baseOpnd, addrOpnd, instr);
  160. return IR::IndirOpnd::New(baseOpnd, 0, type, this->m_func);
  161. }
  162. void
  163. LowererMD::FlipHelperCallArgsOrder()
  164. {
  165. int left = 0;
  166. int right = helperCallArgsCount - 1;
  167. while (left < right)
  168. {
  169. IR::Opnd *tempOpnd = helperCallArgs[left];
  170. helperCallArgs[left] = helperCallArgs[right];
  171. helperCallArgs[right] = tempOpnd;
  172. left++;
  173. right--;
  174. }
  175. }
  176. IR::Instr *
  177. LowererMD::LowerCallHelper(IR::Instr *instrCall)
  178. {
  179. IR::Opnd *argOpnd = instrCall->UnlinkSrc2();
  180. IR::JnHelperMethod helperMethod = instrCall->GetSrc1()->AsHelperCallOpnd()->m_fnHelper;
  181. instrCall->FreeSrc1();
  182. while (argOpnd)
  183. {
  184. Assert(argOpnd->IsRegOpnd());
  185. IR::RegOpnd *regArg = argOpnd->AsRegOpnd();
  186. Assert(regArg->m_sym->m_isSingleDef);
  187. IR::Instr *instrArg = regArg->m_sym->m_instrDef;
  188. Assert(instrArg->m_opcode == Js::OpCode::ArgOut_A);
  189. this->LoadHelperArgument(instrArg, instrArg->UnlinkSrc1());
  190. regArg->Free(this->m_func);
  191. argOpnd = instrArg->GetSrc2();
  192. if (argOpnd)
  193. {
  194. instrArg->UnlinkSrc2();
  195. }
  196. instrArg->Remove();
  197. }
  198. this->m_lowerer->LoadScriptContext(instrCall);
  199. this->FlipHelperCallArgsOrder();
  200. return this->ChangeToHelperCall(instrCall, helperMethod);
  201. }
  202. // Lower a call: May be either helper or native JS call. Just set the opcode, and
  203. // put the result into the return register. (No stack adjustment required.)
  204. IR::Instr *
  205. LowererMD::LowerCall(IR::Instr * callInstr, Js::ArgSlot argCount)
  206. {
  207. IR::Instr *retInstr = callInstr;
  208. IR::Opnd *targetOpnd = callInstr->GetSrc1();
  209. AssertMsg(targetOpnd, "Call without a target?");
  210. if (targetOpnd->IsRegOpnd())
  211. {
  212. // Indirect call
  213. callInstr->m_opcode = Js::OpCode::BLX;
  214. }
  215. else
  216. {
  217. AssertMsg(targetOpnd->IsHelperCallOpnd(), "Why haven't we loaded the call target?");
  218. // Direct call
  219. //
  220. // load the address into a register because we cannot directly access more than 24 bit constants
  221. // in BL instruction. Non helper call methods will already be accessed indirectly.
  222. //
  223. // Skip this for bailout calls. The register allocator will lower that as appropriate, without affecting spill choices.
  224. if (!callInstr->HasBailOutInfo())
  225. {
  226. IR::RegOpnd *regOpnd = IR::RegOpnd::New(nullptr, RegLR, TyMachPtr, this->m_func);
  227. IR::Instr *movInstr = IR::Instr::New(Js::OpCode::LDIMM, regOpnd, callInstr->GetSrc1(), this->m_func);
  228. regOpnd->m_isCallArg = true;
  229. callInstr->UnlinkSrc1();
  230. callInstr->SetSrc1(regOpnd);
  231. callInstr->InsertBefore(movInstr);
  232. }
  233. callInstr->m_opcode = Js::OpCode::BLX;
  234. }
  235. // For the sake of the prolog/epilog, note that we're not in a leaf. (Deliberately not
  236. // overloading Func::m_isLeaf here, as that's used for other purposes.)
  237. this->m_func->m_unwindInfo.SetHasCalls(true);
  238. IR::Opnd *dstOpnd = callInstr->GetDst();
  239. if (dstOpnd)
  240. {
  241. IR::Instr * movInstr;
  242. if(dstOpnd->IsFloat64())
  243. {
  244. movInstr = callInstr->SinkDst(Js::OpCode::VMOV);
  245. callInstr->GetDst()->AsRegOpnd()->SetReg(RETURN_DBL_REG);
  246. movInstr->GetSrc1()->AsRegOpnd()->SetReg(RETURN_DBL_REG);
  247. retInstr = movInstr;
  248. }
  249. else
  250. {
  251. movInstr = callInstr->SinkDst(Js::OpCode::MOV);
  252. callInstr->GetDst()->AsRegOpnd()->SetReg(RETURN_REG);
  253. movInstr->GetSrc1()->AsRegOpnd()->SetReg(RETURN_REG);
  254. retInstr = movInstr;
  255. }
  256. }
  257. //
  258. // assign the arguments to appropriate positions
  259. //
  260. AssertMsg(this->helperCallArgsCount >= 0, "Fatal. helper call arguments ought to be positive");
  261. AssertMsg(this->helperCallArgsCount <= MaxArgumentsToHelper, "Too many helper call arguments");
  262. uint16 argsLeft = this->helperCallArgsCount;
  263. uint16 doubleArgsLeft = this->helperCallDoubleArgsCount;
  264. uint16 intArgsLeft = argsLeft - doubleArgsLeft;
  265. while(argsLeft > 0)
  266. {
  267. IR::Opnd *helperArgOpnd = this->helperCallArgs[this->helperCallArgsCount - argsLeft];
  268. IR::Opnd * opndParam = nullptr;
  269. if (helperArgOpnd->GetType() == TyMachDouble)
  270. {
  271. opndParam = this->GetOpndForArgSlot(doubleArgsLeft - 1, true);
  272. AssertMsg(opndParam->IsRegOpnd(), "NYI for other kind of operands");
  273. --doubleArgsLeft;
  274. }
  275. else
  276. {
  277. opndParam = this->GetOpndForArgSlot(intArgsLeft - 1);
  278. --intArgsLeft;
  279. }
  280. LowererMD::CreateAssign(opndParam, helperArgOpnd, callInstr);
  281. --argsLeft;
  282. }
  283. Assert(doubleArgsLeft == 0 && intArgsLeft == 0 && argsLeft == 0);
  284. // We're done with the args (if any) now, so clear the param location state.
  285. this->FinishArgLowering();
  286. return retInstr;
  287. }
  288. IR::Instr *
  289. LowererMD::LoadDynamicArgument(IR::Instr *instr, uint argNumber)
  290. {
  291. Assert(instr->m_opcode == Js::OpCode::ArgOut_A_Dynamic);
  292. Assert(instr->GetSrc2() == nullptr);
  293. IR::Opnd* dst = GetOpndForArgSlot((Js::ArgSlot) (argNumber - 1));
  294. instr->SetDst(dst);
  295. instr->m_opcode = Js::OpCode::MOV;
  296. LegalizeMD::LegalizeInstr(instr, false);
  297. return instr;
  298. }
  299. IR::Instr *
  300. LowererMD::LoadDynamicArgumentUsingLength(IR::Instr *instr)
  301. {
  302. Assert(instr->m_opcode == Js::OpCode::ArgOut_A_Dynamic);
  303. IR::RegOpnd* src2 = instr->UnlinkSrc2()->AsRegOpnd();
  304. IR::Instr *add = IR::Instr::New(Js::OpCode::SUB, IR::RegOpnd::New(TyInt32, this->m_func), src2, IR::IntConstOpnd::New(1, TyInt8, this->m_func), this->m_func);
  305. instr->InsertBefore(add);
  306. //We need store nth actuals, so stack location is after function object, callinfo & this pointer
  307. IR::RegOpnd *stackPointer = IR::RegOpnd::New(nullptr, GetRegStackPointer(), TyMachReg, this->m_func);
  308. IR::IndirOpnd *actualsLocation = IR::IndirOpnd::New(stackPointer, add->GetDst()->AsRegOpnd(), GetDefaultIndirScale(), TyMachReg, this->m_func);
  309. instr->SetDst(actualsLocation);
  310. instr->m_opcode = Js::OpCode::LDR;
  311. LegalizeMD::LegalizeInstr(instr, false);
  312. return instr;
  313. }
  314. void
  315. LowererMD::SetMaxArgSlots(Js::ArgSlot actualCount /*including this*/)
  316. {
  317. Js::ArgSlot offset = 3;//For function object & callInfo & this
  318. if (this->m_func->m_argSlotsForFunctionsCalled < (uint32) (actualCount + offset))
  319. {
  320. this->m_func->m_argSlotsForFunctionsCalled = (uint32)(actualCount + offset);
  321. }
  322. return;
  323. }
  324. IR::Instr *
  325. LowererMD::LowerCallIDynamic(IR::Instr *callInstr, IR::Instr*saveThisArgOutInstr, IR::Opnd *argsLength, ushort callFlags, IR::Instr * insertBeforeInstrForCFG)
  326. {
  327. callInstr->InsertBefore(saveThisArgOutInstr); //Move this Argout next to call;
  328. this->LoadDynamicArgument(saveThisArgOutInstr, 3); //this pointer is the 3rd argument
  329. //callInfo
  330. if (callInstr->m_func->IsInlinee())
  331. {
  332. Assert(argsLength->AsIntConstOpnd()->GetValue() == callInstr->m_func->actualCount);
  333. this->SetMaxArgSlots((Js::ArgSlot)callInstr->m_func->actualCount);
  334. }
  335. else
  336. {
  337. callInstr->InsertBefore(IR::Instr::New(Js::OpCode::ADD, argsLength, argsLength, IR::IntConstOpnd::New(1, TyInt8, this->m_func), this->m_func));
  338. this->SetMaxArgSlots(Js::InlineeCallInfo::MaxInlineeArgoutCount);
  339. }
  340. LowererMD::CreateAssign( this->GetOpndForArgSlot(1), argsLength, callInstr);
  341. IR::RegOpnd *funcObjOpnd = callInstr->UnlinkSrc1()->AsRegOpnd();
  342. GeneratePreCall(callInstr, funcObjOpnd);
  343. // functionOpnd is the first argument.
  344. IR::Opnd * opndParam = this->GetOpndForArgSlot(0);
  345. LowererMD::CreateAssign(opndParam, funcObjOpnd, callInstr);
  346. return this->LowerCall(callInstr, 0);
  347. }
  348. void
  349. LowererMD::GenerateFunctionObjectTest(IR::Instr * callInstr, IR::RegOpnd *functionObjOpnd, bool isHelper, IR::LabelInstr* continueAfterExLabel /* = nullptr */)
  350. {
  351. AssertMsg(!m_func->IsJitInDebugMode() || continueAfterExLabel, "When jit is in debug mode, continueAfterExLabel must be provided otherwise continue after exception may cause AV.");
  352. if (!functionObjOpnd->IsNotTaggedValue())
  353. {
  354. IR::Instr * insertBeforeInstr = callInstr;
  355. // Need check and error if we are calling a tagged int.
  356. if (!functionObjOpnd->IsTaggedInt())
  357. {
  358. // TST functionObjOpnd, 1
  359. IR::Instr * instr = IR::Instr::New(Js::OpCode::TST, this->m_func);
  360. instr->SetSrc1(functionObjOpnd);
  361. instr->SetSrc2(IR::IntConstOpnd::New(Js::AtomTag, TyMachReg, this->m_func));
  362. callInstr->InsertBefore(instr);
  363. // BNE $helper
  364. // B $callLabel
  365. IR::LabelInstr * helperLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  366. instr = IR::BranchInstr::New(Js::OpCode::BNE, helperLabel, this->m_func);
  367. callInstr->InsertBefore(instr);
  368. IR::LabelInstr * callLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, isHelper);
  369. instr = IR::BranchInstr::New(Js::OpCode::B, callLabel, this->m_func);
  370. callInstr->InsertBefore(instr);
  371. callInstr->InsertBefore(helperLabel);
  372. callInstr->InsertBefore(callLabel);
  373. insertBeforeInstr = callLabel;
  374. }
  375. this->m_lowerer->GenerateRuntimeError(insertBeforeInstr, JSERR_NeedFunction);
  376. if (continueAfterExLabel)
  377. {
  378. // Under debugger the RuntimeError (exception) can be ignored, generate branch right after RunTimeError instr
  379. // to jmp to a safe place (which would normally be debugger bailout check).
  380. IR::BranchInstr* continueAfterEx = IR::BranchInstr::New(LowererMD::MDUncondBranchOpcode, continueAfterExLabel, this->m_func);
  381. insertBeforeInstr->InsertBefore(continueAfterEx);
  382. }
  383. }
  384. }
  385. void LowererMD::InsertIncUInt8PreventOverflow(
  386. IR::Opnd *const dst,
  387. IR::Opnd *const src,
  388. IR::Instr *const insertBeforeInstr,
  389. IR::Instr * *const onOverflowInsertBeforeInstrRef)
  390. {
  391. Assert(dst);
  392. Assert(dst->GetType() == TyUint8);
  393. Assert(src);
  394. Assert(src->GetType() == TyUint8);
  395. Assert(insertBeforeInstr);
  396. Func *const func = insertBeforeInstr->m_func;
  397. // Generate:
  398. // add temp, src, 1
  399. // tst temp, static_cast<uint8>(-1)
  400. // beq $overflow
  401. // mov dst, temp
  402. // b $continue
  403. // $overflow:
  404. // mov dst, static_cast<uint8>(-1)
  405. // $continue:
  406. IR::LabelInstr *const overflowLabel = Lowerer::InsertLabel(false, insertBeforeInstr);
  407. // add temp, src, 1
  408. IR::RegOpnd *const tempOpnd = IR::RegOpnd::New(StackSym::New(TyUint8, func), TyUint8, func);
  409. const IR::AutoReuseOpnd autoReuseTempOpnd(tempOpnd, func);
  410. Lowerer::InsertAdd(false, tempOpnd, src, IR::IntConstOpnd::New(1, TyUint8, func, true), overflowLabel);
  411. // tst temp, 0xff
  412. // beq $overflow
  413. Lowerer::InsertTestBranch(
  414. tempOpnd,
  415. IR::IntConstOpnd::New(static_cast<uint8>(-1), TyUint8, func, true),
  416. Js::OpCode::BrEq_A,
  417. overflowLabel,
  418. overflowLabel);
  419. // mov dst, temp
  420. Lowerer::InsertMove(dst, tempOpnd, overflowLabel);
  421. const bool dstEqualsSrc = dst->IsEqual(src);
  422. if(!dstEqualsSrc || onOverflowInsertBeforeInstrRef)
  423. {
  424. // b $continue
  425. // $overflow:
  426. // mov dst, static_cast<uint8>(-1)
  427. // $continue:
  428. IR::LabelInstr *const continueLabel = Lowerer::InsertLabel(false, insertBeforeInstr);
  429. Lowerer::InsertBranch(Js::OpCode::Br, continueLabel, overflowLabel);
  430. if(!dstEqualsSrc)
  431. {
  432. Lowerer::InsertMove(dst, IR::IntConstOpnd::New(static_cast<uint8>(-1), TyUint8, func, true), continueLabel);
  433. }
  434. if(onOverflowInsertBeforeInstrRef)
  435. {
  436. *onOverflowInsertBeforeInstrRef = continueLabel;
  437. }
  438. }
  439. else
  440. {
  441. // $overflow:
  442. }
  443. }
  444. void LowererMD::InsertDecUInt8PreventOverflow(
  445. IR::Opnd *const dst,
  446. IR::Opnd *const src,
  447. IR::Instr *const insertBeforeInstr,
  448. IR::Instr * *const onOverflowInsertBeforeInstrRef)
  449. {
  450. Assert(dst);
  451. Assert(dst->GetType() == TyUint8);
  452. Assert(src);
  453. Assert(src->GetType() == TyUint8);
  454. Assert(insertBeforeInstr);
  455. Func *const func = insertBeforeInstr->m_func;
  456. // Generate:
  457. // subs temp, src, 1
  458. // bcs $overflow
  459. // mov dst, temp
  460. // b $continue
  461. // $overflow:
  462. // mov dst, 0
  463. // $continue:
  464. IR::LabelInstr *const overflowLabel = Lowerer::InsertLabel(false, insertBeforeInstr);
  465. // subs temp, src, 1
  466. IR::RegOpnd *const tempOpnd = IR::RegOpnd::New(StackSym::New(TyUint8, func), TyUint8, func);
  467. const IR::AutoReuseOpnd autoReuseTempOpnd(tempOpnd, func);
  468. Lowerer::InsertSub(true, tempOpnd, src, IR::IntConstOpnd::New(1, TyUint8, func, true), overflowLabel);
  469. // bcs $overflow
  470. Lowerer::InsertBranch(Js::OpCode::BrLt_A, true, overflowLabel, overflowLabel);
  471. // mov dst, temp
  472. Lowerer::InsertMove(dst, tempOpnd, overflowLabel);
  473. const bool dstEqualsSrc = dst->IsEqual(src);
  474. if(!dstEqualsSrc || onOverflowInsertBeforeInstrRef)
  475. {
  476. // b $continue
  477. // $overflow:
  478. // mov dst, 0
  479. // $continue:
  480. IR::LabelInstr *const continueLabel = Lowerer::InsertLabel(false, insertBeforeInstr);
  481. Lowerer::InsertBranch(Js::OpCode::Br, continueLabel, overflowLabel);
  482. if(!dstEqualsSrc)
  483. {
  484. Lowerer::InsertMove(dst, IR::IntConstOpnd::New(0, TyUint8, func, true), continueLabel);
  485. }
  486. if(onOverflowInsertBeforeInstrRef)
  487. {
  488. *onOverflowInsertBeforeInstrRef = continueLabel;
  489. }
  490. }
  491. else
  492. {
  493. // $overflow:
  494. }
  495. }
  496. IR::Instr*
  497. LowererMD::GeneratePreCall(IR::Instr * callInstr, IR::Opnd *functionObjOpnd)
  498. {
  499. IR::RegOpnd * functionTypeRegOpnd = nullptr;
  500. // For calls to fixed functions we load the function's type directly from the known (hard-coded) function object address.
  501. // For other calls, we need to load it from the function object stored in a register operand.
  502. if (functionObjOpnd->IsAddrOpnd() && functionObjOpnd->AsAddrOpnd()->m_isFunction)
  503. {
  504. functionTypeRegOpnd = this->m_lowerer->GenerateFunctionTypeFromFixedFunctionObject(callInstr, functionObjOpnd);
  505. }
  506. else if (functionObjOpnd->IsRegOpnd())
  507. {
  508. AssertMsg(functionObjOpnd->AsRegOpnd()->m_sym->IsStackSym(), "Expected call target to be stackSym");
  509. functionTypeRegOpnd = IR::RegOpnd::New(TyMachReg, this->m_func);
  510. IR::IndirOpnd* functionTypeIndirOpnd = IR::IndirOpnd::New(functionObjOpnd->AsRegOpnd(),
  511. Js::RecyclableObject::GetOffsetOfType(), TyMachReg, this->m_func);
  512. LowererMD::CreateAssign(functionTypeRegOpnd, functionTypeIndirOpnd, callInstr);
  513. }
  514. else
  515. {
  516. AssertMsg(false, "Unexpected call target operand type.");
  517. }
  518. int entryPointOffset = Js::Type::GetOffsetOfEntryPoint();
  519. IR::IndirOpnd* entryPointOpnd = IR::IndirOpnd::New(functionTypeRegOpnd, entryPointOffset, TyMachPtr, this->m_func);
  520. IR::RegOpnd * targetAddrOpnd = IR::RegOpnd::New(TyMachReg, this->m_func);
  521. IR::Instr * stackParamInsert = LowererMD::CreateAssign(targetAddrOpnd, entryPointOpnd, callInstr);
  522. // targetAddrOpnd is the address we'll call.
  523. callInstr->SetSrc1(targetAddrOpnd);
  524. return stackParamInsert;
  525. }
  526. IR::Instr *
  527. LowererMD::LowerCallI(IR::Instr * callInstr, ushort callFlags, bool isHelper, IR::Instr * insertBeforeInstrForCFG)
  528. {
  529. // Indirect call using JS calling convention:
  530. // R0 = callee func object
  531. // R1 = callinfo
  532. // R2 = arg0 ("this")
  533. // R3 = arg1
  534. // [sp] = arg2
  535. // etc.
  536. // First load the target address. Note that we want to wind up with this:
  537. // ...
  538. // [sp+4] = arg3
  539. // [sp] = arg2
  540. // load target addr from func obj
  541. // R3 = arg1
  542. // ...
  543. // R0 = func obj
  544. // BLX target addr
  545. // This way the register containing the target addr interferes with the param regs
  546. // only, not the regs we use to store params to the stack.
  547. // We're sinking the stores of stack params so that the call sequence is contiguous.
  548. // This is required by nested calls, since each call will re-use the same stack slots.
  549. // But if there is no nesting, stack params can be stored as soon as they're computed.
  550. IR::Opnd * functionObjOpnd = callInstr->UnlinkSrc1();
  551. // If this is a call for new, we already pass the function operand through NewScObject,
  552. // which checks if the function operand is a real function or not, don't need to add a check again.
  553. // If this is a call to a fixed function, we've already verified that the target is, indeed, a function.
  554. if (callInstr->m_opcode != Js::OpCode::CallIFixed && !(callFlags & Js::CallFlags_New))
  555. {
  556. IR::LabelInstr* continueAfterExLabel = Lowerer::InsertContinueAfterExceptionLabelForDebugger(m_func, callInstr, isHelper);
  557. GenerateFunctionObjectTest(callInstr, functionObjOpnd->AsRegOpnd(), isHelper, continueAfterExLabel);
  558. // TODO: Remove unreachable code if we have proved that it is a tagged in.
  559. }
  560. // Can't assert until we remove unreachable code if we have proved that it is a tagged int.
  561. // Assert((callFlags & Js::CallFlags_New) || !functionWrapOpnd->IsTaggedInt());
  562. IR::Instr * stackParamInsert = GeneratePreCall(callInstr, functionObjOpnd);
  563. // We need to get the calculated CallInfo in SimpleJit because that doesn't include any changes for stack alignment
  564. IR::IntConstOpnd *callInfo;
  565. int32 argCount = this->LowerCallArgs(callInstr, stackParamInsert, callFlags, 1, &callInfo);
  566. // functionObjOpnd is the first argument.
  567. IR::Opnd * opndParam = this->GetOpndForArgSlot(0);
  568. LowererMD::CreateAssign(opndParam, functionObjOpnd, callInstr);
  569. IR::Opnd *const finalDst = callInstr->GetDst();
  570. // Finally, lower the call instruction itself.
  571. IR::Instr* ret = this->LowerCall(callInstr, (Js::ArgSlot)argCount);
  572. IR::AutoReuseOpnd autoReuseSavedFunctionObjOpnd;
  573. if (callInstr->IsJitProfilingInstr())
  574. {
  575. Assert(callInstr->m_func->IsSimpleJit());
  576. Assert(!Js::FunctionBody::IsNewSimpleJit());
  577. if(finalDst &&
  578. finalDst->IsRegOpnd() &&
  579. functionObjOpnd->IsRegOpnd() &&
  580. finalDst->AsRegOpnd()->m_sym == functionObjOpnd->AsRegOpnd()->m_sym)
  581. {
  582. // The function object sym is going to be overwritten, so save it in a temp for profiling
  583. IR::RegOpnd *const savedFunctionObjOpnd = IR::RegOpnd::New(functionObjOpnd->GetType(), callInstr->m_func);
  584. autoReuseSavedFunctionObjOpnd.Initialize(savedFunctionObjOpnd, callInstr->m_func);
  585. Lowerer::InsertMove(savedFunctionObjOpnd, functionObjOpnd, callInstr->m_next);
  586. functionObjOpnd = savedFunctionObjOpnd;
  587. }
  588. auto instr = callInstr->AsJitProfilingInstr();
  589. ret = this->m_lowerer->GenerateCallProfiling(
  590. instr->profileId,
  591. instr->inlineCacheIndex,
  592. instr->GetDst(),
  593. functionObjOpnd,
  594. callInfo,
  595. instr->isProfiledReturnCall,
  596. callInstr,
  597. ret);
  598. }
  599. return ret;
  600. }
  601. int32
  602. LowererMD::LowerCallArgs(IR::Instr *callInstr, IR::Instr *stackParamInsert, ushort callFlags, Js::ArgSlot extraParams, IR::IntConstOpnd **callInfoOpndRef)
  603. {
  604. AssertMsg(this->helperCallArgsCount == 0, "We don't support nested helper calls yet");
  605. uint32 argCount = 0;
  606. IR::Opnd* opndParam;
  607. // Now walk the user arguments and remember the arg count.
  608. IR::Instr * argInstr = callInstr;
  609. IR::Opnd *src2Opnd = callInstr->UnlinkSrc2();
  610. while (src2Opnd->IsSymOpnd())
  611. {
  612. // Get the arg instr
  613. IR::SymOpnd * argLinkOpnd = src2Opnd->AsSymOpnd();
  614. StackSym * argLinkSym = argLinkOpnd->m_sym->AsStackSym();
  615. AssertMsg(argLinkSym->IsArgSlotSym() && argLinkSym->m_isSingleDef, "Arg tree not single def...");
  616. argLinkOpnd->Free(this->m_func);
  617. argInstr = argLinkSym->m_instrDef;
  618. // The arg sym isn't assigned a constant directly anymore
  619. argLinkSym->m_isConst = false;
  620. argLinkSym->m_isIntConst = false;
  621. argLinkSym->m_isTaggableIntConst = false;
  622. // The arg slot nums are 1-based, so subtract 1. Then add 1 for the non-user args (callinfo).
  623. auto argSlotNum = argLinkSym->GetArgSlotNum();
  624. if(argSlotNum + extraParams < argSlotNum)
  625. {
  626. Js::Throw::OutOfMemory();
  627. }
  628. opndParam = this->GetOpndForArgSlot(argSlotNum + extraParams);
  629. src2Opnd = argInstr->UnlinkSrc2();
  630. argInstr->ReplaceDst(opndParam);
  631. argInstr->Unlink();
  632. if (opndParam->IsRegOpnd())
  633. {
  634. callInstr->InsertBefore(argInstr);
  635. }
  636. else
  637. {
  638. stackParamInsert->InsertBefore(argInstr);
  639. }
  640. this->ChangeToAssign(argInstr);
  641. argCount++;
  642. }
  643. IR::RegOpnd * argLinkOpnd = src2Opnd->AsRegOpnd();
  644. StackSym *argLinkSym = argLinkOpnd->m_sym->AsStackSym();
  645. AssertMsg(!argLinkSym->IsArgSlotSym() && argLinkSym->m_isSingleDef, "Arg tree not single def...");
  646. IR::Instr *startCallInstr = argLinkSym->m_instrDef;
  647. AssertMsg(startCallInstr->m_opcode == Js::OpCode::StartCall || startCallInstr->m_opcode == Js::OpCode::LoweredStartCall, "Problem with arg chain.");
  648. AssertMsg(startCallInstr->GetArgOutCount(/*getInterpreterArgOutCount*/ false) == argCount,
  649. "ArgCount doesn't match StartCall count");
  650. // Deal with the SC.
  651. this->LowerStartCall(startCallInstr);
  652. // Second argument is the callinfo.
  653. IR::IntConstOpnd *opndCallInfo = Lowerer::MakeCallInfoConst(callFlags, argCount, m_func);
  654. if(callInfoOpndRef)
  655. {
  656. opndCallInfo->Use(m_func);
  657. *callInfoOpndRef = opndCallInfo;
  658. }
  659. opndParam = this->GetOpndForArgSlot(extraParams);
  660. LowererMD::CreateAssign(opndParam, opndCallInfo, callInstr);
  661. return argCount + 1 + extraParams; // + 1 for call flags
  662. }
  663. IR::Instr *
  664. LowererMD::LowerCallPut(IR::Instr * callInstr)
  665. {
  666. Js::ArgSlot argCount = (Js::ArgSlot)this->LowerCallArgs(callInstr, Js::CallFlags_None, 2);
  667. // load native entry point from script function
  668. IR::Opnd * functionWrapOpnd = callInstr->UnlinkSrc1();
  669. AssertMsg(functionWrapOpnd->IsRegOpnd() && functionWrapOpnd->AsRegOpnd()->m_sym->IsStackSym(),
  670. "Expected call src to be stackSym");
  671. // push function wrapper
  672. this->LoadHelperArgument(callInstr, functionWrapOpnd);
  673. this->m_lowerer->LoadScriptContext(callInstr);
  674. IR::HelperCallOpnd *helperCallOpnd = IR::HelperCallOpnd::New(IR::HelperOp_InvokePut, this->m_func);
  675. callInstr->SetSrc1(helperCallOpnd);
  676. return this->LowerCall(callInstr, argCount);
  677. }
  678. IR::Instr *
  679. LowererMD::LowerStartCall(IR::Instr * instr)
  680. {
  681. // StartCall doesn't need to generate a stack adjustment. Just delete it.
  682. instr->m_opcode = Js::OpCode::LoweredStartCall;
  683. return instr;
  684. }
  685. IR::Instr *
  686. LowererMD::LoadHelperArgument(IR::Instr * instr, IR::Opnd * opndArgValue)
  687. {
  688. // Load the given parameter into the appropriate location.
  689. // We update the current param state so we can do this work without making the caller
  690. // do the work.
  691. Assert(this->helperCallArgsCount < LowererMD::MaxArgumentsToHelper);
  692. __analysis_assume(this->helperCallArgsCount < MaxArgumentsToHelper);
  693. helperCallArgs[helperCallArgsCount++] = opndArgValue;
  694. if (opndArgValue->GetType() == TyMachDouble)
  695. {
  696. this->helperCallDoubleArgsCount++;
  697. }
  698. return instr;
  699. }
  700. void
  701. LowererMD::FinishArgLowering()
  702. {
  703. this->helperCallArgsCount = 0;
  704. this->helperCallDoubleArgsCount = 0;
  705. }
  706. IR::Opnd *
  707. LowererMD::GetOpndForArgSlot(Js::ArgSlot argSlot, bool isDoubleArgument)
  708. {
  709. IR::Opnd * opndParam = nullptr;
  710. if (!isDoubleArgument)
  711. {
  712. if (argSlot < NUM_INT_ARG_REGS)
  713. {
  714. // Return an instance of the next arg register.
  715. IR::RegOpnd *regOpnd;
  716. regOpnd = IR::RegOpnd::New(nullptr, (RegNum)(argSlot + FIRST_INT_ARG_REG), TyMachReg, this->m_func);
  717. regOpnd->m_isCallArg = true;
  718. opndParam = regOpnd;
  719. }
  720. else
  721. {
  722. // Create a stack slot reference and bump up the size of this function's outgoing param area,
  723. // if necessary.
  724. argSlot = argSlot - NUM_INT_ARG_REGS;
  725. IntConstType offset = argSlot * MachRegInt;
  726. IR::RegOpnd * spBase = IR::RegOpnd::New(nullptr, this->GetRegStackPointer(), TyMachReg, this->m_func);
  727. opndParam = IR::IndirOpnd::New(spBase, offset, TyMachReg, this->m_func);
  728. if (this->m_func->m_argSlotsForFunctionsCalled < (uint32)(argSlot + 1))
  729. {
  730. this->m_func->m_argSlotsForFunctionsCalled = argSlot + 1;
  731. }
  732. }
  733. }
  734. else
  735. {
  736. if (argSlot < MaxDoubleArgumentsToHelper)
  737. {
  738. // Return an instance of the next arg register.
  739. IR::RegOpnd *regOpnd;
  740. regOpnd = IR::RegOpnd::New(nullptr, (RegNum)(argSlot + FIRST_DOUBLE_ARG_REG), TyMachDouble, this->m_func);
  741. regOpnd->m_isCallArg = true;
  742. opndParam = regOpnd;
  743. }
  744. else
  745. {
  746. AssertMsg(false,"More than 8 double parameter passing disallowed");
  747. }
  748. }
  749. return opndParam;
  750. }
  751. IR::Instr *
  752. LowererMD::LoadDoubleHelperArgument(IR::Instr * instr, IR::Opnd * opndArg)
  753. {
  754. // Load the given parameter into the appropriate location.
  755. // We update the current param state so we can do this work without making the caller
  756. // do the work.
  757. Assert(opndArg->GetType() == TyMachDouble);
  758. return this->LoadHelperArgument(instr, opndArg);
  759. }
  760. void
  761. LowererMD::GenerateStackProbe(IR::Instr *insertInstr, bool afterProlog)
  762. {
  763. //
  764. // Generate a stack overflow check. This can be as simple as a cmp esp, const
  765. // because this function is guaranteed to be called on its base thread only.
  766. // If the check fails call ThreadContext::ProbeCurrentStack which will check again and must throw.
  767. //
  768. // LDIMM r12, ThreadContext::scriptStackLimit + frameSize //Load to register first, as this can be more than 12 bit supported in CMP
  769. // CMP sp, r12
  770. // BGT done
  771. // begin:
  772. // LDIMM r0, frameSize
  773. // LDIMM r1, scriptContext
  774. // LDIMM r2, ThreadContext::ProbeCurrentStack //MUST THROW
  775. // BLX r2 //BX r2 if the stackprobe is before prolog
  776. // done:
  777. //
  778. // For thread context with script interrupt enabled:
  779. // LDIMM r12, &ThreadContext::scriptStackLimitForCurrentThread
  780. // LDR r12, [r12]
  781. // ADD r12, frameSize
  782. // BVS $helper
  783. // CMP sp, r12
  784. // BGT done
  785. // $helper:
  786. // LDIMM r0, frameSize
  787. // LDIMM r1, scriptContext
  788. // LDIMM r2, ThreadContext::ProbeCurrentStack //MUST THROW
  789. // BLX r2 //BX r2 if the stackprobe is before prolog
  790. // done:
  791. //
  792. //m_localStackHeight for ARM contains (m_argSlotsForFunctionsCalled * MachPtr)
  793. uint32 frameSize = this->m_func->m_localStackHeight + Js::Constants::MinStackJIT;
  794. IR::RegOpnd *scratchOpnd = IR::RegOpnd::New(nullptr, SCRATCH_REG, TyMachReg, this->m_func);
  795. IR::LabelInstr *helperLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, afterProlog);
  796. IR::Instr *instr;
  797. ThreadContext *threadContext = this->m_func->GetScriptContext()->GetThreadContext();
  798. bool doInterruptProbe = threadContext->DoInterruptProbe(this->m_func->GetJnFunction());
  799. if (doInterruptProbe || !threadContext->GetIsThreadBound())
  800. {
  801. // Load the current stack limit and add the current frame allocation.
  802. {
  803. void *pLimit = threadContext->GetAddressOfStackLimitForCurrentThread();
  804. this->CreateAssign(scratchOpnd, IR::AddrOpnd::New(pLimit, IR::AddrOpndKindDynamicMisc, this->m_func), insertInstr);
  805. this->CreateAssign(scratchOpnd, IR::IndirOpnd::New(scratchOpnd, 0, TyMachReg, this->m_func), insertInstr);
  806. }
  807. if (EncoderMD::CanEncodeModConst12(frameSize))
  808. {
  809. // If the frame size is small enough, just add the constant.
  810. // Does this ever happen with the size of the MinStackJIT constant?
  811. instr = IR::Instr::New(Js::OpCode::ADDS, scratchOpnd, scratchOpnd,
  812. IR::IntConstOpnd::New(frameSize, TyMachReg, this->m_func), this->m_func);
  813. insertInstr->InsertBefore(instr);
  814. }
  815. else
  816. {
  817. // We need a second scratch reg.
  818. // If we're probing after the prolog, the reg has already been saved and will be restored.
  819. // If not, push and pop it here, knowing that we'll never throw while the stack is whacked.
  820. Assert(!afterProlog || this->m_func->m_unwindInfo.GetSavedScratchReg());
  821. BVUnit scratchBit;
  822. IR::Opnd *opnd;
  823. if (!afterProlog)
  824. {
  825. opnd = IR::IndirOpnd::New(IR::RegOpnd::New(nullptr, RegSP, TyMachReg, this->m_func), (int32)0, TyMachReg, this->m_func);
  826. instr = IR::Instr::New(Js::OpCode::PUSH, opnd, this->m_func);
  827. scratchBit.Set(RegEncode[SP_ALLOC_SCRATCH_REG]);
  828. opnd = IR::RegBVOpnd::New(scratchBit, TyMachReg, this->m_func);
  829. instr->SetSrc1(opnd);
  830. insertInstr->InsertBefore(instr);
  831. }
  832. IR::Opnd *scratchOpnd2 = IR::RegOpnd::New(nullptr, SP_ALLOC_SCRATCH_REG, TyMachReg, this->m_func);
  833. this->CreateAssign(scratchOpnd2, IR::IntConstOpnd::New(frameSize, TyMachReg, this->m_func), insertInstr);
  834. instr = IR::Instr::New(Js::OpCode::ADDS, scratchOpnd, scratchOpnd, scratchOpnd2, this->m_func);
  835. insertInstr->InsertBefore(instr);
  836. if (!afterProlog)
  837. {
  838. Assert(scratchBit.Test(RegEncode[SP_ALLOC_SCRATCH_REG]));
  839. opnd = IR::RegBVOpnd::New(scratchBit, TyMachReg, this->m_func);
  840. instr = IR::Instr::New(Js::OpCode::POP, opnd, this->m_func);
  841. opnd = IR::IndirOpnd::New(IR::RegOpnd::New(nullptr, RegSP, TyMachReg, this->m_func), (int32)0, TyMachReg, this->m_func);
  842. instr->SetSrc1(opnd);
  843. insertInstr->InsertBefore(instr);
  844. }
  845. }
  846. // If this add overflows, we have to call the helper.
  847. instr = IR::BranchInstr::New(Js::OpCode::BVS, helperLabel, this->m_func);
  848. insertInstr->InsertBefore(instr);
  849. }
  850. else
  851. {
  852. uint32 scriptStackLimit = (uint32)threadContext->GetScriptStackLimit();
  853. IR::Opnd *stackLimitOpnd = IR::IntConstOpnd::New(frameSize + scriptStackLimit, TyMachReg, this->m_func);
  854. this->CreateAssign(scratchOpnd, stackLimitOpnd, insertInstr);
  855. }
  856. IR::LabelInstr *doneLabelInstr = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, false);
  857. if (!IS_FAULTINJECT_STACK_PROBE_ON) // Do stack check fastpath only if not doing StackProbe fault injection
  858. {
  859. instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  860. instr->SetSrc1(IR::RegOpnd::New(nullptr, GetRegStackPointer(), TyMachReg, this->m_func));
  861. instr->SetSrc2(scratchOpnd);
  862. insertInstr->InsertBefore(instr);
  863. instr = IR::BranchInstr::New(Js::OpCode::BGT, doneLabelInstr, this->m_func);
  864. insertInstr->InsertBefore(instr);
  865. }
  866. insertInstr->InsertBefore(helperLabel);
  867. // Zero out the pointer to the list of stack nested funcs, since the functions won't be initialized on this path.
  868. scratchOpnd = IR::RegOpnd::New(nullptr, RegR0, TyMachReg, m_func);
  869. IR::RegOpnd *frameReg = IR::RegOpnd::New(nullptr, GetRegFramePointer(), TyMachReg, m_func);
  870. CreateAssign(scratchOpnd, IR::IntConstOpnd::New(0, TyMachReg, m_func), insertInstr);
  871. IR::Opnd *indirOpnd = IR::IndirOpnd::New(
  872. frameReg, -(int32)(Js::Constants::StackNestedFuncList * sizeof(Js::Var)), TyMachReg, m_func);
  873. CreateAssign(indirOpnd, scratchOpnd, insertInstr);
  874. IR::RegOpnd *r0Opnd = IR::RegOpnd::New(nullptr, RegR0, TyMachReg, this->m_func);
  875. this->CreateAssign(r0Opnd, IR::IntConstOpnd::New(frameSize, TyMachReg, this->m_func, true), insertInstr);
  876. IR::RegOpnd *r1Opnd = IR::RegOpnd::New(nullptr, RegR1, TyMachReg, this->m_func);
  877. this->CreateAssign(r1Opnd, this->m_lowerer->LoadScriptContextOpnd(insertInstr), insertInstr);
  878. IR::RegOpnd *r2Opnd = IR::RegOpnd::New(nullptr, RegR2, TyMachReg, m_func);
  879. this->CreateAssign(r2Opnd, IR::HelperCallOpnd::New(IR::HelperProbeCurrentStack, this->m_func), insertInstr);
  880. instr = IR::Instr::New(afterProlog? Js::OpCode::BLX : Js::OpCode::BX, this->m_func);
  881. instr->SetSrc1(r2Opnd);
  882. insertInstr->InsertBefore(instr);
  883. insertInstr->InsertBefore(doneLabelInstr);
  884. Security::InsertRandomFunctionPad(doneLabelInstr);
  885. }
  886. //
  887. // Emits the code to allocate 'size' amount of space on stack. for values smaller than PAGE_SIZE
  888. // this will just emit sub rsp,size otherwise calls _chkstk.
  889. //
  890. void
  891. LowererMD::GenerateStackAllocation(IR::Instr *instr, uint32 allocSize, uint32 probeSize)
  892. {
  893. IR::RegOpnd * spOpnd = IR::RegOpnd::New(nullptr, GetRegStackPointer(), TyMachReg, this->m_func);
  894. if (IsSmallStack(probeSize))
  895. {
  896. AssertMsg(!(allocSize & 0xFFFFF000), "Must fit in 12 bits");
  897. // Generate SUB SP, SP, stackSize
  898. IR::IntConstOpnd * stackSizeOpnd = IR::IntConstOpnd::New(allocSize, TyMachReg, this->m_func, true);
  899. IR::Instr * subInstr = IR::Instr::New(Js::OpCode::SUB, spOpnd, spOpnd, stackSizeOpnd, this->m_func);
  900. instr->InsertBefore(subInstr);
  901. return;
  902. }
  903. //__chkStk is a leaf function and hence alignment is not required.
  904. // Generate _chkstk call
  905. // LDIMM RegR4, stackSize/4 //input: r4 = the number of WORDS (word = 4 bytes) to allocate,
  906. // LDIMM RegR12, HelperCRT_chkstk
  907. // BLX RegR12
  908. // SUB SP, SP, RegR4 //output: r4 = total number of BYTES probed/allocated.
  909. //chkstk expects the stacksize argument in R4 register
  910. IR::RegOpnd *r4Opnd = IR::RegOpnd::New(nullptr, SP_ALLOC_SCRATCH_REG, TyMachReg, this->m_func);
  911. IR::RegOpnd *targetOpnd = IR::RegOpnd::New(nullptr, SCRATCH_REG, TyMachReg, this->m_func);
  912. IR::IntConstOpnd * stackSizeOpnd = IR::IntConstOpnd::New((allocSize/MachPtr), TyMachReg, this->m_func, true);
  913. IR::Instr *movInstr = IR::Instr::New(Js::OpCode::LDIMM, r4Opnd, stackSizeOpnd, this->m_func);
  914. instr->InsertBefore(movInstr);
  915. IR::Instr *movHelperAddrInstr = IR::Instr::New(Js::OpCode::LDIMM, targetOpnd, IR::HelperCallOpnd::New(IR::HelperCRT_chkstk, this->m_func), this->m_func);
  916. instr->InsertBefore(movHelperAddrInstr);
  917. IR::Instr * callInstr = IR::Instr::New(Js::OpCode::BLX, r4Opnd, targetOpnd, this->m_func);
  918. instr->InsertBefore(callInstr);
  919. // Generate SUB SP, SP, R4
  920. IR::Instr * subInstr = IR::Instr::New(Js::OpCode::SUB, spOpnd, spOpnd, r4Opnd, this->m_func);
  921. instr->InsertBefore(subInstr);
  922. }
  923. void
  924. LowererMD::GenerateStackDeallocation(IR::Instr *instr, uint32 allocSize)
  925. {
  926. IR::RegOpnd * spOpnd = IR::RegOpnd::New(nullptr, this->GetRegStackPointer(), TyMachReg, this->m_func);
  927. IR::Instr * spAdjustInstr = IR::Instr::New(Js::OpCode::ADD,
  928. spOpnd,
  929. spOpnd,
  930. IR::IntConstOpnd::New(allocSize, TyMachReg, this->m_func, true), this->m_func);
  931. instr->InsertBefore(spAdjustInstr);
  932. LegalizeMD::LegalizeInstr(spAdjustInstr, true);
  933. }
  934. //------------------------------------------------------------------------------------------
  935. //
  936. // Prologs and epilogs on ARM:
  937. //
  938. // 1. Normal non-leaf function:
  939. //
  940. // MOV r12,0 -- prepare to clear the arg obj slot (not in prolog or pdata)
  941. // $PrologStart:
  942. // PUSH {r0-r3} -- home parameters (homes only r0-r1 for global function, r2 as well for eval with "this"
  943. // PUSH {r11,lr} -- save frame pointer and return address
  944. // MOV r11,sp -- set up frame chain (r11 points to saved r11)
  945. // PUSH {r4-r10,r12} -- save non-volatile regs (only used), clear arg obj slot
  946. // VPUSH {d8-d15} -- save non-volatile double regs (only used)
  947. // SUB sp, stack -- allocate locals and arg out area
  948. // <probe stack> -- not in prolog
  949. // ...
  950. // ADD sp, stack -- deallocate locals and args
  951. // POP {r4-r10,r12} -- restore registers
  952. // POP {r11} -- restore frame pointer
  953. // LDR pc,[sp],#20 -- load return address into pc and deallocate remaining stack
  954. // $EpilogEnd:
  955. //
  956. // 2. Function with large stack
  957. //
  958. // <probe stack> -- not in prolog
  959. // MOV r12,0
  960. // $PrologStart:
  961. // <save params and regs, set up frame chain as above>
  962. // MOV r4, stack/4 -- input param to chkstk is a DWORD count
  963. // LDIMM r12, &chkstk
  964. // BLX r12
  965. // SUB sp, r4 -- byte count returned by chkstk in r4
  966. // ...
  967. // <epilog as above>
  968. //
  969. // 3. Function with try-catch-finally
  970. //
  971. // MOV r12,0
  972. // $PrologStart:
  973. // PUSH {r0-r3}
  974. // PUSH {r11,lr}
  975. // MOV r11,sp
  976. // PUSH {r4-r10,r12}
  977. // MOV r6,sp -- save pointer to the saved regs
  978. // SUB sp, locals -- allocate locals area only
  979. // MOV r7,sp -- set up locals pointer; all accesses to locals in the body are through r7
  980. // PUSH {r6} -- store the saved regs pointer on the stack
  981. // SUB sp, args -- allocate space for out args passed on stack
  982. // ...
  983. // ADD sp, args
  984. // POP {r6} -- load the saved regs pointer
  985. // MOV sp,r6 -- restore sp to the saved regs area
  986. // POP {r4-r10,r12}
  987. // POP {r11}
  988. // LDR pc,[sp],#20
  989. // $EpilogEnd:
  990. IR::Instr *
  991. LowererMD::LowerEntryInstr(IR::EntryInstr * entryInstr)
  992. {
  993. IR::Instr *insertInstr = entryInstr->m_next;
  994. BYTE regEncode;
  995. BOOL hasTry = this->m_func->HasTry();
  996. // Begin recording info for later pdata/xdata emission.
  997. UnwindInfoManager *unwindInfo = &this->m_func->m_unwindInfo;
  998. unwindInfo->Init(this->m_func);
  999. // WRT CheckAlignment:
  1000. // - The code commented out below (which seems to be copied from x86) causes a hang: it trashes LR to make the call.
  1001. // - Ideally, we could save R0-R3, L11, LR to stack (R0-R3 can potentially be trashed + make sure to keep 8 byte alignment)
  1002. // then call the HelperScrFunc_CheckAlignment which should take 1 argument:
  1003. // whether it's leaf (should be 4 byte aligned) or non-leaf function (should be 8-byte aligned),
  1004. // then restore R0-R3, R11, LR from the stack.
  1005. // - But since on ARM currently the helper doesn't do anything, let's just comment this code out.
  1006. // - On x86 there is no LR and all args go to stack, that's why similar code works fine.
  1007. //#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  1008. // if (Js::Configuration::Global.flags.IsEnabled(Js::CheckAlignmentFlag))
  1009. // {
  1010. // IR::Instr * callInstr = IR::Instr::New(Js::OpCode::Call, this->m_func);
  1011. // callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::HelperScrFunc_CheckAlignment, this->m_func));
  1012. // insertInstr->InsertBefore(callInstr);
  1013. //
  1014. // this->LowerCall(callInstr, 0);
  1015. // }
  1016. //#endif
  1017. //First calculate the local stack
  1018. if (hasTry)
  1019. {
  1020. // If there's a try in the function, then the locals area must be 8-byte-aligned. That's because
  1021. // the main function will allocate a locals area, and the try helper will allocate the same stack
  1022. // but without a locals area, and both must be 8-byte aligned. So adding the locals area can't change
  1023. // the alignment.
  1024. this->m_func->m_localStackHeight = Math::Align<int32>(this->m_func->m_localStackHeight, MachStackAlignment);
  1025. }
  1026. if (this->m_func->GetMaxInlineeArgOutCount())
  1027. {
  1028. Assert(this->m_func->HasInlinee());
  1029. // Allocate the inlined arg out stack in the locals. Allocate an additional slot so that
  1030. // we can unconditionally clear the first slot past the current frame.
  1031. this->m_func->m_localStackHeight += this->m_func->GetInlineeArgumentStackSize();
  1032. }
  1033. int32 stackAdjust = this->m_func->m_localStackHeight + (this->m_func->m_argSlotsForFunctionsCalled * MachPtr);
  1034. if (stackAdjust != 0)
  1035. {
  1036. //We might need to call ProbeStack or __chkstk hence mark this function as hasCalls
  1037. unwindInfo->SetHasCalls(true);
  1038. }
  1039. bool hasStackNestedFuncList = false;
  1040. // We need to have the same register saves in the prolog as the arm_CallEhFrame, so that we can use the same
  1041. // epilog. So always allocate a slot for the stack nested func here whether we actually do have any stack
  1042. // nested func or not
  1043. // TODO-STACK-NESTED-FUNC: May be use a different arm_CallEhFrame for when we have stack nested func?
  1044. if (this->m_func->HasAnyStackNestedFunc() || hasTry)
  1045. {
  1046. // Just force it to have calls if we have stack nested func so we have a stable
  1047. // location for the stack nested function list
  1048. hasStackNestedFuncList = true;
  1049. unwindInfo->SetHasCalls(true);
  1050. }
  1051. bool hasCalls = unwindInfo->GetHasCalls();
  1052. // Home the params. This is done to enable on-the-fly creation of the arguments object,
  1053. // Dyno bailout code, etc. For non-global functions, that means homing all the param registers
  1054. // (since we have to assume they all have valid parameters). For the global function,
  1055. // just home r0 (function object) and r1 (callinfo), which the runtime can't get by any other means.
  1056. int32 regSaveArea = 0;
  1057. BVUnit paramRegs;
  1058. int homedParamRegCount;
  1059. // Note: home all the param registers if there's a try, because that's what the try helpers do.
  1060. if (this->m_func->IsLoopBody() && !hasTry)
  1061. {
  1062. // Jitted loop body takes only one "user" param: the pointer to the local slots.
  1063. homedParamRegCount = MIN_HOMED_PARAM_REGS + 1;
  1064. Assert(homedParamRegCount <= NUM_INT_ARG_REGS);
  1065. }
  1066. else if (!hasCalls)
  1067. {
  1068. // A leaf function (no calls of any kind, including helpers) may still need its params, or, if it
  1069. // has none, may still need the function object and call info.
  1070. homedParamRegCount = MIN_HOMED_PARAM_REGS + this->m_func->GetInParamsCount();
  1071. if (homedParamRegCount > NUM_INT_ARG_REGS)
  1072. {
  1073. homedParamRegCount = NUM_INT_ARG_REGS;
  1074. }
  1075. }
  1076. else
  1077. {
  1078. homedParamRegCount = NUM_INT_ARG_REGS;
  1079. }
  1080. Assert((BYTE)homedParamRegCount == homedParamRegCount);
  1081. unwindInfo->SetHomedParamCount((BYTE)homedParamRegCount);
  1082. for (int i = 0; i < homedParamRegCount; i++)
  1083. {
  1084. RegNum reg = (RegNum)(FIRST_INT_ARG_REG + i);
  1085. paramRegs.Set(RegEncode[reg]);
  1086. regSaveArea += MachRegInt;
  1087. }
  1088. // Record used callee-saved registers. This is in the form of a fixed bitfield.
  1089. BVUnit usedRegs;
  1090. int32 fpOffsetSize = 0;
  1091. for (RegNum reg = FIRST_CALLEE_SAVED_GP_REG; reg <= LAST_CALLEE_SAVED_GP_REG; reg = (RegNum)(reg+1))
  1092. {
  1093. Assert(LinearScan::IsCalleeSaved(reg));
  1094. Assert(reg != RegLR);
  1095. // Save all the regs if there's a try, because that's what the try helpers have to do.
  1096. if (this->m_func->m_regsUsed.Test(reg) || hasTry)
  1097. {
  1098. regEncode = RegEncode[reg];
  1099. usedRegs.Set(regEncode);
  1100. unwindInfo->SetSavedReg(regEncode);
  1101. fpOffsetSize += MachRegInt;
  1102. }
  1103. }
  1104. BVUnit32 usedDoubleRegs;
  1105. short doubleRegCount = 0;
  1106. if (!hasTry)
  1107. {
  1108. for (RegNum reg = FIRST_CALLEE_SAVED_DBL_REG; reg <= LAST_CALLEE_SAVED_DBL_REG; reg = (RegNum)(reg+1))
  1109. {
  1110. Assert(LinearScan::IsCalleeSaved(reg));
  1111. if (this->m_func->m_regsUsed.Test(reg))
  1112. {
  1113. regEncode = RegEncode[reg] - RegEncode[RegD0];
  1114. usedDoubleRegs.Set(regEncode);
  1115. doubleRegCount++;
  1116. }
  1117. }
  1118. if (doubleRegCount)
  1119. {
  1120. BYTE lastDoubleReg = UnwindInfoManager::GetLastSavedReg(usedDoubleRegs.GetWord());
  1121. BYTE firstDoubleReg = UnwindInfoManager::GetFirstSavedReg(usedDoubleRegs.GetWord());
  1122. // We do want to push all the double registers in a single VPUSH instructions
  1123. // This might cause us to VPUSH some registers which are not used
  1124. // But this makes unwind & prolog simple. But if we do see this case a lot
  1125. // then consider adding multiple VPUSH
  1126. short count = lastDoubleReg - firstDoubleReg + 1;
  1127. //Register allocator can allocate a temp reg from the other end of the bit vector so that it can keep it live for longer.
  1128. //Hence count may not be equal to doubleRegCount in all scenarios. These are rare and hence its okay to use single VPUSH instruction.
  1129. //handle these scenarios for free builds
  1130. usedDoubleRegs.SetRange(firstDoubleReg, count);
  1131. doubleRegCount = count;
  1132. }
  1133. }
  1134. else
  1135. {
  1136. // Set for all the callee saved double registers
  1137. usedDoubleRegs.SetRange(RegD8-RegD0, CALLEE_SAVED_DOUBLE_REG_COUNT);
  1138. doubleRegCount = CALLEE_SAVED_DOUBLE_REG_COUNT;
  1139. }
  1140. if (doubleRegCount)
  1141. {
  1142. unwindInfo->SetDoubleSavedRegList(usedDoubleRegs.GetWord());
  1143. fpOffsetSize += (doubleRegCount * MachRegDouble);
  1144. //When there is try-catch we allocate registers even if there are no calls. For scenarios see Win8 487030.
  1145. //This seems to be overkill but consistent with int registers.
  1146. AssertMsg(hasCalls || hasTry, "Assigned double registers without any calls?");
  1147. //Anyway handle it for free builds
  1148. if (!hasCalls)
  1149. {
  1150. this->m_func->m_unwindInfo.SetHasCalls(true);
  1151. hasCalls = true;
  1152. }
  1153. }
  1154. regSaveArea += fpOffsetSize;
  1155. if (hasTry)
  1156. {
  1157. // Account for the saved SP on the stack.
  1158. regSaveArea += MachRegInt;
  1159. }
  1160. this->m_func->m_ArgumentsOffset = fpOffsetSize;
  1161. if (hasStackNestedFuncList)
  1162. {
  1163. // use r11 it allocate one more slot in the register save area
  1164. // We will zero it later
  1165. regEncode = RegEncode[RegR11];
  1166. usedRegs.Set(regEncode);
  1167. unwindInfo->SetSavedReg(regEncode);
  1168. regSaveArea += MachRegInt;
  1169. fpOffsetSize += MachRegInt;
  1170. this->m_func->m_ArgumentsOffset += MachRegInt;
  1171. }
  1172. // NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
  1173. //
  1174. // If you change this->m_func->m_localStackHeight after the following code you MUST take that
  1175. // into account below. Otherwise, the stack will become unbalanced or corrupted.
  1176. //
  1177. // NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
  1178. ThreadContext *threadContext = this->m_func->GetScriptContext()->GetThreadContext();
  1179. DWORD stackProbeStackHeight = this->m_func->m_localStackHeight;
  1180. // If we've already got calls and we don't have a try, we need to take adjustments
  1181. // below into account to determine whether our not our final stack height is going to be
  1182. // encodable. We're not going to take into account the adjustment for saving R4, because we're
  1183. // trying to figure out if we will be able to encode if we DON'T save it. If we save it anyway,
  1184. // the point is moot.
  1185. if (hasCalls && !hasTry)
  1186. {
  1187. int32 bytesOnStack = stackAdjust + regSaveArea + 3 * MachRegInt;
  1188. int32 alignPad = Math::Align<int32>(bytesOnStack, MachStackAlignment) - bytesOnStack;
  1189. if (alignPad)
  1190. {
  1191. stackProbeStackHeight += alignPad;
  1192. }
  1193. }
  1194. bool useDynamicStackProbe =
  1195. (threadContext->DoInterruptProbe(this->m_func->GetJnFunction()) || !threadContext->GetIsThreadBound()) &&
  1196. !EncoderMD::CanEncodeModConst12(stackProbeStackHeight + Js::Constants::MinStackJIT);
  1197. if (useDynamicStackProbe && !hasCalls)
  1198. {
  1199. this->m_func->m_unwindInfo.SetHasCalls(true);
  1200. hasCalls = true;
  1201. }
  1202. if (hasCalls)
  1203. {
  1204. //If we need a dedicated arguments slot we mark R12 as the save register.
  1205. //This is to imitate PUSH 0 to arguments slot.
  1206. regEncode = RegEncode[SCRATCH_REG];
  1207. usedRegs.Set(regEncode);
  1208. unwindInfo->SetSavedReg(regEncode);
  1209. //Update register save area and offset to actual in params
  1210. //account for r12 push - MachRegInt
  1211. //account for frame register setup push {r11,lr} - 2 * MachRegInt
  1212. regSaveArea += 3 * MachRegInt;
  1213. this->m_func->m_ArgumentsOffset += 3 * MachRegInt;
  1214. //Note: Separate push instruction is generated for r11 & lr push and hence usedRegs mask is not updated with
  1215. //bit mask for these registers.
  1216. if (!IsSmallStack(stackAdjust) || useDynamicStackProbe)
  1217. {
  1218. unwindInfo->SetSavedScratchReg(true);
  1219. if (!usedRegs.Test(RegEncode[SP_ALLOC_SCRATCH_REG])) //If its a large stack and RegR4 is not already saved.
  1220. {
  1221. // If it is not a small stack we have to call __chkstk.
  1222. // __chkstk has special calling convention and trashes R4
  1223. // And if we're probing the stack dynamically, we need an extra reg to do the frame size calculation.
  1224. //
  1225. // Note that it's possible that we now no longer need a dynamic stack probe because
  1226. // m_localStackHeight may be encodable in Mod12. However, this is a chicken-and-egg
  1227. // problem, so we're going to stick with saving R4 even though it's possible it
  1228. // won't get modified.
  1229. usedRegs.Set(RegEncode[SP_ALLOC_SCRATCH_REG]);
  1230. regSaveArea += MachRegInt;
  1231. fpOffsetSize += MachRegInt;
  1232. this->m_func->m_ArgumentsOffset += MachRegInt;
  1233. unwindInfo->SetSavedReg(RegEncode[SP_ALLOC_SCRATCH_REG]);
  1234. }
  1235. }
  1236. // Frame size is local var area plus stack arg area, 8-byte-aligned (if we're in a non-leaf).
  1237. int32 bytesOnStack = stackAdjust + regSaveArea;
  1238. int32 alignPad = Math::Align<int32>(bytesOnStack, MachStackAlignment) - bytesOnStack;
  1239. if (alignPad)
  1240. {
  1241. stackAdjust += alignPad;
  1242. if (hasTry)
  1243. {
  1244. // We have to align the arg area, since the helper won't allocate a locals area.
  1245. Assert(alignPad % MachRegInt == 0);
  1246. this->m_func->m_argSlotsForFunctionsCalled += alignPad / MachRegInt;
  1247. }
  1248. else
  1249. {
  1250. // Treat the alignment pad as part of the locals area, which will put it as far from SP as possible.
  1251. // Note that we've already handled the change to the stack height above in checking
  1252. // for dynamic probes.
  1253. this->m_func->m_localStackHeight += alignPad;
  1254. }
  1255. }
  1256. }
  1257. Assert(fpOffsetSize >= 0);
  1258. if (this->m_func->GetMaxInlineeArgOutCount())
  1259. {
  1260. // subtracting 2 for frame pointer & return address
  1261. this->m_func->m_workItem->GetFunctionBody()->SetFrameHeight(this->m_func->m_workItem->GetEntryPoint(),
  1262. this->m_func->m_localStackHeight + this->m_func->m_ArgumentsOffset - 2 * MachRegInt);
  1263. }
  1264. //Generate StackProbe for large stack's first even before register push
  1265. bool fStackProbeAfterProlog = IsSmallStack(stackAdjust);
  1266. if (!fStackProbeAfterProlog)
  1267. {
  1268. GenerateStackProbe(insertInstr, false); //stack is already aligned in this case
  1269. }
  1270. IR::RegOpnd * r12Opnd = nullptr;
  1271. // Zero-initialize dedicated arguments slot
  1272. if (hasCalls)
  1273. {
  1274. //R12 acts a dummy zero register which we push to arguments slot
  1275. //mov r12, 0
  1276. Assert(r12Opnd == nullptr);
  1277. IR::RegOpnd* r12Opnd = IR::RegOpnd::New(nullptr, SCRATCH_REG, TyMachReg, this->m_func);
  1278. IR::Instr * instrMov = IR::Instr::New(Js::OpCode::MOV, r12Opnd, IR::IntConstOpnd::New(0, TyMachReg, this->m_func), this->m_func);
  1279. insertInstr->InsertBefore(instrMov);
  1280. IR::LabelInstr *prologStartLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  1281. insertInstr->InsertBefore(prologStartLabel);
  1282. this->m_func->m_unwindInfo.SetPrologStartLabel(prologStartLabel->m_id);
  1283. }
  1284. if (!paramRegs.IsEmpty())
  1285. {
  1286. // Generate PUSH {r0-r3}
  1287. IR::Instr * instrPush = IR::Instr::New(Js::OpCode::PUSH, this->m_func);
  1288. instrPush->SetDst(IR::IndirOpnd::New(IR::RegOpnd::New(nullptr, RegSP, TyMachReg, this->m_func), (int32)0, TyMachReg, this->m_func));
  1289. instrPush->SetSrc1(IR::RegBVOpnd::New(paramRegs, TyMachReg, this->m_func));
  1290. insertInstr->InsertBefore(instrPush);
  1291. }
  1292. // Setup Frame pointer
  1293. if (hasCalls)
  1294. {
  1295. BVUnit frameRegs;
  1296. frameRegs.Set(RegEncode[RegR11]);
  1297. frameRegs.Set(RegEncode[RegLR]);
  1298. // Generate PUSH {r11,lr}
  1299. IR::Instr * instrPush = IR::Instr::New(Js::OpCode::PUSH, this->m_func);
  1300. instrPush->SetDst(IR::IndirOpnd::New(IR::RegOpnd::New(nullptr, RegSP, TyMachReg, this->m_func), (int32)0, TyMachReg, this->m_func));
  1301. instrPush->SetSrc1(IR::RegBVOpnd::New(frameRegs, TyMachReg, this->m_func));
  1302. insertInstr->InsertBefore(instrPush);
  1303. // Generate MOV r11,sp
  1304. IR::RegOpnd* spOpnd = IR::RegOpnd::New(nullptr, RegSP, TyMachReg, this->m_func);
  1305. IR::RegOpnd* r11Opnd = IR::RegOpnd::New(nullptr, RegR11, TyMachReg, this->m_func);
  1306. IR::Instr * instrMov = IR::Instr::New(Js::OpCode::MOV, r11Opnd, spOpnd, this->m_func);
  1307. insertInstr->InsertBefore(instrMov);
  1308. }
  1309. if (!usedRegs.IsEmpty())
  1310. {
  1311. // Generate PUSH {r4-r10,r12}
  1312. IR::Instr * instrPush = IR::Instr::New(Js::OpCode::PUSH, this->m_func);
  1313. instrPush->SetDst(IR::IndirOpnd::New(IR::RegOpnd::New(nullptr, RegSP, TyMachReg, this->m_func), (int32)0, TyMachReg, this->m_func));
  1314. instrPush->SetSrc1(IR::RegBVOpnd::New(usedRegs, TyMachReg, this->m_func));
  1315. insertInstr->InsertBefore(instrPush);
  1316. }
  1317. if (!usedDoubleRegs.IsEmpty())
  1318. {
  1319. // Generate VPUSH {d8-d15}
  1320. IR::Instr * instrPush = IR::Instr::New(Js::OpCode::VPUSH, this->m_func);
  1321. instrPush->SetDst(IR::IndirOpnd::New(IR::RegOpnd::New(nullptr, RegSP, TyMachReg, this->m_func), (int32)0, TyMachReg, this->m_func));
  1322. instrPush->SetSrc1(IR::RegBVOpnd::New(usedDoubleRegs, TyMachReg, this->m_func));
  1323. insertInstr->InsertBefore(instrPush);
  1324. }
  1325. if (hasTry)
  1326. {
  1327. // Copy the value of SP before we allocate the locals area. We'll save this value on the stack below.
  1328. LowererMD::CreateAssign(
  1329. IR::RegOpnd::New(nullptr, EH_STACK_SAVE_REG, TyMachReg, this->m_func),
  1330. IR::RegOpnd::New(nullptr, RegSP, TyMachReg, this->m_func),
  1331. insertInstr);
  1332. }
  1333. uint32 probeSize = stackAdjust;
  1334. RegNum localsReg = this->m_func->GetLocalsPointer();
  1335. if (localsReg != RegSP)
  1336. {
  1337. // Allocate just the locals area first and let the locals pointer point to it.
  1338. // This may or may not generate a chkstk.
  1339. uint32 localsSize = this->m_func->m_localStackHeight;
  1340. if (localsSize != 0)
  1341. {
  1342. GenerateStackAllocation(insertInstr, localsSize, localsSize);
  1343. stackAdjust -= localsSize;
  1344. if (!IsSmallStack(localsSize))
  1345. {
  1346. // The first alloc generated a chkstk, so we only have to probe (again) if the remaining
  1347. // allocation also exceeds a page.
  1348. probeSize = stackAdjust;
  1349. }
  1350. }
  1351. // Set up the locals pointer.
  1352. LowererMD::CreateAssign(
  1353. IR::RegOpnd::New(nullptr, localsReg, TyMachReg, this->m_func),
  1354. IR::RegOpnd::New(nullptr, RegSP, TyMachReg, this->m_func),
  1355. insertInstr);
  1356. }
  1357. if (hasTry)
  1358. {
  1359. // Now push the reg we used above to save the address of the top of the locals area.
  1360. BVUnit ehReg;
  1361. ehReg.Set(RegEncode[EH_STACK_SAVE_REG]);
  1362. IR::Instr * instrPush =
  1363. IR::Instr::New(
  1364. Js::OpCode::PUSH,
  1365. IR::IndirOpnd::New(
  1366. IR::RegOpnd::New(nullptr, RegSP, TyMachReg, this->m_func), (int32)0, TyMachReg, this->m_func),
  1367. IR::RegBVOpnd::New(ehReg, TyMachReg, this->m_func),
  1368. this->m_func);
  1369. insertInstr->InsertBefore(instrPush);
  1370. }
  1371. // If the stack size is less than a page allocate the stack first & then do the stack probe
  1372. // stack limit has a buffer of StackOverflowHandlingBufferPages pages and we are okay here
  1373. if (stackAdjust != 0)
  1374. {
  1375. GenerateStackAllocation(insertInstr, stackAdjust, probeSize);
  1376. }
  1377. //As we have already allocated the stack here, we can safely zero out the inlinee argout slot.
  1378. // Zero initialize the first inlinee frames argc.
  1379. if (this->m_func->GetMaxInlineeArgOutCount())
  1380. {
  1381. // This is done post prolog. so we don't have to emit unwind data.
  1382. if (r12Opnd == nullptr)
  1383. {
  1384. r12Opnd = IR::RegOpnd::New(nullptr, SCRATCH_REG, TyMachReg, this->m_func);
  1385. // mov r12, 0
  1386. IR::Instr * instrMov = IR::Instr::New(Js::OpCode::MOV, r12Opnd, IR::IntConstOpnd::New(0, TyMachReg, this->m_func), this->m_func);
  1387. insertInstr->InsertBefore(instrMov);
  1388. }
  1389. // STR argc, r12
  1390. StackSym *sym = this->m_func->m_symTable->GetArgSlotSym((Js::ArgSlot)-1);
  1391. sym->m_isInlinedArgSlot = true;
  1392. sym->m_offset = 0;
  1393. IR::Opnd *dst = IR::SymOpnd::New(sym, 0, TyMachReg, this->m_func);
  1394. insertInstr->InsertBefore(IR::Instr::New(Js::OpCode::STR,
  1395. dst,
  1396. r12Opnd,
  1397. this->m_func));
  1398. }
  1399. // Now do the stack probe for small stacks
  1400. // hasCalls catches the recursion case
  1401. if ((stackAdjust != 0 || hasCalls) && fStackProbeAfterProlog)
  1402. {
  1403. GenerateStackProbe(insertInstr, true); //stack is already aligned in this case
  1404. }
  1405. return entryInstr;
  1406. }
  1407. IR::Instr *
  1408. LowererMD::LowerExitInstr(IR::ExitInstr * exitInstr)
  1409. {
  1410. // add sp, sp, #local stack space
  1411. // vpop {d8-d15} //restore callee saved double registers.
  1412. // pop {r4-r10, r12} //restore callee saved registers.
  1413. // pop r11 // restore r11 chain.
  1414. // ldr pc, [sp], #offset //homed arguments + 1 for lr
  1415. // See how many params were homed. We don't need to restore the values, just recover the stack space.
  1416. int32 homedParams = this->m_func->m_unwindInfo.GetHomedParamCount();
  1417. BOOL hasTry = this->m_func->HasTry();
  1418. RegNum localsReg = this->m_func->GetLocalsPointer();
  1419. int32 stackAdjust;
  1420. if (hasTry)
  1421. {
  1422. if (this->m_func->DoOptimizeTryCatch())
  1423. {
  1424. this->EnsureEpilogLabel();
  1425. }
  1426. // We'll only deallocate the arg out area then restore SP from the value saved on the stack.
  1427. stackAdjust = (this->m_func->m_argSlotsForFunctionsCalled * MachRegInt);
  1428. }
  1429. else if (localsReg != RegSP)
  1430. {
  1431. // We're going to restore SP from the locals pointer and then deallocate only the locals area.
  1432. LowererMD::CreateAssign(
  1433. IR::RegOpnd::New(nullptr, RegSP, TyMachReg, this->m_func),
  1434. IR::RegOpnd::New(nullptr, localsReg, TyMachReg, this->m_func),
  1435. exitInstr);
  1436. stackAdjust = this->m_func->m_localStackHeight;
  1437. }
  1438. else
  1439. {
  1440. // We're going to deallocate the locals and out arg area at once.
  1441. stackAdjust = (this->m_func->m_argSlotsForFunctionsCalled * MachRegInt) + this->m_func->m_localStackHeight;
  1442. }
  1443. // Record used callee-saved registers. This is in the form of a fixed bitfield.
  1444. BVUnit32 usedRegs;
  1445. for (RegNum reg = FIRST_CALLEE_SAVED_GP_REG; reg <= LAST_CALLEE_SAVED_GP_REG; reg = (RegNum)(reg+1))
  1446. {
  1447. Assert(LinearScan::IsCalleeSaved(reg));
  1448. if (this->m_func->m_regsUsed.Test(reg) || hasTry)
  1449. {
  1450. usedRegs.Set(RegEncode[reg]);
  1451. }
  1452. }
  1453. // We need to have the same register saves in the prolog as the arm_CallEhFrame, so that we can use the same
  1454. // epilog. So always allocate a slot for the stack nested func here whether we actually do have any stack
  1455. // nested func or not
  1456. // TODO-STACK-NESTED-FUNC: May be use a different arm_CallEhFrame for when we have stack nested func?
  1457. if (this->m_func->HasAnyStackNestedFunc() || hasTry)
  1458. {
  1459. usedRegs.Set(RegEncode[RegR11]);
  1460. }
  1461. bool hasCalls = this->m_func->m_unwindInfo.GetHasCalls();
  1462. if (hasCalls)
  1463. {
  1464. // __chkstk has special calling convention and uses R4, and dynamic stack probe on large frames use it too
  1465. if (this->m_func->m_unwindInfo.GetSavedScratchReg())
  1466. {
  1467. usedRegs.Set(RegEncode[SP_ALLOC_SCRATCH_REG]);
  1468. }
  1469. //RegR12 acts a dummy register to deallocate stack allocated for arguments object
  1470. usedRegs.Set(RegEncode[SCRATCH_REG]);
  1471. }
  1472. else if (usedRegs.IsEmpty())
  1473. {
  1474. stackAdjust += homedParams * MachRegInt;
  1475. }
  1476. // 1. Deallocate the stack. In the case of a leaf function with no saved registers, let this
  1477. // deallocation also account for the homed params.
  1478. if (stackAdjust != 0)
  1479. {
  1480. GenerateStackDeallocation(exitInstr, stackAdjust);
  1481. }
  1482. // This is the stack size that the pdata cares about.
  1483. this->m_func->m_unwindInfo.SetStackDepth(stackAdjust);
  1484. if (hasTry)
  1485. {
  1486. // Now restore the locals area by popping the stack.
  1487. BVUnit ehReg;
  1488. ehReg.Set(RegEncode[EH_STACK_SAVE_REG]);
  1489. IR::Instr * instrPop = IR::Instr::New(
  1490. Js::OpCode::POP,
  1491. IR::RegBVOpnd::New(ehReg, TyMachReg, this->m_func),
  1492. IR::IndirOpnd::New(
  1493. IR::RegOpnd::New(nullptr, RegSP, TyMachReg, this->m_func), (int32)0, TyMachReg, this->m_func),
  1494. this->m_func);
  1495. exitInstr->InsertBefore(instrPop);
  1496. LowererMD::CreateAssign(
  1497. IR::RegOpnd::New(nullptr, RegSP, TyMachReg, this->m_func),
  1498. IR::RegOpnd::New(nullptr, EH_STACK_SAVE_REG, TyMachReg, this->m_func),
  1499. exitInstr);
  1500. }
  1501. // 2. Restore saved double registers. Generate vpop {d8-d15}
  1502. BVUnit32 savedDoubleRegs(this->m_func->m_unwindInfo.GetDoubleSavedRegList());
  1503. if (!savedDoubleRegs.IsEmpty())
  1504. {
  1505. IR::Instr * instrVPop = IR::Instr::New(Js::OpCode::VPOP, this->m_func);
  1506. instrVPop->SetDst(IR::RegBVOpnd::New(savedDoubleRegs, TyMachReg, this->m_func));
  1507. instrVPop->SetSrc1(IR::IndirOpnd::New(IR::RegOpnd::New(nullptr, RegSP,TyMachReg, this->m_func), (int32)0, TyMachReg, this->m_func));
  1508. exitInstr->InsertBefore(instrVPop);
  1509. }
  1510. // 3. Restore saved registers. Generate pop {r4-r10,r12}
  1511. if (!usedRegs.IsEmpty())
  1512. {
  1513. IR::Instr * instrPop = IR::Instr::New(Js::OpCode::POP, this->m_func);
  1514. instrPop->SetDst(IR::RegBVOpnd::New(usedRegs, TyMachReg, this->m_func));
  1515. instrPop->SetSrc1(IR::IndirOpnd::New(IR::RegOpnd::New(nullptr, RegSP,TyMachReg, this->m_func), (int32)0, TyMachReg, this->m_func));
  1516. exitInstr->InsertBefore(instrPop);
  1517. }
  1518. if (!hasCalls)
  1519. {
  1520. if (!usedRegs.IsEmpty())
  1521. {
  1522. // We do need to deallocate the area allocated when we homed the params (since we weren't able to fold
  1523. // it into the first stack deallocation).
  1524. // TODO: Consider folding this into the LDM instruction above by having it restore dummy registers.
  1525. IR::RegOpnd * spOpnd = IR::RegOpnd::New(nullptr, this->GetRegStackPointer(), TyMachReg, this->m_func);
  1526. IR::IntConstOpnd * adjustOpnd = IR::IntConstOpnd::New(homedParams * MachRegInt, TyMachReg, this->m_func, true);
  1527. IR::Instr * spAdjustInstr = IR::Instr::New(Js::OpCode::ADD, spOpnd, spOpnd, adjustOpnd, this->m_func);
  1528. exitInstr->InsertBefore(spAdjustInstr);
  1529. }
  1530. // LR is still valid, so return by branching to it.
  1531. IR::Instr * instrRet = IR::Instr::New(
  1532. Js::OpCode::RET,
  1533. IR::RegOpnd::New(nullptr, RegPC, TyMachReg, this->m_func),
  1534. IR::RegOpnd::New(nullptr, RegLR, TyMachReg, this->m_func),
  1535. this->m_func);
  1536. exitInstr->InsertBefore(instrRet);
  1537. }
  1538. else
  1539. {
  1540. // 3. Set up original frame pointer - pop r11
  1541. usedRegs.ClearAll();
  1542. usedRegs.Set(RegEncode[RegR11]);
  1543. IR::Instr * instrPop = IR::Instr::New(Js::OpCode::POP, this->m_func);
  1544. instrPop->SetDst(IR::RegBVOpnd::New(usedRegs, TyMachReg, this->m_func));
  1545. instrPop->SetSrc1(IR::IndirOpnd::New(IR::RegOpnd::New(nullptr, RegSP,TyMachReg, this->m_func), (int32)0, TyMachReg, this->m_func));
  1546. exitInstr->InsertBefore(instrPop);
  1547. // 4. Deallocate homed param area (if necessary) and return.
  1548. // SP now points to the location where we saved LR.
  1549. // So return by doing a LDR pc,[sp],#n, where the postincrement of SP deallocates what remains of the stack.
  1550. // Note: the offset on this indir indicates the postincrement, which is the homed param area plus the size
  1551. // of LR itself.
  1552. IR::IndirOpnd * spIndir = IR::IndirOpnd::New(
  1553. IR::RegOpnd::New(nullptr, RegSP, TyMachReg, this->m_func),
  1554. (homedParams + 1) * MachRegInt,
  1555. TyMachPtr, this->m_func);
  1556. IR::Instr * instrRet = IR::Instr::New(
  1557. Js::OpCode::LDRRET,
  1558. IR::RegOpnd::New(nullptr, RegPC, TyMachReg, this->m_func),
  1559. spIndir,
  1560. this->m_func);
  1561. exitInstr->InsertBefore(instrRet);
  1562. }
  1563. IR::LabelInstr *epilogEndLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  1564. exitInstr->InsertBefore(epilogEndLabel);
  1565. this->m_func->m_unwindInfo.SetEpilogEndLabel(epilogEndLabel->m_id);
  1566. return exitInstr;
  1567. }
  1568. IR::Instr *
  1569. LowererMD::LoadNewScObjFirstArg(IR::Instr * instr, IR::Opnd * argSrc, ushort extraArgs)
  1570. {
  1571. // Spread moves down the argument slot by one.
  1572. // LowerCallArgs will handle the extraArgs. We only need to specify the argument number
  1573. // i.e 1 and not + extraArgs as done in AMD64
  1574. IR::SymOpnd *argOpnd = IR::SymOpnd::New(this->m_func->m_symTable->GetArgSlotSym(1), TyVar, this->m_func);
  1575. IR::Instr *argInstr = IR::Instr::New(Js::OpCode::ArgOut_A, argOpnd, argSrc, this->m_func);
  1576. instr->InsertBefore(argInstr);
  1577. // Insert the argument into the arg chain.
  1578. if (m_lowerer->IsSpreadCall(instr))
  1579. {
  1580. // Spread calls need LdSpreadIndices as the last arg in the arg chain.
  1581. instr = m_lowerer->GetLdSpreadIndicesInstr(instr);
  1582. }
  1583. IR::Opnd *linkOpnd = instr->UnlinkSrc2();
  1584. argInstr->SetSrc2(linkOpnd);
  1585. instr->SetSrc2(argOpnd);
  1586. return argInstr;
  1587. }
  1588. IR::Instr *
  1589. LowererMD::LowerTry(IR::Instr * tryInstr, IR::JnHelperMethod helperMethod)
  1590. {
  1591. // Mark the entry to the try
  1592. IR::Instr * instr = tryInstr->GetNextRealInstrOrLabel();
  1593. AssertMsg(instr->IsLabelInstr(), "No label at the entry to a try?");
  1594. IR::LabelInstr * tryAddr = instr->AsLabelInstr();
  1595. // Arg 7: ScriptContext
  1596. this->m_lowerer->LoadScriptContext(tryAddr);
  1597. if (tryInstr->m_opcode == Js::OpCode::TryCatch)
  1598. {
  1599. // Arg 6 : hasBailedOutOffset
  1600. IR::Opnd * hasBailedOutOffset = IR::IntConstOpnd::New(this->m_func->m_hasBailedOutSym->m_offset, TyInt32, this->m_func);
  1601. this->LoadHelperArgument(tryAddr, hasBailedOutOffset);
  1602. }
  1603. // Arg 5: arg out size
  1604. IR::RegOpnd * argOutSize = IR::RegOpnd::New(TyMachReg, this->m_func);
  1605. instr = IR::Instr::New(Js::OpCode::LDARGOUTSZ, argOutSize, this->m_func);
  1606. tryAddr->InsertBefore(instr);
  1607. this->LoadHelperArgument(tryAddr, argOutSize);
  1608. // Arg 4: locals pointer
  1609. IR::RegOpnd * localsPtr = IR::RegOpnd::New(nullptr, this->m_func->GetLocalsPointer(), TyMachReg, this->m_func);
  1610. this->LoadHelperArgument(tryAddr, localsPtr);
  1611. // Arg 3: frame pointer
  1612. IR::RegOpnd * framePtr = IR::RegOpnd::New(nullptr, FRAME_REG, TyMachReg, this->m_func);
  1613. this->LoadHelperArgument(tryAddr, framePtr);
  1614. // Arg 2: helper address
  1615. IR::LabelInstr * helperAddr = tryInstr->AsBranchInstr()->GetTarget();
  1616. this->LoadHelperArgument(tryAddr, IR::LabelOpnd::New(helperAddr, this->m_func));
  1617. // Arg 1: try address
  1618. this->LoadHelperArgument(tryAddr, IR::LabelOpnd::New(tryAddr, this->m_func));
  1619. // Call the helper
  1620. IR::RegOpnd *continuationAddr =
  1621. IR::RegOpnd::New(StackSym::New(TyMachReg,this->m_func), RETURN_REG, TyMachReg, this->m_func);
  1622. IR::Instr * callInstr = IR::Instr::New(
  1623. Js::OpCode::Call, continuationAddr, IR::HelperCallOpnd::New(helperMethod, this->m_func), this->m_func);
  1624. tryAddr->InsertBefore(callInstr);
  1625. this->LowerCall(callInstr, 0);
  1626. // Jump to the continuation address supplied by the helper
  1627. IR::BranchInstr *branchInstr = IR::MultiBranchInstr::New(Js::OpCode::BX, continuationAddr, this->m_func);
  1628. tryAddr->InsertBefore(branchInstr);
  1629. return tryInstr->m_prev;
  1630. }
  1631. IR::Instr *
  1632. LowererMD::LowerLeave(IR::Instr * leaveInstr, IR::LabelInstr * targetInstr, bool fromFinalLower, bool isOrphanedLeave)
  1633. {
  1634. if (isOrphanedLeave)
  1635. {
  1636. Assert(this->m_func->IsLoopBodyInTry());
  1637. leaveInstr->m_opcode = Js::OpCode::B;
  1638. return leaveInstr->m_prev;
  1639. }
  1640. IR::Instr * instrPrev = leaveInstr->m_prev;
  1641. IR::LabelOpnd *labelOpnd = IR::LabelOpnd::New(targetInstr, this->m_func);
  1642. this->LowerEHRegionReturn(leaveInstr, labelOpnd);
  1643. if (fromFinalLower)
  1644. {
  1645. instrPrev = leaveInstr->m_prev;
  1646. }
  1647. leaveInstr->Remove();
  1648. return instrPrev;
  1649. }
  1650. IR::Instr *
  1651. LowererMD::LowerLeaveNull(IR::Instr * leaveInstr)
  1652. {
  1653. IR::Instr * instrPrev = leaveInstr->m_prev;
  1654. // Return a NULL continuation address to the caller to indicate that the finally did not seize the flow.
  1655. this->LowerEHRegionReturn(leaveInstr, IR::IntConstOpnd::New(0, TyMachReg, this->m_func));
  1656. leaveInstr->Remove();
  1657. return instrPrev;
  1658. }
  1659. IR::Instr *
  1660. LowererMD::LowerEHRegionReturn(IR::Instr * insertBeforeInstr, IR::Opnd * targetOpnd)
  1661. {
  1662. IR::RegOpnd *retReg = IR::RegOpnd::New(nullptr, RETURN_REG, TyMachReg, this->m_func);
  1663. // Load the continuation address into the return register.
  1664. LowererMD::CreateAssign(retReg, targetOpnd, insertBeforeInstr);
  1665. IR::LabelInstr *epilogLabel = this->EnsureEpilogLabel();
  1666. IR::BranchInstr *jmpInstr = IR::BranchInstr::New(Js::OpCode::B, epilogLabel, this->m_func);
  1667. insertBeforeInstr->InsertBefore(jmpInstr);
  1668. // return the last instruction inserted
  1669. return jmpInstr;
  1670. }
  1671. IR::Instr *
  1672. LowererMD::LowerCatch(IR::Instr * instr)
  1673. {
  1674. // t1 = catch => t2(r1) = catch
  1675. // => t1 = t2(r1)
  1676. IR::Opnd *catchObj = instr->UnlinkDst();
  1677. IR::RegOpnd *catchParamReg = IR::RegOpnd::New(TyMachPtr, this->m_func);
  1678. catchParamReg->SetReg(CATCH_OBJ_REG);
  1679. instr->SetDst(catchParamReg);
  1680. instr->InsertAfter(IR::Instr::New(Js::OpCode::MOV, catchObj, catchParamReg, this->m_func));
  1681. return instr->m_prev;
  1682. }
  1683. ///----------------------------------------------------------------------------
  1684. ///
  1685. /// LowererMD::Init
  1686. ///
  1687. ///----------------------------------------------------------------------------
  1688. void
  1689. LowererMD::Init(Lowerer *lowerer)
  1690. {
  1691. m_lowerer = lowerer;
  1692. // The arg slot count computed by an earlier phase (e.g., IRBuilder) doesn't work for
  1693. // ARM if it accounts for nesting. Clear it here and let Lower compute its own value.
  1694. this->m_func->m_argSlotsForFunctionsCalled = 0;
  1695. }
  1696. ///----------------------------------------------------------------------------
  1697. ///
  1698. /// LowererMD::LoadInputParamPtr
  1699. ///
  1700. /// Load the address of the start of the passed-in parameters not including
  1701. /// the this parameter.
  1702. ///
  1703. ///----------------------------------------------------------------------------
  1704. IR::Instr *
  1705. LowererMD::LoadInputParamPtr(IR::Instr * instrInsert, IR::RegOpnd * optionalDstOpnd /* = nullptr */)
  1706. {
  1707. if (this->m_func->GetJnFunction()->IsGenerator())
  1708. {
  1709. IR::RegOpnd * argPtrRegOpnd = Lowerer::LoadGeneratorArgsPtr(instrInsert);
  1710. IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(argPtrRegOpnd, 1 * MachPtr, TyMachPtr, this->m_func);
  1711. IR::RegOpnd * dstOpnd = optionalDstOpnd != nullptr ? optionalDstOpnd : IR::RegOpnd::New(TyMachPtr, this->m_func);
  1712. return Lowerer::InsertLea(dstOpnd, indirOpnd, instrInsert);
  1713. }
  1714. else
  1715. {
  1716. StackSym * paramSym = GetImplicitParamSlotSym(3);
  1717. IR::Instr * instr = this->LoadStackAddress(paramSym);
  1718. instrInsert->InsertBefore(instr);
  1719. return instr;
  1720. }
  1721. }
  1722. ///----------------------------------------------------------------------------
  1723. ///
  1724. /// LowererMD::LoadInputParamCount
  1725. ///
  1726. /// Load the passed-in parameter count from the appropriate r11 slot.
  1727. ///
  1728. ///----------------------------------------------------------------------------
  1729. IR::Instr *
  1730. LowererMD::LoadInputParamCount(IR::Instr * instrInsert, int adjust, bool needFlags)
  1731. {
  1732. IR::Instr * instr;
  1733. IR::RegOpnd * dstOpnd;
  1734. IR::SymOpnd * srcOpnd;
  1735. // LDR Rz, CallInfo
  1736. // LSR Rx, Rz, #28 // Get CallEval bit as bottom bit.
  1737. // AND Rx, Rx, #1 // Mask higher 3 bits, Rx has 1 if FrameDisplay is present, zero otherwise
  1738. // LSL Rz, Rz, #8 // Mask higher 8 bits to get the number of arguments
  1739. // LSR Rz, Rz, #8
  1740. // SUB Rz, Rz, Rx // Now Rz has the right number of parameters
  1741. srcOpnd = Lowerer::LoadCallInfo(instrInsert);
  1742. dstOpnd = IR::RegOpnd::New(TyMachReg, this->m_func);
  1743. instr = IR::Instr::New(Js::OpCode::LDR, dstOpnd, srcOpnd, this->m_func);
  1744. instrInsert->InsertBefore(instr);
  1745. // mask the "calling eval" bit and subtract it from the incoming count.
  1746. // ("Calling eval" means the last param is the frame display, which only the eval built-in should see.)
  1747. IR::RegOpnd * evalBitOpnd = IR::RegOpnd::New(TyMachReg, this->m_func);
  1748. instr = IR::Instr::New(Js::OpCode::LSR, evalBitOpnd, dstOpnd, IR::IntConstOpnd::New(Math::Log2(Js::CallFlags_ExtraArg) + Js::CallInfo::ksizeofCount, TyMachReg, this->m_func), this->m_func);
  1749. instrInsert->InsertBefore(instr);
  1750. // Mask off other call flags from callinfo
  1751. instr = IR::Instr::New(Js::OpCode::AND, evalBitOpnd, evalBitOpnd, IR::IntConstOpnd::New(0x01, TyUint8, this->m_func), this->m_func);
  1752. instrInsert->InsertBefore(instr);
  1753. instr = IR::Instr::New(Js::OpCode::LSL, dstOpnd, dstOpnd, IR::IntConstOpnd::New(Js::CallInfo::ksizeofCallFlags, TyMachReg, this->m_func), this->m_func);
  1754. instrInsert->InsertBefore(instr);
  1755. instr = IR::Instr::New(Js::OpCode::LSR, dstOpnd, dstOpnd, IR::IntConstOpnd::New(Js::CallInfo::ksizeofCallFlags, TyMachReg, this->m_func), this->m_func);
  1756. instrInsert->InsertBefore(instr);
  1757. if (adjust != 0)
  1758. {
  1759. Assert(adjust < 0);
  1760. Lowerer::InsertAdd(false, evalBitOpnd, evalBitOpnd, IR::IntConstOpnd::New(-adjust, TyUint32, this->m_func), instrInsert);
  1761. }
  1762. return Lowerer::InsertSub(needFlags, dstOpnd, dstOpnd, evalBitOpnd, instrInsert);
  1763. }
  1764. IR::Instr *
  1765. LowererMD::LoadStackArgPtr(IR::Instr * instr)
  1766. {
  1767. if (this->m_func->IsLoopBody())
  1768. {
  1769. // Get the first user param from the interpreter frame instance that was passed in.
  1770. // These args don't include the func object and callinfo; we just need to advance past "this".
  1771. // t1 = LDR [prm1 + m_inParams]
  1772. // dst = ADD t1, sizeof(var)
  1773. Assert(this->m_func->m_loopParamSym);
  1774. IR::RegOpnd *baseOpnd = IR::RegOpnd::New(this->m_func->m_loopParamSym, TyMachReg, this->m_func);
  1775. size_t offset = Js::InterpreterStackFrame::GetOffsetOfInParams();
  1776. IR::IndirOpnd *indirOpnd = IR::IndirOpnd::New(baseOpnd, (int32)offset, TyMachReg, this->m_func);
  1777. IR::RegOpnd *tmpOpnd = IR::RegOpnd::New(TyMachReg, this->m_func);
  1778. LowererMD::CreateAssign(tmpOpnd, indirOpnd, instr);
  1779. instr->SetSrc1(tmpOpnd);
  1780. instr->SetSrc2(IR::IntConstOpnd::New(sizeof(Js::Var), TyMachReg, this->m_func));
  1781. }
  1782. else if (this->m_func->GetJnFunction()->IsGenerator())
  1783. {
  1784. IR::Instr *instr2 = LoadInputParamPtr(instr, instr->UnlinkDst()->AsRegOpnd());
  1785. instr->Remove();
  1786. instr = instr2;
  1787. }
  1788. else
  1789. {
  1790. // Get the args pointer relative to r11. We assume that r11 is set up, since we'll only be looking
  1791. // for the stack arg pointer in a non-leaf.
  1792. // dst = ADD r11, "this" offset + sizeof(var)
  1793. instr->SetSrc1(IR::RegOpnd::New(nullptr, FRAME_REG, TyMachReg, this->m_func));
  1794. instr->SetSrc2(IR::IntConstOpnd::New((ArgOffsetFromFramePtr + Js::JavascriptFunctionArgIndex_SecondScriptArg) * sizeof(Js::Var), TyMachReg, this->m_func));
  1795. }
  1796. instr->m_opcode = Js::OpCode::ADD;
  1797. return instr->m_prev;
  1798. }
  1799. IR::Instr *
  1800. LowererMD::LoadArgumentsFromFrame(IR::Instr * instr)
  1801. {
  1802. IR::RegOpnd *baseOpnd;
  1803. int32 offset;
  1804. if (this->m_func->IsLoopBody())
  1805. {
  1806. // Get the arguments ptr from the interpreter frame instance that was passed in.
  1807. Assert(this->m_func->m_loopParamSym);
  1808. baseOpnd = IR::RegOpnd::New(this->m_func->m_loopParamSym, TyMachReg, this->m_func);
  1809. offset = Js::InterpreterStackFrame::GetOffsetOfArguments();
  1810. }
  1811. else
  1812. {
  1813. // Get the arguments relative to the frame pointer.
  1814. baseOpnd = IR::RegOpnd::New(nullptr, FRAME_REG, TyMachReg, this->m_func);
  1815. offset = -MachArgsSlotOffset;
  1816. }
  1817. instr->SetSrc1(IR::IndirOpnd::New(baseOpnd, offset, TyMachReg, this->m_func));
  1818. this->ChangeToAssign(instr);
  1819. return instr->m_prev;
  1820. }
  1821. // load argument count as I4
  1822. IR::Instr *
  1823. LowererMD::LoadArgumentCount(IR::Instr * instr)
  1824. {
  1825. IR::RegOpnd *baseOpnd;
  1826. int32 offset;
  1827. if (this->m_func->IsLoopBody())
  1828. {
  1829. // Pull the arg count from the interpreter frame instance that was passed in.
  1830. // (The callinfo in the loop body's frame just shows the single parameter, the interpreter frame.)
  1831. Assert(this->m_func->m_loopParamSym);
  1832. baseOpnd = IR::RegOpnd::New(this->m_func->m_loopParamSym, TyMachReg, this->m_func);
  1833. offset = Js::InterpreterStackFrame::GetOffsetOfInSlotsCount();
  1834. }
  1835. else
  1836. {
  1837. baseOpnd = IR::RegOpnd::New(nullptr, FRAME_REG, TyMachReg, this->m_func);
  1838. offset = (ArgOffsetFromFramePtr + Js::JavascriptFunctionArgIndex_CallInfo) * sizeof(Js::Var);
  1839. }
  1840. instr->SetSrc1(IR::IndirOpnd::New(baseOpnd, offset, TyInt32, this->m_func));
  1841. this->ChangeToAssign(instr);
  1842. return instr->m_prev;
  1843. }
  1844. ///----------------------------------------------------------------------------
  1845. ///
  1846. /// LowererMD::LoadHeapArguments
  1847. ///
  1848. /// Load the arguments object
  1849. /// NOTE: The same caveat regarding arguments passed on the stack applies here
  1850. /// as in LoadInputParamCount above.
  1851. ///----------------------------------------------------------------------------
  1852. IR::Instr *
  1853. LowererMD::LoadHeapArguments(IR::Instr * instrArgs, bool force /* = false */, IR::Opnd *opndInputParamCount /* = nullptr */)
  1854. {
  1855. ASSERT_INLINEE_FUNC(instrArgs);
  1856. Func *func = instrArgs->m_func;
  1857. // s7 = formals are let decls
  1858. // s6 = memory context
  1859. // s5 = array of property ID's
  1860. // s4 = local frame instance
  1861. // s3 = address of first actual argument (after "this")
  1862. // s2 = actual argument count
  1863. // s1 = current function
  1864. // dst = JavascriptOperators::LoadHeapArguments(s1, s2, s3, s4, s5, s6, s7)
  1865. IR::Instr * instrPrev = instrArgs->m_prev;
  1866. if (!force && func->GetHasStackArgs() && this->m_func->GetHasStackArgs())
  1867. {
  1868. // The initial args slot value is zero.
  1869. instrArgs->m_opcode = Js::OpCode::MOV;
  1870. instrArgs->ReplaceSrc1(IR::AddrOpnd::NewNull(func));
  1871. instrArgs->FreeSrc2();
  1872. }
  1873. else
  1874. {
  1875. // s7 = formals are let decls
  1876. this->LoadHelperArgument(instrArgs, IR::IntConstOpnd::New(instrArgs->m_opcode == Js::OpCode::LdLetHeapArguments ? TRUE : FALSE, TyUint8, func));
  1877. // s6 = memory context
  1878. this->m_lowerer->LoadScriptContext(instrArgs);
  1879. // s5 = array of property ID's
  1880. IR::Opnd * argArray = instrArgs->UnlinkSrc2();
  1881. this->LoadHelperArgument(instrArgs, argArray);
  1882. // s4 = local frame instance
  1883. IR::Opnd * frameObj = instrArgs->UnlinkSrc1();
  1884. this->LoadHelperArgument(instrArgs, frameObj);
  1885. if (func->IsInlinee())
  1886. {
  1887. // s3 = address of first actual argument (after "this").
  1888. StackSym *firstRealArgSlotSym = func->GetInlineeArgvSlotOpnd()->m_sym->AsStackSym();
  1889. this->m_func->SetArgOffset(firstRealArgSlotSym, firstRealArgSlotSym->m_offset + MachPtr);
  1890. IR::Instr *instr = this->LoadStackAddress(firstRealArgSlotSym);
  1891. instrArgs->InsertBefore(instr);
  1892. this->LoadHelperArgument(instrArgs, instr->GetDst());
  1893. // s2 = actual argument count (without counting "this").
  1894. this->LoadHelperArgument(instrArgs, IR::IntConstOpnd::New(func->actualCount - 1, TyUint32, func));
  1895. // s1 = current function.
  1896. this->LoadHelperArgument(instrArgs, func->GetInlineeFunctionObjectSlotOpnd());
  1897. // Save the newly-created args object to its dedicated stack slot.
  1898. IR::SymOpnd *argObjSlotOpnd = func->GetInlineeArgumentsObjectSlotOpnd();
  1899. LowererMD::CreateAssign(argObjSlotOpnd,instrArgs->GetDst(), instrArgs->m_next);
  1900. }
  1901. else
  1902. {
  1903. // s3 = address of first actual argument (after "this")
  1904. // Stack looks like (function object)+0, (arg count)+4, (this)+8, actual args
  1905. IR::Instr * instr = this->LoadInputParamPtr(instrArgs);
  1906. this->LoadHelperArgument(instrArgs, instr->GetDst());
  1907. // s2 = actual argument count (without counting "this")
  1908. if (opndInputParamCount == nullptr)
  1909. {
  1910. instr = this->LoadInputParamCount(instrArgs, -1);
  1911. opndInputParamCount = instr->GetDst();
  1912. }
  1913. this->LoadHelperArgument(instrArgs, opndInputParamCount);
  1914. // s1 = current function
  1915. StackSym * paramSym = GetImplicitParamSlotSym(0);
  1916. IR::Opnd * srcOpnd = IR::SymOpnd::New(paramSym, TyMachReg, func);
  1917. this->LoadHelperArgument(instrArgs, srcOpnd);
  1918. // Save the newly-created args object to its dedicated stack slot.
  1919. IR::IndirOpnd *indirOpnd = IR::IndirOpnd::New(IR::RegOpnd::New(nullptr, FRAME_REG , TyMachReg, func),
  1920. -MachArgsSlotOffset, TyMachPtr, m_func);
  1921. LowererMD::CreateAssign(indirOpnd, instrArgs->GetDst(), instrArgs->m_next);
  1922. }
  1923. this->ChangeToHelperCall(instrArgs, IR::HelperOp_LoadHeapArguments);
  1924. }
  1925. return instrPrev;
  1926. }
  1927. ///----------------------------------------------------------------------------
  1928. ///
  1929. /// LowererMD::LoadHeapArgsCached
  1930. ///
  1931. /// Load the heap-based arguments object using a cached scope
  1932. ///
  1933. ///----------------------------------------------------------------------------
  1934. IR::Instr *
  1935. LowererMD::LoadHeapArgsCached(IR::Instr * instrArgs)
  1936. {
  1937. Assert(!this->m_func->GetJnFunction()->IsGenerator());
  1938. ASSERT_INLINEE_FUNC(instrArgs);
  1939. Func *func = instrArgs->m_func;
  1940. // s7 = formals are let decls
  1941. // s6 = memory context
  1942. // s5 = local frame instance
  1943. // s4 = address of first actual argument (after "this")
  1944. // s3 = formal argument count
  1945. // s2 = actual argument count
  1946. // s1 = current function
  1947. // dst = JavascriptOperators::LoadHeapArgsCached(s1, s2, s3, s4, s5, s6, s7)
  1948. IR::Instr * instrPrev = instrArgs->m_prev;
  1949. // s7 = formals are let decls
  1950. IR::Opnd * formalsAreLetDecls = IR::IntConstOpnd::New((IntConstType)(instrArgs->m_opcode == Js::OpCode::LdLetHeapArgsCached), TyUint8, func);
  1951. this->LoadHelperArgument(instrArgs, formalsAreLetDecls);
  1952. // s6 = memory context
  1953. this->m_lowerer->LoadScriptContext(instrArgs);
  1954. // s5 = local frame instance
  1955. IR::Opnd * frameObj = instrArgs->UnlinkSrc1();
  1956. this->LoadHelperArgument(instrArgs, frameObj);
  1957. if (func->IsInlinee())
  1958. {
  1959. // s4 = address of first actual argument (after "this").
  1960. StackSym *firstRealArgSlotSym = func->GetInlineeArgvSlotOpnd()->m_sym->AsStackSym();
  1961. this->m_func->SetArgOffset(firstRealArgSlotSym, firstRealArgSlotSym->m_offset + MachPtr);
  1962. IR::Instr *instr = this->LoadStackAddress(firstRealArgSlotSym);
  1963. instrArgs->InsertBefore(instr);
  1964. this->LoadHelperArgument(instrArgs, instr->GetDst());
  1965. // s3 = formal argument count (without counting "this").
  1966. uint32 formalsCount = func->GetJnFunction()->GetInParamsCount() - 1;
  1967. this->LoadHelperArgument(instrArgs, IR::IntConstOpnd::New(formalsCount, TyUint32, func));
  1968. // s2 = actual argument count (without counting "this").
  1969. this->LoadHelperArgument(instrArgs, IR::IntConstOpnd::New(func->actualCount - 1, TyUint32, func));
  1970. // s1 = current function.
  1971. this->LoadHelperArgument(instrArgs, func->GetInlineeFunctionObjectSlotOpnd());
  1972. // Save the newly-created args object to its dedicated stack slot.
  1973. IR::SymOpnd *argObjSlotOpnd = func->GetInlineeArgumentsObjectSlotOpnd();
  1974. LowererMD::CreateAssign(argObjSlotOpnd, instrArgs->GetDst(), instrArgs->m_next);
  1975. }
  1976. else
  1977. {
  1978. // s4 = address of first actual argument (after "this")
  1979. IR::Instr * instr = this->LoadInputParamPtr(instrArgs);
  1980. this->LoadHelperArgument(instrArgs, instr->GetDst());
  1981. // s3 = formal argument count (without counting "this")
  1982. uint32 formalsCount = func->GetInParamsCount() - 1;
  1983. this->LoadHelperArgument(instrArgs, IR::IntConstOpnd::New(formalsCount, TyMachReg, func));
  1984. // s2 = actual argument count (without counting "this")
  1985. instr = this->LoadInputParamCount(instrArgs, -1);
  1986. this->LoadHelperArgument(instrArgs, instr->GetDst());
  1987. // s1 = current function
  1988. StackSym * paramSym = GetImplicitParamSlotSym(0);
  1989. IR::Opnd * srcOpnd = IR::SymOpnd::New(paramSym, TyMachReg, func);
  1990. this->LoadHelperArgument(instrArgs, srcOpnd);
  1991. // Save the newly-created args object to its dedicated stack slot.
  1992. IR::IndirOpnd *indirOpnd = IR::IndirOpnd::New(IR::RegOpnd::New(nullptr, FRAME_REG , TyMachReg, func),
  1993. -MachArgsSlotOffset, TyMachPtr, m_func);
  1994. LowererMD::CreateAssign(indirOpnd, instrArgs->GetDst(), instrArgs->m_next);
  1995. }
  1996. this->ChangeToHelperCall(instrArgs, IR::HelperOp_LoadHeapArgsCached);
  1997. return instrPrev;
  1998. }
  1999. IR::Instr *
  2000. LowererMD::LoadFuncExpression(IR::Instr * instrFuncExpr)
  2001. {
  2002. ASSERT_INLINEE_FUNC(instrFuncExpr);
  2003. Func *func = instrFuncExpr->m_func;
  2004. IR::Opnd *paramOpnd = nullptr;
  2005. if (func->IsInlinee())
  2006. {
  2007. paramOpnd = func->GetInlineeFunctionObjectSlotOpnd();
  2008. }
  2009. else
  2010. {
  2011. //function object is first argument and mark it as IsParamSlotSym.
  2012. StackSym * paramSym = GetImplicitParamSlotSym(0);
  2013. paramOpnd = IR::SymOpnd::New(paramSym, TyMachReg, this->m_func);
  2014. }
  2015. instrFuncExpr->SetSrc1(paramOpnd);
  2016. this->ChangeToAssign(instrFuncExpr);
  2017. return instrFuncExpr;
  2018. }
  2019. ///----------------------------------------------------------------------------
  2020. ///
  2021. /// LowererMD::ChangeToHelperCall
  2022. ///
  2023. /// Change the current instruction to a call to the given helper.
  2024. ///
  2025. ///----------------------------------------------------------------------------
  2026. IR::Instr *
  2027. LowererMD::ChangeToHelperCall(IR::Instr * callInstr, IR::JnHelperMethod helperMethod, IR::LabelInstr *labelBailOut,
  2028. IR::Opnd *opndInstance, IR::PropertySymOpnd *propSymOpnd, bool isHelperContinuation)
  2029. {
  2030. IR::Instr * bailOutInstr = callInstr;
  2031. if (callInstr->HasBailOutInfo())
  2032. {
  2033. if (callInstr->GetBailOutKind() == IR::BailOutExpectingObject)
  2034. {
  2035. callInstr = IR::Instr::New(callInstr->m_opcode, callInstr->m_func);
  2036. bailOutInstr->TransferTo(callInstr);
  2037. bailOutInstr->InsertBefore(callInstr);
  2038. bailOutInstr->m_opcode = Js::OpCode::BailOnNotObject;
  2039. bailOutInstr->SetSrc1(opndInstance);
  2040. }
  2041. else
  2042. {
  2043. bailOutInstr = this->m_lowerer->SplitBailOnImplicitCall(callInstr);
  2044. }
  2045. }
  2046. IR::HelperCallOpnd *helperCallOpnd = Lowerer::CreateHelperCallOpnd(helperMethod, this->GetHelperArgsCount(), m_func);
  2047. if (helperCallOpnd->IsDiagHelperCallOpnd())
  2048. {
  2049. // Load arguments for the wrapper.
  2050. this->LoadHelperArgument(callInstr, IR::AddrOpnd::New((Js::Var)IR::GetMethodOriginalAddress(helperMethod), IR::AddrOpndKindDynamicMisc, m_func));
  2051. this->m_lowerer->LoadScriptContext(callInstr);
  2052. }
  2053. callInstr->SetSrc1(helperCallOpnd);
  2054. IR::Instr * instrRet = this->LowerCall(callInstr, 0);
  2055. if (bailOutInstr != callInstr)
  2056. {
  2057. // The bailout needs to be lowered after we lower the helper call because the helper argument
  2058. // has already been loaded. We need to drain them on AMD64 before starting another helper call
  2059. if (bailOutInstr->m_opcode == Js::OpCode::BailOnNotObject)
  2060. {
  2061. this->m_lowerer->LowerBailOnNotObject(bailOutInstr, nullptr, labelBailOut);
  2062. }
  2063. else
  2064. {
  2065. this->m_lowerer->LowerBailOnEqualOrNotEqual(bailOutInstr, nullptr, labelBailOut, propSymOpnd, isHelperContinuation);
  2066. }
  2067. }
  2068. return instrRet;
  2069. }
  2070. IR::Instr* LowererMD::ChangeToHelperCallMem(IR::Instr * instr, IR::JnHelperMethod helperMethod)
  2071. {
  2072. this->m_lowerer->LoadScriptContext(instr);
  2073. return this->ChangeToHelperCall(instr, helperMethod);
  2074. }
  2075. ///----------------------------------------------------------------------------
  2076. ///
  2077. /// LowererMD::ChangeToAssign
  2078. ///
  2079. /// Change to a copy. Handle riscification of operands.
  2080. ///
  2081. ///----------------------------------------------------------------------------
  2082. IR::Instr *
  2083. LowererMD::ChangeToAssign(IR::Instr * instr)
  2084. {
  2085. return ChangeToAssign(instr, instr->GetDst()->GetType());
  2086. }
  2087. IR::Instr *
  2088. LowererMD::ChangeToAssign(IR::Instr * instr, IRType type)
  2089. {
  2090. Assert(!instr->HasBailOutInfo() || instr->GetBailOutKind() == IR::BailOutExpectingInteger
  2091. || instr->GetBailOutKind() == IR::BailOutExpectingString);
  2092. IR::Opnd *src = instr->GetSrc1();
  2093. if (src->IsImmediateOpnd() || src->IsLabelOpnd())
  2094. {
  2095. instr->m_opcode = Js::OpCode::LDIMM;
  2096. }
  2097. else if(type == TyFloat32 && instr->GetDst()->IsRegOpnd())
  2098. {
  2099. Assert(instr->GetSrc1()->IsFloat32());
  2100. instr->m_opcode = Js::OpCode::VLDR32;
  2101. // Note that we allocate double register for single precision floats as well, as the register allocator currently
  2102. // does not support 32-bit float registers
  2103. instr->ReplaceDst(instr->GetDst()->UseWithNewType(TyFloat64, instr->m_func));
  2104. if(instr->GetSrc1()->IsRegOpnd())
  2105. {
  2106. instr->ReplaceSrc1(instr->GetSrc1()->UseWithNewType(TyFloat64, instr->m_func));
  2107. }
  2108. }
  2109. else
  2110. {
  2111. instr->m_opcode = LowererMD::GetMoveOp(type);
  2112. }
  2113. LegalizeMD::LegalizeInstr(instr, false);
  2114. return instr;
  2115. }
  2116. void
  2117. LowererMD::ChangeToWriteBarrierAssign(IR::Instr * assignInstr)
  2118. {
  2119. #ifdef RECYCLER_WRITE_BARRIER_JIT
  2120. // WriteBarrier-TODO- Implement ARM JIT
  2121. #endif
  2122. ChangeToAssign(assignInstr);
  2123. }
  2124. ///----------------------------------------------------------------------------
  2125. ///
  2126. /// LowererMD::ChangeToLea
  2127. ///
  2128. /// Change to a load-effective-address
  2129. ///
  2130. ///----------------------------------------------------------------------------
  2131. IR::Instr *
  2132. LowererMD::ChangeToLea(IR::Instr * instr)
  2133. {
  2134. Assert(instr);
  2135. Assert(instr->GetDst());
  2136. Assert(instr->GetDst()->IsRegOpnd());
  2137. Assert(instr->GetSrc1());
  2138. Assert(instr->GetSrc1()->IsIndirOpnd() || instr->GetSrc1()->IsSymOpnd());
  2139. Assert(!instr->GetSrc2());
  2140. instr->m_opcode = Js::OpCode::LEA;
  2141. Legalize(instr);
  2142. return instr;
  2143. }
  2144. ///----------------------------------------------------------------------------
  2145. ///
  2146. /// LowererMD::CreateAssign
  2147. ///
  2148. /// Create a copy from src to dst. Let ChangeToAssign handle riscification
  2149. /// of operands.
  2150. ///----------------------------------------------------------------------------
  2151. IR::Instr *
  2152. LowererMD::CreateAssign(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsertPt)
  2153. {
  2154. return Lowerer::InsertMove(dst, src, instrInsertPt);
  2155. }
  2156. ///----------------------------------------------------------------------------
  2157. ///
  2158. /// LowererMD::LowerRet
  2159. ///
  2160. /// Lower Ret to "MOV EAX, src"
  2161. /// The real RET is inserted at the exit of the function when emitting the
  2162. /// epilog.
  2163. ///
  2164. ///----------------------------------------------------------------------------
  2165. IR::Instr *
  2166. LowererMD::LowerRet(IR::Instr * retInstr)
  2167. {
  2168. IR::RegOpnd *retReg = IR::RegOpnd::New(nullptr, RETURN_REG, TyMachReg, m_func);
  2169. retInstr->SetDst(retReg);
  2170. return this->ChangeToAssign(retInstr);
  2171. }
  2172. ///----------------------------------------------------------------------------
  2173. ///
  2174. /// LowererMD::LowerUncondBranch
  2175. ///
  2176. ///----------------------------------------------------------------------------
  2177. IR::Instr *
  2178. LowererMD::LowerUncondBranch(IR::Instr * instr)
  2179. {
  2180. instr->m_opcode = Js::OpCode::B;
  2181. return instr;
  2182. }
  2183. ///----------------------------------------------------------------------------
  2184. ///
  2185. /// LowererMD::LowerMultiBranch
  2186. ///
  2187. ///----------------------------------------------------------------------------
  2188. IR::Instr *
  2189. LowererMD::LowerMultiBranch(IR::Instr * instr)
  2190. {
  2191. instr->m_opcode = Js::OpCode::BX;
  2192. return instr;
  2193. }
  2194. ///----------------------------------------------------------------------------
  2195. ///
  2196. /// LowererMD::MDBranchOpcode
  2197. ///
  2198. /// Map HIR branch opcode to machine-dependent equivalent.
  2199. ///
  2200. ///----------------------------------------------------------------------------
  2201. Js::OpCode
  2202. LowererMD::MDBranchOpcode(Js::OpCode opcode)
  2203. {
  2204. switch (opcode)
  2205. {
  2206. case Js::OpCode::BrEq_A:
  2207. case Js::OpCode::BrSrEq_A:
  2208. case Js::OpCode::BrNotNeq_A:
  2209. case Js::OpCode::BrSrNotNeq_A:
  2210. case Js::OpCode::BrAddr_A:
  2211. return Js::OpCode::BEQ;
  2212. case Js::OpCode::BrNeq_A:
  2213. case Js::OpCode::BrSrNeq_A:
  2214. case Js::OpCode::BrNotEq_A:
  2215. case Js::OpCode::BrSrNotEq_A:
  2216. case Js::OpCode::BrNotAddr_A:
  2217. return Js::OpCode::BNE;
  2218. case Js::OpCode::BrLt_A:
  2219. case Js::OpCode::BrNotGe_A:
  2220. return Js::OpCode::BLT;
  2221. case Js::OpCode::BrLe_A:
  2222. case Js::OpCode::BrNotGt_A:
  2223. return Js::OpCode::BLE;
  2224. case Js::OpCode::BrGt_A:
  2225. case Js::OpCode::BrNotLe_A:
  2226. return Js::OpCode::BGT;
  2227. case Js::OpCode::BrGe_A:
  2228. case Js::OpCode::BrNotLt_A:
  2229. return Js::OpCode::BGE;
  2230. case Js::OpCode::BrUnGt_A:
  2231. return Js::OpCode::BHI;
  2232. case Js::OpCode::BrUnGe_A:
  2233. return Js::OpCode::BCS;
  2234. case Js::OpCode::BrUnLt_A:
  2235. return Js::OpCode::BCC;
  2236. case Js::OpCode::BrUnLe_A:
  2237. return Js::OpCode::BLS;
  2238. default:
  2239. AssertMsg(0, "NYI");
  2240. return opcode;
  2241. }
  2242. }
  2243. Js::OpCode
  2244. LowererMD::MDUnsignedBranchOpcode(Js::OpCode opcode)
  2245. {
  2246. switch (opcode)
  2247. {
  2248. case Js::OpCode::BrEq_A:
  2249. case Js::OpCode::BrSrEq_A:
  2250. case Js::OpCode::BrSrNotNeq_A:
  2251. case Js::OpCode::BrNotNeq_A:
  2252. case Js::OpCode::BrAddr_A:
  2253. return Js::OpCode::BEQ;
  2254. case Js::OpCode::BrNeq_A:
  2255. case Js::OpCode::BrSrNeq_A:
  2256. case Js::OpCode::BrSrNotEq_A:
  2257. case Js::OpCode::BrNotEq_A:
  2258. case Js::OpCode::BrNotAddr_A:
  2259. return Js::OpCode::BNE;
  2260. case Js::OpCode::BrLt_A:
  2261. case Js::OpCode::BrNotGe_A:
  2262. return Js::OpCode::BCC;
  2263. case Js::OpCode::BrLe_A:
  2264. case Js::OpCode::BrNotGt_A:
  2265. return Js::OpCode::BLS;
  2266. case Js::OpCode::BrGt_A:
  2267. case Js::OpCode::BrNotLe_A:
  2268. return Js::OpCode::BHI;
  2269. case Js::OpCode::BrGe_A:
  2270. case Js::OpCode::BrNotLt_A:
  2271. return Js::OpCode::BCS;
  2272. default:
  2273. AssertMsg(0, "NYI");
  2274. return opcode;
  2275. }
  2276. }
  2277. Js::OpCode LowererMD::MDCompareWithZeroBranchOpcode(Js::OpCode opcode)
  2278. {
  2279. Assert(opcode == Js::OpCode::BrLt_A || opcode == Js::OpCode::BrGe_A);
  2280. return opcode == Js::OpCode::BrLt_A ? Js::OpCode::BMI : Js::OpCode::BPL;
  2281. }
  2282. void LowererMD::ChangeToAdd(IR::Instr *const instr, const bool needFlags)
  2283. {
  2284. Assert(instr);
  2285. Assert(instr->GetDst());
  2286. Assert(instr->GetSrc1());
  2287. Assert(instr->GetSrc2());
  2288. if(instr->GetDst()->IsFloat64())
  2289. {
  2290. Assert(instr->GetSrc1()->IsFloat64());
  2291. Assert(instr->GetSrc2()->IsFloat64());
  2292. Assert(!needFlags);
  2293. instr->m_opcode = Js::OpCode::VADDF64;
  2294. return;
  2295. }
  2296. instr->m_opcode = needFlags ? Js::OpCode::ADDS : Js::OpCode::ADD;
  2297. }
  2298. void LowererMD::ChangeToSub(IR::Instr *const instr, const bool needFlags)
  2299. {
  2300. Assert(instr);
  2301. Assert(instr->GetDst());
  2302. Assert(instr->GetSrc1());
  2303. Assert(instr->GetSrc2());
  2304. if(instr->GetDst()->IsFloat64())
  2305. {
  2306. Assert(instr->GetSrc1()->IsFloat64());
  2307. Assert(instr->GetSrc2()->IsFloat64());
  2308. Assert(!needFlags);
  2309. instr->m_opcode = Js::OpCode::VSUBF64;
  2310. return;
  2311. }
  2312. instr->m_opcode = needFlags ? Js::OpCode::SUBS : Js::OpCode::SUB;
  2313. }
  2314. void LowererMD::ChangeToShift(IR::Instr *const instr, const bool needFlags)
  2315. {
  2316. Assert(instr);
  2317. Assert(instr->GetDst());
  2318. Assert(instr->GetSrc1());
  2319. Assert(instr->GetSrc2());
  2320. Func *const func = instr->m_func;
  2321. switch(instr->m_opcode)
  2322. {
  2323. case Js::OpCode::Shl_A:
  2324. case Js::OpCode::Shl_I4:
  2325. Assert(!needFlags); // not implemented
  2326. instr->m_opcode = Js::OpCode::LSL;
  2327. break;
  2328. case Js::OpCode::Shr_A:
  2329. case Js::OpCode::Shr_I4:
  2330. instr->m_opcode = needFlags ? Js::OpCode::ASRS : Js::OpCode::ASR;
  2331. break;
  2332. case Js::OpCode::ShrU_A:
  2333. case Js::OpCode::ShrU_I4:
  2334. Assert(!needFlags); // not implemented
  2335. instr->m_opcode = Js::OpCode::LSR;
  2336. break;
  2337. default:
  2338. Assert(false);
  2339. __assume(false);
  2340. }
  2341. // Javascript requires the ShiftCount is masked to the bottom 5 bits.
  2342. if(instr->GetSrc2()->IsIntConstOpnd())
  2343. {
  2344. // In the constant case, do the mask manually.
  2345. IntConstType immed = instr->GetSrc2()->AsIntConstOpnd()->GetValue() & 0x1f;
  2346. if (immed == 0)
  2347. {
  2348. // Shift by zero is just a move, and the shift-right instructions
  2349. // don't permit encoding of a zero shift amount.
  2350. instr->m_opcode = Js::OpCode::MOV;
  2351. instr->FreeSrc2();
  2352. }
  2353. else
  2354. {
  2355. instr->GetSrc2()->AsIntConstOpnd()->SetValue(immed);
  2356. }
  2357. }
  2358. else
  2359. {
  2360. // In the variable case, generate code to do the mask.
  2361. IR::Opnd *const src2 = instr->UnlinkSrc2();
  2362. instr->SetSrc2(IR::RegOpnd::New(TyMachReg, func));
  2363. IR::Instr *const newInstr = IR::Instr::New(
  2364. Js::OpCode::AND, instr->GetSrc2(), src2, IR::IntConstOpnd::New(0x1f, TyInt8, func), func);
  2365. instr->InsertBefore(newInstr);
  2366. }
  2367. }
  2368. const uint16
  2369. LowererMD::GetFormalParamOffset()
  2370. {
  2371. //In ARM formal params are offset into the param area.
  2372. //So we only count the non-user params (Function object & CallInfo and let the encoder account for the saved R11 and LR
  2373. return 2;
  2374. }
  2375. ///----------------------------------------------------------------------------
  2376. ///
  2377. /// LowererMD::LowerCondBranch
  2378. ///
  2379. ///----------------------------------------------------------------------------
  2380. IR::Instr *
  2381. LowererMD::LowerCondBranch(IR::Instr * instr)
  2382. {
  2383. AssertMsg(instr->GetSrc1() != nullptr, "Expected src opnds on conditional branch");
  2384. IR::Opnd * opndSrc1 = instr->UnlinkSrc1();
  2385. IR::Instr * instrPrev = nullptr;
  2386. switch (instr->m_opcode)
  2387. {
  2388. case Js::OpCode::BrTrue_A:
  2389. case Js::OpCode::BrOnNotEmpty:
  2390. case Js::OpCode::BrNotNull_A:
  2391. case Js::OpCode::BrOnObject_A:
  2392. Assert(!opndSrc1->IsFloat64());
  2393. AssertMsg(opndSrc1->IsRegOpnd(),"NYI for other operands");
  2394. AssertMsg(instr->GetSrc2() == nullptr, "Expected 1 src on boolean branch");
  2395. instrPrev = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  2396. instrPrev->SetSrc1(opndSrc1);
  2397. instrPrev->SetSrc2(IR::IntConstOpnd::New(0, TyInt32, m_func));
  2398. instr->InsertBefore(instrPrev);
  2399. instr->m_opcode = Js::OpCode::BNE;
  2400. break;
  2401. case Js::OpCode::BrFalse_A:
  2402. case Js::OpCode::BrOnEmpty:
  2403. Assert(!opndSrc1->IsFloat64());
  2404. AssertMsg(opndSrc1->IsRegOpnd(),"NYI for other operands");
  2405. AssertMsg(instr->GetSrc2() == nullptr, "Expected 1 src on boolean branch");
  2406. instrPrev = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  2407. instrPrev->SetSrc1(opndSrc1);
  2408. instrPrev->SetSrc2(IR::IntConstOpnd::New(0, TyInt32, m_func));
  2409. instr->InsertBefore(instrPrev);
  2410. instr->m_opcode = Js::OpCode::BEQ;
  2411. break;
  2412. default:
  2413. IR::Opnd * opndSrc2 = instr->UnlinkSrc2();
  2414. AssertMsg(opndSrc2 != nullptr, "Expected 2 src's on non-boolean branch");
  2415. if (opndSrc1->IsFloat64())
  2416. {
  2417. AssertMsg(opndSrc1->IsRegOpnd(),"NYI for other operands");
  2418. Assert(opndSrc2->IsFloat64());
  2419. Assert(opndSrc2->IsRegOpnd() && opndSrc1->IsRegOpnd());
  2420. //This comparison updates the FPSCR - floating point status control register
  2421. instrPrev = IR::Instr::New(Js::OpCode::VCMPF64, this->m_func);
  2422. instrPrev->SetSrc1(opndSrc1);
  2423. instrPrev->SetSrc2(opndSrc2);
  2424. instr->InsertBefore(instrPrev);
  2425. LegalizeMD::LegalizeInstr(instrPrev, false);
  2426. //Transfer the result to ARM status register control register.
  2427. instrPrev = IR::Instr::New(Js::OpCode::VMRS, this->m_func);
  2428. instr->InsertBefore(instrPrev);
  2429. instr->m_opcode = LowererMD::MDBranchOpcode(instr->m_opcode);
  2430. }
  2431. else
  2432. {
  2433. AssertMsg(opndSrc2->IsRegOpnd() || opndSrc2->IsIntConstOpnd() || (opndSrc2->IsAddrOpnd()), "NYI for other operands");
  2434. instrPrev = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  2435. instrPrev->SetSrc1(opndSrc1);
  2436. instrPrev->SetSrc2(opndSrc2);
  2437. instr->InsertBefore(instrPrev);
  2438. LegalizeMD::LegalizeInstr(instrPrev, false);
  2439. instr->m_opcode = MDBranchOpcode(instr->m_opcode);
  2440. }
  2441. break;
  2442. }
  2443. return instr;
  2444. }
  2445. ///----------------------------------------------------------------------------
  2446. ///
  2447. /// LowererMD::ForceDstToReg
  2448. ///
  2449. ///----------------------------------------------------------------------------
  2450. IR::Instr*
  2451. LowererMD::ForceDstToReg(IR::Instr *instr)
  2452. {
  2453. IR::Opnd * dst = instr->GetDst();
  2454. if (dst->IsRegOpnd())
  2455. {
  2456. return instr;
  2457. }
  2458. IR::Instr * newInstr = instr->SinkDst(Js::OpCode::Ld_A);
  2459. LowererMD::ChangeToAssign(newInstr);
  2460. return newInstr;
  2461. }
  2462. IR::Instr *
  2463. LowererMD::LoadFunctionObjectOpnd(IR::Instr *instr, IR::Opnd *&functionObjOpnd)
  2464. {
  2465. IR::Opnd * src1 = instr->GetSrc1();
  2466. IR::Instr * instrPrev = instr->m_prev;
  2467. if (src1 == nullptr)
  2468. {
  2469. IR::RegOpnd * regOpnd = IR::RegOpnd::New(TyMachPtr, m_func);
  2470. //function object is first argument and mark it as IsParamSlotSym.
  2471. StackSym *paramSym = GetImplicitParamSlotSym(0);
  2472. IR::SymOpnd *paramOpnd = IR::SymOpnd::New(paramSym, TyMachPtr, m_func);
  2473. instrPrev = LowererMD::CreateAssign(regOpnd, paramOpnd, instr);
  2474. functionObjOpnd = instrPrev->GetDst();
  2475. }
  2476. else
  2477. {
  2478. // Inlinee LdSuper, use the function object opnd on the instruction
  2479. functionObjOpnd = instr->UnlinkSrc1();
  2480. if (!functionObjOpnd->IsRegOpnd())
  2481. {
  2482. Assert(functionObjOpnd->IsAddrOpnd());
  2483. }
  2484. }
  2485. return instrPrev;
  2486. }
  2487. IR::Instr *
  2488. LowererMD::LowerLdSuper(IR::Instr * instr, IR::JnHelperMethod helperOpCode)
  2489. {
  2490. IR::Opnd * functionObjOpnd;
  2491. IR::Instr * instrPrev = LoadFunctionObjectOpnd(instr, functionObjOpnd);
  2492. m_lowerer->LoadScriptContext(instr);
  2493. LoadHelperArgument(instr, functionObjOpnd);
  2494. ChangeToHelperCall(instr, helperOpCode);
  2495. return instrPrev;
  2496. }
  2497. void
  2498. LowererMD::GenerateFastDivByPow2(IR::Instr *instrDiv)
  2499. {
  2500. //// Given:
  2501. //// dst = Div_A src1, src2
  2502. //// where src2 == power of 2
  2503. ////
  2504. //// Generate:
  2505. //// (observation: positive q divides by p equally, where p = power of 2, if q's binary representation
  2506. //// has all zeroes to the right of p's power 2 bit, try to see if that is the case)
  2507. //// s1 = AND src1, 0x80000001 | ((src2Value - 1) << 1)
  2508. //// CMP s1, 1
  2509. //// BNE $doesntDivideEqually
  2510. //// s1 = ASR src1, log2(src2Value) -- do the equal divide
  2511. //// dst = EOR s1, 1 -- restore tagged int bit
  2512. //// B $done
  2513. //// $doesntDivideEqually:
  2514. //// (now check if it divides with the remainder of 1, for which we can do integer divide and accommodate with +0.5
  2515. //// note that we need only the part that is to the left of p's power 2 bit)
  2516. //// s1 = AND s1, 0x80000001 | (src2Value - 1)
  2517. //// CMP s1, 1
  2518. //// BNE $helper
  2519. //// s1 = ASR src1, log2(src2Value) + 1 -- do the integer divide and also shift out the tagged int bit
  2520. //// PUSH 0xXXXXXXXX (ScriptContext)
  2521. //// PUSH s1
  2522. //// dst = CALL Op_FinishOddDivByPow2 -- input: actual value, scriptContext; output: JavascriptNumber with 0.5 added to the input
  2523. //// JMP $done
  2524. //// $helper:
  2525. //// ...
  2526. //// $done:
  2527. //if (instrDiv->GetSrc1()->IsRegOpnd() && instrDiv->GetSrc1()->AsRegOpnd()->m_sym->m_isNotInt)
  2528. //{
  2529. // return;
  2530. //}
  2531. //IR::Opnd *dst = instrDiv->GetDst();
  2532. //IR::Opnd *src1 = instrDiv->GetSrc1();
  2533. //IR::AddrOpnd *src2 = instrDiv->GetSrc2()->IsAddrOpnd() ? instrDiv->GetSrc2()->AsAddrOpnd() : nullptr;
  2534. //IR::LabelInstr *doesntDivideEqually = IR::LabelInstr::New(Js::OpCode::Label, m_func);
  2535. //IR::LabelInstr *helper = IR::LabelInstr::New(Js::OpCode::Label, m_func, true);
  2536. //IR::LabelInstr *done = IR::LabelInstr::New(Js::OpCode::Label, m_func);
  2537. //IR::RegOpnd *s1 = IR::RegOpnd::New(TyVar, m_func);
  2538. //IR::Instr *instr;
  2539. //Assert(src2 && src2->IsVar() && Js::TaggedInt::Is(src2->m_address) && (Math::IsPow2(Js::TaggedInt::ToInt32(src2->m_address))));
  2540. //int32 src2Value = Js::TaggedInt::ToInt32(src2->m_address);
  2541. //// s1 = AND src1, 0x80000001 | ((src2Value - 1) << 1)
  2542. //instr = IR::Instr::New(Js::OpCode::AND, s1, src1, IR::IntConstOpnd::New((0x80000001 | ((src2Value - 1) << 1)), TyInt32, m_func), m_func);
  2543. //instrDiv->InsertBefore(instr);
  2544. //LegalizeMD::LegalizeInstr(instr, false);
  2545. //// CMP s1, 1
  2546. //instr = IR::Instr::New(Js::OpCode::CMP, m_func);
  2547. //instr->SetSrc1(s1);
  2548. //instr->SetSrc2(IR::IntConstOpnd::New(1, TyInt32, m_func));
  2549. //instrDiv->InsertBefore(instr);
  2550. //// BNE $doesntDivideEqually
  2551. //instr = IR::BranchInstr::New(Js::OpCode::BNE, doesntDivideEqually, m_func);
  2552. //instrDiv->InsertBefore(instr);
  2553. //// s1 = ASR src1, log2(src2Value) -- do the equal divide
  2554. //instr = IR::Instr::New(Js::OpCode::ASR, s1, src1, IR::IntConstOpnd::New(Math::Log2(src2Value), TyInt32, m_func), m_func);
  2555. //instrDiv->InsertBefore(instr);
  2556. //LegalizeMD::LegalizeInstr(instr, false);
  2557. //// dst = ORR s1, 1 -- restore tagged int bit
  2558. //instr = IR::Instr::New(Js::OpCode::ORR, dst, s1, IR::IntConstOpnd::New(1, TyInt32, m_func), m_func);
  2559. //instrDiv->InsertBefore(instr);
  2560. //LegalizeMD::LegalizeInstr(instr, false);
  2561. //
  2562. //// B $done
  2563. //instr = IR::BranchInstr::New(Js::OpCode::B, done, m_func);
  2564. //instrDiv->InsertBefore(instr);
  2565. //// $doesntDivideEqually:
  2566. //instrDiv->InsertBefore(doesntDivideEqually);
  2567. //// s1 = AND s1, 0x80000001 | (src2Value - 1)
  2568. //instr = IR::Instr::New(Js::OpCode::AND, s1, s1, IR::IntConstOpnd::New((0x80000001 | (src2Value - 1)), TyInt32, m_func), m_func);
  2569. //instrDiv->InsertBefore(instr);
  2570. //// CMP s1, 1
  2571. //instr = IR::Instr::New(Js::OpCode::CMP, m_func);
  2572. //instr->SetSrc1(s1);
  2573. //instr->SetSrc2(IR::IntConstOpnd::New(1, TyInt32, m_func));
  2574. //instrDiv->InsertBefore(instr);
  2575. //// BNE $helper
  2576. //instrDiv->InsertBefore(IR::BranchInstr::New(Js::OpCode::BNE, helper, m_func));
  2577. //// s1 = ASR src1, log2(src2Value) + 1 -- do the integer divide and also shift out the tagged int bit
  2578. //instr = IR::Instr::New(Js::OpCode::ASR, s1, src1, IR::IntConstOpnd::New(Math::Log2(src2Value) + 1, TyInt32, m_func), m_func);
  2579. //instrDiv->InsertBefore(instr);
  2580. //LegalizeMD::LegalizeInstr(instr, false);
  2581. //// Arg2: scriptContext
  2582. //IR::JnHelperMethod helperMethod;
  2583. //if (instrDiv->dstIsTempNumber)
  2584. //{
  2585. // // Var JavascriptMath::FinishOddDivByPow2_InPlace(uint32 value, ScriptContext *scriptContext, __out JavascriptNumber* result)
  2586. // helperMethod = IR::HelperOp_FinishOddDivByPow2InPlace;
  2587. // Assert(dst->IsRegOpnd());
  2588. // StackSym * tempNumberSym = this->m_lowerer->GetTempNumberSym(dst, instr->dstIsTempNumberTransferred);
  2589. // instr = this->LoadStackAddress(tempNumberSym);
  2590. // instrDiv->InsertBefore(instr);
  2591. // LegalizeMD::LegalizeInstr(instr, false);
  2592. // this->LoadHelperArgument(instrDiv, instr->GetDst());
  2593. //}
  2594. //else
  2595. //{
  2596. // // Var JavascriptMath::FinishOddDivByPow2(uint32 value, ScriptContext *scriptContext)
  2597. // helperMethod = IR::HelperOp_FinishOddDivByPow2;
  2598. //}
  2599. //this->m_lowerer->LoadScriptContext(instrDiv);
  2600. //// Arg1: value
  2601. //this->LoadHelperArgument(instrDiv, s1);
  2602. //// dst = CALL Op_FinishOddDivByPow2 -- input: actual value, output: JavascriptNumber with 0.5 added to the input
  2603. //instr = IR::Instr::New(Js::OpCode::Call, dst, IR::HelperCallOpnd::New(helperMethod, m_func), m_func);
  2604. //instrDiv->InsertBefore(instr);
  2605. //this->LowerCall(instr, 0);
  2606. //// JMP $done
  2607. //instrDiv->InsertBefore(IR::BranchInstr::New(Js::OpCode::B, done, m_func));
  2608. //// $helper:
  2609. //instrDiv->InsertBefore(helper);
  2610. //// $done:
  2611. //instrDiv->InsertAfter(done);
  2612. return;
  2613. }
  2614. bool
  2615. LowererMD::GenerateFastBrString(IR::BranchInstr* instrBr)
  2616. {
  2617. // Generates
  2618. //
  2619. // if operands not already in registers then bail; no fast path
  2620. // if operands not likely string or string then bail; no fast path
  2621. // if operands are not string then generate object test
  2622. // if branch is (Sr)Neq then $notEqual = instrBr->GetTarget()
  2623. // else $notEqual = $fail
  2624. //
  2625. // type1 = LDR [regSrc1 + offset(type)]
  2626. // type2 = LDR [regSrc2 + offset(type)]
  2627. // typeid1 = LDR [type1 + offset(typeid)]
  2628. // typeid2 = LDR [type2 + offset(typeid)]
  2629. //
  2630. // CMP typeid1, TypeIds_String
  2631. // BNE $helper
  2632. // CMP typeid2, TypeIds_String
  2633. // BNE $helper
  2634. //
  2635. // len1 = LDR [regSrc1 + offset(m_charLength)]
  2636. // len2 = LDR [regSrc2 + offset(m_charLength)]
  2637. //
  2638. // CMP len1, len2
  2639. // BNE $notEqual
  2640. //
  2641. // psz1 = LDR [regSrc1 + offset(m_pszValue)]
  2642. // psz2 = LDR [regSrc2 + offset(m_pszValue)]
  2643. //
  2644. // CMP psz1, 0
  2645. // BEQ $helper
  2646. // CMP psz2, 0
  2647. // BEQ $helper
  2648. //
  2649. // ch1 = LDR [psz1]
  2650. // ch2 = LDR [psz2]
  2651. //
  2652. // CMP ch1, ch2
  2653. // BNE $notEqual
  2654. //
  2655. // $helper:
  2656. // instrBr
  2657. //
  2658. // $fail:
  2659. Assert(instrBr->m_opcode == Js::OpCode::BrSrEq_A ||
  2660. instrBr->m_opcode == Js::OpCode::BrSrNeq_A ||
  2661. instrBr->m_opcode == Js::OpCode::BrEq_A ||
  2662. instrBr->m_opcode == Js::OpCode::BrNeq_A ||
  2663. instrBr->m_opcode == Js::OpCode::BrSrNotEq_A ||
  2664. instrBr->m_opcode == Js::OpCode::BrSrNotNeq_A ||
  2665. instrBr->m_opcode == Js::OpCode::BrNotEq_A ||
  2666. instrBr->m_opcode == Js::OpCode::BrNotNeq_A);
  2667. IR::RegOpnd *regSrc1 = instrBr->GetSrc1()->IsRegOpnd() ? instrBr->GetSrc1()->AsRegOpnd() : nullptr;
  2668. IR::RegOpnd *regSrc2 = instrBr->GetSrc2()->IsRegOpnd() ? instrBr->GetSrc2()->AsRegOpnd() : nullptr;
  2669. // Check that we likely have strings or know we have strings as the arguments
  2670. if (!regSrc1 || !regSrc2 ||
  2671. !regSrc1->GetValueType().IsLikelyString() ||
  2672. !regSrc2->GetValueType().IsLikelyString())
  2673. {
  2674. return false;
  2675. }
  2676. // Generate fast path code
  2677. IR::LabelInstr * labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  2678. IR::LabelInstr * labelFail = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  2679. IR::LabelInstr * labelTarget = instrBr->GetTarget();
  2680. IR::Instr * instr;
  2681. IR::IndirOpnd * indirOpnd;
  2682. if (instrBr->m_opcode == Js::OpCode::BrSrEq_A || instrBr->m_opcode == Js::OpCode::BrEq_A
  2683. || instrBr->m_opcode == Js::OpCode::BrNotNeq_A || instrBr->m_opcode == Js::OpCode::BrSrNotNeq_A)
  2684. {
  2685. labelTarget = labelFail;
  2686. }
  2687. if (!regSrc1->GetValueType().IsString() && !regSrc2->GetValueType().IsString())
  2688. {
  2689. GenerateObjectPairTest(regSrc1, regSrc2, instrBr, labelHelper);
  2690. }
  2691. else if (!regSrc1->GetValueType().IsString())
  2692. {
  2693. GenerateObjectTest(regSrc1, instrBr, labelHelper);
  2694. }
  2695. else if (!regSrc2->GetValueType().IsString())
  2696. {
  2697. GenerateObjectTest(regSrc2, instrBr, labelHelper);
  2698. }
  2699. // Check operands for TypeIds_String typeids
  2700. this->m_lowerer->GenerateStringTest(regSrc1, instrBr, labelHelper);
  2701. this->m_lowerer->GenerateStringTest(regSrc2, instrBr, labelHelper);
  2702. // Compare the lengths of the two strings and branch to $fail if not equal
  2703. // len1 = LDR [regSrc1 + offset(m_charLength)]
  2704. IR::RegOpnd *len1 = IR::RegOpnd::New(TyMachReg, this->m_func);
  2705. indirOpnd = IR::IndirOpnd::New(regSrc1, Js::JavascriptString::GetOffsetOfcharLength(), TyMachReg, this->m_func);
  2706. instr = IR::Instr::New(Js::OpCode::LDR, len1, indirOpnd, this->m_func);
  2707. instrBr->InsertBefore(instr);
  2708. // len2 = LDR [regSrc2 + offset(m_charLength)]
  2709. IR::RegOpnd *len2 = IR::RegOpnd::New(TyMachReg, this->m_func);
  2710. indirOpnd = IR::IndirOpnd::New(regSrc2, Js::JavascriptString::GetOffsetOfcharLength(), TyMachReg, this->m_func);
  2711. instr = IR::Instr::New(Js::OpCode::LDR, len2, indirOpnd, this->m_func);
  2712. instrBr->InsertBefore(instr);
  2713. // CMP len1, len2
  2714. instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  2715. instr->SetSrc1(len1);
  2716. instr->SetSrc2(len2);
  2717. instrBr->InsertBefore(instr);
  2718. // BNE $notEqual
  2719. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelTarget, this->m_func);
  2720. instrBr->InsertBefore(instr);
  2721. // Load string pointers and check for null
  2722. // psz1 = LDR [regSrc1 + offset(m_pszValue)]
  2723. IR::RegOpnd *psz1 = IR::RegOpnd::New(TyMachReg, this->m_func);
  2724. indirOpnd = IR::IndirOpnd::New(regSrc1, Js::JavascriptString::GetOffsetOfpszValue(), TyMachReg, this->m_func);
  2725. instr = IR::Instr::New(Js::OpCode::LDR, psz1, indirOpnd, this->m_func);
  2726. instrBr->InsertBefore(instr);
  2727. // psz2 = LDR [regSrc2 + offset(m_pszValue)]
  2728. IR::RegOpnd *psz2 = IR::RegOpnd::New(TyMachReg, this->m_func);
  2729. indirOpnd = IR::IndirOpnd::New(regSrc2, Js::JavascriptString::GetOffsetOfpszValue(), TyMachReg, this->m_func);
  2730. instr = IR::Instr::New(Js::OpCode::LDR, psz2, indirOpnd, this->m_func);
  2731. instrBr->InsertBefore(instr);
  2732. // CMP psz1, 0
  2733. instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  2734. instr->SetSrc1(psz1);
  2735. instr->SetSrc2(IR::IntConstOpnd::New(0, TyMachReg, this->m_func));
  2736. instrBr->InsertBefore(instr);
  2737. // BEQ $helper
  2738. instr = IR::BranchInstr::New(Js::OpCode::BEQ, labelHelper, this->m_func);
  2739. instrBr->InsertBefore(instr);
  2740. // CMP psz2, 0
  2741. instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  2742. instr->SetSrc1(psz2);
  2743. instr->SetSrc2(IR::IntConstOpnd::New(0, TyMachReg, this->m_func));
  2744. instrBr->InsertBefore(instr);
  2745. // BEQ $helper
  2746. instr = IR::BranchInstr::New(Js::OpCode::BEQ, labelHelper, this->m_func);
  2747. instrBr->InsertBefore(instr);
  2748. // ch1 = LDR [psz1]
  2749. IR::RegOpnd *ch1 = IR::RegOpnd::New(TyUint16, this->m_func);
  2750. indirOpnd = IR::IndirOpnd::New(psz1, 0, TyUint16, this->m_func);
  2751. instr = IR::Instr::New(Js::OpCode::LDR, ch1, indirOpnd, this->m_func);
  2752. instrBr->InsertBefore(instr);
  2753. // ch2 = LDR [psz2]
  2754. IR::RegOpnd *ch2 = IR::RegOpnd::New(TyUint16, this->m_func);
  2755. indirOpnd = IR::IndirOpnd::New(psz2, 0, TyUint16, this->m_func);
  2756. instr = IR::Instr::New(Js::OpCode::LDR, ch2, indirOpnd, this->m_func);
  2757. instrBr->InsertBefore(instr);
  2758. // CMP ch1, ch2
  2759. instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  2760. instr->SetSrc1(ch1);
  2761. instr->SetSrc2(ch2);
  2762. instrBr->InsertBefore(instr);
  2763. // BNE $notEqual
  2764. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelTarget, this->m_func);
  2765. instrBr->InsertBefore(instr);
  2766. // $helper:
  2767. // instrBr
  2768. //
  2769. // $fail:
  2770. instrBr->InsertBefore(labelHelper);
  2771. instrBr->InsertAfter(labelFail);
  2772. return true;
  2773. };
  2774. ///----------------------------------------------------------------------------
  2775. ///
  2776. /// LowererMD::GenerateFastCmSrEqConst
  2777. ///
  2778. ///----------------------------------------------------------------------------
  2779. bool
  2780. LowererMD::GenerateFastCmSrEqConst(IR::Instr *instr)
  2781. {
  2782. //
  2783. // Given:
  2784. // s1 = CmSrEq_A s2, s3
  2785. // where either s2 or s3 is 'null', 'true' or 'false'
  2786. //
  2787. // Generate:
  2788. //
  2789. // CMP s2, s3
  2790. // JEQ $mov_true
  2791. // MOV s1, Library.GetFalse()
  2792. // JMP $done
  2793. // $mov_true:
  2794. // MOV s1, Library.GetTrue()
  2795. // $done:
  2796. //
  2797. Assert(m_lowerer->IsConstRegOpnd(instr->GetSrc2()->AsRegOpnd()));
  2798. return false;
  2799. }
  2800. bool LowererMD::GenerateFastCmXxI4(IR::Instr *instr)
  2801. {
  2802. return this->GenerateFastCmXxTaggedInt(instr);
  2803. }
  2804. ///----------------------------------------------------------------------------
  2805. ///
  2806. /// LowererMD::GenerateFastCmXxTaggedInt
  2807. ///
  2808. ///----------------------------------------------------------------------------
  2809. bool LowererMD::GenerateFastCmXxTaggedInt(IR::Instr *instr)
  2810. {
  2811. // The idea is to do an inline compare if we can prove that both sources
  2812. // are tagged ints (i.e., are vars with the low bit set).
  2813. //
  2814. // Given:
  2815. //
  2816. // Cmxx_A dst, src1, src2
  2817. //
  2818. // Generate:
  2819. //
  2820. // (If not Int31's, goto $helper)
  2821. // LDIMM dst, trueResult
  2822. // CMP src1, src2
  2823. // BEQ $fallthru
  2824. // LDIMM dst, falseResult
  2825. // B $fallthru
  2826. // $helper:
  2827. // (caller will generate normal helper call sequence)
  2828. // $fallthru:
  2829. IR::Opnd * src1 = instr->GetSrc1();
  2830. IR::Opnd * src2 = instr->GetSrc2();
  2831. IR::Opnd * dst = instr->GetDst();
  2832. IR::LabelInstr * helper = IR::LabelInstr::New(Js::OpCode::Label, m_func, true);
  2833. IR::LabelInstr * fallthru = IR::LabelInstr::New(Js::OpCode::Label, m_func);
  2834. Assert(src1 && src2 && dst);
  2835. // Not tagged ints?
  2836. if (src1->IsRegOpnd() && src1->AsRegOpnd()->m_sym->m_isNotInt)
  2837. {
  2838. return false;
  2839. }
  2840. if (src2->IsRegOpnd() && src2->AsRegOpnd()->m_sym->m_isNotInt)
  2841. {
  2842. return false;
  2843. }
  2844. Js::OpCode opcode = Js::OpCode::InvalidOpCode;
  2845. switch ( instr->m_opcode)
  2846. {
  2847. case Js::OpCode::CmEq_A:
  2848. case Js::OpCode::CmSrEq_A:
  2849. case Js::OpCode::CmEq_I4:
  2850. opcode = Js::OpCode::BEQ;
  2851. break;
  2852. case Js::OpCode::CmNeq_A:
  2853. case Js::OpCode::CmSrNeq_A:
  2854. case Js::OpCode::CmNeq_I4:
  2855. opcode = Js::OpCode::BNE;
  2856. break;
  2857. case Js::OpCode::CmGt_A:
  2858. case Js::OpCode::CmGt_I4:
  2859. opcode = Js::OpCode::BGT;
  2860. break;
  2861. case Js::OpCode::CmGe_A:
  2862. case Js::OpCode::CmGe_I4:
  2863. opcode = Js::OpCode::BGE;
  2864. break;
  2865. case Js::OpCode::CmLt_A:
  2866. case Js::OpCode::CmLt_I4:
  2867. opcode = Js::OpCode::BLT;
  2868. break;
  2869. case Js::OpCode::CmLe_A:
  2870. case Js::OpCode::CmLe_I4:
  2871. opcode = Js::OpCode::BLE;
  2872. break;
  2873. case Js::OpCode::CmUnGt_A:
  2874. case Js::OpCode::CmUnGt_I4:
  2875. opcode = Js::OpCode::BHI;
  2876. break;
  2877. case Js::OpCode::CmUnGe_A:
  2878. case Js::OpCode::CmUnGe_I4:
  2879. opcode = Js::OpCode::BCS;
  2880. break;
  2881. case Js::OpCode::CmUnLt_A:
  2882. case Js::OpCode::CmUnLt_I4:
  2883. opcode = Js::OpCode::BCC;
  2884. break;
  2885. case Js::OpCode::CmUnLe_A:
  2886. case Js::OpCode::CmUnLe_I4:
  2887. opcode = Js::OpCode::BLS;
  2888. break;
  2889. default: Assert(false);
  2890. }
  2891. // Tagged ints?
  2892. bool isTaggedInts = false;
  2893. if (src1->IsTaggedInt() || src1->IsInt32())
  2894. {
  2895. if (src2->IsTaggedInt() || src2->IsInt32())
  2896. {
  2897. isTaggedInts = true;
  2898. }
  2899. }
  2900. if (!isTaggedInts)
  2901. {
  2902. this->GenerateSmIntPairTest(instr, src1, src2, helper);
  2903. }
  2904. if (dst->IsEqual(src1))
  2905. {
  2906. IR::RegOpnd *newSrc1 = IR::RegOpnd::New(TyMachReg, m_func);
  2907. LowererMD::CreateAssign(newSrc1, src1, instr);
  2908. src1 = newSrc1;
  2909. }
  2910. else if (dst->IsEqual(src2))
  2911. {
  2912. IR::RegOpnd *newSrc2 = IR::RegOpnd::New(TyMachReg, m_func);
  2913. LowererMD::CreateAssign(newSrc2, src2, instr);
  2914. src2 = newSrc2;
  2915. }
  2916. IR::Opnd *opndTrue, *opndFalse;
  2917. if (dst->IsInt32())
  2918. {
  2919. opndTrue = IR::IntConstOpnd::New(1, TyMachReg, this->m_func);
  2920. opndFalse = IR::IntConstOpnd::New(0, TyMachReg, this->m_func);
  2921. }
  2922. else
  2923. {
  2924. opndTrue = m_lowerer->LoadLibraryValueOpnd(instr, LibraryValue::ValueTrue);
  2925. opndFalse = m_lowerer->LoadLibraryValueOpnd(instr, LibraryValue::ValueFalse);
  2926. }
  2927. // LDIMM dst, trueResult
  2928. // CMP src1, src2
  2929. // BEQ $fallthru
  2930. // LDIMM dst, falseResult
  2931. // B $fallthru
  2932. instr->InsertBefore(IR::Instr::New(Js::OpCode::LDIMM, dst, opndTrue, m_func));
  2933. IR::Instr *instrCmp = IR::Instr::New(Js::OpCode::CMP, m_func);
  2934. instrCmp->SetSrc1(src1);
  2935. instrCmp->SetSrc2(src2);
  2936. instr->InsertBefore(instrCmp);
  2937. LegalizeMD::LegalizeInstr(instrCmp,false);
  2938. instr->InsertBefore(IR::BranchInstr::New(opcode, fallthru, m_func));
  2939. instr->InsertBefore(IR::Instr::New(Js::OpCode::LDIMM, dst, opndFalse, m_func));
  2940. if (isTaggedInts)
  2941. {
  2942. instr->InsertAfter(fallthru);
  2943. instr->Remove();
  2944. return true;
  2945. }
  2946. // B $fallthru
  2947. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::B, fallthru, m_func));
  2948. instr->InsertBefore(helper);
  2949. instr->InsertAfter(fallthru);
  2950. return false;
  2951. }
  2952. IR::Instr * LowererMD::GenerateConvBool(IR::Instr *instr)
  2953. {
  2954. // dst = LDIMM true
  2955. // TST src1, src2
  2956. // BNE fallthrough
  2957. // dst = LDIMM false
  2958. // fallthrough:
  2959. IR::RegOpnd *dst = instr->GetDst()->AsRegOpnd();
  2960. IR::RegOpnd *src1 = instr->GetSrc1()->AsRegOpnd();
  2961. IR::Opnd *opndTrue = m_lowerer->LoadLibraryValueOpnd(instr, LibraryValue::ValueTrue);
  2962. IR::Opnd *opndFalse = m_lowerer->LoadLibraryValueOpnd(instr, LibraryValue::ValueFalse);
  2963. IR::LabelInstr *fallthru = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  2964. // dst = LDIMM true
  2965. IR::Instr *instrFirst = IR::Instr::New(Js::OpCode::LDIMM, dst, opndTrue, m_func);
  2966. instr->InsertBefore(instrFirst);
  2967. // TST src1, src2
  2968. IR::Instr *instrTst = IR::Instr::New(Js::OpCode::TST, m_func);
  2969. instrTst->SetSrc1(src1);
  2970. instrTst->SetSrc2(src1);
  2971. instr->InsertBefore(instrTst);
  2972. LegalizeMD::LegalizeInstr(instrTst, false);
  2973. // BNE fallthrough
  2974. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BNE, fallthru, m_func));
  2975. // dst = LDIMM false
  2976. instr->InsertBefore(IR::Instr::New(Js::OpCode::LDIMM, dst, opndFalse, m_func));
  2977. // fallthrough:
  2978. instr->InsertAfter(fallthru);
  2979. instr->Remove();
  2980. return instrFirst;
  2981. }
  2982. ///----------------------------------------------------------------------------
  2983. ///
  2984. /// LowererMD::GenerateFastAdd
  2985. ///
  2986. /// NOTE: We assume that only the sum of two Int31's will have 0x2 set. This
  2987. /// is only true until we have a var type with tag == 0x2.
  2988. ///
  2989. ///----------------------------------------------------------------------------
  2990. bool
  2991. LowererMD::GenerateFastAdd(IR::Instr * instrAdd)
  2992. {
  2993. // Given:
  2994. //
  2995. // dst = Add src1, src2
  2996. //
  2997. // Generate:
  2998. //
  2999. // (If not 2 Int31's, use $helper.)
  3000. // s1 = SUB src1, 1 -- get rid of one of the tag
  3001. // tmp = ADDS s1, src2 -- try an inline add
  3002. // BVS $helper
  3003. // dst = MOV tmp
  3004. // B $done
  3005. // $helper:
  3006. // (caller generates helper call)
  3007. // $done:
  3008. IR::Instr * instr;
  3009. IR::LabelInstr * labelHelper;
  3010. IR::LabelInstr * labelDone;
  3011. IR::Opnd * opndReg;
  3012. IR::Opnd * opndSrc1;
  3013. IR::Opnd * opndSrc2;
  3014. opndSrc1 = instrAdd->GetSrc1();
  3015. opndSrc2 = instrAdd->GetSrc2();
  3016. AssertMsg(opndSrc1 && opndSrc2, "Expected 2 src opnd's on Add instruction");
  3017. // Generate fastpath for Incr_A anyway -
  3018. // Incrementing strings representing integers can be inter-mixed with integers e.g. "1"++ -> converts 1 to an int and thereafter, integer increment is expected.
  3019. if (opndSrc1->IsRegOpnd() && (opndSrc1->AsRegOpnd()->m_sym->m_isNotInt || opndSrc1->GetValueType().IsString()
  3020. || (instrAdd->m_opcode != Js::OpCode::Incr_A && opndSrc1->GetValueType().IsLikelyString())))
  3021. {
  3022. return false;
  3023. }
  3024. if (opndSrc2->IsRegOpnd() && (opndSrc2->AsRegOpnd()->m_sym->m_isNotInt ||
  3025. opndSrc2->GetValueType().IsLikelyString()))
  3026. {
  3027. return true;
  3028. }
  3029. // Load src's at the top so we don't have to do it repeatedly.
  3030. if (!opndSrc1->IsRegOpnd())
  3031. {
  3032. opndSrc1 = IR::RegOpnd::New(opndSrc1->GetType(), this->m_func);
  3033. LowererMD::CreateAssign(opndSrc1, instrAdd->GetSrc1(), instrAdd);
  3034. }
  3035. if (!opndSrc2->IsRegOpnd())
  3036. {
  3037. opndSrc2 = IR::RegOpnd::New(opndSrc2->GetType(), this->m_func);
  3038. LowererMD::CreateAssign(opndSrc2, instrAdd->GetSrc2(), instrAdd);
  3039. }
  3040. labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  3041. // Tagged ints?
  3042. bool isTaggedInts = opndSrc1->IsTaggedInt() && opndSrc2->IsTaggedInt();
  3043. if (!isTaggedInts)
  3044. {
  3045. // (If not 2 Int31's, jump to $helper.)
  3046. this->GenerateSmIntPairTest(instrAdd, opndSrc1, opndSrc2, labelHelper);
  3047. }
  3048. if (opndSrc1->IsAddrOpnd())
  3049. {
  3050. // If opnd1 is a constant, just swap them.
  3051. Swap(opndSrc1, opndSrc2);
  3052. }
  3053. // s1 = SUB src1, 1 -- get rid of one of the tag
  3054. opndReg = IR::RegOpnd::New(TyInt32, this->m_func);
  3055. instr = IR::Instr::New(Js::OpCode::SUB, opndReg, opndSrc1, IR::IntConstOpnd::New(1, TyMachReg, this->m_func), this->m_func);
  3056. instrAdd->InsertBefore(instr);
  3057. // tmp = ADDS s1, src2 -- try an inline add
  3058. IR::RegOpnd *opndTmp = IR::RegOpnd::New(TyMachReg, this->m_func);
  3059. instr = IR::Instr::New(Js::OpCode::ADDS, opndTmp, opndReg, opndSrc2, this->m_func);
  3060. instrAdd->InsertBefore(instr);
  3061. // BVS $helper -- if overflow, branch to helper.
  3062. instr = IR::BranchInstr::New(Js::OpCode::BVS, labelHelper, this->m_func);
  3063. instrAdd->InsertBefore(instr);
  3064. // dst = MOV tmp
  3065. LowererMD::CreateAssign(instrAdd->GetDst(), opndTmp, instrAdd);
  3066. // B $done
  3067. labelDone = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  3068. instr = IR::BranchInstr::New(Js::OpCode::B, labelDone, this->m_func);
  3069. instrAdd->InsertBefore(instr);
  3070. // $helper:
  3071. // (caller generates helper call)
  3072. // $done:
  3073. instrAdd->InsertBefore(labelHelper);
  3074. instrAdd->InsertAfter(labelDone);
  3075. // Return true to indicate the original instr must still be lowered.
  3076. return true;
  3077. }
  3078. ///----------------------------------------------------------------------------
  3079. ///
  3080. /// LowererMD::GenerateFastSub
  3081. ///
  3082. ///
  3083. ///----------------------------------------------------------------------------
  3084. bool
  3085. LowererMD::GenerateFastSub(IR::Instr * instrSub)
  3086. {
  3087. // Given:
  3088. //
  3089. // dst = Sub src1, src2
  3090. //
  3091. // Generate:
  3092. //
  3093. // (If not 2 Int31's, jump to $helper.)
  3094. // s1 = SUBS src1, src2 -- try an inline sub
  3095. // BVS $helper -- bail if the subtract overflowed
  3096. // dst = ADD s1, 1 -- restore the var tag on the result
  3097. // B $fallthru
  3098. // $helper:
  3099. // (caller generates helper call)
  3100. // $fallthru:
  3101. IR::Instr * instr;
  3102. IR::LabelInstr * labelHelper;
  3103. IR::LabelInstr * labelFallThru;
  3104. IR::Opnd * opndReg;
  3105. IR::Opnd * opndSrc1;
  3106. IR::Opnd * opndSrc2;
  3107. opndSrc1 = instrSub->GetSrc1();
  3108. opndSrc2 = instrSub->GetSrc2();
  3109. AssertMsg(opndSrc1 && opndSrc2, "Expected 2 src opnd's on Sub instruction");
  3110. // Not tagged ints?
  3111. if (opndSrc1->IsRegOpnd() && opndSrc1->AsRegOpnd()->m_sym->m_isNotInt ||
  3112. opndSrc2->IsRegOpnd() && opndSrc2->AsRegOpnd()->m_sym->m_isNotInt)
  3113. {
  3114. return false;
  3115. }
  3116. // Load src's at the top so we don't have to do it repeatedly.
  3117. if (!opndSrc1->IsRegOpnd())
  3118. {
  3119. opndSrc1 = IR::RegOpnd::New(opndSrc1->GetType(), this->m_func);
  3120. LowererMD::CreateAssign(opndSrc1, instrSub->GetSrc1(), instrSub);
  3121. }
  3122. if (!opndSrc2->IsRegOpnd())
  3123. {
  3124. opndSrc2 = IR::RegOpnd::New(opndSrc2->GetType(), this->m_func);
  3125. LowererMD::CreateAssign(opndSrc2, instrSub->GetSrc2(), instrSub);
  3126. }
  3127. labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  3128. // Tagged ints?
  3129. bool isTaggedInts = opndSrc1->IsTaggedInt() && opndSrc2->IsTaggedInt();
  3130. if (!isTaggedInts)
  3131. {
  3132. // (If not 2 Int31's, jump to $helper.)
  3133. this->GenerateSmIntPairTest(instrSub, opndSrc1, opndSrc2, labelHelper);
  3134. }
  3135. // s1 = SUBS src1, src2 -- try an inline sub
  3136. opndReg = IR::RegOpnd::New(TyInt32, this->m_func);
  3137. instr = IR::Instr::New(Js::OpCode::SUBS, opndReg, opndSrc1, opndSrc2, this->m_func);
  3138. instrSub->InsertBefore(instr);
  3139. // BVS $helper -- bail if the subtract overflowed
  3140. instr = IR::BranchInstr::New(Js::OpCode::BVS, labelHelper, this->m_func);
  3141. instrSub->InsertBefore(instr);
  3142. // dst = ADD s1, 1 -- restore the var tag on the result
  3143. instr = IR::Instr::New(Js::OpCode::ADD, instrSub->GetDst(), opndReg, IR::IntConstOpnd::New(1, TyMachReg, this->m_func), this->m_func);
  3144. instrSub->InsertBefore(instr);
  3145. LegalizeMD::LegalizeInstr(instr, false);
  3146. // B $fallthru
  3147. labelFallThru = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  3148. instr = IR::BranchInstr::New(Js::OpCode::B, labelFallThru, this->m_func);
  3149. instrSub->InsertBefore(instr);
  3150. // $helper:
  3151. // (caller generates helper call)
  3152. // $fallthru:
  3153. instrSub->InsertBefore(labelHelper);
  3154. instrSub->InsertAfter(labelFallThru);
  3155. // Return true to indicate the original instr must still be lowered.
  3156. return true;
  3157. }
  3158. ///----------------------------------------------------------------------------
  3159. ///
  3160. /// LowererMD::GenerateFastMul
  3161. ///
  3162. ///----------------------------------------------------------------------------
  3163. bool
  3164. LowererMD::GenerateFastMul(IR::Instr * instrMul)
  3165. {
  3166. // Given:
  3167. //
  3168. // dst = Mul src1, src2
  3169. //
  3170. // Generate:
  3171. //
  3172. // (If not 2 Int31's, jump to $helper.)
  3173. // s1 = SUB src1, AtomTag -- clear the var tag from the value to be multiplied
  3174. // s2 = ASR src2, Js::VarTag_Shift -- extract the real src2 amount from the var
  3175. // (r12:)s1 = SMULL s1, (r12,) s1, s2 -- do the signed mul into 64bit r12:s1, the result will be src1 * src2 * 2
  3176. // (SMULL doesn't set the flags but we don't have 32bit overflow <=> r12-unsigned ? r12==0 : all 33 bits of 64bit result are 1's
  3177. // CMP r12, s1, ASR #31 -- check for overflow (== means no overflow)
  3178. // BNE $helper -- bail if the result overflowed
  3179. // TST s1, s1 -- Check 0 vs -0 (Javascript number is technically double, so need to account for -0)
  3180. // BNE $result -- TODO: consider converting 2 instructions into one: CBZ s1, $zero
  3181. // (result of mul was 0. Account for -0)
  3182. // s2 = ADDS s2, src1 -- MUL is 0 => one of (src1, src2) is 0, see if the other one is positive or negative
  3183. // BGT $result -- positive 0. keep it as int31
  3184. // dst= ToVar(-0.0) -- load negative 0
  3185. // B $fallthru
  3186. // $result:
  3187. // dst= ORR s1, AtomTag -- make sure var tag is set on the result
  3188. // B $fallthru
  3189. // $helper:
  3190. // (caller generates helper call)
  3191. // $fallthru:
  3192. IR::LabelInstr * labelHelper;
  3193. IR::LabelInstr * labelFallThru;
  3194. IR::LabelInstr * labelResult;
  3195. IR::Instr * instr;
  3196. IR::RegOpnd * opndReg1;
  3197. IR::RegOpnd * opndReg2;
  3198. IR::Opnd * opndSrc1;
  3199. IR::Opnd * opndSrc2;
  3200. opndSrc1 = instrMul->GetSrc1();
  3201. opndSrc2 = instrMul->GetSrc2();
  3202. AssertMsg(opndSrc1 && opndSrc2, "Expected 2 src opnd's on mul instruction");
  3203. // (If not 2 Int31's, jump to $helper.)
  3204. if (opndSrc1->IsRegOpnd() && opndSrc1->AsRegOpnd()->m_sym->m_isNotInt ||
  3205. opndSrc2->IsRegOpnd() && opndSrc2->AsRegOpnd()->m_sym->m_isNotInt)
  3206. {
  3207. return true;
  3208. }
  3209. labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  3210. labelResult = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  3211. labelFallThru = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  3212. // Load src's at the top so we don't have to do it repeatedly.
  3213. if (!opndSrc1->IsRegOpnd())
  3214. {
  3215. opndSrc1 = IR::RegOpnd::New(opndSrc1->GetType(), this->m_func);
  3216. LowererMD::CreateAssign(opndSrc1, instrMul->GetSrc1(), instrMul);
  3217. }
  3218. if (!opndSrc2->IsRegOpnd())
  3219. {
  3220. opndSrc2 = IR::RegOpnd::New(opndSrc2->GetType(), this->m_func);
  3221. LowererMD::CreateAssign(opndSrc2, instrMul->GetSrc2(), instrMul);
  3222. }
  3223. bool isTaggedInts = opndSrc1->IsTaggedInt() && opndSrc2->IsTaggedInt();
  3224. if (!isTaggedInts)
  3225. {
  3226. // (If not 2 Int31's, jump to $helper.)
  3227. this->GenerateSmIntPairTest(instrMul, opndSrc1->AsRegOpnd(), opndSrc2->AsRegOpnd(), labelHelper);
  3228. }
  3229. // s1 = SUB src1, AtomTag -- clear the var tag from the value to be multiplied
  3230. opndReg1 = IR::RegOpnd::New(TyInt32, this->m_func);
  3231. instr = IR::Instr::New(Js::OpCode::SUB, opndReg1, opndSrc1, IR::IntConstOpnd::New(Js::AtomTag, TyVar, this->m_func), this->m_func); // TODO: TyVar or TyMachReg?
  3232. instrMul->InsertBefore(instr);
  3233. // s2 = ASR src2, Js::VarTag_Shift -- extract the real src2 amount from the var
  3234. opndReg2 = IR::RegOpnd::New(TyInt32, this->m_func);
  3235. instr = IR::Instr::New(Js::OpCode::ASR, opndReg2, opndSrc2,
  3236. IR::IntConstOpnd::New(Js::VarTag_Shift, TyInt8, this->m_func), this->m_func);
  3237. instrMul->InsertBefore(instr);
  3238. // (r12:)s1 = SMULL s1, (r12,) s1, s2 -- do the signed mul into 64bit r12:s1, the result will be src1 * src2 * 2
  3239. instr = IR::Instr::New(Js::OpCode::SMULL, opndReg1, opndReg1, opndReg2, this->m_func);
  3240. instrMul->InsertBefore(instr);
  3241. // (SMULL doesn't set the flags but we don't have 32bit overflow <=> r12-unsigned ? r12==0 : all 33 bits of 64bit result are 1's
  3242. // CMP r12, s1, ASR #31 -- check for overflow (== means no overflow)
  3243. IR::RegOpnd* opndRegR12 = IR::RegOpnd::New(nullptr, RegR12, TyMachReg, this->m_func);
  3244. instr = IR::Instr::New(Js::OpCode::CMP_ASR31, this->m_func);
  3245. instr->SetSrc1(opndRegR12);
  3246. instr->SetSrc2(opndReg1);
  3247. instrMul->InsertBefore(instr);
  3248. // BNE $helper -- bail if the result overflowed
  3249. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelHelper, this->m_func);
  3250. instrMul->InsertBefore(instr);
  3251. // TST s1, s1 -- Check 0 vs -0 (Javascript number is technically double, so need to account for -0)
  3252. instr = IR::Instr::New(Js::OpCode::TST, this->m_func);
  3253. instr->SetSrc1(opndReg1);
  3254. instr->SetSrc2(opndReg1);
  3255. instrMul->InsertBefore(instr);
  3256. // BNE $result -- TODO: consider converting 2 instructions into one: CBZ s1, $zero
  3257. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelResult, this->m_func);
  3258. instrMul->InsertBefore(instr);
  3259. // (result of mul was 0. Account for -0)
  3260. // s2 = ADDS s2, src1 -- MUL is 0 => one of (src1, src2) is 0, see if the other one is positive or negative
  3261. instr = IR::Instr::New(Js::OpCode::ADDS, opndReg2, opndReg2, opndSrc1, this->m_func);
  3262. instrMul->InsertBefore(instr);
  3263. // BGT $result -- positive 0. keep it as int31
  3264. instr = IR::BranchInstr::New(Js::OpCode::BGT, labelResult, this->m_func);
  3265. instrMul->InsertBefore(instr);
  3266. // dst= ToVar(-0.0) -- load negative 0
  3267. instr = LowererMD::CreateAssign(instrMul->GetDst(), m_lowerer->LoadLibraryValueOpnd(instrMul, LibraryValue::ValueNegativeZero), instrMul);
  3268. // No need to insert: CreateAssign creates legalized instr and inserts it.
  3269. // B $fallthru
  3270. instr = IR::BranchInstr::New(Js::OpCode::B, labelFallThru, this->m_func);
  3271. instrMul->InsertBefore(instr);
  3272. // $result:
  3273. instrMul->InsertBefore(labelResult);
  3274. // dst= ORR s1, AtomTag -- make sure var tag is set on the result
  3275. instr = IR::Instr::New(Js::OpCode::ORR, instrMul->GetDst(), opndReg1, IR::IntConstOpnd::New(Js::AtomTag, TyVar, this->m_func), this->m_func);
  3276. instrMul->InsertBefore(instr);
  3277. LegalizeMD::LegalizeInstr(instr, false);
  3278. // B $fallthru
  3279. instr = IR::BranchInstr::New(Js::OpCode::B, labelFallThru, this->m_func);
  3280. instrMul->InsertBefore(instr);
  3281. // $helper:
  3282. // (caller generates helper call)
  3283. // $fallthru:
  3284. instrMul->InsertBefore(labelHelper);
  3285. instrMul->InsertAfter(labelFallThru);
  3286. // Return true to indicate the original instr must still be lowered.
  3287. return true;
  3288. }
  3289. ///----------------------------------------------------------------------------
  3290. ///
  3291. /// LowererMD::GenerateFastAnd
  3292. ///
  3293. ///----------------------------------------------------------------------------
  3294. bool
  3295. LowererMD::GenerateFastAnd(IR::Instr * instrAnd)
  3296. {
  3297. // Given:
  3298. //
  3299. // dst = And src1, src2
  3300. //
  3301. // Generate:
  3302. //
  3303. //
  3304. // If dst is reg:
  3305. //
  3306. // dst = AND src1, src2
  3307. // TST dst, 1
  3308. // BNE $done
  3309. // (caller generates helper sequence)
  3310. // $done:
  3311. // If dst is not reg:
  3312. //
  3313. // dstReg = AND src1, src2
  3314. // TST dstReg, 1
  3315. // BEQ $helper
  3316. // dst = STR dstReg
  3317. // B $done
  3318. // $helper
  3319. // (caller generates helper sequence)
  3320. // $done:
  3321. IR::Opnd *dst = instrAnd->GetDst();
  3322. IR::Opnd *src1 = instrAnd->GetSrc1();
  3323. IR::Opnd *src2 = instrAnd->GetSrc2();
  3324. IR::Instr *instr;
  3325. // Not tagged ints?
  3326. if (src1->IsRegOpnd() && src1->AsRegOpnd()->m_sym->m_isNotInt)
  3327. {
  3328. return true;
  3329. }
  3330. if (src2->IsRegOpnd() && src2->AsRegOpnd()->m_sym->m_isNotInt)
  3331. {
  3332. return true;
  3333. }
  3334. bool isInt = src1->IsTaggedInt() && src2->IsTaggedInt();
  3335. if (!isInt)
  3336. {
  3337. if (!dst->IsRegOpnd() || dst->IsEqual(src1) || dst->IsEqual(src2))
  3338. {
  3339. // Put the result in a reg and store it only when we know it's final.
  3340. dst = IR::RegOpnd::New(dst->GetType(), this->m_func);
  3341. }
  3342. }
  3343. // dstReg = AND src1, src2
  3344. instr = IR::Instr::New(Js::OpCode::AND, dst, src1, src2, this->m_func);
  3345. instrAnd->InsertBefore(instr);
  3346. LegalizeMD::LegalizeInstr(instr, false);
  3347. if (isInt)
  3348. {
  3349. // If both src's are ints, then we're done, and we need no helper call.
  3350. instrAnd->Remove();
  3351. return false;
  3352. }
  3353. // TST dstReg, 1
  3354. instr = IR::Instr::New(Js::OpCode::TST, this->m_func);
  3355. instr->SetSrc1(dst);
  3356. instr->SetSrc2(IR::IntConstOpnd::New(Js::AtomTag, TyMachReg, this->m_func));
  3357. instrAnd->InsertBefore(instr);
  3358. IR::LabelInstr *labelDone = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  3359. if (dst == instrAnd->GetDst())
  3360. {
  3361. // BNE $done
  3362. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelDone, this->m_func);
  3363. instrAnd->InsertBefore(instr);
  3364. }
  3365. else
  3366. {
  3367. // BEQ $helper
  3368. IR::LabelInstr *labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  3369. instr = IR::BranchInstr::New(Js::OpCode::BEQ, labelHelper, this->m_func);
  3370. instrAnd->InsertBefore(instr);
  3371. // dst = STR dstReg
  3372. LowererMD::CreateAssign(instrAnd->GetDst(), dst, instrAnd);
  3373. // B $done
  3374. instr = IR::BranchInstr::New(Js::OpCode::B, labelDone, this->m_func);
  3375. instrAnd->InsertBefore(instr);
  3376. // $helper
  3377. instrAnd->InsertBefore(labelHelper);
  3378. }
  3379. // (caller generates helper sequence)
  3380. // $done:
  3381. instrAnd->InsertAfter(labelDone);
  3382. // Return true to indicate the original instr must still be lowered.
  3383. return true;
  3384. }
  3385. ///----------------------------------------------------------------------------
  3386. ///
  3387. /// LowererMD::GenerateFastOr
  3388. ///
  3389. ///----------------------------------------------------------------------------
  3390. bool
  3391. LowererMD::GenerateFastOr(IR::Instr * instrOr)
  3392. {
  3393. // Given:
  3394. //
  3395. // dst = Or src1, src2
  3396. //
  3397. // Generate:
  3398. //
  3399. // (If not 2 Int31's, jump to $helper.)
  3400. //
  3401. // dst = OR src1, src2
  3402. // B $done
  3403. // $helper:
  3404. // (caller generates helper sequence)
  3405. // $fallthru:
  3406. IR::Opnd *src1 = instrOr->GetSrc1();
  3407. IR::Opnd *src2 = instrOr->GetSrc2();
  3408. IR::Opnd *dst = instrOr->GetDst();
  3409. IR::Instr *instr;
  3410. IR::LabelInstr *labelHelper = nullptr;
  3411. // Not tagged ints?
  3412. if (src1->IsRegOpnd() && src1->AsRegOpnd()->m_sym->m_isNotInt)
  3413. {
  3414. return true;
  3415. }
  3416. if (src2->IsRegOpnd() && src2->AsRegOpnd()->m_sym->m_isNotInt)
  3417. {
  3418. return true;
  3419. }
  3420. // Tagged ints?
  3421. bool isInt = src1->IsTaggedInt() && src2->IsTaggedInt();
  3422. // Load the src's at the top so we don't have to do it repeatedly.
  3423. if (!src1->IsRegOpnd())
  3424. {
  3425. src1 = IR::RegOpnd::New(src1->GetType(), this->m_func);
  3426. LowererMD::CreateAssign(src1, instrOr->GetSrc1(), instrOr);
  3427. }
  3428. if (!src2->IsRegOpnd())
  3429. {
  3430. src2 = IR::RegOpnd::New(src2->GetType(), this->m_func);
  3431. LowererMD::CreateAssign(src2, instrOr->GetSrc2(), instrOr);
  3432. }
  3433. if (!isInt)
  3434. {
  3435. // (If not 2 Int31's, jump to $helper.)
  3436. labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  3437. this->GenerateSmIntPairTest(instrOr, src1, src2, labelHelper);
  3438. }
  3439. // dst = OR src1, src2
  3440. instr = IR::Instr::New(Js::OpCode::ORR, dst, src1, src2, this->m_func);
  3441. instrOr->InsertBefore(instr);
  3442. LegalizeMD::LegalizeInstr(instr, false);
  3443. if (isInt)
  3444. {
  3445. // If both src's are ints, then we're done, and we don't need a helper call.
  3446. instrOr->Remove();
  3447. return false;
  3448. }
  3449. // B $done
  3450. IR::LabelInstr *labelDone = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  3451. instr = IR::BranchInstr::New(Js::OpCode::B, labelDone, this->m_func);
  3452. instrOr->InsertBefore(instr);
  3453. // $helper:
  3454. // (caller generates helper sequence)
  3455. // $done:
  3456. instrOr->InsertBefore(labelHelper);
  3457. instrOr->InsertAfter(labelDone);
  3458. // Return true to indicate the original instr must still be lowered.
  3459. return true;
  3460. }
  3461. ///----------------------------------------------------------------------------
  3462. ///
  3463. /// LowererMD::GenerateFastXor
  3464. ///
  3465. ///----------------------------------------------------------------------------
  3466. bool
  3467. LowererMD::GenerateFastXor(IR::Instr * instrXor)
  3468. {
  3469. // Given:
  3470. //
  3471. // dst = Xor src1, src2
  3472. //
  3473. // Generate:
  3474. //
  3475. // (If not 2 Int31's, jump to $helper.)
  3476. //
  3477. // s1 = MOV src1
  3478. // s1 = XOR s1, src2 -- try an inline XOR
  3479. // s1 = INC s1
  3480. // dst = MOV s1
  3481. // JMP $fallthru
  3482. // $helper:
  3483. // (caller generates helper sequence)
  3484. // $fallthru:
  3485. // Return true to indicate the original instr must still be lowered.
  3486. return true;
  3487. }
  3488. //----------------------------------------------------------------------------
  3489. //
  3490. // LowererMD::GenerateFastNot
  3491. //
  3492. //----------------------------------------------------------------------------
  3493. bool
  3494. LowererMD::GenerateFastNot(IR::Instr * instrNot)
  3495. {
  3496. // Given:
  3497. //
  3498. // dst = Not src
  3499. //
  3500. // Generate:
  3501. //
  3502. // TST src, 1
  3503. // BEQ $helper
  3504. // dst = MVN src
  3505. // dst = INC dst
  3506. // JMP $done
  3507. // $helper:
  3508. // (caller generates helper call)
  3509. // $done:
  3510. IR::LabelInstr *labelHelper = nullptr;
  3511. IR::Opnd *src = instrNot->GetSrc1();
  3512. IR::Opnd *dst = instrNot->GetDst();
  3513. IR::Instr *instr;
  3514. bool isInt = src->IsTaggedInt();
  3515. if (!src->IsRegOpnd())
  3516. {
  3517. // Load the src at the top so we don't have to load it twice.
  3518. src = IR::RegOpnd::New(src->GetType(), this->m_func);
  3519. LowererMD::CreateAssign(src, instrNot->GetSrc1(), instrNot);
  3520. }
  3521. if (!dst->IsRegOpnd())
  3522. {
  3523. // We'll store the dst when we're done.
  3524. dst = IR::RegOpnd::New(dst->GetType(), this->m_func);
  3525. }
  3526. if (!isInt)
  3527. {
  3528. // TST src, 1
  3529. instr = IR::Instr::New(Js::OpCode::TST, this->m_func);
  3530. instr->SetSrc1(src);
  3531. instr->SetSrc2(IR::IntConstOpnd::New(1, TyMachReg, this->m_func));
  3532. instrNot->InsertBefore(instr);
  3533. // BEQ $helper
  3534. labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  3535. instr = IR::BranchInstr::New(Js::OpCode::BEQ, labelHelper, this->m_func);
  3536. instrNot->InsertBefore(instr);
  3537. }
  3538. // dst = MVN src
  3539. instr = IR::Instr::New(Js::OpCode::MVN, dst, src, this->m_func);
  3540. instrNot->InsertBefore(instr);
  3541. // dst = ADD dst, 1
  3542. instr = IR::Instr::New(Js::OpCode::ADD, dst, dst, IR::IntConstOpnd::New(Js::AtomTag, TyMachReg, this->m_func), this->m_func);
  3543. instrNot->InsertBefore(instr);
  3544. if (dst != instrNot->GetDst())
  3545. {
  3546. // Now store the result.
  3547. LowererMD::CreateAssign(instrNot->GetDst(), dst, instrNot);
  3548. }
  3549. if (isInt)
  3550. {
  3551. // If the src is int, then we're done, and we need no helper call.
  3552. instrNot->Remove();
  3553. return false;
  3554. }
  3555. // B $done
  3556. IR::LabelInstr *labelDone = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  3557. instr = IR::BranchInstr::New(Js::OpCode::B, labelDone, this->m_func);
  3558. instrNot->InsertBefore(instr);
  3559. // $helper:
  3560. // (caller generates helper call)
  3561. // $done:
  3562. instrNot->InsertBefore(labelHelper);
  3563. instrNot->InsertAfter(labelDone);
  3564. // Return true to indicate the original instr must still be lowered.
  3565. return true;
  3566. }
  3567. //
  3568. // If value is zero in tagged int representation, jump to $labelHelper.
  3569. //
  3570. void
  3571. LowererMD::GenerateTaggedZeroTest( IR::Opnd * opndSrc, IR::Instr * insertInstr, IR::LabelInstr * labelHelper )
  3572. {
  3573. // CMP src1, AtomTag
  3574. IR::Instr* instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  3575. instr->SetSrc1(opndSrc);
  3576. instr->SetSrc2(IR::IntConstOpnd::New(Js::AtomTag, TyInt32, this->m_func));
  3577. insertInstr->InsertBefore(instr);
  3578. // BEQ $helper
  3579. if(labelHelper != nullptr)
  3580. {
  3581. // BEQ $labelHelper
  3582. instr = IR::BranchInstr::New(Js::OpCode::BEQ, labelHelper, this->m_func);
  3583. insertInstr->InsertBefore(instr);
  3584. }
  3585. }
  3586. bool
  3587. LowererMD::GenerateFastNeg(IR::Instr * instrNeg)
  3588. {
  3589. // Given:
  3590. //
  3591. // dst = Not src
  3592. //
  3593. // Generate:
  3594. //
  3595. // if not int, jump $helper
  3596. // if src == 0 -- test for zero (must be handled by the runtime to preserve
  3597. // BEQ $helper -- Difference between +0 and -0)
  3598. // dst = RSB src, 0 -- do an inline NEG
  3599. // dst = ADD dst, 2 -- restore the var tag on the result
  3600. // BVS $helper
  3601. // B $fallthru
  3602. // $helper:
  3603. // (caller generates helper call)
  3604. // $fallthru:
  3605. IR::Instr * instr;
  3606. IR::LabelInstr * labelHelper = nullptr;
  3607. IR::LabelInstr * labelFallThru = nullptr;
  3608. IR::Opnd * opndSrc1;
  3609. IR::Opnd * opndDst;
  3610. opndSrc1 = instrNeg->GetSrc1();
  3611. AssertMsg(opndSrc1, "Expected src opnd on Neg instruction");
  3612. if (opndSrc1->IsRegOpnd() && opndSrc1->AsRegOpnd()->m_sym->IsIntConst())
  3613. {
  3614. IR::Opnd *newOpnd;
  3615. IntConstType value = opndSrc1->AsRegOpnd()->m_sym->GetIntConstValue();
  3616. if (value == 0)
  3617. {
  3618. // If the negate operand is zero, the result is -0.0, which is a Number rather than an Int31.
  3619. newOpnd = m_lowerer->LoadLibraryValueOpnd(instrNeg, LibraryValue::ValueNegativeZero);
  3620. }
  3621. else
  3622. {
  3623. // negation below can overflow because max negative int32 value > max positive value by 1.
  3624. newOpnd = IR::AddrOpnd::NewFromNumber(-(int64)value, m_func);
  3625. }
  3626. instrNeg->ClearBailOutInfo();
  3627. instrNeg->FreeSrc1();
  3628. instrNeg->SetSrc1(newOpnd);
  3629. instrNeg = this->ChangeToAssign(instrNeg);
  3630. // Skip lowering call to helper
  3631. return false;
  3632. }
  3633. bool isInt = (opndSrc1->IsTaggedInt());
  3634. if (opndSrc1->IsRegOpnd() && opndSrc1->AsRegOpnd()->m_sym->m_isNotInt)
  3635. {
  3636. return true;
  3637. }
  3638. labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  3639. // Load src's at the top so we don't have to do it repeatedly.
  3640. if (!opndSrc1->IsRegOpnd())
  3641. {
  3642. opndSrc1 = IR::RegOpnd::New(opndSrc1->GetType(), this->m_func);
  3643. LowererMD::CreateAssign(opndSrc1, instrNeg->GetSrc1(), instrNeg);
  3644. }
  3645. if (!isInt)
  3646. {
  3647. GenerateSmIntTest(opndSrc1, instrNeg, labelHelper);
  3648. }
  3649. GenerateTaggedZeroTest(opndSrc1, instrNeg, labelHelper);
  3650. opndDst = instrNeg->GetDst();
  3651. if (!opndDst->IsRegOpnd())
  3652. {
  3653. opndDst = IR::RegOpnd::New(opndDst->GetType(), this->m_func);
  3654. }
  3655. // dst = RSB src
  3656. instr = IR::Instr::New(Js::OpCode::RSB, opndDst, opndSrc1, IR::IntConstOpnd::New(0, TyInt32, this->m_func), this->m_func);
  3657. instrNeg->InsertBefore(instr);
  3658. // dst = ADD dst, 2
  3659. instr = IR::Instr::New(Js::OpCode::ADDS, opndDst, opndDst, IR::IntConstOpnd::New(2, TyInt32, this->m_func), this->m_func);
  3660. instrNeg->InsertBefore(instr);
  3661. // BVS $helper
  3662. instr = IR::BranchInstr::New(Js::OpCode::BVS, labelHelper, this->m_func);
  3663. instrNeg->InsertBefore(instr);
  3664. if (opndDst != instrNeg->GetDst())
  3665. {
  3666. //Now store the result.
  3667. LowererMD::CreateAssign(instrNeg->GetDst(), opndDst, instrNeg);
  3668. }
  3669. // B $fallthru
  3670. labelFallThru = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  3671. instr = IR::BranchInstr::New(Js::OpCode::B, labelFallThru, this->m_func);
  3672. instrNeg->InsertBefore(instr);
  3673. // $helper:
  3674. // (caller generates helper sequence)
  3675. // $fallthru:
  3676. AssertMsg(labelHelper, "Should not be NULL");
  3677. instrNeg->InsertBefore(labelHelper);
  3678. instrNeg->InsertAfter(labelFallThru);
  3679. return true;
  3680. }
  3681. ///----------------------------------------------------------------------------
  3682. ///
  3683. /// LowererMD::GenerateFastShiftLeft
  3684. ///
  3685. ///----------------------------------------------------------------------------
  3686. bool
  3687. LowererMD::GenerateFastShiftLeft(IR::Instr * instrShift)
  3688. {
  3689. // Given:
  3690. //
  3691. // dst = Shl src1, src2
  3692. //
  3693. // Generate:
  3694. //
  3695. // (If not 2 Int31's, jump to $helper.)
  3696. // s1 = MOV src1
  3697. // s1 = SAR s1, Js::VarTag_Shift -- Remove the var tag from the value to be shifted
  3698. // s2 = MOV src2
  3699. // s2 = SAR s2, Js::VarTag_Shift -- extract the real shift amount from the var
  3700. // s1 = SHL s1, s2 -- do the inline shift
  3701. // s3 = MOV s1
  3702. // s3 = SHL s3, Js::VarTag_Shift -- restore the var tag on the result
  3703. // JO $ToVar
  3704. // s3 = INC s3
  3705. // dst = MOV s3
  3706. // JMP $fallthru
  3707. //$ToVar:
  3708. // PUSH scriptContext
  3709. // PUSH s1
  3710. // dst = ToVar()
  3711. // JMP $fallthru
  3712. // $helper:
  3713. // (caller generates helper call)
  3714. // $fallthru:
  3715. // Return true to indicate the original instr must still be lowered.
  3716. return true;
  3717. }
  3718. ///----------------------------------------------------------------------------
  3719. ///
  3720. /// LowererMD::GenerateFastShiftRight
  3721. ///
  3722. ///----------------------------------------------------------------------------
  3723. bool
  3724. LowererMD::GenerateFastShiftRight(IR::Instr * instrShift)
  3725. {
  3726. // Given:
  3727. //
  3728. // dst = Shr/Sar src1, src2
  3729. //
  3730. // Generate:
  3731. //
  3732. // s1 = MOV src1
  3733. // TEST s1, 1
  3734. // JEQ $S1ToInt
  3735. // s1 = SAR s1, VarTag_Shift -- extract the real shift amount from the var
  3736. // JMP $src2
  3737. //$S1ToInt:
  3738. // PUSH scriptContext
  3739. // PUSH s1
  3740. // s1 = ToInt32()/ToUInt32
  3741. //$src2:
  3742. // Load s2 in ECX
  3743. // TEST s2, 1
  3744. // JEQ $S2ToUInt
  3745. // s2 = SAR s2, VarTag_Shift -- extract the real shift amount from the var
  3746. // JMP $Shr
  3747. //$S2ToUInt:
  3748. // PUSH scriptContext
  3749. // PUSH s2
  3750. // s2 = ToUInt32()
  3751. //$Shr:
  3752. // s1 = SHR/SAR s1, s2 -- do the inline shift
  3753. // s3 = MOV s1
  3754. // s3 = SHL s3, s2 -- To tagInt
  3755. // JO $ToVar
  3756. // JS $ToVar
  3757. // s3 = INC s3
  3758. // JMP $done
  3759. //$ToVar:
  3760. // PUSH scriptContext
  3761. // PUSH s1
  3762. // s3 = ToVar()
  3763. //$Done:
  3764. // dst = MOV s3
  3765. // Return true to indicate the original instr must still be lowered.
  3766. return true;
  3767. }
  3768. void
  3769. LowererMD::GenerateFastBrS(IR::BranchInstr *brInstr)
  3770. {
  3771. IR::Opnd *src1 = brInstr->UnlinkSrc1();
  3772. Assert(src1->IsIntConstOpnd() || src1->IsAddrOpnd() || src1->IsRegOpnd());
  3773. m_lowerer->InsertTest(
  3774. m_lowerer->LoadOptimizationOverridesValueOpnd(
  3775. brInstr, OptimizationOverridesValue::OptimizationOverridesSideEffects),
  3776. src1,
  3777. brInstr);
  3778. Js::OpCode opcode;
  3779. switch(brInstr->m_opcode)
  3780. {
  3781. case Js::OpCode::BrHasSideEffects:
  3782. opcode = Js::OpCode::BNE;
  3783. break;
  3784. case Js::OpCode::BrNotHasSideEffects:
  3785. opcode = Js::OpCode::BEQ;
  3786. break;
  3787. default:
  3788. Assert(UNREACHED);
  3789. __assume(false);
  3790. }
  3791. brInstr->m_opcode = opcode;
  3792. }
  3793. ///----------------------------------------------------------------------------
  3794. ///
  3795. /// LowererMD::GenerateSmIntPairTest
  3796. ///
  3797. /// Generate code to test whether the given operands are both Int31 vars
  3798. /// and branch to the given label if not.
  3799. ///
  3800. ///----------------------------------------------------------------------------
  3801. IR::Instr *
  3802. LowererMD::GenerateSmIntPairTest(
  3803. IR::Instr * instrInsert,
  3804. IR::Opnd * src1,
  3805. IR::Opnd * src2,
  3806. IR::LabelInstr * labelFail)
  3807. {
  3808. IR::Opnd * opndReg;
  3809. IR::Instr * instrPrev = instrInsert->m_prev;
  3810. IR::Instr * instr;
  3811. Assert(src1->GetType() == TyVar);
  3812. Assert(src2->GetType() == TyVar);
  3813. //src1 and src2 can either be RegOpnd or AddrOpnd at this point
  3814. if (src1->IsTaggedInt())
  3815. {
  3816. Swap(src1, src2);
  3817. }
  3818. if (src2->IsTaggedInt())
  3819. {
  3820. if (src1->IsTaggedInt())
  3821. {
  3822. return instrPrev;
  3823. }
  3824. IR::RegOpnd *opndSrc1 = src1->AsRegOpnd();
  3825. // TST src1, AtomTag
  3826. // BEQ $fail
  3827. instr = IR::Instr::New(Js::OpCode::TST, this->m_func);
  3828. instr->SetSrc1(opndSrc1);
  3829. instr->SetSrc2(IR::IntConstOpnd::New(Js::AtomTag, TyVar, this->m_func));
  3830. instrInsert->InsertBefore(instr);
  3831. }
  3832. else
  3833. {
  3834. IR::RegOpnd *opndSrc1 = src1->AsRegOpnd();
  3835. IR::RegOpnd *opndSrc2 = src2->AsRegOpnd();
  3836. // s1 = AND src1, 1
  3837. // TST s1, src2
  3838. // BEQ $fail
  3839. // s1 = AND src1, AtomTag
  3840. opndReg = IR::RegOpnd::New(TyMachReg, this->m_func);
  3841. instr = IR::Instr::New(
  3842. Js::OpCode::AND, opndReg, opndSrc1, IR::IntConstOpnd::New(Js::AtomTag, TyMachReg, this->m_func), this->m_func);
  3843. instrInsert->InsertBefore(instr);
  3844. // TST s1, src2
  3845. instr = IR::Instr::New(Js::OpCode::TST, this->m_func);
  3846. instr->SetSrc1(opndReg);
  3847. instr->SetSrc2(opndSrc2);
  3848. instrInsert->InsertBefore(instr);
  3849. }
  3850. // BEQ $fail
  3851. instr = IR::BranchInstr::New(Js::OpCode::BEQ, labelFail, this->m_func);
  3852. instrInsert->InsertBefore(instr);
  3853. return instrPrev;
  3854. }
  3855. void LowererMD::GenerateObjectPairTest(IR::Opnd * opndSrc1, IR::Opnd * opndSrc2, IR::Instr * insertInstr, IR::LabelInstr * labelTarget)
  3856. {
  3857. // opndOr = ORR opndSrc1, opndSrc2
  3858. // TST opndOr, AtomTag_Ptr
  3859. // BNE $labelTarget
  3860. IR::RegOpnd * opndOr = IR::RegOpnd::New(TyMachPtr, this->m_func);
  3861. IR::Instr * instr = IR::Instr::New(Js::OpCode::ORR, opndOr, opndSrc1, opndSrc2, this->m_func);
  3862. insertInstr->InsertBefore(instr);
  3863. instr = IR::Instr::New(Js::OpCode::TST, this->m_func);
  3864. instr->SetSrc1(opndOr);
  3865. instr->SetSrc2(IR::IntConstOpnd::New(Js::AtomTag_IntPtr, TyMachReg, this->m_func));
  3866. insertInstr->InsertBefore(instr);
  3867. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelTarget, this->m_func);
  3868. insertInstr->InsertBefore(instr);
  3869. }
  3870. bool LowererMD::GenerateObjectTest(IR::Opnd * opndSrc, IR::Instr * insertInstr, IR::LabelInstr * labelTarget, bool fContinueLabel)
  3871. {
  3872. if (opndSrc->IsTaggedValue() && fContinueLabel)
  3873. {
  3874. // Insert delete branch opcode to tell the dbChecks not to assert on the helper label we may fall through into
  3875. IR::Instr *fakeBr = IR::PragmaInstr::New(Js::OpCode::DeletedNonHelperBranch, 0, this->m_func);
  3876. insertInstr->InsertBefore(fakeBr);
  3877. return false;
  3878. }
  3879. else if (opndSrc->IsNotTaggedValue() && !fContinueLabel)
  3880. {
  3881. return false;
  3882. }
  3883. // TST s1, AtomTag_IntPtr | FloatTag_Value
  3884. IR::Instr * instr = IR::Instr::New(Js::OpCode::TST, this->m_func);
  3885. instr->SetSrc1(opndSrc);
  3886. instr->SetSrc2(IR::IntConstOpnd::New(Js::AtomTag_IntPtr, TyMachReg, this->m_func));
  3887. insertInstr->InsertBefore(instr);
  3888. if (fContinueLabel)
  3889. {
  3890. // BEQ $labelTarget
  3891. instr = IR::BranchInstr::New(Js::OpCode::BEQ, labelTarget, this->m_func);
  3892. insertInstr->InsertBefore(instr);
  3893. IR::LabelInstr *labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  3894. insertInstr->InsertBefore(labelHelper);
  3895. }
  3896. else
  3897. {
  3898. // BNE $labelTarget
  3899. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelTarget, this->m_func);
  3900. insertInstr->InsertBefore(instr);
  3901. }
  3902. return true;
  3903. }
  3904. IR::BranchInstr *
  3905. LowererMD::GenerateLocalInlineCacheCheck(
  3906. IR::Instr * instrLdSt,
  3907. IR::RegOpnd * opndType,
  3908. IR::RegOpnd * opndInlineCache,
  3909. IR::LabelInstr * labelNext,
  3910. bool checkTypeWithoutProperty)
  3911. {
  3912. // Generate:
  3913. //
  3914. // s3 = LDR inlineCache->u.local.type
  3915. // CMP type, s3
  3916. // BNE $next
  3917. IR::Instr * instr;
  3918. IR::IndirOpnd * typeOpnd;
  3919. // s3 = LDR [inlineCache, offset(u.local.type)]
  3920. IR::RegOpnd * s3 = IR::RegOpnd::New(TyMachReg, instrLdSt->m_func);
  3921. if (checkTypeWithoutProperty)
  3922. {
  3923. typeOpnd = IR::IndirOpnd::New(opndInlineCache, (int32)offsetof(Js::InlineCache, u.local.typeWithoutProperty), TyMachPtr, instrLdSt->m_func);
  3924. }
  3925. else
  3926. {
  3927. typeOpnd = IR::IndirOpnd::New(opndInlineCache, (int32)offsetof(Js::InlineCache, u.local.type), TyMachPtr, instrLdSt->m_func);
  3928. }
  3929. instr = IR::Instr::New(Js::OpCode::LDR, s3, typeOpnd, instrLdSt->m_func);
  3930. instrLdSt->InsertBefore(instr);
  3931. // CMP s1, s3
  3932. instr = IR::Instr::New(Js::OpCode::CMP, instrLdSt->m_func);
  3933. instr->SetSrc1(opndType);
  3934. instr->SetSrc2(s3);
  3935. instrLdSt->InsertBefore(instr);
  3936. // BNE $next
  3937. IR::BranchInstr * branchInstr = IR::BranchInstr::New(Js::OpCode::BNE, labelNext, instrLdSt->m_func);
  3938. instrLdSt->InsertBefore(branchInstr);
  3939. return branchInstr;
  3940. }
  3941. void
  3942. LowererMD::GenerateFlagInlineCacheCheckForGetterSetter(
  3943. IR::Instr * insertBeforeInstr,
  3944. IR::RegOpnd * opndInlineCache,
  3945. IR::LabelInstr * labelNext)
  3946. {
  3947. uint accessorFlagMask;
  3948. if (PHASE_OFF(Js::InlineGettersPhase, insertBeforeInstr->m_func->GetJnFunction()))
  3949. {
  3950. accessorFlagMask = Js::InlineCache::GetSetterFlagMask();
  3951. }
  3952. else if (PHASE_OFF(Js::InlineSettersPhase, insertBeforeInstr->m_func->GetJnFunction()))
  3953. {
  3954. accessorFlagMask = Js::InlineCache::GetGetterFlagMask();
  3955. }
  3956. else
  3957. {
  3958. accessorFlagMask = Js::InlineCache::GetGetterSetterFlagMask();
  3959. }
  3960. // Generate:
  3961. //
  3962. // TST [&(inlineCache->u.flags.flags)], Js::InlineCacheGetterFlag | Js::InlineCacheSetterFlag
  3963. // BEQ $next
  3964. IR::Instr * instr;
  3965. IR::Opnd* flagsOpnd;
  3966. flagsOpnd = IR::IndirOpnd::New(opndInlineCache, 0, TyInt8, this->m_func);
  3967. // AND [&(inlineCache->u.flags.flags)], InlineCacheGetterFlag | InlineCacheSetterFlag
  3968. instr = IR::Instr::New(Js::OpCode::TST,this->m_func);
  3969. instr->SetSrc1(flagsOpnd);
  3970. instr->SetSrc2(IR::IntConstOpnd::New(accessorFlagMask, TyInt8, this->m_func));
  3971. insertBeforeInstr->InsertBefore(instr);
  3972. LegalizeMD::LegalizeInstr(instr, false);
  3973. // BEQ $next
  3974. instr = IR::BranchInstr::New(Js::OpCode::BEQ, labelNext, this->m_func);
  3975. insertBeforeInstr->InsertBefore(instr);
  3976. }
  3977. IR::BranchInstr *
  3978. LowererMD::GenerateFlagInlineCacheCheckForNoGetterSetter(
  3979. IR::Instr * instrLdSt,
  3980. IR::RegOpnd * opndInlineCache,
  3981. IR::LabelInstr * labelNext)
  3982. {
  3983. // Generate:
  3984. //
  3985. // TST [&(inlineCache->u.flags.flags)], (Js::InlineCacheGetterFlag | Js::InlineCacheSetterFlag)
  3986. // BNE $next
  3987. IR::Instr * instr;
  3988. IR::Opnd* flagsOpnd;
  3989. flagsOpnd = IR::IndirOpnd::New(opndInlineCache, 0, TyInt8, instrLdSt->m_func);
  3990. instr = IR::Instr::New(Js::OpCode::TST, instrLdSt->m_func);
  3991. instr->SetSrc1(flagsOpnd);
  3992. instr->SetSrc2(IR::IntConstOpnd::New((Js::InlineCacheGetterFlag | Js::InlineCacheSetterFlag) << 1, TyInt8, instrLdSt->m_func));
  3993. instrLdSt->InsertBefore(instr);
  3994. LegalizeMD::LegalizeInstr(instr, false);
  3995. // BNQ $next
  3996. IR::BranchInstr * branchInstr = IR::BranchInstr::New(Js::OpCode::BNE, labelNext, instrLdSt->m_func);
  3997. instrLdSt->InsertBefore(branchInstr);
  3998. return branchInstr;
  3999. }
  4000. IR::BranchInstr *
  4001. LowererMD::GenerateFlagInlineCacheCheck(
  4002. IR::Instr * instrLdSt,
  4003. IR::RegOpnd * opndType,
  4004. IR::RegOpnd * opndInlineCache,
  4005. IR::LabelInstr * labelNext)
  4006. {
  4007. // Generate:
  4008. //
  4009. // s3 = LDR inlineCache->u.flags.type
  4010. // CMP type, s3
  4011. // BNE $next
  4012. IR::Instr * instr;
  4013. // LDR s3, [inlineCache, offset(u.flags.type)]
  4014. IR::RegOpnd *s3 = IR::RegOpnd::New(TyMachReg, instrLdSt->m_func);
  4015. IR::IndirOpnd * opndIndir = IR::IndirOpnd::New(opndInlineCache, offsetof(Js::InlineCache, u.accessor.type), TyMachPtr, instrLdSt->m_func);
  4016. instr = IR::Instr::New(Js::OpCode::LDR, s3, opndIndir, instrLdSt->m_func);
  4017. instrLdSt->InsertBefore(instr);
  4018. // CMP type, s3
  4019. instr = IR::Instr::New(Js::OpCode::CMP, instrLdSt->m_func);
  4020. instr->SetSrc1(opndType);
  4021. instr->SetSrc2(s3);
  4022. instrLdSt->InsertBefore(instr);
  4023. // BNE $next
  4024. IR::BranchInstr * branchInstr = IR::BranchInstr::New(Js::OpCode::BNE, labelNext, instrLdSt->m_func);
  4025. instrLdSt->InsertBefore(branchInstr);
  4026. return branchInstr;
  4027. }
  4028. IR::BranchInstr *
  4029. LowererMD::GenerateProtoInlineCacheCheck(
  4030. IR::Instr * instrLdSt,
  4031. IR::RegOpnd * opndType,
  4032. IR::RegOpnd * opndInlineCache,
  4033. IR::LabelInstr * labelNext)
  4034. {
  4035. // Generate:
  4036. //
  4037. // s3 = LDR inlineCache->u.proto.type
  4038. // CMP type, s3
  4039. // BNE $next
  4040. IR::Instr * instr;
  4041. // LDR s3, [inlineCache, offset(u.proto.type)]
  4042. IR::RegOpnd *s3 = IR::RegOpnd::New(TyMachReg, instrLdSt->m_func);
  4043. IR::IndirOpnd * opndIndir = IR::IndirOpnd::New(opndInlineCache, offsetof(Js::InlineCache, u.proto.type), TyMachPtr, instrLdSt->m_func);
  4044. instr = IR::Instr::New(Js::OpCode::LDR, s3, opndIndir, instrLdSt->m_func);
  4045. instrLdSt->InsertBefore(instr);
  4046. // CMP type, s3
  4047. instr = IR::Instr::New(Js::OpCode::CMP, instrLdSt->m_func);
  4048. instr->SetSrc1(opndType);
  4049. instr->SetSrc2(s3);
  4050. instrLdSt->InsertBefore(instr);
  4051. // BNE $next
  4052. IR::BranchInstr * branchInstr = IR::BranchInstr::New(Js::OpCode::BNE, labelNext, instrLdSt->m_func);
  4053. instrLdSt->InsertBefore(branchInstr);
  4054. return branchInstr;
  4055. }
  4056. void
  4057. LowererMD::GenerateLdFldFromLocalInlineCache(
  4058. IR::Instr * instrLdFld,
  4059. IR::RegOpnd * opndBase,
  4060. IR::Opnd * opndDst,
  4061. IR::RegOpnd * opndInlineCache,
  4062. IR::LabelInstr * labelFallThru,
  4063. bool isInlineSlot)
  4064. {
  4065. // Generate:
  4066. //
  4067. // LDR s1, [base, offset(slots)]
  4068. // LDR s2, [inlineCache, offset(u.local.slotIndex)] -- load the cached slot index
  4069. // LDR dst, [s1, s2, LSL #2] -- load the value directly from the slot (dst = s3 + s4 * 4)
  4070. // B $fallthru
  4071. IR::Instr * instr;
  4072. IR::IndirOpnd * opndIndir;
  4073. IR::RegOpnd * opndSlotArray = nullptr;
  4074. if (!isInlineSlot)
  4075. {
  4076. opndSlotArray = IR::RegOpnd::New(TyMachReg, instrLdFld->m_func);
  4077. opndIndir = IR::IndirOpnd::New(opndBase, Js::DynamicObject::GetOffsetOfAuxSlots(), TyMachReg, instrLdFld->m_func);
  4078. instr = IR::Instr::New(Js::OpCode::LDR, opndSlotArray, opndIndir, instrLdFld->m_func);
  4079. instrLdFld->InsertBefore(instr);
  4080. }
  4081. // s2 = LDR [inlineCache, offset(u.local.slotIndex)] -- load the cached slot index
  4082. IR::RegOpnd * s2 = IR::RegOpnd::New(TyUint16, instrLdFld->m_func);
  4083. opndIndir = IR::IndirOpnd::New(opndInlineCache, offsetof(Js::InlineCache, u.local.slotIndex), TyUint16, instrLdFld->m_func);
  4084. instr = IR::Instr::New(Js::OpCode::LDR, s2, opndIndir, instrLdFld->m_func);
  4085. instrLdFld->InsertBefore(instr);
  4086. if (isInlineSlot)
  4087. {
  4088. // LDR dst, [base, s2, LSL #2] -- load the value directly from the inline slot (dst = base + s2 * 4)
  4089. opndIndir = IR::IndirOpnd::New(opndBase, s2, GetDefaultIndirScale(), TyMachReg, instrLdFld->m_func);
  4090. instr = IR::Instr::New(Js::OpCode::LDR, opndDst, opndIndir, instrLdFld->m_func);
  4091. instrLdFld->InsertBefore(instr);
  4092. }
  4093. else
  4094. {
  4095. // LDR dst, [s1, s2, LSL #2] -- load the value directly from the slot (dst = s1 + s2 * 4)
  4096. opndIndir = IR::IndirOpnd::New(opndSlotArray, s2, GetDefaultIndirScale(), TyMachReg, instrLdFld->m_func);
  4097. instr = IR::Instr::New(Js::OpCode::LDR, opndDst, opndIndir, instrLdFld->m_func);
  4098. instrLdFld->InsertBefore(instr);
  4099. }
  4100. // B $fallthru
  4101. instr = IR::BranchInstr::New(Js::OpCode::B, labelFallThru, instrLdFld->m_func);
  4102. instrLdFld->InsertBefore(instr);
  4103. }
  4104. void
  4105. LowererMD::GenerateLdFldFromProtoInlineCache(
  4106. IR::Instr * instrLdFld,
  4107. IR::RegOpnd * opndBase,
  4108. IR::Opnd * opndDst,
  4109. IR::RegOpnd * opndInlineCache,
  4110. IR::LabelInstr * labelFallThru,
  4111. bool isInlineSlot)
  4112. {
  4113. // Generate:
  4114. //
  4115. // LDR s1, [inlineCache, offset(u.proto.prototypeObject)]
  4116. // LDR s1, [s1, offset(slots)] -- load the slot array
  4117. // LDR s2, [inlineCache, offset(u.proto.slotIndex)]
  4118. // LDR dst, [s1, s2, LSL #2]
  4119. // B $fallthru
  4120. IR::Instr * instr;
  4121. IR::RegOpnd * opndProtoSlots = nullptr;
  4122. // LDR s1, [inlineCache, offset(u.proto.prototypeObject)]
  4123. IR::RegOpnd * opndProto = IR::RegOpnd::New(TyMachReg, instrLdFld->m_func);
  4124. IR::IndirOpnd * opndIndir = IR::IndirOpnd::New(opndInlineCache, (int32)offsetof(Js::InlineCache, u.proto.prototypeObject), TyMachReg, instrLdFld->m_func);
  4125. instr = IR::Instr::New(Js::OpCode::LDR, opndProto, opndIndir, instrLdFld->m_func);
  4126. instrLdFld->InsertBefore(instr);
  4127. if (!isInlineSlot)
  4128. {
  4129. // LDR s1, [s1, offset(slots)] -- load the slot array
  4130. opndProtoSlots = IR::RegOpnd::New(TyMachReg, instrLdFld->m_func);
  4131. opndIndir = IR::IndirOpnd::New(opndProto, Js::DynamicObject::GetOffsetOfAuxSlots(), TyMachReg, instrLdFld->m_func);
  4132. instr = IR::Instr::New(Js::OpCode::LDR, opndProtoSlots, opndIndir, instrLdFld->m_func);
  4133. instrLdFld->InsertBefore(instr);
  4134. }
  4135. // LDR s2, [inlineCache, offset(u.proto.slotIndex)]
  4136. IR::RegOpnd * opndSlotIndex = IR::RegOpnd::New(TyUint16, instrLdFld->m_func);
  4137. opndIndir = IR::IndirOpnd::New(opndInlineCache, offsetof(Js::InlineCache, u.proto.slotIndex), TyUint16, instrLdFld->m_func);
  4138. instr = IR::Instr::New(Js::OpCode::LDR, opndSlotIndex, opndIndir, instrLdFld->m_func);
  4139. instrLdFld->InsertBefore(instr);
  4140. if (isInlineSlot)
  4141. {
  4142. // LDR dst, [s1, s8, LSL #2]
  4143. opndIndir = IR::IndirOpnd::New(opndProto, opndSlotIndex, GetDefaultIndirScale(), TyMachReg, instrLdFld->m_func);
  4144. instr = IR::Instr::New(Js::OpCode::LDR, opndDst, opndIndir, instrLdFld->m_func);
  4145. instrLdFld->InsertBefore(instr);
  4146. }
  4147. else
  4148. {
  4149. // LDR dst, [s7, s8, LSL #2]
  4150. opndIndir = IR::IndirOpnd::New(opndProtoSlots, opndSlotIndex, GetDefaultIndirScale(), TyMachReg, instrLdFld->m_func);
  4151. instr = IR::Instr::New(Js::OpCode::LDR, opndDst, opndIndir, instrLdFld->m_func);
  4152. instrLdFld->InsertBefore(instr);
  4153. }
  4154. // B $fallthru
  4155. instr = IR::BranchInstr::New(Js::OpCode::B, labelFallThru, instrLdFld->m_func);
  4156. instrLdFld->InsertBefore(instr);
  4157. }
  4158. void
  4159. LowererMD::GenerateLdLocalFldFromFlagInlineCache(
  4160. IR::Instr * instrLdFld,
  4161. IR::RegOpnd * opndBase,
  4162. IR::Opnd * opndDst,
  4163. IR::RegOpnd * opndInlineCache,
  4164. IR::LabelInstr * labelFallThru,
  4165. bool isInlineSlot)
  4166. {
  4167. // Generate:
  4168. //
  4169. // LDR s1, [base, offset(slots)]
  4170. // LDR s2, [inlineCache, offset(u.flags.slotIndex)] -- load the cached slot index
  4171. // LDR dst, [s1, s2, LSL #2] -- load the value directly from the slot (dst = s3 + s4 * 4)
  4172. // B $fallthru
  4173. IR::Instr * instr;
  4174. IR::IndirOpnd * opndIndir;
  4175. IR::RegOpnd * opndSlotArray = nullptr;
  4176. if (!isInlineSlot)
  4177. {
  4178. opndSlotArray = IR::RegOpnd::New(TyMachReg, instrLdFld->m_func);
  4179. opndIndir = IR::IndirOpnd::New(opndBase, Js::DynamicObject::GetOffsetOfAuxSlots(), TyMachReg, instrLdFld->m_func);
  4180. instr = IR::Instr::New(Js::OpCode::LDR, opndSlotArray, opndIndir, instrLdFld->m_func);
  4181. instrLdFld->InsertBefore(instr);
  4182. }
  4183. // s2 = LDR [inlineCache, offset(u.local.slotIndex)] -- load the cached slot index
  4184. IR::RegOpnd * s2 = IR::RegOpnd::New(TyUint16, instrLdFld->m_func);
  4185. opndIndir = IR::IndirOpnd::New(opndInlineCache, offsetof(Js::InlineCache, u.accessor.slotIndex), TyUint16, instrLdFld->m_func);
  4186. instr = IR::Instr::New(Js::OpCode::LDR, s2, opndIndir, instrLdFld->m_func);
  4187. instrLdFld->InsertBefore(instr);
  4188. if (isInlineSlot)
  4189. {
  4190. // LDR dst, [base, s2, LSL #2] -- load the value directly from the inline slot (dst = base + s2 * 4)
  4191. opndIndir = IR::IndirOpnd::New(opndBase, s2, GetDefaultIndirScale(), TyMachReg, instrLdFld->m_func);
  4192. instr = IR::Instr::New(Js::OpCode::LDR, opndDst, opndIndir, instrLdFld->m_func);
  4193. instrLdFld->InsertBefore(instr);
  4194. }
  4195. else
  4196. {
  4197. // LDR dst, [s1, s2, LSL #2] -- load the value directly from the slot (dst = s1 + s2 * 4)
  4198. opndIndir = IR::IndirOpnd::New(opndSlotArray, s2, GetDefaultIndirScale(), TyMachReg, instrLdFld->m_func);
  4199. instr = IR::Instr::New(Js::OpCode::LDR, opndDst, opndIndir, instrLdFld->m_func);
  4200. instrLdFld->InsertBefore(instr);
  4201. }
  4202. // B $fallthru
  4203. instr = IR::BranchInstr::New(Js::OpCode::B, labelFallThru, instrLdFld->m_func);
  4204. instrLdFld->InsertBefore(instr);
  4205. }
  4206. void
  4207. LowererMD::GenerateLdFldFromFlagInlineCache(
  4208. IR::Instr * insertBeforeInstr,
  4209. IR::RegOpnd * opndBase,
  4210. IR::RegOpnd * opndInlineCache,
  4211. IR::Opnd * opndDst,
  4212. IR::LabelInstr * labelFallThru,
  4213. bool isInlineSlot)
  4214. {
  4215. // Generate:
  4216. //
  4217. // LDR s1, [inlineCache, offset(u.flags.object)]
  4218. // LDR s1, [s1, offset(slots)] -- load the slot array
  4219. // LDR s2, [inlineCache, offset(u.flags.slotIndex)]
  4220. // LDR dst, [s1, s2, LSL #2]
  4221. // B $fallthru
  4222. IR::Instr * instr;
  4223. IR::RegOpnd * opndObjSlots = nullptr;
  4224. // LDR s1, [inlineCache, offset(u.flags.object)]
  4225. IR::RegOpnd * object = IR::RegOpnd::New(TyMachReg, this->m_func);
  4226. IR::IndirOpnd * opndIndir = IR::IndirOpnd::New(opndInlineCache, (int32)offsetof(Js::InlineCache, u.accessor.object), TyMachReg, this->m_func);
  4227. instr = IR::Instr::New(Js::OpCode::LDR, object, opndIndir, this->m_func);
  4228. insertBeforeInstr->InsertBefore(instr);
  4229. if (!isInlineSlot)
  4230. {
  4231. // LDR s1, [s1, offset(slots)] -- load the slot array
  4232. opndObjSlots = IR::RegOpnd::New(TyMachReg, this->m_func);
  4233. opndIndir = IR::IndirOpnd::New(object, Js::DynamicObject::GetOffsetOfAuxSlots(), TyMachReg, this->m_func);
  4234. instr = IR::Instr::New(Js::OpCode::LDR, opndObjSlots, opndIndir, this->m_func);
  4235. insertBeforeInstr->InsertBefore(instr);
  4236. }
  4237. // LDR s2, [inlineCache, offset(u.flags.slotIndex)]
  4238. IR::RegOpnd * opndSlotIndex = IR::RegOpnd::New(TyUint16, this->m_func);
  4239. opndIndir = IR::IndirOpnd::New(opndInlineCache, offsetof(Js::InlineCache, u.accessor.slotIndex), TyUint16, this->m_func);
  4240. instr = IR::Instr::New(Js::OpCode::LDR, opndSlotIndex, opndIndir, this->m_func);
  4241. insertBeforeInstr->InsertBefore(instr);
  4242. if (isInlineSlot)
  4243. {
  4244. // LDR dst, [s1, s8, LSL #2]
  4245. opndIndir = IR::IndirOpnd::New(object, opndSlotIndex, this->GetDefaultIndirScale(), TyMachReg, this->m_func);
  4246. instr = IR::Instr::New(Js::OpCode::LDR, opndDst, opndIndir, this->m_func);
  4247. insertBeforeInstr->InsertBefore(instr);
  4248. }
  4249. else
  4250. {
  4251. // LDR dst, [s7, s8, LSL #2]
  4252. opndIndir = IR::IndirOpnd::New(opndObjSlots, opndSlotIndex, this->GetDefaultIndirScale(), TyMachReg, this->m_func);
  4253. instr = IR::Instr::New(Js::OpCode::LDR, opndDst, opndIndir, this->m_func);
  4254. insertBeforeInstr->InsertBefore(instr);
  4255. }
  4256. // B $fallthru
  4257. instr = IR::BranchInstr::New(Js::OpCode::B, labelFallThru, this->m_func);
  4258. insertBeforeInstr->InsertBefore(instr);
  4259. }
  4260. void
  4261. LowererMD::GenerateLoadTaggedType(IR::Instr * instrLdSt, IR::RegOpnd * opndType, IR::RegOpnd * opndTaggedType)
  4262. {
  4263. // taggedType = OR type, InlineCacheAuxSlotTypeTag
  4264. IR::IntConstOpnd * opndAuxSlotTag = IR::IntConstOpnd::New(InlineCacheAuxSlotTypeTag, TyInt8, instrLdSt->m_func);
  4265. IR::Instr * instr = IR::Instr::New(Js::OpCode::ORR, opndTaggedType, opndType, opndAuxSlotTag, instrLdSt->m_func);
  4266. instrLdSt->InsertBefore(instr);
  4267. }
  4268. void
  4269. LowererMD::GenerateLoadPolymorphicInlineCacheSlot(IR::Instr * instrLdSt, IR::RegOpnd * opndInlineCache, IR::RegOpnd * opndType, uint polymorphicInlineCacheSize)
  4270. {
  4271. // Generate
  4272. //
  4273. // LDR r1, type
  4274. // LSR r1, r1, #PolymorphicInlineCacheShift
  4275. // AND r1, r1, #(size - 1)
  4276. // LSL r1, r1, #log2(sizeof(Js::InlineCache))
  4277. // ADD inlineCache, inlineCache, r1
  4278. // MOV r1, type
  4279. IR::RegOpnd * opndOffset = IR::RegOpnd::New(TyMachPtr, instrLdSt->m_func);
  4280. IR::Instr * instr = IR::Instr::New(Js::OpCode::MOV, opndOffset, opndType, instrLdSt->m_func);
  4281. instrLdSt->InsertBefore(instr);
  4282. IntConstType rightShiftAmount = PolymorphicInlineCacheShift;
  4283. IntConstType leftShiftAmount = Math::Log2(sizeof(Js::InlineCache));
  4284. // instead of generating
  4285. // LSR r1, r1, #PolymorphicInlineCacheShift
  4286. // AND r1, r1, #(size - 1)
  4287. // LSL r1, r1, #log2(sizeof(Js::InlineCache))
  4288. //
  4289. // we can generate:
  4290. // LSR r1, r1, #(PolymorphicInlineCacheShift - log2(sizeof(Js::InlineCache))
  4291. // AND r1, r1, #(size - 1) << log2(sizeof(Js::InlineCache))
  4292. Assert(rightShiftAmount > leftShiftAmount);
  4293. instr = IR::Instr::New(Js::OpCode::LSR, opndOffset, opndOffset, IR::IntConstOpnd::New(rightShiftAmount - leftShiftAmount, TyUint8, instrLdSt->m_func, true), instrLdSt->m_func);
  4294. instrLdSt->InsertBefore(instr);
  4295. instr = IR::Instr::New(Js::OpCode::AND, opndOffset, opndOffset, IR::IntConstOpnd::New((polymorphicInlineCacheSize - 1) << leftShiftAmount, TyMachPtr, instrLdSt->m_func, true), instrLdSt->m_func);
  4296. instrLdSt->InsertBefore(instr);
  4297. // ADD inlineCache, inlineCache, r1
  4298. instr = IR::Instr::New(Js::OpCode::ADD, opndInlineCache, opndInlineCache, opndOffset, instrLdSt->m_func);
  4299. instrLdSt->InsertBefore(instr);
  4300. }
  4301. ///----------------------------------------------------------------------------
  4302. ///
  4303. /// LowererMD::GenerateFastLdMethodFromFlags
  4304. ///
  4305. /// Make use of the helper to cache the type and slot index used to do a LdFld
  4306. /// and do an inline load from the appropriate slot if the type hasn't changed
  4307. /// since the last time this LdFld was executed.
  4308. ///
  4309. ///----------------------------------------------------------------------------
  4310. bool
  4311. LowererMD::GenerateFastLdMethodFromFlags(IR::Instr * instrLdFld)
  4312. {
  4313. IR::LabelInstr * labelFallThru;
  4314. IR::LabelInstr * bailOutLabel;
  4315. IR::Opnd * opndSrc;
  4316. IR::Opnd * opndDst;
  4317. IR::RegOpnd * opndBase;
  4318. IR::RegOpnd * opndType;
  4319. IR::RegOpnd * opndInlineCache;
  4320. Js::InlineCache * inlineCache;
  4321. opndSrc = instrLdFld->GetSrc1();
  4322. AssertMsg(opndSrc->IsSymOpnd() && opndSrc->AsSymOpnd()->IsPropertySymOpnd() && opndSrc->AsSymOpnd()->m_sym->IsPropertySym(),
  4323. "Expected property sym operand as src of LdFldFlags");
  4324. IR::PropertySymOpnd * propertySymOpnd = opndSrc->AsPropertySymOpnd();
  4325. Assert(propertySymOpnd->m_runtimeInlineCache);
  4326. Assert(!instrLdFld->DoStackArgsOpt(this->m_func));
  4327. // TODO: LdMethodFromFlags doesn't participate in object type specialization. We should be using a temporary
  4328. // register without a type sym here.
  4329. if (propertySymOpnd->IsTypeCheckSeqCandidate())
  4330. {
  4331. AssertMsg(propertySymOpnd->HasObjectTypeSym(), "Type optimized property sym operand without a type sym?");
  4332. StackSym *typeSym = propertySymOpnd->GetObjectTypeSym();
  4333. opndType = IR::RegOpnd::New(typeSym, TyMachReg, this->m_func);
  4334. }
  4335. else
  4336. {
  4337. opndType = IR::RegOpnd::New(TyMachReg, this->m_func);
  4338. }
  4339. opndBase = propertySymOpnd->CreatePropertyOwnerOpnd(m_func);
  4340. opndDst = instrLdFld->GetDst();
  4341. inlineCache = propertySymOpnd->m_runtimeInlineCache;
  4342. Assert(inlineCache != nullptr);
  4343. opndInlineCache = IR::RegOpnd::New(TyMachReg, this->m_func);
  4344. labelFallThru = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  4345. // Label to jump to (or fall through to) when bailing out
  4346. bailOutLabel = IR::LabelInstr::New(Js::OpCode::Label, instrLdFld->m_func, true /* isOpHelper */);
  4347. LowererMD::CreateAssign(opndInlineCache, m_lowerer->LoadRuntimeInlineCacheOpnd(instrLdFld, propertySymOpnd), instrLdFld);
  4348. IR::LabelInstr * labelFlagAux = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  4349. // Check the flag cache with the untagged type
  4350. this->m_lowerer->GenerateObjectTestAndTypeLoad(instrLdFld, opndBase, opndType, bailOutLabel);
  4351. //Blindly do the check for getter flag first and then do the type check
  4352. //We avoid repeated check for getter flag when the function object may be in either
  4353. //inline slots or auxiliary slots
  4354. GenerateFlagInlineCacheCheckForGetterSetter(instrLdFld, opndInlineCache, bailOutLabel);
  4355. GenerateFlagInlineCacheCheck(instrLdFld, opndType, opndInlineCache, labelFlagAux);
  4356. GenerateLdFldFromFlagInlineCache(instrLdFld, opndBase, opndInlineCache, opndDst, labelFallThru, true);
  4357. // Check the flag cache with the tagged type
  4358. instrLdFld->InsertBefore(labelFlagAux);
  4359. IR::RegOpnd * opndTaggedType = IR::RegOpnd::New(TyMachReg, this->m_func);
  4360. GenerateLoadTaggedType(instrLdFld, opndType, opndTaggedType);
  4361. GenerateFlagInlineCacheCheck(instrLdFld, opndTaggedType, opndInlineCache, bailOutLabel);
  4362. GenerateLdFldFromFlagInlineCache(instrLdFld, opndBase, opndInlineCache, opndDst, labelFallThru, false);
  4363. instrLdFld->InsertBefore(bailOutLabel);
  4364. instrLdFld->InsertAfter(labelFallThru);
  4365. instrLdFld->UnlinkSrc1();
  4366. // Generate the bailout helper call. 'instr' will be changed to the CALL into the bailout function, so it can't be used for
  4367. // ordering instructions anymore.
  4368. this->m_lowerer->GenerateBailOut(instrLdFld);
  4369. return true;
  4370. }
  4371. //----------------------------------------------------------------------------
  4372. //
  4373. // LowererMD::GenerateFastScopedFldLookup
  4374. //
  4375. // This is a helper call which generates asm for both
  4376. // ScopedLdFld & ScopedStFld
  4377. //
  4378. //----------------------------------------------------------------------------
  4379. IR::Instr *
  4380. LowererMD::GenerateFastScopedFld(IR::Instr * instrScopedFld, bool isLoad)
  4381. {
  4382. // LDR s1, [base, offset(length)]
  4383. // CMP s1, 1 -- get the length on array and test if it is 1.
  4384. // BNE $helper
  4385. // LDR s2, [base, offset(scopes)] -- load the first scope
  4386. // LDR s3, [s2, offset(type)]
  4387. // LDIMM s4, inlineCache
  4388. // LDR s5, [s4, offset(u.local.type)]
  4389. // CMP s3, s5 -- check type
  4390. // BNE $helper
  4391. // LDR s6, [s2, offset(slots)] -- load the slots array
  4392. // LDR s7 , [s4, offset(u.local.slotIndex)] -- load the cached slot index
  4393. //
  4394. // if (load) {
  4395. // LDR dst, [s6, s7, LSL #2] -- load the value from the slot
  4396. // }
  4397. // else {
  4398. // STR src, [s6, s7, LSL #2]
  4399. // }
  4400. // B $done
  4401. //$helper:
  4402. // dst = BLX PatchGetPropertyScoped(inlineCache, base, field, defaultInstance, scriptContext)
  4403. //$done:
  4404. IR::Instr * instr;
  4405. IR::Instr * instrPrev = instrScopedFld->m_prev;
  4406. IR::RegOpnd * opndBase;
  4407. IR::RegOpnd * opndReg1; //s1
  4408. IR::RegOpnd * opndReg2; //s2
  4409. IR::RegOpnd * opndInlineCache; //s4
  4410. IR::IndirOpnd * indirOpnd;
  4411. IR::Opnd * propertyBase;
  4412. IR::LabelInstr * labelHelper;
  4413. IR::LabelInstr * labelFallThru;
  4414. if (isLoad)
  4415. {
  4416. propertyBase = instrScopedFld->GetSrc1();
  4417. }
  4418. else
  4419. {
  4420. propertyBase = instrScopedFld->GetDst();
  4421. }
  4422. AssertMsg(propertyBase->IsSymOpnd() && propertyBase->AsSymOpnd()->IsPropertySymOpnd() && propertyBase->AsSymOpnd()->m_sym->IsPropertySym(),
  4423. "Expected property sym operand of ScopedLdFld or ScopedStFld");
  4424. IR::PropertySymOpnd * propertySymOpnd = propertyBase->AsPropertySymOpnd();
  4425. opndBase = propertySymOpnd->CreatePropertyOwnerOpnd(m_func);
  4426. AssertMsg(opndBase->m_sym->m_isSingleDef, "We assume this isn't redefined");
  4427. labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  4428. // LDR s1, [base, offset(length)] -- get the length on array and test if it is 1.
  4429. indirOpnd = IR::IndirOpnd::New(opndBase, Js::FrameDisplay::GetOffsetOfLength(), TyInt16, this->m_func);
  4430. opndReg1 = IR::RegOpnd::New(TyInt32, this->m_func);
  4431. instr = IR::Instr::New(Js::OpCode::LDR, opndReg1, indirOpnd, this->m_func);
  4432. instrScopedFld->InsertBefore(instr);
  4433. // CMP s1, 1 -- get the length on array and test if it is 1.
  4434. instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  4435. instr->SetSrc1(opndReg1);
  4436. instr->SetSrc2(IR::IntConstOpnd::New(0x1, TyInt8, this->m_func));
  4437. instrScopedFld->InsertBefore(instr);
  4438. // BNE $helper
  4439. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelHelper, this->m_func);
  4440. instrScopedFld->InsertBefore(instr);
  4441. // LDR s2, [base, offset(scopes)] -- load the first scope
  4442. indirOpnd = IR::IndirOpnd::New(opndBase, Js::FrameDisplay::GetOffsetOfScopes(), TyInt32,this->m_func);
  4443. opndReg2 = IR::RegOpnd::New(TyInt32, this->m_func);
  4444. instr = IR::Instr::New(Js::OpCode::LDR, opndReg2, indirOpnd, this->m_func);
  4445. instrScopedFld->InsertBefore(instr);
  4446. // LDR s3, [s2, offset(type)]
  4447. // LDIMM s4, inlineCache
  4448. // LDR s5, [s4, offset(u.local.type)]
  4449. // CMP s3, s5 -- check type
  4450. // BNE $helper
  4451. opndInlineCache = IR::RegOpnd::New(TyInt32, this->m_func);
  4452. opndReg2->m_sym->m_isNotInt = true;
  4453. IR::RegOpnd * opndType = IR::RegOpnd::New(TyMachReg, this->m_func);
  4454. this->m_lowerer->GenerateObjectTestAndTypeLoad(instrScopedFld, opndReg2, opndType, labelHelper);
  4455. LowererMD::CreateAssign(opndInlineCache, m_lowerer->LoadRuntimeInlineCacheOpnd(instrScopedFld, propertySymOpnd), instrScopedFld);
  4456. labelFallThru = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  4457. // Check the local cache with the tagged type
  4458. IR::RegOpnd * opndTaggedType = IR::RegOpnd::New(TyMachReg, this->m_func);
  4459. GenerateLoadTaggedType(instrScopedFld, opndType, opndTaggedType);
  4460. GenerateLocalInlineCacheCheck(instrScopedFld, opndTaggedType, opndInlineCache, labelHelper);
  4461. if (isLoad)
  4462. {
  4463. IR::Opnd *opndDst = instrScopedFld->GetDst();
  4464. GenerateLdFldFromLocalInlineCache(instrScopedFld, opndReg2, opndDst, opndInlineCache, labelFallThru, false);
  4465. }
  4466. else
  4467. {
  4468. IR::Opnd *opndSrc = instrScopedFld->GetSrc1();
  4469. GenerateStFldFromLocalInlineCache(instrScopedFld, opndReg2, opndSrc, opndInlineCache, labelFallThru, false);
  4470. }
  4471. // $helper:
  4472. // if (isLoad) {
  4473. // dst = BLX PatchGetPropertyScoped(inlineCache, opndBase, propertyId, srcBase, scriptContext)
  4474. // }
  4475. // else {
  4476. // BLX PatchSetPropertyScoped(inlineCache, base, field, value, defaultInstance, scriptContext)
  4477. // }
  4478. // $fallthru:
  4479. instrScopedFld->InsertBefore(labelHelper);
  4480. instrScopedFld->InsertAfter(labelFallThru);
  4481. return instrPrev;
  4482. }
  4483. //----------------------------------------------------------------------------
  4484. //
  4485. // LowererMD::GenerateFastScopedLdFld
  4486. //
  4487. // Make use of the helper to cache the type and slot index used to do a ScopedLdFld
  4488. // when the scope is an array of length 1.
  4489. // Extract the only element from array and do an inline load from the appropriate slot
  4490. // if the type hasn't changed since the last time this ScopedLdFld was executed.
  4491. //
  4492. //----------------------------------------------------------------------------
  4493. IR::Instr *
  4494. LowererMD::GenerateFastScopedLdFld(IR::Instr * instrLdScopedFld)
  4495. {
  4496. //Helper GenerateFastScopedFldLookup generates following:
  4497. //
  4498. // LDR s1, [base, offset(length)]
  4499. // CMP s1, 1 -- get the length on array and test if it is 1.
  4500. // BNE $helper
  4501. // LDR s2, [base, offset(scopes)] -- load the first scope
  4502. // LDR s3, [s2, offset(type)]
  4503. // LDIMM s4, inlineCache
  4504. // LDR s5, [s4, offset(u.local.type)]
  4505. // CMP s3, s5 -- check type
  4506. // BNE $helper
  4507. // LDR s6, [s2, offset(slots)] -- load the slots array
  4508. // LDR s7 , [s4, offset(u.local.slotIndex)] -- load the cached slot index
  4509. // LDR dst, [s6, s7, LSL #2] -- load the value from the slot
  4510. // B $done
  4511. //$helper:
  4512. // dst = BLX PatchGetPropertyScoped(inlineCache, base, field, defaultInstance, scriptContext)
  4513. //$done:
  4514. return GenerateFastScopedFld(instrLdScopedFld, true);
  4515. }
  4516. //----------------------------------------------------------------------------
  4517. //
  4518. // LowererMD::GenerateFastScopedStFld
  4519. //
  4520. // Make use of the helper to cache the type and slot index used to do a ScopedStFld
  4521. // when the scope is an array of length 1.
  4522. // Extract the only element from array and do an inline load from the appropriate slot
  4523. // if the type hasn't changed since the last time this ScopedStFld was executed.
  4524. //
  4525. //----------------------------------------------------------------------------
  4526. IR::Instr *
  4527. LowererMD::GenerateFastScopedStFld(IR::Instr * instrStScopedFld)
  4528. {
  4529. // LDR s1, [base, offset(length)]
  4530. // CMP s1, 1 -- get the length on array and test if it is 1.
  4531. // BNE $helper
  4532. // LDR s2, [base, offset(scopes)] -- load the first scope
  4533. // LDR s3, [s2, offset(type)]
  4534. // LDIMM s4, inlineCache
  4535. // LDR s5, [s4, offset(u.local.type)]
  4536. // CMP s3, s5 -- check type
  4537. // BNE $helper
  4538. // LDR s6, [s2, offset(slots)] -- load the slots array
  4539. // LDR s7 , [s4, offset(u.local.slotIndex)] -- load the cached slot index
  4540. // STR src, [s6, s7, LSL #2] -- store the value directly at the slot
  4541. // B $done
  4542. //$helper:
  4543. // BLX PatchSetPropertyScoped(inlineCache, base, field, value, defaultInstance, scriptContext)
  4544. //$done:
  4545. return GenerateFastScopedFld(instrStScopedFld, false);
  4546. }
  4547. void
  4548. LowererMD::GenerateStFldFromLocalInlineCache(
  4549. IR::Instr * instrStFld,
  4550. IR::RegOpnd * opndBase,
  4551. IR::Opnd * opndSrc,
  4552. IR::RegOpnd * opndInlineCache,
  4553. IR::LabelInstr * labelFallThru,
  4554. bool isInlineSlot)
  4555. {
  4556. IR::RegOpnd * opndSlotArray = nullptr;
  4557. IR::IndirOpnd * opndIndir;
  4558. IR::Instr * instr;
  4559. if (!isInlineSlot)
  4560. {
  4561. // s2 = MOV base->slots -- load the slot array
  4562. opndSlotArray = IR::RegOpnd::New(TyMachReg, instrStFld->m_func);
  4563. opndIndir = IR::IndirOpnd::New(opndBase, Js::DynamicObject::GetOffsetOfAuxSlots(), TyMachReg, instrStFld->m_func);
  4564. LowererMD::CreateAssign(opndSlotArray, opndIndir, instrStFld);
  4565. }
  4566. // LDR s5, [s2, offset(u.local.slotIndex)] -- load the cached slot index
  4567. IR::RegOpnd *opndSlotIndex = IR::RegOpnd::New(TyUint16, instrStFld->m_func);
  4568. opndIndir = IR::IndirOpnd::New(opndInlineCache, offsetof(Js::InlineCache, u.local.slotIndex), TyUint16, instrStFld->m_func);
  4569. instr = IR::Instr::New(Js::OpCode::LDR, opndSlotIndex, opndIndir, instrStFld->m_func);
  4570. instrStFld->InsertBefore(instr);
  4571. if (isInlineSlot)
  4572. {
  4573. // STR src, [base, s5, LSL #2] -- store the value directly to the slot [s4 + s5 * 4] = src
  4574. opndIndir = IR::IndirOpnd::New(opndBase, opndSlotIndex, LowererMD::GetDefaultIndirScale(), TyMachReg, instrStFld->m_func);
  4575. instr = IR::Instr::New(Js::OpCode::STR, opndIndir, opndSrc, instrStFld->m_func);
  4576. instrStFld->InsertBefore(instr);
  4577. LegalizeMD::LegalizeInstr(instr, false);
  4578. }
  4579. else
  4580. {
  4581. // STR src, [s4, s5, LSL #2] -- store the value directly to the slot [s4 + s5 * 4] = src
  4582. opndIndir = IR::IndirOpnd::New(opndSlotArray, opndSlotIndex, LowererMD::GetDefaultIndirScale(), TyMachReg, instrStFld->m_func);
  4583. instr = IR::Instr::New(Js::OpCode::STR, opndIndir, opndSrc, instrStFld->m_func);
  4584. instrStFld->InsertBefore(instr);
  4585. LegalizeMD::LegalizeInstr(instr, false);
  4586. }
  4587. // B $done
  4588. instr = IR::BranchInstr::New(Js::OpCode::B, labelFallThru, instrStFld->m_func);
  4589. instrStFld->InsertBefore(instr);
  4590. }
  4591. IR::IndirOpnd *
  4592. LowererMD::GenerateFastElemIStringIndexCommon(
  4593. IR::Instr * instrInsert,
  4594. bool isStore,
  4595. IR::IndirOpnd *indirOpnd,
  4596. IR::LabelInstr * labelHelper
  4597. )
  4598. {
  4599. // Generates:
  4600. // CMP indexOpnd, PropertyString::`vtable' -- check if index is property string
  4601. // BNE $helper
  4602. // LDR propertyCacheOpnd, index->propCache
  4603. // TST baseOpnd, AtomTag -- check base not tagged int
  4604. // BNE $helper
  4605. // LDR objectTypeOpnd, baseOpnd->type
  4606. // CMP [propertyCacheOpnd->type], objectTypeOpnd -- check if object type match the cache
  4607. // BNE $helper
  4608. // CMP [propertyCacheOpnd->isInlineSlot,1] -- check if it is inline slots
  4609. // BEQ $inlineSlot
  4610. // LDR slotOpnd, [baseOpnd->slot] -- load the aux slot
  4611. // B $afterLabel
  4612. // $inlineSlot:
  4613. // LDR slotOpnd, baseOpnd -- use the object as start of the slot offset
  4614. // $afterLabel:
  4615. // LDR offsetOpnd, [propertyCacheOpnd->dataSlotIndex] -- load the slot index
  4616. // <return slotOpnd, offsetOpnd, PtrSize as ppHead,p pIndex, pScale>
  4617. IR::RegOpnd *indexOpnd = indirOpnd->GetIndexOpnd();
  4618. IR::RegOpnd *baseOpnd = indirOpnd->GetBaseOpnd();
  4619. Assert(baseOpnd != nullptr);
  4620. Assert(indexOpnd->GetValueType().IsString());
  4621. // CMP [indexOpnd], PropertyString::`vtable' -- check if index is property string
  4622. // BNE $helper
  4623. IR::Instr * checkVtableInstr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  4624. checkVtableInstr->SetSrc1(IR::IndirOpnd::New(indexOpnd, (int32)0, TyMachPtr, this->m_func));
  4625. checkVtableInstr->SetSrc2(m_lowerer->LoadVTableValueOpnd(instrInsert, VTableValue::VtablePropertyString));
  4626. instrInsert->InsertBefore(checkVtableInstr);
  4627. LegalizeMD::LegalizeInstr(checkVtableInstr, false);
  4628. instrInsert->InsertBefore(IR::BranchInstr::New(Js::OpCode::BNE, labelHelper, this->m_func));
  4629. // LDR propertyCacheOpnd, index->propCache
  4630. IR::RegOpnd * propertyCacheOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
  4631. IR::Instr * loadPropertyCacheInstr = IR::Instr::New(Js::OpCode::LDR, propertyCacheOpnd,
  4632. IR::IndirOpnd::New(indexOpnd, Js::PropertyString::GetOffsetOfPropertyCache(), TyMachReg, this->m_func), this->m_func);
  4633. instrInsert->InsertBefore(loadPropertyCacheInstr);
  4634. // TST baseOpnd, AtomTag -- check base not tagged int
  4635. // BNE $helper
  4636. if (!(baseOpnd->m_sym->m_isNotInt || baseOpnd->GetValueType().IsNotInt()))
  4637. {
  4638. GenerateObjectTest(baseOpnd, instrInsert, labelHelper);
  4639. }
  4640. // LDR s2, baseOpnd->type
  4641. // CMP [propertyCacheOpnd->type], s2 -- check if object type match the cache
  4642. // BNE $helper
  4643. IR::RegOpnd * objectTypeOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
  4644. IR::Instr * loadObjectTypeInstr = IR::Instr::New(Js::OpCode::LDR,
  4645. objectTypeOpnd, IR::IndirOpnd::New(baseOpnd, Js::RecyclableObject::GetOffsetOfType(), TyMachPtr, this->m_func),
  4646. this->m_func);
  4647. instrInsert->InsertBefore(loadObjectTypeInstr);
  4648. IR::Instr * checkTypeInstr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  4649. checkTypeInstr->SetSrc1(IR::IndirOpnd::New(propertyCacheOpnd, (int32)offsetof(Js::PropertyCache, type), TyMachPtr, this->m_func));
  4650. checkTypeInstr->SetSrc2(objectTypeOpnd);
  4651. instrInsert->InsertBefore(checkTypeInstr);
  4652. LegalizeMD::LegalizeInstr(checkTypeInstr, false);
  4653. instrInsert->InsertBefore(IR::BranchInstr::New(Js::OpCode::BNE, labelHelper, this->m_func));
  4654. if (isStore)
  4655. {
  4656. IR::IndirOpnd* isStoreEnabledOpnd = IR::IndirOpnd::New(propertyCacheOpnd, (int32)offsetof(Js::PropertyCache, isStoreFieldEnabled), TyInt8, this->m_func);
  4657. IR::IntConstOpnd* zeroOpnd = IR::IntConstOpnd::New(0, TyInt8, this->m_func, /* dontEncode = */ true);
  4658. this->m_lowerer->InsertCompareBranch(isStoreEnabledOpnd, zeroOpnd, Js::OpCode::BrEq_A, labelHelper, instrInsert);
  4659. }
  4660. // CMP [propertyCacheOpnd->isInlineSlot,1] -- check if it is inline slots
  4661. // BEQ $inlineSlot
  4662. IR::Instr * inlineSlotTestInstr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  4663. inlineSlotTestInstr->SetSrc1(IR::IndirOpnd::New(propertyCacheOpnd, (int32)offsetof(Js::PropertyCache, isInlineSlot), TyInt8, this->m_func));
  4664. inlineSlotTestInstr->SetSrc2(IR::IntConstOpnd::New(1, TyInt8, this->m_func));
  4665. instrInsert->InsertBefore(inlineSlotTestInstr);
  4666. LegalizeMD::LegalizeInstr(inlineSlotTestInstr, false);
  4667. IR::LabelInstr * isInlineSlotLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  4668. instrInsert->InsertBefore(IR::BranchInstr::New(Js::OpCode::BEQ, isInlineSlotLabel, this->m_func));
  4669. // LDR slotOpnd, [baseOpnd->slot] -- load the aux slot
  4670. // B $afterLabel
  4671. IR::RegOpnd * slotOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
  4672. instrInsert->InsertBefore(IR::Instr::New(Js::OpCode::LDR, slotOpnd,
  4673. IR::IndirOpnd::New(baseOpnd, Js::DynamicObject::GetOffsetOfAuxSlots(), TyMachPtr, this->m_func), this->m_func));
  4674. IR::LabelInstr * afterLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  4675. instrInsert->InsertBefore(IR::BranchInstr::New(Js::OpCode::B, afterLabel, this->m_func));
  4676. // $inlineSlot:
  4677. // LDR slotOpnd, baseOpnd -- use the object as start of the slot offset
  4678. instrInsert->InsertBefore(isInlineSlotLabel);
  4679. instrInsert->InsertBefore(IR::Instr::New(Js::OpCode::LDR, slotOpnd, baseOpnd, this->m_func));
  4680. // $afterLabel:
  4681. // LDR offsetOpnd, [propertyCacheOpnd->dataSlotIndex] -- load the slot index
  4682. instrInsert->InsertBefore(afterLabel);
  4683. IR::RegOpnd * offsetOpnd = IR::RegOpnd::New(TyInt32, this->m_func);
  4684. instrInsert->InsertBefore(IR::Instr::New(Js::OpCode::LDR, offsetOpnd,
  4685. IR::IndirOpnd::New(propertyCacheOpnd, (int32)offsetof(Js::PropertyCache, dataSlotIndex), TyUint16, this->m_func), this->m_func));
  4686. // return [slotOpnd + offsetOpnd * PtrSize]
  4687. return IR::IndirOpnd::New(slotOpnd, offsetOpnd, this->GetDefaultIndirScale(), TyVar, this->m_func);
  4688. }
  4689. IR::Opnd *
  4690. LowererMD::CreateStackArgumentsSlotOpnd()
  4691. {
  4692. // Save the newly-created args object to its dedicated stack slot.
  4693. IR::IndirOpnd *indirOpnd = IR::IndirOpnd::New(IR::RegOpnd::New(nullptr, FRAME_REG , TyMachReg, m_func),
  4694. -MachArgsSlotOffset, TyMachPtr, m_func);
  4695. return indirOpnd;
  4696. }
  4697. //
  4698. // jump to $labelHelper, based on the result of TST
  4699. //
  4700. void LowererMD::GenerateSmIntTest(IR::Opnd *opndSrc, IR::Instr *insertInstr, IR::LabelInstr *labelHelper, IR::Instr **instrFirst, bool fContinueLabel /* = false */)
  4701. {
  4702. // TEST src1, AtomTag
  4703. IR::Instr* instr = IR::Instr::New(Js::OpCode::TST, this->m_func);
  4704. instr->SetSrc1(opndSrc);
  4705. instr->SetSrc2(IR::IntConstOpnd::New(Js::AtomTag, TyInt32, this->m_func));
  4706. insertInstr->InsertBefore(instr);
  4707. if(fContinueLabel)
  4708. {
  4709. // BNE $labelHelper
  4710. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelHelper, this->m_func);
  4711. insertInstr->InsertBefore(instr);
  4712. }
  4713. else
  4714. {
  4715. // BEQ $labelHelper
  4716. instr = IR::BranchInstr::New(Js::OpCode::BEQ, labelHelper, this->m_func);
  4717. insertInstr->InsertBefore(instr);
  4718. }
  4719. }
  4720. void LowererMD::GenerateInt32ToVarConversion(IR::Opnd * opndSrc, IR::Instr * insertInstr )
  4721. {
  4722. AssertMsg(opndSrc->IsRegOpnd(), "NYI for other types");
  4723. // Shift left & tag.
  4724. // For now this is used only for actual arguments count can only be 24 bits long and non need to check for overflow
  4725. IR:: Instr* instr = IR::Instr::New(Js::OpCode::LSL, opndSrc, opndSrc, IR::IntConstOpnd::New(Js::VarTag_Shift, TyInt8, this->m_func), this->m_func);
  4726. insertInstr->InsertBefore(instr);
  4727. instr = IR::Instr::New(Js::OpCode::ADD, opndSrc, opndSrc,
  4728. IR::IntConstOpnd::New(Js::VarTag_Shift, TyMachReg, this->m_func),
  4729. this->m_func);
  4730. insertInstr->InsertBefore(instr);
  4731. }
  4732. IR::RegOpnd *
  4733. LowererMD::GenerateUntagVar(IR::RegOpnd * opnd, IR::LabelInstr * instrFail, IR::Instr * insertBeforeInstr, bool generateTagCheck)
  4734. {
  4735. // Generates:
  4736. // int32Opnd = ASRS opnd, Js::VarTag_Shift -- shift-out tag from opnd
  4737. // BCC $helper -- if not tagged int, jmp to $helper
  4738. // Returns: index32Opnd
  4739. Assert(opnd->IsVar());
  4740. IR::RegOpnd * int32Opnd = IR::RegOpnd::New(TyInt32, this->m_func);
  4741. // int32Opnd = ASRS opnd, Js::VarTag_Shift -- shift-out tag from indexOpnd
  4742. IR::Instr *instr = IR::Instr::New(Js::OpCode::ASRS, int32Opnd, opnd,
  4743. IR::IntConstOpnd::New(Js::VarTag_Shift, TyInt8, this->m_func), this->m_func);
  4744. insertBeforeInstr->InsertBefore(instr);
  4745. LegalizeMD::LegalizeInstr(instr, false);
  4746. // No need to check if we already know that it is a tagged int.
  4747. if (generateTagCheck)
  4748. {
  4749. Assert(!opnd->IsTaggedInt());
  4750. // BCC $helper -- if not tagged int, jmp to $helper
  4751. instr = IR::BranchInstr::New(Js::OpCode::BCC, instrFail, this->m_func);
  4752. insertBeforeInstr->InsertBefore(instr);
  4753. }
  4754. return int32Opnd;
  4755. }
  4756. IR::RegOpnd *LowererMD::LoadNonnegativeIndex(
  4757. IR::RegOpnd *indexOpnd,
  4758. const bool skipNegativeCheck,
  4759. IR::LabelInstr *const notTaggedIntLabel,
  4760. IR::LabelInstr *const negativeLabel,
  4761. IR::Instr *const insertBeforeInstr)
  4762. {
  4763. Assert(indexOpnd);
  4764. Assert(indexOpnd->IsVar() || indexOpnd->GetType() == TyInt32 || indexOpnd->GetType() == TyUint32);
  4765. Assert(indexOpnd->GetType() != TyUint32 || skipNegativeCheck);
  4766. Assert(!indexOpnd->IsVar() || notTaggedIntLabel);
  4767. Assert(skipNegativeCheck || negativeLabel);
  4768. Assert(insertBeforeInstr);
  4769. Func *const func = insertBeforeInstr->m_func;
  4770. IR::AutoReuseOpnd autoReuseIndexOpnd;
  4771. if(indexOpnd->IsVar())
  4772. {
  4773. if (indexOpnd->GetValueType().IsLikelyFloat())
  4774. {
  4775. return m_lowerer->LoadIndexFromLikelyFloat(indexOpnd, skipNegativeCheck, notTaggedIntLabel, negativeLabel, insertBeforeInstr);
  4776. }
  4777. // asrs intIndex, index, 1
  4778. // bcc $notTaggedIntOrNegative
  4779. IR::RegOpnd *const intIndexOpnd = IR::RegOpnd::New(TyInt32, func);
  4780. if(skipNegativeCheck)
  4781. {
  4782. intIndexOpnd->SetType(TyUint32);
  4783. }
  4784. autoReuseIndexOpnd.Initialize(intIndexOpnd, func, false);
  4785. const bool isTaggedInt = indexOpnd->IsTaggedInt();
  4786. Lowerer::InsertShift(
  4787. Js::OpCode::Shr_A,
  4788. !(isTaggedInt && skipNegativeCheck),
  4789. intIndexOpnd,
  4790. indexOpnd,
  4791. IR::IntConstOpnd::New(Js::VarTag_Shift, TyInt8, func),
  4792. insertBeforeInstr);
  4793. if(!isTaggedInt)
  4794. {
  4795. Lowerer::InsertBranch(Js::OpCode::BCC, notTaggedIntLabel, insertBeforeInstr);
  4796. }
  4797. indexOpnd = intIndexOpnd;
  4798. }
  4799. else if(!skipNegativeCheck)
  4800. {
  4801. // tst index, index
  4802. Lowerer::InsertTest(indexOpnd, indexOpnd, insertBeforeInstr);
  4803. }
  4804. if(!skipNegativeCheck)
  4805. {
  4806. // bmi $notTaggedIntOrNegative
  4807. Lowerer::InsertBranch(Js::OpCode::BMI, negativeLabel, insertBeforeInstr);
  4808. }
  4809. return indexOpnd;
  4810. }
  4811. bool LowererMD::GenerateJSBooleanTest(IR::RegOpnd * regSrc, IR::Instr * insertInstr, IR::LabelInstr * labelTarget, bool fContinueLabel)
  4812. {
  4813. IR::Instr* instr;
  4814. if (regSrc->GetValueType().IsBoolean())
  4815. {
  4816. if (fContinueLabel)
  4817. {
  4818. // B $labelTarget
  4819. instr = IR::BranchInstr::New(Js::OpCode::B, labelTarget, this->m_func);
  4820. insertInstr->InsertBefore(instr);
  4821. #if DBG
  4822. if (labelTarget->isOpHelper)
  4823. {
  4824. labelTarget->m_noHelperAssert = true;
  4825. }
  4826. #endif
  4827. }
  4828. return false;
  4829. }
  4830. // CMP src1, vtable<JavaScriptBoolean>
  4831. instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  4832. IR::IndirOpnd *vtablePtrOpnd = IR::IndirOpnd::New(regSrc, 0, TyMachPtr, this->m_func);
  4833. instr->SetSrc1(vtablePtrOpnd);
  4834. IR::Opnd *jsBooleanVTable = m_lowerer->LoadVTableValueOpnd(insertInstr, VTableValue::VtableJavascriptBoolean);
  4835. instr->SetSrc2(jsBooleanVTable);
  4836. insertInstr->InsertBefore(instr);
  4837. LegalizeMD::LegalizeInstr(instr, false);
  4838. if (fContinueLabel)
  4839. {
  4840. // BEQ $labelTarget
  4841. instr = IR::BranchInstr::New(Js::OpCode::BEQ, labelTarget, this->m_func);
  4842. insertInstr->InsertBefore(instr);
  4843. IR::LabelInstr *labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  4844. insertInstr->InsertBefore(labelHelper);
  4845. }
  4846. else
  4847. {
  4848. // BNE $labelTarget
  4849. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelTarget, this->m_func);
  4850. insertInstr->InsertBefore(instr);
  4851. }
  4852. return true;
  4853. }
  4854. void
  4855. LowererMD::GenerateFastBrBReturn(IR::Instr *instr)
  4856. {
  4857. Assert(instr->m_opcode == Js::OpCode::BrOnEmpty || instr->m_opcode == Js::OpCode::BrOnNotEmpty);
  4858. AssertMsg(instr->GetSrc1() != nullptr && instr->GetSrc2() == nullptr, "Expected 1 src opnds on BrB");
  4859. Assert(instr->GetSrc1()->IsRegOpnd());
  4860. IR::RegOpnd * forInEnumeratorOpnd = instr->GetSrc1()->AsRegOpnd();
  4861. IR::LabelInstr * labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  4862. // LDR firstPrototypeOpnd, forInEnumerator->firstPrototype
  4863. // TST firstPrototypeOpnd, firstPrototypeOpnd
  4864. // BNE $helper
  4865. IR::RegOpnd * firstPrototypeOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
  4866. instr->InsertBefore(IR::Instr::New(Js::OpCode::LDR, firstPrototypeOpnd,
  4867. IR::IndirOpnd::New(forInEnumeratorOpnd, Js::ForInObjectEnumerator::GetOffsetOfFirstPrototype(), TyMachPtr, this->m_func), this->m_func));
  4868. IR::Instr * checkFirstPrototypeNullInstr = IR::Instr::New(Js::OpCode::TST, this->m_func);
  4869. checkFirstPrototypeNullInstr->SetSrc1(firstPrototypeOpnd);
  4870. checkFirstPrototypeNullInstr->SetSrc2(firstPrototypeOpnd);
  4871. instr->InsertBefore(checkFirstPrototypeNullInstr);
  4872. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BNE, labelHelper, this->m_func));
  4873. typedef Js::DynamicObjectSnapshotEnumeratorWPCache<Js::BigPropertyIndex, true, false> SmallDynamicObjectSnapshotEnumeratorWPCache;
  4874. // LDR currentEnumeratorOpnd, forInEnumerator->currentEnumerator
  4875. // CMP currentEnumeratorOpnd, SmallDynamicObjectSnapshotEnumeratorWPCache::`vtable
  4876. // BNE $helper
  4877. IR::RegOpnd * currentEnumeratorOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
  4878. instr->InsertBefore(IR::Instr::New(Js::OpCode::LDR, currentEnumeratorOpnd,
  4879. IR::IndirOpnd::New(forInEnumeratorOpnd, Js::ForInObjectEnumerator::GetOffsetOfCurrentEnumerator(), TyMachPtr, this->m_func), this->m_func));
  4880. IR::Instr * checkVtableInstr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  4881. checkVtableInstr->SetSrc1(IR::IndirOpnd::New(currentEnumeratorOpnd, 0, TyMachPtr, this->m_func));
  4882. checkVtableInstr->SetSrc2(m_lowerer->LoadVTableValueOpnd(instr, VTableValue::VtableSmallDynamicObjectSnapshotEnumeratorWPCache));
  4883. instr->InsertBefore(checkVtableInstr);
  4884. LegalizeMD::LegalizeInstr(checkVtableInstr, false);
  4885. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BNE, labelHelper, this->m_func));
  4886. // LDR arrayEnumeratorOpnd, currentEnumerator->arrayEnumerator
  4887. // TST arrayEnumeratorOpnd, arrayEnumeratorOpnd
  4888. // BNE $helper
  4889. IR::RegOpnd * arrayEnumeratorOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
  4890. instr->InsertBefore(IR::Instr::New(Js::OpCode::LDR, arrayEnumeratorOpnd,
  4891. IR::IndirOpnd::New(currentEnumeratorOpnd, SmallDynamicObjectSnapshotEnumeratorWPCache::GetOffsetOfArrayEnumerator(), TyMachPtr, this->m_func), this->m_func));
  4892. IR::Instr * checkArrayEnumeratorNullInstr = IR::Instr::New(Js::OpCode::TST, this->m_func);
  4893. checkArrayEnumeratorNullInstr->SetSrc1(arrayEnumeratorOpnd);
  4894. checkArrayEnumeratorNullInstr->SetSrc2(arrayEnumeratorOpnd);
  4895. instr->InsertBefore(checkArrayEnumeratorNullInstr);
  4896. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BNE, labelHelper, this->m_func));
  4897. // LDR objectOpnd, currentEnumerator->object
  4898. // LDR initialTypeOpnd, currentEnumerator->initialType
  4899. // CMP initialTypeOpnd, objectOpnd->type
  4900. // BNE $helper
  4901. IR::RegOpnd * objectOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
  4902. instr->InsertBefore(IR::Instr::New(Js::OpCode::LDR, objectOpnd,
  4903. IR::IndirOpnd::New(currentEnumeratorOpnd, SmallDynamicObjectSnapshotEnumeratorWPCache::GetOffsetOfObject(), TyMachPtr, this->m_func), this->m_func));
  4904. IR::RegOpnd * initialTypeOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
  4905. instr->InsertBefore(IR::Instr::New(Js::OpCode::LDR, initialTypeOpnd,
  4906. IR::IndirOpnd::New(currentEnumeratorOpnd, SmallDynamicObjectSnapshotEnumeratorWPCache::GetOffsetOfInitialType(), TyMachPtr, this->m_func), this->m_func));
  4907. IR::Instr * checkTypeInstr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  4908. checkTypeInstr->SetSrc1(initialTypeOpnd);
  4909. checkTypeInstr->SetSrc2(IR::IndirOpnd::New(objectOpnd, Js::DynamicObject::GetOffsetOfType(), TyMachPtr, this->m_func));
  4910. instr->InsertBefore(checkTypeInstr);
  4911. LegalizeMD::LegalizeInstr(checkTypeInstr, false);
  4912. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BNE, labelHelper, this->m_func));
  4913. // LDR enumeratedCountOpnd, currentEnumeratorOpnd->enumeratedCount
  4914. // LDR cachedDataOpnd, currentEnumeratorOpnd->cachedData
  4915. // CMP enumeratedCountOpnd, cachedDataOpnd->cachedCount
  4916. // BGE $helper
  4917. IR::RegOpnd * enumeratedCountOpnd = IR::RegOpnd::New(TyUint32, m_func);
  4918. instr->InsertBefore(IR::Instr::New(Js::OpCode::LDR, enumeratedCountOpnd,
  4919. IR::IndirOpnd::New(currentEnumeratorOpnd, SmallDynamicObjectSnapshotEnumeratorWPCache::GetOffsetOfEnumeratedCount(), TyUint32, this->m_func), this->m_func));
  4920. IR::RegOpnd * cachedDataOpnd = IR::RegOpnd::New(TyMachPtr, m_func);
  4921. instr->InsertBefore(IR::Instr::New(Js::OpCode::LDR, cachedDataOpnd,
  4922. IR::IndirOpnd::New(currentEnumeratorOpnd, SmallDynamicObjectSnapshotEnumeratorWPCache::GetOffsetOfCachedData(), TyMachPtr, this->m_func), this->m_func));
  4923. IR::Instr * checkEnumeratedCountInstr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  4924. checkEnumeratedCountInstr->SetSrc1(enumeratedCountOpnd);
  4925. checkEnumeratedCountInstr->SetSrc2(IR::IndirOpnd::New(cachedDataOpnd, SmallDynamicObjectSnapshotEnumeratorWPCache::GetOffsetOfCachedDataCachedCount(), TyUint32, this->m_func));
  4926. instr->InsertBefore(checkEnumeratedCountInstr);
  4927. LegalizeMD::LegalizeInstr(checkEnumeratedCountInstr, false);
  4928. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BGE, labelHelper, this->m_func));
  4929. // LDR propertyAttributesOpnd, cachedData->attributes
  4930. // LDR objectPropertyAttributesOpnd, propertyAttributesOpnd[enumeratedCount]
  4931. // CMP objectPropertyAttributesOpnd & PropertyEnumerable, PropertyEnumerable
  4932. // BNE $helper
  4933. IR::RegOpnd * propertyAttributesOpnd = IR::RegOpnd::New(TyMachPtr, m_func);
  4934. instr->InsertBefore(IR::Instr::New(Js::OpCode::LDR, propertyAttributesOpnd,
  4935. IR::IndirOpnd::New(cachedDataOpnd, SmallDynamicObjectSnapshotEnumeratorWPCache::GetOffsetOfCachedDataPropertyAttributes(), TyMachPtr, this->m_func), this->m_func));
  4936. IR::RegOpnd * objectPropertyAttributesOpnd = IR::RegOpnd::New(TyUint8, m_func);
  4937. IR::Instr * ldObjectPropertyAttributesInstr = IR::Instr::New(Js::OpCode::LDR, objectPropertyAttributesOpnd,
  4938. IR::IndirOpnd::New(propertyAttributesOpnd, enumeratedCountOpnd, IndirScale1, TyUint8, this->m_func), this->m_func);
  4939. instr->InsertBefore(ldObjectPropertyAttributesInstr);
  4940. LegalizeMD::LegalizeInstr(ldObjectPropertyAttributesInstr, false); // Maybe large offset
  4941. IR::Instr * andPropertyEnumerableInstr = Lowerer::InsertAnd(IR::RegOpnd::New(TyUint8, instr->m_func), objectPropertyAttributesOpnd, IR::IntConstOpnd::New(0x01, TyUint8, this->m_func), instr);
  4942. IR::Instr * checkEnumerableInstr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  4943. checkEnumerableInstr->SetSrc1(andPropertyEnumerableInstr->GetDst());
  4944. checkEnumerableInstr->SetSrc2(IR::IntConstOpnd::New(0x01, TyUint8, this->m_func));
  4945. instr->InsertBefore(checkEnumerableInstr);
  4946. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BNE, labelHelper, this->m_func));
  4947. IR::Opnd * opndDst = instr->GetDst(); // ForIn result propertyString
  4948. Assert(opndDst->IsRegOpnd());
  4949. // LDR stringsOpnd, cachedData->strings
  4950. // LDR opndDst, stringsOpnd[enumeratedCount]
  4951. IR::RegOpnd * stringsOpnd = IR::RegOpnd::New(TyMachPtr, m_func);
  4952. instr->InsertBefore(IR::Instr::New(Js::OpCode::LDR, stringsOpnd,
  4953. IR::IndirOpnd::New(cachedDataOpnd, SmallDynamicObjectSnapshotEnumeratorWPCache::GetOffsetOfCachedDataStrings(), TyMachPtr, this->m_func), this->m_func));
  4954. IR::Instr * ldDstInstr = IR::Instr::New(Js::OpCode::LDR, opndDst,
  4955. IR::IndirOpnd::New(stringsOpnd, enumeratedCountOpnd, this->GetDefaultIndirScale(), TyVar, this->m_func), this->m_func);
  4956. instr->InsertBefore(ldDstInstr);
  4957. LegalizeMD::LegalizeInstr(ldDstInstr, false); // Maybe large offset
  4958. // LDR indexesOpnd, cachedData->indexes
  4959. // LDR objectIndexOpnd, indexesOpnd[enumeratedCount]
  4960. // STR objectIndexOpnd, currentEnumeratorOpnd->objectIndex
  4961. IR::RegOpnd * indexesOpnd = IR::RegOpnd::New(TyMachPtr, m_func);
  4962. instr->InsertBefore(IR::Instr::New(Js::OpCode::LDR, indexesOpnd,
  4963. IR::IndirOpnd::New(cachedDataOpnd, SmallDynamicObjectSnapshotEnumeratorWPCache::GetOffsetOfCachedDataIndexes(), TyMachPtr, this->m_func), this->m_func));
  4964. IR::RegOpnd * objectIndexOpnd = IR::RegOpnd::New(TyUint32, m_func);
  4965. IR::Instr * ldObjectIndexInstr = IR::Instr::New(Js::OpCode::LDR, objectIndexOpnd,
  4966. IR::IndirOpnd::New(indexesOpnd, enumeratedCountOpnd, IndirScale4, TyUint32, this->m_func), this->m_func);
  4967. instr->InsertBefore(ldObjectIndexInstr);
  4968. LegalizeMD::LegalizeInstr(ldObjectIndexInstr, false); // Maybe large offset
  4969. instr->InsertBefore(IR::Instr::New(Js::OpCode::STR,
  4970. IR::IndirOpnd::New(currentEnumeratorOpnd, SmallDynamicObjectSnapshotEnumeratorWPCache::GetOffsetOfObjectIndex(), TyUint32, this->m_func),
  4971. objectIndexOpnd, this->m_func));
  4972. // enumeratedCountOpnd = ADD enumeratedCountOpnd, 1
  4973. // STR enumeratedCountOpnd, currentEnumeratorOpnd->enumeratedCount
  4974. instr->InsertBefore(IR::Instr::New(Js::OpCode::ADD, enumeratedCountOpnd, enumeratedCountOpnd, IR::IntConstOpnd::New(1, TyUint32, this->m_func), this->m_func));
  4975. instr->InsertBefore(IR::Instr::New(Js::OpCode::STR,
  4976. IR::IndirOpnd::New(currentEnumeratorOpnd, SmallDynamicObjectSnapshotEnumeratorWPCache::GetOffsetOfEnumeratedCount(), TyUint32, this->m_func),
  4977. enumeratedCountOpnd, this->m_func));
  4978. // We know result propertyString (opndDst) != nullptr
  4979. IR::LabelInstr * labelAfter = instr->GetOrCreateContinueLabel();
  4980. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::B,
  4981. instr->m_opcode == Js::OpCode::BrOnNotEmpty ? instr->AsBranchInstr()->GetTarget() : labelAfter,
  4982. this->m_func));
  4983. // $helper
  4984. instr->InsertBefore(labelHelper);
  4985. // $after
  4986. }
  4987. // Inlines fast-path for int Mul/Add or int Mul/Sub. If not int, call MulAdd/MulSub helper
  4988. bool LowererMD::TryGenerateFastMulAdd(IR::Instr * instrAdd, IR::Instr ** pInstrPrev)
  4989. {
  4990. IR::Instr *instrMul = instrAdd->GetPrevRealInstrOrLabel();
  4991. IR::Opnd *addSrc;
  4992. IR::RegOpnd *addCommonSrcOpnd;
  4993. Assert(instrAdd->m_opcode == Js::OpCode::Add_A || instrAdd->m_opcode == Js::OpCode::Sub_A);
  4994. if (instrAdd->m_opcode != Js::OpCode::Add_A)
  4995. {
  4996. // For Add_A we can use SMLAL, but there is no analog of that for Sub_A.
  4997. return false;
  4998. }
  4999. // Mul needs to be a single def reg
  5000. if (instrMul->m_opcode != Js::OpCode::Mul_A || !instrMul->GetDst()->IsRegOpnd())
  5001. {
  5002. // Cannot generate MulAdd
  5003. return false;
  5004. }
  5005. if (instrMul->HasBailOutInfo())
  5006. {
  5007. // Bailout will be generated for the Add, but not the Mul.
  5008. // We could handle this, but this path isn't used that much anymore.
  5009. return false;
  5010. }
  5011. IR::RegOpnd *regMulDst = instrMul->GetDst()->AsRegOpnd();
  5012. if (!regMulDst->m_sym->m_isSingleDef)
  5013. {
  5014. // Cannot generate MulAdd
  5015. return false;
  5016. }
  5017. // Only handle a * b + c, so dst of Mul needs to match left source of Add
  5018. if (instrMul->GetDst()->IsEqual(instrAdd->GetSrc1()))
  5019. {
  5020. addCommonSrcOpnd = instrAdd->GetSrc1()->AsRegOpnd();
  5021. addSrc = instrAdd->GetSrc2();
  5022. }
  5023. else if (instrMul->GetDst()->IsEqual(instrAdd->GetSrc2()))
  5024. {
  5025. addSrc = instrAdd->GetSrc1();
  5026. addCommonSrcOpnd = instrAdd->GetSrc2()->AsRegOpnd();
  5027. }
  5028. else
  5029. {
  5030. return false;
  5031. }
  5032. // Only handle a * b + c where c != a * b
  5033. if (instrAdd->GetSrc1()->IsEqual(instrAdd->GetSrc2()))
  5034. {
  5035. return false;
  5036. }
  5037. if (!addCommonSrcOpnd->m_isTempLastUse)
  5038. {
  5039. return false;
  5040. }
  5041. IR::Opnd *mulSrc1 = instrMul->GetSrc1();
  5042. IR::Opnd *mulSrc2 = instrMul->GetSrc2();
  5043. if (mulSrc1->IsRegOpnd() && mulSrc1->AsRegOpnd()->IsTaggedInt()
  5044. && mulSrc2->IsRegOpnd() && mulSrc2->AsRegOpnd()->IsTaggedInt())
  5045. {
  5046. return false;
  5047. }
  5048. IR::LabelInstr *labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  5049. IR::LabelInstr *labelDone = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, false);
  5050. // Save prevInstr for the main lower loop
  5051. *pInstrPrev = instrMul->m_prev;
  5052. // Generate int31 fast-path for Mul-Add, go to MulAdd helper if it fails, or one of the source is marked notInt
  5053. if (!(addSrc->IsRegOpnd() && addSrc->AsRegOpnd()->m_sym->AsStackSym()->m_isNotInt)
  5054. && !(mulSrc1->IsRegOpnd() && mulSrc1->AsRegOpnd()->m_sym->AsStackSym()->m_isNotInt)
  5055. && !(mulSrc2->IsRegOpnd() && mulSrc2->AsRegOpnd()->m_sym->AsStackSym()->m_isNotInt))
  5056. {
  5057. // General idea:
  5058. // - mulSrc1: clear 1 but keep *2 - need special test for tagged int
  5059. // - mulSrc2: shift out the tag - test for overflow inplace
  5060. // - addSrc: keep as is - need special test for tagged int
  5061. //
  5062. // Concerns
  5063. // - we don't need to take care of negative zero/-0, here's why:
  5064. // - per ES5 spec, there are only way to get -0 with add/sub: -0 + -0, -0 -0.
  5065. // - first one is not applicable because -0 would not be a tagged int, so we'll use the helper.
  5066. // - second one is also not applicable because this fast path is only for mul-add, not mul-sub.
  5067. //
  5068. // Steps:
  5069. // (If not mulSrc1 and addSrc are Int31's, jump to $helper)
  5070. // s1 = SUB mulSrc1, 1 -- remove the tag from mulSrc1 but keep it as *2
  5071. // s2 = ASRS mulSrc2, 1 -- shift-out tag from mulSrc2
  5072. // BCC $helper -- if not tagged int, jmp to $helper
  5073. // (Now: mulSrc1 in s1, mulSrc2 in s2)
  5074. // r12 = ASR s3, 31 -- make r12 to be sign-extension of the addSrc.
  5075. // r12:s3 = SMLAL s1, s2 -- note: the add source comes from r12:s3, result is already tagged int = mulSrc1Val*2 * mulSrc2Val + addSrcVal * 2 + 1
  5076. // Overflow check:
  5077. // (SMLAL doesn't set the flags but we don't have 32bit overflow <=> r12-unsigned ? r12==0 : all 33 bits of 64bit result are 1's
  5078. // CMP r12, s3, ASR #31 -- check for overflow (== means no overflow)
  5079. // BNE $helper -- bail if the result overflowed
  5080. // Copy the result into dst
  5081. // dst = s3
  5082. // B $done
  5083. // $helper:
  5084. // ...
  5085. // $done:
  5086. IR::Instr* instr;
  5087. IR::RegOpnd* s1 = IR::RegOpnd::New(mulSrc1->GetType(), this->m_func);
  5088. IR::RegOpnd* s2 = IR::RegOpnd::New(mulSrc2->GetType(), this->m_func);
  5089. IR::RegOpnd* s3 = IR::RegOpnd::New(addSrc->GetType(), this->m_func);
  5090. IR::RegOpnd* opndRegR12 = IR::RegOpnd::New(nullptr, RegR12, TyMachReg, this->m_func);
  5091. // (Load mulSrc1 at the top so we don't have to do it repeatedly)
  5092. if (!mulSrc1->IsRegOpnd())
  5093. {
  5094. LowererMD::CreateAssign(s1, mulSrc1, instrAdd);
  5095. mulSrc1 = s1;
  5096. }
  5097. // Now: mulSrc1 is regOpnd (in case if it wasn't it's now s1).
  5098. // Load addSrc into s3. We'll use it as source and destination of SMLAL.
  5099. LowererMD::CreateAssign(s3, addSrc, instrAdd);
  5100. // (If not mulSrc1 and addSrc are Int31's, jump to $helper)
  5101. bool areTaggedInts = mulSrc1->IsTaggedInt() && s3->IsTaggedInt();
  5102. if (!areTaggedInts)
  5103. {
  5104. this->GenerateSmIntPairTest(instrAdd, mulSrc1->AsRegOpnd(), s3->AsRegOpnd(), labelHelper);
  5105. }
  5106. // s1 = SUB mulSrc1, 1 -- remove the tag from mulSrc1 but keep it as *2
  5107. instr = IR::Instr::New(Js::OpCode::SUB, s1, mulSrc1, IR::IntConstOpnd::New(Js::VarTag_Shift, TyVar, this->m_func), m_func);
  5108. instrAdd->InsertBefore(instr);
  5109. // s2 = ASRS mulSrc2, 1 -- shift-out tag from mulSrc2
  5110. // BCC $helper -- if not tagged int, jmp to $helper
  5111. instr = IR::Instr::New(Js::OpCode::ASRS, s2, mulSrc2, IR::IntConstOpnd::New(Js::VarTag_Shift, TyVar, this->m_func), m_func);
  5112. instrAdd->InsertBefore(instr);
  5113. LegalizeMD::LegalizeInstr(instr, false);
  5114. if (!mulSrc2->IsTaggedInt()) // If we already pre-know it's tagged int, no need to check.
  5115. {
  5116. instr = IR::BranchInstr::New(Js::OpCode::BCC, labelHelper, this->m_func);
  5117. instrAdd->InsertBefore(instr);
  5118. }
  5119. // Now: mulSrc1 in s1, mulSrc2 in s2.
  5120. // r12 = ASR s3, 31 -- make r12 to be sign-extension of the addSrc.
  5121. instr = IR::Instr::New(Js::OpCode::ASR, opndRegR12, s3, IR::IntConstOpnd::New(31, TyVar, this->m_func), m_func);
  5122. instrAdd->InsertBefore(instr);
  5123. // r12:s3 = SMLAL s1, s2 -- note: the add source comes from r12:s3, result is already tagged int = mulSrc1Val*2 * mulSrc2Val + addSrcVal * 2 + 1
  5124. instr = IR::Instr::New(Js::OpCode::SMLAL, s3, s1, s2, this->m_func);
  5125. instrAdd->InsertBefore(instr);
  5126. // Overflow check:
  5127. // (SMLAL doesn't set the flags but we don't have 32bit overflow <=> r12-unsigned ? r12==0 : all 33 bits of 64bit result are 1's
  5128. // CMP r12, s3, ASR #31 -- check for overflow (== means no overflow)
  5129. // BNE $helper -- bail if the result overflowed
  5130. instr = IR::Instr::New(Js::OpCode::CMP_ASR31, this->m_func);
  5131. instr->SetSrc1(opndRegR12);
  5132. instr->SetSrc2(s3);
  5133. instrAdd->InsertBefore(instr);
  5134. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelHelper, this->m_func);
  5135. instrAdd->InsertBefore(instr);
  5136. // Copy the result into dst
  5137. // dst = s3
  5138. LowererMD::CreateAssign(instrAdd->GetDst(), s3, instrAdd);
  5139. LegalizeMD::LegalizeInstr(instr, false);
  5140. // B $done
  5141. instr = IR::BranchInstr::New(Js::OpCode::B, labelDone, this->m_func);
  5142. instrAdd->InsertBefore(instr);
  5143. instrAdd->InsertBefore(labelHelper);
  5144. instrAdd->InsertAfter(labelDone);
  5145. }
  5146. // Generate code to call the Mul-Add helper.
  5147. // Although for the case when one of the source is marked notInt we could just return false from here,
  5148. // it seems that since we did all the checks to see that this is mul+add, it makes sense to use mul-add helper
  5149. // rather than 2 separate helpers - one for mul and one for add (by returning false).
  5150. if (instrAdd->dstIsTempNumber)
  5151. {
  5152. m_lowerer->LoadHelperTemp(instrAdd, instrAdd);
  5153. }
  5154. else
  5155. {
  5156. IR::Opnd *tempOpnd = IR::IntConstOpnd::New(0, TyMachPtr, this->m_func);
  5157. this->LoadHelperArgument(instrAdd, tempOpnd);
  5158. }
  5159. this->m_lowerer->LoadScriptContext(instrAdd);
  5160. IR::JnHelperMethod helper;
  5161. if (addSrc == instrAdd->GetSrc2())
  5162. {
  5163. instrAdd->FreeSrc1();
  5164. IR::Opnd *addOpnd = instrAdd->UnlinkSrc2();
  5165. this->LoadHelperArgument(instrAdd, addOpnd);
  5166. helper = IR::HelperOp_MulAddRight;
  5167. }
  5168. else
  5169. {
  5170. AssertMsg(addSrc == instrAdd->GetSrc1(), "How did we get addSrc which not addInstr->Src1/2");
  5171. instrAdd->FreeSrc2();
  5172. IR::Opnd *addOpnd = instrAdd->UnlinkSrc1();
  5173. this->LoadHelperArgument(instrAdd, addOpnd);
  5174. helper = IR::HelperOp_MulAddLeft;
  5175. }
  5176. // Arg2, Arg1:
  5177. IR::Opnd *src2 = instrMul->UnlinkSrc2();
  5178. this->LoadHelperArgument(instrAdd, src2);
  5179. IR::Opnd *src1 = instrMul->UnlinkSrc1();
  5180. this->LoadHelperArgument(instrAdd, src1);
  5181. this->ChangeToHelperCall(instrAdd, helper);
  5182. instrMul->Remove();
  5183. return true;
  5184. }
  5185. IR::Instr *
  5186. LowererMD::LoadCheckedFloat(
  5187. IR::RegOpnd *opndOrig,
  5188. IR::RegOpnd *opndFloat,
  5189. IR::LabelInstr *labelInline,
  5190. IR::LabelInstr *labelHelper,
  5191. IR::Instr *instrInsert,
  5192. const bool checkForNullInLoopBody)
  5193. {
  5194. // Load one floating-point var into a VFP register, inserting checks to see if it's really a float:
  5195. // Rx = ASRS src, 1
  5196. // BCC $non-int
  5197. // Dx = VMOV Rx
  5198. // flt = VCVT.F64.S32 Dx
  5199. // B $labelInline
  5200. // $non-int
  5201. // LDR Ry, [src]
  5202. // CMP Ry, JavascriptNumber::`vtable'
  5203. // BNE $labelHelper
  5204. // flt = VLDR [t0 + offset(value)]
  5205. IR::Instr * instr = nullptr;
  5206. IR::Opnd * opnd = IR::RegOpnd::New(TyMachReg, this->m_func);
  5207. IR::Instr * instrFirst = IR::Instr::New(Js::OpCode::ASRS, opnd, opndOrig,
  5208. IR::IntConstOpnd::New(Js::AtomTag, TyMachReg, this->m_func),
  5209. this->m_func);
  5210. instrInsert->InsertBefore(instrFirst);
  5211. LegalizeMD::LegalizeInstr(instrFirst, false);
  5212. IR::LabelInstr * labelVar = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  5213. instr = IR::BranchInstr::New(Js::OpCode::BCC, labelVar, this->m_func);
  5214. instrInsert->InsertBefore(instr);
  5215. if (opndOrig->GetValueType().IsLikelyFloat())
  5216. {
  5217. // Make this path helper if value is likely a float
  5218. instrInsert->InsertBefore(IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true));
  5219. }
  5220. //Convert integer to floating point
  5221. Assert(opndFloat->GetType() == TyMachDouble);
  5222. instr = IR::Instr::New(Js::OpCode::VMOVARMVFP, opndFloat, opnd, this->m_func);
  5223. instrInsert->InsertBefore(instr);
  5224. //VCVT.F64.S32 opndFloat, opndFloat
  5225. instr = IR::Instr::New(Js::OpCode::VCVTF64S32, opndFloat, opndFloat, this->m_func);
  5226. instrInsert->InsertBefore(instr);
  5227. instr = IR::BranchInstr::New(Js::OpCode::B, labelInline, this->m_func);
  5228. instrInsert->InsertBefore(instr);
  5229. instrInsert->InsertBefore(labelVar);
  5230. LoadFloatValue(opndOrig, opndFloat, labelHelper, instrInsert, checkForNullInLoopBody);
  5231. return instrFirst;
  5232. }
  5233. void
  5234. LowererMD::EmitLoadFloatFromNumber(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr)
  5235. {
  5236. IR::LabelInstr *labelDone;
  5237. IR::Instr *instr;
  5238. labelDone = EmitLoadFloatCommon(dst, src, insertInstr, insertInstr->HasBailOutInfo());
  5239. if (labelDone == nullptr)
  5240. {
  5241. // We're done
  5242. insertInstr->Remove();
  5243. return;
  5244. }
  5245. // $Done note: insertAfter
  5246. insertInstr->InsertAfter(labelDone);
  5247. if (!insertInstr->HasBailOutInfo())
  5248. {
  5249. // $Done
  5250. insertInstr->Remove();
  5251. return;
  5252. }
  5253. IR::LabelInstr *labelNoBailOut = nullptr;
  5254. IR::SymOpnd *tempSymOpnd = nullptr;
  5255. if (insertInstr->GetBailOutKind() == IR::BailOutPrimitiveButString)
  5256. {
  5257. if (!this->m_func->tempSymDouble)
  5258. {
  5259. this->m_func->tempSymDouble = StackSym::New(TyFloat64, this->m_func);
  5260. this->m_func->StackAllocate(this->m_func->tempSymDouble, MachDouble);
  5261. }
  5262. // LEA r3, tempSymDouble
  5263. IR::RegOpnd *reg3Opnd = IR::RegOpnd::New(TyMachReg, this->m_func);
  5264. tempSymOpnd = IR::SymOpnd::New(this->m_func->tempSymDouble, TyFloat64, this->m_func);
  5265. instr = IR::Instr::New(Js::OpCode::LEA, reg3Opnd, tempSymOpnd, this->m_func);
  5266. insertInstr->InsertBefore(instr);
  5267. // regBoolResult = to_number_fromPrimitive(value, &dst, allowUndef, scriptContext);
  5268. this->m_lowerer->LoadScriptContext(insertInstr);
  5269. IR::IntConstOpnd *allowUndefOpnd;
  5270. if (insertInstr->GetBailOutKind() == IR::BailOutPrimitiveButString)
  5271. {
  5272. allowUndefOpnd = IR::IntConstOpnd::New(true, TyInt32, this->m_func);
  5273. }
  5274. else
  5275. {
  5276. Assert(insertInstr->GetBailOutKind() == IR::BailOutNumberOnly);
  5277. allowUndefOpnd = IR::IntConstOpnd::New(false, TyInt32, this->m_func);
  5278. }
  5279. this->LoadHelperArgument(insertInstr, allowUndefOpnd);
  5280. this->LoadHelperArgument(insertInstr, reg3Opnd);
  5281. this->LoadHelperArgument(insertInstr, src);
  5282. IR::RegOpnd *regBoolResult = IR::RegOpnd::New(TyInt32, this->m_func);
  5283. instr = IR::Instr::New(Js::OpCode::Call, regBoolResult, IR::HelperCallOpnd::New(IR::HelperOp_ConvNumber_FromPrimitive, this->m_func), this->m_func);
  5284. insertInstr->InsertBefore(instr);
  5285. this->LowerCall(instr, 0);
  5286. // TEST regBoolResult, regBoolResult
  5287. instr = IR::Instr::New(Js::OpCode::TST, this->m_func);
  5288. instr->SetSrc1(regBoolResult);
  5289. instr->SetSrc2(regBoolResult);
  5290. insertInstr->InsertBefore(instr);
  5291. // BNE $noBailOut
  5292. labelNoBailOut = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  5293. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelNoBailOut, this->m_func);
  5294. insertInstr->InsertBefore(instr);
  5295. }
  5296. // Bailout code
  5297. Assert(insertInstr->m_opcode == Js::OpCode::FromVar);
  5298. insertInstr->UnlinkDst();
  5299. insertInstr->FreeSrc1();
  5300. IR::Instr *bailoutInstr = insertInstr;
  5301. insertInstr = bailoutInstr->m_next;
  5302. this->m_lowerer->GenerateBailOut(bailoutInstr);
  5303. // $noBailOut
  5304. if (labelNoBailOut)
  5305. {
  5306. insertInstr->InsertBefore(labelNoBailOut);
  5307. Assert(dst->IsRegOpnd());
  5308. // VLDR dst, [pResult].f64
  5309. instr = IR::Instr::New(Js::OpCode::VLDR, dst, tempSymOpnd, this->m_func);
  5310. insertInstr->InsertBefore(instr);
  5311. LegalizeMD::LegalizeInstr(instr, false);
  5312. }
  5313. }
  5314. IR::LabelInstr*
  5315. LowererMD::EmitLoadFloatCommon(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, bool needHelperLabel)
  5316. {
  5317. IR::Instr *instr;
  5318. Assert(src->GetType() == TyVar);
  5319. Assert(dst->GetType() == TyFloat64 || TyFloat32);
  5320. bool isFloatConst = false;
  5321. IR::RegOpnd *regFloatOpnd = nullptr;
  5322. if (src->IsRegOpnd() && src->AsRegOpnd()->m_sym->m_isFltConst)
  5323. {
  5324. IR::RegOpnd *regOpnd = src->AsRegOpnd();
  5325. Assert(regOpnd->m_sym->m_isSingleDef);
  5326. Js::Var value = regOpnd->m_sym->GetFloatConstValueAsVar_PostGlobOpt();
  5327. IR::MemRefOpnd *memRef = IR::MemRefOpnd::New((BYTE*)value + Js::JavascriptNumber::GetValueOffset(), TyFloat64, this->m_func, IR::AddrOpndKindDynamicDoubleRef);
  5328. regFloatOpnd = IR::RegOpnd::New(TyFloat64, this->m_func);
  5329. instr = IR::Instr::New(Js::OpCode::VLDR, regFloatOpnd, memRef, this->m_func);
  5330. insertInstr->InsertBefore(instr);
  5331. LegalizeMD::LegalizeInstr(instr, false);
  5332. isFloatConst = true;
  5333. }
  5334. // Src is constant?
  5335. if (src->IsImmediateOpnd() || src->IsFloatConstOpnd())
  5336. {
  5337. regFloatOpnd = IR::RegOpnd::New(TyFloat64, this->m_func);
  5338. m_lowerer->LoadFloatFromNonReg(src, regFloatOpnd, insertInstr);
  5339. isFloatConst = true;
  5340. }
  5341. if (isFloatConst)
  5342. {
  5343. if (dst->GetType() == TyFloat32)
  5344. {
  5345. // VCVT.F32.F64 regOpnd32.f32, regOpnd.f64 -- Convert regOpnd from f64 to f32
  5346. IR::RegOpnd *regOpnd32 = regFloatOpnd->UseWithNewType(TyFloat32, this->m_func)->AsRegOpnd();
  5347. instr = IR::Instr::New(Js::OpCode::VCVTF32F64, regOpnd32, regFloatOpnd, this->m_func);
  5348. insertInstr->InsertBefore(instr);
  5349. // VSTR32 dst, regOpnd32
  5350. instr = IR::Instr::New(Js::OpCode::VMOV, dst, regOpnd32, this->m_func);
  5351. insertInstr->InsertBefore(instr);
  5352. }
  5353. else
  5354. {
  5355. instr = IR::Instr::New(Js::OpCode::VMOV, dst, regFloatOpnd, this->m_func);
  5356. insertInstr->InsertBefore(instr);
  5357. }
  5358. LegalizeMD::LegalizeInstr(instr, false);
  5359. return nullptr;
  5360. }
  5361. Assert(src->IsRegOpnd());
  5362. IR::LabelInstr *labelStore = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  5363. IR::LabelInstr *labelHelper;
  5364. IR::LabelInstr *labelDone = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  5365. if (needHelperLabel)
  5366. {
  5367. labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  5368. }
  5369. else
  5370. {
  5371. labelHelper = labelDone;
  5372. }
  5373. IR::RegOpnd *reg2 = IR::RegOpnd::New(TyMachDouble, this->m_func);
  5374. // Load the float value in reg2
  5375. this->LoadCheckedFloat(src->AsRegOpnd(), reg2, labelStore, labelHelper, insertInstr, needHelperLabel);
  5376. // $Store
  5377. insertInstr->InsertBefore(labelStore);
  5378. if (dst->GetType() == TyFloat32)
  5379. {
  5380. IR::RegOpnd *reg2_32 = reg2->UseWithNewType(TyFloat32, this->m_func)->AsRegOpnd();
  5381. // VCVT.F32.F64 r2_32.f32, r2.f64 -- Convert regOpnd from f64 to f32
  5382. instr = IR::Instr::New(Js::OpCode::VCVTF32F64, reg2_32, reg2, this->m_func);
  5383. insertInstr->InsertBefore(instr);
  5384. // VMOV dst, r2_32
  5385. instr = IR::Instr::New(Js::OpCode::VMOV, dst, reg2_32, this->m_func);
  5386. insertInstr->InsertBefore(instr);
  5387. }
  5388. else
  5389. {
  5390. // VMOV dst, r2
  5391. instr = IR::Instr::New(Js::OpCode::VMOV, dst, reg2, this->m_func);
  5392. insertInstr->InsertBefore(instr);
  5393. }
  5394. LegalizeMD::LegalizeInstr(instr, false);
  5395. // B $Done
  5396. instr = IR::BranchInstr::New(Js::OpCode::B, labelDone, this->m_func);
  5397. insertInstr->InsertBefore(instr);
  5398. if (needHelperLabel)
  5399. {
  5400. // $Helper
  5401. insertInstr->InsertBefore(labelHelper);
  5402. }
  5403. return labelDone;
  5404. }
  5405. IR::RegOpnd *
  5406. LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr)
  5407. {
  5408. IR::LabelInstr *labelDone;
  5409. IR::Instr *instr;
  5410. Assert(src->GetType() == TyVar);
  5411. Assert(dst->GetType() == TyFloat64 || TyFloat32);
  5412. Assert(src->IsRegOpnd());
  5413. if (dst->IsIndirOpnd())
  5414. {
  5415. LegalizeMD::LegalizeIndirOpndForVFP(insertInstr, dst->AsIndirOpnd(), false);
  5416. }
  5417. labelDone = EmitLoadFloatCommon(dst, src, insertInstr, true);
  5418. if (labelDone == nullptr)
  5419. {
  5420. // We're done
  5421. return nullptr;
  5422. }
  5423. IR::Opnd *memAddress = dst;
  5424. if (dst->IsRegOpnd())
  5425. {
  5426. IR::SymOpnd *symOpnd = nullptr;
  5427. if (dst->GetType() == TyFloat32)
  5428. {
  5429. symOpnd = IR::SymOpnd::New(StackSym::New(TyFloat32, this->m_func), TyFloat32, this->m_func);
  5430. this->m_func->StackAllocate(symOpnd->m_sym->AsStackSym(), sizeof(float));
  5431. }
  5432. else
  5433. {
  5434. symOpnd = IR::SymOpnd::New(StackSym::New(TyFloat64,this->m_func), TyMachDouble, this->m_func);
  5435. this->m_func->StackAllocate(symOpnd->m_sym->AsStackSym(), sizeof(double));
  5436. }
  5437. memAddress = symOpnd;
  5438. }
  5439. // LEA r3, dst
  5440. IR::RegOpnd *reg3Opnd = IR::RegOpnd::New(TyMachReg, this->m_func);
  5441. instr = IR::Instr::New(Js::OpCode::LEA, reg3Opnd, memAddress, this->m_func);
  5442. insertInstr->InsertBefore(instr);
  5443. // to_number_full(value, &dst, scriptContext);
  5444. // Create dummy binary op to convert into helper
  5445. instr = IR::Instr::New(Js::OpCode::Add_A, this->m_func);
  5446. instr->SetSrc1(src);
  5447. instr->SetSrc2(reg3Opnd);
  5448. insertInstr->InsertBefore(instr);
  5449. IR::JnHelperMethod helper;
  5450. if (dst->GetType() == TyFloat32)
  5451. {
  5452. helper = IR::HelperOp_ConvFloat_Helper;
  5453. }
  5454. else
  5455. {
  5456. helper = IR::HelperOp_ConvNumber_Helper;
  5457. }
  5458. this->m_lowerer->LowerBinaryHelperMem(instr, helper);
  5459. if (dst->IsRegOpnd())
  5460. {
  5461. Js::OpCode opcode = (dst->GetType() == TyFloat32)? Js::OpCode::VLDR32: Js::OpCode::VLDR;
  5462. instr = IR::Instr::New(opcode, dst , memAddress, this->m_func);
  5463. insertInstr->InsertBefore(instr);
  5464. LegalizeMD::LegalizeInstr(instr, false);
  5465. }
  5466. // $Done
  5467. insertInstr->InsertBefore(labelDone);
  5468. return nullptr;
  5469. }
  5470. void
  5471. LowererMD::GenerateNumberAllocation(IR::RegOpnd * opndDst, IR::Instr * instrInsert, bool isHelper)
  5472. {
  5473. Js::RecyclerJavascriptNumberAllocator * allocator = this->m_lowerer->GetScriptContext()->GetNumberAllocator();
  5474. IR::RegOpnd * loadAllocatorAddressOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
  5475. IR::Instr * loadAllocatorAddressInstr = IR::Instr::New(Js::OpCode::LDIMM, loadAllocatorAddressOpnd,
  5476. m_lowerer->LoadScriptContextValueOpnd(instrInsert, ScriptContextValue::ScriptContextNumberAllocator), this->m_func);
  5477. instrInsert->InsertBefore(loadAllocatorAddressInstr);
  5478. IR::IndirOpnd * endAddressOpnd = IR::IndirOpnd::New(loadAllocatorAddressOpnd,
  5479. Js::RecyclerJavascriptNumberAllocator::GetEndAddressOffset(), TyMachPtr, this->m_func);
  5480. IR::IndirOpnd * freeObjectListOpnd = IR::IndirOpnd::New(loadAllocatorAddressOpnd,
  5481. Js::RecyclerJavascriptNumberAllocator::GetFreeObjectListOffset(), TyMachPtr, this->m_func);
  5482. // LDR dst, allocator->freeObjectList
  5483. IR::Instr * loadMemBlockInstr = IR::Instr::New(Js::OpCode::LDR, opndDst, freeObjectListOpnd, this->m_func);
  5484. instrInsert->InsertBefore(loadMemBlockInstr);
  5485. // nextMemBlock = ADD dst, allocSize
  5486. IR::RegOpnd * nextMemBlockOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
  5487. IR::Instr * loadNextMemBlockInstr = IR::Instr::New(Js::OpCode::ADD, nextMemBlockOpnd, opndDst,
  5488. IR::IntConstOpnd::New(allocator->GetAlignedAllocSize(), TyInt32, this->m_func), this->m_func);
  5489. instrInsert->InsertBefore(loadNextMemBlockInstr);
  5490. // CMP nextMemBlock, allocator->endAddress
  5491. IR::Instr * checkInstr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  5492. checkInstr->SetSrc1(nextMemBlockOpnd);
  5493. checkInstr->SetSrc2(endAddressOpnd);
  5494. instrInsert->InsertBefore(checkInstr);
  5495. LegalizeMD::LegalizeInstr(checkInstr, false);
  5496. // BHI $helper
  5497. IR::LabelInstr * helperLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  5498. IR::BranchInstr * branchInstr = IR::BranchInstr::New(Js::OpCode::BHI, helperLabel, this->m_func);
  5499. instrInsert->InsertBefore(branchInstr);
  5500. // LDR allocator->freeObjectList, nextMemBlock
  5501. IR::Instr * setFreeObjectListInstr = IR::Instr::New(Js::OpCode::LDR, freeObjectListOpnd, nextMemBlockOpnd, this->m_func);
  5502. instrInsert->InsertBefore(setFreeObjectListInstr);
  5503. // B $done
  5504. IR::LabelInstr * doneLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, isHelper);
  5505. IR::BranchInstr * branchToDoneInstr = IR::BranchInstr::New(Js::OpCode::B, doneLabel, this->m_func);
  5506. instrInsert->InsertBefore(branchToDoneInstr);
  5507. // $helper:
  5508. instrInsert->InsertBefore(helperLabel);
  5509. // arg1 = allocator
  5510. this->LoadHelperArgument(instrInsert, m_lowerer->LoadScriptContextValueOpnd(instrInsert, ScriptContextValue::ScriptContextNumberAllocator));
  5511. // dst = Call AllocUninitializedNumber
  5512. IR::Instr * instrCall = IR::Instr::New(Js::OpCode::Call, opndDst,
  5513. IR::HelperCallOpnd::New(IR::HelperAllocUninitializedNumber, this->m_func), this->m_func);
  5514. instrInsert->InsertBefore(instrCall);
  5515. this->LowerCall(instrCall, 0);
  5516. // $done:
  5517. instrInsert->InsertBefore(doneLabel);
  5518. }
  5519. void
  5520. LowererMD::GenerateFastRecyclerAlloc(size_t allocSize, IR::RegOpnd* newObjDst, IR::Instr* insertionPointInstr, IR::LabelInstr* allocHelperLabel, IR::LabelInstr* allocDoneLabel)
  5521. {
  5522. Js::ScriptContext* scriptContext = this->m_func->GetScriptContext();
  5523. Recycler* recycler = scriptContext->GetRecycler();
  5524. void* allocatorAddress;
  5525. uint32 endAddressOffset;
  5526. uint32 freeListOffset;
  5527. recycler->GetNormalHeapBlockAllocatorInfoForNativeAllocation(allocSize, allocatorAddress, endAddressOffset, freeListOffset);
  5528. IR::RegOpnd * allocatorAddressRegOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
  5529. // LDIMM allocatorAddressRegOpnd, allocator
  5530. IR::AddrOpnd* allocatorAddressOpnd = IR::AddrOpnd::New(allocatorAddress, IR::AddrOpndKindDynamicMisc, this->m_func);
  5531. IR::Instr * loadAllocatorAddressInstr = IR::Instr::New(Js::OpCode::LDIMM, allocatorAddressRegOpnd, allocatorAddressOpnd, this->m_func);
  5532. insertionPointInstr->InsertBefore(loadAllocatorAddressInstr);
  5533. IR::IndirOpnd * endAddressOpnd = IR::IndirOpnd::New(allocatorAddressRegOpnd, endAddressOffset, TyMachPtr, this->m_func);
  5534. IR::IndirOpnd * freeObjectListOpnd = IR::IndirOpnd::New(allocatorAddressRegOpnd, freeListOffset, TyMachPtr, this->m_func);
  5535. // LDR newObjDst, allocator->freeObjectList
  5536. IR::Instr * loadMemBlockInstr = IR::Instr::New(Js::OpCode::LDR, newObjDst, freeObjectListOpnd, this->m_func);
  5537. insertionPointInstr->InsertBefore(loadMemBlockInstr);
  5538. LegalizeMD::LegalizeInstr(loadMemBlockInstr, false);
  5539. // nextMemBlock = ADD newObjDst, allocSize
  5540. IR::RegOpnd * nextMemBlockOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
  5541. IR::IntConstOpnd* allocSizeOpnd = IR::IntConstOpnd::New((int32)allocSize, TyInt32, this->m_func);
  5542. IR::Instr * loadNextMemBlockInstr = IR::Instr::New(Js::OpCode::ADD, nextMemBlockOpnd, newObjDst, allocSizeOpnd, this->m_func);
  5543. insertionPointInstr->InsertBefore(loadNextMemBlockInstr);
  5544. LegalizeMD::LegalizeInstr(loadNextMemBlockInstr, false);
  5545. // CMP nextMemBlock, allocator->endAddress
  5546. IR::Instr * checkInstr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  5547. checkInstr->SetSrc1(nextMemBlockOpnd);
  5548. checkInstr->SetSrc2(endAddressOpnd);
  5549. insertionPointInstr->InsertBefore(checkInstr);
  5550. LegalizeMD::LegalizeInstr(checkInstr, false);
  5551. // BHI $allocHelper
  5552. IR::BranchInstr * branchToAllocHelperInstr = IR::BranchInstr::New(Js::OpCode::BHI, allocHelperLabel, this->m_func);
  5553. insertionPointInstr->InsertBefore(branchToAllocHelperInstr);
  5554. // LDR allocator->freeObjectList, nextMemBlock
  5555. IR::Instr * setFreeObjectListInstr = IR::Instr::New(Js::OpCode::LDR, freeObjectListOpnd, nextMemBlockOpnd, this->m_func);
  5556. insertionPointInstr->InsertBefore(setFreeObjectListInstr);
  5557. LegalizeMD::LegalizeInstr(setFreeObjectListInstr, false);
  5558. // B $allocDone
  5559. IR::BranchInstr * branchToAllocDoneInstr = IR::BranchInstr::New(Js::OpCode::B, allocDoneLabel, this->m_func);
  5560. insertionPointInstr->InsertBefore(branchToAllocDoneInstr);
  5561. }
  5562. void
  5563. LowererMD::GenerateClz(IR::Instr * instr)
  5564. {
  5565. Assert(instr->GetSrc1()->IsInt32() || instr->GetSrc1()->IsUInt32());
  5566. Assert(IRType_IsNativeInt(instr->GetDst()->GetType()));
  5567. instr->m_opcode = Js::OpCode::CLZ;
  5568. LegalizeMD::LegalizeInstr(instr, false);
  5569. }
  5570. void
  5571. LowererMD::SaveDoubleToVar(IR::RegOpnd * dstOpnd, IR::RegOpnd *opndFloat, IR::Instr *instrOrig, IR::Instr *instrInsert, bool isHelper)
  5572. {
  5573. // Call JSNumber::ToVar to save the float operand to the result of the original (var) instruction
  5574. IR::Opnd * symVTableDst;
  5575. IR::Opnd * symDblDst;
  5576. IR::Opnd * symTypeDst;
  5577. IR::Instr *newInstr;
  5578. IR::Instr * numberInitInsertInstr = nullptr;
  5579. if (instrOrig->dstIsTempNumber)
  5580. {
  5581. // Use the original dst to get the temp number sym
  5582. StackSym * tempNumberSym = this->m_lowerer->GetTempNumberSym(instrOrig->GetDst(), instrOrig->dstIsTempNumberTransferred);
  5583. // LEA dst, &tempSym
  5584. IR::SymOpnd * symTempSrc = IR::SymOpnd::New(tempNumberSym, TyMachPtr, this->m_func);
  5585. newInstr = IR::Instr::New(Js::OpCode::LEA, dstOpnd, symTempSrc, this->m_func);
  5586. instrInsert->InsertBefore(newInstr);
  5587. LegalizeMD::LegalizeInstr(newInstr, false);
  5588. symVTableDst = IR::SymOpnd::New(tempNumberSym, TyMachPtr, this->m_func);
  5589. symDblDst = IR::SymOpnd::New(tempNumberSym, (uint32)Js::JavascriptNumber::GetValueOffset(), TyFloat64, this->m_func);
  5590. symTypeDst = IR::SymOpnd::New(tempNumberSym, (uint32)Js::JavascriptNumber::GetOffsetOfType(), TyMachPtr, this->m_func);
  5591. if (this->m_lowerer->outerMostLoopLabel == nullptr)
  5592. {
  5593. // If we are not in loop, just insert in place
  5594. numberInitInsertInstr = instrInsert;
  5595. }
  5596. else
  5597. {
  5598. // Otherwise, initialize in the outer most loop top if we haven't initialize it yet.
  5599. numberInitInsertInstr = this->m_lowerer->initializedTempSym->TestAndSet(tempNumberSym->m_id) ?
  5600. nullptr : this->m_lowerer->outerMostLoopLabel;
  5601. }
  5602. }
  5603. else
  5604. {
  5605. this->GenerateNumberAllocation(dstOpnd, instrInsert, isHelper);
  5606. symVTableDst = IR::IndirOpnd::New(dstOpnd, 0, TyMachPtr, this->m_func);
  5607. symDblDst = IR::IndirOpnd::New(dstOpnd, (uint32)Js::JavascriptNumber::GetValueOffset(), TyFloat64, this->m_func);
  5608. symTypeDst = IR::IndirOpnd::New(dstOpnd, (uint32)Js::JavascriptNumber::GetOffsetOfType(), TyMachPtr, this->m_func);
  5609. numberInitInsertInstr = instrInsert;
  5610. }
  5611. if (numberInitInsertInstr)
  5612. {
  5613. IR::Opnd *jsNumberVTable = m_lowerer->LoadVTableValueOpnd(numberInitInsertInstr, VTableValue::VtableJavascriptNumber);
  5614. // STR dst->vtable, JavascriptNumber::vtable
  5615. newInstr = IR::Instr::New(Js::OpCode::STR, symVTableDst, jsNumberVTable, this->m_func);
  5616. numberInitInsertInstr->InsertBefore(newInstr);
  5617. LegalizeMD::LegalizeInstr(newInstr, false);
  5618. // STR dst->type, JavascriptNumber_type
  5619. IR::Opnd *typeOpnd = m_lowerer->LoadLibraryValueOpnd(numberInitInsertInstr, LibraryValue::ValueNumberTypeStatic);
  5620. newInstr = IR::Instr::New(Js::OpCode::STR, symTypeDst, typeOpnd, this->m_func);
  5621. numberInitInsertInstr->InsertBefore(newInstr);
  5622. LegalizeMD::LegalizeInstr(newInstr, false);
  5623. }
  5624. // VSTR dst->value, opndFloat ; copy the float result to the temp JavascriptNumber
  5625. newInstr = IR::Instr::New(Js::OpCode::VSTR, symDblDst, opndFloat, this->m_func);
  5626. instrInsert->InsertBefore(newInstr);
  5627. LegalizeMD::LegalizeInstr(newInstr, false);
  5628. }
  5629. void
  5630. LowererMD::GenerateFastAbs(IR::Opnd *dst, IR::Opnd *src, IR::Instr *callInstr, IR::Instr *insertInstr, IR::LabelInstr *labelHelper, IR::LabelInstr *labelDone)
  5631. {
  5632. // src32 = ASRS src, VarShift
  5633. // BCC $helper <== float abs if emitFloatAbs
  5634. // dst32 = EOR src32, src32 ASR #31
  5635. // dst32 = SUB dst32, src32 ASR #31
  5636. // TIOFLW src32
  5637. // BMI $helper
  5638. // dst = LSL src32, VarShift
  5639. // dst = ADD dst, AtomTag
  5640. // B $done
  5641. // $float
  5642. // CMP [src], JavascriptNumber.vtable
  5643. // BNE $helper
  5644. // VLDR dx, [src + offsetof(value)]
  5645. // VABS.f64 dx, dx
  5646. // dst = DoubleToVar(dx)
  5647. // $helper:
  5648. // <call helper>
  5649. // $done:
  5650. bool isInt = false;
  5651. bool isNotInt = false;
  5652. IR::Instr *instr;
  5653. IR::LabelInstr *labelFloat = nullptr;
  5654. if (src->IsRegOpnd())
  5655. {
  5656. if (src->AsRegOpnd()->IsTaggedInt())
  5657. {
  5658. isInt = true;
  5659. }
  5660. else if (src->AsRegOpnd()->IsNotInt())
  5661. {
  5662. isNotInt = true;
  5663. }
  5664. }
  5665. else if (src->IsAddrOpnd())
  5666. {
  5667. IR::AddrOpnd *varOpnd = src->AsAddrOpnd();
  5668. Assert(varOpnd->IsVar() && Js::TaggedInt::Is(varOpnd->m_address));
  5669. int absValue = abs(Js::TaggedInt::ToInt32(varOpnd->m_address));
  5670. if (!Js::TaggedInt::IsOverflow(absValue))
  5671. {
  5672. varOpnd->SetAddress(Js::TaggedInt::ToVarUnchecked(absValue), IR::AddrOpndKindConstantVar);
  5673. LowererMD::CreateAssign(dst, varOpnd, insertInstr);
  5674. }
  5675. }
  5676. if (src->IsRegOpnd() == false)
  5677. {
  5678. //Lets legalize right away as floating point fast path works on the same src.
  5679. IR::RegOpnd *regOpnd = IR::RegOpnd::New(TyVar, this->m_func);
  5680. instr = IR::Instr::New(Js::OpCode::MOV, regOpnd, src, this->m_func);
  5681. insertInstr->InsertBefore(instr);
  5682. LegalizeMD::LegalizeInstr(instr, false);
  5683. src = regOpnd;
  5684. }
  5685. bool emitFloatAbs = !isInt;
  5686. if (!isNotInt)
  5687. {
  5688. // src32 = ASRS src, VarTag_Shift
  5689. IR::RegOpnd *src32 = src32 = IR::RegOpnd::New(TyMachReg, this->m_func);
  5690. instr = IR::Instr::New(
  5691. Js::OpCode::ASRS, src32, src, IR::IntConstOpnd::New(Js::VarTag_Shift, TyInt32, this->m_func), this->m_func);
  5692. insertInstr->InsertBefore(instr);
  5693. if (!isInt)
  5694. {
  5695. if (emitFloatAbs)
  5696. {
  5697. // BCC $float
  5698. labelFloat = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  5699. instr = IR::BranchInstr::New(Js::OpCode::BCC, labelFloat, this->m_func);
  5700. insertInstr->InsertBefore(instr);
  5701. }
  5702. else
  5703. {
  5704. instr = IR::BranchInstr::New(Js::OpCode::BCC, labelHelper, this->m_func);
  5705. insertInstr->InsertBefore(instr);
  5706. }
  5707. }
  5708. // dst32 = EOR src32, src32 ASR #31
  5709. IR::RegOpnd *dst32 = IR::RegOpnd::New(TyMachReg, this->m_func);
  5710. instr = IR::Instr::New(Js::OpCode::CLRSIGN, dst32, src32, this->m_func);
  5711. insertInstr->InsertBefore(instr);
  5712. // dst32 = SUB dst32, src32 ASR #31
  5713. instr = IR::Instr::New(Js::OpCode::SBCMPLNT, dst32, dst32, src32, this->m_func);
  5714. insertInstr->InsertBefore(instr);
  5715. // TEQ dst32, dst32 LSL #1
  5716. instr = IR::Instr::New(Js::OpCode::TIOFLW, this->m_func);
  5717. instr->SetSrc1(dst32);
  5718. insertInstr->InsertBefore(instr);
  5719. // BMI $helper
  5720. instr = IR::BranchInstr::New(Js::OpCode::BMI, labelHelper, this->m_func);
  5721. insertInstr->InsertBefore(instr);
  5722. // dst32 = LSL dst32, VarShift
  5723. instr = IR::Instr::New(
  5724. Js::OpCode::LSL, dst32, dst32, IR::IntConstOpnd::New(Js::VarTag_Shift, TyMachReg, this->m_func), this->m_func);
  5725. insertInstr->InsertBefore(instr);
  5726. // dst = ADD dst, AtomTag
  5727. instr = IR::Instr::New(
  5728. Js::OpCode::ADD, dst, dst32, IR::IntConstOpnd::New(Js::AtomTag, TyMachReg, this->m_func), this->m_func);
  5729. insertInstr->InsertBefore(instr);
  5730. LegalizeMD::LegalizeInstr(instr, false);
  5731. }
  5732. if (labelFloat)
  5733. {
  5734. // B $done
  5735. instr = IR::BranchInstr::New(Js::OpCode::B, labelDone, this->m_func);
  5736. insertInstr->InsertBefore(instr);
  5737. // $float
  5738. insertInstr->InsertBefore(labelFloat);
  5739. }
  5740. if (emitFloatAbs)
  5741. {
  5742. // CMP [src], JavascriptNumber.vtable
  5743. IR::Opnd *opnd = IR::IndirOpnd::New(src->AsRegOpnd(), (int32)0, TyMachPtr, this->m_func);
  5744. instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  5745. instr->SetSrc1(opnd);
  5746. instr->SetSrc2(m_lowerer->LoadVTableValueOpnd(insertInstr, VTableValue::VtableJavascriptNumber));
  5747. insertInstr->InsertBefore(instr);
  5748. LegalizeMD::LegalizeInstr(instr, false);
  5749. // BNE $helper
  5750. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelHelper, this->m_func);
  5751. insertInstr->InsertBefore(instr);
  5752. // VLDR dx, [src + offsetof(value)]
  5753. opnd = IR::IndirOpnd::New(src->AsRegOpnd(), Js::JavascriptNumber::GetValueOffset(), TyMachDouble, this->m_func);
  5754. IR::RegOpnd *regOpnd = IR::RegOpnd::New(TyMachDouble, this->m_func);
  5755. instr = IR::Instr::New(Js::OpCode::VLDR, regOpnd, opnd, this->m_func);
  5756. insertInstr->InsertBefore(instr);
  5757. // VABS.f64 dy, dx
  5758. IR::RegOpnd *resultRegOpnd = IR::RegOpnd::New(TyMachDouble, this->m_func);
  5759. instr = IR::Instr::New(Js::OpCode::VABS, resultRegOpnd, regOpnd, this->m_func);
  5760. insertInstr->InsertBefore(instr);
  5761. // dst = DoubleToVar(dy)
  5762. SaveDoubleToVar(callInstr->GetDst()->AsRegOpnd(), resultRegOpnd, callInstr, insertInstr);
  5763. }
  5764. }
  5765. bool LowererMD::GenerateFastCharAt(Js::BuiltinFunction index, IR::Opnd *dst, IR::Opnd *srcStr, IR::Opnd *srcIndex, IR::Instr *callInstr,
  5766. IR::Instr *insertInstr, IR::LabelInstr *labelHelper, IR::LabelInstr *labelDone)
  5767. {
  5768. // TST regSrc, AtomTag
  5769. // BNE $helper
  5770. // type = LDR [regSrc + offset(type)]
  5771. // typeid = LDR [type + offset(typeid)]
  5772. // CMP typeid, TypeIds_String
  5773. // BNE $helper
  5774. // psz = LDR [regSrc + offset(m_pszValue)]
  5775. // CMP psz, 0
  5776. // BEQ $helper
  5777. // index32 = ASRS srcIndex, VarShift
  5778. // BCC $helper
  5779. // length = LDR [regSrc + offset(length)]
  5780. // CMP length, index32
  5781. // BLS $helper
  5782. // char = LDRH [regSrc + index32, LSL #1]
  5783. //
  5784. // if (charAt)
  5785. // (r1) = MOV char
  5786. // (r0) = LDIMM scriptContext
  5787. // dst = CALL GetStringFromChar
  5788. //
  5789. // else
  5790. // if (codePointAt)
  5791. // Lowerer.GenerateFastCodePointAt -- Common inline functions
  5792. //
  5793. // char = LSL char, VarShift
  5794. // dst = ADD char, AtomTag
  5795. bool isInt = false;
  5796. IR::Instr *instr;
  5797. IR::IndirOpnd *indirOpnd;
  5798. IR::RegOpnd *regSrcStr;
  5799. if (srcStr->IsRegOpnd())
  5800. {
  5801. if (srcStr->AsRegOpnd()->IsTaggedInt())
  5802. {
  5803. isInt = true;
  5804. }
  5805. }
  5806. if (isInt)
  5807. {
  5808. // Insert delete branch opcode to tell the dbChecks not to assert on this helper label
  5809. IR::Instr *fakeBr = IR::PragmaInstr::New(Js::OpCode::DeletedNonHelperBranch, 0, this->m_func);
  5810. insertInstr->InsertBefore(fakeBr);
  5811. // The "string" is an int. Just bail out.
  5812. instr = IR::BranchInstr::New(Js::OpCode::B, labelHelper, this->m_func);
  5813. insertInstr->InsertBefore(instr);
  5814. return false;
  5815. }
  5816. // Bail out if index a constant and is less than zero.
  5817. if (srcIndex->IsImmediateOpnd() && srcIndex->GetImmediateValue() < 0)
  5818. {
  5819. instr = IR::BranchInstr::New(Js::OpCode::B, labelHelper, this->m_func);
  5820. insertInstr->InsertBefore(instr);
  5821. return false;
  5822. }
  5823. // Force the string into a reg at the top. Otherwise we'll be loading it over and over...
  5824. if (srcStr->IsRegOpnd())
  5825. {
  5826. regSrcStr = srcStr->AsRegOpnd();
  5827. }
  5828. else
  5829. {
  5830. regSrcStr = IR::RegOpnd::New(TyMachReg, this->m_func);
  5831. LowererMD::CreateAssign(regSrcStr, srcStr, insertInstr);
  5832. }
  5833. this->m_lowerer->GenerateStringTest(regSrcStr, insertInstr, labelHelper);
  5834. // psz = LDR [regSrc + offset(m_pszValue)]
  5835. IR::RegOpnd *psz = IR::RegOpnd::New(TyMachPtr, this->m_func);
  5836. indirOpnd = IR::IndirOpnd::New(regSrcStr, Js::JavascriptString::GetOffsetOfpszValue(), TyMachPtr, this->m_func);
  5837. LowererMD::CreateAssign(psz, indirOpnd, insertInstr);
  5838. // CMP psz, 0
  5839. instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  5840. instr->SetSrc1(psz);
  5841. instr->SetSrc2(IR::IntConstOpnd::New(0, TyMachPtr, this->m_func));
  5842. insertInstr->InsertBefore(instr);
  5843. // BEQ $helper
  5844. instr = IR::BranchInstr::New(Js::OpCode::BEQ, labelHelper, this->m_func);
  5845. insertInstr->InsertBefore(instr);
  5846. // Arm should change to Uint32 for the length
  5847. // length = LDR [regSrcStr + offsetof(length)]
  5848. IR::RegOpnd *length = IR::RegOpnd::New(TyMachReg, this->m_func);
  5849. indirOpnd = IR::IndirOpnd::New(regSrcStr, offsetof(Js::JavascriptString, m_charLength), TyUint32, this->m_func);
  5850. LowererMD::CreateAssign(length, indirOpnd, insertInstr);
  5851. if (srcIndex->IsAddrOpnd())
  5852. {
  5853. // The index is a constant, so just use it.
  5854. uint32 constIndex = Js::TaggedInt::ToUInt32(srcIndex->AsAddrOpnd()->m_address);
  5855. // CMP length, index32
  5856. instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  5857. instr->SetSrc1(length);
  5858. instr->SetSrc2(IR::IntConstOpnd::New(constIndex, TyUint32, this->m_func));
  5859. insertInstr->InsertBefore(instr);
  5860. LegalizeMD::LegalizeInstr(instr, false);
  5861. // Use unsigned compare, this should handle negative indexes as well (they become > INT_MAX)
  5862. // BLS $helper
  5863. instr = IR::BranchInstr::New(Js::OpCode::BLS, labelHelper, this->m_func);
  5864. insertInstr->InsertBefore(instr);
  5865. // indir = [psz + index32 * 2]
  5866. indirOpnd = IR::IndirOpnd::New(psz, constIndex * sizeof(wchar_t), TyUint16, this->m_func);
  5867. }
  5868. else
  5869. {
  5870. // index32 = ASRS srcIndex, VarShift
  5871. IR::RegOpnd *index32 = IR::RegOpnd::New(TyMachReg, this->m_func);
  5872. instr = IR::Instr::New(Js::OpCode::ASRS, index32, srcIndex, IR::IntConstOpnd::New(Js::VarTag_Shift, TyInt8, this->m_func), this->m_func);
  5873. insertInstr->InsertBefore(instr);
  5874. if (!srcIndex->IsRegOpnd() || !srcIndex->AsRegOpnd()->IsTaggedInt())
  5875. {
  5876. // BCC $helper
  5877. instr = IR::BranchInstr::New(Js::OpCode::BCC, labelHelper, this->m_func);
  5878. insertInstr->InsertBefore(instr);
  5879. }
  5880. // CMP length, index32
  5881. instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  5882. instr->SetSrc1(length);
  5883. instr->SetSrc2(index32);
  5884. insertInstr->InsertBefore(instr);
  5885. // Use unsigned compare, this should handle negative indexes as well (they become > INT_MAX)
  5886. // BLS $helper
  5887. instr = IR::BranchInstr::New(Js::OpCode::BLS, labelHelper, this->m_func);
  5888. insertInstr->InsertBefore(instr);
  5889. // indir = [psz + index32 * 2]
  5890. indirOpnd = IR::IndirOpnd::New(psz, index32, (byte)Math::Log2(sizeof(wchar_t)), TyUint16, this->m_func);
  5891. }
  5892. // char = LDRH [regSrc + index32, LSL #1]
  5893. IR::RegOpnd *charResult = IR::RegOpnd::New(TyUint32, this->m_func);
  5894. LowererMD::CreateAssign(charResult, indirOpnd, insertInstr);
  5895. if (index == Js::BuiltinFunction::String_CharAt)
  5896. {
  5897. this->m_lowerer->GenerateGetSingleCharString(charResult, dst, labelHelper, labelDone, insertInstr, false);
  5898. }
  5899. else
  5900. {
  5901. Assert(index == Js::BuiltinFunction::String_CharCodeAt || index == Js::BuiltinFunction::String_CodePointAt);
  5902. if (index == Js::BuiltinFunction::String_CodePointAt)
  5903. {
  5904. this->m_lowerer->GenerateFastInlineStringCodePointAt(insertInstr, this->m_func, length, srcIndex, charResult, psz);
  5905. }
  5906. // result = LSL result, VarShift
  5907. instr = IR::Instr::New(Js::OpCode::LSL, charResult, charResult, IR::IntConstOpnd::New(Js::VarTag_Shift, TyInt8, this->m_func), this->m_func);
  5908. insertInstr->InsertBefore(instr);
  5909. // dst = ADD result, AtomTag
  5910. instr = IR::Instr::New(Js::OpCode::ADD, dst, charResult, IR::IntConstOpnd::New(Js::AtomTag, TyMachReg, this->m_func), this->m_func);
  5911. insertInstr->InsertBefore(instr);
  5912. LegalizeMD::LegalizeInstr(instr, false);
  5913. }
  5914. return true;
  5915. }
  5916. IR::Instr *
  5917. LowererMD::LoadStackAddress(StackSym *sym, IR::RegOpnd* regDst)
  5918. {
  5919. if (regDst == nullptr)
  5920. {
  5921. regDst = IR::RegOpnd::New(TyMachReg, this->m_func);
  5922. }
  5923. IR::SymOpnd * symSrc = IR::SymOpnd::New(sym, TyMachPtr, this->m_func);
  5924. IR::Instr * lea = IR::Instr::New(Js::OpCode::LEA, regDst, symSrc, this->m_func);
  5925. return lea;
  5926. }
  5927. void
  5928. LowererMD::EmitPtrInstr(IR::Instr *instr)
  5929. {
  5930. switch (instr->m_opcode)
  5931. {
  5932. case Js::OpCode::Add_Ptr:
  5933. LowererMD::ChangeToAdd(instr, false /* needFlags */);
  5934. break;
  5935. default:
  5936. AssertMsg(UNREACHED, "Un-implemented ptr opcode");
  5937. }
  5938. LegalizeMD::LegalizeInstr(instr, false);
  5939. }
  5940. void
  5941. LowererMD::EmitInt4Instr(IR::Instr *instr)
  5942. {
  5943. IR::Instr * newInstr;
  5944. IR::Opnd * src1;
  5945. IR::Opnd * src2;
  5946. switch (instr->m_opcode)
  5947. {
  5948. case Js::OpCode::Neg_I4:
  5949. instr->m_opcode = Js::OpCode::RSB;
  5950. instr->SetSrc2(IR::IntConstOpnd::New(0, TyInt32, instr->m_func));
  5951. break;
  5952. case Js::OpCode::Not_I4:
  5953. instr->m_opcode = Js::OpCode::MVN;
  5954. break;
  5955. case Js::OpCode::Add_I4:
  5956. ChangeToAdd(instr, false /* needFlags */);
  5957. break;
  5958. case Js::OpCode::Sub_I4:
  5959. ChangeToSub(instr, false /* needFlags */);
  5960. break;
  5961. case Js::OpCode::Mul_I4:
  5962. instr->m_opcode = Js::OpCode::MUL;
  5963. break;
  5964. case Js::OpCode::Div_I4:
  5965. instr->m_opcode = Js::OpCode::SDIV;
  5966. break;
  5967. case Js::OpCode::Rem_I4:
  5968. instr->m_opcode = Js::OpCode::REM;
  5969. break;
  5970. case Js::OpCode::Or_I4:
  5971. instr->m_opcode = Js::OpCode::ORR;
  5972. break;
  5973. case Js::OpCode::Xor_I4:
  5974. instr->m_opcode = Js::OpCode::EOR;
  5975. break;
  5976. case Js::OpCode::And_I4:
  5977. instr->m_opcode = Js::OpCode::AND;
  5978. break;
  5979. case Js::OpCode::Shl_I4:
  5980. case Js::OpCode::ShrU_I4:
  5981. case Js::OpCode::Shr_I4:
  5982. ChangeToShift(instr, false /* needFlags */);
  5983. break;
  5984. case Js::OpCode::BrTrue_I4:
  5985. instr->m_opcode = Js::OpCode::BNE;
  5986. goto br1_Common;
  5987. case Js::OpCode::BrFalse_I4:
  5988. instr->m_opcode = Js::OpCode::BEQ;
  5989. br1_Common:
  5990. src1 = instr->UnlinkSrc1();
  5991. newInstr = IR::Instr::New(Js::OpCode::CMP, instr->m_func);
  5992. instr->InsertBefore(newInstr);
  5993. newInstr->SetSrc1(src1);
  5994. newInstr->SetSrc2(IR::IntConstOpnd::New(0, TyInt32, instr->m_func));
  5995. // We know this CMP is legal.
  5996. return;
  5997. case Js::OpCode::BrEq_I4:
  5998. instr->m_opcode = Js::OpCode::BEQ;
  5999. goto br2_Common;
  6000. case Js::OpCode::BrNeq_I4:
  6001. instr->m_opcode = Js::OpCode::BNE;
  6002. goto br2_Common;
  6003. case Js::OpCode::BrGt_I4:
  6004. instr->m_opcode = Js::OpCode::BGT;
  6005. goto br2_Common;
  6006. case Js::OpCode::BrGe_I4:
  6007. instr->m_opcode = Js::OpCode::BGE;
  6008. goto br2_Common;
  6009. case Js::OpCode::BrLe_I4:
  6010. instr->m_opcode = Js::OpCode::BLE;
  6011. goto br2_Common;
  6012. case Js::OpCode::BrLt_I4:
  6013. instr->m_opcode = Js::OpCode::BLT;
  6014. goto br2_Common;
  6015. case Js::OpCode::BrUnGt_I4:
  6016. instr->m_opcode = Js::OpCode::BHI;
  6017. goto br2_Common;
  6018. case Js::OpCode::BrUnGe_I4:
  6019. instr->m_opcode = Js::OpCode::BCS;
  6020. goto br2_Common;
  6021. case Js::OpCode::BrUnLt_I4:
  6022. instr->m_opcode = Js::OpCode::BCC;
  6023. goto br2_Common;
  6024. case Js::OpCode::BrUnLe_I4:
  6025. instr->m_opcode = Js::OpCode::BLS;
  6026. goto br2_Common;
  6027. br2_Common:
  6028. src1 = instr->UnlinkSrc1();
  6029. src2 = instr->UnlinkSrc2();
  6030. newInstr = IR::Instr::New(Js::OpCode::CMP, instr->m_func);
  6031. instr->InsertBefore(newInstr);
  6032. newInstr->SetSrc1(src1);
  6033. newInstr->SetSrc2(src2);
  6034. // Let instr point to the CMP so we can legalize it.
  6035. instr = newInstr;
  6036. break;
  6037. default:
  6038. AssertMsg(UNREACHED, "NYI I4 instr");
  6039. break;
  6040. }
  6041. LegalizeMD::LegalizeInstr(instr, false);
  6042. }
  6043. void
  6044. LowererMD::LowerInt4NegWithBailOut(
  6045. IR::Instr *const instr,
  6046. const IR::BailOutKind bailOutKind,
  6047. IR::LabelInstr *const bailOutLabel,
  6048. IR::LabelInstr *const skipBailOutLabel)
  6049. {
  6050. Assert(instr);
  6051. Assert(instr->m_opcode == Js::OpCode::Neg_I4);
  6052. Assert(!instr->HasBailOutInfo());
  6053. Assert(bailOutKind & IR::BailOutOnResultConditions || bailOutKind == IR::BailOutOnFailedHoistedLoopCountBasedBoundCheck);
  6054. Assert(bailOutLabel);
  6055. Assert(instr->m_next == bailOutLabel);
  6056. Assert(skipBailOutLabel);
  6057. Assert(instr->GetDst()->IsInt32());
  6058. Assert(instr->GetSrc1()->IsInt32());
  6059. // RSBS dst, src1, #0
  6060. // BVS $bailOutLabel
  6061. // BEQ $bailOutLabel
  6062. // B $skipBailOut
  6063. // $bailOut:
  6064. // ...
  6065. // $skipBailOut:
  6066. // Lower the instruction
  6067. instr->m_opcode = Js::OpCode::RSBS;
  6068. instr->SetSrc2(IR::IntConstOpnd::New(0, TyInt32, instr->m_func));
  6069. Legalize(instr);
  6070. if(bailOutKind & IR::BailOutOnOverflow)
  6071. {
  6072. bailOutLabel->InsertBefore(IR::BranchInstr::New(Js::OpCode::BVS, bailOutLabel, instr->m_func));
  6073. }
  6074. if(bailOutKind & IR::BailOutOnNegativeZero)
  6075. {
  6076. bailOutLabel->InsertBefore(IR::BranchInstr::New(Js::OpCode::BEQ, bailOutLabel, instr->m_func));
  6077. }
  6078. // Skip bailout
  6079. bailOutLabel->InsertBefore(IR::BranchInstr::New(LowererMD::MDUncondBranchOpcode, skipBailOutLabel, instr->m_func));
  6080. }
  6081. void
  6082. LowererMD::LowerInt4AddWithBailOut(
  6083. IR::Instr *const instr,
  6084. const IR::BailOutKind bailOutKind,
  6085. IR::LabelInstr *const bailOutLabel,
  6086. IR::LabelInstr *const skipBailOutLabel)
  6087. {
  6088. Assert(instr);
  6089. Assert(instr->m_opcode == Js::OpCode::Add_I4);
  6090. Assert(!instr->HasBailOutInfo());
  6091. Assert(
  6092. (bailOutKind & IR::BailOutOnResultConditions) == IR::BailOutOnOverflow ||
  6093. bailOutKind == IR::BailOutOnFailedHoistedLoopCountBasedBoundCheck);
  6094. Assert(bailOutLabel);
  6095. Assert(instr->m_next == bailOutLabel);
  6096. Assert(skipBailOutLabel);
  6097. Assert(instr->GetDst()->IsInt32());
  6098. Assert(instr->GetSrc1()->IsInt32());
  6099. Assert(instr->GetSrc2()->IsInt32());
  6100. // ADDS dst, src1, src2
  6101. // BVC skipBailOutLabel
  6102. // fallthrough to bailout
  6103. const auto dst = instr->GetDst(), src1 = instr->GetSrc1(), src2 = instr->GetSrc2();
  6104. Assert(dst->IsRegOpnd());
  6105. const bool dstEquSrc1 = dst->IsEqual(src1), dstEquSrc2 = dst->IsEqual(src2);
  6106. if(dstEquSrc1 || dstEquSrc2)
  6107. {
  6108. LowererMD::ChangeToAssign(instr->SinkDst(Js::OpCode::Ld_I4, RegNOREG, skipBailOutLabel));
  6109. }
  6110. // Lower the instruction
  6111. ChangeToAdd(instr, true /* needFlags */);
  6112. Legalize(instr);
  6113. // Skip bailout on no overflow
  6114. bailOutLabel->InsertBefore(IR::BranchInstr::New(Js::OpCode::BVC, skipBailOutLabel, instr->m_func));
  6115. // Fall through to bailOutLabel
  6116. }
  6117. void
  6118. LowererMD::LowerInt4SubWithBailOut(
  6119. IR::Instr *const instr,
  6120. const IR::BailOutKind bailOutKind,
  6121. IR::LabelInstr *const bailOutLabel,
  6122. IR::LabelInstr *const skipBailOutLabel)
  6123. {
  6124. Assert(instr);
  6125. Assert(instr->m_opcode == Js::OpCode::Sub_I4);
  6126. Assert(!instr->HasBailOutInfo());
  6127. Assert(
  6128. (bailOutKind & IR::BailOutOnResultConditions) == IR::BailOutOnOverflow ||
  6129. bailOutKind == IR::BailOutOnFailedHoistedLoopCountBasedBoundCheck);
  6130. Assert(bailOutLabel);
  6131. Assert(instr->m_next == bailOutLabel);
  6132. Assert(skipBailOutLabel);
  6133. Assert(instr->GetDst()->IsInt32());
  6134. Assert(instr->GetSrc1()->IsInt32());
  6135. Assert(instr->GetSrc2()->IsInt32());
  6136. // SUBS dst, src1, src2
  6137. // BVC skipBailOutLabel
  6138. // fallthrough to bailout
  6139. const auto dst = instr->GetDst(), src1 = instr->GetSrc1(), src2 = instr->GetSrc2();
  6140. Assert(dst->IsRegOpnd());
  6141. const bool dstEquSrc1 = dst->IsEqual(src1), dstEquSrc2 = dst->IsEqual(src2);
  6142. if(dstEquSrc1 || dstEquSrc2)
  6143. {
  6144. LowererMD::ChangeToAssign(instr->SinkDst(Js::OpCode::Ld_I4, RegNOREG, skipBailOutLabel));
  6145. }
  6146. // Lower the instruction
  6147. ChangeToSub(instr, true /* needFlags */);
  6148. Legalize(instr);
  6149. // Skip bailout on no overflow
  6150. bailOutLabel->InsertBefore(IR::BranchInstr::New(Js::OpCode::BVC, skipBailOutLabel, instr->m_func));
  6151. // Fall through to bailOutLabel
  6152. }
  6153. void
  6154. LowererMD::LowerInt4MulWithBailOut(
  6155. IR::Instr *const instr,
  6156. const IR::BailOutKind bailOutKind,
  6157. IR::LabelInstr *const bailOutLabel,
  6158. IR::LabelInstr *const skipBailOutLabel)
  6159. {
  6160. Assert(instr);
  6161. Assert(instr->m_opcode == Js::OpCode::Mul_I4);
  6162. Assert(!instr->HasBailOutInfo());
  6163. Assert(bailOutKind & IR::BailOutOnResultConditions || bailOutKind == IR::BailOutOnFailedHoistedLoopCountBasedBoundCheck);
  6164. Assert(bailOutLabel);
  6165. Assert(instr->m_next == bailOutLabel);
  6166. Assert(skipBailOutLabel);
  6167. IR::Opnd *dst = instr->GetDst();
  6168. IR::Opnd *src1 = instr->GetSrc1();
  6169. IR::Opnd *src2 = instr->GetSrc2();
  6170. IR::Instr *insertInstr;
  6171. Assert(dst->IsInt32());
  6172. Assert(src1->IsInt32());
  6173. Assert(src2->IsInt32());
  6174. // (r12:)dst = SMULL dst, (r12,) src1, src2 -- do the signed mul into 64bit r12:dst, the result will be src1 * src2 * 2
  6175. instr->m_opcode = Js::OpCode::SMULL;
  6176. Legalize(instr);
  6177. //check negative zero
  6178. //
  6179. //If the result is zero, we need to check and only bail out if it would be -0.
  6180. // We know that if the result is 0/-0, at least operand should be zero.
  6181. // We should bailout if src1 + src2 < 0, as this proves that the other operand is negative
  6182. //
  6183. // CMN src1, src2
  6184. // BPL $skipBailOutLabel
  6185. //
  6186. //$bailOutLabel
  6187. // GenerateBailout
  6188. //
  6189. //$skipBailOutLabel
  6190. IR::LabelInstr *checkForNegativeZeroLabel = nullptr;
  6191. if(bailOutKind & IR::BailOutOnNegativeZero)
  6192. {
  6193. checkForNegativeZeroLabel = IR::LabelInstr::New(Js::OpCode::Label, instr->m_func, true);
  6194. bailOutLabel->InsertBefore(checkForNegativeZeroLabel);
  6195. Assert(dst->IsRegOpnd());
  6196. Assert(!src1->IsEqual(src2)); // cannot result in -0 if both operands are the same; GlobOpt should have figured that out
  6197. // CMN src1, src2
  6198. // BPL $skipBailOutLabel
  6199. insertInstr = IR::Instr::New(Js::OpCode::CMN, instr->m_func);
  6200. insertInstr->SetSrc1(src1);
  6201. insertInstr->SetSrc2(src2);
  6202. bailOutLabel->InsertBefore(insertInstr);
  6203. LegalizeMD::LegalizeInstr(insertInstr, false);
  6204. bailOutLabel->InsertBefore(IR::BranchInstr::New(Js::OpCode::BPL, skipBailOutLabel, instr->m_func));
  6205. // Fall through to bailOutLabel
  6206. }
  6207. const auto insertBeforeInstr = checkForNegativeZeroLabel ? checkForNegativeZeroLabel : bailOutLabel;
  6208. //check overflow
  6209. // CMP_ASR31 r12, dst
  6210. // BNE $bailOutLabel
  6211. if(bailOutKind & IR::BailOutOnMulOverflow || bailOutKind == IR::BailOutOnFailedHoistedLoopCountBasedBoundCheck)
  6212. {
  6213. // (SMULL doesn't set the flags but we don't have 32bit overflow <=> r12-unsigned ? r12==0 : all 33 bits of 64bit result are 1's
  6214. // CMP r12, dst, ASR #31 -- check for overflow (== means no overflow)
  6215. IR::RegOpnd* opndRegR12 = IR::RegOpnd::New(nullptr, RegR12, TyMachReg, instr->m_func);
  6216. insertInstr = IR::Instr::New(Js::OpCode::CMP_ASR31, instr->m_func);
  6217. insertInstr->SetSrc1(opndRegR12);
  6218. insertInstr->SetSrc2(dst);
  6219. insertBeforeInstr->InsertBefore(insertInstr);
  6220. // BNE $bailOutLabel -- bail if the result overflowed
  6221. insertInstr = IR::BranchInstr::New(Js::OpCode::BNE, bailOutLabel, instr->m_func);
  6222. insertBeforeInstr->InsertBefore(insertInstr);
  6223. }
  6224. if(bailOutKind & IR::BailOutOnNegativeZero)
  6225. {
  6226. // TST dst, dst
  6227. // BEQ $checkForNegativeZeroLabel
  6228. insertInstr = IR::Instr::New(Js::OpCode::TST, instr->m_func);
  6229. insertInstr->SetSrc1(dst);
  6230. insertInstr->SetSrc2(dst);
  6231. insertBeforeInstr->InsertBefore(insertInstr);
  6232. insertBeforeInstr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BEQ, checkForNegativeZeroLabel, instr->m_func));
  6233. }
  6234. insertBeforeInstr->InsertBefore(IR::BranchInstr::New(Js::OpCode::B, skipBailOutLabel, instr->m_func));
  6235. }
  6236. void
  6237. LowererMD::LowerInt4RemWithBailOut(
  6238. IR::Instr *const instr,
  6239. const IR::BailOutKind bailOutKind,
  6240. IR::LabelInstr *const bailOutLabel,
  6241. IR::LabelInstr *const skipBailOutLabel) const
  6242. {
  6243. Assert(instr);
  6244. Assert(instr->m_opcode == Js::OpCode::Rem_I4);
  6245. Assert(!instr->HasBailOutInfo());
  6246. Assert(bailOutKind & IR::BailOutOnResultConditions);
  6247. Assert(bailOutLabel);
  6248. Assert(instr->m_next == bailOutLabel);
  6249. Assert(skipBailOutLabel);
  6250. IR::Opnd *dst = instr->GetDst();
  6251. IR::Opnd *src1 = instr->GetSrc1();
  6252. IR::Opnd *src2 = instr->GetSrc2();
  6253. Assert(dst->IsInt32());
  6254. Assert(src1->IsInt32());
  6255. Assert(src2->IsInt32());
  6256. //Lower the instruction
  6257. EmitInt4Instr(instr);
  6258. //check for negative zero
  6259. //We have, dst = src1 % src2
  6260. //We need to bailout if dst == 0 and src1 < 0
  6261. // tst dst, dst
  6262. // bne $skipBailOutLabel
  6263. // tst src1,src1
  6264. // bpl $skipBailOutLabel
  6265. //
  6266. //$bailOutLabel
  6267. // GenerateBailout();
  6268. //
  6269. //$skipBailOutLabel
  6270. if(bailOutKind & IR::BailOutOnNegativeZero)
  6271. {
  6272. IR::LabelInstr *checkForNegativeZeroLabel = IR::LabelInstr::New(Js::OpCode::Label, instr->m_func, true);
  6273. bailOutLabel->InsertBefore(checkForNegativeZeroLabel);
  6274. IR::Instr *insertInstr = IR::Instr::New(Js::OpCode::TST, instr->m_func);
  6275. insertInstr->SetSrc1(dst);
  6276. insertInstr->SetSrc2(dst);
  6277. bailOutLabel->InsertBefore(insertInstr);
  6278. IR::Instr *branchInstr = IR::BranchInstr::New(Js::OpCode::BNE, skipBailOutLabel, instr->m_func);
  6279. bailOutLabel->InsertBefore(branchInstr);
  6280. insertInstr = IR::Instr::New(Js::OpCode::TST, instr->m_func);
  6281. insertInstr->SetSrc1(src1);
  6282. insertInstr->SetSrc2(src1);
  6283. bailOutLabel->InsertBefore(insertInstr);
  6284. branchInstr = IR::BranchInstr::New(Js::OpCode::BPL, skipBailOutLabel, instr->m_func);
  6285. bailOutLabel->InsertBefore(branchInstr);
  6286. }
  6287. // Fall through to bailOutLabel
  6288. }
  6289. void
  6290. LowererMD::EmitLoadVar(IR::Instr *instrLoad, bool isFromUint32, bool isHelper)
  6291. {
  6292. // s2 = LSL s1, Js::VarTag_Shift -- restore the var tag on the result
  6293. // BO $ToVar (branch on overflow)
  6294. // dst = OR s2, 1
  6295. // B $done
  6296. //$ToVar:
  6297. // EmitLoadVarNoCheck
  6298. //$Done:
  6299. AssertMsg(instrLoad->GetSrc1()->IsRegOpnd(), "Should be regOpnd");
  6300. bool isInt = false;
  6301. bool isNotInt = false;
  6302. IR::RegOpnd *src1 = instrLoad->GetSrc1()->AsRegOpnd();
  6303. IR::LabelInstr *labelToVar = nullptr;
  6304. IR::LabelInstr *labelDone = nullptr;
  6305. IR::Instr *instr;
  6306. if (src1->IsTaggedInt())
  6307. {
  6308. isInt = true;
  6309. }
  6310. else if (src1->IsNotInt())
  6311. {
  6312. isNotInt = true;
  6313. }
  6314. if (!isNotInt)
  6315. {
  6316. IR::Opnd * opnd32src1 = src1->UseWithNewType(TyInt32, this->m_func);
  6317. IR::RegOpnd * opndReg2 = IR::RegOpnd::New(TyMachReg, this->m_func);
  6318. IR::Opnd * opnd32Reg2 = opndReg2->UseWithNewType(TyInt32, this->m_func);
  6319. if (!isInt)
  6320. {
  6321. labelToVar = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  6322. if (!isFromUint32)
  6323. {
  6324. // TEQ src1,src1 LSL#1 - TIOFLW is an alias for this pattern.
  6325. // XOR the src with itself shifted left one. If there's no overflow,
  6326. // the result should be positive (top bit clear).
  6327. instr = IR::Instr::New(Js::OpCode::TIOFLW, this->m_func);
  6328. instr->SetSrc1(opnd32src1);
  6329. instrLoad->InsertBefore(instr);
  6330. // BMI $ToVar
  6331. // Branch on negative result of the preceding test.
  6332. instr = IR::BranchInstr::New(Js::OpCode::BMI, labelToVar, this->m_func);
  6333. instrLoad->InsertBefore(instr);
  6334. }
  6335. else
  6336. {
  6337. //TST src1, 0xC0000000 -- test for length that is negative or overflows tagged int
  6338. instr = IR::Instr::New(Js::OpCode::TST, this->m_func);
  6339. instr->SetSrc1(opnd32src1);
  6340. instr->SetSrc2(IR::IntConstOpnd::New((int32)0x80000000 >> Js::VarTag_Shift, TyInt32, this->m_func));
  6341. instrLoad->InsertBefore(instr);
  6342. LegalizeMD::LegalizeInstr(instr, false);
  6343. // BNE $helper
  6344. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelToVar, this->m_func);
  6345. instrLoad->InsertBefore(instr);
  6346. }
  6347. }
  6348. // s2 = LSL s1, Js::VarTag_Shift -- restore the var tag on the result
  6349. instr = IR::Instr::New(Js::OpCode::LSL, opnd32Reg2, opnd32src1,
  6350. IR::IntConstOpnd::New(Js::VarTag_Shift, TyInt8, this->m_func),
  6351. this->m_func);
  6352. instrLoad->InsertBefore(instr);
  6353. // dst = ADD s2, 1
  6354. instr = IR::Instr::New(Js::OpCode::ADD, instrLoad->GetDst(), opndReg2, IR::IntConstOpnd::New(1, TyMachReg, this->m_func), this->m_func);
  6355. instrLoad->InsertBefore(instr);
  6356. if (!isInt)
  6357. {
  6358. // B $done
  6359. labelDone = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, isHelper);
  6360. instr = IR::BranchInstr::New(Js::OpCode::B, labelDone, this->m_func);
  6361. instrLoad->InsertBefore(instr);
  6362. }
  6363. }
  6364. instr = instrLoad;
  6365. if (!isInt)
  6366. {
  6367. //$ToVar:
  6368. if (labelToVar)
  6369. {
  6370. instrLoad->InsertBefore(labelToVar);
  6371. }
  6372. this->EmitLoadVarNoCheck(instrLoad->GetDst()->AsRegOpnd(), src1, instrLoad, isFromUint32, isHelper);
  6373. }
  6374. //$Done:
  6375. if (labelDone)
  6376. {
  6377. instr->InsertAfter(labelDone);
  6378. }
  6379. instrLoad->Remove();
  6380. }
  6381. void
  6382. LowererMD::EmitLoadVarNoCheck(IR::RegOpnd * dst, IR::RegOpnd * src, IR::Instr *instrLoad, bool isFromUint32, bool isHelper)
  6383. {
  6384. IR::RegOpnd * floatReg = IR::RegOpnd::New(TyFloat64, this->m_func);
  6385. if (isFromUint32)
  6386. {
  6387. this->EmitUIntToFloat(floatReg, src, instrLoad);
  6388. }
  6389. else
  6390. {
  6391. this->EmitIntToFloat(floatReg, src, instrLoad);
  6392. }
  6393. this->SaveDoubleToVar(dst, floatReg, instrLoad, instrLoad, isHelper);
  6394. }
  6395. bool
  6396. LowererMD::EmitLoadInt32(IR::Instr *instrLoad)
  6397. {
  6398. // isInt:
  6399. // dst = ASR r1, AtomTag
  6400. // isNotInt:
  6401. // dst = ToInt32(r1)
  6402. // else:
  6403. // dst = ASRS r1, AtomTag
  6404. // BCS $Done
  6405. // dst = ToInt32(r1)
  6406. // $Done
  6407. AssertMsg(instrLoad->GetSrc1()->IsRegOpnd(), "Should be regOpnd");
  6408. bool isInt = false;
  6409. bool isNotInt = false;
  6410. IR::RegOpnd *src1 = instrLoad->GetSrc1()->AsRegOpnd();
  6411. IR::LabelInstr *labelDone = nullptr;
  6412. IR::LabelInstr *labelFloat = nullptr;
  6413. IR::LabelInstr *labelHelper = nullptr;
  6414. IR::Instr *instr;
  6415. if (src1->IsTaggedInt())
  6416. {
  6417. isInt = true;
  6418. }
  6419. else if (src1->IsNotInt())
  6420. {
  6421. isNotInt = true;
  6422. }
  6423. if (isInt)
  6424. {
  6425. instrLoad->m_opcode = Js::OpCode::ASR;
  6426. instrLoad->SetSrc2(IR::IntConstOpnd::New(Js::AtomTag, TyMachReg, this->m_func));
  6427. }
  6428. else
  6429. {
  6430. const ValueType src1ValueType(src1->GetValueType());
  6431. const bool doFloatToIntFastPath =
  6432. (src1ValueType.IsLikelyFloat() || src1ValueType.IsLikelyUntaggedInt()) &&
  6433. !(instrLoad->HasBailOutInfo() && (instrLoad->GetBailOutKind() == IR::BailOutIntOnly || instrLoad->GetBailOutKind() == IR::BailOutExpectingInteger));
  6434. if (!isNotInt)
  6435. {
  6436. if (!isInt)
  6437. {
  6438. if(doFloatToIntFastPath)
  6439. {
  6440. labelFloat = IR::LabelInstr::New(Js::OpCode::Label, instrLoad->m_func, false);
  6441. }
  6442. else
  6443. {
  6444. labelHelper = IR::LabelInstr::New(Js::OpCode::Label, instrLoad->m_func, true);
  6445. }
  6446. this->GenerateSmIntTest(src1, instrLoad, labelFloat ? labelFloat : labelHelper);
  6447. }
  6448. instr = IR::Instr::New(
  6449. Js::OpCode::ASRS, instrLoad->GetDst(), src1, IR::IntConstOpnd::New(Js::AtomTag, TyMachReg, this->m_func), this->m_func);
  6450. instrLoad->InsertBefore(instr);
  6451. labelDone = instrLoad->GetOrCreateContinueLabel();
  6452. instr = IR::BranchInstr::New(Js::OpCode::BCS, labelDone, this->m_func);
  6453. instrLoad->InsertBefore(instr);
  6454. }
  6455. if(doFloatToIntFastPath)
  6456. {
  6457. if(labelFloat)
  6458. {
  6459. instrLoad->InsertBefore(labelFloat);
  6460. }
  6461. if(!labelHelper)
  6462. {
  6463. labelHelper = IR::LabelInstr::New(Js::OpCode::Label, instrLoad->m_func, true);
  6464. }
  6465. if(!labelDone)
  6466. {
  6467. labelDone = instrLoad->GetOrCreateContinueLabel();
  6468. }
  6469. IR::RegOpnd *floatReg = IR::RegOpnd::New(TyFloat64, this->m_func);
  6470. this->LoadFloatValue(src1, floatReg, labelHelper, instrLoad, instrLoad->HasBailOutInfo());
  6471. this->ConvertFloatToInt32(instrLoad->GetDst(), floatReg, labelHelper, labelDone, instrLoad);
  6472. }
  6473. if(labelHelper)
  6474. {
  6475. instrLoad->InsertBefore(labelHelper);
  6476. }
  6477. if(instrLoad->HasBailOutInfo() && (instrLoad->GetBailOutKind() == IR::BailOutIntOnly || instrLoad->GetBailOutKind() == IR::BailOutExpectingInteger))
  6478. {
  6479. // Avoid bailout if we have a JavascriptNumber whose value is a signed 32-bit integer
  6480. m_lowerer->LoadInt32FromUntaggedVar(instrLoad);
  6481. // Need to bail out instead of calling a helper
  6482. return true;
  6483. }
  6484. this->m_lowerer->LowerUnaryHelperMem(instrLoad, IR::HelperConv_ToInt32);
  6485. }
  6486. return false;
  6487. }
  6488. IR::Instr *
  6489. LowererMD::LowerGetCachedFunc(IR::Instr *instr)
  6490. {
  6491. // src1 is an ActivationObjectEx, and we want to get the function object identified by the index (src2)
  6492. // dst = MOV (src1)->GetFuncCacheEntry(src2)->func
  6493. //
  6494. // => [src1 + (offsetof(src1, cache) + (src2 * sizeof(FuncCacheEntry)) + offsetof(FuncCacheEntry, func))]
  6495. IR::IntConstOpnd *src2Opnd = instr->UnlinkSrc2()->AsIntConstOpnd();
  6496. IR::RegOpnd *src1Opnd = instr->UnlinkSrc1()->AsRegOpnd();
  6497. IR::Instr *instrPrev = instr->m_prev;
  6498. instr->SetSrc1(IR::IndirOpnd::New(src1Opnd, (src2Opnd->GetValue() * sizeof(Js::FuncCacheEntry)) + Js::ActivationObjectEx::GetOffsetOfCache() + offsetof(Js::FuncCacheEntry, func), TyVar, this->m_func));
  6499. this->ChangeToAssign(instr);
  6500. src2Opnd->Free(this->m_func);
  6501. return instrPrev;
  6502. }
  6503. IR::Instr *
  6504. LowererMD::LowerCommitScope(IR::Instr *instrCommit)
  6505. {
  6506. IR::Instr *instrPrev = instrCommit->m_prev;
  6507. IR::RegOpnd *baseOpnd = instrCommit->UnlinkSrc1()->AsRegOpnd();
  6508. IR::Opnd *opnd;
  6509. IR::Instr * insertInstr = instrCommit->m_next;
  6510. // Write undef to all the local var slots.
  6511. opnd = IR::IndirOpnd::New(baseOpnd, Js::ActivationObjectEx::GetOffsetOfCommitFlag(), TyInt8, this->m_func);
  6512. instrCommit->SetDst(opnd);
  6513. instrCommit->SetSrc1(IR::IntConstOpnd::New(1, TyInt8, this->m_func));
  6514. LowererMD::ChangeToAssign(instrCommit);
  6515. IR::IntConstOpnd *intConstOpnd = instrCommit->UnlinkSrc2()->AsIntConstOpnd();
  6516. const Js::PropertyIdArray *propIds = Js::ByteCodeReader::ReadPropertyIdArray(intConstOpnd->GetValue(), instrCommit->m_func->GetJnFunction());
  6517. intConstOpnd->Free(this->m_func);
  6518. uint firstVarSlot = (uint)Js::ActivationObjectEx::GetFirstVarSlot(propIds);
  6519. if (firstVarSlot < propIds->count)
  6520. {
  6521. // On ARM, instead of re-using the address of "undefined" for each store, put the address in a register
  6522. // and re-use that. (Would that be good for x86/amd64 as well?)
  6523. IR::RegOpnd *undefOpnd = IR::RegOpnd::New(TyMachReg, this->m_func);
  6524. LowererMD::CreateAssign(undefOpnd, m_lowerer->LoadLibraryValueOpnd(insertInstr, LibraryValue::ValueUndefined), insertInstr);
  6525. IR::RegOpnd *slotBaseOpnd = IR::RegOpnd::New(TyMachReg, this->m_func);
  6526. // Load a pointer to the aux slots. We assume that all ActivationObject's have only aux slots.
  6527. opnd = IR::IndirOpnd::New(baseOpnd, Js::DynamicObject::GetOffsetOfAuxSlots(), TyMachReg, this->m_func);
  6528. this->CreateAssign(slotBaseOpnd, opnd, insertInstr);
  6529. for (uint i = firstVarSlot; i < propIds->count; i++)
  6530. {
  6531. opnd = IR::IndirOpnd::New(slotBaseOpnd, i << this->GetDefaultIndirScale(), TyMachReg, this->m_func);
  6532. LowererMD::CreateAssign(opnd, undefOpnd, insertInstr);
  6533. }
  6534. }
  6535. return instrPrev;
  6536. }
  6537. void
  6538. LowererMD::ImmedSrcToReg(IR::Instr * instr, IR::Opnd * newOpnd, int srcNum)
  6539. {
  6540. if (srcNum == 2)
  6541. {
  6542. instr->SetSrc2(newOpnd);
  6543. }
  6544. else
  6545. {
  6546. Assert(srcNum == 1);
  6547. instr->SetSrc1(newOpnd);
  6548. }
  6549. switch (instr->m_opcode)
  6550. {
  6551. case Js::OpCode::LDIMM:
  6552. instr->m_opcode = Js::OpCode::MOV;
  6553. break;
  6554. default:
  6555. // Nothing to do (unless we have immed/reg variations for other instructions).
  6556. break;
  6557. }
  6558. }
  6559. void
  6560. LowererMD::MarkOneFltTmpSym(StackSym *sym, BVSparse<JitArenaAllocator> *bvTmps, bool fFltPrefOp)
  6561. {
  6562. // Nothing to do here. It may be called when lowering a switch if fast paths are on.
  6563. }
  6564. IR::LabelInstr *
  6565. LowererMD::GetBailOutStackRestoreLabel(BailOutInfo * bailOutInfo, IR::LabelInstr * exitTargetInstr)
  6566. {
  6567. return exitTargetInstr;
  6568. }
  6569. bool
  6570. LowererMD::AnyFloatTmps()
  6571. {
  6572. // no float preferencing for ARM yet
  6573. return false;
  6574. }
  6575. IR::LabelInstr*
  6576. LowererMD::InsertBeforeRecoveryForFloatTemps(
  6577. IR::Instr * insertBefore,
  6578. IR::LabelInstr * labelRecover,
  6579. const bool isInHelperBlock)
  6580. {
  6581. AssertMsg(0, "NYI");
  6582. return nullptr;
  6583. }
  6584. StackSym *
  6585. LowererMD::GetImplicitParamSlotSym(Js::ArgSlot argSlot)
  6586. {
  6587. return GetImplicitParamSlotSym(argSlot, this->m_func);
  6588. }
  6589. StackSym *
  6590. LowererMD::GetImplicitParamSlotSym(Js::ArgSlot argSlot, Func * func)
  6591. {
  6592. // For ARM, offset for implicit params always start at 0
  6593. // TODO: Consider not to use the argSlot number for the param slot sym, which can
  6594. // be confused with arg slot number from javascript
  6595. StackSym * stackSym = StackSym::NewParamSlotSym(argSlot, func);
  6596. func->SetArgOffset(stackSym, argSlot * MachPtr);
  6597. return stackSym;
  6598. }
  6599. IR::LabelInstr *
  6600. LowererMD::EnsureEpilogLabel()
  6601. {
  6602. if (this->m_func->m_epilogLabel)
  6603. {
  6604. return this->m_func->m_epilogLabel;
  6605. }
  6606. IR::Instr *exitInstr = this->m_func->m_exitInstr;
  6607. IR::Instr *prevInstr = exitInstr->GetPrevRealInstrOrLabel();
  6608. if (prevInstr->IsLabelInstr())
  6609. {
  6610. this->m_func->m_epilogLabel = prevInstr->AsLabelInstr();
  6611. return prevInstr->AsLabelInstr();
  6612. }
  6613. IR::LabelInstr *labelInstr = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  6614. exitInstr->InsertBefore(labelInstr);
  6615. this->m_func->m_epilogLabel = labelInstr;
  6616. return labelInstr;
  6617. }
  6618. bool
  6619. LowererMD::GenerateLdThisStrict(IR::Instr* insertInstr)
  6620. {
  6621. IR::RegOpnd * src1 = insertInstr->GetSrc1()->AsRegOpnd();
  6622. IR::RegOpnd * typeId = IR::RegOpnd::New(TyMachReg, this->m_func);
  6623. IR::RegOpnd * type = IR::RegOpnd::New(TyMachReg, this->m_func);
  6624. IR::LabelInstr * done = IR::LabelInstr::New(Js::OpCode::Label, m_func);
  6625. IR::LabelInstr * fallthrough = IR::LabelInstr::New(Js::OpCode::Label, m_func);
  6626. IR::LabelInstr * helper = IR::LabelInstr::New(Js::OpCode::Label, m_func, /*helper*/ true);
  6627. IR::Instr* instr;
  6628. bool assign = insertInstr->GetDst() && !insertInstr->GetDst()->IsEqual(src1);
  6629. if (!src1->m_sym->m_isNotInt)
  6630. {
  6631. GenerateObjectTest(src1, insertInstr, assign ? done : fallthrough);
  6632. }
  6633. // LDR r1, [src1 + offset(type)]
  6634. {
  6635. IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(src1->AsRegOpnd(), Js::RecyclableObject::GetOffsetOfType(), TyMachReg, this->m_func);
  6636. this->CreateAssign(type, indirOpnd, insertInstr);
  6637. }
  6638. // LDR r1, [r1 + offset(typeId)]
  6639. {
  6640. IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(type, Js::Type::GetOffsetOfTypeId(), TyMachReg, this->m_func);
  6641. this->CreateAssign(typeId, indirOpnd, insertInstr);
  6642. }
  6643. // CMP typeid, TypeIds_ActivationObject
  6644. instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  6645. instr->SetSrc1(typeId);
  6646. instr->SetSrc2(IR::IntConstOpnd::New(Js::TypeIds_ActivationObject, TyMachReg, this->m_func));
  6647. insertInstr->InsertBefore(instr);
  6648. LegalizeMD::LegalizeInstr(instr, false);
  6649. // BE $helper
  6650. instr = IR::BranchInstr::New(Js::OpCode::BEQ, helper, this->m_func);
  6651. insertInstr->InsertBefore(instr);
  6652. if(assign)
  6653. {
  6654. //$done:
  6655. insertInstr->InsertBefore(done);
  6656. // LDR $dest, $src
  6657. LowererMD::CreateAssign(insertInstr->GetDst(), insertInstr->GetSrc1(), insertInstr);
  6658. }
  6659. // B $fallthrough
  6660. instr = IR::BranchInstr::New(Js::OpCode::B, fallthrough, this->m_func);
  6661. insertInstr->InsertBefore(instr);
  6662. insertInstr->InsertBefore(helper);
  6663. if(insertInstr->GetDst())
  6664. {
  6665. // LDR dst, undefined
  6666. LowererMD::CreateAssign(insertInstr->GetDst(), m_lowerer->LoadLibraryValueOpnd(insertInstr, LibraryValue::ValueUndefined), insertInstr);
  6667. }
  6668. // $fallthrough:
  6669. insertInstr->InsertAfter(fallthrough);
  6670. return true;
  6671. }
  6672. void LowererMD::GenerateIsDynamicObject(IR::RegOpnd *regOpnd, IR::Instr *insertInstr, IR::LabelInstr *labelHelper, bool fContinueLabel)
  6673. {
  6674. // CMP [srcReg], Js::DynamicObject::`vtable'
  6675. IR::Instr *cmp = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  6676. cmp->SetSrc1(IR::IndirOpnd::New(regOpnd, 0, TyMachPtr, m_func));
  6677. cmp->SetSrc2(m_lowerer->LoadVTableValueOpnd(insertInstr, VTableValue::VtableDynamicObject));
  6678. insertInstr->InsertBefore(cmp);
  6679. LegalizeMD::LegalizeInstr(cmp, false);
  6680. if (fContinueLabel)
  6681. {
  6682. // BEQ $continue
  6683. IR::Instr * jne = IR::BranchInstr::New(Js::OpCode::BEQ, labelHelper, this->m_func);
  6684. insertInstr->InsertBefore(jne);
  6685. }
  6686. else
  6687. {
  6688. // BNE $helper
  6689. IR::Instr * jne = IR::BranchInstr::New(Js::OpCode::BNE, labelHelper, this->m_func);
  6690. insertInstr->InsertBefore(jne);
  6691. }
  6692. }
  6693. void LowererMD::GenerateIsRecyclableObject(IR::RegOpnd *regOpnd, IR::Instr *insertInstr, IR::LabelInstr *labelHelper, bool checkObjectAndDynamicObject)
  6694. {
  6695. // CMP [srcReg], Js::DynamicObject::`vtable'
  6696. // BEQ $fallThough
  6697. // LDR r1, [src1 + offset(type)]
  6698. // LDR r1, [r1 + offset(typeId)]
  6699. // SUB r1, -(~TypeIds_LastJavascriptPrimitiveType) -- if (typeId > TypeIds_LastJavascriptPrimitiveType && typeId <= TypeIds_LastTrueJavascriptObjectType)
  6700. // CMP r1, (TypeIds_LastTrueJavascriptObjectType - TypeIds_LastJavascriptPrimitiveType - 1)
  6701. // BHI $helper
  6702. //fallThrough:
  6703. IR::LabelInstr *labelFallthrough = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  6704. if (checkObjectAndDynamicObject)
  6705. {
  6706. if (!regOpnd->m_sym->m_isNotInt)
  6707. {
  6708. GenerateObjectTest(regOpnd, insertInstr, labelHelper);
  6709. }
  6710. this->GenerateIsDynamicObject(regOpnd, insertInstr, labelFallthrough, true);
  6711. }
  6712. IR::RegOpnd * r1 = IR::RegOpnd::New(TyMachReg, this->m_func);
  6713. // LDR r1, [src1 + offset(type)]
  6714. {
  6715. IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(regOpnd, Js::RecyclableObject::GetOffsetOfType(), TyMachReg, this->m_func);
  6716. this->CreateAssign(r1, indirOpnd, insertInstr);
  6717. }
  6718. // LDR r1, [r1 + offset(typeId)]
  6719. {
  6720. IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(r1, Js::Type::GetOffsetOfTypeId(), TyMachReg, this->m_func);
  6721. this->CreateAssign(r1, indirOpnd, insertInstr);
  6722. }
  6723. // SUB r1, -(~TypeIds_LastJavascriptPrimitiveType)
  6724. {
  6725. IR::Instr * add = IR::Instr::New(Js::OpCode::SUB, r1, r1, IR::IntConstOpnd::New(-(~Js::TypeIds_LastJavascriptPrimitiveType), TyInt32, this->m_func, true), this->m_func);
  6726. insertInstr->InsertBefore(add);
  6727. LegalizeMD::LegalizeInstr(add, false);
  6728. }
  6729. // CMP r1, (TypeIds_LastTrueJavascriptObjectType - TypeIds_LastJavascriptPrimitiveType - 1)
  6730. {
  6731. IR::Instr * cmp = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  6732. cmp->SetSrc1(r1);
  6733. cmp->SetSrc2(IR::IntConstOpnd::New(Js::TypeIds_LastTrueJavascriptObjectType - Js::TypeIds_LastJavascriptPrimitiveType - 1, TyInt32, this->m_func));
  6734. insertInstr->InsertBefore(cmp);
  6735. LegalizeMD::LegalizeInstr(cmp, false);
  6736. }
  6737. // BHI $helper
  6738. {
  6739. IR::Instr * jbe = IR::BranchInstr::New(Js::OpCode::BHI, labelHelper, this->m_func);
  6740. insertInstr->InsertBefore(jbe);
  6741. }
  6742. // $fallThrough
  6743. insertInstr->InsertBefore(labelFallthrough);
  6744. }
  6745. bool
  6746. LowererMD::GenerateLdThisCheck(IR::Instr * instr)
  6747. {
  6748. //
  6749. // If not an object, jump to $helper
  6750. // MOV dst, src1 -- return the object itself
  6751. // B $fallthrough
  6752. // $helper:
  6753. // (caller generates helper call)
  6754. // $fallthrough:
  6755. //
  6756. IR::RegOpnd * src1 = instr->GetSrc1()->AsRegOpnd();
  6757. IR::LabelInstr * helper = IR::LabelInstr::New(Js::OpCode::Label, m_func, true);
  6758. IR::LabelInstr * fallthrough = IR::LabelInstr::New(Js::OpCode::Label, m_func);
  6759. this->GenerateIsRecyclableObject(src1, instr, helper);
  6760. // MOV dst, src1
  6761. if (instr->GetDst() && !instr->GetDst()->IsEqual(src1))
  6762. {
  6763. this->CreateAssign(instr->GetDst(), src1, instr);
  6764. }
  6765. // B $fallthrough
  6766. {
  6767. IR::Instr * jmp = IR::BranchInstr::New(Js::OpCode::B, fallthrough, this->m_func);
  6768. instr->InsertBefore(jmp);
  6769. }
  6770. // $helper:
  6771. // (caller generates helper call)
  6772. // $fallthrough:
  6773. instr->InsertBefore(helper);
  6774. instr->InsertAfter(fallthrough);
  6775. return true;
  6776. }
  6777. // given object instanceof function, functionReg is a register with function,
  6778. // objectReg is a register with instance and inlineCache is an InstIsInlineCache.
  6779. // We want to generate:
  6780. //
  6781. // fallback on helper (will patch the inline cache) if function does not match the cache
  6782. // LDIMM dst, Js::false
  6783. // LDR cache, [&(inlineCache->function)]
  6784. // CMP functionReg, cache
  6785. // BNE helper
  6786. //
  6787. // fallback if object is a tagged int
  6788. // TST objectReg, Js::AtomTag
  6789. // BNE done
  6790. //
  6791. // return false if object is a primitive
  6792. // LDR typeReg, objectSrc + offsetof(RecyclableObject::type)
  6793. // LDR typeID, [typeReg + offsetof(Type::typeid)]
  6794. // CMP typeID, TypeIds_LastJavascriptPrimitiveType
  6795. // BLE done
  6796. //
  6797. // fallback if object's type is not the cached type
  6798. // CMP typeReg, [&(inlineCache->type]
  6799. // BNE helper
  6800. //
  6801. // use the cached result and fallthrough
  6802. // LDR dst, [&(inlineCache->result)]
  6803. // B done
  6804. //
  6805. // $helper
  6806. // $done
  6807. bool
  6808. LowererMD::GenerateFastIsInst(IR::Instr * instr)
  6809. {
  6810. IR::LabelInstr * helper = IR::LabelInstr::New(Js::OpCode::Label, m_func, true);
  6811. IR::LabelInstr * done = IR::LabelInstr::New(Js::OpCode::Label, m_func);
  6812. IR::RegOpnd * typeReg = IR::RegOpnd::New(TyMachReg, this->m_func);
  6813. IR::Opnd * objectSrc;
  6814. IR::RegOpnd * objectReg;
  6815. IR::Opnd * functionSrc;
  6816. IR::RegOpnd * functionReg;
  6817. Js::IsInstInlineCache * inlineCache;
  6818. IR::Instr * instrArg;
  6819. // We are going to use the extra ArgOut_A instructions to lower the helper call later,
  6820. // so we leave them alone here and clean them up then.
  6821. inlineCache = instr->m_func->GetJnFunction()->GetIsInstInlineCache(instr->GetSrc1()->AsIntConstOpnd()->GetValue());
  6822. Assert(instr->GetSrc2()->AsRegOpnd()->m_sym->m_isSingleDef);
  6823. instrArg = instr->GetSrc2()->AsRegOpnd()->m_sym->m_instrDef;
  6824. objectSrc = instrArg->GetSrc1();
  6825. Assert(instrArg->GetSrc2()->AsRegOpnd()->m_sym->m_isSingleDef);
  6826. instrArg = instrArg->GetSrc2()->AsRegOpnd()->m_sym->m_instrDef;
  6827. functionSrc = instrArg->GetSrc1();
  6828. Assert(instrArg->GetSrc2() == nullptr);
  6829. IR::Opnd* opndDst = instr->GetDst();
  6830. if (!opndDst->IsRegOpnd())
  6831. {
  6832. opndDst = IR::RegOpnd::New(opndDst->GetType(), this->m_func);
  6833. }
  6834. // LDR dst, Js::false
  6835. instr->InsertBefore(IR::Instr::New(Js::OpCode::LDR, opndDst,
  6836. m_lowerer->LoadLibraryValueOpnd(instr, LibraryValue::ValueFalse), m_func));
  6837. if (functionSrc->IsRegOpnd())
  6838. {
  6839. functionReg = functionSrc->AsRegOpnd();
  6840. }
  6841. else
  6842. {
  6843. functionReg = IR::RegOpnd::New(TyMachReg, this->m_func);
  6844. LowererMD::CreateAssign(functionReg, functionSrc, instr);
  6845. }
  6846. // CMP functionReg, [&(inlineCache->function)]
  6847. {
  6848. IR::Instr * cmp = IR::Instr::New(Js::OpCode::CMP, m_func);
  6849. cmp->SetSrc1(functionReg);
  6850. cmp->SetSrc2(IR::MemRefOpnd::New((void*)&(inlineCache->function), TyMachReg, m_func,
  6851. IR::AddrOpndKindDynamicIsInstInlineCacheFunctionRef));
  6852. instr->InsertBefore(cmp);
  6853. LegalizeMD::LegalizeInstr(cmp, false);
  6854. }
  6855. // BNE helper
  6856. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BNE, helper, m_func));
  6857. if (objectSrc->IsRegOpnd())
  6858. {
  6859. objectReg = objectSrc->AsRegOpnd();
  6860. }
  6861. else
  6862. {
  6863. objectReg = IR::RegOpnd::New(TyMachReg, this->m_func);
  6864. LowererMD::CreateAssign(objectReg, objectSrc, instr);
  6865. }
  6866. // TST objectReg, Js::AtomTag
  6867. // BNE done
  6868. if (!objectReg->m_sym->m_isNotInt)
  6869. {
  6870. GenerateObjectTest(objectReg, instr, done);
  6871. }
  6872. // LDR typeReg, objectSrc + offsetof(RecyclableObject::type)
  6873. instr->InsertBefore(IR::Instr::New(Js::OpCode::LDR, typeReg,
  6874. IR::IndirOpnd::New(objectReg, Js::RecyclableObject::GetOffsetOfType(), TyMachReg, m_func),
  6875. m_func));
  6876. // CMP [typeReg + offsetof(Type::typeid)], TypeIds_LastJavascriptPrimitiveType
  6877. {
  6878. IR::Instr * cmp = IR::Instr::New(Js::OpCode::CMP, m_func);
  6879. cmp->SetSrc1(IR::IndirOpnd::New(typeReg, Js::Type::GetOffsetOfTypeId(), TyInt32, m_func));
  6880. cmp->SetSrc2(IR::IntConstOpnd::New(Js::TypeId::TypeIds_LastJavascriptPrimitiveType, TyInt32, m_func));
  6881. instr->InsertBefore(cmp);
  6882. LegalizeMD::LegalizeInstr(cmp, false);
  6883. }
  6884. // BLE done
  6885. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BLE, done, m_func));
  6886. // CMP typeReg, [&(inlineCache->type]
  6887. {
  6888. IR::Instr * cmp = IR::Instr::New(Js::OpCode::CMP, m_func);
  6889. cmp->SetSrc1(typeReg);
  6890. cmp->SetSrc2(IR::MemRefOpnd::New((void*)&(inlineCache->type), TyMachReg, m_func));
  6891. instr->InsertBefore(cmp);
  6892. LegalizeMD::LegalizeInstr(cmp, false);
  6893. }
  6894. // BNE helper
  6895. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BNE, helper, m_func));
  6896. // LDR dst, [&(inlineCache->result)]
  6897. IR::Instr *result = IR::Instr::New(Js::OpCode::LDR, opndDst,
  6898. IR::MemRefOpnd::New((void*)&(inlineCache->result), TyMachReg, m_func), m_func);
  6899. instr->InsertBefore(result);
  6900. LegalizeMD::LegalizeInstr(result, false);
  6901. if (opndDst != instr->GetDst())
  6902. {
  6903. LowererMD::CreateAssign(instr->GetDst(), opndDst, instr);
  6904. }
  6905. // B done
  6906. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::B, done, m_func));
  6907. // LABEL helper
  6908. instr->InsertBefore(helper);
  6909. instr->InsertAfter(done);
  6910. return true;
  6911. }
  6912. // Helper method: inserts legalized assign for given srcOpnd into RegD0 in front of given instr in the following way:
  6913. // dstReg = CreateAssign srcOpnd
  6914. // Used to put args of inline built-in call into RegD0 and RegD1 before we call actual CRT function.
  6915. void LowererMD::GenerateAssignForBuiltinArg(RegNum dstReg, IR::Opnd* srcOpnd, IR::Instr* instr)
  6916. {
  6917. IR::RegOpnd* tempDst = IR::RegOpnd::New(nullptr, dstReg, TyMachDouble, this->m_func);
  6918. tempDst->m_isCallArg = true; // This is to make sure that lifetime of opnd is virtually extended until next CALL instr.
  6919. this->CreateAssign(tempDst, srcOpnd, instr);
  6920. }
  6921. // For given InlineMathXXX instr, generate the call to actual CRT function/CPU instr.
  6922. void LowererMD::GenerateFastInlineBuiltInCall(IR::Instr* instr, IR::JnHelperMethod helperMethod)
  6923. {
  6924. switch (instr->m_opcode)
  6925. {
  6926. case Js::OpCode::InlineMathSqrt:
  6927. // Sqrt maps directly to the VFP instruction.
  6928. // src and dst are already float, all we need is just change the opcode and legalize.
  6929. // Before:
  6930. // dst = InlineMathSqrt src1
  6931. // After:
  6932. // <potential VSTR by legalizer if src1 is not a register>
  6933. // dst = VSQRT src1
  6934. Assert(helperMethod == (IR::JnHelperMethod)0);
  6935. Assert(instr->GetSrc2() == nullptr);
  6936. instr->m_opcode = Js::OpCode::VSQRT;
  6937. LegalizeMD::LegalizeInstr(instr, /* fPostRegAlloc = */ false);
  6938. break;
  6939. case Js::OpCode::InlineMathAbs:
  6940. Assert(helperMethod == (IR::JnHelperMethod)0);
  6941. return GenerateFastInlineBuiltInMathAbs(instr);
  6942. case Js::OpCode::InlineMathFloor:
  6943. Assert(helperMethod == (IR::JnHelperMethod)0);
  6944. return GenerateFastInlineBuiltInMathFloor(instr);
  6945. case Js::OpCode::InlineMathCeil:
  6946. Assert(helperMethod == (IR::JnHelperMethod)0);
  6947. return GenerateFastInlineBuiltInMathCeil(instr);
  6948. case Js::OpCode::InlineMathRound:
  6949. Assert(helperMethod == (IR::JnHelperMethod)0);
  6950. return GenerateFastInlineBuiltInMathRound(instr);
  6951. case Js::OpCode::InlineMathMin:
  6952. case Js::OpCode::InlineMathMax:
  6953. {
  6954. IR::Opnd* src1 = instr->GetSrc1();
  6955. IR::Opnd* src2 = instr->GetSrc2();
  6956. IR::Opnd* dst = instr->GetDst();
  6957. IR::LabelInstr* doneLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  6958. IR::LabelInstr* labelNaNHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  6959. IR::LabelInstr* labelNegZeroCheckHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  6960. IR::Instr* branchInstr;
  6961. bool min = instr->m_opcode == Js::OpCode::InlineMathMin ? true : false;
  6962. //(V)MOV dst, src1;
  6963. Assert(!dst->IsEqual(src1));
  6964. this->m_lowerer->InsertMove(dst, src1, instr);
  6965. if(dst->IsInt32())
  6966. {
  6967. // CMP src1, src2
  6968. if(min)
  6969. {
  6970. // BLT $continueLabel
  6971. branchInstr = IR::BranchInstr::New(Js::OpCode::BrLt_I4, doneLabel, src1, src2, instr->m_func);
  6972. instr->InsertBefore(branchInstr);
  6973. this->EmitInt4Instr(branchInstr);
  6974. }
  6975. else
  6976. {
  6977. // BGT $continueLabel
  6978. branchInstr = IR::BranchInstr::New(Js::OpCode::BrGt_I4, doneLabel, src1, src2, instr->m_func);
  6979. instr->InsertBefore(branchInstr);
  6980. this->EmitInt4Instr(branchInstr);
  6981. }
  6982. // MOV dst, src2
  6983. this->m_lowerer->InsertMove(dst, src2, instr);
  6984. }
  6985. else if(dst->IsFloat64())
  6986. {
  6987. // VCMPF64 src1, src2
  6988. // BCC (min)/ BGT (max) $doneLabel
  6989. // BVS $labelNaNHelper
  6990. // BEQ $labelNegZeroCheckHelper
  6991. // VMOV dst, src2
  6992. // B $doneLabel
  6993. //
  6994. // $labelNegZeroCheckHelper
  6995. // if(min)
  6996. // {
  6997. // if(src2 == -0.0)
  6998. // VMOV dst, src2
  6999. // }
  7000. // else
  7001. // {
  7002. // if(src1 == -0.0)
  7003. // VMOV dst, src2
  7004. // }
  7005. // B $doneLabel
  7006. //
  7007. // $labelNaNHelper
  7008. // VMOV dst, NaN
  7009. //
  7010. // $doneLabel
  7011. if(min)
  7012. {
  7013. this->m_lowerer->InsertCompareBranch(src1, src2, Js::OpCode::BrLt_A, doneLabel, instr); // Lowering of BrLt_A for floats is done to JA with operands swapped
  7014. }
  7015. else
  7016. {
  7017. this->m_lowerer->InsertCompareBranch(src1, src2, Js::OpCode::BrGt_A, doneLabel, instr);
  7018. }
  7019. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BVS, labelNaNHelper, instr->m_func));
  7020. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BEQ, labelNegZeroCheckHelper, instr->m_func));
  7021. this->m_lowerer->InsertMove(dst, src2, instr);
  7022. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::B, doneLabel, instr->m_func));
  7023. instr->InsertBefore(labelNegZeroCheckHelper);
  7024. IR::Opnd* isNegZero;
  7025. if(min)
  7026. {
  7027. isNegZero = IsOpndNegZero(src2, instr);
  7028. }
  7029. else
  7030. {
  7031. isNegZero = IsOpndNegZero(src1, instr);
  7032. }
  7033. this->m_lowerer->InsertCompareBranch(isNegZero, IR::IntConstOpnd::New(0x00000000, IRType::TyInt32, this->m_func), Js::OpCode::BrEq_A, doneLabel, instr);
  7034. this->m_lowerer->InsertMove(dst, src2, instr);
  7035. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::B, doneLabel, instr->m_func));
  7036. instr->InsertBefore(labelNaNHelper);
  7037. IR::Opnd * opndNaN = IR::MemRefOpnd::New((double*)&(Js::JavascriptNumber::k_Nan), IRType::TyFloat64, this->m_func,
  7038. IR::AddrOpndKindDynamicDoubleRef);
  7039. this->m_lowerer->InsertMove(dst, opndNaN, instr);
  7040. }
  7041. instr->InsertBefore(doneLabel);
  7042. instr->Remove();
  7043. break;
  7044. }
  7045. default:
  7046. // Before:
  7047. // dst = <Built-in call> src1, src2
  7048. // After:
  7049. // d0 = CreateAssign src1
  7050. // lr = MOV helperAddr
  7051. // BLX lr
  7052. // dst = CreateAssign call->dst (d0)
  7053. // Src1
  7054. AssertMsg(instr->GetDst()->IsFloat(), "Currently accepting only float args for math helpers -- dst.");
  7055. AssertMsg(instr->GetSrc1()->IsFloat(), "Currently accepting only float args for math helpers -- src1.");
  7056. AssertMsg(!instr->GetSrc2() || instr->GetSrc2()->IsFloat(), "Currently accepting only float args for math helpers -- src2.");
  7057. this->GenerateAssignForBuiltinArg((RegNum)FIRST_FLOAT_REG, instr->UnlinkSrc1(), instr);
  7058. // Src2
  7059. if (instr->GetSrc2() != nullptr)
  7060. {
  7061. this->GenerateAssignForBuiltinArg((RegNum)(FIRST_FLOAT_REG + 1), instr->UnlinkSrc2(), instr);
  7062. }
  7063. // Call CRT.
  7064. IR::RegOpnd* floatCallDst = IR::RegOpnd::New(nullptr, (RegNum)(FIRST_FLOAT_REG), TyMachDouble, this->m_func); // Dst in d0.
  7065. IR::Instr* floatCall = IR::Instr::New(Js::OpCode::BLX, floatCallDst, this->m_func);
  7066. instr->InsertBefore(floatCall);
  7067. // lr = MOV helperAddr
  7068. // BLX lr
  7069. IR::AddrOpnd* targetAddr = IR::AddrOpnd::New((Js::Var)IR::GetMethodOriginalAddress(helperMethod), IR::AddrOpndKind::AddrOpndKindDynamicMisc, this->m_func);
  7070. IR::RegOpnd *targetOpnd = IR::RegOpnd::New(nullptr, RegLR, TyMachPtr, this->m_func);
  7071. IR::Instr *movInstr = IR::Instr::New(Js::OpCode::LDIMM, targetOpnd, targetAddr, this->m_func);
  7072. targetOpnd->m_isCallArg = true;
  7073. floatCall->SetSrc1(targetOpnd);
  7074. floatCall->InsertBefore(movInstr);
  7075. // Save the result.
  7076. this->CreateAssign(instr->UnlinkDst(), floatCall->GetDst(), instr);
  7077. instr->Remove();
  7078. break;
  7079. }
  7080. }
  7081. void
  7082. LowererMD::GenerateFastInlineBuiltInMathAbs(IR::Instr *inlineInstr)
  7083. {
  7084. IR::Opnd* src = inlineInstr->GetSrc1();
  7085. IR::Opnd* dst = inlineInstr->UnlinkDst();
  7086. Assert(src);
  7087. IR::Instr* tmpInstr;
  7088. IRType srcType = src->GetType();
  7089. IR::Instr* nextInstr = IR::LabelInstr::New(Js::OpCode::Label, m_func);
  7090. IR::Instr* continueInstr = m_lowerer->LowerBailOnIntMin(inlineInstr);
  7091. continueInstr->InsertAfter(nextInstr);
  7092. if (srcType == IRType::TyInt32)
  7093. {
  7094. // Note: if execution gets so far, we always get (untagged) int32 here.
  7095. // Since -x = ~x + 1, abs(x) = x, abs(-x) = -x, sign-extend(x) = 0, sign_extend(-x) = -1, where 0 <= x.
  7096. // Then: abs(x) = sign-extend(x) XOR x - sign-extend(x)
  7097. // Expected input (otherwise bailout):
  7098. // - src1 is (untagged) int, not equal to int_min (abs(int_min) would produce overflow, as there's no corresponding positive int).
  7099. Assert(src->IsRegOpnd());
  7100. // tmpDst = EOR src, src ASR #31
  7101. IR::RegOpnd *tmpDst = IR::RegOpnd::New(TyMachReg, this->m_func);
  7102. tmpInstr = IR::Instr::New(Js::OpCode::CLRSIGN, tmpDst, src, this->m_func);
  7103. nextInstr->InsertBefore(tmpInstr);
  7104. // tmpDst = SUB tmpDst, src ASR #31
  7105. tmpInstr = IR::Instr::New(Js::OpCode::SBCMPLNT, tmpDst, tmpDst, src, this->m_func);
  7106. nextInstr->InsertBefore(tmpInstr);
  7107. // MOV dst, tmpDst
  7108. tmpInstr = IR::Instr::New(Js::OpCode::MOV, dst, tmpDst, this->m_func);
  7109. nextInstr->InsertBefore(tmpInstr);
  7110. }
  7111. else if (srcType == IRType::TyFloat64)
  7112. {
  7113. // VABS dst, src
  7114. tmpInstr = IR::Instr::New(Js::OpCode::VABS, dst, src, this->m_func);
  7115. nextInstr->InsertBefore(tmpInstr);
  7116. }
  7117. else
  7118. {
  7119. AssertMsg(FALSE, "GenerateFastInlineBuiltInMathAbs: unexpected type of the src!");
  7120. }
  7121. }
  7122. void
  7123. LowererMD::GenerateFastInlineBuiltInMathFloor(IR::Instr* instr)
  7124. {
  7125. Assert(instr->GetDst()->IsInt32());
  7126. IR::LabelInstr * checkNegZeroLabelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  7127. IR::LabelInstr * checkOverflowLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  7128. IR::LabelInstr * doneLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  7129. // VMOV floatOpnd, src
  7130. IR::Opnd * src = instr->UnlinkSrc1();
  7131. IR::RegOpnd* floatOpnd = IR::RegOpnd::New(TyFloat64, this->m_func);
  7132. this->m_lowerer->InsertMove(floatOpnd, src, instr);
  7133. IR::LabelInstr * bailoutLabel;
  7134. bool sharedBailout = (instr->GetBailOutInfo()->bailOutInstr != instr) ? true : false;
  7135. if(sharedBailout)
  7136. {
  7137. bailoutLabel = instr->GetBailOutInfo()->bailOutInstr->AsLabelInstr();
  7138. }
  7139. else
  7140. {
  7141. bailoutLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  7142. }
  7143. // NaN check
  7144. IR::Instr *instrCmp = IR::Instr::New(Js::OpCode::VCMPF64, this->m_func);
  7145. instrCmp->SetSrc1(floatOpnd);
  7146. instrCmp->SetSrc2(floatOpnd);
  7147. instr->InsertBefore(instrCmp);
  7148. LegalizeMD::LegalizeInstr(instrCmp, false);
  7149. // VMRS APSR, FPSCR
  7150. // BVS $bailoutLabel
  7151. instr->InsertBefore(IR::Instr::New(Js::OpCode::VMRS, this->m_func));
  7152. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BVS, bailoutLabel, this->m_func));
  7153. IR::Opnd * zeroReg = IR::RegOpnd::New(TyFloat64, this->m_func);
  7154. this->LoadFloatZero(zeroReg, instr);
  7155. // VMRS Rorig, FPSCR
  7156. // VMRS Rt, FPSCR
  7157. // BIC Rt, Rt, 0x400000
  7158. // ORR Rt, Rt, 0x800000
  7159. // VMSR FPSCR, Rt
  7160. IR::Opnd* regOrig = IR::RegOpnd::New(TyInt32, this->m_func);
  7161. IR::Opnd* reg = IR::RegOpnd::New(TyInt32, this->m_func);
  7162. instr->InsertBefore(
  7163. IR::Instr::New(Js::OpCode::VMRSR, regOrig, instr->m_func));
  7164. instr->InsertBefore(
  7165. IR::Instr::New(Js::OpCode::VMRSR, reg, instr->m_func));
  7166. instr->InsertBefore(
  7167. IR::Instr::New(Js::OpCode::BIC, reg, reg, IR::IntConstOpnd::New(0x400000, IRType::TyInt32, this->m_func), instr->m_func));
  7168. instr->InsertBefore(
  7169. IR::Instr::New(Js::OpCode::ORR, reg, reg, IR::IntConstOpnd::New(0x800000, IRType::TyInt32, this->m_func), instr->m_func));
  7170. IR::Instr* setFPSCRInstr = IR::Instr::New(Js::OpCode::VMSR, instr->m_func);
  7171. setFPSCRInstr->SetSrc1(reg);
  7172. instr->InsertBefore(setFPSCRInstr);
  7173. // VCVTRS32F64 floatreg, floatOpnd
  7174. IR::RegOpnd *floatReg = IR::RegOpnd::New(TyFloat32, this->m_func);
  7175. IR::Opnd * intOpnd = IR::RegOpnd::New(TyInt32, this->m_func);
  7176. instr->InsertBefore(
  7177. IR::Instr::New(Js::OpCode::VCVTRS32F64, floatReg, floatOpnd, instr->m_func));
  7178. // VMOVARMVFP intOpnd, floatReg
  7179. instr->InsertBefore(IR::Instr::New(Js::OpCode::VMOVARMVFP, intOpnd, floatReg, this->m_func));
  7180. // VMSR FPSCR, Rorig
  7181. IR::Instr* restoreFPSCRInstr = IR::Instr::New(Js::OpCode::VMSR, instr->m_func);
  7182. restoreFPSCRInstr->SetSrc1(regOrig);
  7183. instr->InsertBefore(restoreFPSCRInstr);
  7184. //negZero bailout
  7185. // TST intOpnd, intOpnd
  7186. // BNE checkOverflowLabel
  7187. this->m_lowerer->InsertTestBranch(intOpnd, intOpnd, Js::OpCode::BNE, checkOverflowLabel, instr);
  7188. instr->InsertBefore(checkNegZeroLabelHelper);
  7189. if(instr->ShouldCheckForNegativeZero())
  7190. {
  7191. IR::Opnd * isNegZero = IR::RegOpnd::New(TyInt32, this->m_func);
  7192. isNegZero = this->IsOpndNegZero(src, instr);
  7193. this->m_lowerer->InsertCompareBranch(isNegZero, IR::IntConstOpnd::New(0x00000000, IRType::TyInt32, this->m_func), Js::OpCode::BrNeq_A, bailoutLabel, instr);
  7194. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::B, doneLabel, instr->m_func));
  7195. }
  7196. instr->InsertBefore(checkOverflowLabel);
  7197. CheckOverflowOnFloatToInt32(instr, intOpnd, bailoutLabel, doneLabel);
  7198. IR::Opnd * dst = instr->UnlinkDst();
  7199. instr->InsertAfter(doneLabel);
  7200. if(!sharedBailout)
  7201. {
  7202. instr->InsertBefore(bailoutLabel);
  7203. }
  7204. this->m_lowerer->GenerateBailOut(instr);
  7205. // MOV dst, intOpnd
  7206. IR::Instr* movInstr = IR::Instr::New(Js::OpCode::MOV, dst, intOpnd, this->m_func);
  7207. doneLabel->InsertAfter(movInstr);
  7208. }
  7209. void
  7210. LowererMD::GenerateFastInlineBuiltInMathCeil(IR::Instr* instr)
  7211. {
  7212. Assert(instr->GetDst()->IsInt32());
  7213. IR::LabelInstr * checkNegZeroLabelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  7214. IR::LabelInstr * checkOverflowLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  7215. IR::LabelInstr * doneLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  7216. // VMOV floatOpnd, src
  7217. IR::Opnd * src = instr->UnlinkSrc1();
  7218. IR::RegOpnd* floatOpnd = IR::RegOpnd::New(TyFloat64, this->m_func);
  7219. this->m_lowerer->InsertMove(floatOpnd, src, instr);
  7220. IR::LabelInstr * bailoutLabel;
  7221. bool sharedBailout = (instr->GetBailOutInfo()->bailOutInstr != instr) ? true : false;
  7222. if(sharedBailout)
  7223. {
  7224. bailoutLabel = instr->GetBailOutInfo()->bailOutInstr->AsLabelInstr();
  7225. }
  7226. else
  7227. {
  7228. bailoutLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  7229. }
  7230. // NaN check
  7231. IR::Instr *instrCmp = IR::Instr::New(Js::OpCode::VCMPF64, this->m_func);
  7232. instrCmp->SetSrc1(floatOpnd);
  7233. instrCmp->SetSrc2(floatOpnd);
  7234. instr->InsertBefore(instrCmp);
  7235. LegalizeMD::LegalizeInstr(instrCmp, false);
  7236. // VMRS APSR, FPSCR
  7237. // BVS $bailoutLabel
  7238. instr->InsertBefore(IR::Instr::New(Js::OpCode::VMRS, this->m_func));
  7239. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BVS, bailoutLabel, this->m_func));
  7240. IR::Opnd * zeroReg = IR::RegOpnd::New(TyFloat64, this->m_func);
  7241. this->LoadFloatZero(zeroReg, instr);
  7242. // VMRS Rorig, FPSCR
  7243. // VMRS Rt, FPSCR
  7244. // BIC Rt, Rt, 0x800000
  7245. // ORR Rt, Rt, 0x400000
  7246. // VMSR FPSCR, Rt
  7247. IR::Opnd* regOrig = IR::RegOpnd::New(TyInt32, this->m_func);
  7248. IR::Opnd* reg = IR::RegOpnd::New(TyInt32, this->m_func);
  7249. instr->InsertBefore(
  7250. IR::Instr::New(Js::OpCode::VMRSR, regOrig, instr->m_func));
  7251. instr->InsertBefore(
  7252. IR::Instr::New(Js::OpCode::VMRSR, reg, instr->m_func));
  7253. instr->InsertBefore(
  7254. IR::Instr::New(Js::OpCode::BIC, reg, reg, IR::IntConstOpnd::New(0x800000, IRType::TyInt32, this->m_func), instr->m_func));
  7255. instr->InsertBefore(
  7256. IR::Instr::New(Js::OpCode::ORR, reg, reg, IR::IntConstOpnd::New(0x400000, IRType::TyInt32, this->m_func), instr->m_func));
  7257. IR::Instr* setFPSCRInstr = IR::Instr::New(Js::OpCode::VMSR, instr->m_func);
  7258. setFPSCRInstr->SetSrc1(reg);
  7259. instr->InsertBefore(setFPSCRInstr);
  7260. // VCVTRS32F64 floatreg, floatOpnd
  7261. IR::RegOpnd *floatReg = IR::RegOpnd::New(TyFloat32, this->m_func);
  7262. IR::Opnd * intOpnd = IR::RegOpnd::New(TyInt32, this->m_func);
  7263. instr->InsertBefore(
  7264. IR::Instr::New(Js::OpCode::VCVTRS32F64, floatReg, floatOpnd, instr->m_func));
  7265. // VMOVARMVFP intOpnd, floatReg
  7266. instr->InsertBefore(IR::Instr::New(Js::OpCode::VMOVARMVFP, intOpnd, floatReg, this->m_func));
  7267. // VMSR FPSCR, Rorig
  7268. IR::Instr* restoreFPSCRInstr = IR::Instr::New(Js::OpCode::VMSR, instr->m_func);
  7269. restoreFPSCRInstr->SetSrc1(regOrig);
  7270. instr->InsertBefore(restoreFPSCRInstr);
  7271. //negZero bailout
  7272. // TST intOpnd, intOpnd
  7273. // BNE checkOverflowLabel
  7274. this->m_lowerer->InsertTestBranch(intOpnd, intOpnd, Js::OpCode::BNE, checkOverflowLabel, instr);
  7275. instr->InsertBefore(checkNegZeroLabelHelper);
  7276. if(instr->ShouldCheckForNegativeZero())
  7277. {
  7278. IR::Opnd * isNegZero = IR::RegOpnd::New(TyInt32, this->m_func);
  7279. IR::Opnd * negOne = IR::MemRefOpnd::New((double*)&(Js::JavascriptNumber::k_NegOne), IRType::TyFloat64, this->m_func, IR::AddrOpndKindDynamicDoubleRef);
  7280. this->m_lowerer->InsertCompareBranch(floatOpnd, negOne, Js::OpCode::BrNotGe_A, doneLabel, instr);
  7281. this->m_lowerer->InsertCompareBranch(floatOpnd, zeroReg, Js::OpCode::BrNotGe_A, bailoutLabel, instr);
  7282. isNegZero = this->IsOpndNegZero(src, instr);
  7283. this->m_lowerer->InsertCompareBranch(isNegZero, IR::IntConstOpnd::New(0x00000000, IRType::TyInt32, this->m_func), Js::OpCode::BrNeq_A, bailoutLabel, instr);
  7284. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::B, doneLabel, instr->m_func));
  7285. }
  7286. instr->InsertBefore(checkOverflowLabel);
  7287. CheckOverflowOnFloatToInt32(instr, intOpnd, bailoutLabel, doneLabel);
  7288. IR::Opnd * dst = instr->UnlinkDst();
  7289. instr->InsertAfter(doneLabel);
  7290. if(!sharedBailout)
  7291. {
  7292. instr->InsertBefore(bailoutLabel);
  7293. }
  7294. this->m_lowerer->GenerateBailOut(instr);
  7295. // MOV dst, intOpnd
  7296. IR::Instr* movInstr = IR::Instr::New(Js::OpCode::MOV, dst, intOpnd, this->m_func);
  7297. doneLabel->InsertAfter(movInstr);
  7298. }
  7299. void
  7300. LowererMD::GenerateFastInlineBuiltInMathRound(IR::Instr* instr)
  7301. {
  7302. Assert(instr->GetDst()->IsInt32());
  7303. IR::LabelInstr * checkNegZeroLabelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  7304. IR::LabelInstr * checkOverflowLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  7305. IR::LabelInstr * doneLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  7306. // VMOV floatOpnd, src
  7307. IR::Opnd * src = instr->UnlinkSrc1();
  7308. IR::RegOpnd* floatOpnd = IR::RegOpnd::New(TyFloat64, this->m_func);
  7309. this->m_lowerer->InsertMove(floatOpnd, src, instr);
  7310. IR::LabelInstr * bailoutLabel;
  7311. bool sharedBailout = (instr->GetBailOutInfo()->bailOutInstr != instr) ? true : false;
  7312. if(sharedBailout)
  7313. {
  7314. bailoutLabel = instr->GetBailOutInfo()->bailOutInstr->AsLabelInstr();
  7315. }
  7316. else
  7317. {
  7318. bailoutLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  7319. }
  7320. // NaN check
  7321. IR::Instr *instrCmp = IR::Instr::New(Js::OpCode::VCMPF64, this->m_func);
  7322. instrCmp->SetSrc1(floatOpnd);
  7323. instrCmp->SetSrc2(floatOpnd);
  7324. instr->InsertBefore(instrCmp);
  7325. LegalizeMD::LegalizeInstr(instrCmp, false);
  7326. // VMRS APSR, FPSCR
  7327. // BVS $bailoutLabel
  7328. instr->InsertBefore(IR::Instr::New(Js::OpCode::VMRS, this->m_func));
  7329. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::BVS, bailoutLabel, this->m_func));
  7330. IR::Opnd * zeroReg = IR::RegOpnd::New(TyFloat64, this->m_func);
  7331. this->LoadFloatZero(zeroReg, instr);
  7332. // Add 0.5
  7333. IR::Opnd * pointFive = IR::MemRefOpnd::New((double*)&(Js::JavascriptNumber::k_PointFive), IRType::TyFloat64, this->m_func, IR::AddrOpndKindDynamicDoubleRef);
  7334. this->m_lowerer->InsertAdd(false, floatOpnd, floatOpnd, pointFive, instr);
  7335. // VMRS Rorig, FPSCR
  7336. // VMRS Rt, FPSCR
  7337. // BIC Rt, Rt, 0x400000
  7338. // ORR Rt, Rt, 0x800000
  7339. // VMSR FPSCR, Rt
  7340. IR::Opnd* regOrig = IR::RegOpnd::New(TyInt32, this->m_func);
  7341. IR::Opnd* reg = IR::RegOpnd::New(TyInt32, this->m_func);
  7342. instr->InsertBefore(
  7343. IR::Instr::New(Js::OpCode::VMRSR, regOrig, instr->m_func));
  7344. instr->InsertBefore(
  7345. IR::Instr::New(Js::OpCode::VMRSR, reg, instr->m_func));
  7346. instr->InsertBefore(
  7347. IR::Instr::New(Js::OpCode::BIC, reg, reg, IR::IntConstOpnd::New(0x400000, IRType::TyInt32, this->m_func), instr->m_func));
  7348. instr->InsertBefore(
  7349. IR::Instr::New(Js::OpCode::ORR, reg, reg, IR::IntConstOpnd::New(0x800000, IRType::TyInt32, this->m_func), instr->m_func));
  7350. IR::Instr* setFPSCRInstr = IR::Instr::New(Js::OpCode::VMSR, instr->m_func);
  7351. setFPSCRInstr->SetSrc1(reg);
  7352. instr->InsertBefore(setFPSCRInstr);
  7353. // VCVTRS32F64 floatreg, floatOpnd
  7354. IR::RegOpnd *floatReg = IR::RegOpnd::New(TyFloat32, this->m_func);
  7355. IR::Opnd * intOpnd = IR::RegOpnd::New(TyInt32, this->m_func);
  7356. instr->InsertBefore(
  7357. IR::Instr::New(Js::OpCode::VCVTRS32F64, floatReg, floatOpnd, instr->m_func));
  7358. // VMOVARMVFP intOpnd, floatReg
  7359. instr->InsertBefore(IR::Instr::New(Js::OpCode::VMOVARMVFP, intOpnd, floatReg, this->m_func));
  7360. // VMSR FPSCR, Rorig
  7361. IR::Instr* restoreFPSCRInstr = IR::Instr::New(Js::OpCode::VMSR, instr->m_func);
  7362. restoreFPSCRInstr->SetSrc1(regOrig);
  7363. instr->InsertBefore(restoreFPSCRInstr);
  7364. //negZero bailout
  7365. // TST intOpnd, intOpnd
  7366. // BNE checkOverflowLabel
  7367. this->m_lowerer->InsertTestBranch(intOpnd, intOpnd, Js::OpCode::BNE, checkOverflowLabel, instr);
  7368. instr->InsertBefore(checkNegZeroLabelHelper);
  7369. if(instr->ShouldCheckForNegativeZero())
  7370. {
  7371. IR::Opnd * isNegZero = IR::RegOpnd::New(TyInt32, this->m_func);
  7372. IR::Opnd * negPointFive = IR::MemRefOpnd::New((double*)&(Js::JavascriptNumber::k_NegPointFive), IRType::TyFloat64, this->m_func, IR::AddrOpndKindDynamicDoubleRef);
  7373. this->m_lowerer->InsertCompareBranch(src, negPointFive, Js::OpCode::BrNotGe_A, doneLabel, instr);
  7374. this->m_lowerer->InsertCompareBranch(src, zeroReg, Js::OpCode::BrNotGe_A, bailoutLabel, instr);
  7375. isNegZero = this->IsOpndNegZero(src, instr);
  7376. this->m_lowerer->InsertCompareBranch(isNegZero, IR::IntConstOpnd::New(0x00000000, IRType::TyInt32, this->m_func), Js::OpCode::BrNeq_A, bailoutLabel, instr);
  7377. instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::B, doneLabel, instr->m_func));
  7378. }
  7379. instr->InsertBefore(checkOverflowLabel);
  7380. CheckOverflowOnFloatToInt32(instr, intOpnd, bailoutLabel, doneLabel);
  7381. IR::Opnd * dst = instr->UnlinkDst();
  7382. instr->InsertAfter(doneLabel);
  7383. if(!sharedBailout)
  7384. {
  7385. instr->InsertBefore(bailoutLabel);
  7386. }
  7387. this->m_lowerer->GenerateBailOut(instr);
  7388. // MOV dst, intOpnd
  7389. IR::Instr* movInstr = IR::Instr::New(Js::OpCode::MOV, dst, intOpnd, this->m_func);
  7390. doneLabel->InsertAfter(movInstr);
  7391. }
  7392. IR::Opnd* LowererMD::IsOpndNegZero(IR::Opnd* opnd, IR::Instr* instr)
  7393. {
  7394. IR::Opnd * isNegZero = IR::RegOpnd::New(TyInt32, this->m_func);
  7395. LoadDoubleHelperArgument(instr, opnd);
  7396. IR::Instr * helperCallInstr = IR::Instr::New(Js::OpCode::Call, isNegZero, this->m_func);
  7397. instr->InsertBefore(helperCallInstr);
  7398. this->ChangeToHelperCall(helperCallInstr, IR::HelperIsNegZero);
  7399. return isNegZero;
  7400. }
  7401. IR::Instr *
  7402. LowererMD::LowerToFloat(IR::Instr *instr)
  7403. {
  7404. switch (instr->m_opcode)
  7405. {
  7406. case Js::OpCode::Add_A:
  7407. instr->m_opcode = Js::OpCode::VADDF64;
  7408. break;
  7409. case Js::OpCode::Sub_A:
  7410. instr->m_opcode = Js::OpCode::VSUBF64;
  7411. break;
  7412. case Js::OpCode::Mul_A:
  7413. instr->m_opcode = Js::OpCode::VMULF64;
  7414. break;
  7415. case Js::OpCode::Div_A:
  7416. instr->m_opcode = Js::OpCode::VDIVF64;
  7417. break;
  7418. case Js::OpCode::Neg_A:
  7419. instr->m_opcode = Js::OpCode::VNEGF64;
  7420. break;
  7421. case Js::OpCode::BrEq_A:
  7422. case Js::OpCode::BrNeq_A:
  7423. case Js::OpCode::BrSrEq_A:
  7424. case Js::OpCode::BrSrNeq_A:
  7425. case Js::OpCode::BrGt_A:
  7426. case Js::OpCode::BrGe_A:
  7427. case Js::OpCode::BrLt_A:
  7428. case Js::OpCode::BrLe_A:
  7429. case Js::OpCode::BrNotEq_A:
  7430. case Js::OpCode::BrNotNeq_A:
  7431. case Js::OpCode::BrSrNotEq_A:
  7432. case Js::OpCode::BrSrNotNeq_A:
  7433. case Js::OpCode::BrNotGt_A:
  7434. case Js::OpCode::BrNotGe_A:
  7435. case Js::OpCode::BrNotLt_A:
  7436. case Js::OpCode::BrNotLe_A:
  7437. return this->LowerFloatCondBranch(instr->AsBranchInstr());
  7438. default:
  7439. Assume(UNREACHED);
  7440. }
  7441. LegalizeMD::LegalizeInstr(instr, false);
  7442. return instr;
  7443. }
  7444. IR::BranchInstr *
  7445. LowererMD::LowerFloatCondBranch(IR::BranchInstr *instrBranch, bool ignoreNaN)
  7446. {
  7447. IR::Instr *instr;
  7448. Js::OpCode brOpcode = Js::OpCode::InvalidOpCode;
  7449. bool addNaNCheck = false;
  7450. Func * func = instrBranch->m_func;
  7451. IR::Opnd *src1 = instrBranch->UnlinkSrc1();
  7452. IR::Opnd *src2 = instrBranch->UnlinkSrc2();
  7453. IR::Instr *instrCmp = IR::Instr::New(Js::OpCode::VCMPF64, func);
  7454. instrCmp->SetSrc1(src1);
  7455. instrCmp->SetSrc2(src2);
  7456. instrBranch->InsertBefore(instrCmp);
  7457. LegalizeMD::LegalizeInstr(instrCmp, false);
  7458. instrBranch->InsertBefore(IR::Instr::New(Js::OpCode::VMRS, func));
  7459. switch (instrBranch->m_opcode)
  7460. {
  7461. case Js::OpCode::BrSrEq_A:
  7462. case Js::OpCode::BrEq_A:
  7463. case Js::OpCode::BrNotNeq_A:
  7464. case Js::OpCode::BrSrNotNeq_A:
  7465. brOpcode = Js::OpCode::BEQ;
  7466. break;
  7467. case Js::OpCode::BrNeq_A:
  7468. case Js::OpCode::BrSrNeq_A:
  7469. case Js::OpCode::BrSrNotEq_A:
  7470. case Js::OpCode::BrNotEq_A:
  7471. brOpcode = Js::OpCode::BNE;
  7472. addNaNCheck = !ignoreNaN; //Special check for BNE as it is set when the operands are unordered (NaN).
  7473. break;
  7474. case Js::OpCode::BrLe_A:
  7475. brOpcode = Js::OpCode::BLS; //Can't use BLE as it is set when the operands are unordered (NaN).
  7476. break;
  7477. case Js::OpCode::BrLt_A:
  7478. brOpcode = Js::OpCode::BCC; //Can't use BLT as is set when the operands are unordered (NaN).
  7479. break;
  7480. case Js::OpCode::BrGe_A:
  7481. brOpcode = Js::OpCode::BGE;
  7482. break;
  7483. case Js::OpCode::BrGt_A:
  7484. brOpcode = Js::OpCode::BGT;
  7485. break;
  7486. case Js::OpCode::BrNotLe_A:
  7487. brOpcode = Js::OpCode::BHI;
  7488. break;
  7489. case Js::OpCode::BrNotLt_A:
  7490. brOpcode = Js::OpCode::BPL;
  7491. break;
  7492. case Js::OpCode::BrNotGe_A:
  7493. brOpcode = Js::OpCode::BLT;
  7494. break;
  7495. case Js::OpCode::BrNotGt_A:
  7496. brOpcode = Js::OpCode::BLE;
  7497. break;
  7498. default:
  7499. Assert(false);
  7500. break;
  7501. }
  7502. if (addNaNCheck)
  7503. {
  7504. instr = IR::BranchInstr::New(Js::OpCode::BVS, instrBranch->GetTarget(), func);
  7505. instrBranch->InsertBefore(instr);
  7506. }
  7507. instr = IR::BranchInstr::New(brOpcode, instrBranch->GetTarget(), func);
  7508. instrBranch->InsertBefore(instr);
  7509. instrBranch->Remove();
  7510. return instr->AsBranchInstr();
  7511. }
  7512. void
  7513. LowererMD::EmitIntToFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert)
  7514. {
  7515. IR::Instr *instr;
  7516. IR::RegOpnd *floatReg = IR::RegOpnd::New(TyFloat64, this->m_func);
  7517. Assert(dst->IsRegOpnd() && dst->IsFloat64());
  7518. Assert(src->IsRegOpnd() && src->IsInt32());
  7519. instr = IR::Instr::New(Js::OpCode::VMOVARMVFP, floatReg, src, this->m_func);
  7520. instrInsert->InsertBefore(instr);
  7521. // Convert to Float
  7522. instr = IR::Instr::New(Js::OpCode::VCVTF64S32, dst, floatReg, this->m_func);
  7523. instrInsert->InsertBefore(instr);
  7524. }
  7525. void
  7526. LowererMD::EmitUIntToFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert)
  7527. {
  7528. IR::Instr *instr;
  7529. IR::RegOpnd *floatReg = IR::RegOpnd::New(TyFloat64, this->m_func);
  7530. Assert(dst->IsRegOpnd() && dst->IsFloat64());
  7531. Assert(src->IsRegOpnd() && src->IsInt32());
  7532. instr = IR::Instr::New(Js::OpCode::VMOVARMVFP, floatReg, src, this->m_func);
  7533. instrInsert->InsertBefore(instr);
  7534. // Convert to Float
  7535. instr = IR::Instr::New(Js::OpCode::VCVTF64U32, dst, floatReg, this->m_func);
  7536. instrInsert->InsertBefore(instr);
  7537. }
  7538. void LowererMD::ConvertFloatToInt32(IR::Opnd* intOpnd, IR::Opnd* floatOpnd, IR::LabelInstr * labelHelper, IR::LabelInstr * labelDone, IR::Instr * instrInsert)
  7539. {
  7540. Assert(floatOpnd->IsFloat64());
  7541. Assert(intOpnd->IsInt32());
  7542. IR::RegOpnd *floatReg = IR::RegOpnd::New(TyFloat32, this->m_func);
  7543. // VCVTS32F64 dst.i32, src.f64
  7544. // Convert to int
  7545. IR::Instr * instr = IR::Instr::New(Js::OpCode::VCVTS32F64, floatReg, floatOpnd, this->m_func);
  7546. instrInsert->InsertBefore(instr);
  7547. Legalize(instr);
  7548. //Move to integer reg
  7549. instr = IR::Instr::New(Js::OpCode::VMOVARMVFP, intOpnd, floatReg, this->m_func);
  7550. instrInsert->InsertBefore(instr);
  7551. Legalize(instr);
  7552. this->CheckOverflowOnFloatToInt32(instrInsert, intOpnd, labelHelper, labelDone);
  7553. }
  7554. void
  7555. LowererMD::CheckOverflowOnFloatToInt32(IR::Instr* instrInsert, IR::Opnd* intOpnd, IR::LabelInstr * labelHelper, IR::LabelInstr * labelDone)
  7556. {
  7557. // CMP intOpnd, 0x80000000 -- Check for overflow
  7558. IR::Instr* instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  7559. instr->SetSrc1(intOpnd);
  7560. instr->SetSrc2(IR::IntConstOpnd::New(0x80000000, TyInt32, this->m_func, true));
  7561. instrInsert->InsertBefore(instr);
  7562. LegalizeMD::LegalizeInstr(instr, false);
  7563. // BEQ $helper
  7564. instr = IR::BranchInstr::New(Js::OpCode::BEQ, labelHelper, this->m_func);
  7565. instrInsert->InsertBefore(instr);
  7566. // CMP intOpnd, 0x7fffffff -- Check for overflow
  7567. IR::RegOpnd *regOpnd= IR::RegOpnd::New(TyMachReg, this->m_func);
  7568. instr = IR::Instr::New(Js::OpCode::MVN,
  7569. regOpnd,
  7570. IR::IntConstOpnd::New(0x80000000, TyInt32, this->m_func, true),
  7571. this->m_func);
  7572. instrInsert->InsertBefore(instr);
  7573. LegalizeMD::LegalizeInstr(instr, false);
  7574. instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  7575. instr->SetSrc1(intOpnd);
  7576. instr->SetSrc2(regOpnd);
  7577. instrInsert->InsertBefore(instr);
  7578. LegalizeMD::LegalizeInstr(instr, false);
  7579. // BNE $done
  7580. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelDone, this->m_func);
  7581. instrInsert->InsertBefore(instr);
  7582. }
  7583. void
  7584. LowererMD::EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert)
  7585. {
  7586. IR::LabelInstr *labelDone = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
  7587. IR::LabelInstr *labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
  7588. IR::Instr *instr;
  7589. ConvertFloatToInt32(dst, src, labelHelper, labelDone, instrInsert);
  7590. // $Helper
  7591. instrInsert->InsertBefore(labelHelper);
  7592. // dst = ToInt32Core(src);
  7593. LoadDoubleHelperArgument(instrInsert, src);
  7594. instr = IR::Instr::New(Js::OpCode::Call, dst, this->m_func);
  7595. instrInsert->InsertBefore(instr);
  7596. this->ChangeToHelperCall(instr, IR::HelperConv_ToInt32Core);
  7597. // $Done
  7598. instrInsert->InsertBefore(labelDone);
  7599. }
  7600. IR::Instr *
  7601. LowererMD::InsertConvertFloat64ToInt32(const RoundMode roundMode, IR::Opnd *const dst, IR::Opnd *const src, IR::Instr *const insertBeforeInstr)
  7602. {
  7603. Assert(dst);
  7604. Assert(dst->IsInt32());
  7605. Assert(src);
  7606. Assert(src->IsFloat64());
  7607. Assert(insertBeforeInstr);
  7608. // The caller is expected to check for overflow. To have that work be done automatically, use LowererMD::EmitFloatToInt.
  7609. Func *const func = insertBeforeInstr->m_func;
  7610. IR::AutoReuseOpnd autoReuseSrcPlusHalf;
  7611. IR::Instr *instr = nullptr;
  7612. switch (roundMode)
  7613. {
  7614. case RoundModeTowardInteger:
  7615. {
  7616. // Conversion with rounding towards nearest integer is not supported by the architecture. Add 0.5 and do a
  7617. // round-toward-zero conversion instead.
  7618. IR::RegOpnd *const srcPlusHalf = IR::RegOpnd::New(TyFloat64, func);
  7619. autoReuseSrcPlusHalf.Initialize(srcPlusHalf, func);
  7620. Lowerer::InsertAdd(
  7621. false /* needFlags */,
  7622. srcPlusHalf,
  7623. src,
  7624. IR::MemRefOpnd::New((double*)&(Js::JavascriptNumber::k_PointFive), TyFloat64, func,
  7625. IR::AddrOpndKindDynamicDoubleRef),
  7626. insertBeforeInstr);
  7627. instr = IR::Instr::New(LowererMD::MDConvertFloat64ToInt32Opcode(RoundModeTowardZero), dst, srcPlusHalf, func);
  7628. insertBeforeInstr->InsertBefore(instr);
  7629. LowererMD::Legalize(instr);
  7630. return instr;
  7631. }
  7632. case RoundModeHalfToEven:
  7633. {
  7634. // On ARM we need to set the rounding mode bits of the FPSCR.
  7635. // These are bits 22 and 23 and we need them to be off for "Round to Nearest (RN) mode"
  7636. // After doing the convert (via VCVTRS32F64) we need to restore the original FPSCR state.
  7637. // VMRS Rorig, FPSCR
  7638. // VMRS Rt, FPSCR
  7639. // BIC Rt, Rt, 0xC00000
  7640. // VMSR FPSCR, Rt
  7641. IR::Opnd* regOrig = IR::RegOpnd::New(TyInt32, func);
  7642. IR::Opnd* reg = IR::RegOpnd::New(TyInt32, func);
  7643. insertBeforeInstr->InsertBefore(
  7644. IR::Instr::New(Js::OpCode::VMRSR, regOrig, func));
  7645. insertBeforeInstr->InsertBefore(
  7646. IR::Instr::New(Js::OpCode::VMRSR, reg, func));
  7647. insertBeforeInstr->InsertBefore(
  7648. IR::Instr::New(Js::OpCode::BIC, reg, reg, IR::IntConstOpnd::New(0xC00000, IRType::TyInt32, func), func));
  7649. IR::Instr* setFPSCRInstr = IR::Instr::New(Js::OpCode::VMSR, func);
  7650. setFPSCRInstr->SetSrc1(reg);
  7651. insertBeforeInstr->InsertBefore(setFPSCRInstr);
  7652. // VCVTRS32F64 floatreg, regSrc
  7653. IR::RegOpnd *floatReg = IR::RegOpnd::New(TyFloat32, func);
  7654. insertBeforeInstr->InsertBefore(
  7655. IR::Instr::New(LowererMD::MDConvertFloat64ToInt32Opcode(RoundModeHalfToEven), floatReg, src, func));
  7656. // VMOVARMVFP regOpnd, floatReg
  7657. insertBeforeInstr->InsertBefore(IR::Instr::New(Js::OpCode::VMOVARMVFP, dst, floatReg, func));
  7658. // VMSR FPSCR, Rorig
  7659. IR::Instr* restoreFPSCRInstr = IR::Instr::New(Js::OpCode::VMSR, func);
  7660. restoreFPSCRInstr->SetSrc1(regOrig);
  7661. insertBeforeInstr->InsertBefore(restoreFPSCRInstr);
  7662. return restoreFPSCRInstr;
  7663. }
  7664. default:
  7665. AssertMsg(0, "RoundMode not supported.");
  7666. return nullptr;
  7667. }
  7668. }
  7669. IR::Instr *
  7670. LowererMD::LoadFloatZero(IR::Opnd * opndDst, IR::Instr * instrInsert)
  7671. {
  7672. Assert(opndDst->GetType() == TyFloat64);
  7673. IR::Opnd * zero = IR::MemRefOpnd::New((double*)&(Js::JavascriptNumber::k_Zero), TyFloat64, instrInsert->m_func, IR::AddrOpndKindDynamicDoubleRef);
  7674. return Lowerer::InsertMove(opndDst, zero, instrInsert);
  7675. }
  7676. IR::Instr *
  7677. LowererMD::LoadFloatValue(IR::Opnd * opndDst, double value, IR::Instr * instrInsert)
  7678. {
  7679. // Floating point zero is a common value to load. Let's use a single memory location instead of allocating new memory for each.
  7680. const bool isFloatZero = value == 0.0 && !Js::JavascriptNumber::IsNegZero(value); // (-0.0 == 0.0) yields true
  7681. if (isFloatZero)
  7682. {
  7683. return LowererMD::LoadFloatZero(opndDst, instrInsert);
  7684. }
  7685. double *pValue = NativeCodeDataNew(instrInsert->m_func->GetNativeCodeDataAllocator(), double, value);
  7686. IR::Opnd * opnd = IR::MemRefOpnd::New((void*)pValue, TyMachDouble, instrInsert->m_func);
  7687. IR::Instr * instr = IR::Instr::New(Js::OpCode::VLDR, opndDst, opnd, instrInsert->m_func);
  7688. instrInsert->InsertBefore(instr);
  7689. LegalizeMD::LegalizeInstr(instr,false);
  7690. return instr;
  7691. }
  7692. void LowererMD::GenerateFloatTest(IR::RegOpnd * opndSrc, IR::Instr * insertInstr, IR::LabelInstr* labelHelper, const bool checkForNullInLoopBody)
  7693. {
  7694. if (opndSrc->GetValueType().IsFloat())
  7695. {
  7696. return;
  7697. }
  7698. if(checkForNullInLoopBody && m_func->IsLoopBody())
  7699. {
  7700. // It's possible that the value was determined dead by the jitted function and was not restored. The jitted loop
  7701. // body may not realize that it's dead and may try to use it. Check for null in loop bodies.
  7702. // test src1, src1
  7703. // jz $helper (bail out)
  7704. m_lowerer->InsertCompareBranch(
  7705. opndSrc,
  7706. IR::AddrOpnd::NewNull(m_func),
  7707. Js::OpCode::BrEq_A,
  7708. labelHelper,
  7709. insertInstr);
  7710. }
  7711. IR::RegOpnd *vt = IR::RegOpnd::New(TyMachPtr, this->m_func);
  7712. IR::Opnd* opnd = IR::IndirOpnd::New(opndSrc, (int32)0, TyMachPtr, this->m_func);
  7713. LowererMD::CreateAssign(vt, opnd, insertInstr);
  7714. // CMP [number], JavascriptNumber::vtable
  7715. IR::Instr* instr = IR::Instr::New(Js::OpCode::CMP, this->m_func);
  7716. instr->SetSrc1(vt);
  7717. instr->SetSrc2(m_lowerer->LoadVTableValueOpnd(insertInstr, VTableValue::VtableJavascriptNumber));
  7718. insertInstr->InsertBefore(instr);
  7719. LegalizeMD::LegalizeInstr(instr,false);
  7720. // BNE $helper
  7721. instr = IR::BranchInstr::New(Js::OpCode::BNE, labelHelper, this->m_func);
  7722. insertInstr->InsertBefore(instr);
  7723. }
  7724. void LowererMD::LoadFloatValue(IR::RegOpnd * javascriptNumber, IR::RegOpnd * opndFloat, IR::LabelInstr * labelHelper, IR::Instr * instrInsert, const bool checkForNullInLoopBody)
  7725. {
  7726. IR::Instr* instr;
  7727. IR::Opnd* opnd;
  7728. // Make sure it is float
  7729. this->GenerateFloatTest(javascriptNumber, instrInsert, labelHelper, checkForNullInLoopBody);
  7730. // VLDR opndFloat, [number + offsetof(value)]
  7731. opnd = IR::IndirOpnd::New(javascriptNumber, Js::JavascriptNumber::GetValueOffset(), TyMachDouble, this->m_func);
  7732. instr = IR::Instr::New(Js::OpCode::VLDR, opndFloat, opnd, this->m_func);
  7733. instrInsert->InsertBefore(instr);
  7734. }
  7735. template <bool verify>
  7736. void
  7737. LowererMD::Legalize(IR::Instr *const instr, bool fPostRegAlloc)
  7738. {
  7739. Func *const func = instr->m_func;
  7740. if(instr->m_opcode == Js::OpCode::VCVTS32F64 && instr->GetDst()->IsInt32())
  7741. {
  7742. if (verify)
  7743. {
  7744. AssertMsg(false, "Missing legalization");
  7745. return;
  7746. }
  7747. // This needs to be split into two steps
  7748. IR::RegOpnd *const float32Reg = IR::RegOpnd::New(TyFloat32, func);
  7749. const IR::AutoReuseOpnd autoReuseFloat32Reg(float32Reg, func);
  7750. IR::Instr *const newInstr = IR::Instr::New(Js::OpCode::VCVTS32F64, float32Reg, instr->GetSrc1(), func);
  7751. instr->InsertBefore(newInstr);
  7752. LegalizeMD::LegalizeInstr(newInstr, false);
  7753. instr->m_opcode = Js::OpCode::VMOVARMVFP;
  7754. instr->ReplaceSrc1(float32Reg);
  7755. }
  7756. if (verify)
  7757. {
  7758. // NYI for the rest of legalization
  7759. return;
  7760. }
  7761. LegalizeMD::LegalizeInstr(instr, fPostRegAlloc);
  7762. }
  7763. template void LowererMD::Legalize<false>(IR::Instr *const instr, bool fPostRegalloc);
  7764. #if DBG
  7765. template void LowererMD::Legalize<true>(IR::Instr *const instr, bool fPostRegalloc);
  7766. #endif
  7767. void
  7768. LowererMD::FinalLower()
  7769. {
  7770. NoRecoverMemoryArenaAllocator tempAlloc(L"BE-ARMFinalLower", m_func->m_alloc->GetPageAllocator(), Js::Throw::OutOfMemory);
  7771. EncodeReloc *pRelocList = nullptr;
  7772. uint32 instrOffset = 0;
  7773. FOREACH_INSTR_BACKWARD_EDITING_IN_RANGE(instr, instrPrev, this->m_func->m_tailInstr, this->m_func->m_headInstr)
  7774. {
  7775. if (instr->IsLowered() == false)
  7776. {
  7777. if (instr->IsLabelInstr())
  7778. {
  7779. //This is not the real set, Real offset gets set in encoder.
  7780. IR::LabelInstr *labelInstr = instr->AsLabelInstr();
  7781. labelInstr->SetOffset(instrOffset);
  7782. }
  7783. switch (instr->m_opcode)
  7784. {
  7785. case Js::OpCode::Leave:
  7786. Assert(this->m_func->DoOptimizeTryCatch() && !this->m_func->IsLoopBodyInTry());
  7787. instrPrev = this->LowerLeave(instr, instr->AsBranchInstr()->GetTarget(), true /*fromFinalLower*/);
  7788. break;
  7789. }
  7790. }
  7791. else
  7792. {
  7793. //We are conservative here, assume each instruction take 4 bytes
  7794. instrOffset = instrOffset + MachMaxInstrSize;
  7795. if (instr->IsBranchInstr())
  7796. {
  7797. IR::BranchInstr *branchInstr = instr->AsBranchInstr();
  7798. if (branchInstr->GetTarget() && !LowererMD::IsUnconditionalBranch(branchInstr)) //Ignore BX register based branches & B
  7799. {
  7800. uint32 targetOffset = branchInstr->GetTarget()->GetOffset();
  7801. if (targetOffset != 0)
  7802. {
  7803. // this is backward reference
  7804. if (LegalizeMD::LegalizeDirectBranch(branchInstr, instrOffset))
  7805. {
  7806. //There might be an instruction inserted for legalizing conditional branch
  7807. instrOffset = instrOffset + MachMaxInstrSize;
  7808. }
  7809. }
  7810. else
  7811. {
  7812. EncodeReloc::New(&pRelocList, RelocTypeBranch20, (BYTE*)instrOffset, branchInstr, &tempAlloc);
  7813. //Assume this is a forward long branch, we shall fix up after complete pass, be conservative here
  7814. instrOffset = instrOffset + MachMaxInstrSize;
  7815. }
  7816. }
  7817. }
  7818. else if (LowererMD::IsAssign(instr) || instr->m_opcode == Js::OpCode::LEA || instr->m_opcode == Js::OpCode::LDARGOUTSZ || instr->m_opcode == Js::OpCode::REM)
  7819. {
  7820. // Cleanup spill code
  7821. // INSTR_BACKWARD_EDITING_IN_RANGE implies that next loop iteration will use instrPrev (instr->m_prev computed before entering current loop iteration).
  7822. IR::Instr* instrNext = instr->m_next;
  7823. bool canExpand = this->FinalLowerAssign(instr);
  7824. if (canExpand)
  7825. {
  7826. uint32 expandedInstrCount = 0; // The number of instrs the LDIMM expands into.
  7827. FOREACH_INSTR_IN_RANGE(instr, instrPrev->m_next, instrNext)
  7828. {
  7829. ++expandedInstrCount;
  7830. }
  7831. NEXT_INSTR_IN_RANGE;
  7832. Assert(expandedInstrCount > 0);
  7833. // Adjust the offset for expanded instrs.
  7834. instrOffset += (expandedInstrCount - 1) * MachMaxInstrSize; // We already accounted for one MachMaxInstrSize.
  7835. }
  7836. }
  7837. }
  7838. } NEXT_INSTR_BACKWARD_EDITING_IN_RANGE;
  7839. //Fixup all the forward branches
  7840. for (EncodeReloc *reloc = pRelocList; reloc; reloc = reloc->m_next)
  7841. {
  7842. AssertMsg((uint32)reloc->m_consumerOffset < reloc->m_relocInstr->AsBranchInstr()->GetTarget()->GetOffset(), "Only forward branches require fixup");
  7843. LegalizeMD::LegalizeDirectBranch(reloc->m_relocInstr->AsBranchInstr(), (uint32)reloc->m_consumerOffset);
  7844. }
  7845. return;
  7846. }
  7847. // Returns true, if and only if the assign may expand into multiple instrs.
  7848. bool
  7849. LowererMD::FinalLowerAssign(IR::Instr * instr)
  7850. {
  7851. if (instr->m_opcode == Js::OpCode::LDIMM)
  7852. {
  7853. LegalizeMD::LegalizeInstr(instr, true);
  7854. // LDIMM can expand into MOV/MOVT when the immediate is more than 16 bytes,
  7855. // it can also expand into multiple different no-op (normally MOV) instrs when we obfuscate it, which is randomly.
  7856. return true;
  7857. }
  7858. else if (EncoderMD::IsLoad(instr) || instr->m_opcode == Js::OpCode::LEA)
  7859. {
  7860. Assert(instr->GetDst()->IsRegOpnd());
  7861. if (!instr->GetSrc1()->IsRegOpnd())
  7862. {
  7863. LegalizeMD::LegalizeSrc(instr, instr->GetSrc1(), 1, true);
  7864. return true;
  7865. }
  7866. instr->m_opcode = (instr->GetSrc1()->GetType() == TyMachDouble) ? Js::OpCode::VMOV : Js::OpCode::MOV;
  7867. }
  7868. else if (EncoderMD::IsStore(instr))
  7869. {
  7870. Assert(instr->GetSrc1()->IsRegOpnd());
  7871. if (!instr->GetDst()->IsRegOpnd())
  7872. {
  7873. LegalizeMD::LegalizeDst(instr, true);
  7874. return true;
  7875. }
  7876. instr->m_opcode = (instr->GetDst()->GetType() == TyMachDouble) ? Js::OpCode::VMOV : Js::OpCode::MOV;
  7877. }
  7878. else if (instr->m_opcode == Js::OpCode::LDARGOUTSZ)
  7879. {
  7880. Assert(instr->GetDst()->IsRegOpnd());
  7881. Assert((instr->GetSrc1() == nullptr) && (instr->GetSrc2() == nullptr));
  7882. // dst = LDARGOUTSZ
  7883. // This loads the function's arg out area size into the dst operand. We need a pseudo-op,
  7884. // because we generate the instruction during Lower but don't yet know the value of the constant it needs
  7885. // to load. Change it to the appropriate LDIMM here.
  7886. uint32 argOutSize = UInt32Math::Mul(this->m_func->m_argSlotsForFunctionsCalled, MachRegInt, Js::Throw::OutOfMemory);
  7887. instr->SetSrc1(IR::IntConstOpnd::New(argOutSize, TyMachReg, this->m_func));
  7888. instr->m_opcode = Js::OpCode::LDIMM;
  7889. LegalizeMD::LegalizeInstr(instr, true);
  7890. return true;
  7891. }
  7892. else if (instr->m_opcode == Js::OpCode::REM)
  7893. {
  7894. IR::Opnd* dst = instr->GetDst();
  7895. IR::Opnd* src1 = instr->GetSrc1();
  7896. IR::Opnd* src2 = instr->GetSrc2();
  7897. Assert(src1->IsRegOpnd() && src1->AsRegOpnd()->GetReg() != RegR12);
  7898. Assert(src2->IsRegOpnd() && src2->AsRegOpnd()->GetReg() != RegR12);
  7899. //r12 = SDIV src1, src2
  7900. IR::RegOpnd *regR12 = IR::RegOpnd::New(nullptr, RegR12, TyMachReg, instr->m_func);
  7901. IR::Instr *insertInstr = IR::Instr::New(Js::OpCode::SDIV, regR12, src1, src2, instr->m_func);
  7902. instr->InsertBefore(insertInstr);
  7903. // dst = MLS (r12,) src2, src1
  7904. insertInstr = IR::Instr::New(Js::OpCode::MLS, dst, src2, src1, instr->m_func);
  7905. instr->InsertBefore(insertInstr);
  7906. instr->Remove();
  7907. return true;
  7908. }
  7909. return false;
  7910. }
  7911. IR::Opnd *
  7912. LowererMD::GenerateArgOutForStackArgs(IR::Instr* callInstr, IR::Instr* stackArgsInstr)
  7913. {
  7914. return this->m_lowerer->GenerateArgOutForStackArgs(callInstr, stackArgsInstr);
  7915. }
  7916. IR::Instr *
  7917. LowererMD::LowerDivI4AndBailOnReminder(IR::Instr * instr, IR::LabelInstr * bailOutLabel)
  7918. {
  7919. // result = SDIV numerator, denominator
  7920. // mulResult = MUL result, denominator
  7921. // CMP mulResult, numerator
  7922. // BNE bailout
  7923. // <Caller insert more checks here>
  7924. // dst = MOV result <-- insertBeforeInstr
  7925. instr->m_opcode = Js::OpCode::SDIV;
  7926. // delay assigning to the final dst.
  7927. IR::Instr * sinkedInstr = instr->SinkDst(Js::OpCode::MOV);
  7928. LegalizeMD::LegalizeInstr(instr, false);
  7929. LegalizeMD::LegalizeInstr(sinkedInstr, false);
  7930. IR::Opnd * resultOpnd = instr->GetDst();
  7931. IR::Opnd * numerator = instr->GetSrc1();
  7932. IR::Opnd * denominatorOpnd = instr->GetSrc2();
  7933. // Insert all check before the assignment to the actual
  7934. IR::Instr * insertBeforeInstr = instr->m_next;
  7935. // Jump to bailout if the reminder is not 0 (or the divResult * denominator is not same as the numerator)
  7936. IR::RegOpnd * mulResult = IR::RegOpnd::New(TyInt32, m_func);
  7937. IR::Instr * mulInstr = IR::Instr::New(Js::OpCode::MUL, mulResult, resultOpnd, denominatorOpnd, m_func);
  7938. insertBeforeInstr->InsertBefore(mulInstr);
  7939. LegalizeMD::LegalizeInstr(mulInstr, false);
  7940. this->m_lowerer->InsertCompareBranch(mulResult, numerator, Js::OpCode::BrNeq_A, bailOutLabel, insertBeforeInstr);
  7941. return insertBeforeInstr;
  7942. }
  7943. void
  7944. LowererMD::LowerInlineSpreadArgOutLoop(IR::Instr *callInstr, IR::RegOpnd *indexOpnd, IR::RegOpnd *arrayElementsStartOpnd)
  7945. {
  7946. this->m_lowerer->LowerInlineSpreadArgOutLoopUsingRegisters(callInstr, indexOpnd, arrayElementsStartOpnd);
  7947. }