Remove dead includes in hphp/runtime/vm
[hiphop-php.git] / hphp / runtime / vm / jit / translator.cpp
blobd92f80efd8b26618e5906106a42781ee64404984
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 <stdint.h>
23 #include <algorithm>
24 #include <memory>
25 #include <string>
26 #include <utility>
27 #include <vector>
29 #include <folly/Conv.h>
30 #include <folly/MapUtil.h>
32 #include "hphp/util/arch.h"
33 #include "hphp/util/ringbuffer.h"
34 #include "hphp/util/timer.h"
35 #include "hphp/util/trace.h"
37 #include "hphp/runtime/base/repo-auth-type-codec.h"
38 #include "hphp/runtime/base/runtime-option.h"
39 #include "hphp/runtime/base/stats.h"
40 #include "hphp/runtime/base/unit-cache.h"
41 #include "hphp/runtime/ext/generator/ext_generator.h"
42 #include "hphp/runtime/vm/bc-pattern.h"
43 #include "hphp/runtime/vm/bytecode.h"
44 #include "hphp/runtime/vm/hhbc-codec.h"
45 #include "hphp/runtime/vm/hhbc.h"
46 #include "hphp/runtime/vm/method-lookup.h"
47 #include "hphp/runtime/vm/runtime.h"
48 #include "hphp/runtime/vm/treadmill.h"
49 #include "hphp/runtime/vm/type-profile.h"
51 #include "hphp/runtime/vm/jit/inlining-decider.h"
52 #include "hphp/runtime/vm/jit/ir-unit.h"
53 #include "hphp/runtime/vm/jit/irgen-basic.h"
54 #include "hphp/runtime/vm/jit/irgen-control.h"
55 #include "hphp/runtime/vm/jit/irgen-exit.h"
56 #include "hphp/runtime/vm/jit/irgen-interpone.h"
57 #include "hphp/runtime/vm/jit/irgen.h"
58 #include "hphp/runtime/vm/jit/normalized-instruction.h"
59 #include "hphp/runtime/vm/jit/print.h"
60 #include "hphp/runtime/vm/jit/prof-data.h"
61 #include "hphp/runtime/vm/jit/punt.h"
62 #include "hphp/runtime/vm/jit/region-selection.h"
63 #include "hphp/runtime/vm/jit/translate-region.h"
64 #include "hphp/runtime/vm/jit/translator-inline.h"
65 #include "hphp/runtime/vm/jit/type.h"
68 TRACE_SET_MOD(trans);
70 namespace HPHP { namespace jit {
71 ///////////////////////////////////////////////////////////////////////////////
74 * NB: this opcode structure is sparse; it cannot just be indexed by
75 * opcode.
77 using namespace InstrFlags;
78 static const struct {
79 Op op;
80 InstrInfo info;
81 } instrInfoSparse [] = {
83 // Op Inputs Outputs OutputTypes
84 // -- ------ ------- -----------
86 /*** 1. Basic instructions ***/
88 { OpPopC, {Stack1|
89 DontGuardStack1, None, OutNone }},
90 { OpPopU, {Stack1|
91 DontGuardStack1, None, OutNone }},
92 { OpPopU2, {StackTop2|
93 DontGuardAny, Stack1, OutSameAsInput1 }},
94 { OpPopFrame, {StackN|
95 DontGuardAny, StackN, OutUnknown }},
96 { OpPopL, {Stack1|Local, Local, OutNone }},
97 { OpDup, {Stack1, StackTop2, OutSameAsInput1 }},
99 /*** 2. Literal and constant instructions ***/
101 { OpNull, {None, Stack1, OutNull }},
102 { OpNullUninit, {None, Stack1, OutNullUninit }},
103 { OpTrue, {None, Stack1, OutBooleanImm }},
104 { OpFalse, {None, Stack1, OutBooleanImm }},
105 { OpInt, {None, Stack1, OutInt64 }},
106 { OpDouble, {None, Stack1, OutDouble }},
107 { OpString, {None, Stack1, OutStringImm }},
108 { OpArray, {None, Stack1, OutArrayImm }},
109 { OpDict, {None, Stack1, OutDictImm }},
110 { OpKeyset, {None, Stack1, OutKeysetImm }},
111 { OpVec, {None, Stack1, OutVecImm }},
112 { OpNewArray, {None, Stack1, OutArray }},
113 { OpNewMixedArray, {None, Stack1, OutArray }},
114 { OpNewDictArray, {None, Stack1, OutDict }},
115 { OpNewLikeArrayL, {Local, Stack1, OutArray }},
116 { OpNewPackedArray, {StackN, Stack1, OutArray }},
117 { OpNewStructArray, {StackN, Stack1, OutArray }},
118 { OpNewStructDArray,{StackN, Stack1, OutArray }},
119 { OpNewStructDict, {StackN, Stack1, OutDict }},
120 { OpNewVecArray, {StackN, Stack1, OutVec }},
121 { OpNewKeysetArray, {StackN, Stack1, OutKeyset }},
122 { OpNewVArray, {StackN, Stack1, OutArray }},
123 { OpNewDArray, {None, Stack1, OutArray }},
124 { OpAddElemC, {StackTop3, Stack1, OutModifiedInput3 }},
125 { OpAddNewElemC, {StackTop2, Stack1, OutModifiedInput2 }},
126 { OpNewCol, {None, Stack1, OutObject }},
127 { OpNewPair, {StackTop2, Stack1, OutObject }},
128 { OpNewRecord, {StackN, Stack1, OutRecord }},
129 { OpNewRecordArray, {StackN, Stack1, OutArray }},
130 { OpColFromArray, {Stack1, Stack1, OutObject }},
131 { OpCnsE, {None, Stack1, OutCns }},
132 { OpClsCns, {Stack1, Stack1, OutUnknown }},
133 { OpClsCnsD, {None, Stack1, OutUnknown }},
134 { OpFile, {None, Stack1, OutString }},
135 { OpDir, {None, Stack1, OutString }},
136 { OpMethod, {None, Stack1, OutString }},
137 { OpClassName, {Stack1, Stack1, OutString }},
138 { OpFuncCred, {None, Stack1, OutObject }},
140 /*** 3. Operator instructions ***/
142 /* Binary string */
143 { OpConcat, {StackTop2, Stack1, OutString }},
144 { OpConcatN, {StackN, Stack1, OutString }},
145 /* Arithmetic ops */
146 { OpAdd, {StackTop2, Stack1, OutArith }},
147 { OpSub, {StackTop2, Stack1, OutArith }},
148 { OpMul, {StackTop2, Stack1, OutArith }},
149 /* Arithmetic ops that overflow ints to floats */
150 { OpAddO, {StackTop2, Stack1, OutArithO }},
151 { OpSubO, {StackTop2, Stack1, OutArithO }},
152 { OpMulO, {StackTop2, Stack1, OutArithO }},
153 /* Div and mod might return boolean false. Sigh. */
154 { OpDiv, {StackTop2, Stack1, OutUnknown }},
155 { OpMod, {StackTop2, Stack1, OutUnknown }},
156 { OpPow, {StackTop2, Stack1, OutUnknown }},
157 /* Logical ops */
158 { OpXor, {StackTop2, Stack1, OutBoolean }},
159 { OpNot, {Stack1, Stack1, OutBoolean }},
160 { OpSame, {StackTop2, Stack1, OutBoolean }},
161 { OpNSame, {StackTop2, Stack1, OutBoolean }},
162 { OpEq, {StackTop2, Stack1, OutBoolean }},
163 { OpNeq, {StackTop2, Stack1, OutBoolean }},
164 { OpLt, {StackTop2, Stack1, OutBoolean }},
165 { OpLte, {StackTop2, Stack1, OutBoolean }},
166 { OpGt, {StackTop2, Stack1, OutBoolean }},
167 { OpGte, {StackTop2, Stack1, OutBoolean }},
168 { OpCmp, {StackTop2, Stack1, OutInt64 }},
169 /* Bitwise ops */
170 { OpBitAnd, {StackTop2, Stack1, OutBitOp }},
171 { OpBitOr, {StackTop2, Stack1, OutBitOp }},
172 { OpBitXor, {StackTop2, Stack1, OutBitOp }},
173 { OpBitNot, {Stack1, Stack1, OutBitOp }},
174 { OpShl, {StackTop2, Stack1, OutInt64 }},
175 { OpShr, {StackTop2, Stack1, OutInt64 }},
176 /* Cast instructions */
177 { OpCastBool, {Stack1, Stack1, OutBoolean }},
178 { OpCastInt, {Stack1, Stack1, OutInt64 }},
179 { OpCastDouble, {Stack1, Stack1, OutDouble }},
180 { OpCastString, {Stack1, Stack1, OutString }},
181 { OpCastArray, {Stack1, Stack1, OutArray }},
182 { OpCastDict, {Stack1, Stack1, OutDict }},
183 { OpCastKeyset, {Stack1, Stack1, OutKeyset }},
184 { OpCastVec, {Stack1, Stack1, OutVec }},
185 { OpCastVArray, {Stack1, Stack1, OutArray }},
186 { OpCastDArray, {Stack1, Stack1, OutArray }},
187 { OpDblAsBits, {Stack1, Stack1, OutInt64 }},
188 { OpInstanceOf, {StackTop2, Stack1, OutBoolean }},
189 { OpInstanceOfD, {Stack1, Stack1, OutPredBool }},
190 { OpIsLateBoundCls,{Stack1, Stack1, OutBoolean }},
191 { OpIsTypeStructC,{StackTop2, Stack1, OutBoolean }},
192 { OpThrowAsTypeStructException,
193 {StackTop2, None, OutNone }},
194 { OpCombineAndResolveTypeStruct,
195 {StackN, Stack1, OutDArray }},
196 { OpSelect, {StackTop3, Stack1, OutUnknown }},
197 { OpPrint, {Stack1, Stack1, OutInt64 }},
198 { OpClone, {Stack1, Stack1, OutObject }},
199 { OpExit, {Stack1, Stack1, OutNull }},
200 { OpFatal, {Stack1, None, OutNone }},
202 /*** 4. Control flow instructions ***/
204 { OpJmp, {None, None, OutNone }},
205 { OpJmpNS, {None, None, OutNone }},
206 { OpJmpZ, {Stack1, None, OutNone }},
207 { OpJmpNZ, {Stack1, None, OutNone }},
208 { OpSwitch, {Stack1, None, OutNone }},
209 { OpSSwitch, {Stack1, None, OutNone }},
211 * RetC and RetM are special. Their manipulation of the runtime stack are
212 * outside the boundaries of the tracelet abstraction; since they always end
213 * a basic block, they behave more like "glue" between BBs than the
214 * instructions in the body of a BB.
216 * RetC and RetM consume values from the stack, and these values' types needs
217 * to be known at compile-time.
219 { OpRetC, {None, None, OutNone }},
220 { OpRetM, {None, None, OutNone }},
221 { OpRetCSuspended,
222 {None, None, OutNone }},
223 { OpThrow, {Stack1, None, OutNone }},
225 /*** 5. Get instructions ***/
227 { OpCGetL, {Local, Stack1, OutCInputL }},
228 { OpCGetL2, {Stack1|DontGuardStack1|
229 Local, StackIns1, OutCInputL }},
230 { OpCGetQuietL, {Local, Stack1, OutCInputL }},
231 // In OpCUGetL we rely on OutCInputL returning TCell (which covers Uninit
232 // values) instead of TInitCell.
233 { OpCUGetL, {Local, Stack1, OutCInputL }},
234 { OpPushL, {Local, Stack1|Local, OutCInputL }},
235 { OpCGetG, {Stack1, Stack1, OutUnknown }},
236 { OpCGetS, {StackTop2, Stack1, OutUnknown }},
237 { OpClassGetC, {Stack1, Stack1, OutClass }},
238 { OpClassGetTS, {Stack1, StackTop2, OutUnknown }},
240 /*** 6. Isset, Empty, and type querying instructions ***/
242 { OpAKExists, {StackTop2, Stack1, OutBoolean }},
243 { OpIssetL, {Local, Stack1, OutBoolean }},
244 { OpIssetG, {Stack1, Stack1, OutBoolean }},
245 { OpIssetS, {StackTop2, Stack1, OutBoolean }},
246 { OpEmptyL, {Local, Stack1, OutBoolean }},
247 { OpEmptyG, {Stack1, Stack1, OutBoolean }},
248 { OpEmptyS, {StackTop2, Stack1, OutBoolean }},
249 { OpIsTypeC, {Stack1|
250 DontGuardStack1, Stack1, OutBoolean }},
251 { OpIsTypeL, {Local, Stack1, OutIsTypeL }},
253 /*** 7. Mutator instructions ***/
255 { OpSetL, {Stack1|Local, Stack1|Local, OutSameAsInput1 }},
256 { OpSetG, {StackTop2, Stack1, OutSameAsInput1 }},
257 { OpSetS, {StackTop3, Stack1, OutSameAsInput1 }},
258 { OpSetOpL, {Stack1|Local, Stack1|Local, OutSetOp }},
259 { OpSetOpG, {StackTop2, Stack1, OutUnknown }},
260 { OpSetOpS, {StackTop3, Stack1, OutUnknown }},
261 { OpIncDecL, {Local, Stack1|Local, OutIncDec }},
262 { OpIncDecG, {Stack1, Stack1, OutUnknown }},
263 { OpIncDecS, {StackTop2, Stack1, OutUnknown }},
264 { OpUnsetL, {Local, Local, OutNone }},
265 { OpUnsetG, {Stack1, None, OutNone }},
267 /*** 8. Call instructions ***/
269 { OpNewObj, {Stack1, Stack1, OutObject }},
270 { OpNewObjR, {StackTop2, Stack1, OutObject }},
271 { OpNewObjD, {None, Stack1, OutObject }},
272 { OpNewObjRD, {Stack1, Stack1, OutObject }},
273 { OpNewObjS, {None, Stack1, OutObject }},
274 { OpLockObj, {Stack1, Stack1, OutSameAsInput1 }},
277 * FCall* are special. Like the Ret* instructions, their manipulation of
278 * the runtime stack are outside the boundaries of the tracelet abstraction.
280 { OpFCallClsMethod,
281 {StackTop2, StackN, OutUnknown }},
282 { OpFCallClsMethodD,
283 {None, StackN, OutUnknown }},
284 { OpFCallClsMethodS,
285 {Stack1, StackN, OutUnknown }},
286 { OpFCallClsMethodSD,
287 {None, StackN, OutUnknown }},
288 { OpFCallCtor, {None, StackN, OutUnknown }},
289 { OpFCallFunc, {Stack1, StackN, OutUnknown }},
290 { OpFCallFuncD, {None, StackN, OutUnknown }},
291 { OpFCallObjMethod,
292 {Stack1, StackN, OutUnknown }},
293 { OpFCallObjMethodD,
294 {None, StackN, OutUnknown }},
295 { OpFCallBuiltin,{BStackN|DontGuardAny,
296 StackN, OutUnknown }},
298 /*** 11. Iterator instructions ***/
300 { OpIterInit, {Stack1, Local, OutUnknown }},
301 { OpLIterInit, {Local, Local, OutUnknown }},
302 { OpIterNext, {None, Local, OutUnknown }},
303 { OpLIterNext, {Local, Local, OutUnknown }},
304 { OpIterFree, {None, None, OutNone }},
305 { OpLIterFree, {Local, None, OutNone }},
306 { OpIterBreak, {Local, None, OutNone }},
308 /*** 12. Include, eval, and define instructions ***/
310 { OpIncl, {Stack1, Stack1, OutUnknown }},
311 { OpInclOnce, {Stack1, Stack1, OutUnknown }},
312 { OpReq, {Stack1, Stack1, OutUnknown }},
313 { OpReqOnce, {Stack1, Stack1, OutUnknown }},
314 { OpReqDoc, {Stack1, Stack1, OutUnknown }},
315 { OpEval, {Stack1, Stack1, OutUnknown }},
316 { OpDefTypeAlias,{None, None, OutNone }},
317 { OpDefCls, {None, None, OutNone }},
318 { OpDefRecord, {None, None, OutNone }},
319 { OpDefCns, {Stack1, Stack1, OutBoolean }},
320 { OpAliasCls, {Stack1, Stack1, OutBoolean }},
322 /*** 13. Miscellaneous instructions ***/
324 { OpThis, {None, Stack1, OutThisObject }},
325 { OpBareThis, {None, Stack1, OutUnknown }},
326 { OpCheckThis, {This, None, OutNone }},
327 { OpInitThisLoc,
328 {None, Local, OutUnknown }},
329 { OpChainFaults, {StackTop2, Stack1, OutObject }},
330 { OpVerifyParamType,
331 {Local, Local, OutUnknown }},
332 { OpVerifyParamTypeTS,
333 {Local|Stack1, Local, OutUnknown }},
334 { OpVerifyRetTypeC,
335 {Stack1, Stack1, OutSameAsInput1 }},
336 { OpVerifyRetTypeTS,
337 {StackTop2, Stack1, OutSameAsInput2 }},
338 { OpVerifyRetNonNullC,
339 {Stack1, Stack1, OutSameAsInput1 }},
340 { OpVerifyOutType,
341 {Stack1, Stack1, OutSameAsInput1 }},
342 { OpOODeclExists,
343 {StackTop2, Stack1, OutBoolean }},
344 { OpSelf, {None, Stack1, OutClass }},
345 { OpParent, {None, Stack1, OutClass }},
346 { OpLateBoundCls,{None, Stack1, OutClass }},
347 { OpRecordReifiedGeneric,
348 {Stack1, Stack1, OutVArray }},
349 { OpCheckReifiedGenericMismatch,
350 {Stack1, None, OutNone }},
351 { OpNativeImpl, {None, None, OutNone }},
352 { OpCreateCl, {BStackN, Stack1, OutObject }},
353 { OpIdx, {StackTop3, Stack1, OutUnknown }},
354 { OpArrayIdx, {StackTop3, Stack1, OutUnknown }},
355 { OpCheckProp, {None, Stack1, OutBoolean }},
356 { OpInitProp, {Stack1, None, OutNone }},
357 { OpSilence, {Local|DontGuardAny,
358 Local, OutNone }},
359 { OpAssertRATL, {None, None, OutNone }},
360 { OpAssertRATStk,{None, None, OutNone }},
361 { OpBreakTraceHint,{None, None, OutNone }},
362 { OpGetMemoKeyL, {Local, Stack1, OutUnknown }},
363 { OpResolveFunc, {None, Stack1, OutFunc }},
364 { OpResolveObjMethod,
365 {StackTop2, Stack1, OutVArray }},
366 { OpResolveClsMethod,
367 {StackTop2, Stack1, OutClsMeth }},
369 /*** 14. Generator instructions ***/
371 { OpCreateCont, {None, Stack1, OutNull }},
372 { OpContEnter, {Stack1, Stack1, OutUnknown }},
373 { OpContRaise, {Stack1, Stack1, OutUnknown }},
374 { OpYield, {Stack1, Stack1, OutUnknown }},
375 { OpYieldK, {StackTop2, Stack1, OutUnknown }},
376 { OpContAssignDelegate,
377 {Stack1, None, OutNone }},
378 { OpContEnterDelegate,
379 {Stack1, None, OutNone }},
380 { OpYieldFromDelegate,
381 {None, Stack1, OutUnknown }},
382 { OpContUnsetDelegate,
383 {None, None, OutNone }},
384 { OpContCheck, {None, None, OutNone }},
385 { OpContValid, {None, Stack1, OutBoolean }},
386 { OpContKey, {None, Stack1, OutUnknown }},
387 { OpContCurrent, {None, Stack1, OutUnknown }},
388 { OpContGetReturn,
389 {None, Stack1, OutUnknown }},
391 /*** 15. Async functions instructions ***/
393 { OpWHResult, {Stack1, Stack1, OutUnknown }},
394 { OpAwait, {Stack1, Stack1, OutUnknown }},
395 { OpAwaitAll, {LocalRange, Stack1, OutNull }},
397 /*** 16. Member instructions ***/
399 { OpBaseGC, {StackI, MBase, OutNone }},
400 { OpBaseGL, {Local, MBase, OutNone }},
401 { OpBaseSC, {StackI|StackI2, MBase, OutNone }},
402 { OpBaseL, {Local, MBase, OutNone }},
403 { OpBaseC, {StackI, MBase, OutNone }},
404 { OpBaseH, {None, MBase, OutNone }},
405 { OpDim, {MBase|MKey, MBase, OutNone }},
406 { OpQueryM, {BStackN|MBase|MKey,
407 Stack1, OutUnknown }},
408 { OpSetM, {Stack1|BStackN|MBase|MKey,
409 Stack1, OutUnknown }},
410 { OpSetRangeM, {BStackN|MBase, None, OutNone }},
411 { OpIncDecM, {BStackN|MBase|MKey,
412 Stack1, OutUnknown }},
413 { OpSetOpM, {Stack1|BStackN|MBase|MKey,
414 Stack1, OutUnknown }},
415 { OpUnsetM, {BStackN|MBase|MKey,
416 None, OutNone }},
417 { OpMemoGet, {LocalRange, None, OutUnknown }},
418 { OpMemoGetEager,{LocalRange, None, OutUnknown }},
419 { OpMemoSet, {Stack1|LocalRange,
420 Stack1, OutSameAsInput1 }},
421 { OpMemoSetEager, {Stack1|LocalRange,
422 Stack1, OutSameAsInput1 }},
425 namespace {
426 hphp_hash_map<Op, InstrInfo> instrInfo;
427 bool instrInfoInited;
430 void initInstrInfo() {
431 if (!instrInfoInited) {
432 for (auto& info : instrInfoSparse) {
433 instrInfo[info.op] = info.info;
435 if (!RuntimeOption::EvalCheckReturnTypeHints) {
436 auto& ii = instrInfo[OpVerifyRetTypeC];
437 ii.in = ii.out = None;
438 ii.type = OutNone;
440 auto& ii2 = instrInfo[OpVerifyRetTypeTS];
441 ii2.in = Stack1;
442 ii2.out = None;
443 ii2.type = OutNone;
445 instrInfoInited = true;
449 const InstrInfo& getInstrInfo(Op op) {
450 assertx(instrInfoInited);
451 return instrInfo[op];
454 namespace {
455 int64_t countOperands(uint64_t mask) {
456 const uint64_t ignore = Local | Iter | DontGuardStack1 |
457 DontGuardAny | This | MBase | StackI | StackI2 | MKey | LocalRange |
458 DontGuardBase;
459 mask &= ~ignore;
461 static const uint64_t counts[][2] = {
462 {Stack3, 1},
463 {Stack2, 1},
464 {Stack1, 1},
465 {StackIns1, 2},
468 int64_t count = 0;
469 for (auto const& pair : counts) {
470 if (mask & pair[0]) {
471 count += pair[1];
472 mask &= ~pair[0];
475 assertx(mask == 0);
476 return count;
480 int64_t getStackPopped(PC pc) {
481 auto const op = peek_op(pc);
482 switch (op) {
483 case Op::FCallClsMethod:
484 case Op::FCallClsMethodD:
485 case Op::FCallClsMethodS:
486 case Op::FCallClsMethodSD:
487 case Op::FCallCtor:
488 case Op::FCallFunc:
489 case Op::FCallFuncD:
490 case Op::FCallObjMethod:
491 case Op::FCallObjMethodD: {
492 auto const fca = getImm(pc, 0).u_FCA;
493 auto const nin = countOperands(getInstrInfo(op).in);
494 return nin + fca.numInputs() + kNumActRecCells - 1 + fca.numRets;
497 case Op::QueryM:
498 case Op::IncDecM:
499 case Op::UnsetM:
500 case Op::NewPackedArray:
501 case Op::NewVecArray:
502 case Op::NewKeysetArray:
503 case Op::NewVArray:
504 case Op::ConcatN:
505 case Op::CombineAndResolveTypeStruct:
506 case Op::CreateCl:
507 return getImm(pc, 0).u_IVA;
509 case Op::FCallBuiltin:
510 return getImm(pc, 0).u_IVA + getImm(pc, 2).u_IVA;
512 case Op::PopFrame:
513 return getImm(pc, 0).u_IVA + 3;
515 case Op::SetM:
516 case Op::SetOpM:
517 return getImm(pc, 0).u_IVA + 1;
519 case Op::NewRecord:
520 case Op::NewRecordArray:
521 case Op::NewStructArray:
522 case Op::NewStructDArray:
523 case Op::NewStructDict:
524 return getImmVector(pc).size();
526 default:
527 break;
530 uint64_t mask = getInstrInfo(op).in;
532 // All instructions with these properties are handled above
533 assertx((mask & (StackN | BStackN)) == 0);
535 return countOperands(mask);
538 int64_t getStackPushed(PC pc) {
539 auto const op = peek_op(pc);
540 switch (op) {
541 case Op::FCallClsMethod:
542 case Op::FCallClsMethodD:
543 case Op::FCallClsMethodS:
544 case Op::FCallClsMethodSD:
545 case Op::FCallCtor:
546 case Op::FCallFunc:
547 case Op::FCallFuncD:
548 case Op::FCallObjMethod:
549 case Op::FCallObjMethodD:
550 return getImm(pc, 0).u_FCA.numRets;
551 case Op::FCallBuiltin:
552 return getImm(pc, 2).u_IVA + 1;
553 case Op::PopFrame:
554 return getImm(pc, 0).u_IVA;
555 default:
556 break;
559 uint64_t mask = getInstrInfo(op).out;
561 // All instructions with these properties are handled above
562 assertx((mask & (StackN | BStackN)) == 0);
564 return countOperands(mask);
567 bool isAlwaysNop(const NormalizedInstruction& ni) {
568 switch (ni.op()) {
569 case Op::DefClsNop:
570 case Op::Nop:
571 case Op::CGetCUNop:
572 case Op::UGetCUNop:
573 case Op::EntryNop:
574 return true;
575 case Op::VerifyRetTypeC:
576 return !RuntimeOption::EvalCheckReturnTypeHints;
577 default:
578 return false;
582 #define NA
583 #define ONE(a) a(0)
584 #define TWO(a, b) a(0) b(1)
585 #define THREE(a, b, c) a(0) b(1) c(2)
586 #define FOUR(a, b, c, d) a(0) b(1) c(2) d(3)
587 #define FIVE(a, b, c, d, e) a(0) b(1) c(2) d(3) e(4)
588 #define SIX(a, b, c, d, e, f) a(0) b(1) c(2) d(3) e(4) f(5)
589 // Iterator bytecodes are handled specially here
590 #define LA(n) assertx(idx == 0xff); idx = n;
591 #define MA(n)
592 #define BLA(n)
593 #define SLA(n)
594 #define ILA(n)
595 #define IVA(n)
596 #define I64A(n)
597 #define IA(n)
598 #define DA(n)
599 #define SA(n)
600 #define AA(n)
601 #define RATA(n)
602 #define BA(n)
603 #define OA(op) BA
604 #define VSA(n)
605 #define KA(n)
606 #define LAR(n)
607 #define ITA(n)
608 #define FCA(n)
609 #define O(name, imm, ...) case Op::name: imm break;
611 size_t localImmIdx(Op op) {
612 switch (op) {
613 case Op::LIterInit:
614 case Op::LIterNext:
615 case Op::LIterFree:
616 return 1;
617 default:
618 break;
621 size_t idx = 0xff;
622 switch (op) {
623 OPCODES
625 assertx(idx != 0xff);
626 return idx;
629 size_t memberKeyImmIdx(Op op) {
630 size_t idx = 0xff;
631 switch (op) {
632 #undef LA
633 #undef KA
634 #define LA(n)
635 #define KA(n) assertx(idx == 0xff); idx = n;
636 OPCODES
638 assertx(idx != 0xff);
639 return idx;
642 #undef ONE
643 #undef TWO
644 #undef THREE
645 #undef FOUR
646 #undef FIVE
647 #undef SIX
648 #undef LA
649 #undef MA
650 #undef BLA
651 #undef SLA
652 #undef ILA
653 #undef IVA
654 #undef I64A
655 #undef IA
656 #undef DA
657 #undef SA
658 #undef AA
659 #undef RATA
660 #undef BA
661 #undef OA
662 #undef VSA
663 #undef KA
664 #undef LAR
665 #undef ITA
666 #undef FCA
667 #undef O
670 * Get location metadata for the inputs of `ni'.
672 InputInfoVec getInputs(const NormalizedInstruction& ni, FPInvOffset bcSPOff) {
673 InputInfoVec inputs;
674 if (isAlwaysNop(ni)) return inputs;
676 always_assert_flog(
677 instrInfo.count(ni.op()),
678 "Invalid opcode in getInputsImpl: {}\n",
679 opcodeToName(ni.op())
681 UNUSED auto const sk = ni.source;
683 auto const& info = instrInfo[ni.op()];
684 auto const flags = info.in;
685 auto stackOff = bcSPOff;
687 if (flags & Stack1) {
688 SKTRACE(1, sk, "getInputs: Stack1 %d\n", stackOff.offset);
689 inputs.emplace_back(Location::Stack { stackOff-- });
691 if (flags & DontGuardStack1) inputs.back().dontGuard = true;
693 if (flags & Stack2) {
694 SKTRACE(1, sk, "getInputs: Stack2 %d\n", stackOff.offset);
695 inputs.emplace_back(Location::Stack { stackOff-- });
697 if (flags & Stack3) {
698 SKTRACE(1, sk, "getInputs: Stack3 %d\n", stackOff.offset);
699 inputs.emplace_back(Location::Stack { stackOff-- });
703 if (flags & StackI) {
704 inputs.emplace_back(Location::Stack {
705 BCSPRelOffset{safe_cast<int32_t>(ni.imm[0].u_IVA)}.
706 to<FPInvOffset>(bcSPOff)
709 if (flags & StackI2) {
710 inputs.emplace_back(Location::Stack {
711 BCSPRelOffset{safe_cast<int32_t>(ni.imm[1].u_IVA)}.
712 to<FPInvOffset>(bcSPOff)
716 if (flags & StackN) {
717 int numArgs = [&] () -> int {
718 switch (ni.op()) {
719 case Op::NewPackedArray:
720 case Op::NewVecArray:
721 case Op::NewKeysetArray:
722 case Op::NewVArray:
723 case Op::CombineAndResolveTypeStruct:
724 case Op::ConcatN:
725 return ni.imm[0].u_IVA;
726 case Op::PopFrame:
727 return ni.imm[0].u_IVA + 3;
728 default:
729 return ni.immVec.numStackValues();
731 not_reached();
732 }();
734 SKTRACE(1, sk, "getInputs: StackN %d %d\n", stackOff.offset, numArgs);
735 for (int i = 0; i < numArgs; i++) {
736 inputs.emplace_back(Location::Stack { stackOff-- });
737 inputs.back().dontGuard = true;
738 inputs.back().dontBreak = true;
741 if (flags & BStackN) {
742 int numArgs = ni.imm[0].u_IVA;
744 SKTRACE(1, sk, "getInputs: BStackN %d %d\n", stackOff.offset, numArgs);
745 for (int i = 0; i < numArgs; i++) {
746 inputs.emplace_back(Location::Stack { stackOff-- });
749 if (isFCall(ni.op())) {
750 auto const& fca = ni.imm[0].u_FCA;
751 SKTRACE(1, sk, "getInputs: %s %d %d\n",
752 opcodeToName(ni.op()), stackOff.offset, fca.numInputs());
754 if (fca.hasGenerics()) inputs.emplace_back(Location::Stack { stackOff-- });
755 if (fca.hasUnpack()) inputs.emplace_back(Location::Stack { stackOff-- });
756 stackOff -= fca.numArgs + 2;
758 switch (ni.op()) {
759 case Op::FCallCtor:
760 case Op::FCallObjMethod:
761 case Op::FCallObjMethodD:
762 inputs.emplace_back(Location::Stack { stackOff-- });
763 break;
764 default:
765 stackOff--;
766 break;
770 if (flags & Local) {
771 // (Almost) all instructions that take a Local have its index at their
772 // first immediate.
773 if (ni.op() == Op::IterBreak) {
774 for (auto const& it : ni.immIters) {
775 if (it.kind != KindOfLIter) continue;
776 SKTRACE(1, sk, "getInputs: local %d\n", it.local);
777 inputs.emplace_back(Location::Local { uint32_t(it.local) });
779 } else {
780 auto const loc = ni.imm[localImmIdx(ni.op())].u_IVA;
781 SKTRACE(1, sk, "getInputs: local %d\n", loc);
782 inputs.emplace_back(Location::Local { uint32_t(loc) });
786 if (flags & LocalRange) {
787 auto const& range = ni.imm[1].u_LAR;
788 SKTRACE(1, sk, "getInputs: localRange %d+%d\n",
789 range.first, range.count);
790 for (int i = 0; i < range.count; ++i) {
791 inputs.emplace_back(Location::Local { uint32_t(range.first + i) });
795 if (flags & MKey) {
796 auto mk = ni.imm[memberKeyImmIdx(ni.op())].u_KA;
797 switch (mk.mcode) {
798 case MEL:
799 case MPL:
800 inputs.emplace_back(Location::Local { uint32_t(mk.iva) });
801 break;
802 case MEC:
803 case MPC:
804 inputs.emplace_back(Location::Stack {
805 BCSPRelOffset{int32_t(mk.iva)}.to<FPInvOffset>(bcSPOff)
807 break;
808 case MW:
809 case MEI:
810 case MET:
811 case MPT:
812 case MQT:
813 // The inputs vector is only used for deciding when to break the
814 // tracelet, which can never happen for these cases.
815 break;
818 if (flags & MBase) {
819 inputs.emplace_back(Location::MBase{});
820 if (flags & DontGuardBase) {
821 inputs.back().dontGuard = true;
822 inputs.back().dontBreak = true;
826 SKTRACE(1, sk, "stack args: virtual sfo now %d\n", stackOff.offset);
827 TRACE(1, "%s\n", Trace::prettyNode("Inputs", inputs).c_str());
829 if ((flags & DontGuardAny) || dontGuardAnyInputs(ni)) {
830 for (auto& info : inputs) info.dontGuard = true;
832 return inputs;
835 bool dontGuardAnyInputs(const NormalizedInstruction& ni) {
836 switch (ni.op()) {
837 case Op::IterBreak:
838 case Op::IterNext:
839 case Op::LIterNext:
840 case Op::IterInit:
841 case Op::LIterInit:
842 case Op::JmpZ:
843 case Op::JmpNZ:
844 case Op::Jmp:
845 case Op::JmpNS:
846 case Op::ClsCnsD:
847 case Op::FCallBuiltin:
848 case Op::NewStructArray:
849 case Op::NewStructDArray:
850 case Op::NewStructDict:
851 case Op::Switch:
852 case Op::SSwitch:
853 case Op::Lt:
854 case Op::Lte:
855 case Op::Gt:
856 case Op::Gte:
857 case Op::Cmp:
858 case Op::SetOpL:
859 case Op::InitProp:
860 case Op::BreakTraceHint:
861 case Op::IsTypeL:
862 case Op::IsTypeC:
863 case Op::IncDecL:
864 case Op::DefCls:
865 case Op::DefRecord:
866 case Op::AliasCls:
867 case Op::Eq:
868 case Op::Neq:
869 case Op::AssertRATL:
870 case Op::AssertRATStk:
871 case Op::SetL:
872 case Op::EmptyL:
873 case Op::CastBool:
874 case Op::Same:
875 case Op::NSame:
876 case Op::Yield:
877 case Op::YieldK:
878 case Op::ContEnter:
879 case Op::ContRaise:
880 case Op::CreateCont:
881 case Op::Await:
882 case Op::AwaitAll:
883 case Op::BitAnd:
884 case Op::BitOr:
885 case Op::BitXor:
886 case Op::Sub:
887 case Op::Mul:
888 case Op::SubO:
889 case Op::MulO:
890 case Op::Add:
891 case Op::AddO:
892 case Op::ClassGetC:
893 case Op::ClassGetTS:
894 case Op::AKExists:
895 case Op::AddElemC:
896 case Op::AddNewElemC:
897 case Op::Array:
898 case Op::Dict:
899 case Op::Keyset:
900 case Op::Vec:
901 case Op::ArrayIdx:
902 case Op::BareThis:
903 case Op::BitNot:
904 case Op::CGetG:
905 case Op::CGetL:
906 case Op::CGetQuietL:
907 case Op::CGetL2:
908 case Op::CGetS:
909 case Op::CUGetL:
910 case Op::CastArray:
911 case Op::CastDouble:
912 case Op::CastInt:
913 case Op::CastString:
914 case Op::CastDict:
915 case Op::CastKeyset:
916 case Op::CastVec:
917 case Op::CastVArray:
918 case Op::CastDArray:
919 case Op::DblAsBits:
920 case Op::CheckProp:
921 case Op::CheckThis:
922 case Op::Clone:
923 case Op::CnsE:
924 case Op::ColFromArray:
925 case Op::CombineAndResolveTypeStruct:
926 case Op::RecordReifiedGeneric:
927 case Op::CheckReifiedGenericMismatch:
928 case Op::ConcatN:
929 case Op::Concat:
930 case Op::ContCheck:
931 case Op::ContCurrent:
932 case Op::ContKey:
933 case Op::ContValid:
934 case Op::ContGetReturn:
935 case Op::CreateCl:
936 case Op::DefCns:
937 case Op::Dir:
938 case Op::Div:
939 case Op::Double:
940 case Op::Dup:
941 case Op::EmptyG:
942 case Op::EmptyS:
943 case Op::FCallClsMethod:
944 case Op::FCallClsMethodD:
945 case Op::FCallClsMethodS:
946 case Op::FCallClsMethodSD:
947 case Op::FCallCtor:
948 case Op::FCallFunc:
949 case Op::FCallFuncD:
950 case Op::FCallObjMethodD:
951 case Op::ResolveFunc:
952 case Op::ResolveClsMethod:
953 case Op::ResolveObjMethod:
954 case Op::False:
955 case Op::File:
956 case Op::FuncCred:
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::Int:
965 case Op::IssetG:
966 case Op::IssetL:
967 case Op::IssetS:
968 case Op::IterFree:
969 case Op::LIterFree:
970 case Op::LateBoundCls:
971 case Op::Method:
972 case Op::Mod:
973 case Op::Pow:
974 case Op::ClassName:
975 case Op::NativeImpl:
976 case Op::NewArray:
977 case Op::NewCol:
978 case Op::NewPair:
979 case Op::NewLikeArrayL:
980 case Op::NewMixedArray:
981 case Op::NewDictArray:
982 case Op::NewPackedArray:
983 case Op::NewVecArray:
984 case Op::NewKeysetArray:
985 case Op::NewVArray:
986 case Op::NewDArray:
987 case Op::NewObj:
988 case Op::NewObjR:
989 case Op::NewObjD:
990 case Op::NewObjRD:
991 case Op::NewObjS:
992 case Op::NewRecord:
993 case Op::NewRecordArray:
994 case Op::Not:
995 case Op::Null:
996 case Op::NullUninit:
997 case Op::OODeclExists:
998 case Op::Parent:
999 case Op::PopC:
1000 case Op::PopU:
1001 case Op::PopU2:
1002 case Op::PopFrame:
1003 case Op::PopL:
1004 case Op::Print:
1005 case Op::PushL:
1006 case Op::RetC:
1007 case Op::RetCSuspended:
1008 case Op::Self:
1009 case Op::SetG:
1010 case Op::SetS:
1011 case Op::Shl:
1012 case Op::Shr:
1013 case Op::Silence:
1014 case Op::String:
1015 case Op::This:
1016 case Op::Throw:
1017 case Op::ThrowAsTypeStructException:
1018 case Op::True:
1019 case Op::UnsetL:
1020 case Op::VerifyParamType:
1021 case Op::VerifyParamTypeTS:
1022 case Op::VerifyRetTypeC:
1023 case Op::VerifyRetTypeTS:
1024 case Op::VerifyRetNonNullC:
1025 case Op::VerifyOutType:
1026 case Op::WHResult:
1027 case Op::Xor:
1028 case Op::BaseGC:
1029 case Op::BaseGL:
1030 case Op::BaseSC:
1031 case Op::BaseL:
1032 case Op::BaseC:
1033 case Op::BaseH:
1034 case Op::Dim:
1035 case Op::QueryM:
1036 case Op::SetM:
1037 case Op::IncDecM:
1038 case Op::SetOpM:
1039 case Op::UnsetM:
1040 case Op::SetRangeM:
1041 case Op::MemoGet:
1042 case Op::MemoGetEager:
1043 case Op::MemoSet:
1044 case Op::MemoSetEager:
1045 case Op::RetM:
1046 case Op::Select:
1047 case Op::LockObj:
1048 return false;
1050 // These are instructions that are always interp-one'd, or are always no-ops.
1051 case Op::Nop:
1052 case Op::EntryNop:
1053 case Op::CGetCUNop:
1054 case Op::UGetCUNop:
1055 case Op::ClsCns:
1056 case Op::Exit:
1057 case Op::Fatal:
1058 case Op::SetOpG:
1059 case Op::SetOpS:
1060 case Op::IncDecG:
1061 case Op::IncDecS:
1062 case Op::UnsetG:
1063 case Op::FCallObjMethod:
1064 case Op::Incl:
1065 case Op::InclOnce:
1066 case Op::Req:
1067 case Op::ReqOnce:
1068 case Op::ReqDoc:
1069 case Op::Eval:
1070 case Op::DefClsNop:
1071 case Op::DefTypeAlias:
1072 case Op::ChainFaults:
1073 case Op::ContAssignDelegate:
1074 case Op::ContEnterDelegate:
1075 case Op::YieldFromDelegate:
1076 case Op::ContUnsetDelegate:
1077 return true;
1080 always_assert_flog(0, "invalid opcode {}\n", static_cast<uint32_t>(ni.op()));
1083 bool instrBreaksProfileBB(const NormalizedInstruction* inst) {
1084 if (isFCall(inst->op())) return true;
1086 if (instrIsNonCallControlFlow(inst->op()) ||
1087 inst->op() == OpAwait || // may branch to scheduler and suspend execution
1088 inst->op() == OpAwaitAll || // similar to Await
1089 inst->op() == OpClsCnsD || // side exits if misses in the RDS
1090 inst->op() == OpVerifyParamTypeTS || // avoids combinatorial explosion
1091 inst->op() == OpVerifyParamType) { // with nullable types
1092 return true;
1094 // In profiling mode, don't trace through a control flow merge point,
1095 // however, allow inlining of default parameter funclets
1096 assertx(profData());
1097 if (profData()->anyBlockEndsAt(inst->func(), inst->offset()) &&
1098 !inst->func()->isEntry(inst->nextSk().offset())) {
1099 return true;
1101 return false;
1104 //////////////////////////////////////////////////////////////////////
1106 #define IMM_BLA(n) ni.immVec
1107 #define IMM_SLA(n) ni.immVec
1108 #define IMM_ILA(n) ni.immIters
1109 #define IMM_VSA(n) ni.immVec
1110 #define IMM_IVA(n) ni.imm[n].u_IVA
1111 #define IMM_I64A(n) ni.imm[n].u_I64A
1112 #define IMM_LA(n) ni.imm[n].u_LA
1113 #define IMM_IA(n) ni.imm[n].u_IA
1114 #define IMM_DA(n) ni.imm[n].u_DA
1115 #define IMM_SA(n) ni.unit()->lookupLitstrId(ni.imm[n].u_SA)
1116 #define IMM_RATA(n) ni.imm[n].u_RATA
1117 #define IMM_AA(n) ni.unit()->lookupArrayId(ni.imm[n].u_AA)
1118 #define IMM_BA(n) ni.imm[n].u_BA
1119 #define IMM_OA_IMPL(n) ni.imm[n].u_OA
1120 #define IMM_OA(subop) (subop)IMM_OA_IMPL
1121 #define IMM_KA(n) ni.imm[n].u_KA
1122 #define IMM_LAR(n) ni.imm[n].u_LAR
1123 #define IMM_ITA(n) ni.imm[n].u_ITA
1124 #define IMM_FCA(n) ni.imm[n].u_FCA
1126 #define ONE(x0) , IMM_##x0(0)
1127 #define TWO(x0,x1) , IMM_##x0(0), IMM_##x1(1)
1128 #define THREE(x0,x1,x2) , IMM_##x0(0), IMM_##x1(1), IMM_##x2(2)
1129 #define FOUR(x0,x1,x2,x3) , IMM_##x0(0), IMM_##x1(1), IMM_##x2(2), IMM_##x3(3)
1130 #define FIVE(x0,x1,x2,x3,x4) , IMM_##x0(0), IMM_##x1(1), IMM_##x2(2), IMM_##x3(3), IMM_##x4(4)
1131 #define SIX(x0,x1,x2,x3,x4,x5) , IMM_##x0(0), IMM_##x1(1), IMM_##x2(2), IMM_##x3(3), IMM_##x4(4), IMM_##x5(5)
1132 #define NA /* */
1134 static void translateDispatch(irgen::IRGS& irgs,
1135 const NormalizedInstruction& ni) {
1136 #define O(nm, imms, ...) case Op::nm: irgen::emit##nm(irgs imms); return;
1137 switch (ni.op()) { OPCODES }
1138 #undef O
1141 #undef SIX
1142 #undef FIVE
1143 #undef FOUR
1144 #undef THREE
1145 #undef TWO
1146 #undef ONE
1147 #undef NA
1149 #undef IMM_BLA
1150 #undef IMM_SLA
1151 #undef IMM_ILA
1152 #undef IMM_IVA
1153 #undef IMM_I64A
1154 #undef IMM_LA
1155 #undef IMM_IA
1156 #undef IMM_DA
1157 #undef IMM_SA
1158 #undef IMM_RATA
1159 #undef IMM_AA
1160 #undef IMM_BA
1161 #undef IMM_OA_IMPL
1162 #undef IMM_OA
1163 #undef IMM_VSA
1164 #undef IMM_KA
1165 #undef IMM_LAR
1166 #undef IMM_ITA
1167 #undef IMM_FCA
1169 //////////////////////////////////////////////////////////////////////
1171 namespace {
1173 Type flavorToType(FlavorDesc f) {
1174 switch (f) {
1175 case NOV: not_reached();
1177 case CV: return TCell; // TODO(#3029148) this could be InitCell
1178 case CUV: return TCell;
1179 case UV: return TUninit;
1181 not_reached();
1186 void translateInstr(irgen::IRGS& irgs, const NormalizedInstruction& ni,
1187 bool /*checkOuterTypeOnly*/, bool /*firstInst*/
1189 irgen::prepareForNextHHBC(irgs, ni.source);
1191 const Func* builtinFunc = nullptr;
1192 if (ni.op() == OpFCallBuiltin) {
1193 auto str = ni.m_unit->lookupLitstrId(ni.imm[3].u_SA);
1194 builtinFunc = Unit::lookupBuiltin(str);
1196 auto pc = ni.pc();
1197 for (auto i = 0, num = instrNumPops(pc); i < num; ++i) {
1198 if (isFCall(ni.op()) && instrInputFlavor(pc, i) == UV) {
1199 // This is a hack to deal with the fact that this instruction is
1200 // actually popping an ActRec in the middle of its "pops." We could
1201 // assert on the Uninit values on the stack, but the call is going to
1202 // override them anyway so it's not worth guarding on them.
1203 break;
1205 auto const type =
1206 !builtinFunc ? flavorToType(instrInputFlavor(pc, i)) : TCell;
1207 irgen::assertTypeStack(irgs, BCSPRelOffset{i}, type);
1210 FTRACE(1, "\nTranslating {}: {} with state:\n{}\n",
1211 ni.offset(), ni, show(irgs));
1213 irgen::ringbufferEntry(irgs, Trace::RBTypeBytecodeStart, ni.source, 2);
1214 irgen::implIncStat(irgs, Stats::Instr_TC);
1215 if (Stats::enableInstrCount()) {
1216 irgen::implIncStat(irgs, Stats::opToTranslStat(ni.op()));
1219 if (isAlwaysNop(ni)) return;
1220 if (ni.interp || RuntimeOption::EvalJitAlwaysInterpOne) {
1221 irgen::interpOne(irgs);
1222 return;
1225 if (ni.forceSurpriseCheck) {
1226 surpriseCheck(irgs);
1229 translateDispatch(irgs, ni);
1231 FTRACE(3, "\nTranslated {}: {} with state:\n{}\n",
1232 ni.offset(), ni, show(irgs));
1235 //////////////////////////////////////////////////////////////////////
1239 void invalidatePath(const std::string& path) {
1240 TRACE(1, "invalidatePath: abspath %s\n", path.c_str());
1241 assertx(path.size() >= 1 && path[0] == '/');
1242 Treadmill::enqueue([path] {
1244 * inotify saw this path change. Now poke the unit loader; it will
1245 * notice the underlying php file has changed.
1247 * We don't actually need to *do* anything with the Unit* from
1248 * this lookup; since the path has changed, the file we'll get out is
1249 * going to be some new file, not the old file that needs invalidation.
1251 String spath(path);
1252 lookupUnit(spath.get(), "", nullptr /* initial_opt */,
1253 Native::s_noNativeFuncs, false);
1257 ///////////////////////////////////////////////////////////////////////////////