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 #ifndef incl_HPHP_VM_HHBC_H_
18 #define incl_HPHP_VM_HHBC_H_
20 #include <type_traits>
22 #include <folly/Optional.h>
24 #include "hphp/runtime/base/repo-auth-type.h"
25 #include "hphp/runtime/base/typed-value.h"
26 #include "hphp/runtime/base/types.h"
27 #include "hphp/runtime/base/header-kind.h"
28 #include "hphp/runtime/vm/member-key.h"
29 #include "hphp/util/compact-vector.h"
30 #include "hphp/util/either.h"
31 #include "hphp/util/functional.h"
32 #include "hphp/util/hash-set.h"
36 //////////////////////////////////////////////////////////////////////
42 constexpr size_t kMaxHhbcImms
= 5;
44 // A contiguous range of locals. The count is the number of locals
45 // including the first. If the range is empty, count will be zero and
46 // first's value is arbitrary.
53 // Arguments to FCall opcodes.
54 // hhas format: <flags> <numArgs> <numRets> <byRefs> <asyncEagerOffset>
55 // hhbc format: <uint8:flags> ?<iva:numArgs> ?<iva:numRets>
56 // ?<boolvec:byRefs> ?<ba:asyncEagerOffset>
57 // flags = flags (hhas doesn't have HHBC-only flags)
58 // numArgs = flags >> kFirstNumArgsBit
59 // ? flags >> kFirstNumArgsBit - 1 : decode_iva()
60 // numRets = flags & HasInOut ? decode_iva() : 1
61 // byRefs = flags & EnforceReffiness ? decode bool vec : nullptr
62 // asyncEagerOffset = flags & HasAEO ? decode_ba() : kInvalidOffset
63 struct FCallArgsBase
{
64 enum Flags
: uint8_t {
66 // Unpack remaining arguments from a varray passed by ...$args.
68 // Callee is known to support async eager return.
69 SupportsAsyncEagerReturn
= (1 << 1),
70 // HHBC-only: is the number of returns provided? false => 1
72 // HHBC-only: should this FCall enforce argument reffiness?
73 EnforceReffiness
= (1 << 3),
74 // HHBC-only: is the async eager offset provided? false => kInvalidOffset
75 HasAsyncEagerOffset
= (1 << 4),
76 // HHBC-only: the remaining space is used for number of arguments
77 NumArgsStart
= (1 << 5),
80 // Flags that are valid on FCallArgsBase::flags struct (i.e. non-HHBC-only).
81 static constexpr uint8_t kInternalFlags
=
82 HasUnpack
| SupportsAsyncEagerReturn
;
83 // The first (lowest) bit of numArgs.
84 static constexpr uint8_t kFirstNumArgsBit
= 5;
86 explicit FCallArgsBase(Flags flags
, uint32_t numArgs
, uint32_t numRets
)
87 : numArgs(numArgs
), numRets(numRets
), flags(flags
) {
88 assertx(!(flags
& ~kInternalFlags
));
90 bool hasUnpack() const { return flags
& Flags::HasUnpack
; }
91 bool supportsAsyncEagerReturn() const {
92 return flags
& Flags::SupportsAsyncEagerReturn
;
99 struct FCallArgs
: FCallArgsBase
{
100 explicit FCallArgs(Flags flags
, uint32_t numArgs
, uint32_t numRets
,
101 const uint8_t* byRefs
, Offset asyncEagerOffset
)
102 : FCallArgsBase(flags
, numArgs
, numRets
)
103 , asyncEagerOffset(asyncEagerOffset
)
105 assertx(IMPLIES(byRefs
!= nullptr, numArgs
!= 0));
106 assertx(IMPLIES(asyncEagerOffset
== kInvalidOffset
,
107 !supportsAsyncEagerReturn()));
109 bool enforceReffiness() const { return byRefs
!= nullptr; }
110 bool byRef(uint32_t i
) const {
111 assertx(enforceReffiness());
112 return byRefs
[i
/ 8] & (1 << (i
% 8));
114 Offset asyncEagerOffset
;
115 const uint8_t* byRefs
;
118 static_assert(1 << FCallArgs::kFirstNumArgsBit
== FCallArgs::NumArgsStart
, "");
120 std::string
show(const LocalRange
&);
121 std::string
show(const FCallArgsBase
&, const uint8_t* byRefs
,
122 std::string asyncEagerLabel
);
125 * Variable-size immediates are implemented as follows: To determine which size
126 * the immediate is, examine the first byte where the immediate is expected,
127 * and examine its high-order bit. If it is zero, it's a 1-byte immediate
128 * and the byte is the value. Otherwise, it's 4 bytes, and bits 8..31 must be
129 * logical-shifted to the right by one to get rid of the flag bit.
131 * The types in this macro for BLA, SLA, ILA, I32LA and VSA are meaningless
132 * since they are never read out of ArgUnion (they use ImmVector).
134 * ArgTypes and their various decoding helpers should be kept in sync with the
135 * `hhx' bytecode inspection GDB command.
138 ARGTYPE(NA, void*) /* unused */ \
139 ARGTYPEVEC(BLA, Offset) /* Bytecode offset vector immediate */ \
140 ARGTYPEVEC(SLA, Id) /* String id/offset pair vector */ \
141 ARGTYPEVEC(ILA, Id) /* IterKind/IterId pair vector */ \
142 ARGTYPEVEC(I32LA,uint32_t) /* Vector of 32-bit uint */ \
143 ARGTYPE(IVA, uint32_t) /* Variable size: 8 or 32-bit uint */ \
144 ARGTYPE(I64A, int64_t) /* 64-bit Integer */ \
145 ARGTYPE(LA, int32_t) /* Local variable ID: 8 or 32-bit int */ \
146 ARGTYPE(IA, int32_t) /* Iterator ID: 8 or 32-bit int */ \
147 ARGTYPE(CAR, int32_t) /* Class-ref slot (read): 8 or 32-bit int */ \
148 ARGTYPE(CAW, int32_t) /* Class-ref slot (write): 8 or 32-bit int */ \
149 ARGTYPE(DA, double) /* Double */ \
150 ARGTYPE(SA, Id) /* Static string ID */ \
151 ARGTYPE(AA, Id) /* Static array ID */ \
152 ARGTYPE(RATA, RepoAuthType) /* Statically inferred RepoAuthType */ \
153 ARGTYPE(BA, Offset) /* Bytecode offset */ \
154 ARGTYPE(OA, unsigned char) /* Sub-opcode, untyped */ \
155 ARGTYPE(KA, MemberKey) /* Member key: local, stack, int, str */ \
156 ARGTYPE(LAR, LocalRange) /* Contiguous range of locals */ \
157 ARGTYPE(FCA, FCallArgs) /* FCall arguments */ \
158 ARGTYPEVEC(VSA, Id) /* Vector of static string IDs */
161 #define ARGTYPE(name, type) name,
162 #define ARGTYPEVEC(name, type) name,
169 ArgUnion() : u_LA
{0} {}
171 #define ARGTYPE(name, type) type u_##name;
172 #define ARGTYPEVEC(name, type) type u_##name;
178 const Offset InvalidAbsoluteOffset
= -1;
185 CVV
, // Cell or Var argument
186 CUV
, // Cell, or Uninit argument
187 CVUV
, // Cell, Var, or Uninit argument
194 /* Terminal: next instruction is not reachable via fall through or the callee
195 * returning control. This includes instructions like Throw and Unwind that
196 * always throw exceptions. */
199 /* Control flow: If this instruction finishes executing (doesn't throw an
200 * exception), vmpc() is not guaranteed to point to the next instruction in
201 * the bytecode stream. This does not take VM reentry into account, as that
202 * operation is part of the instruction that performed the reentry, and does
203 * not affect what vmpc() is set to after the instruction completes. */
206 /* Instruction pushes an FPI */
209 /* Shorthand for common combinations. */
220 INCDEC_OP(PostIncO) \
222 INCDEC_OP(PostDecO) \
224 enum class IncDecOp : uint8_t {
225 #define INCDEC_OP(incDecOp) incDecOp,
230 inline bool isPre(IncDecOp op
) {
232 op
== IncDecOp::PreInc
|| op
== IncDecOp::PreIncO
||
233 op
== IncDecOp::PreDec
|| op
== IncDecOp::PreDecO
;
236 inline bool isInc(IncDecOp op
) {
238 op
== IncDecOp::PreInc
|| op
== IncDecOp::PreIncO
||
239 op
== IncDecOp::PostInc
|| op
== IncDecOp::PostIncO
;
242 inline bool isIncDecO(IncDecOp op
) {
244 op
== IncDecOp::PreIncO
|| op
== IncDecOp::PreDecO
||
245 op
== IncDecOp::PostIncO
|| op
== IncDecOp::PostDecO
;
266 enum class IsTypeOp
: uint8_t {
267 #define ISTYPE_OP(op) op,
272 #define INITPROP_OPS \
273 INITPROP_OP(Static) \
274 INITPROP_OP(NonStatic)
276 enum class InitPropOp
: uint8_t {
277 #define INITPROP_OP(op) op,
290 FATAL_OP(RuntimeOmitFrame)
292 enum class FatalOp
: uint8_t {
293 #define FATAL_OP(x) x,
298 // Each of the setop ops maps to a binary bytecode op. We have reasons
299 // for using distinct bitwise representations, though. This macro records
300 // their correspondence for mapping either direction.
302 SETOP_OP(PlusEqual, OpAdd) \
303 SETOP_OP(MinusEqual, OpSub) \
304 SETOP_OP(MulEqual, OpMul) \
305 SETOP_OP(ConcatEqual, OpConcat) \
306 SETOP_OP(DivEqual, OpDiv) \
307 SETOP_OP(PowEqual, OpPow) \
308 SETOP_OP(ModEqual, OpMod) \
309 SETOP_OP(AndEqual, OpBitAnd) \
310 SETOP_OP(OrEqual, OpBitOr) \
311 SETOP_OP(XorEqual, OpBitXor) \
312 SETOP_OP(SlEqual, OpShl) \
313 SETOP_OP(SrEqual, OpShr) \
314 SETOP_OP(PlusEqualO, OpAddO) \
315 SETOP_OP(MinusEqualO, OpSubO) \
316 SETOP_OP(MulEqualO, OpMulO) \
318 enum class SetOpOp : uint8_t {
319 #define SETOP_OP(setOpOp, bcOp) setOpOp,
324 #define BARETHIS_OPS \
325 BARETHIS_OP(Notice) \
326 BARETHIS_OP(NoNotice) \
327 BARETHIS_OP(NeverNull)
329 enum class BareThisOp
: uint8_t {
330 #define BARETHIS_OP(x) x,
335 #define SILENCE_OPS \
339 enum class SilenceOp
: uint8_t {
340 #define SILENCE_OP(x) x,
345 #define OO_DECL_EXISTS_OPS \
346 OO_DECL_EXISTS_OP(Class) \
347 OO_DECL_EXISTS_OP(Interface) \
348 OO_DECL_EXISTS_OP(Trait)
350 enum class OODeclExistsOp
: uint8_t {
351 #define OO_DECL_EXISTS_OP(x) x,
353 #undef OO_DECL_EXISTS_OP
356 #define OBJMETHOD_OPS \
357 OBJMETHOD_OP(NullThrows) \
358 OBJMETHOD_OP(NullSafe)
360 enum class ObjMethodOp
: uint8_t {
361 #define OBJMETHOD_OP(x) x,
366 #define SWITCH_KINDS \
370 enum class SwitchKind
: uint8_t {
381 /* InOut mode restricts allowed bases to the
382 array like types. */ \
385 enum class MOpMode
: uint8_t {
386 #define MODE(name) name,
391 #define QUERY_M_OPS \
398 enum class QueryMOp
: uint8_t {
399 #define OP(name) name,
404 #define SET_RANGE_OPS \
408 enum class SetRangeOp
: uint8_t {
409 #define OP(name) name,
414 #define TYPE_STRUCT_RESOLVE_OPS \
418 enum class TypeStructResolveOp
: uint8_t {
419 #define OP(name) name,
420 TYPE_STRUCT_RESOLVE_OPS
424 #define HAS_GENERICS_OPS \
429 enum class HasGenericsOp
: uint8_t {
430 #define OP(name) name,
435 #define CONT_CHECK_OPS \
436 CONT_CHECK_OP(IgnoreStarted) \
437 CONT_CHECK_OP(CheckStarted)
439 enum class ContCheckOp
: uint8_t {
440 #define CONT_CHECK_OP(name) name,
449 enum class CudOp
: uint8_t {
450 #define CUD_OP(name) name,
455 #define SPECIAL_CLS_REFS \
460 enum class SpecialClsRef
: uint8_t {
461 #define REF(name) name,
466 constexpr uint32_t kMaxConcatN
= 4;
468 // name immediates inputs outputs flags
470 O(Nop, NA, NOV, NOV, NF) \
471 O(EntryNop, NA, NOV, NOV, NF) \
472 O(BreakTraceHint, NA, NOV, NOV, NF) \
473 O(DiscardClsRef, ONE(CAR), NOV, NOV, NF) \
474 O(PopC, NA, ONE(CV), NOV, NF) \
475 O(PopV, NA, ONE(VV), NOV, NF) \
476 O(PopU, NA, ONE(UV), NOV, NF) \
477 O(PopU2, NA, TWO(CV,UV), ONE(CV), NF) \
478 O(PopL, ONE(LA), ONE(CV), NOV, NF) \
479 O(Dup, NA, ONE(CV), TWO(CV,CV), NF) \
480 O(Box, NA, ONE(CV), ONE(VV), NF) \
481 O(Unbox, NA, ONE(VV), ONE(CV), NF) \
482 O(CGetCUNop, NA, ONE(CUV), ONE(CV), NF) \
483 O(UGetCUNop, NA, ONE(CUV), ONE(UV), NF) \
484 O(Null, NA, NOV, ONE(CV), NF) \
485 O(NullUninit, NA, NOV, ONE(UV), NF) \
486 O(True, NA, NOV, ONE(CV), NF) \
487 O(False, NA, NOV, ONE(CV), NF) \
488 O(Int, ONE(I64A), NOV, ONE(CV), NF) \
489 O(Double, ONE(DA), NOV, ONE(CV), NF) \
490 O(String, ONE(SA), NOV, ONE(CV), NF) \
491 O(Array, ONE(AA), NOV, ONE(CV), NF) \
492 O(Dict, ONE(AA), NOV, ONE(CV), NF) \
493 O(Keyset, ONE(AA), NOV, ONE(CV), NF) \
494 O(Vec, ONE(AA), NOV, ONE(CV), NF) \
495 O(NewArray, ONE(IVA), NOV, ONE(CV), NF) \
496 O(NewMixedArray, ONE(IVA), NOV, ONE(CV), NF) \
497 O(NewDictArray, ONE(IVA), NOV, ONE(CV), NF) \
498 O(NewLikeArrayL, TWO(LA,IVA), NOV, ONE(CV), NF) \
499 O(NewPackedArray, ONE(IVA), CMANY, ONE(CV), NF) \
500 O(NewStructArray, ONE(VSA), SMANY, ONE(CV), NF) \
501 O(NewStructDArray, ONE(VSA), SMANY, ONE(CV), NF) \
502 O(NewStructDict, ONE(VSA), SMANY, ONE(CV), NF) \
503 O(NewVecArray, ONE(IVA), CMANY, ONE(CV), NF) \
504 O(NewKeysetArray, ONE(IVA), CMANY, ONE(CV), NF) \
505 O(NewVArray, ONE(IVA), CMANY, ONE(CV), NF) \
506 O(NewDArray, ONE(IVA), NOV, ONE(CV), NF) \
507 O(AddElemC, NA, THREE(CV,CV,CV), ONE(CV), NF) \
508 O(AddElemV, NA, THREE(VV,CV,CV), ONE(CV), NF) \
509 O(AddNewElemC, NA, TWO(CV,CV), ONE(CV), NF) \
510 O(AddNewElemV, NA, TWO(VV,CV), ONE(CV), NF) \
511 O(NewCol, ONE(OA(CollectionType)), \
513 O(NewPair, NA, TWO(CV,CV), ONE(CV), NF) \
514 O(ColFromArray, ONE(OA(CollectionType)), \
515 ONE(CV), ONE(CV), NF) \
516 O(Cns, ONE(SA), NOV, ONE(CV), NF) \
517 O(CnsE, ONE(SA), NOV, ONE(CV), NF) \
518 O(CnsU, TWO(SA,SA), NOV, ONE(CV), NF) \
519 O(CnsUE, TWO(SA,SA), NOV, ONE(CV), NF) \
520 O(ClsCns, TWO(SA,CAR), NOV, ONE(CV), NF) \
521 O(ClsCnsD, TWO(SA,SA), NOV, ONE(CV), NF) \
522 O(ClsRefName, ONE(CAR), NOV, ONE(CV), NF) \
523 O(File, NA, NOV, ONE(CV), NF) \
524 O(Dir, NA, NOV, ONE(CV), NF) \
525 O(Method, NA, NOV, ONE(CV), NF) \
526 O(Concat, NA, TWO(CV,CV), ONE(CV), NF) \
527 O(ConcatN, ONE(IVA), CMANY, ONE(CV), NF) \
528 O(Add, NA, TWO(CV,CV), ONE(CV), NF) \
529 O(Sub, NA, TWO(CV,CV), ONE(CV), NF) \
530 O(Mul, NA, TWO(CV,CV), ONE(CV), NF) \
531 O(AddO, NA, TWO(CV,CV), ONE(CV), NF) \
532 O(SubO, NA, TWO(CV,CV), ONE(CV), NF) \
533 O(MulO, NA, TWO(CV,CV), ONE(CV), NF) \
534 O(Div, NA, TWO(CV,CV), ONE(CV), NF) \
535 O(Mod, NA, TWO(CV,CV), ONE(CV), NF) \
536 O(Pow, NA, TWO(CV,CV), ONE(CV), NF) \
537 O(Xor, NA, TWO(CV,CV), ONE(CV), NF) \
538 O(Not, NA, ONE(CV), ONE(CV), NF) \
539 O(Same, NA, TWO(CV,CV), ONE(CV), NF) \
540 O(NSame, NA, TWO(CV,CV), ONE(CV), NF) \
541 O(Eq, NA, TWO(CV,CV), ONE(CV), NF) \
542 O(Neq, NA, TWO(CV,CV), ONE(CV), NF) \
543 O(Lt, NA, TWO(CV,CV), ONE(CV), NF) \
544 O(Lte, NA, TWO(CV,CV), ONE(CV), NF) \
545 O(Gt, NA, TWO(CV,CV), ONE(CV), NF) \
546 O(Gte, NA, TWO(CV,CV), ONE(CV), NF) \
547 O(Cmp, NA, TWO(CV,CV), ONE(CV), NF) \
548 O(BitAnd, NA, TWO(CV,CV), ONE(CV), NF) \
549 O(BitOr, NA, TWO(CV,CV), ONE(CV), NF) \
550 O(BitXor, NA, TWO(CV,CV), ONE(CV), NF) \
551 O(BitNot, NA, ONE(CV), ONE(CV), NF) \
552 O(Shl, NA, TWO(CV,CV), ONE(CV), NF) \
553 O(Shr, NA, TWO(CV,CV), ONE(CV), NF) \
554 O(CastBool, NA, ONE(CV), ONE(CV), NF) \
555 O(CastInt, NA, ONE(CV), ONE(CV), NF) \
556 O(CastDouble, NA, ONE(CV), ONE(CV), NF) \
557 O(CastString, NA, ONE(CV), ONE(CV), NF) \
558 O(CastArray, NA, ONE(CV), ONE(CV), NF) \
559 O(CastObject, NA, ONE(CV), ONE(CV), NF) \
560 O(CastDict, NA, ONE(CV), ONE(CV), NF) \
561 O(CastKeyset, NA, ONE(CV), ONE(CV), NF) \
562 O(CastVec, NA, ONE(CV), ONE(CV), NF) \
563 O(CastVArray, NA, ONE(CV), ONE(CV), NF) \
564 O(CastDArray, NA, ONE(CV), ONE(CV), NF) \
565 O(DblAsBits, NA, ONE(CV), ONE(CV), NF) \
566 O(InstanceOf, NA, TWO(CV,CV), ONE(CV), NF) \
567 O(InstanceOfD, ONE(SA), ONE(CV), ONE(CV), NF) \
568 O(IsTypeStructC, ONE(OA(TypeStructResolveOp)), \
569 TWO(CV,CV), ONE(CV), NF) \
570 O(AsTypeStructC, ONE(OA(TypeStructResolveOp)), \
571 TWO(CV,CV), ONE(CV), NF) \
572 O(CombineAndResolveTypeStruct, \
573 ONE(IVA), CMANY, ONE(CV), NF) \
574 O(Select, NA, THREE(CV,CV,CV), ONE(CV), NF) \
575 O(Print, NA, ONE(CV), ONE(CV), NF) \
576 O(Clone, NA, ONE(CV), ONE(CV), NF) \
577 O(Exit, NA, ONE(CV), ONE(CV), TF) \
578 O(Fatal, ONE(OA(FatalOp)), ONE(CV), NOV, TF) \
579 O(Jmp, ONE(BA), NOV, NOV, CF_TF) \
580 O(JmpNS, ONE(BA), NOV, NOV, CF_TF) \
581 O(JmpZ, ONE(BA), ONE(CV), NOV, CF) \
582 O(JmpNZ, ONE(BA), ONE(CV), NOV, CF) \
583 O(Switch, THREE(OA(SwitchKind),I64A,BLA), \
584 ONE(CV), NOV, CF_TF) \
585 O(SSwitch, ONE(SLA), ONE(CV), NOV, CF_TF) \
586 O(RetC, NA, ONE(CV), NOV, CF_TF) \
587 O(RetM, ONE(IVA), CMANY, NOV, CF_TF) \
588 O(RetCSuspended, NA, ONE(CV), NOV, CF_TF) \
589 O(Unwind, NA, NOV, NOV, TF) \
590 O(Throw, NA, ONE(CV), NOV, CF_TF) \
591 O(CGetL, ONE(LA), NOV, ONE(CV), NF) \
592 O(CGetQuietL, ONE(LA), NOV, ONE(CV), NF) \
593 O(CUGetL, ONE(LA), NOV, ONE(CUV), NF) \
594 O(CGetL2, ONE(LA), NOV, INS_1(CV), NF) \
595 O(PushL, ONE(LA), NOV, ONE(CV), NF) \
596 O(CGetG, NA, ONE(CV), ONE(CV), NF) \
597 O(CGetQuietG, NA, ONE(CV), ONE(CV), NF) \
598 O(CGetS, ONE(CAR), ONE(CV), ONE(CV), NF) \
599 O(VGetL, ONE(LA), NOV, ONE(VV), NF) \
600 O(VGetG, NA, ONE(CV), ONE(VV), NF) \
601 O(VGetS, ONE(CAR), ONE(CV), ONE(VV), NF) \
602 O(ClsRefGetC, ONE(CAW), ONE(CV), NOV, NF) \
603 O(ClsRefGetTS, ONE(CAW), ONE(CV), NOV, NF) \
604 O(GetMemoKeyL, ONE(LA), NOV, ONE(CV), NF) \
605 O(AKExists, NA, TWO(CV,CV), ONE(CV), NF) \
606 O(IssetL, ONE(LA), NOV, ONE(CV), NF) \
607 O(IssetG, NA, ONE(CV), ONE(CV), NF) \
608 O(IssetS, ONE(CAR), ONE(CV), ONE(CV), NF) \
609 O(EmptyL, ONE(LA), NOV, ONE(CV), NF) \
610 O(EmptyG, NA, ONE(CV), ONE(CV), NF) \
611 O(EmptyS, ONE(CAR), ONE(CV), ONE(CV), NF) \
612 O(IsTypeC, ONE(OA(IsTypeOp)),ONE(CV), ONE(CV), NF) \
614 OA(IsTypeOp)), NOV, ONE(CV), NF) \
615 O(AssertRATL, TWO(LA,RATA), NOV, NOV, NF) \
616 O(AssertRATStk, TWO(IVA,RATA), NOV, NOV, NF) \
617 O(SetL, ONE(LA), ONE(CV), ONE(CV), NF) \
618 O(SetG, NA, TWO(CV,CV), ONE(CV), NF) \
619 O(SetS, ONE(CAR), TWO(CV,CV), ONE(CV), NF) \
621 OA(SetOpOp)), ONE(CV), ONE(CV), NF) \
622 O(SetOpG, ONE(OA(SetOpOp)), TWO(CV,CV), ONE(CV), NF) \
623 O(SetOpS, TWO(OA(SetOpOp),CAR), \
624 TWO(CV,CV), ONE(CV), NF) \
626 OA(IncDecOp)), NOV, ONE(CV), NF) \
627 O(IncDecG, ONE(OA(IncDecOp)),ONE(CV), ONE(CV), NF) \
628 O(IncDecS, TWO(OA(IncDecOp),CAR), \
629 ONE(CV), ONE(CV), NF) \
630 O(BindL, ONE(LA), ONE(VV), ONE(VV), NF) \
631 O(BindG, NA, TWO(VV,CV), ONE(VV), NF) \
632 O(BindS, ONE(CAR), TWO(VV,CV), ONE(VV), NF) \
633 O(UnsetL, ONE(LA), NOV, NOV, NF) \
634 O(UnsetG, NA, ONE(CV), NOV, NF) \
636 O(FPushFunc, TWO(IVA,I32LA), ONE(CV), NOV, PF) \
637 O(FPushFuncD, TWO(IVA,SA), NOV, NOV, PF) \
638 O(FPushFuncU, THREE(IVA,SA,SA), NOV, NOV, PF) \
639 O(ResolveFunc, ONE(SA), NOV, ONE(CV), NF) \
640 O(ResolveObjMethod,NA, TWO(CV,CV), ONE(CV), NF) \
641 O(ResolveClsMethod,NA, TWO(CV,CV), ONE(CV), NF) \
642 O(FPushObjMethod, THREE(IVA, \
644 I32LA), TWO(CV,CV), NOV, PF) \
645 O(FPushObjMethodD, THREE(IVA,SA, \
646 OA(ObjMethodOp)), ONE(CV), NOV, PF) \
647 O(FPushClsMethod, THREE(IVA,CAR,I32LA), \
649 O(FPushClsMethodS, THREE(IVA,OA(SpecialClsRef),I32LA), \
651 O(FPushClsMethodSD,THREE(IVA,OA(SpecialClsRef),SA), \
653 O(FPushClsMethodD, THREE(IVA,SA,SA), NOV, NOV, PF) \
654 O(NewObj, TWO(CAR,OA(HasGenericsOp)), \
656 O(NewObjD, ONE(SA), NOV, ONE(CV), NF) \
657 O(NewObjI, ONE(IVA), NOV, ONE(CV), NF) \
658 O(NewObjS, ONE(OA(SpecialClsRef)), \
660 O(FPushCtor, ONE(IVA), ONE(CV), NOV, PF) \
661 O(FCall, THREE(FCA,SA,SA), FCALL, FCALL, CF) \
662 O(FCallBuiltin, THREE(IVA,IVA,SA),CVUMANY, ONE(CV), NF) \
663 O(IterInit, THREE(IA,BA,LA), ONE(CV), NOV, CF) \
664 O(LIterInit, FOUR(IA,LA,BA,LA),NOV, NOV, CF) \
665 O(IterInitK, FOUR(IA,BA,LA,LA),ONE(CV), NOV, CF) \
666 O(LIterInitK, FIVE(IA,LA,BA,LA,LA),NOV, NOV, CF) \
667 O(IterNext, THREE(IA,BA,LA), NOV, NOV, CF) \
668 O(LIterNext, FOUR(IA,LA,BA,LA),NOV, NOV, CF) \
669 O(IterNextK, FOUR(IA,BA,LA,LA),NOV, NOV, CF) \
670 O(LIterNextK, FIVE(IA,LA,BA,LA,LA),NOV, NOV, CF) \
671 O(IterFree, ONE(IA), NOV, NOV, NF) \
672 O(LIterFree, TWO(IA,LA), NOV, NOV, NF) \
673 O(IterBreak, TWO(BA,ILA), NOV, NOV, CF_TF) \
674 O(Incl, NA, ONE(CV), ONE(CV), CF) \
675 O(InclOnce, NA, ONE(CV), ONE(CV), CF) \
676 O(Req, NA, ONE(CV), ONE(CV), CF) \
677 O(ReqOnce, NA, ONE(CV), ONE(CV), CF) \
678 O(ReqDoc, NA, ONE(CV), ONE(CV), CF) \
679 O(Eval, NA, ONE(CV), ONE(CV), CF) \
680 O(DefCls, ONE(IVA), NOV, NOV, NF) \
681 O(DefClsNop, ONE(IVA), NOV, NOV, NF) \
682 O(AliasCls, TWO(SA,SA), ONE(CV), ONE(CV), NF) \
683 O(DefCns, ONE(SA), ONE(CV), ONE(CV), NF) \
684 O(DefTypeAlias, ONE(IVA), NOV, NOV, NF) \
685 O(This, NA, NOV, ONE(CV), NF) \
686 O(BareThis, ONE(OA(BareThisOp)), \
688 O(CheckThis, NA, NOV, NOV, NF) \
689 O(InitThisLoc, ONE(LA), NOV, NOV, NF) \
690 O(FuncNumArgs, NA, NOV, ONE(CV), NF) \
691 O(StaticLocCheck, TWO(LA,SA), NOV, ONE(CV), NF) \
692 O(StaticLocDef, TWO(LA,SA), ONE(CV), NOV, NF) \
693 O(StaticLocInit, TWO(LA,SA), ONE(CV), NOV, NF) \
694 O(Catch, NA, NOV, ONE(CV), NF) \
695 O(ChainFaults, NA, TWO(CV,CV), ONE(CV), NF) \
696 O(OODeclExists, ONE(OA(OODeclExistsOp)), \
697 TWO(CV,CV), ONE(CV), NF) \
698 O(VerifyOutType, ONE(IVA), ONE(CV), ONE(CV), NF) \
699 O(VerifyParamType, ONE(LA), NOV, NOV, NF) \
700 O(VerifyParamTypeTS, ONE(LA), ONE(CV), NOV, NF) \
701 O(VerifyRetTypeC, NA, ONE(CV), ONE(CV), NF) \
702 O(VerifyRetTypeTS, NA, TWO(CV,CV), ONE(CV), NF) \
703 O(VerifyRetNonNullC, NA, ONE(CV), ONE(CV), NF) \
704 O(Self, ONE(CAW), NOV, NOV, NF) \
705 O(Parent, ONE(CAW), NOV, NOV, NF) \
706 O(LateBoundCls, ONE(CAW), NOV, NOV, NF) \
707 O(RecordReifiedGeneric, \
708 ONE(IVA), CMANY, ONE(CV), NF) \
709 O(ReifiedName, TWO(IVA,SA), CMANY, ONE(CV), NF) \
710 O(CheckReifiedGenericMismatch, NA, ONE(CV), NOV, NF) \
711 O(NativeImpl, NA, NOV, NOV, CF_TF) \
712 O(CreateCl, TWO(IVA,IVA), CVUMANY, ONE(CV), NF) \
713 O(CreateCont, NA, NOV, ONE(CV), CF) \
714 O(ContEnter, NA, ONE(CV), ONE(CV), CF) \
715 O(ContRaise, NA, ONE(CV), ONE(CV), CF) \
716 O(Yield, NA, ONE(CV), ONE(CV), CF) \
717 O(YieldK, NA, TWO(CV,CV), ONE(CV), CF) \
718 O(ContAssignDelegate, \
719 ONE(IA), ONE(CV), NOV, NF) \
720 O(ContEnterDelegate, \
721 NA, ONE(CV), NOV, CF) \
722 O(YieldFromDelegate, \
723 TWO(IA,BA), NOV, ONE(CV), CF) \
724 O(ContUnsetDelegate, TWO(OA(CudOp),IA), NOV, NOV, NF) \
725 O(ContCheck, ONE(OA(ContCheckOp)), NOV, NOV, NF) \
726 O(ContValid, NA, NOV, ONE(CV), NF) \
727 O(ContKey, NA, NOV, ONE(CV), NF) \
728 O(ContCurrent, NA, NOV, ONE(CV), NF) \
729 O(ContGetReturn, NA, NOV, ONE(CV), NF) \
730 O(WHResult, NA, ONE(CV), ONE(CV), NF) \
731 O(Await, NA, ONE(CV), ONE(CV), CF) \
732 O(AwaitAll, ONE(LAR), NOV, ONE(CV), CF) \
733 O(Idx, NA, THREE(CV,CV,CV), ONE(CV), NF) \
734 O(ArrayIdx, NA, THREE(CV,CV,CV), ONE(CV), NF) \
735 O(CheckProp, ONE(SA), NOV, ONE(CV), NF) \
736 O(InitProp, TWO(SA, \
737 OA(InitPropOp)),ONE(CV), NOV, NF) \
738 O(Silence, TWO(LA,OA(SilenceOp)), \
740 O(BaseGC, TWO(IVA, OA(MOpMode)), \
742 O(BaseGL, TWO(LA, OA(MOpMode)), \
744 O(BaseSC, THREE(IVA, CAR, OA(MOpMode)), \
746 O(BaseL, TWO(LA, OA(MOpMode)), \
748 O(BaseC, TWO(IVA, OA(MOpMode)), \
750 O(BaseH, NA, NOV, NOV, NF) \
751 O(Dim, TWO(OA(MOpMode), KA), \
753 O(QueryM, THREE(IVA, OA(QueryMOp), KA), \
754 MFINAL, ONE(CV), NF) \
755 O(VGetM, TWO(IVA, KA), MFINAL, ONE(VV), NF) \
756 O(SetM, TWO(IVA, KA), C_MFINAL(1), ONE(CV), NF) \
757 O(SetRangeM, THREE(IVA, OA(SetRangeOp), IVA), \
758 C_MFINAL(3), NOV, NF) \
759 O(IncDecM, THREE(IVA, OA(IncDecOp), KA), \
760 MFINAL, ONE(CV), NF) \
761 O(SetOpM, THREE(IVA, OA(SetOpOp), KA), \
762 C_MFINAL(1), ONE(CV), NF) \
763 O(BindM, TWO(IVA, KA), V_MFINAL, ONE(VV), NF) \
764 O(UnsetM, TWO(IVA, KA), MFINAL, NOV, NF) \
765 O(MemoGet, TWO(BA, LAR), NOV, ONE(CV), CF) \
766 O(MemoGetEager, THREE(BA, BA, LAR), \
768 O(MemoSet, ONE(LAR), ONE(CV), ONE(CV), NF) \
769 O(MemoSetEager, ONE(LAR), ONE(CV), ONE(CV), NF)
771 enum class Op
: uint16_t {
772 #define O(name, ...) name,
778 constexpr size_t Op_count
= OPCODES
;
782 * Also put Op* in the enclosing namespace, to avoid having to change every
783 * existing usage site of the enum values.
785 #define O(name, ...) UNUSED auto constexpr Op##name = Op::name;
789 // These are comparable by default under MSVC.
791 inline constexpr bool operator<(Op a
, Op b
) { return size_t(a
) < size_t(b
); }
792 inline constexpr bool operator>(Op a
, Op b
) { return size_t(a
) > size_t(b
); }
793 inline constexpr bool operator<=(Op a
, Op b
) {
794 return size_t(a
) <= size_t(b
);
796 inline constexpr bool operator>=(Op a
, Op b
) {
797 return size_t(a
) >= size_t(b
);
801 constexpr bool isValidOpcode(Op op
) {
802 return size_t(op
) < Op_count
;
805 inline MOpMode
getQueryMOpMode(QueryMOp op
) {
807 case QueryMOp::CGet
: return MOpMode::Warn
;
808 case QueryMOp::CGetQuiet
:
809 case QueryMOp::Isset
:
810 case QueryMOp::Empty
: return MOpMode::None
;
811 case QueryMOp::InOut
: return MOpMode::InOut
;
813 always_assert(false);
816 #define HIGH_OPCODES \
821 OpHighStart
= Op_count
-1,
822 #define O(name) Op##name,
833 explicit ImmVector() : m_start(0) {}
835 explicit ImmVector(const uint8_t* start
,
839 , m_numStack(numStack
)
843 bool isValid() const { return m_start
!= 0; }
845 const int32_t* vec32() const {
846 return reinterpret_cast<const int32_t*>(m_start
);
848 folly::Range
<const int32_t*> range32() const {
850 return {base
, base
+ size()};
852 const StrVecItem
* strvec() const {
853 return reinterpret_cast<const StrVecItem
*>(m_start
);
857 * Returns the length of the immediate vector in bytes (for M
858 * vectors) or elements (for switch vectors)
860 int32_t size() const { return m_length
; }
863 * Returns the number of elements on the execution stack that this vector
864 * will need to access.
866 int numStackValues() const { return m_numStack
; }
871 const uint8_t* m_start
;
874 struct IterTableEnt
{
879 using IterTable
= CompactVector
<IterTableEnt
>;
881 // Must be an opcode that actually has an ImmVector.
882 ImmVector
getImmVector(PC opcode
);
884 // Must be an opcode that actually has an IterTable.
885 IterTable
getIterTable(PC opcode
);
887 // Some decoding helper functions.
888 int numImmediates(Op opcode
);
889 ArgType
immType(Op opcode
, int idx
);
890 bool hasImmVector(Op opcode
);
891 bool hasIterTable(Op opcode
);
892 int instrLen(PC opcode
);
893 int numSuccs(PC opcode
);
894 bool pushesActRec(Op opcode
);
897 IterTable
iterTableFromStream(PC
&);
900 * The returned struct has normalized variable-sized immediates. u must be
901 * provided unless you know that the immediate is not of type KA.
903 * Don't use with RATA immediates.
905 ArgUnion
getImm(PC opcode
, int idx
, const Unit
* u
= nullptr);
907 // Don't use this with variable-sized immediates!
908 ArgUnion
* getImmPtr(PC opcode
, int idx
);
910 void staticStreamer(const TypedValue
* tv
, std::string
& out
);
912 std::string
instrToString(PC it
, Either
<const Unit
*, const UnitEmitter
*> u
);
913 void staticArrayStreamer(const ArrayData
*, std::string
&);
916 * Convert subopcodes or opcodes into strings.
918 const char* opcodeToName(Op op
);
919 const char* subopToName(InitPropOp
);
920 const char* subopToName(IsTypeOp
);
921 const char* subopToName(FatalOp
);
922 const char* subopToName(CollectionType
);
923 const char* subopToName(SetOpOp
);
924 const char* subopToName(IncDecOp
);
925 const char* subopToName(BareThisOp
);
926 const char* subopToName(SilenceOp
);
927 const char* subopToName(OODeclExistsOp
);
928 const char* subopToName(ObjMethodOp
);
929 const char* subopToName(SwitchKind
);
930 const char* subopToName(MOpMode
);
931 const char* subopToName(QueryMOp
);
932 const char* subopToName(SetRangeOp
);
933 const char* subopToName(TypeStructResolveOp
);
934 const char* subopToName(HasGenericsOp
);
935 const char* subopToName(ContCheckOp
);
936 const char* subopToName(CudOp
);
937 const char* subopToName(SpecialClsRef
);
940 * Returns true iff the given SubOp is in the valid range for its type.
942 template<class Subop
>
943 bool subopValid(Subop
);
946 * Try to parse a string into a subop name of a given type.
948 * Returns folly::none if the string is not recognized as that type of
951 template<class SubOpType
>
952 folly::Optional
<SubOpType
> nameToSubop(const char*);
954 using OffsetList
= std::vector
<Offset
>;
956 // Returns a jump offsets relative to the instruction, or nothing if
957 // the instruction cannot jump.
958 OffsetList
instrJumpOffsets(PC instr
);
960 // returns absolute address of targets, or nothing if instruction
962 OffsetList
instrJumpTargets(PC instrs
, Offset pos
);
965 * Returns the set of bytecode offsets for the instructions that may
966 * be executed immediately after opc.
968 using OffsetSet
= hphp_hash_set
<Offset
>;
969 OffsetSet
instrSuccOffsets(PC opc
, const Func
* func
);
971 struct StackTransInfo
{
977 int numPops
; // only for PushPop
978 int numPushes
; // only for PushPop
979 int pos
; // only for InsertMid
983 * Some CF instructions can be treated as non-CF instructions for most analysis
984 * purposes, such as bytecode verification and HHBBC. These instructions change
985 * vmpc() to point somewhere in a different function, but the runtime
986 * guarantees that if excution ever returns to the original frame, it will be
987 * at the location immediately following the instruction in question. This
988 * creates the illusion that the instruction fell through normally to the
989 * instruction after it, within the context of its execution frame.
991 * The canonical example of this behavior is the FCall instruction, so we use
992 * "non-call control flow" to describe the set of CF instruction that do not
993 * exhibit this behavior. This function returns true if `opcode' is a non-call
994 * control flow instruction.
996 bool instrIsNonCallControlFlow(Op opcode
);
998 bool instrAllowsFallThru(Op opcode
);
1000 constexpr InstrFlags instrFlagsData
[] = {
1001 #define O(unusedName, unusedImm, unusedPop, unusedPush, flags) flags,
1006 constexpr InstrFlags
instrFlags(Op opcode
) {
1007 return instrFlagsData
[size_t(opcode
)];
1010 constexpr bool instrIsControlFlow(Op opcode
) {
1011 return (instrFlags(opcode
) & CF
) != 0;
1014 constexpr bool isUnconditionalJmp(Op opcode
) {
1015 return opcode
== Op::Jmp
|| opcode
== Op::JmpNS
;
1018 constexpr bool isConditionalJmp(Op opcode
) {
1019 return opcode
== Op::JmpZ
|| opcode
== Op::JmpNZ
;
1022 constexpr bool isJmp(Op opcode
) {
1024 opcode
== Op::Jmp
||
1025 opcode
== Op::JmpNS
||
1026 opcode
== Op::JmpZ
||
1027 opcode
== Op::JmpNZ
;
1030 constexpr bool isFPush(Op opcode
) {
1031 return (instrFlags(opcode
) & PF
) != 0;
1034 constexpr bool isFPushClsMethod(Op opcode
) {
1036 opcode
== OpFPushClsMethod
||
1037 opcode
== OpFPushClsMethodS
||
1038 opcode
== OpFPushClsMethodSD
||
1039 opcode
== OpFPushClsMethodD
;
1042 constexpr bool isFPushObjMethod(Op opcode
) {
1044 opcode
== OpFPushObjMethod
||
1045 opcode
== OpFPushObjMethodD
;
1048 constexpr bool isFPushFunc(Op opcode
) {
1050 opcode
== OpFPushFunc
||
1051 opcode
== OpFPushFuncD
||
1052 opcode
== OpFPushFuncU
;
1055 inline bool isFCallStar(Op opcode
) {
1064 constexpr bool isRet(Op op
) {
1065 return op
== OpRetC
|| op
== OpRetCSuspended
|| op
== OpRetM
;
1068 constexpr bool isReturnish(Op op
) {
1069 return isRet(op
) || op
== Op::NativeImpl
;
1072 constexpr bool isSwitch(Op op
) {
1073 return op
== OpSwitch
|| op
== OpSSwitch
;
1076 constexpr bool isTypeAssert(Op op
) {
1077 return op
== OpAssertRATL
|| op
== OpAssertRATStk
;
1080 inline bool isMemberBaseOp(Op op
) {
1095 inline bool isMemberDimOp(Op op
) {
1096 return op
== Op::Dim
;
1099 inline bool isMemberFinalOp(Op op
) {
1116 inline bool isMemberOp(Op op
) {
1117 return isMemberBaseOp(op
) || isMemberDimOp(op
) || isMemberFinalOp(op
);
1120 inline MOpMode
finalMemberOpMode(Op op
) {
1128 return MOpMode::Define
;
1130 return MOpMode::Unset
;
1132 return MOpMode::None
;
1135 false, "Unknown final member op {}", opcodeToName(op
)
1140 // true if the opcode body can set pc=0 to halt the interpreter.
1141 constexpr bool instrCanHalt(Op op
) {
1142 return op
== OpRetC
|| op
== OpNativeImpl
||
1143 op
== OpAwait
|| op
== OpAwaitAll
|| op
== OpCreateCont
||
1144 op
== OpYield
|| op
== OpYieldK
|| op
== OpRetM
||
1145 op
== OpRetCSuspended
|| op
== OpYieldFromDelegate
;
1148 int instrNumPops(PC opcode
);
1149 int instrNumPushes(PC opcode
);
1150 FlavorDesc
instrInputFlavor(PC op
, uint32_t idx
);
1151 StackTransInfo
instrStackTransInfo(PC opcode
);
1155 //////////////////////////////////////////////////////////////////////
1159 struct hash
<HPHP::Op
> {
1160 size_t operator()(HPHP::Op op
) const {
1161 return HPHP::hash_int64(size_t(op
));
1166 //////////////////////////////////////////////////////////////////////