2 +----------------------------------------------------------------------+
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"
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"
70 namespace HPHP
{ namespace jit
{
71 ///////////////////////////////////////////////////////////////////////////////
74 * NB: this opcode structure is sparse; it cannot just be indexed by
77 using namespace InstrFlags
;
81 } instrInfoSparse
[] = {
83 // Op Inputs Outputs OutputTypes
84 // -- ------ ------- -----------
86 /*** 1. Basic instructions ***/
89 DontGuardStack1
, None
, OutNone
}},
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 ***/
143 { OpConcat
, {StackTop2
, Stack1
, OutString
}},
144 { OpConcatN
, {StackN
, Stack1
, OutString
}},
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
}},
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
}},
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
}},
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.
281 {StackTop2
, StackN
, OutUnknown
}},
283 {None
, StackN
, OutUnknown
}},
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
}},
292 {Stack1
, StackN
, OutUnknown
}},
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
}},
328 {None
, Local
, OutUnknown
}},
329 { OpChainFaults
, {StackTop2
, Stack1
, OutObject
}},
331 {Local
, Local
, OutUnknown
}},
332 { OpVerifyParamTypeTS
,
333 {Local
|Stack1
, Local
, OutUnknown
}},
335 {Stack1
, Stack1
, OutSameAsInput1
}},
337 {StackTop2
, Stack1
, OutSameAsInput2
}},
338 { OpVerifyRetNonNullC
,
339 {Stack1
, Stack1
, OutSameAsInput1
}},
341 {Stack1
, Stack1
, OutSameAsInput1
}},
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
,
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
}},
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
,
417 { OpMemoGet
, {LocalRange
, None
, OutUnknown
}},
418 { OpMemoGetEager
,{LocalRange
, None
, OutUnknown
}},
419 { OpMemoSet
, {Stack1
|LocalRange
,
420 Stack1
, OutSameAsInput1
}},
421 { OpMemoSetEager
, {Stack1
|LocalRange
,
422 Stack1
, OutSameAsInput1
}},
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
;
440 auto& ii2
= instrInfo
[OpVerifyRetTypeTS
];
445 instrInfoInited
= true;
449 const InstrInfo
& getInstrInfo(Op op
) {
450 assertx(instrInfoInited
);
451 return instrInfo
[op
];
455 int64_t countOperands(uint64_t mask
) {
456 const uint64_t ignore
= Local
| Iter
| DontGuardStack1
|
457 DontGuardAny
| This
| MBase
| StackI
| StackI2
| MKey
| LocalRange
|
461 static const uint64_t counts
[][2] = {
469 for (auto const& pair
: counts
) {
470 if (mask
& pair
[0]) {
480 int64_t getStackPopped(PC pc
) {
481 auto const op
= peek_op(pc
);
483 case Op::FCallClsMethod
:
484 case Op::FCallClsMethodD
:
485 case Op::FCallClsMethodS
:
486 case Op::FCallClsMethodSD
:
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
;
500 case Op::NewPackedArray
:
501 case Op::NewVecArray
:
502 case Op::NewKeysetArray
:
505 case Op::CombineAndResolveTypeStruct
:
507 return getImm(pc
, 0).u_IVA
;
509 case Op::FCallBuiltin
:
510 return getImm(pc
, 0).u_IVA
+ getImm(pc
, 2).u_IVA
;
513 return getImm(pc
, 0).u_IVA
+ 3;
517 return getImm(pc
, 0).u_IVA
+ 1;
520 case Op::NewRecordArray
:
521 case Op::NewStructArray
:
522 case Op::NewStructDArray
:
523 case Op::NewStructDict
:
524 return getImmVector(pc
).size();
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
);
541 case Op::FCallClsMethod
:
542 case Op::FCallClsMethodD
:
543 case Op::FCallClsMethodS
:
544 case Op::FCallClsMethodSD
:
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;
554 return getImm(pc
, 0).u_IVA
;
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
) {
575 case Op::VerifyRetTypeC
:
576 return !RuntimeOption::EvalCheckReturnTypeHints
;
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;
609 #define O(name, imm, ...) case Op::name: imm break;
611 size_t localImmIdx(Op op
) {
625 assertx(idx
!= 0xff);
629 size_t memberKeyImmIdx(Op op
) {
635 #define KA(n) assertx(idx == 0xff); idx = n;
638 assertx(idx
!= 0xff);
670 * Get location metadata for the inputs of `ni'.
672 InputInfoVec
getInputs(const NormalizedInstruction
& ni
, FPInvOffset bcSPOff
) {
674 if (isAlwaysNop(ni
)) return inputs
;
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 {
719 case Op::NewPackedArray
:
720 case Op::NewVecArray
:
721 case Op::NewKeysetArray
:
723 case Op::CombineAndResolveTypeStruct
:
725 return ni
.imm
[0].u_IVA
;
727 return ni
.imm
[0].u_IVA
+ 3;
729 return ni
.immVec
.numStackValues();
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;
760 case Op::FCallObjMethod
:
761 case Op::FCallObjMethodD
:
762 inputs
.emplace_back(Location::Stack
{ stackOff
-- });
771 // (Almost) all instructions that take a Local have its index at their
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
) });
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
) });
796 auto mk
= ni
.imm
[memberKeyImmIdx(ni
.op())].u_KA
;
800 inputs
.emplace_back(Location::Local
{ uint32_t(mk
.iva
) });
804 inputs
.emplace_back(Location::Stack
{
805 BCSPRelOffset
{int32_t(mk
.iva
)}.to
<FPInvOffset
>(bcSPOff
)
813 // The inputs vector is only used for deciding when to break the
814 // tracelet, which can never happen for these cases.
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;
835 bool dontGuardAnyInputs(const NormalizedInstruction
& ni
) {
847 case Op::FCallBuiltin
:
848 case Op::NewStructArray
:
849 case Op::NewStructDArray
:
850 case Op::NewStructDict
:
860 case Op::BreakTraceHint
:
870 case Op::AssertRATStk
:
896 case Op::AddNewElemC
:
924 case Op::ColFromArray
:
925 case Op::CombineAndResolveTypeStruct
:
926 case Op::RecordReifiedGeneric
:
927 case Op::CheckReifiedGenericMismatch
:
931 case Op::ContCurrent
:
934 case Op::ContGetReturn
:
943 case Op::FCallClsMethod
:
944 case Op::FCallClsMethodD
:
945 case Op::FCallClsMethodS
:
946 case Op::FCallClsMethodSD
:
950 case Op::FCallObjMethodD
:
951 case Op::ResolveFunc
:
952 case Op::ResolveClsMethod
:
953 case Op::ResolveObjMethod
:
957 case Op::GetMemoKeyL
:
959 case Op::InitThisLoc
:
961 case Op::InstanceOfD
:
962 case Op::IsLateBoundCls
:
963 case Op::IsTypeStructC
:
970 case Op::LateBoundCls
:
979 case Op::NewLikeArrayL
:
980 case Op::NewMixedArray
:
981 case Op::NewDictArray
:
982 case Op::NewPackedArray
:
983 case Op::NewVecArray
:
984 case Op::NewKeysetArray
:
993 case Op::NewRecordArray
:
997 case Op::OODeclExists
:
1007 case Op::RetCSuspended
:
1017 case Op::ThrowAsTypeStructException
:
1020 case Op::VerifyParamType
:
1021 case Op::VerifyParamTypeTS
:
1022 case Op::VerifyRetTypeC
:
1023 case Op::VerifyRetTypeTS
:
1024 case Op::VerifyRetNonNullC
:
1025 case Op::VerifyOutType
:
1042 case Op::MemoGetEager
:
1044 case Op::MemoSetEager
:
1050 // These are instructions that are always interp-one'd, or are always no-ops.
1063 case Op::FCallObjMethod
:
1071 case Op::DefTypeAlias
:
1072 case Op::ChainFaults
:
1073 case Op::ContAssignDelegate
:
1074 case Op::ContEnterDelegate
:
1075 case Op::YieldFromDelegate
:
1076 case Op::ContUnsetDelegate
:
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
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())) {
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)
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
}
1169 //////////////////////////////////////////////////////////////////////
1173 Type
flavorToType(FlavorDesc 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
;
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
);
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.
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
);
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.
1252 lookupUnit(spath
.get(), "", nullptr /* initial_opt */,
1253 Native::s_noNativeFuncs
, false);
1257 ///////////////////////////////////////////////////////////////////////////////