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
) {}
114 explicit FCallArgs(Flags flags
, uint32_t numArgs
, uint32_t numRets
,
115 std::unique_ptr
<uint8_t[]> byRefs
,
116 BlockId asyncEagerTarget
)
117 : FCallArgsBase(flags
, numArgs
, numRets
)
118 , asyncEagerTarget(asyncEagerTarget
)
119 , byRefs(std::move(byRefs
)) {
120 assertx(IMPLIES(asyncEagerTarget
== NoBlockId
,
121 !supportsAsyncEagerReturn()));
123 FCallArgs(const FCallArgs
& o
)
124 : FCallArgs(o
.flags
, o
.numArgs
, o
.numRets
, nullptr, o
.asyncEagerTarget
) {
126 auto const numBytes
= (numArgs
+ 7) / 8;
127 byRefs
= std::make_unique
<uint8_t[]>(numBytes
);
128 memcpy(byRefs
.get(), o
.byRefs
.get(), numBytes
);
131 FCallArgs(FCallArgs
&& o
)
132 : FCallArgs(o
.flags
, o
.numArgs
, o
.numRets
, std::move(o
.byRefs
),
133 o
.asyncEagerTarget
) {}
135 bool enforceReffiness() const { return byRefs
.get() != nullptr; }
136 bool byRef(uint32_t i
) const {
137 assertx(enforceReffiness());
138 return byRefs
[i
/ 8] & (1 << (i
% 8));
140 BlockId asyncEagerTarget
;
141 std::unique_ptr
<uint8_t[]> byRefs
;
144 inline bool operator==(const FCallArgs
& a
, const FCallArgs
& b
) {
145 auto const eq
= [&] (uint8_t* a
, uint8_t* b
, uint32_t bytes
) {
146 if (a
== nullptr && b
== nullptr) return true;
147 if (a
== nullptr || b
== nullptr) return false;
148 return memcmp(a
, b
, bytes
) == 0;
152 a
.flags
== b
.flags
&& a
.numArgs
== b
.numArgs
&& a
.numRets
== b
.numRets
&&
153 eq(a
.byRefs
.get(), b
.byRefs
.get(), (a
.numArgs
+ 7 / 8)) &&
154 a
.asyncEagerTarget
== b
.asyncEagerTarget
;
157 inline bool operator!=(const FCallArgs
& a
, const FCallArgs
& b
) {
167 inline bool operator==(const IterTabEnt
& a
, const IterTabEnt
& b
) {
168 return std::tie(a
.kind
, a
.id
, a
.local
) == std::tie(b
.kind
, b
.id
, b
.local
);
171 inline bool operator!=(const IterTabEnt
& a
, const IterTabEnt
& b
) {
175 using IterTab
= CompactVector
<IterTabEnt
>;
177 using SwitchTab
= CompactVector
<BlockId
>;
179 // The final entry in the SSwitchTab is the default case, it will
180 // always have a nullptr for the string.
181 using SSwitchTabEnt
= std::pair
<LSString
,BlockId
>;
182 using SSwitchTab
= CompactVector
<SSwitchTabEnt
>;
184 //////////////////////////////////////////////////////////////////////
188 //////////////////////////////////////////////////////////////////////
193 * Trivial hasher overrides, which will cause bytecodes to hash according to
194 * the hashes defined by hasher_impl.
196 struct hasher_default
{};
199 * Default bytecode immediate hashers.
202 static size_t hash(SString s
) { return s
->hash(); }
203 static size_t hash(LSString s
) { return s
->hash(); }
204 static size_t hash(RepoAuthType rat
) { return rat
.hash(); }
206 static size_t hash(const IterTabEnt
& iterTab
) {
207 auto const partial
= folly::hash::hash_128_to_64(
208 iterTab
.kind
, iterTab
.id
210 return static_cast<size_t>(
211 folly::hash::hash_128_to_64(iterTab
.local
, partial
)
215 static size_t hash(MKey mkey
) {
216 return HPHP::hash_int64_pair(mkey
.mcode
, mkey
.int64
);
220 static size_t hash(const CompactVector
<T
>& v
) {
221 return v
.empty() ? 0 : v
.size() ^ hash(v
.front());
224 static size_t hash(std::pair
<LSString
,BlockId
> kv
) {
225 return HPHP::hash_int64_pair(kv
.first
->hash(), kv
.second
);
228 static size_t hash(LocalRange range
) {
229 return HPHP::hash_int64_pair(range
.first
, range
.count
);
232 static size_t hash(FCallArgs fca
) {
233 uint64_t hash
= HPHP::hash_int64_pair(fca
.numArgs
, fca
.numRets
);
234 hash
= HPHP::hash_int64_pair(hash
, fca
.flags
);
236 auto const br
= reinterpret_cast<char*>(fca
.byRefs
.get());
237 auto const hash_br
= hash_string_cs(br
, (fca
.numArgs
+ 7 / 8));
238 hash
= HPHP::hash_int64_pair(hash
, hash_br
);
240 hash
= HPHP::hash_int64_pair(hash
, fca
.asyncEagerTarget
);
241 return static_cast<size_t>(hash
);
245 static typename
std::enable_if
<
246 std::is_enum
<T
>::value
,
249 using U
= typename
std::underlying_type
<T
>::type
;
250 return std::hash
<U
>()(static_cast<U
>(t
));
254 static typename
std::enable_if
<
255 !std::is_enum
<T
>::value
,
257 >::type
hash(const T
& t
) { return std::hash
<T
>()(t
); }
261 * Hash operand wrapper.
263 template<typename T
, typename S
>
264 struct hash_operand
{ const T
& val
; S type
; };
266 // this template isn't really needed. its a workaround for T44007494
267 template<typename S
> struct hash_operand
<void*, S
> { void* const val
; S type
; };
270 * Hash T using H::operator() if it is compatible, else fall back to
271 * hasher_impl (e.g., if H := hasher_default).
273 template<typename T
, typename S
, class H
>
274 auto hash(const hash_operand
<T
,S
>& t
, H h
) -> decltype(h(t
.val
)) {
277 template<typename T
, typename S
, class H
>
278 auto hash(const hash_operand
<T
,S
>& t
, H h
) -> decltype(h(t
.val
, t
.type
)) {
279 return h(t
.val
, t
.type
);
281 template<typename T
, typename S
, typename
... Unused
>
282 typename
std::enable_if
<sizeof...(Unused
) == 1, size_t>::type
283 hash(const hash_operand
<T
,S
>& t
, Unused
...) {
284 return hasher_impl::hash(t
.val
);
288 * Clone of folly::hash::hash_combine_generic.
291 size_t hash_combine(H
/*h*/) {
295 template<class H
, typename T
, typename
... Ts
>
296 size_t hash_combine(H h
, const T
& t
, const Ts
&... ts
) {
297 auto const seed
= size_t{hash(t
, h
)};
298 if (sizeof...(ts
) == 0) return seed
;
300 auto const remainder
= hash_combine(h
, ts
...);
301 return static_cast<size_t>(folly::hash::hash_128_to_64(seed
, remainder
));
305 * Trivial equality overrides, which will cause bytecodes to compare via
306 * operator==() on the various immediate types.
308 struct eq_default
{};
311 * Equality operand wrapper.
313 template<typename T
, typename S
>
314 struct eq_operand
{ const T
& l
; const T
& r
; S type
; };
316 // this template isn't really needed. its a workaround for T44007494
317 template<typename S
> struct eq_operand
<void*, S
> {
318 void* const l
; void* const r
; S type
;
322 * Compare two values, using E::operator() if it exists, else the default
325 template<typename T
, typename S
, class E
>
326 auto equals(const eq_operand
<T
,S
>& t
, E e
) -> decltype(e(t
.l
, t
.r
)) {
329 template<typename T
, typename S
, class E
>
330 auto equals(const eq_operand
<T
,S
>& t
, E e
) -> decltype(e(t
.l
, t
.r
, t
.type
)) {
331 return e(t
.l
, t
.r
, t
.type
);
333 template<typename T
, typename S
, typename
... Unused
>
334 typename
std::enable_if
<sizeof...(Unused
) == 1, bool>::type
335 equals(const eq_operand
<T
,S
>& t
, Unused
...) {
340 * Check if a list of eq_operands are pairwise-equal.
343 bool eq_pairs(E
/*e*/) {
347 template<class E
, typename T
, typename
... Ts
>
348 bool eq_pairs(E e
, const T
& t
, const Ts
&... ts
) {
349 return equals(t
, e
) && (sizeof...(ts
) == 0 || eq_pairs(e
, ts
...));
354 //////////////////////////////////////////////////////////////////////
357 * Bytecode immediate type tags.
360 #define ARGTYPE(name, type) enum class name : uint8_t {};
361 #define ARGTYPEVEC(name, type) enum class name : uint8_t {};
367 #define IMM_ID_BLA BLA
368 #define IMM_ID_SLA SLA
369 #define IMM_ID_ILA ILA
370 #define IMM_ID_I32LA I32LA
371 #define IMM_ID_IVA IVA
372 #define IMM_ID_I64A I64A
375 #define IMM_ID_CAR CAR
376 #define IMM_ID_CAW CAW
379 #define IMM_ID_RATA RATA
382 #define IMM_ID_OA(type) OA
383 #define IMM_ID_VSA VSA
385 #define IMM_ID_LAR LAR
386 #define IMM_ID_FCA FCA
388 #define IMM_TY_BLA SwitchTab
389 #define IMM_TY_SLA SSwitchTab
390 #define IMM_TY_ILA IterTab
391 #define IMM_TY_I32LA CompactVector<uint32_t>
392 #define IMM_TY_IVA uint32_t
393 #define IMM_TY_I64A int64_t
394 #define IMM_TY_LA LocalId
395 #define IMM_TY_IA IterId
396 #define IMM_TY_CAR ClsRefSlotId
397 #define IMM_TY_CAW ClsRefSlotId
398 #define IMM_TY_DA double
399 #define IMM_TY_SA LSString
400 #define IMM_TY_RATA RepoAuthType
401 #define IMM_TY_AA SArray
402 #define IMM_TY_BA BlockId
403 #define IMM_TY_OA(type) type
404 #define IMM_TY_VSA CompactVector<LSString>
405 #define IMM_TY_KA MKey
406 #define IMM_TY_LAR LocalRange
407 #define IMM_TY_FCA FCallArgs
409 #define IMM_NAME_BLA(n) targets
410 #define IMM_NAME_SLA(n) targets
411 #define IMM_NAME_ILA(n) iterTab
412 #define IMM_NAME_I32LA(n) argv
413 #define IMM_NAME_IVA(n) arg##n
414 #define IMM_NAME_I64A(n) arg##n
415 #define IMM_NAME_LA(n) loc##n
416 #define IMM_NAME_IA(n) iter##n
417 #define IMM_NAME_CAR(n) slot
418 #define IMM_NAME_CAW(n) slot
419 #define IMM_NAME_DA(n) dbl##n
420 #define IMM_NAME_SA(n) str##n
421 #define IMM_NAME_RATA(n) rat
422 #define IMM_NAME_AA(n) arr##n
423 #define IMM_NAME_BA(n) target##n
424 #define IMM_NAME_OA_IMPL(n) subop##n
425 #define IMM_NAME_OA(type) IMM_NAME_OA_IMPL
426 #define IMM_NAME_VSA(n) keys
427 #define IMM_NAME_KA(n) mkey
428 #define IMM_NAME_LAR(n) locrange
429 #define IMM_NAME_FCA(n) fca
431 #define IMM_TARGETS_BLA(n) for (auto& t : targets) f(t);
432 #define IMM_TARGETS_SLA(n) for (auto& kv : targets) f(kv.second);
433 #define IMM_TARGETS_ILA(n)
434 #define IMM_TARGETS_I32LA(n)
435 #define IMM_TARGETS_IVA(n)
436 #define IMM_TARGETS_I64A(n)
437 #define IMM_TARGETS_LA(n)
438 #define IMM_TARGETS_IA(n)
439 #define IMM_TARGETS_CAR(n)
440 #define IMM_TARGETS_CAW(n)
441 #define IMM_TARGETS_DA(n)
442 #define IMM_TARGETS_SA(n)
443 #define IMM_TARGETS_RATA(n)
444 #define IMM_TARGETS_AA(n)
445 #define IMM_TARGETS_BA(n) f(target##n);
446 #define IMM_TARGETS_OA_IMPL(n)
447 #define IMM_TARGETS_OA(type) IMM_TARGETS_OA_IMPL
448 #define IMM_TARGETS_VSA(n)
449 #define IMM_TARGETS_KA(n)
450 #define IMM_TARGETS_LAR(n)
451 #define IMM_TARGETS_FCA(n) if (fca.asyncEagerTarget != NoBlockId) { \
452 f(fca.asyncEagerTarget); \
455 #define IMM_EXTRA_BLA
456 #define IMM_EXTRA_SLA
457 #define IMM_EXTRA_ILA
458 #define IMM_EXTRA_I32LA
459 #define IMM_EXTRA_IVA
460 #define IMM_EXTRA_I64A
463 #define IMM_EXTRA_CAR using has_car_flag = std::true_type;
464 #define IMM_EXTRA_CAW using has_caw_flag = std::true_type;
467 #define IMM_EXTRA_RATA
470 #define IMM_EXTRA_OA(x)
471 #define IMM_EXTRA_VSA
473 #define IMM_EXTRA_LAR
474 #define IMM_EXTRA_FCA
476 #define IMM_MEM(which, n) IMM_TY_##which IMM_NAME_##which(n)
478 #define IMM_MEM_ONE(x) IMM_MEM(x, 1);
479 #define IMM_MEM_TWO(x, y) IMM_MEM(x, 1); IMM_MEM(y, 2);
480 #define IMM_MEM_THREE(x, y, z) IMM_MEM(x, 1); IMM_MEM(y, 2); \
482 #define IMM_MEM_FOUR(x, y, z, l) IMM_MEM(x, 1); IMM_MEM(y, 2); \
483 IMM_MEM(z, 3); IMM_MEM(l, 4);
484 #define IMM_MEM_FIVE(x, y, z, l, m) IMM_MEM(x, 1); IMM_MEM(y, 2); \
485 IMM_MEM(z, 3); IMM_MEM(l, 4); \
488 #define IMM_EQ_WRAP(e, ...) detail::eq_pairs(e, __VA_ARGS__)
489 #define IMM_EQ(which, n) detail::eq_operand< \
491 imm::IMM_ID_##which \
493 IMM_NAME_##which(n), \
494 o.IMM_NAME_##which(n) \
496 #define IMM_EQ_NA detail::eq_operand<void*,imm::NA> { 0, 0 }
497 #define IMM_EQ_ONE(x) IMM_EQ(x, 1)
498 #define IMM_EQ_TWO(x, y) IMM_EQ_ONE(x), IMM_EQ(y, 2)
499 #define IMM_EQ_THREE(x, y, z) IMM_EQ_TWO(x, y), IMM_EQ(z, 3)
500 #define IMM_EQ_FOUR(x, y, z, l) IMM_EQ_THREE(x, y, z), IMM_EQ(l, 4)
501 #define IMM_EQ_FIVE(x, y, z, l, m) IMM_EQ_FOUR(x, y, z, l), IMM_EQ(m, 5)
503 #define IMM_HASH_WRAP(h, ...) detail::hash_combine(h, __VA_ARGS__)
504 #define IMM_HASH(which, n) detail::hash_operand< \
506 imm::IMM_ID_##which \
507 > { IMM_NAME_##which(n) }
508 #define IMM_HASH_NA detail::hash_operand<void*,imm::NA> { 0 }
509 #define IMM_HASH_ONE(x) IMM_HASH(x, 1)
510 #define IMM_HASH_TWO(x, y) IMM_HASH_ONE(x), IMM_HASH(y, 2)
511 #define IMM_HASH_THREE(x, y, z) IMM_HASH_TWO(x, y), IMM_HASH(z, 3)
512 #define IMM_HASH_FOUR(x, y, z, l) IMM_HASH_THREE(x, y, z), IMM_HASH(l, 4)
513 #define IMM_HASH_FIVE(x, y, z, l, m) IMM_HASH_FOUR(x, y, z, l), IMM_HASH(m, 5)
515 #define IMM_TARGETS_NA
516 #define IMM_TARGETS_ONE(x) IMM_TARGETS_##x(1)
517 #define IMM_TARGETS_TWO(x,y) IMM_TARGETS_ONE(x) IMM_TARGETS_##y(2)
518 #define IMM_TARGETS_THREE(x,y,z) IMM_TARGETS_TWO(x,y) IMM_TARGETS_##z(3)
519 #define IMM_TARGETS_FOUR(x,y,z,l) IMM_TARGETS_THREE(x,y,z) IMM_TARGETS_##l(4)
520 #define IMM_TARGETS_FIVE(x,y,z,l,m) IMM_TARGETS_FOUR(x,y,z,l) IMM_TARGETS_##m(5)
523 #define IMM_EXTRA_ONE(x) IMM_EXTRA_##x
524 #define IMM_EXTRA_TWO(x,y) IMM_EXTRA_ONE(x) IMM_EXTRA_ONE(y)
525 #define IMM_EXTRA_THREE(x,y,z) IMM_EXTRA_TWO(x,y) IMM_EXTRA_ONE(z)
526 #define IMM_EXTRA_FOUR(x,y,z,l) IMM_EXTRA_THREE(x,y,z) IMM_EXTRA_ONE(l)
527 #define IMM_EXTRA_FIVE(x,y,z,l,m) IMM_EXTRA_FOUR(x,y,z,l) IMM_EXTRA_ONE(m)
529 #define IMM_CTOR(which, n) IMM_TY_##which IMM_NAME_##which(n)
531 #define IMM_CTOR_ONE(x) IMM_CTOR(x, 1)
532 #define IMM_CTOR_TWO(x, y) IMM_CTOR(x, 1), IMM_CTOR(y, 2)
533 #define IMM_CTOR_THREE(x, y, z) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
535 #define IMM_CTOR_FOUR(x, y, z, l) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
536 IMM_CTOR(z, 3), IMM_CTOR(l, 4)
537 #define IMM_CTOR_FIVE(x, y, z, l, m) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
538 IMM_CTOR(z, 3), IMM_CTOR(l, 4), \
541 #define IMM_INIT(which, n) IMM_NAME_##which(n) \
542 ( std::move(IMM_NAME_##which(n)) )
544 #define IMM_INIT_ONE(x) : IMM_INIT(x, 1)
545 #define IMM_INIT_TWO(x, y) : IMM_INIT(x, 1), IMM_INIT(y, 2)
546 #define IMM_INIT_THREE(x, y, z) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
548 #define IMM_INIT_FOUR(x, y, z, l) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
549 IMM_INIT(z, 3), IMM_INIT(l, 4)
550 #define IMM_INIT_FIVE(x, y, z, l, m) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
551 IMM_INIT(z, 3), IMM_INIT(l, 4), \
554 #define POP_UV if (i == 0) return Flavor::U
555 #define POP_CV if (i == 0) return Flavor::C
556 #define POP_VV if (i == 0) return Flavor::V
557 #define POP_CUV if (i == 0) return Flavor::CU
559 #define POP_NOV uint32_t numPop() const { return 0; } \
560 Flavor popFlavor(uint32_t) const { not_reached(); }
562 #define POP_ONE(x) uint32_t numPop() const { return 1; } \
563 Flavor popFlavor(uint32_t i) const { \
564 POP_##x; not_reached(); \
567 #define POP_TWO(x, y) uint32_t numPop() const { return 2; } \
568 Flavor popFlavor(uint32_t i) const { \
569 POP_##x; --i; POP_##y; not_reached(); \
572 #define POP_THREE(x, y, z) uint32_t numPop() const { return 3; } \
573 Flavor popFlavor(uint32_t i) const { \
574 POP_##x; --i; POP_##y; --i; POP_##z; \
578 #define POP_MFINAL uint32_t numPop() const { return arg1; } \
579 Flavor popFlavor(uint32_t) const { not_reached(); }
581 #define POP_C_MFINAL(n) uint32_t numPop() const { return arg1 + n; } \
582 Flavor popFlavor(uint32_t) const { not_reached(); }
584 #define POP_CMANY uint32_t numPop() const { return arg1; } \
585 Flavor popFlavor(uint32_t i) const { \
586 assert(i < numPop()); \
590 #define POP_SMANY uint32_t numPop() const { return keys.size(); } \
591 Flavor popFlavor(uint32_t i) const { \
592 assert(i < numPop()); \
596 #define POP_CUMANY uint32_t numPop() const { return arg1; } \
597 Flavor popFlavor(uint32_t i) const { \
598 assert(i < numPop()); \
602 #define POP_CVUMANY uint32_t numPop() const { return arg1; } \
603 Flavor popFlavor(uint32_t i) const { \
604 assert(i < numPop()); \
605 return Flavor::CVU; \
608 #define POP_FPUSH(nin, nobj) \
609 uint32_t numPop() const { return arg1 + nin + 3; } \
610 Flavor popFlavor(uint32_t i) const { \
611 assert(i < numPop()); \
612 if (i < nin) return Flavor::C; \
614 if (i < arg1) return Flavor::CV; \
616 if (i < 2) return Flavor::U; \
617 return nobj ? Flavor::C : Flavor::U; \
620 #define POP_FCALL uint32_t numPop() const { \
621 return fca.numArgs + (fca.hasUnpack() ? 1 : 0) + \
624 Flavor popFlavor(uint32_t i) const { \
625 assert(i < numPop()); \
626 if (i == 0 && fca.hasUnpack()) return Flavor::C; \
627 auto const cv = fca.numArgs + (fca.hasUnpack() ? 1 : 0); \
628 return i < cv ? Flavor::CV : Flavor::U; \
631 #define PUSH_NOV uint32_t numPush() const { return 0; }
633 #define PUSH_ONE(x) uint32_t numPush() const { return 1; }
635 #define PUSH_TWO(x, y) uint32_t numPush() const { return 2; }
637 #define PUSH_FPUSH uint32_t numPush() const { return arg1; }
638 #define PUSH_FCALL uint32_t numPush() const { return fca.numRets; }
644 #define FLAGS_PF bool has_unpack;
648 #define FLAGS_CTOR_NF
649 #define FLAGS_CTOR_TF
650 #define FLAGS_CTOR_CF
651 #define FLAGS_CTOR_FF
652 #define FLAGS_CTOR_PF ,bool hu
653 #define FLAGS_CTOR_CF_TF
654 #define FLAGS_CTOR_CF_FF
656 #define FLAGS_INIT_NF
657 #define FLAGS_INIT_TF
658 #define FLAGS_INIT_CF
659 #define FLAGS_INIT_FF
660 #define FLAGS_INIT_PF ,has_unpack(hu)
661 #define FLAGS_INIT_CF_TF
662 #define FLAGS_INIT_CF_FF
664 #define O(opcode, imms, inputs, outputs, flags) \
666 static constexpr Op op = Op::opcode; \
667 explicit opcode ( IMM_CTOR_##imms \
668 FLAGS_CTOR_##flags) \
679 bool operator==(const opcode& o) const { \
680 return equals(o, detail::eq_default{}); \
683 bool operator!=(const opcode& o) const { \
684 return !(*this == o); \
688 bool equals(const opcode& o, E e) const { \
689 return IMM_EQ_WRAP(e, IMM_EQ_##imms); \
692 size_t hash() const { \
693 return IMM_HASH_WRAP( \
694 detail::hasher_default{}, \
699 size_t hash(H h) const { \
700 return IMM_HASH_WRAP(h, IMM_HASH_##imms); \
703 template <typename F> \
704 void forEachTarget(F&& f) { \
707 template <typename F> \
708 void forEachTarget(F&& f) const { \
728 #undef FLAGS_CTOR_CF_TF
729 #undef FLAGS_CTOR_CF_FF
736 #undef FLAGS_INIT_CF_TF
737 #undef FLAGS_INIT_CF_FF
784 // These are deliberately not undefined, so they can be used in other
786 // #undef IMM_NAME_BLA
787 // #undef IMM_NAME_SLA
788 // #undef IMM_NAME_ILA
789 // #undef IMM_NAME_I32LA
790 // #undef IMM_NAME_IVA
791 // #undef IMM_NAME_I64A
792 // #undef IMM_NAME_LA
793 // #undef IMM_NAME_IA
794 // #undef IMM_NAME_CAR
795 // #undef IMM_NAME_CAW
796 // #undef IMM_NAME_DA
797 // #undef IMM_NAME_SA
798 // #undef IMM_NAME_RATA
799 // #undef IMM_NAME_AA
800 // #undef IMM_NAME_BA
801 // #undef IMM_NAME_OA
802 // #undef IMM_NAME_OA_IMPL
803 // #undef IMM_NAME_LAR
804 // #undef IMM_NAME_FCA
806 #undef IMM_TARGETS_BLA
807 #undef IMM_TARGETS_SLA
808 #undef IMM_TARGETS_ILA
809 #undef IMM_TARGETS_I32LA
810 #undef IMM_TARGETS_IVA
811 #undef IMM_TARGETS_I64A
812 #undef IMM_TARGETS_LA
813 #undef IMM_TARGETS_IA
814 #undef IMM_TARGETS_CAR
815 #undef IMM_TARGETS_CAW
816 #undef IMM_TARGETS_DA
817 #undef IMM_TARGETS_SA
818 #undef IMM_TARGETS_RATA
819 #undef IMM_TARGETS_AA
820 #undef IMM_TARGETS_BA
821 #undef IMM_TARGETS_OA
822 #undef IMM_TARGETS_KA
823 #undef IMM_TARGETS_LAR
824 #undef IMM_TARGETS_FCA
826 #undef IMM_TARGETS_NA
827 #undef IMM_TARGETS_ONE
828 #undef IMM_TARGETS_TWO
829 #undef IMM_TARGETS_THREE
830 #undef IMM_TARGETS_FOUR
831 #undef IMM_TARGETS_FIVE
836 #undef IMM_EXTRA_I32LA
838 #undef IMM_EXTRA_I64A
845 #undef IMM_EXTRA_RATA
874 #undef IMM_HASH_THREE
882 #undef IMM_CTOR_THREE
890 #undef IMM_INIT_THREE
895 //////////////////////////////////////////////////////////////////////
898 * Bytecode is a tagged-union that can hold any HHBC opcode struct
899 * defined above. You can visit a bytecode with a static_visitor
900 * using visit(), defined below.
902 * Each Bytecode also carries a corresponding SrcLoc that indicates
903 * which line of PHP code the bytecode was generated for.
906 // Default construction creates a Nop.
912 #define O(opcode, ...) \
913 /* implicit */ Bytecode(bc::opcode data) \
916 new (&opcode) bc::opcode(std::move(data)); \
923 // Note: assuming bc::Nop is empty and has trivial dtor/ctor.
925 Bytecode(const Bytecode
& o
) : op(Op::Nop
) { *this = o
; }
926 Bytecode(Bytecode
&& o
) noexcept
: op(Op::Nop
) { *this = std::move(o
); }
928 Bytecode
& operator=(const Bytecode
& o
) {
932 #define O(opcode, ...) \
933 case Op::opcode: new (&opcode) bc::opcode(o.opcode); break;
934 switch (o
.op
) { OPCODES
}
940 Bytecode
& operator=(Bytecode
&& o
) {
943 #define O(opcode, ...) \
944 case Op::opcode: new (&opcode) bc::opcode(std::move(o.opcode)); break;
945 switch (o
.op
) { OPCODES
}
951 ~Bytecode() { destruct(); }
953 uint32_t numPop() const {
954 #define O(opcode, ...) \
955 case Op::opcode: return opcode.numPop();
956 switch (op
) { OPCODES
}
961 Flavor
popFlavor(uint32_t i
) const {
962 #define O(opcode, ...) \
963 case Op::opcode: return opcode.popFlavor(i);
964 switch (op
) { OPCODES
}
969 uint32_t numPush() const {
970 #define O(opcode, ...) \
971 case Op::opcode: return opcode.numPush();
972 switch (op
) { OPCODES
}
977 template <typename F
>
978 void forEachTarget(F
&& f
) const {
979 #define O(opcode, ...) \
980 case Op::opcode: return opcode.forEachTarget(std::forward<F>(f));
981 switch (op
) { OPCODES
}
986 template <typename F
>
987 void forEachTarget(F
&& f
) {
988 #define O(opcode, ...) \
989 case Op::opcode: return opcode.forEachTarget(std::forward<F>(f));
990 switch (op
) { OPCODES
}
998 #define O(opcode, ...) bc::opcode opcode;
1005 #define O(opcode, ...) \
1007 { typedef bc::opcode X; \
1008 this->opcode.~X(); } \
1016 //////////////////////////////////////////////////////////////////////
1018 inline bool operator==(const Bytecode
& a
, const Bytecode
& b
) {
1019 if (a
.op
!= b
.op
) return false;
1020 #define O(opcode, ...) case Op::opcode: return a.opcode == b.opcode;
1021 switch (a
.op
) { OPCODES
}
1026 inline bool operator!=(const Bytecode
& a
, const Bytecode
& b
) {
1031 inline bool equals(const Bytecode
& a
, const Bytecode
& b
, E equals
) {
1032 if (a
.op
!= b
.op
) return false;
1033 #define O(opcode, ...) \
1034 case Op::opcode: return a.opcode.equals(b.opcode, equals);
1035 switch (a
.op
) { OPCODES
}
1041 inline size_t hash(const Bytecode
& b
, H hasher
) {
1042 auto hash
= 14695981039346656037ULL;
1043 auto o
= static_cast<size_t>(b
.op
);
1045 #define O(opcode, ...) \
1047 return folly::hash::hash_combine(b.opcode.hash(hasher), hash);
1048 switch (b
.op
) { OPCODES
}
1053 inline size_t hash(const Bytecode
& b
) {
1054 return hash(b
, bc::detail::hasher_default
{});
1057 //////////////////////////////////////////////////////////////////////
1060 * Helper for making a Bytecode with a given srcLoc.
1063 * auto b = bc_with_loc(something.srcLoc, bc::Nop {});
1065 template<class T
> Bytecode
bc_with_loc(int32_t loc
, const T
& t
) {
1071 //////////////////////////////////////////////////////////////////////
1074 * Visit a bytecode using a StaticVisitor, similar to
1075 * boost::apply_visitor or match().
1077 * The `v' argument should be a function object that accepts a call
1078 * operator for all the bc::Foo types. Its result type should be
1079 * independent of bytecode, but may vary with constness.
1081 template<class Visit
>
1082 auto visit(Bytecode
& b
, Visit v
) {
1083 #define O(opcode, ...) case Op::opcode: return v(b.opcode);
1084 switch (b
.op
) { OPCODES
}
1089 template<class Visit
>
1090 auto visit(const Bytecode
& b
, Visit v
) {
1091 #define O(opcode, ...) case Op::opcode: return v(b.opcode);
1092 switch (b
.op
) { OPCODES
}
1097 //////////////////////////////////////////////////////////////////////
1099 struct ReadClsRefSlotVisitor
{
1100 ReadClsRefSlotVisitor() {}
1102 template<typename T
>
1103 auto fun(T
&, int) const { return NoClsRefSlotId
; }
1105 template<typename T
>
1106 auto fun(T
& t
, bool) const -> decltype(typename
T::has_car_flag
{},t
.slot
) {
1110 template<typename T
>
1111 ClsRefSlotId
operator()(T
& t
) const { return fun(t
, true); }
1114 struct WriteClsRefSlotVisitor
{
1115 template<typename T
>
1116 auto fun(T
&, int) const { return NoClsRefSlotId
; }
1118 template<typename T
>
1119 auto fun(T
& t
, bool) const -> decltype(typename
T::has_caw_flag
{},t
.slot
) {
1123 template<typename T
>
1124 ClsRefSlotId
operator()(T
& t
) const { return fun(t
, true); }
1127 //////////////////////////////////////////////////////////////////////