Fix invariant() argument checking
[hiphop-php.git] / hphp / hhbbc / bc.h
blob8e8e1c906941d8211cb44162dfaa7034f3549f3e
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, false) {}
114 explicit FCallArgs(Flags flags, uint32_t numArgs, uint32_t numRets,
115 std::unique_ptr<uint8_t[]> inoutArgs,
116 BlockId asyncEagerTarget, bool lockWhileUnwinding)
117 : FCallArgsBase(flags, numArgs, numRets, lockWhileUnwinding)
118 , asyncEagerTarget(asyncEagerTarget)
119 , inoutArgs(std::move(inoutArgs)) {
120 assertx(IMPLIES(asyncEagerTarget == NoBlockId,
121 !supportsAsyncEagerReturn()));
123 FCallArgs(const FCallArgs& o)
124 : FCallArgs(o.flags, o.numArgs, o.numRets, nullptr, o.asyncEagerTarget,
125 o.lockWhileUnwinding) {
126 if (o.inoutArgs) {
127 auto const numBytes = (numArgs + 7) / 8;
128 inoutArgs = std::make_unique<uint8_t[]>(numBytes);
129 memcpy(inoutArgs.get(), o.inoutArgs.get(), numBytes);
132 FCallArgs(FCallArgs&& o)
133 : FCallArgs(o.flags, o.numArgs, o.numRets, std::move(o.inoutArgs),
134 o.asyncEagerTarget, o.lockWhileUnwinding) {}
136 bool enforceInOut() const { return inoutArgs.get() != nullptr; }
137 bool isInOut(uint32_t i) const {
138 assertx(enforceInOut());
139 return inoutArgs[i / 8] & (1 << (i % 8));
142 FCallArgs withoutGenerics() const {
143 auto fca = *this;
144 fca.flags = static_cast<Flags>(fca.flags & ~Flags::HasGenerics);
145 return fca;
148 BlockId asyncEagerTarget;
149 std::unique_ptr<uint8_t[]> inoutArgs;
152 inline bool operator==(const FCallArgs& a, const FCallArgs& b) {
153 auto const eq = [&] (uint8_t* a, uint8_t* b, uint32_t bytes) {
154 if (a == nullptr && b == nullptr) return true;
155 if (a == nullptr || b == nullptr) return false;
156 return memcmp(a, b, bytes) == 0;
159 return
160 a.flags == b.flags && a.numArgs == b.numArgs && a.numRets == b.numRets &&
161 eq(a.inoutArgs.get(), b.inoutArgs.get(), (a.numArgs + 7) / 8) &&
162 a.asyncEagerTarget == b.asyncEagerTarget &&
163 a.lockWhileUnwinding == b.lockWhileUnwinding;
166 inline bool operator!=(const FCallArgs& a, const FCallArgs& b) {
167 return !(a == b);
170 struct IterTabEnt {
171 IterKind kind;
172 IterId id;
173 LocalId local;
176 inline bool operator==(const IterTabEnt& a, const IterTabEnt& b) {
177 return std::tie(a.kind, a.id, a.local) == std::tie(b.kind, b.id, b.local);
180 inline bool operator!=(const IterTabEnt& a, const IterTabEnt& b) {
181 return !(a == b);
184 using IterTab = CompactVector<IterTabEnt>;
186 using SwitchTab = CompactVector<BlockId>;
188 // The final entry in the SSwitchTab is the default case, it will
189 // always have a nullptr for the string.
190 using SSwitchTabEnt = std::pair<LSString,BlockId>;
191 using SSwitchTab = CompactVector<SSwitchTabEnt>;
193 //////////////////////////////////////////////////////////////////////
195 namespace bc {
197 //////////////////////////////////////////////////////////////////////
199 namespace detail {
202 * Trivial hasher overrides, which will cause bytecodes to hash according to
203 * the hashes defined by hasher_impl.
205 struct hasher_default {};
208 * Default bytecode immediate hashers.
210 struct hasher_impl {
211 static size_t hash(SString s) { return s->hash(); }
212 static size_t hash(LSString s) { return s->hash(); }
213 static size_t hash(RepoAuthType rat) { return rat.hash(); }
215 static size_t hash(const IterTabEnt& iterTab) {
216 auto const partial = folly::hash::hash_128_to_64(
217 iterTab.kind, iterTab.id
219 return static_cast<size_t>(
220 folly::hash::hash_128_to_64(iterTab.local, partial)
224 static size_t hash(MKey mkey) {
225 return HPHP::hash_int64_pair(mkey.mcode, mkey.int64);
228 template<class T>
229 static size_t hash(const CompactVector<T>& v) {
230 return v.empty() ? 0 : v.size() ^ hash(v.front());
233 static size_t hash(std::pair<LSString,BlockId> kv) {
234 return HPHP::hash_int64_pair(kv.first->hash(), kv.second);
237 static size_t hash(LocalRange range) {
238 return HPHP::hash_int64_pair(range.first, range.count);
241 static size_t hash(IterArgs ita) {
242 auto hash = HPHP::hash_int64_pair(ita.flags, ita.iterId);
243 hash = HPHP::hash_int64_pair(hash, ita.keyId);
244 return HPHP::hash_int64_pair(hash, ita.valId);
247 static size_t hash(FCallArgs fca) {
248 uint64_t hash = HPHP::hash_int64_pair(fca.numArgs, fca.numRets);
249 hash = HPHP::hash_int64_pair(hash, fca.flags);
250 if (fca.inoutArgs) {
251 auto const br = reinterpret_cast<char*>(fca.inoutArgs.get());
252 auto const hash_br = hash_string_cs(br, (fca.numArgs + 7) / 8);
253 hash = HPHP::hash_int64_pair(hash, hash_br);
255 hash = HPHP::hash_int64_pair(hash, fca.asyncEagerTarget);
256 hash = HPHP::hash_int64_pair(hash, fca.lockWhileUnwinding);
257 return static_cast<size_t>(hash);
260 template<class T>
261 static typename std::enable_if<
262 std::is_enum<T>::value,
263 size_t
264 >::type hash(T t) {
265 using U = typename std::underlying_type<T>::type;
266 return std::hash<U>()(static_cast<U>(t));
269 template<class T>
270 static typename std::enable_if<
271 !std::is_enum<T>::value,
272 size_t
273 >::type hash(const T& t) { return std::hash<T>()(t); }
277 * Hash operand wrapper.
279 template<typename T, typename S>
280 struct hash_operand { const T& val; S type; };
282 // this template isn't really needed. its a workaround for T44007494
283 template<typename S> struct hash_operand<void*, S> { void* const val; S type; };
286 * Hash T using H::operator() if it is compatible, else fall back to
287 * hasher_impl (e.g., if H := hasher_default).
289 template<typename T, typename S, class H>
290 auto hash(const hash_operand<T,S>& t, H h) -> decltype(h(t.val)) {
291 return h(t.val);
293 template<typename T, typename S, class H>
294 auto hash(const hash_operand<T,S>& t, H h) -> decltype(h(t.val, t.type)) {
295 return h(t.val, t.type);
297 template<typename T, typename S, typename... Unused>
298 typename std::enable_if<sizeof...(Unused) == 1, size_t>::type
299 hash(const hash_operand<T,S>& t, Unused...) {
300 return hasher_impl::hash(t.val);
304 * Clone of folly::hash::hash_combine_generic.
306 template <class H>
307 size_t hash_combine(H /*h*/) {
308 return 0;
311 template<class H, typename T, typename... Ts>
312 size_t hash_combine(H h, const T& t, const Ts&... ts) {
313 auto const seed = size_t{hash(t, h)};
314 if (sizeof...(ts) == 0) return seed;
316 auto const remainder = hash_combine(h, ts...);
317 return static_cast<size_t>(folly::hash::hash_128_to_64(seed, remainder));
321 * Trivial equality overrides, which will cause bytecodes to compare via
322 * operator==() on the various immediate types.
324 struct eq_default {};
327 * Equality operand wrapper.
329 template<typename T, typename S>
330 struct eq_operand { const T& l; const T& r; S type; };
332 // this template isn't really needed. its a workaround for T44007494
333 template<typename S> struct eq_operand<void*, S> {
334 void* const l; void* const r; S type;
338 * Compare two values, using E::operator() if it exists, else the default
339 * operator==.
341 template<typename T, typename S, class E>
342 auto equals(const eq_operand<T,S>& t, E e) -> decltype(e(t.l, t.r)) {
343 return e(t.l, t.r);
345 template<typename T, typename S, class E>
346 auto equals(const eq_operand<T,S>& t, E e) -> decltype(e(t.l, t.r, t.type)) {
347 return e(t.l, t.r, t.type);
349 template<typename T, typename S, typename... Unused>
350 typename std::enable_if<sizeof...(Unused) == 1, bool>::type
351 equals(const eq_operand<T,S>& t, Unused...) {
352 return t.l == t.r;
356 * Check if a list of eq_operands are pairwise-equal.
358 template <class E>
359 bool eq_pairs(E /*e*/) {
360 return true;
363 template<class E, typename T, typename... Ts>
364 bool eq_pairs(E e, const T& t, const Ts&... ts) {
365 return equals(t, e) && (sizeof...(ts) == 0 || eq_pairs(e, ts...));
370 //////////////////////////////////////////////////////////////////////
373 * Bytecode immediate type tags.
375 namespace imm {
376 #define ARGTYPE(name, type) enum class name : uint8_t {};
377 #define ARGTYPEVEC(name, type) enum class name : uint8_t {};
378 ARGTYPES
379 #undef ARGTYPE
380 #undef ARGTYPEVEC
383 #define IMM_ID_BLA BLA
384 #define IMM_ID_SLA SLA
385 #define IMM_ID_ILA ILA
386 #define IMM_ID_IVA IVA
387 #define IMM_ID_I64A I64A
388 #define IMM_ID_LA LA
389 #define IMM_ID_IA IA
390 #define IMM_ID_DA DA
391 #define IMM_ID_SA SA
392 #define IMM_ID_RATA RATA
393 #define IMM_ID_AA AA
394 #define IMM_ID_BA BA
395 #define IMM_ID_OA(type) OA
396 #define IMM_ID_VSA VSA
397 #define IMM_ID_KA KA
398 #define IMM_ID_LAR LAR
399 #define IMM_ID_ITA ITA
400 #define IMM_ID_FCA FCA
402 #define IMM_TY_BLA SwitchTab
403 #define IMM_TY_SLA SSwitchTab
404 #define IMM_TY_ILA IterTab
405 #define IMM_TY_IVA uint32_t
406 #define IMM_TY_I64A int64_t
407 #define IMM_TY_LA LocalId
408 #define IMM_TY_IA IterId
409 #define IMM_TY_DA double
410 #define IMM_TY_SA LSString
411 #define IMM_TY_RATA RepoAuthType
412 #define IMM_TY_AA SArray
413 #define IMM_TY_BA BlockId
414 #define IMM_TY_OA(type) type
415 #define IMM_TY_VSA CompactVector<LSString>
416 #define IMM_TY_KA MKey
417 #define IMM_TY_LAR LocalRange
418 #define IMM_TY_ITA IterArgs
419 #define IMM_TY_FCA FCallArgs
421 #define IMM_NAME_BLA(n) targets
422 #define IMM_NAME_SLA(n) targets
423 #define IMM_NAME_ILA(n) iterTab
424 #define IMM_NAME_IVA(n) arg##n
425 #define IMM_NAME_I64A(n) arg##n
426 #define IMM_NAME_LA(n) loc##n
427 #define IMM_NAME_IA(n) iter##n
428 #define IMM_NAME_DA(n) dbl##n
429 #define IMM_NAME_SA(n) str##n
430 #define IMM_NAME_RATA(n) rat
431 #define IMM_NAME_AA(n) arr##n
432 #define IMM_NAME_BA(n) target##n
433 #define IMM_NAME_OA_IMPL(n) subop##n
434 #define IMM_NAME_OA(type) IMM_NAME_OA_IMPL
435 #define IMM_NAME_VSA(n) keys
436 #define IMM_NAME_KA(n) mkey
437 #define IMM_NAME_LAR(n) locrange
438 #define IMM_NAME_ITA(n) ita
439 #define IMM_NAME_FCA(n) fca
441 #define IMM_TARGETS_BLA(n) for (auto& t : targets) f(t);
442 #define IMM_TARGETS_SLA(n) for (auto& kv : targets) f(kv.second);
443 #define IMM_TARGETS_ILA(n)
444 #define IMM_TARGETS_IVA(n)
445 #define IMM_TARGETS_I64A(n)
446 #define IMM_TARGETS_LA(n)
447 #define IMM_TARGETS_IA(n)
448 #define IMM_TARGETS_DA(n)
449 #define IMM_TARGETS_SA(n)
450 #define IMM_TARGETS_RATA(n)
451 #define IMM_TARGETS_AA(n)
452 #define IMM_TARGETS_BA(n) f(target##n);
453 #define IMM_TARGETS_OA_IMPL(n)
454 #define IMM_TARGETS_OA(type) IMM_TARGETS_OA_IMPL
455 #define IMM_TARGETS_VSA(n)
456 #define IMM_TARGETS_KA(n)
457 #define IMM_TARGETS_LAR(n)
458 #define IMM_TARGETS_ITA(n)
459 #define IMM_TARGETS_FCA(n) if (fca.asyncEagerTarget != NoBlockId) { \
460 f(fca.asyncEagerTarget); \
463 #define IMM_EXTRA_BLA
464 #define IMM_EXTRA_SLA
465 #define IMM_EXTRA_ILA
466 #define IMM_EXTRA_IVA
467 #define IMM_EXTRA_I64A
468 #define IMM_EXTRA_LA
469 #define IMM_EXTRA_IA
470 #define IMM_EXTRA_DA
471 #define IMM_EXTRA_SA
472 #define IMM_EXTRA_RATA
473 #define IMM_EXTRA_AA
474 #define IMM_EXTRA_BA
475 #define IMM_EXTRA_OA(x)
476 #define IMM_EXTRA_VSA
477 #define IMM_EXTRA_KA
478 #define IMM_EXTRA_LAR
479 #define IMM_EXTRA_ITA
480 #define IMM_EXTRA_FCA
482 #define IMM_MEM(which, n) IMM_TY_##which IMM_NAME_##which(n)
483 #define IMM_MEM_NA
484 #define IMM_MEM_ONE(x) IMM_MEM(x, 1);
485 #define IMM_MEM_TWO(x, y) IMM_MEM(x, 1); IMM_MEM(y, 2);
486 #define IMM_MEM_THREE(x, y, z) IMM_MEM(x, 1); IMM_MEM(y, 2); \
487 IMM_MEM(z, 3);
488 #define IMM_MEM_FOUR(x, y, z, l) IMM_MEM(x, 1); IMM_MEM(y, 2); \
489 IMM_MEM(z, 3); IMM_MEM(l, 4);
490 #define IMM_MEM_FIVE(x, y, z, l, m) IMM_MEM(x, 1); IMM_MEM(y, 2); \
491 IMM_MEM(z, 3); IMM_MEM(l, 4); \
492 IMM_MEM(m, 5);
493 #define IMM_MEM_SIX(x, y, z, l, m, n) IMM_MEM(x, 1); IMM_MEM(y, 2); \
494 IMM_MEM(z, 3); IMM_MEM(l, 4); \
495 IMM_MEM(m, 5); IMM_MEM(n, 6);
497 #define IMM_EQ_WRAP(e, ...) detail::eq_pairs(e, __VA_ARGS__)
498 #define IMM_EQ(which, n) detail::eq_operand< \
499 IMM_TY_##which, \
500 imm::IMM_ID_##which \
501 > { \
502 IMM_NAME_##which(n), \
503 o.IMM_NAME_##which(n) \
505 #define IMM_EQ_NA detail::eq_operand<void*,imm::NA> { 0, 0 }
506 #define IMM_EQ_ONE(x) IMM_EQ(x, 1)
507 #define IMM_EQ_TWO(x, y) IMM_EQ_ONE(x), IMM_EQ(y, 2)
508 #define IMM_EQ_THREE(x, y, z) IMM_EQ_TWO(x, y), IMM_EQ(z, 3)
509 #define IMM_EQ_FOUR(x, y, z, l) IMM_EQ_THREE(x, y, z), IMM_EQ(l, 4)
510 #define IMM_EQ_FIVE(x, y, z, l, m) IMM_EQ_FOUR(x, y, z, l), IMM_EQ(m, 5)
511 #define IMM_EQ_SIX(x, y, z, l, m, n) IMM_EQ_FIVE(x, y, z, l, m), IMM_EQ(n, 6)
513 #define IMM_HASH_WRAP(h, ...) detail::hash_combine(h, __VA_ARGS__)
514 #define IMM_HASH(which, n) detail::hash_operand< \
515 IMM_TY_##which, \
516 imm::IMM_ID_##which \
517 > { IMM_NAME_##which(n) }
518 #define IMM_HASH_NA detail::hash_operand<void*,imm::NA> { 0 }
519 #define IMM_HASH_ONE(x) IMM_HASH(x, 1)
520 #define IMM_HASH_TWO(x, y) IMM_HASH_ONE(x), IMM_HASH(y, 2)
521 #define IMM_HASH_THREE(x, y, z) IMM_HASH_TWO(x, y), IMM_HASH(z, 3)
522 #define IMM_HASH_FOUR(x, y, z, l) IMM_HASH_THREE(x, y, z), IMM_HASH(l, 4)
523 #define IMM_HASH_FIVE(x, y, z, l, m) IMM_HASH_FOUR(x, y, z, l), IMM_HASH(m, 5)
524 #define IMM_HASH_SIX(x, y, z, l, m, n) IMM_HASH_FIVE(x, y, z, l, m), IMM_HASH(n, 6)
526 #define IMM_TARGETS_NA
527 #define IMM_TARGETS_ONE(x) IMM_TARGETS_##x(1)
528 #define IMM_TARGETS_TWO(x,y) IMM_TARGETS_ONE(x) IMM_TARGETS_##y(2)
529 #define IMM_TARGETS_THREE(x,y,z) IMM_TARGETS_TWO(x,y) IMM_TARGETS_##z(3)
530 #define IMM_TARGETS_FOUR(x,y,z,l) IMM_TARGETS_THREE(x,y,z) IMM_TARGETS_##l(4)
531 #define IMM_TARGETS_FIVE(x,y,z,l,m) IMM_TARGETS_FOUR(x,y,z,l) IMM_TARGETS_##m(5)
532 #define IMM_TARGETS_SIX(x,y,z,l,m,n) IMM_TARGETS_FIVE(x,y,z,l,m) IMM_TARGETS_##n(6)
534 #define IMM_EXTRA_NA
535 #define IMM_EXTRA_ONE(x) IMM_EXTRA_##x
536 #define IMM_EXTRA_TWO(x,y) IMM_EXTRA_ONE(x) IMM_EXTRA_ONE(y)
537 #define IMM_EXTRA_THREE(x,y,z) IMM_EXTRA_TWO(x,y) IMM_EXTRA_ONE(z)
538 #define IMM_EXTRA_FOUR(x,y,z,l) IMM_EXTRA_THREE(x,y,z) IMM_EXTRA_ONE(l)
539 #define IMM_EXTRA_FIVE(x,y,z,l,m) IMM_EXTRA_FOUR(x,y,z,l) IMM_EXTRA_ONE(m)
540 #define IMM_EXTRA_SIX(x,y,z,l,m,n) IMM_EXTRA_FIVE(x,y,z,l,m) IMM_EXTRA_ONE(n)
542 #define IMM_CTOR(which, n) IMM_TY_##which IMM_NAME_##which(n)
543 #define IMM_CTOR_NA
544 #define IMM_CTOR_ONE(x) IMM_CTOR(x, 1)
545 #define IMM_CTOR_TWO(x, y) IMM_CTOR(x, 1), IMM_CTOR(y, 2)
546 #define IMM_CTOR_THREE(x, y, z) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
547 IMM_CTOR(z, 3)
548 #define IMM_CTOR_FOUR(x, y, z, l) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
549 IMM_CTOR(z, 3), IMM_CTOR(l, 4)
550 #define IMM_CTOR_FIVE(x, y, z, l, m) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
551 IMM_CTOR(z, 3), IMM_CTOR(l, 4), \
552 IMM_CTOR(m, 5)
553 #define IMM_CTOR_SIX(x, y, z, l, m, n) IMM_CTOR(x, 1), IMM_CTOR(y, 2), \
554 IMM_CTOR(z, 3), IMM_CTOR(l, 4), \
555 IMM_CTOR(m, 5), IMM_CTOR(n, 6)
557 #define IMM_INIT(which, n) IMM_NAME_##which(n) \
558 ( std::move(IMM_NAME_##which(n)) )
559 #define IMM_INIT_NA
560 #define IMM_INIT_ONE(x) : IMM_INIT(x, 1)
561 #define IMM_INIT_TWO(x, y) : IMM_INIT(x, 1), IMM_INIT(y, 2)
562 #define IMM_INIT_THREE(x, y, z) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
563 IMM_INIT(z, 3)
564 #define IMM_INIT_FOUR(x, y, z, l) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
565 IMM_INIT(z, 3), IMM_INIT(l, 4)
566 #define IMM_INIT_FIVE(x, y, z, l, m) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
567 IMM_INIT(z, 3), IMM_INIT(l, 4), \
568 IMM_INIT(m, 5)
569 #define IMM_INIT_SIX(x, y, z, l, m, n) : IMM_INIT(x, 1), IMM_INIT(y, 2), \
570 IMM_INIT(z, 3), IMM_INIT(l, 4), \
571 IMM_INIT(m, 5), IMM_INIT(n, 6)
573 #define POP_UV if (i == 0) return Flavor::U
574 #define POP_CV if (i == 0) return Flavor::C
575 #define POP_CU if (i == 0) return Flavor::CU
576 #define POP_CUV POP_CU
578 #define POP_NOV uint32_t numPop() const { return 0; } \
579 Flavor popFlavor(uint32_t) const { not_reached(); }
581 #define POP_ONE(x) uint32_t numPop() const { return 1; } \
582 Flavor popFlavor(uint32_t i) const { \
583 POP_##x; not_reached(); \
586 #define POP_TWO(x, y) uint32_t numPop() const { return 2; } \
587 Flavor popFlavor(uint32_t i) const { \
588 POP_##x; --i; POP_##y; not_reached(); \
591 #define POP_THREE(x, y, z) uint32_t numPop() const { return 3; } \
592 Flavor popFlavor(uint32_t i) const { \
593 POP_##x; --i; POP_##y; --i; POP_##z; \
594 not_reached(); \
597 #define POP_MFINAL uint32_t numPop() const { return arg1; } \
598 Flavor popFlavor(uint32_t) const { not_reached(); }
600 #define POP_C_MFINAL(n) uint32_t numPop() const { return arg1 + n; } \
601 Flavor popFlavor(uint32_t) const { not_reached(); }
603 #define POP_CMANY uint32_t numPop() const { return arg1; } \
604 Flavor popFlavor(uint32_t i) const { \
605 assert(i < numPop()); \
606 return Flavor::C; \
609 #define POP_SMANY uint32_t numPop() const { return keys.size(); } \
610 Flavor popFlavor(uint32_t i) const { \
611 assert(i < numPop()); \
612 return Flavor::C; \
615 #define POP_CUMANY uint32_t numPop() const { return arg1; } \
616 Flavor popFlavor(uint32_t i) const { \
617 assert(i < numPop()); \
618 return Flavor::CU; \
621 #define POP_CMANY_U3 \
622 uint32_t numPop() const { return arg1 + 3; } \
623 Flavor popFlavor(uint32_t i) const { \
624 assert(i < numPop()); \
625 return i < arg1 ? Flavor::C : Flavor::U; \
628 #define POP_CALLNATIVE uint32_t numPop() const { return arg1 + arg3; } \
629 Flavor popFlavor(uint32_t i) const { \
630 assert(i < numPop()); \
631 return i < arg1 ? Flavor::CU : Flavor::U; \
634 #define POP_FCALL(nin, nobj) \
635 uint32_t numPop() const { \
636 return nin + fca.numInputs() + 2 + fca.numRets; \
638 Flavor popFlavor(uint32_t i) const { \
639 assert(i < numPop()); \
640 if (i < nin) return Flavor::C; \
641 i -= nin; \
642 if (i == 0 && fca.hasGenerics()) return Flavor::C; \
643 i -= fca.hasGenerics() ? 1 : 0; \
644 if (i == 0 && fca.hasUnpack()) return Flavor::C; \
645 i -= fca.hasUnpack() ? 1 : 0; \
646 if (i < fca.numArgs) return Flavor::C; \
647 i -= fca.numArgs; \
648 if (i == 2 && nobj) return Flavor::C; \
649 return Flavor::U; \
652 #define PUSH_NOV uint32_t numPush() const { return 0; }
654 #define PUSH_ONE(x) uint32_t numPush() const { return 1; }
656 #define PUSH_TWO(x, y) uint32_t numPush() const { return 2; }
658 #define PUSH_CMANY uint32_t numPush() const { return arg1; }
659 #define PUSH_FCALL uint32_t numPush() const { return fca.numRets; }
660 #define PUSH_CALLNATIVE uint32_t numPush() const { return arg3 + 1; }
662 #define FLAGS_NF
663 #define FLAGS_TF
664 #define FLAGS_CF
665 #define FLAGS_FF
666 #define FLAGS_CF_TF
667 #define FLAGS_CF_FF
669 #define FLAGS_CTOR_NF
670 #define FLAGS_CTOR_TF
671 #define FLAGS_CTOR_CF
672 #define FLAGS_CTOR_FF
673 #define FLAGS_CTOR_CF_TF
674 #define FLAGS_CTOR_CF_FF
676 #define FLAGS_INIT_NF
677 #define FLAGS_INIT_TF
678 #define FLAGS_INIT_CF
679 #define FLAGS_INIT_FF
680 #define FLAGS_INIT_CF_TF
681 #define FLAGS_INIT_CF_FF
683 #define O(opcode, imms, inputs, outputs, flags) \
684 struct opcode { \
685 static constexpr Op op = Op::opcode; \
686 explicit opcode ( IMM_CTOR_##imms \
687 FLAGS_CTOR_##flags) \
688 IMM_INIT_##imms \
689 FLAGS_INIT_##flags \
690 {} \
692 IMM_MEM_##imms \
693 FLAGS_##flags \
694 IMM_EXTRA_##imms \
695 POP_##inputs \
696 PUSH_##outputs \
698 bool operator==(const opcode& o) const { \
699 return equals(o, detail::eq_default{}); \
702 bool operator!=(const opcode& o) const { \
703 return !(*this == o); \
706 template<class E> \
707 bool equals(const opcode& o, E e) const { \
708 return IMM_EQ_WRAP(e, IMM_EQ_##imms); \
711 size_t hash() const { \
712 return IMM_HASH_WRAP( \
713 detail::hasher_default{}, \
714 IMM_HASH_##imms); \
717 template<class H> \
718 size_t hash(H h) const { \
719 return IMM_HASH_WRAP(h, IMM_HASH_##imms); \
722 template <typename F> \
723 void forEachTarget(F&& f) { \
724 IMM_TARGETS_##imms \
726 template <typename F> \
727 void forEachTarget(F&& f) const { \
728 IMM_TARGETS_##imms \
731 OPCODES
732 #undef O
734 #undef FLAGS_NA
735 #undef FLAGS_TF
736 #undef FLAGS_CF
737 #undef FLAGS_FF
738 #undef FLAGS_CF_TF
739 #undef FLAGS_CF_FF
741 #undef FLAGS_CTOR_NA
742 #undef FLAGS_CTOR_TF
743 #undef FLAGS_CTOR_CF
744 #undef FLAGS_CTOR_FF
745 #undef FLAGS_CTOR_CF_TF
746 #undef FLAGS_CTOR_CF_FF
748 #undef FLAGS_INIT_NA
749 #undef FLAGS_INIT_TF
750 #undef FLAGS_INIT_CF
751 #undef FLAGS_INIT_FF
752 #undef FLAGS_INIT_CF_TF
753 #undef FLAGS_INIT_CF_FF
755 #undef PUSH_NOV
756 #undef PUSH_ONE
757 #undef PUSH_TWO
758 #undef PUSH_CMANY
759 #undef PUSH_FCALL
760 #undef PUSH_CALLNATIVE
762 #undef POP_UV
763 #undef POP_CV
764 #undef POP_CU
765 #undef POP_CUV
767 #undef POP_NOV
768 #undef POP_ONE
769 #undef POP_TWO
770 #undef POP_THREE
771 #undef POP_MFINAL
772 #undef POP_C_MFINAL
773 #undef POP_CMANY
774 #undef POP_SMANY
775 #undef POP_CUMANY
776 #undef POP_CMANY_U3
777 #undef POP_CALLNATIVE
778 #undef POP_FCALL
780 #undef IMM_TY_MA
781 #undef IMM_TY_BLA
782 #undef IMM_TY_SLA
783 #undef IMM_TY_ILA
784 #undef IMM_TY_IVA
785 #undef IMM_TY_I64A
786 #undef IMM_TY_LA
787 #undef IMM_TY_IA
788 #undef IMM_TY_DA
789 #undef IMM_TY_SA
790 #undef IMM_TY_RATA
791 #undef IMM_TY_AA
792 #undef IMM_TY_BA
793 #undef IMM_TY_OA
794 #undef IMM_TY_VSA
795 #undef IMM_TY_KA
796 #undef IMM_TY_LAR
797 #undef IMM_TY_ITA
798 #undef IMM_TY_FCA
800 // These are deliberately not undefined, so they can be used in other
801 // places.
802 // #undef IMM_NAME_BLA
803 // #undef IMM_NAME_SLA
804 // #undef IMM_NAME_ILA
805 // #undef IMM_NAME_IVA
806 // #undef IMM_NAME_I64A
807 // #undef IMM_NAME_LA
808 // #undef IMM_NAME_IA
809 // #undef IMM_NAME_DA
810 // #undef IMM_NAME_SA
811 // #undef IMM_NAME_RATA
812 // #undef IMM_NAME_AA
813 // #undef IMM_NAME_BA
814 // #undef IMM_NAME_OA
815 // #undef IMM_NAME_OA_IMPL
816 // #undef IMM_NAME_LAR
817 // #undef IMM_NAME_FCA
819 #undef IMM_TARGETS_BLA
820 #undef IMM_TARGETS_SLA
821 #undef IMM_TARGETS_ILA
822 #undef IMM_TARGETS_IVA
823 #undef IMM_TARGETS_I64A
824 #undef IMM_TARGETS_LA
825 #undef IMM_TARGETS_IA
826 #undef IMM_TARGETS_DA
827 #undef IMM_TARGETS_SA
828 #undef IMM_TARGETS_RATA
829 #undef IMM_TARGETS_AA
830 #undef IMM_TARGETS_BA
831 #undef IMM_TARGETS_OA
832 #undef IMM_TARGETS_KA
833 #undef IMM_TARGETS_LAR
834 #undef IMM_TARGETS_ITA
835 #undef IMM_TARGETS_FCA
837 #undef IMM_TARGETS_NA
838 #undef IMM_TARGETS_ONE
839 #undef IMM_TARGETS_TWO
840 #undef IMM_TARGETS_THREE
841 #undef IMM_TARGETS_FOUR
842 #undef IMM_TARGETS_FIVE
843 #undef IMM_TARGETS_SIX
845 #undef IMM_EXTRA_BLA
846 #undef IMM_EXTRA_SLA
847 #undef IMM_EXTRA_ILA
848 #undef IMM_EXTRA_IVA
849 #undef IMM_EXTRA_I64A
850 #undef IMM_EXTRA_LA
851 #undef IMM_EXTRA_IA
852 #undef IMM_EXTRA_DA
853 #undef IMM_EXTRA_SA
854 #undef IMM_EXTRA_RATA
855 #undef IMM_EXTRA_AA
856 #undef IMM_EXTRA_BA
857 #undef IMM_EXTRA_OA
858 #undef IMM_EXTRA_KA
859 #undef IMM_EXTRA_LAR
860 #undef IMM_EXTRA_ITA
861 #undef IMM_EXTRA_FCA
863 #undef IMM_MEM
864 #undef IMM_MEM_NA
865 #undef IMM_MEM_ONE
866 #undef IMM_MEM_TWO
867 #undef IMM_MEM_THREE
868 #undef IMM_MEM_FOUR
869 #undef IMM_MEM_FIVE
870 #undef IMM_MEM_SIX
872 #undef IMM_EQ
873 #undef IMM_EQ_NA
874 #undef IMM_EQ_ONE
875 #undef IMM_EQ_TWO
876 #undef IMM_EQ_THREE
877 #undef IMM_EQ_FOUR
878 #undef IMM_EQ_FIVE
879 #undef IMM_EQ_SIX
881 #undef IMM_HASH
882 #undef IMM_HASH_DO
883 #undef IMM_HASH_NA
884 #undef IMM_HASH_ONE
885 #undef IMM_HASH_TWO
886 #undef IMM_HASH_THREE
887 #undef IMM_HASH_FOUR
888 #undef IMM_HASH_FIVE
889 #undef IMM_HASH_SIX
891 #undef IMM_CTOR
892 #undef IMM_CTOR_NA
893 #undef IMM_CTOR_ONE
894 #undef IMM_CTOR_TWO
895 #undef IMM_CTOR_THREE
896 #undef IMM_CTOR_FOUR
897 #undef IMM_CTOR_FIVE
898 #undef IMM_CTOR_SIX
900 #undef IMM_INIT
901 #undef IMM_INIT_NA
902 #undef IMM_INIT_ONE
903 #undef IMM_INIT_TWO
904 #undef IMM_INIT_THREE
905 #undef IMM_INIT_FOUR
909 //////////////////////////////////////////////////////////////////////
912 * Bytecode is a tagged-union that can hold any HHBC opcode struct
913 * defined above. You can visit a bytecode with a static_visitor
914 * using visit(), defined below.
916 * Each Bytecode also carries a corresponding SrcLoc that indicates
917 * which line of PHP code the bytecode was generated for.
919 struct Bytecode {
920 // Default construction creates a Nop.
921 Bytecode()
922 : op(Op::Nop)
923 , Nop(bc::Nop{})
926 #define O(opcode, ...) \
927 /* implicit */ Bytecode(bc::opcode data) \
928 : op(Op::opcode) \
930 new (&opcode) bc::opcode(std::move(data)); \
933 OPCODES
935 #undef O
937 // Note: assuming bc::Nop is empty and has trivial dtor/ctor.
939 Bytecode(const Bytecode& o) : op(Op::Nop) { *this = o; }
940 Bytecode(Bytecode&& o) noexcept : op(Op::Nop) { *this = std::move(o); }
942 Bytecode& operator=(const Bytecode& o) {
943 destruct();
944 op = Op::Nop;
945 srcLoc = o.srcLoc;
946 #define O(opcode, ...) \
947 case Op::opcode: new (&opcode) bc::opcode(o.opcode); break;
948 switch (o.op) { OPCODES }
949 #undef O
950 op = o.op;
951 return *this;
954 Bytecode& operator=(Bytecode&& o) {
955 destruct();
956 srcLoc = o.srcLoc;
957 #define O(opcode, ...) \
958 case Op::opcode: new (&opcode) bc::opcode(std::move(o.opcode)); break;
959 switch (o.op) { OPCODES }
960 #undef O
961 op = o.op;
962 return *this;
965 ~Bytecode() { destruct(); }
967 uint32_t numPop() const {
968 #define O(opcode, ...) \
969 case Op::opcode: return opcode.numPop();
970 switch (op) { OPCODES }
971 #undef O
972 not_reached();
975 Flavor popFlavor(uint32_t i) const {
976 #define O(opcode, ...) \
977 case Op::opcode: return opcode.popFlavor(i);
978 switch (op) { OPCODES }
979 #undef O
980 not_reached();
983 uint32_t numPush() const {
984 #define O(opcode, ...) \
985 case Op::opcode: return opcode.numPush();
986 switch (op) { OPCODES }
987 #undef O
988 not_reached();
991 template <typename F>
992 void forEachTarget(F&& f) const {
993 #define O(opcode, ...) \
994 case Op::opcode: return opcode.forEachTarget(std::forward<F>(f));
995 switch (op) { OPCODES }
996 #undef O
997 not_reached();
1000 template <typename F>
1001 void forEachTarget(F&& f) {
1002 #define O(opcode, ...) \
1003 case Op::opcode: return opcode.forEachTarget(std::forward<F>(f));
1004 switch (op) { OPCODES }
1005 #undef O
1006 not_reached();
1009 Op op;
1010 int32_t srcLoc{-1};
1012 #define O(opcode, ...) bc::opcode opcode;
1013 union { OPCODES };
1014 #undef O
1016 private:
1017 void destruct() {
1018 switch (op) {
1019 #define O(opcode, ...) \
1020 case Op::opcode: \
1021 { typedef bc::opcode X; \
1022 this->opcode.~X(); } \
1023 break;
1024 OPCODES
1025 #undef O
1030 //////////////////////////////////////////////////////////////////////
1032 inline bool operator==(const Bytecode& a, const Bytecode& b) {
1033 if (a.op != b.op) return false;
1034 #define O(opcode, ...) case Op::opcode: return a.opcode == b.opcode;
1035 switch (a.op) { OPCODES }
1036 #undef O
1037 not_reached();
1040 inline bool operator!=(const Bytecode& a, const Bytecode& b) {
1041 return !(a == b);
1044 template<class E>
1045 inline bool equals(const Bytecode& a, const Bytecode& b, E equals) {
1046 if (a.op != b.op) return false;
1047 #define O(opcode, ...) \
1048 case Op::opcode: return a.opcode.equals(b.opcode, equals);
1049 switch (a.op) { OPCODES }
1050 #undef O
1051 not_reached();
1054 template<class H>
1055 inline size_t hash(const Bytecode& b, H hasher) {
1056 auto hash = 14695981039346656037ULL;
1057 auto o = static_cast<size_t>(b.op);
1058 hash ^= o;
1059 #define O(opcode, ...) \
1060 case Op::opcode: \
1061 return folly::hash::hash_combine(b.opcode.hash(hasher), hash);
1062 switch (b.op) { OPCODES }
1063 #undef O
1064 not_reached();
1067 inline size_t hash(const Bytecode& b) {
1068 return hash(b, bc::detail::hasher_default{});
1071 //////////////////////////////////////////////////////////////////////
1074 * Helper for making a Bytecode with a given srcLoc.
1076 * Ex:
1077 * auto b = bc_with_loc(something.srcLoc, bc::Nop {});
1079 template<class T> Bytecode bc_with_loc(int32_t loc, const T& t) {
1080 Bytecode b = t;
1081 b.srcLoc = loc;
1082 return b;
1085 //////////////////////////////////////////////////////////////////////
1088 * Visit a bytecode using a StaticVisitor, similar to
1089 * boost::apply_visitor or match().
1091 * The `v' argument should be a function object that accepts a call
1092 * operator for all the bc::Foo types. Its result type should be
1093 * independent of bytecode, but may vary with constness.
1095 template<class Visit>
1096 auto visit(Bytecode& b, Visit v) {
1097 #define O(opcode, ...) case Op::opcode: return v(b.opcode);
1098 switch (b.op) { OPCODES }
1099 #undef O
1100 not_reached();
1103 template<class Visit>
1104 auto visit(const Bytecode& b, Visit v) {
1105 #define O(opcode, ...) case Op::opcode: return v(b.opcode);
1106 switch (b.op) { OPCODES }
1107 #undef O
1108 not_reached();
1111 //////////////////////////////////////////////////////////////////////
1117 #endif