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 +----------------------------------------------------------------------+
19 #include <type_traits>
21 #include "hphp/runtime/base/repo-auth-type.h"
22 #include "hphp/runtime/base/typed-value.h"
23 #include "hphp/runtime/base/types.h"
24 #include "hphp/runtime/base/header-kind.h"
25 #include "hphp/runtime/vm/member-key.h"
26 #include "hphp/util/compact-vector.h"
27 #include "hphp/util/either.h"
28 #include "hphp/util/functional.h"
29 #include "hphp/util/hash-set.h"
33 //////////////////////////////////////////////////////////////////////
40 constexpr size_t kMaxHhbcImms
= 6;
42 // A contiguous range of locals. The count is the number of locals
43 // including the first. If the range is empty, count will be zero and
44 // first's value is arbitrary.
51 * Arguments to IterInit / IterNext opcodes.
52 * hhas format: <iterId> K:<keyId> V:<valId> (for key-value iters)
53 * <iterId> NK V:<valId> (for value-only iters)
54 * hhbc format: <uint8:flags> <iva:iterId> <iva:(keyId + 1)> <iva:valId>
56 * For value-only iters, keyId will be -1 (an invalid local ID); to take
57 * advantage of the one-byte encoding for IVA arguments, we add 1 to the key
58 * when encoding these args in the hhbc format.
60 * We don't accept flags from hhas because our flags require analyses that we
61 * currently only do in HHBBC.
64 enum Flags
: uint8_t {
66 // The base is stored in a local, and that local is unmodified in the loop.
70 static constexpr int32_t kNoKey
= -1;
72 explicit IterArgs(Flags flags
, int32_t iterId
, int32_t keyId
, int32_t valId
)
73 : iterId(iterId
), keyId(keyId
), valId(valId
), flags(flags
) {}
76 assertx(keyId
== kNoKey
|| keyId
>= 0);
77 return keyId
!= kNoKey
;
80 bool operator==(const IterArgs
& other
) const {
81 return iterId
== other
.iterId
&& keyId
== other
.keyId
&&
82 valId
== other
.valId
&& flags
== other
.flags
;
91 // Arguments to FCall opcodes.
92 // hhas format: <flags> <numArgs> <numRets> <inoutArgs> <asyncEagerOffset>
93 // hhbc format: <uint8:flags> ?<iva:numArgs> ?<iva:numRets>
94 // ?<boolvec:inoutArgs> ?<ba:asyncEagerOffset>
95 // flags = flags (hhas doesn't have HHBC-only flags)
96 // numArgs = flags >> kFirstNumArgsBit
97 // ? flags >> kFirstNumArgsBit - 1 : decode_iva()
98 // numRets = flags & HasInOut ? decode_iva() : 1
99 // inoutArgs = flags & EnforceInOut ? decode bool vec : nullptr
100 // asyncEagerOffset = flags & HasAEO ? decode_ba() : kInvalidOffset
101 struct FCallArgsBase
{
102 enum Flags
: uint16_t {
104 // Unpack remaining arguments from a varray passed by ...$args.
105 HasUnpack
= (1 << 0),
106 // Pass generics to the callee.
107 HasGenerics
= (1 << 1),
108 // Lock newly constructed object if unwinding the constructor call.
109 LockWhileUnwinding
= (1 << 2),
110 // Arguments are known to be compatible with prologue of the callee and
111 // do not need to be repacked.
112 SkipRepack
= (1 << 3),
113 // HHBC-only: Op should be resolved using an explicit context class
114 ExplicitContext
= (1 << 4),
115 // HHBC-only: is the number of returns provided? false => 1
117 // HHBC-only: should this FCall enforce argument inout-ness?
118 EnforceInOut
= (1 << 6),
119 // HHBC-only: is the async eager offset provided? false => kInvalidOffset
120 HasAsyncEagerOffset
= (1 << 7),
121 // HHBC-only: the remaining space is used for number of arguments
122 NumArgsStart
= (1 << 8),
125 // Flags that are valid on FCallArgsBase::flags struct (i.e. non-HHBC-only).
126 static constexpr uint8_t kInternalFlags
=
127 HasUnpack
| HasGenerics
| LockWhileUnwinding
| SkipRepack
;
128 // The first (lowest) bit of numArgs.
129 static constexpr uint8_t kFirstNumArgsBit
= 8;
131 explicit FCallArgsBase(Flags flags
, uint32_t numArgs
, uint32_t numRets
)
136 assertx(!(flags
& ~kInternalFlags
));
138 bool hasUnpack() const { return flags
& Flags::HasUnpack
; }
139 bool hasGenerics() const { return flags
& Flags::HasGenerics
; }
140 bool lockWhileUnwinding() const { return flags
& Flags::LockWhileUnwinding
; }
141 bool skipRepack() const { return flags
& Flags::SkipRepack
; }
142 uint32_t numInputs() const {
143 return numArgs
+ (hasUnpack() ? 1 : 0) + (hasGenerics() ? 1 : 0);
150 struct FCallArgs
: FCallArgsBase
{
151 explicit FCallArgs(Flags flags
, uint32_t numArgs
, uint32_t numRets
,
152 const uint8_t* inoutArgs
, Offset asyncEagerOffset
,
153 const StringData
* context
)
154 : FCallArgsBase(flags
, numArgs
, numRets
)
155 , asyncEagerOffset(asyncEagerOffset
)
156 , inoutArgs(inoutArgs
)
158 assertx(IMPLIES(inoutArgs
!= nullptr, numArgs
!= 0));
160 bool enforceInOut() const { return inoutArgs
!= nullptr; }
161 bool isInOut(uint32_t i
) const {
162 assertx(enforceInOut());
163 return inoutArgs
[i
/ 8] & (1 << (i
% 8));
165 FCallArgs
withGenerics() const {
166 assertx(!hasGenerics());
168 static_cast<Flags
>(flags
| Flags::HasGenerics
),
169 numArgs
, numRets
, inoutArgs
, asyncEagerOffset
, context
);
171 Offset asyncEagerOffset
;
172 const uint8_t* inoutArgs
;
173 const StringData
* context
;
176 static_assert(1 << FCallArgs::kFirstNumArgsBit
== FCallArgs::NumArgsStart
, "");
178 using PrintLocal
= std::function
<std::string(int32_t local
)>;
179 std::string
show(const IterArgs
&, PrintLocal
);
181 std::string
show(const LocalRange
&);
182 std::string
show(uint32_t numArgs
, const uint8_t* inoutArgs
);
183 std::string
show(const FCallArgsBase
&, const uint8_t* inoutArgs
,
184 std::string asyncEagerLabel
, const StringData
* ctx
);
187 * Variable-size immediates are implemented as follows: To determine which size
188 * the immediate is, examine the first byte where the immediate is expected,
189 * and examine its high-order bit. If it is zero, it's a 1-byte immediate
190 * and the byte is the value. Otherwise, it's 4 bytes, and bits 8..31 must be
191 * logical-shifted to the right by one to get rid of the flag bit.
193 * The types in this macro for BLA, SLA, and VSA are meaningless since they
194 * are never read out of ArgUnion (they use ImmVector).
196 * There are several different local immediate types:
197 * - LA immediates are for bytecodes that only require the TypedValue* to
198 * perform their operation.
199 * - ILA immediates are used by bytecodes that need both the TypedValue* and
200 * the slot index to implement their operation. This could be used by
201 * opcodes that print an error message including this slot info.
202 * - NLA immediates are used by bytecodes that need both the TypedValue* and
203 * the name of the local to be implemented. This is commonly used for
204 * ops that raise warnings for undefined local uses.
206 * ArgTypes and their various decoding helpers should be kept in sync with the
207 * `hhx' bytecode inspection GDB command.
210 ARGTYPE(NA, void*) /* unused */ \
211 ARGTYPEVEC(BLA, Offset) /* Bytecode offset vector immediate */ \
212 ARGTYPEVEC(SLA, Id) /* String id/offset pair vector */ \
213 ARGTYPE(IVA, uint32_t) /* Variable size: 8 or 32-bit uint */ \
214 ARGTYPE(I64A, int64_t) /* 64-bit Integer */ \
215 ARGTYPE(LA, int32_t) /* Local: 8 or 32-bit int */ \
216 ARGTYPE(NLA, NamedLocal) /* Local w/ name: 2x 8 or 32-bit int */ \
217 ARGTYPE(ILA, int32_t) /* Local w/ ID: 8 or 32-bit int */ \
218 ARGTYPE(IA, int32_t) /* Iterator ID: 8 or 32-bit int */ \
219 ARGTYPE(DA, double) /* Double */ \
220 ARGTYPE(SA, Id) /* Static string ID */ \
221 ARGTYPE(AA, Id) /* Static array ID */ \
222 ARGTYPE(RATA, RepoAuthType) /* Statically inferred RepoAuthType */ \
223 ARGTYPE(BA, Offset) /* Bytecode offset */ \
224 ARGTYPE(OA, unsigned char) /* Sub-opcode, untyped */ \
225 ARGTYPE(KA, MemberKey) /* Member key: local, stack, int, str */ \
226 ARGTYPE(LAR, LocalRange) /* Contiguous range of locals */ \
227 ARGTYPE(ITA, IterArgs) /* Iterator arguments */ \
228 ARGTYPE(FCA, FCallArgs) /* FCall arguments */ \
229 ARGTYPEVEC(VSA, Id) /* Vector of static string IDs */
232 #define ARGTYPE(name, type) name,
233 #define ARGTYPEVEC(name, type) name,
240 ArgUnion() : u_LA
{0} {}
242 #define ARGTYPE(name, type) type u_##name;
243 #define ARGTYPEVEC(name, type) type u_##name;
253 CUV
, // TypedValue, or Uninit argument
260 /* Terminal: next instruction is not reachable via fall through or the callee
261 * returning control. This includes instructions like Throw that always throw
265 /* Control flow: If this instruction finishes executing (doesn't throw an
266 * exception), vmpc() is not guaranteed to point to the next instruction in
267 * the bytecode stream. This does not take VM reentry into account, as that
268 * operation is part of the instruction that performed the reentry, and does
269 * not affect what vmpc() is set to after the instruction completes. */
272 /* Shorthand for common combinations. */
283 INCDEC_OP(PostIncO) \
285 INCDEC_OP(PostDecO) \
287 enum class IncDecOp : uint8_t {
288 #define INCDEC_OP(incDecOp) incDecOp,
293 inline bool isPre(IncDecOp op
) {
295 op
== IncDecOp::PreInc
|| op
== IncDecOp::PreIncO
||
296 op
== IncDecOp::PreDec
|| op
== IncDecOp::PreDecO
;
299 inline bool isInc(IncDecOp op
) {
301 op
== IncDecOp::PreInc
|| op
== IncDecOp::PreIncO
||
302 op
== IncDecOp::PostInc
|| op
== IncDecOp::PostIncO
;
305 inline bool isIncDecO(IncDecOp op
) {
307 op
== IncDecOp::PreIncO
|| op
== IncDecOp::PreDecO
||
308 op
== IncDecOp::PostIncO
|| op
== IncDecOp::PostDecO
;
323 ISTYPE_OP(LegacyArrLike) \
329 enum class IsTypeOp
: uint8_t {
330 #define ISTYPE_OP(op) op,
335 #define INITPROP_OPS \
336 INITPROP_OP(Static) \
337 INITPROP_OP(NonStatic)
339 enum class InitPropOp
: uint8_t {
340 #define INITPROP_OP(op) op,
348 FATAL_OP(RuntimeOmitFrame)
350 enum class FatalOp
: uint8_t {
351 #define FATAL_OP(x) x,
356 // Each of the setop ops maps to a binary bytecode op. We have reasons
357 // for using distinct bitwise representations, though. This macro records
358 // their correspondence for mapping either direction.
360 SETOP_OP(PlusEqual, OpAdd) \
361 SETOP_OP(MinusEqual, OpSub) \
362 SETOP_OP(MulEqual, OpMul) \
363 SETOP_OP(ConcatEqual, OpConcat) \
364 SETOP_OP(DivEqual, OpDiv) \
365 SETOP_OP(PowEqual, OpPow) \
366 SETOP_OP(ModEqual, OpMod) \
367 SETOP_OP(AndEqual, OpBitAnd) \
368 SETOP_OP(OrEqual, OpBitOr) \
369 SETOP_OP(XorEqual, OpBitXor) \
370 SETOP_OP(SlEqual, OpShl) \
371 SETOP_OP(SrEqual, OpShr) \
372 SETOP_OP(PlusEqualO, OpAddO) \
373 SETOP_OP(MinusEqualO, OpSubO) \
374 SETOP_OP(MulEqualO, OpMulO) \
376 enum class SetOpOp : uint8_t {
377 #define SETOP_OP(setOpOp, bcOp) setOpOp,
382 #define BARETHIS_OPS \
383 BARETHIS_OP(Notice) \
384 BARETHIS_OP(NoNotice) \
385 BARETHIS_OP(NeverNull)
387 enum class BareThisOp
: uint8_t {
388 #define BARETHIS_OP(x) x,
393 #define SILENCE_OPS \
397 enum class SilenceOp
: uint8_t {
398 #define SILENCE_OP(x) x,
403 #define OO_DECL_EXISTS_OPS \
404 OO_DECL_EXISTS_OP(Class) \
405 OO_DECL_EXISTS_OP(Interface) \
406 OO_DECL_EXISTS_OP(Trait)
408 enum class OODeclExistsOp
: uint8_t {
409 #define OO_DECL_EXISTS_OP(x) x,
411 #undef OO_DECL_EXISTS_OP
414 #define OBJMETHOD_OPS \
415 OBJMETHOD_OP(NullThrows) \
416 OBJMETHOD_OP(NullSafe)
418 enum class ObjMethodOp
: uint8_t {
419 #define OBJMETHOD_OP(x) x,
424 #define SWITCH_KINDS \
428 enum class SwitchKind
: uint8_t {
439 /* InOut mode restricts allowed bases to the
440 array like types. */ \
443 enum class MOpMode
: uint8_t {
444 #define MODE(name) name,
449 #define QUERY_M_OPS \
455 enum class QueryMOp
: uint8_t {
456 #define OP(name) name,
461 #define SET_RANGE_OPS \
465 enum class SetRangeOp
: uint8_t {
466 #define OP(name) name,
471 #define TYPE_STRUCT_RESOLVE_OPS \
475 enum class TypeStructResolveOp
: uint8_t {
476 #define OP(name) name,
477 TYPE_STRUCT_RESOLVE_OPS
481 #define CONT_CHECK_OPS \
482 CONT_CHECK_OP(IgnoreStarted) \
483 CONT_CHECK_OP(CheckStarted)
485 enum class ContCheckOp
: uint8_t {
486 #define CONT_CHECK_OP(name) name,
495 enum class CudOp
: uint8_t {
496 #define CUD_OP(name) name,
501 #define SPECIAL_CLS_REFS \
506 enum class SpecialClsRef
: uint8_t {
507 #define REF(name) name,
512 #define IS_LOG_AS_DYNAMIC_CALL_OPS \
513 IS_LOG_AS_DYNAMIC_CALL_OP(LogAsDynamicCall) \
514 IS_LOG_AS_DYNAMIC_CALL_OP(DontLogAsDynamicCall)
516 enum class IsLogAsDynamicCallOp
: uint8_t {
517 #define IS_LOG_AS_DYNAMIC_CALL_OP(name) name,
518 IS_LOG_AS_DYNAMIC_CALL_OPS
519 #undef IS_LOG_AS_DYNAMIC_CALL_OP
522 constexpr uint32_t kMaxConcatN
= 4;
524 // name immediates inputs outputs flags
526 O(Nop, NA, NOV, NOV, NF) \
527 O(EntryNop, NA, NOV, NOV, NF) \
528 O(BreakTraceHint, NA, NOV, NOV, NF) \
529 O(PopC, NA, ONE(CV), NOV, NF) \
530 O(PopU, NA, ONE(UV), NOV, NF) \
531 O(PopU2, NA, TWO(CV,UV), ONE(CV), NF) \
532 O(PopL, ONE(LA), ONE(CV), NOV, NF) \
533 O(Dup, NA, ONE(CV), TWO(CV,CV), NF) \
534 O(CGetCUNop, NA, ONE(CUV), ONE(CV), NF) \
535 O(UGetCUNop, NA, ONE(CUV), ONE(UV), NF) \
536 O(Null, NA, NOV, ONE(CV), NF) \
537 O(NullUninit, NA, NOV, ONE(UV), NF) \
538 O(True, NA, NOV, ONE(CV), NF) \
539 O(False, NA, NOV, ONE(CV), NF) \
540 O(FuncCred, NA, NOV, ONE(CV), NF) \
541 O(Int, ONE(I64A), NOV, ONE(CV), NF) \
542 O(Double, ONE(DA), NOV, ONE(CV), NF) \
543 O(String, ONE(SA), NOV, ONE(CV), NF) \
544 O(Dict, ONE(AA), NOV, ONE(CV), NF) \
545 O(Keyset, ONE(AA), NOV, ONE(CV), NF) \
546 O(Vec, ONE(AA), NOV, ONE(CV), NF) \
547 O(NewDictArray, ONE(IVA), NOV, ONE(CV), NF) \
548 O(NewStructDict, ONE(VSA), SMANY, ONE(CV), NF) \
549 O(NewVec, ONE(IVA), CMANY, ONE(CV), NF) \
550 O(NewKeysetArray, ONE(IVA), CMANY, ONE(CV), NF) \
551 O(NewRecord, TWO(SA,VSA), SMANY, ONE(CV), NF) \
552 O(AddElemC, NA, THREE(CV,CV,CV), ONE(CV), NF) \
553 O(AddNewElemC, NA, TWO(CV,CV), ONE(CV), NF) \
554 O(NewCol, ONE(OA(CollectionType)), \
556 O(NewPair, NA, TWO(CV,CV), ONE(CV), NF) \
557 O(ColFromArray, ONE(OA(CollectionType)), \
558 ONE(CV), ONE(CV), NF) \
559 O(CnsE, ONE(SA), NOV, ONE(CV), NF) \
560 O(ClsCns, ONE(SA), ONE(CV), ONE(CV), NF) \
561 O(ClsCnsD, TWO(SA,SA), NOV, ONE(CV), NF) \
562 O(ClsCnsL, ONE(LA), ONE(CV), ONE(CV), NF) \
563 O(ClassName, NA, ONE(CV), ONE(CV), NF) \
564 O(LazyClassFromClass, NA, ONE(CV), ONE(CV), NF) \
565 O(File, NA, NOV, ONE(CV), NF) \
566 O(Dir, NA, NOV, ONE(CV), NF) \
567 O(Method, NA, NOV, ONE(CV), NF) \
568 O(Concat, NA, TWO(CV,CV), ONE(CV), NF) \
569 O(ConcatN, ONE(IVA), CMANY, ONE(CV), NF) \
570 O(Add, NA, TWO(CV,CV), ONE(CV), NF) \
571 O(Sub, NA, TWO(CV,CV), ONE(CV), NF) \
572 O(Mul, NA, TWO(CV,CV), ONE(CV), NF) \
573 O(AddO, NA, TWO(CV,CV), ONE(CV), NF) \
574 O(SubO, NA, TWO(CV,CV), ONE(CV), NF) \
575 O(MulO, NA, TWO(CV,CV), ONE(CV), NF) \
576 O(Div, NA, TWO(CV,CV), ONE(CV), NF) \
577 O(Mod, NA, TWO(CV,CV), ONE(CV), NF) \
578 O(Pow, NA, TWO(CV,CV), ONE(CV), NF) \
579 O(Not, NA, ONE(CV), ONE(CV), NF) \
580 O(Same, NA, TWO(CV,CV), ONE(CV), NF) \
581 O(NSame, NA, TWO(CV,CV), ONE(CV), NF) \
582 O(Eq, NA, TWO(CV,CV), ONE(CV), NF) \
583 O(Neq, NA, TWO(CV,CV), ONE(CV), NF) \
584 O(Lt, NA, TWO(CV,CV), ONE(CV), NF) \
585 O(Lte, NA, TWO(CV,CV), ONE(CV), NF) \
586 O(Gt, NA, TWO(CV,CV), ONE(CV), NF) \
587 O(Gte, NA, TWO(CV,CV), ONE(CV), NF) \
588 O(Cmp, NA, TWO(CV,CV), ONE(CV), NF) \
589 O(BitAnd, NA, TWO(CV,CV), ONE(CV), NF) \
590 O(BitOr, NA, TWO(CV,CV), ONE(CV), NF) \
591 O(BitXor, NA, TWO(CV,CV), ONE(CV), NF) \
592 O(BitNot, NA, ONE(CV), ONE(CV), NF) \
593 O(Shl, NA, TWO(CV,CV), ONE(CV), NF) \
594 O(Shr, NA, TWO(CV,CV), ONE(CV), NF) \
595 O(CastBool, NA, ONE(CV), ONE(CV), NF) \
596 O(CastInt, NA, ONE(CV), ONE(CV), NF) \
597 O(CastDouble, NA, ONE(CV), ONE(CV), NF) \
598 O(CastString, NA, ONE(CV), ONE(CV), NF) \
599 O(CastDict, NA, ONE(CV), ONE(CV), NF) \
600 O(CastKeyset, NA, ONE(CV), ONE(CV), NF) \
601 O(CastVec, NA, ONE(CV), ONE(CV), NF) \
602 O(DblAsBits, NA, ONE(CV), ONE(CV), NF) \
603 O(InstanceOf, NA, TWO(CV,CV), ONE(CV), NF) \
604 O(InstanceOfD, ONE(SA), ONE(CV), ONE(CV), NF) \
605 O(IsLateBoundCls, NA, ONE(CV), ONE(CV), NF) \
606 O(IsTypeStructC, ONE(OA(TypeStructResolveOp)), \
607 TWO(CV,CV), ONE(CV), NF) \
608 O(ThrowAsTypeStructException, \
609 NA, TWO(CV,CV), NOV, TF) \
610 O(CombineAndResolveTypeStruct, \
611 ONE(IVA), CMANY, ONE(CV), NF) \
612 O(Select, NA, THREE(CV,CV,CV), ONE(CV), NF) \
613 O(Print, NA, ONE(CV), ONE(CV), NF) \
614 O(Clone, NA, ONE(CV), ONE(CV), NF) \
615 O(Exit, NA, ONE(CV), ONE(CV), TF) \
616 O(Fatal, ONE(OA(FatalOp)), ONE(CV), NOV, TF) \
617 O(Jmp, ONE(BA), NOV, NOV, CF_TF) \
618 O(JmpNS, ONE(BA), NOV, NOV, CF_TF) \
619 O(JmpZ, ONE(BA), ONE(CV), NOV, CF) \
620 O(JmpNZ, ONE(BA), ONE(CV), NOV, CF) \
621 O(Switch, THREE(OA(SwitchKind),I64A,BLA), \
622 ONE(CV), NOV, CF_TF) \
623 O(SSwitch, ONE(SLA), ONE(CV), NOV, CF_TF) \
624 O(RetC, NA, ONE(CV), NOV, CF_TF) \
625 O(RetM, ONE(IVA), CMANY, NOV, CF_TF) \
626 O(RetCSuspended, NA, ONE(CV), NOV, CF_TF) \
627 O(Throw, NA, ONE(CV), NOV, CF_TF) \
628 O(CGetL, ONE(NLA), NOV, ONE(CV), NF) \
629 O(CGetQuietL, ONE(LA), NOV, ONE(CV), NF) \
630 O(CUGetL, ONE(LA), NOV, ONE(CUV), NF) \
631 O(CGetL2, ONE(NLA), ONE(CV), TWO(CV,CV), NF) \
632 O(PushL, ONE(LA), NOV, ONE(CV), NF) \
633 O(CGetG, NA, ONE(CV), ONE(CV), NF) \
634 O(CGetS, ONE(OA(ReadOnlyOp)), \
635 TWO(CV,CV), ONE(CV), NF) \
636 O(ClassGetC, NA, ONE(CV), ONE(CV), NF) \
637 O(ClassGetTS, NA, ONE(CV), TWO(CV,CV), NF) \
638 O(GetMemoKeyL, ONE(NLA), NOV, ONE(CV), NF) \
639 O(AKExists, NA, TWO(CV,CV), ONE(CV), NF) \
640 O(IssetL, ONE(LA), NOV, ONE(CV), NF) \
641 O(IssetG, NA, ONE(CV), ONE(CV), NF) \
642 O(IssetS, NA, TWO(CV,CV), ONE(CV), NF) \
643 O(IsUnsetL, ONE(LA), NOV, ONE(CV), NF) \
644 O(IsTypeC, ONE(OA(IsTypeOp)),ONE(CV), ONE(CV), NF) \
645 O(IsTypeL, TWO(NLA, \
646 OA(IsTypeOp)), NOV, ONE(CV), NF) \
647 O(AssertRATL, TWO(ILA,RATA), NOV, NOV, NF) \
648 O(AssertRATStk, TWO(IVA,RATA), NOV, NOV, NF) \
649 O(SetL, ONE(LA), ONE(CV), ONE(CV), NF) \
650 O(SetG, NA, TWO(CV,CV), ONE(CV), NF) \
651 O(SetS, ONE(OA(ReadOnlyOp)), \
652 THREE(CV,CV,CV), ONE(CV), NF) \
654 OA(SetOpOp)), ONE(CV), ONE(CV), NF) \
655 O(SetOpG, ONE(OA(SetOpOp)), TWO(CV,CV), ONE(CV), NF) \
656 O(SetOpS, ONE(OA(SetOpOp)), THREE(CV,CV,CV), ONE(CV), NF) \
657 O(IncDecL, TWO(NLA, OA(IncDecOp)), \
659 O(IncDecG, ONE(OA(IncDecOp)),ONE(CV), ONE(CV), NF) \
660 O(IncDecS, ONE(OA(IncDecOp)),TWO(CV,CV), ONE(CV), NF) \
661 O(UnsetL, ONE(LA), NOV, NOV, NF) \
662 O(UnsetG, NA, ONE(CV), NOV, NF) \
664 O(ResolveFunc, ONE(SA), NOV, ONE(CV), NF) \
665 O(ResolveMethCaller,ONE(SA), NOV, ONE(CV), NF) \
666 O(ResolveRFunc, ONE(SA), ONE(CV), ONE(CV), NF) \
667 O(ResolveObjMethod,NA, TWO(CV,CV), ONE(CV), NF) \
668 O(ResolveClsMethod,ONE(SA), ONE(CV), ONE(CV), NF) \
669 O(ResolveClsMethodD, \
670 TWO(SA,SA), NOV, ONE(CV), NF) \
671 O(ResolveClsMethodS, \
672 TWO(OA(SpecialClsRef),SA), \
674 O(ResolveRClsMethod, \
675 ONE(SA), TWO(CV,CV), ONE(CV), NF) \
676 O(ResolveRClsMethodD, \
677 TWO(SA,SA), ONE(CV), ONE(CV), NF) \
678 O(ResolveRClsMethodS, \
679 TWO(OA(SpecialClsRef),SA), \
680 ONE(CV), ONE(CV), NF) \
681 O(ResolveClass, ONE(SA), NOV, ONE(CV), NF) \
682 O(LazyClass, ONE(SA), NOV, ONE(CV), NF) \
683 O(NewObj, NA, ONE(CV), ONE(CV), NF) \
684 O(NewObjR, NA, TWO(CV,CV), ONE(CV), NF) \
685 O(NewObjD, ONE(SA), NOV, ONE(CV), NF) \
686 O(NewObjRD, ONE(SA), ONE(CV), ONE(CV), NF) \
687 O(NewObjS, ONE(OA(SpecialClsRef)), \
689 O(LockObj, NA, ONE(CV), ONE(CV), NF) \
690 O(FCallClsMethod, THREE(FCA,SA,OA(IsLogAsDynamicCallOp)), \
691 FCALL(2, 0), FCALL, CF) \
692 O(FCallClsMethodD, FOUR(FCA,SA,SA,SA), \
693 FCALL(0, 0), FCALL, CF) \
694 O(FCallClsMethodS, THREE(FCA,SA,OA(SpecialClsRef)), \
695 FCALL(1, 0), FCALL, CF) \
696 O(FCallClsMethodSD,FOUR(FCA,SA,OA(SpecialClsRef),SA), \
697 FCALL(0, 0), FCALL, CF) \
698 O(FCallCtor, TWO(FCA,SA), FCALL(0, 1), FCALL, CF) \
699 O(FCallFunc, ONE(FCA), FCALL(1, 0), FCALL, CF) \
700 O(FCallFuncD, TWO(FCA,SA), FCALL(0, 0), FCALL, CF) \
701 O(FCallObjMethod, THREE(FCA,SA,OA(ObjMethodOp)), \
702 FCALL(1, 1), FCALL, CF) \
703 O(FCallObjMethodD, FOUR(FCA,SA,OA(ObjMethodOp),SA), \
704 FCALL(0, 1), FCALL, CF) \
705 O(IterInit, TWO(ITA,BA), ONE(CV), NOV, CF) \
706 O(LIterInit, THREE(ITA,LA,BA), NOV, NOV, CF) \
707 O(IterNext, TWO(ITA,BA), NOV, NOV, CF) \
708 O(LIterNext, THREE(ITA,LA,BA), NOV, NOV, CF) \
709 O(IterFree, ONE(IA), NOV, NOV, NF) \
710 O(LIterFree, TWO(IA,LA), NOV, NOV, NF) \
711 O(Incl, NA, ONE(CV), ONE(CV), NF) \
712 O(InclOnce, NA, ONE(CV), ONE(CV), NF) \
713 O(Req, NA, ONE(CV), ONE(CV), NF) \
714 O(ReqOnce, NA, ONE(CV), ONE(CV), NF) \
715 O(ReqDoc, NA, ONE(CV), ONE(CV), NF) \
716 O(Eval, NA, ONE(CV), ONE(CV), NF) \
717 O(This, NA, NOV, ONE(CV), NF) \
718 O(BareThis, ONE(OA(BareThisOp)), \
720 O(CheckThis, NA, NOV, NOV, NF) \
721 O(ChainFaults, NA, TWO(CV,CV), ONE(CV), NF) \
722 O(OODeclExists, ONE(OA(OODeclExistsOp)), \
723 TWO(CV,CV), ONE(CV), NF) \
724 O(VerifyOutType, ONE(IVA), ONE(CV), ONE(CV), NF) \
725 O(VerifyParamType, ONE(ILA), NOV, NOV, NF) \
726 O(VerifyParamTypeTS, ONE(ILA), ONE(CV), NOV, NF) \
727 O(VerifyRetTypeC, NA, ONE(CV), ONE(CV), NF) \
728 O(VerifyRetTypeTS, NA, TWO(CV,CV), ONE(CV), NF) \
729 O(VerifyRetNonNullC, NA, ONE(CV), ONE(CV), NF) \
730 O(Self, NA, NOV, ONE(CV), NF) \
731 O(Parent, NA, NOV, ONE(CV), NF) \
732 O(LateBoundCls, NA, NOV, ONE(CV), NF) \
733 O(RecordReifiedGeneric, NA, ONE(CV), ONE(CV), NF) \
734 O(CheckReifiedGenericMismatch, NA, ONE(CV), NOV, NF) \
735 O(NativeImpl, NA, NOV, NOV, CF_TF) \
736 O(CreateCl, TWO(IVA,IVA), CUMANY, ONE(CV), NF) \
737 O(CreateCont, NA, NOV, ONE(CV), CF) \
738 O(ContEnter, NA, ONE(CV), ONE(CV), CF) \
739 O(ContRaise, NA, ONE(CV), ONE(CV), CF) \
740 O(Yield, NA, ONE(CV), ONE(CV), CF) \
741 O(YieldK, NA, TWO(CV,CV), ONE(CV), CF) \
742 O(ContCheck, ONE(OA(ContCheckOp)), NOV, NOV, NF) \
743 O(ContValid, NA, NOV, ONE(CV), NF) \
744 O(ContKey, NA, NOV, ONE(CV), NF) \
745 O(ContCurrent, NA, NOV, ONE(CV), NF) \
746 O(ContGetReturn, NA, NOV, ONE(CV), NF) \
747 O(WHResult, NA, ONE(CV), ONE(CV), NF) \
748 O(Await, NA, ONE(CV), ONE(CV), CF) \
749 O(AwaitAll, ONE(LAR), NOV, ONE(CV), CF) \
750 O(Idx, NA, THREE(CV,CV,CV), ONE(CV), NF) \
751 O(ArrayIdx, NA, THREE(CV,CV,CV), ONE(CV), NF) \
752 O(ArrayMarkLegacy, NA, TWO(CV,CV), ONE(CV), NF) \
753 O(ArrayUnmarkLegacy, NA, TWO(CV,CV), ONE(CV), NF) \
754 O(CheckProp, ONE(SA), NOV, ONE(CV), NF) \
755 O(InitProp, TWO(SA, OA(InitPropOp)), \
757 O(Silence, TWO(LA,OA(SilenceOp)), \
759 O(ThrowNonExhaustiveSwitch, NA, NOV, NOV, NF) \
760 O(RaiseClassStringConversionWarning, \
762 O(BaseGC, TWO(IVA, OA(MOpMode)), \
764 O(BaseGL, TWO(LA, OA(MOpMode)), \
766 O(BaseSC, FOUR(IVA, IVA, OA(MOpMode), OA(ReadOnlyOp)), \
768 O(BaseL, TWO(NLA, OA(MOpMode)), \
770 O(BaseC, TWO(IVA, OA(MOpMode)), \
772 O(BaseH, NA, NOV, NOV, NF) \
773 O(Dim, TWO(OA(MOpMode), KA), \
775 O(QueryM, THREE(IVA, OA(QueryMOp), KA), \
776 MFINAL, ONE(CV), NF) \
777 O(SetM, TWO(IVA, KA), C_MFINAL(1), ONE(CV), NF) \
778 O(SetRangeM, THREE(IVA, IVA, OA(SetRangeOp)), \
779 C_MFINAL(3), NOV, NF) \
780 O(IncDecM, THREE(IVA, OA(IncDecOp), KA), \
781 MFINAL, ONE(CV), NF) \
782 O(SetOpM, THREE(IVA, OA(SetOpOp), KA), \
783 C_MFINAL(1), ONE(CV), NF) \
784 O(UnsetM, TWO(IVA, KA), MFINAL, NOV, NF) \
785 O(MemoGet, TWO(BA, LAR), NOV, ONE(CV), CF) \
786 O(MemoGetEager, THREE(BA, BA, LAR), \
788 O(MemoSet, ONE(LAR), ONE(CV), ONE(CV), NF) \
789 O(MemoSetEager, ONE(LAR), ONE(CV), ONE(CV), NF)
791 enum class Op
: uint16_t {
792 #define O(name, ...) name,
798 constexpr size_t Op_count
= OPCODES
;
802 * Also put Op* in the enclosing namespace, to avoid having to change every
803 * existing usage site of the enum values.
805 #define O(name, ...) UNUSED auto constexpr Op##name = Op::name;
809 // These are comparable by default under MSVC.
811 inline constexpr bool operator<(Op a
, Op b
) { return size_t(a
) < size_t(b
); }
812 inline constexpr bool operator>(Op a
, Op b
) { return size_t(a
) > size_t(b
); }
813 inline constexpr bool operator<=(Op a
, Op b
) {
814 return size_t(a
) <= size_t(b
);
816 inline constexpr bool operator>=(Op a
, Op b
) {
817 return size_t(a
) >= size_t(b
);
821 constexpr bool isValidOpcode(Op op
) {
822 return size_t(op
) < Op_count
;
825 inline MOpMode
getQueryMOpMode(QueryMOp op
) {
827 case QueryMOp::CGet
: return MOpMode::Warn
;
828 case QueryMOp::CGetQuiet
:
829 case QueryMOp::Isset
: return MOpMode::None
;
830 case QueryMOp::InOut
: return MOpMode::InOut
;
832 always_assert(false);
835 #define HIGH_OPCODES \
840 OpHighStart
= Op_count
-1,
841 #define O(name) Op##name,
852 explicit ImmVector() : m_start(0) {}
854 explicit ImmVector(const uint8_t* start
,
858 , m_numStack(numStack
)
862 bool isValid() const { return m_start
!= 0; }
864 const int32_t* vec32() const {
865 return reinterpret_cast<const int32_t*>(m_start
);
867 folly::Range
<const int32_t*> range32() const {
869 return {base
, base
+ size()};
871 const StrVecItem
* strvec() const {
872 return reinterpret_cast<const StrVecItem
*>(m_start
);
876 * Returns the length of the immediate vector in bytes (for M
877 * vectors) or elements (for switch vectors)
879 int32_t size() const { return m_length
; }
882 * Returns the number of elements on the execution stack that this vector
883 * will need to access.
885 int numStackValues() const { return m_numStack
; }
890 const uint8_t* m_start
;
893 // Must be an opcode that actually has an ImmVector.
894 ImmVector
getImmVector(PC opcode
);
896 // Some decoding helper functions.
897 int numImmediates(Op opcode
);
898 ArgType
immType(Op opcode
, int idx
);
899 bool hasImmVector(Op opcode
);
900 int instrLen(PC opcode
);
901 int numSuccs(PC opcode
);
906 * The returned struct has normalized variable-sized immediates. u must be
907 * provided unless you know that the immediate is not of type KA.
909 * Don't use with RATA immediates.
911 ArgUnion
getImm(PC opcode
, int idx
, const Unit
* u
= nullptr);
913 // Don't use this with variable-sized immediates!
914 ArgUnion
* getImmPtr(PC opcode
, int idx
);
916 void staticStreamer(const TypedValue
* tv
, std::string
& out
);
918 std::string
instrToString(PC it
, Either
<const Func
*, const FuncEmitter
*> f
);
919 void staticArrayStreamer(const ArrayData
*, std::string
&);
922 * Convert subopcodes or opcodes into strings.
924 const char* opcodeToName(Op op
);
925 const char* subopToName(InitPropOp
);
926 const char* subopToName(IsTypeOp
);
927 const char* subopToName(FatalOp
);
928 const char* subopToName(CollectionType
);
929 const char* subopToName(SetOpOp
);
930 const char* subopToName(IncDecOp
);
931 const char* subopToName(BareThisOp
);
932 const char* subopToName(SilenceOp
);
933 const char* subopToName(OODeclExistsOp
);
934 const char* subopToName(ObjMethodOp
);
935 const char* subopToName(SwitchKind
);
936 const char* subopToName(MOpMode
);
937 const char* subopToName(QueryMOp
);
938 const char* subopToName(SetRangeOp
);
939 const char* subopToName(TypeStructResolveOp
);
940 const char* subopToName(ContCheckOp
);
941 const char* subopToName(CudOp
);
942 const char* subopToName(SpecialClsRef
);
943 const char* subopToName(IsLogAsDynamicCallOp
);
944 const char* subopToName(ReadOnlyOp
);
947 * Returns true iff the given SubOp is in the valid range for its type.
949 template<class Subop
>
950 bool subopValid(Subop
);
953 * Try to parse a string into a subop name of a given type.
955 * Returns std::nullopt if the string is not recognized as that type of
958 template<class SubOpType
>
959 Optional
<SubOpType
> nameToSubop(const char*);
961 using OffsetList
= std::vector
<Offset
>;
963 // Returns a jump offsets relative to the instruction, or nothing if
964 // the instruction cannot jump.
965 OffsetList
instrJumpOffsets(PC instr
);
967 // returns absolute address of targets, or nothing if instruction
969 OffsetList
instrJumpTargets(PC instrs
, Offset pos
);
972 * Returns the set of bytecode offsets for the instructions that may
973 * be executed immediately after opc.
975 using OffsetSet
= hphp_hash_set
<Offset
>;
976 OffsetSet
instrSuccOffsets(PC opc
, const Func
* func
);
979 * Some CF instructions can be treated as non-CF instructions for most analysis
980 * purposes, such as bytecode verification and HHBBC. These instructions change
981 * vmpc() to point somewhere in a different function, but the runtime
982 * guarantees that if excution ever returns to the original frame, it will be
983 * at the location immediately following the instruction in question. This
984 * creates the illusion that the instruction fell through normally to the
985 * instruction after it, within the context of its execution frame.
987 * The canonical example of this behavior are the FCall* instructions, so we use
988 * "non-call control flow" to describe the set of CF instruction that do not
989 * exhibit this behavior. This function returns true if `opcode' is a non-call
990 * control flow instruction.
992 bool instrIsNonCallControlFlow(Op opcode
);
994 bool instrAllowsFallThru(Op opcode
);
996 constexpr InstrFlags instrFlagsData
[] = {
997 #define O(unusedName, unusedImm, unusedPop, unusedPush, flags) flags,
1002 constexpr InstrFlags
instrFlags(Op opcode
) {
1003 return instrFlagsData
[size_t(opcode
)];
1006 constexpr bool instrIsControlFlow(Op opcode
) {
1007 return (instrFlags(opcode
) & CF
) != 0;
1010 constexpr bool isUnconditionalJmp(Op opcode
) {
1011 return opcode
== Op::Jmp
|| opcode
== Op::JmpNS
;
1014 constexpr bool isConditionalJmp(Op opcode
) {
1015 return opcode
== Op::JmpZ
|| opcode
== Op::JmpNZ
;
1018 constexpr bool isJmp(Op opcode
) {
1020 opcode
== Op::Jmp
||
1021 opcode
== Op::JmpNS
||
1022 opcode
== Op::JmpZ
||
1023 opcode
== Op::JmpNZ
;
1026 constexpr bool isObjectConstructorOp(Op opcode
) {
1028 opcode
== Op::NewObj
||
1029 opcode
== Op::NewObjD
||
1030 opcode
== Op::NewObjR
||
1031 opcode
== Op::NewObjRD
||
1032 opcode
== Op::NewObjS
;
1035 constexpr bool isArrLikeConstructorOp(Op opcode
) {
1037 opcode
== Op::Dict
||
1038 opcode
== Op::Keyset
||
1039 opcode
== Op::Vec
||
1040 opcode
== Op::NewDictArray
||
1041 opcode
== Op::NewStructDict
||
1042 opcode
== Op::NewVec
||
1043 opcode
== Op::NewKeysetArray
;
1046 constexpr bool isArrLikeCastOp(Op opcode
) {
1048 opcode
== Op::CastVec
||
1049 opcode
== Op::CastDict
||
1050 opcode
== Op::CastKeyset
;
1053 constexpr bool isComparisonOp(Op opcode
) {
1055 opcode
== Op::Cmp
||
1057 opcode
== Op::Neq
||
1059 opcode
== Op::Gte
||
1061 opcode
== Op::Lte
||
1062 opcode
== Op::Same
||
1063 opcode
== Op::NSame
;
1066 constexpr bool isFCallClsMethod(Op opcode
) {
1068 opcode
== OpFCallClsMethod
||
1069 opcode
== OpFCallClsMethodD
||
1070 opcode
== OpFCallClsMethodS
||
1071 opcode
== OpFCallClsMethodSD
;
1074 constexpr bool isFCallFunc(Op opcode
) {
1076 opcode
== OpFCallFunc
||
1077 opcode
== OpFCallFuncD
;
1080 constexpr bool isFCallObjMethod(Op opcode
) {
1082 opcode
== OpFCallObjMethod
||
1083 opcode
== OpFCallObjMethodD
;
1086 constexpr bool isFCall(Op opcode
) {
1088 opcode
== OpFCallCtor
||
1089 isFCallClsMethod(opcode
) ||
1090 isFCallFunc(opcode
) ||
1091 isFCallObjMethod(opcode
);
1094 constexpr bool isRet(Op op
) {
1095 return op
== OpRetC
|| op
== OpRetCSuspended
|| op
== OpRetM
;
1098 constexpr bool isReturnish(Op op
) {
1099 return isRet(op
) || op
== Op::NativeImpl
;
1102 constexpr bool isSwitch(Op op
) {
1103 return op
== OpSwitch
|| op
== OpSSwitch
;
1106 constexpr bool isTypeAssert(Op op
) {
1107 return op
== OpAssertRATL
|| op
== OpAssertRATStk
;
1110 constexpr bool isIteratorOp(Op op
) {
1111 return op
== OpIterInit
|| op
== Op::LIterInit
||
1112 op
== OpIterNext
|| op
== Op::LIterNext
;
1115 inline bool isMemberBaseOp(Op op
) {
1130 inline bool isMemberDimOp(Op op
) {
1131 return op
== Op::Dim
;
1134 inline bool isMemberFinalOp(Op op
) {
1149 inline bool isMemberOp(Op op
) {
1150 return isMemberBaseOp(op
) || isMemberDimOp(op
) || isMemberFinalOp(op
);
1153 inline MOpMode
finalMemberOpMode(Op op
) {
1159 return MOpMode::Define
;
1161 return MOpMode::Unset
;
1163 return MOpMode::None
;
1166 false, "Unknown final member op {}", opcodeToName(op
)
1171 // true if the opcode body can set pc=0 to halt the interpreter.
1172 constexpr bool instrCanHalt(Op op
) {
1173 return op
== OpRetC
|| op
== OpNativeImpl
||
1174 op
== OpAwait
|| op
== OpAwaitAll
|| op
== OpCreateCont
||
1175 op
== OpYield
|| op
== OpYieldK
|| op
== OpRetM
||
1176 op
== OpRetCSuspended
;
1179 int instrNumPops(PC opcode
);
1180 int instrNumPushes(PC opcode
);
1181 FlavorDesc
instrInputFlavor(PC op
, uint32_t idx
);
1185 //////////////////////////////////////////////////////////////////////
1189 struct hash
<HPHP::Op
> {
1190 size_t operator()(HPHP::Op op
) const {
1191 return HPHP::hash_int64(size_t(op
));
1196 //////////////////////////////////////////////////////////////////////