New flag and error message for class to memo key conversion
[hiphop-php.git] / hphp / hhbbc / bc.h
blob3fc7825153546e370b0c71c3e23dd2db4f908d9f
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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 #pragma once
18 #include <vector>
19 #include <utility>
20 #include <type_traits>
22 #include <algorithm>
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 {
35 struct Bytecode;
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.
44 * E.g.
46 * auto pushInt = bc::Int { 2 }; // Push literal int
49 //////////////////////////////////////////////////////////////////////
51 struct NamedLocal {
52 NamedLocal()
53 : name(kInvalidLocalName)
54 , id(NoLocalId)
56 NamedLocal(LocalName name, LocalId id)
57 : name(name)
58 , id(id)
60 /* implicit */ NamedLocal(const ::HPHP::NamedLocal& nl)
61 : name(nl.name)
62 , id(nl.id)
64 /* implicit */ operator auto(){
65 return ::HPHP::NamedLocal{name, static_cast<int32_t>(id)};
67 LocalName name;
68 LocalId 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) {
76 return !(a == b);
79 struct MKey {
80 MKey()
81 : mcode{MW}
82 , rop{ReadonlyOp::Any}
83 , int64{0}
86 MKey(MemberCode mcode, NamedLocal local, ReadonlyOp rop)
87 : mcode{mcode}
88 , rop{rop}
89 , local{local}
92 MKey(MemberCode mcode, int32_t idx, ReadonlyOp rop)
93 : mcode{mcode}
94 , rop{rop}
95 , idx{idx}
98 MKey(MemberCode mcode, int64_t int64, ReadonlyOp rop)
99 : mcode{mcode}
100 , rop{rop}
101 , int64{int64}
104 MKey(MemberCode mcode, SString litstr, ReadonlyOp rop)
105 : mcode{mcode}
106 , rop{rop}
107 , litstr{litstr}
110 MemberCode mcode;
111 ReadonlyOp rop;
112 union {
113 SString litstr;
114 int64_t int64;
115 int64_t idx;
116 NamedLocal local;
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) {
125 return !(a == 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
130 // arbitrary.
131 struct LocalRange {
132 LocalId first;
133 uint32_t count;
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) {
141 return !(a == 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)
153 , context(context) {
155 FCallArgsLong(const FCallArgsLong& o)
156 : FCallArgsLong(o.flags, o.numArgs, o.numRets, nullptr, nullptr,
157 o.asyncEagerTarget, o.context) {
158 if (o.inoutArgs) {
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 {
182 auto fca = *this;
183 fca.flags = static_cast<Flags>(fca.flags & ~Flags::HasGenerics);
184 return fca;
186 FCallArgsLong withoutLockWhileUnwinding() const {
187 auto fca = *this;
188 fca.flags = static_cast<Flags>(fca.flags & ~Flags::LockWhileUnwinding);
189 return fca;
191 FCallArgsLong withoutEnforceMutableReturn() const {
192 auto fca = *this;
193 fca.flags = static_cast<Flags>(fca.flags & ~Flags::EnforceMutableReturn);
194 return fca;
196 FCallArgsLong withoutEnforceReadonlyThis() const {
197 auto fca = *this;
198 fca.flags = static_cast<Flags>(fca.flags & ~Flags::EnforceReadonlyThis);
199 return fca;
202 FCallArgsLong withoutRepack() const {
203 auto fca = *this;
204 fca.flags = static_cast<Flags>(fca.flags | Flags::SkipRepack);
205 return fca;
208 FCallArgsLong withoutCoeffectsCheck() const {
209 auto fca = *this;
210 fca.flags = static_cast<Flags>(fca.flags | Flags::SkipCoeffectsCheck);
211 return fca;
214 FCallArgsLong withoutInOut() const {
215 auto fca = *this;
216 fca.inoutArgs = nullptr;
217 return fca;
220 FCallArgsLong withoutReadonly() const {
221 auto fca = *this;
222 fca.readonlyArgs = nullptr;
223 return fca;
226 FCallArgsLong withoutAsyncEagerTarget() const {
227 auto fca = *this;
228 fca.asyncEagerTarget = NoBlockId;
229 return fca;
231 template<typename F>
232 void applyIO(F f) const {
233 f((numArgs + 7) / 8, inoutArgs.get());
235 template<typename F>
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;
246 return
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);
269 template<int nin>
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;
277 i -= nin;
278 if (hasGenerics()) {
279 if (i == 0) return Flavor::C;
280 i--;
282 if (hasUnpack()) {
283 if (i == 0) return Flavor::C;
284 i--;
286 if (i < numArgs) return Flavor::C;
287 i -= numArgs;
288 if (i == 1 && nobj) return Flavor::C;
289 return Flavor::U;
291 FCallArgsBase base() const { return *this; }
293 std::unique_ptr<uint8_t[]> inoutArgs;
294 std::unique_ptr<uint8_t[]> readonlyArgs;
295 BlockId asyncEagerTarget;
296 LSString context;
299 struct FCallArgs {
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,
307 LSString context) {
308 raw = 0;
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{} {
315 l = o.l;
317 ~FCallArgs() {
318 l.~copy_ptr();
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 {
366 return l->base();
369 friend bool operator==(const FCallArgs& a, const FCallArgs& b) {
370 if (a.l.get() == b.l.get()) return true;
371 return *a.l == *b.l;
374 size_t hash() const {
375 return l->hash();
378 uint32_t numArgs() const {
379 return l->numArgs;
382 uint32_t numRets() const {
383 return l->numRets;
386 const uint8_t* inoutArgs() const {
387 return l->inoutArgs.get();
389 template<typename F>
390 void applyIO(F f) const {
391 assertx(enforceInOut());
392 l->applyIO(f);
395 const uint8_t* readonlyArgs() const {
396 return l->readonlyArgs.get();
398 template<typename F>
399 void applyReadonly(F f) const {
400 assertx(enforceReadonly());
401 l->applyReadonly(f);
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();
434 template<int nin>
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; }
443 private:
444 union {
445 copy_ptr<FCallArgsLong> l;
446 uint64_t raw;
450 inline bool operator!=(const FCallArgs& a, const FCallArgs& b) {
451 return !(a == 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 //////////////////////////////////////////////////////////////////////
463 namespace bc {
465 //////////////////////////////////////////////////////////////////////
467 namespace detail {
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.
478 struct hasher_impl {
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);
487 template<class T>
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) {
511 return fca.hash();
514 template<class T>
515 static typename std::enable_if<
516 std::is_enum<T>::value,
517 size_t
518 >::type hash(T t) {
519 using U = typename std::underlying_type<T>::type;
520 return std::hash<U>()(static_cast<U>(t));
523 template<class T>
524 static typename std::enable_if<
525 !std::is_enum<T>::value,
526 size_t
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)) {
545 return 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.
560 template <class H>
561 size_t hash_combine(H /*h*/) {
562 return 0;
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
593 * operator==.
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)) {
597 return 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...) {
606 return t.l == t.r;
610 * Check if a list of eq_operands are pairwise-equal.
612 template <class E>
613 bool eq_pairs(E /*e*/) {
614 return true;
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.
629 namespace imm {
630 #define ARGTYPE(name, type) enum class name : uint8_t {};
631 #define ARGTYPEVEC(name, type) enum class name : uint8_t {};
632 ARGTYPES
633 #undef ARGTYPE
634 #undef ARGTYPEVEC
637 #define IMM_ID_BLA BLA
638 #define IMM_ID_SLA SLA
639 #define IMM_ID_IVA IVA
640 #define IMM_ID_I64A I64A
641 #define IMM_ID_LA LA
642 #define IMM_ID_NLA NLA
643 #define IMM_ID_ILA ILA
644 #define IMM_ID_IA IA
645 #define IMM_ID_DA DA
646 #define IMM_ID_SA SA
647 #define IMM_ID_RATA RATA
648 #define IMM_ID_AA AA
649 #define IMM_ID_BA BA
650 #define IMM_ID_OA(type) OA
651 #define IMM_ID_VSA VSA
652 #define IMM_ID_KA KA
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
725 #define IMM_EXTRA_LA
726 #define IMM_EXTRA_NLA
727 #define IMM_EXTRA_ILA
728 #define IMM_EXTRA_IA
729 #define IMM_EXTRA_DA
730 #define IMM_EXTRA_SA
731 #define IMM_EXTRA_RATA
732 #define IMM_EXTRA_AA
733 #define IMM_EXTRA_BA
734 #define IMM_EXTRA_OA(x)
735 #define IMM_EXTRA_VSA
736 #define IMM_EXTRA_KA
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)
742 #define IMM_MEM_NA
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); \
746 IMM_MEM(z, 3);
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); \
751 IMM_MEM(m, 5);
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< \
758 IMM_TY_##which, \
759 imm::IMM_ID_##which \
760 > { \
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< \
774 IMM_TY_##which, \
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)
793 #define IMM_EXTRA_NA
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)
802 #define IMM_CTOR_NA
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), \
806 IMM_CTOR(z, 3)
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), \
811 IMM_CTOR(m, 5)
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)) )
818 #define IMM_INIT_NA
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), \
822 IMM_INIT(z, 3)
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), \
827 IMM_INIT(m, 5)
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; \
853 not_reached(); \
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()); \
865 return Flavor::C; \
868 #define POP_SMANY uint32_t numPop() const { return keys.size(); } \
869 Flavor popFlavor(uint32_t i) const { \
870 assertx(i < numPop()); \
871 return Flavor::C; \
874 #define POP_CUMANY uint32_t numPop() const { return arg1; } \
875 Flavor popFlavor(uint32_t i) const { \
876 assertx(i < numPop()); \
877 return Flavor::CU; \
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) \
898 struct opcode { \
899 static constexpr Op op = Op::opcode; \
900 explicit opcode (IMM_CTOR_##imms) \
901 IMM_INIT_##imms \
902 {} \
904 IMM_MEM_##imms \
905 IMM_EXTRA_##imms \
906 POP_##inputs \
907 PUSH_##outputs \
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); \
917 template<class E> \
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{}, \
925 IMM_HASH_##imms); \
928 template<class H> \
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) { \
935 IMM_TARGETS_##imms \
937 template <typename F> \
938 void forEachTarget(F&& f) const { \
939 IMM_TARGETS_##imms \
942 OPCODES
943 #undef O
945 #undef PUSH_NOV
946 #undef PUSH_ONE
947 #undef PUSH_TWO
948 #undef PUSH_CMANY
949 #undef PUSH_FCALL
951 #undef POP_UV
952 #undef POP_CV
953 #undef POP_CU
954 #undef POP_CUV
956 #undef POP_NOV
957 #undef POP_ONE
958 #undef POP_TWO
959 #undef POP_THREE
960 #undef POP_MFINAL
961 #undef POP_C_MFINAL
962 #undef POP_CMANY
963 #undef POP_SMANY
964 #undef POP_CUMANY
965 #undef POP_FCALL
967 #undef IMM_TY_MA
968 #undef IMM_TY_BLA
969 #undef IMM_TY_SLA
970 #undef IMM_TY_IVA
971 #undef IMM_TY_I64A
972 #undef IMM_TY_LA
973 #undef IMM_TY_NLA
974 #undef IMM_TY_ILA
975 #undef IMM_TY_IA
976 #undef IMM_TY_DA
977 #undef IMM_TY_SA
978 #undef IMM_TY_RATA
979 #undef IMM_TY_AA
980 #undef IMM_TY_BA
981 #undef IMM_TY_OA
982 #undef IMM_TY_VSA
983 #undef IMM_TY_KA
984 #undef IMM_TY_LAR
985 #undef IMM_TY_ITA
986 #undef IMM_TY_FCA
988 // These are deliberately not undefined, so they can be used in other
989 // places.
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
1039 #undef IMM_EXTRA_LA
1040 #undef IMM_EXTRA_NLA
1041 #undef IMM_EXTRA_ILA
1042 #undef IMM_EXTRA_IA
1043 #undef IMM_EXTRA_DA
1044 #undef IMM_EXTRA_SA
1045 #undef IMM_EXTRA_RATA
1046 #undef IMM_EXTRA_AA
1047 #undef IMM_EXTRA_BA
1048 #undef IMM_EXTRA_OA
1049 #undef IMM_EXTRA_KA
1050 #undef IMM_EXTRA_LAR
1051 #undef IMM_EXTRA_ITA
1052 #undef IMM_EXTRA_FCA
1054 #undef IMM_MEM
1055 #undef IMM_MEM_NA
1056 #undef IMM_MEM_ONE
1057 #undef IMM_MEM_TWO
1058 #undef IMM_MEM_THREE
1059 #undef IMM_MEM_FOUR
1060 #undef IMM_MEM_FIVE
1061 #undef IMM_MEM_SIX
1063 #undef IMM_EQ
1064 #undef IMM_EQ_NA
1065 #undef IMM_EQ_ONE
1066 #undef IMM_EQ_TWO
1067 #undef IMM_EQ_THREE
1068 #undef IMM_EQ_FOUR
1069 #undef IMM_EQ_FIVE
1070 #undef IMM_EQ_SIX
1072 #undef IMM_HASH
1073 #undef IMM_HASH_DO
1074 #undef IMM_HASH_NA
1075 #undef IMM_HASH_ONE
1076 #undef IMM_HASH_TWO
1077 #undef IMM_HASH_THREE
1078 #undef IMM_HASH_FOUR
1079 #undef IMM_HASH_FIVE
1080 #undef IMM_HASH_SIX
1082 #undef IMM_CTOR
1083 #undef IMM_CTOR_NA
1084 #undef IMM_CTOR_ONE
1085 #undef IMM_CTOR_TWO
1086 #undef IMM_CTOR_THREE
1087 #undef IMM_CTOR_FOUR
1088 #undef IMM_CTOR_FIVE
1089 #undef IMM_CTOR_SIX
1091 #undef IMM_INIT
1092 #undef IMM_INIT_NA
1093 #undef IMM_INIT_ONE
1094 #undef IMM_INIT_TWO
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.
1110 struct Bytecode {
1111 // Default construction creates a Nop.
1112 Bytecode()
1113 : op(Op::Nop)
1114 , Nop(bc::Nop{})
1117 #define O(opcode, ...) \
1118 /* implicit */ Bytecode(bc::opcode data) \
1119 : op(Op::opcode) \
1121 new (&opcode) bc::opcode(std::move(data)); \
1124 OPCODES
1126 #undef O
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) {
1134 destruct();
1135 op = Op::Nop;
1136 srcLoc = o.srcLoc;
1137 #define O(opcode, ...) \
1138 case Op::opcode: new (&opcode) bc::opcode(o.opcode); break;
1139 switch (o.op) { OPCODES }
1140 #undef O
1141 op = o.op;
1142 return *this;
1145 Bytecode& operator=(Bytecode&& o) {
1146 destruct();
1147 srcLoc = o.srcLoc;
1148 #define O(opcode, ...) \
1149 case Op::opcode: new (&opcode) bc::opcode(std::move(o.opcode)); break;
1150 switch (o.op) { OPCODES }
1151 #undef O
1152 op = o.op;
1153 return *this;
1156 ~Bytecode() { destruct(); }
1158 uint32_t numPop() const {
1159 #define O(opcode, ...) \
1160 case Op::opcode: return opcode.numPop();
1161 switch (op) { OPCODES }
1162 #undef O
1163 not_reached();
1166 Flavor popFlavor(uint32_t i) const {
1167 #define O(opcode, ...) \
1168 case Op::opcode: return opcode.popFlavor(i);
1169 switch (op) { OPCODES }
1170 #undef O
1171 not_reached();
1174 uint32_t numPush() const {
1175 #define O(opcode, ...) \
1176 case Op::opcode: return opcode.numPush();
1177 switch (op) { OPCODES }
1178 #undef O
1179 not_reached();
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 }
1187 #undef O
1188 not_reached();
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 }
1196 #undef O
1197 not_reached();
1200 Op op;
1201 int32_t srcLoc{-1};
1203 #define O(opcode, ...) bc::opcode opcode;
1204 union { OPCODES };
1205 #undef O
1207 private:
1208 void destruct() {
1209 switch (op) {
1210 #define O(opcode, ...) \
1211 case Op::opcode: \
1212 { typedef bc::opcode X; \
1213 this->opcode.~X(); } \
1214 break;
1215 OPCODES
1216 #undef O
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 }
1227 #undef O
1228 not_reached();
1231 inline bool operator!=(const Bytecode& a, const Bytecode& b) {
1232 return !(a == b);
1235 template<class E>
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 }
1241 #undef O
1242 not_reached();
1245 template<class H>
1246 inline size_t hash(const Bytecode& b, H hasher) {
1247 auto hash = 14695981039346656037ULL;
1248 auto o = static_cast<size_t>(b.op);
1249 hash ^= o;
1250 #define O(opcode, ...) \
1251 case Op::opcode: \
1252 return folly::hash::hash_combine(b.opcode.hash(hasher), hash);
1253 switch (b.op) { OPCODES }
1254 #undef O
1255 not_reached();
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.
1267 * Ex:
1268 * auto b = bc_with_loc(something.srcLoc, bc::Nop {});
1270 template<class T> Bytecode bc_with_loc(int32_t loc, const T& t) {
1271 Bytecode b = t;
1272 b.srcLoc = loc;
1273 return b;
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 }
1290 #undef O
1291 not_reached();
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 }
1298 #undef O
1299 not_reached();
1302 //////////////////////////////////////////////////////////////////////