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 +----------------------------------------------------------------------+
16 #ifndef incl_HHBBC_BC_H_
17 #define incl_HHBBC_BC_H_
21 #include <type_traits>
25 #include <folly/Hash.h>
27 #include "hphp/util/compact-vector.h"
28 #include "hphp/runtime/vm/hhbc.h"
30 #include "hphp/hhbbc/src-loc.h"
31 #include "hphp/hhbbc/misc.h"
32 #include "hphp/hhbbc/type-system.h"
34 namespace HPHP
{ namespace HHBBC
{
38 //////////////////////////////////////////////////////////////////////
41 * The following creates a struct for each bytecode using the opcode
42 * table. Each opcode will be named bc::opcode, and has a single
43 * constructor that takes its immediate types in order.
47 * auto pushInt = bc::Int { 2 }; // Push literal int
50 //////////////////////////////////////////////////////////////////////
54 : name(kInvalidLocalName
)
57 NamedLocal(LocalName name
, LocalId id
)
61 /* implicit */ NamedLocal(const ::HPHP::NamedLocal
& nl
)
65 /* implicit */ operator auto(){
66 return ::HPHP::NamedLocal
{name
, static_cast<int32_t>(id
)};
72 inline bool operator==(NamedLocal a
, NamedLocal b
) {
73 return a
.name
== b
.name
&& a
.id
== b
.id
;
76 inline bool operator!=(NamedLocal a
, NamedLocal b
) {
86 MKey(MemberCode mcode
, NamedLocal local
)
91 MKey(MemberCode mcode
, int32_t idx
)
96 MKey(MemberCode mcode
, int64_t int64
)
101 MKey(MemberCode mcode
, SString litstr
)
115 inline bool operator==(MKey a
, MKey b
) {
116 return a
.mcode
== b
.mcode
&& a
.int64
== b
.int64
;
119 inline bool operator!=(MKey a
, MKey b
) {
123 // A contiguous range of locals. The count is the number of locals including the
124 // first. If the range is empty, count will be zero and first's value is
131 inline bool operator==(const LocalRange
& a
, const LocalRange
& b
) {
132 return a
.first
== b
.first
&& a
.count
== b
.count
;
135 inline bool operator!=(const LocalRange
& a
, const LocalRange
& b
) {
139 struct FCallArgsShort
{
140 using Flags
= FCallArgsBase::Flags
;
141 static constexpr int kValidFlag
= (1u << 15);
142 static constexpr int kSkipNumArgsCheck
= (1u << 14);
143 static constexpr int kLockWhileUnwinding
= (1u << 13);
144 static constexpr int kEnforceInOut
= (1u << 12);
145 static constexpr int kInoutMask
= (1u << 12) - 1;
147 BlockId asyncEagerTarget
;
151 uint16_t inoutArgsAndFlags
;
152 static FCallArgsShort
create(
153 Flags flags
, uint32_t numArgs
, uint32_t numRets
,
154 const std::unique_ptr
<uint8_t[]>& inoutArgs
,
155 BlockId asyncEagerTarget
, bool lockWhileUnwinding
,
156 bool skipNumArgsCheck
, SString context
) {
159 (inoutArgs
&& numArgs
> 8 && inoutArgs
[1] > (kInoutMask
>> 8)) ||
161 return FCallArgsShort
{};
163 FCallArgsShort fca
{};
164 fca
.asyncEagerTarget
= asyncEagerTarget
;
166 fca
.numArgs
= numArgs
;
167 fca
.numRets
= numRets
;
169 fca
.inoutArgsAndFlags
= inoutArgs
[0];
171 fca
.inoutArgsAndFlags
|= inoutArgs
[1] << 8;
173 fca
.inoutArgsAndFlags
|= kEnforceInOut
;
175 if (skipNumArgsCheck
) fca
.inoutArgsAndFlags
|= kSkipNumArgsCheck
;
176 if (lockWhileUnwinding
) fca
.inoutArgsAndFlags
|= kLockWhileUnwinding
;
177 fca
.inoutArgsAndFlags
|= kValidFlag
;
180 bool valid() const { return inoutArgsAndFlags
& kValidFlag
; }
181 bool skipNumArgsCheck() const {
182 return inoutArgsAndFlags
& kSkipNumArgsCheck
;
184 bool lockWhileUnwinding() const {
185 return inoutArgsAndFlags
& kLockWhileUnwinding
;
187 bool enforceInOut() const {
188 return inoutArgsAndFlags
& kEnforceInOut
;
190 bool isInOut(uint32_t i
) const {
191 assertx(enforceInOut());
192 assertx(i
< numArgs
);
193 return inoutArgsAndFlags
& kInoutMask
& (1 << i
);
195 FCallArgsShort
withoutGenerics() const {
197 fca
.flags
= static_cast<Flags
>(fca
.flags
& ~Flags::HasGenerics
);
200 FCallArgsShort
withoutInOut() const {
202 fca
.inoutArgsAndFlags
&= ~(kInoutMask
| kEnforceInOut
);
205 FCallArgsShort
withoutLockWhileUnwinding() const {
207 fca
.inoutArgsAndFlags
&= ~kLockWhileUnwinding
;
210 FCallArgsShort
fixEager(bool supports_eager
) const {
212 if (supports_eager
) {
213 assertx(fca
.asyncEagerTarget
!= NoBlockId
);
214 fca
.flags
= static_cast<Flags
>(
215 fca
.flags
| Flags::SupportsAsyncEagerReturn
218 fca
.asyncEagerTarget
= NoBlockId
;
219 fca
.flags
= static_cast<Flags
>(
220 fca
.flags
& ~Flags::SupportsAsyncEagerReturn
227 void applyIO(F f
) const {
229 br
[0] = inoutArgsAndFlags
;
230 br
[1] = (inoutArgsAndFlags
& kInoutMask
) >> 8;
231 f(numArgs
> 8 ? 2 : 1, br
);
234 FCallArgsShort
withoutNumArgsCheck() const {
236 fca
.inoutArgsAndFlags
|= kSkipNumArgsCheck
;
239 friend bool operator==(const FCallArgsShort
& a
, const FCallArgsShort
& b
) {
241 a
.flags
== b
.flags
&& a
.numArgs
== b
.numArgs
&& a
.numRets
== b
.numRets
&&
242 a
.inoutArgsAndFlags
== b
.inoutArgsAndFlags
&&
243 a
.asyncEagerTarget
== b
.asyncEagerTarget
;
245 size_t hash() const {
246 uint64_t hash
= HPHP::hash_int64_pair(numArgs
, numRets
);
247 hash
= HPHP::hash_int64_pair(hash
, flags
);
248 if (enforceInOut()) {
250 [&] (int numBytes
, const uint8_t* br
) {
251 auto const s
= reinterpret_cast<const char*>(br
);
252 auto const hash_br
= hash_string_cs(s
, numBytes
);
253 hash
= HPHP::hash_int64_pair(hash
, hash_br
);
257 hash
= HPHP::hash_int64_pair(hash
, asyncEagerTarget
);
258 hash
= HPHP::hash_int64_pair(hash
, lockWhileUnwinding());
259 return static_cast<size_t>(hash
);
261 bool hasUnpack() const {
262 return flags
& Flags::HasUnpack
;
264 bool hasGenerics() const {
265 return flags
& Flags::HasGenerics
;
267 bool supportsAsyncEagerReturn() const {
268 return flags
& Flags::SupportsAsyncEagerReturn
;
270 uint32_t numInputs() const {
273 (hasUnpack() ? 1 : 0) +
274 (hasGenerics() ? 1 : 0);
276 const uint8_t* inoutArgs() const {
277 return enforceInOut() ?
278 reinterpret_cast<const uint8_t*>(&inoutArgsAndFlags
) :
283 uint32_t numPop() const {
284 return nin
+ numInputs() + 2 + numRets
;
286 template<int nin
, int nobj
>
287 Flavor
popFlavor(uint32_t i
) const {
288 assert(i
< this->template numPop
<nin
>());
289 if (i
< nin
) return Flavor::C
;
292 if (i
== 0) return Flavor::C
;
296 if (i
== 0) return Flavor::C
;
299 if (i
< numArgs
) return Flavor::C
;
301 if (i
== 2 && nobj
) return Flavor::C
;
304 FCallArgsBase
base() const {
305 return FCallArgsBase
{
306 flags
, numArgs
, numRets
,
307 lockWhileUnwinding(), skipNumArgsCheck()
312 struct FCallArgsLong
: FCallArgsBase
{
313 explicit FCallArgsLong(uint32_t numArgs
)
314 : FCallArgsLong(Flags::None
, numArgs
, 1,
315 nullptr, NoBlockId
, false, false, nullptr) {
317 explicit FCallArgsLong(Flags flags
, uint32_t numArgs
, uint32_t numRets
,
318 std::unique_ptr
<uint8_t[]> inoutArgs
,
319 BlockId asyncEagerTarget
, bool lockWhileUnwinding
,
320 bool skipNumArgsCheck
, SString context
)
321 : FCallArgsBase(flags
, numArgs
, numRets
,
322 lockWhileUnwinding
, skipNumArgsCheck
)
323 , asyncEagerTarget(asyncEagerTarget
)
324 , inoutArgs(std::move(inoutArgs
))
326 assertx(IMPLIES(asyncEagerTarget
== NoBlockId
,
327 !supportsAsyncEagerReturn()));
329 FCallArgsLong(const FCallArgsLong
& o
)
330 : FCallArgsLong(o
.flags
, o
.numArgs
, o
.numRets
, nullptr, o
.asyncEagerTarget
,
331 o
.lockWhileUnwinding
, o
.skipNumArgsCheck
, o
.context
) {
333 auto const numBytes
= (numArgs
+ 7) / 8;
334 inoutArgs
= std::make_unique
<uint8_t[]>(numBytes
);
335 memcpy(inoutArgs
.get(), o
.inoutArgs
.get(), numBytes
);
338 FCallArgsLong(FCallArgsLong
&& o
)
339 : FCallArgsLong(o
.flags
, o
.numArgs
, o
.numRets
, std::move(o
.inoutArgs
),
340 o
.asyncEagerTarget
, o
.lockWhileUnwinding
,
341 o
.skipNumArgsCheck
, o
.context
) {}
343 bool enforceInOut() const { return inoutArgs
.get() != nullptr; }
344 bool isInOut(uint32_t i
) const {
345 assertx(enforceInOut());
346 return inoutArgs
[i
/ 8] & (1 << (i
% 8));
349 FCallArgsLong
withoutGenerics() const {
351 fca
.flags
= static_cast<Flags
>(fca
.flags
& ~Flags::HasGenerics
);
355 FCallArgsLong
withoutInOut() const {
357 fca
.inoutArgs
= nullptr;
360 FCallArgsLong
withoutLockWhileUnwinding() const {
362 fca
.lockWhileUnwinding
= false;
366 FCallArgsLong
fixEager(bool supports_eager
) const {
368 if (supports_eager
) {
369 assertx(fca
.asyncEagerTarget
!= NoBlockId
);
370 fca
.flags
= static_cast<Flags
>(
371 fca
.flags
| SupportsAsyncEagerReturn
374 fca
.asyncEagerTarget
= NoBlockId
;
375 fca
.flags
= static_cast<Flags
>(
376 fca
.flags
& ~SupportsAsyncEagerReturn
381 FCallArgsLong
withoutNumArgsCheck() const {
383 fca
.skipNumArgsCheck
= true;
387 void applyIO(F f
) const {
388 f((numArgs
+ 7) / 8, inoutArgs
.get());
390 friend bool operator==(const FCallArgsLong
& a
, const FCallArgsLong
& b
) {
391 auto const eq
= [&] (uint8_t* a
, uint8_t* b
, uint32_t bytes
) {
392 if (a
== nullptr && b
== nullptr) return true;
393 if (a
== nullptr || b
== nullptr) return false;
394 return memcmp(a
, b
, bytes
) == 0;
398 a
.flags
== b
.flags
&& a
.numArgs
== b
.numArgs
&& a
.numRets
== b
.numRets
&&
399 eq(a
.inoutArgs
.get(), b
.inoutArgs
.get(), (a
.numArgs
+ 7) / 8) &&
400 a
.asyncEagerTarget
== b
.asyncEagerTarget
&&
401 a
.lockWhileUnwinding
== b
.lockWhileUnwinding
&&
402 a
.skipNumArgsCheck
== b
.skipNumArgsCheck
&&
403 a
.context
== b
.context
;
406 size_t hash() const {
407 uint64_t hash
= HPHP::hash_int64_pair(numArgs
, numRets
);
408 hash
= HPHP::hash_int64_pair(hash
, flags
);
409 if (auto const br
= reinterpret_cast<const char*>(inoutArgs
.get())) {
410 auto const hash_br
= hash_string_cs(br
, (numArgs
+ 7) / 8);
411 hash
= HPHP::hash_int64_pair(hash
, hash_br
);
413 hash
= HPHP::hash_int64_pair(hash
, asyncEagerTarget
);
414 hash
= HPHP::hash_int64_pair(hash
, lockWhileUnwinding
);
415 if (context
) hash
= HPHP::hash_int64_pair(hash
, context
->hash());
416 return static_cast<size_t>(hash
);
419 uint32_t numPop() const {
420 return nin
+ numInputs() + 2 + numRets
;
422 template<int nin
, int nobj
>
423 Flavor
popFlavor(uint32_t i
) const {
424 assert(i
< this->template numPop
<nin
>());
425 if (i
< nin
) return Flavor::C
;
428 if (i
== 0) return Flavor::C
;
432 if (i
== 0) return Flavor::C
;
435 if (i
< numArgs
) return Flavor::C
;
437 if (i
== 2 && nobj
) return Flavor::C
;
440 FCallArgsBase
base() const { return *this; }
442 BlockId asyncEagerTarget
;
443 std::unique_ptr
<uint8_t[]> inoutArgs
;
448 using Flags
= FCallArgsBase::Flags
;
449 explicit FCallArgs(uint32_t numArgs
)
450 : FCallArgs(Flags::None
, numArgs
, 1, nullptr, NoBlockId
, false, false,
452 FCallArgs(Flags flags
, uint32_t numArgs
, uint32_t numRets
,
453 std::unique_ptr
<uint8_t[]> inoutArgs
,
454 BlockId asyncEagerTarget
, bool lockWhileUnwinding
,
455 bool skipNumArgsCheck
, SString context
)
456 : s
{FCallArgsShort::create(flags
, numArgs
, numRets
, inoutArgs
,
457 asyncEagerTarget
, lockWhileUnwinding
,
458 skipNumArgsCheck
, context
)} {
461 l
.emplace(flags
, numArgs
, numRets
, std::move(inoutArgs
), asyncEagerTarget
,
462 lockWhileUnwinding
, skipNumArgsCheck
, context
);
465 FCallArgs(const FCallArgsShort
& o
) : s
{o
} {}
466 FCallArgs(const FCallArgsLong
& o
) : l
{o
} {}
467 FCallArgs(const FCallArgs
& o
) : l
{} {
474 FCallArgs(FCallArgs
&& o
) : l
{} {
481 FCallArgs
& operator=(const FCallArgs
& o
) {
484 new (this) FCallArgs
{o
};
488 FCallArgs
& operator=(FCallArgs
&& o
) {
489 assertx(raw
!= o
.raw
);
491 new (this) FCallArgs
{std::move(o
)};
495 if (!s
.valid()) l
.~copy_ptr();
498 bool enforceInOut() const {
499 return s
.valid() ? s
.enforceInOut() : l
->enforceInOut();
501 bool isInOut(uint32_t i
) const {
502 assertx(enforceInOut());
503 return s
.valid() ? s
.isInOut(i
) : l
->isInOut(i
);
506 FCallArgs
withoutGenerics() const {
507 if (s
.valid()) return s
.withoutGenerics();
508 return l
->withoutGenerics();
511 FCallArgs
withoutInOut() const {
512 if (s
.valid()) return s
.withoutInOut();
513 return l
->withoutInOut();
515 FCallArgs
withoutLockWhileUnwinding() const {
516 if (s
.valid()) return s
.withoutLockWhileUnwinding();
517 return l
->withoutLockWhileUnwinding();
519 FCallArgs
fixEager(bool supports_eager
) const {
520 if (s
.valid()) return s
.fixEager(supports_eager
);
521 return l
->fixEager(supports_eager
);
523 FCallArgs
withoutNumArgsCheck() const {
524 if (s
.valid()) return s
.withoutNumArgsCheck();
525 return l
->withoutNumArgsCheck();
528 FCallArgsBase
base() const {
529 return s
.valid() ? s
.base() : l
->base();
532 friend bool operator==(const FCallArgs
& a
, const FCallArgs
& b
) {
534 if (!b
.s
.valid()) return false;
537 if (b
.s
.valid()) return false;
538 if (a
.l
.get() == b
.l
.get()) return true;
542 size_t hash() const {
543 return s
.valid() ? s
.hash() : l
->hash();
546 uint32_t numArgs() const {
547 return s
.valid() ? s
.numArgs
: l
->numArgs
;
550 uint32_t numRets() const {
551 return s
.valid() ? s
.numRets
: l
->numRets
;
554 const uint8_t* inoutArgs() const {
555 return s
.valid() ? s
.inoutArgs() : l
->inoutArgs
.get();
558 void applyIO(F f
) const {
559 assertx(enforceInOut());
567 BlockId
asyncEagerTarget() const {
568 return s
.valid() ? s
.asyncEagerTarget
: l
->asyncEagerTarget
;
570 BlockId
& asyncEagerTarget() {
571 return s
.valid() ? s
.asyncEagerTarget
: l
.mutate()->asyncEagerTarget
;
573 bool lockWhileUnwinding() const {
574 return s
.valid() ? s
.lockWhileUnwinding() : l
->lockWhileUnwinding
;
576 bool hasUnpack() const {
577 return s
.valid() ? s
.hasUnpack() : l
->hasUnpack();
579 bool hasGenerics() const {
580 return s
.valid() ? s
.hasGenerics() : l
->hasGenerics();
582 uint32_t numInputs() const {
583 return s
.valid() ? s
.numInputs() : l
->numInputs();
585 bool supportsAsyncEagerReturn() const {
587 s
.supportsAsyncEagerReturn() :
588 l
->supportsAsyncEagerReturn();
590 bool skipNumArgsCheck() const {
592 s
.skipNumArgsCheck() :
596 uint32_t numPop() const {
597 return s
.valid() ? s
.template numPop
<nin
>() : l
->template numPop
<nin
>();
599 template<int nin
, int nobj
>
600 Flavor
popFlavor(uint32_t i
) const {
602 s
.template popFlavor
<nin
,nobj
>(i
) : l
->template popFlavor
<nin
,nobj
>(i
);
604 SString
context() const { return s
.valid() ? nullptr : l
->context
; }
607 copy_ptr
<FCallArgsLong
> l
;
613 inline bool operator!=(const FCallArgs
& a
, const FCallArgs
& b
) {
617 using SwitchTab
= CompactVector
<BlockId
>;
619 // The final entry in the SSwitchTab is the default case, it will
620 // always have a nullptr for the string.
621 using SSwitchTabEnt
= std::pair
<LSString
,BlockId
>;
622 using SSwitchTab
= CompactVector
<SSwitchTabEnt
>;
624 //////////////////////////////////////////////////////////////////////
628 //////////////////////////////////////////////////////////////////////
633 * Trivial hasher overrides, which will cause bytecodes to hash according to
634 * the hashes defined by hasher_impl.
636 struct hasher_default
{};
639 * Default bytecode immediate hashers.
642 static size_t hash(SString s
) { return s
->hash(); }
643 static size_t hash(LSString s
) { return s
->hash(); }
644 static size_t hash(RepoAuthType rat
) { return rat
.hash(); }
646 static size_t hash(MKey mkey
) {
647 return HPHP::hash_int64_pair(mkey
.mcode
, mkey
.int64
);
651 static size_t hash(const CompactVector
<T
>& v
) {
652 return v
.empty() ? 0 : v
.size() ^ hash(v
.front());
655 static size_t hash(std::pair
<LSString
,BlockId
> kv
) {
656 return HPHP::hash_int64_pair(kv
.first
->hash(), kv
.second
);
659 static size_t hash(NamedLocal loc
) {
660 return HPHP::hash_int64((((uint64_t)loc
.name
) << 32) | loc
.id
);
663 static size_t hash(LocalRange range
) {
664 return HPHP::hash_int64_pair(range
.first
, range
.count
);
667 static size_t hash(IterArgs ita
) {
668 auto hash
= HPHP::hash_int64_pair(ita
.flags
, ita
.iterId
);
669 hash
= HPHP::hash_int64_pair(hash
, ita
.keyId
);
670 return HPHP::hash_int64_pair(hash
, ita
.valId
);
673 static size_t hash(FCallArgs fca
) {
678 static typename
std::enable_if
<
679 std::is_enum
<T
>::value
,
682 using U
= typename
std::underlying_type
<T
>::type
;
683 return std::hash
<U
>()(static_cast<U
>(t
));
687 static typename
std::enable_if
<
688 !std::is_enum
<T
>::value
,
690 >::type
hash(const T
& t
) { return std::hash
<T
>()(t
); }
694 * Hash operand wrapper.
696 template<typename T
, typename S
>
697 struct hash_operand
{ const T
& val
; S type
; };
699 // this template isn't really needed. its a workaround for T44007494
700 template<typename S
> struct hash_operand
<void*, S
> { void* const val
; S type
; };
703 * Hash T using H::operator() if it is compatible, else fall back to
704 * hasher_impl (e.g., if H := hasher_default).
706 template<typename T
, typename S
, class H
>
707 auto hash(const hash_operand
<T
,S
>& t
, H h
) -> decltype(h(t
.val
)) {
710 template<typename T
, typename S
, class H
>
711 auto hash(const hash_operand
<T
,S
>& t
, H h
) -> decltype(h(t
.val
, t
.type
)) {
712 return h(t
.val
, t
.type
);
714 template<typename T
, typename S
, typename
... Unused
>
715 typename
std::enable_if
<sizeof...(Unused
) == 1, size_t>::type
716 hash(const hash_operand
<T
,S
>& t
, Unused
...) {
717 return hasher_impl::hash(t
.val
);
721 * Clone of folly::hash::hash_combine_generic.
724 size_t hash_combine(H
/*h*/) {
728 template<class H
, typename T
, typename
... Ts
>
729 size_t hash_combine(H h
, const T
& t
, const Ts
&... ts
) {
730 auto const seed
= size_t{hash(t
, h
)};
731 if (sizeof...(ts
) == 0) return seed
;
733 auto const remainder
= hash_combine(h
, ts
...);
734 return static_cast<size_t>(folly::hash::hash_128_to_64(seed
, remainder
));
738 * Trivial equality overrides, which will cause bytecodes to compare via
739 * operator==() on the various immediate types.
741 struct eq_default
{};
744 * Equality operand wrapper.
746 template<typename T
, typename S
>
747 struct eq_operand
{ const T
& l
; const T
& r
; S type
; };
749 // this template isn't really needed. its a workaround for T44007494
750 template<typename S
> struct eq_operand
<void*, S
> {
751 void* const l
; void* const r
; S type
;
755 * Compare two values, using E::operator() if it exists, else the default
758 template<typename T
, typename S
, class E
>
759 auto equals(const eq_operand
<T
,S
>& t
, E e
) -> decltype(e(t
.l
, t
.r
)) {
762 template<typename T
, typename S
, class E
>
763 auto equals(const eq_operand
<T
,S
>& t
, E e
) -> decltype(e(t
.l
, t
.r
, t
.type
)) {
764 return e(t
.l
, t
.r
, t
.type
);
766 template<typename T
, typename S
, typename
... Unused
>
767 typename
std::enable_if
<sizeof...(Unused
) == 1, bool>::type
768 equals(const eq_operand
<T
,S
>& t
, Unused
...) {
773 * Check if a list of eq_operands are pairwise-equal.
776 bool eq_pairs(E
/*e*/) {
780 template<class E
, typename T
, typename
... Ts
>
781 bool eq_pairs(E e
, const T
& t
, const Ts
&... ts
) {
782 return equals(t
, e
) && (sizeof...(ts
) == 0 || eq_pairs(e
, ts
...));
787 //////////////////////////////////////////////////////////////////////
790 * Bytecode immediate type tags.
793 #define ARGTYPE(name, type) enum class name : uint8_t {};
794 #define ARGTYPEVEC(name, type) enum class name : uint8_t {};
800 #define IMM_ID_BLA BLA
801 #define IMM_ID_SLA SLA
802 #define IMM_ID_IVA IVA
803 #define IMM_ID_I64A I64A
805 #define IMM_ID_NLA NLA
806 #define IMM_ID_ILA ILA
810 #define IMM_ID_RATA RATA
813 #define IMM_ID_OA(type) OA
814 #define IMM_ID_VSA VSA
816 #define IMM_ID_LAR LAR
817 #define IMM_ID_ITA ITA
818 #define IMM_ID_FCA FCA
820 #define IMM_TY_BLA SwitchTab
821 #define IMM_TY_SLA SSwitchTab
822 #define IMM_TY_IVA uint32_t
823 #define IMM_TY_I64A int64_t
824 #define IMM_TY_LA LocalId
825 #define IMM_TY_NLA NamedLocal
826 #define IMM_TY_ILA LocalId
827 #define IMM_TY_IA IterId
828 #define IMM_TY_DA double
829 #define IMM_TY_SA LSString
830 #define IMM_TY_RATA RepoAuthType
831 #define IMM_TY_AA SArray
832 #define IMM_TY_BA BlockId
833 #define IMM_TY_OA(type) type
834 #define IMM_TY_VSA CompactVector<LSString>
835 #define IMM_TY_KA MKey
836 #define IMM_TY_LAR LocalRange
837 #define IMM_TY_ITA IterArgs
838 #define IMM_TY_FCA FCallArgs
840 #define IMM_NAME_BLA(n) targets
841 #define IMM_NAME_SLA(n) targets
842 #define IMM_NAME_IVA(n) arg##n
843 #define IMM_NAME_I64A(n) arg##n
844 #define IMM_NAME_LA(n) loc##n
845 #define IMM_NAME_NLA(n) nloc##n
846 #define IMM_NAME_ILA(n) loc##n
847 #define IMM_NAME_IA(n) iter##n
848 #define IMM_NAME_DA(n) dbl##n
849 #define IMM_NAME_SA(n) str##n
850 #define IMM_NAME_RATA(n) rat
851 #define IMM_NAME_AA(n) arr##n
852 #define IMM_NAME_BA(n) target##n
853 #define IMM_NAME_OA_IMPL(n) subop##n
854 #define IMM_NAME_OA(type) IMM_NAME_OA_IMPL
855 #define IMM_NAME_VSA(n) keys
856 #define IMM_NAME_KA(n) mkey
857 #define IMM_NAME_LAR(n) locrange
858 #define IMM_NAME_ITA(n) ita
859 #define IMM_NAME_FCA(n) fca
861 #define IMM_TARGETS_BLA(n) for (auto& t : targets) f(t);
862 #define IMM_TARGETS_SLA(n) for (auto& kv : targets) f(kv.second);
863 #define IMM_TARGETS_IVA(n)
864 #define IMM_TARGETS_I64A(n)
865 #define IMM_TARGETS_LA(n)
866 #define IMM_TARGETS_NLA(n)
867 #define IMM_TARGETS_ILA(n)
868 #define IMM_TARGETS_IA(n)
869 #define IMM_TARGETS_DA(n)
870 #define IMM_TARGETS_SA(n)
871 #define IMM_TARGETS_RATA(n)
872 #define IMM_TARGETS_AA(n)
873 #define IMM_TARGETS_BA(n) f(target##n);
874 #define IMM_TARGETS_OA_IMPL(n)
875 #define IMM_TARGETS_OA(type) IMM_TARGETS_OA_IMPL
876 #define IMM_TARGETS_VSA(n)
877 #define IMM_TARGETS_KA(n)
878 #define IMM_TARGETS_LAR(n)
879 #define IMM_TARGETS_ITA(n)
880 #define IMM_TARGETS_FCA(n) if (fca.asyncEagerTarget() != NoBlockId) { \
881 f(fca.asyncEagerTarget()); \
884 #define IMM_EXTRA_BLA
885 #define IMM_EXTRA_SLA
886 #define IMM_EXTRA_IVA
887 #define IMM_EXTRA_I64A
889 #define IMM_EXTRA_NLA
890 #define IMM_EXTRA_ILA
894 #define IMM_EXTRA_RATA
897 #define IMM_EXTRA_OA(x)
898 #define IMM_EXTRA_VSA
900 #define IMM_EXTRA_LAR
901 #define IMM_EXTRA_ITA
902 #define IMM_EXTRA_FCA
904 #define IMM_MEM(which, n) IMM_TY_##which IMM_NAME_##which(n)
906 #define IMM_MEM_ONE(x) IMM_MEM(x, 1);
907 #define IMM_MEM_TWO(x, y) IMM_MEM(x, 1); IMM_MEM(y, 2);
908 #define IMM_MEM_THREE(x, y, z) IMM_MEM(x, 1); IMM_MEM(y, 2); \
910 #define IMM_MEM_FOUR(x, y, z, l) IMM_MEM(x, 1); IMM_MEM(y, 2); \
911 IMM_MEM(z, 3); IMM_MEM(l, 4);
912 #define IMM_MEM_FIVE(x, y, z, l, m) IMM_MEM(x, 1); IMM_MEM(y, 2); \
913 IMM_MEM(z, 3); IMM_MEM(l, 4); \
915 #define IMM_MEM_SIX(x, y, z, l, m, n) IMM_MEM(x, 1); IMM_MEM(y, 2); \
916 IMM_MEM(z, 3); IMM_MEM(l, 4); \
917 IMM_MEM(m, 5); IMM_MEM(n, 6);
919 #define IMM_EQ_WRAP(e, ...) detail::eq_pairs(e, __VA_ARGS__)
920 #define IMM_EQ(which, n) detail::eq_operand< \
922 imm::IMM_ID_##which \
924 IMM_NAME_##which(n), \
925 o.IMM_NAME_##which(n) \
927 #define IMM_EQ_NA detail::eq_operand<void*,imm::NA> { 0, 0 }
928 #define IMM_EQ_ONE(x) IMM_EQ(x, 1)
929 #define IMM_EQ_TWO(x, y) IMM_EQ_ONE(x), IMM_EQ(y, 2)
930 #define IMM_EQ_THREE(x, y, z) IMM_EQ_TWO(x, y), IMM_EQ(z, 3)
931 #define IMM_EQ_FOUR(x, y, z, l) IMM_EQ_THREE(x, y, z), IMM_EQ(l, 4)
932 #define IMM_EQ_FIVE(x, y, z, l, m) IMM_EQ_FOUR(x, y, z, l), IMM_EQ(m, 5)
933 #define IMM_EQ_SIX(x, y, z, l, m, n) IMM_EQ_FIVE(x, y, z, l, m), IMM_EQ(n, 6)
935 #define IMM_HASH_WRAP(h, ...) detail::hash_combine(h, __VA_ARGS__)
936 #define IMM_HASH(which, n) detail::hash_operand< \
938 imm::IMM_ID_##which \
939 > { IMM_NAME_##which(n) }
940 #define IMM_HASH_NA detail::hash_operand<void*,imm::NA> { 0 }
941 #define IMM_HASH_ONE(x) IMM_HASH(x, 1)
942 #define IMM_HASH_TWO(x, y) IMM_HASH_ONE(x), IMM_HASH(y, 2)
943 #define IMM_HASH_THREE(x, y, z) IMM_HASH_TWO(x, y), IMM_HASH(z, 3)
944 #define IMM_HASH_FOUR(x, y, z, l) IMM_HASH_THREE(x, y, z), IMM_HASH(l, 4)
945 #define IMM_HASH_FIVE(x, y, z, l, m) IMM_HASH_FOUR(x, y, z, l), IMM_HASH(m, 5)
946 #define IMM_HASH_SIX(x, y, z, l, m, n) IMM_HASH_FIVE(x, y, z, l, m), IMM_HASH(n, 6)
948 #define IMM_TARGETS_NA
949 #define IMM_TARGETS_ONE(x) IMM_TARGETS_##x(1)
950 #define IMM_TARGETS_TWO(x,y) IMM_TARGETS_ONE(x) IMM_TARGETS_##y(2)
951 #define IMM_TARGETS_THREE(x,y,z) IMM_TARGETS_TWO(x,y) IMM_TARGETS_##z(3)
952 #define IMM_TARGETS_FOUR(x,y,z,l) IMM_TARGETS_THREE(x,y,z) IMM_TARGETS_##l(4)
953 #define IMM_TARGETS_FIVE(x,y,z,l,m) IMM_TARGETS_FOUR(x,y,z,l) IMM_TARGETS_##m(5)
954 #define IMM_TARGETS_SIX(x,y,z,l,m,n) IMM_TARGETS_FIVE(x,y,z,l,m) IMM_TARGETS_##n(6)
957 #define IMM_EXTRA_ONE(x) IMM_EXTRA_##x
958 #define IMM_EXTRA_TWO(x,y) IMM_EXTRA_ONE(x) IMM_EXTRA_ONE(y)
959 #define IMM_EXTRA_THREE(x,y,z) IMM_EXTRA_TWO(x,y) IMM_EXTRA_ONE(z)
960 #define IMM_EXTRA_FOUR(x,y,z,l) IMM_EXTRA_THREE(x,y,z) IMM_EXTRA_ONE(l)
961 #define IMM_EXTRA_FIVE(x,y,z,l,m) IMM_EXTRA_FOUR(x,y,z,l) IMM_EXTRA_ONE(m)
962 #define IMM_EXTRA_SIX(x,y,z,l,m,n) IMM_EXTRA_FIVE(x,y,z,l,m) IMM_EXTRA_ONE(n)
964 #define IMM_CTOR(which, n) IMM_TY_##which IMM_NAME_##which(n)
966 #define IMM_CTOR_ONE(x) IMM_CTOR(x, 1)
967 #define IMM_CTOR_TWO(x, y) IMM_CTOR(x, 1), IMM_CTOR(y, 2)
968 #define IMM_CTOR_THREE(x, y, z) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
970 #define IMM_CTOR_FOUR(x, y, z, l) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
971 IMM_CTOR(z, 3), IMM_CTOR(l, 4)
972 #define IMM_CTOR_FIVE(x, y, z, l, m) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
973 IMM_CTOR(z, 3), IMM_CTOR(l, 4), \
975 #define IMM_CTOR_SIX(x, y, z, l, m, n) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
976 IMM_CTOR(z, 3), IMM_CTOR(l, 4), \
977 IMM_CTOR(m, 5), IMM_CTOR(n, 6)
979 #define IMM_INIT(which, n) IMM_NAME_##which(n) \
980 ( std::move(IMM_NAME_##which(n)) )
982 #define IMM_INIT_ONE(x) : IMM_INIT(x, 1)
983 #define IMM_INIT_TWO(x, y) : IMM_INIT(x, 1), IMM_INIT(y, 2)
984 #define IMM_INIT_THREE(x, y, z) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
986 #define IMM_INIT_FOUR(x, y, z, l) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
987 IMM_INIT(z, 3), IMM_INIT(l, 4)
988 #define IMM_INIT_FIVE(x, y, z, l, m) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
989 IMM_INIT(z, 3), IMM_INIT(l, 4), \
991 #define IMM_INIT_SIX(x, y, z, l, m, n) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
992 IMM_INIT(z, 3), IMM_INIT(l, 4), \
993 IMM_INIT(m, 5), IMM_INIT(n, 6)
995 #define POP_UV if (i == 0) return Flavor::U
996 #define POP_CV if (i == 0) return Flavor::C
997 #define POP_CU if (i == 0) return Flavor::CU
998 #define POP_CUV POP_CU
1000 #define POP_NOV uint32_t numPop() const { return 0; } \
1001 Flavor popFlavor(uint32_t) const { not_reached(); }
1003 #define POP_ONE(x) uint32_t numPop() const { return 1; } \
1004 Flavor popFlavor(uint32_t i) const { \
1005 POP_##x; not_reached(); \
1008 #define POP_TWO(x, y) uint32_t numPop() const { return 2; } \
1009 Flavor popFlavor(uint32_t i) const { \
1010 POP_##x; --i; POP_##y; not_reached(); \
1013 #define POP_THREE(x, y, z) uint32_t numPop() const { return 3; } \
1014 Flavor popFlavor(uint32_t i) const { \
1015 POP_##x; --i; POP_##y; --i; POP_##z; \
1019 #define POP_MFINAL uint32_t numPop() const { return arg1; } \
1020 Flavor popFlavor(uint32_t) const { not_reached(); }
1022 #define POP_C_MFINAL(n) uint32_t numPop() const { return arg1 + n; } \
1023 Flavor popFlavor(uint32_t) const { not_reached(); }
1025 #define POP_CMANY uint32_t numPop() const { return arg1; } \
1026 Flavor popFlavor(uint32_t i) const { \
1027 assert(i < numPop()); \
1031 #define POP_SMANY uint32_t numPop() const { return keys.size(); } \
1032 Flavor popFlavor(uint32_t i) const { \
1033 assert(i < numPop()); \
1037 #define POP_CUMANY uint32_t numPop() const { return arg1; } \
1038 Flavor popFlavor(uint32_t i) const { \
1039 assert(i < numPop()); \
1040 return Flavor::CU; \
1043 #define POP_CMANY_U3 \
1044 uint32_t numPop() const { return arg1 + 3; } \
1045 Flavor popFlavor(uint32_t i) const { \
1046 assert(i < numPop()); \
1047 return i < arg1 ? Flavor::C : Flavor::U; \
1050 #define POP_CALLNATIVE uint32_t numPop() const { return arg1 + arg3; } \
1051 Flavor popFlavor(uint32_t i) const { \
1052 assert(i < numPop()); \
1053 return i < arg1 ? Flavor::CU : Flavor::U; \
1056 #define POP_FCALL(nin, nobj) \
1057 uint32_t numPop() const { \
1058 return fca.template numPop<nin>(); \
1060 Flavor popFlavor(uint32_t i) const { \
1061 return fca.template popFlavor<nin,nobj>(i); \
1064 #define PUSH_NOV uint32_t numPush() const { return 0; }
1066 #define PUSH_ONE(x) uint32_t numPush() const { return 1; }
1068 #define PUSH_TWO(x, y) uint32_t numPush() const { return 2; }
1070 #define PUSH_CMANY uint32_t numPush() const { return arg1; }
1071 #define PUSH_FCALL uint32_t numPush() const { return fca.numRets(); }
1072 #define PUSH_CALLNATIVE uint32_t numPush() const { return arg3 + 1; }
1081 #define FLAGS_CTOR_NF
1082 #define FLAGS_CTOR_TF
1083 #define FLAGS_CTOR_CF
1084 #define FLAGS_CTOR_FF
1085 #define FLAGS_CTOR_CF_TF
1086 #define FLAGS_CTOR_CF_FF
1088 #define FLAGS_INIT_NF
1089 #define FLAGS_INIT_TF
1090 #define FLAGS_INIT_CF
1091 #define FLAGS_INIT_FF
1092 #define FLAGS_INIT_CF_TF
1093 #define FLAGS_INIT_CF_FF
1095 #define O(opcode, imms, inputs, outputs, flags) \
1097 static constexpr Op op = Op::opcode; \
1098 explicit opcode ( IMM_CTOR_##imms \
1099 FLAGS_CTOR_##flags) \
1101 FLAGS_INIT_##flags \
1110 bool operator==(const opcode& o) const { \
1111 return equals(o, detail::eq_default{}); \
1114 bool operator!=(const opcode& o) const { \
1115 return !(*this == o); \
1119 bool equals(const opcode& o, E e) const { \
1120 return IMM_EQ_WRAP(e, IMM_EQ_##imms); \
1123 size_t hash() const { \
1124 return IMM_HASH_WRAP( \
1125 detail::hasher_default{}, \
1130 size_t hash(H h) const { \
1131 return IMM_HASH_WRAP(h, IMM_HASH_##imms); \
1134 template <typename F> \
1135 void forEachTarget(F&& f) { \
1136 IMM_TARGETS_##imms \
1138 template <typename F> \
1139 void forEachTarget(F&& f) const { \
1140 IMM_TARGETS_##imms \
1153 #undef FLAGS_CTOR_NA
1154 #undef FLAGS_CTOR_TF
1155 #undef FLAGS_CTOR_CF
1156 #undef FLAGS_CTOR_FF
1157 #undef FLAGS_CTOR_CF_TF
1158 #undef FLAGS_CTOR_CF_FF
1160 #undef FLAGS_INIT_NA
1161 #undef FLAGS_INIT_TF
1162 #undef FLAGS_INIT_CF
1163 #undef FLAGS_INIT_FF
1164 #undef FLAGS_INIT_CF_TF
1165 #undef FLAGS_INIT_CF_FF
1172 #undef PUSH_CALLNATIVE
1189 #undef POP_CALLNATIVE
1213 // These are deliberately not undefined, so they can be used in other
1215 // #undef IMM_NAME_BLA
1216 // #undef IMM_NAME_SLA
1217 // #undef IMM_NAME_IVA
1218 // #undef IMM_NAME_I64A
1219 // #undef IMM_NAME_LA
1220 // #undef IMM_NAME_NLA
1221 // #undef IMM_NAME_ILA
1222 // #undef IMM_NAME_IA
1223 // #undef IMM_NAME_DA
1224 // #undef IMM_NAME_SA
1225 // #undef IMM_NAME_RATA
1226 // #undef IMM_NAME_AA
1227 // #undef IMM_NAME_BA
1228 // #undef IMM_NAME_OA
1229 // #undef IMM_NAME_OA_IMPL
1230 // #undef IMM_NAME_LAR
1231 // #undef IMM_NAME_FCA
1233 #undef IMM_TARGETS_BLA
1234 #undef IMM_TARGETS_SLA
1235 #undef IMM_TARGETS_IVA
1236 #undef IMM_TARGETS_I64A
1237 #undef IMM_TARGETS_LA
1238 #undef IMM_TARGETS_NLA
1239 #undef IMM_TARGETS_ILA
1240 #undef IMM_TARGETS_IA
1241 #undef IMM_TARGETS_DA
1242 #undef IMM_TARGETS_SA
1243 #undef IMM_TARGETS_RATA
1244 #undef IMM_TARGETS_AA
1245 #undef IMM_TARGETS_BA
1246 #undef IMM_TARGETS_OA
1247 #undef IMM_TARGETS_KA
1248 #undef IMM_TARGETS_LAR
1249 #undef IMM_TARGETS_ITA
1250 #undef IMM_TARGETS_FCA
1252 #undef IMM_TARGETS_NA
1253 #undef IMM_TARGETS_ONE
1254 #undef IMM_TARGETS_TWO
1255 #undef IMM_TARGETS_THREE
1256 #undef IMM_TARGETS_FOUR
1257 #undef IMM_TARGETS_FIVE
1258 #undef IMM_TARGETS_SIX
1260 #undef IMM_EXTRA_BLA
1261 #undef IMM_EXTRA_SLA
1262 #undef IMM_EXTRA_IVA
1263 #undef IMM_EXTRA_I64A
1265 #undef IMM_EXTRA_NLA
1266 #undef IMM_EXTRA_ILA
1270 #undef IMM_EXTRA_RATA
1275 #undef IMM_EXTRA_LAR
1276 #undef IMM_EXTRA_ITA
1277 #undef IMM_EXTRA_FCA
1283 #undef IMM_MEM_THREE
1302 #undef IMM_HASH_THREE
1303 #undef IMM_HASH_FOUR
1304 #undef IMM_HASH_FIVE
1311 #undef IMM_CTOR_THREE
1312 #undef IMM_CTOR_FOUR
1313 #undef IMM_CTOR_FIVE
1320 #undef IMM_INIT_THREE
1321 #undef IMM_INIT_FOUR
1325 //////////////////////////////////////////////////////////////////////
1328 * Bytecode is a tagged-union that can hold any HHBC opcode struct
1329 * defined above. You can visit a bytecode with a static_visitor
1330 * using visit(), defined below.
1332 * Each Bytecode also carries a corresponding SrcLoc that indicates
1333 * which line of PHP code the bytecode was generated for.
1336 // Default construction creates a Nop.
1342 #define O(opcode, ...) \
1343 /* implicit */ Bytecode(bc::opcode data) \
1346 new (&opcode) bc::opcode(std::move(data)); \
1353 // Note: assuming bc::Nop is empty and has trivial dtor/ctor.
1355 Bytecode(const Bytecode
& o
) : op(Op::Nop
) { *this = o
; }
1356 Bytecode(Bytecode
&& o
) noexcept
: op(Op::Nop
) { *this = std::move(o
); }
1358 Bytecode
& operator=(const Bytecode
& o
) {
1362 #define O(opcode, ...) \
1363 case Op::opcode: new (&opcode) bc::opcode(o.opcode); break;
1364 switch (o
.op
) { OPCODES
}
1370 Bytecode
& operator=(Bytecode
&& o
) {
1373 #define O(opcode, ...) \
1374 case Op::opcode: new (&opcode) bc::opcode(std::move(o.opcode)); break;
1375 switch (o
.op
) { OPCODES
}
1381 ~Bytecode() { destruct(); }
1383 uint32_t numPop() const {
1384 #define O(opcode, ...) \
1385 case Op::opcode: return opcode.numPop();
1386 switch (op
) { OPCODES
}
1391 Flavor
popFlavor(uint32_t i
) const {
1392 #define O(opcode, ...) \
1393 case Op::opcode: return opcode.popFlavor(i);
1394 switch (op
) { OPCODES
}
1399 uint32_t numPush() const {
1400 #define O(opcode, ...) \
1401 case Op::opcode: return opcode.numPush();
1402 switch (op
) { OPCODES
}
1407 template <typename F
>
1408 void forEachTarget(F
&& f
) const {
1409 #define O(opcode, ...) \
1410 case Op::opcode: return opcode.forEachTarget(std::forward<F>(f));
1411 switch (op
) { OPCODES
}
1416 template <typename F
>
1417 void forEachTarget(F
&& f
) {
1418 #define O(opcode, ...) \
1419 case Op::opcode: return opcode.forEachTarget(std::forward<F>(f));
1420 switch (op
) { OPCODES
}
1428 #define O(opcode, ...) bc::opcode opcode;
1435 #define O(opcode, ...) \
1437 { typedef bc::opcode X; \
1438 this->opcode.~X(); } \
1446 //////////////////////////////////////////////////////////////////////
1448 inline bool operator==(const Bytecode
& a
, const Bytecode
& b
) {
1449 if (a
.op
!= b
.op
) return false;
1450 #define O(opcode, ...) case Op::opcode: return a.opcode == b.opcode;
1451 switch (a
.op
) { OPCODES
}
1456 inline bool operator!=(const Bytecode
& a
, const Bytecode
& b
) {
1461 inline bool equals(const Bytecode
& a
, const Bytecode
& b
, E equals
) {
1462 if (a
.op
!= b
.op
) return false;
1463 #define O(opcode, ...) \
1464 case Op::opcode: return a.opcode.equals(b.opcode, equals);
1465 switch (a
.op
) { OPCODES
}
1471 inline size_t hash(const Bytecode
& b
, H hasher
) {
1472 auto hash
= 14695981039346656037ULL;
1473 auto o
= static_cast<size_t>(b
.op
);
1475 #define O(opcode, ...) \
1477 return folly::hash::hash_combine(b.opcode.hash(hasher), hash);
1478 switch (b
.op
) { OPCODES
}
1483 inline size_t hash(const Bytecode
& b
) {
1484 return hash(b
, bc::detail::hasher_default
{});
1487 //////////////////////////////////////////////////////////////////////
1490 * Helper for making a Bytecode with a given srcLoc.
1493 * auto b = bc_with_loc(something.srcLoc, bc::Nop {});
1495 template<class T
> Bytecode
bc_with_loc(int32_t loc
, const T
& t
) {
1501 //////////////////////////////////////////////////////////////////////
1504 * Visit a bytecode using a StaticVisitor, similar to
1505 * boost::apply_visitor or match().
1507 * The `v' argument should be a function object that accepts a call
1508 * operator for all the bc::Foo types. Its result type should be
1509 * independent of bytecode, but may vary with constness.
1511 template<class Visit
>
1512 auto visit(Bytecode
& b
, Visit v
) {
1513 #define O(opcode, ...) case Op::opcode: return v(b.opcode);
1514 switch (b
.op
) { OPCODES
}
1519 template<class Visit
>
1520 auto visit(const Bytecode
& b
, Visit v
) {
1521 #define O(opcode, ...) case Op::opcode: return v(b.opcode);
1522 switch (b
.op
) { OPCODES
}
1527 //////////////////////////////////////////////////////////////////////