2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/hhbbc/emit.h"
23 #include <type_traits>
25 #include <folly/gen/Base.h>
26 #include <folly/Conv.h>
27 #include <folly/Optional.h>
28 #include <folly/Memory.h>
30 #include "hphp/hhbbc/cfg.h"
31 #include "hphp/hhbbc/class-util.h"
32 #include "hphp/hhbbc/context.h"
33 #include "hphp/hhbbc/func-util.h"
34 #include "hphp/hhbbc/index.h"
35 #include "hphp/hhbbc/options.h"
36 #include "hphp/hhbbc/representation.h"
37 #include "hphp/hhbbc/unit-util.h"
39 #include "hphp/runtime/base/repo-auth-type-array.h"
40 #include "hphp/runtime/base/repo-auth-type-codec.h"
41 #include "hphp/runtime/base/repo-auth-type.h"
42 #include "hphp/runtime/base/tv-comparisons.h"
44 #include "hphp/runtime/vm/bytecode.h"
45 #include "hphp/runtime/vm/func-emitter.h"
46 #include "hphp/runtime/vm/native.h"
47 #include "hphp/runtime/vm/preclass-emitter.h"
48 #include "hphp/runtime/vm/record-emitter.h"
49 #include "hphp/runtime/vm/unit-emitter.h"
51 namespace HPHP
{ namespace HHBBC
{
53 TRACE_SET_MOD(hhbbc_emit
);
57 //////////////////////////////////////////////////////////////////////
59 const StaticString
s_invoke("__invoke");
61 //////////////////////////////////////////////////////////////////////
73 struct EmitUnitState
{
74 explicit EmitUnitState(const Index
& index
, const php::Unit
* unit
) :
75 index(index
), unit(unit
) {}
78 * Access to the Index for this program.
83 * Access to the unit we're emitting
85 const php::Unit
* unit
;
88 * While emitting bytecode, we keep track of the classes and funcs
91 std::vector
<Offset
> classOffsets
;
92 std::vector
<PceInfo
> pceInfo
;
93 std::vector
<FeInfo
> feInfo
;
94 std::vector
<Id
> typeAliasInfo
;
96 std::unordered_set
<Id
> processedTypeAlias
;
100 * Some bytecodes need to be mutated before being emitted. Pass those
101 * bytecodes by value to their respective emit_op functions.
104 struct OpInfoHelper
{
105 static constexpr bool by_value
=
106 T::op
== Op::DefCls
||
107 T::op
== Op::DefClsNop
||
108 T::op
== Op::CreateCl
||
109 T::op
== Op::DefTypeAlias
;
111 using type
= typename
std::conditional
<by_value
, T
, const T
&>::type
;
115 using OpInfo
= typename OpInfoHelper
<T
>::type
;
118 * Helper to conditionally call fun on data provided data is of the
121 template<Op op
, typename F
, typename Bc
>
122 std::enable_if_t
<std::remove_reference_t
<Bc
>::op
== op
>
123 caller(F
&& fun
, Bc
&& data
) { fun(std::forward
<Bc
>(data
)); }
125 template<Op op
, typename F
, typename Bc
>
126 std::enable_if_t
<std::remove_reference_t
<Bc
>::op
!= op
>
129 Id
recordClass(EmitUnitState
& euState
, UnitEmitter
& ue
, Id id
) {
130 auto cls
= euState
.unit
->classes
[id
].get();
131 euState
.pceInfo
.push_back(
132 { ue
.newPreClassEmitter(cls
->name
->toCppString(), cls
->hoistability
), id
}
134 return euState
.pceInfo
.back().pce
->id();
137 //////////////////////////////////////////////////////////////////////
139 php::SrcLoc
srcLoc(const php::Func
& func
, int32_t ix
) {
140 if (ix
< 0) return php::SrcLoc
{};
141 auto const unit
= func
.originalUnit
? func
.originalUnit
: func
.unit
;
142 return unit
->srcLocs
[ix
];
146 * Order the blocks for bytecode emission.
148 * Rules about block order:
150 * - Each DV funclet must have all of its blocks contiguous, with the
153 * - Main entry point must be the first block.
155 * It is not a requirement, but we attempt to locate all the DV entry
156 * points after the rest of the primary function body. The normal
157 * case for DV initializers is that each one falls through to the
158 * next, with the block jumping back to the main entry point.
160 std::vector
<BlockId
> order_blocks(const php::Func
& f
) {
161 auto sorted
= rpoSortFromMain(f
);
163 // Get the DV blocks, without the rest of the primary function body,
164 // and then add them to the end of sorted.
165 auto const dvBlocks
= [&] {
166 auto withDVs
= rpoSortAddDVs(f
);
168 std::find(begin(withDVs
), end(withDVs
), sorted
.front()),
173 sorted
.insert(end(sorted
), begin(dvBlocks
), end(dvBlocks
));
175 FTRACE(2, " block order:{}\n",
178 for (auto const bid
: sorted
) {
180 ret
+= folly::to
<std::string
>(bid
);
188 // While emitting bytecode, we learn about some metadata that will
189 // need to be registered in the FuncEmitter.
191 struct JmpFixup
{ Offset instrOff
; Offset jmpImmedOff
; };
195 : offset(kInvalidOffset
)
196 , past(kInvalidOffset
)
200 // The offset of the block, if we've already emitted it.
201 // Otherwise kInvalidOffset.
204 // The offset past the end of this block.
207 // How many catch regions the jump at the end of this block is leaving.
208 // 0 if there is no jump or if the jump is to the same catch region or a
212 // When we emit a forward jump to a block we haven't seen yet, we
213 // write down where the jump was so we can fix it up when we get
215 std::vector
<JmpFixup
> forwardJumps
;
217 // When we see a forward jump to a block, we record the stack
218 // depth at the jump site here. This is needed to track
219 // currentStackDepth correctly (and we also assert all the jumps
220 // have the same depth).
221 folly::Optional
<uint32_t> expectedStackDepth
;
224 std::vector
<BlockId
> blockOrder
;
225 uint32_t maxStackDepth
;
227 std::vector
<BlockInfo
> blockInfo
;
230 using ExnNodePtr
= php::ExnNode
*;
232 bool handleEquivalent(const php::Func
& func
, ExnNodeId eh1
, ExnNodeId eh2
) {
233 auto entry
= [&] (ExnNodeId eid
) {
234 return func
.exnNodes
[eid
].region
.catchEntry
;
238 assertx(eh1
!= NoExnNodeId
&&
239 eh2
!= NoExnNodeId
&&
240 func
.exnNodes
[eh1
].depth
== func
.exnNodes
[eh2
].depth
);
241 if (entry(eh1
) != entry(eh2
)) return false;
242 eh1
= func
.exnNodes
[eh1
].parent
;
243 eh2
= func
.exnNodes
[eh2
].parent
;
249 // The common parent P of eh1 and eh2 is the deepest region such that
250 // eh1 and eh2 are both handle-equivalent to P or a child of P
251 ExnNodeId
commonParent(const php::Func
& func
, ExnNodeId eh1
, ExnNodeId eh2
) {
252 if (eh1
== NoExnNodeId
|| eh2
== NoExnNodeId
) return NoExnNodeId
;
253 while (func
.exnNodes
[eh1
].depth
> func
.exnNodes
[eh2
].depth
) {
254 eh1
= func
.exnNodes
[eh1
].parent
;
256 while (func
.exnNodes
[eh2
].depth
> func
.exnNodes
[eh1
].depth
) {
257 eh2
= func
.exnNodes
[eh2
].parent
;
259 while (!handleEquivalent(func
, eh1
, eh2
)) {
260 eh1
= func
.exnNodes
[eh1
].parent
;
261 eh2
= func
.exnNodes
[eh2
].parent
;
267 s_hhbbc_fail_verification("__hhvm_intrinsics\\hhbbc_fail_verification");
269 EmitBcInfo
emit_bytecode(EmitUnitState
& euState
,
271 const php::Func
& func
) {
273 auto& blockInfo
= ret
.blockInfo
;
274 blockInfo
.resize(func
.blocks
.size());
276 // Track the stack depth while emitting to determine maxStackDepth.
277 int32_t currentStackDepth
{ 0 };
279 // Temporary buffer for vector immediates. (Hoisted so it's not
280 // allocated in the loop.)
281 std::vector
<uint8_t> immVec
;
283 // Offset of the last emitted bytecode.
284 Offset lastOff
{ 0 };
286 bool traceBc
= false;
290 SCOPE_ASSERT_DETAIL("emit") {
292 for (auto bid
: func
.blockRange()) {
296 show(func
, *func
.blocks
[bid
])
303 auto const pseudomain
= is_pseudomain(&func
);
304 auto process_mergeable
= [&] (const Bytecode
& bc
) {
305 if (!pseudomain
) return;
309 if (!ue
.m_returnSeen
) {
310 auto const& cls
= euState
.unit
->classes
[
311 bc
.op
== Op::DefCls
? bc
.DefCls
.arg1
: bc
.DefClsNop
.arg1
];
312 if (cls
->hoistability
== PreClass::NotHoistable
) {
313 cls
->hoistability
= PreClass::Mergeable
;
318 case Op::AssertRATStk
:
323 if (ue
.m_returnSeen
|| tos
.subtypeOf(BBottom
)) break;
326 auto val
= euState
.index
.lookup_persistent_constant(bc
.DefCns
.str1
);
327 // If there's a persistent constant with the same name, either
328 // this is the one and only definition, or the persistent
329 // definition is in systemlib (and this one will always fail).
330 auto const kind
= val
&& cellSame(*val
, *top
) ?
331 Unit::MergeKind::PersistentDefine
: Unit::MergeKind::Define
;
332 ue
.pushMergeableDef(kind
, bc
.DefCns
.str1
, *top
);
335 case Op::DefTypeAlias
: {
336 auto tid
= bc
.DefTypeAlias
.arg1
;
337 ue
.pushMergeableTypeAlias(tid
);
338 euState
.processedTypeAlias
.insert(tid
);
342 case Op::Null
: tos
= TInitNull
; return;
343 case Op::True
: tos
= TTrue
; return;
344 case Op::False
: tos
= TFalse
; return;
345 case Op::Int
: tos
= ival(bc
.Int
.arg1
); return;
346 case Op::Double
: tos
= dval(bc
.Double
.dbl1
); return;
347 case Op::String
: tos
= sval(bc
.String
.str1
); return;
348 case Op::Vec
: tos
= vec_val(bc
.Vec
.arr1
); return;
349 case Op::Dict
: tos
= dict_val(bc
.Dict
.arr1
); return;
350 case Op::Keyset
: tos
= keyset_val(bc
.Keyset
.arr1
); return;
351 case Op::Array
: tos
= aval(bc
.Array
.arr1
); return;
356 if (ue
.m_returnSeen
|| tos
.subtypeOf(BBottom
)) break;
359 ue
.m_returnSeen
= true;
360 ue
.m_mainReturn
= *top
;
367 ue
.m_returnSeen
= true;
368 ue
.m_mainReturn
= make_tv
<KindOfUninit
>();
372 auto map_local
= [&] (LocalId id
) {
373 auto const loc
= func
.locals
[id
];
375 assert(loc
.id
<= id
);
379 auto set_expected_depth
= [&] (BlockId block
) {
380 auto& info
= blockInfo
[block
];
382 if (info
.expectedStackDepth
) {
383 assert(*info
.expectedStackDepth
== currentStackDepth
);
385 info
.expectedStackDepth
= currentStackDepth
;
389 auto make_member_key
= [&] (MKey mkey
) {
390 switch (mkey
.mcode
) {
392 return MemberKey
{mkey
.mcode
, mkey
.idx
};
395 mkey
.mcode
, static_cast<int32_t>(map_local(mkey
.local
))
397 case MET
: case MPT
: case MQT
:
398 return MemberKey
{mkey
.mcode
, mkey
.litstr
};
400 return MemberKey
{mkey
.mcode
, mkey
.int64
};
407 auto emit_inst
= [&] (const Bytecode
& inst
) {
408 process_mergeable(inst
);
409 auto const startOffset
= ue
.bcPos();
410 lastOff
= startOffset
;
412 FTRACE(4, " emit: {} -- {} @ {}\n", currentStackDepth
, show(&func
, inst
),
413 show(srcLoc(func
, inst
.srcLoc
)));
415 if (options
.TraceBytecodes
.count(inst
.op
)) traceBc
= true;
417 auto emit_vsa
= [&] (const CompactVector
<LSString
>& keys
) {
418 auto n
= keys
.size();
420 for (size_t i
= 0; i
< n
; ++i
) {
421 ue
.emitInt32(ue
.mergeLitstr(keys
[i
]));
425 auto emit_branch
= [&] (BlockId id
) {
426 auto& info
= blockInfo
[id
];
427 if (info
.offset
!= kInvalidOffset
) {
428 ue
.emitInt32(info
.offset
- startOffset
);
430 info
.forwardJumps
.push_back({ startOffset
, ue
.bcPos() });
435 auto emit_switch
= [&] (const SwitchTab
& targets
) {
436 ue
.emitIVA(targets
.size());
437 for (auto t
: targets
) {
438 set_expected_depth(t
);
443 auto emit_sswitch
= [&] (const SSwitchTab
& targets
) {
444 ue
.emitIVA(targets
.size());
445 for (size_t i
= 0; i
< targets
.size() - 1; ++i
) {
446 set_expected_depth(targets
[i
].second
);
447 ue
.emitInt32(ue
.mergeLitstr(targets
[i
].first
));
448 emit_branch(targets
[i
].second
);
451 set_expected_depth(targets
[targets
.size() - 1].second
);
452 emit_branch(targets
[targets
.size() - 1].second
);
455 auto emit_itertab
= [&] (const IterTab
& iterTab
) {
456 ue
.emitIVA(iterTab
.size());
457 for (auto const& kv
: iterTab
) {
460 if (kv
.kind
== KindOfLIter
) {
461 always_assert(kv
.local
!= NoLocalId
);
462 ue
.emitIVA(map_local(kv
.local
));
464 always_assert(kv
.local
== NoLocalId
);
469 auto emit_srcloc
= [&] {
470 auto const sl
= srcLoc(func
, inst
.srcLoc
);
471 if (!sl
.isValid()) return;
472 Location::Range
loc(sl
.start
.line
, sl
.start
.col
,
473 sl
.past
.line
, sl
.past
.col
);
474 ue
.recordSourceLocation(loc
, startOffset
);
477 auto pop
= [&] (int32_t n
) {
478 currentStackDepth
-= n
;
479 assert(currentStackDepth
>= 0);
481 auto push
= [&] (int32_t n
) {
482 currentStackDepth
+= n
;
484 std::max
<uint32_t>(ret
.maxStackDepth
, currentStackDepth
);
487 auto ret_assert
= [&] { assert(currentStackDepth
== inst
.numPop()); };
489 auto clsid_impl
= [&] (uint32_t& id
, bool closure
) {
490 if (euState
.classOffsets
[id
] != kInvalidOffset
) {
491 always_assert(closure
);
492 for (auto const& elm
: euState
.pceInfo
) {
493 if (elm
.origId
== id
) {
498 always_assert(false);
500 euState
.classOffsets
[id
] = startOffset
;
501 id
= recordClass(euState
, ue
, id
);
503 auto defcls
= [&] (auto& data
) { clsid_impl(data
.arg1
, false); };
504 auto defclsnop
= [&] (auto& data
) { clsid_impl(data
.arg1
, false); };
505 auto createcl
= [&] (auto& data
) { clsid_impl(data
.arg2
, true); };
506 auto deftype
= [&] (auto& data
) {
507 euState
.typeAliasInfo
.push_back(data
.arg1
);
508 data
.arg1
= euState
.typeAliasInfo
.size() - 1;
511 auto emit_lar
= [&](const LocalRange
& range
) {
512 always_assert(range
.first
+ range
.count
<= func
.locals
.size());
513 auto const first
= (range
.count
> 0) ? map_local(range
.first
) : 0;
514 encodeLocalRange(ue
, HPHP::LocalRange
{first
, range
.count
});
517 #define IMM_BLA(n) emit_switch(data.targets);
518 #define IMM_SLA(n) emit_sswitch(data.targets);
519 #define IMM_ILA(n) emit_itertab(data.iterTab);
520 #define IMM_IVA(n) ue.emitIVA(data.arg##n);
521 #define IMM_I64A(n) ue.emitInt64(data.arg##n);
522 #define IMM_LA(n) ue.emitIVA(map_local(data.loc##n));
523 #define IMM_IA(n) ue.emitIVA(data.iter##n);
524 #define IMM_DA(n) ue.emitDouble(data.dbl##n);
525 #define IMM_SA(n) ue.emitInt32(ue.mergeLitstr(data.str##n));
526 #define IMM_RATA(n) encodeRAT(ue, data.rat);
527 #define IMM_AA(n) ue.emitInt32(ue.mergeArray(data.arr##n));
528 #define IMM_OA_IMPL(n) ue.emitByte(static_cast<uint8_t>(data.subop##n));
529 #define IMM_OA(type) IMM_OA_IMPL
530 #define IMM_BA(n) targets[numTargets++] = data.target##n; \
531 emit_branch(data.target##n);
532 #define IMM_VSA(n) emit_vsa(data.keys);
533 #define IMM_KA(n) encode_member_key(make_member_key(data.mkey), ue);
534 #define IMM_LAR(n) emit_lar(data.locrange);
535 #define IMM_FCA(n) encodeFCallArgs( \
536 ue, data.fca, data.fca.inoutArgs.get(), \
537 data.fca.asyncEagerTarget != NoBlockId, \
539 set_expected_depth(data.fca.asyncEagerTarget); \
540 emit_branch(data.fca.asyncEagerTarget); \
542 if (!data.fca.hasUnpack()) ret.containsCalls = true;\
545 #define IMM_ONE(x) IMM_##x(1)
546 #define IMM_TWO(x, y) IMM_##x(1); IMM_##y(2);
547 #define IMM_THREE(x, y, z) IMM_TWO(x, y); IMM_##z(3);
548 #define IMM_FOUR(x, y, z, n) IMM_THREE(x, y, z); IMM_##n(4);
549 #define IMM_FIVE(x, y, z, n, m) IMM_FOUR(x, y, z, n); IMM_##m(5);
550 #define IMM_SIX(x, y, z, n, m, o) IMM_FIVE(x, y, z, n, m); IMM_##o(6);
553 #define POP_ONE(x) pop(1);
554 #define POP_TWO(x, y) pop(2);
555 #define POP_THREE(x, y, z) pop(3);
557 #define POP_MFINAL pop(data.arg1);
558 #define POP_C_MFINAL(n) pop(n); pop(data.arg1);
559 #define POP_CMANY pop(data.arg##1);
560 #define POP_SMANY pop(data.keys.size());
561 #define POP_CUMANY pop(data.arg##1);
562 #define POP_CMANY_U3 pop(data.arg1 + 3);
563 #define POP_CALLNATIVE pop(data.arg1 + data.arg3);
564 #define POP_FCALL(nin, nobj) \
565 pop(nin + data.fca.numInputs() + 2 + data.fca.numRets);
568 #define PUSH_ONE(x) push(1);
569 #define PUSH_TWO(x, y) push(2);
570 #define PUSH_THREE(x, y, z) push(3);
571 #define PUSH_CMANY push(data.arg1);
572 #define PUSH_FCALL push(data.fca.numRets);
573 #define PUSH_CALLNATIVE push(data.arg3 + 1);
575 #define O(opcode, imms, inputs, outputs, flags) \
576 auto emit_##opcode = [&] (OpInfo<bc::opcode> data) { \
577 if (RuntimeOption::EnableIntrinsicsExtension) { \
578 if (Op::opcode == Op::FCallBuiltin && \
579 inst.FCallBuiltin.str4->isame( \
580 s_hhbbc_fail_verification.get())) { \
581 ue.emitOp(Op::CheckProp); \
583 ue.mergeLitstr(inst.FCallBuiltin.str4)); \
584 ue.emitOp(Op::PopC); \
587 caller<Op::DefCls>(defcls, data); \
588 caller<Op::DefClsNop>(defclsnop, data); \
589 caller<Op::CreateCl>(createcl, data); \
590 caller<Op::DefTypeAlias>(deftype, data); \
592 if (isRet(Op::opcode)) ret_assert(); \
593 ue.emitOp(Op::opcode); \
596 size_t numTargets = 0; \
597 std::array<BlockId, kMaxHhbcImms> targets; \
599 if (Op::opcode == Op::MemoGet) { \
601 assertx(numTargets == 1); \
602 set_expected_depth(targets[0]); \
604 } else if (Op::opcode == Op::MemoGetEager) { \
606 assertx(numTargets == 2); \
607 set_expected_depth(targets[0]); \
609 set_expected_depth(targets[1]); \
613 for (size_t i = 0; i < numTargets; ++i) { \
614 set_expected_depth(targets[i]); \
618 if (flags & TF) currentStackDepth = 0; \
663 #undef POP_CALLNATIVE
674 #undef PUSH_CALLNATIVE
676 #define O(opcode, ...) \
678 if (Op::opcode != Op::Nop) emit_##opcode(inst.opcode); \
680 switch (inst
.op
) { OPCODES
}
684 ret
.blockOrder
= order_blocks(func
);
685 auto blockIt
= begin(ret
.blockOrder
);
686 auto const endBlockIt
= end(ret
.blockOrder
);
687 for (; blockIt
!= endBlockIt
; ++blockIt
) {
689 auto& info
= blockInfo
[bid
];
690 auto const b
= func
.blocks
[bid
].get();
692 info
.offset
= ue
.bcPos();
693 FTRACE(2, " block {}: {}\n", bid
, info
.offset
);
695 for (auto& fixup
: info
.forwardJumps
) {
696 ue
.emitInt32(info
.offset
- fixup
.instrOff
, fixup
.jmpImmedOff
);
699 if (!info
.expectedStackDepth
) {
700 // unreachable, or entry block
701 info
.expectedStackDepth
= b
->catchEntry
? 1 : 0;
704 currentStackDepth
= *info
.expectedStackDepth
;
706 auto fallthrough
= b
->fallthrough
;
707 auto end
= b
->hhbcs
.end();
710 if (is_single_nop(*b
)) {
711 if (blockIt
== begin(ret
.blockOrder
)) {
712 // If the first block is just a Nop, this means that there is
713 // a jump to the second block from somewhere in the
714 // function. We don't want this, so we change this nop to an
715 // EntryNop so it doesn't get optimized away
716 emit_inst(bc_with_loc(b
->hhbcs
.front().srcLoc
, bc::EntryNop
{}));
719 // If the block ends with JmpZ or JmpNZ to the next block, flip
720 // the condition to make the fallthrough the next block
721 if (b
->hhbcs
.back().op
== Op::JmpZ
||
722 b
->hhbcs
.back().op
== Op::JmpNZ
) {
723 auto const& bc
= b
->hhbcs
.back();
725 bc
.op
== Op::JmpNZ
? bc
.JmpNZ
.target1
: bc
.JmpZ
.target1
;
726 if (std::next(blockIt
) != endBlockIt
&& blockIt
[1] == target
) {
727 fallthrough
= target
;
733 for (auto iit
= b
->hhbcs
.begin(); iit
!= end
; ++iit
) emit_inst(*iit
);
735 if (end
->op
== Op::JmpNZ
) {
736 emit_inst(bc_with_loc(end
->srcLoc
, bc::JmpZ
{ b
->fallthrough
}));
738 emit_inst(bc_with_loc(end
->srcLoc
, bc::JmpNZ
{ b
->fallthrough
}));
743 info
.past
= ue
.bcPos();
745 if (fallthrough
!= NoBlockId
) {
746 set_expected_depth(fallthrough
);
747 if (std::next(blockIt
) == endBlockIt
||
748 blockIt
[1] != fallthrough
) {
749 if (b
->fallthroughNS
) {
750 emit_inst(bc::JmpNS
{ fallthrough
});
752 emit_inst(bc::Jmp
{ fallthrough
});
755 auto const parent
= commonParent(func
,
756 func
.blocks
[fallthrough
]->exnNodeId
,
759 auto depth
= [&] (ExnNodeId eid
) {
760 return eid
== NoExnNodeId
? 0 : func
.exnNodes
[eid
].depth
;
762 // If we are in an exn region we pop from the current region to the
763 // common parent. If the common parent is null, we pop all regions
764 info
.regionsToPop
= depth(b
->exnNodeId
) - depth(parent
);
765 assert(info
.regionsToPop
>= 0);
766 FTRACE(4, " popped catch regions: {}\n", info
.regionsToPop
);
770 if (b
->throwExit
!= NoBlockId
) {
771 FTRACE(4, " throw: {}\n", b
->throwExit
);
773 if (fallthrough
!= NoBlockId
) {
774 FTRACE(4, " fallthrough: {}\n", fallthrough
);
776 FTRACE(2, " block {} end: {}\n", bid
, info
.past
);
780 FTRACE(0, "TraceBytecode (emit): {}::{} in {}\n",
781 func
.cls
? func
.cls
->name
->data() : "",
782 func
.name
, func
.unit
->filename
);
788 void emit_locals_and_params(FuncEmitter
& fe
,
789 const php::Func
& func
,
790 const EmitBcInfo
& info
) {
793 for (auto& loc
: func
.locals
) {
794 if (loc
.id
< func
.params
.size()) {
796 auto& param
= func
.params
[id
];
797 FuncEmitter::ParamInfo pinfo
;
798 pinfo
.defaultValue
= param
.defaultValue
;
799 pinfo
.typeConstraint
= param
.typeConstraint
;
800 pinfo
.userType
= param
.userTypeConstraint
;
801 pinfo
.phpCode
= param
.phpCode
;
802 pinfo
.userAttributes
= param
.userAttributes
;
803 pinfo
.builtinType
= param
.builtinType
;
804 pinfo
.inout
= param
.inout
;
805 pinfo
.variadic
= param
.isVariadic
;
806 fe
.appendParam(func
.locals
[id
].name
, pinfo
);
807 auto const dv
= param
.dvEntryPoint
;
808 if (dv
!= NoBlockId
) {
809 fe
.params
[id
].funcletOff
= info
.blockInfo
[dv
].offset
;
812 } else if (!loc
.killed
) {
814 fe
.allocVarId(loc
.name
);
815 assert(fe
.lookupVarId(loc
.name
) == id
);
816 assert(loc
.id
== id
);
818 fe
.allocUnnamedLocal();
823 assert(fe
.numLocals() == id
);
824 fe
.setNumIterators(func
.numIters
);
828 const php::ExnNode
* node
;
834 template<class BlockInfo
, class ParentIndexMap
>
835 void emit_eh_region(FuncEmitter
& fe
,
836 const EHRegion
* region
,
837 const BlockInfo
& blockInfo
,
838 ParentIndexMap
& parentIndexMap
) {
839 FTRACE(2, " func {}: ExnNode {}\n", fe
.name
, region
->node
->idx
);
841 auto const unreachable
= [&] (const php::ExnNode
& node
) {
842 return blockInfo
[node
.region
.catchEntry
].offset
== kInvalidOffset
;
845 // A region on a single empty block.
846 if (region
->start
== region
->past
) {
847 FTRACE(2, " Skipping\n");
849 } else if (unreachable(*region
->node
)) {
850 FTRACE(2, " Unreachable\n");
854 FTRACE(2, " Process @ {}-{}\n", region
->start
, region
->past
);
856 auto& eh
= fe
.addEHEnt();
857 eh
.m_base
= region
->start
;
858 eh
.m_past
= region
->past
;
859 assert(eh
.m_past
>= eh
.m_base
);
860 assert(eh
.m_base
!= kInvalidOffset
&& eh
.m_past
!= kInvalidOffset
);
862 // An unreachable parent won't be emitted (and thus its offset won't be set),
863 // so find the closest reachable one.
864 auto parent
= region
->parent
;
865 while (parent
&& unreachable(*parent
->node
)) parent
= parent
->parent
;
867 auto parentIt
= parentIndexMap
.find(parent
);
868 assert(parentIt
!= end(parentIndexMap
));
869 eh
.m_parentIndex
= parentIt
->second
;
871 eh
.m_parentIndex
= -1;
873 parentIndexMap
[region
] = fe
.ehtab
.size() - 1;
875 auto const& cr
= region
->node
->region
;
876 eh
.m_handler
= blockInfo
[cr
.catchEntry
].offset
;
877 eh
.m_end
= kInvalidOffset
;
878 eh
.m_iterId
= cr
.iterId
;
880 assert(eh
.m_handler
!= kInvalidOffset
);
883 void exn_path(const php::Func
& func
,
884 std::vector
<const php::ExnNode
*>& ret
,
886 if (id
== NoExnNodeId
) return;
887 auto const& n
= func
.exnNodes
[id
];
888 exn_path(func
, ret
, n
.parent
);
892 // Return the count of shared elements in the front of two forward
894 template<class ForwardRange1
, class ForwardRange2
>
895 size_t shared_prefix(ForwardRange1
& r1
, ForwardRange2
& r2
) {
896 auto r1it
= begin(r1
);
897 auto r2it
= begin(r2
);
898 auto const r1end
= end(r1
);
899 auto const r2end
= end(r2
);
900 auto ret
= size_t{0};
901 while (r1it
!= r1end
&& r2it
!= r2end
&& *r1it
== *r2it
) {
902 ++ret
; ++r1it
; ++r2it
;
908 * Traverse the actual block layout, and find out the intervals for
909 * each exception region in the tree.
911 * The basic idea here is that we haven't constrained block layout
912 * based on the exception tree, but adjacent blocks are still
913 * reasonably likely to have the same ExnNode. Try to coalesce the EH
914 * regions we create for in those cases.
916 void emit_ehent_tree(FuncEmitter
& fe
, const php::Func
& func
,
917 const EmitBcInfo
& info
) {
920 std::vector
<std::unique_ptr
<EHRegion
>>
924 * While walking over the blocks in layout order, we track the set
925 * of "active" exnNodes. This are a list of exnNodes that inherit
926 * from each other. When a new active node is pushed, begin an
927 * EHEnt, and when it's popped, it's done.
929 std::vector
<const php::ExnNode
*> activeList
;
931 auto pop_active
= [&] (Offset past
) {
932 auto p
= activeList
.back();
933 activeList
.pop_back();
934 exnMap
[p
].back()->past
= past
;
937 auto push_active
= [&] (const php::ExnNode
* p
, Offset start
) {
938 auto const parent
= activeList
.empty()
940 : exnMap
[activeList
.back()].back().get();
942 std::make_unique
<EHRegion
>(
943 EHRegion
{ p
, parent
, start
, kInvalidOffset
}
946 activeList
.push_back(p
);
950 * Walk over the blocks, and compare the new block's exnNode path to
951 * the active one. Find the least common ancestor of the two paths,
952 * then modify the active list by popping and then pushing nodes to
953 * set it to the new block's path.
955 for (auto const bid
: info
.blockOrder
) {
956 auto const b
= func
.blocks
[bid
].get();
957 auto const offset
= info
.blockInfo
[bid
].offset
;
959 if (b
->exnNodeId
== NoExnNodeId
) {
960 while (!activeList
.empty()) pop_active(offset
);
964 std::vector
<const php::ExnNode
*> current
;
965 exn_path(func
, current
, b
->exnNodeId
);
967 auto const prefix
= shared_prefix(current
, activeList
);
968 for (size_t i
= prefix
, sz
= activeList
.size(); i
< sz
; ++i
) {
971 for (size_t i
= prefix
, sz
= current
.size(); i
< sz
; ++i
) {
972 push_active(current
[i
], offset
);
975 for (int i
= 0; i
< info
.blockInfo
[bid
].regionsToPop
; i
++) {
976 // If the block ended in a jump out of the catch region, this effectively
977 // ends all catch regions deeper than the one we are jumping to
978 pop_active(info
.blockInfo
[bid
].past
);
981 if (debug
&& !activeList
.empty()) {
983 exn_path(func
, current
, activeList
.back()->idx
);
984 assert(current
== activeList
);
988 while (!activeList
.empty()) {
989 pop_active(info
.blockInfo
[info
.blockOrder
.back()].past
);
993 * We've created all our regions, but we need to sort them instead
994 * of trying to get the UnitEmitter to do it.
996 * The UnitEmitter expects EH regions that look a certain way
997 * (basically the way emitter.cpp likes them). There are some rules
998 * about the order it needs to have at runtime, which we set up
1001 * Essentially, an entry a is less than an entry b iff:
1003 * - a starts before b
1004 * - a starts at the same place, but encloses b entirely
1005 * - a has the same extents as b, but is a parent of b
1007 std::vector
<EHRegion
*> regions
;
1008 for (auto& mapEnt
: exnMap
) {
1009 for (auto& region
: mapEnt
.second
) {
1010 regions
.push_back(region
.get());
1014 begin(regions
), end(regions
),
1015 [&] (const EHRegion
* a
, const EHRegion
* b
) {
1016 if (a
== b
) return false;
1017 if (a
->start
== b
->start
) {
1018 if (a
->past
== b
->past
) {
1019 // When regions exactly overlap, the parent is less than the
1021 for (auto p
= b
->parent
; p
!= nullptr; p
= p
->parent
) {
1022 if (p
== a
) return true;
1024 // If a is not a parent of b, and they have the same region;
1025 // then b better be a parent of a.
1028 for (; p
!= b
&& p
!= nullptr; p
= p
->parent
) continue;
1033 return a
->past
> b
->past
;
1035 return a
->start
< b
->start
;
1039 hphp_fast_map
<const EHRegion
*,uint32_t> parentIndexMap
;
1040 for (auto& r
: regions
) {
1041 emit_eh_region(fe
, r
, info
.blockInfo
, parentIndexMap
);
1043 fe
.setEHTabIsSorted();
1046 void merge_repo_auth_type(UnitEmitter
& ue
, RepoAuthType rat
) {
1047 using T
= RepoAuthType::Tag
;
1049 switch (rat
.tag()) {
1061 case T::OptUncArrKey
:
1063 case T::OptUncStrLike
:
1113 // NOTE: In repo mode, RAT's in Array's might only contain global litstr
1114 // id's. No need to merge. In non-repo mode, RAT's in Array's might contain
1115 // local litstr id's.
1116 if (RuntimeOption::RepoAuthoritative
) return;
1118 if (rat
.hasArrData()) {
1119 auto arr
= rat
.array();
1120 switch (arr
->tag()) {
1121 case RepoAuthType::Array::Tag::Packed
:
1122 for (uint32_t i
= 0; i
< arr
->size(); ++i
) {
1123 merge_repo_auth_type(ue
, arr
->packedElem(i
));
1126 case RepoAuthType::Array::Tag::PackedN
:
1127 merge_repo_auth_type(ue
, arr
->elemType());
1134 case T::OptExactObj
:
1138 case T::OptExactCls
:
1141 ue
.mergeLitstr(rat
.clsName());
1146 void emit_finish_func(EmitUnitState
& state
,
1147 const php::Func
& func
,
1149 const EmitBcInfo
& info
) {
1150 if (info
.containsCalls
) fe
.containsCalls
= true;;
1152 emit_locals_and_params(fe
, func
, info
);
1153 emit_ehent_tree(fe
, func
, info
);
1155 // Nothing should look at the bytecode from now on. Free it up to
1156 // compensate for the UnitEmitter we're creating.
1157 const_cast<php::Func
&>(func
).blocks
.clear();
1159 fe
.userAttributes
= func
.userAttributes
;
1160 fe
.retUserType
= func
.returnUserType
;
1161 fe
.originalFilename
=
1162 func
.originalFilename
? func
.originalFilename
:
1163 func
.originalUnit
? func
.originalUnit
->filename
: nullptr;
1164 fe
.isClosureBody
= func
.isClosureBody
;
1165 fe
.isAsync
= func
.isAsync
;
1166 fe
.isGenerator
= func
.isGenerator
;
1167 fe
.isPairGenerator
= func
.isPairGenerator
;
1168 fe
.isNative
= func
.nativeInfo
!= nullptr;
1169 fe
.isMemoizeWrapper
= func
.isMemoizeWrapper
;
1170 fe
.isMemoizeWrapperLSB
= func
.isMemoizeWrapperLSB
;
1171 fe
.isRxDisabled
= func
.isRxDisabled
;
1173 auto const retTy
= state
.index
.lookup_return_type_raw(&func
);
1174 if (!retTy
.subtypeOf(BBottom
)) {
1175 auto const rat
= make_repo_type(*state
.index
.array_table_builder(), retTy
);
1176 merge_repo_auth_type(fe
.ue(), rat
);
1177 fe
.repoReturnType
= rat
;
1180 if (is_specialized_wait_handle(retTy
)) {
1181 auto const awaitedTy
= wait_handle_inner(retTy
);
1182 if (!awaitedTy
.subtypeOf(BBottom
)) {
1183 auto const rat
= make_repo_type(
1184 *state
.index
.array_table_builder(),
1187 merge_repo_auth_type(fe
.ue(), rat
);
1188 fe
.repoAwaitedReturnType
= rat
;
1192 if (func
.nativeInfo
) {
1193 fe
.hniReturnType
= func
.nativeInfo
->returnType
;
1195 fe
.retTypeConstraint
= func
.retTypeConstraint
;
1197 fe
.maxStackCells
= info
.maxStackDepth
+
1199 fe
.numIterators() * kNumIterCells
;
1201 fe
.finish(fe
.ue().bcPos());
1204 void renumber_locals(const php::Func
& func
) {
1207 for (auto& loc
: const_cast<php::Func
&>(func
).locals
) {
1209 // make sure its out of range, in case someone tries to read it.
1217 void emit_init_func(FuncEmitter
& fe
, const php::Func
& func
) {
1218 renumber_locals(func
);
1220 std::get
<0>(func
.srcInfo
.loc
),
1221 std::get
<1>(func
.srcInfo
.loc
),
1225 func
.srcInfo
.docComment
1229 void emit_func(EmitUnitState
& state
, UnitEmitter
& ue
,
1230 FuncEmitter
* fe
, const php::Func
& func
) {
1231 FTRACE(2, " func {}\n", func
.name
->data());
1232 emit_init_func(*fe
, func
);
1233 auto const info
= emit_bytecode(state
, ue
, func
);
1234 emit_finish_func(state
, func
, *fe
, info
);
1237 void emit_pseudomain(EmitUnitState
& state
,
1239 const php::Unit
& unit
) {
1240 FTRACE(2, " pseudomain\n");
1241 auto& pm
= *unit
.pseudomain
;
1242 renumber_locals(pm
);
1243 ue
.initMain(std::get
<0>(pm
.srcInfo
.loc
),
1244 std::get
<1>(pm
.srcInfo
.loc
));
1245 auto const fe
= ue
.getMain();
1246 auto const info
= emit_bytecode(state
, ue
, pm
);
1247 if (is_systemlib_part(unit
)) {
1248 ue
.m_mergeOnly
= true;
1249 auto const tv
= make_tv
<KindOfInt64
>(1);
1250 ue
.m_mainReturn
= tv
;
1253 ue
.m_returnSeen
&& ue
.m_mainReturn
.m_type
!= KindOfUninit
;
1256 emit_finish_func(state
, pm
, *fe
, info
);
1259 void emit_record(UnitEmitter
& ue
, const php::Record
& rec
) {
1260 auto const re
= ue
.newRecordEmitter(rec
.name
->toCppString());
1262 std::get
<0>(rec
.srcInfo
.loc
),
1263 std::get
<1>(rec
.srcInfo
.loc
),
1265 rec
.parentName
? rec
.parentName
: staticEmptyString(),
1266 rec
.srcInfo
.docComment
1268 re
->setUserAttributes(rec
.userAttributes
);
1269 for (auto&& f
: rec
.fields
) {
1283 void emit_class(EmitUnitState
& state
,
1285 PreClassEmitter
* pce
,
1287 const php::Class
& cls
) {
1288 FTRACE(2, " class: {}\n", cls
.name
->data());
1290 std::get
<0>(cls
.srcInfo
.loc
),
1291 std::get
<1>(cls
.srcInfo
.loc
),
1292 offset
== kInvalidOffset
? ue
.bcPos() : offset
,
1294 cls
.parentName
? cls
.parentName
: staticEmptyString(),
1295 cls
.srcInfo
.docComment
1297 pce
->setUserAttributes(cls
.userAttributes
);
1299 for (auto& x
: cls
.interfaceNames
) pce
->addInterface(x
);
1300 for (auto& x
: cls
.usedTraitNames
) pce
->addUsedTrait(x
);
1301 for (auto& x
: cls
.requirements
) pce
->addClassRequirement(x
);
1302 for (auto& x
: cls
.traitPrecRules
) pce
->addTraitPrecRule(x
);
1303 for (auto& x
: cls
.traitAliasRules
) pce
->addTraitAliasRule(x
);
1305 pce
->setIfaceVtableSlot(state
.index
.lookup_iface_vtable_slot(&cls
));
1307 bool needs86cinit
= false;
1309 auto const nativeConsts
= cls
.attrs
& AttrBuiltin
?
1310 Native::getClassConstants(cls
.name
) : nullptr;
1312 for (auto& cconst
: cls
.constants
) {
1313 if (nativeConsts
&& nativeConsts
->count(cconst
.name
)) {
1316 if (!cconst
.val
.hasValue()) {
1317 pce
->addAbstractConstant(
1319 cconst
.typeConstraint
,
1323 needs86cinit
|= cconst
.val
->m_type
== KindOfUninit
;
1327 cconst
.typeConstraint
,
1328 &cconst
.val
.value(),
1335 for (auto& m
: cls
.methods
) {
1336 if (!needs86cinit
&& m
->name
== s_86cinit
.get()) continue;
1337 FTRACE(2, " method: {}\n", m
->name
->data());
1338 auto const fe
= ue
.newMethodEmitter(m
->name
, pce
);
1339 emit_init_func(*fe
, *m
);
1341 auto const info
= emit_bytecode(state
, ue
, *m
);
1342 emit_finish_func(state
, *m
, *fe
, info
);
1345 CompactVector
<Type
> useVars
;
1346 if (is_closure(cls
)) {
1347 auto f
= find_method(&cls
, s_invoke
.get());
1348 useVars
= state
.index
.lookup_closure_use_vars(f
, true);
1350 auto uvIt
= useVars
.begin();
1352 auto const privateProps
= state
.index
.lookup_private_props(&cls
, true);
1353 auto const privateStatics
= state
.index
.lookup_private_statics(&cls
, true);
1354 for (auto& prop
: cls
.properties
) {
1355 auto const makeRat
= [&] (const Type
& ty
) -> RepoAuthType
{
1356 if (!ty
.subtypeOf(BCell
)) return RepoAuthType
{};
1357 if (ty
.subtypeOf(BBottom
)) {
1358 // A property can be TBottom if no sets (nor its initial value) is
1359 // compatible with its type-constraint, or if its LateInit and there's
1360 // no sets to it. The repo auth type here doesn't particularly matter,
1361 // since such a prop will be inaccessible.
1362 return RepoAuthType
{};
1365 auto const rat
= make_repo_type(*state
.index
.array_table_builder(), ty
);
1366 merge_repo_auth_type(ue
, rat
);
1370 auto const privPropTy
= [&] (const PropState
& ps
) -> Type
{
1371 if (is_closure(cls
)) {
1372 // For closures use variables will be the first properties of the
1373 // closure object, in declaration order
1374 if (uvIt
!= useVars
.end()) return *uvIt
++;
1378 auto it
= ps
.find(prop
.name
);
1379 if (it
== end(ps
)) return Type
{};
1380 return it
->second
.ty
;
1384 auto const attrs
= prop
.attrs
;
1385 if (attrs
& AttrPrivate
) {
1386 propTy
= privPropTy((attrs
& AttrStatic
) ? privateStatics
: privateProps
);
1387 } else if ((attrs
& AttrPublic
) && (attrs
& AttrStatic
)) {
1388 propTy
= state
.index
.lookup_public_static(Context
{}, &cls
, prop
.name
);
1395 prop
.typeConstraint
,
1402 assert(uvIt
== useVars
.end());
1404 pce
->setEnumBaseTy(cls
.enumBaseTy
);
1407 void emit_typealias(UnitEmitter
& ue
, const php::TypeAlias
& alias
,
1408 const EmitUnitState
& state
) {
1409 auto const id
= ue
.addTypeAlias(alias
);
1410 if (state
.processedTypeAlias
.find(id
) == state
.processedTypeAlias
.end()) {
1411 ue
.pushMergeableTypeAlias(id
);
1415 //////////////////////////////////////////////////////////////////////
1419 std::unique_ptr
<UnitEmitter
> emit_unit(const Index
& index
,
1420 const php::Unit
& unit
) {
1422 Trace::hhbbc_emit
, kSystemLibBump
, is_systemlib_part(unit
)
1425 assert(check(unit
));
1427 auto ue
= std::make_unique
<UnitEmitter
>(unit
.sha1
,
1429 Native::s_noNativeFuncs
,
1431 FTRACE(1, " unit {}\n", unit
.filename
->data());
1432 ue
->m_filepath
= unit
.filename
;
1433 ue
->m_isHHFile
= unit
.isHHFile
;
1434 ue
->m_metaData
= unit
.metaData
;
1435 ue
->m_fileAttributes
= unit
.fileAttributes
;
1437 EmitUnitState state
{ index
, &unit
};
1438 state
.classOffsets
.resize(unit
.classes
.size(), kInvalidOffset
);
1440 emit_pseudomain(state
, *ue
, unit
);
1442 std::vector
<std::unique_ptr
<FuncEmitter
> > top_fes
;
1444 * Top level funcs are always defined when the unit is loaded, and
1445 * don't have a DefFunc bytecode. Process them up front.
1447 for (size_t id
= 0; id
< unit
.funcs
.size(); ++id
) {
1448 auto const f
= unit
.funcs
[id
].get();
1449 assertx(f
!= unit
.pseudomain
.get());
1450 if (!f
->top
) continue;
1451 top_fes
.push_back(std::make_unique
<FuncEmitter
>(*ue
, -1, -1, f
->name
));
1452 emit_func(state
, *ue
, top_fes
.back().get(), *f
);
1456 * Find any top-level classes that need to be included due to
1457 * hoistability, even though the corresponding DefCls was not
1460 for (size_t id
= 0; id
< unit
.classes
.size(); ++id
) {
1461 if (state
.classOffsets
[id
] != kInvalidOffset
) continue;
1462 auto const c
= unit
.classes
[id
].get();
1463 if (c
->hoistability
!= PreClass::MaybeHoistable
&&
1464 c
->hoistability
!= PreClass::AlwaysHoistable
) {
1467 // Closures are AlwaysHoistable; but there's no need to include
1468 // them unless there's a reachable CreateCl.
1469 if (is_closure(*c
)) continue;
1470 recordClass(state
, *ue
, id
);
1473 size_t pceId
= 0, feId
= 0;
1475 // Note that state.pceInfo can grow inside the loop
1476 while (pceId
< state
.pceInfo
.size()) {
1477 auto const& pceInfo
= state
.pceInfo
[pceId
++];
1478 auto const& c
= unit
.classes
[pceInfo
.origId
];
1479 emit_class(state
, *ue
, pceInfo
.pce
,
1480 state
.classOffsets
[pceInfo
.origId
], *c
);
1483 while (feId
< state
.feInfo
.size()) {
1484 auto const& feInfo
= state
.feInfo
[feId
++];
1485 // DefFunc ids are off by one wrt unit.funcs because we don't
1486 // store the pseudomain there.
1487 auto const& f
= unit
.funcs
[feInfo
.origId
- 1];
1488 emit_func(state
, *ue
, feInfo
.fe
, *f
);
1490 } while (pceId
< state
.pceInfo
.size());
1492 for (auto tid
: state
.typeAliasInfo
) {
1493 emit_typealias(*ue
, *unit
.typeAliases
[tid
], state
);
1496 for (size_t id
= 0; id
< unit
.records
.size(); ++id
) {
1497 emit_record(*ue
, *unit
.records
[id
]);
1500 // Top level funcs need to go after any non-top level funcs. See
1501 // Unit::merge for details.
1502 for (auto& fe
: top_fes
) ue
->appendTopEmitter(std::move(fe
));
1507 //////////////////////////////////////////////////////////////////////