Compact the FCallArgs encoding
[hiphop-php.git] / hphp / runtime / vm / hhbc.h
blobfc17b5d07e8ed497e318d424822e24bee0e1bcde
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #pragma once
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"
31 namespace HPHP {
33 //////////////////////////////////////////////////////////////////////
35 struct Unit;
36 struct UnitEmitter;
37 struct Func;
38 struct FuncEmitter;
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.
45 struct LocalRange {
46 uint32_t first;
47 uint32_t count;
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.
63 struct IterArgs {
64 enum Flags : uint8_t {
65 None = 0,
66 // The base is stored in a local, and that local is unmodified in the loop.
67 BaseConst = (1 << 0),
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) {}
75 bool hasKey() const {
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;
85 int32_t iterId;
86 int32_t keyId;
87 int32_t valId;
88 Flags 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 {
103 None = 0,
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
116 HasInOut = (1 << 5),
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)
132 : numArgs(numArgs)
133 , numRets(numRets)
134 , flags(flags)
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);
145 uint32_t numArgs;
146 uint32_t numRets;
147 Flags flags;
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)
157 , context(context) {
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());
167 return FCallArgs(
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.
209 #define ARGTYPES \
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 */
231 enum ArgType {
232 #define ARGTYPE(name, type) name,
233 #define ARGTYPEVEC(name, type) name,
234 ARGTYPES
235 #undef ARGTYPE
236 #undef ARGTYPEVEC
239 union ArgUnion {
240 ArgUnion() : u_LA{0} {}
241 uint8_t bytes[0];
242 #define ARGTYPE(name, type) type u_##name;
243 #define ARGTYPEVEC(name, type) type u_##name;
244 ARGTYPES
245 #undef ARGTYPE
246 #undef ARGTYPEVEC
249 enum FlavorDesc {
250 NOV, // None
251 CV, // TypedValue
252 UV, // Uninit
253 CUV, // TypedValue, or Uninit argument
256 enum InstrFlags {
257 /* No flags. */
258 NF = 0x0,
260 /* Terminal: next instruction is not reachable via fall through or the callee
261 * returning control. This includes instructions like Throw that always throw
262 * exceptions. */
263 TF = 0x1,
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. */
270 CF = 0x2,
272 /* Shorthand for common combinations. */
273 CF_TF = (CF | TF),
276 #define INCDEC_OPS \
277 INCDEC_OP(PreInc) \
278 INCDEC_OP(PostInc) \
279 INCDEC_OP(PreDec) \
280 INCDEC_OP(PostDec) \
282 INCDEC_OP(PreIncO) \
283 INCDEC_OP(PostIncO) \
284 INCDEC_OP(PreDecO) \
285 INCDEC_OP(PostDecO) \
287 enum class IncDecOp : uint8_t {
288 #define INCDEC_OP(incDecOp) incDecOp,
289 INCDEC_OPS
290 #undef INCDEC_OP
293 inline bool isPre(IncDecOp op) {
294 return
295 op == IncDecOp::PreInc || op == IncDecOp::PreIncO ||
296 op == IncDecOp::PreDec || op == IncDecOp::PreDecO;
299 inline bool isInc(IncDecOp op) {
300 return
301 op == IncDecOp::PreInc || op == IncDecOp::PreIncO ||
302 op == IncDecOp::PostInc || op == IncDecOp::PostIncO;
305 inline bool isIncDecO(IncDecOp op) {
306 return
307 op == IncDecOp::PreIncO || op == IncDecOp::PreDecO ||
308 op == IncDecOp::PostIncO || op == IncDecOp::PostDecO;
311 #define ISTYPE_OPS \
312 ISTYPE_OP(Null) \
313 ISTYPE_OP(Bool) \
314 ISTYPE_OP(Int) \
315 ISTYPE_OP(Dbl) \
316 ISTYPE_OP(Str) \
317 ISTYPE_OP(Vec) \
318 ISTYPE_OP(Dict) \
319 ISTYPE_OP(Keyset) \
320 ISTYPE_OP(Obj) \
321 ISTYPE_OP(Scalar) \
322 ISTYPE_OP(ArrLike) \
323 ISTYPE_OP(LegacyArrLike) \
324 ISTYPE_OP(Res) \
325 ISTYPE_OP(ClsMeth) \
326 ISTYPE_OP(Func) \
327 ISTYPE_OP(Class)
329 enum class IsTypeOp : uint8_t {
330 #define ISTYPE_OP(op) op,
331 ISTYPE_OPS
332 #undef ISTYPE_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,
341 INITPROP_OPS
342 #undef INITPROP_OP
345 #define FATAL_OPS \
346 FATAL_OP(Runtime) \
347 FATAL_OP(Parse) \
348 FATAL_OP(RuntimeOmitFrame)
350 enum class FatalOp : uint8_t {
351 #define FATAL_OP(x) x,
352 FATAL_OPS
353 #undef FATAL_OP
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.
359 #define SETOP_OPS \
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,
378 SETOP_OPS
379 #undef SETOP_OP
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,
389 BARETHIS_OPS
390 #undef BARETHIS_OP
393 #define SILENCE_OPS \
394 SILENCE_OP(Start) \
395 SILENCE_OP(End)
397 enum class SilenceOp : uint8_t {
398 #define SILENCE_OP(x) x,
399 SILENCE_OPS
400 #undef SILENCE_OP
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,
410 OO_DECL_EXISTS_OPS
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,
420 OBJMETHOD_OPS
421 #undef OBJMETHOD_OP
424 #define SWITCH_KINDS \
425 KIND(Unbounded) \
426 KIND(Bounded)
428 enum class SwitchKind : uint8_t {
429 #define KIND(x) x,
430 SWITCH_KINDS
431 #undef KIND
434 #define M_OP_MODES \
435 MODE(None) \
436 MODE(Warn) \
437 MODE(Define) \
438 MODE(Unset) \
439 /* InOut mode restricts allowed bases to the
440 array like types. */ \
441 MODE(InOut)
443 enum class MOpMode : uint8_t {
444 #define MODE(name) name,
445 M_OP_MODES
446 #undef MODE
449 #define QUERY_M_OPS \
450 OP(CGet) \
451 OP(CGetQuiet) \
452 OP(Isset) \
453 OP(InOut)
455 enum class QueryMOp : uint8_t {
456 #define OP(name) name,
457 QUERY_M_OPS
458 #undef OP
461 #define SET_RANGE_OPS \
462 OP(Forward) \
463 OP(Reverse)
465 enum class SetRangeOp : uint8_t {
466 #define OP(name) name,
467 SET_RANGE_OPS
468 #undef OP
471 #define TYPE_STRUCT_RESOLVE_OPS \
472 OP(Resolve) \
473 OP(DontResolve)
475 enum class TypeStructResolveOp : uint8_t {
476 #define OP(name) name,
477 TYPE_STRUCT_RESOLVE_OPS
478 #undef OP
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,
487 CONT_CHECK_OPS
488 #undef CONT_CHECK_OP
491 #define CUD_OPS \
492 CUD_OP(IgnoreIter) \
493 CUD_OP(FreeIter)
495 enum class CudOp : uint8_t {
496 #define CUD_OP(name) name,
497 CUD_OPS
498 #undef CUD_OP
501 #define SPECIAL_CLS_REFS \
502 REF(Self) \
503 REF(Static) \
504 REF(Parent)
506 enum class SpecialClsRef : uint8_t {
507 #define REF(name) name,
508 SPECIAL_CLS_REFS
509 #undef REF
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
525 #define OPCODES \
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)), \
555 NOV, ONE(CV), NF) \
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) \
653 O(SetOpL, TWO(LA, \
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)), \
658 NOV, ONE(CV), NF) \
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), \
673 NOV, ONE(CV), NF) \
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)), \
688 NOV, ONE(CV), NF) \
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)), \
719 NOV, ONE(CV), NF) \
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)), \
756 ONE(CV), NOV, NF) \
757 O(Silence, TWO(LA,OA(SilenceOp)), \
758 NOV, NOV, NF) \
759 O(ThrowNonExhaustiveSwitch, NA, NOV, NOV, NF) \
760 O(RaiseClassStringConversionWarning, \
761 NA, NOV, NOV, NF) \
762 O(BaseGC, TWO(IVA, OA(MOpMode)), \
763 NOV, NOV, NF) \
764 O(BaseGL, TWO(LA, OA(MOpMode)), \
765 NOV, NOV, NF) \
766 O(BaseSC, FOUR(IVA, IVA, OA(MOpMode), OA(ReadOnlyOp)), \
767 NOV, NOV, NF) \
768 O(BaseL, TWO(NLA, OA(MOpMode)), \
769 NOV, NOV, NF) \
770 O(BaseC, TWO(IVA, OA(MOpMode)), \
771 NOV, NOV, NF) \
772 O(BaseH, NA, NOV, NOV, NF) \
773 O(Dim, TWO(OA(MOpMode), KA), \
774 NOV, NOV, NF) \
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), \
787 NOV, ONE(CV), CF) \
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,
793 OPCODES
794 #undef O
797 #define O(...) + 1
798 constexpr size_t Op_count = OPCODES;
799 #undef O
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;
806 OPCODES
807 #undef O
809 // These are comparable by default under MSVC.
810 #ifndef _MSC_VER
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);
819 #endif
821 constexpr bool isValidOpcode(Op op) {
822 return size_t(op) < Op_count;
825 inline MOpMode getQueryMOpMode(QueryMOp op) {
826 switch (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 \
836 O(FuncPrologue) \
837 O(TraceletGuard)
839 enum HighOp {
840 OpHighStart = Op_count-1,
841 #define O(name) Op##name,
842 HIGH_OPCODES
843 #undef O
846 struct StrVecItem {
847 Id str;
848 Offset dest;
851 struct ImmVector {
852 explicit ImmVector() : m_start(0) {}
854 explicit ImmVector(const uint8_t* start,
855 int32_t length,
856 int32_t numStack)
857 : m_length(length)
858 , m_numStack(numStack)
859 , m_start(start)
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 {
868 auto base = vec32();
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; }
887 private:
888 int32_t m_length;
889 int32_t 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);
903 PC skipCall(PC pc);
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
956 * subop.
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
968 // cannot jump
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,
998 OPCODES
999 #undef O
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) {
1019 return
1020 opcode == Op::Jmp ||
1021 opcode == Op::JmpNS ||
1022 opcode == Op::JmpZ ||
1023 opcode == Op::JmpNZ;
1026 constexpr bool isObjectConstructorOp(Op opcode) {
1027 return
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) {
1036 return
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) {
1047 return
1048 opcode == Op::CastVec ||
1049 opcode == Op::CastDict ||
1050 opcode == Op::CastKeyset;
1053 constexpr bool isComparisonOp(Op opcode) {
1054 return
1055 opcode == Op::Cmp ||
1056 opcode == Op::Eq ||
1057 opcode == Op::Neq ||
1058 opcode == Op::Gt ||
1059 opcode == Op::Gte ||
1060 opcode == Op::Lt ||
1061 opcode == Op::Lte ||
1062 opcode == Op::Same ||
1063 opcode == Op::NSame;
1066 constexpr bool isFCallClsMethod(Op opcode) {
1067 return
1068 opcode == OpFCallClsMethod ||
1069 opcode == OpFCallClsMethodD ||
1070 opcode == OpFCallClsMethodS ||
1071 opcode == OpFCallClsMethodSD;
1074 constexpr bool isFCallFunc(Op opcode) {
1075 return
1076 opcode == OpFCallFunc ||
1077 opcode == OpFCallFuncD;
1080 constexpr bool isFCallObjMethod(Op opcode) {
1081 return
1082 opcode == OpFCallObjMethod ||
1083 opcode == OpFCallObjMethodD;
1086 constexpr bool isFCall(Op opcode) {
1087 return
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) {
1116 switch (op) {
1117 case Op::BaseGC:
1118 case Op::BaseGL:
1119 case Op::BaseSC:
1120 case Op::BaseL:
1121 case Op::BaseC:
1122 case Op::BaseH:
1123 return true;
1125 default:
1126 return false;
1130 inline bool isMemberDimOp(Op op) {
1131 return op == Op::Dim;
1134 inline bool isMemberFinalOp(Op op) {
1135 switch (op) {
1136 case Op::QueryM:
1137 case Op::SetM:
1138 case Op::SetRangeM:
1139 case Op::IncDecM:
1140 case Op::SetOpM:
1141 case Op::UnsetM:
1142 return true;
1144 default:
1145 return false;
1149 inline bool isMemberOp(Op op) {
1150 return isMemberBaseOp(op) || isMemberDimOp(op) || isMemberFinalOp(op);
1153 inline MOpMode finalMemberOpMode(Op op) {
1154 switch(op){
1155 case Op::SetM:
1156 case Op::SetRangeM:
1157 case Op::IncDecM:
1158 case Op::SetOpM:
1159 return MOpMode::Define;
1160 case Op::UnsetM:
1161 return MOpMode::Unset;
1162 case Op::QueryM:
1163 return MOpMode::None;
1164 default:
1165 always_assert_flog(
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 //////////////////////////////////////////////////////////////////////
1187 namespace std {
1188 template<>
1189 struct hash<HPHP::Op> {
1190 size_t operator()(HPHP::Op op) const {
1191 return HPHP::hash_int64(size_t(op));
1196 //////////////////////////////////////////////////////////////////////