Workaround a gcc9.1 bug in hhbbc
[hiphop-php.git] / hphp / hhbbc / bc.h
blobb432f5f32f2012369d6f2c22e08a8974e18d6b8a
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 MKey {
53 MKey()
54 : mcode{MW}
55 , int64{0}
58 MKey(MemberCode mcode, LocalId local)
59 : mcode{mcode}
60 , local{local}
63 MKey(MemberCode mcode, int32_t idx)
64 : mcode{mcode}
65 , idx{idx}
68 MKey(MemberCode mcode, int64_t int64)
69 : mcode{mcode}
70 , int64{int64}
73 MKey(MemberCode mcode, SString litstr)
74 : mcode{mcode}
75 , litstr{litstr}
78 MemberCode mcode;
79 union {
80 SString litstr;
81 int64_t int64;
82 int64_t idx;
83 LocalId local;
87 inline bool operator==(MKey a, MKey b) {
88 return a.mcode == b.mcode && a.int64 == b.int64;
91 inline bool operator!=(MKey a, MKey b) {
92 return !(a == b);
95 // A contiguous range of locals. The count is the number of locals including the
96 // first. If the range is empty, count will be zero and first's value is
97 // arbitrary.
98 struct LocalRange {
99 LocalId first;
100 uint32_t count;
103 inline bool operator==(const LocalRange& a, const LocalRange& b) {
104 return a.first == b.first && a.count == b.count;
107 inline bool operator!=(const LocalRange& a, const LocalRange& b) {
108 return !(a == b);
111 struct FCallArgs : FCallArgsBase {
112 explicit FCallArgs(uint32_t numArgs)
113 : FCallArgs(Flags::None, numArgs, 1, nullptr, NoBlockId) {}
114 explicit FCallArgs(Flags flags, uint32_t numArgs, uint32_t numRets,
115 std::unique_ptr<uint8_t[]> byRefs,
116 BlockId asyncEagerTarget)
117 : FCallArgsBase(flags, numArgs, numRets)
118 , asyncEagerTarget(asyncEagerTarget)
119 , byRefs(std::move(byRefs)) {
120 assertx(IMPLIES(asyncEagerTarget == NoBlockId,
121 !supportsAsyncEagerReturn()));
123 FCallArgs(const FCallArgs& o)
124 : FCallArgs(o.flags, o.numArgs, o.numRets, nullptr, o.asyncEagerTarget) {
125 if (o.byRefs) {
126 auto const numBytes = (numArgs + 7) / 8;
127 byRefs = std::make_unique<uint8_t[]>(numBytes);
128 memcpy(byRefs.get(), o.byRefs.get(), numBytes);
131 FCallArgs(FCallArgs&& o)
132 : FCallArgs(o.flags, o.numArgs, o.numRets, std::move(o.byRefs),
133 o.asyncEagerTarget) {}
135 bool enforceReffiness() const { return byRefs.get() != nullptr; }
136 bool byRef(uint32_t i) const {
137 assertx(enforceReffiness());
138 return byRefs[i / 8] & (1 << (i % 8));
140 BlockId asyncEagerTarget;
141 std::unique_ptr<uint8_t[]> byRefs;
144 inline bool operator==(const FCallArgs& a, const FCallArgs& b) {
145 auto const eq = [&] (uint8_t* a, uint8_t* b, uint32_t bytes) {
146 if (a == nullptr && b == nullptr) return true;
147 if (a == nullptr || b == nullptr) return false;
148 return memcmp(a, b, bytes) == 0;
151 return
152 a.flags == b.flags && a.numArgs == b.numArgs && a.numRets == b.numRets &&
153 eq(a.byRefs.get(), b.byRefs.get(), (a.numArgs + 7 / 8)) &&
154 a.asyncEagerTarget == b.asyncEagerTarget;
157 inline bool operator!=(const FCallArgs& a, const FCallArgs& b) {
158 return !(a == b);
161 struct IterTabEnt {
162 IterKind kind;
163 IterId id;
164 LocalId local;
167 inline bool operator==(const IterTabEnt& a, const IterTabEnt& b) {
168 return std::tie(a.kind, a.id, a.local) == std::tie(b.kind, b.id, b.local);
171 inline bool operator!=(const IterTabEnt& a, const IterTabEnt& b) {
172 return !(a == b);
175 using IterTab = CompactVector<IterTabEnt>;
177 using SwitchTab = CompactVector<BlockId>;
179 // The final entry in the SSwitchTab is the default case, it will
180 // always have a nullptr for the string.
181 using SSwitchTabEnt = std::pair<LSString,BlockId>;
182 using SSwitchTab = CompactVector<SSwitchTabEnt>;
184 //////////////////////////////////////////////////////////////////////
186 namespace bc {
188 //////////////////////////////////////////////////////////////////////
190 namespace detail {
193 * Trivial hasher overrides, which will cause bytecodes to hash according to
194 * the hashes defined by hasher_impl.
196 struct hasher_default {};
199 * Default bytecode immediate hashers.
201 struct hasher_impl {
202 static size_t hash(SString s) { return s->hash(); }
203 static size_t hash(LSString s) { return s->hash(); }
204 static size_t hash(RepoAuthType rat) { return rat.hash(); }
206 static size_t hash(const IterTabEnt& iterTab) {
207 auto const partial = folly::hash::hash_128_to_64(
208 iterTab.kind, iterTab.id
210 return static_cast<size_t>(
211 folly::hash::hash_128_to_64(iterTab.local, partial)
215 static size_t hash(MKey mkey) {
216 return HPHP::hash_int64_pair(mkey.mcode, mkey.int64);
219 template<class T>
220 static size_t hash(const CompactVector<T>& v) {
221 return v.empty() ? 0 : v.size() ^ hash(v.front());
224 static size_t hash(std::pair<LSString,BlockId> kv) {
225 return HPHP::hash_int64_pair(kv.first->hash(), kv.second);
228 static size_t hash(LocalRange range) {
229 return HPHP::hash_int64_pair(range.first, range.count);
232 static size_t hash(FCallArgs fca) {
233 uint64_t hash = HPHP::hash_int64_pair(fca.numArgs, fca.numRets);
234 hash = HPHP::hash_int64_pair(hash, fca.flags);
235 if (fca.byRefs) {
236 auto const br = reinterpret_cast<char*>(fca.byRefs.get());
237 auto const hash_br = hash_string_cs(br, (fca.numArgs + 7 / 8));
238 hash = HPHP::hash_int64_pair(hash, hash_br);
240 hash = HPHP::hash_int64_pair(hash, fca.asyncEagerTarget);
241 return static_cast<size_t>(hash);
244 template<class T>
245 static typename std::enable_if<
246 std::is_enum<T>::value,
247 size_t
248 >::type hash(T t) {
249 using U = typename std::underlying_type<T>::type;
250 return std::hash<U>()(static_cast<U>(t));
253 template<class T>
254 static typename std::enable_if<
255 !std::is_enum<T>::value,
256 size_t
257 >::type hash(const T& t) { return std::hash<T>()(t); }
261 * Hash operand wrapper.
263 template<typename T, typename S>
264 struct hash_operand { const T& val; S type; };
266 // this template isn't really needed. its a workaround for T44007494
267 template<typename S> struct hash_operand<void*, S> { void* const val; S type; };
270 * Hash T using H::operator() if it is compatible, else fall back to
271 * hasher_impl (e.g., if H := hasher_default).
273 template<typename T, typename S, class H>
274 auto hash(const hash_operand<T,S>& t, H h) -> decltype(h(t.val)) {
275 return h(t.val);
277 template<typename T, typename S, class H>
278 auto hash(const hash_operand<T,S>& t, H h) -> decltype(h(t.val, t.type)) {
279 return h(t.val, t.type);
281 template<typename T, typename S, typename... Unused>
282 typename std::enable_if<sizeof...(Unused) == 1, size_t>::type
283 hash(const hash_operand<T,S>& t, Unused...) {
284 return hasher_impl::hash(t.val);
288 * Clone of folly::hash::hash_combine_generic.
290 template <class H>
291 size_t hash_combine(H /*h*/) {
292 return 0;
295 template<class H, typename T, typename... Ts>
296 size_t hash_combine(H h, const T& t, const Ts&... ts) {
297 auto const seed = size_t{hash(t, h)};
298 if (sizeof...(ts) == 0) return seed;
300 auto const remainder = hash_combine(h, ts...);
301 return static_cast<size_t>(folly::hash::hash_128_to_64(seed, remainder));
305 * Trivial equality overrides, which will cause bytecodes to compare via
306 * operator==() on the various immediate types.
308 struct eq_default {};
311 * Equality operand wrapper.
313 template<typename T, typename S>
314 struct eq_operand { const T& l; const T& r; S type; };
316 // this template isn't really needed. its a workaround for T44007494
317 template<typename S> struct eq_operand<void*, S> {
318 void* const l; void* const r; S type;
322 * Compare two values, using E::operator() if it exists, else the default
323 * operator==.
325 template<typename T, typename S, class E>
326 auto equals(const eq_operand<T,S>& t, E e) -> decltype(e(t.l, t.r)) {
327 return e(t.l, t.r);
329 template<typename T, typename S, class E>
330 auto equals(const eq_operand<T,S>& t, E e) -> decltype(e(t.l, t.r, t.type)) {
331 return e(t.l, t.r, t.type);
333 template<typename T, typename S, typename... Unused>
334 typename std::enable_if<sizeof...(Unused) == 1, bool>::type
335 equals(const eq_operand<T,S>& t, Unused...) {
336 return t.l == t.r;
340 * Check if a list of eq_operands are pairwise-equal.
342 template <class E>
343 bool eq_pairs(E /*e*/) {
344 return true;
347 template<class E, typename T, typename... Ts>
348 bool eq_pairs(E e, const T& t, const Ts&... ts) {
349 return equals(t, e) && (sizeof...(ts) == 0 || eq_pairs(e, ts...));
354 //////////////////////////////////////////////////////////////////////
357 * Bytecode immediate type tags.
359 namespace imm {
360 #define ARGTYPE(name, type) enum class name : uint8_t {};
361 #define ARGTYPEVEC(name, type) enum class name : uint8_t {};
362 ARGTYPES
363 #undef ARGTYPE
364 #undef ARGTYPEVEC
367 #define IMM_ID_BLA BLA
368 #define IMM_ID_SLA SLA
369 #define IMM_ID_ILA ILA
370 #define IMM_ID_I32LA I32LA
371 #define IMM_ID_IVA IVA
372 #define IMM_ID_I64A I64A
373 #define IMM_ID_LA LA
374 #define IMM_ID_IA IA
375 #define IMM_ID_CAR CAR
376 #define IMM_ID_CAW CAW
377 #define IMM_ID_DA DA
378 #define IMM_ID_SA SA
379 #define IMM_ID_RATA RATA
380 #define IMM_ID_AA AA
381 #define IMM_ID_BA BA
382 #define IMM_ID_OA(type) OA
383 #define IMM_ID_VSA VSA
384 #define IMM_ID_KA KA
385 #define IMM_ID_LAR LAR
386 #define IMM_ID_FCA FCA
388 #define IMM_TY_BLA SwitchTab
389 #define IMM_TY_SLA SSwitchTab
390 #define IMM_TY_ILA IterTab
391 #define IMM_TY_I32LA CompactVector<uint32_t>
392 #define IMM_TY_IVA uint32_t
393 #define IMM_TY_I64A int64_t
394 #define IMM_TY_LA LocalId
395 #define IMM_TY_IA IterId
396 #define IMM_TY_CAR ClsRefSlotId
397 #define IMM_TY_CAW ClsRefSlotId
398 #define IMM_TY_DA double
399 #define IMM_TY_SA LSString
400 #define IMM_TY_RATA RepoAuthType
401 #define IMM_TY_AA SArray
402 #define IMM_TY_BA BlockId
403 #define IMM_TY_OA(type) type
404 #define IMM_TY_VSA CompactVector<LSString>
405 #define IMM_TY_KA MKey
406 #define IMM_TY_LAR LocalRange
407 #define IMM_TY_FCA FCallArgs
409 #define IMM_NAME_BLA(n) targets
410 #define IMM_NAME_SLA(n) targets
411 #define IMM_NAME_ILA(n) iterTab
412 #define IMM_NAME_I32LA(n) argv
413 #define IMM_NAME_IVA(n) arg##n
414 #define IMM_NAME_I64A(n) arg##n
415 #define IMM_NAME_LA(n) loc##n
416 #define IMM_NAME_IA(n) iter##n
417 #define IMM_NAME_CAR(n) slot
418 #define IMM_NAME_CAW(n) slot
419 #define IMM_NAME_DA(n) dbl##n
420 #define IMM_NAME_SA(n) str##n
421 #define IMM_NAME_RATA(n) rat
422 #define IMM_NAME_AA(n) arr##n
423 #define IMM_NAME_BA(n) target##n
424 #define IMM_NAME_OA_IMPL(n) subop##n
425 #define IMM_NAME_OA(type) IMM_NAME_OA_IMPL
426 #define IMM_NAME_VSA(n) keys
427 #define IMM_NAME_KA(n) mkey
428 #define IMM_NAME_LAR(n) locrange
429 #define IMM_NAME_FCA(n) fca
431 #define IMM_TARGETS_BLA(n) for (auto& t : targets) f(t);
432 #define IMM_TARGETS_SLA(n) for (auto& kv : targets) f(kv.second);
433 #define IMM_TARGETS_ILA(n)
434 #define IMM_TARGETS_I32LA(n)
435 #define IMM_TARGETS_IVA(n)
436 #define IMM_TARGETS_I64A(n)
437 #define IMM_TARGETS_LA(n)
438 #define IMM_TARGETS_IA(n)
439 #define IMM_TARGETS_CAR(n)
440 #define IMM_TARGETS_CAW(n)
441 #define IMM_TARGETS_DA(n)
442 #define IMM_TARGETS_SA(n)
443 #define IMM_TARGETS_RATA(n)
444 #define IMM_TARGETS_AA(n)
445 #define IMM_TARGETS_BA(n) f(target##n);
446 #define IMM_TARGETS_OA_IMPL(n)
447 #define IMM_TARGETS_OA(type) IMM_TARGETS_OA_IMPL
448 #define IMM_TARGETS_VSA(n)
449 #define IMM_TARGETS_KA(n)
450 #define IMM_TARGETS_LAR(n)
451 #define IMM_TARGETS_FCA(n) if (fca.asyncEagerTarget != NoBlockId) { \
452 f(fca.asyncEagerTarget); \
455 #define IMM_EXTRA_BLA
456 #define IMM_EXTRA_SLA
457 #define IMM_EXTRA_ILA
458 #define IMM_EXTRA_I32LA
459 #define IMM_EXTRA_IVA
460 #define IMM_EXTRA_I64A
461 #define IMM_EXTRA_LA
462 #define IMM_EXTRA_IA
463 #define IMM_EXTRA_CAR using has_car_flag = std::true_type;
464 #define IMM_EXTRA_CAW using has_caw_flag = std::true_type;
465 #define IMM_EXTRA_DA
466 #define IMM_EXTRA_SA
467 #define IMM_EXTRA_RATA
468 #define IMM_EXTRA_AA
469 #define IMM_EXTRA_BA
470 #define IMM_EXTRA_OA(x)
471 #define IMM_EXTRA_VSA
472 #define IMM_EXTRA_KA
473 #define IMM_EXTRA_LAR
474 #define IMM_EXTRA_FCA
476 #define IMM_MEM(which, n) IMM_TY_##which IMM_NAME_##which(n)
477 #define IMM_MEM_NA
478 #define IMM_MEM_ONE(x) IMM_MEM(x, 1);
479 #define IMM_MEM_TWO(x, y) IMM_MEM(x, 1); IMM_MEM(y, 2);
480 #define IMM_MEM_THREE(x, y, z) IMM_MEM(x, 1); IMM_MEM(y, 2); \
481 IMM_MEM(z, 3);
482 #define IMM_MEM_FOUR(x, y, z, l) IMM_MEM(x, 1); IMM_MEM(y, 2); \
483 IMM_MEM(z, 3); IMM_MEM(l, 4);
484 #define IMM_MEM_FIVE(x, y, z, l, m) IMM_MEM(x, 1); IMM_MEM(y, 2); \
485 IMM_MEM(z, 3); IMM_MEM(l, 4); \
486 IMM_MEM(m, 5);
488 #define IMM_EQ_WRAP(e, ...) detail::eq_pairs(e, __VA_ARGS__)
489 #define IMM_EQ(which, n) detail::eq_operand< \
490 IMM_TY_##which, \
491 imm::IMM_ID_##which \
492 > { \
493 IMM_NAME_##which(n), \
494 o.IMM_NAME_##which(n) \
496 #define IMM_EQ_NA detail::eq_operand<void*,imm::NA> { 0, 0 }
497 #define IMM_EQ_ONE(x) IMM_EQ(x, 1)
498 #define IMM_EQ_TWO(x, y) IMM_EQ_ONE(x), IMM_EQ(y, 2)
499 #define IMM_EQ_THREE(x, y, z) IMM_EQ_TWO(x, y), IMM_EQ(z, 3)
500 #define IMM_EQ_FOUR(x, y, z, l) IMM_EQ_THREE(x, y, z), IMM_EQ(l, 4)
501 #define IMM_EQ_FIVE(x, y, z, l, m) IMM_EQ_FOUR(x, y, z, l), IMM_EQ(m, 5)
503 #define IMM_HASH_WRAP(h, ...) detail::hash_combine(h, __VA_ARGS__)
504 #define IMM_HASH(which, n) detail::hash_operand< \
505 IMM_TY_##which, \
506 imm::IMM_ID_##which \
507 > { IMM_NAME_##which(n) }
508 #define IMM_HASH_NA detail::hash_operand<void*,imm::NA> { 0 }
509 #define IMM_HASH_ONE(x) IMM_HASH(x, 1)
510 #define IMM_HASH_TWO(x, y) IMM_HASH_ONE(x), IMM_HASH(y, 2)
511 #define IMM_HASH_THREE(x, y, z) IMM_HASH_TWO(x, y), IMM_HASH(z, 3)
512 #define IMM_HASH_FOUR(x, y, z, l) IMM_HASH_THREE(x, y, z), IMM_HASH(l, 4)
513 #define IMM_HASH_FIVE(x, y, z, l, m) IMM_HASH_FOUR(x, y, z, l), IMM_HASH(m, 5)
515 #define IMM_TARGETS_NA
516 #define IMM_TARGETS_ONE(x) IMM_TARGETS_##x(1)
517 #define IMM_TARGETS_TWO(x,y) IMM_TARGETS_ONE(x) IMM_TARGETS_##y(2)
518 #define IMM_TARGETS_THREE(x,y,z) IMM_TARGETS_TWO(x,y) IMM_TARGETS_##z(3)
519 #define IMM_TARGETS_FOUR(x,y,z,l) IMM_TARGETS_THREE(x,y,z) IMM_TARGETS_##l(4)
520 #define IMM_TARGETS_FIVE(x,y,z,l,m) IMM_TARGETS_FOUR(x,y,z,l) IMM_TARGETS_##m(5)
522 #define IMM_EXTRA_NA
523 #define IMM_EXTRA_ONE(x) IMM_EXTRA_##x
524 #define IMM_EXTRA_TWO(x,y) IMM_EXTRA_ONE(x) IMM_EXTRA_ONE(y)
525 #define IMM_EXTRA_THREE(x,y,z) IMM_EXTRA_TWO(x,y) IMM_EXTRA_ONE(z)
526 #define IMM_EXTRA_FOUR(x,y,z,l) IMM_EXTRA_THREE(x,y,z) IMM_EXTRA_ONE(l)
527 #define IMM_EXTRA_FIVE(x,y,z,l,m) IMM_EXTRA_FOUR(x,y,z,l) IMM_EXTRA_ONE(m)
529 #define IMM_CTOR(which, n) IMM_TY_##which IMM_NAME_##which(n)
530 #define IMM_CTOR_NA
531 #define IMM_CTOR_ONE(x) IMM_CTOR(x, 1)
532 #define IMM_CTOR_TWO(x, y) IMM_CTOR(x, 1), IMM_CTOR(y, 2)
533 #define IMM_CTOR_THREE(x, y, z) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
534 IMM_CTOR(z, 3)
535 #define IMM_CTOR_FOUR(x, y, z, l) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
536 IMM_CTOR(z, 3), IMM_CTOR(l, 4)
537 #define IMM_CTOR_FIVE(x, y, z, l, m) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
538 IMM_CTOR(z, 3), IMM_CTOR(l, 4), \
539 IMM_CTOR(m, 5)
541 #define IMM_INIT(which, n) IMM_NAME_##which(n) \
542 ( std::move(IMM_NAME_##which(n)) )
543 #define IMM_INIT_NA
544 #define IMM_INIT_ONE(x) : IMM_INIT(x, 1)
545 #define IMM_INIT_TWO(x, y) : IMM_INIT(x, 1), IMM_INIT(y, 2)
546 #define IMM_INIT_THREE(x, y, z) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
547 IMM_INIT(z, 3)
548 #define IMM_INIT_FOUR(x, y, z, l) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
549 IMM_INIT(z, 3), IMM_INIT(l, 4)
550 #define IMM_INIT_FIVE(x, y, z, l, m) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
551 IMM_INIT(z, 3), IMM_INIT(l, 4), \
552 IMM_INIT(m, 5)
554 #define POP_UV if (i == 0) return Flavor::U
555 #define POP_CV if (i == 0) return Flavor::C
556 #define POP_VV if (i == 0) return Flavor::V
557 #define POP_CUV if (i == 0) return Flavor::CU
559 #define POP_NOV uint32_t numPop() const { return 0; } \
560 Flavor popFlavor(uint32_t) const { not_reached(); }
562 #define POP_ONE(x) uint32_t numPop() const { return 1; } \
563 Flavor popFlavor(uint32_t i) const { \
564 POP_##x; not_reached(); \
567 #define POP_TWO(x, y) uint32_t numPop() const { return 2; } \
568 Flavor popFlavor(uint32_t i) const { \
569 POP_##x; --i; POP_##y; not_reached(); \
572 #define POP_THREE(x, y, z) uint32_t numPop() const { return 3; } \
573 Flavor popFlavor(uint32_t i) const { \
574 POP_##x; --i; POP_##y; --i; POP_##z; \
575 not_reached(); \
578 #define POP_MFINAL uint32_t numPop() const { return arg1; } \
579 Flavor popFlavor(uint32_t) const { not_reached(); }
581 #define POP_C_MFINAL(n) uint32_t numPop() const { return arg1 + n; } \
582 Flavor popFlavor(uint32_t) const { not_reached(); }
584 #define POP_CMANY uint32_t numPop() const { return arg1; } \
585 Flavor popFlavor(uint32_t i) const { \
586 assert(i < numPop()); \
587 return Flavor::C; \
590 #define POP_SMANY uint32_t numPop() const { return keys.size(); } \
591 Flavor popFlavor(uint32_t i) const { \
592 assert(i < numPop()); \
593 return Flavor::C; \
596 #define POP_CUMANY uint32_t numPop() const { return arg1; } \
597 Flavor popFlavor(uint32_t i) const { \
598 assert(i < numPop()); \
599 return Flavor::CU; \
602 #define POP_CVUMANY uint32_t numPop() const { return arg1; } \
603 Flavor popFlavor(uint32_t i) const { \
604 assert(i < numPop()); \
605 return Flavor::CVU; \
608 #define POP_FPUSH(nin, nobj) \
609 uint32_t numPop() const { return arg1 + nin + 3; } \
610 Flavor popFlavor(uint32_t i) const { \
611 assert(i < numPop()); \
612 if (i < nin) return Flavor::C; \
613 i -= nin; \
614 if (i < arg1) return Flavor::CV; \
615 i -= arg1; \
616 if (i < 2) return Flavor::U; \
617 return nobj ? Flavor::C : Flavor::U; \
620 #define POP_FCALL uint32_t numPop() const { \
621 return fca.numArgs + (fca.hasUnpack() ? 1 : 0) + \
622 fca.numRets - 1; \
624 Flavor popFlavor(uint32_t i) const { \
625 assert(i < numPop()); \
626 if (i == 0 && fca.hasUnpack()) return Flavor::C; \
627 auto const cv = fca.numArgs + (fca.hasUnpack() ? 1 : 0); \
628 return i < cv ? Flavor::CV : Flavor::U; \
631 #define PUSH_NOV uint32_t numPush() const { return 0; }
633 #define PUSH_ONE(x) uint32_t numPush() const { return 1; }
635 #define PUSH_TWO(x, y) uint32_t numPush() const { return 2; }
637 #define PUSH_FPUSH uint32_t numPush() const { return arg1; }
638 #define PUSH_FCALL uint32_t numPush() const { return fca.numRets; }
640 #define FLAGS_NF
641 #define FLAGS_TF
642 #define FLAGS_CF
643 #define FLAGS_FF
644 #define FLAGS_PF bool has_unpack;
645 #define FLAGS_CF_TF
646 #define FLAGS_CF_FF
648 #define FLAGS_CTOR_NF
649 #define FLAGS_CTOR_TF
650 #define FLAGS_CTOR_CF
651 #define FLAGS_CTOR_FF
652 #define FLAGS_CTOR_PF ,bool hu
653 #define FLAGS_CTOR_CF_TF
654 #define FLAGS_CTOR_CF_FF
656 #define FLAGS_INIT_NF
657 #define FLAGS_INIT_TF
658 #define FLAGS_INIT_CF
659 #define FLAGS_INIT_FF
660 #define FLAGS_INIT_PF ,has_unpack(hu)
661 #define FLAGS_INIT_CF_TF
662 #define FLAGS_INIT_CF_FF
664 #define O(opcode, imms, inputs, outputs, flags) \
665 struct opcode { \
666 static constexpr Op op = Op::opcode; \
667 explicit opcode ( IMM_CTOR_##imms \
668 FLAGS_CTOR_##flags) \
669 IMM_INIT_##imms \
670 FLAGS_INIT_##flags \
671 {} \
673 IMM_MEM_##imms \
674 FLAGS_##flags \
675 IMM_EXTRA_##imms \
676 POP_##inputs \
677 PUSH_##outputs \
679 bool operator==(const opcode& o) const { \
680 return equals(o, detail::eq_default{}); \
683 bool operator!=(const opcode& o) const { \
684 return !(*this == o); \
687 template<class E> \
688 bool equals(const opcode& o, E e) const { \
689 return IMM_EQ_WRAP(e, IMM_EQ_##imms); \
692 size_t hash() const { \
693 return IMM_HASH_WRAP( \
694 detail::hasher_default{}, \
695 IMM_HASH_##imms); \
698 template<class H> \
699 size_t hash(H h) const { \
700 return IMM_HASH_WRAP(h, IMM_HASH_##imms); \
703 template <typename F> \
704 void forEachTarget(F&& f) { \
705 IMM_TARGETS_##imms \
707 template <typename F> \
708 void forEachTarget(F&& f) const { \
709 IMM_TARGETS_##imms \
712 OPCODES
713 #undef O
715 #undef FLAGS_NA
716 #undef FLAGS_TF
717 #undef FLAGS_CF
718 #undef FLAGS_FF
719 #undef FLAGS_PF
720 #undef FLAGS_CF_TF
721 #undef FLAGS_CF_FF
723 #undef FLAGS_CTOR_NA
724 #undef FLAGS_CTOR_TF
725 #undef FLAGS_CTOR_CF
726 #undef FLAGS_CTOR_FF
727 #undef FLAGS_CTOR_PF
728 #undef FLAGS_CTOR_CF_TF
729 #undef FLAGS_CTOR_CF_FF
731 #undef FLAGS_INIT_NA
732 #undef FLAGS_INIT_TF
733 #undef FLAGS_INIT_CF
734 #undef FLAGS_INIT_FF
735 #undef FLAGS_INIT_PF
736 #undef FLAGS_INIT_CF_TF
737 #undef FLAGS_INIT_CF_FF
739 #undef PUSH_NOV
740 #undef PUSH_ONE
741 #undef PUSH_TWO
742 #undef PUSH_FPUSH
743 #undef PUSH_FCALL
745 #undef POP_UV
746 #undef POP_CV
747 #undef POP_VV
749 #undef POP_NOV
750 #undef POP_ONE
751 #undef POP_TWO
752 #undef POP_THREE
753 #undef POP_MFINAL
754 #undef POP_C_MFINAL
755 #undef POP_CMANY
756 #undef POP_SMANY
757 #undef POP_CUMANY
758 #undef POP_CVUMANY
759 #undef POP_FPUSH
760 #undef POP_FCALL
762 #undef IMM_TY_MA
763 #undef IMM_TY_BLA
764 #undef IMM_TY_SLA
765 #undef IMM_TY_ILA
766 #undef IMM_TY_I32LA
767 #undef IMM_TY_IVA
768 #undef IMM_TY_I64A
769 #undef IMM_TY_LA
770 #undef IMM_TY_IA
771 #undef IMM_TY_CAR
772 #undef IMM_TY_CAW
773 #undef IMM_TY_DA
774 #undef IMM_TY_SA
775 #undef IMM_TY_RATA
776 #undef IMM_TY_AA
777 #undef IMM_TY_BA
778 #undef IMM_TY_OA
779 #undef IMM_TY_VSA
780 #undef IMM_TY_KA
781 #undef IMM_TY_LAR
782 #undef IMM_TY_FCA
784 // These are deliberately not undefined, so they can be used in other
785 // places.
786 // #undef IMM_NAME_BLA
787 // #undef IMM_NAME_SLA
788 // #undef IMM_NAME_ILA
789 // #undef IMM_NAME_I32LA
790 // #undef IMM_NAME_IVA
791 // #undef IMM_NAME_I64A
792 // #undef IMM_NAME_LA
793 // #undef IMM_NAME_IA
794 // #undef IMM_NAME_CAR
795 // #undef IMM_NAME_CAW
796 // #undef IMM_NAME_DA
797 // #undef IMM_NAME_SA
798 // #undef IMM_NAME_RATA
799 // #undef IMM_NAME_AA
800 // #undef IMM_NAME_BA
801 // #undef IMM_NAME_OA
802 // #undef IMM_NAME_OA_IMPL
803 // #undef IMM_NAME_LAR
804 // #undef IMM_NAME_FCA
806 #undef IMM_TARGETS_BLA
807 #undef IMM_TARGETS_SLA
808 #undef IMM_TARGETS_ILA
809 #undef IMM_TARGETS_I32LA
810 #undef IMM_TARGETS_IVA
811 #undef IMM_TARGETS_I64A
812 #undef IMM_TARGETS_LA
813 #undef IMM_TARGETS_IA
814 #undef IMM_TARGETS_CAR
815 #undef IMM_TARGETS_CAW
816 #undef IMM_TARGETS_DA
817 #undef IMM_TARGETS_SA
818 #undef IMM_TARGETS_RATA
819 #undef IMM_TARGETS_AA
820 #undef IMM_TARGETS_BA
821 #undef IMM_TARGETS_OA
822 #undef IMM_TARGETS_KA
823 #undef IMM_TARGETS_LAR
824 #undef IMM_TARGETS_FCA
826 #undef IMM_TARGETS_NA
827 #undef IMM_TARGETS_ONE
828 #undef IMM_TARGETS_TWO
829 #undef IMM_TARGETS_THREE
830 #undef IMM_TARGETS_FOUR
831 #undef IMM_TARGETS_FIVE
833 #undef IMM_EXTRA_BLA
834 #undef IMM_EXTRA_SLA
835 #undef IMM_EXTRA_ILA
836 #undef IMM_EXTRA_I32LA
837 #undef IMM_EXTRA_IVA
838 #undef IMM_EXTRA_I64A
839 #undef IMM_EXTRA_LA
840 #undef IMM_EXTRA_IA
841 #undef IMM_EXTRA_CAR
842 #undef IMM_EXTRA_CAW
843 #undef IMM_EXTRA_DA
844 #undef IMM_EXTRA_SA
845 #undef IMM_EXTRA_RATA
846 #undef IMM_EXTRA_AA
847 #undef IMM_EXTRA_BA
848 #undef IMM_EXTRA_OA
849 #undef IMM_EXTRA_KA
850 #undef IMM_EXTRA_LAR
851 #undef IMM_EXTRA_FCA
853 #undef IMM_MEM
854 #undef IMM_MEM_NA
855 #undef IMM_MEM_ONE
856 #undef IMM_MEM_TWO
857 #undef IMM_MEM_THREE
858 #undef IMM_MEM_FOUR
859 #undef IMM_MEM_FIVE
861 #undef IMM_EQ
862 #undef IMM_EQ_NA
863 #undef IMM_EQ_ONE
864 #undef IMM_EQ_TWO
865 #undef IMM_EQ_THREE
866 #undef IMM_EQ_FOUR
867 #undef IMM_EQ_FIVE
869 #undef IMM_HASH
870 #undef IMM_HASH_DO
871 #undef IMM_HASH_NA
872 #undef IMM_HASH_ONE
873 #undef IMM_HASH_TWO
874 #undef IMM_HASH_THREE
875 #undef IMM_HASH_FOUR
876 #undef IMM_HASH_FIVE
878 #undef IMM_CTOR
879 #undef IMM_CTOR_NA
880 #undef IMM_CTOR_ONE
881 #undef IMM_CTOR_TWO
882 #undef IMM_CTOR_THREE
883 #undef IMM_CTOR_FOUR
884 #undef IMM_CTOR_FIVE
886 #undef IMM_INIT
887 #undef IMM_INIT_NA
888 #undef IMM_INIT_ONE
889 #undef IMM_INIT_TWO
890 #undef IMM_INIT_THREE
891 #undef IMM_INIT_FOUR
895 //////////////////////////////////////////////////////////////////////
898 * Bytecode is a tagged-union that can hold any HHBC opcode struct
899 * defined above. You can visit a bytecode with a static_visitor
900 * using visit(), defined below.
902 * Each Bytecode also carries a corresponding SrcLoc that indicates
903 * which line of PHP code the bytecode was generated for.
905 struct Bytecode {
906 // Default construction creates a Nop.
907 Bytecode()
908 : op(Op::Nop)
909 , Nop(bc::Nop{})
912 #define O(opcode, ...) \
913 /* implicit */ Bytecode(bc::opcode data) \
914 : op(Op::opcode) \
916 new (&opcode) bc::opcode(std::move(data)); \
919 OPCODES
921 #undef O
923 // Note: assuming bc::Nop is empty and has trivial dtor/ctor.
925 Bytecode(const Bytecode& o) : op(Op::Nop) { *this = o; }
926 Bytecode(Bytecode&& o) noexcept : op(Op::Nop) { *this = std::move(o); }
928 Bytecode& operator=(const Bytecode& o) {
929 destruct();
930 op = Op::Nop;
931 srcLoc = o.srcLoc;
932 #define O(opcode, ...) \
933 case Op::opcode: new (&opcode) bc::opcode(o.opcode); break;
934 switch (o.op) { OPCODES }
935 #undef O
936 op = o.op;
937 return *this;
940 Bytecode& operator=(Bytecode&& o) {
941 destruct();
942 srcLoc = o.srcLoc;
943 #define O(opcode, ...) \
944 case Op::opcode: new (&opcode) bc::opcode(std::move(o.opcode)); break;
945 switch (o.op) { OPCODES }
946 #undef O
947 op = o.op;
948 return *this;
951 ~Bytecode() { destruct(); }
953 uint32_t numPop() const {
954 #define O(opcode, ...) \
955 case Op::opcode: return opcode.numPop();
956 switch (op) { OPCODES }
957 #undef O
958 not_reached();
961 Flavor popFlavor(uint32_t i) const {
962 #define O(opcode, ...) \
963 case Op::opcode: return opcode.popFlavor(i);
964 switch (op) { OPCODES }
965 #undef O
966 not_reached();
969 uint32_t numPush() const {
970 #define O(opcode, ...) \
971 case Op::opcode: return opcode.numPush();
972 switch (op) { OPCODES }
973 #undef O
974 not_reached();
977 template <typename F>
978 void forEachTarget(F&& f) const {
979 #define O(opcode, ...) \
980 case Op::opcode: return opcode.forEachTarget(std::forward<F>(f));
981 switch (op) { OPCODES }
982 #undef O
983 not_reached();
986 template <typename F>
987 void forEachTarget(F&& f) {
988 #define O(opcode, ...) \
989 case Op::opcode: return opcode.forEachTarget(std::forward<F>(f));
990 switch (op) { OPCODES }
991 #undef O
992 not_reached();
995 Op op;
996 int32_t srcLoc{-1};
998 #define O(opcode, ...) bc::opcode opcode;
999 union { OPCODES };
1000 #undef O
1002 private:
1003 void destruct() {
1004 switch (op) {
1005 #define O(opcode, ...) \
1006 case Op::opcode: \
1007 { typedef bc::opcode X; \
1008 this->opcode.~X(); } \
1009 break;
1010 OPCODES
1011 #undef O
1016 //////////////////////////////////////////////////////////////////////
1018 inline bool operator==(const Bytecode& a, const Bytecode& b) {
1019 if (a.op != b.op) return false;
1020 #define O(opcode, ...) case Op::opcode: return a.opcode == b.opcode;
1021 switch (a.op) { OPCODES }
1022 #undef O
1023 not_reached();
1026 inline bool operator!=(const Bytecode& a, const Bytecode& b) {
1027 return !(a == b);
1030 template<class E>
1031 inline bool equals(const Bytecode& a, const Bytecode& b, E equals) {
1032 if (a.op != b.op) return false;
1033 #define O(opcode, ...) \
1034 case Op::opcode: return a.opcode.equals(b.opcode, equals);
1035 switch (a.op) { OPCODES }
1036 #undef O
1037 not_reached();
1040 template<class H>
1041 inline size_t hash(const Bytecode& b, H hasher) {
1042 auto hash = 14695981039346656037ULL;
1043 auto o = static_cast<size_t>(b.op);
1044 hash ^= o;
1045 #define O(opcode, ...) \
1046 case Op::opcode: \
1047 return folly::hash::hash_combine(b.opcode.hash(hasher), hash);
1048 switch (b.op) { OPCODES }
1049 #undef O
1050 not_reached();
1053 inline size_t hash(const Bytecode& b) {
1054 return hash(b, bc::detail::hasher_default{});
1057 //////////////////////////////////////////////////////////////////////
1060 * Helper for making a Bytecode with a given srcLoc.
1062 * Ex:
1063 * auto b = bc_with_loc(something.srcLoc, bc::Nop {});
1065 template<class T> Bytecode bc_with_loc(int32_t loc, const T& t) {
1066 Bytecode b = t;
1067 b.srcLoc = loc;
1068 return b;
1071 //////////////////////////////////////////////////////////////////////
1074 * Visit a bytecode using a StaticVisitor, similar to
1075 * boost::apply_visitor or match().
1077 * The `v' argument should be a function object that accepts a call
1078 * operator for all the bc::Foo types. Its result type should be
1079 * independent of bytecode, but may vary with constness.
1081 template<class Visit>
1082 auto visit(Bytecode& b, Visit v) {
1083 #define O(opcode, ...) case Op::opcode: return v(b.opcode);
1084 switch (b.op) { OPCODES }
1085 #undef O
1086 not_reached();
1089 template<class Visit>
1090 auto visit(const Bytecode& b, Visit v) {
1091 #define O(opcode, ...) case Op::opcode: return v(b.opcode);
1092 switch (b.op) { OPCODES }
1093 #undef O
1094 not_reached();
1097 //////////////////////////////////////////////////////////////////////
1099 struct ReadClsRefSlotVisitor {
1100 ReadClsRefSlotVisitor() {}
1102 template<typename T>
1103 auto fun(T&, int) const { return NoClsRefSlotId; }
1105 template<typename T>
1106 auto fun(T& t, bool) const -> decltype(typename T::has_car_flag{},t.slot) {
1107 return t.slot;
1110 template<typename T>
1111 ClsRefSlotId operator()(T& t) const { return fun(t, true); }
1114 struct WriteClsRefSlotVisitor {
1115 template<typename T>
1116 auto fun(T&, int) const { return NoClsRefSlotId; }
1118 template<typename T>
1119 auto fun(T& t, bool) const -> decltype(typename T::has_caw_flag{},t.slot) {
1120 return t.slot;
1123 template<typename T>
1124 ClsRefSlotId operator()(T& t) const { return fun(t, true); }
1127 //////////////////////////////////////////////////////////////////////
1133 #endif