naming 2/2 - use naming_db_path provider
[hiphop-php.git] / hphp / hhbbc / bc.h
blob44f82618fae9428ad60c08da288a08ebcfddf8c3
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 #ifndef incl_HHBBC_BC_H_
17 #define incl_HHBBC_BC_H_
19 #include <vector>
20 #include <utility>
21 #include <type_traits>
23 #include <algorithm>
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 {
36 struct Bytecode;
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.
45 * E.g.
47 * auto pushInt = bc::Int { 2 }; // Push literal int
50 //////////////////////////////////////////////////////////////////////
52 struct NamedLocal {
53 NamedLocal()
54 : name(kInvalidLocalName)
55 , id(NoLocalId)
57 NamedLocal(LocalName name, LocalId id)
58 : name(name)
59 , id(id)
61 /* implicit */ NamedLocal(const ::HPHP::NamedLocal& nl)
62 : name(nl.name)
63 , id(nl.id)
65 /* implicit */ operator auto(){
66 return ::HPHP::NamedLocal{name, static_cast<int32_t>(id)};
68 LocalName name;
69 LocalId id;
72 inline bool operator==(NamedLocal a, NamedLocal b) {
73 return a.name == b.name && a.id == b.id;
76 inline bool operator!=(NamedLocal a, NamedLocal b) {
77 return !(a == b);
80 struct MKey {
81 MKey()
82 : mcode{MW}
83 , int64{0}
86 MKey(MemberCode mcode, NamedLocal local)
87 : mcode{mcode}
88 , local{local}
91 MKey(MemberCode mcode, int32_t idx)
92 : mcode{mcode}
93 , idx{idx}
96 MKey(MemberCode mcode, int64_t int64)
97 : mcode{mcode}
98 , int64{int64}
101 MKey(MemberCode mcode, SString litstr)
102 : mcode{mcode}
103 , litstr{litstr}
106 MemberCode mcode;
107 union {
108 SString litstr;
109 int64_t int64;
110 int64_t idx;
111 NamedLocal local;
115 inline bool operator==(MKey a, MKey b) {
116 return a.mcode == b.mcode && a.int64 == b.int64;
119 inline bool operator!=(MKey a, MKey b) {
120 return !(a == b);
123 // A contiguous range of locals. The count is the number of locals including the
124 // first. If the range is empty, count will be zero and first's value is
125 // arbitrary.
126 struct LocalRange {
127 LocalId first;
128 uint32_t count;
131 inline bool operator==(const LocalRange& a, const LocalRange& b) {
132 return a.first == b.first && a.count == b.count;
135 inline bool operator!=(const LocalRange& a, const LocalRange& b) {
136 return !(a == b);
139 struct FCallArgsShort {
140 using Flags = FCallArgsBase::Flags;
141 static constexpr int kValidFlag = (1u << 15);
142 static constexpr int kSkipNumArgsCheck = (1u << 14);
143 static constexpr int kLockWhileUnwinding = (1u << 13);
144 static constexpr int kEnforceInOut = (1u << 12);
145 static constexpr int kInoutMask = (1u << 12) - 1;
147 BlockId asyncEagerTarget;
148 Flags flags;
149 uint8_t numArgs : 4;
150 uint8_t numRets : 4;
151 uint16_t inoutArgsAndFlags;
152 static FCallArgsShort create(
153 Flags flags, uint32_t numArgs, uint32_t numRets,
154 const std::unique_ptr<uint8_t[]>& inoutArgs,
155 BlockId asyncEagerTarget, bool lockWhileUnwinding,
156 bool skipNumArgsCheck, SString context) {
157 if (numArgs >= 16 ||
158 numRets >= 16 ||
159 (inoutArgs && numArgs > 8 && inoutArgs[1] > (kInoutMask >> 8)) ||
160 context) {
161 return FCallArgsShort{};
163 FCallArgsShort fca{};
164 fca.asyncEagerTarget = asyncEagerTarget;
165 fca.flags = flags;
166 fca.numArgs = numArgs;
167 fca.numRets = numRets;
168 if (inoutArgs) {
169 fca.inoutArgsAndFlags = inoutArgs[0];
170 if (numArgs > 8) {
171 fca.inoutArgsAndFlags |= inoutArgs[1] << 8;
173 fca.inoutArgsAndFlags |= kEnforceInOut;
175 if (skipNumArgsCheck) fca.inoutArgsAndFlags |= kSkipNumArgsCheck;
176 if (lockWhileUnwinding) fca.inoutArgsAndFlags |= kLockWhileUnwinding;
177 fca.inoutArgsAndFlags |= kValidFlag;
178 return fca;
180 bool valid() const { return inoutArgsAndFlags & kValidFlag; }
181 bool skipNumArgsCheck() const {
182 return inoutArgsAndFlags & kSkipNumArgsCheck;
184 bool lockWhileUnwinding() const {
185 return inoutArgsAndFlags & kLockWhileUnwinding;
187 bool enforceInOut() const {
188 return inoutArgsAndFlags & kEnforceInOut;
190 bool isInOut(uint32_t i) const {
191 assertx(enforceInOut());
192 assertx(i < numArgs);
193 return inoutArgsAndFlags & kInoutMask & (1 << i);
195 FCallArgsShort withoutGenerics() const {
196 auto fca = *this;
197 fca.flags = static_cast<Flags>(fca.flags & ~Flags::HasGenerics);
198 return fca;
200 FCallArgsShort withoutInOut() const {
201 auto fca = *this;
202 fca.inoutArgsAndFlags &= ~(kInoutMask | kEnforceInOut);
203 return fca;
205 FCallArgsShort withoutLockWhileUnwinding() const {
206 auto fca = *this;
207 fca.inoutArgsAndFlags &= ~kLockWhileUnwinding;
208 return fca;
210 FCallArgsShort fixEager(bool supports_eager) const {
211 auto fca = *this;
212 if (supports_eager) {
213 assertx(fca.asyncEagerTarget != NoBlockId);
214 fca.flags = static_cast<Flags>(
215 fca.flags | Flags::SupportsAsyncEagerReturn
217 } else {
218 fca.asyncEagerTarget = NoBlockId;
219 fca.flags = static_cast<Flags>(
220 fca.flags & ~Flags::SupportsAsyncEagerReturn
223 return fca;
226 template<typename F>
227 void applyIO(F f) const {
228 uint8_t br[2];
229 br[0] = inoutArgsAndFlags;
230 br[1] = (inoutArgsAndFlags & kInoutMask) >> 8;
231 f(numArgs > 8 ? 2 : 1, br);
234 FCallArgsShort withoutNumArgsCheck() const {
235 auto fca = *this;
236 fca.inoutArgsAndFlags |= kSkipNumArgsCheck;
237 return fca;
239 friend bool operator==(const FCallArgsShort& a, const FCallArgsShort& b) {
240 return
241 a.flags == b.flags && a.numArgs == b.numArgs && a.numRets == b.numRets &&
242 a.inoutArgsAndFlags == b.inoutArgsAndFlags &&
243 a.asyncEagerTarget == b.asyncEagerTarget;
245 size_t hash() const {
246 uint64_t hash = HPHP::hash_int64_pair(numArgs, numRets);
247 hash = HPHP::hash_int64_pair(hash, flags);
248 if (enforceInOut()) {
249 applyIO(
250 [&] (int numBytes, const uint8_t* br) {
251 auto const s = reinterpret_cast<const char*>(br);
252 auto const hash_br = hash_string_cs(s, numBytes);
253 hash = HPHP::hash_int64_pair(hash, hash_br);
257 hash = HPHP::hash_int64_pair(hash, asyncEagerTarget);
258 hash = HPHP::hash_int64_pair(hash, lockWhileUnwinding());
259 return static_cast<size_t>(hash);
261 bool hasUnpack() const {
262 return flags & Flags::HasUnpack;
264 bool hasGenerics() const {
265 return flags & Flags::HasGenerics;
267 bool supportsAsyncEagerReturn() const {
268 return flags & Flags::SupportsAsyncEagerReturn;
270 uint32_t numInputs() const {
271 return
272 numArgs +
273 (hasUnpack() ? 1 : 0) +
274 (hasGenerics() ? 1 : 0);
276 const uint8_t* inoutArgs() const {
277 return enforceInOut() ?
278 reinterpret_cast<const uint8_t*>(&inoutArgsAndFlags) :
279 nullptr;
282 template<int nin>
283 uint32_t numPop() const {
284 return nin + numInputs() + 2 + numRets;
286 template<int nin, int nobj>
287 Flavor popFlavor(uint32_t i) const {
288 assert(i < this->template numPop<nin>());
289 if (i < nin) return Flavor::C;
290 i -= nin;
291 if (hasGenerics()) {
292 if (i == 0) return Flavor::C;
293 i--;
295 if (hasUnpack()) {
296 if (i == 0) return Flavor::C;
297 i--;
299 if (i < numArgs) return Flavor::C;
300 i -= numArgs;
301 if (i == 2 && nobj) return Flavor::C;
302 return Flavor::U;
304 FCallArgsBase base() const {
305 return FCallArgsBase{
306 flags, numArgs, numRets,
307 lockWhileUnwinding(), skipNumArgsCheck()
312 struct FCallArgsLong : FCallArgsBase {
313 explicit FCallArgsLong(uint32_t numArgs)
314 : FCallArgsLong(Flags::None, numArgs, 1,
315 nullptr, NoBlockId, false, false, nullptr) {
317 explicit FCallArgsLong(Flags flags, uint32_t numArgs, uint32_t numRets,
318 std::unique_ptr<uint8_t[]> inoutArgs,
319 BlockId asyncEagerTarget, bool lockWhileUnwinding,
320 bool skipNumArgsCheck, SString context)
321 : FCallArgsBase(flags, numArgs, numRets,
322 lockWhileUnwinding, skipNumArgsCheck)
323 , asyncEagerTarget(asyncEagerTarget)
324 , inoutArgs(std::move(inoutArgs))
325 , context(context) {
326 assertx(IMPLIES(asyncEagerTarget == NoBlockId,
327 !supportsAsyncEagerReturn()));
329 FCallArgsLong(const FCallArgsLong& o)
330 : FCallArgsLong(o.flags, o.numArgs, o.numRets, nullptr, o.asyncEagerTarget,
331 o.lockWhileUnwinding, o.skipNumArgsCheck, o.context) {
332 if (o.inoutArgs) {
333 auto const numBytes = (numArgs + 7) / 8;
334 inoutArgs = std::make_unique<uint8_t[]>(numBytes);
335 memcpy(inoutArgs.get(), o.inoutArgs.get(), numBytes);
338 FCallArgsLong(FCallArgsLong&& o)
339 : FCallArgsLong(o.flags, o.numArgs, o.numRets, std::move(o.inoutArgs),
340 o.asyncEagerTarget, o.lockWhileUnwinding,
341 o.skipNumArgsCheck, o.context) {}
343 bool enforceInOut() const { return inoutArgs.get() != nullptr; }
344 bool isInOut(uint32_t i) const {
345 assertx(enforceInOut());
346 return inoutArgs[i / 8] & (1 << (i % 8));
349 FCallArgsLong withoutGenerics() const {
350 auto fca = *this;
351 fca.flags = static_cast<Flags>(fca.flags & ~Flags::HasGenerics);
352 return fca;
355 FCallArgsLong withoutInOut() const {
356 auto fca = *this;
357 fca.inoutArgs = nullptr;
358 return fca;
360 FCallArgsLong withoutLockWhileUnwinding() const {
361 auto fca = *this;
362 fca.lockWhileUnwinding = false;
363 return fca;
366 FCallArgsLong fixEager(bool supports_eager) const {
367 auto fca = *this;
368 if (supports_eager) {
369 assertx(fca.asyncEagerTarget != NoBlockId);
370 fca.flags = static_cast<Flags>(
371 fca.flags | SupportsAsyncEagerReturn
373 } else {
374 fca.asyncEagerTarget = NoBlockId;
375 fca.flags = static_cast<Flags>(
376 fca.flags & ~SupportsAsyncEagerReturn
379 return fca;
381 FCallArgsLong withoutNumArgsCheck() const {
382 auto fca = *this;
383 fca.skipNumArgsCheck = true;
384 return fca;
386 template<typename F>
387 void applyIO(F f) const {
388 f((numArgs + 7) / 8, inoutArgs.get());
390 friend bool operator==(const FCallArgsLong& a, const FCallArgsLong& b) {
391 auto const eq = [&] (uint8_t* a, uint8_t* b, uint32_t bytes) {
392 if (a == nullptr && b == nullptr) return true;
393 if (a == nullptr || b == nullptr) return false;
394 return memcmp(a, b, bytes) == 0;
397 return
398 a.flags == b.flags && a.numArgs == b.numArgs && a.numRets == b.numRets &&
399 eq(a.inoutArgs.get(), b.inoutArgs.get(), (a.numArgs + 7) / 8) &&
400 a.asyncEagerTarget == b.asyncEagerTarget &&
401 a.lockWhileUnwinding == b.lockWhileUnwinding &&
402 a.skipNumArgsCheck == b.skipNumArgsCheck &&
403 a.context == b.context;
406 size_t hash() const {
407 uint64_t hash = HPHP::hash_int64_pair(numArgs, numRets);
408 hash = HPHP::hash_int64_pair(hash, flags);
409 if (auto const br = reinterpret_cast<const char*>(inoutArgs.get())) {
410 auto const hash_br = hash_string_cs(br, (numArgs + 7) / 8);
411 hash = HPHP::hash_int64_pair(hash, hash_br);
413 hash = HPHP::hash_int64_pair(hash, asyncEagerTarget);
414 hash = HPHP::hash_int64_pair(hash, lockWhileUnwinding);
415 if (context) hash = HPHP::hash_int64_pair(hash, context->hash());
416 return static_cast<size_t>(hash);
418 template<int nin>
419 uint32_t numPop() const {
420 return nin + numInputs() + 2 + numRets;
422 template<int nin, int nobj>
423 Flavor popFlavor(uint32_t i) const {
424 assert(i < this->template numPop<nin>());
425 if (i < nin) return Flavor::C;
426 i -= nin;
427 if (hasGenerics()) {
428 if (i == 0) return Flavor::C;
429 i--;
431 if (hasUnpack()) {
432 if (i == 0) return Flavor::C;
433 i--;
435 if (i < numArgs) return Flavor::C;
436 i -= numArgs;
437 if (i == 2 && nobj) return Flavor::C;
438 return Flavor::U;
440 FCallArgsBase base() const { return *this; }
442 BlockId asyncEagerTarget;
443 std::unique_ptr<uint8_t[]> inoutArgs;
444 SString context;
447 struct FCallArgs {
448 using Flags = FCallArgsBase::Flags;
449 explicit FCallArgs(uint32_t numArgs)
450 : FCallArgs(Flags::None, numArgs, 1, nullptr, NoBlockId, false, false,
451 nullptr) {}
452 FCallArgs(Flags flags, uint32_t numArgs, uint32_t numRets,
453 std::unique_ptr<uint8_t[]> inoutArgs,
454 BlockId asyncEagerTarget, bool lockWhileUnwinding,
455 bool skipNumArgsCheck, SString context)
456 : s{FCallArgsShort::create(flags, numArgs, numRets, inoutArgs,
457 asyncEagerTarget, lockWhileUnwinding,
458 skipNumArgsCheck, context)} {
459 if (!s.valid()) {
460 assertx(!l);
461 l.emplace(flags, numArgs, numRets, std::move(inoutArgs), asyncEagerTarget,
462 lockWhileUnwinding, skipNumArgsCheck, context);
465 FCallArgs(const FCallArgsShort& o) : s{o} {}
466 FCallArgs(const FCallArgsLong& o) : l{o} {}
467 FCallArgs(const FCallArgs& o) : l{} {
468 if (o.s.valid()) {
469 s = o.s;
470 } else {
471 l = o.l;
474 FCallArgs(FCallArgs&& o) : l{} {
475 if (o.s.valid()) {
476 s = std::move(o.s);
477 } else {
478 l = std::move(o.l);
481 FCallArgs& operator=(const FCallArgs& o) {
482 if (raw != o.raw) {
483 this->~FCallArgs();
484 new (this) FCallArgs{o};
486 return *this;
488 FCallArgs& operator=(FCallArgs&& o) {
489 assertx(raw != o.raw);
490 this->~FCallArgs();
491 new (this) FCallArgs{std::move(o)};
492 return *this;
494 ~FCallArgs() {
495 if (!s.valid()) l.~copy_ptr();
498 bool enforceInOut() const {
499 return s.valid() ? s.enforceInOut() : l->enforceInOut();
501 bool isInOut(uint32_t i) const {
502 assertx(enforceInOut());
503 return s.valid() ? s.isInOut(i) : l->isInOut(i);
506 FCallArgs withoutGenerics() const {
507 if (s.valid()) return s.withoutGenerics();
508 return l->withoutGenerics();
511 FCallArgs withoutInOut() const {
512 if (s.valid()) return s.withoutInOut();
513 return l->withoutInOut();
515 FCallArgs withoutLockWhileUnwinding() const {
516 if (s.valid()) return s.withoutLockWhileUnwinding();
517 return l->withoutLockWhileUnwinding();
519 FCallArgs fixEager(bool supports_eager) const {
520 if (s.valid()) return s.fixEager(supports_eager);
521 return l->fixEager(supports_eager);
523 FCallArgs withoutNumArgsCheck() const {
524 if (s.valid()) return s.withoutNumArgsCheck();
525 return l->withoutNumArgsCheck();
528 FCallArgsBase base() const {
529 return s.valid() ? s.base() : l->base();
532 friend bool operator==(const FCallArgs& a, const FCallArgs& b) {
533 if (a.s.valid()) {
534 if (!b.s.valid()) return false;
535 return a.s == b.s;
537 if (b.s.valid()) return false;
538 if (a.l.get() == b.l.get()) return true;
539 return *a.l == *b.l;
542 size_t hash() const {
543 return s.valid() ? s.hash() : l->hash();
546 uint32_t numArgs() const {
547 return s.valid() ? s.numArgs : l->numArgs;
550 uint32_t numRets() const {
551 return s.valid() ? s.numRets : l->numRets;
554 const uint8_t* inoutArgs() const {
555 return s.valid() ? s.inoutArgs() : l->inoutArgs.get();
557 template<typename F>
558 void applyIO(F f) const {
559 assertx(enforceInOut());
560 if (s.valid()) {
561 s.applyIO(f);
562 } else {
563 l->applyIO(f);
567 BlockId asyncEagerTarget() const {
568 return s.valid() ? s.asyncEagerTarget : l->asyncEagerTarget;
570 BlockId& asyncEagerTarget() {
571 return s.valid() ? s.asyncEagerTarget : l.mutate()->asyncEagerTarget;
573 bool lockWhileUnwinding() const {
574 return s.valid() ? s.lockWhileUnwinding() : l->lockWhileUnwinding;
576 bool hasUnpack() const {
577 return s.valid() ? s.hasUnpack() : l->hasUnpack();
579 bool hasGenerics() const {
580 return s.valid() ? s.hasGenerics() : l->hasGenerics();
582 uint32_t numInputs() const {
583 return s.valid() ? s.numInputs() : l->numInputs();
585 bool supportsAsyncEagerReturn() const {
586 return s.valid() ?
587 s.supportsAsyncEagerReturn() :
588 l->supportsAsyncEagerReturn();
590 bool skipNumArgsCheck() const {
591 return s.valid() ?
592 s.skipNumArgsCheck() :
593 l->skipNumArgsCheck;
595 template<int nin>
596 uint32_t numPop() const {
597 return s.valid() ? s.template numPop<nin>() : l->template numPop<nin>();
599 template<int nin, int nobj>
600 Flavor popFlavor(uint32_t i) const {
601 return s.valid() ?
602 s.template popFlavor<nin,nobj>(i) : l->template popFlavor<nin,nobj>(i);
604 SString context() const { return s.valid() ? nullptr : l->context; }
605 private:
606 union {
607 copy_ptr<FCallArgsLong> l;
608 FCallArgsShort s;
609 uint64_t raw;
613 inline bool operator!=(const FCallArgs& a, const FCallArgs& b) {
614 return !(a == b);
617 using SwitchTab = CompactVector<BlockId>;
619 // The final entry in the SSwitchTab is the default case, it will
620 // always have a nullptr for the string.
621 using SSwitchTabEnt = std::pair<LSString,BlockId>;
622 using SSwitchTab = CompactVector<SSwitchTabEnt>;
624 //////////////////////////////////////////////////////////////////////
626 namespace bc {
628 //////////////////////////////////////////////////////////////////////
630 namespace detail {
633 * Trivial hasher overrides, which will cause bytecodes to hash according to
634 * the hashes defined by hasher_impl.
636 struct hasher_default {};
639 * Default bytecode immediate hashers.
641 struct hasher_impl {
642 static size_t hash(SString s) { return s->hash(); }
643 static size_t hash(LSString s) { return s->hash(); }
644 static size_t hash(RepoAuthType rat) { return rat.hash(); }
646 static size_t hash(MKey mkey) {
647 return HPHP::hash_int64_pair(mkey.mcode, mkey.int64);
650 template<class T>
651 static size_t hash(const CompactVector<T>& v) {
652 return v.empty() ? 0 : v.size() ^ hash(v.front());
655 static size_t hash(std::pair<LSString,BlockId> kv) {
656 return HPHP::hash_int64_pair(kv.first->hash(), kv.second);
659 static size_t hash(NamedLocal loc) {
660 return HPHP::hash_int64((((uint64_t)loc.name) << 32) | loc.id);
663 static size_t hash(LocalRange range) {
664 return HPHP::hash_int64_pair(range.first, range.count);
667 static size_t hash(IterArgs ita) {
668 auto hash = HPHP::hash_int64_pair(ita.flags, ita.iterId);
669 hash = HPHP::hash_int64_pair(hash, ita.keyId);
670 return HPHP::hash_int64_pair(hash, ita.valId);
673 static size_t hash(FCallArgs fca) {
674 return fca.hash();
677 template<class T>
678 static typename std::enable_if<
679 std::is_enum<T>::value,
680 size_t
681 >::type hash(T t) {
682 using U = typename std::underlying_type<T>::type;
683 return std::hash<U>()(static_cast<U>(t));
686 template<class T>
687 static typename std::enable_if<
688 !std::is_enum<T>::value,
689 size_t
690 >::type hash(const T& t) { return std::hash<T>()(t); }
694 * Hash operand wrapper.
696 template<typename T, typename S>
697 struct hash_operand { const T& val; S type; };
699 // this template isn't really needed. its a workaround for T44007494
700 template<typename S> struct hash_operand<void*, S> { void* const val; S type; };
703 * Hash T using H::operator() if it is compatible, else fall back to
704 * hasher_impl (e.g., if H := hasher_default).
706 template<typename T, typename S, class H>
707 auto hash(const hash_operand<T,S>& t, H h) -> decltype(h(t.val)) {
708 return h(t.val);
710 template<typename T, typename S, class H>
711 auto hash(const hash_operand<T,S>& t, H h) -> decltype(h(t.val, t.type)) {
712 return h(t.val, t.type);
714 template<typename T, typename S, typename... Unused>
715 typename std::enable_if<sizeof...(Unused) == 1, size_t>::type
716 hash(const hash_operand<T,S>& t, Unused...) {
717 return hasher_impl::hash(t.val);
721 * Clone of folly::hash::hash_combine_generic.
723 template <class H>
724 size_t hash_combine(H /*h*/) {
725 return 0;
728 template<class H, typename T, typename... Ts>
729 size_t hash_combine(H h, const T& t, const Ts&... ts) {
730 auto const seed = size_t{hash(t, h)};
731 if (sizeof...(ts) == 0) return seed;
733 auto const remainder = hash_combine(h, ts...);
734 return static_cast<size_t>(folly::hash::hash_128_to_64(seed, remainder));
738 * Trivial equality overrides, which will cause bytecodes to compare via
739 * operator==() on the various immediate types.
741 struct eq_default {};
744 * Equality operand wrapper.
746 template<typename T, typename S>
747 struct eq_operand { const T& l; const T& r; S type; };
749 // this template isn't really needed. its a workaround for T44007494
750 template<typename S> struct eq_operand<void*, S> {
751 void* const l; void* const r; S type;
755 * Compare two values, using E::operator() if it exists, else the default
756 * operator==.
758 template<typename T, typename S, class E>
759 auto equals(const eq_operand<T,S>& t, E e) -> decltype(e(t.l, t.r)) {
760 return e(t.l, t.r);
762 template<typename T, typename S, class E>
763 auto equals(const eq_operand<T,S>& t, E e) -> decltype(e(t.l, t.r, t.type)) {
764 return e(t.l, t.r, t.type);
766 template<typename T, typename S, typename... Unused>
767 typename std::enable_if<sizeof...(Unused) == 1, bool>::type
768 equals(const eq_operand<T,S>& t, Unused...) {
769 return t.l == t.r;
773 * Check if a list of eq_operands are pairwise-equal.
775 template <class E>
776 bool eq_pairs(E /*e*/) {
777 return true;
780 template<class E, typename T, typename... Ts>
781 bool eq_pairs(E e, const T& t, const Ts&... ts) {
782 return equals(t, e) && (sizeof...(ts) == 0 || eq_pairs(e, ts...));
787 //////////////////////////////////////////////////////////////////////
790 * Bytecode immediate type tags.
792 namespace imm {
793 #define ARGTYPE(name, type) enum class name : uint8_t {};
794 #define ARGTYPEVEC(name, type) enum class name : uint8_t {};
795 ARGTYPES
796 #undef ARGTYPE
797 #undef ARGTYPEVEC
800 #define IMM_ID_BLA BLA
801 #define IMM_ID_SLA SLA
802 #define IMM_ID_IVA IVA
803 #define IMM_ID_I64A I64A
804 #define IMM_ID_LA LA
805 #define IMM_ID_NLA NLA
806 #define IMM_ID_ILA ILA
807 #define IMM_ID_IA IA
808 #define IMM_ID_DA DA
809 #define IMM_ID_SA SA
810 #define IMM_ID_RATA RATA
811 #define IMM_ID_AA AA
812 #define IMM_ID_BA BA
813 #define IMM_ID_OA(type) OA
814 #define IMM_ID_VSA VSA
815 #define IMM_ID_KA KA
816 #define IMM_ID_LAR LAR
817 #define IMM_ID_ITA ITA
818 #define IMM_ID_FCA FCA
820 #define IMM_TY_BLA SwitchTab
821 #define IMM_TY_SLA SSwitchTab
822 #define IMM_TY_IVA uint32_t
823 #define IMM_TY_I64A int64_t
824 #define IMM_TY_LA LocalId
825 #define IMM_TY_NLA NamedLocal
826 #define IMM_TY_ILA LocalId
827 #define IMM_TY_IA IterId
828 #define IMM_TY_DA double
829 #define IMM_TY_SA LSString
830 #define IMM_TY_RATA RepoAuthType
831 #define IMM_TY_AA SArray
832 #define IMM_TY_BA BlockId
833 #define IMM_TY_OA(type) type
834 #define IMM_TY_VSA CompactVector<LSString>
835 #define IMM_TY_KA MKey
836 #define IMM_TY_LAR LocalRange
837 #define IMM_TY_ITA IterArgs
838 #define IMM_TY_FCA FCallArgs
840 #define IMM_NAME_BLA(n) targets
841 #define IMM_NAME_SLA(n) targets
842 #define IMM_NAME_IVA(n) arg##n
843 #define IMM_NAME_I64A(n) arg##n
844 #define IMM_NAME_LA(n) loc##n
845 #define IMM_NAME_NLA(n) nloc##n
846 #define IMM_NAME_ILA(n) loc##n
847 #define IMM_NAME_IA(n) iter##n
848 #define IMM_NAME_DA(n) dbl##n
849 #define IMM_NAME_SA(n) str##n
850 #define IMM_NAME_RATA(n) rat
851 #define IMM_NAME_AA(n) arr##n
852 #define IMM_NAME_BA(n) target##n
853 #define IMM_NAME_OA_IMPL(n) subop##n
854 #define IMM_NAME_OA(type) IMM_NAME_OA_IMPL
855 #define IMM_NAME_VSA(n) keys
856 #define IMM_NAME_KA(n) mkey
857 #define IMM_NAME_LAR(n) locrange
858 #define IMM_NAME_ITA(n) ita
859 #define IMM_NAME_FCA(n) fca
861 #define IMM_TARGETS_BLA(n) for (auto& t : targets) f(t);
862 #define IMM_TARGETS_SLA(n) for (auto& kv : targets) f(kv.second);
863 #define IMM_TARGETS_IVA(n)
864 #define IMM_TARGETS_I64A(n)
865 #define IMM_TARGETS_LA(n)
866 #define IMM_TARGETS_NLA(n)
867 #define IMM_TARGETS_ILA(n)
868 #define IMM_TARGETS_IA(n)
869 #define IMM_TARGETS_DA(n)
870 #define IMM_TARGETS_SA(n)
871 #define IMM_TARGETS_RATA(n)
872 #define IMM_TARGETS_AA(n)
873 #define IMM_TARGETS_BA(n) f(target##n);
874 #define IMM_TARGETS_OA_IMPL(n)
875 #define IMM_TARGETS_OA(type) IMM_TARGETS_OA_IMPL
876 #define IMM_TARGETS_VSA(n)
877 #define IMM_TARGETS_KA(n)
878 #define IMM_TARGETS_LAR(n)
879 #define IMM_TARGETS_ITA(n)
880 #define IMM_TARGETS_FCA(n) if (fca.asyncEagerTarget() != NoBlockId) { \
881 f(fca.asyncEagerTarget()); \
884 #define IMM_EXTRA_BLA
885 #define IMM_EXTRA_SLA
886 #define IMM_EXTRA_IVA
887 #define IMM_EXTRA_I64A
888 #define IMM_EXTRA_LA
889 #define IMM_EXTRA_NLA
890 #define IMM_EXTRA_ILA
891 #define IMM_EXTRA_IA
892 #define IMM_EXTRA_DA
893 #define IMM_EXTRA_SA
894 #define IMM_EXTRA_RATA
895 #define IMM_EXTRA_AA
896 #define IMM_EXTRA_BA
897 #define IMM_EXTRA_OA(x)
898 #define IMM_EXTRA_VSA
899 #define IMM_EXTRA_KA
900 #define IMM_EXTRA_LAR
901 #define IMM_EXTRA_ITA
902 #define IMM_EXTRA_FCA
904 #define IMM_MEM(which, n) IMM_TY_##which IMM_NAME_##which(n)
905 #define IMM_MEM_NA
906 #define IMM_MEM_ONE(x) IMM_MEM(x, 1);
907 #define IMM_MEM_TWO(x, y) IMM_MEM(x, 1); IMM_MEM(y, 2);
908 #define IMM_MEM_THREE(x, y, z) IMM_MEM(x, 1); IMM_MEM(y, 2); \
909 IMM_MEM(z, 3);
910 #define IMM_MEM_FOUR(x, y, z, l) IMM_MEM(x, 1); IMM_MEM(y, 2); \
911 IMM_MEM(z, 3); IMM_MEM(l, 4);
912 #define IMM_MEM_FIVE(x, y, z, l, m) IMM_MEM(x, 1); IMM_MEM(y, 2); \
913 IMM_MEM(z, 3); IMM_MEM(l, 4); \
914 IMM_MEM(m, 5);
915 #define IMM_MEM_SIX(x, y, z, l, m, n) IMM_MEM(x, 1); IMM_MEM(y, 2); \
916 IMM_MEM(z, 3); IMM_MEM(l, 4); \
917 IMM_MEM(m, 5); IMM_MEM(n, 6);
919 #define IMM_EQ_WRAP(e, ...) detail::eq_pairs(e, __VA_ARGS__)
920 #define IMM_EQ(which, n) detail::eq_operand< \
921 IMM_TY_##which, \
922 imm::IMM_ID_##which \
923 > { \
924 IMM_NAME_##which(n), \
925 o.IMM_NAME_##which(n) \
927 #define IMM_EQ_NA detail::eq_operand<void*,imm::NA> { 0, 0 }
928 #define IMM_EQ_ONE(x) IMM_EQ(x, 1)
929 #define IMM_EQ_TWO(x, y) IMM_EQ_ONE(x), IMM_EQ(y, 2)
930 #define IMM_EQ_THREE(x, y, z) IMM_EQ_TWO(x, y), IMM_EQ(z, 3)
931 #define IMM_EQ_FOUR(x, y, z, l) IMM_EQ_THREE(x, y, z), IMM_EQ(l, 4)
932 #define IMM_EQ_FIVE(x, y, z, l, m) IMM_EQ_FOUR(x, y, z, l), IMM_EQ(m, 5)
933 #define IMM_EQ_SIX(x, y, z, l, m, n) IMM_EQ_FIVE(x, y, z, l, m), IMM_EQ(n, 6)
935 #define IMM_HASH_WRAP(h, ...) detail::hash_combine(h, __VA_ARGS__)
936 #define IMM_HASH(which, n) detail::hash_operand< \
937 IMM_TY_##which, \
938 imm::IMM_ID_##which \
939 > { IMM_NAME_##which(n) }
940 #define IMM_HASH_NA detail::hash_operand<void*,imm::NA> { 0 }
941 #define IMM_HASH_ONE(x) IMM_HASH(x, 1)
942 #define IMM_HASH_TWO(x, y) IMM_HASH_ONE(x), IMM_HASH(y, 2)
943 #define IMM_HASH_THREE(x, y, z) IMM_HASH_TWO(x, y), IMM_HASH(z, 3)
944 #define IMM_HASH_FOUR(x, y, z, l) IMM_HASH_THREE(x, y, z), IMM_HASH(l, 4)
945 #define IMM_HASH_FIVE(x, y, z, l, m) IMM_HASH_FOUR(x, y, z, l), IMM_HASH(m, 5)
946 #define IMM_HASH_SIX(x, y, z, l, m, n) IMM_HASH_FIVE(x, y, z, l, m), IMM_HASH(n, 6)
948 #define IMM_TARGETS_NA
949 #define IMM_TARGETS_ONE(x) IMM_TARGETS_##x(1)
950 #define IMM_TARGETS_TWO(x,y) IMM_TARGETS_ONE(x) IMM_TARGETS_##y(2)
951 #define IMM_TARGETS_THREE(x,y,z) IMM_TARGETS_TWO(x,y) IMM_TARGETS_##z(3)
952 #define IMM_TARGETS_FOUR(x,y,z,l) IMM_TARGETS_THREE(x,y,z) IMM_TARGETS_##l(4)
953 #define IMM_TARGETS_FIVE(x,y,z,l,m) IMM_TARGETS_FOUR(x,y,z,l) IMM_TARGETS_##m(5)
954 #define IMM_TARGETS_SIX(x,y,z,l,m,n) IMM_TARGETS_FIVE(x,y,z,l,m) IMM_TARGETS_##n(6)
956 #define IMM_EXTRA_NA
957 #define IMM_EXTRA_ONE(x) IMM_EXTRA_##x
958 #define IMM_EXTRA_TWO(x,y) IMM_EXTRA_ONE(x) IMM_EXTRA_ONE(y)
959 #define IMM_EXTRA_THREE(x,y,z) IMM_EXTRA_TWO(x,y) IMM_EXTRA_ONE(z)
960 #define IMM_EXTRA_FOUR(x,y,z,l) IMM_EXTRA_THREE(x,y,z) IMM_EXTRA_ONE(l)
961 #define IMM_EXTRA_FIVE(x,y,z,l,m) IMM_EXTRA_FOUR(x,y,z,l) IMM_EXTRA_ONE(m)
962 #define IMM_EXTRA_SIX(x,y,z,l,m,n) IMM_EXTRA_FIVE(x,y,z,l,m) IMM_EXTRA_ONE(n)
964 #define IMM_CTOR(which, n) IMM_TY_##which IMM_NAME_##which(n)
965 #define IMM_CTOR_NA
966 #define IMM_CTOR_ONE(x) IMM_CTOR(x, 1)
967 #define IMM_CTOR_TWO(x, y) IMM_CTOR(x, 1), IMM_CTOR(y, 2)
968 #define IMM_CTOR_THREE(x, y, z) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
969 IMM_CTOR(z, 3)
970 #define IMM_CTOR_FOUR(x, y, z, l) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
971 IMM_CTOR(z, 3), IMM_CTOR(l, 4)
972 #define IMM_CTOR_FIVE(x, y, z, l, m) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
973 IMM_CTOR(z, 3), IMM_CTOR(l, 4), \
974 IMM_CTOR(m, 5)
975 #define IMM_CTOR_SIX(x, y, z, l, m, n) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
976 IMM_CTOR(z, 3), IMM_CTOR(l, 4), \
977 IMM_CTOR(m, 5), IMM_CTOR(n, 6)
979 #define IMM_INIT(which, n) IMM_NAME_##which(n) \
980 ( std::move(IMM_NAME_##which(n)) )
981 #define IMM_INIT_NA
982 #define IMM_INIT_ONE(x) : IMM_INIT(x, 1)
983 #define IMM_INIT_TWO(x, y) : IMM_INIT(x, 1), IMM_INIT(y, 2)
984 #define IMM_INIT_THREE(x, y, z) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
985 IMM_INIT(z, 3)
986 #define IMM_INIT_FOUR(x, y, z, l) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
987 IMM_INIT(z, 3), IMM_INIT(l, 4)
988 #define IMM_INIT_FIVE(x, y, z, l, m) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
989 IMM_INIT(z, 3), IMM_INIT(l, 4), \
990 IMM_INIT(m, 5)
991 #define IMM_INIT_SIX(x, y, z, l, m, n) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
992 IMM_INIT(z, 3), IMM_INIT(l, 4), \
993 IMM_INIT(m, 5), IMM_INIT(n, 6)
995 #define POP_UV if (i == 0) return Flavor::U
996 #define POP_CV if (i == 0) return Flavor::C
997 #define POP_CU if (i == 0) return Flavor::CU
998 #define POP_CUV POP_CU
1000 #define POP_NOV uint32_t numPop() const { return 0; } \
1001 Flavor popFlavor(uint32_t) const { not_reached(); }
1003 #define POP_ONE(x) uint32_t numPop() const { return 1; } \
1004 Flavor popFlavor(uint32_t i) const { \
1005 POP_##x; not_reached(); \
1008 #define POP_TWO(x, y) uint32_t numPop() const { return 2; } \
1009 Flavor popFlavor(uint32_t i) const { \
1010 POP_##x; --i; POP_##y; not_reached(); \
1013 #define POP_THREE(x, y, z) uint32_t numPop() const { return 3; } \
1014 Flavor popFlavor(uint32_t i) const { \
1015 POP_##x; --i; POP_##y; --i; POP_##z; \
1016 not_reached(); \
1019 #define POP_MFINAL uint32_t numPop() const { return arg1; } \
1020 Flavor popFlavor(uint32_t) const { not_reached(); }
1022 #define POP_C_MFINAL(n) uint32_t numPop() const { return arg1 + n; } \
1023 Flavor popFlavor(uint32_t) const { not_reached(); }
1025 #define POP_CMANY uint32_t numPop() const { return arg1; } \
1026 Flavor popFlavor(uint32_t i) const { \
1027 assert(i < numPop()); \
1028 return Flavor::C; \
1031 #define POP_SMANY uint32_t numPop() const { return keys.size(); } \
1032 Flavor popFlavor(uint32_t i) const { \
1033 assert(i < numPop()); \
1034 return Flavor::C; \
1037 #define POP_CUMANY uint32_t numPop() const { return arg1; } \
1038 Flavor popFlavor(uint32_t i) const { \
1039 assert(i < numPop()); \
1040 return Flavor::CU; \
1043 #define POP_CMANY_U3 \
1044 uint32_t numPop() const { return arg1 + 3; } \
1045 Flavor popFlavor(uint32_t i) const { \
1046 assert(i < numPop()); \
1047 return i < arg1 ? Flavor::C : Flavor::U; \
1050 #define POP_CALLNATIVE uint32_t numPop() const { return arg1 + arg3; } \
1051 Flavor popFlavor(uint32_t i) const { \
1052 assert(i < numPop()); \
1053 return i < arg1 ? Flavor::CU : Flavor::U; \
1056 #define POP_FCALL(nin, nobj) \
1057 uint32_t numPop() const { \
1058 return fca.template numPop<nin>(); \
1060 Flavor popFlavor(uint32_t i) const { \
1061 return fca.template popFlavor<nin,nobj>(i); \
1064 #define PUSH_NOV uint32_t numPush() const { return 0; }
1066 #define PUSH_ONE(x) uint32_t numPush() const { return 1; }
1068 #define PUSH_TWO(x, y) uint32_t numPush() const { return 2; }
1070 #define PUSH_CMANY uint32_t numPush() const { return arg1; }
1071 #define PUSH_FCALL uint32_t numPush() const { return fca.numRets(); }
1072 #define PUSH_CALLNATIVE uint32_t numPush() const { return arg3 + 1; }
1074 #define FLAGS_NF
1075 #define FLAGS_TF
1076 #define FLAGS_CF
1077 #define FLAGS_FF
1078 #define FLAGS_CF_TF
1079 #define FLAGS_CF_FF
1081 #define FLAGS_CTOR_NF
1082 #define FLAGS_CTOR_TF
1083 #define FLAGS_CTOR_CF
1084 #define FLAGS_CTOR_FF
1085 #define FLAGS_CTOR_CF_TF
1086 #define FLAGS_CTOR_CF_FF
1088 #define FLAGS_INIT_NF
1089 #define FLAGS_INIT_TF
1090 #define FLAGS_INIT_CF
1091 #define FLAGS_INIT_FF
1092 #define FLAGS_INIT_CF_TF
1093 #define FLAGS_INIT_CF_FF
1095 #define O(opcode, imms, inputs, outputs, flags) \
1096 struct opcode { \
1097 static constexpr Op op = Op::opcode; \
1098 explicit opcode ( IMM_CTOR_##imms \
1099 FLAGS_CTOR_##flags) \
1100 IMM_INIT_##imms \
1101 FLAGS_INIT_##flags \
1102 {} \
1104 IMM_MEM_##imms \
1105 FLAGS_##flags \
1106 IMM_EXTRA_##imms \
1107 POP_##inputs \
1108 PUSH_##outputs \
1110 bool operator==(const opcode& o) const { \
1111 return equals(o, detail::eq_default{}); \
1114 bool operator!=(const opcode& o) const { \
1115 return !(*this == o); \
1118 template<class E> \
1119 bool equals(const opcode& o, E e) const { \
1120 return IMM_EQ_WRAP(e, IMM_EQ_##imms); \
1123 size_t hash() const { \
1124 return IMM_HASH_WRAP( \
1125 detail::hasher_default{}, \
1126 IMM_HASH_##imms); \
1129 template<class H> \
1130 size_t hash(H h) const { \
1131 return IMM_HASH_WRAP(h, IMM_HASH_##imms); \
1134 template <typename F> \
1135 void forEachTarget(F&& f) { \
1136 IMM_TARGETS_##imms \
1138 template <typename F> \
1139 void forEachTarget(F&& f) const { \
1140 IMM_TARGETS_##imms \
1143 OPCODES
1144 #undef O
1146 #undef FLAGS_NA
1147 #undef FLAGS_TF
1148 #undef FLAGS_CF
1149 #undef FLAGS_FF
1150 #undef FLAGS_CF_TF
1151 #undef FLAGS_CF_FF
1153 #undef FLAGS_CTOR_NA
1154 #undef FLAGS_CTOR_TF
1155 #undef FLAGS_CTOR_CF
1156 #undef FLAGS_CTOR_FF
1157 #undef FLAGS_CTOR_CF_TF
1158 #undef FLAGS_CTOR_CF_FF
1160 #undef FLAGS_INIT_NA
1161 #undef FLAGS_INIT_TF
1162 #undef FLAGS_INIT_CF
1163 #undef FLAGS_INIT_FF
1164 #undef FLAGS_INIT_CF_TF
1165 #undef FLAGS_INIT_CF_FF
1167 #undef PUSH_NOV
1168 #undef PUSH_ONE
1169 #undef PUSH_TWO
1170 #undef PUSH_CMANY
1171 #undef PUSH_FCALL
1172 #undef PUSH_CALLNATIVE
1174 #undef POP_UV
1175 #undef POP_CV
1176 #undef POP_CU
1177 #undef POP_CUV
1179 #undef POP_NOV
1180 #undef POP_ONE
1181 #undef POP_TWO
1182 #undef POP_THREE
1183 #undef POP_MFINAL
1184 #undef POP_C_MFINAL
1185 #undef POP_CMANY
1186 #undef POP_SMANY
1187 #undef POP_CUMANY
1188 #undef POP_CMANY_U3
1189 #undef POP_CALLNATIVE
1190 #undef POP_FCALL
1192 #undef IMM_TY_MA
1193 #undef IMM_TY_BLA
1194 #undef IMM_TY_SLA
1195 #undef IMM_TY_IVA
1196 #undef IMM_TY_I64A
1197 #undef IMM_TY_LA
1198 #undef IMM_TY_NLA
1199 #undef IMM_TY_ILA
1200 #undef IMM_TY_IA
1201 #undef IMM_TY_DA
1202 #undef IMM_TY_SA
1203 #undef IMM_TY_RATA
1204 #undef IMM_TY_AA
1205 #undef IMM_TY_BA
1206 #undef IMM_TY_OA
1207 #undef IMM_TY_VSA
1208 #undef IMM_TY_KA
1209 #undef IMM_TY_LAR
1210 #undef IMM_TY_ITA
1211 #undef IMM_TY_FCA
1213 // These are deliberately not undefined, so they can be used in other
1214 // places.
1215 // #undef IMM_NAME_BLA
1216 // #undef IMM_NAME_SLA
1217 // #undef IMM_NAME_IVA
1218 // #undef IMM_NAME_I64A
1219 // #undef IMM_NAME_LA
1220 // #undef IMM_NAME_NLA
1221 // #undef IMM_NAME_ILA
1222 // #undef IMM_NAME_IA
1223 // #undef IMM_NAME_DA
1224 // #undef IMM_NAME_SA
1225 // #undef IMM_NAME_RATA
1226 // #undef IMM_NAME_AA
1227 // #undef IMM_NAME_BA
1228 // #undef IMM_NAME_OA
1229 // #undef IMM_NAME_OA_IMPL
1230 // #undef IMM_NAME_LAR
1231 // #undef IMM_NAME_FCA
1233 #undef IMM_TARGETS_BLA
1234 #undef IMM_TARGETS_SLA
1235 #undef IMM_TARGETS_IVA
1236 #undef IMM_TARGETS_I64A
1237 #undef IMM_TARGETS_LA
1238 #undef IMM_TARGETS_NLA
1239 #undef IMM_TARGETS_ILA
1240 #undef IMM_TARGETS_IA
1241 #undef IMM_TARGETS_DA
1242 #undef IMM_TARGETS_SA
1243 #undef IMM_TARGETS_RATA
1244 #undef IMM_TARGETS_AA
1245 #undef IMM_TARGETS_BA
1246 #undef IMM_TARGETS_OA
1247 #undef IMM_TARGETS_KA
1248 #undef IMM_TARGETS_LAR
1249 #undef IMM_TARGETS_ITA
1250 #undef IMM_TARGETS_FCA
1252 #undef IMM_TARGETS_NA
1253 #undef IMM_TARGETS_ONE
1254 #undef IMM_TARGETS_TWO
1255 #undef IMM_TARGETS_THREE
1256 #undef IMM_TARGETS_FOUR
1257 #undef IMM_TARGETS_FIVE
1258 #undef IMM_TARGETS_SIX
1260 #undef IMM_EXTRA_BLA
1261 #undef IMM_EXTRA_SLA
1262 #undef IMM_EXTRA_IVA
1263 #undef IMM_EXTRA_I64A
1264 #undef IMM_EXTRA_LA
1265 #undef IMM_EXTRA_NLA
1266 #undef IMM_EXTRA_ILA
1267 #undef IMM_EXTRA_IA
1268 #undef IMM_EXTRA_DA
1269 #undef IMM_EXTRA_SA
1270 #undef IMM_EXTRA_RATA
1271 #undef IMM_EXTRA_AA
1272 #undef IMM_EXTRA_BA
1273 #undef IMM_EXTRA_OA
1274 #undef IMM_EXTRA_KA
1275 #undef IMM_EXTRA_LAR
1276 #undef IMM_EXTRA_ITA
1277 #undef IMM_EXTRA_FCA
1279 #undef IMM_MEM
1280 #undef IMM_MEM_NA
1281 #undef IMM_MEM_ONE
1282 #undef IMM_MEM_TWO
1283 #undef IMM_MEM_THREE
1284 #undef IMM_MEM_FOUR
1285 #undef IMM_MEM_FIVE
1286 #undef IMM_MEM_SIX
1288 #undef IMM_EQ
1289 #undef IMM_EQ_NA
1290 #undef IMM_EQ_ONE
1291 #undef IMM_EQ_TWO
1292 #undef IMM_EQ_THREE
1293 #undef IMM_EQ_FOUR
1294 #undef IMM_EQ_FIVE
1295 #undef IMM_EQ_SIX
1297 #undef IMM_HASH
1298 #undef IMM_HASH_DO
1299 #undef IMM_HASH_NA
1300 #undef IMM_HASH_ONE
1301 #undef IMM_HASH_TWO
1302 #undef IMM_HASH_THREE
1303 #undef IMM_HASH_FOUR
1304 #undef IMM_HASH_FIVE
1305 #undef IMM_HASH_SIX
1307 #undef IMM_CTOR
1308 #undef IMM_CTOR_NA
1309 #undef IMM_CTOR_ONE
1310 #undef IMM_CTOR_TWO
1311 #undef IMM_CTOR_THREE
1312 #undef IMM_CTOR_FOUR
1313 #undef IMM_CTOR_FIVE
1314 #undef IMM_CTOR_SIX
1316 #undef IMM_INIT
1317 #undef IMM_INIT_NA
1318 #undef IMM_INIT_ONE
1319 #undef IMM_INIT_TWO
1320 #undef IMM_INIT_THREE
1321 #undef IMM_INIT_FOUR
1325 //////////////////////////////////////////////////////////////////////
1328 * Bytecode is a tagged-union that can hold any HHBC opcode struct
1329 * defined above. You can visit a bytecode with a static_visitor
1330 * using visit(), defined below.
1332 * Each Bytecode also carries a corresponding SrcLoc that indicates
1333 * which line of PHP code the bytecode was generated for.
1335 struct Bytecode {
1336 // Default construction creates a Nop.
1337 Bytecode()
1338 : op(Op::Nop)
1339 , Nop(bc::Nop{})
1342 #define O(opcode, ...) \
1343 /* implicit */ Bytecode(bc::opcode data) \
1344 : op(Op::opcode) \
1346 new (&opcode) bc::opcode(std::move(data)); \
1349 OPCODES
1351 #undef O
1353 // Note: assuming bc::Nop is empty and has trivial dtor/ctor.
1355 Bytecode(const Bytecode& o) : op(Op::Nop) { *this = o; }
1356 Bytecode(Bytecode&& o) noexcept : op(Op::Nop) { *this = std::move(o); }
1358 Bytecode& operator=(const Bytecode& o) {
1359 destruct();
1360 op = Op::Nop;
1361 srcLoc = o.srcLoc;
1362 #define O(opcode, ...) \
1363 case Op::opcode: new (&opcode) bc::opcode(o.opcode); break;
1364 switch (o.op) { OPCODES }
1365 #undef O
1366 op = o.op;
1367 return *this;
1370 Bytecode& operator=(Bytecode&& o) {
1371 destruct();
1372 srcLoc = o.srcLoc;
1373 #define O(opcode, ...) \
1374 case Op::opcode: new (&opcode) bc::opcode(std::move(o.opcode)); break;
1375 switch (o.op) { OPCODES }
1376 #undef O
1377 op = o.op;
1378 return *this;
1381 ~Bytecode() { destruct(); }
1383 uint32_t numPop() const {
1384 #define O(opcode, ...) \
1385 case Op::opcode: return opcode.numPop();
1386 switch (op) { OPCODES }
1387 #undef O
1388 not_reached();
1391 Flavor popFlavor(uint32_t i) const {
1392 #define O(opcode, ...) \
1393 case Op::opcode: return opcode.popFlavor(i);
1394 switch (op) { OPCODES }
1395 #undef O
1396 not_reached();
1399 uint32_t numPush() const {
1400 #define O(opcode, ...) \
1401 case Op::opcode: return opcode.numPush();
1402 switch (op) { OPCODES }
1403 #undef O
1404 not_reached();
1407 template <typename F>
1408 void forEachTarget(F&& f) const {
1409 #define O(opcode, ...) \
1410 case Op::opcode: return opcode.forEachTarget(std::forward<F>(f));
1411 switch (op) { OPCODES }
1412 #undef O
1413 not_reached();
1416 template <typename F>
1417 void forEachTarget(F&& f) {
1418 #define O(opcode, ...) \
1419 case Op::opcode: return opcode.forEachTarget(std::forward<F>(f));
1420 switch (op) { OPCODES }
1421 #undef O
1422 not_reached();
1425 Op op;
1426 int32_t srcLoc{-1};
1428 #define O(opcode, ...) bc::opcode opcode;
1429 union { OPCODES };
1430 #undef O
1432 private:
1433 void destruct() {
1434 switch (op) {
1435 #define O(opcode, ...) \
1436 case Op::opcode: \
1437 { typedef bc::opcode X; \
1438 this->opcode.~X(); } \
1439 break;
1440 OPCODES
1441 #undef O
1446 //////////////////////////////////////////////////////////////////////
1448 inline bool operator==(const Bytecode& a, const Bytecode& b) {
1449 if (a.op != b.op) return false;
1450 #define O(opcode, ...) case Op::opcode: return a.opcode == b.opcode;
1451 switch (a.op) { OPCODES }
1452 #undef O
1453 not_reached();
1456 inline bool operator!=(const Bytecode& a, const Bytecode& b) {
1457 return !(a == b);
1460 template<class E>
1461 inline bool equals(const Bytecode& a, const Bytecode& b, E equals) {
1462 if (a.op != b.op) return false;
1463 #define O(opcode, ...) \
1464 case Op::opcode: return a.opcode.equals(b.opcode, equals);
1465 switch (a.op) { OPCODES }
1466 #undef O
1467 not_reached();
1470 template<class H>
1471 inline size_t hash(const Bytecode& b, H hasher) {
1472 auto hash = 14695981039346656037ULL;
1473 auto o = static_cast<size_t>(b.op);
1474 hash ^= o;
1475 #define O(opcode, ...) \
1476 case Op::opcode: \
1477 return folly::hash::hash_combine(b.opcode.hash(hasher), hash);
1478 switch (b.op) { OPCODES }
1479 #undef O
1480 not_reached();
1483 inline size_t hash(const Bytecode& b) {
1484 return hash(b, bc::detail::hasher_default{});
1487 //////////////////////////////////////////////////////////////////////
1490 * Helper for making a Bytecode with a given srcLoc.
1492 * Ex:
1493 * auto b = bc_with_loc(something.srcLoc, bc::Nop {});
1495 template<class T> Bytecode bc_with_loc(int32_t loc, const T& t) {
1496 Bytecode b = t;
1497 b.srcLoc = loc;
1498 return b;
1501 //////////////////////////////////////////////////////////////////////
1504 * Visit a bytecode using a StaticVisitor, similar to
1505 * boost::apply_visitor or match().
1507 * The `v' argument should be a function object that accepts a call
1508 * operator for all the bc::Foo types. Its result type should be
1509 * independent of bytecode, but may vary with constness.
1511 template<class Visit>
1512 auto visit(Bytecode& b, Visit v) {
1513 #define O(opcode, ...) case Op::opcode: return v(b.opcode);
1514 switch (b.op) { OPCODES }
1515 #undef O
1516 not_reached();
1519 template<class Visit>
1520 auto visit(const Bytecode& b, Visit v) {
1521 #define O(opcode, ...) case Op::opcode: return v(b.opcode);
1522 switch (b.op) { OPCODES }
1523 #undef O
1524 not_reached();
1527 //////////////////////////////////////////////////////////////////////
1533 #endif