1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209 |
- /*
- +----------------------------------------------------------------------+
- | 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> |
- +----------------------------------------------------------------------+
- */
- #include "main/php.h"
- #include "main/SAPI.h"
- #include "php_version.h"
- #include <ZendAccelerator.h>
- #include "zend_shared_alloc.h"
- #include "Zend/zend_execute.h"
- #include "Zend/zend_vm.h"
- #include "Zend/zend_exceptions.h"
- #include "Zend/zend_constants.h"
- #include "Zend/zend_closures.h"
- #include "Zend/zend_ini.h"
- #include "Zend/zend_observer.h"
- #include "zend_smart_str.h"
- #include "jit/zend_jit.h"
- #ifdef HAVE_JIT
- #include "Optimizer/zend_func_info.h"
- #include "Optimizer/zend_ssa.h"
- #include "Optimizer/zend_inference.h"
- #include "Optimizer/zend_call_graph.h"
- #include "Optimizer/zend_dump.h"
- #if ZEND_JIT_TARGET_X86
- # include "jit/zend_jit_x86.h"
- #elif ZEND_JIT_TARGET_ARM64
- # include "jit/zend_jit_arm64.h"
- #endif
- #include "jit/zend_jit_internal.h"
- #ifdef ZTS
- int jit_globals_id;
- #else
- zend_jit_globals jit_globals;
- #endif
- //#define CONTEXT_THREADED_JIT
- #define ZEND_JIT_USE_RC_INFERENCE
- #ifdef ZEND_JIT_USE_RC_INFERENCE
- # define ZEND_SSA_RC_INFERENCE_FLAG ZEND_SSA_RC_INFERENCE
- # define RC_MAY_BE_1(info) (((info) & (MAY_BE_RC1|MAY_BE_REF)) != 0)
- # define RC_MAY_BE_N(info) (((info) & (MAY_BE_RCN|MAY_BE_REF)) != 0)
- #else
- # define ZEND_SSA_RC_INFERENCE_FLAG 0
- # define RC_MAY_BE_1(info) 1
- # define RC_MAY_BE_N(info) 1
- #endif
- #define JIT_PREFIX "JIT$"
- #define JIT_STUB_PREFIX "JIT$$"
- #define TRACE_PREFIX "TRACE-"
- #define DASM_M_GROW(ctx, t, p, sz, need) \
- do { \
- size_t _sz = (sz), _need = (need); \
- if (_sz < _need) { \
- if (_sz < 16) _sz = 16; \
- while (_sz < _need) _sz += _sz; \
- (p) = (t *)erealloc((p), _sz); \
- (sz) = _sz; \
- } \
- } while(0)
- #define DASM_M_FREE(ctx, p, sz) efree(p)
- #if ZEND_DEBUG
- # define DASM_CHECKS 1
- #endif
- #include "dynasm/dasm_proto.h"
- typedef struct _zend_jit_stub {
- const char *name;
- int (*stub)(dasm_State **Dst);
- uint32_t offset;
- uint32_t adjustment;
- } zend_jit_stub;
- #define JIT_STUB(name, offset, adjustment) \
- {JIT_STUB_PREFIX #name, zend_jit_ ## name ## _stub, offset, adjustment}
- zend_ulong zend_jit_profile_counter = 0;
- int zend_jit_profile_counter_rid = -1;
- int16_t zend_jit_hot_counters[ZEND_HOT_COUNTERS_COUNT];
- const zend_op *zend_jit_halt_op = NULL;
- static int zend_jit_vm_kind = 0;
- static void *dasm_buf = NULL;
- static void *dasm_end = NULL;
- static void **dasm_ptr = NULL;
- static size_t dasm_size = 0;
- static zend_long jit_bisect_pos = 0;
- static const void *zend_jit_runtime_jit_handler = NULL;
- static const void *zend_jit_profile_jit_handler = NULL;
- static const void *zend_jit_func_hot_counter_handler = NULL;
- static const void *zend_jit_loop_hot_counter_handler = NULL;
- static const void *zend_jit_func_trace_counter_handler = NULL;
- static const void *zend_jit_ret_trace_counter_handler = NULL;
- static const void *zend_jit_loop_trace_counter_handler = NULL;
- static int ZEND_FASTCALL zend_runtime_jit(void);
- static int zend_jit_trace_op_len(const zend_op *opline);
- static int zend_jit_trace_may_exit(const zend_op_array *op_array, const zend_op *opline);
- static uint32_t zend_jit_trace_get_exit_point(const zend_op *to_opline, uint32_t flags);
- static const void *zend_jit_trace_get_exit_addr(uint32_t n);
- static void zend_jit_trace_add_code(const void *start, uint32_t size);
- static bool zend_jit_needs_arg_dtor(const zend_function *func, uint32_t arg_num, zend_call_info *call_info);
- #if ZEND_JIT_TARGET_ARM64
- static zend_jit_trace_info *zend_jit_get_current_trace_info(void);
- static uint32_t zend_jit_trace_find_exit_point(const void* addr);
- #endif
- static int zend_jit_assign_to_variable(dasm_State **Dst,
- const zend_op *opline,
- zend_jit_addr var_use_addr,
- zend_jit_addr var_addr,
- uint32_t var_info,
- uint32_t var_def_info,
- zend_uchar val_type,
- zend_jit_addr val_addr,
- uint32_t val_info,
- zend_jit_addr res_addr,
- bool check_exception);
- static bool dominates(const zend_basic_block *blocks, int a, int b) {
- while (blocks[b].level > blocks[a].level) {
- b = blocks[b].idom;
- }
- return a == b;
- }
- static bool zend_ssa_is_last_use(const zend_op_array *op_array, const zend_ssa *ssa, int var, int use)
- {
- int next_use;
- if (ssa->vars[var].phi_use_chain) {
- zend_ssa_phi *phi = ssa->vars[var].phi_use_chain;
- do {
- if (!ssa->vars[phi->ssa_var].no_val) {
- return 0;
- }
- phi = zend_ssa_next_use_phi(ssa, var, phi);
- } while (phi);
- }
- if (ssa->cfg.blocks[ssa->cfg.map[use]].loop_header > 0
- || (ssa->cfg.blocks[ssa->cfg.map[use]].flags & ZEND_BB_LOOP_HEADER)) {
- int b = ssa->cfg.map[use];
- int prev_use = ssa->vars[var].use_chain;
- while (prev_use >= 0 && prev_use != use) {
- if (b != ssa->cfg.map[prev_use]
- && dominates(ssa->cfg.blocks, b, ssa->cfg.map[prev_use])
- && !zend_ssa_is_no_val_use(op_array->opcodes + prev_use, ssa->ops + prev_use, var)) {
- return 0;
- }
- prev_use = zend_ssa_next_use(ssa->ops, var, prev_use);
- }
- }
- next_use = zend_ssa_next_use(ssa->ops, var, use);
- if (next_use < 0) {
- return 1;
- } else if (zend_ssa_is_no_val_use(op_array->opcodes + next_use, ssa->ops + next_use, var)) {
- return 1;
- }
- return 0;
- }
- static bool zend_ival_is_last_use(const zend_lifetime_interval *ival, int use)
- {
- if (ival->flags & ZREG_LAST_USE) {
- const zend_life_range *range = &ival->range;
- while (range->next) {
- range = range->next;
- }
- return range->end == use;
- }
- return 0;
- }
- static bool zend_is_commutative(zend_uchar opcode)
- {
- return
- opcode == ZEND_ADD ||
- opcode == ZEND_MUL ||
- opcode == ZEND_BW_OR ||
- opcode == ZEND_BW_AND ||
- opcode == ZEND_BW_XOR;
- }
- static int zend_jit_is_constant_cmp_long_long(const zend_op *opline,
- zend_ssa_range *op1_range,
- zend_jit_addr op1_addr,
- zend_ssa_range *op2_range,
- zend_jit_addr op2_addr,
- bool *result)
- {
- zend_long op1_min;
- zend_long op1_max;
- zend_long op2_min;
- zend_long op2_max;
- if (op1_range) {
- op1_min = op1_range->min;
- op1_max = op1_range->max;
- } else if (Z_MODE(op1_addr) == IS_CONST_ZVAL) {
- ZEND_ASSERT(Z_TYPE_P(Z_ZV(op1_addr)) == IS_LONG);
- op1_min = op1_max = Z_LVAL_P(Z_ZV(op1_addr));
- } else {
- return 0;
- }
- if (op2_range) {
- op2_min = op2_range->min;
- op2_max = op2_range->max;
- } else if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
- ZEND_ASSERT(Z_TYPE_P(Z_ZV(op2_addr)) == IS_LONG);
- op2_min = op2_max = Z_LVAL_P(Z_ZV(op2_addr));
- } else {
- return 0;
- }
- switch (opline->opcode) {
- case ZEND_IS_EQUAL:
- case ZEND_IS_IDENTICAL:
- case ZEND_CASE:
- case ZEND_CASE_STRICT:
- if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) {
- *result = 1;
- return 1;
- } else if (op1_max < op2_min || op1_min > op2_max) {
- *result = 0;
- return 1;
- }
- return 0;
- case ZEND_IS_NOT_EQUAL:
- case ZEND_IS_NOT_IDENTICAL:
- if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) {
- *result = 0;
- return 1;
- } else if (op1_max < op2_min || op1_min > op2_max) {
- *result = 1;
- return 1;
- }
- return 0;
- case ZEND_IS_SMALLER:
- if (op1_max < op2_min) {
- *result = 1;
- return 1;
- } else if (op1_min >= op2_max) {
- *result = 0;
- return 1;
- }
- return 0;
- case ZEND_IS_SMALLER_OR_EQUAL:
- if (op1_max <= op2_min) {
- *result = 1;
- return 1;
- } else if (op1_min > op2_max) {
- *result = 0;
- return 1;
- }
- return 0;
- default:
- ZEND_UNREACHABLE();
- }
- return 0;
- }
- static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, const zend_op *opline, int call_level, zend_jit_trace_rec *trace)
- {
- int skip;
- if (trace) {
- zend_jit_trace_rec *p = trace;
- ssa_op++;
- while (1) {
- if (p->op == ZEND_JIT_TRACE_VM) {
- switch (p->opline->opcode) {
- case ZEND_SEND_ARRAY:
- case ZEND_SEND_USER:
- case ZEND_SEND_UNPACK:
- case ZEND_INIT_FCALL:
- case ZEND_INIT_METHOD_CALL:
- case ZEND_INIT_STATIC_METHOD_CALL:
- case ZEND_INIT_FCALL_BY_NAME:
- case ZEND_INIT_NS_FCALL_BY_NAME:
- case ZEND_INIT_DYNAMIC_CALL:
- case ZEND_NEW:
- case ZEND_INIT_USER_CALL:
- case ZEND_FAST_CALL:
- case ZEND_JMP:
- case ZEND_JMPZNZ:
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_JMP_NULL:
- case ZEND_ASSERT_CHECK:
- case ZEND_CATCH:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- return 1;
- case ZEND_DO_ICALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- case ZEND_DO_FCALL:
- case ZEND_CALLABLE_CONVERT:
- return 0;
- case ZEND_SEND_VAL:
- case ZEND_SEND_VAR:
- case ZEND_SEND_VAL_EX:
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_FUNC_ARG:
- case ZEND_SEND_REF:
- case ZEND_SEND_VAR_NO_REF:
- case ZEND_SEND_VAR_NO_REF_EX:
- /* skip */
- break;
- default:
- if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
- return 1;
- }
- }
- ssa_op += zend_jit_trace_op_len(opline);
- } else if (p->op == ZEND_JIT_TRACE_ENTER ||
- p->op == ZEND_JIT_TRACE_BACK ||
- p->op == ZEND_JIT_TRACE_END) {
- return 1;
- }
- p++;
- }
- }
- if (!call_info) {
- const zend_op *end = op_array->opcodes + op_array->last;
- opline++;
- ssa_op++;
- skip = (call_level == 1);
- while (opline != end) {
- if (!skip) {
- if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
- return 1;
- }
- }
- switch (opline->opcode) {
- case ZEND_SEND_VAL:
- case ZEND_SEND_VAR:
- case ZEND_SEND_VAL_EX:
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_FUNC_ARG:
- case ZEND_SEND_REF:
- case ZEND_SEND_VAR_NO_REF:
- case ZEND_SEND_VAR_NO_REF_EX:
- skip = 0;
- break;
- case ZEND_SEND_ARRAY:
- case ZEND_SEND_USER:
- case ZEND_SEND_UNPACK:
- case ZEND_INIT_FCALL:
- case ZEND_INIT_METHOD_CALL:
- case ZEND_INIT_STATIC_METHOD_CALL:
- case ZEND_INIT_FCALL_BY_NAME:
- case ZEND_INIT_NS_FCALL_BY_NAME:
- case ZEND_INIT_DYNAMIC_CALL:
- case ZEND_NEW:
- case ZEND_INIT_USER_CALL:
- case ZEND_FAST_CALL:
- case ZEND_JMP:
- case ZEND_JMPZNZ:
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_JMP_NULL:
- case ZEND_ASSERT_CHECK:
- case ZEND_CATCH:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- return 1;
- case ZEND_DO_ICALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- case ZEND_DO_FCALL:
- case ZEND_CALLABLE_CONVERT:
- end = opline;
- if (end - op_array->opcodes >= ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len) {
- /* INIT_FCALL and DO_FCALL in different BasicBlocks */
- return 1;
- }
- return 0;
- }
- opline++;
- ssa_op++;
- }
- return 1;
- } else {
- const zend_op *end = call_info->caller_call_opline;
- /* end may be null if an opcode like EXIT is part of the argument list. */
- if (!end || end - op_array->opcodes >= ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len) {
- /* INIT_FCALL and DO_FCALL in different BasicBlocks */
- return 1;
- }
- opline++;
- ssa_op++;
- skip = (call_level == 1);
- while (opline != end) {
- if (skip) {
- switch (opline->opcode) {
- case ZEND_SEND_VAL:
- case ZEND_SEND_VAR:
- case ZEND_SEND_VAL_EX:
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_FUNC_ARG:
- case ZEND_SEND_REF:
- case ZEND_SEND_VAR_NO_REF:
- case ZEND_SEND_VAR_NO_REF_EX:
- skip = 0;
- break;
- case ZEND_SEND_ARRAY:
- case ZEND_SEND_USER:
- case ZEND_SEND_UNPACK:
- return 1;
- }
- } else {
- if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
- return 1;
- }
- }
- opline++;
- ssa_op++;
- }
- return 0;
- }
- }
- static uint32_t skip_valid_arguments(const zend_op_array *op_array, zend_ssa *ssa, const zend_call_info *call_info)
- {
- uint32_t num_args = 0;
- zend_function *func = call_info->callee_func;
- /* It's okay to handle prototypes here, because they can only increase the accepted arguments.
- * Anything legal for the parent method is also legal for the parent method. */
- while (num_args < call_info->num_args) {
- zend_arg_info *arg_info = func->op_array.arg_info + num_args;
- if (ZEND_TYPE_IS_SET(arg_info->type)) {
- if (ZEND_TYPE_IS_ONLY_MASK(arg_info->type)) {
- zend_op *opline = call_info->arg_info[num_args].opline;
- zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
- uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type);
- if ((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~type_mask) {
- break;
- }
- } else {
- break;
- }
- }
- num_args++;
- }
- return num_args;
- }
- static uint32_t zend_ssa_cv_info(const zend_op_array *op_array, zend_ssa *ssa, uint32_t var)
- {
- uint32_t j, info;
- if (ssa->vars && ssa->var_info) {
- info = ssa->var_info[var].type;
- for (j = op_array->last_var; j < ssa->vars_count; j++) {
- if (ssa->vars[j].var == var) {
- info |= ssa->var_info[j].type;
- }
- }
- } else {
- info = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_UNDEF |
- MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
- }
- #ifdef ZEND_JIT_USE_RC_INFERENCE
- /* Refcount may be increased by RETURN opcode */
- if ((info & MAY_BE_RC1) && !(info & MAY_BE_RCN)) {
- for (j = 0; j < ssa->cfg.blocks_count; j++) {
- if ((ssa->cfg.blocks[j].flags & ZEND_BB_REACHABLE) &&
- ssa->cfg.blocks[j].len > 0) {
- const zend_op *opline = op_array->opcodes + ssa->cfg.blocks[j].start + ssa->cfg.blocks[j].len - 1;
- if (opline->opcode == ZEND_RETURN) {
- if (opline->op1_type == IS_CV && opline->op1.var == EX_NUM_TO_VAR(var)) {
- info |= MAY_BE_RCN;
- break;
- }
- }
- }
- }
- }
- #endif
- return info;
- }
- static bool zend_jit_may_avoid_refcounting(const zend_op *opline, uint32_t op1_info)
- {
- switch (opline->opcode) {
- case ZEND_FETCH_OBJ_FUNC_ARG:
- if (!JIT_G(current_frame) ||
- !JIT_G(current_frame)->call->func ||
- !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
- return 0;
- }
- /* break missing intentionally */
- case ZEND_FETCH_OBJ_R:
- case ZEND_FETCH_OBJ_IS:
- if ((op1_info & MAY_BE_OBJECT)
- && 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 1;
- }
- break;
- case ZEND_FETCH_DIM_FUNC_ARG:
- if (!JIT_G(current_frame) ||
- !JIT_G(current_frame)->call->func ||
- !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
- return 0;
- }
- /* break missing intentionally */
- case ZEND_FETCH_DIM_R:
- case ZEND_FETCH_DIM_IS:
- return 1;
- case ZEND_ISSET_ISEMPTY_DIM_OBJ:
- if (!(opline->extended_value & ZEND_ISEMPTY)) {
- return 1;
- }
- break;
- }
- return 0;
- }
- static bool zend_jit_is_persistent_constant(zval *key, uint32_t flags)
- {
- zval *zv;
- zend_constant *c = NULL;
- /* null/true/false are resolved during compilation, so don't check for them here. */
- zv = zend_hash_find_known_hash(EG(zend_constants), Z_STR_P(key));
- if (zv) {
- c = (zend_constant*)Z_PTR_P(zv);
- } else if (flags & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
- key++;
- zv = zend_hash_find_known_hash(EG(zend_constants), Z_STR_P(key));
- if (zv) {
- c = (zend_constant*)Z_PTR_P(zv);
- }
- }
- return c && (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT);
- }
- static zend_property_info* zend_get_known_property_info(const zend_op_array *op_array, zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename)
- {
- zend_property_info *info = NULL;
- if ((on_this && (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) ||
- !ce ||
- !(ce->ce_flags & ZEND_ACC_LINKED) ||
- (ce->ce_flags & ZEND_ACC_TRAIT) ||
- ce->create_object) {
- return NULL;
- }
- if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
- if (ce->info.user.filename != filename) {
- /* class declaration might be changed independently */
- return NULL;
- }
- if (ce->parent) {
- zend_class_entry *parent = ce->parent;
- do {
- if (parent->type == ZEND_INTERNAL_CLASS) {
- break;
- } else if (parent->info.user.filename != filename) {
- /* some of parents class declarations might be changed independently */
- /* TODO: this check may be not enough, because even
- * in the same it's possible to conditionally define
- * few classes with the same name, and "parent" may
- * change from request to request.
- */
- return NULL;
- }
- parent = parent->parent;
- } while (parent);
- }
- }
- info = (zend_property_info*)zend_hash_find_ptr(&ce->properties_info, member);
- if (info == NULL ||
- !IS_VALID_PROPERTY_OFFSET(info->offset) ||
- (info->flags & ZEND_ACC_STATIC)) {
- return NULL;
- }
- if (info->flags & ZEND_ACC_PUBLIC) {
- return info;
- } else if (on_this) {
- if (ce == info->ce) {
- return info;
- } else if ((info->flags & ZEND_ACC_PROTECTED)
- && instanceof_function_slow(ce, info->ce)) {
- return info;
- }
- }
- return NULL;
- }
- static bool zend_may_be_dynamic_property(zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename)
- {
- zend_property_info *info;
- if (!ce || (ce->ce_flags & ZEND_ACC_TRAIT)) {
- return 1;
- }
- if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
- if (ce->info.user.filename != filename) {
- /* class declaration might be changed independently */
- return 1;
- }
- }
- info = (zend_property_info*)zend_hash_find_ptr(&ce->properties_info, member);
- if (info == NULL ||
- !IS_VALID_PROPERTY_OFFSET(info->offset) ||
- (info->flags & ZEND_ACC_STATIC)) {
- return 1;
- }
- if (!(info->flags & ZEND_ACC_PUBLIC) &&
- (!on_this || info->ce != ce)) {
- return 1;
- }
- return 0;
- }
- #define OP_RANGE(ssa_op, opN) \
- (((opline->opN##_type & (IS_TMP_VAR|IS_VAR|IS_CV)) && \
- ssa->var_info && \
- (ssa_op)->opN##_use >= 0 && \
- ssa->var_info[(ssa_op)->opN##_use].has_range) ? \
- &ssa->var_info[(ssa_op)->opN##_use].range : NULL)
- #define OP1_RANGE() OP_RANGE(ssa_op, op1)
- #define OP2_RANGE() OP_RANGE(ssa_op, op2)
- #define OP1_DATA_RANGE() OP_RANGE(ssa_op + 1, op1)
- #if ZEND_JIT_TARGET_X86
- # include "dynasm/dasm_x86.h"
- #elif ZEND_JIT_TARGET_ARM64
- static int zend_jit_add_veneer(dasm_State *Dst, void *buffer, uint32_t ins, int *b, uint32_t *cp, ptrdiff_t offset);
- # define DASM_ADD_VENEER zend_jit_add_veneer
- # include "dynasm/dasm_arm64.h"
- #endif
- #include "jit/zend_jit_helpers.c"
- #include "jit/zend_jit_disasm.c"
- #ifndef _WIN32
- # include "jit/zend_jit_gdb.h"
- # include "jit/zend_jit_perf_dump.c"
- #endif
- #ifdef HAVE_OPROFILE
- # include "jit/zend_jit_oprofile.c"
- #endif
- #include "Zend/zend_cpuinfo.h"
- #ifdef HAVE_VALGRIND
- # include <valgrind/valgrind.h>
- #endif
- #ifdef HAVE_GCC_GLOBAL_REGS
- # define GCC_GLOBAL_REGS 1
- #else
- # define GCC_GLOBAL_REGS 0
- #endif
- /* By default avoid JITing inline handlers if it does not seem profitable due to lack of
- * type information. Disabling this option allows testing some JIT handlers in the
- * presence of try/catch blocks, which prevent SSA construction. */
- #ifndef PROFITABILITY_CHECKS
- # define PROFITABILITY_CHECKS 1
- #endif
- #define BP_JIT_IS 6 /* Used for ISSET_ISEMPTY_DIM_OBJ. see BP_VAR_*defines in Zend/zend_compile.h */
- typedef enum _sp_adj_kind {
- SP_ADJ_NONE,
- SP_ADJ_RET,
- SP_ADJ_VM,
- SP_ADJ_JIT,
- SP_ADJ_ASSIGN,
- SP_ADJ_LAST
- } sp_adj_kind;
- static int sp_adj[SP_ADJ_LAST];
- /* The generated code may contain tautological comparisons, ignore them. */
- #if defined(__clang__)
- # pragma clang diagnostic push
- # pragma clang diagnostic ignored "-Wtautological-compare"
- # pragma clang diagnostic ignored "-Wstring-compare"
- #endif
- #if ZEND_JIT_TARGET_X86
- # include "jit/zend_jit_vtune.c"
- # include "jit/zend_jit_x86.c"
- #elif ZEND_JIT_TARGET_ARM64
- # include "jit/zend_jit_arm64.c"
- #endif
- #if defined(__clang__)
- # pragma clang diagnostic pop
- #endif
- #if _WIN32
- # include <Windows.h>
- #else
- # include <sys/mman.h>
- # if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
- # define MAP_ANONYMOUS MAP_ANON
- # endif
- #endif
- ZEND_EXT_API void zend_jit_status(zval *ret)
- {
- zval stats;
- array_init(&stats);
- add_assoc_bool(&stats, "enabled", JIT_G(enabled));
- add_assoc_bool(&stats, "on", JIT_G(on));
- add_assoc_long(&stats, "kind", JIT_G(trigger));
- add_assoc_long(&stats, "opt_level", JIT_G(opt_level));
- add_assoc_long(&stats, "opt_flags", JIT_G(opt_flags));
- if (dasm_buf) {
- add_assoc_long(&stats, "buffer_size", (char*)dasm_end - (char*)dasm_buf);
- add_assoc_long(&stats, "buffer_free", (char*)dasm_end - (char*)*dasm_ptr);
- } else {
- add_assoc_long(&stats, "buffer_size", 0);
- add_assoc_long(&stats, "buffer_free", 0);
- }
- add_assoc_zval(ret, "jit", &stats);
- }
- static zend_string *zend_jit_func_name(const zend_op_array *op_array)
- {
- smart_str buf = {0};
- if (op_array->function_name) {
- if (op_array->scope) {
- smart_str_appends(&buf, JIT_PREFIX);
- 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));
- smart_str_0(&buf);
- return buf.s;
- } else {
- smart_str_appends(&buf, JIT_PREFIX);
- smart_str_appendl(&buf, ZSTR_VAL(op_array->function_name), ZSTR_LEN(op_array->function_name));
- smart_str_0(&buf);
- return buf.s;
- }
- } else if (op_array->filename) {
- smart_str_appends(&buf, JIT_PREFIX);
- smart_str_appendl(&buf, ZSTR_VAL(op_array->filename), ZSTR_LEN(op_array->filename));
- smart_str_0(&buf);
- return buf.s;
- } else {
- return NULL;
- }
- }
- #if ZEND_DEBUG
- static void handle_dasm_error(int ret) {
- switch (ret & 0xff000000u) {
- case DASM_S_NOMEM:
- fprintf(stderr, "DASM_S_NOMEM\n");
- break;
- case DASM_S_PHASE:
- fprintf(stderr, "DASM_S_PHASE\n");
- break;
- case DASM_S_MATCH_SEC:
- fprintf(stderr, "DASM_S_MATCH_SEC\n");
- break;
- case DASM_S_RANGE_I:
- fprintf(stderr, "DASM_S_RANGE_I\n");
- break;
- case DASM_S_RANGE_SEC:
- fprintf(stderr, "DASM_S_RANGE_SEC\n");
- break;
- case DASM_S_RANGE_LG:
- fprintf(stderr, "DASM_S_RANGE_LG\n");
- break;
- case DASM_S_RANGE_PC:
- fprintf(stderr, "DASM_S_RANGE_PC %d\n", ret & 0xffffffu);
- break;
- #ifdef DASM_S_RANGE_VREG
- case DASM_S_RANGE_VREG:
- fprintf(stderr, "DASM_S_RANGE_VREG\n");
- break;
- #endif
- #ifdef DASM_S_UNDEF_L
- case DASM_S_UNDEF_L:
- fprintf(stderr, "DASM_S_UNDEF_L\n");
- break;
- #endif
- #ifdef DASM_S_UNDEF_LG
- case DASM_S_UNDEF_LG:
- fprintf(stderr, "DASM_S_UNDEF_LG\n");
- break;
- #endif
- #ifdef DASM_S_RANGE_REL
- case DASM_S_RANGE_REL:
- fprintf(stderr, "DASM_S_RANGE_REL\n");
- break;
- #endif
- case DASM_S_UNDEF_PC:
- fprintf(stderr, "DASM_S_UNDEF_PC\n");
- break;
- default:
- fprintf(stderr, "DASM_S_%0x\n", ret & 0xff000000u);
- break;
- }
- ZEND_UNREACHABLE();
- }
- #endif
- static void *dasm_link_and_encode(dasm_State **dasm_state,
- const zend_op_array *op_array,
- zend_ssa *ssa,
- const zend_op *rt_opline,
- zend_lifetime_interval **ra,
- const char *name,
- uint32_t trace_num,
- uint32_t sp_offset,
- uint32_t sp_adjustment)
- {
- size_t size;
- int ret;
- void *entry;
- #if defined(HAVE_DISASM) || defined(HAVE_GDB) || defined(HAVE_OPROFILE) || defined(HAVE_PERFTOOLS) || defined(HAVE_VTUNE)
- zend_string *str = NULL;
- #endif
- if (rt_opline && ssa && ssa->cfg.map) {
- /* Create additional entry point, to switch from interpreter to JIT-ed
- * code at run-time.
- */
- int b = ssa->cfg.map[rt_opline - op_array->opcodes];
- //#ifdef CONTEXT_THREADED_JIT
- // if (!(ssa->cfg.blocks[b].flags & (ZEND_BB_START|ZEND_BB_RECV_ENTRY))) {
- //#else
- if (!(ssa->cfg.blocks[b].flags & (ZEND_BB_START|ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY))) {
- //#endif
- zend_jit_label(dasm_state, ssa->cfg.blocks_count + b);
- zend_jit_prologue(dasm_state);
- if (ra) {
- int i;
- zend_lifetime_interval *ival;
- zend_life_range *range;
- uint32_t pos = rt_opline - op_array->opcodes;
- for (i = 0; i < ssa->vars_count; i++) {
- ival = ra[i];
- if (ival && ival->reg != ZREG_NONE) {
- range = &ival->range;
- if (pos >= range->start && pos <= range->end) {
- if (!zend_jit_load_var(dasm_state, ssa->var_info[i].type, ssa->vars[i].var, ival->reg)) {
- return NULL;
- }
- break;
- }
- range = range->next;
- }
- }
- }
- zend_jit_jmp(dasm_state, b);
- }
- }
- ret = dasm_link(dasm_state, &size);
- if (ret != DASM_S_OK) {
- #if ZEND_DEBUG
- handle_dasm_error(ret);
- #endif
- return NULL;
- }
- if ((void*)((char*)*dasm_ptr + size) > dasm_end) {
- *dasm_ptr = dasm_end; //prevent further try
- // TODO: jit_buffer_size overflow ???
- return NULL;
- }
- #if ZEND_JIT_TARGET_ARM64
- dasm_venners_size = 0;
- #endif
- ret = dasm_encode(dasm_state, *dasm_ptr);
- if (ret != DASM_S_OK) {
- #if ZEND_DEBUG
- handle_dasm_error(ret);
- #endif
- return NULL;
- }
- #if ZEND_JIT_TARGET_ARM64
- size += dasm_venners_size;
- #endif
- entry = *dasm_ptr;
- *dasm_ptr = (void*)((char*)*dasm_ptr + ZEND_MM_ALIGNED_SIZE_EX(size, DASM_ALIGNMENT));
- /* flush the hardware I-cache */
- JIT_CACHE_FLUSH(entry, entry + size);
- if (trace_num) {
- zend_jit_trace_add_code(entry, dasm_getpclabel(dasm_state, 1));
- }
- if (op_array && ssa) {
- int b;
- for (b = 0; b < ssa->cfg.blocks_count; b++) {
- //#ifdef CONTEXT_THREADED_JIT
- // if (ssa->cfg.blocks[b].flags & (ZEND_BB_START|ZEND_BB_RECV_ENTRY)) {
- //#else
- if (ssa->cfg.blocks[b].flags & (ZEND_BB_START|ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY)) {
- //#endif
- zend_op *opline = op_array->opcodes + ssa->cfg.blocks[b].start;
- int offset = dasm_getpclabel(dasm_state, ssa->cfg.blocks_count + b);
- if (offset >= 0) {
- opline->handler = (void*)(((char*)entry) + offset);
- }
- }
- }
- if (rt_opline && ssa && ssa->cfg.map) {
- int b = ssa->cfg.map[rt_opline - op_array->opcodes];
- zend_op *opline = (zend_op*)rt_opline;
- int offset = dasm_getpclabel(dasm_state, ssa->cfg.blocks_count + b);
- if (offset >= 0) {
- opline->handler = (void*)(((char*)entry) + offset);
- }
- }
- }
- #if defined(HAVE_DISASM) || defined(HAVE_GDB) || defined(HAVE_OPROFILE) || defined(HAVE_PERFTOOLS) || defined(HAVE_VTUNE)
- if (!name) {
- if (JIT_G(debug) & (ZEND_JIT_DEBUG_ASM|ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_OPROFILE|ZEND_JIT_DEBUG_PERF|ZEND_JIT_DEBUG_VTUNE|ZEND_JIT_DEBUG_PERF_DUMP)) {
- str = zend_jit_func_name(op_array);
- if (str) {
- name = ZSTR_VAL(str);
- }
- }
- #ifdef HAVE_DISASM
- if (JIT_G(debug) & ZEND_JIT_DEBUG_ASM) {
- zend_jit_disasm_add_symbol(name, (uintptr_t)entry, size);
- zend_jit_disasm(
- name,
- (op_array && op_array->filename) ? ZSTR_VAL(op_array->filename) : NULL,
- op_array,
- &ssa->cfg,
- entry,
- size);
- }
- } else {
- if (JIT_G(debug) & (ZEND_JIT_DEBUG_ASM_STUBS|ZEND_JIT_DEBUG_ASM)) {
- zend_jit_disasm_add_symbol(name, (uintptr_t)entry, size);
- if ((JIT_G(debug) & (trace_num ? ZEND_JIT_DEBUG_ASM : ZEND_JIT_DEBUG_ASM_STUBS)) != 0) {
- zend_jit_disasm(
- name,
- (op_array && op_array->filename) ? ZSTR_VAL(op_array->filename) : NULL,
- op_array,
- ssa ? &ssa->cfg : NULL,
- entry,
- size);
- }
- }
- # endif
- }
- #endif
- #ifdef HAVE_GDB
- if (JIT_G(debug) & ZEND_JIT_DEBUG_GDB) {
- if (name) {
- zend_jit_gdb_register(
- name,
- op_array,
- entry,
- size,
- sp_adj[sp_offset],
- sp_adj[sp_adjustment]);
- }
- }
- #endif
- #ifdef HAVE_OPROFILE
- if (JIT_G(debug) & ZEND_JIT_DEBUG_OPROFILE) {
- zend_jit_oprofile_register(
- name,
- entry,
- size);
- }
- #endif
- #ifdef HAVE_PERFTOOLS
- if (JIT_G(debug) & (ZEND_JIT_DEBUG_PERF|ZEND_JIT_DEBUG_PERF_DUMP)) {
- if (name) {
- zend_jit_perf_map_register(
- name,
- entry,
- size);
- if (JIT_G(debug) & ZEND_JIT_DEBUG_PERF_DUMP) {
- zend_jit_perf_jitdump_register(
- name,
- entry,
- size);
- }
- }
- }
- #endif
- #ifdef HAVE_VTUNE
- if (JIT_G(debug) & ZEND_JIT_DEBUG_VTUNE) {
- if (name) {
- zend_jit_vtune_register(
- name,
- entry,
- size);
- }
- }
- #endif
- #if defined(HAVE_DISASM) || defined(HAVE_GDB) || defined(HAVE_OPROFILE) || defined(HAVE_PERFTOOLS) || defined(HAVE_VTUNE)
- if (str) {
- zend_string_release(str);
- }
- #endif
- return entry;
- }
- static int zend_may_overflow(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa)
- {
- int res;
- zend_long op1_min, op1_max, op2_min, op2_max;
- if (!ssa->ops || !ssa->var_info) {
- return 1;
- }
- switch (opline->opcode) {
- case ZEND_PRE_INC:
- case ZEND_POST_INC:
- res = ssa_op->op1_def;
- if (res < 0
- || !ssa->var_info[res].has_range
- || ssa->var_info[res].range.overflow) {
- if (!OP1_HAS_RANGE()) {
- return 1;
- }
- op1_max = OP1_MAX_RANGE();
- if (op1_max == ZEND_LONG_MAX) {
- return 1;
- }
- }
- return 0;
- case ZEND_PRE_DEC:
- case ZEND_POST_DEC:
- res = ssa_op->op1_def;
- if (res < 0
- || !ssa->var_info[res].has_range
- || ssa->var_info[res].range.underflow) {
- if (!OP1_HAS_RANGE()) {
- return 1;
- }
- op1_min = OP1_MIN_RANGE();
- if (op1_min == ZEND_LONG_MIN) {
- return 1;
- }
- }
- return 0;
- case ZEND_ADD:
- res = ssa_op->result_def;
- if (res < 0
- || !ssa->var_info[res].has_range
- || ssa->var_info[res].range.underflow) {
- if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
- return 1;
- }
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- if (zend_add_will_overflow(op1_min, op2_min)) {
- return 1;
- }
- }
- if (res < 0
- || !ssa->var_info[res].has_range
- || ssa->var_info[res].range.overflow) {
- if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
- return 1;
- }
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- if (zend_add_will_overflow(op1_max, op2_max)) {
- return 1;
- }
- }
- return 0;
- case ZEND_SUB:
- res = ssa_op->result_def;
- if (res < 0
- || !ssa->var_info[res].has_range
- || ssa->var_info[res].range.underflow) {
- if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
- return 1;
- }
- op1_min = OP1_MIN_RANGE();
- op2_max = OP2_MAX_RANGE();
- if (zend_sub_will_overflow(op1_min, op2_max)) {
- return 1;
- }
- }
- if (res < 0
- || !ssa->var_info[res].has_range
- || ssa->var_info[res].range.overflow) {
- if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
- return 1;
- }
- op1_max = OP1_MAX_RANGE();
- op2_min = OP2_MIN_RANGE();
- if (zend_sub_will_overflow(op1_max, op2_min)) {
- return 1;
- }
- }
- return 0;
- case ZEND_MUL:
- res = ssa_op->result_def;
- return (res < 0 ||
- !ssa->var_info[res].has_range ||
- ssa->var_info[res].range.underflow ||
- ssa->var_info[res].range.overflow);
- case ZEND_ASSIGN_OP:
- if (opline->extended_value == ZEND_ADD) {
- res = ssa_op->op1_def;
- if (res < 0
- || !ssa->var_info[res].has_range
- || ssa->var_info[res].range.underflow) {
- if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
- return 1;
- }
- op1_min = OP1_MIN_RANGE();
- op2_min = OP2_MIN_RANGE();
- if (zend_add_will_overflow(op1_min, op2_min)) {
- return 1;
- }
- }
- if (res < 0
- || !ssa->var_info[res].has_range
- || ssa->var_info[res].range.overflow) {
- if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
- return 1;
- }
- op1_max = OP1_MAX_RANGE();
- op2_max = OP2_MAX_RANGE();
- if (zend_add_will_overflow(op1_max, op2_max)) {
- return 1;
- }
- }
- return 0;
- } else if (opline->extended_value == ZEND_SUB) {
- res = ssa_op->op1_def;
- if (res < 0
- || !ssa->var_info[res].has_range
- || ssa->var_info[res].range.underflow) {
- if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
- return 1;
- }
- op1_min = OP1_MIN_RANGE();
- op2_max = OP2_MAX_RANGE();
- if (zend_sub_will_overflow(op1_min, op2_max)) {
- return 1;
- }
- }
- if (res < 0
- || !ssa->var_info[res].has_range
- || ssa->var_info[res].range.overflow) {
- if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
- return 1;
- }
- op1_max = OP1_MAX_RANGE();
- op2_min = OP2_MIN_RANGE();
- if (zend_sub_will_overflow(op1_max, op2_min)) {
- return 1;
- }
- }
- return 0;
- } else if (opline->extended_value == ZEND_MUL) {
- res = ssa_op->op1_def;
- return (res < 0 ||
- !ssa->var_info[res].has_range ||
- ssa->var_info[res].range.underflow ||
- ssa->var_info[res].range.overflow);
- }
- ZEND_FALLTHROUGH;
- default:
- return 1;
- }
- }
- static int zend_jit_build_cfg(const zend_op_array *op_array, zend_cfg *cfg)
- {
- uint32_t flags;
- flags = ZEND_CFG_STACKLESS | ZEND_CFG_NO_ENTRY_PREDECESSORS | ZEND_SSA_RC_INFERENCE_FLAG | ZEND_SSA_USE_CV_RESULTS | ZEND_CFG_RECV_ENTRY;
- if (zend_build_cfg(&CG(arena), op_array, flags, cfg) != SUCCESS) {
- return FAILURE;
- }
- /* Don't JIT huge functions. Apart from likely being detrimental due to the amount of
- * generated code, some of our analysis is recursive and will stack overflow with many
- * blocks. */
- if (cfg->blocks_count > 100000) {
- return FAILURE;
- }
- if (zend_cfg_build_predecessors(&CG(arena), cfg) != SUCCESS) {
- return FAILURE;
- }
- /* Compute Dominators Tree */
- if (zend_cfg_compute_dominators_tree(op_array, cfg) != SUCCESS) {
- return FAILURE;
- }
- /* Identify reducible and irreducible loops */
- if (zend_cfg_identify_loops(op_array, cfg) != SUCCESS) {
- return FAILURE;
- }
- return SUCCESS;
- }
- static int zend_jit_op_array_analyze1(const zend_op_array *op_array, zend_script *script, zend_ssa *ssa)
- {
- if (zend_jit_build_cfg(op_array, &ssa->cfg) != SUCCESS) {
- return FAILURE;
- }
- #if 0
- /* TODO: debugger and profiler supports? */
- if ((ssa->cfg.flags & ZEND_FUNC_HAS_EXTENDED_INFO)) {
- return FAILURE;
- }
- #endif
- /* TODO: move this to zend_cfg.c ? */
- if (!op_array->function_name) {
- ssa->cfg.flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
- }
- if ((JIT_G(opt_level) >= ZEND_JIT_LEVEL_OPT_FUNC)
- && ssa->cfg.blocks
- && op_array->last_try_catch == 0
- && !(op_array->fn_flags & ZEND_ACC_GENERATOR)
- && !(ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS)) {
- if (zend_build_ssa(&CG(arena), script, op_array, ZEND_SSA_RC_INFERENCE | ZEND_SSA_USE_CV_RESULTS, ssa) != SUCCESS) {
- return FAILURE;
- }
- if (zend_ssa_compute_use_def_chains(&CG(arena), op_array, ssa) != SUCCESS) {
- return FAILURE;
- }
- if (zend_ssa_find_false_dependencies(op_array, ssa) != SUCCESS) {
- return FAILURE;
- }
- if (zend_ssa_find_sccs(op_array, ssa) != SUCCESS){
- return FAILURE;
- }
- }
- return SUCCESS;
- }
- static int zend_jit_op_array_analyze2(const zend_op_array *op_array, zend_script *script, zend_ssa *ssa, uint32_t optimization_level)
- {
- if ((JIT_G(opt_level) >= ZEND_JIT_LEVEL_OPT_FUNC)
- && ssa->cfg.blocks
- && op_array->last_try_catch == 0
- && !(op_array->fn_flags & ZEND_ACC_GENERATOR)
- && !(ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS)) {
- if (zend_ssa_inference(&CG(arena), op_array, script, ssa,
- optimization_level & ~ZEND_OPTIMIZER_NARROW_TO_DOUBLE) != SUCCESS) {
- return FAILURE;
- }
- }
- return SUCCESS;
- }
- static int zend_jit_add_range(zend_lifetime_interval **intervals, int var, uint32_t from, uint32_t to)
- {
- zend_lifetime_interval *ival = intervals[var];
- if (!ival) {
- ival = zend_arena_alloc(&CG(arena), sizeof(zend_lifetime_interval));
- if (!ival) {
- return FAILURE;
- }
- ival->ssa_var = var;
- ival->reg = ZREG_NONE;
- ival->flags = 0;
- ival->range.start = from;
- ival->range.end = to;
- ival->range.next = NULL;
- ival->hint = NULL;
- ival->used_as_hint = NULL;
- intervals[var] = ival;
- } else if (ival->range.start > to + 1) {
- zend_life_range *range = zend_arena_alloc(&CG(arena), sizeof(zend_life_range));
- if (!range) {
- return FAILURE;
- }
- range->start = ival->range.start;
- range->end = ival->range.end;
- range->next = ival->range.next;
- ival->range.start = from;
- ival->range.end = to;
- ival->range.next = range;
- } else if (ival->range.start == to + 1) {
- ival->range.start = from;
- } else {
- zend_life_range *range = &ival->range;
- zend_life_range *last = NULL;
- do {
- if (range->start > to + 1) {
- break;
- } else if (range->end + 1 >= from) {
- if (range->start > from) {
- range->start = from;
- }
- last = range;
- range = range->next;
- while (range) {
- if (range->start > to + 1) {
- break;
- }
- last->end = range->end;
- range = range->next;
- last->next = range;
- }
- if (to > last->end) {
- last->end = to;
- }
- return SUCCESS;
- }
- last = range;
- range = range->next;
- } while (range);
- range = zend_arena_alloc(&CG(arena), sizeof(zend_life_range));
- if (!range) {
- return FAILURE;
- }
- range->start = from;
- range->end = to;
- range->next = last->next;
- last->next = range;
- }
- return SUCCESS;
- }
- static int zend_jit_begin_range(zend_lifetime_interval **intervals, int var, uint32_t block_start, uint32_t from)
- {
- if (block_start != from && intervals[var]) {
- zend_life_range *range = &intervals[var]->range;
- do {
- if (from >= range->start && from <= range->end) {
- if (range->start == block_start) {
- range->start = from;
- } else {
- zend_life_range *r = zend_arena_alloc(&CG(arena), sizeof(zend_life_range));
- if (!r) {
- return FAILURE;
- }
- r->start = from;
- r->end = range->end;
- r->next = range->next;
- range->end = block_start - 1;
- range->next = r;
- }
- return SUCCESS;
- }
- range = range->next;
- } while (range);
- }
- // dead store
- return zend_jit_add_range(intervals, var, from, from);
- }
- static void zend_jit_insert_interval(zend_lifetime_interval **list, zend_lifetime_interval *ival)
- {
- while (1) {
- if (*list == NULL) {
- *list = ival;
- ival->list_next = NULL;
- return;
- } else if (ival->range.start < (*list)->range.start) {
- ival->list_next = *list;
- *list = ival;
- return;
- }
- list = &(*list)->list_next;
- }
- }
- static int zend_jit_split_interval(zend_lifetime_interval *current, uint32_t pos, zend_lifetime_interval **list, zend_lifetime_interval **free)
- {
- zend_lifetime_interval *ival;
- zend_life_range *range = ¤t->range;
- zend_life_range *prev = NULL;
- if (*free) {
- ival = *free;
- *free = ival->list_next;
- } else {
- ival = zend_arena_alloc(&CG(arena), sizeof(zend_lifetime_interval));
- if (!ival) {
- return FAILURE;
- }
- }
- current->flags |= ZREG_STORE;
- ival->ssa_var = current->ssa_var;
- ival->reg = ZREG_NONE;
- ival->flags |= ZREG_SPLIT | ZREG_LOAD;
- ival->flags &= ~ZREG_STORE;
- ival->hint = NULL;
- do {
- if (pos >= range->start && pos <= range->end) {
- break;
- }
- prev = range;
- range = range->next;
- } while(range);
- ZEND_ASSERT(range != NULL);
- ival->range.start = pos;
- ival->range.end = range->end;
- ival->range.next = range->next;
- if (pos == range->start) {
- ZEND_ASSERT(prev != NULL);
- prev->next = NULL;
- } else {
- range->end = pos - 1;
- }
- zend_jit_insert_interval(list, ival);
- return SUCCESS;
- }
- static zend_lifetime_interval *zend_jit_sort_intervals(zend_lifetime_interval **intervals, int count)
- {
- zend_lifetime_interval *list, *last;
- int i;
- list = NULL;
- i = 0;
- while (i < count) {
- list = intervals[i];
- i++;
- if (list) {
- last = list;
- last->list_next = NULL;
- break;
- }
- }
- while (i < count) {
- zend_lifetime_interval *ival = intervals[i];
- i++;
- if (ival) {
- if ((ival->range.start > last->range.start) ||
- (ival->range.start == last->range.start &&
- ((!ival->hint && last->hint && last->hint != ival) ||
- ival->range.end > last->range.end))) {
- last->list_next = ival;
- last = ival;
- ival->list_next = NULL;
- } else {
- zend_lifetime_interval **p = &list;
- while (1) {
- if (*p == NULL) {
- *p = last = ival;
- ival->list_next = NULL;
- break;
- } else if ((ival->range.start < (*p)->range.start) ||
- (ival->range.start == (*p)->range.start &&
- ((ival->hint && !(*p)->hint && ival->hint != *p) ||
- ival->range.end < (*p)->range.end))) {
- ival->list_next = *p;
- *p = ival;
- break;
- }
- p = &(*p)->list_next;
- }
- }
- }
- }
- return list;
- }
- static ZEND_ATTRIBUTE_UNUSED void zend_jit_print_regset(zend_regset regset)
- {
- zend_reg reg;
- int first = 1;
- ZEND_REGSET_FOREACH(regset, reg) {
- if (first) {
- first = 0;
- fprintf(stderr, "%s", zend_reg_name[reg]);
- } else {
- fprintf(stderr, ", %s", zend_reg_name[reg]);
- }
- } ZEND_REGSET_FOREACH_END();
- }
- static int *zend_jit_compute_block_order_int(zend_ssa *ssa, int n, int *block_order)
- {
- zend_basic_block *b = ssa->cfg.blocks + n;
- tail_call:
- *block_order = n;
- block_order++;
- n = b->children;
- while (n >= 0) {
- b = ssa->cfg.blocks + n;
- if (b->next_child < 0) {
- goto tail_call;
- }
- block_order = zend_jit_compute_block_order_int(ssa, n, block_order);
- n = b->next_child;
- }
- return block_order;
- }
- static int zend_jit_compute_block_order(zend_ssa *ssa, int *block_order)
- {
- int *end = zend_jit_compute_block_order_int(ssa, 0, block_order);
- return end - block_order;
- }
- static bool zend_jit_in_loop(zend_ssa *ssa, int header, zend_basic_block *b)
- {
- while (b->loop_header >= 0) {
- if (b->loop_header == header) {
- return 1;
- }
- b = ssa->cfg.blocks + b->loop_header;
- }
- return 0;
- }
- static void zend_jit_compute_loop_body(zend_ssa *ssa, int header, int n, zend_bitset loop_body)
- {
- zend_basic_block *b = ssa->cfg.blocks + n;
- uint32_t i;
- tail_call:
- if (b->len) {
- for (i = b->start; i < b->start + b->len; i++) {
- zend_bitset_incl(loop_body, i);
- }
- }
- n = b->children;
- while (n >= 0) {
- b = ssa->cfg.blocks + n;
- if (zend_jit_in_loop(ssa, header, b)) {
- if (b->next_child < 0) {
- goto tail_call;
- }
- zend_jit_compute_loop_body(ssa, header, n, loop_body);
- }
- n = b->next_child;
- }
- }
- static void zend_jit_add_hint(zend_lifetime_interval **intervals, int dst, int src)
- {
- if (intervals[dst]->range.start < intervals[src]->range.start) {
- int tmp = src;
- src = dst;
- dst = tmp;
- }
- while (dst != src && intervals[dst]->hint) {
- if (intervals[dst]->hint->range.start < intervals[src]->range.start) {
- int tmp = src;
- src = intervals[dst]->hint->ssa_var;
- dst = tmp;
- } else {
- dst = intervals[dst]->hint->ssa_var;
- }
- }
- if (dst != src) {
- intervals[dst]->hint = intervals[src];
- }
- }
- /* See "Linear Scan Register Allocation on SSA Form", Christian Wimmer and
- Michael Franz, CGO'10 (2010), Figure 4. */
- static int zend_jit_compute_liveness(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset candidates, zend_lifetime_interval **list)
- {
- int set_size, i, j, k, l;
- uint32_t n;
- zend_bitset live, live_in, pi_vars, loop_body;
- int *block_order;
- zend_ssa_phi *phi;
- zend_lifetime_interval **intervals;
- size_t mem_size;
- ALLOCA_FLAG(use_heap);
- set_size = zend_bitset_len(ssa->vars_count);
- mem_size =
- ZEND_MM_ALIGNED_SIZE(ssa->vars_count * sizeof(zend_lifetime_interval*)) +
- ZEND_MM_ALIGNED_SIZE((set_size * ssa->cfg.blocks_count) * ZEND_BITSET_ELM_SIZE) +
- ZEND_MM_ALIGNED_SIZE(set_size * ZEND_BITSET_ELM_SIZE) +
- ZEND_MM_ALIGNED_SIZE(set_size * ZEND_BITSET_ELM_SIZE) +
- ZEND_MM_ALIGNED_SIZE(zend_bitset_len(op_array->last) * ZEND_BITSET_ELM_SIZE) +
- ZEND_MM_ALIGNED_SIZE(ssa->cfg.blocks_count * sizeof(int));
- intervals = do_alloca(mem_size, use_heap);
- if (!intervals) {
- *list = NULL;
- return FAILURE;
- }
- live_in = (zend_bitset)((char*)intervals + ZEND_MM_ALIGNED_SIZE(ssa->vars_count * sizeof(zend_lifetime_interval*)));
- live = (zend_bitset)((char*)live_in + ZEND_MM_ALIGNED_SIZE((set_size * ssa->cfg.blocks_count) * ZEND_BITSET_ELM_SIZE));
- pi_vars = (zend_bitset)((char*)live + ZEND_MM_ALIGNED_SIZE(set_size * ZEND_BITSET_ELM_SIZE));
- loop_body = (zend_bitset)((char*)pi_vars + ZEND_MM_ALIGNED_SIZE(set_size * ZEND_BITSET_ELM_SIZE));
- block_order = (int*)((char*)loop_body + ZEND_MM_ALIGNED_SIZE(zend_bitset_len(op_array->last) * ZEND_BITSET_ELM_SIZE));
- memset(intervals, 0, ssa->vars_count * sizeof(zend_lifetime_interval*));
- zend_bitset_clear(live_in, set_size * ssa->cfg.blocks_count);
- /* TODO: Provide a linear block order where all dominators of a block
- * are before this block, and where all blocks belonging to the same loop
- * are contiguous ???
- */
- for (l = zend_jit_compute_block_order(ssa, block_order) - 1; l >= 0; l--) {
- zend_basic_block *b;
- i = block_order[l];
- b = ssa->cfg.blocks + i;
- /* live = UNION of successor.liveIn for each successor of b */
- /* live.add(phi.inputOf(b)) for each phi of successors of b */
- zend_bitset_clear(live, set_size);
- for (j = 0; j < b->successors_count; j++) {
- int succ = b->successors[j];
- zend_bitset_union(live, live_in + set_size * succ, set_size);
- zend_bitset_clear(pi_vars, set_size);
- for (phi = ssa->blocks[succ].phis; phi; phi = phi->next) {
- if (ssa->vars[phi->ssa_var].no_val) {
- /* skip */
- } else if (phi->pi >= 0) {
- if (phi->pi == i && phi->sources[0] >= 0) {
- if (zend_bitset_in(candidates, phi->sources[0])) {
- zend_bitset_incl(live, phi->sources[0]);
- }
- zend_bitset_incl(pi_vars, phi->var);
- }
- } else if (!zend_bitset_in(pi_vars, phi->var)) {
- for (k = 0; k < ssa->cfg.blocks[succ].predecessors_count; k++) {
- if (ssa->cfg.predecessors[ssa->cfg.blocks[succ].predecessor_offset + k] == i) {
- if (phi->sources[k] >= 0 && zend_bitset_in(candidates, phi->sources[k])) {
- zend_bitset_incl(live, phi->sources[k]);
- }
- break;
- }
- }
- }
- }
- }
- /* addRange(var, b.from, b.to) for each var in live */
- ZEND_BITSET_FOREACH(live, set_size, j) {
- if (zend_bitset_in(candidates, j)) {
- if (zend_jit_add_range(intervals, j, b->start, b->start + b->len - 1) != SUCCESS) {
- goto failure;
- }
- }
- } ZEND_BITSET_FOREACH_END();
- /* for each operation op of b in reverse order */
- for (n = b->start + b->len; n > b->start;) {
- zend_ssa_op *op;
- const zend_op *opline;
- uint32_t num;
- n--;
- op = ssa->ops + n;
- opline = op_array->opcodes + n;
- if (UNEXPECTED(opline->opcode == ZEND_OP_DATA)) {
- num = n - 1;
- } else {
- num = n;
- }
- /* for each output operand opd of op do */
- /* setFrom(opd, op) */
- /* live.remove(opd) */
- if (op->op1_def >= 0 && zend_bitset_in(candidates, op->op1_def)) {
- if (zend_jit_begin_range(intervals, op->op1_def, b->start, num) != SUCCESS) {
- goto failure;
- }
- zend_bitset_excl(live, op->op1_def);
- }
- if (op->op2_def >= 0 && zend_bitset_in(candidates, op->op2_def)) {
- if (zend_jit_begin_range(intervals, op->op2_def, b->start, num) != SUCCESS) {
- goto failure;
- }
- zend_bitset_excl(live, op->op2_def);
- }
- if (op->result_def >= 0 && zend_bitset_in(candidates, op->result_def)) {
- if (zend_jit_begin_range(intervals, op->result_def, b->start, num) != SUCCESS) {
- goto failure;
- }
- zend_bitset_excl(live, op->result_def);
- }
- /* for each input operand opd of op do */
- /* live.add(opd) */
- /* addRange(opd, b.from, op) */
- if (op->op1_use >= 0
- && zend_bitset_in(candidates, op->op1_use)
- && !zend_ssa_is_no_val_use(opline, op, op->op1_use)) {
- zend_bitset_incl(live, op->op1_use);
- if (zend_jit_add_range(intervals, op->op1_use, b->start, num) != SUCCESS) {
- goto failure;
- }
- }
- if (op->op2_use >= 0
- && zend_bitset_in(candidates, op->op2_use)
- && !zend_ssa_is_no_val_use(opline, op, op->op2_use)) {
- zend_bitset_incl(live, op->op2_use);
- if (zend_jit_add_range(intervals, op->op2_use, b->start, num) != SUCCESS) {
- goto failure;
- }
- }
- if (op->result_use >= 0
- && zend_bitset_in(candidates, op->result_use)
- && !zend_ssa_is_no_val_use(opline, op, op->result_use)) {
- zend_bitset_incl(live, op->result_use);
- if (zend_jit_add_range(intervals, op->result_use, b->start, num) != SUCCESS) {
- goto failure;
- }
- }
- }
- /* live.remove(phi.output) for each phi of b */
- for (phi = ssa->blocks[i].phis; phi; phi = phi->next) {
- zend_bitset_excl(live, phi->ssa_var);
- }
- /* b.liveIn = live */
- zend_bitset_copy(live_in + set_size * i, live, set_size);
- }
- for (i = ssa->cfg.blocks_count - 1; i >= 0; i--) {
- zend_basic_block *b = ssa->cfg.blocks + i;
- /* if b is loop header */
- if ((b->flags & ZEND_BB_LOOP_HEADER)) {
- live = live_in + set_size * i;
- if (!zend_bitset_empty(live, set_size)) {
- uint32_t set_size2 = zend_bitset_len(op_array->last);
- zend_bitset_clear(loop_body, set_size2);
- zend_jit_compute_loop_body(ssa, i, i, loop_body);
- while (!zend_bitset_empty(loop_body, set_size2)) {
- uint32_t from = zend_bitset_first(loop_body, set_size2);
- uint32_t to = from;
- do {
- zend_bitset_excl(loop_body, to);
- to++;
- } while (zend_bitset_in(loop_body, to));
- to--;
- ZEND_BITSET_FOREACH(live, set_size, j) {
- if (zend_jit_add_range(intervals, j, from, to) != SUCCESS) {
- goto failure;
- }
- } ZEND_BITSET_FOREACH_END();
- }
- }
- }
- }
- if (JIT_G(opt_flags) & ZEND_JIT_REG_ALLOC_GLOBAL) {
- /* Register hinting (a cheap way for register coalescing) */
- for (i = 0; i < ssa->vars_count; i++) {
- if (intervals[i]) {
- int src;
- if (ssa->vars[i].definition_phi) {
- zend_ssa_phi *phi = ssa->vars[i].definition_phi;
- if (phi->pi >= 0) {
- src = phi->sources[0];
- if (intervals[src]) {
- zend_jit_add_hint(intervals, i, src);
- }
- } else {
- for (k = 0; k < ssa->cfg.blocks[phi->block].predecessors_count; k++) {
- src = phi->sources[k];
- if (src >= 0) {
- if (ssa->vars[src].definition_phi
- && ssa->vars[src].definition_phi->pi >= 0
- && phi->block == ssa->vars[src].definition_phi->block) {
- /* Skip zero-length interval for Pi variable */
- src = ssa->vars[src].definition_phi->sources[0];
- }
- if (intervals[src]) {
- zend_jit_add_hint(intervals, i, src);
- }
- }
- }
- }
- }
- }
- }
- 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 = op_array->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:
- 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_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_jit_add_hint(intervals, i, ssa->ops[line].op2_use);
- }
- }
- break;
- }
- }
- }
- }
- }
- *list = zend_jit_sort_intervals(intervals, ssa->vars_count);
- if (*list) {
- zend_lifetime_interval *ival = *list;
- while (ival) {
- if (ival->hint) {
- ival->hint->used_as_hint = ival;
- }
- ival = ival->list_next;
- }
- }
- free_alloca(intervals, use_heap);
- return SUCCESS;
- failure:
- *list = NULL;
- free_alloca(intervals, use_heap);
- return FAILURE;
- }
- static uint32_t zend_interval_end(zend_lifetime_interval *ival)
- {
- zend_life_range *range = &ival->range;
- while (range->next) {
- range = range->next;
- }
- return range->end;
- }
- static bool zend_interval_covers(zend_lifetime_interval *ival, uint32_t position)
- {
- zend_life_range *range = &ival->range;
- do {
- if (position >= range->start && position <= range->end) {
- return 1;
- }
- range = range->next;
- } while (range);
- return 0;
- }
- static uint32_t zend_interval_intersection(zend_lifetime_interval *ival1, zend_lifetime_interval *ival2)
- {
- zend_life_range *r1 = &ival1->range;
- zend_life_range *r2 = &ival2->range;
- do {
- if (r1->start <= r2->end) {
- if (r2->start <= r1->end) {
- return MAX(r1->start, r2->start);
- } else {
- r2 = r2->next;
- }
- } else {
- r1 = r1->next;
- }
- } while (r1 && r2);
- return 0xffffffff;
- }
- /* See "Optimized Interval Splitting in a Linear Scan Register Allocator",
- Christian Wimmer VEE'05 (2005), Figure 4. Allocation without spilling */
- static int zend_jit_try_allocate_free_reg(const zend_op_array *op_array, const zend_op **ssa_opcodes, zend_ssa *ssa, zend_lifetime_interval *current, zend_regset available, zend_regset *hints, zend_lifetime_interval *active, zend_lifetime_interval *inactive, zend_lifetime_interval **list, zend_lifetime_interval **free)
- {
- zend_lifetime_interval *it;
- uint32_t freeUntilPos[ZREG_NUM];
- uint32_t pos, pos2;
- zend_reg i, reg, reg2;
- zend_reg hint = ZREG_NONE;
- zend_regset low_priority_regs;
- zend_life_range *range;
- if ((ssa->var_info[current->ssa_var].type & MAY_BE_ANY) == MAY_BE_DOUBLE) {
- available = ZEND_REGSET_INTERSECTION(available, ZEND_REGSET_FP);
- } else {
- available = ZEND_REGSET_INTERSECTION(available, ZEND_REGSET_GP);
- }
- /* TODO: Allow usage of preserved registers ???
- * Their values have to be stored in prologue and restored in epilogue
- */
- available = ZEND_REGSET_DIFFERENCE(available, ZEND_REGSET_PRESERVED);
- /* Set freeUntilPos of all physical registers to maxInt */
- for (i = 0; i < ZREG_NUM; i++) {
- freeUntilPos[i] = 0xffffffff;
- }
- /* for each interval it in active do */
- /* freeUntilPos[it.reg] = 0 */
- it = active;
- if (ssa->vars[current->ssa_var].definition == current->range.start) {
- while (it) {
- if (current->range.start != zend_interval_end(it)) {
- freeUntilPos[it->reg] = 0;
- } else if (zend_jit_may_reuse_reg(
- ssa_opcodes ? ssa_opcodes[current->range.start] : op_array->opcodes + current->range.start,
- ssa->ops + current->range.start, ssa, current->ssa_var, it->ssa_var)) {
- if (!ZEND_REGSET_IN(*hints, it->reg) &&
- /* TODO: Avoid most often scratch registers. Find a better way ??? */
- (!current->used_as_hint ||
- !ZEND_REGSET_IN(ZEND_REGSET_LOW_PRIORITY, it->reg))) {
- hint = it->reg;
- }
- } else {
- freeUntilPos[it->reg] = 0;
- }
- it = it->list_next;
- }
- } else {
- while (it) {
- freeUntilPos[it->reg] = 0;
- it = it->list_next;
- }
- }
- if (current->hint) {
- hint = current->hint->reg;
- if (hint != ZREG_NONE && current->hint->used_as_hint == current) {
- ZEND_REGSET_EXCL(*hints, hint);
- }
- }
- if (hint == ZREG_NONE && ZEND_REGSET_IS_EMPTY(available)) {
- return 0;
- }
- /* See "Linear Scan Register Allocation on SSA Form", Christian Wimmer and
- Michael Franz, CGO'10 (2010), Figure 6. */
- if (current->flags & ZREG_SPLIT) {
- /* for each interval it in inactive intersecting with current do */
- /* freeUntilPos[it.reg] = next intersection of it with current */
- it = inactive;
- while (it) {
- uint32_t next = zend_interval_intersection(current, it);
- //ZEND_ASSERT(next != 0xffffffff && !current->split);
- if (next < freeUntilPos[it->reg]) {
- freeUntilPos[it->reg] = next;
- }
- it = it->list_next;
- }
- }
- /* Handle Scratch Registers */
- /* TODO: Optimize ??? */
- range = ¤t->range;
- do {
- uint32_t line = range->start;
- uint32_t last_use_line = (uint32_t)-1;
- zend_regset regset;
- zend_reg reg;
- if ((current->flags & ZREG_LAST_USE) && !range->next) {
- last_use_line = range->end;
- }
- if (ssa->ops[line].op1_def == current->ssa_var ||
- ssa->ops[line].op2_def == current->ssa_var ||
- ssa->ops[line].result_def == current->ssa_var) {
- regset = zend_jit_get_def_scratch_regset(
- ssa_opcodes ? ssa_opcodes[line] : op_array->opcodes + line,
- ssa->ops + line,
- op_array, ssa, current->ssa_var, line == last_use_line);
- ZEND_REGSET_FOREACH(regset, reg) {
- if (line < freeUntilPos[reg]) {
- freeUntilPos[reg] = line;
- }
- } ZEND_REGSET_FOREACH_END();
- line++;
- }
- while (line <= range->end) {
- regset = zend_jit_get_scratch_regset(
- ssa_opcodes ? ssa_opcodes[line] : op_array->opcodes + line,
- ssa->ops + line,
- op_array, ssa, current->ssa_var, line == last_use_line);
- ZEND_REGSET_FOREACH(regset, reg) {
- if (line < freeUntilPos[reg]) {
- freeUntilPos[reg] = line;
- }
- } ZEND_REGSET_FOREACH_END();
- line++;
- }
- range = range->next;
- } while (range);
- #if 0
- /* Coalescing */
- if (ssa->vars[current->ssa_var].definition == current->start) {
- zend_op *opline = op_array->opcodes + current->start;
- int hint = -1;
- switch (opline->opcode) {
- case ZEND_ASSIGN:
- hint = ssa->ops[current->start].op2_use;
- case ZEND_QM_ASSIGN:
- hint = ssa->ops[current->start].op1_use;
- break;
- case ZEND_ADD:
- case ZEND_SUB:
- case ZEND_MUL:
- hint = ssa->ops[current->start].op1_use;
- break;
- case ZEND_ASSIGN_OP:
- if (opline->extended_value == ZEND_ADD
- || opline->extended_value == ZEND_SUB
- || opline->extended_value == ZEND_MUL) {
- hint = ssa->ops[current->start].op1_use;
- }
- break;
- }
- if (hint >= 0) {
- }
- }
- #endif
- if (hint != ZREG_NONE && freeUntilPos[hint] > zend_interval_end(current)) {
- current->reg = hint;
- if (current->used_as_hint) {
- ZEND_REGSET_INCL(*hints, hint);
- }
- return 1;
- }
- if (ZEND_REGSET_IS_EMPTY(available)) {
- return 0;
- }
- pos = 0; reg = ZREG_NONE;
- pos2 = 0; reg2 = ZREG_NONE;
- low_priority_regs = *hints;
- if (current->used_as_hint) {
- /* TODO: Avoid most often scratch registers. Find a better way ??? */
- low_priority_regs = ZEND_REGSET_UNION(low_priority_regs, ZEND_REGSET_LOW_PRIORITY);
- }
- ZEND_REGSET_FOREACH(available, i) {
- if (ZEND_REGSET_IN(low_priority_regs, i)) {
- if (freeUntilPos[i] > pos2) {
- reg2 = i;
- pos2 = freeUntilPos[i];
- }
- } else if (freeUntilPos[i] > pos) {
- reg = i;
- pos = freeUntilPos[i];
- }
- } ZEND_REGSET_FOREACH_END();
- if (reg == ZREG_NONE) {
- if (reg2 != ZREG_NONE) {
- reg = reg2;
- pos = pos2;
- reg2 = ZREG_NONE;
- }
- }
- if (reg == ZREG_NONE) {
- /* no register available without spilling */
- return 0;
- } else if (zend_interval_end(current) < pos) {
- /* register available for the whole interval */
- current->reg = reg;
- if (current->used_as_hint) {
- ZEND_REGSET_INCL(*hints, reg);
- }
- return 1;
- #if 0
- // TODO: allow low priority register usage
- } else if (reg2 != ZREG_NONE && zend_interval_end(current) < pos2) {
- /* register available for the whole interval */
- current->reg = reg2;
- if (current->used_as_hint) {
- ZEND_REGSET_INCL(*hints, reg2);
- }
- return 1;
- #endif
- } else {
- /* TODO: enable interval splitting ??? */
- /* register available for the first part of the interval */
- if (1 || zend_jit_split_interval(current, pos, list, free) != SUCCESS) {
- return 0;
- }
- current->reg = reg;
- if (current->used_as_hint) {
- ZEND_REGSET_INCL(*hints, reg);
- }
- return 1;
- }
- }
- /* See "Optimized Interval Splitting in a Linear Scan Register Allocator",
- Christian Wimmer VEE'05 (2005), Figure 5. Allocation with spilling.
- and "Linear Scan Register Allocation on SSA Form", Christian Wimmer and
- Michael Franz, CGO'10 (2010), Figure 6. */
- static int zend_jit_allocate_blocked_reg(void)
- {
- /* TODO: ??? */
- return 0;
- }
- /* See "Optimized Interval Splitting in a Linear Scan Register Allocator",
- Christian Wimmer VEE'10 (2005), Figure 2. */
- static zend_lifetime_interval* zend_jit_linear_scan(const zend_op_array *op_array, const zend_op **ssa_opcodes, zend_ssa *ssa, zend_lifetime_interval *list)
- {
- zend_lifetime_interval *unhandled, *active, *inactive, *handled, *free;
- zend_lifetime_interval *current, **p, *q;
- uint32_t position;
- zend_regset available = ZEND_REGSET_UNION(ZEND_REGSET_GP, ZEND_REGSET_FP);
- zend_regset hints = ZEND_REGSET_EMPTY;
- unhandled = list;
- /* active = inactive = handled = free = {} */
- active = inactive = handled = free = NULL;
- while (unhandled != NULL) {
- current = unhandled;
- unhandled = unhandled->list_next;
- position = current->range.start;
- p = &active;
- while (*p) {
- uint32_t end = zend_interval_end(*p);
- q = *p;
- if (end < position) {
- /* move ival from active to handled */
- ZEND_REGSET_INCL(available, q->reg);
- *p = q->list_next;
- q->list_next = handled;
- handled = q;
- } else if (!zend_interval_covers(q, position)) {
- /* move ival from active to inactive */
- ZEND_REGSET_INCL(available, q->reg);
- *p = q->list_next;
- q->list_next = inactive;
- inactive = q;
- } else {
- p = &q->list_next;
- }
- }
- p = &inactive;
- while (*p) {
- uint32_t end = zend_interval_end(*p);
- q = *p;
- if (end < position) {
- /* move ival from inactive to handled */
- *p = q->list_next;
- q->list_next = handled;
- handled = q;
- } else if (zend_interval_covers(q, position)) {
- /* move ival from inactive to active */
- ZEND_REGSET_EXCL(available, q->reg);
- *p = q->list_next;
- q->list_next = active;
- active = q;
- } else {
- p = &q->list_next;
- }
- }
- if (zend_jit_try_allocate_free_reg(op_array, ssa_opcodes, ssa, current, available, &hints, active, inactive, &unhandled, &free) ||
- zend_jit_allocate_blocked_reg()) {
- ZEND_REGSET_EXCL(available, current->reg);
- current->list_next = active;
- active = current;
- } else {
- current->list_next = free;
- free = current;
- }
- }
- /* move active to handled */
- while (active) {
- current = active;
- active = active->list_next;
- current->list_next = handled;
- handled = current;
- }
- /* move inactive to handled */
- while (inactive) {
- current = inactive;
- inactive = inactive->list_next;
- current->list_next = handled;
- handled = current;
- }
- return handled;
- }
- static void zend_jit_dump_lifetime_interval(const zend_op_array *op_array, const zend_ssa *ssa, const zend_lifetime_interval *ival)
- {
- zend_life_range *range;
- int var_num = ssa->vars[ival->ssa_var].var;
- fprintf(stderr, "#%d.", ival->ssa_var);
- zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : 0), var_num);
- fprintf(stderr, ": %u-%u", ival->range.start, ival->range.end);
- range = ival->range.next;
- while (range) {
- fprintf(stderr, ", %u-%u", range->start, range->end);
- range = range->next;
- }
- if (ival->reg != ZREG_NONE) {
- fprintf(stderr, " (%s)", zend_reg_name[ival->reg]);
- }
- if (ival->flags & ZREG_LAST_USE) {
- fprintf(stderr, " last_use");
- }
- if (ival->flags & ZREG_LOAD) {
- fprintf(stderr, " load");
- }
- if (ival->flags & ZREG_STORE) {
- fprintf(stderr, " store");
- }
- if (ival->hint) {
- fprintf(stderr, " hint");
- if (ival->hint->ssa_var >= 0) {
- var_num = ssa->vars[ival->hint->ssa_var].var;
- fprintf(stderr, "=#%d.", ival->hint->ssa_var);
- zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : 0), var_num);
- }
- if (ival->hint->reg != ZREG_NONE) {
- fprintf(stderr, " (%s)", zend_reg_name[ival->hint->reg]);
- }
- }
- fprintf(stderr, "\n");
- }
- static zend_lifetime_interval** zend_jit_allocate_registers(const zend_op_array *op_array, zend_ssa *ssa)
- {
- void *checkpoint;
- int set_size, candidates_count, i;
- zend_bitset candidates = NULL;
- zend_lifetime_interval *list, *ival;
- zend_lifetime_interval **intervals;
- ALLOCA_FLAG(use_heap);
- if (!ssa->var_info) {
- return NULL;
- }
- /* Identify SSA variables suitable for register allocation */
- set_size = zend_bitset_len(ssa->vars_count);
- candidates = ZEND_BITSET_ALLOCA(set_size, use_heap);
- if (!candidates) {
- return NULL;
- }
- candidates_count = 0;
- zend_bitset_clear(candidates, set_size);
- for (i = 0; i < ssa->vars_count; i++) {
- if (zend_jit_may_be_in_reg(op_array, ssa, i)) {
- zend_bitset_incl(candidates, i);
- candidates_count++;
- }
- }
- if (!candidates_count) {
- free_alloca(candidates, use_heap);
- return NULL;
- }
- checkpoint = zend_arena_checkpoint(CG(arena));
- /* Find life-time intervals */
- if (zend_jit_compute_liveness(op_array, ssa, candidates, &list) != SUCCESS) {
- goto failure;
- }
- if (list) {
- /* Set ZREG_LAST_USE flags */
- ival = list;
- while (ival) {
- zend_life_range *range = &ival->range;
- while (range->next) {
- range = range->next;
- }
- if (zend_ssa_is_last_use(op_array, ssa, ival->ssa_var, range->end)) {
- ival->flags |= ZREG_LAST_USE;
- }
- ival = ival->list_next;
- }
- }
- if (list) {
- if (JIT_G(debug) & ZEND_JIT_DEBUG_REG_ALLOC) {
- fprintf(stderr, "Live Ranges \"%s\"\n", op_array->function_name ? ZSTR_VAL(op_array->function_name) : "[main]");
- ival = list;
- while (ival) {
- zend_jit_dump_lifetime_interval(op_array, ssa, ival);
- ival = ival->list_next;
- }
- fprintf(stderr, "\n");
- }
- /* Linear Scan Register Allocation */
- list = zend_jit_linear_scan(op_array, NULL, ssa, list);
- if (list) {
- intervals = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_lifetime_interval*));
- if (!intervals) {
- goto failure;
- }
- ival = list;
- while (ival != NULL) {
- zend_lifetime_interval *next = ival->list_next;
- ival->list_next = intervals[ival->ssa_var];
- intervals[ival->ssa_var] = ival;
- ival = next;
- }
- if (JIT_G(opt_flags) & ZEND_JIT_REG_ALLOC_GLOBAL) {
- /* Naive SSA resolution */
- for (i = 0; i < ssa->vars_count; i++) {
- if (ssa->vars[i].definition_phi && !ssa->vars[i].no_val) {
- zend_ssa_phi *phi = ssa->vars[i].definition_phi;
- int k, src;
- if (phi->pi >= 0) {
- if (!ssa->vars[i].phi_use_chain
- || ssa->vars[i].phi_use_chain->block != phi->block) {
- src = phi->sources[0];
- if (intervals[i]) {
- if (!intervals[src]) {
- intervals[i]->flags |= ZREG_LOAD;
- } else if (intervals[i]->reg != intervals[src]->reg) {
- intervals[i]->flags |= ZREG_LOAD;
- intervals[src]->flags |= ZREG_STORE;
- }
- } else if (intervals[src]) {
- intervals[src]->flags |= ZREG_STORE;
- }
- }
- } else {
- int need_move = 0;
- for (k = 0; k < ssa->cfg.blocks[phi->block].predecessors_count; k++) {
- src = phi->sources[k];
- if (src >= 0) {
- if (ssa->vars[src].definition_phi
- && ssa->vars[src].definition_phi->pi >= 0
- && phi->block == ssa->vars[src].definition_phi->block) {
- /* Skip zero-length interval for Pi variable */
- src = ssa->vars[src].definition_phi->sources[0];
- }
- if (intervals[i]) {
- if (!intervals[src]) {
- need_move = 1;
- } else if (intervals[i]->reg != intervals[src]->reg) {
- need_move = 1;
- }
- } else if (intervals[src]) {
- need_move = 1;
- }
- }
- }
- if (need_move) {
- if (intervals[i]) {
- intervals[i]->flags |= ZREG_LOAD;
- }
- for (k = 0; k < ssa->cfg.blocks[phi->block].predecessors_count; k++) {
- src = phi->sources[k];
- if (src >= 0) {
- if (ssa->vars[src].definition_phi
- && ssa->vars[src].definition_phi->pi >= 0
- && phi->block == ssa->vars[src].definition_phi->block) {
- /* Skip zero-length interval for Pi variable */
- src = ssa->vars[src].definition_phi->sources[0];
- }
- if (intervals[src]) {
- intervals[src]->flags |= ZREG_STORE;
- }
- }
- }
- }
- }
- }
- }
- /* Remove useless register allocation */
- for (i = 0; i < ssa->vars_count; i++) {
- if (intervals[i] &&
- ((intervals[i]->flags & ZREG_LOAD) ||
- ((intervals[i]->flags & ZREG_STORE) && ssa->vars[i].definition >= 0)) &&
- ssa->vars[i].use_chain < 0) {
- bool may_remove = 1;
- zend_ssa_phi *phi = ssa->vars[i].phi_use_chain;
- while (phi) {
- if (intervals[phi->ssa_var] &&
- !(intervals[phi->ssa_var]->flags & ZREG_LOAD)) {
- may_remove = 0;
- break;
- }
- phi = zend_ssa_next_use_phi(ssa, i, phi);
- }
- if (may_remove) {
- intervals[i] = NULL;
- }
- }
- }
- /* Remove intervals used once */
- for (i = 0; i < ssa->vars_count; i++) {
- if (intervals[i] &&
- (intervals[i]->flags & ZREG_LOAD) &&
- (intervals[i]->flags & ZREG_STORE) &&
- (ssa->vars[i].use_chain < 0 ||
- zend_ssa_next_use(ssa->ops, i, ssa->vars[i].use_chain) < 0)) {
- bool may_remove = 1;
- zend_ssa_phi *phi = ssa->vars[i].phi_use_chain;
- while (phi) {
- if (intervals[phi->ssa_var] &&
- !(intervals[phi->ssa_var]->flags & ZREG_LOAD)) {
- may_remove = 0;
- break;
- }
- phi = zend_ssa_next_use_phi(ssa, i, phi);
- }
- if (may_remove) {
- intervals[i] = NULL;
- }
- }
- }
- }
- if (JIT_G(debug) & ZEND_JIT_DEBUG_REG_ALLOC) {
- fprintf(stderr, "Allocated Live Ranges \"%s\"\n", op_array->function_name ? ZSTR_VAL(op_array->function_name) : "[main]");
- for (i = 0; i < ssa->vars_count; i++) {
- ival = intervals[i];
- while (ival) {
- zend_jit_dump_lifetime_interval(op_array, ssa, ival);
- ival = ival->list_next;
- }
- }
- fprintf(stderr, "\n");
- }
- free_alloca(candidates, use_heap);
- return intervals;
- }
- }
- failure:
- zend_arena_release(&CG(arena), checkpoint);
- free_alloca(candidates, use_heap);
- return NULL;
- }
- static bool zend_jit_next_is_send_result(const zend_op *opline)
- {
- if (opline->result_type == IS_TMP_VAR
- && (opline+1)->opcode == ZEND_SEND_VAL
- && (opline+1)->op1_type == IS_TMP_VAR
- && (opline+1)->op2_type != IS_CONST
- && (opline+1)->op1.var == opline->result.var) {
- return 1;
- }
- return 0;
- }
- static bool zend_jit_supported_binary_op(zend_uchar op, uint32_t op1_info, uint32_t op2_info)
- {
- if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) {
- return false;
- }
- switch (op) {
- case ZEND_POW:
- case ZEND_DIV:
- // TODO: check for division by zero ???
- return false;
- case ZEND_ADD:
- case ZEND_SUB:
- case ZEND_MUL:
- return (op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE))
- && (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE));
- case ZEND_BW_OR:
- case ZEND_BW_AND:
- case ZEND_BW_XOR:
- case ZEND_SL:
- case ZEND_SR:
- case ZEND_MOD:
- return (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG);
- case ZEND_CONCAT:
- return (op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING);
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- }
- static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op *rt_opline)
- {
- int b, i, end;
- zend_op *opline;
- dasm_State* dasm_state = NULL;
- void *handler;
- int call_level = 0;
- void *checkpoint = NULL;
- zend_lifetime_interval **ra = NULL;
- bool is_terminated = 1; /* previous basic block is terminated by jump */
- bool recv_emitted = 0; /* emitted at least one RECV opcode */
- zend_uchar smart_branch_opcode;
- uint32_t target_label, target_label2;
- uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info;
- 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;
- if (JIT_G(bisect_limit)) {
- jit_bisect_pos++;
- if (jit_bisect_pos >= JIT_G(bisect_limit)) {
- if (jit_bisect_pos == JIT_G(bisect_limit)) {
- fprintf(stderr, "Not JITing %s%s%s in %s:%d and after due to jit_bisect_limit\n",
- 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), op_array->line_start);
- }
- return FAILURE;
- }
- }
- if (JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) {
- checkpoint = zend_arena_checkpoint(CG(arena));
- ra = zend_jit_allocate_registers(op_array, ssa);
- }
- /* mark hidden branch targets */
- for (b = 0; b < ssa->cfg.blocks_count; b++) {
- if (ssa->cfg.blocks[b].flags & ZEND_BB_REACHABLE &&
- ssa->cfg.blocks[b].len > 1) {
- opline = op_array->opcodes + ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len - 1;
- if (opline->opcode == ZEND_DO_FCALL &&
- (opline-1)->opcode == ZEND_NEW) {
- ssa->cfg.blocks[ssa->cfg.blocks[b].successors[0]].flags |= ZEND_BB_TARGET;
- }
- }
- }
- dasm_init(&dasm_state, DASM_MAXSECTION);
- dasm_setupglobal(&dasm_state, dasm_labels, zend_lb_MAX);
- dasm_setup(&dasm_state, dasm_actions);
- dasm_growpc(&dasm_state, ssa->cfg.blocks_count * 2 + 1);
- zend_jit_align_func(&dasm_state);
- for (b = 0; b < ssa->cfg.blocks_count; b++) {
- if ((ssa->cfg.blocks[b].flags & ZEND_BB_REACHABLE) == 0) {
- continue;
- }
- //#ifndef CONTEXT_THREADED_JIT
- if (ssa->cfg.blocks[b].flags & ZEND_BB_ENTRY) {
- if (ssa->cfg.blocks[b].flags & ZEND_BB_TARGET) {
- /* pass */
- } else if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE &&
- ssa->cfg.blocks[b].len == 1 &&
- (ssa->cfg.blocks[b].flags & ZEND_BB_EXIT) &&
- op_array->opcodes[ssa->cfg.blocks[b].start].opcode != ZEND_JMP) {
- /* don't generate code for BB with single opcode */
- continue;
- }
- if (ssa->cfg.blocks[b].flags & ZEND_BB_FOLLOW) {
- if (!is_terminated) {
- zend_jit_jmp(&dasm_state, b);
- }
- }
- zend_jit_label(&dasm_state, ssa->cfg.blocks_count + b);
- zend_jit_prologue(&dasm_state);
- } else
- //#endif
- if (ssa->cfg.blocks[b].flags & (ZEND_BB_START|ZEND_BB_RECV_ENTRY)) {
- opline = op_array->opcodes + ssa->cfg.blocks[b].start;
- if (ssa->cfg.flags & ZEND_CFG_RECV_ENTRY) {
- if (opline->opcode == ZEND_RECV_INIT) {
- if (opline == op_array->opcodes ||
- (opline-1)->opcode != ZEND_RECV_INIT) {
- if (recv_emitted) {
- zend_jit_jmp(&dasm_state, b);
- }
- zend_jit_label(&dasm_state, ssa->cfg.blocks_count + b);
- for (i = 1; (opline+i)->opcode == ZEND_RECV_INIT; i++) {
- zend_jit_label(&dasm_state, ssa->cfg.blocks_count + b + i);
- }
- zend_jit_prologue(&dasm_state);
- }
- recv_emitted = 1;
- } else if (opline->opcode == ZEND_RECV) {
- if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
- /* skip */
- continue;
- } else if (recv_emitted) {
- zend_jit_jmp(&dasm_state, b);
- zend_jit_label(&dasm_state, ssa->cfg.blocks_count + b);
- zend_jit_prologue(&dasm_state);
- } else {
- zend_arg_info *arg_info;
- if (opline->op1.num <= op_array->num_args) {
- arg_info = &op_array->arg_info[opline->op1.num - 1];
- } else if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
- arg_info = &op_array->arg_info[op_array->num_args];
- } else {
- /* skip */
- continue;
- }
- if (!ZEND_TYPE_IS_SET(arg_info->type)) {
- /* skip */
- continue;
- }
- zend_jit_label(&dasm_state, ssa->cfg.blocks_count + b);
- zend_jit_prologue(&dasm_state);
- recv_emitted = 1;
- }
- } else {
- if (recv_emitted) {
- zend_jit_jmp(&dasm_state, b);
- } else if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE &&
- ssa->cfg.blocks[b].len == 1 &&
- (ssa->cfg.blocks[b].flags & ZEND_BB_EXIT)) {
- /* don't generate code for BB with single opcode */
- dasm_free(&dasm_state);
- if (JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) {
- zend_arena_release(&CG(arena), checkpoint);
- }
- return SUCCESS;
- }
- zend_jit_label(&dasm_state, ssa->cfg.blocks_count + b);
- zend_jit_prologue(&dasm_state);
- recv_emitted = 1;
- }
- } else if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE &&
- ssa->cfg.blocks[b].len == 1 &&
- (ssa->cfg.blocks[b].flags & ZEND_BB_EXIT)) {
- /* don't generate code for BB with single opcode */
- dasm_free(&dasm_state);
- if (JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) {
- zend_arena_release(&CG(arena), checkpoint);
- }
- return SUCCESS;
- } else {
- zend_jit_label(&dasm_state, ssa->cfg.blocks_count + b);
- zend_jit_prologue(&dasm_state);
- }
- }
- is_terminated = 0;
- zend_jit_label(&dasm_state, b);
- if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE) {
- if ((ssa->cfg.blocks[b].flags & ZEND_BB_FOLLOW)
- && ssa->cfg.blocks[b].start != 0
- && (op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_NOP
- || op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_SWITCH_LONG
- || op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_SWITCH_STRING
- || op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_MATCH)) {
- zend_jit_reset_last_valid_opline();
- if (!zend_jit_set_ip(&dasm_state, op_array->opcodes + ssa->cfg.blocks[b].start)) {
- goto jit_failure;
- }
- } else {
- zend_jit_set_last_valid_opline(op_array->opcodes + ssa->cfg.blocks[b].start);
- }
- } else if (ssa->cfg.blocks[b].flags & ZEND_BB_TARGET) {
- zend_jit_reset_last_valid_opline();
- } else if (ssa->cfg.blocks[b].flags & (ZEND_BB_START|ZEND_BB_RECV_ENTRY|ZEND_BB_ENTRY)) {
- zend_jit_set_last_valid_opline(op_array->opcodes + ssa->cfg.blocks[b].start);
- }
- if (ssa->cfg.blocks[b].flags & ZEND_BB_LOOP_HEADER) {
- if (!zend_jit_check_timeout(&dasm_state, op_array->opcodes + ssa->cfg.blocks[b].start, NULL)) {
- goto jit_failure;
- }
- }
- if (!ssa->cfg.blocks[b].len) {
- continue;
- }
- if ((JIT_G(opt_flags) & ZEND_JIT_REG_ALLOC_GLOBAL) && ra) {
- zend_ssa_phi *phi = ssa->blocks[b].phis;
- while (phi) {
- zend_lifetime_interval *ival = ra[phi->ssa_var];
- if (ival) {
- if (ival->flags & ZREG_LOAD) {
- ZEND_ASSERT(ival->reg != ZREG_NONE);
- 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);
- if (!zend_jit_store_var(&dasm_state, ssa->var_info[phi->ssa_var].type, ssa->vars[phi->ssa_var].var, ival->reg, 1)) {
- goto jit_failure;
- }
- }
- }
- phi = phi->next;
- }
- }
- end = ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len - 1;
- for (i = ssa->cfg.blocks[b].start; i <= end; i++) {
- zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[i] : NULL;
- opline = op_array->opcodes + i;
- 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:
- call_level++;
- }
- if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_INLINE) {
- 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();
- if (!(op1_info & MAY_BE_LONG)) {
- break;
- }
- if (opline->result_type != IS_UNUSED) {
- res_use_info = -1;
- if (opline->result_type == IS_CV
- && ssa->vars
- && ssa_op->result_use >= 0
- && !ssa->vars[ssa_op->result_use].no_val) {
- zend_jit_addr res_use_addr = RES_USE_REG_ADDR();
- if (Z_MODE(res_use_addr) != IS_REG
- || Z_LOAD(res_use_addr)
- || Z_STORE(res_use_addr)) {
- res_use_info = RES_USE_INFO();
- }
- }
- 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 (!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_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, ssa_op, op_array, ssa),
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- goto done;
- case ZEND_BW_OR:
- case ZEND_BW_AND:
- case ZEND_BW_XOR:
- case ZEND_SL:
- case ZEND_SR:
- case ZEND_MOD:
- if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
- break;
- }
- op1_info = OP1_INFO();
- op2_info = OP2_INFO();
- 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
- && (i + 1) <= end
- && zend_jit_next_is_send_result(opline)) {
- i++;
- 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 = -1;
- if (opline->result_type == IS_CV
- && ssa->vars
- && ssa_op->result_use >= 0
- && !ssa->vars[ssa_op->result_use].no_val) {
- zend_jit_addr res_use_addr = RES_USE_REG_ADDR();
- if (Z_MODE(res_use_addr) != IS_REG
- || Z_LOAD(res_use_addr)
- || Z_STORE(res_use_addr)) {
- res_use_info = RES_USE_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 ???
- if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
- break;
- }
- op1_info = OP1_INFO();
- op2_info = OP2_INFO();
- 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
- && (i + 1) <= end
- && zend_jit_next_is_send_result(opline)) {
- i++;
- 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 = -1;
- if (opline->result_type == IS_CV
- && ssa->vars
- && ssa_op->result_use >= 0
- && !ssa->vars[ssa_op->result_use].no_val) {
- zend_jit_addr res_use_addr = RES_USE_REG_ADDR();
- if (Z_MODE(res_use_addr) != IS_REG
- || Z_LOAD(res_use_addr)
- || Z_STORE(res_use_addr)) {
- res_use_info = RES_USE_INFO();
- }
- }
- }
- 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_REG_ADDR(), op2_info, OP2_REG_ADDR(), res_addr)) {
- goto jit_failure;
- }
- } else {
- if (!zend_jit_math(&dasm_state, opline,
- op1_info, OP1_REG_ADDR(),
- op2_info, OP2_REG_ADDR(),
- res_use_info, res_info, res_addr,
- (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, ssa_op, op_array, ssa),
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- }
- goto done;
- case ZEND_CONCAT:
- case ZEND_FAST_CONCAT:
- if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
- break;
- }
- op1_info = OP1_INFO();
- op2_info = OP2_INFO();
- 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 ((i + 1) <= end
- && zend_jit_next_is_send_result(opline)) {
- i++;
- 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;
- }
- if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
- break;
- }
- op1_info = OP1_INFO();
- op2_info = OP2_INFO();
- if (!zend_jit_supported_binary_op(
- opline->extended_value, op1_info, op2_info)) {
- break;
- }
- op1_def_info = OP1_DEF_INFO();
- 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) && zend_may_overflow(opline, ssa_op, op_array, ssa),
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- goto done;
- case ZEND_ASSIGN_DIM_OP:
- if (opline->op1_type != IS_CV || opline->result_type != IS_UNUSED) {
- break;
- }
- if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
- break;
- }
- if (!zend_jit_supported_binary_op(
- opline->extended_value, MAY_BE_ANY, OP1_DATA_INFO())) {
- break;
- }
- if (!zend_jit_assign_dim_op(&dasm_state, opline,
- OP1_INFO(), OP1_DEF_INFO(), OP1_REG_ADDR(), OP2_INFO(),
- OP1_DATA_INFO(), OP1_DATA_RANGE(), IS_UNKNOWN,
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- goto done;
- case ZEND_ASSIGN_DIM:
- if (opline->op1_type != IS_CV) {
- break;
- }
- if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
- break;
- }
- if (!zend_jit_assign_dim(&dasm_state, opline,
- OP1_INFO(), OP1_REG_ADDR(), OP2_INFO(), OP1_DATA_INFO(), IS_UNKNOWN,
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- 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;
- }
- if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
- break;
- }
- ce = NULL;
- ce_is_instanceof = 0;
- on_this = 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 (ssa->var_info && ssa->ops) {
- zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
- 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 (!zend_jit_incdec_obj(&dasm_state, opline, op_array, ssa, ssa_op,
- op1_info, op1_addr,
- 0, ce, ce_is_instanceof, on_this, 0, NULL, IS_UNKNOWN)) {
- 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 (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
- 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 = 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 (ssa->var_info && ssa->ops) {
- zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
- 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 (!zend_jit_assign_obj_op(&dasm_state, opline, op_array, ssa, ssa_op,
- op1_info, op1_addr, OP1_DATA_INFO(), OP1_DATA_RANGE(),
- 0, ce, ce_is_instanceof, on_this, 0, NULL, IS_UNKNOWN)) {
- 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;
- }
- if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
- break;
- }
- ce = NULL;
- ce_is_instanceof = 0;
- on_this = 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 (ssa->var_info && ssa->ops) {
- zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
- 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 (!zend_jit_assign_obj(&dasm_state, opline, op_array, ssa, ssa_op,
- op1_info, op1_addr, OP1_DATA_INFO(),
- 0, ce, ce_is_instanceof, on_this, 0, NULL, IS_UNKNOWN,
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- goto done;
- case ZEND_ASSIGN:
- if (opline->op1_type != IS_CV) {
- break;
- }
- if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
- break;
- }
- op2_addr = OP2_REG_ADDR();
- if (ra
- && ssa->ops[opline - op_array->opcodes].op2_def >= 0
- && !ssa->vars[ssa->ops[opline - op_array->opcodes].op2_def].no_val) {
- op2_def_addr = OP2_DEF_REG_ADDR();
- } else {
- op2_def_addr = op2_addr;
- }
- op1_info = OP1_INFO();
- if (ra && ssa->vars[ssa_op->op1_use].no_val) {
- op1_info |= MAY_BE_UNDEF; // requres type assignment
- }
- 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
- && (i + 1) <= end
- && zend_jit_next_is_send_result(opline)
- && (!(op1_info & MAY_HAVE_DTOR) || !(op1_info & MAY_BE_RC1))) {
- i++;
- 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_REG_ADDR(),
- OP1_DEF_INFO(), OP1_DEF_REG_ADDR(),
- OP2_INFO(), op2_addr, op2_def_addr,
- res_info, res_addr,
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- goto done;
- case ZEND_QM_ASSIGN:
- op1_addr = OP1_REG_ADDR();
- if (ra
- && ssa->ops[opline - op_array->opcodes].op1_def >= 0
- && !ssa->vars[ssa->ops[opline - op_array->opcodes].op1_def].no_val) {
- op1_def_addr = OP1_DEF_REG_ADDR();
- } else {
- op1_def_addr = op1_addr;
- }
- if (!zend_jit_qm_assign(&dasm_state, opline,
- OP1_INFO(), op1_addr, op1_def_addr,
- -1, RES_INFO(), RES_REG_ADDR())) {
- goto jit_failure;
- }
- goto done;
- case ZEND_INIT_FCALL:
- case ZEND_INIT_FCALL_BY_NAME:
- case ZEND_INIT_NS_FCALL_BY_NAME:
- if (!zend_jit_init_fcall(&dasm_state, opline, b, op_array, ssa, ssa_op, call_level, NULL, 0)) {
- 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 (yet) */
- break;
- }
- if (opline->opcode == ZEND_SEND_VAL_EX
- && opline->op2.num > MAX_ARG_FLAG_NUM) {
- break;
- }
- if (!zend_jit_send_val(&dasm_state, opline,
- OP1_INFO(), OP1_REG_ADDR())) {
- goto jit_failure;
- }
- goto done;
- case ZEND_SEND_REF:
- if (opline->op2_type == IS_CONST) {
- /* Named parameters not supported in JIT (yet) */
- break;
- }
- if (!zend_jit_send_ref(&dasm_state, opline, op_array,
- OP1_INFO(), 0)) {
- goto jit_failure;
- }
- 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 (yet) */
- 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->ops[opline - op_array->opcodes].op1_def >= 0
- && !ssa->vars[ssa->ops[opline - op_array->opcodes].op1_def].no_val) {
- op1_def_addr = OP1_DEF_REG_ADDR();
- } else {
- op1_def_addr = op1_addr;
- }
- if (!zend_jit_send_var(&dasm_state, opline, op_array,
- OP1_INFO(), op1_addr, op1_def_addr)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_CHECK_FUNC_ARG:
- if (opline->op2_type == IS_CONST) {
- /* Named parameters not supported in JIT (yet) */
- break;
- }
- if (opline->op2.num > MAX_ARG_FLAG_NUM) {
- break;
- }
- if (!zend_jit_check_func_arg(&dasm_state, opline)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_CHECK_UNDEF_ARGS:
- if (!zend_jit_check_undef_args(&dasm_state, opline)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_DO_UCALL:
- is_terminated = 1;
- ZEND_FALLTHROUGH;
- case ZEND_DO_ICALL:
- case ZEND_DO_FCALL_BY_NAME:
- case ZEND_DO_FCALL:
- if (!zend_jit_do_fcall(&dasm_state, opline, op_array, ssa, call_level, b + 1, NULL)) {
- 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: {
- res_addr = RES_REG_ADDR();
- if ((opline->result_type & IS_TMP_VAR)
- && (i + 1) <= end
- && ((opline+1)->opcode == ZEND_JMPZ
- || (opline+1)->opcode == ZEND_JMPNZ
- || (opline+1)->opcode == ZEND_JMPZ_EX
- || (opline+1)->opcode == ZEND_JMPNZ_EX
- || (opline+1)->opcode == ZEND_JMPZNZ)
- && (opline+1)->op1_type == IS_TMP_VAR
- && (opline+1)->op1.var == opline->result.var) {
- i++;
- smart_branch_opcode = (opline+1)->opcode;
- target_label = ssa->cfg.blocks[b].successors[0];
- target_label2 = ssa->cfg.blocks[b].successors[1];
- /* For EX variant write into the result of EX opcode. */
- if ((opline+1)->opcode == ZEND_JMPZ_EX
- || (opline+1)->opcode == ZEND_JMPNZ_EX) {
- res_addr = OP_REG_ADDR(opline + 1, result_type, result, result_def);
- }
- } else {
- smart_branch_opcode = 0;
- target_label = target_label2 = (uint32_t)-1;
- }
- if (!zend_jit_cmp(&dasm_state, opline,
- OP1_INFO(), OP1_RANGE(), OP1_REG_ADDR(),
- OP2_INFO(), OP2_RANGE(), OP2_REG_ADDR(),
- res_addr,
- zend_may_throw(opline, ssa_op, op_array, ssa),
- smart_branch_opcode, target_label, target_label2,
- NULL, 0)) {
- goto jit_failure;
- }
- goto done;
- }
- case ZEND_IS_IDENTICAL:
- case ZEND_IS_NOT_IDENTICAL:
- case ZEND_CASE_STRICT:
- if ((opline->result_type & IS_TMP_VAR)
- && (i + 1) <= end
- && ((opline+1)->opcode == ZEND_JMPZ
- || (opline+1)->opcode == ZEND_JMPNZ
- || (opline+1)->opcode == ZEND_JMPZNZ)
- && (opline+1)->op1_type == IS_TMP_VAR
- && (opline+1)->op1.var == opline->result.var) {
- i++;
- smart_branch_opcode = (opline+1)->opcode;
- target_label = ssa->cfg.blocks[b].successors[0];
- target_label2 = ssa->cfg.blocks[b].successors[1];
- } else {
- smart_branch_opcode = 0;
- target_label = target_label2 = (uint32_t)-1;
- }
- 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, target_label, target_label2,
- NULL, 0)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_DEFINED:
- if ((opline->result_type & IS_TMP_VAR)
- && (i + 1) <= end
- && ((opline+1)->opcode == ZEND_JMPZ
- || (opline+1)->opcode == ZEND_JMPNZ
- || (opline+1)->opcode == ZEND_JMPZNZ)
- && (opline+1)->op1_type == IS_TMP_VAR
- && (opline+1)->op1.var == opline->result.var) {
- i++;
- smart_branch_opcode = (opline+1)->opcode;
- target_label = ssa->cfg.blocks[b].successors[0];
- target_label2 = ssa->cfg.blocks[b].successors[1];
- } else {
- smart_branch_opcode = 0;
- target_label = target_label2 = (uint32_t)-1;
- }
- if (!zend_jit_defined(&dasm_state, opline, smart_branch_opcode, target_label, target_label2, NULL)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_TYPE_CHECK:
- if (opline->extended_value == MAY_BE_RESOURCE) {
- // TODO: support for is_resource() ???
- break;
- }
- if ((opline->result_type & IS_TMP_VAR)
- && (i + 1) <= end
- && ((opline+1)->opcode == ZEND_JMPZ
- || (opline+1)->opcode == ZEND_JMPNZ
- || (opline+1)->opcode == ZEND_JMPZNZ)
- && (opline+1)->op1_type == IS_TMP_VAR
- && (opline+1)->op1.var == opline->result.var) {
- i++;
- smart_branch_opcode = (opline+1)->opcode;
- target_label = ssa->cfg.blocks[b].successors[0];
- target_label2 = ssa->cfg.blocks[b].successors[1];
- } else {
- smart_branch_opcode = 0;
- target_label = target_label2 = (uint32_t)-1;
- }
- if (!zend_jit_type_check(&dasm_state, opline, OP1_INFO(), smart_branch_opcode, target_label, target_label2, NULL)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_RETURN:
- op1_info = OP1_INFO();
- if ((PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info))
- || 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_tail_handler(&dasm_state, opline)) {
- goto jit_failure;
- }
- } else {
- int j;
- bool left_frame = 0;
- if (!zend_jit_return(&dasm_state, opline, op_array,
- op1_info, OP1_REG_ADDR())) {
- goto jit_failure;
- }
- if (jit_return_label >= 0) {
- if (!zend_jit_jmp(&dasm_state, jit_return_label)) {
- goto jit_failure;
- }
- goto done;
- }
- jit_return_label = ssa->cfg.blocks_count * 2;
- if (!zend_jit_label(&dasm_state, jit_return_label)) {
- 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_ssa_cv_info(op_array, ssa, j);
- 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 (!zend_jit_leave_func(&dasm_state, op_array, opline, op1_info, left_frame,
- NULL, NULL, (ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0, 1)) {
- goto jit_failure;
- }
- }
- goto done;
- case ZEND_BOOL:
- case ZEND_BOOL_NOT:
- 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:
- if (opline > op_array->opcodes + ssa->cfg.blocks[b].start &&
- ((opline-1)->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- /* smart branch */
- if (!zend_jit_cond_jmp(&dasm_state, opline + 1, ssa->cfg.blocks[b].successors[0])) {
- goto jit_failure;
- }
- goto done;
- }
- ZEND_FALLTHROUGH;
- case ZEND_JMPZNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- 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,
- ssa->cfg.blocks[b].successors[0], ssa->cfg.blocks[b].successors[1],
- zend_may_throw(opline, ssa_op, op_array, ssa),
- opline->opcode, NULL)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_ISSET_ISEMPTY_CV:
- if ((opline->extended_value & ZEND_ISEMPTY)) {
- // TODO: support for empty() ???
- break;
- }
- if ((opline->result_type & IS_TMP_VAR)
- && (i + 1) <= end
- && ((opline+1)->opcode == ZEND_JMPZ
- || (opline+1)->opcode == ZEND_JMPNZ
- || (opline+1)->opcode == ZEND_JMPZNZ)
- && (opline+1)->op1_type == IS_TMP_VAR
- && (opline+1)->op1.var == opline->result.var) {
- i++;
- smart_branch_opcode = (opline+1)->opcode;
- target_label = ssa->cfg.blocks[b].successors[0];
- target_label2 = ssa->cfg.blocks[b].successors[1];
- } else {
- smart_branch_opcode = 0;
- target_label = target_label2 = (uint32_t)-1;
- }
- if (!zend_jit_isset_isempty_cv(&dasm_state, opline,
- OP1_INFO(), OP1_REG_ADDR(),
- smart_branch_opcode, target_label, target_label2,
- NULL)) {
- 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();
- if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != MAY_BE_STRING) {
- break;
- }
- if ((opline->result_type & IS_TMP_VAR)
- && (i + 1) <= end
- && ((opline+1)->opcode == ZEND_JMPZ
- || (opline+1)->opcode == ZEND_JMPNZ
- || (opline+1)->opcode == ZEND_JMPZNZ)
- && (opline+1)->op1_type == IS_TMP_VAR
- && (opline+1)->op1.var == opline->result.var) {
- i++;
- smart_branch_opcode = (opline+1)->opcode;
- target_label = ssa->cfg.blocks[b].successors[0];
- target_label2 = ssa->cfg.blocks[b].successors[1];
- } else {
- smart_branch_opcode = 0;
- target_label = target_label2 = (uint32_t)-1;
- }
- if (!zend_jit_in_array(&dasm_state, opline,
- op1_info, OP1_REG_ADDR(),
- smart_branch_opcode, target_label, target_label2,
- NULL)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_FETCH_DIM_R:
- case ZEND_FETCH_DIM_IS:
- case ZEND_FETCH_LIST_R:
- if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
- break;
- }
- if (!zend_jit_fetch_dim_read(&dasm_state, opline, ssa, ssa_op,
- OP1_INFO(), OP1_REG_ADDR(), 0,
- OP2_INFO(), RES_INFO(), RES_REG_ADDR(), IS_UNKNOWN)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_FETCH_DIM_W:
- case ZEND_FETCH_DIM_RW:
- // case ZEND_FETCH_DIM_UNSET:
- case ZEND_FETCH_LIST_W:
- if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
- break;
- }
- if (opline->op1_type != IS_CV) {
- break;
- }
- if (!zend_jit_fetch_dim(&dasm_state, opline,
- OP1_INFO(), OP1_REG_ADDR(), OP2_INFO(), RES_REG_ADDR(), IS_UNKNOWN)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_ISSET_ISEMPTY_DIM_OBJ:
- if ((opline->extended_value & ZEND_ISEMPTY)) {
- // TODO: support for empty() ???
- break;
- }
- if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
- break;
- }
- if ((opline->result_type & IS_TMP_VAR)
- && (i + 1) <= end
- && ((opline+1)->opcode == ZEND_JMPZ
- || (opline+1)->opcode == ZEND_JMPNZ
- || (opline+1)->opcode == ZEND_JMPZNZ)
- && (opline+1)->op1_type == IS_TMP_VAR
- && (opline+1)->op1.var == opline->result.var) {
- i++;
- smart_branch_opcode = (opline+1)->opcode;
- target_label = ssa->cfg.blocks[b].successors[0];
- target_label2 = ssa->cfg.blocks[b].successors[1];
- } else {
- smart_branch_opcode = 0;
- target_label = target_label2 = (uint32_t)-1;
- }
- if (!zend_jit_isset_isempty_dim(&dasm_state, opline,
- OP1_INFO(), OP1_REG_ADDR(), 0,
- OP2_INFO(), IS_UNKNOWN,
- zend_may_throw(opline, ssa_op, op_array, ssa),
- smart_branch_opcode, target_label, target_label2,
- NULL)) {
- goto jit_failure;
- }
- goto done;
- 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;
- }
- ce = NULL;
- ce_is_instanceof = 0;
- on_this = 0;
- if (opline->op1_type == IS_UNUSED) {
- op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
- op1_addr = 0;
- ce = op_array->scope;
- ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
- on_this = 1;
- } else {
- op1_info = OP1_INFO();
- if (!(op1_info & MAY_BE_OBJECT)) {
- break;
- }
- op1_addr = OP1_REG_ADDR();
- if (ssa->var_info && ssa->ops) {
- zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
- 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 (!zend_jit_fetch_obj(&dasm_state, opline, op_array, ssa, ssa_op,
- op1_info, op1_addr, 0, ce, ce_is_instanceof, on_this, 0, 0, NULL,
- IS_UNKNOWN,
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- goto done;
- case ZEND_BIND_GLOBAL:
- if (!ssa->ops || !ssa->var_info) {
- op1_info = MAY_BE_ANY|MAY_BE_REF;
- } else {
- op1_info = OP1_INFO();
- }
- if (!zend_jit_bind_global(&dasm_state, opline, op1_info)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_RECV:
- if (!zend_jit_recv(&dasm_state, opline, op_array)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_RECV_INIT:
- 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;
- }
- goto done;
- case ZEND_FREE:
- case ZEND_FE_FREE:
- 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();
- 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();
- 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_REG_ADDR(), RES_REG_ADDR())) {
- goto jit_failure;
- }
- goto done;
- case ZEND_COUNT:
- op1_info = OP1_INFO();
- 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_REG_ADDR(), RES_REG_ADDR(), zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- goto done;
- case ZEND_FETCH_THIS:
- if (!zend_jit_fetch_this(&dasm_state, opline, op_array, 0)) {
- 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, ssa, NULL, NULL)) {
- 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;
- }
- 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();
- 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();
- if ((op1_info & MAY_BE_ANY) != MAY_BE_ARRAY) {
- break;
- }
- if (!zend_jit_fe_fetch(&dasm_state, opline, op1_info, OP2_INFO(),
- ssa->cfg.blocks[b].successors[0], opline->opcode, NULL)) {
- 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) {
- break;
- }
- ce = NULL;
- ce_is_instanceof = 0;
- on_this = 0;
- if (opline->op1_type == IS_UNUSED) {
- op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
- op1_addr = 0;
- ce = op_array->scope;
- ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
- on_this = 1;
- } else {
- op1_info = OP1_INFO();
- if (!(op1_info & MAY_BE_OBJECT)) {
- break;
- }
- op1_addr = OP1_REG_ADDR();
- if (ssa->var_info && ssa->ops) {
- zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
- 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 (!zend_jit_init_method_call(&dasm_state, opline, b, op_array, ssa, ssa_op, call_level,
- op1_info, op1_addr, ce, ce_is_instanceof, on_this, 0, NULL,
- NULL, 0, 0)) {
- goto jit_failure;
- }
- goto done;
- case ZEND_ROPE_INIT:
- case ZEND_ROPE_ADD:
- case ZEND_ROPE_END:
- op2_info = OP2_INFO();
- 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;
- }
- }
- switch (opline->opcode) {
- case ZEND_RECV_INIT:
- case ZEND_BIND_GLOBAL:
- if (opline == op_array->opcodes ||
- opline->opcode != op_array->opcodes[i-1].opcode) {
- /* repeatable opcodes */
- if (!zend_jit_handler(&dasm_state, opline,
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- }
- zend_jit_set_last_valid_opline(opline+1);
- break;
- case ZEND_NOP:
- case ZEND_OP_DATA:
- case ZEND_SWITCH_LONG:
- case ZEND_SWITCH_STRING:
- case ZEND_MATCH:
- break;
- case ZEND_JMP:
- if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE) {
- const zend_op *target = OP_JMP_ADDR(opline, opline->op1);
- if (!zend_jit_set_ip(&dasm_state, target)) {
- goto jit_failure;
- }
- }
- if (!zend_jit_jmp(&dasm_state, ssa->cfg.blocks[b].successors[0])) {
- goto jit_failure;
- }
- is_terminated = 1;
- break;
- case ZEND_CATCH:
- case ZEND_FAST_CALL:
- case ZEND_FAST_RET:
- case ZEND_GENERATOR_CREATE:
- case ZEND_GENERATOR_RETURN:
- case ZEND_RETURN_BY_REF:
- case ZEND_RETURN:
- case ZEND_EXIT:
- case ZEND_MATCH_ERROR:
- /* switch through trampoline */
- case ZEND_YIELD:
- case ZEND_YIELD_FROM:
- if (!zend_jit_tail_handler(&dasm_state, opline)) {
- goto jit_failure;
- }
- is_terminated = 1;
- break;
- /* stackless execution */
- case ZEND_INCLUDE_OR_EVAL:
- case ZEND_DO_FCALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- if (!zend_jit_call(&dasm_state, opline, b + 1)) {
- goto jit_failure;
- }
- is_terminated = 1;
- break;
- case ZEND_JMPZNZ:
- if (!zend_jit_handler(&dasm_state, opline,
- zend_may_throw(opline, ssa_op, op_array, ssa)) ||
- !zend_jit_cond_jmp(&dasm_state, OP_JMP_ADDR(opline, opline->op2), ssa->cfg.blocks[b].successors[1]) ||
- !zend_jit_jmp(&dasm_state, ssa->cfg.blocks[b].successors[0])) {
- goto jit_failure;
- }
- is_terminated = 1;
- break;
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- if (opline > op_array->opcodes + ssa->cfg.blocks[b].start &&
- ((opline-1)->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- /* smart branch */
- if (!zend_jit_cond_jmp(&dasm_state, opline + 1, ssa->cfg.blocks[b].successors[0])) {
- goto jit_failure;
- }
- goto done;
- }
- ZEND_FALLTHROUGH;
- 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:
- if (!zend_jit_handler(&dasm_state, opline,
- zend_may_throw(opline, ssa_op, op_array, ssa)) ||
- !zend_jit_cond_jmp(&dasm_state, opline + 1, ssa->cfg.blocks[b].successors[0])) {
- goto jit_failure;
- }
- break;
- case ZEND_NEW:
- if (!zend_jit_handler(&dasm_state, opline, 1)) {
- return 0;
- }
- if (opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL) {
- zend_class_entry *ce = NULL;
- if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_OPT_FUNC) {
- if (ssa->ops && ssa->var_info) {
- zend_ssa_var_info *res_ssa = &ssa->var_info[ssa->ops[opline - op_array->opcodes].result_def];
- if (res_ssa->ce && !res_ssa->is_instanceof) {
- ce = res_ssa->ce;
- }
- }
- } else {
- if (opline->op1_type == IS_CONST) {
- zval *zv = RT_CONSTANT(opline, opline->op1);
- if (Z_TYPE_P(zv) == IS_STRING) {
- zval *lc = zv + 1;
- ce = (zend_class_entry*)zend_hash_find_ptr(EG(class_table), Z_STR_P(lc));
- }
- }
- }
- i++;
- if (!ce || !(ce->ce_flags & ZEND_ACC_LINKED) || ce->constructor) {
- const zend_op *next_opline = opline + 1;
- zend_jit_cond_jmp(&dasm_state, next_opline, ssa->cfg.blocks[b].successors[0]);
- if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE) {
- zend_jit_call(&dasm_state, next_opline, b + 1);
- is_terminated = 1;
- } else {
- zend_jit_do_fcall(&dasm_state, next_opline, op_array, ssa, call_level, b + 1, NULL);
- }
- }
- /* We skip over the DO_FCALL, so decrement call_level ourselves. */
- call_level--;
- }
- break;
- default:
- if (!zend_jit_handler(&dasm_state, opline,
- zend_may_throw(opline, ssa_op, op_array, ssa))) {
- goto jit_failure;
- }
- if (i == end
- && (opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
- /* smart branch split across basic blocks */
- if (!zend_jit_cond_jmp(&dasm_state, opline + 2, ssa->cfg.blocks[b+1].successors[0])) {
- goto jit_failure;
- }
- if (!zend_jit_jmp(&dasm_state, ssa->cfg.blocks[b+1].successors[1])) {
- goto jit_failure;
- }
- is_terminated = 1;
- }
- }
- done:
- switch (opline->opcode) {
- case ZEND_DO_FCALL:
- case ZEND_DO_ICALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- case ZEND_CALLABLE_CONVERT:
- call_level--;
- }
- }
- }
- handler = dasm_link_and_encode(&dasm_state, op_array, ssa, rt_opline, ra, NULL, 0,
- (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) ? SP_ADJ_VM : SP_ADJ_RET, SP_ADJ_JIT);
- if (!handler) {
- goto jit_failure;
- }
- dasm_free(&dasm_state);
- if (JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) {
- zend_arena_release(&CG(arena), checkpoint);
- }
- return SUCCESS;
- jit_failure:
- if (dasm_state) {
- dasm_free(&dasm_state);
- }
- if (JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) {
- zend_arena_release(&CG(arena), checkpoint);
- }
- return FAILURE;
- }
- static int zend_jit_collect_calls(zend_op_array *op_array, zend_script *script)
- {
- zend_func_info *func_info;
- if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC ||
- JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST ||
- JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS) {
- func_info = ZEND_FUNC_INFO(op_array);
- } else {
- func_info = zend_arena_calloc(&CG(arena), 1, sizeof(zend_func_info));
- ZEND_SET_FUNC_INFO(op_array, func_info);
- }
- return zend_analyze_calls(&CG(arena), script, ZEND_CALL_TREE, op_array, func_info);
- }
- static void zend_jit_cleanup_func_info(zend_op_array *op_array)
- {
- zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
- zend_call_info *caller_info, *callee_info;
- if (func_info) {
- caller_info = func_info->caller_info;
- callee_info = func_info->callee_info;
- if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC ||
- JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST ||
- JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS) {
- func_info->num = 0;
- 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(&func_info->ssa, 0, sizeof(zend_func_info) - offsetof(zend_func_info, ssa));
- } else {
- ZEND_SET_FUNC_INFO(op_array, NULL);
- }
- while (caller_info) {
- if (caller_info->caller_op_array) {
- zend_jit_cleanup_func_info(caller_info->caller_op_array);
- }
- caller_info = caller_info->next_caller;
- }
- while (callee_info) {
- if (callee_info->callee_func && callee_info->callee_func->type == ZEND_USER_FUNCTION) {
- zend_jit_cleanup_func_info(&callee_info->callee_func->op_array);
- }
- callee_info = callee_info->next_callee;
- }
- }
- }
- static int zend_real_jit_func(zend_op_array *op_array, zend_script *script, const zend_op *rt_opline)
- {
- zend_ssa ssa;
- void *checkpoint;
- zend_func_info *func_info;
- if (*dasm_ptr == dasm_end) {
- return FAILURE;
- }
- checkpoint = zend_arena_checkpoint(CG(arena));
- /* Build SSA */
- memset(&ssa, 0, sizeof(zend_ssa));
- if (zend_jit_op_array_analyze1(op_array, script, &ssa) != SUCCESS) {
- goto jit_failure;
- }
- if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_OPT_FUNCS) {
- if (zend_jit_collect_calls(op_array, script) != SUCCESS) {
- goto jit_failure;
- }
- func_info = ZEND_FUNC_INFO(op_array);
- func_info->call_map = zend_build_call_map(&CG(arena), func_info, op_array);
- if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
- zend_init_func_return_info(op_array, script, &func_info->return_info);
- }
- }
- if (zend_jit_op_array_analyze2(op_array, script, &ssa, ZCG(accel_directives).optimization_level) != SUCCESS) {
- goto jit_failure;
- }
- 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);
- }
- if (zend_jit(op_array, &ssa, rt_opline) != SUCCESS) {
- goto jit_failure;
- }
- zend_jit_cleanup_func_info(op_array);
- zend_arena_release(&CG(arena), checkpoint);
- return SUCCESS;
- jit_failure:
- zend_jit_cleanup_func_info(op_array);
- zend_arena_release(&CG(arena), checkpoint);
- return FAILURE;
- }
- /* Run-time JIT handler */
- static int ZEND_FASTCALL zend_runtime_jit(void)
- {
- zend_execute_data *execute_data = EG(current_execute_data);
- zend_op_array *op_array = &EX(func)->op_array;
- zend_op *opline = op_array->opcodes;
- zend_jit_op_array_extension *jit_extension;
- bool do_bailout = 0;
- zend_shared_alloc_lock();
- if (ZEND_FUNC_INFO(op_array)) {
- SHM_UNPROTECT();
- zend_jit_unprotect();
- zend_try {
- /* restore original opcode handlers */
- if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
- while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) {
- opline++;
- }
- }
- jit_extension = (zend_jit_op_array_extension*)ZEND_FUNC_INFO(op_array);
- opline->handler = jit_extension->orig_handler;
- /* perform real JIT for this function */
- zend_real_jit_func(op_array, NULL, NULL);
- } zend_catch {
- do_bailout = 0;
- } zend_end_try();
- zend_jit_protect();
- SHM_PROTECT();
- }
- zend_shared_alloc_unlock();
- if (do_bailout) {
- zend_bailout();
- }
- /* JIT-ed code is going to be called by VM */
- return 0;
- }
- void zend_jit_check_funcs(HashTable *function_table, bool is_method) {
- zend_op *opline;
- zend_function *func;
- zend_op_array *op_array;
- uintptr_t counter;
- zend_jit_op_array_extension *jit_extension;
- ZEND_HASH_REVERSE_FOREACH_PTR(function_table, func) {
- if (func->type == ZEND_INTERNAL_FUNCTION) {
- break;
- }
- op_array = &func->op_array;
- 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 (opline->handler == zend_jit_profile_jit_handler) {
- if (!RUN_TIME_CACHE(op_array)) {
- continue;
- }
- counter = (uintptr_t)ZEND_COUNTER_INFO(op_array);
- ZEND_COUNTER_INFO(op_array) = 0;
- jit_extension = (zend_jit_op_array_extension*)ZEND_FUNC_INFO(op_array);
- opline->handler = jit_extension->orig_handler;
- if (((double)counter / (double)zend_jit_profile_counter) > JIT_G(prof_threshold)) {
- zend_real_jit_func(op_array, NULL, NULL);
- }
- }
- } ZEND_HASH_FOREACH_END();
- }
- void ZEND_FASTCALL zend_jit_hot_func(zend_execute_data *execute_data, const zend_op *opline)
- {
- zend_op_array *op_array = &EX(func)->op_array;
- zend_jit_op_array_hot_extension *jit_extension;
- uint32_t i;
- bool do_bailout = 0;
- zend_shared_alloc_lock();
- jit_extension = (zend_jit_op_array_hot_extension*)ZEND_FUNC_INFO(op_array);
- if (jit_extension) {
- SHM_UNPROTECT();
- zend_jit_unprotect();
- zend_try {
- for (i = 0; i < op_array->last; i++) {
- op_array->opcodes[i].handler = jit_extension->orig_handlers[i];
- }
- /* perform real JIT for this function */
- zend_real_jit_func(op_array, NULL, opline);
- } zend_catch {
- do_bailout = 1;
- } zend_end_try();
- zend_jit_protect();
- SHM_PROTECT();
- }
- zend_shared_alloc_unlock();
- if (do_bailout) {
- zend_bailout();
- }
- /* JIT-ed code is going to be called by VM */
- }
- static void zend_jit_setup_hot_counters_ex(zend_op_array *op_array, zend_cfg *cfg)
- {
- if (JIT_G(hot_func)) {
- zend_op *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++;
- }
- }
- opline->handler = (const void*)zend_jit_func_hot_counter_handler;
- }
- if (JIT_G(hot_loop)) {
- uint32_t i;
- for (i = 0; i < cfg->blocks_count; i++) {
- if ((cfg->blocks[i].flags & ZEND_BB_REACHABLE) &&
- (cfg->blocks[i].flags & ZEND_BB_LOOP_HEADER)) {
- op_array->opcodes[cfg->blocks[i].start].handler =
- (const void*)zend_jit_loop_hot_counter_handler;
- }
- }
- }
- }
- static int zend_jit_restart_hot_counters(zend_op_array *op_array)
- {
- zend_jit_op_array_hot_extension *jit_extension;
- zend_cfg cfg;
- uint32_t i;
- jit_extension = (zend_jit_op_array_hot_extension*)ZEND_FUNC_INFO(op_array);
- for (i = 0; i < op_array->last; i++) {
- op_array->opcodes[i].handler = jit_extension->orig_handlers[i];
- }
- if (zend_jit_build_cfg(op_array, &cfg) != SUCCESS) {
- return FAILURE;
- }
- zend_jit_setup_hot_counters_ex(op_array, &cfg);
- return SUCCESS;
- }
- static int zend_jit_setup_hot_counters(zend_op_array *op_array)
- {
- zend_jit_op_array_hot_extension *jit_extension;
- zend_cfg cfg;
- uint32_t i;
- ZEND_ASSERT(zend_jit_func_hot_counter_handler != NULL);
- ZEND_ASSERT(zend_jit_loop_hot_counter_handler != NULL);
- if (zend_jit_build_cfg(op_array, &cfg) != SUCCESS) {
- return FAILURE;
- }
- jit_extension = (zend_jit_op_array_hot_extension*)zend_shared_alloc(sizeof(zend_jit_op_array_hot_extension) + (op_array->last - 1) * sizeof(void*));
- 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_COUNTERS;
- jit_extension->counter = &zend_jit_hot_counters[zend_jit_op_array_hash(op_array) & (ZEND_HOT_COUNTERS_COUNT - 1)];
- for (i = 0; i < op_array->last; i++) {
- jit_extension->orig_handlers[i] = op_array->opcodes[i].handler;
- }
- ZEND_SET_FUNC_INFO(op_array, (void*)jit_extension);
- zend_jit_setup_hot_counters_ex(op_array, &cfg);
- zend_shared_alloc_register_xlat_entry(op_array->opcodes, jit_extension);
- return SUCCESS;
- }
- #include "jit/zend_jit_trace.c"
- ZEND_EXT_API int zend_jit_op_array(zend_op_array *op_array, zend_script *script)
- {
- if (dasm_ptr == NULL) {
- return FAILURE;
- }
- if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC) {
- zend_jit_op_array_extension *jit_extension;
- zend_op *opline = op_array->opcodes;
- if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) {
- ZEND_SET_FUNC_INFO(op_array, NULL);
- zend_error(E_WARNING, "Preloading is incompatible with first-exec and profile triggered JIT");
- return SUCCESS;
- }
- /* Set run-time JIT handler */
- ZEND_ASSERT(zend_jit_runtime_jit_handler != NULL);
- if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
- while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) {
- opline++;
- }
- }
- jit_extension = (zend_jit_op_array_extension*)zend_shared_alloc(sizeof(zend_jit_op_array_extension));
- if (!jit_extension) {
- return FAILURE;
- }
- memset(&jit_extension->func_info, 0, sizeof(zend_func_info));
- jit_extension->func_info.flags = ZEND_FUNC_JIT_ON_FIRST_EXEC;
- jit_extension->orig_handler = (void*)opline->handler;
- ZEND_SET_FUNC_INFO(op_array, (void*)jit_extension);
- opline->handler = (const void*)zend_jit_runtime_jit_handler;
- zend_shared_alloc_register_xlat_entry(op_array->opcodes, jit_extension);
- return SUCCESS;
- } else if (JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST) {
- zend_jit_op_array_extension *jit_extension;
- zend_op *opline = op_array->opcodes;
- if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) {
- ZEND_SET_FUNC_INFO(op_array, NULL);
- zend_error(E_WARNING, "Preloading is incompatible with first-exec and profile triggered JIT");
- return SUCCESS;
- }
- ZEND_ASSERT(zend_jit_profile_jit_handler != NULL);
- if (op_array->function_name) {
- if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
- while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) {
- opline++;
- }
- }
- jit_extension = (zend_jit_op_array_extension*)zend_shared_alloc(sizeof(zend_jit_op_array_extension));
- if (!jit_extension) {
- return FAILURE;
- }
- memset(&jit_extension->func_info, 0, sizeof(zend_func_info));
- jit_extension->func_info.flags = ZEND_FUNC_JIT_ON_PROF_REQUEST;
- jit_extension->orig_handler = (void*)opline->handler;
- ZEND_SET_FUNC_INFO(op_array, (void*)jit_extension);
- opline->handler = (const void*)zend_jit_profile_jit_handler;
- zend_shared_alloc_register_xlat_entry(op_array->opcodes, jit_extension);
- }
- return SUCCESS;
- } else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS) {
- return zend_jit_setup_hot_counters(op_array);
- } else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
- return zend_jit_setup_hot_trace_counters(op_array);
- } else if (JIT_G(trigger) == ZEND_JIT_ON_SCRIPT_LOAD) {
- return zend_real_jit_func(op_array, script, NULL);
- } else {
- ZEND_UNREACHABLE();
- }
- }
- ZEND_EXT_API int zend_jit_script(zend_script *script)
- {
- void *checkpoint;
- zend_call_graph call_graph;
- zend_func_info *info;
- int i;
- if (dasm_ptr == NULL || *dasm_ptr == dasm_end) {
- return FAILURE;
- }
- checkpoint = zend_arena_checkpoint(CG(arena));
- call_graph.op_arrays_count = 0;
- if (zend_build_call_graph(&CG(arena), script, &call_graph) != SUCCESS) {
- goto jit_failure;
- }
- zend_analyze_call_graph(&CG(arena), script, &call_graph);
- if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC ||
- JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST ||
- JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS ||
- JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- if (zend_jit_op_array(call_graph.op_arrays[i], script) != SUCCESS) {
- goto jit_failure;
- }
- }
- } else if (JIT_G(trigger) == ZEND_JIT_ON_SCRIPT_LOAD) {
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
- if (info) {
- if (zend_jit_op_array_analyze1(call_graph.op_arrays[i], script, &info->ssa) != SUCCESS) {
- goto jit_failure;
- }
- info->flags = info->ssa.cfg.flags;
- }
- }
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
- if (info) {
- info->call_map = zend_build_call_map(&CG(arena), info, call_graph.op_arrays[i]);
- if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
- zend_init_func_return_info(call_graph.op_arrays[i], script, &info->return_info);
- }
- }
- }
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
- if (info) {
- if (zend_jit_op_array_analyze2(call_graph.op_arrays[i], script, &info->ssa, ZCG(accel_directives).optimization_level) != SUCCESS) {
- goto jit_failure;
- }
- info->flags = info->ssa.cfg.flags;
- }
- }
- if (JIT_G(debug) & ZEND_JIT_DEBUG_SSA) {
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
- if (info) {
- zend_dump_op_array(call_graph.op_arrays[i], ZEND_DUMP_HIDE_UNREACHABLE|ZEND_DUMP_RC_INFERENCE|ZEND_DUMP_SSA, "JIT", &info->ssa);
- }
- }
- }
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
- if (info) {
- if (zend_jit(call_graph.op_arrays[i], &info->ssa, NULL) != SUCCESS) {
- goto jit_failure;
- }
- }
- }
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
- }
- } else {
- ZEND_UNREACHABLE();
- }
- zend_arena_release(&CG(arena), checkpoint);
- if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC
- || JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST
- || JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS
- || JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
- zend_class_entry *ce;
- zend_op_array *op_array;
- ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
- ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
- if (!ZEND_FUNC_INFO(op_array)) {
- void *jit_extension = zend_shared_alloc_get_xlat_entry(op_array->opcodes);
- if (jit_extension) {
- ZEND_SET_FUNC_INFO(op_array, jit_extension);
- }
- }
- } ZEND_HASH_FOREACH_END();
- } ZEND_HASH_FOREACH_END();
- }
- return SUCCESS;
- jit_failure:
- if (JIT_G(trigger) == ZEND_JIT_ON_SCRIPT_LOAD) {
- for (i = 0; i < call_graph.op_arrays_count; i++) {
- ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
- }
- }
- zend_arena_release(&CG(arena), checkpoint);
- return FAILURE;
- }
- ZEND_EXT_API void zend_jit_unprotect(void)
- {
- #ifdef HAVE_MPROTECT
- if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) {
- int opts = PROT_READ | PROT_WRITE;
- #ifdef ZTS
- /* TODO: EXEC+WRITE is not supported in macOS. Removing EXEC is still buggy as
- * other threads, which are executing the JITed code, would crash anyway. */
- # ifndef __APPLE__
- /* Another thread may be executing JITed code. */
- opts |= PROT_EXEC;
- # endif
- #endif
- if (mprotect(dasm_buf, dasm_size, opts) != 0) {
- fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno));
- }
- }
- #elif _WIN32
- if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) {
- DWORD old, new;
- #ifdef ZTS
- new = PAGE_EXECUTE_READWRITE;
- #else
- new = PAGE_READWRITE;
- #endif
- if (!VirtualProtect(dasm_buf, dasm_size, new, &old)) {
- DWORD err = GetLastError();
- char *msg = php_win32_error_to_msg(err);
- fprintf(stderr, "VirtualProtect() failed [%u] %s\n", err, msg);
- php_win32_error_msg_free(msg);
- }
- }
- #endif
- }
- ZEND_EXT_API void zend_jit_protect(void)
- {
- #ifdef HAVE_MPROTECT
- if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) {
- if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_EXEC) != 0) {
- fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno));
- }
- }
- #elif _WIN32
- if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) {
- DWORD old;
- if (!VirtualProtect(dasm_buf, dasm_size, PAGE_EXECUTE_READ, &old)) {
- DWORD err = GetLastError();
- char *msg = php_win32_error_to_msg(err);
- fprintf(stderr, "VirtualProtect() failed [%u] %s\n", err, msg);
- php_win32_error_msg_free(msg);
- }
- }
- #endif
- }
- static void zend_jit_init_handlers(void)
- {
- if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
- zend_jit_runtime_jit_handler = dasm_labels[zend_lbhybrid_runtime_jit];
- zend_jit_profile_jit_handler = dasm_labels[zend_lbhybrid_profile_jit];
- zend_jit_func_hot_counter_handler = dasm_labels[zend_lbhybrid_func_hot_counter];
- zend_jit_loop_hot_counter_handler = dasm_labels[zend_lbhybrid_loop_hot_counter];
- zend_jit_func_trace_counter_handler = dasm_labels[zend_lbhybrid_func_trace_counter];
- zend_jit_ret_trace_counter_handler = dasm_labels[zend_lbhybrid_ret_trace_counter];
- zend_jit_loop_trace_counter_handler = dasm_labels[zend_lbhybrid_loop_trace_counter];
- } else {
- zend_jit_runtime_jit_handler = (const void*)zend_runtime_jit;
- zend_jit_profile_jit_handler = (const void*)zend_jit_profile_helper;
- zend_jit_func_hot_counter_handler = (const void*)zend_jit_func_counter_helper;
- zend_jit_loop_hot_counter_handler = (const void*)zend_jit_loop_counter_helper;
- zend_jit_func_trace_counter_handler = (const void*)zend_jit_func_trace_helper;
- zend_jit_ret_trace_counter_handler = (const void*)zend_jit_ret_trace_helper;
- zend_jit_loop_trace_counter_handler = (const void*)zend_jit_loop_trace_helper;
- }
- }
- static int zend_jit_make_stubs(void)
- {
- dasm_State* dasm_state = NULL;
- uint32_t i;
- dasm_init(&dasm_state, DASM_MAXSECTION);
- dasm_setupglobal(&dasm_state, dasm_labels, zend_lb_MAX);
- for (i = 0; i < sizeof(zend_jit_stubs)/sizeof(zend_jit_stubs[0]); i++) {
- dasm_setup(&dasm_state, dasm_actions);
- if (!zend_jit_stubs[i].stub(&dasm_state)) {
- return 0;
- }
- if (!dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, zend_jit_stubs[i].name, 0,
- zend_jit_stubs[i].offset, zend_jit_stubs[i].adjustment)) {
- return 0;
- }
- }
- zend_jit_init_handlers();
- dasm_free(&dasm_state);
- return 1;
- }
- static void zend_jit_globals_ctor(zend_jit_globals *jit_globals)
- {
- memset(jit_globals, 0, sizeof(zend_jit_globals));
- zend_jit_trace_init_caches();
- }
- static int zend_jit_parse_config_num(zend_long jit)
- {
- if (jit == 0) {
- JIT_G(on) = 0;
- return SUCCESS;
- }
- if (jit < 0) return FAILURE;
- if (jit % 10 == 0 || jit % 10 > 5) return FAILURE;
- JIT_G(opt_level) = jit % 10;
- jit /= 10;
- if (jit % 10 > 5) return FAILURE;
- JIT_G(trigger) = jit % 10;
- jit /= 10;
- if (jit % 10 > 2) return FAILURE;
- JIT_G(opt_flags) = jit % 10;
- jit /= 10;
- if (jit % 10 > 1) return FAILURE;
- JIT_G(opt_flags) |= ((jit % 10) ? ZEND_JIT_CPU_AVX : 0);
- if (jit / 10 != 0) return FAILURE;
- JIT_G(on) = 1;
- return SUCCESS;
- }
- ZEND_EXT_API int zend_jit_config(zend_string *jit, int stage)
- {
- if (stage != ZEND_INI_STAGE_STARTUP && !JIT_G(enabled)) {
- if (stage == ZEND_INI_STAGE_RUNTIME) {
- zend_error(E_WARNING, "Cannot change opcache.jit setting at run-time (JIT is disabled)");
- }
- return FAILURE;
- }
- if (ZSTR_LEN(jit) == 0
- || zend_string_equals_literal_ci(jit, "disable")) {
- JIT_G(enabled) = 0;
- JIT_G(on) = 0;
- return SUCCESS;
- } else if (zend_string_equals_literal_ci(jit, "0")
- || zend_string_equals_literal_ci(jit, "off")
- || zend_string_equals_literal_ci(jit, "no")
- || zend_string_equals_literal_ci(jit, "false")) {
- JIT_G(enabled) = 1;
- JIT_G(on) = 0;
- return SUCCESS;
- } else if (zend_string_equals_literal_ci(jit, "1")
- || zend_string_equals_literal_ci(jit, "on")
- || zend_string_equals_literal_ci(jit, "yes")
- || zend_string_equals_literal_ci(jit, "true")
- || zend_string_equals_literal_ci(jit, "tracing")) {
- JIT_G(enabled) = 1;
- JIT_G(on) = 1;
- JIT_G(opt_level) = ZEND_JIT_LEVEL_OPT_FUNCS;
- JIT_G(trigger) = ZEND_JIT_ON_HOT_TRACE;
- JIT_G(opt_flags) = ZEND_JIT_REG_ALLOC_GLOBAL | ZEND_JIT_CPU_AVX;
- return SUCCESS;
- } else if (zend_string_equals_literal_ci(jit, "function")) {
- JIT_G(enabled) = 1;
- JIT_G(on) = 1;
- JIT_G(opt_level) = ZEND_JIT_LEVEL_OPT_SCRIPT;
- JIT_G(trigger) = ZEND_JIT_ON_SCRIPT_LOAD;
- JIT_G(opt_flags) = ZEND_JIT_REG_ALLOC_GLOBAL | ZEND_JIT_CPU_AVX;
- return SUCCESS;
- } else {
- char *end;
- zend_long num = ZEND_STRTOL(ZSTR_VAL(jit), &end, 10);
- if (end != ZSTR_VAL(jit) + ZSTR_LEN(jit) || zend_jit_parse_config_num(num) != SUCCESS) {
- goto failure;
- }
- JIT_G(enabled) = 1;
- return SUCCESS;
- }
- failure:
- zend_error(E_WARNING, "Invalid \"opcache.jit\" setting. Should be \"disable\", \"on\", \"off\", \"tracing\", \"function\" or 4-digit number");
- JIT_G(enabled) = 0;
- JIT_G(on) = 0;
- return FAILURE;
- }
- ZEND_EXT_API int zend_jit_debug_config(zend_long old_val, zend_long new_val, int stage)
- {
- if (stage != ZEND_INI_STAGE_STARTUP) {
- if (((old_val ^ new_val) & ZEND_JIT_DEBUG_PERSISTENT) != 0) {
- if (stage == ZEND_INI_STAGE_RUNTIME) {
- zend_error(E_WARNING, "Some opcache.jit_debug bits cannot be changed after startup");
- }
- return FAILURE;
- }
- #ifdef HAVE_DISASM
- if (new_val & (ZEND_JIT_DEBUG_ASM|ZEND_JIT_DEBUG_ASM_STUBS)) {
- if (JIT_G(enabled) && !JIT_G(symbols) && !zend_jit_disasm_init()) {
- // TODO: error reporting and cleanup ???
- return FAILURE;
- }
- // TODO: symbols for JIT-ed code compiled before are missing ???
- }
- #endif
- }
- return SUCCESS;
- }
- ZEND_EXT_API void zend_jit_init(void)
- {
- #ifdef ZTS
- jit_globals_id = ts_allocate_id(&jit_globals_id, sizeof(zend_jit_globals), (ts_allocate_ctor) zend_jit_globals_ctor, NULL);
- #else
- zend_jit_globals_ctor(&jit_globals);
- #endif
- }
- ZEND_EXT_API int zend_jit_check_support(void)
- {
- int i;
- zend_jit_vm_kind = zend_vm_kind();
- if (zend_jit_vm_kind != ZEND_VM_KIND_CALL &&
- zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
- zend_error(E_WARNING, "JIT is compatible only with CALL and HYBRID VM. JIT disabled.");
- JIT_G(enabled) = 0;
- JIT_G(on) = 0;
- return FAILURE;
- }
- if (zend_execute_ex != execute_ex) {
- if (strcmp(sapi_module.name, "phpdbg") != 0) {
- zend_error(E_WARNING, "JIT is incompatible with third party extensions that override zend_execute_ex(). JIT disabled.");
- }
- JIT_G(enabled) = 0;
- JIT_G(on) = 0;
- return FAILURE;
- }
- for (i = 0; i <= 256; i++) {
- switch (i) {
- /* JIT has no effect on these opcodes */
- case ZEND_BEGIN_SILENCE:
- case ZEND_END_SILENCE:
- case ZEND_EXIT:
- break;
- default:
- if (zend_get_user_opcode_handler(i) != NULL) {
- zend_error(E_WARNING, "JIT is incompatible with third party extensions that setup user opcode handlers. JIT disabled.");
- JIT_G(enabled) = 0;
- JIT_G(on) = 0;
- return FAILURE;
- }
- }
- }
- return SUCCESS;
- }
- ZEND_EXT_API int zend_jit_startup(void *buf, size_t size, bool reattached)
- {
- int ret;
- zend_jit_halt_op = zend_get_halt_op();
- if (zend_jit_setup() != SUCCESS) {
- // TODO: error reporting and cleanup ???
- return FAILURE;
- }
- zend_jit_profile_counter_rid = zend_get_op_array_extension_handle(ACCELERATOR_PRODUCT_NAME);
- #ifdef HAVE_GDB
- zend_jit_gdb_init();
- #endif
- #ifdef HAVE_OPROFILE
- if (JIT_G(debug) & ZEND_JIT_DEBUG_OPROFILE) {
- if (!zend_jit_oprofile_startup()) {
- // TODO: error reporting and cleanup ???
- return FAILURE;
- }
- }
- #endif
- dasm_buf = buf;
- dasm_size = size;
- #ifdef HAVE_MPROTECT
- if (JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP)) {
- if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) {
- fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno));
- }
- } else {
- if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_EXEC) != 0) {
- fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno));
- }
- }
- #elif _WIN32
- if (JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP)) {
- DWORD old;
- if (!VirtualProtect(dasm_buf, dasm_size, PAGE_EXECUTE_READWRITE, &old)) {
- DWORD err = GetLastError();
- char *msg = php_win32_error_to_msg(err);
- fprintf(stderr, "VirtualProtect() failed [%u] %s\n", err, msg);
- php_win32_error_msg_free(msg);
- }
- } else {
- DWORD old;
- if (!VirtualProtect(dasm_buf, dasm_size, PAGE_EXECUTE_READ, &old)) {
- DWORD err = GetLastError();
- char *msg = php_win32_error_to_msg(err);
- fprintf(stderr, "VirtualProtect() failed [%u] %s\n", err, msg);
- php_win32_error_msg_free(msg);
- }
- }
- #endif
- dasm_ptr = dasm_end = (void*)(((char*)dasm_buf) + size - sizeof(*dasm_ptr) * 2);
- if (!reattached) {
- zend_jit_unprotect();
- *dasm_ptr = dasm_buf;
- #if _WIN32
- /* reserve space for global labels */
- *dasm_ptr = (void**)*dasm_ptr + zend_lb_MAX;
- #endif
- zend_jit_protect();
- }
- #ifdef HAVE_DISASM
- if (JIT_G(debug) & (ZEND_JIT_DEBUG_ASM|ZEND_JIT_DEBUG_ASM_STUBS)) {
- if (!zend_jit_disasm_init()) {
- // TODO: error reporting and cleanup ???
- return FAILURE;
- }
- }
- #endif
- #ifdef HAVE_PERFTOOLS
- if (JIT_G(debug) & ZEND_JIT_DEBUG_PERF_DUMP) {
- zend_jit_perf_jitdump_open();
- }
- #endif
- if (!reattached) {
- zend_jit_unprotect();
- ret = zend_jit_make_stubs();
- #if _WIN32
- /* save global labels */
- memcpy(dasm_buf, dasm_labels, sizeof(void*) * zend_lb_MAX);
- #endif
- zend_jit_protect();
- if (!ret) {
- // TODO: error reporting and cleanup ???
- return FAILURE;
- }
- } else {
- #if _WIN32
- /* restore global labels */
- memcpy(dasm_labels, dasm_buf, sizeof(void*) * zend_lb_MAX);
- zend_jit_init_handlers();
- #endif
- }
- if (zend_jit_trace_startup(reattached) != SUCCESS) {
- return FAILURE;
- }
- zend_jit_unprotect();
- #if ZEND_JIT_TARGET_ARM64
- /* reserve space for global labels veneers */
- dasm_labels_veneers = *dasm_ptr;
- *dasm_ptr = (void**)*dasm_ptr + ZEND_MM_ALIGNED_SIZE_EX(zend_lb_MAX, DASM_ALIGNMENT);
- memset(dasm_labels_veneers, 0, sizeof(void*) * ZEND_MM_ALIGNED_SIZE_EX(zend_lb_MAX, DASM_ALIGNMENT));
- #endif
- /* save JIT buffer pos */
- dasm_ptr[1] = dasm_ptr[0];
- zend_jit_protect();
- return SUCCESS;
- }
- ZEND_EXT_API void zend_jit_shutdown(void)
- {
- if (JIT_G(debug) & ZEND_JIT_DEBUG_SIZE) {
- fprintf(stderr, "\nJIT memory usage: %td\n", (ptrdiff_t)((char*)*dasm_ptr - (char*)dasm_buf));
- }
- #ifdef HAVE_OPROFILE
- if (JIT_G(debug) & ZEND_JIT_DEBUG_OPROFILE) {
- zend_jit_oprofile_shutdown();
- }
- #endif
- #ifdef HAVE_GDB
- if (JIT_G(debug) & ZEND_JIT_DEBUG_GDB) {
- zend_jit_gdb_unregister();
- }
- #endif
- #ifdef HAVE_DISASM
- zend_jit_disasm_shutdown();
- #endif
- #ifdef HAVE_PERFTOOLS
- if (JIT_G(debug) & ZEND_JIT_DEBUG_PERF_DUMP) {
- zend_jit_perf_jitdump_close();
- }
- #endif
- if (JIT_G(exit_counters)) {
- free(JIT_G(exit_counters));
- }
- }
- static void zend_jit_reset_counters(void)
- {
- int i;
- for (i = 0; i < ZEND_HOT_COUNTERS_COUNT; i++) {
- zend_jit_hot_counters[i] = ZEND_JIT_COUNTER_INIT;
- }
- }
- ZEND_EXT_API void zend_jit_activate(void)
- {
- zend_jit_profile_counter = 0;
- if (JIT_G(on)) {
- if (JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS) {
- zend_jit_reset_counters();
- } else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
- zend_jit_reset_counters();
- zend_jit_trace_reset_caches();
- }
- }
- }
- ZEND_EXT_API void zend_jit_deactivate(void)
- {
- if (zend_jit_profile_counter) {
- zend_class_entry *ce;
- zend_shared_alloc_lock();
- SHM_UNPROTECT();
- zend_jit_unprotect();
- zend_jit_check_funcs(EG(function_table), 0);
- ZEND_HASH_REVERSE_FOREACH_PTR(EG(class_table), ce) {
- if (ce->type == ZEND_INTERNAL_CLASS) {
- break;
- }
- zend_jit_check_funcs(&ce->function_table, 1);
- } ZEND_HASH_FOREACH_END();
- zend_jit_protect();
- SHM_PROTECT();
- zend_shared_alloc_unlock();
- zend_jit_profile_counter = 0;
- }
- }
- static void zend_jit_restart_preloaded_op_array(zend_op_array *op_array)
- {
- zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
- if (!func_info) {
- return;
- }
- if (func_info->flags & ZEND_FUNC_JIT_ON_HOT_TRACE) {
- zend_jit_restart_hot_trace_counters(op_array);
- } else if (func_info->flags & ZEND_FUNC_JIT_ON_HOT_COUNTERS) {
- zend_jit_restart_hot_counters(op_array);
- #if 0
- // TODO: We have to restore handlers for some inner basic-blocks, but we didn't store them ???
- } else if (func_info->flags & (ZEND_FUNC_JIT_ON_FIRST_EXEC|ZEND_FUNC_JIT_ON_PROF_REQUEST)) {
- zend_op *opline = op_array->opcodes;
- zend_jit_op_array_extension *jit_extension =
- (zend_jit_op_array_extension*)func_info;
- if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
- while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) {
- opline++;
- }
- }
- if (func_info->flags & ZEND_FUNC_JIT_ON_FIRST_EXEC) {
- opline->handler = (const void*)zend_jit_runtime_jit_handler;
- } else {
- opline->handler = (const void*)zend_jit_profile_jit_handler;
- }
- #endif
- }
- }
- static void zend_jit_restart_preloaded_script(zend_persistent_script *script)
- {
- zend_class_entry *ce;
- zend_op_array *op_array;
- zend_jit_restart_preloaded_op_array(&script->script.main_op_array);
- ZEND_HASH_FOREACH_PTR(&script->script.function_table, op_array) {
- zend_jit_restart_preloaded_op_array(op_array);
- } ZEND_HASH_FOREACH_END();
- ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) {
- ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
- if (op_array->type == ZEND_USER_FUNCTION) {
- zend_jit_restart_preloaded_op_array(op_array);
- }
- } ZEND_HASH_FOREACH_END();
- } ZEND_HASH_FOREACH_END();
- }
- ZEND_EXT_API void zend_jit_restart(void)
- {
- if (dasm_buf) {
- zend_jit_unprotect();
- #if ZEND_JIT_TARGET_ARM64
- memset(dasm_labels_veneers, 0, sizeof(void*) * ZEND_MM_ALIGNED_SIZE_EX(zend_lb_MAX, DASM_ALIGNMENT));
- #endif
- /* restore JIT buffer pos */
- dasm_ptr[0] = dasm_ptr[1];
- zend_jit_trace_restart();
- if (ZCSG(preload_script)) {
- zend_jit_restart_preloaded_script(ZCSG(preload_script));
- if (ZCSG(saved_scripts)) {
- zend_persistent_script **p = ZCSG(saved_scripts);
- while (*p) {
- zend_jit_restart_preloaded_script(*p);
- p++;
- }
- }
- }
- zend_jit_protect();
- }
- }
- #endif /* HAVE_JIT */
|