Basic JIT support for Records
[hiphop-php.git] / hphp / runtime / vm / jit / translator.cpp
blob014a476f010d5ba46a038c4ee4e30f45ce1ade55
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/translator.h"
19 #include <cinttypes>
20 #include <assert.h>
21 #include <stdarg.h>
22 #include <stdint.h>
24 #include <algorithm>
25 #include <memory>
26 #include <string>
27 #include <utility>
28 #include <vector>
30 #include <folly/Conv.h>
31 #include <folly/MapUtil.h>
33 #include "hphp/util/arch.h"
34 #include "hphp/util/map-walker.h"
35 #include "hphp/util/ringbuffer.h"
36 #include "hphp/util/timer.h"
37 #include "hphp/util/trace.h"
39 #include "hphp/runtime/base/repo-auth-type-codec.h"
40 #include "hphp/runtime/base/runtime-option.h"
41 #include "hphp/runtime/base/stats.h"
42 #include "hphp/runtime/base/unit-cache.h"
43 #include "hphp/runtime/ext/generator/ext_generator.h"
44 #include "hphp/runtime/vm/bc-pattern.h"
45 #include "hphp/runtime/vm/bytecode.h"
46 #include "hphp/runtime/vm/hhbc-codec.h"
47 #include "hphp/runtime/vm/hhbc.h"
48 #include "hphp/runtime/vm/method-lookup.h"
49 #include "hphp/runtime/vm/runtime.h"
50 #include "hphp/runtime/vm/treadmill.h"
51 #include "hphp/runtime/vm/type-profile.h"
53 #include "hphp/runtime/vm/jit/annotation.h"
54 #include "hphp/runtime/vm/jit/inlining-decider.h"
55 #include "hphp/runtime/vm/jit/ir-unit.h"
56 #include "hphp/runtime/vm/jit/irgen-basic.h"
57 #include "hphp/runtime/vm/jit/irgen-control.h"
58 #include "hphp/runtime/vm/jit/irgen-exit.h"
59 #include "hphp/runtime/vm/jit/irgen.h"
60 #include "hphp/runtime/vm/jit/normalized-instruction.h"
61 #include "hphp/runtime/vm/jit/print.h"
62 #include "hphp/runtime/vm/jit/prof-data.h"
63 #include "hphp/runtime/vm/jit/punt.h"
64 #include "hphp/runtime/vm/jit/region-selection.h"
65 #include "hphp/runtime/vm/jit/timer.h"
66 #include "hphp/runtime/vm/jit/translate-region.h"
67 #include "hphp/runtime/vm/jit/translator-inline.h"
68 #include "hphp/runtime/vm/jit/type.h"
71 TRACE_SET_MOD(trans);
73 namespace HPHP { namespace jit {
74 ///////////////////////////////////////////////////////////////////////////////
77 * NB: this opcode structure is sparse; it cannot just be indexed by
78 * opcode.
80 using namespace InstrFlags;
81 static const struct {
82 Op op;
83 InstrInfo info;
84 } instrInfoSparse [] = {
86 // Op Inputs Outputs OutputTypes
87 // -- ------ ------- -----------
89 /*** 1. Basic instructions ***/
91 { OpDiscardClsRef, {None, None, OutNone }},
92 { OpPopC, {Stack1|
93 DontGuardStack1, None, OutNone }},
94 { OpPopV, {Stack1|
95 DontGuardStack1, None, OutNone }},
96 { OpPopU, {Stack1|
97 DontGuardStack1, None, OutNone }},
98 { OpPopU2, {StackTop2|
99 DontGuardAny, Stack1, OutSameAsInput1 }},
100 { OpPopL, {Stack1|Local, Local, OutNone }},
101 { OpDup, {Stack1, StackTop2, OutSameAsInput1 }},
102 { OpBox, {Stack1, Stack1, OutVInput }},
103 { OpUnbox, {Stack1, Stack1, OutCInput }},
105 /*** 2. Literal and constant instructions ***/
107 { OpNull, {None, Stack1, OutNull }},
108 { OpNullUninit, {None, Stack1, OutNullUninit }},
109 { OpTrue, {None, Stack1, OutBooleanImm }},
110 { OpFalse, {None, Stack1, OutBooleanImm }},
111 { OpInt, {None, Stack1, OutInt64 }},
112 { OpDouble, {None, Stack1, OutDouble }},
113 { OpString, {None, Stack1, OutStringImm }},
114 { OpArray, {None, Stack1, OutArrayImm }},
115 { OpDict, {None, Stack1, OutDictImm }},
116 { OpKeyset, {None, Stack1, OutKeysetImm }},
117 { OpVec, {None, Stack1, OutVecImm }},
118 { OpNewArray, {None, Stack1, OutArray }},
119 { OpNewMixedArray, {None, Stack1, OutArray }},
120 { OpNewDictArray, {None, Stack1, OutDict }},
121 { OpNewLikeArrayL, {Local, Stack1, OutArray }},
122 { OpNewPackedArray, {StackN, Stack1, OutArray }},
123 { OpNewStructArray, {StackN, Stack1, OutArray }},
124 { OpNewStructDArray,{StackN, Stack1, OutArray }},
125 { OpNewStructDict, {StackN, Stack1, OutDict }},
126 { OpNewVecArray, {StackN, Stack1, OutVec }},
127 { OpNewKeysetArray, {StackN, Stack1, OutKeyset }},
128 { OpNewVArray, {StackN, Stack1, OutArray }},
129 { OpNewDArray, {None, Stack1, OutArray }},
130 { OpAddElemC, {StackTop3, Stack1, OutModifiedInput3 }},
131 { OpAddElemV, {StackTop3, Stack1, OutArray }},
132 { OpAddNewElemC, {StackTop2, Stack1, OutModifiedInput2 }},
133 { OpAddNewElemV, {StackTop2, Stack1, OutArray }},
134 { OpNewCol, {None, Stack1, OutObject }},
135 { OpNewPair, {StackTop2, Stack1, OutObject }},
136 { OpNewRecord, {StackN, Stack1, OutRecord }},
137 { OpColFromArray, {Stack1, Stack1, OutObject }},
138 { OpCnsE, {None, Stack1, OutCns }},
139 { OpClsCns, {None, Stack1, OutUnknown }},
140 { OpClsCnsD, {None, Stack1, OutUnknown }},
141 { OpFile, {None, Stack1, OutString }},
142 { OpDir, {None, Stack1, OutString }},
143 { OpMethod, {None, Stack1, OutString }},
144 { OpClsRefName, {None, Stack1, OutString }},
146 /*** 3. Operator instructions ***/
148 /* Binary string */
149 { OpConcat, {StackTop2, Stack1, OutString }},
150 { OpConcatN, {StackN, Stack1, OutString }},
151 /* Arithmetic ops */
152 { OpAdd, {StackTop2, Stack1, OutArith }},
153 { OpSub, {StackTop2, Stack1, OutArith }},
154 { OpMul, {StackTop2, Stack1, OutArith }},
155 /* Arithmetic ops that overflow ints to floats */
156 { OpAddO, {StackTop2, Stack1, OutArithO }},
157 { OpSubO, {StackTop2, Stack1, OutArithO }},
158 { OpMulO, {StackTop2, Stack1, OutArithO }},
159 /* Div and mod might return boolean false. Sigh. */
160 { OpDiv, {StackTop2, Stack1, OutUnknown }},
161 { OpMod, {StackTop2, Stack1, OutUnknown }},
162 { OpPow, {StackTop2, Stack1, OutUnknown }},
163 /* Logical ops */
164 { OpXor, {StackTop2, Stack1, OutBoolean }},
165 { OpNot, {Stack1, Stack1, OutBoolean }},
166 { OpSame, {StackTop2, Stack1, OutBoolean }},
167 { OpNSame, {StackTop2, Stack1, OutBoolean }},
168 { OpEq, {StackTop2, Stack1, OutBoolean }},
169 { OpNeq, {StackTop2, Stack1, OutBoolean }},
170 { OpLt, {StackTop2, Stack1, OutBoolean }},
171 { OpLte, {StackTop2, Stack1, OutBoolean }},
172 { OpGt, {StackTop2, Stack1, OutBoolean }},
173 { OpGte, {StackTop2, Stack1, OutBoolean }},
174 { OpCmp, {StackTop2, Stack1, OutInt64 }},
175 /* Bitwise ops */
176 { OpBitAnd, {StackTop2, Stack1, OutBitOp }},
177 { OpBitOr, {StackTop2, Stack1, OutBitOp }},
178 { OpBitXor, {StackTop2, Stack1, OutBitOp }},
179 { OpBitNot, {Stack1, Stack1, OutBitOp }},
180 { OpShl, {StackTop2, Stack1, OutInt64 }},
181 { OpShr, {StackTop2, Stack1, OutInt64 }},
182 /* Cast instructions */
183 { OpCastBool, {Stack1, Stack1, OutBoolean }},
184 { OpCastInt, {Stack1, Stack1, OutInt64 }},
185 { OpCastDouble, {Stack1, Stack1, OutDouble }},
186 { OpCastString, {Stack1, Stack1, OutString }},
187 { OpCastArray, {Stack1, Stack1, OutArray }},
188 { OpCastObject, {Stack1, Stack1, OutObject }},
189 { OpCastDict, {Stack1, Stack1, OutDict }},
190 { OpCastKeyset, {Stack1, Stack1, OutKeyset }},
191 { OpCastVec, {Stack1, Stack1, OutVec }},
192 { OpCastVArray, {Stack1, Stack1, OutArray }},
193 { OpCastDArray, {Stack1, Stack1, OutArray }},
194 { OpDblAsBits, {Stack1, Stack1, OutInt64 }},
195 { OpInstanceOf, {StackTop2, Stack1, OutBoolean }},
196 { OpInstanceOfD, {Stack1, Stack1, OutPredBool }},
197 { OpIsLateBoundCls,{Stack1, Stack1, OutBoolean }},
198 { OpIsTypeStructC,{StackTop2, Stack1, OutBoolean }},
199 { OpAsTypeStructC,{StackTop2, Stack1, OutSameAsInput2 }},
200 { OpCombineAndResolveTypeStruct,
201 {StackN, Stack1, OutDArray }},
202 { OpSelect, {StackTop3, Stack1, OutUnknown }},
203 { OpPrint, {Stack1, Stack1, OutInt64 }},
204 { OpClone, {Stack1, Stack1, OutObject }},
205 { OpExit, {Stack1, Stack1, OutNull }},
206 { OpFatal, {Stack1, None, OutNone }},
208 /*** 4. Control flow instructions ***/
210 { OpJmp, {None, None, OutNone }},
211 { OpJmpNS, {None, None, OutNone }},
212 { OpJmpZ, {Stack1, None, OutNone }},
213 { OpJmpNZ, {Stack1, None, OutNone }},
214 { OpSwitch, {Stack1, None, OutNone }},
215 { OpSSwitch, {Stack1, None, OutNone }},
217 * RetC and RetM are special. Their manipulation of the runtime stack are
218 * outside the boundaries of the tracelet abstraction; since they always end
219 * a basic block, they behave more like "glue" between BBs than the
220 * instructions in the body of a BB.
222 * RetC and RetM consume values from the stack, and these values' types needs
223 * to be known at compile-time.
225 { OpRetC, {None, None, OutNone }},
226 { OpRetM, {None, None, OutNone }},
227 { OpRetCSuspended,
228 {None, None, OutNone }},
229 { OpThrow, {Stack1, None, OutNone }},
230 { OpUnwind, {None, None, OutNone }},
232 /*** 5. Get instructions ***/
234 { OpCGetL, {Local, Stack1, OutCInputL }},
235 { OpCGetL2, {Stack1|DontGuardStack1|
236 Local, StackIns1, OutCInputL }},
237 { OpCGetQuietL, {Local, Stack1, OutCInputL }},
238 // In OpCUGetL we rely on OutCInputL returning TCell (which covers Uninit
239 // values) instead of TInitCell.
240 { OpCUGetL, {Local, Stack1, OutCInputL }},
241 { OpPushL, {Local, Stack1|Local, OutCInputL }},
242 { OpCGetG, {Stack1, Stack1, OutUnknown }},
243 { OpCGetQuietG, {Stack1, Stack1, OutUnknown }},
244 { OpCGetS, {Stack1, Stack1, OutUnknown }},
245 { OpVGetL, {Local, Stack1|Local, OutVInputL }},
246 // TODO: In pseudo-main, the VGetG instruction invalidates what we know
247 // about the types of the locals because it could cause any one of the
248 // local variables to become "boxed". We need to add logic to tracelet
249 // analysis to deal with this properly.
250 { OpVGetG, {Stack1, Stack1, OutVUnknown }},
251 { OpVGetS, {Stack1, Stack1, OutVUnknown }},
252 { OpClsRefGetC, {Stack1, None, OutNone }},
253 { OpClsRefGetTS, {Stack1, None, OutNone }},
255 /*** 6. Isset, Empty, and type querying instructions ***/
257 { OpAKExists, {StackTop2, Stack1, OutBoolean }},
258 { OpIssetL, {Local, Stack1, OutBoolean }},
259 { OpIssetG, {Stack1, Stack1, OutBoolean }},
260 { OpIssetS, {Stack1, Stack1, OutBoolean }},
261 { OpEmptyL, {Local, Stack1, OutBoolean }},
262 { OpEmptyG, {Stack1, Stack1, OutBoolean }},
263 { OpEmptyS, {Stack1, Stack1, OutBoolean }},
264 { OpIsTypeC, {Stack1|
265 DontGuardStack1, Stack1, OutBoolean }},
266 { OpIsTypeL, {Local, Stack1, OutIsTypeL }},
268 /*** 7. Mutator instructions ***/
270 { OpSetL, {Stack1|Local, Stack1|Local, OutSameAsInput1 }},
271 { OpSetG, {StackTop2, Stack1, OutSameAsInput1 }},
272 { OpSetS, {StackTop2, Stack1, OutSameAsInput1 }},
273 { OpSetOpL, {Stack1|Local, Stack1|Local, OutSetOp }},
274 { OpSetOpG, {StackTop2, Stack1, OutUnknown }},
275 { OpSetOpS, {StackTop2, Stack1, OutUnknown }},
276 { OpIncDecL, {Local, Stack1|Local, OutIncDec }},
277 { OpIncDecG, {Stack1, Stack1, OutUnknown }},
278 { OpIncDecS, {Stack1, Stack1, OutUnknown }},
279 { OpBindL, {Stack1|Local, Stack1|Local, OutSameAsInput1 }},
280 { OpBindG, {StackTop2, Stack1, OutSameAsInput1 }},
281 { OpBindS, {StackTop2, Stack1, OutSameAsInput1 }},
282 { OpUnsetL, {Local, Local, OutNone }},
283 { OpUnsetG, {Stack1, None, OutNone }},
285 /*** 8. Call instructions ***/
287 { OpFPushFunc, {Stack1, FStack, OutFDesc }},
288 { OpFPushFuncD, {None, FStack, OutFDesc }},
289 { OpFPushFuncU, {None, FStack, OutFDesc }},
290 { OpFPushObjMethod,
291 {StackTop2, FStack, OutFDesc }},
292 { OpFPushObjMethodD,
293 {Stack1, FStack, OutFDesc }},
294 { OpFPushClsMethod,
295 {Stack1, FStack, OutFDesc }},
296 { OpFPushClsMethodS,
297 {Stack1, FStack, OutFDesc }},
298 { OpFPushClsMethodSD,
299 {None, FStack, OutFDesc }},
300 { OpFPushClsMethodD,
301 {None, FStack, OutFDesc }},
302 { OpNewObj, {None, Stack1, OutObject }},
303 { OpNewObjD, {None, Stack1, OutObject }},
304 { OpNewObjS, {None, Stack1, OutObject }},
305 { OpFPushCtor, {Stack1, FStack, OutFDesc }},
307 * FCall is special. Like the Ret* instructions, its manipulation of the
308 * runtime stack are outside the boundaries of the tracelet abstraction.
310 { OpFCall, {FStack, StackN, OutUnknown }},
311 { OpFCallBuiltin,{BStackN|DontGuardAny,
312 Stack1, OutUnknown }},
314 /*** 11. Iterator instructions ***/
316 { OpIterInit, {Stack1, Local, OutUnknown }},
317 { OpLIterInit, {Local, Local, OutUnknown }},
318 { OpIterInitK, {Stack1, Local, OutUnknown }},
319 { OpLIterInitK, {Local, Local, OutUnknown }},
320 { OpIterNext, {None, Local, OutUnknown }},
321 { OpLIterNext, {Local, Local, OutUnknown }},
322 { OpIterNextK, {None, Local, OutUnknown }},
323 { OpLIterNextK, {Local, Local, OutUnknown }},
324 { OpIterFree, {None, None, OutNone }},
325 { OpLIterFree, {Local, None, OutNone }},
326 { OpIterBreak, {Local, None, OutNone }},
328 /*** 12. Include, eval, and define instructions ***/
330 { OpIncl, {Stack1, Stack1, OutUnknown }},
331 { OpInclOnce, {Stack1, Stack1, OutUnknown }},
332 { OpReq, {Stack1, Stack1, OutUnknown }},
333 { OpReqOnce, {Stack1, Stack1, OutUnknown }},
334 { OpReqDoc, {Stack1, Stack1, OutUnknown }},
335 { OpEval, {Stack1, Stack1, OutUnknown }},
336 { OpDefTypeAlias,{None, None, OutNone }},
337 { OpDefCls, {None, None, OutNone }},
338 { OpDefRecord, {None, None, OutNone }},
339 { OpDefCns, {Stack1, Stack1, OutBoolean }},
340 { OpAliasCls, {Stack1, Stack1, OutBoolean }},
342 /*** 13. Miscellaneous instructions ***/
344 { OpThis, {None, Stack1, OutThisObject }},
345 { OpBareThis, {None, Stack1, OutUnknown }},
346 { OpCheckThis, {This, None, OutNone }},
347 { OpInitThisLoc,
348 {None, Local, OutUnknown }},
349 { OpFuncNumArgs, {None, Stack1, OutInt64 }},
350 { OpStaticLocCheck,
351 {None, Stack1|Local, OutBoolean }},
352 { OpStaticLocDef,
353 {Stack1, Local, OutVUnknown }},
354 { OpStaticLocInit,
355 {Stack1, Local, OutVUnknown }},
356 { OpCatch, {None, Stack1, OutObject }},
357 { OpChainFaults, {StackTop2, Stack1, OutObject }},
358 { OpVerifyParamType,
359 {Local, Local, OutUnknown }},
360 { OpVerifyParamTypeTS,
361 {Local|Stack1, Local, OutUnknown }},
362 { OpVerifyRetTypeC,
363 {Stack1, Stack1, OutSameAsInput1 }},
364 { OpVerifyRetTypeTS,
365 {StackTop2, Stack1, OutSameAsInput2 }},
366 { OpVerifyRetNonNullC,
367 {Stack1, Stack1, OutSameAsInput1 }},
368 { OpVerifyOutType,
369 {Stack1, Stack1, OutSameAsInput1 }},
370 { OpOODeclExists,
371 {StackTop2, Stack1, OutBoolean }},
372 { OpSelf, {None, None, OutNone }},
373 { OpParent, {None, None, OutNone }},
374 { OpLateBoundCls,{None, None, OutNone }},
375 { OpRecordReifiedGeneric,
376 {StackN, Stack1, OutVArray }},
377 { OpReifiedName, {StackN, Stack1, OutString }},
378 { OpCheckReifiedGenericMismatch,
379 {Stack1, None, OutNone }},
380 { OpNativeImpl, {None, None, OutNone }},
381 { OpCreateCl, {BStackN, Stack1, OutObject }},
382 { OpIdx, {StackTop3, Stack1, OutUnknown }},
383 { OpArrayIdx, {StackTop3, Stack1, OutUnknown }},
384 { OpCheckProp, {None, Stack1, OutBoolean }},
385 { OpInitProp, {Stack1, None, OutNone }},
386 { OpSilence, {Local|DontGuardAny,
387 Local, OutNone }},
388 { OpAssertRATL, {None, None, OutNone }},
389 { OpAssertRATStk,{None, None, OutNone }},
390 { OpBreakTraceHint,{None, None, OutNone }},
391 { OpGetMemoKeyL, {Local, Stack1, OutUnknown }},
392 { OpResolveFunc, {None, Stack1, OutFunc }},
393 { OpResolveObjMethod,
394 {StackTop2, Stack1, OutVArray }},
395 { OpResolveClsMethod,
396 {StackTop2, Stack1, OutClsMeth }},
398 /*** 14. Generator instructions ***/
400 { OpCreateCont, {None, Stack1, OutNull }},
401 { OpContEnter, {Stack1, Stack1, OutUnknown }},
402 { OpContRaise, {Stack1, Stack1, OutUnknown }},
403 { OpYield, {Stack1, Stack1, OutUnknown }},
404 { OpYieldK, {StackTop2, Stack1, OutUnknown }},
405 { OpContAssignDelegate,
406 {Stack1, None, OutNone }},
407 { OpContEnterDelegate,
408 {Stack1, None, OutNone }},
409 { OpYieldFromDelegate,
410 {None, Stack1, OutUnknown }},
411 { OpContUnsetDelegate,
412 {None, None, OutNone }},
413 { OpContCheck, {None, None, OutNone }},
414 { OpContValid, {None, Stack1, OutBoolean }},
415 { OpContKey, {None, Stack1, OutUnknown }},
416 { OpContCurrent, {None, Stack1, OutUnknown }},
417 { OpContGetReturn,
418 {None, Stack1, OutUnknown }},
420 /*** 15. Async functions instructions ***/
422 { OpWHResult, {Stack1, Stack1, OutUnknown }},
423 { OpAwait, {Stack1, Stack1, OutUnknown }},
424 { OpAwaitAll, {LocalRange, Stack1, OutNull }},
426 /*** 16. Member instructions ***/
428 { OpBaseGC, {StackI, MBase, OutNone }},
429 { OpBaseGL, {Local, MBase, OutNone }},
430 { OpBaseSC, {StackI, MBase, OutNone }},
431 { OpBaseL, {Local, MBase, OutNone }},
432 { OpBaseC, {StackI, MBase, OutNone }},
433 { OpBaseH, {None, MBase, OutNone }},
434 { OpDim, {MBase|MKey, MBase, OutNone }},
435 { OpQueryM, {BStackN|MBase|MKey,
436 Stack1, OutUnknown }},
437 { OpVGetM, {BStackN|MBase|MKey,
438 Stack1, OutVUnknown }},
439 { OpSetM, {Stack1|BStackN|MBase|MKey,
440 Stack1, OutUnknown }},
441 { OpSetRangeM, {BStackN|MBase, None, OutNone }},
442 { OpIncDecM, {BStackN|MBase|MKey,
443 Stack1, OutUnknown }},
444 { OpSetOpM, {Stack1|BStackN|MBase|MKey,
445 Stack1, OutUnknown }},
446 { OpBindM, {Stack1|BStackN|MBase|MKey,
447 Stack1, OutSameAsInput1 }},
448 { OpUnsetM, {BStackN|MBase|MKey,
449 None, OutNone }},
450 { OpMemoGet, {LocalRange, None, OutUnknown }},
451 { OpMemoGetEager,{LocalRange, None, OutUnknown }},
452 { OpMemoSet, {Stack1|LocalRange,
453 Stack1, OutSameAsInput1 }},
454 { OpMemoSetEager, {Stack1|LocalRange,
455 Stack1, OutSameAsInput1 }},
458 namespace {
459 hphp_hash_map<Op, InstrInfo> instrInfo;
460 bool instrInfoInited;
463 void initInstrInfo() {
464 if (!instrInfoInited) {
465 for (auto& info : instrInfoSparse) {
466 instrInfo[info.op] = info.info;
468 if (!RuntimeOption::EvalCheckReturnTypeHints) {
469 auto& ii = instrInfo[OpVerifyRetTypeC];
470 ii.in = ii.out = None;
471 ii.type = OutNone;
473 auto& ii2 = instrInfo[OpVerifyRetTypeTS];
474 ii2.in = Stack1;
475 ii2.out = None;
476 ii2.type = OutNone;
478 instrInfoInited = true;
482 const InstrInfo& getInstrInfo(Op op) {
483 assertx(instrInfoInited);
484 return instrInfo[op];
487 namespace {
488 int64_t countOperands(uint64_t mask) {
489 const uint64_t ignore = Local | Iter | DontGuardStack1 |
490 DontGuardAny | This | MBase | StackI | MKey | LocalRange |
491 DontGuardBase;
492 mask &= ~ignore;
494 static const uint64_t counts[][2] = {
495 {Stack3, 1},
496 {Stack2, 1},
497 {Stack1, 1},
498 {StackIns1, 2},
499 {FStack, kNumActRecCells},
502 int64_t count = 0;
503 for (auto const& pair : counts) {
504 if (mask & pair[0]) {
505 count += pair[1];
506 mask &= ~pair[0];
509 assertx(mask == 0);
510 return count;
514 int64_t getStackPopped(PC pc) {
515 auto const op = peek_op(pc);
516 switch (op) {
517 case Op::FCall: {
518 auto const fca = getImm(pc, 0).u_FCA;
519 return fca.numArgs + (fca.hasUnpack() ? 1 : 0) + fca.numRets +
520 kNumActRecCells - 1;
523 case Op::QueryM:
524 case Op::VGetM:
525 case Op::IncDecM:
526 case Op::UnsetM:
527 case Op::NewPackedArray:
528 case Op::NewVecArray:
529 case Op::NewKeysetArray:
530 case Op::NewVArray:
531 case Op::ConcatN:
532 case Op::CombineAndResolveTypeStruct:
533 case Op::RecordReifiedGeneric:
534 case Op::ReifiedName:
535 case Op::FCallBuiltin:
536 case Op::CreateCl:
537 return getImm(pc, 0).u_IVA;
539 case Op::SetM:
540 case Op::SetOpM:
541 case Op::BindM:
542 return getImm(pc, 0).u_IVA + 1;
544 case Op::NewRecord:
545 case Op::NewStructArray:
546 case Op::NewStructDArray:
547 case Op::NewStructDict:
548 return getImmVector(pc).size();
550 default: break;
553 uint64_t mask = getInstrInfo(op).in;
554 int64_t count = 0;
556 // All instructions with these properties are handled above
557 assertx((mask & (StackN | BStackN)) == 0);
559 return count + countOperands(mask);
562 int64_t getStackPushed(PC pc) {
563 auto const op = peek_op(pc);
564 switch (op) {
565 case Op::FCall: return getImm(pc, 0).u_FCA.numRets;
566 default: break;
569 uint64_t mask = getInstrInfo(op).out;
571 // All instructions with these properties are handled above
572 assertx((mask & (StackN | BStackN)) == 0);
574 return countOperands(mask);
577 bool isAlwaysNop(const NormalizedInstruction& ni) {
578 switch (ni.op()) {
579 case Op::DefClsNop:
580 case Op::Nop:
581 case Op::CGetCUNop:
582 case Op::UGetCUNop:
583 case Op::EntryNop:
584 return true;
585 case Op::VerifyRetTypeC:
586 return !RuntimeOption::EvalCheckReturnTypeHints;
587 default:
588 return false;
592 #define NA
593 #define ONE(a) a(0)
594 #define TWO(a, b) a(0) b(1)
595 #define THREE(a, b, c) a(0) b(1) c(2)
596 #define FOUR(a, b, c, d) a(0) b(1) c(2) d(3)
597 #define FIVE(a, b, c, d, e) a(0) b(1) c(2) d(3) e(4)
598 // Iterator bytecodes are handled specially here
599 #define LA(n) assertx(idx == 0xff); idx = n;
600 #define MA(n)
601 #define BLA(n)
602 #define SLA(n)
603 #define ILA(n)
604 #define I32LA(n)
605 #define IVA(n)
606 #define I64A(n)
607 #define IA(n)
608 #define CAR(n)
609 #define CAW(n)
610 #define DA(n)
611 #define SA(n)
612 #define AA(n)
613 #define RATA(n)
614 #define BA(n)
615 #define OA(op) BA
616 #define VSA(n)
617 #define KA(n)
618 #define LAR(n)
619 #define FCA(n)
620 #define O(name, imm, ...) case Op::name: imm break;
622 size_t localImmIdx(Op op) {
623 switch (op) {
624 case Op::LIterInit:
625 case Op::LIterInitK:
626 case Op::LIterNext:
627 case Op::LIterNextK:
628 case Op::LIterFree:
629 return 1;
630 default:
631 break;
634 size_t idx = 0xff;
635 switch (op) {
636 OPCODES
638 assertx(idx != 0xff);
639 return idx;
642 size_t memberKeyImmIdx(Op op) {
643 size_t idx = 0xff;
644 switch (op) {
645 #undef LA
646 #undef KA
647 #define LA(n)
648 #define KA(n) assertx(idx == 0xff); idx = n;
649 OPCODES
651 assertx(idx != 0xff);
652 return idx;
655 #undef ONE
656 #undef TWO
657 #undef THREE
658 #undef FOUR
659 #undef FIVE
660 #undef LA
661 #undef MA
662 #undef BLA
663 #undef SLA
664 #undef ILA
665 #undef I32LA
666 #undef IVA
667 #undef I64A
668 #undef IA
669 #undef CAR
670 #undef CAW
671 #undef DA
672 #undef SA
673 #undef AA
674 #undef RATA
675 #undef BA
676 #undef OA
677 #undef VSA
678 #undef KA
679 #undef LAR
680 #undef FCA
681 #undef O
684 * Get location metadata for the inputs of `ni'.
686 InputInfoVec getInputs(const NormalizedInstruction& ni, FPInvOffset bcSPOff) {
687 InputInfoVec inputs;
688 if (isAlwaysNop(ni)) return inputs;
690 always_assert_flog(
691 instrInfo.count(ni.op()),
692 "Invalid opcode in getInputsImpl: {}\n",
693 opcodeToName(ni.op())
695 UNUSED auto const sk = ni.source;
697 auto const& info = instrInfo[ni.op()];
698 auto const flags = info.in;
699 auto stackOff = bcSPOff;
701 if (flags & FStack) {
702 assertx(isFCallStar(ni.op()));
703 stackOff -= ni.imm[0].u_FCA.numArgs; // arguments consumed
704 stackOff -= (ni.imm[0].u_FCA.hasUnpack() ? 1 : 0); // unpack
705 stackOff -= kNumActRecCells; // ActRec is torn down as well
708 if (flags & Stack1) {
709 SKTRACE(1, sk, "getInputs: Stack1 %d\n", stackOff.offset);
710 inputs.emplace_back(Location::Stack { stackOff-- });
712 if (flags & DontGuardStack1) inputs.back().dontGuard = true;
714 if (flags & Stack2) {
715 SKTRACE(1, sk, "getInputs: Stack2 %d\n", stackOff.offset);
716 inputs.emplace_back(Location::Stack { stackOff-- });
718 if (flags & Stack3) {
719 SKTRACE(1, sk, "getInputs: Stack3 %d\n", stackOff.offset);
720 inputs.emplace_back(Location::Stack { stackOff-- });
724 if (flags & StackI) {
725 inputs.emplace_back(Location::Stack {
726 BCSPRelOffset{safe_cast<int32_t>(ni.imm[0].u_IVA)}.
727 to<FPInvOffset>(bcSPOff)
730 if (flags & StackN) {
731 int numArgs = (ni.op() == Op::NewPackedArray ||
732 ni.op() == Op::NewVecArray ||
733 ni.op() == Op::NewKeysetArray ||
734 ni.op() == Op::NewVArray ||
735 ni.op() == Op::CombineAndResolveTypeStruct ||
736 ni.op() == Op::RecordReifiedGeneric ||
737 ni.op() == Op::ReifiedName ||
738 ni.op() == Op::ConcatN)
739 ? ni.imm[0].u_IVA
740 : ni.immVec.numStackValues();
742 SKTRACE(1, sk, "getInputs: StackN %d %d\n", stackOff.offset, numArgs);
743 for (int i = 0; i < numArgs; i++) {
744 inputs.emplace_back(Location::Stack { stackOff-- });
745 inputs.back().dontGuard = true;
746 inputs.back().dontBreak = true;
749 if (flags & BStackN) {
750 int numArgs = ni.imm[0].u_IVA;
752 SKTRACE(1, sk, "getInputs: BStackN %d %d\n", stackOff.offset, numArgs);
753 for (int i = 0; i < numArgs; i++) {
754 inputs.emplace_back(Location::Stack { stackOff-- });
758 if (flags & Local) {
759 // (Almost) all instructions that take a Local have its index at their
760 // first immediate.
761 if (ni.op() == Op::IterBreak) {
762 for (auto const& it : ni.immIters) {
763 if (it.kind != KindOfLIter) continue;
764 SKTRACE(1, sk, "getInputs: local %d\n", it.local);
765 inputs.emplace_back(Location::Local { uint32_t(it.local) });
767 } else {
768 auto const loc = ni.imm[localImmIdx(ni.op())].u_IVA;
769 SKTRACE(1, sk, "getInputs: local %d\n", loc);
770 inputs.emplace_back(Location::Local { uint32_t(loc) });
774 if (flags & LocalRange) {
775 auto const& range = ni.imm[1].u_LAR;
776 SKTRACE(1, sk, "getInputs: localRange %d+%d\n",
777 range.first, range.count);
778 for (int i = 0; i < range.count; ++i) {
779 inputs.emplace_back(Location::Local { uint32_t(range.first + i) });
783 if (flags & MKey) {
784 auto mk = ni.imm[memberKeyImmIdx(ni.op())].u_KA;
785 switch (mk.mcode) {
786 case MEL:
787 case MPL:
788 inputs.emplace_back(Location::Local { uint32_t(mk.iva) });
789 break;
790 case MEC:
791 case MPC:
792 inputs.emplace_back(Location::Stack {
793 BCSPRelOffset{int32_t(mk.iva)}.to<FPInvOffset>(bcSPOff)
795 break;
796 case MW:
797 case MEI:
798 case MET:
799 case MPT:
800 case MQT:
801 // The inputs vector is only used for deciding when to break the
802 // tracelet, which can never happen for these cases.
803 break;
806 if (flags & MBase) {
807 inputs.emplace_back(Location::MBase{});
808 if (flags & DontGuardBase) {
809 inputs.back().dontGuard = true;
810 inputs.back().dontBreak = true;
814 SKTRACE(1, sk, "stack args: virtual sfo now %d\n", stackOff.offset);
815 TRACE(1, "%s\n", Trace::prettyNode("Inputs", inputs).c_str());
817 if ((flags & DontGuardAny) || dontGuardAnyInputs(ni)) {
818 for (auto& info : inputs) info.dontGuard = true;
820 return inputs;
823 bool dontGuardAnyInputs(const NormalizedInstruction& ni) {
824 switch (ni.op()) {
825 case Op::IterBreak:
826 case Op::IterNext:
827 case Op::IterNextK:
828 case Op::LIterNext:
829 case Op::LIterNextK:
830 case Op::IterInitK:
831 case Op::IterInit:
832 case Op::LIterInitK:
833 case Op::LIterInit:
834 case Op::JmpZ:
835 case Op::JmpNZ:
836 case Op::Jmp:
837 case Op::JmpNS:
838 case Op::FCall:
839 case Op::ClsCnsD:
840 case Op::FCallBuiltin:
841 case Op::NewStructArray:
842 case Op::NewStructDArray:
843 case Op::NewStructDict:
844 case Op::Switch:
845 case Op::SSwitch:
846 case Op::Lt:
847 case Op::Lte:
848 case Op::Gt:
849 case Op::Gte:
850 case Op::Cmp:
851 case Op::SetOpL:
852 case Op::InitProp:
853 case Op::BreakTraceHint:
854 case Op::IsTypeL:
855 case Op::IsTypeC:
856 case Op::IncDecL:
857 case Op::DefCls:
858 case Op::DefRecord:
859 case Op::AliasCls:
860 case Op::Eq:
861 case Op::Neq:
862 case Op::AssertRATL:
863 case Op::AssertRATStk:
864 case Op::SetL:
865 case Op::BindL:
866 case Op::EmptyL:
867 case Op::CastBool:
868 case Op::Same:
869 case Op::NSame:
870 case Op::Yield:
871 case Op::YieldK:
872 case Op::ContEnter:
873 case Op::ContRaise:
874 case Op::CreateCont:
875 case Op::Await:
876 case Op::AwaitAll:
877 case Op::BitAnd:
878 case Op::BitOr:
879 case Op::BitXor:
880 case Op::Sub:
881 case Op::Mul:
882 case Op::SubO:
883 case Op::MulO:
884 case Op::Add:
885 case Op::AddO:
886 case Op::ClsRefGetC:
887 case Op::ClsRefGetTS:
888 case Op::AKExists:
889 case Op::AddElemC:
890 case Op::AddNewElemC:
891 case Op::Array:
892 case Op::Dict:
893 case Op::Keyset:
894 case Op::Vec:
895 case Op::ArrayIdx:
896 case Op::BareThis:
897 case Op::BindG:
898 case Op::BindS:
899 case Op::BitNot:
900 case Op::CGetG:
901 case Op::CGetQuietG:
902 case Op::CGetL:
903 case Op::CGetQuietL:
904 case Op::CGetL2:
905 case Op::CGetS:
906 case Op::CUGetL:
907 case Op::CastArray:
908 case Op::CastDouble:
909 case Op::CastInt:
910 case Op::CastObject:
911 case Op::CastString:
912 case Op::CastDict:
913 case Op::CastKeyset:
914 case Op::CastVec:
915 case Op::CastVArray:
916 case Op::CastDArray:
917 case Op::DblAsBits:
918 case Op::CheckProp:
919 case Op::CheckThis:
920 case Op::Clone:
921 case Op::CnsE:
922 case Op::ColFromArray:
923 case Op::CombineAndResolveTypeStruct:
924 case Op::RecordReifiedGeneric:
925 case Op::ReifiedName:
926 case Op::CheckReifiedGenericMismatch:
927 case Op::ConcatN:
928 case Op::Concat:
929 case Op::ContCheck:
930 case Op::ContCurrent:
931 case Op::ContKey:
932 case Op::ContValid:
933 case Op::ContGetReturn:
934 case Op::CreateCl:
935 case Op::DefCns:
936 case Op::Dir:
937 case Op::Div:
938 case Op::Double:
939 case Op::Dup:
940 case Op::EmptyG:
941 case Op::EmptyS:
942 case Op::FPushClsMethodD:
943 case Op::FPushClsMethod:
944 case Op::FPushClsMethodS:
945 case Op::FPushClsMethodSD:
946 case Op::FPushCtor:
947 case Op::FPushFunc:
948 case Op::FPushFuncD:
949 case Op::FPushFuncU:
950 case Op::FPushObjMethodD:
951 case Op::FuncNumArgs:
952 case Op::ResolveFunc:
953 case Op::ResolveClsMethod:
954 case Op::ResolveObjMethod:
955 case Op::False:
956 case Op::File:
957 case Op::GetMemoKeyL:
958 case Op::Idx:
959 case Op::InitThisLoc:
960 case Op::InstanceOf:
961 case Op::InstanceOfD:
962 case Op::IsLateBoundCls:
963 case Op::IsTypeStructC:
964 case Op::AsTypeStructC:
965 case Op::Int:
966 case Op::IssetG:
967 case Op::IssetL:
968 case Op::IssetS:
969 case Op::IterFree:
970 case Op::LIterFree:
971 case Op::LateBoundCls:
972 case Op::Method:
973 case Op::Mod:
974 case Op::Pow:
975 case Op::ClsRefName:
976 case Op::NativeImpl:
977 case Op::NewArray:
978 case Op::NewCol:
979 case Op::NewPair:
980 case Op::NewLikeArrayL:
981 case Op::NewMixedArray:
982 case Op::NewDictArray:
983 case Op::NewPackedArray:
984 case Op::NewVecArray:
985 case Op::NewKeysetArray:
986 case Op::NewVArray:
987 case Op::NewDArray:
988 case Op::NewObj:
989 case Op::NewObjD:
990 case Op::NewObjS:
991 case Op::NewRecord:
992 case Op::Not:
993 case Op::Null:
994 case Op::NullUninit:
995 case Op::OODeclExists:
996 case Op::Parent:
997 case Op::DiscardClsRef:
998 case Op::PopC:
999 case Op::PopV:
1000 case Op::PopU:
1001 case Op::PopU2:
1002 case Op::PopL:
1003 case Op::Print:
1004 case Op::PushL:
1005 case Op::RetC:
1006 case Op::RetCSuspended:
1007 case Op::Self:
1008 case Op::SetG:
1009 case Op::SetS:
1010 case Op::Shl:
1011 case Op::Shr:
1012 case Op::Silence:
1013 case Op::StaticLocDef:
1014 case Op::StaticLocCheck:
1015 case Op::StaticLocInit:
1016 case Op::String:
1017 case Op::This:
1018 case Op::Throw:
1019 case Op::True:
1020 case Op::Unbox:
1021 case Op::UnsetL:
1022 case Op::VGetG:
1023 case Op::VGetL:
1024 case Op::VGetS:
1025 case Op::VerifyParamType:
1026 case Op::VerifyParamTypeTS:
1027 case Op::VerifyRetTypeC:
1028 case Op::VerifyRetTypeTS:
1029 case Op::VerifyRetNonNullC:
1030 case Op::VerifyOutType:
1031 case Op::WHResult:
1032 case Op::Xor:
1033 case Op::BaseGC:
1034 case Op::BaseGL:
1035 case Op::BaseSC:
1036 case Op::BaseL:
1037 case Op::BaseC:
1038 case Op::BaseH:
1039 case Op::Dim:
1040 case Op::QueryM:
1041 case Op::VGetM:
1042 case Op::SetM:
1043 case Op::IncDecM:
1044 case Op::SetOpM:
1045 case Op::BindM:
1046 case Op::UnsetM:
1047 case Op::SetRangeM:
1048 case Op::MemoGet:
1049 case Op::MemoGetEager:
1050 case Op::MemoSet:
1051 case Op::MemoSetEager:
1052 case Op::RetM:
1053 case Op::Select:
1054 return false;
1056 // These are instructions that are always interp-one'd, or are always no-ops.
1057 case Op::Nop:
1058 case Op::EntryNop:
1059 case Op::Box:
1060 case Op::CGetCUNop:
1061 case Op::UGetCUNop:
1062 case Op::AddElemV:
1063 case Op::AddNewElemV:
1064 case Op::ClsCns:
1065 case Op::Exit:
1066 case Op::Fatal:
1067 case Op::Unwind:
1068 case Op::SetOpG:
1069 case Op::SetOpS:
1070 case Op::IncDecG:
1071 case Op::IncDecS:
1072 case Op::UnsetG:
1073 case Op::FPushObjMethod:
1074 case Op::Incl:
1075 case Op::InclOnce:
1076 case Op::Req:
1077 case Op::ReqOnce:
1078 case Op::ReqDoc:
1079 case Op::Eval:
1080 case Op::DefClsNop:
1081 case Op::DefTypeAlias:
1082 case Op::Catch:
1083 case Op::ChainFaults:
1084 case Op::ContAssignDelegate:
1085 case Op::ContEnterDelegate:
1086 case Op::YieldFromDelegate:
1087 case Op::ContUnsetDelegate:
1088 return true;
1091 always_assert_flog(0, "invalid opcode {}\n", static_cast<uint32_t>(ni.op()));
1094 bool instrBreaksProfileBB(const NormalizedInstruction* inst) {
1095 if (isFCallStar(inst->op())) return true;
1097 if (instrIsNonCallControlFlow(inst->op()) ||
1098 inst->op() == OpAwait || // may branch to scheduler and suspend execution
1099 inst->op() == OpAwaitAll || // similar to Await
1100 inst->op() == OpClsCnsD || // side exits if misses in the RDS
1101 inst->op() == OpVerifyParamTypeTS || // avoids combinatorial explosion
1102 inst->op() == OpVerifyParamType) { // with nullable types
1103 return true;
1105 // In profiling mode, don't trace through a control flow merge point,
1106 // however, allow inlining of default parameter funclets
1107 assertx(profData());
1108 if (profData()->anyBlockEndsAt(inst->func(), inst->offset()) &&
1109 !inst->func()->isEntry(inst->nextSk().offset())) {
1110 return true;
1112 return false;
1115 //////////////////////////////////////////////////////////////////////
1117 #define IMM_BLA(n) ni.immVec
1118 #define IMM_SLA(n) ni.immVec
1119 #define IMM_ILA(n) ni.immIters
1120 #define IMM_I32LA(n) ni.immVec
1121 #define IMM_VSA(n) ni.immVec
1122 #define IMM_IVA(n) ni.imm[n].u_IVA
1123 #define IMM_I64A(n) ni.imm[n].u_I64A
1124 #define IMM_LA(n) ni.imm[n].u_LA
1125 #define IMM_IA(n) ni.imm[n].u_IA
1126 #define IMM_CAR(n) ni.imm[n].u_CAR
1127 #define IMM_CAW(n) ni.imm[n].u_CAW
1128 #define IMM_DA(n) ni.imm[n].u_DA
1129 #define IMM_SA(n) ni.unit()->lookupLitstrId(ni.imm[n].u_SA)
1130 #define IMM_RATA(n) ni.imm[n].u_RATA
1131 #define IMM_AA(n) ni.unit()->lookupArrayId(ni.imm[n].u_AA)
1132 #define IMM_BA(n) ni.imm[n].u_BA
1133 #define IMM_OA_IMPL(n) ni.imm[n].u_OA
1134 #define IMM_OA(subop) (subop)IMM_OA_IMPL
1135 #define IMM_KA(n) ni.imm[n].u_KA
1136 #define IMM_LAR(n) ni.imm[n].u_LAR
1137 #define IMM_FCA(n) ni.imm[n].u_FCA
1139 #define ONE(x0) , IMM_##x0(0)
1140 #define TWO(x0,x1) , IMM_##x0(0), IMM_##x1(1)
1141 #define THREE(x0,x1,x2) , IMM_##x0(0), IMM_##x1(1), IMM_##x2(2)
1142 #define FOUR(x0,x1,x2,x3) , IMM_##x0(0), IMM_##x1(1), IMM_##x2(2), IMM_##x3(3)
1143 #define FIVE(x0,x1,x2,x3,x4) , IMM_##x0(0), IMM_##x1(1), IMM_##x2(2), IMM_##x3(3), IMM_##x4(4)
1144 #define NA /* */
1146 static void translateDispatch(irgen::IRGS& irgs,
1147 const NormalizedInstruction& ni) {
1148 #define O(nm, imms, ...) case Op::nm: irgen::emit##nm(irgs imms); return;
1149 switch (ni.op()) { OPCODES }
1150 #undef O
1153 #undef FIVE
1154 #undef FOUR
1155 #undef THREE
1156 #undef TWO
1157 #undef ONE
1158 #undef NA
1160 #undef IMM_BLA
1161 #undef IMM_SLA
1162 #undef IMM_ILA
1163 #undef IMM_I32LA
1164 #undef IMM_IVA
1165 #undef IMM_I64A
1166 #undef IMM_LA
1167 #undef IMM_IA
1168 #undef IMM_CAR
1169 #undef IMM_CAW
1170 #undef IMM_DA
1171 #undef IMM_SA
1172 #undef IMM_RATA
1173 #undef IMM_AA
1174 #undef IMM_BA
1175 #undef IMM_OA_IMPL
1176 #undef IMM_OA
1177 #undef IMM_VSA
1178 #undef IMM_KA
1179 #undef IMM_LAR
1180 #undef IMM_FCA
1182 //////////////////////////////////////////////////////////////////////
1184 namespace {
1186 Type flavorToType(FlavorDesc f) {
1187 switch (f) {
1188 case NOV: not_reached();
1190 case CV: return TCell; // TODO(#3029148) this could be InitCell
1191 case CUV: return TCell;
1192 case UV: return TUninit;
1193 case VV: return TBoxedInitCell;
1194 case CVV: case CVUV: return TGen;
1196 not_reached();
1201 void translateInstr(irgen::IRGS& irgs, const NormalizedInstruction& ni,
1202 bool /*checkOuterTypeOnly*/, bool /*firstInst*/
1204 irgen::prepareForNextHHBC(
1205 irgs,
1206 &ni,
1207 ni.source,
1208 ni.endsRegion && !irgen::isInlining(irgs)
1211 const Func* builtinFunc = nullptr;
1212 if (ni.op() == OpFCallBuiltin) {
1213 auto str = ni.m_unit->lookupLitstrId(ni.imm[2].u_SA);
1214 builtinFunc = Unit::lookupBuiltin(str);
1216 auto pc = ni.pc();
1217 for (auto i = 0, num = instrNumPops(pc); i < num; ++i) {
1218 if (ni.op() == OpFCall && instrInputFlavor(pc, i) == UV) {
1219 // This is a hack to deal with the fact that this instruction is
1220 // actually popping an ActRec in the middle of its "pops." We could
1221 // assert on the Uninit values on the stack, but the call is going to
1222 // override them anyway so it's not worth guarding on them.
1223 break;
1225 auto const type =
1226 !builtinFunc ? flavorToType(instrInputFlavor(pc, i)) :
1227 builtinFunc->byRef(num - i - 1) ? TGen : TCell;
1228 irgen::assertTypeStack(irgs, BCSPRelOffset{i}, type);
1231 FTRACE(1, "\nTranslating {}: {} with state:\n{}\n",
1232 ni.offset(), ni, show(irgs));
1234 irgen::ringbufferEntry(irgs, Trace::RBTypeBytecodeStart, ni.source, 2);
1235 irgen::implIncStat(irgs, Stats::Instr_TC);
1236 if (Stats::enableInstrCount()) {
1237 irgen::implIncStat(irgs, Stats::opToTranslStat(ni.op()));
1240 if (isAlwaysNop(ni)) return;
1241 if (ni.interp || RuntimeOption::EvalJitAlwaysInterpOne) {
1242 irgen::interpOne(irgs, ni);
1243 return;
1246 if (ni.forceSurpriseCheck) {
1247 surpriseCheck(irgs);
1250 translateDispatch(irgs, ni);
1252 FTRACE(3, "\nTranslated {}: {} with state:\n{}\n",
1253 ni.offset(), ni, show(irgs));
1256 //////////////////////////////////////////////////////////////////////
1258 const Func* lookupImmutableMethod(const Class* cls, const StringData* name,
1259 bool& magicCall, bool staticLookup,
1260 const Func* ctxFunc,
1261 bool exactClass) {
1262 if (!cls) return nullptr;
1263 if (cls->attrs() & AttrInterface) return nullptr;
1264 auto ctx = ctxFunc->cls();
1265 const Func* func;
1266 LookupResult res = staticLookup ?
1267 lookupClsMethod(func, cls, name, nullptr, ctx, false) :
1268 lookupObjMethod(func, cls, name, ctx, false);
1270 if (res == LookupResult::MethodNotFound) return nullptr;
1272 if (func->isAbstract() && exactClass) return nullptr;
1274 assertx(res == LookupResult::MethodFoundWithThis ||
1275 res == LookupResult::MethodFoundNoThis ||
1276 (staticLookup ?
1277 res == LookupResult::MagicCallStaticFound :
1278 res == LookupResult::MagicCallFound));
1280 magicCall =
1281 res == LookupResult::MagicCallStaticFound ||
1282 res == LookupResult::MagicCallFound;
1284 if (staticLookup) {
1285 if (magicCall) {
1286 if (ctx && !ctxFunc->isStatic() &&
1287 (ctx->classof(cls) || cls->classof(ctx))) {
1288 // we might need to call __call instead
1289 return nullptr;
1291 if (!exactClass && !(cls->attrs() & AttrNoOverride)) {
1292 // there might be a derived class which defines the method
1293 return nullptr;
1296 } else if (!exactClass && !(func->attrs() & AttrPrivate)) {
1297 if (magicCall) {
1298 if (!(cls->attrs() & AttrNoOverride)) {
1299 return nullptr;
1303 return func;
1306 const Func* lookupImmutableCtor(const Class* cls, const Class* ctx) {
1307 if (!cls) return nullptr;
1309 auto const func = cls->getCtor();
1310 if (func && !(func->attrs() & AttrPublic)) {
1311 auto fcls = func->cls();
1312 if (fcls != ctx) {
1313 if (!ctx) return nullptr;
1314 if ((func->attrs() & AttrPrivate) ||
1315 !(ctx->classof(fcls) || fcls->classof(ctx))) {
1316 return nullptr;
1321 return func;
1324 ImmutableFuncLookup lookupImmutableFunc(const Unit* unit,
1325 const StringData* name) {
1326 // Trust nothing if we can rename functions
1327 if (RuntimeOption::EvalJitEnableRenameFunction) return {nullptr, true};
1329 auto const ne = NamedEntity::get(name);
1330 if (auto const f = ne->uniqueFunc()) {
1331 // We have an unique function. However, it may be conditionally defined
1332 // (!f->top()) or interceptable, which means we can't use it directly.
1333 if (!f->top() || f->isInterceptable()) return {nullptr, true};
1334 // We can use this function. If its persistent (which means its unit's
1335 // pseudo-main is trivial), its safe to use unconditionally. If its defined
1336 // in the same unit as the caller, its also safe to use unconditionally. By
1337 // virtue of the fact that we're already in the unit, we know its already
1338 // defined.
1339 if (f->isPersistent() || f->unit() == unit) return {f, false};
1340 // Use the function, but ensure its unit is loaded.
1341 return {f, true};
1344 // There's no unique function currently known for this name. However, if the
1345 // current unit defines a single top-level function with this name, we can use
1346 // it. Why? When this unit is loaded, either it successfully defined the
1347 // function, in which case its the correct function, or it fataled, which
1348 // means this code won't run anyways.
1350 Func* found = nullptr;
1351 for (auto& f : unit->funcs()) {
1352 if (f->isPseudoMain()) continue;
1353 if (!f->name()->isame(name)) continue;
1354 if (found) {
1355 // Function with duplicate name
1356 found = nullptr;
1357 break;
1359 found = f;
1362 if (found && found->top() && !found->isInterceptable()) {
1363 return {found, false};
1365 return {nullptr, true};
1368 ///////////////////////////////////////////////////////////////////////////////
1371 void invalidatePath(const std::string& path) {
1372 TRACE(1, "invalidatePath: abspath %s\n", path.c_str());
1373 assertx(path.size() >= 1 && path[0] == '/');
1374 Treadmill::enqueue([path] {
1376 * inotify saw this path change. Now poke the unit loader; it will
1377 * notice the underlying php file has changed.
1379 * We don't actually need to *do* anything with the Unit* from
1380 * this lookup; since the path has changed, the file we'll get out is
1381 * going to be some new file, not the old file that needs invalidation.
1383 String spath(path);
1384 lookupUnit(spath.get(), "", nullptr /* initial_opt */,
1385 Native::s_noNativeFuncs);
1389 ///////////////////////////////////////////////////////////////////////////////