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"
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"
73 namespace HPHP
{ namespace jit
{
74 ///////////////////////////////////////////////////////////////////////////////
77 * NB: this opcode structure is sparse; it cannot just be indexed by
80 using namespace InstrFlags
;
84 } instrInfoSparse
[] = {
86 // Op Inputs Outputs OutputTypes
87 // -- ------ ------- -----------
89 /*** 1. Basic instructions ***/
91 { OpDiscardClsRef
, {None
, None
, OutNone
}},
93 DontGuardStack1
, None
, OutNone
}},
95 DontGuardStack1
, None
, OutNone
}},
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 ***/
149 { OpConcat
, {StackTop2
, Stack1
, OutString
}},
150 { OpConcatN
, {StackN
, Stack1
, OutString
}},
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
}},
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
}},
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
}},
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
}},
291 {StackTop2
, FStack
, OutFDesc
}},
293 {Stack1
, FStack
, OutFDesc
}},
295 {Stack1
, FStack
, OutFDesc
}},
297 {Stack1
, FStack
, OutFDesc
}},
298 { OpFPushClsMethodSD
,
299 {None
, FStack
, OutFDesc
}},
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
}},
348 {None
, Local
, OutUnknown
}},
349 { OpFuncNumArgs
, {None
, Stack1
, OutInt64
}},
351 {None
, Stack1
|Local
, OutBoolean
}},
353 {Stack1
, Local
, OutVUnknown
}},
355 {Stack1
, Local
, OutVUnknown
}},
356 { OpCatch
, {None
, Stack1
, OutObject
}},
357 { OpChainFaults
, {StackTop2
, Stack1
, OutObject
}},
359 {Local
, Local
, OutUnknown
}},
360 { OpVerifyParamTypeTS
,
361 {Local
|Stack1
, Local
, OutUnknown
}},
363 {Stack1
, Stack1
, OutSameAsInput1
}},
365 {StackTop2
, Stack1
, OutSameAsInput2
}},
366 { OpVerifyRetNonNullC
,
367 {Stack1
, Stack1
, OutSameAsInput1
}},
369 {Stack1
, Stack1
, OutSameAsInput1
}},
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
,
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
}},
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
,
450 { OpMemoGet
, {LocalRange
, None
, OutUnknown
}},
451 { OpMemoGetEager
,{LocalRange
, None
, OutUnknown
}},
452 { OpMemoSet
, {Stack1
|LocalRange
,
453 Stack1
, OutSameAsInput1
}},
454 { OpMemoSetEager
, {Stack1
|LocalRange
,
455 Stack1
, OutSameAsInput1
}},
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
;
473 auto& ii2
= instrInfo
[OpVerifyRetTypeTS
];
478 instrInfoInited
= true;
482 const InstrInfo
& getInstrInfo(Op op
) {
483 assertx(instrInfoInited
);
484 return instrInfo
[op
];
488 int64_t countOperands(uint64_t mask
) {
489 const uint64_t ignore
= Local
| Iter
| DontGuardStack1
|
490 DontGuardAny
| This
| MBase
| StackI
| MKey
| LocalRange
|
494 static const uint64_t counts
[][2] = {
499 {FStack
, kNumActRecCells
},
503 for (auto const& pair
: counts
) {
504 if (mask
& pair
[0]) {
514 int64_t getStackPopped(PC pc
) {
515 auto const op
= peek_op(pc
);
518 auto const fca
= getImm(pc
, 0).u_FCA
;
519 return fca
.numArgs
+ (fca
.hasUnpack() ? 1 : 0) + fca
.numRets
+
527 case Op::NewPackedArray
:
528 case Op::NewVecArray
:
529 case Op::NewKeysetArray
:
532 case Op::CombineAndResolveTypeStruct
:
533 case Op::RecordReifiedGeneric
:
534 case Op::ReifiedName
:
535 case Op::FCallBuiltin
:
537 return getImm(pc
, 0).u_IVA
;
542 return getImm(pc
, 0).u_IVA
+ 1;
545 case Op::NewStructArray
:
546 case Op::NewStructDArray
:
547 case Op::NewStructDict
:
548 return getImmVector(pc
).size();
553 uint64_t mask
= getInstrInfo(op
).in
;
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
);
565 case Op::FCall
: return getImm(pc
, 0).u_FCA
.numRets
;
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
) {
585 case Op::VerifyRetTypeC
:
586 return !RuntimeOption::EvalCheckReturnTypeHints
;
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;
620 #define O(name, imm, ...) case Op::name: imm break;
622 size_t localImmIdx(Op op
) {
638 assertx(idx
!= 0xff);
642 size_t memberKeyImmIdx(Op op
) {
648 #define KA(n) assertx(idx == 0xff); idx = n;
651 assertx(idx
!= 0xff);
684 * Get location metadata for the inputs of `ni'.
686 InputInfoVec
getInputs(const NormalizedInstruction
& ni
, FPInvOffset bcSPOff
) {
688 if (isAlwaysNop(ni
)) return inputs
;
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
)
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
-- });
759 // (Almost) all instructions that take a Local have its index at their
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
) });
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
) });
784 auto mk
= ni
.imm
[memberKeyImmIdx(ni
.op())].u_KA
;
788 inputs
.emplace_back(Location::Local
{ uint32_t(mk
.iva
) });
792 inputs
.emplace_back(Location::Stack
{
793 BCSPRelOffset
{int32_t(mk
.iva
)}.to
<FPInvOffset
>(bcSPOff
)
801 // The inputs vector is only used for deciding when to break the
802 // tracelet, which can never happen for these cases.
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;
823 bool dontGuardAnyInputs(const NormalizedInstruction
& ni
) {
840 case Op::FCallBuiltin
:
841 case Op::NewStructArray
:
842 case Op::NewStructDArray
:
843 case Op::NewStructDict
:
853 case Op::BreakTraceHint
:
863 case Op::AssertRATStk
:
887 case Op::ClsRefGetTS
:
890 case Op::AddNewElemC
:
922 case Op::ColFromArray
:
923 case Op::CombineAndResolveTypeStruct
:
924 case Op::RecordReifiedGeneric
:
925 case Op::ReifiedName
:
926 case Op::CheckReifiedGenericMismatch
:
930 case Op::ContCurrent
:
933 case Op::ContGetReturn
:
942 case Op::FPushClsMethodD
:
943 case Op::FPushClsMethod
:
944 case Op::FPushClsMethodS
:
945 case Op::FPushClsMethodSD
:
950 case Op::FPushObjMethodD
:
951 case Op::FuncNumArgs
:
952 case Op::ResolveFunc
:
953 case Op::ResolveClsMethod
:
954 case Op::ResolveObjMethod
:
957 case Op::GetMemoKeyL
:
959 case Op::InitThisLoc
:
961 case Op::InstanceOfD
:
962 case Op::IsLateBoundCls
:
963 case Op::IsTypeStructC
:
964 case Op::AsTypeStructC
:
971 case Op::LateBoundCls
:
980 case Op::NewLikeArrayL
:
981 case Op::NewMixedArray
:
982 case Op::NewDictArray
:
983 case Op::NewPackedArray
:
984 case Op::NewVecArray
:
985 case Op::NewKeysetArray
:
995 case Op::OODeclExists
:
997 case Op::DiscardClsRef
:
1006 case Op::RetCSuspended
:
1013 case Op::StaticLocDef
:
1014 case Op::StaticLocCheck
:
1015 case Op::StaticLocInit
:
1025 case Op::VerifyParamType
:
1026 case Op::VerifyParamTypeTS
:
1027 case Op::VerifyRetTypeC
:
1028 case Op::VerifyRetTypeTS
:
1029 case Op::VerifyRetNonNullC
:
1030 case Op::VerifyOutType
:
1049 case Op::MemoGetEager
:
1051 case Op::MemoSetEager
:
1056 // These are instructions that are always interp-one'd, or are always no-ops.
1063 case Op::AddNewElemV
:
1073 case Op::FPushObjMethod
:
1081 case Op::DefTypeAlias
:
1083 case Op::ChainFaults
:
1084 case Op::ContAssignDelegate
:
1085 case Op::ContEnterDelegate
:
1086 case Op::YieldFromDelegate
:
1087 case Op::ContUnsetDelegate
:
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
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())) {
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)
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
}
1182 //////////////////////////////////////////////////////////////////////
1186 Type
flavorToType(FlavorDesc 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
;
1201 void translateInstr(irgen::IRGS
& irgs
, const NormalizedInstruction
& ni
,
1202 bool /*checkOuterTypeOnly*/, bool /*firstInst*/
1204 irgen::prepareForNextHHBC(
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
);
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.
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
);
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
,
1262 if (!cls
) return nullptr;
1263 if (cls
->attrs() & AttrInterface
) return nullptr;
1264 auto ctx
= ctxFunc
->cls();
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
||
1277 res
== LookupResult::MagicCallStaticFound
:
1278 res
== LookupResult::MagicCallFound
));
1281 res
== LookupResult::MagicCallStaticFound
||
1282 res
== LookupResult::MagicCallFound
;
1286 if (ctx
&& !ctxFunc
->isStatic() &&
1287 (ctx
->classof(cls
) || cls
->classof(ctx
))) {
1288 // we might need to call __call instead
1291 if (!exactClass
&& !(cls
->attrs() & AttrNoOverride
)) {
1292 // there might be a derived class which defines the method
1296 } else if (!exactClass
&& !(func
->attrs() & AttrPrivate
)) {
1298 if (!(cls
->attrs() & AttrNoOverride
)) {
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();
1313 if (!ctx
) return nullptr;
1314 if ((func
->attrs() & AttrPrivate
) ||
1315 !(ctx
->classof(fcls
) || fcls
->classof(ctx
))) {
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
1339 if (f
->isPersistent() || f
->unit() == unit
) return {f
, false};
1340 // Use the function, but ensure its unit is loaded.
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;
1355 // Function with duplicate name
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.
1384 lookupUnit(spath
.get(), "", nullptr /* initial_opt */,
1385 Native::s_noNativeFuncs
);
1389 ///////////////////////////////////////////////////////////////////////////////