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 +----------------------------------------------------------------------+
20 #include <type_traits>
24 #include <folly/Hash.h>
26 #include "hphp/util/compact-vector.h"
27 #include "hphp/runtime/vm/hhbc.h"
29 #include "hphp/hhbbc/src-loc.h"
30 #include "hphp/hhbbc/misc.h"
31 #include "hphp/hhbbc/type-system.h"
33 namespace HPHP
{ namespace HHBBC
{
37 //////////////////////////////////////////////////////////////////////
40 * The following creates a struct for each bytecode using the opcode
41 * table. Each opcode will be named bc::opcode, and has a single
42 * constructor that takes its immediate types in order.
46 * auto pushInt = bc::Int { 2 }; // Push literal int
49 //////////////////////////////////////////////////////////////////////
53 : name(kInvalidLocalName
)
56 NamedLocal(LocalName name
, LocalId id
)
60 /* implicit */ NamedLocal(const ::HPHP::NamedLocal
& nl
)
64 /* implicit */ operator auto(){
65 return ::HPHP::NamedLocal
{name
, static_cast<int32_t>(id
)};
71 inline bool operator==(NamedLocal a
, NamedLocal b
) {
72 return a
.name
== b
.name
&& a
.id
== b
.id
;
75 inline bool operator!=(NamedLocal a
, NamedLocal b
) {
82 , rop
{ReadonlyOp::Any
}
86 MKey(MemberCode mcode
, NamedLocal local
, ReadonlyOp rop
)
92 MKey(MemberCode mcode
, int32_t idx
, ReadonlyOp rop
)
98 MKey(MemberCode mcode
, int64_t int64
, ReadonlyOp rop
)
104 MKey(MemberCode mcode
, SString litstr
, ReadonlyOp rop
)
120 inline bool operator==(MKey a
, MKey b
) {
121 return a
.mcode
== b
.mcode
&& a
.int64
== b
.int64
&& a
.rop
== b
.rop
;
124 inline bool operator!=(MKey a
, MKey b
) {
128 // A contiguous range of locals. The count is the number of locals including the
129 // first. If the range is empty, count will be zero and first's value is
136 inline bool operator==(const LocalRange
& a
, const LocalRange
& b
) {
137 return a
.first
== b
.first
&& a
.count
== b
.count
;
140 inline bool operator!=(const LocalRange
& a
, const LocalRange
& b
) {
144 struct FCallArgsLong
: FCallArgsBase
{
145 explicit FCallArgsLong(Flags flags
, uint32_t numArgs
, uint32_t numRets
,
146 std::unique_ptr
<uint8_t[]> inoutArgs
,
147 std::unique_ptr
<uint8_t[]> readonlyArgs
,
148 BlockId asyncEagerTarget
, LSString context
)
149 : FCallArgsBase(flags
, numArgs
, numRets
)
150 , inoutArgs(std::move(inoutArgs
))
151 , readonlyArgs(std::move(readonlyArgs
))
152 , asyncEagerTarget(asyncEagerTarget
)
155 FCallArgsLong(const FCallArgsLong
& o
)
156 : FCallArgsLong(o
.flags
, o
.numArgs
, o
.numRets
, nullptr, nullptr,
157 o
.asyncEagerTarget
, o
.context
) {
159 auto const numBytes
= (numArgs
+ 7) / 8;
160 inoutArgs
= std::make_unique
<uint8_t[]>(numBytes
);
161 memcpy(inoutArgs
.get(), o
.inoutArgs
.get(), numBytes
);
163 if (o
.readonlyArgs
) {
164 auto const numBytes
= (numArgs
+ 7) / 8;
165 readonlyArgs
= std::make_unique
<uint8_t[]>(numBytes
);
166 memcpy(readonlyArgs
.get(), o
.readonlyArgs
.get(), numBytes
);
170 bool enforceInOut() const { return inoutArgs
.get() != nullptr; }
171 bool isInOut(uint32_t i
) const {
172 assertx(enforceInOut());
173 return inoutArgs
[i
/ 8] & (1 << (i
% 8));
175 bool enforceReadonly() const { return readonlyArgs
.get() != nullptr; }
176 bool isReadonly(uint32_t i
) const {
177 assertx(enforceReadonly());
178 return readonlyArgs
[i
/ 8] & (1 << (i
% 8));
181 FCallArgsLong
withoutGenerics() const {
183 fca
.flags
= static_cast<Flags
>(fca
.flags
& ~Flags::HasGenerics
);
186 FCallArgsLong
withoutLockWhileUnwinding() const {
188 fca
.flags
= static_cast<Flags
>(fca
.flags
& ~Flags::LockWhileUnwinding
);
191 FCallArgsLong
withoutEnforceMutableReturn() const {
193 fca
.flags
= static_cast<Flags
>(fca
.flags
& ~Flags::EnforceMutableReturn
);
196 FCallArgsLong
withoutEnforceReadonlyThis() const {
198 fca
.flags
= static_cast<Flags
>(fca
.flags
& ~Flags::EnforceReadonlyThis
);
202 FCallArgsLong
withoutRepack() const {
204 fca
.flags
= static_cast<Flags
>(fca
.flags
| Flags::SkipRepack
);
208 FCallArgsLong
withoutCoeffectsCheck() const {
210 fca
.flags
= static_cast<Flags
>(fca
.flags
| Flags::SkipCoeffectsCheck
);
214 FCallArgsLong
withoutInOut() const {
216 fca
.inoutArgs
= nullptr;
220 FCallArgsLong
withoutReadonly() const {
222 fca
.readonlyArgs
= nullptr;
226 FCallArgsLong
withoutAsyncEagerTarget() const {
228 fca
.asyncEagerTarget
= NoBlockId
;
232 void applyIO(F f
) const {
233 f((numArgs
+ 7) / 8, inoutArgs
.get());
236 void applyReadonly(F f
) const {
237 f((numArgs
+ 7) / 8, readonlyArgs
.get());
239 friend bool operator==(const FCallArgsLong
& a
, const FCallArgsLong
& b
) {
240 auto const eq
= [&] (uint8_t* a
, uint8_t* b
, uint32_t bytes
) {
241 if (a
== nullptr && b
== nullptr) return true;
242 if (a
== nullptr || b
== nullptr) return false;
243 return memcmp(a
, b
, bytes
) == 0;
247 a
.flags
== b
.flags
&& a
.numArgs
== b
.numArgs
&& a
.numRets
== b
.numRets
&&
248 eq(a
.inoutArgs
.get(), b
.inoutArgs
.get(), (a
.numArgs
+ 7) / 8) &&
249 eq(a
.readonlyArgs
.get(), b
.readonlyArgs
.get(), (a
.numArgs
+ 7) / 8) &&
250 a
.asyncEagerTarget
== b
.asyncEagerTarget
&&
251 a
.context
== b
.context
;
254 size_t hash() const {
255 uint64_t hash
= HPHP::hash_int64_pair(numArgs
, numRets
);
256 hash
= HPHP::hash_int64_pair(hash
, flags
);
257 if (auto const br
= reinterpret_cast<const char*>(inoutArgs
.get())) {
258 auto const hash_br
= hash_string_cs(br
, (numArgs
+ 7) / 8);
259 hash
= HPHP::hash_int64_pair(hash
, hash_br
);
261 if (auto const br
= reinterpret_cast<const char*>(readonlyArgs
.get())) {
262 auto const hash_br
= hash_string_cs(br
, (numArgs
+ 7) / 8);
263 hash
= HPHP::hash_int64_pair(hash
, hash_br
);
265 hash
= HPHP::hash_int64_pair(hash
, asyncEagerTarget
);
266 if (context
) hash
= HPHP::hash_int64_pair(hash
, context
->hash());
267 return static_cast<size_t>(hash
);
270 uint32_t numPop() const {
271 return nin
+ numInputs() + 1 + numRets
;
273 template<int nin
, int nobj
>
274 Flavor
popFlavor(uint32_t i
) const {
275 assertx(i
< this->template numPop
<nin
>());
276 if (i
< nin
) return Flavor::C
;
279 if (i
== 0) return Flavor::C
;
283 if (i
== 0) return Flavor::C
;
286 if (i
< numArgs
) return Flavor::C
;
288 if (i
== 1 && nobj
) return Flavor::C
;
291 FCallArgsBase
base() const { return *this; }
293 std::unique_ptr
<uint8_t[]> inoutArgs
;
294 std::unique_ptr
<uint8_t[]> readonlyArgs
;
295 BlockId asyncEagerTarget
;
300 using Flags
= FCallArgsBase::Flags
;
301 explicit FCallArgs(uint32_t numArgs
)
302 : FCallArgs(Flags::None
, numArgs
, 1, nullptr, nullptr, NoBlockId
, nullptr) {}
303 FCallArgs(Flags flags
, uint32_t numArgs
, uint32_t numRets
,
304 std::unique_ptr
<uint8_t[]> inoutArgs
,
305 std::unique_ptr
<uint8_t[]> readonlyArgs
,
306 BlockId asyncEagerTarget
,
309 l
.emplace(flags
, numArgs
, numRets
,
310 std::move(inoutArgs
), std::move(readonlyArgs
),
311 asyncEagerTarget
, context
);
313 FCallArgs(const FCallArgsLong
& o
) : l
{o
} {}
314 FCallArgs(const FCallArgs
& o
) : l
{} {
321 bool enforceInOut() const {
322 return l
->enforceInOut();
324 bool isInOut(uint32_t i
) const {
325 assertx(enforceInOut());
326 return l
->isInOut(i
);
328 bool enforceReadonly() const {
329 return l
->enforceReadonly();
331 bool isReadonly(uint32_t i
) const {
332 assertx(enforceReadonly());
333 return l
->isReadonly(i
);
336 FCallArgs
withoutGenerics() const {
337 return l
->withoutGenerics();
339 FCallArgs
withoutEnforceMutableReturn() const {
340 return l
->withoutEnforceMutableReturn();
342 FCallArgs
withoutEnforceReadonlyThis() const {
343 return l
->withoutEnforceReadonlyThis();
346 FCallArgs
withoutInOut() const {
347 return l
->withoutInOut();
349 FCallArgs
withoutReadonly() const {
350 return l
->withoutReadonly();
352 FCallArgs
withoutLockWhileUnwinding() const {
353 return l
->withoutLockWhileUnwinding();
355 FCallArgs
withoutAsyncEagerTarget() const {
356 return l
->withoutAsyncEagerTarget();
358 FCallArgs
withoutRepack() const {
359 return l
->withoutRepack();
361 FCallArgs
withoutCoeffectsCheck() const {
362 return l
->withoutCoeffectsCheck();
365 FCallArgsBase
base() const {
369 friend bool operator==(const FCallArgs
& a
, const FCallArgs
& b
) {
370 if (a
.l
.get() == b
.l
.get()) return true;
374 size_t hash() const {
378 uint32_t numArgs() const {
382 uint32_t numRets() const {
386 const uint8_t* inoutArgs() const {
387 return l
->inoutArgs
.get();
390 void applyIO(F f
) const {
391 assertx(enforceInOut());
395 const uint8_t* readonlyArgs() const {
396 return l
->readonlyArgs
.get();
399 void applyReadonly(F f
) const {
400 assertx(enforceReadonly());
404 BlockId
asyncEagerTarget() const {
405 return l
->asyncEagerTarget
;
407 BlockId
& asyncEagerTarget() {
408 return l
.mutate()->asyncEagerTarget
;
410 bool hasUnpack() const {
411 return l
->hasUnpack();
413 bool hasGenerics() const {
414 return l
->hasGenerics();
416 bool enforceMutableReturn() const {
417 return l
->enforceMutableReturn();
419 bool enforceReadonlyThis() const {
420 return l
->enforceReadonlyThis();
422 bool lockWhileUnwinding() const {
423 return l
->lockWhileUnwinding();
425 uint32_t numInputs() const {
426 return l
->numInputs();
428 bool skipRepack() const {
429 return l
->skipRepack();
431 bool skipCoeffectsCheck() const {
432 return l
->skipCoeffectsCheck();
435 uint32_t numPop() const {
436 return l
->template numPop
<nin
>();
438 template<int nin
, int nobj
>
439 Flavor
popFlavor(uint32_t i
) const {
440 return l
->template popFlavor
<nin
,nobj
>(i
);
442 LSString
context() const { return l
->context
; }
445 copy_ptr
<FCallArgsLong
> l
;
450 inline bool operator!=(const FCallArgs
& a
, const FCallArgs
& b
) {
454 using SwitchTab
= CompactVector
<BlockId
>;
456 // The final entry in the SSwitchTab is the default case, it will
457 // always have a nullptr for the string.
458 using SSwitchTabEnt
= std::pair
<LSString
,BlockId
>;
459 using SSwitchTab
= CompactVector
<SSwitchTabEnt
>;
461 //////////////////////////////////////////////////////////////////////
465 //////////////////////////////////////////////////////////////////////
470 * Trivial hasher overrides, which will cause bytecodes to hash according to
471 * the hashes defined by hasher_impl.
473 struct hasher_default
{};
476 * Default bytecode immediate hashers.
479 static size_t hash(LSString s
) { return s
->hash(); }
480 static size_t hash(RepoAuthType rat
) { return rat
.hash(); }
482 static size_t hash(MKey mkey
) {
483 auto hash
= HPHP::hash_int64_pair(mkey
.mcode
, mkey
.int64
);
484 return HPHP::hash_int64_pair(hash
, (uint64_t)mkey
.rop
);
488 static size_t hash(const CompactVector
<T
>& v
) {
489 return v
.empty() ? 0 : v
.size() ^ hash(v
.front());
492 static size_t hash(std::pair
<LSString
,BlockId
> kv
) {
493 return HPHP::hash_int64_pair(kv
.first
->hash(), kv
.second
);
496 static size_t hash(NamedLocal loc
) {
497 return HPHP::hash_int64((((uint64_t)loc
.name
) << 32) | loc
.id
);
500 static size_t hash(LocalRange range
) {
501 return HPHP::hash_int64_pair(range
.first
, range
.count
);
504 static size_t hash(IterArgs ita
) {
505 auto hash
= HPHP::hash_int64_pair(ita
.flags
, ita
.iterId
);
506 hash
= HPHP::hash_int64_pair(hash
, ita
.keyId
);
507 return HPHP::hash_int64_pair(hash
, ita
.valId
);
510 static size_t hash(FCallArgs fca
) {
515 static typename
std::enable_if
<
516 std::is_enum
<T
>::value
,
519 using U
= typename
std::underlying_type
<T
>::type
;
520 return std::hash
<U
>()(static_cast<U
>(t
));
524 static typename
std::enable_if
<
525 !std::is_enum
<T
>::value
,
527 >::type
hash(const T
& t
) { return std::hash
<T
>()(t
); }
531 * Hash operand wrapper.
533 template<typename T
, typename S
>
534 struct hash_operand
{ const T
& val
; S type
; };
536 // this template isn't really needed. its a workaround for T44007494
537 template<typename S
> struct hash_operand
<void*, S
> { void* const val
; S type
; };
540 * Hash T using H::operator() if it is compatible, else fall back to
541 * hasher_impl (e.g., if H := hasher_default).
543 template<typename T
, typename S
, class H
>
544 auto hash(const hash_operand
<T
,S
>& t
, H h
) -> decltype(h(t
.val
)) {
547 template<typename T
, typename S
, class H
>
548 auto hash(const hash_operand
<T
,S
>& t
, H h
) -> decltype(h(t
.val
, t
.type
)) {
549 return h(t
.val
, t
.type
);
551 template<typename T
, typename S
, typename
... Unused
>
552 typename
std::enable_if
<sizeof...(Unused
) == 1, size_t>::type
553 hash(const hash_operand
<T
,S
>& t
, Unused
...) {
554 return hasher_impl::hash(t
.val
);
558 * Clone of folly::hash::hash_combine_generic.
561 size_t hash_combine(H
/*h*/) {
565 template<class H
, typename T
, typename
... Ts
>
566 size_t hash_combine(H h
, const T
& t
, const Ts
&... ts
) {
567 auto const seed
= size_t{hash(t
, h
)};
568 if (sizeof...(ts
) == 0) return seed
;
570 auto const remainder
= hash_combine(h
, ts
...);
571 return static_cast<size_t>(folly::hash::hash_128_to_64(seed
, remainder
));
575 * Trivial equality overrides, which will cause bytecodes to compare via
576 * operator==() on the various immediate types.
578 struct eq_default
{};
581 * Equality operand wrapper.
583 template<typename T
, typename S
>
584 struct eq_operand
{ const T
& l
; const T
& r
; S type
; };
586 // this template isn't really needed. its a workaround for T44007494
587 template<typename S
> struct eq_operand
<void*, S
> {
588 void* const l
; void* const r
; S type
;
592 * Compare two values, using E::operator() if it exists, else the default
595 template<typename T
, typename S
, class E
>
596 auto equals(const eq_operand
<T
,S
>& t
, E e
) -> decltype(e(t
.l
, t
.r
)) {
599 template<typename T
, typename S
, class E
>
600 auto equals(const eq_operand
<T
,S
>& t
, E e
) -> decltype(e(t
.l
, t
.r
, t
.type
)) {
601 return e(t
.l
, t
.r
, t
.type
);
603 template<typename T
, typename S
, typename
... Unused
>
604 typename
std::enable_if
<sizeof...(Unused
) == 1, bool>::type
605 equals(const eq_operand
<T
,S
>& t
, Unused
...) {
610 * Check if a list of eq_operands are pairwise-equal.
613 bool eq_pairs(E
/*e*/) {
617 template<class E
, typename T
, typename
... Ts
>
618 bool eq_pairs(E e
, const T
& t
, const Ts
&... ts
) {
619 return equals(t
, e
) && (sizeof...(ts
) == 0 || eq_pairs(e
, ts
...));
624 //////////////////////////////////////////////////////////////////////
627 * Bytecode immediate type tags.
630 #define ARGTYPE(name, type) enum class name : uint8_t {};
631 #define ARGTYPEVEC(name, type) enum class name : uint8_t {};
637 #define IMM_ID_BLA BLA
638 #define IMM_ID_SLA SLA
639 #define IMM_ID_IVA IVA
640 #define IMM_ID_I64A I64A
642 #define IMM_ID_NLA NLA
643 #define IMM_ID_ILA ILA
647 #define IMM_ID_RATA RATA
650 #define IMM_ID_OA(type) OA
651 #define IMM_ID_VSA VSA
653 #define IMM_ID_LAR LAR
654 #define IMM_ID_ITA ITA
655 #define IMM_ID_FCA FCA
657 #define IMM_TY_BLA SwitchTab
658 #define IMM_TY_SLA SSwitchTab
659 #define IMM_TY_IVA uint32_t
660 #define IMM_TY_I64A int64_t
661 #define IMM_TY_LA LocalId
662 #define IMM_TY_NLA NamedLocal
663 #define IMM_TY_ILA LocalId
664 #define IMM_TY_IA IterId
665 #define IMM_TY_DA double
666 #define IMM_TY_SA LSString
667 #define IMM_TY_RATA RepoAuthType
668 #define IMM_TY_AA SArray
669 #define IMM_TY_BA BlockId
670 #define IMM_TY_OA(type) type
671 #define IMM_TY_VSA CompactVector<LSString>
672 #define IMM_TY_KA MKey
673 #define IMM_TY_LAR LocalRange
674 #define IMM_TY_ITA IterArgs
675 #define IMM_TY_FCA FCallArgs
677 #define IMM_NAME_BLA(n) targets
678 #define IMM_NAME_SLA(n) targets
679 #define IMM_NAME_IVA(n) arg##n
680 #define IMM_NAME_I64A(n) arg##n
681 #define IMM_NAME_LA(n) loc##n
682 #define IMM_NAME_NLA(n) nloc##n
683 #define IMM_NAME_ILA(n) loc##n
684 #define IMM_NAME_IA(n) iter##n
685 #define IMM_NAME_DA(n) dbl##n
686 #define IMM_NAME_SA(n) str##n
687 #define IMM_NAME_RATA(n) rat
688 #define IMM_NAME_AA(n) arr##n
689 #define IMM_NAME_BA(n) target##n
690 #define IMM_NAME_OA_IMPL(n) subop##n
691 #define IMM_NAME_OA(type) IMM_NAME_OA_IMPL
692 #define IMM_NAME_VSA(n) keys
693 #define IMM_NAME_KA(n) mkey
694 #define IMM_NAME_LAR(n) locrange
695 #define IMM_NAME_ITA(n) ita
696 #define IMM_NAME_FCA(n) fca
698 #define IMM_TARGETS_BLA(n) for (auto& t : targets) f(t);
699 #define IMM_TARGETS_SLA(n) for (auto& kv : targets) f(kv.second);
700 #define IMM_TARGETS_IVA(n)
701 #define IMM_TARGETS_I64A(n)
702 #define IMM_TARGETS_LA(n)
703 #define IMM_TARGETS_NLA(n)
704 #define IMM_TARGETS_ILA(n)
705 #define IMM_TARGETS_IA(n)
706 #define IMM_TARGETS_DA(n)
707 #define IMM_TARGETS_SA(n)
708 #define IMM_TARGETS_RATA(n)
709 #define IMM_TARGETS_AA(n)
710 #define IMM_TARGETS_BA(n) f(target##n);
711 #define IMM_TARGETS_OA_IMPL(n)
712 #define IMM_TARGETS_OA(type) IMM_TARGETS_OA_IMPL
713 #define IMM_TARGETS_VSA(n)
714 #define IMM_TARGETS_KA(n)
715 #define IMM_TARGETS_LAR(n)
716 #define IMM_TARGETS_ITA(n)
717 #define IMM_TARGETS_FCA(n) if (fca.asyncEagerTarget() != NoBlockId) { \
718 f(fca.asyncEagerTarget()); \
721 #define IMM_EXTRA_BLA
722 #define IMM_EXTRA_SLA
723 #define IMM_EXTRA_IVA
724 #define IMM_EXTRA_I64A
726 #define IMM_EXTRA_NLA
727 #define IMM_EXTRA_ILA
731 #define IMM_EXTRA_RATA
734 #define IMM_EXTRA_OA(x)
735 #define IMM_EXTRA_VSA
737 #define IMM_EXTRA_LAR
738 #define IMM_EXTRA_ITA
739 #define IMM_EXTRA_FCA
741 #define IMM_MEM(which, n) IMM_TY_##which IMM_NAME_##which(n)
743 #define IMM_MEM_ONE(x) IMM_MEM(x, 1);
744 #define IMM_MEM_TWO(x, y) IMM_MEM(x, 1); IMM_MEM(y, 2);
745 #define IMM_MEM_THREE(x, y, z) IMM_MEM(x, 1); IMM_MEM(y, 2); \
747 #define IMM_MEM_FOUR(x, y, z, l) IMM_MEM(x, 1); IMM_MEM(y, 2); \
748 IMM_MEM(z, 3); IMM_MEM(l, 4);
749 #define IMM_MEM_FIVE(x, y, z, l, m) IMM_MEM(x, 1); IMM_MEM(y, 2); \
750 IMM_MEM(z, 3); IMM_MEM(l, 4); \
752 #define IMM_MEM_SIX(x, y, z, l, m, n) IMM_MEM(x, 1); IMM_MEM(y, 2); \
753 IMM_MEM(z, 3); IMM_MEM(l, 4); \
754 IMM_MEM(m, 5); IMM_MEM(n, 6);
756 #define IMM_EQ_WRAP(e, ...) detail::eq_pairs(e, __VA_ARGS__)
757 #define IMM_EQ(which, n) detail::eq_operand< \
759 imm::IMM_ID_##which \
761 IMM_NAME_##which(n), \
762 o.IMM_NAME_##which(n) \
764 #define IMM_EQ_NA detail::eq_operand<void*,imm::NA> { 0, 0 }
765 #define IMM_EQ_ONE(x) IMM_EQ(x, 1)
766 #define IMM_EQ_TWO(x, y) IMM_EQ_ONE(x), IMM_EQ(y, 2)
767 #define IMM_EQ_THREE(x, y, z) IMM_EQ_TWO(x, y), IMM_EQ(z, 3)
768 #define IMM_EQ_FOUR(x, y, z, l) IMM_EQ_THREE(x, y, z), IMM_EQ(l, 4)
769 #define IMM_EQ_FIVE(x, y, z, l, m) IMM_EQ_FOUR(x, y, z, l), IMM_EQ(m, 5)
770 #define IMM_EQ_SIX(x, y, z, l, m, n) IMM_EQ_FIVE(x, y, z, l, m), IMM_EQ(n, 6)
772 #define IMM_HASH_WRAP(h, ...) detail::hash_combine(h, __VA_ARGS__)
773 #define IMM_HASH(which, n) detail::hash_operand< \
775 imm::IMM_ID_##which \
776 > { IMM_NAME_##which(n) }
777 #define IMM_HASH_NA detail::hash_operand<void*,imm::NA> { 0 }
778 #define IMM_HASH_ONE(x) IMM_HASH(x, 1)
779 #define IMM_HASH_TWO(x, y) IMM_HASH_ONE(x), IMM_HASH(y, 2)
780 #define IMM_HASH_THREE(x, y, z) IMM_HASH_TWO(x, y), IMM_HASH(z, 3)
781 #define IMM_HASH_FOUR(x, y, z, l) IMM_HASH_THREE(x, y, z), IMM_HASH(l, 4)
782 #define IMM_HASH_FIVE(x, y, z, l, m) IMM_HASH_FOUR(x, y, z, l), IMM_HASH(m, 5)
783 #define IMM_HASH_SIX(x, y, z, l, m, n) IMM_HASH_FIVE(x, y, z, l, m), IMM_HASH(n, 6)
785 #define IMM_TARGETS_NA
786 #define IMM_TARGETS_ONE(x) IMM_TARGETS_##x(1)
787 #define IMM_TARGETS_TWO(x,y) IMM_TARGETS_ONE(x) IMM_TARGETS_##y(2)
788 #define IMM_TARGETS_THREE(x,y,z) IMM_TARGETS_TWO(x,y) IMM_TARGETS_##z(3)
789 #define IMM_TARGETS_FOUR(x,y,z,l) IMM_TARGETS_THREE(x,y,z) IMM_TARGETS_##l(4)
790 #define IMM_TARGETS_FIVE(x,y,z,l,m) IMM_TARGETS_FOUR(x,y,z,l) IMM_TARGETS_##m(5)
791 #define IMM_TARGETS_SIX(x,y,z,l,m,n) IMM_TARGETS_FIVE(x,y,z,l,m) IMM_TARGETS_##n(6)
794 #define IMM_EXTRA_ONE(x) IMM_EXTRA_##x
795 #define IMM_EXTRA_TWO(x,y) IMM_EXTRA_ONE(x) IMM_EXTRA_ONE(y)
796 #define IMM_EXTRA_THREE(x,y,z) IMM_EXTRA_TWO(x,y) IMM_EXTRA_ONE(z)
797 #define IMM_EXTRA_FOUR(x,y,z,l) IMM_EXTRA_THREE(x,y,z) IMM_EXTRA_ONE(l)
798 #define IMM_EXTRA_FIVE(x,y,z,l,m) IMM_EXTRA_FOUR(x,y,z,l) IMM_EXTRA_ONE(m)
799 #define IMM_EXTRA_SIX(x,y,z,l,m,n) IMM_EXTRA_FIVE(x,y,z,l,m) IMM_EXTRA_ONE(n)
801 #define IMM_CTOR(which, n) IMM_TY_##which IMM_NAME_##which(n)
803 #define IMM_CTOR_ONE(x) IMM_CTOR(x, 1)
804 #define IMM_CTOR_TWO(x, y) IMM_CTOR(x, 1), IMM_CTOR(y, 2)
805 #define IMM_CTOR_THREE(x, y, z) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
807 #define IMM_CTOR_FOUR(x, y, z, l) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
808 IMM_CTOR(z, 3), IMM_CTOR(l, 4)
809 #define IMM_CTOR_FIVE(x, y, z, l, m) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
810 IMM_CTOR(z, 3), IMM_CTOR(l, 4), \
812 #define IMM_CTOR_SIX(x, y, z, l, m, n) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
813 IMM_CTOR(z, 3), IMM_CTOR(l, 4), \
814 IMM_CTOR(m, 5), IMM_CTOR(n, 6)
816 #define IMM_INIT(which, n) IMM_NAME_##which(n) \
817 ( std::move(IMM_NAME_##which(n)) )
819 #define IMM_INIT_ONE(x) : IMM_INIT(x, 1)
820 #define IMM_INIT_TWO(x, y) : IMM_INIT(x, 1), IMM_INIT(y, 2)
821 #define IMM_INIT_THREE(x, y, z) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
823 #define IMM_INIT_FOUR(x, y, z, l) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
824 IMM_INIT(z, 3), IMM_INIT(l, 4)
825 #define IMM_INIT_FIVE(x, y, z, l, m) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
826 IMM_INIT(z, 3), IMM_INIT(l, 4), \
828 #define IMM_INIT_SIX(x, y, z, l, m, n) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
829 IMM_INIT(z, 3), IMM_INIT(l, 4), \
830 IMM_INIT(m, 5), IMM_INIT(n, 6)
832 #define POP_UV if (i == 0) return Flavor::U
833 #define POP_CV if (i == 0) return Flavor::C
834 #define POP_CU if (i == 0) return Flavor::CU
835 #define POP_CUV POP_CU
837 #define POP_NOV uint32_t numPop() const { return 0; } \
838 Flavor popFlavor(uint32_t) const { not_reached(); }
840 #define POP_ONE(x) uint32_t numPop() const { return 1; } \
841 Flavor popFlavor(uint32_t i) const { \
842 POP_##x; not_reached(); \
845 #define POP_TWO(x, y) uint32_t numPop() const { return 2; } \
846 Flavor popFlavor(uint32_t i) const { \
847 POP_##x; --i; POP_##y; not_reached(); \
850 #define POP_THREE(x, y, z) uint32_t numPop() const { return 3; } \
851 Flavor popFlavor(uint32_t i) const { \
852 POP_##x; --i; POP_##y; --i; POP_##z; \
856 #define POP_MFINAL uint32_t numPop() const { return arg1; } \
857 Flavor popFlavor(uint32_t) const { not_reached(); }
859 #define POP_C_MFINAL(n) uint32_t numPop() const { return arg1 + n; } \
860 Flavor popFlavor(uint32_t) const { not_reached(); }
862 #define POP_CMANY uint32_t numPop() const { return arg1; } \
863 Flavor popFlavor(uint32_t i) const { \
864 assertx(i < numPop()); \
868 #define POP_SMANY uint32_t numPop() const { return keys.size(); } \
869 Flavor popFlavor(uint32_t i) const { \
870 assertx(i < numPop()); \
874 #define POP_CUMANY uint32_t numPop() const { return arg1; } \
875 Flavor popFlavor(uint32_t i) const { \
876 assertx(i < numPop()); \
880 #define POP_FCALL(nin, nobj) \
881 uint32_t numPop() const { \
882 return fca.template numPop<nin>(); \
884 Flavor popFlavor(uint32_t i) const { \
885 return fca.template popFlavor<nin,nobj>(i); \
888 #define PUSH_NOV uint32_t numPush() const { return 0; }
890 #define PUSH_ONE(x) uint32_t numPush() const { return 1; }
892 #define PUSH_TWO(x, y) uint32_t numPush() const { return 2; }
894 #define PUSH_CMANY uint32_t numPush() const { return arg1; }
895 #define PUSH_FCALL uint32_t numPush() const { return fca.numRets(); }
897 #define O(opcode, imms, inputs, outputs, flags) \
899 static constexpr Op op = Op::opcode; \
900 explicit opcode (IMM_CTOR_##imms) \
909 bool operator==(const opcode& o) const { \
910 return equals(o, detail::eq_default{}); \
913 bool operator!=(const opcode& o) const { \
914 return !(*this == o); \
918 bool equals(const opcode& o, E e) const { \
919 return IMM_EQ_WRAP(e, IMM_EQ_##imms); \
922 size_t hash() const { \
923 return IMM_HASH_WRAP( \
924 detail::hasher_default{}, \
929 size_t hash(H h) const { \
930 return IMM_HASH_WRAP(h, IMM_HASH_##imms); \
933 template <typename F> \
934 void forEachTarget(F&& f) { \
937 template <typename F> \
938 void forEachTarget(F&& f) const { \
988 // These are deliberately not undefined, so they can be used in other
990 // #undef IMM_NAME_BLA
991 // #undef IMM_NAME_SLA
992 // #undef IMM_NAME_IVA
993 // #undef IMM_NAME_I64A
994 // #undef IMM_NAME_LA
995 // #undef IMM_NAME_NLA
996 // #undef IMM_NAME_ILA
997 // #undef IMM_NAME_IA
998 // #undef IMM_NAME_DA
999 // #undef IMM_NAME_SA
1000 // #undef IMM_NAME_RATA
1001 // #undef IMM_NAME_AA
1002 // #undef IMM_NAME_BA
1003 // #undef IMM_NAME_OA
1004 // #undef IMM_NAME_OA_IMPL
1005 // #undef IMM_NAME_LAR
1006 // #undef IMM_NAME_FCA
1008 #undef IMM_TARGETS_BLA
1009 #undef IMM_TARGETS_SLA
1010 #undef IMM_TARGETS_IVA
1011 #undef IMM_TARGETS_I64A
1012 #undef IMM_TARGETS_LA
1013 #undef IMM_TARGETS_NLA
1014 #undef IMM_TARGETS_ILA
1015 #undef IMM_TARGETS_IA
1016 #undef IMM_TARGETS_DA
1017 #undef IMM_TARGETS_SA
1018 #undef IMM_TARGETS_RATA
1019 #undef IMM_TARGETS_AA
1020 #undef IMM_TARGETS_BA
1021 #undef IMM_TARGETS_OA
1022 #undef IMM_TARGETS_KA
1023 #undef IMM_TARGETS_LAR
1024 #undef IMM_TARGETS_ITA
1025 #undef IMM_TARGETS_FCA
1027 #undef IMM_TARGETS_NA
1028 #undef IMM_TARGETS_ONE
1029 #undef IMM_TARGETS_TWO
1030 #undef IMM_TARGETS_THREE
1031 #undef IMM_TARGETS_FOUR
1032 #undef IMM_TARGETS_FIVE
1033 #undef IMM_TARGETS_SIX
1035 #undef IMM_EXTRA_BLA
1036 #undef IMM_EXTRA_SLA
1037 #undef IMM_EXTRA_IVA
1038 #undef IMM_EXTRA_I64A
1040 #undef IMM_EXTRA_NLA
1041 #undef IMM_EXTRA_ILA
1045 #undef IMM_EXTRA_RATA
1050 #undef IMM_EXTRA_LAR
1051 #undef IMM_EXTRA_ITA
1052 #undef IMM_EXTRA_FCA
1058 #undef IMM_MEM_THREE
1077 #undef IMM_HASH_THREE
1078 #undef IMM_HASH_FOUR
1079 #undef IMM_HASH_FIVE
1086 #undef IMM_CTOR_THREE
1087 #undef IMM_CTOR_FOUR
1088 #undef IMM_CTOR_FIVE
1095 #undef IMM_INIT_THREE
1096 #undef IMM_INIT_FOUR
1100 //////////////////////////////////////////////////////////////////////
1103 * Bytecode is a tagged-union that can hold any HHBC opcode struct
1104 * defined above. You can visit a bytecode with a static_visitor
1105 * using visit(), defined below.
1107 * Each Bytecode also carries a corresponding SrcLoc that indicates
1108 * which line of PHP code the bytecode was generated for.
1111 // Default construction creates a Nop.
1117 #define O(opcode, ...) \
1118 /* implicit */ Bytecode(bc::opcode data) \
1121 new (&opcode) bc::opcode(std::move(data)); \
1128 // Note: assuming bc::Nop is empty and has trivial dtor/ctor.
1130 Bytecode(const Bytecode
& o
) : op(Op::Nop
) { *this = o
; }
1131 Bytecode(Bytecode
&& o
) noexcept
: op(Op::Nop
) { *this = std::move(o
); }
1133 Bytecode
& operator=(const Bytecode
& o
) {
1137 #define O(opcode, ...) \
1138 case Op::opcode: new (&opcode) bc::opcode(o.opcode); break;
1139 switch (o
.op
) { OPCODES
}
1145 Bytecode
& operator=(Bytecode
&& o
) {
1148 #define O(opcode, ...) \
1149 case Op::opcode: new (&opcode) bc::opcode(std::move(o.opcode)); break;
1150 switch (o
.op
) { OPCODES
}
1156 ~Bytecode() { destruct(); }
1158 uint32_t numPop() const {
1159 #define O(opcode, ...) \
1160 case Op::opcode: return opcode.numPop();
1161 switch (op
) { OPCODES
}
1166 Flavor
popFlavor(uint32_t i
) const {
1167 #define O(opcode, ...) \
1168 case Op::opcode: return opcode.popFlavor(i);
1169 switch (op
) { OPCODES
}
1174 uint32_t numPush() const {
1175 #define O(opcode, ...) \
1176 case Op::opcode: return opcode.numPush();
1177 switch (op
) { OPCODES
}
1182 template <typename F
>
1183 void forEachTarget(F
&& f
) const {
1184 #define O(opcode, ...) \
1185 case Op::opcode: return opcode.forEachTarget(std::forward<F>(f));
1186 switch (op
) { OPCODES
}
1191 template <typename F
>
1192 void forEachTarget(F
&& f
) {
1193 #define O(opcode, ...) \
1194 case Op::opcode: return opcode.forEachTarget(std::forward<F>(f));
1195 switch (op
) { OPCODES
}
1203 #define O(opcode, ...) bc::opcode opcode;
1210 #define O(opcode, ...) \
1212 { typedef bc::opcode X; \
1213 this->opcode.~X(); } \
1221 //////////////////////////////////////////////////////////////////////
1223 inline bool operator==(const Bytecode
& a
, const Bytecode
& b
) {
1224 if (a
.op
!= b
.op
) return false;
1225 #define O(opcode, ...) case Op::opcode: return a.opcode == b.opcode;
1226 switch (a
.op
) { OPCODES
}
1231 inline bool operator!=(const Bytecode
& a
, const Bytecode
& b
) {
1236 inline bool equals(const Bytecode
& a
, const Bytecode
& b
, E equals
) {
1237 if (a
.op
!= b
.op
) return false;
1238 #define O(opcode, ...) \
1239 case Op::opcode: return a.opcode.equals(b.opcode, equals);
1240 switch (a
.op
) { OPCODES
}
1246 inline size_t hash(const Bytecode
& b
, H hasher
) {
1247 auto hash
= 14695981039346656037ULL;
1248 auto o
= static_cast<size_t>(b
.op
);
1250 #define O(opcode, ...) \
1252 return folly::hash::hash_combine(b.opcode.hash(hasher), hash);
1253 switch (b
.op
) { OPCODES
}
1258 inline size_t hash(const Bytecode
& b
) {
1259 return hash(b
, bc::detail::hasher_default
{});
1262 //////////////////////////////////////////////////////////////////////
1265 * Helper for making a Bytecode with a given srcLoc.
1268 * auto b = bc_with_loc(something.srcLoc, bc::Nop {});
1270 template<class T
> Bytecode
bc_with_loc(int32_t loc
, const T
& t
) {
1276 //////////////////////////////////////////////////////////////////////
1279 * Visit a bytecode using a StaticVisitor, similar to
1280 * boost::apply_visitor or match().
1282 * The `v' argument should be a function object that accepts a call
1283 * operator for all the bc::Foo types. Its result type should be
1284 * independent of bytecode, but may vary with constness.
1286 template<class Visit
>
1287 auto visit(Bytecode
& b
, Visit v
) {
1288 #define O(opcode, ...) case Op::opcode: return v(b.opcode);
1289 switch (b
.op
) { OPCODES
}
1294 template<class Visit
>
1295 auto visit(const Bytecode
& b
, Visit v
) {
1296 #define O(opcode, ...) case Op::opcode: return v(b.opcode);
1297 switch (b
.op
) { OPCODES
}
1302 //////////////////////////////////////////////////////////////////////