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 //////////////////////////////////////////////////////////////////////
58 MKey(MemberCode mcode
, LocalId local
)
63 MKey(MemberCode mcode
, int32_t idx
)
68 MKey(MemberCode mcode
, int64_t int64
)
73 MKey(MemberCode mcode
, SString litstr
)
87 inline bool operator==(MKey a
, MKey b
) {
88 return a
.mcode
== b
.mcode
&& a
.int64
== b
.int64
;
91 inline bool operator!=(MKey a
, MKey b
) {
95 // A contiguous range of locals. The count is the number of locals including the
96 // first. If the range is empty, count will be zero and first's value is
103 inline bool operator==(const LocalRange
& a
, const LocalRange
& b
) {
104 return a
.first
== b
.first
&& a
.count
== b
.count
;
107 inline bool operator!=(const LocalRange
& a
, const LocalRange
& b
) {
111 struct FCallArgs
: FCallArgsBase
{
112 explicit FCallArgs(uint32_t numArgs
)
113 : FCallArgs(Flags::None
, numArgs
, 1, nullptr, NoBlockId
, false) {}
114 explicit FCallArgs(Flags flags
, uint32_t numArgs
, uint32_t numRets
,
115 std::unique_ptr
<uint8_t[]> inoutArgs
,
116 BlockId asyncEagerTarget
, bool lockWhileUnwinding
)
117 : FCallArgsBase(flags
, numArgs
, numRets
, lockWhileUnwinding
)
118 , asyncEagerTarget(asyncEagerTarget
)
119 , inoutArgs(std::move(inoutArgs
)) {
120 assertx(IMPLIES(asyncEagerTarget
== NoBlockId
,
121 !supportsAsyncEagerReturn()));
123 FCallArgs(const FCallArgs
& o
)
124 : FCallArgs(o
.flags
, o
.numArgs
, o
.numRets
, nullptr, o
.asyncEagerTarget
,
125 o
.lockWhileUnwinding
) {
127 auto const numBytes
= (numArgs
+ 7) / 8;
128 inoutArgs
= std::make_unique
<uint8_t[]>(numBytes
);
129 memcpy(inoutArgs
.get(), o
.inoutArgs
.get(), numBytes
);
132 FCallArgs(FCallArgs
&& o
)
133 : FCallArgs(o
.flags
, o
.numArgs
, o
.numRets
, std::move(o
.inoutArgs
),
134 o
.asyncEagerTarget
, o
.lockWhileUnwinding
) {}
136 bool enforceInOut() const { return inoutArgs
.get() != nullptr; }
137 bool isInOut(uint32_t i
) const {
138 assertx(enforceInOut());
139 return inoutArgs
[i
/ 8] & (1 << (i
% 8));
142 FCallArgs
withoutGenerics() const {
144 fca
.flags
= static_cast<Flags
>(fca
.flags
& ~Flags::HasGenerics
);
148 BlockId asyncEagerTarget
;
149 std::unique_ptr
<uint8_t[]> inoutArgs
;
152 inline bool operator==(const FCallArgs
& a
, const FCallArgs
& b
) {
153 auto const eq
= [&] (uint8_t* a
, uint8_t* b
, uint32_t bytes
) {
154 if (a
== nullptr && b
== nullptr) return true;
155 if (a
== nullptr || b
== nullptr) return false;
156 return memcmp(a
, b
, bytes
) == 0;
160 a
.flags
== b
.flags
&& a
.numArgs
== b
.numArgs
&& a
.numRets
== b
.numRets
&&
161 eq(a
.inoutArgs
.get(), b
.inoutArgs
.get(), (a
.numArgs
+ 7) / 8) &&
162 a
.asyncEagerTarget
== b
.asyncEagerTarget
&&
163 a
.lockWhileUnwinding
== b
.lockWhileUnwinding
;
166 inline bool operator!=(const FCallArgs
& a
, const FCallArgs
& b
) {
176 inline bool operator==(const IterTabEnt
& a
, const IterTabEnt
& b
) {
177 return std::tie(a
.kind
, a
.id
, a
.local
) == std::tie(b
.kind
, b
.id
, b
.local
);
180 inline bool operator!=(const IterTabEnt
& a
, const IterTabEnt
& b
) {
184 using IterTab
= CompactVector
<IterTabEnt
>;
186 using SwitchTab
= CompactVector
<BlockId
>;
188 // The final entry in the SSwitchTab is the default case, it will
189 // always have a nullptr for the string.
190 using SSwitchTabEnt
= std::pair
<LSString
,BlockId
>;
191 using SSwitchTab
= CompactVector
<SSwitchTabEnt
>;
193 //////////////////////////////////////////////////////////////////////
197 //////////////////////////////////////////////////////////////////////
202 * Trivial hasher overrides, which will cause bytecodes to hash according to
203 * the hashes defined by hasher_impl.
205 struct hasher_default
{};
208 * Default bytecode immediate hashers.
211 static size_t hash(SString s
) { return s
->hash(); }
212 static size_t hash(LSString s
) { return s
->hash(); }
213 static size_t hash(RepoAuthType rat
) { return rat
.hash(); }
215 static size_t hash(const IterTabEnt
& iterTab
) {
216 auto const partial
= folly::hash::hash_128_to_64(
217 iterTab
.kind
, iterTab
.id
219 return static_cast<size_t>(
220 folly::hash::hash_128_to_64(iterTab
.local
, partial
)
224 static size_t hash(MKey mkey
) {
225 return HPHP::hash_int64_pair(mkey
.mcode
, mkey
.int64
);
229 static size_t hash(const CompactVector
<T
>& v
) {
230 return v
.empty() ? 0 : v
.size() ^ hash(v
.front());
233 static size_t hash(std::pair
<LSString
,BlockId
> kv
) {
234 return HPHP::hash_int64_pair(kv
.first
->hash(), kv
.second
);
237 static size_t hash(LocalRange range
) {
238 return HPHP::hash_int64_pair(range
.first
, range
.count
);
241 static size_t hash(IterArgs ita
) {
242 auto hash
= HPHP::hash_int64_pair(ita
.flags
, ita
.iterId
);
243 hash
= HPHP::hash_int64_pair(hash
, ita
.keyId
);
244 return HPHP::hash_int64_pair(hash
, ita
.valId
);
247 static size_t hash(FCallArgs fca
) {
248 uint64_t hash
= HPHP::hash_int64_pair(fca
.numArgs
, fca
.numRets
);
249 hash
= HPHP::hash_int64_pair(hash
, fca
.flags
);
251 auto const br
= reinterpret_cast<char*>(fca
.inoutArgs
.get());
252 auto const hash_br
= hash_string_cs(br
, (fca
.numArgs
+ 7) / 8);
253 hash
= HPHP::hash_int64_pair(hash
, hash_br
);
255 hash
= HPHP::hash_int64_pair(hash
, fca
.asyncEagerTarget
);
256 hash
= HPHP::hash_int64_pair(hash
, fca
.lockWhileUnwinding
);
257 return static_cast<size_t>(hash
);
261 static typename
std::enable_if
<
262 std::is_enum
<T
>::value
,
265 using U
= typename
std::underlying_type
<T
>::type
;
266 return std::hash
<U
>()(static_cast<U
>(t
));
270 static typename
std::enable_if
<
271 !std::is_enum
<T
>::value
,
273 >::type
hash(const T
& t
) { return std::hash
<T
>()(t
); }
277 * Hash operand wrapper.
279 template<typename T
, typename S
>
280 struct hash_operand
{ const T
& val
; S type
; };
282 // this template isn't really needed. its a workaround for T44007494
283 template<typename S
> struct hash_operand
<void*, S
> { void* const val
; S type
; };
286 * Hash T using H::operator() if it is compatible, else fall back to
287 * hasher_impl (e.g., if H := hasher_default).
289 template<typename T
, typename S
, class H
>
290 auto hash(const hash_operand
<T
,S
>& t
, H h
) -> decltype(h(t
.val
)) {
293 template<typename T
, typename S
, class H
>
294 auto hash(const hash_operand
<T
,S
>& t
, H h
) -> decltype(h(t
.val
, t
.type
)) {
295 return h(t
.val
, t
.type
);
297 template<typename T
, typename S
, typename
... Unused
>
298 typename
std::enable_if
<sizeof...(Unused
) == 1, size_t>::type
299 hash(const hash_operand
<T
,S
>& t
, Unused
...) {
300 return hasher_impl::hash(t
.val
);
304 * Clone of folly::hash::hash_combine_generic.
307 size_t hash_combine(H
/*h*/) {
311 template<class H
, typename T
, typename
... Ts
>
312 size_t hash_combine(H h
, const T
& t
, const Ts
&... ts
) {
313 auto const seed
= size_t{hash(t
, h
)};
314 if (sizeof...(ts
) == 0) return seed
;
316 auto const remainder
= hash_combine(h
, ts
...);
317 return static_cast<size_t>(folly::hash::hash_128_to_64(seed
, remainder
));
321 * Trivial equality overrides, which will cause bytecodes to compare via
322 * operator==() on the various immediate types.
324 struct eq_default
{};
327 * Equality operand wrapper.
329 template<typename T
, typename S
>
330 struct eq_operand
{ const T
& l
; const T
& r
; S type
; };
332 // this template isn't really needed. its a workaround for T44007494
333 template<typename S
> struct eq_operand
<void*, S
> {
334 void* const l
; void* const r
; S type
;
338 * Compare two values, using E::operator() if it exists, else the default
341 template<typename T
, typename S
, class E
>
342 auto equals(const eq_operand
<T
,S
>& t
, E e
) -> decltype(e(t
.l
, t
.r
)) {
345 template<typename T
, typename S
, class E
>
346 auto equals(const eq_operand
<T
,S
>& t
, E e
) -> decltype(e(t
.l
, t
.r
, t
.type
)) {
347 return e(t
.l
, t
.r
, t
.type
);
349 template<typename T
, typename S
, typename
... Unused
>
350 typename
std::enable_if
<sizeof...(Unused
) == 1, bool>::type
351 equals(const eq_operand
<T
,S
>& t
, Unused
...) {
356 * Check if a list of eq_operands are pairwise-equal.
359 bool eq_pairs(E
/*e*/) {
363 template<class E
, typename T
, typename
... Ts
>
364 bool eq_pairs(E e
, const T
& t
, const Ts
&... ts
) {
365 return equals(t
, e
) && (sizeof...(ts
) == 0 || eq_pairs(e
, ts
...));
370 //////////////////////////////////////////////////////////////////////
373 * Bytecode immediate type tags.
376 #define ARGTYPE(name, type) enum class name : uint8_t {};
377 #define ARGTYPEVEC(name, type) enum class name : uint8_t {};
383 #define IMM_ID_BLA BLA
384 #define IMM_ID_SLA SLA
385 #define IMM_ID_ILA ILA
386 #define IMM_ID_IVA IVA
387 #define IMM_ID_I64A I64A
392 #define IMM_ID_RATA RATA
395 #define IMM_ID_OA(type) OA
396 #define IMM_ID_VSA VSA
398 #define IMM_ID_LAR LAR
399 #define IMM_ID_ITA ITA
400 #define IMM_ID_FCA FCA
402 #define IMM_TY_BLA SwitchTab
403 #define IMM_TY_SLA SSwitchTab
404 #define IMM_TY_ILA IterTab
405 #define IMM_TY_IVA uint32_t
406 #define IMM_TY_I64A int64_t
407 #define IMM_TY_LA LocalId
408 #define IMM_TY_IA IterId
409 #define IMM_TY_DA double
410 #define IMM_TY_SA LSString
411 #define IMM_TY_RATA RepoAuthType
412 #define IMM_TY_AA SArray
413 #define IMM_TY_BA BlockId
414 #define IMM_TY_OA(type) type
415 #define IMM_TY_VSA CompactVector<LSString>
416 #define IMM_TY_KA MKey
417 #define IMM_TY_LAR LocalRange
418 #define IMM_TY_ITA IterArgs
419 #define IMM_TY_FCA FCallArgs
421 #define IMM_NAME_BLA(n) targets
422 #define IMM_NAME_SLA(n) targets
423 #define IMM_NAME_ILA(n) iterTab
424 #define IMM_NAME_IVA(n) arg##n
425 #define IMM_NAME_I64A(n) arg##n
426 #define IMM_NAME_LA(n) loc##n
427 #define IMM_NAME_IA(n) iter##n
428 #define IMM_NAME_DA(n) dbl##n
429 #define IMM_NAME_SA(n) str##n
430 #define IMM_NAME_RATA(n) rat
431 #define IMM_NAME_AA(n) arr##n
432 #define IMM_NAME_BA(n) target##n
433 #define IMM_NAME_OA_IMPL(n) subop##n
434 #define IMM_NAME_OA(type) IMM_NAME_OA_IMPL
435 #define IMM_NAME_VSA(n) keys
436 #define IMM_NAME_KA(n) mkey
437 #define IMM_NAME_LAR(n) locrange
438 #define IMM_NAME_ITA(n) ita
439 #define IMM_NAME_FCA(n) fca
441 #define IMM_TARGETS_BLA(n) for (auto& t : targets) f(t);
442 #define IMM_TARGETS_SLA(n) for (auto& kv : targets) f(kv.second);
443 #define IMM_TARGETS_ILA(n)
444 #define IMM_TARGETS_IVA(n)
445 #define IMM_TARGETS_I64A(n)
446 #define IMM_TARGETS_LA(n)
447 #define IMM_TARGETS_IA(n)
448 #define IMM_TARGETS_DA(n)
449 #define IMM_TARGETS_SA(n)
450 #define IMM_TARGETS_RATA(n)
451 #define IMM_TARGETS_AA(n)
452 #define IMM_TARGETS_BA(n) f(target##n);
453 #define IMM_TARGETS_OA_IMPL(n)
454 #define IMM_TARGETS_OA(type) IMM_TARGETS_OA_IMPL
455 #define IMM_TARGETS_VSA(n)
456 #define IMM_TARGETS_KA(n)
457 #define IMM_TARGETS_LAR(n)
458 #define IMM_TARGETS_ITA(n)
459 #define IMM_TARGETS_FCA(n) if (fca.asyncEagerTarget != NoBlockId) { \
460 f(fca.asyncEagerTarget); \
463 #define IMM_EXTRA_BLA
464 #define IMM_EXTRA_SLA
465 #define IMM_EXTRA_ILA
466 #define IMM_EXTRA_IVA
467 #define IMM_EXTRA_I64A
472 #define IMM_EXTRA_RATA
475 #define IMM_EXTRA_OA(x)
476 #define IMM_EXTRA_VSA
478 #define IMM_EXTRA_LAR
479 #define IMM_EXTRA_ITA
480 #define IMM_EXTRA_FCA
482 #define IMM_MEM(which, n) IMM_TY_##which IMM_NAME_##which(n)
484 #define IMM_MEM_ONE(x) IMM_MEM(x, 1);
485 #define IMM_MEM_TWO(x, y) IMM_MEM(x, 1); IMM_MEM(y, 2);
486 #define IMM_MEM_THREE(x, y, z) IMM_MEM(x, 1); IMM_MEM(y, 2); \
488 #define IMM_MEM_FOUR(x, y, z, l) IMM_MEM(x, 1); IMM_MEM(y, 2); \
489 IMM_MEM(z, 3); IMM_MEM(l, 4);
490 #define IMM_MEM_FIVE(x, y, z, l, m) IMM_MEM(x, 1); IMM_MEM(y, 2); \
491 IMM_MEM(z, 3); IMM_MEM(l, 4); \
493 #define IMM_MEM_SIX(x, y, z, l, m, n) IMM_MEM(x, 1); IMM_MEM(y, 2); \
494 IMM_MEM(z, 3); IMM_MEM(l, 4); \
495 IMM_MEM(m, 5); IMM_MEM(n, 6);
497 #define IMM_EQ_WRAP(e, ...) detail::eq_pairs(e, __VA_ARGS__)
498 #define IMM_EQ(which, n) detail::eq_operand< \
500 imm::IMM_ID_##which \
502 IMM_NAME_##which(n), \
503 o.IMM_NAME_##which(n) \
505 #define IMM_EQ_NA detail::eq_operand<void*,imm::NA> { 0, 0 }
506 #define IMM_EQ_ONE(x) IMM_EQ(x, 1)
507 #define IMM_EQ_TWO(x, y) IMM_EQ_ONE(x), IMM_EQ(y, 2)
508 #define IMM_EQ_THREE(x, y, z) IMM_EQ_TWO(x, y), IMM_EQ(z, 3)
509 #define IMM_EQ_FOUR(x, y, z, l) IMM_EQ_THREE(x, y, z), IMM_EQ(l, 4)
510 #define IMM_EQ_FIVE(x, y, z, l, m) IMM_EQ_FOUR(x, y, z, l), IMM_EQ(m, 5)
511 #define IMM_EQ_SIX(x, y, z, l, m, n) IMM_EQ_FIVE(x, y, z, l, m), IMM_EQ(n, 6)
513 #define IMM_HASH_WRAP(h, ...) detail::hash_combine(h, __VA_ARGS__)
514 #define IMM_HASH(which, n) detail::hash_operand< \
516 imm::IMM_ID_##which \
517 > { IMM_NAME_##which(n) }
518 #define IMM_HASH_NA detail::hash_operand<void*,imm::NA> { 0 }
519 #define IMM_HASH_ONE(x) IMM_HASH(x, 1)
520 #define IMM_HASH_TWO(x, y) IMM_HASH_ONE(x), IMM_HASH(y, 2)
521 #define IMM_HASH_THREE(x, y, z) IMM_HASH_TWO(x, y), IMM_HASH(z, 3)
522 #define IMM_HASH_FOUR(x, y, z, l) IMM_HASH_THREE(x, y, z), IMM_HASH(l, 4)
523 #define IMM_HASH_FIVE(x, y, z, l, m) IMM_HASH_FOUR(x, y, z, l), IMM_HASH(m, 5)
524 #define IMM_HASH_SIX(x, y, z, l, m, n) IMM_HASH_FIVE(x, y, z, l, m), IMM_HASH(n, 6)
526 #define IMM_TARGETS_NA
527 #define IMM_TARGETS_ONE(x) IMM_TARGETS_##x(1)
528 #define IMM_TARGETS_TWO(x,y) IMM_TARGETS_ONE(x) IMM_TARGETS_##y(2)
529 #define IMM_TARGETS_THREE(x,y,z) IMM_TARGETS_TWO(x,y) IMM_TARGETS_##z(3)
530 #define IMM_TARGETS_FOUR(x,y,z,l) IMM_TARGETS_THREE(x,y,z) IMM_TARGETS_##l(4)
531 #define IMM_TARGETS_FIVE(x,y,z,l,m) IMM_TARGETS_FOUR(x,y,z,l) IMM_TARGETS_##m(5)
532 #define IMM_TARGETS_SIX(x,y,z,l,m,n) IMM_TARGETS_FIVE(x,y,z,l,m) IMM_TARGETS_##n(6)
535 #define IMM_EXTRA_ONE(x) IMM_EXTRA_##x
536 #define IMM_EXTRA_TWO(x,y) IMM_EXTRA_ONE(x) IMM_EXTRA_ONE(y)
537 #define IMM_EXTRA_THREE(x,y,z) IMM_EXTRA_TWO(x,y) IMM_EXTRA_ONE(z)
538 #define IMM_EXTRA_FOUR(x,y,z,l) IMM_EXTRA_THREE(x,y,z) IMM_EXTRA_ONE(l)
539 #define IMM_EXTRA_FIVE(x,y,z,l,m) IMM_EXTRA_FOUR(x,y,z,l) IMM_EXTRA_ONE(m)
540 #define IMM_EXTRA_SIX(x,y,z,l,m,n) IMM_EXTRA_FIVE(x,y,z,l,m) IMM_EXTRA_ONE(n)
542 #define IMM_CTOR(which, n) IMM_TY_##which IMM_NAME_##which(n)
544 #define IMM_CTOR_ONE(x) IMM_CTOR(x, 1)
545 #define IMM_CTOR_TWO(x, y) IMM_CTOR(x, 1), IMM_CTOR(y, 2)
546 #define IMM_CTOR_THREE(x, y, z) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
548 #define IMM_CTOR_FOUR(x, y, z, l) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
549 IMM_CTOR(z, 3), IMM_CTOR(l, 4)
550 #define IMM_CTOR_FIVE(x, y, z, l, m) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
551 IMM_CTOR(z, 3), IMM_CTOR(l, 4), \
553 #define IMM_CTOR_SIX(x, y, z, l, m, n) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
554 IMM_CTOR(z, 3), IMM_CTOR(l, 4), \
555 IMM_CTOR(m, 5), IMM_CTOR(n, 6)
557 #define IMM_INIT(which, n) IMM_NAME_##which(n) \
558 ( std::move(IMM_NAME_##which(n)) )
560 #define IMM_INIT_ONE(x) : IMM_INIT(x, 1)
561 #define IMM_INIT_TWO(x, y) : IMM_INIT(x, 1), IMM_INIT(y, 2)
562 #define IMM_INIT_THREE(x, y, z) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
564 #define IMM_INIT_FOUR(x, y, z, l) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
565 IMM_INIT(z, 3), IMM_INIT(l, 4)
566 #define IMM_INIT_FIVE(x, y, z, l, m) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
567 IMM_INIT(z, 3), IMM_INIT(l, 4), \
569 #define IMM_INIT_SIX(x, y, z, l, m, n) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
570 IMM_INIT(z, 3), IMM_INIT(l, 4), \
571 IMM_INIT(m, 5), IMM_INIT(n, 6)
573 #define POP_UV if (i == 0) return Flavor::U
574 #define POP_CV if (i == 0) return Flavor::C
575 #define POP_CU if (i == 0) return Flavor::CU
576 #define POP_CUV POP_CU
578 #define POP_NOV uint32_t numPop() const { return 0; } \
579 Flavor popFlavor(uint32_t) const { not_reached(); }
581 #define POP_ONE(x) uint32_t numPop() const { return 1; } \
582 Flavor popFlavor(uint32_t i) const { \
583 POP_##x; not_reached(); \
586 #define POP_TWO(x, y) uint32_t numPop() const { return 2; } \
587 Flavor popFlavor(uint32_t i) const { \
588 POP_##x; --i; POP_##y; not_reached(); \
591 #define POP_THREE(x, y, z) uint32_t numPop() const { return 3; } \
592 Flavor popFlavor(uint32_t i) const { \
593 POP_##x; --i; POP_##y; --i; POP_##z; \
597 #define POP_MFINAL uint32_t numPop() const { return arg1; } \
598 Flavor popFlavor(uint32_t) const { not_reached(); }
600 #define POP_C_MFINAL(n) uint32_t numPop() const { return arg1 + n; } \
601 Flavor popFlavor(uint32_t) const { not_reached(); }
603 #define POP_CMANY uint32_t numPop() const { return arg1; } \
604 Flavor popFlavor(uint32_t i) const { \
605 assert(i < numPop()); \
609 #define POP_SMANY uint32_t numPop() const { return keys.size(); } \
610 Flavor popFlavor(uint32_t i) const { \
611 assert(i < numPop()); \
615 #define POP_CUMANY uint32_t numPop() const { return arg1; } \
616 Flavor popFlavor(uint32_t i) const { \
617 assert(i < numPop()); \
621 #define POP_CMANY_U3 \
622 uint32_t numPop() const { return arg1 + 3; } \
623 Flavor popFlavor(uint32_t i) const { \
624 assert(i < numPop()); \
625 return i < arg1 ? Flavor::C : Flavor::U; \
628 #define POP_CALLNATIVE uint32_t numPop() const { return arg1 + arg3; } \
629 Flavor popFlavor(uint32_t i) const { \
630 assert(i < numPop()); \
631 return i < arg1 ? Flavor::CU : Flavor::U; \
634 #define POP_FCALL(nin, nobj) \
635 uint32_t numPop() const { \
636 return nin + fca.numInputs() + 2 + fca.numRets; \
638 Flavor popFlavor(uint32_t i) const { \
639 assert(i < numPop()); \
640 if (i < nin) return Flavor::C; \
642 if (i == 0 && fca.hasGenerics()) return Flavor::C; \
643 i -= fca.hasGenerics() ? 1 : 0; \
644 if (i == 0 && fca.hasUnpack()) return Flavor::C; \
645 i -= fca.hasUnpack() ? 1 : 0; \
646 if (i < fca.numArgs) return Flavor::C; \
648 if (i == 2 && nobj) return Flavor::C; \
652 #define PUSH_NOV uint32_t numPush() const { return 0; }
654 #define PUSH_ONE(x) uint32_t numPush() const { return 1; }
656 #define PUSH_TWO(x, y) uint32_t numPush() const { return 2; }
658 #define PUSH_CMANY uint32_t numPush() const { return arg1; }
659 #define PUSH_FCALL uint32_t numPush() const { return fca.numRets; }
660 #define PUSH_CALLNATIVE uint32_t numPush() const { return arg3 + 1; }
669 #define FLAGS_CTOR_NF
670 #define FLAGS_CTOR_TF
671 #define FLAGS_CTOR_CF
672 #define FLAGS_CTOR_FF
673 #define FLAGS_CTOR_CF_TF
674 #define FLAGS_CTOR_CF_FF
676 #define FLAGS_INIT_NF
677 #define FLAGS_INIT_TF
678 #define FLAGS_INIT_CF
679 #define FLAGS_INIT_FF
680 #define FLAGS_INIT_CF_TF
681 #define FLAGS_INIT_CF_FF
683 #define O(opcode, imms, inputs, outputs, flags) \
685 static constexpr Op op = Op::opcode; \
686 explicit opcode ( IMM_CTOR_##imms \
687 FLAGS_CTOR_##flags) \
698 bool operator==(const opcode& o) const { \
699 return equals(o, detail::eq_default{}); \
702 bool operator!=(const opcode& o) const { \
703 return !(*this == o); \
707 bool equals(const opcode& o, E e) const { \
708 return IMM_EQ_WRAP(e, IMM_EQ_##imms); \
711 size_t hash() const { \
712 return IMM_HASH_WRAP( \
713 detail::hasher_default{}, \
718 size_t hash(H h) const { \
719 return IMM_HASH_WRAP(h, IMM_HASH_##imms); \
722 template <typename F> \
723 void forEachTarget(F&& f) { \
726 template <typename F> \
727 void forEachTarget(F&& f) const { \
745 #undef FLAGS_CTOR_CF_TF
746 #undef FLAGS_CTOR_CF_FF
752 #undef FLAGS_INIT_CF_TF
753 #undef FLAGS_INIT_CF_FF
760 #undef PUSH_CALLNATIVE
777 #undef POP_CALLNATIVE
800 // These are deliberately not undefined, so they can be used in other
802 // #undef IMM_NAME_BLA
803 // #undef IMM_NAME_SLA
804 // #undef IMM_NAME_ILA
805 // #undef IMM_NAME_IVA
806 // #undef IMM_NAME_I64A
807 // #undef IMM_NAME_LA
808 // #undef IMM_NAME_IA
809 // #undef IMM_NAME_DA
810 // #undef IMM_NAME_SA
811 // #undef IMM_NAME_RATA
812 // #undef IMM_NAME_AA
813 // #undef IMM_NAME_BA
814 // #undef IMM_NAME_OA
815 // #undef IMM_NAME_OA_IMPL
816 // #undef IMM_NAME_LAR
817 // #undef IMM_NAME_FCA
819 #undef IMM_TARGETS_BLA
820 #undef IMM_TARGETS_SLA
821 #undef IMM_TARGETS_ILA
822 #undef IMM_TARGETS_IVA
823 #undef IMM_TARGETS_I64A
824 #undef IMM_TARGETS_LA
825 #undef IMM_TARGETS_IA
826 #undef IMM_TARGETS_DA
827 #undef IMM_TARGETS_SA
828 #undef IMM_TARGETS_RATA
829 #undef IMM_TARGETS_AA
830 #undef IMM_TARGETS_BA
831 #undef IMM_TARGETS_OA
832 #undef IMM_TARGETS_KA
833 #undef IMM_TARGETS_LAR
834 #undef IMM_TARGETS_ITA
835 #undef IMM_TARGETS_FCA
837 #undef IMM_TARGETS_NA
838 #undef IMM_TARGETS_ONE
839 #undef IMM_TARGETS_TWO
840 #undef IMM_TARGETS_THREE
841 #undef IMM_TARGETS_FOUR
842 #undef IMM_TARGETS_FIVE
843 #undef IMM_TARGETS_SIX
849 #undef IMM_EXTRA_I64A
854 #undef IMM_EXTRA_RATA
886 #undef IMM_HASH_THREE
895 #undef IMM_CTOR_THREE
904 #undef IMM_INIT_THREE
909 //////////////////////////////////////////////////////////////////////
912 * Bytecode is a tagged-union that can hold any HHBC opcode struct
913 * defined above. You can visit a bytecode with a static_visitor
914 * using visit(), defined below.
916 * Each Bytecode also carries a corresponding SrcLoc that indicates
917 * which line of PHP code the bytecode was generated for.
920 // Default construction creates a Nop.
926 #define O(opcode, ...) \
927 /* implicit */ Bytecode(bc::opcode data) \
930 new (&opcode) bc::opcode(std::move(data)); \
937 // Note: assuming bc::Nop is empty and has trivial dtor/ctor.
939 Bytecode(const Bytecode
& o
) : op(Op::Nop
) { *this = o
; }
940 Bytecode(Bytecode
&& o
) noexcept
: op(Op::Nop
) { *this = std::move(o
); }
942 Bytecode
& operator=(const Bytecode
& o
) {
946 #define O(opcode, ...) \
947 case Op::opcode: new (&opcode) bc::opcode(o.opcode); break;
948 switch (o
.op
) { OPCODES
}
954 Bytecode
& operator=(Bytecode
&& o
) {
957 #define O(opcode, ...) \
958 case Op::opcode: new (&opcode) bc::opcode(std::move(o.opcode)); break;
959 switch (o
.op
) { OPCODES
}
965 ~Bytecode() { destruct(); }
967 uint32_t numPop() const {
968 #define O(opcode, ...) \
969 case Op::opcode: return opcode.numPop();
970 switch (op
) { OPCODES
}
975 Flavor
popFlavor(uint32_t i
) const {
976 #define O(opcode, ...) \
977 case Op::opcode: return opcode.popFlavor(i);
978 switch (op
) { OPCODES
}
983 uint32_t numPush() const {
984 #define O(opcode, ...) \
985 case Op::opcode: return opcode.numPush();
986 switch (op
) { OPCODES
}
991 template <typename F
>
992 void forEachTarget(F
&& f
) const {
993 #define O(opcode, ...) \
994 case Op::opcode: return opcode.forEachTarget(std::forward<F>(f));
995 switch (op
) { OPCODES
}
1000 template <typename F
>
1001 void forEachTarget(F
&& f
) {
1002 #define O(opcode, ...) \
1003 case Op::opcode: return opcode.forEachTarget(std::forward<F>(f));
1004 switch (op
) { OPCODES
}
1012 #define O(opcode, ...) bc::opcode opcode;
1019 #define O(opcode, ...) \
1021 { typedef bc::opcode X; \
1022 this->opcode.~X(); } \
1030 //////////////////////////////////////////////////////////////////////
1032 inline bool operator==(const Bytecode
& a
, const Bytecode
& b
) {
1033 if (a
.op
!= b
.op
) return false;
1034 #define O(opcode, ...) case Op::opcode: return a.opcode == b.opcode;
1035 switch (a
.op
) { OPCODES
}
1040 inline bool operator!=(const Bytecode
& a
, const Bytecode
& b
) {
1045 inline bool equals(const Bytecode
& a
, const Bytecode
& b
, E equals
) {
1046 if (a
.op
!= b
.op
) return false;
1047 #define O(opcode, ...) \
1048 case Op::opcode: return a.opcode.equals(b.opcode, equals);
1049 switch (a
.op
) { OPCODES
}
1055 inline size_t hash(const Bytecode
& b
, H hasher
) {
1056 auto hash
= 14695981039346656037ULL;
1057 auto o
= static_cast<size_t>(b
.op
);
1059 #define O(opcode, ...) \
1061 return folly::hash::hash_combine(b.opcode.hash(hasher), hash);
1062 switch (b
.op
) { OPCODES
}
1067 inline size_t hash(const Bytecode
& b
) {
1068 return hash(b
, bc::detail::hasher_default
{});
1071 //////////////////////////////////////////////////////////////////////
1074 * Helper for making a Bytecode with a given srcLoc.
1077 * auto b = bc_with_loc(something.srcLoc, bc::Nop {});
1079 template<class T
> Bytecode
bc_with_loc(int32_t loc
, const T
& t
) {
1085 //////////////////////////////////////////////////////////////////////
1088 * Visit a bytecode using a StaticVisitor, similar to
1089 * boost::apply_visitor or match().
1091 * The `v' argument should be a function object that accepts a call
1092 * operator for all the bc::Foo types. Its result type should be
1093 * independent of bytecode, but may vary with constness.
1095 template<class Visit
>
1096 auto visit(Bytecode
& b
, Visit v
) {
1097 #define O(opcode, ...) case Op::opcode: return v(b.opcode);
1098 switch (b
.op
) { OPCODES
}
1103 template<class Visit
>
1104 auto visit(const Bytecode
& b
, Visit v
) {
1105 #define O(opcode, ...) case Op::opcode: return v(b.opcode);
1106 switch (b
.op
) { OPCODES
}
1111 //////////////////////////////////////////////////////////////////////