123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307 |
- /*
- +----------------------------------------------------------------------+
- | Zend JIT |
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | https://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
- */
- static zend_op_array dummy_op_array;
- static zend_jit_trace_info *zend_jit_traces = NULL;
- static const void **zend_jit_exit_groups = NULL;
- #define ZEND_JIT_COUNTER_NUM zend_jit_traces[0].root
- #define ZEND_JIT_TRACE_NUM zend_jit_traces[0].id
- #define ZEND_JIT_EXIT_NUM zend_jit_traces[0].exit_count
- #define ZEND_JIT_EXIT_COUNTERS zend_jit_traces[0].exit_counters
- #define ZEND_JIT_TRACE_STOP_DESCRIPTION(name, description) \
- description,
- static const char * zend_jit_trace_stop_description[] = {
- ZEND_JIT_TRACE_STOP(ZEND_JIT_TRACE_STOP_DESCRIPTION)
- };
- static zend_always_inline const char *zend_jit_trace_star_desc(uint8_t trace_flags)
- {
- if (trace_flags & ZEND_JIT_TRACE_START_LOOP) {
- return "loop";
- } else if (trace_flags & ZEND_JIT_TRACE_START_ENTER) {
- return "enter";
- } else if (trace_flags & ZEND_JIT_TRACE_START_RETURN) {
- return "return";
- } else {
- ZEND_UNREACHABLE();
- return "???";
- }
- }
- static int zend_jit_trace_startup(zend_bool reattached)
- {
- if (!reattached) {
- zend_jit_traces = (zend_jit_trace_info*)zend_shared_alloc(sizeof(zend_jit_trace_info) * JIT_G(max_root_traces));
- if (!zend_jit_traces) {
- return FAILURE;
- }
- zend_jit_exit_groups = (const void**)zend_shared_alloc(sizeof(void*) * (ZEND_JIT_TRACE_MAX_EXITS/ZEND_JIT_EXIT_POINTS_PER_GROUP));
- if (!zend_jit_exit_groups) {
- return FAILURE;
- }
- ZEND_JIT_TRACE_NUM = 1;
- ZEND_JIT_COUNTER_NUM = 0;
- ZEND_JIT_EXIT_NUM = 0;
- ZEND_JIT_EXIT_COUNTERS = 0;
- ZCSG(jit_traces) = zend_jit_traces;
- ZCSG(jit_exit_groups) = zend_jit_exit_groups;
- } else {
- zend_jit_traces = ZCSG(jit_traces);
- if (!zend_jit_traces) {
- return FAILURE;
- }
- zend_jit_exit_groups = ZCSG(jit_exit_groups);
- if (!zend_jit_exit_groups) {
- return FAILURE;
- }
- }
- memset(&dummy_op_array, 0, sizeof(dummy_op_array));
- dummy_op_array.fn_flags = ZEND_ACC_DONE_PASS_TWO;
- JIT_G(exit_counters) = calloc(JIT_G(max_exit_counters), 1);
- if (JIT_G(exit_counters) == NULL) {
- return FAILURE;
- }
- return SUCCESS;
- }
- static const void *zend_jit_trace_allocate_exit_group(uint32_t n)
- {
- dasm_State* dasm_state = NULL;
- const void *entry;
- char name[32];
- dasm_init(&dasm_state, DASM_MAXSECTION);
- dasm_setupglobal(&dasm_state, dasm_labels, zend_lb_MAX);
- dasm_setup(&dasm_state, dasm_actions);
- zend_jit_trace_exit_group_stub(&dasm_state, n);
- sprintf(name, "jit$$trace_exit_%d", n);
- entry = dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, name, 0, SP_ADJ_JIT, SP_ADJ_NONE);
- dasm_free(&dasm_state);
- #ifdef HAVE_DISASM
- if (JIT_G(debug) & ZEND_JIT_DEBUG_ASM) {
- uint32_t i;
- for (i = 0; i < ZEND_JIT_EXIT_POINTS_PER_GROUP; i++) {
- sprintf(name, "jit$$trace_exit_%d", n + i);
- zend_jit_disasm_add_symbol(name, (uintptr_t)entry + (i * ZEND_JIT_EXIT_POINTS_SPACING), ZEND_JIT_EXIT_POINTS_SPACING);
- }
- }
- #endif
- return entry;
- }
- static const void *zend_jit_trace_allocate_exit_point(uint32_t n)
- {
- const void *group = NULL;
- if (UNEXPECTED(n >= ZEND_JIT_TRACE_MAX_EXITS)) {
- return NULL;
- }
- do {
- group = zend_jit_trace_allocate_exit_group(ZEND_JIT_EXIT_NUM);
- if (!group) {
- return NULL;
- }
- zend_jit_exit_groups[ZEND_JIT_EXIT_NUM / ZEND_JIT_EXIT_POINTS_PER_GROUP] =
- group;
- ZEND_JIT_EXIT_NUM += ZEND_JIT_EXIT_POINTS_PER_GROUP;
- } while (n >= ZEND_JIT_EXIT_NUM);
- return (const void*)
- ((const char*)group +
- ((n % ZEND_JIT_EXIT_POINTS_PER_GROUP) * ZEND_JIT_EXIT_POINTS_SPACING));
- }
- static const void *zend_jit_trace_get_exit_addr(uint32_t n)
- {
- if (UNEXPECTED(n >= ZEND_JIT_EXIT_NUM)) {
- return zend_jit_trace_allocate_exit_point(n);
- }
- return (const void*)
- ((const char*)zend_jit_exit_groups[n / ZEND_JIT_EXIT_POINTS_PER_GROUP] +
- ((n % ZEND_JIT_EXIT_POINTS_PER_GROUP) * ZEND_JIT_EXIT_POINTS_SPACING));
- }
- #if ZEND_JIT_TARGET_ARM64
- static zend_jit_trace_info *zend_jit_get_current_trace_info(void)
- {
- return &zend_jit_traces[ZEND_JIT_TRACE_NUM];
- }
- static uint32_t zend_jit_trace_find_exit_point(const void* addr)
- {
- uint32_t n = ZEND_JIT_EXIT_NUM / ZEND_JIT_EXIT_POINTS_PER_GROUP;
- uint32_t i;
- for (i = 0; i < n; i++) {
- if ((const char*)addr >= (const char*)zend_jit_exit_groups[i]
- && (const char*)addr < (const char*)zend_jit_exit_groups[i] +
- (ZEND_JIT_EXIT_POINTS_PER_GROUP * ZEND_JIT_EXIT_POINTS_SPACING)) {
- return (i * ZEND_JIT_EXIT_POINTS_PER_GROUP) +
- ((const char*)addr - (const char*)zend_jit_exit_groups[i]) / ZEND_JIT_EXIT_POINTS_SPACING;
- }
- }
- return (uint32_t)-1;
- }
- #endif
- static uint32_t zend_jit_trace_get_exit_point(const zend_op *to_opline, uint32_t flags)
- {
- zend_jit_trace_info *t = &zend_jit_traces[ZEND_JIT_TRACE_NUM];
- uint32_t exit_point;
- const zend_op_array *op_array;
- uint32_t stack_offset = (uint32_t)-1;
- uint32_t stack_size;
- zend_jit_trace_stack *stack = NULL;
- if (delayed_call_chain) {
- assert(to_opline != NULL); /* CALL and IP share the same register */
- flags |= ZEND_JIT_EXIT_RESTORE_CALL;
- }
- if (JIT_G(current_frame)) {
- op_array = &JIT_G(current_frame)->func->op_array;
- stack_size = op_array->last_var + op_array->T;
- if (stack_size) {
- stack = JIT_G(current_frame)->stack;
- do {
- if (STACK_TYPE(stack, stack_size-1) != IS_UNKNOWN
- || STACK_MEM_TYPE(stack, stack_size-1) != IS_UNKNOWN
- || STACK_REG(stack, stack_size-1) != ZREG_NONE) {
- break;
- }
- stack_size--;
- } while (stack_size);
- }
- } else {
- op_array = NULL;
- stack_size = 0;
- }
- /* Try to reuse exit points */
- if (to_opline != NULL && t->exit_count > 0) {
- uint32_t i = t->exit_count;
- do {
- i--;
- if (stack_size == 0
- || (t->exit_info[i].stack_size >= stack_size
- && memcmp(t->stack_map + t->exit_info[i].stack_offset, stack, stack_size * sizeof(zend_jit_trace_stack)) == 0)) {
- stack_offset = t->exit_info[i].stack_offset;
- if (t->exit_info[i].opline == to_opline
- && t->exit_info[i].flags == flags
- && t->exit_info[i].stack_size == stack_size) {
- return i;
- }
- }
- } while (i > 0);
- }
- exit_point = t->exit_count;
- if (exit_point < ZEND_JIT_TRACE_MAX_EXITS) {
- if (stack_size != 0 && stack_offset == (uint32_t)-1) {
- stack_offset = t->stack_map_size;
- t->stack_map_size += stack_size;
- // TODO: reduce number of reallocations ???
- t->stack_map = erealloc(t->stack_map, t->stack_map_size * sizeof(zend_jit_trace_stack));
- memcpy(t->stack_map + stack_offset, stack, stack_size * sizeof(zend_jit_trace_stack));
- }
- t->exit_count++;
- t->exit_info[exit_point].opline = to_opline;
- t->exit_info[exit_point].op_array = op_array;
- t->exit_info[exit_point].flags = flags;
- t->exit_info[exit_point].stack_size = stack_size;
- t->exit_info[exit_point].stack_offset = stack_offset;
- }
- return exit_point;
- }
- static void zend_jit_trace_add_code(const void *start, uint32_t size)
- {
- zend_jit_trace_info *t = &zend_jit_traces[ZEND_JIT_TRACE_NUM];
- t->code_start = start;
- t->code_size = size;
- }
- static uint32_t zend_jit_find_trace(const void *addr)
- {
- uint32_t i;
- for (i = 1; i < ZEND_JIT_TRACE_NUM; i++) {
- if (zend_jit_traces[i].code_start == addr) {
- return i;
- }
- }
- ZEND_UNREACHABLE();
- return 0;
- }
- static zend_string *zend_jit_trace_name(const zend_op_array *op_array, uint32_t lineno)
- {
- smart_str buf = {0};
- smart_str_appends(&buf, TRACE_PREFIX);
- smart_str_append_long(&buf, (zend_long)ZEND_JIT_TRACE_NUM);
- smart_str_appendc(&buf, '$');
- if (op_array->function_name) {
- if (op_array->scope) {
- smart_str_appendl(&buf, ZSTR_VAL(op_array->scope->name), ZSTR_LEN(op_array->scope->name));
- smart_str_appends(&buf, "::");
- smart_str_appendl(&buf, ZSTR_VAL(op_array->function_name), ZSTR_LEN(op_array->function_name));
- } else {
- smart_str_appendl(&buf, ZSTR_VAL(op_array->function_name), ZSTR_LEN(op_array->function_name));
- }
- } else if (op_array->filename) {
- smart_str_appendl(&buf, ZSTR_VAL(op_array->filename), ZSTR_LEN(op_array->filename));
- }
- smart_str_appendc(&buf, '$');
- smart_str_append_long(&buf, (zend_long)lineno);
- smart_str_0(&buf);
- return buf.s;
- }
- static int zend_jit_trace_may_exit(const zend_op_array *op_array, const zend_op *opline)
- {
- switch (opline->opcode) {
- case ZEND_IS_IDENTICAL:
- case ZEND_IS_NOT_IDENTICAL:
- case ZEND_IS_EQUAL:
- case ZEND_IS_NOT_EQUAL:
- case ZEND_IS_SMALLER:
- case ZEND_IS_SMALLER_OR_EQUAL:
- case ZEND_CASE:
- case ZEND_CASE_STRICT:
- case ZEND_ISSET_ISEMPTY_CV:
- case ZEND_ISSET_ISEMPTY_VAR:
- case ZEND_ISSET_ISEMPTY_DIM_OBJ:
- case ZEND_ISSET_ISEMPTY_PROP_OBJ:
- case ZEND_ISSET_ISEMPTY_STATIC_PROP:
- case ZEND_INSTANCEOF:
- case ZEND_TYPE_CHECK:
- case ZEND_DEFINED:
- case ZEND_IN_ARRAY:
- case ZEND_ARRAY_KEY_EXISTS:
- if (opline->result_type & (IS_SMART_BRANCH_JMPNZ | IS_SMART_BRANCH_JMPZ)) {
- /* smart branch */
- return 1;
- }
- break;
- case ZEND_JMPZNZ:
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_JMP_NULL:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_ASSERT_CHECK:
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- /* branch opcodes */
- return 1;
- case ZEND_NEW:
- if (opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL) {
- /* NEW may skip constructor without arguments */
- return 1;
- }
- break;
- case ZEND_CATCH:
- case ZEND_FAST_CALL:
- case ZEND_FAST_RET:
- case ZEND_GENERATOR_CREATE:
- case ZEND_GENERATOR_RETURN:
- case ZEND_EXIT:
- case ZEND_YIELD:
- case ZEND_YIELD_FROM:
- case ZEND_INCLUDE_OR_EVAL:
- case ZEND_MATCH_ERROR:
- /* unsupported */
- return 1;
- case ZEND_DO_FCALL:
- /* potentially polymorphic call */
- return 1;
- #if 0
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- /* monomorphic call */
- // TODO: recompilation may change target ???
- return 0;
- #endif
- case ZEND_RETURN_BY_REF:
- case ZEND_RETURN:
- /* return */
- return !JIT_G(current_frame) || TRACE_FRAME_IS_UNKNOWN_RETURN(JIT_G(current_frame));
- default:
- break;
- }
- return 0;
- }
- static zend_always_inline uint32_t zend_jit_trace_type_to_info_ex(zend_uchar type, uint32_t info)
- {
- if (type == IS_UNKNOWN) {
- return info;
- }
- ZEND_ASSERT(info & (1 << type));
- if (type < IS_STRING) {
- return (1 << type);
- } else if (type != IS_ARRAY) {
- return (1 << type) | (info & (MAY_BE_RC1|MAY_BE_RCN));
- } else {
- return MAY_BE_ARRAY | (info & (MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_ARRAY_KEY_ANY|MAY_BE_RC1|MAY_BE_RCN));
- }
- }
- static zend_always_inline uint32_t zend_jit_trace_type_to_info(zend_uchar type)
- {
- return zend_jit_trace_type_to_info_ex(type, -1);
- }
- static zend_always_inline zend_ssa_alias_kind zend_jit_var_may_alias(const zend_op_array *op_array, const zend_ssa *ssa, uint32_t var)
- {
- if (var >= op_array->last_var) {
- return NO_ALIAS;
- } else if ((!op_array->function_name || (ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS))) {
- return SYMTABLE_ALIAS;
- } else if (ssa->vars) {
- return ssa->vars[var].alias;
- } else if (zend_string_equals_literal(op_array->vars[var], "http_response_header")) {
- return HTTP_RESPONSE_HEADER_ALIAS;
- }
- return NO_ALIAS;
- }
- static zend_always_inline void zend_jit_trace_add_op_guard(zend_ssa *tssa,
- int ssa_var,
- uint8_t op_type)
- {
- zend_ssa_var_info *info = &tssa->var_info[ssa_var];
- if ((info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) != (1 << op_type)) {
- if (UNEXPECTED(tssa->vars[ssa_var].alias != NO_ALIAS)) {
- info->type |= MAY_BE_GUARD;
- } else {
- info->type = MAY_BE_GUARD | zend_jit_trace_type_to_info_ex(op_type, info->type);
- }
- }
- }
- #define ADD_OP_GUARD(_ssa_var, _op_type) do { \
- if (_ssa_var >= 0 && _op_type != IS_UNKNOWN) { \
- zend_jit_trace_add_op_guard(tssa, _ssa_var, _op_type); \
- } \
- } while (0)
- #define CHECK_OP_TRACE_TYPE(_var, _ssa_var, op_info, op_type) do { \
- if (op_type != IS_UNKNOWN) { \
- if ((op_info & MAY_BE_GUARD) != 0) { \
- if (!zend_jit_type_guard(&dasm_state, opline, _var, op_type)) { \
- goto jit_failure; \
- } \
- if (ssa->vars[_ssa_var].alias != NO_ALIAS) { \
- SET_STACK_TYPE(stack, EX_VAR_TO_NUM(_var), IS_UNKNOWN, 1); \
- op_info = zend_jit_trace_type_to_info(op_type); \
- } else { \
- SET_STACK_TYPE(stack, EX_VAR_TO_NUM(_var), op_type, 1); \
- op_info &= ~MAY_BE_GUARD; \
- ssa->var_info[_ssa_var].type &= op_info; \
- } \
- } \
- } \
- } while (0)
- #define ADD_OP1_TRACE_GUARD() \
- ADD_OP_GUARD(tssa->ops[idx].op1_use, op1_type)
- #define ADD_OP2_TRACE_GUARD() \
- ADD_OP_GUARD(tssa->ops[idx].op2_use, op2_type)
- #define ADD_OP1_DATA_TRACE_GUARD() \
- ADD_OP_GUARD(tssa->ops[idx+1].op1_use, op3_type)
- #define CHECK_OP1_TRACE_TYPE() \
- CHECK_OP_TRACE_TYPE(opline->op1.var, ssa_op->op1_use, op1_info, op1_type)
- #define CHECK_OP2_TRACE_TYPE() \
- CHECK_OP_TRACE_TYPE(opline->op2.var, ssa_op->op2_use, op2_info, op2_type)
- #define CHECK_OP1_DATA_TRACE_TYPE() \
- CHECK_OP_TRACE_TYPE((opline+1)->op1.var, (ssa_op+1)->op1_use, op1_data_info, op3_type)
- static zend_always_inline size_t zend_jit_trace_frame_size(const zend_op_array *op_array)
- {
- if (op_array && op_array->type == ZEND_USER_FUNCTION) {
- return ZEND_MM_ALIGNED_SIZE(offsetof(zend_jit_trace_stack_frame, stack) + ZEND_MM_ALIGNED_SIZE((op_array->last_var + op_array->T) * sizeof(zend_jit_trace_stack)));
- } else if (op_array) {
- return ZEND_MM_ALIGNED_SIZE(offsetof(zend_jit_trace_stack_frame, stack) + ZEND_MM_ALIGNED_SIZE(op_array->num_args * sizeof(zend_jit_trace_stack)));
- } else {
- return ZEND_MM_ALIGNED_SIZE(offsetof(zend_jit_trace_stack_frame, stack));
- }
- }
- static zend_jit_trace_stack_frame* zend_jit_trace_call_frame(zend_jit_trace_stack_frame *frame, const zend_op_array *op_array)
- {
- return (zend_jit_trace_stack_frame*)((char*)frame + zend_jit_trace_frame_size(op_array));
- }
- static zend_jit_trace_stack_frame* zend_jit_trace_ret_frame(zend_jit_trace_stack_frame *frame, const zend_op_array *op_array)
- {
- return (zend_jit_trace_stack_frame*)((char*)frame - zend_jit_trace_frame_size(op_array));
- }
- static void zend_jit_trace_send_type(const zend_op *opline, zend_jit_trace_stack_frame *call, zend_uchar type)
- {
- zend_jit_trace_stack *stack = call->stack;
- const zend_op_array *op_array = &call->func->op_array;
- uint32_t arg_num = opline->op2.num;
- if (arg_num > op_array->num_args) {
- return;
- }
- if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
- zend_arg_info *arg_info;
- ZEND_ASSERT(arg_num <= op_array->num_args);
- arg_info = &op_array->arg_info[arg_num-1];
- if (ZEND_TYPE_IS_SET(arg_info->type)) {
- if (!(ZEND_TYPE_FULL_MASK(arg_info->type) & (1u << type))) {
- return;
- }
- }
- }
- SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), type, 1);
- }
- static bool zend_jit_needs_arg_dtor(const zend_function *func, uint32_t arg_num, zend_call_info *call_info)
- {
- if (func
- && func->type == ZEND_INTERNAL_FUNCTION
- && (func->internal_function.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0
- && arg_num < func->internal_function.num_args) {
- const zend_internal_arg_info *arg_info = &func->internal_function.arg_info[arg_num];
- if (ZEND_ARG_SEND_MODE(arg_info) == ZEND_SEND_BY_VAL
- && ZEND_TYPE_IS_SET(arg_info->type)
- && (ZEND_TYPE_FULL_MASK(arg_info->type) & MAY_BE_ANY) != MAY_BE_ANY) {
- if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
- && JIT_G(current_frame)
- && JIT_G(current_frame)->call
- && JIT_G(current_frame)->call->func) {
- uint32_t type = STACK_TYPE(JIT_G(current_frame)->call->stack, arg_num);
- if (type != IS_UNKNOWN
- && type < IS_STRING
- && ZEND_TYPE_FULL_MASK(arg_info->type) & (1u << type)) {
- return 0;
- }
- }
- if (call_info && arg_num < call_info->num_args && call_info->arg_info[arg_num].opline) {
- const zend_op *opline = call_info->arg_info[arg_num].opline;
- if (opline->opcode == ZEND_SEND_VAL && opline->op1_type == IS_CONST) {
- zval *zv = RT_CONSTANT(opline, opline->op1);
- if (!Z_REFCOUNTED_P(zv)) {
- uint32_t type = Z_TYPE_P(zv);
- // TODO: few functions (e.g. pcntl_exec) modify arrays in-place ???
- if (type != IS_ARRAY
- && (ZEND_TYPE_FULL_MASK(arg_info->type) & (1u << type))) {
- return 0;
- }
- }
- }
- }
- }
- }
- return 1;
- }
- static zend_ssa *zend_jit_trace_build_ssa(const zend_op_array *op_array, zend_script *script)
- {
- zend_jit_op_array_trace_extension *jit_extension;
- zend_ssa *ssa;
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- jit_extension->func_info.num = 0;
- jit_extension->func_info.flags &= ZEND_FUNC_JIT_ON_FIRST_EXEC
- | ZEND_FUNC_JIT_ON_PROF_REQUEST
- | ZEND_FUNC_JIT_ON_HOT_COUNTERS
- | ZEND_FUNC_JIT_ON_HOT_TRACE;
- memset(&jit_extension->func_info.ssa, 0, sizeof(zend_func_info) - offsetof(zend_func_info, ssa));
- ssa = &jit_extension->func_info.ssa;
- if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_OPT_FUNC) {
- do {
- if (zend_jit_op_array_analyze1(op_array, script, ssa) != SUCCESS) {
- break;
- }
- if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_OPT_FUNCS) {
- if (zend_analyze_calls(&CG(arena), script, ZEND_CALL_TREE, (zend_op_array*)op_array, &jit_extension->func_info) != SUCCESS) {
- break;
- }
- jit_extension->func_info.call_map = zend_build_call_map(&CG(arena), &jit_extension->func_info, op_array);
- if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
- zend_init_func_return_info(op_array, script, &jit_extension->func_info.return_info);
- }
- }
- if (zend_jit_op_array_analyze2(op_array, script, ssa, 0) != SUCCESS) {
- break;
- }
- if (JIT_G(debug) & ZEND_JIT_DEBUG_SSA) {
- zend_dump_op_array(op_array, ZEND_DUMP_HIDE_UNREACHABLE|ZEND_DUMP_RC_INFERENCE|ZEND_DUMP_SSA, "JIT", ssa);
- }
- return ssa;
- } while (0);
- }
- memset(ssa, 0, sizeof(zend_ssa));
- ssa->cfg.blocks_count = 1;
- if (JIT_G(opt_level) == ZEND_JIT_LEVEL_INLINE) {
- zend_cfg cfg;
- void *checkpoint = zend_arena_checkpoint(CG(arena));
- if (zend_jit_build_cfg(op_array, &cfg) == SUCCESS) {
- ssa->cfg.flags = cfg.flags;
- } else{
- ssa->cfg.flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
- }
- /* TODO: move this to zend_cfg.c ? */
- if (!op_array->function_name) {
- ssa->cfg.flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
- }
- zend_arena_release(&CG(arena), checkpoint);
- }
- return ssa;
- }
- static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa);
- static void zend_jit_dump_exit_info(zend_jit_trace_info *t);
- static zend_always_inline int zend_jit_trace_op_len(const zend_op *opline)
- {
- int len;
- switch (opline->opcode) {
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_OBJ:
- case ZEND_ASSIGN_STATIC_PROP:
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- case ZEND_ASSIGN_OBJ_REF:
- case ZEND_ASSIGN_STATIC_PROP_REF:
- return 2; /* OP_DATA */
- case ZEND_RECV_INIT:
- len = 1;
- opline++;
- while (opline->opcode == ZEND_RECV_INIT) {
- len++;
- opline++;
- }
- return len;
- case ZEND_BIND_GLOBAL:
- len = 1;
- opline++;
- while (opline->opcode == ZEND_BIND_GLOBAL) {
- len++;
- opline++;
- }
- return len;
- // case ZEND_IS_IDENTICAL:
- // case ZEND_IS_NOT_IDENTICAL:
- // case ZEND_IS_EQUAL:
- // case ZEND_IS_NOT_EQUAL:
- // case ZEND_IS_SMALLER:
- // case ZEND_IS_SMALLER_OR_EQUAL:
- // case ZEND_CASE:
- // case ZEND_ISSET_ISEMPTY_CV:
- // case ZEND_ISSET_ISEMPTY_VAR:
- // case ZEND_ISSET_ISEMPTY_DIM_OBJ:
- // case ZEND_ISSET_ISEMPTY_PROP_OBJ:
- // case ZEND_ISSET_ISEMPTY_STATIC_PROP:
- // case ZEND_INSTANCEOF:
- // case ZEND_TYPE_CHECK:
- // case ZEND_DEFINED:
- // case ZEND_IN_ARRAY:
- // case ZEND_ARRAY_KEY_EXISTS:
- default:
- if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- return 2; /* JMPZ/JMPNZ */
- }
- return 1;
- }
- }
- static int zend_jit_trace_add_phis(zend_jit_trace_rec *trace_buffer, uint32_t ssa_vars_count, zend_ssa *tssa, zend_jit_trace_stack *stack)
- {
- const zend_op_array *op_array;
- zend_jit_trace_rec *p;
- int k, vars_count;
- zend_bitset use, def;
- uint32_t build_flags = ZEND_SSA_RC_INFERENCE | ZEND_SSA_USE_CV_RESULTS;
- uint32_t set_size;
- zend_ssa_phi *prev = NULL;
- int level = 0;
- ALLOCA_FLAG(use_heap);
- op_array = trace_buffer->op_array;
- set_size = zend_bitset_len(op_array->last_var + op_array->T);
- use = ZEND_BITSET_ALLOCA(set_size * 2, use_heap);
- memset(use, 0, set_size * 2 * ZEND_BITSET_ELM_SIZE);
- def = use + set_size;
- p = trace_buffer + ZEND_JIT_TRACE_START_REC_SIZE;
- for (;;p++) {
- if (p->op == ZEND_JIT_TRACE_VM && level == 0) {
- const zend_op *opline = p->opline;
- int len;
- zend_dfg_add_use_def_op(op_array, opline, build_flags, use, def);
- len = zend_jit_trace_op_len(opline);
- while (len > 1) {
- opline++;
- if (opline->opcode != ZEND_OP_DATA) {
- zend_dfg_add_use_def_op(op_array, opline, build_flags, use, def);
- }
- len--;
- }
- } else if (p->op == ZEND_JIT_TRACE_INIT_CALL) {
- } else if (p->op == ZEND_JIT_TRACE_DO_ICALL) {
- } else if (p->op == ZEND_JIT_TRACE_ENTER) {
- level++;
- } else if (p->op == ZEND_JIT_TRACE_BACK) {
- if (level == 0) {
- // Phi for recursive calls and returns are not supported yet ???
- assert(0);
- } else {
- level--;
- }
- } else if (p->op == ZEND_JIT_TRACE_END) {
- break;
- }
- }
- zend_bitset_intersection(use, def, set_size);
- if (trace_buffer->start == ZEND_JIT_TRACE_START_ENTER) {
- vars_count = op_array->last_var;
- } else {
- vars_count = op_array->last_var + op_array->T;
- }
- for (k = 0; k < vars_count; k++) {
- if (zend_bitset_in(use, k)) {
- zend_ssa_phi *phi = zend_arena_calloc(&CG(arena), 1,
- ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)) +
- ZEND_MM_ALIGNED_SIZE(sizeof(int) * 2) +
- sizeof(void*) * 2);
- phi->sources = (int*)(((char*)phi) + ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)));
- phi->sources[0] = STACK_VAR(stack, k);
- phi->sources[1] = -1;
- phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + ZEND_MM_ALIGNED_SIZE(sizeof(int) * 2));
- phi->pi = -1;
- phi->var = k;
- phi->ssa_var = ssa_vars_count;
- SET_STACK_VAR(stack, k, ssa_vars_count);
- ssa_vars_count++;
- phi->block = 1;
- if (prev) {
- prev->next = phi;
- } else {
- tssa->blocks[1].phis = phi;
- }
- prev = phi;
- }
- }
- free_alloca(use, use_heap);
- return ssa_vars_count;
- }
- static int zend_jit_trace_add_call_phis(zend_jit_trace_rec *trace_buffer, uint32_t ssa_vars_count, zend_ssa *tssa, zend_jit_trace_stack *stack)
- {
- zend_ssa_phi *prev = NULL;
- const zend_op_array *op_array = trace_buffer->op_array;
- const zend_op *opline = trace_buffer[1].opline;
- int count = opline - op_array->opcodes;
- int i;
- for(i = 0; i < count; i++) {
- zend_ssa_phi *phi = zend_arena_calloc(&CG(arena), 1,
- ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)) +
- ZEND_MM_ALIGNED_SIZE(sizeof(int) * 2) +
- sizeof(void*) * 2);
- phi->sources = (int*)(((char*)phi) + ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)));
- phi->sources[0] = STACK_VAR(stack, i);
- phi->sources[1] = -1;
- phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + ZEND_MM_ALIGNED_SIZE(sizeof(int) * 2));
- phi->pi = -1;
- phi->var = i;
- phi->ssa_var = ssa_vars_count;
- SET_STACK_VAR(stack, i, ssa_vars_count);
- ssa_vars_count++;
- phi->block = 1;
- if (prev) {
- prev->next = phi;
- } else {
- tssa->blocks[1].phis = phi;
- }
- prev = phi;
- }
- return ssa_vars_count;
- }
- static int zend_jit_trace_add_ret_phis(zend_jit_trace_rec *trace_buffer, uint32_t ssa_vars_count, zend_ssa *tssa, zend_jit_trace_stack *stack)
- {
- const zend_op *opline = trace_buffer[1].opline - 1;
- int i;
- if (RETURN_VALUE_USED(opline)) {
- zend_ssa_phi *phi = zend_arena_calloc(&CG(arena), 1,
- ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)) +
- ZEND_MM_ALIGNED_SIZE(sizeof(int) * 2) +
- sizeof(void*) * 2);
- i = EX_VAR_TO_NUM(opline->result.var);
- phi->sources = (int*)(((char*)phi) + ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)));
- phi->sources[0] = STACK_VAR(stack, i);
- phi->sources[1] = -1;
- phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + ZEND_MM_ALIGNED_SIZE(sizeof(int) * 2));
- phi->pi = -1;
- phi->var = i;
- phi->ssa_var = ssa_vars_count;
- SET_STACK_VAR(stack, i, ssa_vars_count);
- ssa_vars_count++;
- phi->block = 1;
- tssa->blocks[1].phis = phi;
- }
- return ssa_vars_count;
- }
- static int zend_jit_trace_copy_ssa_var_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op **tssa_opcodes, zend_ssa *tssa, int ssa_var)
- {
- int var, use;
- zend_ssa_op *op;
- zend_ssa_var_info *info;
- unsigned int no_val;
- zend_ssa_alias_kind alias;
- if (tssa->vars[ssa_var].phi_use_chain) {
- // TODO: this may be incorrect ???
- var = tssa->vars[ssa_var].phi_use_chain->ssa_var;
- } else {
- var = ssa_var;
- }
- use = tssa->vars[var].use_chain;
- if (use >= 0) {
- ZEND_ASSERT((tssa_opcodes[use] - op_array->opcodes) < op_array->last);
- op = ssa->ops + (tssa_opcodes[use] - op_array->opcodes);
- if (tssa->ops[use].op1_use == var) {
- no_val = ssa->vars[op->op1_use].no_val;
- alias = ssa->vars[op->op1_use].alias;
- info = ssa->var_info + op->op1_use;
- } else if (tssa->ops[use].op2_use == var) {
- no_val = ssa->vars[op->op2_use].no_val;
- alias = ssa->vars[op->op2_use].alias;
- info = ssa->var_info + op->op2_use;
- } else if (tssa->ops[use].result_use == var) {
- no_val = ssa->vars[op->result_use].no_val;
- alias = ssa->vars[op->result_use].alias;
- info = ssa->var_info + op->result_use;
- } else {
- assert(0);
- return 0;
- }
- tssa->vars[ssa_var].no_val = no_val;
- tssa->vars[ssa_var].alias = alias;
- memcpy(&tssa->var_info[ssa_var], info, sizeof(zend_ssa_var_info));
- return 1;
- }
- return 0;
- }
- static void zend_jit_trace_propagate_range(const zend_op_array *op_array, const zend_op **tssa_opcodes, zend_ssa *tssa, int ssa_var)
- {
- zend_ssa_range tmp;
- int def = tssa->vars[ssa_var].definition;
- if (tssa->vars[ssa_var].alias == NO_ALIAS
- && zend_inference_propagate_range(op_array, tssa, (zend_op*)tssa_opcodes[def], (zend_ssa_op*)&tssa->ops[def], ssa_var, &tmp)) {
- tssa->var_info[ssa_var].range.min = tmp.min;
- tssa->var_info[ssa_var].range.max = tmp.max;
- tssa->var_info[ssa_var].range.underflow = tmp.underflow;
- tssa->var_info[ssa_var].range.overflow = tmp.overflow;
- tssa->var_info[ssa_var].has_range = 1;
- }
- }
- static void zend_jit_trace_copy_ssa_var_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op **tssa_opcodes, zend_ssa *tssa, int ssa_var)
- {
- int def;
- zend_ssa_op *op;
- zend_ssa_var_info *info;
- unsigned int no_val;
- zend_ssa_alias_kind alias;
- def = tssa->vars[ssa_var].definition;
- if (def >= 0) {
- ZEND_ASSERT((tssa_opcodes[def] - op_array->opcodes) < op_array->last);
- op = ssa->ops + (tssa_opcodes[def] - op_array->opcodes);
- if (tssa->ops[def].op1_def == ssa_var) {
- no_val = ssa->vars[op->op1_def].no_val;
- alias = ssa->vars[op->op1_def].alias;
- info = ssa->var_info + op->op1_def;
- } else if (tssa->ops[def].op2_def == ssa_var) {
- no_val = ssa->vars[op->op2_def].no_val;
- alias = ssa->vars[op->op2_def].alias;
- info = ssa->var_info + op->op2_def;
- } else if (tssa->ops[def].result_def == ssa_var) {
- no_val = ssa->vars[op->result_def].no_val;
- alias = ssa->vars[op->result_def].alias;
- info = ssa->var_info + op->result_def;
- } else {
- assert(0);
- return;
- }
- tssa->vars[ssa_var].no_val = no_val;
- tssa->vars[ssa_var].alias = alias;
- if (!(info->type & MAY_BE_REF)) {
- zend_jit_trace_propagate_range(op_array, tssa_opcodes, tssa, ssa_var);
- }
- if (info->has_range) {
- if (tssa->var_info[ssa_var].has_range) {
- tssa->var_info[ssa_var].range.min = MAX(tssa->var_info[ssa_var].range.min, info->range.min);
- tssa->var_info[ssa_var].range.max = MIN(tssa->var_info[ssa_var].range.max, info->range.max);
- tssa->var_info[ssa_var].range.underflow = tssa->var_info[ssa_var].range.underflow && info->range.underflow;
- tssa->var_info[ssa_var].range.overflow = tssa->var_info[ssa_var].range.overflow && info->range.overflow;
- } else {
- tssa->var_info[ssa_var].has_range = 1;
- tssa->var_info[ssa_var].range = info->range;
- }
- }
- }
- }
- static int zend_jit_trace_restrict_ssa_var_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op **tssa_opcodes, zend_ssa *tssa, int ssa_var)
- {
- int def;
- zend_ssa_op *op;
- zend_ssa_var_info *info;
- def = tssa->vars[ssa_var].definition;
- if (def >= 0) {
- ZEND_ASSERT((tssa_opcodes[def] - op_array->opcodes) < op_array->last);
- op = ssa->ops + (tssa_opcodes[def] - op_array->opcodes);
- if (tssa->ops[def].op1_def == ssa_var) {
- info = ssa->var_info + op->op1_def;
- } else if (tssa->ops[def].op2_def == ssa_var) {
- info = ssa->var_info + op->op2_def;
- } else if (tssa->ops[def].result_def == ssa_var) {
- info = ssa->var_info + op->result_def;
- } else {
- assert(0);
- return 0;
- }
- tssa->var_info[ssa_var].type &= info->type;
- if (info->ce) {
- if (tssa->var_info[ssa_var].ce) {
- if (tssa->var_info[ssa_var].ce != info->ce) {
- if (instanceof_function(tssa->var_info[ssa_var].ce, info->ce)) {
- /* everything fine */
- } else if (instanceof_function(info->ce, tssa->var_info[ssa_var].ce)) {
- // TODO: TSSA may miss Pi() functions and corresponding instanceof() constraints ???
- } else {
- // TODO: classes may implement the same interface ???
- //ZEND_UNREACHABLE();
- }
- }
- tssa->var_info[ssa_var].is_instanceof =
- tssa->var_info[ssa_var].is_instanceof && info->is_instanceof;
- } else {
- tssa->var_info[ssa_var].ce = info->ce;
- tssa->var_info[ssa_var].is_instanceof = info->is_instanceof;
- }
- }
- if (info->has_range) {
- if (tssa->var_info[ssa_var].has_range) {
- tssa->var_info[ssa_var].range.min = MAX(tssa->var_info[ssa_var].range.min, info->range.min);
- tssa->var_info[ssa_var].range.max = MIN(tssa->var_info[ssa_var].range.max, info->range.max);
- tssa->var_info[ssa_var].range.underflow = tssa->var_info[ssa_var].range.underflow && info->range.underflow;
- tssa->var_info[ssa_var].range.overflow = tssa->var_info[ssa_var].range.overflow && info->range.overflow;
- } else {
- tssa->var_info[ssa_var].has_range = 1;
- tssa->var_info[ssa_var].range = info->range;
- }
- }
- return 1;
- }
- return 0;
- }
- static int find_return_ssa_var(zend_jit_trace_rec *p, zend_ssa_op *ssa_op)
- {
- while (1) {
- if (p->op == ZEND_JIT_TRACE_VM) {
- if (p->opline->opcode == ZEND_DO_UCALL
- || p->opline->opcode == ZEND_DO_FCALL_BY_NAME
- || p->opline->opcode == ZEND_DO_FCALL) {
- if (p->opline->result_type != IS_UNUSED) {
- return ssa_op->result_def;
- }
- }
- return -1;
- } else if (p->op >= ZEND_JIT_TRACE_OP1_TYPE && p->op <= ZEND_JIT_TRACE_VAL_INFO) {
- /*skip */
- } else {
- return -1;
- }
- p--;
- }
- }
- static const zend_op *zend_jit_trace_find_init_fcall_op(zend_jit_trace_rec *p, const zend_op_array *op_array)
- {
- if (!(p->info & ZEND_JIT_TRACE_FAKE_INIT_CALL)) {
- p--;
- while (1) {
- if (p->op == ZEND_JIT_TRACE_VM) {
- if (p->opline->opcode == ZEND_INIT_FCALL
- || p->opline->opcode == ZEND_INIT_FCALL_BY_NAME
- || p->opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME
- || p->opline->opcode == ZEND_INIT_DYNAMIC_CALL
- || p->opline->opcode == ZEND_INIT_USER_CALL
- || p->opline->opcode == ZEND_NEW
- || p->opline->opcode == ZEND_INIT_METHOD_CALL
- || p->opline->opcode == ZEND_INIT_STATIC_METHOD_CALL) {
- return p->opline;
- }
- return NULL;
- } else if (p->op >= ZEND_JIT_TRACE_OP1_TYPE && p->op <= ZEND_JIT_TRACE_VAL_INFO) {
- /*skip */
- } else {
- return NULL;
- }
- p--;
- }
- } else {
- const zend_op *opline = NULL;
- int call_level = 0;
- p++;
- while (1) {
- if (p->op == ZEND_JIT_TRACE_VM) {
- opline = p->opline;
- break;
- } else if (p->op == ZEND_JIT_TRACE_INIT_CALL) {
- call_level++;
- /*skip */
- } else {
- return NULL;
- }
- p--;
- }
- if (opline) {
- while (opline > op_array->opcodes) {
- opline--;
- switch (opline->opcode) {
- case ZEND_INIT_FCALL:
- case ZEND_INIT_FCALL_BY_NAME:
- case ZEND_INIT_NS_FCALL_BY_NAME:
- case ZEND_INIT_METHOD_CALL:
- case ZEND_INIT_DYNAMIC_CALL:
- case ZEND_INIT_STATIC_METHOD_CALL:
- case ZEND_INIT_USER_CALL:
- case ZEND_NEW:
- if (call_level == 0) {
- return opline;
- }
- call_level--;
- break;
- case ZEND_DO_FCALL:
- case ZEND_DO_ICALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- call_level++;
- break;
- }
- }
- }
- }
- return NULL;
- }
- static int is_checked_guard(const zend_ssa *tssa, const zend_op **ssa_opcodes, uint32_t var, uint32_t phi_var)
- {
- if ((tssa->var_info[phi_var].type & MAY_BE_ANY) == MAY_BE_LONG
- && !(tssa->var_info[var].type & MAY_BE_REF)) {
- int idx = tssa->vars[var].definition;
- if (idx >= 0) {
- if (tssa->ops[idx].op1_def == var) {
- const zend_op *opline = ssa_opcodes[idx];
- if (opline->opcode == ZEND_PRE_DEC
- || opline->opcode == ZEND_PRE_INC
- || opline->opcode == ZEND_POST_DEC
- || opline->opcode == ZEND_POST_INC) {
- if (tssa->ops[idx].op1_use >= 0
- && (tssa->var_info[tssa->ops[idx].op1_use].type & MAY_BE_STRING)) {
- return 0;
- }
- if (!(tssa->var_info[tssa->ops[idx].op1_use].type & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
- return 0;
- }
- return 1;
- } else if (opline->opcode == ZEND_ASSIGN_OP
- && (opline->extended_value == ZEND_ADD
- || opline->extended_value == ZEND_SUB
- || opline->extended_value == ZEND_MUL)) {
- if ((opline->op2_type & (IS_VAR|IS_CV))
- && tssa->ops[idx].op2_use >= 0
- && (tssa->var_info[tssa->ops[idx].op2_use].type & MAY_BE_REF)) {
- return 0;
- }
- if (!(tssa->var_info[tssa->ops[idx].op1_use].type & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
- return 0;
- }
- if (opline->op2_type == IS_CONST) {
- zval *zv = RT_CONSTANT(opline, opline->op2);
- if (Z_TYPE_P(zv) != IS_LONG && Z_TYPE_P(zv) != IS_DOUBLE) {
- return 0;
- }
- } else if (!(tssa->var_info[tssa->ops[idx].op2_use].type & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
- return 0;
- }
- return 1;
- }
- }
- if (tssa->ops[idx].result_def == var) {
- const zend_op *opline = ssa_opcodes[idx];
- if (opline->opcode == ZEND_ADD
- || opline->opcode == ZEND_SUB
- || opline->opcode == ZEND_MUL) {
- if ((opline->op1_type & (IS_VAR|IS_CV))
- && tssa->ops[idx].op1_use >= 0
- && (tssa->var_info[tssa->ops[idx].op1_use].type & MAY_BE_REF)) {
- return 0;
- }
- if ((opline->op2_type & (IS_VAR|IS_CV))
- && tssa->ops[idx].op2_use >= 0
- && (tssa->var_info[tssa->ops[idx].op2_use].type & MAY_BE_REF)) {
- return 0;
- }
- if (opline->op1_type == IS_CONST) {
- zval *zv = RT_CONSTANT(opline, opline->op1);
- if (Z_TYPE_P(zv) != IS_LONG && Z_TYPE_P(zv) != IS_DOUBLE) {
- return 0;
- }
- } else if (!(tssa->var_info[tssa->ops[idx].op1_use].type & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
- return 0;
- }
- if (opline->op2_type == IS_CONST) {
- zval *zv = RT_CONSTANT(opline, opline->op2);
- if (Z_TYPE_P(zv) != IS_LONG && Z_TYPE_P(zv) != IS_DOUBLE) {
- return 0;
- }
- } else if (!(tssa->var_info[tssa->ops[idx].op2_use].type & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
- return 0;
- }
- } else if (opline->opcode == ZEND_PRE_DEC
- || opline->opcode == ZEND_PRE_INC
- || opline->opcode == ZEND_POST_DEC
- || opline->opcode == ZEND_POST_INC) {
- if ((opline->op1_type & (IS_VAR|IS_CV))
- && tssa->ops[idx].op1_use >= 0
- && (tssa->var_info[tssa->ops[idx].op1_use].type & MAY_BE_REF)) {
- return 0;
- }
- if (!(tssa->var_info[tssa->ops[idx].op1_use].type & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
- return 0;
- }
- return 1;
- }
- }
- }
- }
- return 0;
- }
- typedef struct _zend_tssa {
- zend_ssa ssa;
- const zend_op **tssa_opcodes;
- int used_stack;
- } zend_tssa;
- static const zend_op _nop_opcode = {0};
- static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uint32_t parent_trace, uint32_t exit_num, zend_script *script, const zend_op_array **op_arrays, int *num_op_arrays_ptr)
- {
- zend_ssa *tssa;
- zend_ssa_op *ssa_ops, *op;
- zend_ssa_var *ssa_vars;
- zend_ssa_var_info *ssa_var_info;
- const zend_op_array *op_array;
- const zend_op *opline;
- const zend_op **ssa_opcodes;
- zend_jit_trace_rec *p;
- int i, v, idx, len, ssa_ops_count, vars_count, ssa_vars_count;
- zend_jit_trace_stack *stack;
- uint32_t build_flags = ZEND_SSA_RC_INFERENCE | ZEND_SSA_USE_CV_RESULTS;
- uint32_t optimization_level = 0;
- int call_level, level, num_op_arrays, used_stack, max_used_stack;
- size_t frame_size, stack_top, stack_size, stack_bottom;
- zend_jit_op_array_trace_extension *jit_extension;
- zend_ssa *ssa;
- zend_jit_trace_stack_frame *frame, *top, *call;
- zend_ssa_var_info return_value_info;
- /* 1. Count number of TSSA opcodes;
- * Count number of activation frames;
- * Calculate size of abstract stack;
- * Construct regular SSA for involved op_array */
- op_array = trace_buffer->op_array;
- stack_top = stack_size = zend_jit_trace_frame_size(op_array);
- stack_bottom = 0;
- p = trace_buffer + ZEND_JIT_TRACE_START_REC_SIZE;
- ssa_ops_count = 0;
- call_level = 0;
- level = 0;
- num_op_arrays = 0;
- /* Remember op_array to cleanup */
- op_arrays[num_op_arrays++] = op_array;
- /* Build SSA */
- ssa = zend_jit_trace_build_ssa(op_array, script);
- for (;;p++) {
- if (p->op == ZEND_JIT_TRACE_VM) {
- if (JIT_G(opt_level) < ZEND_JIT_LEVEL_OPT_FUNC) {
- const zend_op *opline = p->opline;
- switch (opline->opcode) {
- case ZEND_INCLUDE_OR_EVAL:
- ssa->cfg.flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
- break;
- case ZEND_FETCH_R:
- case ZEND_FETCH_W:
- case ZEND_FETCH_RW:
- case ZEND_FETCH_FUNC_ARG:
- case ZEND_FETCH_IS:
- case ZEND_FETCH_UNSET:
- case ZEND_UNSET_VAR:
- case ZEND_ISSET_ISEMPTY_VAR:
- if (opline->extended_value & ZEND_FETCH_LOCAL) {
- ssa->cfg.flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
- } else if ((opline->extended_value & (ZEND_FETCH_GLOBAL | ZEND_FETCH_GLOBAL_LOCK)) &&
- !op_array->function_name) {
- ssa->cfg.flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
- }
- break;
- }
- }
- ssa_ops_count += zend_jit_trace_op_len(p->opline);
- } else if (p->op == ZEND_JIT_TRACE_INIT_CALL) {
- call_level++;
- stack_top += zend_jit_trace_frame_size(p->op_array);
- if (stack_top > stack_size) {
- stack_size = stack_top;
- }
- } else if (p->op == ZEND_JIT_TRACE_DO_ICALL) {
- if (JIT_G(opt_level) < ZEND_JIT_LEVEL_OPT_FUNC) {
- if (p->func != (zend_function*)&zend_pass_function
- && (zend_string_equals_literal(p->func->common.function_name, "extract")
- || zend_string_equals_literal(p->func->common.function_name, "compact")
- || zend_string_equals_literal(p->func->common.function_name, "get_defined_vars"))) {
- ssa->cfg.flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
- }
- }
- frame_size = zend_jit_trace_frame_size(p->op_array);
- if (call_level == 0) {
- if (stack_top + frame_size > stack_size) {
- stack_size = stack_top + frame_size;
- }
- } else {
- call_level--;
- stack_top -= frame_size;
- }
- } else if (p->op == ZEND_JIT_TRACE_ENTER) {
- op_array = p->op_array;
- if (call_level == 0) {
- stack_top += zend_jit_trace_frame_size(op_array);
- if (stack_top > stack_size) {
- stack_size = stack_top;
- }
- } else {
- call_level--;
- }
- level++;
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- ssa = &jit_extension->func_info.ssa;
- if (ssa->cfg.blocks_count) {
- /* pass */
- } else if (num_op_arrays == ZEND_JIT_TRACE_MAX_FUNCS) {
- /* Too many functions in single trace */
- *num_op_arrays_ptr = num_op_arrays;
- return NULL;
- } else {
- /* Remember op_array to cleanup */
- op_arrays[num_op_arrays++] = op_array;
- /* Build SSA */
- ssa = zend_jit_trace_build_ssa(op_array, script);
- }
- } else if (p->op == ZEND_JIT_TRACE_BACK) {
- if (level == 0) {
- stack_bottom += zend_jit_trace_frame_size(p->op_array);
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- ssa = &jit_extension->func_info.ssa;
- if (ssa->cfg.blocks_count) {
- /* pass */
- } else if (num_op_arrays == ZEND_JIT_TRACE_MAX_FUNCS) {
- /* Too many functions in single trace */
- *num_op_arrays_ptr = num_op_arrays;
- return NULL;
- } else {
- /* Remember op_array to cleanup */
- op_arrays[num_op_arrays++] = op_array;
- /* Build SSA */
- ssa = zend_jit_trace_build_ssa(op_array, script);
- }
- } else {
- stack_top -= zend_jit_trace_frame_size(op_array);
- level--;
- }
- op_array = p->op_array;
- } else if (p->op == ZEND_JIT_TRACE_END) {
- break;
- }
- }
- *num_op_arrays_ptr = num_op_arrays;
- /* Allocate space for abstract stack */
- JIT_G(current_frame) = frame = (zend_jit_trace_stack_frame*)((char*)zend_arena_alloc(&CG(arena), stack_bottom + stack_size) + stack_bottom);
- /* 2. Construct TSSA */
- tssa = zend_arena_calloc(&CG(arena), 1, sizeof(zend_tssa));
- tssa->cfg.flags = ZEND_SSA_TSSA;
- tssa->cfg.blocks = zend_arena_calloc(&CG(arena), 2, sizeof(zend_basic_block));
- tssa->blocks = zend_arena_calloc(&CG(arena), 2, sizeof(zend_ssa_block));
- tssa->cfg.predecessors = zend_arena_calloc(&CG(arena), 2, sizeof(int));
- if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- tssa->cfg.blocks_count = 2;
- tssa->cfg.edges_count = 2;
- tssa->cfg.predecessors[0] = 0;
- tssa->cfg.predecessors[1] = 1;
- tssa->cfg.blocks[0].flags = ZEND_BB_START|ZEND_BB_REACHABLE;
- tssa->cfg.blocks[0].successors_count = 1;
- tssa->cfg.blocks[0].predecessors_count = 0;
- tssa->cfg.blocks[0].successors = tssa->cfg.blocks[0].successors_storage;
- tssa->cfg.blocks[0].successors[0] = 1;
- tssa->cfg.blocks[0].flags = ZEND_BB_FOLLOW|ZEND_BB_TARGET|ZEND_BB_LOOP_HEADER|ZEND_BB_REACHABLE;
- tssa->cfg.blocks[1].successors_count = 1;
- tssa->cfg.blocks[1].predecessors_count = 2;
- tssa->cfg.blocks[1].successors = tssa->cfg.blocks[1].successors_storage;
- tssa->cfg.blocks[1].successors[1] = 1;
- } else {
- tssa->cfg.blocks_count = 1;
- tssa->cfg.edges_count = 0;
- tssa->cfg.blocks[0].flags = ZEND_BB_START|ZEND_BB_EXIT|ZEND_BB_REACHABLE;
- tssa->cfg.blocks[0].successors_count = 0;
- tssa->cfg.blocks[0].predecessors_count = 0;
- }
- ((zend_tssa*)tssa)->used_stack = -1;
- if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE) {
- return tssa;
- }
- tssa->ops = ssa_ops = zend_arena_alloc(&CG(arena), ssa_ops_count * sizeof(zend_ssa_op));
- memset(ssa_ops, -1, ssa_ops_count * sizeof(zend_ssa_op));
- ssa_opcodes = zend_arena_calloc(&CG(arena), ssa_ops_count + 1, sizeof(zend_op*));
- ((zend_tssa*)tssa)->tssa_opcodes = ssa_opcodes;
- ssa_opcodes[ssa_ops_count] = &_nop_opcode;
- op_array = trace_buffer->op_array;
- if (trace_buffer->start == ZEND_JIT_TRACE_START_ENTER) {
- ssa_vars_count = op_array->last_var;
- } else {
- ssa_vars_count = op_array->last_var + op_array->T;
- }
- stack = frame->stack;
- for (i = 0; i < ssa_vars_count; i++) {
- SET_STACK_VAR(stack, i, i);
- }
- if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP) {
- // TODO: For tracing, it's possible, to create pseudo Phi functions
- // at the end of loop, without this additional pass (like LuaJIT) ???
- ssa_vars_count = zend_jit_trace_add_phis(trace_buffer, ssa_vars_count, tssa, stack);
- } else if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL) {
- ssa_vars_count = zend_jit_trace_add_call_phis(trace_buffer, ssa_vars_count, tssa, stack);
- } else if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- ssa_vars_count = zend_jit_trace_add_ret_phis(trace_buffer, ssa_vars_count, tssa, stack);
- }
- p = trace_buffer + ZEND_JIT_TRACE_START_REC_SIZE;
- idx = 0;
- level = 0;
- for (;;p++) {
- if (p->op == ZEND_JIT_TRACE_VM) {
- opline = p->opline;
- ssa_opcodes[idx] = opline;
- ssa_vars_count = zend_ssa_rename_op(op_array, opline, idx, build_flags, ssa_vars_count, ssa_ops, (int*)stack);
- idx++;
- len = zend_jit_trace_op_len(p->opline);
- while (len > 1) {
- opline++;
- ssa_opcodes[idx] = opline;
- if (opline->opcode != ZEND_OP_DATA) {
- ssa_vars_count = zend_ssa_rename_op(op_array, opline, idx, build_flags, ssa_vars_count, ssa_ops, (int*)stack);
- }
- idx++;
- len--;
- }
- } else if (p->op == ZEND_JIT_TRACE_ENTER) {
- frame = zend_jit_trace_call_frame(frame, op_array);
- stack = frame->stack;
- op_array = p->op_array;
- level++;
- if (ssa_vars_count >= ZEND_JIT_TRACE_MAX_SSA_VAR) {
- return NULL;
- }
- ZEND_JIT_TRACE_SET_FIRST_SSA_VAR(p->info, ssa_vars_count);
- for (i = 0; i < op_array->last_var; i++) {
- SET_STACK_VAR(stack, i, ssa_vars_count++);
- }
- } else if (p->op == ZEND_JIT_TRACE_BACK) {
- op_array = p->op_array;
- frame = zend_jit_trace_ret_frame(frame, op_array);
- stack = frame->stack;
- if (level == 0) {
- if (ssa_vars_count >= ZEND_JIT_TRACE_MAX_SSA_VAR) {
- return NULL;
- }
- ZEND_JIT_TRACE_SET_FIRST_SSA_VAR(p->info, ssa_vars_count);
- for (i = 0; i < op_array->last_var + op_array->T; i++) {
- SET_STACK_VAR(stack, i, ssa_vars_count++);
- }
- } else {
- level--;
- }
- } else if (p->op == ZEND_JIT_TRACE_END) {
- break;
- }
- }
- op_array = trace_buffer->op_array;
- tssa->vars_count = ssa_vars_count;
- tssa->vars = ssa_vars = zend_arena_calloc(&CG(arena), tssa->vars_count, sizeof(zend_ssa_var));
- if (trace_buffer->start == ZEND_JIT_TRACE_START_ENTER) {
- vars_count = op_array->last_var;
- } else {
- vars_count = op_array->last_var + op_array->T;
- }
- i = 0;
- while (i < vars_count) {
- ssa_vars[i].var = i;
- ssa_vars[i].scc = -1;
- ssa_vars[i].definition = -1;
- ssa_vars[i].use_chain = -1;
- i++;
- }
- while (i < tssa->vars_count) {
- ssa_vars[i].var = -1;
- ssa_vars[i].scc = -1;
- ssa_vars[i].definition = -1;
- ssa_vars[i].use_chain = -1;
- i++;
- }
- if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- /* Update Phi sources */
- zend_ssa_phi *phi = tssa->blocks[1].phis;
- while (phi) {
- phi->sources[1] = STACK_VAR(stack, phi->var);
- ssa_vars[phi->ssa_var].var = phi->var;
- ssa_vars[phi->ssa_var].definition_phi = phi;
- ssa_vars[phi->sources[0]].phi_use_chain = phi;
- ssa_vars[phi->sources[1]].phi_use_chain = phi;
- phi = phi->next;
- }
- }
- /* 3. Compute use-def chains */
- idx = (ssa_ops_count - 1);
- op = ssa_ops + idx;
- while (idx >= 0) {
- opline = ssa_opcodes[idx];
- if (op->op1_use >= 0) {
- op->op1_use_chain = ssa_vars[op->op1_use].use_chain;
- ssa_vars[op->op1_use].use_chain = idx;
- }
- if (op->op2_use >= 0 && op->op2_use != op->op1_use) {
- op->op2_use_chain = ssa_vars[op->op2_use].use_chain;
- ssa_vars[op->op2_use].use_chain = idx;
- }
- if (op->result_use >= 0 && op->result_use != op->op1_use && op->result_use != op->op2_use) {
- op->res_use_chain = ssa_vars[op->result_use].use_chain;
- ssa_vars[op->result_use].use_chain = idx;
- }
- if (op->op1_def >= 0) {
- ssa_vars[op->op1_def].var = EX_VAR_TO_NUM(opline->op1.var);
- ssa_vars[op->op1_def].definition = idx;
- }
- if (op->op2_def >= 0) {
- ssa_vars[op->op2_def].var = EX_VAR_TO_NUM(opline->op2.var);
- ssa_vars[op->op2_def].definition = idx;
- }
- if (op->result_def >= 0) {
- ssa_vars[op->result_def].var = EX_VAR_TO_NUM(opline->result.var);
- ssa_vars[op->result_def].definition = idx;
- }
- op--;
- idx--;
- }
- /* 4. Type inference */
- op_array = trace_buffer->op_array;
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- ssa = &jit_extension->func_info.ssa;
- tssa->var_info = ssa_var_info = zend_arena_calloc(&CG(arena), tssa->vars_count, sizeof(zend_ssa_var_info));
- if (trace_buffer->start == ZEND_JIT_TRACE_START_ENTER) {
- i = 0;
- while (i < op_array->last_var) {
- if (i < op_array->num_args) {
- if (ssa->var_info
- && zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, i)) {
- /* pass */
- } else {
- if (ssa->vars) {
- ssa_vars[i].no_val = ssa->vars[i].no_val;
- ssa_vars[i].alias = ssa->vars[i].alias;
- } else {
- ssa_vars[i].alias = zend_jit_var_may_alias(op_array, ssa, i);
- }
- if (op_array->arg_info) {
- zend_arg_info *arg_info = &op_array->arg_info[i];
- zend_class_entry *ce;
- uint32_t tmp = zend_fetch_arg_info_type(script, arg_info, &ce);
- if (ZEND_ARG_SEND_MODE(arg_info)) {
- tmp |= MAY_BE_REF;
- }
- ssa_var_info[i].type = tmp;
- ssa_var_info[i].ce = ce;
- ssa_var_info[i].is_instanceof = 1;
- } else {
- ssa_var_info[i].type = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- }
- }
- } else {
- if (ssa->vars) {
- ssa_vars[i].no_val = ssa->vars[i].no_val;
- ssa_vars[i].alias = ssa->vars[i].alias;
- } else {
- ssa_vars[i].alias = zend_jit_var_may_alias(op_array, ssa, i);
- }
- if (ssa_vars[i].alias == NO_ALIAS) {
- ssa_var_info[i].type = MAY_BE_UNDEF;
- } else {
- ssa_var_info[i].type = MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- }
- }
- i++;
- }
- } else {
- int parent_vars_count = 0;
- zend_jit_trace_stack *parent_stack = NULL;
- i = 0;
- if (parent_trace) {
- parent_vars_count = MIN(zend_jit_traces[parent_trace].exit_info[exit_num].stack_size,
- op_array->last_var + op_array->T);
- if (parent_vars_count) {
- parent_stack =
- zend_jit_traces[parent_trace].stack_map +
- zend_jit_traces[parent_trace].exit_info[exit_num].stack_offset;
- }
- }
- while (i < op_array->last_var + op_array->T) {
- if (!ssa->var_info
- || !zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, i)) {
- if (ssa->vars && i < ssa->vars_count) {
- ssa_vars[i].alias = ssa->vars[i].alias;
- } else {
- ssa_vars[i].alias = zend_jit_var_may_alias(op_array, ssa, i);
- }
- if (i < op_array->last_var) {
- ssa_var_info[i].type = MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- } else {
- ssa_var_info[i].type = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- }
- }
- if (i < parent_vars_count) {
- /* Initialize TSSA variable from parent trace */
- zend_uchar op_type = STACK_TYPE(parent_stack, i);
- if (op_type != IS_UNKNOWN) {
- ssa_var_info[i].type &= zend_jit_trace_type_to_info(op_type);
- if (!ssa_var_info[i].type
- && op_type == IS_UNDEF
- && i >= op_array->last_var) {
- // TODO: It's better to use NULL instead of UNDEF for temporary variables
- ssa_var_info[i].type |= MAY_BE_UNDEF;
- }
- }
- }
- i++;
- }
- }
- if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- /* Propagate initial value through Phi functions */
- zend_ssa_phi *phi = tssa->blocks[1].phis;
- while (phi) {
- if (!ssa->var_info
- || !zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, phi->ssa_var)) {
- ssa_vars[phi->ssa_var].alias = ssa_vars[phi->sources[0]].alias;
- ssa_var_info[phi->ssa_var].type = ssa_var_info[phi->sources[0]].type;
- }
- phi = phi->next;
- }
- }
- frame = JIT_G(current_frame);
- top = zend_jit_trace_call_frame(frame, op_array);
- TRACE_FRAME_INIT(frame, op_array, 0, 0);
- TRACE_FRAME_SET_RETURN_SSA_VAR(frame, -1);
- frame->used_stack = 0;
- for (i = 0; i < op_array->last_var + op_array->T; i++) {
- SET_STACK_TYPE(frame->stack, i, IS_UNKNOWN, 1);
- }
- memset(&return_value_info, 0, sizeof(return_value_info));
- if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP) {
- max_used_stack = used_stack = 0;
- } else {
- max_used_stack = used_stack = -1;
- }
- p = trace_buffer + ZEND_JIT_TRACE_START_REC_SIZE;
- idx = 0;
- level = 0;
- opline = NULL;
- for (;;p++) {
- if (p->op == ZEND_JIT_TRACE_VM) {
- uint8_t orig_op1_type, orig_op2_type, op1_type, op2_type, op3_type;
- uint8_t val_type = IS_UNKNOWN;
- // zend_class_entry *op1_ce = NULL;
- zend_class_entry *op2_ce = NULL;
- opline = p->opline;
- op1_type = orig_op1_type = p->op1_type;
- op2_type = orig_op2_type = p->op2_type;
- op3_type = p->op3_type;
- if (op1_type & (IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)) {
- op1_type = IS_UNKNOWN;
- }
- if (op1_type != IS_UNKNOWN) {
- op1_type &= ~IS_TRACE_PACKED;
- }
- if (op2_type & (IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)) {
- op2_type = IS_UNKNOWN;
- }
- if (op3_type & (IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)) {
- op3_type = IS_UNKNOWN;
- }
- if ((p+1)->op == ZEND_JIT_TRACE_OP1_TYPE) {
- // op1_ce = (zend_class_entry*)(p+1)->ce;
- p++;
- }
- if ((p+1)->op == ZEND_JIT_TRACE_OP2_TYPE) {
- op2_ce = (zend_class_entry*)(p+1)->ce;
- p++;
- }
- if ((p+1)->op == ZEND_JIT_TRACE_VAL_INFO) {
- val_type = (p+1)->op1_type;
- p++;
- }
- switch (opline->opcode) {
- case ZEND_ASSIGN_OP:
- if (opline->extended_value == ZEND_POW
- || opline->extended_value == ZEND_DIV) {
- // TODO: check for division by zero ???
- break;
- }
- if (opline->op1_type != IS_CV || opline->result_type != IS_UNUSED) {
- break;
- }
- ADD_OP1_TRACE_GUARD();
- ADD_OP2_TRACE_GUARD();
- break;
- case ZEND_ASSIGN_DIM_OP:
- if (opline->extended_value == ZEND_POW
- || opline->extended_value == ZEND_DIV) {
- // TODO: check for division by zero ???
- break;
- }
- if (opline->result_type != IS_UNUSED) {
- break;
- }
- if (op3_type != IS_UNKNOWN
- && !zend_jit_supported_binary_op(
- opline->extended_value, MAY_BE_ANY, (1<<op3_type))) {
- break;
- }
- ZEND_FALLTHROUGH;
- case ZEND_ASSIGN_DIM:
- if (opline->op1_type == IS_CV) {
- if ((opline+1)->op1_type == IS_CV
- && (opline+1)->op1.var == opline->op1.var) {
- /* skip $a[x] = $a; */
- break;
- }
- ADD_OP1_DATA_TRACE_GUARD();
- ADD_OP2_TRACE_GUARD();
- ADD_OP1_TRACE_GUARD();
- } else if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_INDIRECT)
- && opline->result_type == IS_UNUSED) {
- if (opline->opcode == ZEND_ASSIGN_DIM_OP) {
- ADD_OP1_DATA_TRACE_GUARD();
- }
- ADD_OP2_TRACE_GUARD();
- }
- if (op1_type == IS_ARRAY
- && ((opline->op2_type == IS_CONST
- && Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_LONG)
- || (opline->op2_type != IS_CONST
- && op2_type == IS_LONG))) {
- if (!(orig_op1_type & IS_TRACE_PACKED)) {
- zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use];
- if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)) {
- info->type |= MAY_BE_PACKED_GUARD;
- info->type &= ~MAY_BE_ARRAY_PACKED;
- }
- } else if (opline->opcode == ZEND_ASSIGN_DIM_OP
- && val_type != IS_UNKNOWN
- && val_type != IS_UNDEF) {
- zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use];
- if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)) {
- info->type |= MAY_BE_PACKED_GUARD;
- info->type &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH);
- }
- }
- }
- break;
- case ZEND_ASSIGN_OBJ_OP:
- if (opline->extended_value == ZEND_POW
- || opline->extended_value == ZEND_DIV) {
- // TODO: check for division by zero ???
- break;
- }
- if (opline->result_type != IS_UNUSED) {
- break;
- }
- ZEND_FALLTHROUGH;
- case ZEND_ASSIGN_OBJ:
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- if (opline->op2_type != IS_CONST
- || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
- || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
- break;
- }
- if (opline->opcode == ZEND_ASSIGN_OBJ_OP) {
- if (opline->op1_type == IS_CV
- && (opline+1)->op1_type == IS_CV
- && (opline+1)->op1.var == opline->op1.var) {
- /* skip $a->prop += $a; */
- break;
- }
- ADD_OP1_DATA_TRACE_GUARD();
- }
- ADD_OP1_TRACE_GUARD();
- break;
- case ZEND_CONCAT:
- case ZEND_FAST_CONCAT:
- if ((opline->op1_type == IS_CONST || orig_op1_type == IS_STRING)
- && (opline->op2_type == IS_CONST || orig_op2_type == IS_STRING)) {
- ADD_OP2_TRACE_GUARD();
- ADD_OP1_TRACE_GUARD();
- }
- break;
- case ZEND_IS_EQUAL:
- case ZEND_IS_NOT_EQUAL:
- case ZEND_IS_SMALLER:
- case ZEND_IS_SMALLER_OR_EQUAL:
- case ZEND_CASE:
- case ZEND_IS_IDENTICAL:
- case ZEND_IS_NOT_IDENTICAL:
- case ZEND_CASE_STRICT:
- case ZEND_BW_OR:
- case ZEND_BW_AND:
- case ZEND_BW_XOR:
- case ZEND_SL:
- case ZEND_SR:
- case ZEND_MOD:
- case ZEND_ADD:
- case ZEND_SUB:
- case ZEND_MUL:
- // case ZEND_DIV: // TODO: check for division by zero ???
- ADD_OP2_TRACE_GUARD();
- ZEND_FALLTHROUGH;
- case ZEND_ECHO:
- case ZEND_STRLEN:
- case ZEND_COUNT:
- case ZEND_QM_ASSIGN:
- case ZEND_FE_RESET_R:
- case ZEND_FE_FETCH_R:
- ADD_OP1_TRACE_GUARD();
- break;
- case ZEND_VERIFY_RETURN_TYPE:
- if (opline->op1_type == IS_UNUSED) {
- /* Always throws */
- break;
- }
- if (opline->op1_type == IS_CONST) {
- /* TODO Different instruction format, has return value */
- break;
- }
- if (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) {
- /* Not worth bothering with */
- break;
- }
- ADD_OP1_TRACE_GUARD();
- break;
- case ZEND_FETCH_DIM_FUNC_ARG:
- if (!frame
- || !frame->call
- || !frame->call->func
- || !TRACE_FRAME_IS_LAST_SEND_BY_VAL(frame->call)) {
- break;
- }
- ADD_OP2_TRACE_GUARD();
- ADD_OP1_TRACE_GUARD();
- break;
- case ZEND_PRE_INC:
- case ZEND_PRE_DEC:
- case ZEND_POST_INC:
- case ZEND_POST_DEC:
- if (opline->op1_type != IS_CV) {
- break;
- }
- ADD_OP1_TRACE_GUARD();
- break;
- case ZEND_ASSIGN:
- if (opline->op1_type != IS_CV) {
- break;
- }
- ADD_OP2_TRACE_GUARD();
- if (op1_type != IS_UNKNOWN
- && (tssa->var_info[tssa->ops[idx].op1_use].type & MAY_BE_REF)) {
- ADD_OP1_TRACE_GUARD();
- }
- break;
- case ZEND_CAST:
- if (opline->extended_value != op1_type) {
- break;
- }
- ADD_OP1_TRACE_GUARD();
- break;
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_BOOL:
- case ZEND_BOOL_NOT:
- ADD_OP1_TRACE_GUARD();
- break;
- case ZEND_ISSET_ISEMPTY_CV:
- if ((opline->extended_value & ZEND_ISEMPTY)) {
- // TODO: support for empty() ???
- break;
- }
- ADD_OP1_TRACE_GUARD();
- break;
- case ZEND_IN_ARRAY:
- if (opline->op1_type == IS_VAR || opline->op1_type == IS_TMP_VAR) {
- break;
- }
- ADD_OP1_TRACE_GUARD();
- break;
- case ZEND_ISSET_ISEMPTY_DIM_OBJ:
- if ((opline->extended_value & ZEND_ISEMPTY)) {
- // TODO: support for empty() ???
- break;
- }
- ZEND_FALLTHROUGH;
- case ZEND_FETCH_DIM_R:
- case ZEND_FETCH_DIM_IS:
- case ZEND_FETCH_LIST_R:
- ADD_OP1_TRACE_GUARD();
- ADD_OP2_TRACE_GUARD();
- if (op1_type == IS_ARRAY
- && opline->op1_type != IS_CONST
- && ((opline->op2_type == IS_CONST
- && Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_LONG)
- || (opline->op2_type != IS_CONST
- && op2_type == IS_LONG))) {
- zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use];
- if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)) {
- info->type |= MAY_BE_PACKED_GUARD;
- if (orig_op1_type & IS_TRACE_PACKED) {
- info->type &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH);
- } else {
- info->type &= ~MAY_BE_ARRAY_PACKED;
- }
- }
- }
- break;
- case ZEND_FETCH_DIM_W:
- case ZEND_FETCH_DIM_RW:
- // case ZEND_FETCH_DIM_UNSET:
- case ZEND_FETCH_LIST_W:
- if (opline->op1_type != IS_CV
- && (orig_op1_type == IS_UNKNOWN
- || !(orig_op1_type & IS_TRACE_INDIRECT))) {
- break;
- }
- ADD_OP1_TRACE_GUARD();
- ADD_OP2_TRACE_GUARD();
- if (op1_type == IS_ARRAY
- && !(orig_op1_type & IS_TRACE_PACKED)
- && ((opline->op2_type == IS_CONST
- && Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_LONG)
- || (opline->op2_type != IS_CONST
- && op2_type == IS_LONG))) {
- zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use];
- if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)) {
- info->type |= MAY_BE_PACKED_GUARD;
- info->type &= ~MAY_BE_ARRAY_PACKED;
- }
- }
- break;
- case ZEND_SEND_VAL_EX:
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_VAR_NO_REF_EX:
- if (opline->op2_type == IS_CONST) {
- /* Named parameters not supported in JIT */
- break;
- }
- if (opline->op2.num > MAX_ARG_FLAG_NUM) {
- goto propagate_arg;
- }
- ZEND_FALLTHROUGH;
- case ZEND_SEND_VAL:
- case ZEND_SEND_VAR:
- case ZEND_SEND_VAR_NO_REF:
- case ZEND_SEND_FUNC_ARG:
- if (opline->op2_type == IS_CONST) {
- /* Named parameters not supported in JIT */
- break;
- }
- ADD_OP1_TRACE_GUARD();
- propagate_arg:
- /* Propagate argument type */
- if (frame->call
- && frame->call->func
- && frame->call->func->type == ZEND_USER_FUNCTION
- && opline->op2.num <= frame->call->func->op_array.num_args) {
- uint32_t info;
- if (opline->op1_type == IS_CONST) {
- info = _const_op_type(RT_CONSTANT(opline, opline->op1));
- } else {
- ZEND_ASSERT(ssa_ops[idx].op1_use >= 0);
- info = ssa_var_info[ssa_ops[idx].op1_use].type & ~MAY_BE_GUARD;
- }
- if (frame->call->func->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
- zend_arg_info *arg_info;
- ZEND_ASSERT(frame->call->func->op_array.arg_info);
- arg_info = &frame->call->func->op_array.arg_info[opline->op2.num - 1];
- if (ZEND_TYPE_IS_SET(arg_info->type)) {
- zend_class_entry *ce;
- uint32_t tmp = zend_fetch_arg_info_type(script, arg_info, &ce);
- info &= tmp;
- if (!info) {
- break;
- }
- }
- }
- if (opline->op1_type == IS_CV && (info & MAY_BE_RC1)) {
- info |= MAY_BE_RCN;
- }
- if (info & MAY_BE_UNDEF) {
- info |= MAY_BE_NULL;
- info &= ~MAY_BE_UNDEF;
- }
- if (ARG_SHOULD_BE_SENT_BY_REF(frame->call->func, opline->op2.num)) {
- info |= MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY;
- }
- SET_STACK_INFO(frame->call->stack, opline->op2.num - 1, info);
- }
- break;
- case ZEND_RETURN:
- ADD_OP1_TRACE_GUARD();
- /* Propagate return value types */
- if (opline->op1_type == IS_UNUSED) {
- return_value_info.type = MAY_BE_NULL;
- } else if (opline->op1_type == IS_CONST) {
- return_value_info.type = _const_op_type(RT_CONSTANT(opline, opline->op1));
- } else {
- ZEND_ASSERT(ssa_ops[idx].op1_use >= 0);
- return_value_info = ssa_var_info[ssa_ops[idx].op1_use];
- if (return_value_info.type & MAY_BE_UNDEF) {
- return_value_info.type &= ~MAY_BE_UNDEF;
- return_value_info.type |= MAY_BE_NULL;
- }
- if (return_value_info.type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
- /* CVs are going to be destructed and the reference-counter
- of return value may be decremented to 1 */
- return_value_info.type |= MAY_BE_RC1;
- }
- return_value_info.type &= ~MAY_BE_GUARD;
- }
- break;
- case ZEND_CHECK_FUNC_ARG:
- if (!frame
- || !frame->call
- || !frame->call->func) {
- break;
- }
- if (opline->op2_type == IS_CONST
- || opline->op2.num > MAX_ARG_FLAG_NUM) {
- /* Named parameters not supported in JIT */
- TRACE_FRAME_SET_LAST_SEND_UNKNOWN(frame->call);
- break;
- }
- if (ARG_SHOULD_BE_SENT_BY_REF(frame->call->func, opline->op2.num)) {
- TRACE_FRAME_SET_LAST_SEND_BY_REF(frame->call);
- } else {
- TRACE_FRAME_SET_LAST_SEND_BY_VAL(frame->call);
- }
- break;
- case ZEND_FETCH_OBJ_FUNC_ARG:
- if (!frame
- || !frame->call
- || !frame->call->func
- || !TRACE_FRAME_IS_LAST_SEND_BY_VAL(frame->call)) {
- break;
- }
- ZEND_FALLTHROUGH;
- case ZEND_FETCH_OBJ_R:
- case ZEND_FETCH_OBJ_IS:
- case ZEND_FETCH_OBJ_W:
- if (opline->op2_type != IS_CONST
- || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
- || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
- break;
- }
- if (opline->op1_type != IS_UNUSED && op1_type == IS_OBJECT) {
- ADD_OP1_TRACE_GUARD();
- }
- break;
- case ZEND_INIT_METHOD_CALL:
- if (opline->op2_type != IS_CONST
- || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING) {
- break;
- }
- ADD_OP1_TRACE_GUARD();
- break;
- case ZEND_INIT_DYNAMIC_CALL:
- if (orig_op2_type == IS_OBJECT && op2_ce == zend_ce_closure) {
- ADD_OP2_TRACE_GUARD();
- }
- break;
- case ZEND_SEND_ARRAY:
- case ZEND_SEND_UNPACK:
- case ZEND_CHECK_UNDEF_ARGS:
- case ZEND_INCLUDE_OR_EVAL:
- max_used_stack = used_stack = -1;
- break;
- case ZEND_TYPE_CHECK:
- if (opline->extended_value == MAY_BE_RESOURCE) {
- // TODO: support for is_resource() ???
- break;
- }
- if (op1_type != IS_UNKNOWN
- && (opline->extended_value == (1 << op1_type)
- || opline->extended_value == MAY_BE_ANY - (1 << op1_type))) {
- /* add guards only for exact checks, to avoid code duplication */
- ADD_OP1_TRACE_GUARD();
- }
- break;
- case ZEND_ROPE_INIT:
- case ZEND_ROPE_ADD:
- case ZEND_ROPE_END:
- if (op2_type == IS_STRING) {
- ADD_OP2_TRACE_GUARD();
- }
- break;
- default:
- break;
- }
- len = zend_jit_trace_op_len(opline);
- if (ssa->var_info) {
- /* Add statically inferred ranges */
- if (ssa_ops[idx].op1_def >= 0) {
- zend_jit_trace_copy_ssa_var_range(op_array, ssa, ssa_opcodes, tssa, ssa_ops[idx].op1_def);
- }
- if (ssa_ops[idx].op2_def >= 0) {
- zend_jit_trace_copy_ssa_var_range(op_array, ssa, ssa_opcodes, tssa, ssa_ops[idx].op2_def);
- }
- if (ssa_ops[idx].result_def >= 0) {
- zend_jit_trace_copy_ssa_var_range(op_array, ssa, ssa_opcodes, tssa, ssa_ops[idx].result_def);
- }
- if (len == 2 && (opline+1)->opcode == ZEND_OP_DATA) {
- if (ssa_ops[idx+1].op1_def >= 0) {
- zend_jit_trace_copy_ssa_var_range(op_array, ssa, ssa_opcodes, tssa, ssa_ops[idx+1].op1_def);
- }
- if (ssa_ops[idx+1].op2_def >= 0) {
- zend_jit_trace_copy_ssa_var_range(op_array, ssa, ssa_opcodes, tssa, ssa_ops[idx+1].op2_def);
- }
- if (ssa_ops[idx+1].result_def >= 0) {
- zend_jit_trace_copy_ssa_var_range(op_array, ssa, ssa_opcodes, tssa, ssa_ops[idx+1].result_def);
- }
- }
- } else {
- if (ssa_ops[idx].op1_def >= 0) {
- ssa_vars[ssa_ops[idx].op1_def].alias = zend_jit_var_may_alias(op_array, ssa, EX_VAR_TO_NUM(opline->op1.var));
- if (ssa_ops[idx].op1_use < 0 || !(ssa_var_info[ssa_ops[idx].op1_use].type & MAY_BE_REF)) {
- zend_jit_trace_propagate_range(op_array, ssa_opcodes, tssa, ssa_ops[idx].op1_def);
- }
- }
- if (ssa_ops[idx].op2_def >= 0) {
- ssa_vars[ssa_ops[idx].op2_def].alias = zend_jit_var_may_alias(op_array, ssa, EX_VAR_TO_NUM(opline->op2.var));
- if (ssa_ops[idx].op2_use < 0 || !(ssa_var_info[ssa_ops[idx].op2_use].type & MAY_BE_REF)) {
- zend_jit_trace_propagate_range(op_array, ssa_opcodes, tssa, ssa_ops[idx].op2_def);
- }
- }
- if (ssa_ops[idx].result_def >= 0) {
- ssa_vars[ssa_ops[idx].result_def].alias = zend_jit_var_may_alias(op_array, ssa, EX_VAR_TO_NUM(opline->result.var));
- if (ssa_ops[idx].result_use < 0 || !(ssa_var_info[ssa_ops[idx].result_use].type & MAY_BE_REF)) {
- zend_jit_trace_propagate_range(op_array, ssa_opcodes, tssa, ssa_ops[idx].result_def);
- }
- }
- if (len == 2 && (opline+1)->opcode == ZEND_OP_DATA) {
- if (ssa_ops[idx+1].op1_def >= 0) {
- ssa_vars[ssa_ops[idx+1].op1_def].alias = zend_jit_var_may_alias(op_array, ssa, EX_VAR_TO_NUM((opline+1)->op1.var));
- if (ssa_ops[idx+1].op1_use < 0 || !(ssa_var_info[ssa_ops[idx+1].op1_use].type & MAY_BE_REF)) {
- zend_jit_trace_propagate_range(op_array, ssa_opcodes, tssa, ssa_ops[idx+1].op1_def);
- }
- }
- if (ssa_ops[idx+1].op2_def >= 0) {
- ssa_vars[ssa_ops[idx+1].op2_def].alias = zend_jit_var_may_alias(op_array, ssa, EX_VAR_TO_NUM((opline+1)->op2.var));
- if (ssa_ops[idx+1].op2_use < 0 || !(ssa_var_info[ssa_ops[idx+1].op2_use].type & MAY_BE_REF)) {
- zend_jit_trace_propagate_range(op_array, ssa_opcodes, tssa, ssa_ops[idx+1].op2_def);
- }
- }
- if (ssa_ops[idx+1].result_def >= 0) {
- ssa_vars[ssa_ops[idx+1].result_def].alias = zend_jit_var_may_alias(op_array, ssa, EX_VAR_TO_NUM((opline+1)->result.var));
- if (ssa_ops[idx+1].result_use < 0 || !(ssa_var_info[ssa_ops[idx+1].result_use].type & MAY_BE_REF)) {
- zend_jit_trace_propagate_range(op_array, ssa_opcodes, tssa, ssa_ops[idx+1].result_def);
- }
- }
- }
- }
- if (opline->opcode == ZEND_RECV_INIT
- && !(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
- /* RECV_INIT always copy the constant */
- ssa_var_info[ssa_ops[idx].result_def].type = _const_op_type(RT_CONSTANT(opline, opline->op2));
- } else if ((opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW)
- && ssa_opcodes[idx + 1] == ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value)) {
- if (ssa_ops[idx].op2_use >= 0 && ssa_ops[idx].op2_def >= 0) {
- ssa_var_info[ssa_ops[idx].op2_def] = ssa_var_info[ssa_ops[idx].op2_use];
- }
- } else {
- if (zend_update_type_info(op_array, tssa, script, (zend_op*)opline, ssa_ops + idx, ssa_opcodes, optimization_level) == FAILURE) {
- // TODO:
- assert(0);
- }
- if (opline->opcode == ZEND_ASSIGN_DIM_OP
- && ssa_ops[idx].op1_def > 0
- && op1_type == IS_ARRAY
- && (orig_op1_type & IS_TRACE_PACKED)
- && val_type != IS_UNKNOWN
- && val_type != IS_UNDEF
- && ((opline->op2_type == IS_CONST
- && Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_LONG)
- || (opline->op2_type != IS_CONST
- && op2_type == IS_LONG))) {
- zend_ssa_var_info *info = &ssa_var_info[ssa_ops[idx].op1_def];
- info->type &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH);
- }
- }
- if (ssa->var_info) {
- /* Add statically inferred restrictions */
- if (ssa_ops[idx].op1_def >= 0) {
- if (opline->opcode == ZEND_SEND_VAR_EX
- && frame
- && frame->call
- && frame->call->func
- && !ARG_SHOULD_BE_SENT_BY_REF(frame->call->func, opline->op2.num)) {
- ssa_var_info[ssa_ops[idx].op1_def] = ssa_var_info[ssa_ops[idx].op1_use];
- ssa_var_info[ssa_ops[idx].op1_def].type &= ~MAY_BE_GUARD;
- if (ssa_var_info[ssa_ops[idx].op1_def].type & MAY_BE_RC1) {
- ssa_var_info[ssa_ops[idx].op1_def].type |= MAY_BE_RCN;
- }
- } else {
- zend_jit_trace_restrict_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, ssa_ops[idx].op1_def);
- }
- }
- if (ssa_ops[idx].op2_def >= 0) {
- if ((opline->opcode != ZEND_FE_FETCH_R && opline->opcode != ZEND_FE_FETCH_RW)
- || ssa_opcodes[idx + 1] != ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value)) {
- zend_jit_trace_restrict_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, ssa_ops[idx].op2_def);
- }
- }
- if (ssa_ops[idx].result_def >= 0) {
- zend_jit_trace_restrict_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, ssa_ops[idx].result_def);
- }
- }
- idx++;
- while (len > 1) {
- opline++;
- if (opline->opcode != ZEND_OP_DATA) {
- if (ssa->var_info) {
- /* Add statically inferred ranges */
- if (ssa_ops[idx].op1_def >= 0) {
- zend_jit_trace_copy_ssa_var_range(op_array, ssa, ssa_opcodes, tssa, ssa_ops[idx].op1_def);
- }
- if (ssa_ops[idx].op2_def >= 0) {
- zend_jit_trace_copy_ssa_var_range(op_array, ssa, ssa_opcodes, tssa, ssa_ops[idx].op2_def);
- }
- if (ssa_ops[idx].result_def >= 0) {
- zend_jit_trace_copy_ssa_var_range(op_array, ssa, ssa_opcodes, tssa, ssa_ops[idx].result_def);
- }
- } else {
- if (ssa_ops[idx].op1_def >= 0) {
- ssa_vars[ssa_ops[idx].op1_def].alias = zend_jit_var_may_alias(op_array, ssa, EX_VAR_TO_NUM(opline->op1.var));
- if (ssa_ops[idx].op1_use < 0 || !(ssa_var_info[ssa_ops[idx].op1_use].type & MAY_BE_REF)) {
- zend_jit_trace_propagate_range(op_array, ssa_opcodes, tssa, ssa_ops[idx].op1_def);
- }
- }
- if (ssa_ops[idx].op2_def >= 0) {
- ssa_vars[ssa_ops[idx].op2_def].alias = zend_jit_var_may_alias(op_array, ssa, EX_VAR_TO_NUM(opline->op2.var));
- if (ssa_ops[idx].op2_use < 0 || !(ssa_var_info[ssa_ops[idx].op2_use].type & MAY_BE_REF)) {
- zend_jit_trace_propagate_range(op_array, ssa_opcodes, tssa, ssa_ops[idx].op2_def);
- }
- }
- if (ssa_ops[idx].result_def >= 0) {
- ssa_vars[ssa_ops[idx].result_def].alias = zend_jit_var_may_alias(op_array, ssa, EX_VAR_TO_NUM(opline->result.var));
- if (ssa_ops[idx].result_use < 0 || !(ssa_var_info[ssa_ops[idx].result_use].type & MAY_BE_REF)) {
- zend_jit_trace_propagate_range(op_array, ssa_opcodes, tssa, ssa_ops[idx].result_def);
- }
- }
- }
- if (opline->opcode == ZEND_RECV_INIT
- && !(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
- /* RECV_INIT always copy the constant */
- ssa_var_info[ssa_ops[idx].result_def].type = _const_op_type(RT_CONSTANT(opline, opline->op2));
- } else {
- if (zend_update_type_info(op_array, tssa, script, (zend_op*)opline, ssa_ops + idx, ssa_opcodes, optimization_level) == FAILURE) {
- // TODO:
- assert(0);
- }
- }
- }
- if (ssa->var_info) {
- /* Add statically inferred restrictions */
- if (ssa_ops[idx].op1_def >= 0) {
- zend_jit_trace_restrict_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, ssa_ops[idx].op1_def);
- }
- if (ssa_ops[idx].op2_def >= 0) {
- zend_jit_trace_restrict_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, ssa_ops[idx].op2_def);
- }
- if (ssa_ops[idx].result_def >= 0) {
- zend_jit_trace_restrict_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, ssa_ops[idx].result_def);
- }
- }
- idx++;
- len--;
- }
- } else if (p->op == ZEND_JIT_TRACE_ENTER) {
- op_array = p->op_array;
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- ssa = &jit_extension->func_info.ssa;
- call = frame->call;
- if (!call) {
- /* Trace missed INIT_FCALL opcode */
- call = top;
- TRACE_FRAME_INIT(call, op_array, 0, 0);
- call->used_stack = 0;
- top = zend_jit_trace_call_frame(top, op_array);
- for (i = 0; i < op_array->last_var + op_array->T; i++) {
- SET_STACK_TYPE(call->stack, i, IS_UNKNOWN, 1);
- }
- } else {
- ZEND_ASSERT(&call->func->op_array == op_array);
- }
- frame->call = call->prev;
- call->prev = frame;
- TRACE_FRAME_SET_RETURN_SSA_VAR(call, find_return_ssa_var(p - 1, ssa_ops + (idx - 1)));
- frame = call;
- level++;
- i = 0;
- v = ZEND_JIT_TRACE_GET_FIRST_SSA_VAR(p->info);
- while (i < op_array->last_var) {
- ssa_vars[v].var = i;
- if (i < op_array->num_args) {
- if (ssa->var_info
- && zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, v)) {
- /* pass */
- } else {
- ssa_vars[v].alias = zend_jit_var_may_alias(op_array, ssa, i);
- if (op_array->arg_info) {
- zend_arg_info *arg_info = &op_array->arg_info[i];
- zend_class_entry *ce;
- uint32_t tmp = zend_fetch_arg_info_type(script, arg_info, &ce);
- if (ZEND_ARG_SEND_MODE(arg_info)) {
- tmp |= MAY_BE_REF;
- }
- ssa_var_info[v].type = tmp;
- ssa_var_info[v].ce = ce;
- ssa_var_info[v].is_instanceof = 1;
- } else {
- ssa_var_info[v].type = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- }
- }
- } else {
- if (ssa->vars) {
- ssa_vars[v].no_val = ssa->vars[i].no_val;
- ssa_vars[v].alias = ssa->vars[i].alias;
- } else {
- ssa_vars[v].alias = zend_jit_var_may_alias(op_array, ssa, i);
- }
- if (ssa_vars[v].alias == NO_ALIAS) {
- ssa_var_info[v].type = MAY_BE_UNDEF;
- } else {
- ssa_var_info[v].type = MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- }
- }
- if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
- && i < op_array->num_args) {
- /* Propagate argument type */
- ssa_var_info[v].type &= STACK_INFO(frame->stack, i);
- }
- i++;
- v++;
- }
- } else if (p->op == ZEND_JIT_TRACE_BACK) {
- op_array = p->op_array;
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- ssa = &jit_extension->func_info.ssa;
- if (level == 0) {
- i = 0;
- v = ZEND_JIT_TRACE_GET_FIRST_SSA_VAR(p->info);
- while (i < op_array->last_var) {
- ssa_vars[v].var = i;
- if (!ssa->var_info
- || !zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, v)) {
- ssa_var_info[v].type = MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- }
- i++;
- v++;
- }
- while (i < op_array->last_var + op_array->T) {
- ssa_vars[v].var = i;
- if (!ssa->var_info
- || !zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, v)) {
- ssa_var_info[v].type = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- }
- i++;
- v++;
- }
- if (return_value_info.type != 0) {
- zend_jit_trace_rec *q = p + 1;
- while (q->op == ZEND_JIT_TRACE_INIT_CALL) {
- q++;
- }
- if (q->op == ZEND_JIT_TRACE_VM
- || (q->op == ZEND_JIT_TRACE_END
- && q->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET)) {
- const zend_op *opline = q->opline - 1;
- if (opline->result_type != IS_UNUSED) {
- ssa_var_info[
- ZEND_JIT_TRACE_GET_FIRST_SSA_VAR(p->info) +
- EX_VAR_TO_NUM(opline->result.var)] = return_value_info;
- }
- }
- memset(&return_value_info, 0, sizeof(return_value_info));
- }
- } else {
- level--;
- if (return_value_info.type != 0) {
- if ((p+1)->op == ZEND_JIT_TRACE_VM) {
- const zend_op *opline = (p+1)->opline - 1;
- if (opline->result_type != IS_UNUSED) {
- if (TRACE_FRAME_RETURN_SSA_VAR(frame) >= 0) {
- ssa_var_info[TRACE_FRAME_RETURN_SSA_VAR(frame)] = return_value_info;
- }
- }
- }
- memset(&return_value_info, 0, sizeof(return_value_info));
- }
- }
- top = frame;
- if (frame->prev) {
- if (used_stack > 0) {
- used_stack -= frame->used_stack;
- }
- frame = frame->prev;
- ZEND_ASSERT(&frame->func->op_array == op_array);
- } else {
- max_used_stack = used_stack = -1;
- frame = zend_jit_trace_ret_frame(frame, op_array);
- TRACE_FRAME_INIT(frame, op_array, 0, 0);
- TRACE_FRAME_SET_RETURN_SSA_VAR(frame, -1);
- frame->used_stack = 0;
- for (i = 0; i < op_array->last_var + op_array->T; i++) {
- SET_STACK_TYPE(frame->stack, i, IS_UNKNOWN, 1);
- }
- }
- } else if (p->op == ZEND_JIT_TRACE_INIT_CALL) {
- call = top;
- TRACE_FRAME_INIT(call, p->func, 0, 0);
- call->prev = frame->call;
- call->used_stack = 0;
- frame->call = call;
- top = zend_jit_trace_call_frame(top, p->op_array);
- if (p->func && p->func->type == ZEND_USER_FUNCTION) {
- for (i = 0; i < p->op_array->last_var + p->op_array->T; i++) {
- SET_STACK_INFO(call->stack, i, -1);
- }
- }
- if (used_stack >= 0
- && !(p->info & ZEND_JIT_TRACE_FAKE_INIT_CALL)) {
- if (p->func == NULL || (p-1)->op != ZEND_JIT_TRACE_VM) {
- max_used_stack = used_stack = -1;
- } else {
- const zend_op *opline = (p-1)->opline;
- switch (opline->opcode) {
- case ZEND_INIT_FCALL:
- case ZEND_INIT_FCALL_BY_NAME:
- case ZEND_INIT_NS_FCALL_BY_NAME:
- case ZEND_INIT_METHOD_CALL:
- case ZEND_INIT_DYNAMIC_CALL:
- //case ZEND_INIT_STATIC_METHOD_CALL:
- //case ZEND_INIT_USER_CALL:
- //case ZEND_NEW:
- frame->used_stack = zend_vm_calc_used_stack(opline->extended_value, (zend_function*)p->func);
- used_stack += frame->used_stack;
- if (used_stack > max_used_stack) {
- max_used_stack = used_stack;
- }
- break;
- default:
- max_used_stack = used_stack = -1;
- }
- }
- }
- } else if (p->op == ZEND_JIT_TRACE_DO_ICALL) {
- call = frame->call;
- if (call) {
- top = call;
- frame->call = call->prev;
- }
- if (idx > 0
- && ssa_ops[idx-1].result_def >= 0
- && (p->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)
- && !(p->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
- ZEND_ASSERT(ssa_opcodes[idx-1] == opline);
- ZEND_ASSERT(opline->opcode == ZEND_DO_ICALL ||
- opline->opcode == ZEND_DO_FCALL ||
- opline->opcode == ZEND_DO_FCALL_BY_NAME);
- if (opline->result_type != IS_UNDEF) {
- zend_class_entry *ce;
- const zend_function *func = p->func;
- zend_arg_info *ret_info = func->common.arg_info - 1;
- uint32_t ret_type = zend_fetch_arg_info_type(NULL, ret_info, &ce);
- ssa_var_info[ssa_ops[idx-1].result_def].type &= ret_type;
- }
- }
- } else if (p->op == ZEND_JIT_TRACE_END) {
- break;
- }
- }
- ((zend_tssa*)tssa)->used_stack = max_used_stack;
- if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- /* Propagate guards through Phi sources */
- zend_ssa_phi *phi = tssa->blocks[1].phis;
- op_array = trace_buffer->op_array;
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- ssa = &jit_extension->func_info.ssa;
- while (phi) {
- uint32_t t = ssa_var_info[phi->ssa_var].type;
- if ((t & MAY_BE_GUARD) && tssa->vars[phi->ssa_var].alias == NO_ALIAS) {
- uint32_t t0 = ssa_var_info[phi->sources[0]].type;
- uint32_t t1 = ssa_var_info[phi->sources[1]].type;
- if (((t0 | t1) & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) == (t & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF))) {
- if (!((t0 | t1) & MAY_BE_GUARD)) {
- ssa_var_info[phi->ssa_var].type = t & ~MAY_BE_GUARD;
- }
- } else if ((t1 & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) == (t & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF))) {
- if (!(t1 & MAY_BE_GUARD)
- || is_checked_guard(tssa, ssa_opcodes, phi->sources[1], phi->ssa_var)) {
- ssa_var_info[phi->ssa_var].type = t & ~MAY_BE_GUARD;
- t0 = (t & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) |
- (t0 & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) |
- MAY_BE_GUARD;
- if (!(t0 & MAY_BE_ARRAY)) {
- t0 &= ~(MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_ARRAY_KEY_ANY);
- }
- if (!(t0 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
- t0 &= ~(MAY_BE_RC1|MAY_BE_RCN);
- }
- ssa_var_info[phi->sources[0]].type = t0;
- ssa_var_info[phi->sources[0]].type = t0;
- }
- } else {
- if ((t0 & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != (t & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF))) {
- t0 = (t & t0 & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) |
- (t0 & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) |
- MAY_BE_GUARD;
- if (!(t0 & MAY_BE_ARRAY)) {
- t0 &= ~(MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_ARRAY_KEY_ANY);
- }
- if (!(t0 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
- t0 &= ~(MAY_BE_RC1|MAY_BE_RCN);
- }
- ssa_var_info[phi->sources[0]].type = t0;
- }
- if ((t1 & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != (t & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF))) {
- if (((t & t1) & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != 0
- && is_checked_guard(tssa, ssa_opcodes, phi->sources[1], phi->ssa_var)) {
- t1 = (t & t1 & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) |
- (t1 & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) |
- MAY_BE_GUARD;
- if (!(t1 & MAY_BE_ARRAY)) {
- t1 &= ~(MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_ARRAY_KEY_ANY);
- }
- if (!(t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
- t1 &= ~(MAY_BE_RC1|MAY_BE_RCN);
- }
- ssa_var_info[phi->sources[1]].type = t1;
- ssa_var_info[phi->ssa_var].type = t & ~MAY_BE_GUARD;
- }
- }
- }
- t = ssa_var_info[phi->ssa_var].type;
- }
- if ((t & MAY_BE_PACKED_GUARD) && tssa->vars[phi->ssa_var].alias == NO_ALIAS) {
- uint32_t t0 = ssa_var_info[phi->sources[0]].type;
- uint32_t t1 = ssa_var_info[phi->sources[1]].type;
- if (((t0 | t1) & MAY_BE_ARRAY_KEY_ANY) == (t & MAY_BE_ARRAY_KEY_ANY)) {
- if (!((t0 | t1) & MAY_BE_PACKED_GUARD)) {
- ssa_var_info[phi->ssa_var].type = t & ~MAY_BE_PACKED_GUARD;
- }
- } else if ((t1 & MAY_BE_ARRAY_KEY_ANY) == (t & MAY_BE_ARRAY_KEY_ANY)) {
- if (!(t1 & MAY_BE_PACKED_GUARD)) {
- ssa_var_info[phi->ssa_var].type = t & ~MAY_BE_PACKED_GUARD;
- ssa_var_info[phi->sources[0]].type =
- (t0 & ~MAY_BE_ARRAY_KEY_ANY) | (t & MAY_BE_ARRAY_KEY_ANY) | MAY_BE_PACKED_GUARD;
- }
- }
- }
- phi = phi->next;
- }
- }
- if (UNEXPECTED(JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_TSSA)) {
- if (parent_trace) {
- fprintf(stderr, "---- TRACE %d TSSA start (side trace %d/%d) %s%s%s() %s:%d\n",
- ZEND_JIT_TRACE_NUM,
- parent_trace,
- exit_num,
- trace_buffer->op_array->scope ? ZSTR_VAL(trace_buffer->op_array->scope->name) : "",
- trace_buffer->op_array->scope ? "::" : "",
- trace_buffer->op_array->function_name ?
- ZSTR_VAL(trace_buffer->op_array->function_name) : "$main",
- ZSTR_VAL(trace_buffer->op_array->filename),
- trace_buffer[1].opline->lineno);
- } else {
- fprintf(stderr, "---- TRACE %d TSSA start (%s) %s%s%s() %s:%d\n",
- ZEND_JIT_TRACE_NUM,
- zend_jit_trace_star_desc(trace_buffer->start),
- trace_buffer->op_array->scope ? ZSTR_VAL(trace_buffer->op_array->scope->name) : "",
- trace_buffer->op_array->scope ? "::" : "",
- trace_buffer->op_array->function_name ?
- ZSTR_VAL(trace_buffer->op_array->function_name) : "$main",
- ZSTR_VAL(trace_buffer->op_array->filename),
- trace_buffer[1].opline->lineno);
- }
- zend_jit_dump_trace(trace_buffer, tssa);
- if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LINK) {
- uint32_t idx = trace_buffer[1].last;
- uint32_t link_to = zend_jit_find_trace(trace_buffer[idx].opline->handler);
- fprintf(stderr, "---- TRACE %d TSSA stop (link to %d)\n",
- ZEND_JIT_TRACE_NUM,
- link_to);
- } else {
- fprintf(stderr, "---- TRACE %d TSSA stop (%s)\n",
- ZEND_JIT_TRACE_NUM,
- zend_jit_trace_stop_description[trace_buffer->stop]);
- }
- }
- return tssa;
- }
- static void zend_jit_close_var(zend_jit_trace_stack *stack, uint32_t n, int *start, int *end, uint8_t *flags, int line)
- {
- int32_t var = STACK_VAR(stack, n);
- if (var >= 0 && start[var] >= 0 && !(flags[var] & ZREG_LAST_USE)) {
- // TODO: shrink interval to last side exit ????
- end[var] = line;
- }
- }
- static void zend_jit_trace_use_var(int line, int var, int def, int use_chain, int *start, int *end, uint8_t *flags, const zend_ssa *ssa, const zend_op **ssa_opcodes, const zend_op_array *op_array, const zend_ssa *op_array_ssa)
- {
- ZEND_ASSERT(start[var] >= 0);
- ZEND_ASSERT(!(flags[var] & ZREG_LAST_USE));
- end[var] = line;
- if (def >= 0) {
- flags[var] |= ZREG_LAST_USE;
- } else if (use_chain < 0 && (flags[var] & (ZREG_LOAD|ZREG_STORE))) {
- flags[var] |= ZREG_LAST_USE;
- } else if (use_chain >= 0 && !zend_ssa_is_no_val_use(ssa_opcodes[use_chain], ssa->ops + use_chain, var)) {
- /* pass */
- } else if (op_array_ssa->vars) {
- uint32_t use = ssa_opcodes[line] - op_array->opcodes;
- if (ssa->ops[line].op1_use == var) {
- if (zend_ssa_is_last_use(op_array, op_array_ssa, op_array_ssa->ops[use].op1_use, use)) {
- flags[var] |= ZREG_LAST_USE;
- }
- } else if (ssa->ops[line].op2_use == var) {
- if (zend_ssa_is_last_use(op_array, op_array_ssa, op_array_ssa->ops[use].op2_use, use)) {
- flags[var] |= ZREG_LAST_USE;
- }
- } else if (ssa->ops[line].result_use == var) {
- if (zend_ssa_is_last_use(op_array, op_array_ssa, op_array_ssa->ops[use].result_use, use)) {
- flags[var] |= ZREG_LAST_USE;
- }
- }
- }
- }
- static zend_lifetime_interval** zend_jit_trace_allocate_registers(zend_jit_trace_rec *trace_buffer, zend_ssa *ssa, uint32_t parent_trace, uint32_t exit_num)
- {
- const zend_op **ssa_opcodes = ((zend_tssa*)ssa)->tssa_opcodes;
- zend_jit_trace_rec *p;
- const zend_op_array *op_array;
- zend_jit_op_array_trace_extension *jit_extension;
- const zend_ssa *op_array_ssa;
- const zend_ssa_op *ssa_op;
- int i, j, idx, count, level;
- int last_idx = -1;
- int *start, *end;
- uint8_t *flags;
- const zend_op_array **vars_op_array;
- zend_lifetime_interval **intervals, *list, *ival;
- void *checkpoint;
- zend_jit_trace_stack_frame *frame;
- zend_jit_trace_stack *stack;
- uint32_t parent_vars_count = parent_trace ?
- zend_jit_traces[parent_trace].exit_info[exit_num].stack_size : 0;
- zend_jit_trace_stack *parent_stack = parent_trace ?
- zend_jit_traces[parent_trace].stack_map +
- zend_jit_traces[parent_trace].exit_info[exit_num].stack_offset : NULL;
- ALLOCA_FLAG(use_heap);
- ZEND_ASSERT(ssa->var_info != NULL);
- start = do_alloca(sizeof(int) * ssa->vars_count * 2 +
- ZEND_MM_ALIGNED_SIZE(sizeof(uint8_t) * ssa->vars_count) +
- ZEND_MM_ALIGNED_SIZE(sizeof(zend_op_array*) * ssa->vars_count),
- use_heap);
- if (!start) {
- return NULL;
- }
- end = start + ssa->vars_count;
- flags = (uint8_t*)(end + ssa->vars_count);
- vars_op_array = (const zend_op_array**)(flags + ZEND_MM_ALIGNED_SIZE(sizeof(uint8_t) * ssa->vars_count));
- memset(start, -1, sizeof(int) * ssa->vars_count * 2);
- memset(flags, 0, sizeof(uint8_t) * ssa->vars_count);
- memset(ZEND_VOIDP(vars_op_array), 0, sizeof(zend_op_array*) * ssa->vars_count);
- op_array = trace_buffer->op_array;
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- op_array_ssa = &jit_extension->func_info.ssa;
- frame = JIT_G(current_frame);
- frame->prev = NULL;
- frame->func = (const zend_function*)op_array;
- stack = frame->stack;
- count = 0;
- i = 0;
- j = op_array->last_var;
- if (trace_buffer->start != ZEND_JIT_TRACE_START_ENTER) {
- j += op_array->T;
- }
- while (i < j) {
- SET_STACK_VAR(stack, i, i);
- vars_op_array[i] = op_array;
- /* We don't start intervals for variables used in Phi */
- if ((ssa->vars[i].use_chain >= 0 /*|| ssa->vars[i].phi_use_chain*/)
- && !zend_ssa_is_no_val_use(ssa_opcodes[ssa->vars[i].use_chain], ssa->ops + ssa->vars[i].use_chain, i)
- && ssa->vars[i].alias == NO_ALIAS
- && zend_jit_var_supports_reg(ssa, i)) {
- start[i] = 0;
- if (i < parent_vars_count
- && STACK_REG(parent_stack, i) != ZREG_NONE
- && STACK_REG(parent_stack, i) < ZREG_NUM) {
- /* We will try to reuse register from parent trace */
- flags[i] = STACK_FLAGS(parent_stack, i);
- count += 2;
- } else {
- flags[i] = ZREG_LOAD;
- count++;
- }
- }
- i++;
- }
- if (trace_buffer->start == ZEND_JIT_TRACE_START_ENTER) {
- j = op_array->last_var + op_array->T;
- while (i < j) {
- SET_STACK_VAR(stack, i, -1);
- i++;
- }
- }
- if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- zend_ssa_phi *phi = ssa->blocks[1].phis;
- while (phi) {
- SET_STACK_VAR(stack, phi->var, phi->ssa_var);
- vars_op_array[phi->ssa_var] = op_array;
- if (ssa->vars[phi->ssa_var].use_chain >= 0
- && ssa->vars[phi->ssa_var].alias == NO_ALIAS
- && zend_jit_var_supports_reg(ssa, phi->ssa_var)) {
- start[phi->ssa_var] = 0;
- count++;
- }
- phi = phi->next;
- }
- }
- p = trace_buffer + ZEND_JIT_TRACE_START_REC_SIZE;
- level = 0;
- ssa_op = ssa->ops;
- idx = 0;
- for (;;p++) {
- if (p->op == ZEND_JIT_TRACE_VM) {
- const zend_op *opline = p->opline;
- int len;
- bool support_opline;
- support_opline =
- zend_jit_opline_supports_reg(op_array, ssa, opline, ssa_op, p);
- if (support_opline
- && opline->opcode == ZEND_ASSIGN
- && opline->op1_type == IS_CV
- && ssa_op->op1_def >= 0
- && ssa->vars[ssa_op->op1_def].alias != NO_ALIAS) {
- /* avoid register allocation in case of possibility of indirect modification*/
- support_opline = 0;
- }
- if (ssa_op->op1_use >= 0
- && start[ssa_op->op1_use] >= 0
- && !zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op1_use)) {
- if (support_opline) {
- zend_jit_trace_use_var(idx, ssa_op->op1_use, ssa_op->op1_def, ssa_op->op1_use_chain, start, end, flags, ssa, ssa_opcodes, op_array, op_array_ssa);
- if (opline->op1_type != IS_CV) {
- if (opline->opcode == ZEND_CASE
- || opline->opcode == ZEND_CASE_STRICT
- || opline->opcode == ZEND_SWITCH_LONG
- || opline->opcode == ZEND_MATCH
- || opline->opcode == ZEND_FETCH_LIST_R
- || opline->opcode == ZEND_COPY_TMP
- || opline->opcode == ZEND_SWITCH_STRING
- || opline->opcode == ZEND_FE_FETCH_R
- || opline->opcode == ZEND_FE_FETCH_RW
- || opline->opcode == ZEND_FETCH_LIST_W
- || opline->opcode == ZEND_VERIFY_RETURN_TYPE
- || opline->opcode == ZEND_BIND_LEXICAL
- || opline->opcode == ZEND_ROPE_ADD) {
- /* The value is kept alive and may be used outside of the trace */
- flags[ssa_op->op1_use] |= ZREG_STORE;
- } else {
- flags[ssa_op->op1_use] |= ZREG_LAST_USE;
- }
- }
- } else {
- start[ssa_op->op1_use] = -1;
- end[ssa_op->op1_use] = -1;
- count--;
- }
- }
- if (ssa_op->op2_use >= 0
- && ssa_op->op2_use != ssa_op->op1_use
- && start[ssa_op->op2_use] >= 0
- && !zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op2_use)) {
- if (support_opline) {
- zend_jit_trace_use_var(idx, ssa_op->op2_use, ssa_op->op2_def, ssa_op->op2_use_chain, start, end, flags, ssa, ssa_opcodes, op_array, op_array_ssa);
- if (opline->op2_type != IS_CV) {
- flags[ssa_op->op2_use] |= ZREG_LAST_USE;
- }
- } else {
- start[ssa_op->op2_use] = -1;
- end[ssa_op->op2_use] = -1;
- count--;
- }
- }
- if (ssa_op->result_use >= 0
- && ssa_op->result_use != ssa_op->op1_use
- && ssa_op->result_use != ssa_op->op2_use
- && start[ssa_op->result_use] >= 0
- && !zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->result_use)) {
- if (support_opline) {
- zend_jit_trace_use_var(idx, ssa_op->result_use, ssa_op->result_def, ssa_op->res_use_chain, start, end, flags, ssa, ssa_opcodes, op_array, op_array_ssa);
- } else {
- start[ssa_op->result_use] = -1;
- end[ssa_op->result_use] = -1;
- count--;
- }
- }
- if (ssa_op->op1_def >= 0) {
- zend_jit_close_var(stack, EX_VAR_TO_NUM(opline->op1.var), start, end, flags, idx);
- SET_STACK_VAR(stack, EX_VAR_TO_NUM(opline->op1.var), ssa_op->op1_def);
- }
- if (ssa_op->op2_def >= 0) {
- zend_jit_close_var(stack, EX_VAR_TO_NUM(opline->op2.var), start, end, flags, idx);
- SET_STACK_VAR(stack, EX_VAR_TO_NUM(opline->op2.var), ssa_op->op2_def);
- }
- if (ssa_op->result_def >= 0) {
- zend_jit_close_var(stack, EX_VAR_TO_NUM(opline->result.var), start, end, flags, idx);
- SET_STACK_VAR(stack, EX_VAR_TO_NUM(opline->result.var), ssa_op->result_def);
- }
- if (support_opline) {
- if (ssa_op->result_def >= 0
- && (ssa->vars[ssa_op->result_def].use_chain >= 0
- || ssa->vars[ssa_op->result_def].phi_use_chain)
- && ssa->vars[ssa_op->result_def].alias == NO_ALIAS
- && zend_jit_var_supports_reg(ssa, ssa_op->result_def)) {
- if (!(ssa->var_info[ssa_op->result_def].type & MAY_BE_GUARD)
- || opline->opcode == ZEND_PRE_INC
- || opline->opcode == ZEND_PRE_DEC
- || opline->opcode == ZEND_POST_INC
- || opline->opcode == ZEND_POST_DEC
- || opline->opcode == ZEND_ADD
- || opline->opcode == ZEND_SUB
- || opline->opcode == ZEND_MUL
- || opline->opcode == ZEND_FETCH_DIM_R
- || opline->opcode == ZEND_FETCH_CONSTANT) {
- if (!(ssa->var_info[ssa_op->result_def].type & MAY_BE_DOUBLE)
- || (opline->opcode != ZEND_PRE_INC && opline->opcode != ZEND_PRE_DEC)) {
- start[ssa_op->result_def] = idx;
- vars_op_array[ssa_op->result_def] = op_array;
- count++;
- }
- }
- }
- if (ssa_op->op1_def >= 0
- && (ssa->vars[ssa_op->op1_def].use_chain >= 0
- || ssa->vars[ssa_op->op1_def].phi_use_chain)
- && ssa->vars[ssa_op->op1_def].alias == NO_ALIAS
- && zend_jit_var_supports_reg(ssa, ssa_op->op1_def)) {
- start[ssa_op->op1_def] = idx;
- vars_op_array[ssa_op->op1_def] = op_array;
- count++;
- }
- if (ssa_op->op2_def >= 0
- && (ssa->vars[ssa_op->op2_def].use_chain >= 0
- || ssa->vars[ssa_op->op2_def].phi_use_chain)
- && ssa->vars[ssa_op->op2_def].alias == NO_ALIAS
- && zend_jit_var_supports_reg(ssa, ssa_op->op2_def)) {
- start[ssa_op->op2_def] = idx;
- vars_op_array[ssa_op->op2_def] = op_array;
- count++;
- }
- }
- len = zend_jit_trace_op_len(opline);
- switch (opline->opcode) {
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_OBJ:
- case ZEND_ASSIGN_STATIC_PROP:
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- case ZEND_ASSIGN_OBJ_REF:
- case ZEND_ASSIGN_STATIC_PROP_REF:
- /* OP_DATA */
- ssa_op++;
- opline++;
- if (ssa_op->op1_use >= 0
- && start[ssa_op->op1_use] >= 0
- && !zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op1_use)) {
- if (support_opline) {
- zend_jit_trace_use_var(idx, ssa_op->op1_use, ssa_op->op1_def, ssa_op->op1_use_chain, start, end, flags, ssa, ssa_opcodes, op_array, op_array_ssa);
- if (opline->op1_type != IS_CV) {
- flags[ssa_op->op1_use] |= ZREG_LAST_USE;
- }
- } else {
- start[ssa_op->op1_use] = -1;
- end[ssa_op->op1_use] = -1;
- count--;
- }
- }
- if (ssa_op->op1_def >= 0) {
- zend_jit_close_var(stack, EX_VAR_TO_NUM(opline->op1.var), start, end, flags, idx);
- SET_STACK_VAR(stack, EX_VAR_TO_NUM(opline->op1.var), ssa_op->op1_def);
- if (support_opline
- && (ssa->vars[ssa_op->op1_def].use_chain >= 0
- || ssa->vars[ssa_op->op1_def].phi_use_chain)
- && ssa->vars[ssa_op->op1_def].alias == NO_ALIAS
- && zend_jit_var_supports_reg(ssa, ssa_op->op1_def)) {
- start[ssa_op->op1_def] = idx;
- vars_op_array[ssa_op->op1_def] = op_array;
- count++;
- }
- }
- ssa_op++;
- opline++;
- idx+=2;
- break;
- case ZEND_RECV_INIT:
- ssa_op++;
- opline++;
- idx++;
- while (opline->opcode == ZEND_RECV_INIT) {
- /* RECV_INIT doesn't support registers */
- if (ssa_op->result_def >= 0) {
- zend_jit_close_var(stack, EX_VAR_TO_NUM(opline->result.var), start, end, flags, idx);
- SET_STACK_VAR(stack, EX_VAR_TO_NUM(opline->result.var), ssa_op->result_def);
- }
- ssa_op++;
- opline++;
- idx++;
- }
- break;
- case ZEND_BIND_GLOBAL:
- ssa_op++;
- opline++;
- idx++;
- while (opline->opcode == ZEND_BIND_GLOBAL) {
- /* BIND_GLOBAL doesn't support registers */
- if (ssa_op->op1_def >= 0) {
- zend_jit_close_var(stack, EX_VAR_TO_NUM(opline->op1.var), start, end, flags, idx);
- SET_STACK_VAR(stack, EX_VAR_TO_NUM(opline->op1.var), ssa_op->op1_def);
- }
- ssa_op++;
- opline++;
- idx++;
- }
- break;
- default:
- ssa_op += len;
- idx += len;
- break;
- }
- } else if (p->op == ZEND_JIT_TRACE_ENTER) {
- /* New call frames */
- zend_jit_trace_stack_frame *prev_frame = frame;
- frame = zend_jit_trace_call_frame(frame, op_array);
- frame->prev = prev_frame;
- frame->func = (const zend_function*)p->op_array;
- stack = frame->stack;
- op_array = p->op_array;
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- op_array_ssa = &jit_extension->func_info.ssa;
- j = ZEND_JIT_TRACE_GET_FIRST_SSA_VAR(p->info);
- for (i = 0; i < op_array->last_var; i++) {
- SET_STACK_VAR(stack, i, j);
- vars_op_array[j] = op_array;
- if (ssa->vars[j].use_chain >= 0
- && ssa->vars[j].alias == NO_ALIAS
- && zend_jit_var_supports_reg(ssa, j)) {
- start[j] = idx;
- flags[j] = ZREG_LOAD;
- count++;
- }
- j++;
- }
- for (i = op_array->last_var; i < op_array->last_var + op_array->T; i++) {
- SET_STACK_VAR(stack, i, -1);
- }
- level++;
- } else if (p->op == ZEND_JIT_TRACE_BACK) {
- /* Close exiting call frames */
- for (i = 0; i < op_array->last_var; i++) {
- zend_jit_close_var(stack, i, start, end, flags, idx-1);
- }
- op_array = p->op_array;
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- op_array_ssa = &jit_extension->func_info.ssa;
- frame = zend_jit_trace_ret_frame(frame, op_array);
- stack = frame->stack;
- if (level == 0) {
- /* New return frames */
- frame->prev = NULL;
- frame->func = (const zend_function*)op_array;
- j = ZEND_JIT_TRACE_GET_FIRST_SSA_VAR(p->info);
- for (i = 0; i < op_array->last_var + op_array->T; i++) {
- SET_STACK_VAR(stack, i, j);
- vars_op_array[j] = op_array;
- if (ssa->vars[j].use_chain >= 0
- && ssa->vars[j].alias == NO_ALIAS
- && zend_jit_var_supports_reg(ssa, j)) {
- start[j] = idx;
- flags[j] = ZREG_LOAD;
- count++;
- }
- j++;
- }
- } else {
- level--;
- }
- } else if (p->op == ZEND_JIT_TRACE_END) {
- break;
- }
- }
- if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- zend_ssa_phi *phi = ssa->blocks[1].phis;
- while (phi) {
- i = phi->sources[1];
- if (start[i] >= 0 && !ssa->vars[phi->ssa_var].no_val) {
- end[i] = idx;
- flags[i] &= ~ZREG_LAST_USE;
- }
- phi = phi->next;
- }
- if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP) {
- for (i = 0; i < op_array->last_var; i++) {
- if (start[i] >= 0 && !ssa->vars[i].phi_use_chain) {
- end[i] = idx;
- flags[i] &= ~ZREG_LAST_USE;
- } else {
- zend_jit_close_var(stack, i, start, end, flags, idx);
- }
- }
- }
- } else {
- last_idx = idx;
- for (i = 0; i < op_array->last_var; i++) {
- zend_jit_close_var(stack, i, start, end, flags, idx);
- }
- while (frame->prev) {
- frame = frame->prev;
- op_array = &frame->func->op_array;
- stack = frame->stack;
- for (i = 0; i < op_array->last_var; i++) {
- zend_jit_close_var(stack, i, start, end, flags, idx);
- }
- }
- }
- if (!count) {
- free_alloca(start, use_heap);
- return NULL;
- }
- checkpoint = zend_arena_checkpoint(CG(arena));
- intervals = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_lifetime_interval));
- memset(intervals, 0, sizeof(zend_lifetime_interval*) * ssa->vars_count);
- list = zend_arena_alloc(&CG(arena), sizeof(zend_lifetime_interval) * count);
- j = 0;
- for (i = 0; i < ssa->vars_count; i++) {
- if (start[i] >= 0 && end[i] >= 0) {
- ZEND_ASSERT(j < count);
- if ((flags[i] & ZREG_LOAD) &&
- (flags[i] & ZREG_LAST_USE) &&
- end[i] == ssa->vars[i].use_chain) {
- /* skip life range with single use */
- continue;
- }
- intervals[i] = &list[j];
- list[j].ssa_var = i;
- list[j].reg = ZREG_NONE;
- list[j].flags = flags[i];
- list[j].range.start = start[i];
- list[j].range.end = end[i];
- list[j].range.next = NULL;
- list[j].hint = NULL;
- list[j].used_as_hint = NULL;
- list[j].list_next = NULL;
- j++;
- }
- }
- count = j;
- free_alloca(start, use_heap);
- start = end = NULL;
- if (!count) {
- zend_arena_release(&CG(arena), checkpoint);
- return NULL;
- }
- /* Add hints */
- if (parent_vars_count) {
- i = trace_buffer->op_array->last_var;
- if (trace_buffer->start != ZEND_JIT_TRACE_START_ENTER) {
- i += trace_buffer->op_array->T;
- }
- if ((uint32_t)i > parent_vars_count) {
- i = parent_vars_count;
- }
- while (i > 0) {
- i--;
- if (intervals[i]
- && STACK_REG(parent_stack, i) != ZREG_NONE
- && STACK_REG(parent_stack, i) < ZREG_NUM) {
- list[j].ssa_var = - 1;
- list[j].reg = STACK_REG(parent_stack, i);
- list[j].flags = 0;
- list[j].range.start = -1;
- list[j].range.end = -1;
- list[j].range.next = NULL;
- list[j].hint = NULL;
- list[j].used_as_hint = NULL;
- list[j].list_next = NULL;
- intervals[i]->hint = &list[j];
- j++;
- }
- }
- }
- if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- zend_ssa_phi *phi = ssa->blocks[1].phis;
- while (phi) {
- if (intervals[phi->ssa_var]) {
- if (intervals[phi->sources[1]]
- && (ssa->var_info[phi->sources[1]].type & MAY_BE_ANY) ==
- (ssa->var_info[phi->ssa_var].type & MAY_BE_ANY)) {
- intervals[phi->sources[1]]->hint = intervals[phi->ssa_var];
- }
- }
- phi = phi->next;
- }
- }
- for (i = 0; i < ssa->vars_count; i++) {
- if (intervals[i] && !intervals[i]->hint) {
- if (ssa->vars[i].definition >= 0) {
- uint32_t line = ssa->vars[i].definition;
- const zend_op *opline = ssa_opcodes[line];
- switch (opline->opcode) {
- case ZEND_QM_ASSIGN:
- case ZEND_POST_INC:
- case ZEND_POST_DEC:
- if (ssa->ops[line].op1_use >= 0 &&
- intervals[ssa->ops[line].op1_use] &&
- (i == ssa->ops[line].op1_def ||
- (i == ssa->ops[line].result_def &&
- (ssa->ops[line].op1_def < 0 ||
- !intervals[ssa->ops[line].op1_def])))) {
- zend_jit_add_hint(intervals, i, ssa->ops[line].op1_use);
- }
- break;
- case ZEND_SEND_VAR:
- if (opline->op2_type == IS_CONST) {
- /* Named parameters not supported in JIT */
- break;
- }
- case ZEND_PRE_INC:
- case ZEND_PRE_DEC:
- if (i == ssa->ops[line].op1_def &&
- ssa->ops[line].op1_use >= 0 &&
- intervals[ssa->ops[line].op1_use]) {
- zend_jit_add_hint(intervals, i, ssa->ops[line].op1_use);
- }
- break;
- case ZEND_ASSIGN:
- if (ssa->ops[line].op2_use >= 0 &&
- intervals[ssa->ops[line].op2_use] &&
- (i == ssa->ops[line].op2_def ||
- (i == ssa->ops[line].op1_def &&
- (ssa->ops[line].op2_def < 0 ||
- !intervals[ssa->ops[line].op2_def])) ||
- (i == ssa->ops[line].result_def &&
- (ssa->ops[line].op2_def < 0 ||
- !intervals[ssa->ops[line].op2_def]) &&
- (ssa->ops[line].op1_def < 0 ||
- !intervals[ssa->ops[line].op1_def])))) {
- zend_jit_add_hint(intervals, i, ssa->ops[line].op2_use);
- }
- break;
- case ZEND_SUB:
- case ZEND_ADD:
- case ZEND_MUL:
- case ZEND_BW_OR:
- case ZEND_BW_AND:
- case ZEND_BW_XOR:
- if (i == ssa->ops[line].result_def) {
- if (ssa->ops[line].op1_use >= 0 &&
- intervals[ssa->ops[line].op1_use] &&
- ssa->ops[line].op1_use_chain < 0 &&
- !ssa->vars[ssa->ops[line].op1_use].phi_use_chain &&
- (ssa->var_info[i].type & MAY_BE_ANY) ==
- (ssa->var_info[ssa->ops[line].op1_use].type & MAY_BE_ANY)) {
- zend_ssa_phi *phi = ssa->vars[ssa->ops[line].op1_use].definition_phi;
- if (phi &&
- intervals[phi->sources[1]] &&
- intervals[phi->sources[1]]->hint == intervals[ssa->ops[line].op1_use]) {
- break;
- }
- zend_jit_add_hint(intervals, i, ssa->ops[line].op1_use);
- } else if (opline->opcode != ZEND_SUB &&
- ssa->ops[line].op2_use >= 0 &&
- intervals[ssa->ops[line].op2_use] &&
- ssa->ops[line].op2_use_chain < 0 &&
- !ssa->vars[ssa->ops[line].op2_use].phi_use_chain &&
- (ssa->var_info[i].type & MAY_BE_ANY) ==
- (ssa->var_info[ssa->ops[line].op2_use].type & MAY_BE_ANY)) {
- zend_ssa_phi *phi = ssa->vars[ssa->ops[line].op2_use].definition_phi;
- if (phi &&
- intervals[phi->sources[1]] &&
- intervals[phi->sources[1]]->hint == intervals[ssa->ops[line].op2_use]) {
- break;
- }
- zend_jit_add_hint(intervals, i, ssa->ops[line].op2_use);
- }
- }
- break;
- }
- }
- }
- }
- list = zend_jit_sort_intervals(intervals, ssa->vars_count);
- if (list) {
- ival = list;
- while (ival) {
- if (ival->hint) {
- ival->hint->used_as_hint = ival;
- }
- ival = ival->list_next;
- }
- }
- if (list) {
- if (JIT_G(debug) & ZEND_JIT_DEBUG_REG_ALLOC) {
- fprintf(stderr, "---- TRACE %d Live Ranges\n", ZEND_JIT_TRACE_NUM);
- ival = list;
- while (ival) {
- zend_jit_dump_lifetime_interval(vars_op_array[ival->ssa_var], ssa, ival);
- ival = ival->list_next;
- }
- }
- }
- /* Linear Scan Register Allocation (op_array is not actually used, only fn_flags matters) */
- list = zend_jit_linear_scan(&dummy_op_array, ssa_opcodes, ssa, list);
- if (list) {
- zend_lifetime_interval *ival, *next;
- memset(intervals, 0, ssa->vars_count * sizeof(zend_lifetime_interval*));
- ival = list;
- count = 0;
- while (ival != NULL) {
- ZEND_ASSERT(ival->reg != ZREG_NONE);
- count++;
- next = ival->list_next;
- ival->list_next = intervals[ival->ssa_var];
- intervals[ival->ssa_var] = ival;
- ival = next;
- }
- if (!count) {
- zend_arena_release(&CG(arena), checkpoint);
- return NULL;
- }
- /* Add LOAD flag to registers that can't reuse register from parent trace */
- if (parent_vars_count) {
- i = trace_buffer->op_array->last_var;
- if (trace_buffer->start != ZEND_JIT_TRACE_START_ENTER) {
- i += trace_buffer->op_array->T;
- }
- if ((uint32_t)i > parent_vars_count) {
- i = parent_vars_count;
- }
- while (i > 0) {
- i--;
- if (intervals[i] && intervals[i]->reg != STACK_REG(parent_stack, i)) {
- intervals[i]->flags |= ZREG_LOAD;
- }
- }
- }
- /* SSA resolution */
- if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- zend_ssa_phi *phi = ssa->blocks[1].phis;
- while (phi) {
- int def = phi->ssa_var;
- int use = phi->sources[1];
- if (intervals[def]) {
- if (!intervals[use]) {
- intervals[def]->flags |= ZREG_LOAD;
- if ((intervals[def]->flags & ZREG_LAST_USE)
- && ssa->vars[def].use_chain >= 0
- && ssa->vars[def].use_chain == intervals[def]->range.end) {
- /* remove interval used once */
- intervals[def] = NULL;
- count--;
- }
- } else if (intervals[def]->reg != intervals[use]->reg) {
- intervals[def]->flags |= ZREG_LOAD;
- if (ssa->vars[use].use_chain >= 0) {
- intervals[use]->flags |= ZREG_STORE;
- } else {
- intervals[use] = NULL;
- count--;
- }
- } else {
- use = phi->sources[0];
- ZEND_ASSERT(!intervals[use]);
- intervals[use] = zend_arena_alloc(&CG(arena), sizeof(zend_lifetime_interval));
- intervals[use]->ssa_var = phi->sources[0];
- intervals[use]->reg = intervals[def]->reg;
- intervals[use]->flags = ZREG_LOAD;
- intervals[use]->range.start = 0;
- intervals[use]->range.end = 0;
- intervals[use]->range.next = NULL;
- intervals[use]->hint = NULL;
- intervals[use]->used_as_hint = NULL;
- intervals[use]->list_next = NULL;
- }
- } else if (intervals[use]
- && (!ssa->vars[def].no_val
- || ssa->var_info[def].type != ssa->var_info[use].type)) {
- if (ssa->vars[use].use_chain >= 0) {
- intervals[use]->flags |= ZREG_STORE;
- } else {
- intervals[use] = NULL;
- count--;
- }
- }
- phi = phi->next;
- }
- } else {
- for (i = 0; i < ssa->vars_count; i++) {
- if (intervals[i]
- && intervals[i]->range.end == last_idx
- && !(intervals[i]->flags & (ZREG_LOAD|ZREG_STORE))) {
- intervals[i]->flags |= ZREG_STORE;
- }
- }
- }
- if (!count) {
- zend_arena_release(&CG(arena), checkpoint);
- return NULL;
- }
- if (JIT_G(debug) & ZEND_JIT_DEBUG_REG_ALLOC) {
- fprintf(stderr, "---- TRACE %d Allocated Live Ranges\n", ZEND_JIT_TRACE_NUM);
- for (i = 0; i < ssa->vars_count; i++) {
- ival = intervals[i];
- while (ival) {
- zend_jit_dump_lifetime_interval(vars_op_array[ival->ssa_var], ssa, ival);
- ival = ival->list_next;
- }
- }
- }
- return intervals;
- }
- zend_arena_release(&CG(arena), checkpoint); //???
- return NULL;
- }
- static void zend_jit_trace_clenup_stack(zend_jit_trace_stack *stack, const zend_op *opline, const zend_ssa_op *ssa_op, const zend_ssa *ssa, zend_lifetime_interval **ra)
- {
- uint32_t line = ssa_op - ssa->ops;
- if (ssa_op->op1_use >= 0
- && ra[ssa_op->op1_use]
- && ra[ssa_op->op1_use]->range.end == line) {
- SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
- }
- if (ssa_op->op2_use >= 0
- && ra[ssa_op->op2_use]
- && ra[ssa_op->op2_use]->range.end == line) {
- SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op2.var), ZREG_NONE);
- }
- if (ssa_op->result_use >= 0
- && ra[ssa_op->result_use]
- && ra[ssa_op->result_use]->range.end == line) {
- SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_NONE);
- }
- }
- static void zend_jit_trace_setup_ret_counter(const zend_op *opline, size_t offset)
- {
- zend_op *next_opline = (zend_op*)(opline + 1);
- if (JIT_G(hot_return) && !ZEND_OP_TRACE_INFO(next_opline, offset)->trace_flags) {
- ZEND_ASSERT(zend_jit_ret_trace_counter_handler != NULL);
- if (!ZEND_OP_TRACE_INFO(next_opline, offset)->counter) {
- ZEND_OP_TRACE_INFO(next_opline, offset)->counter =
- &zend_jit_hot_counters[ZEND_JIT_COUNTER_NUM];
- ZEND_JIT_COUNTER_NUM = (ZEND_JIT_COUNTER_NUM + 1) % ZEND_HOT_COUNTERS_COUNT;
- }
- ZEND_OP_TRACE_INFO(next_opline, offset)->trace_flags = ZEND_JIT_TRACE_START_RETURN;
- next_opline->handler = (const void*)zend_jit_ret_trace_counter_handler;
- }
- }
- static bool zend_jit_may_delay_fetch_this(const zend_op_array *op_array, zend_ssa *ssa, const zend_op **ssa_opcodes, const zend_ssa_op *ssa_op)
- {
- int var = ssa_op->result_def;
- int i;
- int use = ssa->vars[var].use_chain;
- const zend_op *opline;
- if (use < 0
- || ssa->vars[var].phi_use_chain
- || ssa->ops[use].op1_use != var
- || ssa->ops[use].op1_use_chain != -1) {
- return 0;
- }
- opline = ssa_opcodes[use];
- if (opline->opcode == ZEND_INIT_METHOD_CALL) {
- return (opline->op2_type == IS_CONST &&
- Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_STRING);
- } else if (opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG) {
- if (!JIT_G(current_frame)
- || !JIT_G(current_frame)->call
- || !JIT_G(current_frame)->call->func
- || !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
- return 0;
- }
- } else if (opline->opcode != ZEND_FETCH_OBJ_R
- && opline->opcode != ZEND_FETCH_OBJ_IS
- && opline->opcode != ZEND_FETCH_OBJ_W
- && opline->opcode != ZEND_ASSIGN_OBJ
- && opline->opcode != ZEND_ASSIGN_OBJ_OP
- && opline->opcode != ZEND_PRE_INC_OBJ
- && opline->opcode != ZEND_PRE_DEC_OBJ
- && opline->opcode != ZEND_POST_INC_OBJ
- && opline->opcode != ZEND_POST_DEC_OBJ) {
- return 0;
- }
- if (opline->op2_type != IS_CONST
- || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
- || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
- return 0;
- }
- if (opline->opcode == ZEND_ASSIGN_OBJ_OP) {
- if (opline->op1_type == IS_CV
- && (opline+1)->op1_type == IS_CV
- && (opline+1)->op1.var == opline->op1.var) {
- /* skip $a->prop += $a; */
- return 0;
- }
- if (!zend_jit_supported_binary_op(
- opline->extended_value, MAY_BE_ANY, OP1_DATA_INFO())) {
- return 0;
- }
- }
- for (i = ssa->vars[var].definition; i < use; i++) {
- if (ssa_opcodes[i]->opcode == ZEND_DO_UCALL
- || ssa_opcodes[i]->opcode == ZEND_DO_FCALL_BY_NAME
- || ssa_opcodes[i]->opcode == ZEND_DO_FCALL
- || ssa_opcodes[i]->opcode == ZEND_INCLUDE_OR_EVAL) {
- return 0;
- }
- }
- return 1;
- }
- static int zend_jit_trace_stack_needs_deoptimization(zend_jit_trace_stack *stack, uint32_t stack_size)
- {
- uint32_t i;
- for (i = 0; i < stack_size; i++) {
- if (STACK_REG(stack, i) != ZREG_NONE
- && !(STACK_FLAGS(stack, i) & (ZREG_LOAD|ZREG_STORE))) {
- return 1;
- }
- }
- return 0;
- }
- static int zend_jit_trace_exit_needs_deoptimization(uint32_t trace_num, uint32_t exit_num)
- {
- const zend_op *opline = zend_jit_traces[trace_num].exit_info[exit_num].opline;
- uint32_t flags = zend_jit_traces[trace_num].exit_info[exit_num].flags;
- uint32_t stack_size;
- zend_jit_trace_stack *stack;
- if (opline || (flags & (ZEND_JIT_EXIT_RESTORE_CALL|ZEND_JIT_EXIT_FREE_OP1|ZEND_JIT_EXIT_FREE_OP2))) {
- return 1;
- }
- stack_size = zend_jit_traces[trace_num].exit_info[exit_num].stack_size;
- stack = zend_jit_traces[trace_num].stack_map + zend_jit_traces[trace_num].exit_info[exit_num].stack_offset;
- return zend_jit_trace_stack_needs_deoptimization(stack, stack_size);
- }
- static int zend_jit_trace_deoptimization(dasm_State **Dst,
- uint32_t flags,
- const zend_op *opline,
- zend_jit_trace_stack *parent_stack,
- int parent_vars_count,
- zend_ssa *ssa,
- zend_jit_trace_stack *stack,
- zend_lifetime_interval **ra,
- bool polymorphic_side_trace)
- {
- int i;
- bool has_constants = 0;
- bool has_unsaved_vars = 0;
- // TODO: Merge this loop with the following register LOAD loop to implement parallel move ???
- for (i = 0; i < parent_vars_count; i++) {
- int8_t reg = STACK_REG(parent_stack, i);
- if (reg != ZREG_NONE) {
- if (reg < ZREG_NUM) {
- if (ssa && ssa->vars[i].no_val) {
- /* pass */
- } else if (ra && ra[i] && ra[i]->reg == reg) {
- /* register already loaded by parent trace */
- if (stack) {
- SET_STACK_REG_EX(stack, i, reg, STACK_FLAGS(parent_stack, i));
- }
- has_unsaved_vars = 1;
- } else {
- uint8_t type = STACK_TYPE(parent_stack, i);
- if (!(STACK_FLAGS(parent_stack, i) & (ZREG_LOAD|ZREG_STORE))
- && !zend_jit_store_var(Dst, 1 << type, i, reg,
- STACK_MEM_TYPE(parent_stack, i) != type)) {
- return 0;
- }
- if (stack) {
- SET_STACK_TYPE(stack, i, type, 1);
- }
- }
- } else {
- /* delay custom deoptimization instructions to prevent register clobbering */
- has_constants = 1;
- }
- }
- }
- if (has_unsaved_vars
- && (has_constants
- || (flags & (ZEND_JIT_EXIT_RESTORE_CALL|ZEND_JIT_EXIT_FREE_OP1|ZEND_JIT_EXIT_FREE_OP2)))) {
- for (i = 0; i < parent_vars_count; i++) {
- int8_t reg = STACK_REG(parent_stack, i);
- if (reg != ZREG_NONE) {
- if (reg < ZREG_NUM) {
- if (ssa && ssa->vars[i].no_val) {
- /* pass */
- } else if (ra && ra[i] && ra[i]->reg == reg) {
- uint8_t type = STACK_TYPE(parent_stack, i);
- if (stack) {
- SET_STACK_TYPE(stack, i, type, 1);
- }
- if (!(STACK_FLAGS(parent_stack, i) & (ZREG_LOAD|ZREG_STORE))
- && !zend_jit_store_var(Dst, 1 << type, i, reg,
- STACK_MEM_TYPE(parent_stack, i) != type)) {
- return 0;
- }
- }
- }
- }
- }
- }
- if (has_constants) {
- for (i = 0; i < parent_vars_count; i++) {
- int8_t reg = STACK_REG(parent_stack, i);
- if (reg != ZREG_NONE) {
- if (reg < ZREG_NUM) {
- /* pass */
- } else if (reg == ZREG_THIS) {
- if (polymorphic_side_trace) {
- ssa->var_info[i].delayed_fetch_this = 1;
- if (stack) {
- SET_STACK_REG(stack, i, ZREG_THIS);
- }
- } else if (!zend_jit_load_this(Dst, EX_NUM_TO_VAR(i))) {
- return 0;
- }
- } else {
- if (reg == ZREG_ZVAL_COPY_GPR0
- &&!zend_jit_escape_if_undef_r0(Dst, i, flags, opline)) {
- return 0;
- }
- if (!zend_jit_store_const(Dst, i, reg)) {
- return 0;
- }
- }
- }
- }
- }
- if (flags & ZEND_JIT_EXIT_RESTORE_CALL) {
- if (!zend_jit_save_call_chain(Dst, -1)) {
- return 0;
- }
- }
- if (flags & ZEND_JIT_EXIT_FREE_OP2) {
- const zend_op *op = opline - 1;
- if (!zend_jit_free_op(Dst, op, -1, op->op2.var)) {
- return 0;
- }
- }
- if (flags & ZEND_JIT_EXIT_FREE_OP1) {
- const zend_op *op = opline - 1;
- if (!zend_jit_free_op(Dst, op, -1, op->op1.var)) {
- return 0;
- }
- }
- if (flags & (ZEND_JIT_EXIT_FREE_OP1|ZEND_JIT_EXIT_FREE_OP2)) {
- if (!zend_jit_check_exception(Dst)) {
- return 0;
- }
- }
- if ((flags & ZEND_JIT_EXIT_METHOD_CALL) && !polymorphic_side_trace) {
- if (!zend_jit_free_trampoline(Dst)) {
- return 0;
- }
- }
- return 1;
- }
- static void zend_jit_trace_set_var_range(zend_ssa_var_info *info, zend_long min, zend_long max)
- {
- info->has_range = 1;
- info->range.min = min;
- info->range.max = max;
- info->range.underflow = 0;
- info->range.overflow = 0;
- }
- static void zend_jit_trace_update_condition_ranges(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, bool exit_if_true)
- {
- zend_long op1_min, op1_max, op2_min, op2_max;
- if ((OP1_INFO() & MAY_BE_ANY) != MAY_BE_LONG
- || (OP1_INFO() & MAY_BE_ANY) != MAY_BE_LONG) {
- return;
- }
- op1_min = OP1_MIN_RANGE();
- op1_max = OP1_MAX_RANGE();
- op2_min = OP2_MIN_RANGE();
- op2_max = OP2_MAX_RANGE();
- switch (opline->opcode) {
- case ZEND_IS_EQUAL:
- case ZEND_CASE:
- case ZEND_IS_IDENTICAL:
- case ZEND_CASE_STRICT:
- case ZEND_IS_NOT_IDENTICAL:
- if (!exit_if_true) {
- /* op1 == op2 */
- if (ssa_op->op1_use >= 0) {
- zend_jit_trace_set_var_range(
- &ssa->var_info[ssa_op->op1_use],
- MAX(op1_min, op2_min),
- MIN(op1_max, op2_max));
- }
- if (ssa_op->op2_use >= 0) {
- zend_jit_trace_set_var_range(
- &ssa->var_info[ssa_op->op2_use],
- MAX(op2_min, op1_min),
- MIN(op2_max, op1_max));
- }
- }
- break;
- case ZEND_IS_NOT_EQUAL:
- if (exit_if_true) {
- /* op1 == op2 */
- if (ssa_op->op1_use >= 0) {
- zend_jit_trace_set_var_range(
- &ssa->var_info[ssa_op->op1_use],
- MAX(op1_min, op2_min),
- MIN(op1_max, op2_max));
- }
- if (ssa_op->op2_use >= 0) {
- zend_jit_trace_set_var_range(
- &ssa->var_info[ssa_op->op2_use],
- MAX(op2_min, op1_min),
- MIN(op2_max, op1_max));
- }
- }
- break;
- case ZEND_IS_SMALLER_OR_EQUAL:
- if (!exit_if_true) {
- /* op1 <= op2 */
- if (ssa_op->op1_use >= 0) {
- zend_jit_trace_set_var_range(
- &ssa->var_info[ssa_op->op1_use],
- op1_min,
- MIN(op1_max, op2_max));
- }
- if (ssa_op->op2_use >= 0) {
- zend_jit_trace_set_var_range(
- &ssa->var_info[ssa_op->op2_use],
- MAX(op2_min, op1_min),
- op2_max);
- }
- } else {
- /* op1 > op2 */
- if (ssa_op->op1_use >= 0) {
- zend_jit_trace_set_var_range(
- &ssa->var_info[ssa_op->op1_use],
- op2_min != ZEND_LONG_MAX ? MAX(op1_min, op2_min + 1) : op1_min,
- op1_max);
- }
- if (ssa_op->op2_use >= 0) {
- zend_jit_trace_set_var_range(
- &ssa->var_info[ssa_op->op2_use],
- op2_min,
- op2_max != ZEND_LONG_MIN ?MIN(op2_max, op1_max - 1) : op1_max);
- }
- }
- break;
- case ZEND_IS_SMALLER:
- if (!exit_if_true) {
- /* op1 < op2 */
- if (ssa_op->op1_use >= 0) {
- zend_jit_trace_set_var_range(
- &ssa->var_info[ssa_op->op1_use],
- op1_min,
- op2_max != ZEND_LONG_MIN ? MIN(op1_max, op2_max - 1) : op1_max);
- }
- if (ssa_op->op2_use >= 0) {
- zend_jit_trace_set_var_range(
- &ssa->var_info[ssa_op->op2_use],
- op1_min != ZEND_LONG_MAX ? MAX(op2_min, op1_min + 1) : op2_min,
- op2_max);
- }
- } else {
- /* op1 >= op2 */
- if (ssa_op->op1_use >= 0) {
- zend_jit_trace_set_var_range(
- &ssa->var_info[ssa_op->op1_use],
- MAX(op1_min, op2_min),
- op1_max);
- }
- if (ssa_op->op2_use >= 0) {
- zend_jit_trace_set_var_range(
- &ssa->var_info[ssa_op->op2_use],
- op2_min,
- MIN(op2_max, op1_max));
- }
- }
- break;
- }
- }
- static bool zend_jit_may_skip_comparison(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_ssa *ssa, const zend_op **ssa_opcodes, const zend_op_array *op_array)
- {
- zend_uchar prev_opcode;
- if (opline->op1_type == IS_CONST
- && Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_LONG
- && Z_LVAL_P(RT_CONSTANT(opline, opline->op1)) == 0) {
- if (ssa_op->op2_use >= 0) {
- if ((ssa_op-1)->op1_def == ssa_op->op2_use) {
- ssa_op--;
- opline = ssa_opcodes[ssa_op - ssa->ops];
- prev_opcode = opline->opcode;
- if (prev_opcode == ZEND_PRE_INC
- || prev_opcode == ZEND_PRE_DEC
- || prev_opcode == ZEND_POST_INC
- || prev_opcode == ZEND_POST_DEC) {
- return (OP1_INFO() & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)-MAY_BE_LONG)) == 0;
- }
- } else if ((ssa_op-1)->result_def == ssa_op->op2_use) {
- ssa_op--;
- opline = ssa_opcodes[ssa_op - ssa->ops];
- prev_opcode = opline->opcode;
- if (prev_opcode == ZEND_ADD
- || prev_opcode == ZEND_SUB) {
- return (OP1_INFO() & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)-MAY_BE_LONG)) == 0 &&
- (OP2_INFO() & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)-MAY_BE_LONG)) == 0;
- }
- }
- }
- } else if (opline->op2_type == IS_CONST
- && Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_LONG
- && Z_LVAL_P(RT_CONSTANT(opline, opline->op2)) == 0) {
- if (ssa_op->op1_use >= 0) {
- if ((ssa_op-1)->op1_def == ssa_op->op1_use) {
- ssa_op--;
- opline = ssa_opcodes[ssa_op - ssa->ops];
- prev_opcode = opline->opcode;
- if (prev_opcode == ZEND_PRE_INC
- || prev_opcode == ZEND_PRE_DEC
- || prev_opcode == ZEND_POST_INC
- || prev_opcode == ZEND_POST_DEC) {
- return (OP1_INFO() & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)-MAY_BE_LONG)) == 0;
- }
- } else if ((ssa_op-1)->result_def == ssa_op->op1_use) {
- ssa_op--;
- opline = ssa_opcodes[ssa_op - ssa->ops];
- prev_opcode = opline->opcode;
- if (prev_opcode == ZEND_ADD
- || prev_opcode == ZEND_SUB) {
- return (OP1_INFO() & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)-MAY_BE_LONG)) == 0 &&
- (OP2_INFO() & ((MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)-MAY_BE_LONG)) == 0;
- }
- }
- }
- } else {
- const zend_ssa_op *prev_ssa_op = ssa_op - 1;
- prev_opcode = ssa_opcodes[prev_ssa_op - ssa->ops]->opcode;
- if ((prev_opcode == ZEND_JMPZ || prev_opcode == ZEND_JMPNZ)
- && prev_ssa_op != ssa->ops
- && prev_ssa_op->op1_use >= 0
- && prev_ssa_op->op1_use == (prev_ssa_op-1)->result_def) {
- prev_ssa_op--;
- prev_opcode = ssa_opcodes[prev_ssa_op - ssa->ops]->opcode;
- }
- if (ssa_op->op1_use == prev_ssa_op->op1_use
- && ssa_op->op2_use == prev_ssa_op->op2_use) {
- if (prev_opcode == ZEND_IS_EQUAL
- || prev_opcode == ZEND_IS_NOT_EQUAL
- || prev_opcode == ZEND_IS_SMALLER
- || prev_opcode == ZEND_IS_SMALLER_OR_EQUAL
- || prev_opcode == ZEND_CASE
- || prev_opcode == ZEND_IS_IDENTICAL
- || prev_opcode == ZEND_IS_NOT_IDENTICAL
- || prev_opcode == ZEND_CASE_STRICT) {
- if (ssa_op->op1_use < 0) {
- if (RT_CONSTANT(opline, opline->op1) != RT_CONSTANT(&ssa_opcodes[prev_ssa_op - ssa->ops], ssa_opcodes[prev_ssa_op - ssa->ops]->op1)) {
- return 0;
- }
- }
- if (ssa_op->op2_use < 0) {
- if (RT_CONSTANT(opline, opline->op2) != RT_CONSTANT(&ssa_opcodes[prev_ssa_op - ssa->ops], ssa_opcodes[prev_ssa_op - ssa->ops]->op2)) {
- return 0;
- }
- }
- return 1;
- }
- }
- }
- return 0;
- }
- static bool zend_jit_trace_next_is_send_result(const zend_op *opline,
- zend_jit_trace_rec *p,
- zend_jit_trace_stack_frame *frame)
- {
- if (opline->result_type == IS_TMP_VAR
- && (p+1)->op == ZEND_JIT_TRACE_VM
- && (p+1)->opline == opline + 1
- && ((opline+1)->opcode == ZEND_SEND_VAL
- || ((opline+1)->opcode == ZEND_SEND_VAL_EX
- && frame
- && frame->call
- && frame->call->func
- && !ARG_MUST_BE_SENT_BY_REF(frame->call->func, (opline+1)->op2.num)))
- && (opline+1)->op1_type == IS_TMP_VAR
- && (opline+1)->op2_type != IS_CONST /* Named parameters not supported in JIT */
- && (opline+1)->op1.var == opline->result.var) {
- if (frame->call && frame->call->func) {
- uint8_t res_type = (p+1)->op1_type;
- if (res_type != IS_UNKNOWN && !(res_type & IS_TRACE_REFERENCE) ) {
- zend_jit_trace_send_type(opline+1, frame->call, res_type);
- }
- }
- return 1;
- }
- return 0;
- }
- static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t parent_trace, uint32_t exit_num)
- {
- const void *handler = NULL;
- dasm_State* dasm_state = NULL;
- zend_script *script = NULL;
- zend_lifetime_interval **ra = NULL;
- zend_string *name = NULL;
- void *checkpoint;
- const zend_op_array *op_array;
- zend_ssa *ssa, *op_array_ssa;
- const zend_op **ssa_opcodes;
- zend_jit_trace_rec *p;
- zend_jit_op_array_trace_extension *jit_extension;
- int num_op_arrays = 0;
- zend_jit_trace_info *t;
- const zend_op_array *op_arrays[ZEND_JIT_TRACE_MAX_FUNCS];
- zend_uchar smart_branch_opcode;
- const void *exit_addr;
- uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info, op1_data_info;
- bool send_result = 0;
- bool skip_comparison;
- zend_jit_addr op1_addr, op1_def_addr, op2_addr, op2_def_addr, res_addr;
- zend_class_entry *ce;
- bool ce_is_instanceof;
- bool on_this = 0;
- bool delayed_fetch_this = 0;
- bool avoid_refcounting = 0;
- bool polymorphic_side_trace =
- parent_trace &&
- (zend_jit_traces[parent_trace].exit_info[exit_num].flags & ZEND_JIT_EXIT_METHOD_CALL);
- uint32_t i;
- zend_jit_trace_stack_frame *frame, *top, *call;
- zend_jit_trace_stack *stack;
- zend_uchar res_type = IS_UNKNOWN;
- const zend_op *opline, *orig_opline;
- const zend_ssa_op *ssa_op, *orig_ssa_op;
- int checked_stack;
- int peek_checked_stack;
- uint32_t frame_flags = 0;
- JIT_G(current_trace) = trace_buffer;
- checkpoint = zend_arena_checkpoint(CG(arena));
- ssa = zend_jit_trace_build_tssa(trace_buffer, parent_trace, exit_num, script, op_arrays, &num_op_arrays);
- if (!ssa) {
- goto jit_cleanup;
- }
- ssa_opcodes = ((zend_tssa*)ssa)->tssa_opcodes;
- /* Register allocation */
- if ((JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL))
- && JIT_G(opt_level) >= ZEND_JIT_LEVEL_INLINE) {
- ra = zend_jit_trace_allocate_registers(trace_buffer, ssa, parent_trace, exit_num);
- }
- p = trace_buffer;
- ZEND_ASSERT(p->op == ZEND_JIT_TRACE_START);
- op_array = p->op_array;
- frame = JIT_G(current_frame);
- top = zend_jit_trace_call_frame(frame, op_array);
- TRACE_FRAME_INIT(frame, op_array, TRACE_FRAME_MASK_UNKNOWN_RETURN, -1);
- frame->used_stack = checked_stack = peek_checked_stack = 0;
- stack = frame->stack;
- for (i = 0; i < op_array->last_var + op_array->T; i++) {
- SET_STACK_TYPE(stack, i, IS_UNKNOWN, 1);
- }
- opline = p[1].opline;
- name = zend_jit_trace_name(op_array, opline->lineno);
- p += ZEND_JIT_TRACE_START_REC_SIZE;
- dasm_init(&dasm_state, DASM_MAXSECTION);
- dasm_setupglobal(&dasm_state, dasm_labels, zend_lb_MAX);
- dasm_setup(&dasm_state, dasm_actions);
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- op_array_ssa = &jit_extension->func_info.ssa;
- dasm_growpc(&dasm_state, 2); /* =>0: loop header */
- /* =>1: end of code */
- zend_jit_align_func(&dasm_state);
- if (!parent_trace) {
- zend_jit_prologue(&dasm_state);
- }
- zend_jit_trace_begin(&dasm_state, ZEND_JIT_TRACE_NUM,
- parent_trace ? &zend_jit_traces[parent_trace] : NULL, exit_num);
- if (!parent_trace) {
- zend_jit_set_last_valid_opline(opline);
- zend_jit_track_last_valid_opline();
- } else {
- if (zend_jit_traces[parent_trace].exit_info[exit_num].opline == NULL) {
- zend_jit_trace_opline_guard(&dasm_state, opline);
- } else {
- zend_jit_reset_last_valid_opline();
- }
- }
- if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_INLINE) {
- int last_var;
- int parent_vars_count = 0;
- zend_jit_trace_stack *parent_stack = NULL;
- int used_stack = ((zend_tssa*)ssa)->used_stack;
- if (used_stack > 0) {
- peek_checked_stack = used_stack;
- if (!zend_jit_stack_check(&dasm_state, opline, used_stack)) {
- goto jit_failure;
- }
- }
- if (parent_trace) {
- parent_vars_count = MIN(zend_jit_traces[parent_trace].exit_info[exit_num].stack_size,
- op_array->last_var + op_array->T);
- if (parent_vars_count) {
- parent_stack =
- zend_jit_traces[parent_trace].stack_map +
- zend_jit_traces[parent_trace].exit_info[exit_num].stack_offset;
- }
- }
- last_var = op_array->last_var;
- if (trace_buffer->start != ZEND_JIT_TRACE_START_ENTER) {
- last_var += op_array->T;
- }
- for (i = 0; i < last_var; i++) {
- uint32_t info = ssa->var_info[i].type;
- if (!(info & MAY_BE_GUARD) && has_concrete_type(info)) {
- uint8_t type, mem_type;
- type = concrete_type(info);
- if (i < parent_vars_count
- && STACK_TYPE(parent_stack, i) == type) {
- mem_type = STACK_MEM_TYPE(parent_stack, i);
- if (mem_type != IS_UNKNOWN) {
- SET_STACK_TYPE(stack, i, mem_type, 1);
- }
- SET_STACK_TYPE(stack, i, type, 0);
- } else {
- SET_STACK_TYPE(stack, i, type, 1);
- }
- } else if (ssa->vars[i].alias != NO_ALIAS) {
- SET_STACK_TYPE(stack, i, IS_UNKNOWN, 1);
- } else if (i < parent_vars_count
- && STACK_TYPE(parent_stack, i) != IS_UNKNOWN) {
- /* This must be already handled by trace type inference */
- ZEND_UNREACHABLE();
- // SET_STACK_TYPE(stack, i, STACK_TYPE(parent_stack, i));
- } else if ((info & MAY_BE_GUARD) != 0
- && (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
- || (trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET
- && (opline-1)->result_type == IS_VAR
- && EX_VAR_TO_NUM((opline-1)->result.var) == i))
- && (ssa->vars[i].use_chain != -1
- || (ssa->vars[i].phi_use_chain
- && !(ssa->var_info[ssa->vars[i].phi_use_chain->ssa_var].type & MAY_BE_GUARD)))) {
- /* Check loop-invariant variable type */
- if (!zend_jit_type_guard(&dasm_state, opline, EX_NUM_TO_VAR(i), concrete_type(info))) {
- goto jit_failure;
- }
- info &= ~MAY_BE_GUARD;
- ssa->var_info[i].type = info;
- SET_STACK_TYPE(stack, i, concrete_type(info), 1);
- } else if (trace_buffer->start == ZEND_JIT_TRACE_START_ENTER
- && op_array->function_name
- && i >= op_array->num_args) {
- /* This must be already handled by trace type inference */
- ZEND_UNREACHABLE();
- // SET_STACK_TYPE(stack, i, IS_UNDEF, 1);
- }
- if ((info & MAY_BE_PACKED_GUARD) != 0
- && (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET)
- && (ssa->vars[i].use_chain != -1
- || (ssa->vars[i].phi_use_chain
- && !(ssa->var_info[ssa->vars[i].phi_use_chain->ssa_var].type & MAY_BE_PACKED_GUARD)))) {
- if (!zend_jit_packed_guard(&dasm_state, opline, EX_NUM_TO_VAR(i), info)) {
- goto jit_failure;
- }
- info &= ~MAY_BE_PACKED_GUARD;
- ssa->var_info[i].type = info;
- }
- }
- if (parent_trace) {
- /* Deoptimization */
- if (!zend_jit_trace_deoptimization(&dasm_state,
- zend_jit_traces[parent_trace].exit_info[exit_num].flags,
- zend_jit_traces[parent_trace].exit_info[exit_num].opline,
- parent_stack, parent_vars_count, ssa, stack, ra,
- polymorphic_side_trace)) {
- goto jit_failure;
- }
- }
- if (ra
- && trace_buffer->stop != ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
- && trace_buffer->stop != ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- for (i = 0; i < last_var; i++) {
- if (ra[i]
- && (ra[i]->flags & ZREG_LOAD) != 0
- && ra[i]->reg != stack[i].reg) {
- if ((ssa->var_info[i].type & MAY_BE_GUARD) != 0) {
- uint8_t op_type;
- ssa->var_info[i].type &= ~MAY_BE_GUARD;
- op_type = concrete_type(ssa->var_info[i].type);
- if (!zend_jit_type_guard(&dasm_state, opline, EX_NUM_TO_VAR(i), op_type)) {
- goto jit_failure;
- }
- SET_STACK_TYPE(stack, i, op_type, 1);
- }
- SET_STACK_REG_EX(stack, i, ra[i]->reg, ZREG_LOAD);
- if (!zend_jit_load_var(&dasm_state, ssa->var_info[i].type, i, ra[i]->reg)) {
- goto jit_failure;
- }
- }
- }
- }
- }
- if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- zend_jit_label(&dasm_state, 0); /* start of of trace loop */
- if (ra) {
- zend_ssa_phi *phi = ssa->blocks[1].phis;
- while (phi) {
- zend_lifetime_interval *ival = ra[phi->ssa_var];
- if (ival) {
- if (ival->flags & ZREG_LOAD) {
- uint32_t info = ssa->var_info[phi->ssa_var].type;
- ZEND_ASSERT(ival->reg != ZREG_NONE);
- if (info & MAY_BE_GUARD) {
- if (!zend_jit_type_guard(&dasm_state, opline, EX_NUM_TO_VAR(phi->var), concrete_type(info))) {
- goto jit_failure;
- }
- info &= ~MAY_BE_GUARD;
- ssa->var_info[phi->ssa_var].type = info;
- SET_STACK_TYPE(stack, phi->var, concrete_type(info), 1);
- }
- SET_STACK_REG_EX(stack, phi->var, ival->reg, ZREG_LOAD);
- if (!zend_jit_load_var(&dasm_state, ssa->var_info[phi->ssa_var].type, ssa->vars[phi->ssa_var].var, ival->reg)) {
- goto jit_failure;
- }
- } else if (ival->flags & ZREG_STORE) {
- ZEND_ASSERT(ival->reg != ZREG_NONE);
- SET_STACK_REG_EX(stack, phi->var, ival->reg, ZREG_STORE);
- if (!zend_jit_store_var(&dasm_state, ssa->var_info[phi->ssa_var].type, ssa->vars[phi->ssa_var].var, ival->reg,
- STACK_MEM_TYPE(stack, phi->var) != ssa->var_info[phi->ssa_var].type)) {
- goto jit_failure;
- }
- } else {
- /* Register has to be written back on side exit */
- SET_STACK_REG(stack, phi->var, ival->reg);
- }
- }
- phi = phi->next;
- }
- }
- // if (trace_buffer->stop != ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- // if (ra && zend_jit_trace_stack_needs_deoptimization(stack, op_array->last_var + op_array->T)) {
- // uint32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
- //
- // timeout_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
- // if (!timeout_exit_addr) {
- // goto jit_failure;
- // }
- // }
- // }
- if (ra && trace_buffer->stop != ZEND_JIT_TRACE_STOP_LOOP) {
- int last_var = op_array->last_var;
- if (trace_buffer->start != ZEND_JIT_TRACE_START_ENTER) {
- last_var += op_array->T;
- }
- for (i = 0; i < last_var; i++) {
- if (ra && ra[i] && (ra[i]->flags & ZREG_LOAD) != 0) {
- SET_STACK_REG_EX(stack, i, ra[i]->reg, ZREG_LOAD);
- if (!zend_jit_load_var(&dasm_state, ssa->var_info[i].type, i, ra[i]->reg)) {
- goto jit_failure;
- }
- }
- }
- }
- }
- ssa_op = (JIT_G(opt_level) >= ZEND_JIT_LEVEL_INLINE) ? ssa->ops : NULL;
- for (;;p++) {
- if (p->op == ZEND_JIT_TRACE_VM) {
- uint8_t op1_type = p->op1_type;
- uint8_t op2_type = p->op2_type;
- uint8_t op3_type = p->op3_type;
- uint8_t orig_op1_type = op1_type;
- uint8_t orig_op2_type = op2_type;
- uint8_t val_type = IS_UNKNOWN;
- bool op1_indirect;
- zend_class_entry *op1_ce = NULL;
- zend_class_entry *op2_ce = NULL;
- bool gen_handler;
- opline = p->opline;
- if (op1_type & (IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)) {
- op1_type = IS_UNKNOWN;
- }
- if (op1_type != IS_UNKNOWN) {
- op1_type &= ~IS_TRACE_PACKED;
- }
- if (op2_type & (IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)) {
- op2_type = IS_UNKNOWN;
- }
- if (op3_type & (IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)) {
- op3_type = IS_UNKNOWN;
- }
- if ((p+1)->op == ZEND_JIT_TRACE_OP1_TYPE) {
- op1_ce = (zend_class_entry*)(p+1)->ce;
- p++;
- }
- if ((p+1)->op == ZEND_JIT_TRACE_OP2_TYPE) {
- op2_ce = (zend_class_entry*)(p+1)->ce;
- p++;
- }
- if ((p+1)->op == ZEND_JIT_TRACE_VAL_INFO) {
- val_type = (p+1)->op1_type;
- p++;
- }
- frame_flags = 0;
- switch (opline->opcode) {
- case ZEND_INIT_FCALL:
- case ZEND_INIT_FCALL_BY_NAME:
- case ZEND_INIT_NS_FCALL_BY_NAME:
- case ZEND_INIT_METHOD_CALL:
- case ZEND_INIT_DYNAMIC_CALL:
- case ZEND_INIT_STATIC_METHOD_CALL:
- case ZEND_INIT_USER_CALL:
- case ZEND_NEW:
- frame->call_level++;
- }
- if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_INLINE) {
- gen_handler = 0;
- switch (opline->opcode) {
- case ZEND_PRE_INC:
- case ZEND_PRE_DEC:
- case ZEND_POST_INC:
- case ZEND_POST_DEC:
- if (opline->op1_type != IS_CV) {
- break;
- }
- op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
- if (!(op1_info & MAY_BE_LONG)) {
- break;
- }
- if (opline->result_type != IS_UNUSED) {
- res_use_info = zend_jit_trace_type_to_info(
- STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->result.var)))
- & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE);
- res_info = RES_INFO();
- res_addr = RES_REG_ADDR();
- } else {
- res_use_info = -1;
- res_info = -1;
- res_addr = 0;
- }
- op1_def_info = OP1_DEF_INFO();
- if (op1_def_info & MAY_BE_GUARD
- && !has_concrete_type(op1_def_info)) {
- op1_def_info &= ~MAY_BE_GUARD;
- }
- if (!zend_jit_inc_dec(&dasm_state, opline,
- op1_info, OP1_REG_ADDR(),
- op1_def_info, OP1_DEF_REG_ADDR(),
- res_use_info, res_info,
- res_addr,
- (op1_def_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow(opline, ssa_op, op_array, ssa),
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- if ((op1_def_info & (MAY_BE_ANY|MAY_BE_GUARD)) == (MAY_BE_LONG|MAY_BE_GUARD)
- && !(op1_info & MAY_BE_STRING)) {
- ssa->var_info[ssa_op->op1_def].type &= ~MAY_BE_GUARD;
- if (opline->result_type != IS_UNUSED) {
- ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
- }
- }
- if (opline->result_type != IS_UNUSED
- && (res_info & (MAY_BE_ANY|MAY_BE_GUARD)) == (MAY_BE_LONG|MAY_BE_GUARD)
- && !(op1_info & MAY_BE_STRING)) {
- ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
- }
- goto done;
- case ZEND_BW_OR:
- case ZEND_BW_AND:
- case ZEND_BW_XOR:
- case ZEND_SL:
- case ZEND_SR:
- case ZEND_MOD:
- op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
- op2_info = OP2_INFO();
- CHECK_OP2_TRACE_TYPE();
- if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) {
- break;
- }
- if (!(op1_info & MAY_BE_LONG)
- || !(op2_info & MAY_BE_LONG)) {
- break;
- }
- res_addr = RES_REG_ADDR();
- if (Z_MODE(res_addr) != IS_REG
- && zend_jit_trace_next_is_send_result(opline, p, frame)) {
- send_result = 1;
- res_use_info = -1;
- res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, (opline+1)->result.var);
- if (!zend_jit_reuse_ip(&dasm_state)) {
- goto jit_failure;
- }
- } else {
- res_use_info = zend_jit_trace_type_to_info(
- STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->result.var)))
- & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE);
- }
- res_info = RES_INFO();
- if (!zend_jit_long_math(&dasm_state, opline,
- op1_info, OP1_RANGE(), OP1_REG_ADDR(),
- op2_info, OP2_RANGE(), OP2_REG_ADDR(),
- res_use_info, res_info, res_addr,
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- goto done;
- case ZEND_ADD:
- case ZEND_SUB:
- case ZEND_MUL:
- // case ZEND_DIV: // TODO: check for division by zero ???
- op1_info = OP1_INFO();
- op1_addr = OP1_REG_ADDR();
- op2_info = OP2_INFO();
- op2_addr = OP2_REG_ADDR();
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_REFERENCE)
- && opline->op1_type == IS_CV
- && (Z_MODE(op2_addr) != IS_REG || Z_REG(op2_addr) != ZREG_FCARG1)
- && (orig_op2_type == IS_UNKNOWN || !(orig_op2_type & IS_TRACE_REFERENCE))) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
- !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
- goto jit_failure;
- }
- if (ssa->vars[ssa_op->op1_use].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_use].guarded_reference = 1;
- }
- } else {
- CHECK_OP1_TRACE_TYPE();
- }
- if (orig_op2_type != IS_UNKNOWN
- && (orig_op2_type & IS_TRACE_REFERENCE)
- && opline->op2_type == IS_CV
- && (Z_MODE(op1_addr) != IS_REG || Z_REG(op1_addr) != ZREG_FCARG1)
- && (orig_op1_type == IS_UNKNOWN || !(orig_op1_type & IS_TRACE_REFERENCE))) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op2_type, &op2_info, &op2_addr,
- !ssa->var_info[ssa_op->op2_use].guarded_reference, 1)) {
- goto jit_failure;
- }
- if (ssa->vars[ssa_op->op2_use].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op2_use].guarded_reference = 1;
- }
- } else {
- CHECK_OP2_TRACE_TYPE();
- }
- if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) {
- break;
- }
- if (opline->opcode == ZEND_ADD &&
- (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY &&
- (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
- /* pass */
- } else if (!(op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) ||
- !(op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
- break;
- }
- res_addr = RES_REG_ADDR();
- if (Z_MODE(res_addr) != IS_REG
- && zend_jit_trace_next_is_send_result(opline, p, frame)) {
- send_result = 1;
- res_use_info = -1;
- res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, (opline+1)->result.var);
- if (!zend_jit_reuse_ip(&dasm_state)) {
- goto jit_failure;
- }
- } else {
- res_use_info = zend_jit_trace_type_to_info(
- STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->result.var)))
- & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE);
- }
- res_info = RES_INFO();
- if (opline->opcode == ZEND_ADD &&
- (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY &&
- (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
- if (!zend_jit_add_arrays(&dasm_state, opline, op1_info, op1_addr, op2_info, op2_addr, res_addr)) {
- goto jit_failure;
- }
- } else {
- if (!zend_jit_math(&dasm_state, opline,
- op1_info, op1_addr,
- op2_info, op2_addr,
- res_use_info, res_info, res_addr,
- (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow(opline, ssa_op, op_array, ssa),
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- if (((res_info & (MAY_BE_ANY|MAY_BE_GUARD)) == (MAY_BE_LONG|MAY_BE_GUARD)
- || (res_info & (MAY_BE_ANY|MAY_BE_GUARD)) == (MAY_BE_DOUBLE|MAY_BE_GUARD))
- && has_concrete_type(op1_info)
- && has_concrete_type(op2_info)) {
- ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
- }
- }
- goto done;
- case ZEND_CONCAT:
- case ZEND_FAST_CONCAT:
- op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
- op2_info = OP2_INFO();
- CHECK_OP2_TRACE_TYPE();
- if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) {
- break;
- }
- if (!(op1_info & MAY_BE_STRING) ||
- !(op2_info & MAY_BE_STRING)) {
- break;
- }
- res_addr = RES_REG_ADDR();
- if (zend_jit_trace_next_is_send_result(opline, p, frame)) {
- send_result = 1;
- res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, (opline+1)->result.var);
- if (!zend_jit_reuse_ip(&dasm_state)) {
- goto jit_failure;
- }
- }
- if (!zend_jit_concat(&dasm_state, opline,
- op1_info, op2_info, res_addr,
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- goto done;
- case ZEND_ASSIGN_OP:
- if (opline->op1_type != IS_CV || opline->result_type != IS_UNUSED) {
- break;
- }
- op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
- op2_info = OP2_INFO();
- CHECK_OP2_TRACE_TYPE();
- if (!zend_jit_supported_binary_op(
- opline->extended_value, op1_info, op2_info)) {
- break;
- }
- op1_def_info = OP1_DEF_INFO();
- if (op1_def_info & MAY_BE_GUARD
- && !has_concrete_type(op1_def_info)) {
- op1_def_info &= ~MAY_BE_GUARD;
- }
- if (!zend_jit_assign_op(&dasm_state, opline,
- op1_info, op1_def_info, OP1_RANGE(),
- op2_info, OP2_RANGE(),
- (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (op1_def_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow(opline, ssa_op, op_array, ssa),
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- if ((op1_def_info & (MAY_BE_ANY|MAY_BE_GUARD)) == (MAY_BE_LONG|MAY_BE_GUARD)
- && has_concrete_type(op1_info)
- && has_concrete_type(op2_info)) {
- ssa->var_info[ssa_op->op1_def].type &= ~MAY_BE_GUARD;
- if (opline->result_type != IS_UNUSED) {
- ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
- }
- }
- goto done;
- case ZEND_ASSIGN_DIM_OP:
- if (opline->result_type != IS_UNUSED) {
- break;
- }
- if (!zend_jit_supported_binary_op(
- opline->extended_value, MAY_BE_ANY, OP1_DATA_INFO())) {
- break;
- }
- if (opline->op1_type == IS_CV
- && (opline+1)->op1_type == IS_CV
- && (opline+1)->op1.var == opline->op1.var) {
- /* skip $a[x] += $a; */
- break;
- }
- op1_info = OP1_INFO();
- op1_addr = OP1_REG_ADDR();
- if (opline->op1_type == IS_VAR) {
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_INDIRECT)) {
- if (!zend_jit_fetch_indirect_var(&dasm_state, opline, orig_op1_type,
- &op1_info, &op1_addr, !ssa->var_info[ssa_op->op1_use].indirect_reference)) {
- goto jit_failure;
- }
- } else {
- break;
- }
- }
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_REFERENCE)) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
- !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa->vars[ssa_op->op1_def].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_def].guarded_reference = 1;
- }
- } else {
- CHECK_OP1_TRACE_TYPE();
- }
- op2_info = OP2_INFO();
- CHECK_OP2_TRACE_TYPE();
- op1_data_info = OP1_DATA_INFO();
- CHECK_OP1_DATA_TRACE_TYPE();
- op1_def_info = OP1_DEF_INFO();
- if (!zend_jit_assign_dim_op(&dasm_state, opline,
- op1_info, op1_def_info, op1_addr, op2_info,
- op1_data_info, OP1_DATA_RANGE(), val_type,
- zend_may_throw_ex(opline, ssa_op, op_array, ssa, op1_info, op2_info))) {
- goto jit_failure;
- }
- goto done;
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_POST_INC_OBJ:
- case ZEND_POST_DEC_OBJ:
- if (opline->op2_type != IS_CONST
- || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
- || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
- break;
- }
- ce = NULL;
- ce_is_instanceof = 0;
- on_this = delayed_fetch_this = 0;
- op1_indirect = 0;
- if (opline->op1_type == IS_UNUSED) {
- op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
- ce = op_array->scope;
- ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
- op1_addr = 0;
- on_this = 1;
- } else {
- if (ssa_op->op1_use >= 0) {
- delayed_fetch_this = ssa->var_info[ssa_op->op1_use].delayed_fetch_this;
- }
- op1_info = OP1_INFO();
- if (!(op1_info & MAY_BE_OBJECT)) {
- break;
- }
- op1_addr = OP1_REG_ADDR();
- if (opline->op1_type == IS_VAR) {
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_INDIRECT)) {
- op1_indirect = 1;
- if (!zend_jit_fetch_indirect_var(&dasm_state, opline, orig_op1_type,
- &op1_info, &op1_addr, !ssa->var_info[ssa_op->op1_use].indirect_reference)) {
- goto jit_failure;
- }
- }
- }
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_REFERENCE)) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
- !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa->vars[ssa_op->op1_def].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_def].guarded_reference = 1;
- }
- } else {
- CHECK_OP1_TRACE_TYPE();
- }
- if (!(op1_info & MAY_BE_OBJECT)) {
- break;
- }
- if (ssa->var_info && ssa->ops) {
- if (ssa_op->op1_use >= 0) {
- zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
- if (op1_ssa->ce && !op1_ssa->ce->create_object) {
- ce = op1_ssa->ce;
- ce_is_instanceof = op1_ssa->is_instanceof;
- }
- }
- }
- if (delayed_fetch_this) {
- on_this = 1;
- } else if (ssa_op->op1_use >= 0 && ssa->vars[ssa_op->op1_use].definition >= 0) {
- on_this = ssa_opcodes[ssa->vars[ssa_op->op1_use].definition]->opcode == ZEND_FETCH_THIS;
- } else if (op_array_ssa->ops
- && op_array_ssa->vars
- && op_array_ssa->ops[opline-op_array->opcodes].op1_use >= 0
- && op_array_ssa->vars[op_array_ssa->ops[opline-op_array->opcodes].op1_use].definition >= 0) {
- on_this = op_array->opcodes[op_array_ssa->vars[op_array_ssa->ops[opline-op_array->opcodes].op1_use].definition].opcode == ZEND_FETCH_THIS;
- }
- }
- if (!zend_jit_incdec_obj(&dasm_state, opline, op_array, ssa, ssa_op,
- op1_info, op1_addr,
- op1_indirect, ce, ce_is_instanceof, on_this, delayed_fetch_this, op1_ce,
- val_type)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_ASSIGN_OBJ_OP:
- if (opline->result_type != IS_UNUSED) {
- break;
- }
- if (opline->op2_type != IS_CONST
- || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
- || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
- break;
- }
- if (opline->op1_type == IS_CV
- && (opline+1)->op1_type == IS_CV
- && (opline+1)->op1.var == opline->op1.var) {
- /* skip $a->prop += $a; */
- break;
- }
- if (!zend_jit_supported_binary_op(
- opline->extended_value, MAY_BE_ANY, OP1_DATA_INFO())) {
- break;
- }
- ce = NULL;
- ce_is_instanceof = 0;
- on_this = delayed_fetch_this = 0;
- op1_indirect = 0;
- if (opline->op1_type == IS_UNUSED) {
- op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
- ce = op_array->scope;
- ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
- op1_addr = 0;
- on_this = 1;
- } else {
- if (ssa_op->op1_use >= 0) {
- delayed_fetch_this = ssa->var_info[ssa_op->op1_use].delayed_fetch_this;
- }
- op1_info = OP1_INFO();
- if (!(op1_info & MAY_BE_OBJECT)) {
- break;
- }
- op1_addr = OP1_REG_ADDR();
- if (opline->op1_type == IS_VAR) {
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_INDIRECT)) {
- op1_indirect = 1;
- if (!zend_jit_fetch_indirect_var(&dasm_state, opline, orig_op1_type,
- &op1_info, &op1_addr, !ssa->var_info[ssa_op->op1_use].indirect_reference)) {
- goto jit_failure;
- }
- }
- }
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_REFERENCE)) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
- !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa->vars[ssa_op->op1_def].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_def].guarded_reference = 1;
- }
- } else {
- CHECK_OP1_TRACE_TYPE();
- }
- if (!(op1_info & MAY_BE_OBJECT)) {
- break;
- }
- if (ssa->var_info && ssa->ops) {
- if (ssa_op->op1_use >= 0) {
- zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
- if (op1_ssa->ce && !op1_ssa->ce->create_object) {
- ce = op1_ssa->ce;
- ce_is_instanceof = op1_ssa->is_instanceof;
- }
- }
- }
- if (delayed_fetch_this) {
- on_this = 1;
- } else if (ssa_op->op1_use >= 0 && ssa->vars[ssa_op->op1_use].definition >= 0) {
- on_this = ssa_opcodes[ssa->vars[ssa_op->op1_use].definition]->opcode == ZEND_FETCH_THIS;
- } else if (op_array_ssa->ops
- && op_array_ssa->vars
- && op_array_ssa->ops[opline-op_array->opcodes].op1_use >= 0
- && op_array_ssa->vars[op_array_ssa->ops[opline-op_array->opcodes].op1_use].definition >= 0) {
- on_this = op_array->opcodes[op_array_ssa->vars[op_array_ssa->ops[opline-op_array->opcodes].op1_use].definition].opcode == ZEND_FETCH_THIS;
- }
- }
- op1_data_info = OP1_DATA_INFO();
- CHECK_OP1_DATA_TRACE_TYPE();
- if (!zend_jit_assign_obj_op(&dasm_state, opline, op_array, ssa, ssa_op,
- op1_info, op1_addr, op1_data_info, OP1_DATA_RANGE(),
- op1_indirect, ce, ce_is_instanceof, on_this, delayed_fetch_this, op1_ce,
- val_type)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_ASSIGN_OBJ:
- if (opline->op2_type != IS_CONST
- || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
- || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
- break;
- }
- ce = NULL;
- ce_is_instanceof = 0;
- on_this = delayed_fetch_this = 0;
- op1_indirect = 0;
- if (opline->op1_type == IS_UNUSED) {
- op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
- ce = op_array->scope;
- ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
- op1_addr = 0;
- on_this = 1;
- } else {
- if (ssa_op->op1_use >= 0) {
- delayed_fetch_this = ssa->var_info[ssa_op->op1_use].delayed_fetch_this;
- }
- op1_info = OP1_INFO();
- if (!(op1_info & MAY_BE_OBJECT)) {
- break;
- }
- op1_addr = OP1_REG_ADDR();
- if (opline->op1_type == IS_VAR) {
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_INDIRECT)) {
- op1_indirect = 1;
- if (!zend_jit_fetch_indirect_var(&dasm_state, opline, orig_op1_type,
- &op1_info, &op1_addr, !ssa->var_info[ssa_op->op1_use].indirect_reference)) {
- goto jit_failure;
- }
- }
- }
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_REFERENCE)) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
- !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa->vars[ssa_op->op1_def].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_def].guarded_reference = 1;
- }
- } else {
- CHECK_OP1_TRACE_TYPE();
- }
- if (!(op1_info & MAY_BE_OBJECT)) {
- break;
- }
- if (ssa->var_info && ssa->ops) {
- if (ssa_op->op1_use >= 0) {
- zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
- if (op1_ssa->ce && !op1_ssa->ce->create_object) {
- ce = op1_ssa->ce;
- ce_is_instanceof = op1_ssa->is_instanceof;
- }
- }
- }
- if (delayed_fetch_this) {
- on_this = 1;
- } else if (ssa_op->op1_use >= 0 && ssa->vars[ssa_op->op1_use].definition >= 0) {
- on_this = ssa_opcodes[ssa->vars[ssa_op->op1_use].definition]->opcode == ZEND_FETCH_THIS;
- } else if (op_array_ssa->ops
- && op_array_ssa->vars
- && op_array_ssa->ops[opline-op_array->opcodes].op1_use >= 0
- && op_array_ssa->vars[op_array_ssa->ops[opline-op_array->opcodes].op1_use].definition >= 0) {
- on_this = op_array->opcodes[op_array_ssa->vars[op_array_ssa->ops[opline-op_array->opcodes].op1_use].definition].opcode == ZEND_FETCH_THIS;
- }
- }
- op1_data_info = OP1_DATA_INFO();
- CHECK_OP1_DATA_TRACE_TYPE();
- if (!zend_jit_assign_obj(&dasm_state, opline, op_array, ssa, ssa_op,
- op1_info, op1_addr, op1_data_info,
- op1_indirect, ce, ce_is_instanceof, on_this, delayed_fetch_this, op1_ce,
- val_type,
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- if ((opline+1)->op1_type == IS_CV
- && (ssa_op+1)->op1_def >= 0
- && ssa->vars[(ssa_op+1)->op1_def].alias == NO_ALIAS) {
- ssa->var_info[(ssa_op+1)->op1_def].guarded_reference = ssa->var_info[(ssa_op+1)->op1_use].guarded_reference;
- }
- goto done;
- case ZEND_ASSIGN_DIM:
- op1_info = OP1_INFO();
- op1_addr = OP1_REG_ADDR();
- if (opline->op1_type == IS_CV
- && (opline+1)->op1_type == IS_CV
- && (opline+1)->op1.var == opline->op1.var) {
- /* skip $a[x] = $a; */
- break;
- }
- if (opline->op1_type == IS_VAR) {
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_INDIRECT)
- && opline->result_type == IS_UNUSED) {
- if (!zend_jit_fetch_indirect_var(&dasm_state, opline, orig_op1_type,
- &op1_info, &op1_addr, !ssa->var_info[ssa_op->op1_use].indirect_reference)) {
- goto jit_failure;
- }
- } else {
- break;
- }
- }
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_REFERENCE)) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
- !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa->vars[ssa_op->op1_def].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_def].guarded_reference = 1;
- }
- } else {
- CHECK_OP1_TRACE_TYPE();
- }
- op2_info = OP2_INFO();
- CHECK_OP2_TRACE_TYPE();
- op1_data_info = OP1_DATA_INFO();
- CHECK_OP1_DATA_TRACE_TYPE();
- if (!zend_jit_assign_dim(&dasm_state, opline,
- op1_info, op1_addr, op2_info, op1_data_info, val_type,
- zend_may_throw_ex(opline, ssa_op, op_array, ssa, op1_info, op2_info))) {
- goto jit_failure;
- }
- if ((opline+1)->op1_type == IS_CV
- && (ssa_op+1)->op1_def >= 0
- && ssa->vars[(ssa_op+1)->op1_def].alias == NO_ALIAS) {
- ssa->var_info[(ssa_op+1)->op1_def].guarded_reference = ssa->var_info[(ssa_op+1)->op1_use].guarded_reference;
- }
- goto done;
- case ZEND_ASSIGN:
- if (opline->op1_type != IS_CV) {
- break;
- }
- op2_addr = OP2_REG_ADDR();
- op2_info = OP2_INFO();
- if (ra
- && ssa_op->op2_def >= 0
- && (!ssa->vars[ssa_op->op2_def].no_val
- || (zend_jit_trace_type_to_info(STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->op2.var))) & MAY_BE_ANY) !=
- (op2_info & MAY_BE_ANY))) {
- op2_def_addr = OP2_DEF_REG_ADDR();
- } else {
- op2_def_addr = op2_addr;
- }
- CHECK_OP2_TRACE_TYPE();
- op1_info = OP1_INFO();
- op1_def_info = OP1_DEF_INFO();
- if (op1_type != IS_UNKNOWN && (op1_info & MAY_BE_GUARD)) {
- if (op1_type < IS_STRING
- && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) != (op1_def_info & (MAY_BE_ANY|MAY_BE_UNDEF))) {
- if (!zend_jit_scalar_type_guard(&dasm_state, opline, opline->op1.var)) {
- goto jit_failure;
- }
- op1_info &= ~(MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF|MAY_BE_GUARD);
- } else {
- CHECK_OP1_TRACE_TYPE();
- }
- }
- op1_addr = OP1_REG_ADDR();
- op1_def_addr = OP1_DEF_REG_ADDR();
- if (Z_MODE(op1_def_addr) != IS_REG &&
- STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var)) !=
- STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var))) {
- /* type may be not set */
- op1_info |= MAY_BE_NULL;
- }
- if (orig_op1_type != IS_UNKNOWN) {
- if (orig_op1_type & IS_TRACE_REFERENCE) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
- !ssa->var_info[ssa_op->op1_use].guarded_reference, 0)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa->vars[ssa_op->op1_def].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_def].guarded_reference = 1;
- }
- if (opline->result_type == IS_UNUSED) {
- res_addr = 0;
- } else {
- res_addr = RES_REG_ADDR();
- }
- if (!zend_jit_assign_to_typed_ref(&dasm_state, opline, opline->op2_type, op2_addr, res_addr, 1)) {
- goto jit_failure;
- }
- op1_def_addr = op1_addr;
- op1_def_info &= ~MAY_BE_REF;
- } else if (op1_info & MAY_BE_REF) {
- if (!zend_jit_noref_guard(&dasm_state, opline, op1_addr)) {
- goto jit_failure;
- }
- op1_info &= ~MAY_BE_REF;
- op1_def_info &= ~MAY_BE_REF;
- }
- }
- if (opline->result_type == IS_UNUSED) {
- res_addr = 0;
- res_info = -1;
- } else {
- res_addr = RES_REG_ADDR();
- res_info = RES_INFO();
- if (Z_MODE(res_addr) != IS_REG
- && zend_jit_trace_next_is_send_result(opline, p, frame)) {
- send_result = 1;
- res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, (opline+1)->result.var);
- if (!zend_jit_reuse_ip(&dasm_state)) {
- goto jit_failure;
- }
- }
- }
- if (!zend_jit_assign(&dasm_state, opline,
- op1_info, op1_addr,
- op1_def_info, op1_def_addr,
- op2_info, op2_addr, op2_def_addr,
- res_info, res_addr,
- zend_may_throw_ex(opline, ssa_op, op_array, ssa, op1_info, op2_info))) {
- goto jit_failure;
- }
- if (opline->op2_type == IS_CV
- && ssa_op->op2_def >= 0
- && ssa->vars[ssa_op->op2_def].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op2_def].guarded_reference = ssa->var_info[ssa_op->op2_use].guarded_reference;
- }
- goto done;
- case ZEND_CAST:
- if (opline->extended_value != op1_type) {
- break;
- }
- ZEND_FALLTHROUGH;
- case ZEND_QM_ASSIGN:
- op1_addr = OP1_REG_ADDR();
- if (ra
- && ssa_op->op1_def >= 0
- && !ssa->vars[ssa_op->op1_def].no_val) {
- op1_def_addr = OP1_DEF_REG_ADDR();
- } else {
- op1_def_addr = op1_addr;
- }
- op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
- res_info = RES_INFO();
- res_use_info = zend_jit_trace_type_to_info(
- STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->result.var)))
- & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE);
- res_addr = RES_REG_ADDR();
- if (Z_MODE(res_addr) != IS_REG &&
- STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var)) !=
- STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->result.var))) {
- /* type may be not set */
- res_use_info |= MAY_BE_NULL;
- }
- if (!zend_jit_qm_assign(&dasm_state, opline,
- op1_info, op1_addr, op1_def_addr,
- res_use_info, res_info, res_addr)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa_op->op1_def >= 0
- && ssa->vars[ssa_op->op1_def].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_def].guarded_reference = ssa->var_info[ssa_op->op1_use].guarded_reference;
- }
- goto done;
- case ZEND_INIT_FCALL:
- case ZEND_INIT_FCALL_BY_NAME:
- case ZEND_INIT_NS_FCALL_BY_NAME:
- frame_flags = TRACE_FRAME_MASK_NESTED;
- if (!zend_jit_init_fcall(&dasm_state, opline, op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, op_array, ssa, ssa_op, frame->call_level, p + 1, peek_checked_stack - checked_stack)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_SEND_VAL:
- case ZEND_SEND_VAL_EX:
- if (opline->op2_type == IS_CONST) {
- /* Named parameters not supported in JIT */
- break;
- }
- if (opline->opcode == ZEND_SEND_VAL_EX
- && opline->op2.num > MAX_ARG_FLAG_NUM) {
- break;
- }
- op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
- if (!zend_jit_send_val(&dasm_state, opline,
- op1_info, OP1_REG_ADDR())) {
- goto jit_failure;
- }
- if (frame->call && frame->call->func) {
- if (opline->op1_type == IS_CONST) {
- zend_jit_trace_send_type(opline, frame->call, Z_TYPE_P(RT_CONSTANT(opline, opline->op1)));
- } else if (op1_type != IS_UNKNOWN) {
- if (op1_type == IS_UNDEF) {
- op1_type = IS_NULL;
- }
- zend_jit_trace_send_type(opline, frame->call, op1_type);
- }
- }
- goto done;
- case ZEND_SEND_REF:
- if (opline->op2_type == IS_CONST) {
- /* Named parameters not supported in JIT */
- break;
- }
- op1_info = OP1_INFO();
- if (!zend_jit_send_ref(&dasm_state, opline, op_array,
- op1_info, 0)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa->vars[ssa_op->op1_def].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_def].guarded_reference = 1;
- }
- goto done;
- case ZEND_SEND_VAR:
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_VAR_NO_REF:
- case ZEND_SEND_VAR_NO_REF_EX:
- case ZEND_SEND_FUNC_ARG:
- if (opline->op2_type == IS_CONST) {
- /* Named parameters not supported in JIT */
- break;
- }
- if ((opline->opcode == ZEND_SEND_VAR_EX
- || opline->opcode == ZEND_SEND_VAR_NO_REF_EX)
- && opline->op2.num > MAX_ARG_FLAG_NUM) {
- break;
- }
- op1_addr = OP1_REG_ADDR();
- if (ra
- && ssa_op->op1_def >= 0
- && !ssa->vars[ssa_op->op1_def].no_val) {
- op1_def_addr = OP1_DEF_REG_ADDR();
- } else {
- op1_def_addr = op1_addr;
- }
- op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
- if (!zend_jit_send_var(&dasm_state, opline, op_array,
- op1_info, op1_addr, op1_def_addr)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa_op->op1_def >= 0
- && ssa->vars[ssa_op->op1_def].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_def].guarded_reference = ssa->var_info[ssa_op->op1_use].guarded_reference;
- }
- if (frame->call && frame->call->func) {
- if ((opline->opcode == ZEND_SEND_VAR_EX
- || opline->opcode == ZEND_SEND_FUNC_ARG)
- && ARG_SHOULD_BE_SENT_BY_REF(frame->call->func, opline->op2.num)) {
- goto done;
- }
- if (op1_type != IS_UNKNOWN) {
- if (op1_type == IS_UNDEF) {
- op1_type = IS_NULL;
- }
- zend_jit_trace_send_type(opline, frame->call, op1_type);
- }
- }
- goto done;
- case ZEND_CHECK_FUNC_ARG:
- if (!JIT_G(current_frame)
- || !JIT_G(current_frame)->call
- || !JIT_G(current_frame)->call->func) {
- break;
- }
- if (opline->op2_type == IS_CONST
- || opline->op2.num > MAX_ARG_FLAG_NUM) {
- /* Named parameters not supported in JIT */
- TRACE_FRAME_SET_LAST_SEND_UNKNOWN(JIT_G(current_frame)->call);
- break;
- }
- if (!zend_jit_check_func_arg(&dasm_state, opline)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_CHECK_UNDEF_ARGS:
- if (JIT_G(current_frame)
- && JIT_G(current_frame)->call) {
- TRACE_FRAME_SET_UNKNOWN_NUM_ARGS(JIT_G(current_frame)->call);
- }
- if (!zend_jit_check_undef_args(&dasm_state, opline)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_DO_UCALL:
- case ZEND_DO_ICALL:
- case ZEND_DO_FCALL_BY_NAME:
- case ZEND_DO_FCALL:
- if (!zend_jit_do_fcall(&dasm_state, opline, op_array, op_array_ssa, frame->call_level, -1, p + 1)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_IS_EQUAL:
- case ZEND_IS_NOT_EQUAL:
- case ZEND_IS_SMALLER:
- case ZEND_IS_SMALLER_OR_EQUAL:
- case ZEND_CASE:
- op1_info = OP1_INFO();
- op2_info = OP2_INFO();
- skip_comparison =
- ssa_op != ssa->ops &&
- (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_LONG &&
- (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_LONG &&
- zend_jit_may_skip_comparison(opline, ssa_op, ssa, ssa_opcodes, op_array);
- CHECK_OP1_TRACE_TYPE();
- CHECK_OP2_TRACE_TYPE();
- if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- bool exit_if_true = 0;
- const zend_op *exit_opline = zend_jit_trace_get_exit_opline(p + 1, opline + 1, &exit_if_true);
- uint32_t exit_point;
- if (ra) {
- zend_jit_trace_clenup_stack(stack, opline, ssa_op, ssa, ra);
- }
- exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
- exit_addr = zend_jit_trace_get_exit_addr(exit_point);
- if (!exit_addr) {
- goto jit_failure;
- }
- smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ;
- if (!zend_jit_cmp(&dasm_state, opline,
- op1_info, OP1_RANGE(), OP1_REG_ADDR(),
- op2_info, OP2_RANGE(), OP2_REG_ADDR(),
- RES_REG_ADDR(),
- zend_may_throw(opline, ssa_op, op_array, ssa),
- smart_branch_opcode, -1, -1, exit_addr, skip_comparison)) {
- goto jit_failure;
- }
- zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true);
- } else {
- smart_branch_opcode = 0;
- exit_addr = NULL;
- if (!zend_jit_cmp(&dasm_state, opline,
- op1_info, OP1_RANGE(), OP1_REG_ADDR(),
- op2_info, OP2_RANGE(), OP2_REG_ADDR(),
- RES_REG_ADDR(),
- zend_may_throw(opline, ssa_op, op_array, ssa),
- smart_branch_opcode, -1, -1, exit_addr, skip_comparison)) {
- goto jit_failure;
- }
- }
- goto done;
- case ZEND_IS_IDENTICAL:
- case ZEND_IS_NOT_IDENTICAL:
- case ZEND_CASE_STRICT:
- op1_info = OP1_INFO();
- op2_info = OP2_INFO();
- skip_comparison =
- ssa_op != ssa->ops &&
- (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_LONG &&
- (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_LONG &&
- zend_jit_may_skip_comparison(opline, ssa_op, ssa, ssa_opcodes, op_array);
- CHECK_OP1_TRACE_TYPE();
- CHECK_OP2_TRACE_TYPE();
- if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- bool exit_if_true = 0;
- const zend_op *exit_opline = zend_jit_trace_get_exit_opline(p + 1, opline + 1, &exit_if_true);
- uint32_t exit_point;
- if (ra) {
- zend_jit_trace_clenup_stack(stack, opline, ssa_op, ssa, ra);
- }
- exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
- exit_addr = zend_jit_trace_get_exit_addr(exit_point);
- if (!exit_addr) {
- goto jit_failure;
- }
- if (opline->opcode == ZEND_IS_NOT_IDENTICAL) {
- exit_if_true = !exit_if_true;
- }
- smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ;
- if (!zend_jit_identical(&dasm_state, opline,
- op1_info, OP1_RANGE(), OP1_REG_ADDR(),
- op2_info, OP2_RANGE(), OP2_REG_ADDR(),
- RES_REG_ADDR(),
- zend_may_throw(opline, ssa_op, op_array, ssa),
- smart_branch_opcode, -1, -1, exit_addr, skip_comparison)) {
- goto jit_failure;
- }
- zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true);
- } else {
- smart_branch_opcode = 0;
- exit_addr = NULL;
- if (!zend_jit_identical(&dasm_state, opline,
- op1_info, OP1_RANGE(), OP1_REG_ADDR(),
- op2_info, OP2_RANGE(), OP2_REG_ADDR(),
- RES_REG_ADDR(),
- zend_may_throw(opline, ssa_op, op_array, ssa),
- smart_branch_opcode, -1, -1, exit_addr, skip_comparison)) {
- goto jit_failure;
- }
- }
- goto done;
- case ZEND_DEFINED:
- if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- bool exit_if_true = 0;
- const zend_op *exit_opline = zend_jit_trace_get_exit_opline(p + 1, opline + 1, &exit_if_true);
- uint32_t exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
- exit_addr = zend_jit_trace_get_exit_addr(exit_point);
- if (!exit_addr) {
- goto jit_failure;
- }
- smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ;
- } else {
- smart_branch_opcode = 0;
- exit_addr = NULL;
- }
- if (!zend_jit_defined(&dasm_state, opline, smart_branch_opcode, -1, -1, exit_addr)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_TYPE_CHECK:
- if (opline->extended_value == MAY_BE_RESOURCE) {
- // TODO: support for is_resource() ???
- break;
- }
- op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
- if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- bool exit_if_true = 0;
- const zend_op *exit_opline = zend_jit_trace_get_exit_opline(p + 1, opline + 1, &exit_if_true);
- uint32_t exit_point;
- if (ra) {
- zend_jit_trace_clenup_stack(stack, opline, ssa_op, ssa, ra);
- }
- exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
- exit_addr = zend_jit_trace_get_exit_addr(exit_point);
- if (!exit_addr) {
- goto jit_failure;
- }
- smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ;
- } else {
- smart_branch_opcode = 0;
- exit_addr = NULL;
- }
- if (!zend_jit_type_check(&dasm_state, opline, op1_info, smart_branch_opcode, -1, -1, exit_addr)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_RETURN:
- op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
- if (opline->op1_type == IS_CONST) {
- res_type = Z_TYPE_P(RT_CONSTANT(opline, opline->op1));
- } else if (op1_type != IS_UNKNOWN) {
- res_type = op1_type;
- }
- if (op_array->type == ZEND_EVAL_CODE
- // TODO: support for top-level code
- || !op_array->function_name
- // TODO: support for IS_UNDEF ???
- || (op1_info & MAY_BE_UNDEF)) {
- if (!zend_jit_trace_handler(&dasm_state, op_array, opline, zend_may_throw(opline, ssa_op, op_array, ssa), p + 1)) {
- goto jit_failure;
- }
- } else {
- int j;
- int may_throw = 0;
- bool left_frame = 0;
- if (!zend_jit_return(&dasm_state, opline, op_array,
- op1_info, OP1_REG_ADDR())) {
- goto jit_failure;
- }
- if (op_array->last_var > 100) {
- /* To many CVs to unroll */
- if (!zend_jit_free_cvs(&dasm_state)) {
- goto jit_failure;
- }
- left_frame = 1;
- }
- if (!left_frame) {
- for (j = 0 ; j < op_array->last_var; j++) {
- uint32_t info;
- zend_uchar type;
- info = zend_ssa_cv_info(op_array, op_array_ssa, j);
- type = STACK_TYPE(stack, j);
- info = zend_jit_trace_type_to_info_ex(type, info);
- if (opline->op1_type == IS_CV
- && EX_VAR_TO_NUM(opline->op1.var) == j
- && !(op1_info & (MAY_BE_REF|MAY_BE_OBJECT))) {
- if (JIT_G(current_frame)
- && TRACE_FRAME_IS_RETURN_VALUE_USED(JIT_G(current_frame))) {
- continue;
- } else {
- info |= MAY_BE_NULL;
- }
- }
- if (info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
- if (!left_frame) {
- left_frame = 1;
- if (!zend_jit_leave_frame(&dasm_state)) {
- goto jit_failure;
- }
- }
- if (!zend_jit_free_cv(&dasm_state, info, j)) {
- goto jit_failure;
- }
- if (info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_RESOURCE)) {
- if (info & MAY_BE_RC1) {
- may_throw = 1;
- }
- }
- }
- }
- }
- if (!zend_jit_leave_func(&dasm_state, op_array, opline, op1_info, left_frame,
- p + 1, &zend_jit_traces[ZEND_JIT_TRACE_NUM],
- (op_array_ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0, may_throw)) {
- goto jit_failure;
- }
- }
- goto done;
- case ZEND_BOOL:
- case ZEND_BOOL_NOT:
- op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
- if (!zend_jit_bool_jmpznz(&dasm_state, opline,
- op1_info, OP1_REG_ADDR(), RES_REG_ADDR(),
- -1, -1,
- zend_may_throw(opline, ssa_op, op_array, ssa),
- opline->opcode, NULL)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
- if ((p+1)->op == ZEND_JIT_TRACE_VM || (p+1)->op == ZEND_JIT_TRACE_END) {
- const zend_op *exit_opline = NULL;
- uint32_t exit_point;
- if ((p+1)->opline == OP_JMP_ADDR(opline, opline->op2)) {
- /* taken branch */
- if (opline->opcode == ZEND_JMPNZ_EX) {
- smart_branch_opcode = ZEND_JMPZ_EX;
- } else if (opline->opcode == ZEND_JMPZ_EX) {
- smart_branch_opcode = ZEND_JMPNZ_EX;
- } else if (opline->opcode == ZEND_JMPNZ) {
- smart_branch_opcode = ZEND_JMPZ;
- } else {
- smart_branch_opcode = ZEND_JMPNZ;
- }
- exit_opline = (opline->opcode == ZEND_JMPZNZ) ?
- ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) :
- opline + 1;
- } else if (opline->opcode == ZEND_JMPZNZ) {
- ZEND_ASSERT((p+1)->opline == ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value));
- smart_branch_opcode = ZEND_JMPZ;
- exit_opline = OP_JMP_ADDR(opline, opline->op2);
- } else if ((p+1)->opline == opline + 1) {
- /* not taken branch */
- smart_branch_opcode = opline->opcode;
- exit_opline = OP_JMP_ADDR(opline, opline->op2);
- } else {
- ZEND_UNREACHABLE();
- }
- if (ra) {
- zend_jit_trace_clenup_stack(stack, opline, ssa_op, ssa, ra);
- }
- if (!(op1_info & MAY_BE_GUARD)
- && has_concrete_type(op1_info)
- && concrete_type(op1_info) <= IS_TRUE) {
- /* unconditional branch */
- exit_addr = NULL;
- } else if (opline->result_type == IS_TMP_VAR) {
- zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
- uint32_t old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
- SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
- exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
- SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_info);
- exit_addr = zend_jit_trace_get_exit_addr(exit_point);
- if (!exit_addr) {
- goto jit_failure;
- }
- } else {
- exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
- exit_addr = zend_jit_trace_get_exit_addr(exit_point);
- if (!exit_addr) {
- goto jit_failure;
- }
- }
- } else {
- ZEND_UNREACHABLE();
- }
- if (opline->result_type == IS_UNDEF) {
- res_addr = 0;
- } else {
- res_addr = RES_REG_ADDR();
- }
- if (!zend_jit_bool_jmpznz(&dasm_state, opline,
- op1_info, OP1_REG_ADDR(), res_addr,
- -1, -1,
- zend_may_throw(opline, ssa_op, op_array, ssa),
- smart_branch_opcode, exit_addr)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_ISSET_ISEMPTY_CV:
- if ((opline->extended_value & ZEND_ISEMPTY)) {
- // TODO: support for empty() ???
- break;
- }
- op1_info = OP1_INFO();
- op1_addr = OP1_REG_ADDR();
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_REFERENCE)) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
- !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa->vars[ssa_op->op1_use].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_use].guarded_reference = 1;
- }
- } else {
- CHECK_OP1_TRACE_TYPE();
- }
- if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- bool exit_if_true = 0;
- const zend_op *exit_opline = zend_jit_trace_get_exit_opline(p + 1, opline + 1, &exit_if_true);
- uint32_t exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
- exit_addr = zend_jit_trace_get_exit_addr(exit_point);
- if (!exit_addr) {
- goto jit_failure;
- }
- smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ;
- } else {
- smart_branch_opcode = 0;
- exit_addr = NULL;
- }
- if (!zend_jit_isset_isempty_cv(&dasm_state, opline,
- op1_info, op1_addr,
- smart_branch_opcode, -1, -1, exit_addr)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_IN_ARRAY:
- if (opline->op1_type == IS_VAR || opline->op1_type == IS_TMP_VAR) {
- break;
- }
- op1_info = OP1_INFO();
- op1_addr = OP1_REG_ADDR();
- CHECK_OP1_TRACE_TYPE();
- if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != MAY_BE_STRING) {
- break;
- }
- if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- bool exit_if_true = 0;
- const zend_op *exit_opline = zend_jit_trace_get_exit_opline(p + 1, opline + 1, &exit_if_true);
- uint32_t exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
- exit_addr = zend_jit_trace_get_exit_addr(exit_point);
- if (!exit_addr) {
- goto jit_failure;
- }
- smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ;
- } else {
- smart_branch_opcode = 0;
- exit_addr = NULL;
- }
- if (!zend_jit_in_array(&dasm_state, opline,
- op1_info, op1_addr,
- smart_branch_opcode, -1, -1, exit_addr)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_FETCH_DIM_FUNC_ARG:
- if (!JIT_G(current_frame)
- || !JIT_G(current_frame)->call
- || !JIT_G(current_frame)->call->func
- || !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
- break;
- }
- ZEND_FALLTHROUGH;
- case ZEND_FETCH_DIM_R:
- case ZEND_FETCH_DIM_IS:
- case ZEND_FETCH_LIST_R:
- op1_info = OP1_INFO();
- op1_addr = OP1_REG_ADDR();
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_REFERENCE)) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
- !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa->vars[ssa_op->op1_use].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_use].guarded_reference = 1;
- if (ssa_op->op1_def >= 0) {
- ssa->var_info[ssa_op->op1_def].guarded_reference = 1;
- }
- }
- } else {
- CHECK_OP1_TRACE_TYPE();
- }
- op2_info = OP2_INFO();
- CHECK_OP2_TRACE_TYPE();
- res_info = RES_INFO();
- avoid_refcounting =
- ssa_op->op1_use >= 0 &&
- ssa->var_info[ssa_op->op1_use].avoid_refcounting;
- if (op1_info & MAY_BE_PACKED_GUARD) {
- ssa->var_info[ssa_op->op1_use].type &= ~MAY_BE_PACKED_GUARD;
- } else if ((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG
- && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY
- && MAY_BE_PACKED(op1_info)
- && MAY_BE_HASH(op1_info)
- && orig_op1_type != IS_UNKNOWN) {
- op1_info |= MAY_BE_PACKED_GUARD;
- if (orig_op1_type & IS_TRACE_PACKED) {
- op1_info &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH);
- if (op1_type != IS_UNKNOWN) {
- ssa->var_info[ssa_op->op1_use].type &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH);
- }
- } else {
- op1_info &= ~MAY_BE_ARRAY_PACKED;
- if (op1_type != IS_UNKNOWN) {
- ssa->var_info[ssa_op->op1_use].type &= ~MAY_BE_ARRAY_PACKED;
- }
- }
- }
- if (!zend_jit_fetch_dim_read(&dasm_state, opline, ssa, ssa_op,
- op1_info, op1_addr, avoid_refcounting,
- op2_info, res_info, RES_REG_ADDR(), val_type)) {
- goto jit_failure;
- }
- if (ssa_op->op1_def >= 0 && op1_type != IS_UNKNOWN) {
- ssa->var_info[ssa_op->op1_def].type = ssa->var_info[ssa_op->op1_use].type;
- }
- goto done;
- case ZEND_FETCH_DIM_W:
- case ZEND_FETCH_DIM_RW:
- // case ZEND_FETCH_DIM_UNSET:
- case ZEND_FETCH_LIST_W:
- if (opline->op1_type != IS_CV
- && (orig_op1_type == IS_UNKNOWN
- || !(orig_op1_type & IS_TRACE_INDIRECT))) {
- break;
- }
- op1_info = OP1_INFO();
- op1_addr = OP1_REG_ADDR();
- if (opline->op1_type == IS_VAR) {
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_INDIRECT)) {
- if (!zend_jit_fetch_indirect_var(&dasm_state, opline, orig_op1_type,
- &op1_info, &op1_addr, !ssa->var_info[ssa_op->op1_use].indirect_reference)) {
- goto jit_failure;
- }
- } else {
- break;
- }
- }
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_REFERENCE)) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
- !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa->vars[ssa_op->op1_def].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_def].guarded_reference = 1;
- }
- } else {
- CHECK_OP1_TRACE_TYPE();
- }
- op2_info = OP2_INFO();
- CHECK_OP2_TRACE_TYPE();
- op1_def_info = OP1_DEF_INFO();
- if (!zend_jit_fetch_dim(&dasm_state, opline,
- op1_info, op1_addr, op2_info, RES_REG_ADDR(), val_type)) {
- goto jit_failure;
- }
- if (ssa_op->result_def > 0
- && (opline->opcode == ZEND_FETCH_DIM_W || opline->opcode == ZEND_FETCH_LIST_W)
- && !(op1_info & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
- && !(op2_info & (MAY_BE_UNDEF|MAY_BE_RESOURCE|MAY_BE_ARRAY|MAY_BE_OBJECT))) {
- ssa->var_info[ssa_op->result_def].indirect_reference = 1;
- }
- goto done;
- case ZEND_ISSET_ISEMPTY_DIM_OBJ:
- if ((opline->extended_value & ZEND_ISEMPTY)) {
- // TODO: support for empty() ???
- break;
- }
- op1_info = OP1_INFO();
- op1_addr = OP1_REG_ADDR();
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_REFERENCE)) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
- !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa->vars[ssa_op->op1_use].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_use].guarded_reference = 1;
- }
- } else {
- CHECK_OP1_TRACE_TYPE();
- }
- op2_info = OP2_INFO();
- CHECK_OP2_TRACE_TYPE();
- if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- bool exit_if_true = 0;
- const zend_op *exit_opline = zend_jit_trace_get_exit_opline(p + 1, opline + 1, &exit_if_true);
- uint32_t exit_point;
- if (ra) {
- zend_jit_trace_clenup_stack(stack, opline, ssa_op, ssa, ra);
- }
- if (ssa_op->op1_use >= 0
- && ssa->var_info[ssa_op->op1_use].avoid_refcounting) {
- /* Temporary reset ZREG_ZVAL_TRY_ADDREF */
- zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
- uint32_t old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var));
- SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
- exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
- SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_info);
- } else {
- exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
- }
- exit_addr = zend_jit_trace_get_exit_addr(exit_point);
- if (!exit_addr) {
- goto jit_failure;
- }
- smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ;
- } else {
- smart_branch_opcode = 0;
- exit_addr = NULL;
- }
- avoid_refcounting =
- ssa_op->op1_use >= 0 &&
- ssa->var_info[ssa_op->op1_use].avoid_refcounting;
- if (op1_info & MAY_BE_PACKED_GUARD) {
- ssa->var_info[ssa_op->op1_use].type &= ~MAY_BE_PACKED_GUARD;
- } else if ((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG
- && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY
- && MAY_BE_PACKED(op1_info)
- && MAY_BE_HASH(op1_info)
- && orig_op1_type != IS_UNKNOWN) {
- op1_info |= MAY_BE_PACKED_GUARD;
- if (orig_op1_type & IS_TRACE_PACKED) {
- op1_info &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH);
- } else {
- op1_info &= ~MAY_BE_ARRAY_PACKED;
- }
- }
- if (!zend_jit_isset_isempty_dim(&dasm_state, opline,
- op1_info, op1_addr, avoid_refcounting,
- op2_info, val_type,
- zend_may_throw_ex(opline, ssa_op, op_array, ssa, op1_info, op2_info),
- smart_branch_opcode, -1, -1,
- exit_addr)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_FETCH_OBJ_FUNC_ARG:
- if (!JIT_G(current_frame)
- || !JIT_G(current_frame)->call
- || !JIT_G(current_frame)->call->func
- || !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
- break;
- }
- ZEND_FALLTHROUGH;
- case ZEND_FETCH_OBJ_R:
- case ZEND_FETCH_OBJ_IS:
- case ZEND_FETCH_OBJ_W:
- on_this = delayed_fetch_this = 0;
- avoid_refcounting = 0;
- if (opline->op2_type != IS_CONST
- || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
- || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
- break;
- }
- ce = NULL;
- ce_is_instanceof = 0;
- op1_indirect = 0;
- if (opline->op1_type == IS_UNUSED) {
- op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
- ce = op_array->scope;
- ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
- op1_addr = 0;
- on_this = 1;
- } else {
- op1_info = OP1_INFO();
- if (!(op1_info & MAY_BE_OBJECT)) {
- break;
- }
- op1_addr = OP1_REG_ADDR();
- if (opline->op1_type == IS_VAR
- && opline->opcode == ZEND_FETCH_OBJ_W) {
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_INDIRECT)) {
- op1_indirect = 1;
- if (!zend_jit_fetch_indirect_var(&dasm_state, opline, orig_op1_type,
- &op1_info, &op1_addr, !ssa->var_info[ssa_op->op1_use].indirect_reference)) {
- goto jit_failure;
- }
- }
- }
- if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_REFERENCE)) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
- !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa->vars[ssa_op->op1_use].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_def >= 0 ? ssa_op->op1_def : ssa_op->op1_use].guarded_reference = 1;
- }
- } else {
- CHECK_OP1_TRACE_TYPE();
- }
- if (!(op1_info & MAY_BE_OBJECT)) {
- break;
- }
- if (ssa->var_info && ssa->ops) {
- if (ssa_op->op1_use >= 0) {
- zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
- if (op1_ssa->ce && !op1_ssa->ce->create_object) {
- ce = op1_ssa->ce;
- ce_is_instanceof = op1_ssa->is_instanceof;
- }
- }
- }
- if (ssa_op->op1_use >= 0) {
- delayed_fetch_this = ssa->var_info[ssa_op->op1_use].delayed_fetch_this;
- avoid_refcounting = ssa->var_info[ssa_op->op1_use].avoid_refcounting;
- }
- if (delayed_fetch_this) {
- on_this = 1;
- } else if (ssa_op->op1_use >= 0 && ssa->vars[ssa_op->op1_use].definition >= 0) {
- on_this = ssa_opcodes[ssa->vars[ssa_op->op1_use].definition]->opcode == ZEND_FETCH_THIS;
- } else if (op_array_ssa->ops
- && op_array_ssa->vars
- && op_array_ssa->ops[opline-op_array->opcodes].op1_use >= 0
- && op_array_ssa->vars[op_array_ssa->ops[opline-op_array->opcodes].op1_use].definition >= 0) {
- on_this = op_array->opcodes[op_array_ssa->vars[op_array_ssa->ops[opline-op_array->opcodes].op1_use].definition].opcode == ZEND_FETCH_THIS;
- }
- }
- if (!zend_jit_fetch_obj(&dasm_state, opline, op_array, ssa, ssa_op,
- op1_info, op1_addr, op1_indirect, ce, ce_is_instanceof,
- on_this, delayed_fetch_this, avoid_refcounting, op1_ce, val_type,
- zend_may_throw_ex(opline, ssa_op, op_array, ssa, op1_info, MAY_BE_STRING))) {
- goto jit_failure;
- }
- goto done;
- case ZEND_BIND_GLOBAL:
- orig_opline = opline;
- orig_ssa_op = ssa_op;
- while (1) {
- if (!ssa->ops || !ssa->var_info) {
- op1_info = MAY_BE_ANY|MAY_BE_REF;
- } else {
- op1_info = OP1_INFO();
- }
- if (ssa->vars[ssa_op->op1_def].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_def].guarded_reference = 1;
- }
- if (!zend_jit_bind_global(&dasm_state, opline, op1_info)) {
- goto jit_failure;
- }
- if ((opline+1)->opcode == ZEND_BIND_GLOBAL) {
- opline++;
- ssa_op++;
- } else {
- break;
- }
- }
- opline = orig_opline;
- ssa_op = orig_ssa_op;
- goto done;
- case ZEND_RECV:
- if (!zend_jit_recv(&dasm_state, opline, op_array)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_RECV_INIT:
- orig_opline = opline;
- orig_ssa_op = ssa_op;
- while (1) {
- if (!zend_jit_recv_init(&dasm_state, opline, op_array,
- (opline + 1)->opcode != ZEND_RECV_INIT,
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- if ((opline+1)->opcode == ZEND_RECV_INIT) {
- opline++;
- ssa_op++;
- } else {
- break;
- }
- }
- opline = orig_opline;
- ssa_op = orig_ssa_op;
- goto done;
- case ZEND_FREE:
- case ZEND_FE_FREE:
- op1_info = OP1_INFO();
- if (!zend_jit_free(&dasm_state, opline, op1_info,
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- goto done;
- case ZEND_ECHO:
- op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
- if ((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_STRING) {
- break;
- }
- if (!zend_jit_echo(&dasm_state, opline, op1_info)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_STRLEN:
- op1_info = OP1_INFO();
- op1_addr = OP1_REG_ADDR();
- if (orig_op1_type == (IS_TRACE_REFERENCE|IS_STRING)) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
- !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa->vars[ssa_op->op1_use].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_use].guarded_reference = 1;
- }
- } else {
- CHECK_OP1_TRACE_TYPE();
- if ((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_STRING) {
- break;
- }
- }
- if (!zend_jit_strlen(&dasm_state, opline, op1_info, op1_addr, RES_REG_ADDR())) {
- goto jit_failure;
- }
- goto done;
- case ZEND_COUNT:
- op1_info = OP1_INFO();
- op1_addr = OP1_REG_ADDR();
- if (orig_op1_type == (IS_TRACE_REFERENCE|IS_ARRAY)) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
- !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa->vars[ssa_op->op1_use].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_use].guarded_reference = 1;
- }
- } else {
- CHECK_OP1_TRACE_TYPE();
- if ((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_ARRAY) {
- break;
- }
- }
- if (!zend_jit_count(&dasm_state, opline, op1_info, op1_addr, RES_REG_ADDR(), zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- goto done;
- case ZEND_FETCH_THIS:
- delayed_fetch_this = 0;
- if (ssa_op->result_def >= 0 && opline->result_type != IS_CV) {
- if (zend_jit_may_delay_fetch_this(op_array, ssa, ssa_opcodes, ssa_op)) {
- ssa->var_info[ssa_op->result_def].delayed_fetch_this = 1;
- delayed_fetch_this = 1;
- }
- }
- if (!zend_jit_fetch_this(&dasm_state, opline, op_array, delayed_fetch_this)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- if (!zend_jit_switch(&dasm_state, opline, op_array, op_array_ssa, p+1, &zend_jit_traces[ZEND_JIT_TRACE_NUM])) {
- goto jit_failure;
- }
- goto done;
- case ZEND_VERIFY_RETURN_TYPE:
- if (opline->op1_type == IS_UNUSED) {
- /* Always throws */
- break;
- }
- if (opline->op1_type == IS_CONST) {
- /* TODO Different instruction format, has return value */
- break;
- }
- if (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) {
- /* Not worth bothering with */
- break;
- }
- op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
- if (op1_info & MAY_BE_REF) {
- /* TODO May need reference unwrapping. */
- break;
- }
- if (!zend_jit_verify_return_type(&dasm_state, opline, op_array, op1_info)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_FE_RESET_R:
- op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
- if ((op1_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) != MAY_BE_ARRAY) {
- break;
- }
- if (!zend_jit_fe_reset(&dasm_state, opline, op1_info)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_FE_FETCH_R:
- op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
- if ((op1_info & MAY_BE_ANY) != MAY_BE_ARRAY) {
- break;
- }
- if ((p+1)->op == ZEND_JIT_TRACE_VM || (p+1)->op == ZEND_JIT_TRACE_END) {
- const zend_op *exit_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
- uint32_t exit_point;
- if ((p+1)->opline == exit_opline) {
- /* taken branch (exit from loop) */
- exit_opline = opline;
- smart_branch_opcode = ZEND_NOP;
- } else if ((p+1)->opline == opline + 1) {
- /* not taken branch (loop) */
- smart_branch_opcode = ZEND_JMP;
- } else {
- ZEND_UNREACHABLE();
- }
- exit_point = zend_jit_trace_get_exit_point(exit_opline, 0);
- exit_addr = zend_jit_trace_get_exit_addr(exit_point);
- if (!exit_addr) {
- goto jit_failure;
- }
- } else {
- ZEND_UNREACHABLE();
- }
- if (!zend_jit_fe_fetch(&dasm_state, opline, op1_info, OP2_INFO(),
- -1, smart_branch_opcode, exit_addr)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_FETCH_CONSTANT:
- if (!zend_jit_fetch_constant(&dasm_state, opline, op_array, ssa, ssa_op, RES_REG_ADDR())) {
- goto jit_failure;
- }
- goto done;
- case ZEND_INIT_METHOD_CALL:
- if (opline->op2_type != IS_CONST
- || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING) {
- goto generic_dynamic_call;
- }
- on_this = delayed_fetch_this = 0;
- ce = NULL;
- ce_is_instanceof = 0;
- if (opline->op1_type == IS_UNUSED) {
- op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
- ce = op_array->scope;
- ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
- op1_addr = 0;
- on_this = 1;
- } else {
- op1_info = OP1_INFO();
- op1_addr = OP1_REG_ADDR();
- if (polymorphic_side_trace) {
- op1_info = MAY_BE_OBJECT;
- op1_addr = 0;
- } else if (orig_op1_type != IS_UNKNOWN
- && (orig_op1_type & IS_TRACE_REFERENCE)) {
- if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr,
- !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
- goto jit_failure;
- }
- if (opline->op1_type == IS_CV
- && ssa->vars[ssa_op->op1_use].alias == NO_ALIAS) {
- ssa->var_info[ssa_op->op1_use].guarded_reference = 1;
- }
- } else {
- CHECK_OP1_TRACE_TYPE();
- }
- if (ssa->var_info && ssa->ops) {
- if (ssa_op->op1_use >= 0) {
- zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
- if (op1_ssa->ce && !op1_ssa->ce->create_object) {
- ce = op1_ssa->ce;
- ce_is_instanceof = op1_ssa->is_instanceof;
- }
- }
- }
- if (ssa_op->op1_use >= 0) {
- delayed_fetch_this = ssa->var_info[ssa_op->op1_use].delayed_fetch_this;
- }
- if (delayed_fetch_this) {
- on_this = 1;
- } else if (ssa_op->op1_use >= 0 && ssa->vars[ssa_op->op1_use].definition >= 0) {
- on_this = ssa_opcodes[ssa->vars[ssa_op->op1_use].definition]->opcode == ZEND_FETCH_THIS;
- } else if (op_array_ssa->ops
- && op_array_ssa->vars
- && op_array_ssa->ops[opline-op_array->opcodes].op1_use >= 0
- && op_array_ssa->vars[op_array_ssa->ops[opline-op_array->opcodes].op1_use].definition >= 0) {
- on_this = op_array->opcodes[op_array_ssa->vars[op_array_ssa->ops[opline-op_array->opcodes].op1_use].definition].opcode == ZEND_FETCH_THIS;
- }
- }
- frame_flags = TRACE_FRAME_MASK_NESTED;
- if (!zend_jit_init_method_call(&dasm_state, opline,
- op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1,
- op_array, ssa, ssa_op, frame->call_level,
- op1_info, op1_addr, ce, ce_is_instanceof, on_this, delayed_fetch_this, op1_ce,
- p + 1, peek_checked_stack - checked_stack, polymorphic_side_trace)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_INIT_DYNAMIC_CALL:
- if (orig_op2_type != IS_OBJECT || op2_ce != zend_ce_closure) {
- goto generic_dynamic_call;
- }
- op2_info = OP2_INFO();
- CHECK_OP2_TRACE_TYPE();
- frame_flags = TRACE_FRAME_MASK_NESTED;
- if (!zend_jit_init_closure_call(&dasm_state, opline, op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, op_array, ssa, ssa_op, frame->call_level, p + 1, peek_checked_stack - checked_stack)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_INIT_STATIC_METHOD_CALL:
- generic_dynamic_call:
- if (!zend_jit_trace_handler(&dasm_state, op_array, opline, zend_may_throw(opline, ssa_op, op_array, ssa), p + 1)) {
- goto jit_failure;
- }
- if ((p+1)->op == ZEND_JIT_TRACE_INIT_CALL && (p+1)->func
- && (opline->opcode != ZEND_INIT_STATIC_METHOD_CALL
- || opline->op1_type != IS_CONST
- || opline->op2_type != IS_CONST
- || zend_jit_may_be_modified((p+1)->func, op_array))) {
- if (!zend_jit_init_fcall_guard(&dasm_state, 0, (p+1)->func, opline+1)) {
- goto jit_failure;
- }
- }
- goto done;
- case ZEND_INIT_USER_CALL:
- if (!zend_jit_trace_handler(&dasm_state, op_array, opline, zend_may_throw(opline, ssa_op, op_array, ssa), p + 1)) {
- goto jit_failure;
- }
- if ((p+1)->op == ZEND_JIT_TRACE_INIT_CALL && (p+1)->func
- && (opline->op2_type != IS_CONST
- || zend_jit_may_be_modified((p+1)->func, op_array))) {
- if (!zend_jit_init_fcall_guard(&dasm_state, 0, (p+1)->func, opline+1)) {
- goto jit_failure;
- }
- }
- goto done;
- case ZEND_NEW:
- if (!zend_jit_trace_handler(&dasm_state, op_array, opline, zend_may_throw(opline, ssa_op, op_array, ssa), p + 1)) {
- goto jit_failure;
- }
- if ((p+1)->op == ZEND_JIT_TRACE_INIT_CALL && (p+1)->func
- && (opline->op1_type != IS_CONST
- || zend_jit_may_be_modified((p+1)->func, op_array))) {
- SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_OBJECT, 1);
- if (!zend_jit_init_fcall_guard(&dasm_state, 0, (p+1)->func, opline+1)) {
- goto jit_failure;
- }
- }
- goto done;
- case ZEND_SEND_ARRAY:
- case ZEND_SEND_UNPACK:
- if (JIT_G(current_frame)
- && JIT_G(current_frame)->call) {
- TRACE_FRAME_SET_UNKNOWN_NUM_ARGS(JIT_G(current_frame)->call);
- }
- break;
- case ZEND_ROPE_INIT:
- case ZEND_ROPE_ADD:
- case ZEND_ROPE_END:
- op2_info = OP2_INFO();
- CHECK_OP2_TRACE_TYPE();
- if ((op2_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_STRING) {
- break;
- }
- if (!zend_jit_rope(&dasm_state, opline, op2_info)) {
- goto jit_failure;
- }
- goto done;
- default:
- break;
- }
- }
- if (opline->opcode != ZEND_NOP && opline->opcode != ZEND_JMP) {
- gen_handler = 1;
- op1_info = OP1_INFO();
- op2_info = OP2_INFO();
- if (op1_info & MAY_BE_GUARD) {
- op1_info = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- }
- if (op2_info & MAY_BE_GUARD) {
- op2_info = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- }
- if (!zend_jit_trace_handler(&dasm_state, op_array, opline,
- zend_may_throw_ex(opline, ssa_op, op_array, ssa, op1_info, op2_info), p + 1)) {
- goto jit_failure;
- }
- }
- done:
- polymorphic_side_trace = 0;
- switch (opline->opcode) {
- case ZEND_DO_FCALL:
- case ZEND_DO_ICALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- frame->call_level--;
- }
- if (ra) {
- zend_jit_trace_clenup_stack(stack, opline, ssa_op, ssa, ra);
- }
- if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
- && STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var)) > ZREG_NUM) {
- SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
- }
- if (opline->opcode == ZEND_ROPE_INIT) {
- /* clear stack slots used by rope */
- uint32_t var = EX_VAR_TO_NUM(opline->result.var);
- uint32_t count =
- ((opline->extended_value * sizeof(void*)) + (sizeof(zval)-1)) / sizeof(zval);
- do {
- SET_STACK_TYPE(stack, var, IS_UNKNOWN, 1);
- var++;
- count--;
- } while (count);
- }
- if (ssa_op) {
- zend_ssa_range tmp;
- /* Keep information about known types on abstract stack */
- if (ssa_op->result_def >= 0) {
- zend_uchar type = IS_UNKNOWN;
- if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0
- || send_result) {
- /* we didn't set result variable */
- type = IS_UNKNOWN;
- } else if (!(ssa->var_info[ssa_op->result_def].type & MAY_BE_GUARD)
- && has_concrete_type(ssa->var_info[ssa_op->result_def].type)) {
- type = concrete_type(ssa->var_info[ssa_op->result_def].type);
- } else if (opline->opcode == ZEND_QM_ASSIGN) {
- if (opline->op1_type != IS_CONST) {
- /* copy */
- type = STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var));
- }
- } else if (opline->opcode == ZEND_ASSIGN) {
- if (opline->op2_type != IS_CONST
- && ssa_op->op1_use >= 0
- /* assignment to typed reference may cause conversion */
- && (ssa->var_info[ssa_op->op1_use].type & MAY_BE_REF) == 0) {
- /* copy */
- type = STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op2.var));
- }
- } else if (opline->opcode == ZEND_POST_INC
- || opline->opcode == ZEND_POST_DEC) {
- /* copy */
- type = STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var));
- }
- if (opline->opcode == ZEND_JMP_SET
- || opline->opcode == ZEND_COALESCE
- || opline->opcode == ZEND_JMP_NULL) {
- if ((p+1)->op != ZEND_JIT_TRACE_VM) {
- SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN, 1);
- } else if ((p+1)->opline != (opline + 1)) {
- SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), type, 1);
- }
- } else {
- SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), type,
- (gen_handler || type == IS_UNKNOWN || !ra || !ra[ssa_op->result_def]));
- if (ssa->var_info[ssa_op->result_def].type & MAY_BE_INDIRECT) {
- RESET_STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->result.var));
- }
- if (type != IS_UNKNOWN) {
- ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;
- if (opline->opcode == ZEND_FETCH_THIS
- && delayed_fetch_this) {
- SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_THIS);
- } else if (ssa->var_info[ssa_op->result_def].avoid_refcounting) {
- SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_TRY_ADDREF);
- } else if (ra && ra[ssa_op->result_def]) {
- SET_STACK_REG_EX(stack, EX_VAR_TO_NUM(opline->result.var), ra[ssa_op->result_def]->reg,
- ra[ssa_op->result_def]->flags & ZREG_STORE);
- }
- }
- }
- if (type == IS_LONG
- && zend_inference_propagate_range(op_array, ssa, (zend_op*)opline, (zend_ssa_op*)ssa_op, ssa_op->result_def, &tmp)) {
- ssa->var_info[ssa_op->result_def].range.min = tmp.min;
- ssa->var_info[ssa_op->result_def].range.max = tmp.max;
- ssa->var_info[ssa_op->result_def].range.underflow = 0;
- ssa->var_info[ssa_op->result_def].range.overflow = 0;
- ssa->var_info[ssa_op->result_def].has_range = 1;
- }
- }
- if (ssa_op->op1_def >= 0
- && (opline->opcode != ZEND_QM_ASSIGN
- || opline->result_type != IS_CV
- || opline->result.var != opline->op1.var)) {
- zend_uchar type = IS_UNKNOWN;
- if (!(ssa->var_info[ssa_op->op1_def].type & MAY_BE_GUARD)
- && has_concrete_type(ssa->var_info[ssa_op->op1_def].type)) {
- type = concrete_type(ssa->var_info[ssa_op->op1_def].type);
- } else if (opline->opcode == ZEND_ASSIGN) {
- if (!(OP1_INFO() & MAY_BE_REF)
- || STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var)) != IS_UNKNOWN) {
- if (opline->op2_type != IS_CONST) {
- /* copy */
- type = STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op2.var));
- }
- }
- } else if (opline->opcode == ZEND_SEND_VAR
- || opline->opcode == ZEND_CAST
- || opline->opcode == ZEND_QM_ASSIGN
- || opline->opcode == ZEND_JMP_SET
- || opline->opcode == ZEND_COALESCE
- || opline->opcode == ZEND_JMP_NULL
- || opline->opcode == ZEND_FE_RESET_R) {
- /* keep old value */
- type = STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var));
- }
- SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), type,
- (gen_handler || type == IS_UNKNOWN || !ra ||
- (!ra[ssa_op->op1_def] &&
- (opline->opcode == ZEND_ASSIGN || !ssa->vars[ssa_op->op1_def].no_val))));
- if (type != IS_UNKNOWN) {
- ssa->var_info[ssa_op->op1_def].type &= ~MAY_BE_GUARD;
- if (ra && ra[ssa_op->op1_def]) {
- uint8_t flags = ra[ssa_op->op1_def]->flags & ZREG_STORE;
- if (ssa_op->op1_use >= 0) {
- if (opline->opcode == ZEND_SEND_VAR
- || opline->opcode == ZEND_CAST
- || opline->opcode == ZEND_QM_ASSIGN
- || opline->opcode == ZEND_JMP_SET
- || opline->opcode == ZEND_COALESCE
- || opline->opcode == ZEND_JMP_NULL
- || opline->opcode == ZEND_FE_RESET_R) {
- if (!ra[ssa_op->op1_use]) {
- flags |= ZREG_LOAD;
- }
- }
- }
- SET_STACK_REG_EX(stack, EX_VAR_TO_NUM(opline->op1.var), ra[ssa_op->op1_def]->reg, flags);
- }
- }
- if (type == IS_LONG
- && zend_inference_propagate_range(op_array, ssa, (zend_op*)opline, (zend_ssa_op*)ssa_op, ssa_op->op1_def, &tmp)) {
- ssa->var_info[ssa_op->op1_def].range.min = tmp.min;
- ssa->var_info[ssa_op->op1_def].range.max = tmp.max;
- ssa->var_info[ssa_op->op1_def].range.underflow = 0;
- ssa->var_info[ssa_op->op1_def].range.overflow = 0;
- ssa->var_info[ssa_op->op1_def].has_range = 1;
- }
- }
- if (ssa_op->op2_def >= 0
- && (opline->opcode != ZEND_ASSIGN
- || opline->op1_type != IS_CV
- || opline->op1.var != opline->op2.var)) {
- zend_uchar type = IS_UNKNOWN;
- if (!(ssa->var_info[ssa_op->op2_def].type & MAY_BE_GUARD)
- && has_concrete_type(ssa->var_info[ssa_op->op2_def].type)) {
- type = concrete_type(ssa->var_info[ssa_op->op2_def].type);
- } else if (opline->opcode == ZEND_ASSIGN) {
- /* keep old value */
- type = STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op2.var));
- }
- SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op2.var), type,
- (gen_handler || type == IS_UNKNOWN || !ra ||
- (!ra[ssa_op->op2_def] && !ssa->vars[ssa_op->op2_def].no_val)));
- if (type != IS_UNKNOWN) {
- ssa->var_info[ssa_op->op2_def].type &= ~MAY_BE_GUARD;
- if (ra && ra[ssa_op->op2_def]) {
- uint8_t flags = ra[ssa_op->op2_def]->flags & ZREG_STORE;
- if (ssa_op->op2_use >= 0) {
- if (opline->opcode == ZEND_ASSIGN) {
- if (!ra[ssa_op->op2_use]
- || ra[ssa_op->op2_use]->reg != ra[ssa_op->op2_def]->reg) {
- flags |= ZREG_LOAD;
- }
- }
- }
- SET_STACK_REG_EX(stack, EX_VAR_TO_NUM(opline->op2.var), ra[ssa_op->op2_def]->reg, flags);
- }
- }
- if (type == IS_LONG
- && zend_inference_propagate_range(op_array, ssa, (zend_op*)opline, (zend_ssa_op*)ssa_op, ssa_op->op2_def, &tmp)) {
- ssa->var_info[ssa_op->op2_def].range.min = tmp.min;
- ssa->var_info[ssa_op->op2_def].range.max = tmp.max;
- ssa->var_info[ssa_op->op2_def].range.underflow = 0;
- ssa->var_info[ssa_op->op2_def].range.overflow = 0;
- ssa->var_info[ssa_op->op2_def].has_range = 1;
- }
- }
- switch (opline->opcode) {
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_OBJ:
- case ZEND_ASSIGN_STATIC_PROP:
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- case ZEND_ASSIGN_OBJ_REF:
- case ZEND_ASSIGN_STATIC_PROP_REF:
- /* OP_DATA */
- ssa_op++;
- opline++;
- if (ssa_op->op1_def >= 0) {
- zend_uchar type = IS_UNKNOWN;
- if (!(ssa->var_info[ssa_op->op1_def].type & MAY_BE_GUARD)
- && has_concrete_type(ssa->var_info[ssa_op->op1_def].type)) {
- type = concrete_type(ssa->var_info[ssa_op->op1_def].type);
- } else if ((opline-1)->opcode == ZEND_ASSIGN_DIM
- || (opline-1)->opcode == ZEND_ASSIGN_OBJ
- || (opline-1)->opcode == ZEND_ASSIGN_STATIC_PROP) {
- /* keep old value */
- type = STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var));
- }
- SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), type,
- (gen_handler || type == IS_UNKNOWN || !ra || !ra[ssa_op->op1_def]));
- if (type != IS_UNKNOWN) {
- ssa->var_info[ssa_op->op1_def].type &= ~MAY_BE_GUARD;
- if (ra && ra[ssa_op->op1_def]) {
- SET_STACK_REG_EX(stack, EX_VAR_TO_NUM(opline->op1.var), ra[ssa_op->op1_def]->reg,
- ra[ssa_op->op1_def]->flags & ZREG_STORE);
- }
- }
- if (type == IS_LONG
- && zend_inference_propagate_range(op_array, ssa, (zend_op*)opline, (zend_ssa_op*)ssa_op, ssa_op->op1_def, &tmp)) {
- ssa->var_info[ssa_op->op1_def].range.min = tmp.min;
- ssa->var_info[ssa_op->op1_def].range.max = tmp.max;
- ssa->var_info[ssa_op->op1_def].range.underflow = 0;
- ssa->var_info[ssa_op->op1_def].range.overflow = 0;
- ssa->var_info[ssa_op->op1_def].has_range = 1;
- }
- }
- ssa_op++;
- break;
- case ZEND_RECV_INIT:
- ssa_op++;
- opline++;
- while (opline->opcode == ZEND_RECV_INIT) {
- if (ssa_op->result_def >= 0) {
- zend_uchar type = IS_UNKNOWN;
- if (!(ssa->var_info[ssa_op->result_def].type & MAY_BE_GUARD)
- && has_concrete_type(ssa->var_info[ssa_op->result_def].type)) {
- type = concrete_type(ssa->var_info[ssa_op->result_def].type);
- }
- SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), type,
- (gen_handler || !ra || !ra[ssa_op->result_def]));
- if (ra && ra[ssa_op->result_def]) {
- SET_STACK_REG_EX(stack, EX_VAR_TO_NUM(opline->result.var), ra[ssa_op->result_def]->reg,
- ra[ssa_op->result_def]->flags & ZREG_STORE);
- }
- }
- ssa_op++;
- opline++;
- }
- break;
- case ZEND_BIND_GLOBAL:
- ssa_op++;
- opline++;
- while (opline->opcode == ZEND_BIND_GLOBAL) {
- if (ssa_op->op1_def >= 0) {
- zend_uchar type = IS_UNKNOWN;
- if (!(ssa->var_info[ssa_op->op1_def].type & MAY_BE_GUARD)
- && has_concrete_type(ssa->var_info[ssa_op->op1_def].type)) {
- type = concrete_type(ssa->var_info[ssa_op->op1_def].type);
- }
- SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), type,
- (gen_handler || !ra || !ra[ssa_op->op1_def]));
- if (ra && ra[ssa_op->op1_def]) {
- SET_STACK_REG_EX(stack, EX_VAR_TO_NUM(opline->op1.var), ra[ssa_op->op1_def]->reg,
- ra[ssa_op->op1_def]->flags & ZREG_STORE);
- }
- }
- ssa_op++;
- opline++;
- }
- break;
- default:
- ssa_op += zend_jit_trace_op_len(opline);
- break;
- }
- if (send_result) {
- ssa_op++;
- p++;
- if ((p+1)->op == ZEND_JIT_TRACE_OP1_TYPE) {
- p++;
- }
- send_result = 0;
- }
- }
- } else if (p->op == ZEND_JIT_TRACE_ENTER) {
- call = frame->call;
- assert(call && &call->func->op_array == p->op_array);
- if (opline->opcode == ZEND_DO_UCALL
- || opline->opcode == ZEND_DO_FCALL_BY_NAME
- || opline->opcode == ZEND_DO_FCALL) {
- frame->call_opline = opline;
- /* Check if SEND_UNPACK/SEND_ARRAY may cause enter at different opline */
- if (opline > op_array->opcodes) {
- const zend_op *prev_opline = opline - 1;
- while (prev_opline->opcode == ZEND_EXT_FCALL_BEGIN || prev_opline->opcode == ZEND_TICKS) {
- prev_opline--;
- }
- JIT_G(current_frame) = call;
- if ((prev_opline->opcode == ZEND_SEND_ARRAY
- || prev_opline->opcode == ZEND_SEND_UNPACK
- || prev_opline->opcode == ZEND_CHECK_UNDEF_ARGS)
- && p->op_array->num_args
- && (p->op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0
- && ((p+1)->op == ZEND_JIT_TRACE_VM
- || (p+1)->op == ZEND_JIT_TRACE_END)
- && (TRACE_FRAME_NUM_ARGS(call) < 0
- || TRACE_FRAME_NUM_ARGS(call) < p->op_array->num_args)
- && !zend_jit_trace_opline_guard(&dasm_state, (p+1)->opline)) {
- goto jit_failure;
- }
- JIT_G(current_frame) = frame;
- }
- }
- if ((p+1)->op == ZEND_JIT_TRACE_END) {
- p++;
- break;
- }
- if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
- if (TRACE_FRAME_IS_THIS_CHECKED(frame)) {
- TRACE_FRAME_SET_THIS_CHECKED(call);
- }
- } else if (op_array->scope && !(op_array->fn_flags & ZEND_ACC_STATIC)) {
- TRACE_FRAME_SET_THIS_CHECKED(call);
- }
- op_array = (zend_op_array*)p->op_array;
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- op_array_ssa = &jit_extension->func_info.ssa;
- frame->call = call->prev;
- call->prev = frame;
- if (p->info & ZEND_JIT_TRACE_RETURN_VALUE_USED) {
- TRACE_FRAME_SET_RETURN_VALUE_USED(call);
- } else {
- TRACE_FRAME_SET_RETURN_VALUE_UNUSED(call);
- }
- JIT_G(current_frame) = frame = call;
- stack = frame->stack;
- if (ra) {
- int j = ZEND_JIT_TRACE_GET_FIRST_SSA_VAR(p->info);
- for (i = 0; i < op_array->last_var; i++,j++) {
- if (ra[j] && (ra[j]->flags & ZREG_LOAD) != 0) {
- SET_STACK_REG_EX(stack, i, ra[j]->reg, ZREG_LOAD);
- if (!zend_jit_load_var(&dasm_state, ssa->var_info[j].type, i, ra[j]->reg)) {
- goto jit_failure;
- }
- }
- }
- }
- } else if (p->op == ZEND_JIT_TRACE_BACK) {
- op_array = (zend_op_array*)p->op_array;
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- op_array_ssa = &jit_extension->func_info.ssa;
- top = frame;
- if (frame->prev) {
- checked_stack -= frame->used_stack;
- frame = frame->prev;
- stack = frame->stack;
- ZEND_ASSERT(&frame->func->op_array == op_array);
- } else {
- frame = zend_jit_trace_ret_frame(frame, op_array);
- TRACE_FRAME_INIT(frame, op_array, TRACE_FRAME_MASK_UNKNOWN_RETURN, -1);
- frame->used_stack = checked_stack = peek_checked_stack = 0;
- stack = frame->stack;
- if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_INLINE) {
- uint32_t j = ZEND_JIT_TRACE_GET_FIRST_SSA_VAR(p->info);
- for (i = 0; i < op_array->last_var + op_array->T; i++, j++) {
- /* Initialize abstract stack using SSA */
- if (!(ssa->var_info[j].type & MAY_BE_GUARD)
- && has_concrete_type(ssa->var_info[j].type)) {
- SET_STACK_TYPE(stack, i, concrete_type(ssa->var_info[j].type), 1);
- } else {
- SET_STACK_TYPE(stack, i, IS_UNKNOWN, 1);
- }
- }
- if (ra) {
- j = ZEND_JIT_TRACE_GET_FIRST_SSA_VAR(p->info);
- for (i = 0; i < op_array->last_var + op_array->T; i++, j++) {
- if (ra[j] && (ra[j]->flags & ZREG_LOAD) != 0) {
- SET_STACK_REG_EX(stack, i, ra[j]->reg, ZREG_LOAD);
- if (!zend_jit_load_var(&dasm_state, ssa->var_info[j].type, i, ra[j]->reg)) {
- goto jit_failure;
- }
- }
- }
- }
- } else {
- for (i = 0; i < op_array->last_var + op_array->T; i++) {
- SET_STACK_TYPE(stack, i, IS_UNKNOWN, 1);
- }
- }
- opline = NULL;
- }
- JIT_G(current_frame) = frame;
- if (res_type != IS_UNKNOWN
- && (p+1)->op == ZEND_JIT_TRACE_VM) {
- const zend_op *opline = (p+1)->opline - 1;
- if (opline->result_type != IS_UNUSED) {
- SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), res_type, 1);
- }
- }
- res_type = IS_UNKNOWN;
- } else if (p->op == ZEND_JIT_TRACE_END) {
- break;
- } else if (p->op == ZEND_JIT_TRACE_INIT_CALL) {
- const zend_op *init_opline = zend_jit_trace_find_init_fcall_op(p, op_array);
- int num_args = -1;
- if (init_opline
- && init_opline->extended_value <= TRACE_FRAME_MAX_NUM_ARGS) {
- num_args = init_opline->extended_value;
- }
- call = top;
- TRACE_FRAME_INIT(call, p->func, frame_flags, num_args);
- call->prev = frame->call;
- if (!(p->info & ZEND_JIT_TRACE_FAKE_INIT_CALL)) {
- TRACE_FRAME_SET_LAST_SEND_BY_VAL(call);
- }
- if (init_opline
- && init_opline->opcode != ZEND_NEW
- && (init_opline->opcode != ZEND_INIT_METHOD_CALL
- || init_opline->op1_type == IS_UNDEF)
- && (init_opline->opcode != ZEND_INIT_USER_CALL
- || (p->func && (!p->func->common.scope || (p->func->common.fn_flags & ZEND_ACC_STATIC))))
- && (init_opline->opcode != ZEND_INIT_DYNAMIC_CALL
- || (p->func && (!p->func->common.scope || (p->func->common.fn_flags & ZEND_ACC_STATIC))))
- ) {
- TRACE_FRAME_SET_NO_NEED_RELEASE_THIS(call);
- }
- frame->call = call;
- top = zend_jit_trace_call_frame(top, p->op_array);
- if (p->func) {
- if (p->func->type == ZEND_USER_FUNCTION) {
- if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_INLINE) {
- zend_jit_op_array_trace_extension *jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(p->op_array);
- i = 0;
- while (i < p->op_array->num_args) {
- /* Types of arguments are going to be stored in abstract stack when processing SEV instruction */
- SET_STACK_TYPE(call->stack, i, IS_UNKNOWN, 1);
- i++;
- }
- while (i < p->op_array->last_var) {
- if (jit_extension
- && zend_jit_var_may_alias(p->op_array, &jit_extension->func_info.ssa, i) != NO_ALIAS) {
- SET_STACK_TYPE(call->stack, i, IS_UNKNOWN, 1);
- } else {
- SET_STACK_TYPE(call->stack, i, IS_UNDEF, 1);
- }
- i++;
- }
- while (i < p->op_array->last_var + p->op_array->T) {
- SET_STACK_TYPE(call->stack, i, IS_UNKNOWN, 1);
- i++;
- }
- } else {
- for (i = 0; i < p->op_array->last_var + p->op_array->T; i++) {
- SET_STACK_TYPE(call->stack, i, IS_UNKNOWN, 1);
- }
- }
- } else {
- ZEND_ASSERT(p->func->type == ZEND_INTERNAL_FUNCTION);
- for (i = 0; i < p->op_array->num_args; i++) {
- SET_STACK_TYPE(call->stack, i, IS_UNKNOWN, 1);
- }
- }
- if (p->info & ZEND_JIT_TRACE_FAKE_INIT_CALL) {
- int skip_guard = 0;
- if (init_opline) {
- zend_call_info *call_info = jit_extension->func_info.callee_info;
- while (call_info) {
- if (call_info->caller_init_opline == init_opline
- && !call_info->is_prototype) {
- if (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE) {
- if (init_opline->opcode == ZEND_INIT_STATIC_METHOD_CALL
- && init_opline->op1_type != IS_CONST) {
- break;
- } else if (init_opline->opcode == ZEND_INIT_METHOD_CALL) {
- break;
- }
- }
- skip_guard = 1;
- break;
- }
- call_info = call_info->next_callee;
- }
- if (!skip_guard
- && !zend_jit_may_be_polymorphic_call(init_opline)
- && !zend_jit_may_be_modified(p->func, op_array)) {
- skip_guard = 1;
- }
- }
- if (!skip_guard) {
- if (!opline) {
- zend_jit_trace_rec *q = p + 1;
- while (q->op != ZEND_JIT_TRACE_VM && q->op != ZEND_JIT_TRACE_END) {
- q++;
- }
- opline = q->opline;
- ZEND_ASSERT(opline != NULL);
- }
- if (!zend_jit_init_fcall_guard(&dasm_state,
- ZEND_JIT_TRACE_FAKE_LEVEL(p->info), p->func, opline)) {
- goto jit_failure;
- }
- }
- }
- }
- if (p->info & ZEND_JIT_TRACE_FAKE_INIT_CALL) {
- frame->call_level++;
- call->used_stack = 0;
- } else {
- if (p->func) {
- call->used_stack = zend_vm_calc_used_stack(init_opline->extended_value, (zend_function*)p->func);
- } else {
- call->used_stack = (ZEND_CALL_FRAME_SLOT + init_opline->extended_value) * sizeof(zval);
- }
- checked_stack += call->used_stack;
- if (checked_stack > peek_checked_stack) {
- peek_checked_stack = checked_stack;
- }
- }
- } else if (p->op == ZEND_JIT_TRACE_DO_ICALL) {
- call = frame->call;
- if (call) {
- checked_stack -= call->used_stack;
- top = call;
- frame->call = call->prev;
- }
- } else {
- ZEND_UNREACHABLE();
- }
- }
- ZEND_ASSERT(p->op == ZEND_JIT_TRACE_END);
- t = &zend_jit_traces[ZEND_JIT_TRACE_NUM];
- if (!parent_trace && zend_jit_trace_uses_initial_ip()) {
- t->flags |= ZEND_JIT_TRACE_USES_INITIAL_IP;
- }
- if (p->stop == ZEND_JIT_TRACE_STOP_LOOP
- || p->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
- || p->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- if (ra) {
- zend_ssa_phi *phi = ssa->blocks[1].phis;
- while (phi) {
- if (ra[phi->ssa_var]
- && ra[phi->sources[1]]
- && STACK_MEM_TYPE(stack, phi->var) != STACK_TYPE(stack, phi->var)
- && (ra[phi->ssa_var]->flags & (ZREG_LOAD|ZREG_STORE)) == 0
- && (ra[phi->sources[1]]->flags & (ZREG_LOAD|ZREG_STORE)) == 0) {
- /* Store actual type to memory to avoid deoptimization mistakes */
- /* TODO: Alternatively, we may try to update alredy generated deoptimization info */
- zend_jit_store_var_type(&dasm_state, phi->var, STACK_TYPE(stack, phi->var));
- }
- phi = phi->next;
- }
- }
- if (p->stop != ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- if ((t->flags & ZEND_JIT_TRACE_USES_INITIAL_IP)
- && !zend_jit_set_ip(&dasm_state, p->opline)) {
- goto jit_failure;
- }
- }
- t->link = ZEND_JIT_TRACE_NUM;
- if (p->stop != ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- t->flags |= ZEND_JIT_TRACE_CHECK_INTERRUPT;
- }
- if (!(t->flags & ZEND_JIT_TRACE_LOOP)) {
- const void *timeout_exit_addr = NULL;
- t->flags |= ZEND_JIT_TRACE_LOOP;
- if (trace_buffer->stop != ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- if (!(t->flags & ZEND_JIT_TRACE_USES_INITIAL_IP)
- || (ra
- && zend_jit_trace_stack_needs_deoptimization(stack, op_array->last_var + op_array->T))) {
- uint32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
- timeout_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
- if (!timeout_exit_addr) {
- goto jit_failure;
- }
- } else {
- timeout_exit_addr = dasm_labels[zend_lbinterrupt_handler];
- }
- }
- zend_jit_trace_end_loop(&dasm_state, 0, timeout_exit_addr); /* jump back to start of the trace loop */
- }
- } else if (p->stop == ZEND_JIT_TRACE_STOP_LINK
- || p->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) {
- if (!zend_jit_trace_deoptimization(&dasm_state, 0, NULL,
- stack, op_array->last_var + op_array->T, NULL, NULL, NULL, 0)) {
- goto jit_failure;
- }
- if (p->stop == ZEND_JIT_TRACE_STOP_LINK) {
- const void *timeout_exit_addr = NULL;
- t->link = zend_jit_find_trace(p->opline->handler);
- if ((zend_jit_traces[t->link].flags & ZEND_JIT_TRACE_USES_INITIAL_IP)
- && !zend_jit_set_ip(&dasm_state, p->opline)) {
- goto jit_failure;
- }
- if (!parent_trace && zend_jit_trace_uses_initial_ip()) {
- t->flags |= ZEND_JIT_TRACE_USES_INITIAL_IP;
- }
- if (parent_trace
- && (zend_jit_traces[t->link].flags & ZEND_JIT_TRACE_CHECK_INTERRUPT)
- && zend_jit_traces[parent_trace].root == t->link) {
- if (!(zend_jit_traces[t->link].flags & ZEND_JIT_TRACE_USES_INITIAL_IP)) {
- uint32_t exit_point;
- for (i = 0; i < op_array->last_var + op_array->T; i++) {
- SET_STACK_TYPE(stack, i, IS_UNKNOWN, 1);
- }
- exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
- timeout_exit_addr = zend_jit_trace_get_exit_addr(exit_point);
- if (!timeout_exit_addr) {
- goto jit_failure;
- }
- } else {
- timeout_exit_addr = dasm_labels[zend_lbinterrupt_handler];
- }
- }
- zend_jit_trace_link_to_root(&dasm_state, &zend_jit_traces[t->link], timeout_exit_addr);
- } else {
- zend_jit_trace_return(&dasm_state, 0, NULL);
- }
- } else if (p->stop == ZEND_JIT_TRACE_STOP_RETURN) {
- zend_jit_trace_return(&dasm_state, 0, NULL);
- } else {
- // TODO: not implemented ???
- ZEND_ASSERT(0 && p->stop);
- }
- if (ZEND_JIT_EXIT_COUNTERS + t->exit_count >= JIT_G(max_exit_counters)) {
- goto jit_failure;
- }
- if (!zend_jit_trace_end(&dasm_state, t)) {
- goto jit_failure;
- }
- handler = dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, ZSTR_VAL(name), ZEND_JIT_TRACE_NUM,
- parent_trace ? SP_ADJ_JIT : ((zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) ? SP_ADJ_VM : SP_ADJ_RET),
- parent_trace ? SP_ADJ_NONE : SP_ADJ_JIT);
- if (handler) {
- if (p->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL) {
- const zend_op_array *rec_op_array;
- rec_op_array = op_array = trace_buffer->op_array;
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- p = trace_buffer + ZEND_JIT_TRACE_START_REC_SIZE;
- for (;;p++) {
- if (p->op == ZEND_JIT_TRACE_VM) {
- opline = p->opline;
- } else if (p->op == ZEND_JIT_TRACE_ENTER) {
- if (p->op_array == rec_op_array) {
- zend_jit_trace_setup_ret_counter(opline, jit_extension->offset);
- }
- op_array = p->op_array;
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- } else if (p->op == ZEND_JIT_TRACE_BACK) {
- op_array = p->op_array;
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- } else if (p->op == ZEND_JIT_TRACE_END) {
- break;
- }
- }
- } else if (p->stop == ZEND_JIT_TRACE_STOP_LINK
- || p->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) {
- if (opline
- && (opline->opcode == ZEND_DO_UCALL
- || opline->opcode == ZEND_DO_FCALL
- || opline->opcode == ZEND_DO_FCALL_BY_NAME
- || opline->opcode == ZEND_YIELD
- || opline->opcode == ZEND_YIELD_FROM
- || opline->opcode == ZEND_INCLUDE_OR_EVAL)) {
- zend_jit_trace_setup_ret_counter(opline, jit_extension->offset);
- }
- if (JIT_G(current_frame)
- && JIT_G(current_frame)->prev) {
- frame = JIT_G(current_frame)->prev;
- do {
- if (frame->call_opline) {
- op_array = &frame->func->op_array;
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- zend_jit_trace_setup_ret_counter(frame->call_opline, jit_extension->offset);
- }
- frame = frame->prev;
- } while (frame);
- }
- }
- }
- jit_failure:
- dasm_free(&dasm_state);
- if (name) {
- zend_string_release(name);
- }
- jit_cleanup:
- /* Clean up used op_arrays */
- while (num_op_arrays > 0) {
- op_array = op_arrays[--num_op_arrays];
- jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- jit_extension->func_info.num = 0;
- jit_extension->func_info.flags &= ZEND_FUNC_JIT_ON_FIRST_EXEC
- | ZEND_FUNC_JIT_ON_PROF_REQUEST
- | ZEND_FUNC_JIT_ON_HOT_COUNTERS
- | ZEND_FUNC_JIT_ON_HOT_TRACE;
- memset(&jit_extension->func_info.ssa, 0, sizeof(zend_func_info) - offsetof(zend_func_info, ssa));
- }
- zend_arena_release(&CG(arena), checkpoint);
- JIT_G(current_frame) = NULL;
- JIT_G(current_trace) = NULL;
- return handler;
- }
- static const void *zend_jit_trace_exit_to_vm(uint32_t trace_num, uint32_t exit_num)
- {
- const void *handler = NULL;
- dasm_State* dasm_state = NULL;
- void *checkpoint;
- char name[32];
- const zend_op *opline;
- uint32_t stack_size;
- zend_jit_trace_stack *stack;
- bool original_handler = 0;
- if (!zend_jit_trace_exit_needs_deoptimization(trace_num, exit_num)) {
- return dasm_labels[zend_lbtrace_escape];
- }
- checkpoint = zend_arena_checkpoint(CG(arena));;
- sprintf(name, "ESCAPE-%d-%d", trace_num, exit_num);
- dasm_init(&dasm_state, DASM_MAXSECTION);
- dasm_setupglobal(&dasm_state, dasm_labels, zend_lb_MAX);
- dasm_setup(&dasm_state, dasm_actions);
- zend_jit_align_func(&dasm_state);
- /* Deoptimization */
- stack_size = zend_jit_traces[trace_num].exit_info[exit_num].stack_size;
- stack = zend_jit_traces[trace_num].stack_map + zend_jit_traces[trace_num].exit_info[exit_num].stack_offset;
- if (!zend_jit_trace_deoptimization(&dasm_state,
- zend_jit_traces[trace_num].exit_info[exit_num].flags,
- zend_jit_traces[trace_num].exit_info[exit_num].opline,
- stack, stack_size, NULL, NULL, NULL, 0)) {
- goto jit_failure;
- }
- opline = zend_jit_traces[trace_num].exit_info[exit_num].opline;
- if (opline) {
- if (opline == zend_jit_traces[zend_jit_traces[trace_num].root].opline) {
- /* prevent endless loop */
- original_handler = 1;
- }
- zend_jit_set_ip_ex(&dasm_state, opline, original_handler);
- }
- zend_jit_trace_return(&dasm_state, original_handler, opline);
- handler = dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, name, ZEND_JIT_TRACE_NUM, SP_ADJ_JIT, SP_ADJ_NONE);
- jit_failure:
- dasm_free(&dasm_state);
- zend_arena_release(&CG(arena), checkpoint);
- return handler;
- }
- static zend_jit_trace_stop zend_jit_compile_root_trace(zend_jit_trace_rec *trace_buffer, const zend_op *opline, size_t offset)
- {
- zend_jit_trace_stop ret;
- const void *handler;
- uint8_t orig_trigger;
- zend_jit_trace_info *t = NULL;
- zend_jit_trace_exit_info exit_info[ZEND_JIT_TRACE_MAX_EXITS];
- bool do_bailout = 0;
- zend_shared_alloc_lock();
- /* Checks under lock */
- if ((ZEND_OP_TRACE_INFO(opline, offset)->trace_flags & ZEND_JIT_TRACE_JITED)) {
- ret = ZEND_JIT_TRACE_STOP_ALREADY_DONE;
- } else if (ZEND_JIT_TRACE_NUM >= JIT_G(max_root_traces)) {
- ret = ZEND_JIT_TRACE_STOP_TOO_MANY_TRACES;
- } else {
- zend_try {
- SHM_UNPROTECT();
- zend_jit_unprotect();
- t = &zend_jit_traces[ZEND_JIT_TRACE_NUM];
- t->id = ZEND_JIT_TRACE_NUM;
- t->root = ZEND_JIT_TRACE_NUM;
- t->parent = 0;
- t->link = 0;
- t->exit_count = 0;
- t->child_count = 0;
- t->stack_map_size = 0;
- t->flags = 0;
- t->polymorphism = 0;
- t->jmp_table_size = 0;
- t->op_array = trace_buffer[0].op_array;
- t->opline = trace_buffer[1].opline;
- t->exit_info = exit_info;
- t->stack_map = NULL;
- orig_trigger = JIT_G(trigger);
- JIT_G(trigger) = ZEND_JIT_ON_HOT_TRACE;
- handler = zend_jit_trace(trace_buffer, 0, 0);
- JIT_G(trigger) = orig_trigger;
- if (handler) {
- zend_jit_trace_exit_info *shared_exit_info = NULL;
- t->exit_info = NULL;
- if (t->exit_count) {
- /* reallocate exit_info into shared memory */
- shared_exit_info = (zend_jit_trace_exit_info*)zend_shared_alloc(
- sizeof(zend_jit_trace_exit_info) * t->exit_count);
- if (!shared_exit_info) {
- if (t->stack_map) {
- efree(t->stack_map);
- t->stack_map = NULL;
- }
- ret = ZEND_JIT_TRACE_STOP_NO_SHM;
- goto exit;
- }
- memcpy(shared_exit_info, exit_info,
- sizeof(zend_jit_trace_exit_info) * t->exit_count);
- t->exit_info = shared_exit_info;
- }
- if (t->stack_map_size) {
- zend_jit_trace_stack *shared_stack_map = (zend_jit_trace_stack*)zend_shared_alloc(t->stack_map_size * sizeof(zend_jit_trace_stack));
- if (!shared_stack_map) {
- ret = ZEND_JIT_TRACE_STOP_NO_SHM;
- goto exit;
- }
- memcpy(shared_stack_map, t->stack_map, t->stack_map_size * sizeof(zend_jit_trace_stack));
- efree(t->stack_map);
- t->stack_map = shared_stack_map;
- }
- t->exit_counters = ZEND_JIT_EXIT_COUNTERS;
- ZEND_JIT_EXIT_COUNTERS += t->exit_count;
- ((zend_op*)opline)->handler = handler;
- ZEND_JIT_TRACE_NUM++;
- ZEND_OP_TRACE_INFO(opline, offset)->trace_flags |= ZEND_JIT_TRACE_JITED;
- ret = ZEND_JIT_TRACE_STOP_COMPILED;
- } else if (t->exit_count >= ZEND_JIT_TRACE_MAX_EXITS ||
- ZEND_JIT_EXIT_COUNTERS + t->exit_count >= JIT_G(max_exit_counters)) {
- if (t->stack_map) {
- efree(t->stack_map);
- t->stack_map = NULL;
- }
- ret = ZEND_JIT_TRACE_STOP_TOO_MANY_EXITS;
- } else {
- if (t->stack_map) {
- efree(t->stack_map);
- t->stack_map = NULL;
- }
- ret = ZEND_JIT_TRACE_STOP_COMPILER_ERROR;
- }
- exit:;
- } zend_catch {
- do_bailout = 1;
- } zend_end_try();
- zend_jit_protect();
- SHM_PROTECT();
- }
- zend_shared_alloc_unlock();
- if (do_bailout) {
- zend_bailout();
- }
- if ((JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_EXIT_INFO) != 0
- && ret == ZEND_JIT_TRACE_STOP_COMPILED
- && t->exit_count > 0) {
- zend_jit_dump_exit_info(t);
- }
- return ret;
- }
- static void zend_jit_blacklist_root_trace(const zend_op *opline, size_t offset)
- {
- zend_shared_alloc_lock();
- if (!(ZEND_OP_TRACE_INFO(opline, offset)->trace_flags & ZEND_JIT_TRACE_BLACKLISTED)) {
- SHM_UNPROTECT();
- zend_jit_unprotect();
- ((zend_op*)opline)->handler =
- ZEND_OP_TRACE_INFO(opline, offset)->orig_handler;
- ZEND_OP_TRACE_INFO(opline, offset)->trace_flags |= ZEND_JIT_TRACE_BLACKLISTED;
- zend_jit_protect();
- SHM_PROTECT();
- }
- zend_shared_alloc_unlock();
- }
- static bool zend_jit_trace_is_bad_root(const zend_op *opline, zend_jit_trace_stop stop, size_t offset)
- {
- const zend_op **cache_opline = JIT_G(bad_root_cache_opline);
- uint8_t *cache_count = JIT_G(bad_root_cache_count);
- uint8_t *cache_stop = JIT_G(bad_root_cache_stop);
- uint32_t cache_slot = JIT_G(bad_root_slot);
- uint32_t i;
- for (i = 0; i < ZEND_JIT_TRACE_BAD_ROOT_SLOTS; i++) {
- if (cache_opline[i] == opline) {
- if (cache_count[i] >= JIT_G(blacklist_root_trace) - 1) {
- cache_opline[i] = NULL;
- return 1;
- } else {
- #if 0
- if (ZEND_OP_TRACE_INFO(opline, offset)->counter) {
- *ZEND_OP_TRACE_INFO(opline, offset)->counter =
- random() % ZEND_JIT_TRACE_COUNTER_MAX;
- }
- #endif
- cache_count[i]++;
- cache_stop[i] = stop;
- return 0;
- }
- }
- }
- i = cache_slot;
- cache_opline[i] = opline;
- cache_count[i] = 1;
- cache_stop[i] = stop;
- cache_slot = (i + 1) % ZEND_JIT_TRACE_BAD_ROOT_SLOTS;
- JIT_G(bad_root_slot) = cache_slot;
- return 0;
- }
- static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa)
- {
- zend_jit_trace_rec *p = trace_buffer;
- const zend_op_array *op_array;
- const zend_op *opline;
- uint32_t level = 1 + trace_buffer[0].level;
- int idx, len, i, v, vars_count, call_level;
- ZEND_ASSERT(p->op == ZEND_JIT_TRACE_START);
- op_array = p->op_array;
- p += ZEND_JIT_TRACE_START_REC_SIZE;
- idx = 0;
- call_level = 0;
- if (tssa && tssa->var_info) {
- if (trace_buffer->start == ZEND_JIT_TRACE_START_ENTER) {
- vars_count = op_array->last_var;
- } else {
- vars_count = op_array->last_var + op_array->T;
- }
- for (i = 0; i < vars_count; i++) {
- if (tssa->vars[i].use_chain >= 0 || tssa->vars[i].phi_use_chain) {
- fprintf(stderr, " %*c;", level, ' ');
- zend_dump_ssa_var(op_array, tssa, i, 0, i, ZEND_DUMP_RC_INFERENCE);
- fprintf(stderr, "\n");
- }
- }
- if (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) {
- zend_ssa_phi *p = tssa->blocks[1].phis;
- fprintf(stderr, "LOOP:\n");
- while (p) {
- fprintf(stderr, " ;");
- zend_dump_ssa_var(op_array, tssa, p->ssa_var, 0, p->var, ZEND_DUMP_RC_INFERENCE);
- fprintf(stderr, " = Phi(");
- zend_dump_ssa_var(op_array, tssa, p->sources[0], 0, p->var, ZEND_DUMP_RC_INFERENCE);
- fprintf(stderr, ", ");
- zend_dump_ssa_var(op_array, tssa, p->sources[1], 0, p->var, ZEND_DUMP_RC_INFERENCE);
- fprintf(stderr, ")\n");
- p = p->next;
- }
- }
- }
- while (1) {
- if (p->op == ZEND_JIT_TRACE_VM) {
- uint8_t op1_type, op2_type, op3_type;
- opline = p->opline;
- fprintf(stderr, "%04d%*c",
- (int)(opline - op_array->opcodes),
- level, ' ');
- zend_dump_op(op_array, NULL, opline, ZEND_DUMP_RC_INFERENCE, tssa, (tssa && tssa->ops) ? tssa->ops + idx : NULL);
- op1_type = p->op1_type;
- op2_type = p->op2_type;
- op3_type = p->op3_type;
- if (op1_type != IS_UNKNOWN || op2_type != IS_UNKNOWN || op3_type != IS_UNKNOWN) {
- fprintf(stderr, " ;");
- if (op1_type != IS_UNKNOWN) {
- const char *ref = (op1_type & IS_TRACE_INDIRECT) ?
- ((op1_type & IS_TRACE_REFERENCE) ? "*&" : "*") :
- ((op1_type & IS_TRACE_REFERENCE) ? "&" : "");
- if ((p+1)->op == ZEND_JIT_TRACE_OP1_TYPE) {
- p++;
- fprintf(stderr, " op1(%sobject of class %s)", ref,
- ZSTR_VAL(p->ce->name));
- } else {
- const char *type = (op1_type == 0) ? "undef" : zend_get_type_by_const(op1_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT|IS_TRACE_PACKED));
- fprintf(stderr, " op1(%s%s%s)", ref, (op1_type & IS_TRACE_PACKED) ? "packed " : "", type);
- }
- }
- if (op2_type != IS_UNKNOWN) {
- const char *ref = (op2_type & IS_TRACE_INDIRECT) ?
- ((op2_type & IS_TRACE_REFERENCE) ? "*&" : "*") :
- ((op2_type & IS_TRACE_REFERENCE) ? "&" : "");
- if ((p+1)->op == ZEND_JIT_TRACE_OP2_TYPE) {
- p++;
- fprintf(stderr, " op2(%sobject of class %s)", ref,
- ZSTR_VAL(p->ce->name));
- } else {
- const char *type = (op2_type == 0) ? "undef" : zend_get_type_by_const(op2_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT));
- fprintf(stderr, " op2(%s%s)", ref, type);
- }
- }
- if (op3_type != IS_UNKNOWN) {
- const char *ref = (op3_type & IS_TRACE_INDIRECT) ?
- ((op3_type & IS_TRACE_REFERENCE) ? "*&" : "*") :
- ((op3_type & IS_TRACE_REFERENCE) ? "&" : "");
- const char *type = (op3_type == 0) ? "undef" : zend_get_type_by_const(op3_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT));
- fprintf(stderr, " op3(%s%s)", ref, type);
- }
- }
- if ((p+1)->op == ZEND_JIT_TRACE_VAL_INFO) {
- uint8_t val_type;
- const char *type;
- if (op1_type == IS_UNKNOWN && op2_type == IS_UNKNOWN && op3_type == IS_UNKNOWN) {
- fprintf(stderr, " ;");
- }
- p++;
- val_type = p->op1_type;
- if (val_type == IS_UNDEF) {
- type = "undef";
- } else if (val_type == IS_REFERENCE) {
- type = "ref";
- } else {
- type = zend_get_type_by_const(val_type);
- }
- fprintf(stderr, " val(%s)", type);
- }
- fprintf(stderr, "\n");
- idx++;
- len = zend_jit_trace_op_len(opline);
- while (len > 1) {
- opline++;
- fprintf(stderr, "%04d%*c;",
- (int)(opline - op_array->opcodes),
- level, ' ');
- zend_dump_op(op_array, NULL, opline, ZEND_DUMP_RC_INFERENCE, tssa, (tssa && tssa->ops) ? tssa->ops + idx : NULL);
- idx++;
- len--;
- fprintf(stderr, "\n");
- }
- } else if (p->op == ZEND_JIT_TRACE_ENTER) {
- op_array = p->op_array;
- fprintf(stderr, " %*c>enter %s%s%s\n",
- level, ' ',
- op_array->scope ? ZSTR_VAL(op_array->scope->name) : "",
- op_array->scope ? "::" : "",
- op_array->function_name ?
- ZSTR_VAL(op_array->function_name) :
- ZSTR_VAL(op_array->filename));
- level++;
- if (tssa && tssa->var_info) {
- call_level++;
- v = ZEND_JIT_TRACE_GET_FIRST_SSA_VAR(p->info);
- vars_count = op_array->last_var;
- for (i = 0; i < vars_count; i++, v++) {
- if (tssa->vars[v].use_chain >= 0 || tssa->vars[v].phi_use_chain) {
- fprintf(stderr, " %*c;", level, ' ');
- zend_dump_ssa_var(op_array, tssa, v, 0, i, ZEND_DUMP_RC_INFERENCE);
- fprintf(stderr, "\n");
- }
- }
- }
- } else if (p->op == ZEND_JIT_TRACE_BACK) {
- op_array = p->op_array;
- level--;
- fprintf(stderr, " %*c<back %s%s%s\n",
- level, ' ',
- op_array->scope ? ZSTR_VAL(op_array->scope->name) : "",
- op_array->scope ? "::" : "",
- op_array->function_name ?
- ZSTR_VAL(op_array->function_name) :
- ZSTR_VAL(op_array->filename));
- if (tssa && tssa->var_info) {
- if (call_level == 0) {
- v = ZEND_JIT_TRACE_GET_FIRST_SSA_VAR(p->info);
- vars_count = op_array->last_var + op_array->T;
- for (i = 0; i < vars_count; i++, v++) {
- if (tssa->vars[v].use_chain >= 0 || tssa->vars[v].phi_use_chain) {
- fprintf(stderr, " %*c;", level, ' ');
- zend_dump_ssa_var(op_array, tssa, v, 0, i, ZEND_DUMP_RC_INFERENCE);
- fprintf(stderr, "\n");
- }
- }
- } else {
- call_level--;
- }
- }
- } else if (p->op == ZEND_JIT_TRACE_INIT_CALL) {
- if (p->func != (zend_function*)&zend_pass_function) {
- fprintf(stderr, (p->info & ZEND_JIT_TRACE_FAKE_INIT_CALL) ? " %*c>fake_init %s%s%s\n" : " %*c>init %s%s%s\n",
- level, ' ',
- (p->func && p->func->common.scope) ? ZSTR_VAL(p->func->common.scope->name) : "",
- (p->func && p->func->common.scope) ? "::" : "",
- p->func ? ZSTR_VAL(p->func->common.function_name) : "???");
- } else {
- fprintf(stderr, " %*c>skip\n",
- level, ' ');
- }
- } else if (p->op == ZEND_JIT_TRACE_DO_ICALL) {
- if (p->func != (zend_function*)&zend_pass_function) {
- fprintf(stderr, " %*c>call %s%s%s\n",
- level, ' ',
- p->func->common.scope ? ZSTR_VAL(p->func->common.scope->name) : "",
- p->func->common.scope ? "::" : "",
- ZSTR_VAL(p->func->common.function_name));
- } else {
- fprintf(stderr, " %*c>skip\n",
- level, ' ');
- }
- } else if (p->op == ZEND_JIT_TRACE_END) {
- break;
- }
- p++;
- }
- }
- static void zend_jit_dump_exit_info(zend_jit_trace_info *t)
- {
- int i, j;
- fprintf(stderr, "---- TRACE %d exit info\n", t->id);
- for (i = 0; i < t->exit_count; i++) {
- const zend_op_array *op_array = t->exit_info[i].op_array;
- uint32_t stack_size = t->exit_info[i].stack_size;
- zend_jit_trace_stack *stack = t->stack_map + t->exit_info[i].stack_offset;
- fprintf(stderr, " exit_%d:", i);
- if (t->exit_info[i].opline) {
- fprintf(stderr, " %04d/", (int)(t->exit_info[i].opline - op_array->opcodes));
- } else {
- fprintf(stderr, " ----/");
- }
- if (t->exit_info[i].stack_size) {
- fprintf(stderr, "%04d/%d", t->exit_info[i].stack_offset, t->exit_info[i].stack_size);
- } else {
- fprintf(stderr, "----/0");
- }
- if (t->exit_info[i].flags & ZEND_JIT_EXIT_TO_VM) {
- fprintf(stderr, "/VM");
- }
- if (t->exit_info[i].flags & ZEND_JIT_EXIT_RESTORE_CALL) {
- fprintf(stderr, "/CALL");
- }
- if (t->exit_info[i].flags & (ZEND_JIT_EXIT_POLYMORPHISM|ZEND_JIT_EXIT_METHOD_CALL|ZEND_JIT_EXIT_CLOSURE_CALL)) {
- fprintf(stderr, "/POLY");
- }
- if (t->exit_info[i].flags & ZEND_JIT_EXIT_FREE_OP1) {
- fprintf(stderr, "/FREE_OP1");
- }
- if (t->exit_info[i].flags & ZEND_JIT_EXIT_FREE_OP2) {
- fprintf(stderr, "/FREE_OP2");
- }
- for (j = 0; j < stack_size; j++) {
- zend_uchar type = STACK_TYPE(stack, j);
- if (type != IS_UNKNOWN) {
- fprintf(stderr, " ");
- zend_dump_var(op_array, (j < op_array->last_var) ? IS_CV : 0, j);
- fprintf(stderr, ":");
- if (type == IS_UNDEF) {
- fprintf(stderr, "undef");
- } else {
- fprintf(stderr, "%s", zend_get_type_by_const(type));
- }
- if (STACK_REG(stack, j) != ZREG_NONE) {
- if (STACK_REG(stack, j) < ZREG_NUM) {
- fprintf(stderr, "(%s)", zend_reg_name[STACK_REG(stack, j)]);
- } else if (STACK_REG(stack, j) == ZREG_THIS) {
- fprintf(stderr, "(this)");
- } else if (STACK_REG(stack, j) == ZREG_ZVAL_TRY_ADDREF) {
- fprintf(stderr, "(zval_try_addref)");
- } else {
- fprintf(stderr, "(const_%d)", STACK_REG(stack, j) - ZREG_NUM);
- }
- }
- } else if (STACK_REG(stack, j) == ZREG_ZVAL_TRY_ADDREF) {
- fprintf(stderr, " ");
- zend_dump_var(op_array, (j < op_array->last_var) ? IS_CV : 0, j);
- fprintf(stderr, ":unknown(zval_try_addref)");
- } else if (STACK_REG(stack, j) == ZREG_ZVAL_COPY_GPR0) {
- fprintf(stderr, " ");
- zend_dump_var(op_array, (j < op_array->last_var) ? IS_CV : 0, j);
- fprintf(stderr, ":unknown(zval_copy(%s))", zend_reg_name[ZREG_COPY]);
- }
- }
- fprintf(stderr, "\n");
- }
- }
- int ZEND_FASTCALL zend_jit_trace_hot_root(zend_execute_data *execute_data, const zend_op *opline)
- {
- const zend_op *orig_opline;
- zend_jit_trace_stop stop;
- int ret = 0;
- zend_op_array *op_array;
- zend_jit_op_array_trace_extension *jit_extension;
- size_t offset;
- uint32_t trace_num;
- zend_jit_trace_rec trace_buffer[ZEND_JIT_TRACE_MAX_LENGTH];
- ZEND_ASSERT(EX(func)->type == ZEND_USER_FUNCTION);
- ZEND_ASSERT(opline >= EX(func)->op_array.opcodes &&
- opline < EX(func)->op_array.opcodes + EX(func)->op_array.last);
- repeat:
- trace_num = ZEND_JIT_TRACE_NUM;
- orig_opline = opline;
- op_array = &EX(func)->op_array;
- jit_extension = (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- offset = jit_extension->offset;
- EX(opline) = opline;
- /* Lock-free check if the root trace was already JIT-ed or blacklist-ed in another process */
- if (ZEND_OP_TRACE_INFO(opline, offset)->trace_flags & (ZEND_JIT_TRACE_JITED|ZEND_JIT_TRACE_BLACKLISTED)) {
- return 0;
- }
- if (JIT_G(tracing)) {
- ++(*ZEND_OP_TRACE_INFO(opline, offset)->counter);
- return 0;
- }
- if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_START) {
- fprintf(stderr, "---- TRACE %d start (%s) %s%s%s() %s:%d\n",
- trace_num,
- zend_jit_trace_star_desc(ZEND_OP_TRACE_INFO(opline, offset)->trace_flags),
- EX(func)->op_array.scope ? ZSTR_VAL(EX(func)->op_array.scope->name) : "",
- EX(func)->op_array.scope ? "::" : "",
- EX(func)->op_array.function_name ?
- ZSTR_VAL(EX(func)->op_array.function_name) : "$main",
- ZSTR_VAL(EX(func)->op_array.filename),
- opline->lineno);
- }
- if (ZEND_JIT_TRACE_NUM >= JIT_G(max_root_traces)) {
- stop = ZEND_JIT_TRACE_STOP_TOO_MANY_TRACES;
- goto abort;
- }
- JIT_G(tracing) = 1;
- stop = zend_jit_trace_execute(execute_data, opline, trace_buffer,
- ZEND_OP_TRACE_INFO(opline, offset)->trace_flags & ZEND_JIT_TRACE_START_MASK, 0);
- JIT_G(tracing) = 0;
- if (stop & ZEND_JIT_TRACE_HALT) {
- ret = -1;
- }
- stop &= ~ZEND_JIT_TRACE_HALT;
- if (UNEXPECTED(trace_buffer[1].opline != orig_opline)) {
- orig_opline = trace_buffer[1].opline;
- op_array = (zend_op_array*)trace_buffer[0].op_array;
- jit_extension = (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- offset = jit_extension->offset;
- if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_START) {
- const zend_op_array *op_array = trace_buffer[0].op_array;
- const zend_op *opline = trace_buffer[1].opline;
- zend_jit_op_array_trace_extension *jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- size_t offset = jit_extension->offset;
- fprintf(stderr, "---- TRACE %d start (%s) %s%s%s() %s:%d\n",
- trace_num,
- zend_jit_trace_star_desc(ZEND_OP_TRACE_INFO(opline, offset)->trace_flags),
- op_array->scope ? ZSTR_VAL(op_array->scope->name) : "",
- op_array->scope ? "::" : "",
- op_array->function_name ?
- ZSTR_VAL(op_array->function_name) : "$main",
- ZSTR_VAL(op_array->filename),
- opline->lineno);
- }
- }
- if (UNEXPECTED(JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_BYTECODE)) {
- zend_jit_dump_trace(trace_buffer, NULL);
- }
- if (ZEND_JIT_TRACE_STOP_OK(stop)) {
- if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_STOP) {
- if (stop == ZEND_JIT_TRACE_STOP_LINK) {
- uint32_t idx = trace_buffer[1].last;
- uint32_t link_to = zend_jit_find_trace(trace_buffer[idx].opline->handler);
- fprintf(stderr, "---- TRACE %d stop (link to %d)\n",
- trace_num,
- link_to);
- } else {
- fprintf(stderr, "---- TRACE %d stop (%s)\n",
- trace_num,
- zend_jit_trace_stop_description[stop]);
- }
- }
- stop = zend_jit_compile_root_trace(trace_buffer, orig_opline, offset);
- if (EXPECTED(ZEND_JIT_TRACE_STOP_DONE(stop))) {
- if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_COMPILED) {
- fprintf(stderr, "---- TRACE %d %s\n",
- trace_num,
- zend_jit_trace_stop_description[stop]);
- }
- } else {
- goto abort;
- }
- } else {
- abort:
- if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_ABORT) {
- fprintf(stderr, "---- TRACE %d abort (%s)\n",
- trace_num,
- zend_jit_trace_stop_description[stop]);
- }
- if (!ZEND_JIT_TRACE_STOP_MAY_RECOVER(stop)
- || zend_jit_trace_is_bad_root(orig_opline, stop, offset)) {
- if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_BLACKLIST) {
- fprintf(stderr, "---- TRACE %d blacklisted\n",
- trace_num);
- }
- zend_jit_blacklist_root_trace(orig_opline, offset);
- }
- if (ZEND_JIT_TRACE_STOP_REPEAT(stop)) {
- execute_data = EG(current_execute_data);
- opline = EX(opline);
- goto repeat;
- }
- }
- if (JIT_G(debug) & (ZEND_JIT_DEBUG_TRACE_STOP|ZEND_JIT_DEBUG_TRACE_ABORT|ZEND_JIT_DEBUG_TRACE_COMPILED|ZEND_JIT_DEBUG_TRACE_BLACKLIST)) {
- fprintf(stderr, "\n");
- }
- return ret;
- }
- static void zend_jit_blacklist_trace_exit(uint32_t trace_num, uint32_t exit_num)
- {
- const void *handler;
- bool do_bailout = 0;
- zend_shared_alloc_lock();
- if (!(zend_jit_traces[trace_num].exit_info[exit_num].flags & (ZEND_JIT_EXIT_JITED|ZEND_JIT_EXIT_BLACKLISTED))) {
- SHM_UNPROTECT();
- zend_jit_unprotect();
- zend_try {
- handler = zend_jit_trace_exit_to_vm(trace_num, exit_num);
- if (handler) {
- zend_jit_link_side_trace(
- zend_jit_traces[trace_num].code_start,
- zend_jit_traces[trace_num].code_size,
- zend_jit_traces[trace_num].jmp_table_size,
- exit_num,
- handler);
- }
- zend_jit_traces[trace_num].exit_info[exit_num].flags |= ZEND_JIT_EXIT_BLACKLISTED;
- } zend_catch {
- do_bailout = 1;
- } zend_end_try();
- zend_jit_protect();
- SHM_PROTECT();
- }
- zend_shared_alloc_unlock();
- if (do_bailout) {
- zend_bailout();
- }
- }
- static bool zend_jit_trace_exit_is_bad(uint32_t trace_num, uint32_t exit_num)
- {
- uint8_t *counter = JIT_G(exit_counters) +
- zend_jit_traces[trace_num].exit_counters + exit_num;
- if (*counter + 1 >= JIT_G(hot_side_exit) + JIT_G(blacklist_side_trace)) {
- return 1;
- }
- (*counter)++;
- return 0;
- }
- static bool zend_jit_trace_exit_is_hot(uint32_t trace_num, uint32_t exit_num)
- {
- uint8_t *counter = JIT_G(exit_counters) +
- zend_jit_traces[trace_num].exit_counters + exit_num;
- if (*counter + 1 >= JIT_G(hot_side_exit)) {
- return 1;
- }
- (*counter)++;
- return 0;
- }
- static zend_jit_trace_stop zend_jit_compile_side_trace(zend_jit_trace_rec *trace_buffer, uint32_t parent_num, uint32_t exit_num, uint32_t polymorphism)
- {
- zend_jit_trace_stop ret;
- const void *handler;
- uint8_t orig_trigger;
- zend_jit_trace_info *t;
- zend_jit_trace_exit_info exit_info[ZEND_JIT_TRACE_MAX_EXITS];
- bool do_bailout = 0;
- zend_shared_alloc_lock();
- /* Checks under lock */
- if (zend_jit_traces[parent_num].exit_info[exit_num].flags & (ZEND_JIT_EXIT_JITED|ZEND_JIT_EXIT_BLACKLISTED)) {
- ret = ZEND_JIT_TRACE_STOP_ALREADY_DONE;
- } else if (ZEND_JIT_TRACE_NUM >= JIT_G(max_root_traces)) {
- ret = ZEND_JIT_TRACE_STOP_TOO_MANY_TRACES;
- } else if (zend_jit_traces[zend_jit_traces[parent_num].root].child_count >= JIT_G(max_side_traces)) {
- ret = ZEND_JIT_TRACE_STOP_TOO_MANY_CHILDREN;
- } else {
- SHM_UNPROTECT();
- zend_jit_unprotect();
- zend_try {
- t = &zend_jit_traces[ZEND_JIT_TRACE_NUM];
- t->id = ZEND_JIT_TRACE_NUM;
- t->root = zend_jit_traces[parent_num].root;
- t->parent = parent_num;
- t->link = 0;
- t->exit_count = 0;
- t->child_count = 0;
- t->stack_map_size = 0;
- t->flags = 0;
- t->polymorphism = polymorphism;
- t->jmp_table_size = 0;
- t->opline = NULL;
- t->exit_info = exit_info;
- t->stack_map = NULL;
- orig_trigger = JIT_G(trigger);
- JIT_G(trigger) = ZEND_JIT_ON_HOT_TRACE;
- handler = zend_jit_trace(trace_buffer, parent_num, exit_num);
- JIT_G(trigger) = orig_trigger;
- if (handler) {
- zend_jit_trace_exit_info *shared_exit_info = NULL;
- t->exit_info = NULL;
- if (t->exit_count) {
- /* reallocate exit_info into shared memory */
- shared_exit_info = (zend_jit_trace_exit_info*)zend_shared_alloc(
- sizeof(zend_jit_trace_exit_info) * t->exit_count);
- if (!shared_exit_info) {
- if (t->stack_map) {
- efree(t->stack_map);
- t->stack_map = NULL;
- }
- ret = ZEND_JIT_TRACE_STOP_NO_SHM;
- goto exit;
- }
- memcpy(shared_exit_info, exit_info,
- sizeof(zend_jit_trace_exit_info) * t->exit_count);
- t->exit_info = shared_exit_info;
- }
- if (t->stack_map_size) {
- zend_jit_trace_stack *shared_stack_map = (zend_jit_trace_stack*)zend_shared_alloc(t->stack_map_size * sizeof(zend_jit_trace_stack));
- if (!shared_stack_map) {
- efree(t->stack_map);
- ret = ZEND_JIT_TRACE_STOP_NO_SHM;
- goto exit;
- }
- memcpy(shared_stack_map, t->stack_map, t->stack_map_size * sizeof(zend_jit_trace_stack));
- efree(t->stack_map);
- t->stack_map = shared_stack_map;
- }
- zend_jit_link_side_trace(
- zend_jit_traces[parent_num].code_start,
- zend_jit_traces[parent_num].code_size,
- zend_jit_traces[parent_num].jmp_table_size,
- exit_num,
- handler);
- t->exit_counters = ZEND_JIT_EXIT_COUNTERS;
- ZEND_JIT_EXIT_COUNTERS += t->exit_count;
- zend_jit_traces[zend_jit_traces[parent_num].root].child_count++;
- ZEND_JIT_TRACE_NUM++;
- zend_jit_traces[parent_num].exit_info[exit_num].flags |= ZEND_JIT_EXIT_JITED;
- ret = ZEND_JIT_TRACE_STOP_COMPILED;
- } else if (t->exit_count >= ZEND_JIT_TRACE_MAX_EXITS ||
- ZEND_JIT_EXIT_COUNTERS + t->exit_count >= JIT_G(max_exit_counters)) {
- if (t->stack_map) {
- efree(t->stack_map);
- t->stack_map = NULL;
- }
- ret = ZEND_JIT_TRACE_STOP_TOO_MANY_EXITS;
- } else {
- if (t->stack_map) {
- efree(t->stack_map);
- t->stack_map = NULL;
- }
- ret = ZEND_JIT_TRACE_STOP_COMPILER_ERROR;
- }
- exit:;
- } zend_catch {
- do_bailout = 1;
- } zend_end_try();
- zend_jit_protect();
- SHM_PROTECT();
- }
- zend_shared_alloc_unlock();
- if (do_bailout) {
- zend_bailout();
- }
- if ((JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_EXIT_INFO) != 0
- && ret == ZEND_JIT_TRACE_STOP_COMPILED
- && t->exit_count > 0) {
- zend_jit_dump_exit_info(t);
- }
- return ret;
- }
- int ZEND_FASTCALL zend_jit_trace_hot_side(zend_execute_data *execute_data, uint32_t parent_num, uint32_t exit_num)
- {
- zend_jit_trace_stop stop;
- int ret = 0;
- uint32_t trace_num;
- zend_jit_trace_rec trace_buffer[ZEND_JIT_TRACE_MAX_LENGTH];
- uint32_t is_megamorphic = 0;
- uint32_t polymorphism = 0;
- trace_num = ZEND_JIT_TRACE_NUM;
- /* Lock-free check if the side trace was already JIT-ed or blacklist-ed in another process */
- if (zend_jit_traces[parent_num].exit_info[exit_num].flags & (ZEND_JIT_EXIT_JITED|ZEND_JIT_EXIT_BLACKLISTED)) {
- return 0;
- }
- if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_START) {
- fprintf(stderr, "---- TRACE %d start (side trace %d/%d) %s%s%s() %s:%d\n",
- trace_num, parent_num, exit_num,
- EX(func)->op_array.scope ? ZSTR_VAL(EX(func)->op_array.scope->name) : "",
- EX(func)->op_array.scope ? "::" : "",
- EX(func)->op_array.function_name ?
- ZSTR_VAL(EX(func)->op_array.function_name) : "$main",
- ZSTR_VAL(EX(func)->op_array.filename),
- EX(opline)->lineno);
- }
- if (ZEND_JIT_TRACE_NUM >= JIT_G(max_root_traces)) {
- stop = ZEND_JIT_TRACE_STOP_TOO_MANY_TRACES;
- goto abort;
- }
- if (zend_jit_traces[zend_jit_traces[parent_num].root].child_count >= JIT_G(max_side_traces)) {
- stop = ZEND_JIT_TRACE_STOP_TOO_MANY_CHILDREN;
- goto abort;
- }
- if (JIT_G(max_polymorphic_calls) > 0) {
- if ((zend_jit_traces[parent_num].exit_info[exit_num].flags & (ZEND_JIT_EXIT_METHOD_CALL|ZEND_JIT_EXIT_CLOSURE_CALL))
- || ((zend_jit_traces[parent_num].exit_info[exit_num].flags & ZEND_JIT_EXIT_POLYMORPHISM)
- && EX(call))) {
- if (zend_jit_traces[parent_num].polymorphism >= JIT_G(max_polymorphic_calls) - 1) {
- is_megamorphic = zend_jit_traces[parent_num].exit_info[exit_num].flags &
- (ZEND_JIT_EXIT_METHOD_CALL | ZEND_JIT_EXIT_CLOSURE_CALL | ZEND_JIT_EXIT_POLYMORPHISM);
- } else if (!zend_jit_traces[parent_num].polymorphism) {
- polymorphism = 1;
- } else if (exit_num == 0) {
- polymorphism = zend_jit_traces[parent_num].polymorphism + 1;
- }
- }
- }
- JIT_G(tracing) = 1;
- stop = zend_jit_trace_execute(execute_data, EX(opline), trace_buffer, ZEND_JIT_TRACE_START_SIDE, is_megamorphic);
- JIT_G(tracing) = 0;
- if (stop & ZEND_JIT_TRACE_HALT) {
- ret = -1;
- }
- stop &= ~ZEND_JIT_TRACE_HALT;
- if (UNEXPECTED(trace_buffer->start != ZEND_JIT_TRACE_START_SIDE)) {
- if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_START) {
- const zend_op_array *op_array = trace_buffer[0].op_array;
- const zend_op *opline = trace_buffer[1].opline;
- zend_jit_op_array_trace_extension *jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- size_t offset = jit_extension->offset;
- fprintf(stderr, "---- TRACE %d start (%s) %s%s%s() %s:%d\n",
- trace_num,
- zend_jit_trace_star_desc(ZEND_OP_TRACE_INFO(opline, offset)->trace_flags),
- op_array->scope ? ZSTR_VAL(op_array->scope->name) : "",
- op_array->scope ? "::" : "",
- op_array->function_name ?
- ZSTR_VAL(op_array->function_name) : "$main",
- ZSTR_VAL(op_array->filename),
- opline->lineno);
- }
- }
- if (UNEXPECTED(JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_BYTECODE)) {
- zend_jit_dump_trace(trace_buffer, NULL);
- }
- if (ZEND_JIT_TRACE_STOP_OK(stop)) {
- if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_STOP) {
- if (stop == ZEND_JIT_TRACE_STOP_LINK) {
- uint32_t idx = trace_buffer[1].last;
- uint32_t link_to = zend_jit_find_trace(trace_buffer[idx].opline->handler);;
- fprintf(stderr, "---- TRACE %d stop (link to %d)\n",
- trace_num,
- link_to);
- } else {
- fprintf(stderr, "---- TRACE %d stop (%s)\n",
- trace_num,
- zend_jit_trace_stop_description[stop]);
- }
- }
- if (EXPECTED(trace_buffer->start == ZEND_JIT_TRACE_START_SIDE)) {
- stop = zend_jit_compile_side_trace(trace_buffer, parent_num, exit_num, polymorphism);
- } else {
- const zend_op_array *op_array = trace_buffer[0].op_array;
- zend_jit_op_array_trace_extension *jit_extension =
- (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- const zend_op *opline = trace_buffer[1].opline;
- stop = zend_jit_compile_root_trace(trace_buffer, opline, jit_extension->offset);
- }
- if (EXPECTED(ZEND_JIT_TRACE_STOP_DONE(stop))) {
- if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_COMPILED) {
- fprintf(stderr, "---- TRACE %d %s\n",
- trace_num,
- zend_jit_trace_stop_description[stop]);
- }
- } else {
- goto abort;
- }
- } else {
- abort:
- if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_ABORT) {
- fprintf(stderr, "---- TRACE %d abort (%s)\n",
- trace_num,
- zend_jit_trace_stop_description[stop]);
- }
- if (!ZEND_JIT_TRACE_STOP_MAY_RECOVER(stop)
- || zend_jit_trace_exit_is_bad(parent_num, exit_num)) {
- zend_jit_blacklist_trace_exit(parent_num, exit_num);
- if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_BLACKLIST) {
- fprintf(stderr, "---- EXIT %d/%d blacklisted\n",
- parent_num, exit_num);
- }
- }
- if (ZEND_JIT_TRACE_STOP_REPEAT(stop)) {
- execute_data = EG(current_execute_data);
- return zend_jit_trace_hot_root(execute_data, EX(opline));
- }
- }
- if (JIT_G(debug) & (ZEND_JIT_DEBUG_TRACE_STOP|ZEND_JIT_DEBUG_TRACE_ABORT|ZEND_JIT_DEBUG_TRACE_COMPILED|ZEND_JIT_DEBUG_TRACE_BLACKLIST)) {
- fprintf(stderr, "\n");
- }
- return ret;
- }
- int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf *regs)
- {
- uint32_t trace_num = EG(jit_trace_num);
- zend_execute_data *execute_data = EG(current_execute_data);
- const zend_op *orig_opline = EX(opline);
- const zend_op *opline;
- zend_jit_trace_info *t = &zend_jit_traces[trace_num];
- int repeat_last_opline = 0;
- /* Deoptimizatoion of VM stack state */
- uint32_t i;
- uint32_t stack_size = t->exit_info[exit_num].stack_size;
- zend_jit_trace_stack *stack = t->stack_map + t->exit_info[exit_num].stack_offset;
- if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_RESTORE_CALL) {
- zend_execute_data *call = (zend_execute_data *)regs->gpr[ZREG_RX];
- call->prev_execute_data = EX(call);
- EX(call) = call;
- }
- for (i = 0; i < stack_size; i++) {
- if (STACK_REG(stack, i) != ZREG_NONE) {
- if (STACK_TYPE(stack, i) == IS_LONG) {
- zend_long val;
- if (STACK_REG(stack, i) < ZREG_NUM) {
- val = regs->gpr[STACK_REG(stack, i)];
- } else if (STACK_REG(stack, i) == ZREG_LONG_MIN) {
- val = ZEND_LONG_MIN;
- } else if (STACK_REG(stack, i) == ZREG_LONG_MAX) {
- val = ZEND_LONG_MAX;
- } else {
- ZEND_UNREACHABLE();
- }
- ZVAL_LONG(EX_VAR_NUM(i), val);
- } else if (STACK_TYPE(stack, i) == IS_DOUBLE) {
- double val;
- if (STACK_REG(stack, i) < ZREG_NUM) {
- val = regs->fpr[STACK_REG(stack, i) - ZREG_FIRST_FPR];
- } else if (STACK_REG(stack, i) == ZREG_LONG_MIN_MINUS_1) {
- val = (double)ZEND_LONG_MIN - 1.0;
- } else if (STACK_REG(stack, i) == ZREG_LONG_MAX_PLUS_1) {
- val = (double)ZEND_LONG_MAX + 1.0;
- } else {
- ZEND_UNREACHABLE();
- }
- ZVAL_DOUBLE(EX_VAR_NUM(i), val);
- } else if (STACK_REG(stack, i) == ZREG_THIS) {
- zend_object *obj = Z_OBJ(EX(This));
- GC_ADDREF(obj);
- ZVAL_OBJ(EX_VAR_NUM(i), obj);
- } else if (STACK_REG(stack, i) == ZREG_NULL) {
- ZVAL_NULL(EX_VAR_NUM(i));
- } else if (STACK_REG(stack, i) == ZREG_ZVAL_TRY_ADDREF) {
- Z_TRY_ADDREF_P(EX_VAR_NUM(i));
- } else if (STACK_REG(stack, i) == ZREG_ZVAL_COPY_GPR0) {
- zval *val = (zval*)regs->gpr[ZREG_COPY];
- if (UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)) {
- /* Undefined array index or property */
- repeat_last_opline = 1;
- } else {
- ZVAL_COPY(EX_VAR_NUM(i), val);
- }
- } else {
- ZEND_UNREACHABLE();
- }
- }
- }
- if (repeat_last_opline) {
- EX(opline) = t->exit_info[exit_num].opline - 1;
- if ((EX(opline)->op1_type & (IS_VAR|IS_TMP_VAR))
- && !(t->exit_info[exit_num].flags & ZEND_JIT_EXIT_FREE_OP1)
- && EX(opline)->opcode != ZEND_FETCH_LIST_R) {
- Z_TRY_ADDREF_P(EX_VAR(EX(opline)->op1.var));
- }
- return 1;
- }
- opline = t->exit_info[exit_num].opline;
- if (opline) {
- if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_FREE_OP2) {
- ZEND_ASSERT((opline-1)->opcode == ZEND_FETCH_DIM_R
- || (opline-1)->opcode == ZEND_FETCH_DIM_IS
- || (opline-1)->opcode == ZEND_FETCH_LIST_R
- || (opline-1)->opcode == ZEND_FETCH_DIM_FUNC_ARG);
- EX(opline) = opline-1;
- zval_ptr_dtor_nogc(EX_VAR((opline-1)->op2.var));
- }
- if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_FREE_OP1) {
- ZEND_ASSERT((opline-1)->opcode == ZEND_FETCH_DIM_R
- || (opline-1)->opcode == ZEND_FETCH_DIM_IS
- || (opline-1)->opcode == ZEND_FETCH_DIM_FUNC_ARG
- || (opline-1)->opcode == ZEND_FETCH_OBJ_R
- || (opline-1)->opcode == ZEND_FETCH_OBJ_IS
- || (opline-1)->opcode == ZEND_FETCH_OBJ_FUNC_ARG);
- EX(opline) = opline-1;
- zval_ptr_dtor_nogc(EX_VAR((opline-1)->op1.var));
- }
- if (t->exit_info[exit_num].flags & (ZEND_JIT_EXIT_FREE_OP1|ZEND_JIT_EXIT_FREE_OP2)) {
- if (EG(exception)) {
- return 1;
- }
- }
- if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_METHOD_CALL) {
- zend_function *func = (zend_function*)regs->gpr[ZREG_COPY];
- if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
- zend_string_release_ex(func->common.function_name, 0);
- zend_free_trampoline(func);
- EX(opline) = opline;
- return 1;
- }
- }
- /* Set VM opline to continue interpretation */
- EX(opline) = opline;
- }
- if (EG(vm_interrupt) || JIT_G(tracing)) {
- return 1;
- /* Lock-free check if the side trace was already JIT-ed or blacklist-ed in another process */
- } else if (t->exit_info[exit_num].flags & (ZEND_JIT_EXIT_JITED|ZEND_JIT_EXIT_BLACKLISTED)) {
- return 0;
- }
- ZEND_ASSERT(EX(func)->type == ZEND_USER_FUNCTION);
- ZEND_ASSERT(EX(opline) >= EX(func)->op_array.opcodes &&
- EX(opline) < EX(func)->op_array.opcodes + EX(func)->op_array.last);
- if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_EXIT) {
- fprintf(stderr, " TRACE %d exit %d %s%s%s() %s:%d\n",
- trace_num,
- exit_num,
- EX(func)->op_array.scope ? ZSTR_VAL(EX(func)->op_array.scope->name) : "",
- EX(func)->op_array.scope ? "::" : "",
- EX(func)->op_array.function_name ?
- ZSTR_VAL(EX(func)->op_array.function_name) : "$main",
- ZSTR_VAL(EX(func)->op_array.filename),
- EX(opline)->lineno);
- }
- if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_INVALIDATE) {
- zend_jit_op_array_trace_extension *jit_extension;
- uint32_t num = trace_num;
- while (t->root != num) {
- num = t->root;
- t = &zend_jit_traces[num];
- }
- SHM_UNPROTECT();
- zend_jit_unprotect();
- jit_extension = (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(t->op_array);
- if (ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags & ZEND_JIT_TRACE_START_LOOP) {
- ((zend_op*)(t->opline))->handler = (const void*)zend_jit_loop_trace_counter_handler;
- } else if (ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags & ZEND_JIT_TRACE_START_ENTER) {
- ((zend_op*)(t->opline))->handler = (const void*)zend_jit_func_trace_counter_handler;
- } else if (ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags & ZEND_JIT_TRACE_START_RETURN) {
- ((zend_op*)(t->opline))->handler = (const void*)zend_jit_ret_trace_counter_handler;
- }
- ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags &=
- ZEND_JIT_TRACE_START_LOOP|ZEND_JIT_TRACE_START_ENTER|ZEND_JIT_TRACE_START_RETURN;
- zend_jit_protect();
- SHM_PROTECT();
- return 0;
- }
- if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_TO_VM) {
- if (zend_jit_trace_exit_is_bad(trace_num, exit_num)) {
- zend_jit_blacklist_trace_exit(trace_num, exit_num);
- if (JIT_G(debug) & ZEND_JIT_DEBUG_TRACE_BLACKLIST) {
- fprintf(stderr, "---- EXIT %d/%d blacklisted\n",
- trace_num, exit_num);
- }
- return 0;
- }
- } else if (JIT_G(hot_side_exit) && zend_jit_trace_exit_is_hot(trace_num, exit_num)) {
- return zend_jit_trace_hot_side(execute_data, trace_num, exit_num);
- }
- /* Return 1 to call original handler instead of the same JIT-ed trace */
- return (orig_opline == t->opline && EX(opline) == orig_opline);
- }
- static zend_always_inline uint8_t zend_jit_trace_supported(const zend_op *opline)
- {
- switch (opline->opcode) {
- case ZEND_CATCH:
- case ZEND_FAST_CALL:
- case ZEND_FAST_RET:
- return ZEND_JIT_TRACE_UNSUPPORTED;
- default:
- return ZEND_JIT_TRACE_SUPPORTED;
- }
- }
- static int zend_jit_restart_hot_trace_counters(zend_op_array *op_array)
- {
- zend_jit_op_array_trace_extension *jit_extension;
- uint32_t i;
- jit_extension = (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array);
- for (i = 0; i < op_array->last; i++) {
- jit_extension->trace_info[i].trace_flags &=
- ZEND_JIT_TRACE_START_LOOP | ZEND_JIT_TRACE_START_ENTER | ZEND_JIT_TRACE_UNSUPPORTED;
- if (jit_extension->trace_info[i].trace_flags == ZEND_JIT_TRACE_START_LOOP) {
- op_array->opcodes[i].handler = (const void*)zend_jit_loop_trace_counter_handler;
- } else if (jit_extension->trace_info[i].trace_flags == ZEND_JIT_TRACE_START_ENTER) {
- op_array->opcodes[i].handler = (const void*)zend_jit_func_trace_counter_handler;
- } else {
- op_array->opcodes[i].handler = jit_extension->trace_info[i].orig_handler;
- }
- }
- return SUCCESS;
- }
- static int zend_jit_setup_hot_trace_counters(zend_op_array *op_array)
- {
- zend_op *opline;
- zend_jit_op_array_trace_extension *jit_extension;
- uint32_t i;
- ZEND_ASSERT(sizeof(zend_op_trace_info) == sizeof(zend_op));
- jit_extension = (zend_jit_op_array_trace_extension*)zend_shared_alloc(sizeof(zend_jit_op_array_trace_extension) + (op_array->last - 1) * sizeof(zend_op_trace_info));
- if (!jit_extension) {
- return FAILURE;
- }
- memset(&jit_extension->func_info, 0, sizeof(zend_func_info));
- jit_extension->func_info.flags = ZEND_FUNC_JIT_ON_HOT_TRACE;
- jit_extension->op_array = op_array;
- jit_extension->offset = (char*)jit_extension->trace_info - (char*)op_array->opcodes;
- for (i = 0; i < op_array->last; i++) {
- jit_extension->trace_info[i].orig_handler = op_array->opcodes[i].handler;
- jit_extension->trace_info[i].call_handler = zend_get_opcode_handler_func(&op_array->opcodes[i]);
- jit_extension->trace_info[i].counter = NULL;
- jit_extension->trace_info[i].trace_flags =
- zend_jit_trace_supported(&op_array->opcodes[i]);
- }
- ZEND_SET_FUNC_INFO(op_array, (void*)jit_extension);
- if (JIT_G(hot_loop)) {
- zend_cfg cfg;
- ZEND_ASSERT(zend_jit_loop_trace_counter_handler != NULL);
- if (zend_jit_build_cfg(op_array, &cfg) != SUCCESS) {
- return FAILURE;
- }
- for (i = 0; i < cfg.blocks_count; i++) {
- if (cfg.blocks[i].flags & ZEND_BB_REACHABLE) {
- if (cfg.blocks[i].flags & ZEND_BB_LOOP_HEADER) {
- /* loop header */
- opline = op_array->opcodes + cfg.blocks[i].start;
- if (!(ZEND_OP_TRACE_INFO(opline, jit_extension->offset)->trace_flags & ZEND_JIT_TRACE_UNSUPPORTED)) {
- opline->handler = (const void*)zend_jit_loop_trace_counter_handler;
- if (!ZEND_OP_TRACE_INFO(opline, jit_extension->offset)->counter) {
- ZEND_OP_TRACE_INFO(opline, jit_extension->offset)->counter =
- &zend_jit_hot_counters[ZEND_JIT_COUNTER_NUM];
- ZEND_JIT_COUNTER_NUM = (ZEND_JIT_COUNTER_NUM + 1) % ZEND_HOT_COUNTERS_COUNT;
- }
- ZEND_OP_TRACE_INFO(opline, jit_extension->offset)->trace_flags |=
- ZEND_JIT_TRACE_START_LOOP;
- }
- }
- }
- }
- }
- if (JIT_G(hot_func)) {
- ZEND_ASSERT(zend_jit_func_trace_counter_handler != NULL);
- opline = op_array->opcodes;
- if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
- while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) {
- opline++;
- }
- }
- if (!ZEND_OP_TRACE_INFO(opline, jit_extension->offset)->trace_flags) {
- /* function entry */
- opline->handler = (const void*)zend_jit_func_trace_counter_handler;
- ZEND_OP_TRACE_INFO(opline, jit_extension->offset)->counter =
- &zend_jit_hot_counters[ZEND_JIT_COUNTER_NUM];
- ZEND_JIT_COUNTER_NUM = (ZEND_JIT_COUNTER_NUM + 1) % ZEND_HOT_COUNTERS_COUNT;
- ZEND_OP_TRACE_INFO(opline, jit_extension->offset)->trace_flags |=
- ZEND_JIT_TRACE_START_ENTER;
- }
- }
- zend_shared_alloc_register_xlat_entry(op_array->opcodes, jit_extension);
- return SUCCESS;
- }
- static void zend_jit_trace_init_caches(void)
- {
- memset(ZEND_VOIDP(JIT_G(bad_root_cache_opline)), 0, sizeof(JIT_G(bad_root_cache_opline)));
- memset(JIT_G(bad_root_cache_count), 0, sizeof(JIT_G(bad_root_cache_count)));
- memset(JIT_G(bad_root_cache_stop), 0, sizeof(JIT_G(bad_root_cache_count)));
- JIT_G(bad_root_slot) = 0;
- if (JIT_G(exit_counters)) {
- memset(JIT_G(exit_counters), 0, JIT_G(max_exit_counters));
- }
- }
- static void zend_jit_trace_reset_caches(void)
- {
- JIT_G(tracing) = 0;
- #ifdef ZTS
- if (!JIT_G(exit_counters)) {
- JIT_G(exit_counters) = calloc(JIT_G(max_exit_counters), 1);
- }
- #endif
- }
- static void zend_jit_trace_restart(void)
- {
- ZEND_JIT_TRACE_NUM = 1;
- ZEND_JIT_COUNTER_NUM = 0;
- ZEND_JIT_EXIT_NUM = 0;
- ZEND_JIT_EXIT_COUNTERS = 0;
- zend_jit_trace_init_caches();
- }
|