take &self
[hiphop-php.git] / hphp / hhbbc / emit.cpp
blob4a30d7ae0e87d16b8dee5c3bc64b8aeb98fddc33
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 #include "hphp/hhbbc/emit.h"
18 #include <vector>
19 #include <algorithm>
20 #include <iterator>
21 #include <map>
22 #include <memory>
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);
55 namespace {
57 //////////////////////////////////////////////////////////////////////
59 const StaticString s_invoke("__invoke");
61 //////////////////////////////////////////////////////////////////////
63 struct PceInfo {
64 PreClassEmitter* pce;
65 Id origId;
68 struct FeInfo {
69 FuncEmitter* fe;
70 Id origId;
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.
80 const Index& index;
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
89 * we emit.
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.
103 template<typename T>
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;
114 template<typename T>
115 using OpInfo = typename OpInfoHelper<T>::type;
118 * Helper to conditionally call fun on data provided data is of the
119 * given type.
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>
127 caller(F&&, Bc&&) {}
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
151 * entry block first.
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);
167 withDVs.erase(
168 std::find(begin(withDVs), end(withDVs), sorted.front()),
169 end(withDVs)
171 return withDVs;
172 }();
173 sorted.insert(end(sorted), begin(dvBlocks), end(dvBlocks));
175 FTRACE(2, " block order:{}\n",
176 [&] {
177 std::string ret;
178 for (auto const bid : sorted) {
179 ret += " ";
180 ret += folly::to<std::string>(bid);
182 return ret;
185 return sorted;
188 // While emitting bytecode, we learn about some metadata that will
189 // need to be registered in the FuncEmitter.
190 struct EmitBcInfo {
191 struct JmpFixup { Offset instrOff; Offset jmpImmedOff; };
193 struct BlockInfo {
194 BlockInfo()
195 : offset(kInvalidOffset)
196 , past(kInvalidOffset)
197 , regionsToPop(0)
200 // The offset of the block, if we've already emitted it.
201 // Otherwise kInvalidOffset.
202 Offset offset;
204 // The offset past the end of this block.
205 Offset past;
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
209 // child
210 int regionsToPop;
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
214 // to that block.
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;
226 bool containsCalls;
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;
237 while (eh1 != eh2) {
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;
246 return true;
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;
263 return eh1;
266 const StaticString
267 s_hhbbc_fail_verification("__hhvm_intrinsics\\hhbbc_fail_verification");
269 EmitBcInfo emit_bytecode(EmitUnitState& euState,
270 UnitEmitter& ue,
271 const php::Func& func) {
272 EmitBcInfo ret = {};
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;
288 Type tos{};
290 SCOPE_ASSERT_DETAIL("emit") {
291 std::string ret;
292 for (auto bid : func.blockRange()) {
293 folly::format(&ret,
294 "block #{}\n{}",
295 bid,
296 show(func, *func.blocks[bid])
300 return ret;
303 auto const pseudomain = is_pseudomain(&func);
304 auto process_mergeable = [&] (const Bytecode& bc) {
305 if (!pseudomain) return;
306 switch (bc.op) {
307 case Op::DefCls:
308 case Op::DefClsNop:
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;
316 return;
317 case Op::AssertRATL:
318 case Op::AssertRATStk:
319 case Op::Nop:
320 return;
322 case Op::DefCns: {
323 if (ue.m_returnSeen || tos.subtypeOf(BBottom)) break;
324 auto top = tv(tos);
325 assertx(top);
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);
333 return;
335 case Op::DefTypeAlias: {
336 auto tid = bc.DefTypeAlias.arg1;
337 ue.pushMergeableTypeAlias(tid);
338 euState.processedTypeAlias.insert(tid);
339 return;
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;
352 case Op::PopC:
353 tos = TBottom;
354 return;
355 case Op::RetC: {
356 if (ue.m_returnSeen || tos.subtypeOf(BBottom)) break;
357 auto top = tv(tos);
358 assertx(top);
359 ue.m_returnSeen = true;
360 ue.m_mainReturn = *top;
361 tos = TBottom;
362 return;
364 default:
365 break;
367 ue.m_returnSeen = true;
368 ue.m_mainReturn = make_tv<KindOfUninit>();
369 tos = TBottom;
372 auto map_local = [&] (LocalId id) {
373 auto const loc = func.locals[id];
374 assert(!loc.killed);
375 assert(loc.id <= id);
376 return loc.id;
379 auto set_expected_depth = [&] (BlockId block) {
380 auto& info = blockInfo[block];
382 if (info.expectedStackDepth) {
383 assert(*info.expectedStackDepth == currentStackDepth);
384 } else {
385 info.expectedStackDepth = currentStackDepth;
389 auto make_member_key = [&] (MKey mkey) {
390 switch (mkey.mcode) {
391 case MEC: case MPC:
392 return MemberKey{mkey.mcode, mkey.idx};
393 case MEL: case MPL:
394 return MemberKey{
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};
399 case MEI:
400 return MemberKey{mkey.mcode, mkey.int64};
401 case MW:
402 return MemberKey{};
404 not_reached();
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();
419 ue.emitIVA(n);
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);
429 } else {
430 info.forwardJumps.push_back({ startOffset, ue.bcPos() });
431 ue.emitInt32(0);
435 auto emit_switch = [&] (const SwitchTab& targets) {
436 ue.emitIVA(targets.size());
437 for (auto t : targets) {
438 set_expected_depth(t);
439 emit_branch(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);
450 ue.emitInt32(-1);
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) {
458 ue.emitIVA(kv.kind);
459 ue.emitIVA(kv.id);
460 if (kv.kind == KindOfLIter) {
461 always_assert(kv.local != NoLocalId);
462 ue.emitIVA(map_local(kv.local));
463 } else {
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;
483 ret.maxStackDepth =
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) {
494 id = elm.pce->id();
495 return;
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, \
538 [&] { \
539 set_expected_depth(data.fca.asyncEagerTarget); \
540 emit_branch(data.fca.asyncEagerTarget); \
541 }); \
542 if (!data.fca.hasUnpack()) ret.containsCalls = true;\
544 #define IMM_NA
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);
552 #define POP_NOV
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);
567 #define PUSH_NOV
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); \
582 ue.emitInt32( \
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); \
594 POP_##inputs \
596 size_t numTargets = 0; \
597 std::array<BlockId, kMaxHhbcImms> targets; \
599 if (Op::opcode == Op::MemoGet) { \
600 IMM_##imms \
601 assertx(numTargets == 1); \
602 set_expected_depth(targets[0]); \
603 PUSH_##outputs \
604 } else if (Op::opcode == Op::MemoGetEager) { \
605 IMM_##imms \
606 assertx(numTargets == 2); \
607 set_expected_depth(targets[0]); \
608 PUSH_##outputs \
609 set_expected_depth(targets[1]); \
610 } else { \
611 PUSH_##outputs \
612 IMM_##imms \
613 for (size_t i = 0; i < numTargets; ++i) { \
614 set_expected_depth(targets[i]); \
618 if (flags & TF) currentStackDepth = 0; \
619 emit_srcloc(); \
622 OPCODES
624 #undef O
626 #undef IMM_MA
627 #undef IMM_BLA
628 #undef IMM_SLA
629 #undef IMM_ILA
630 #undef IMM_IVA
631 #undef IMM_I64A
632 #undef IMM_LA
633 #undef IMM_IA
634 #undef IMM_DA
635 #undef IMM_SA
636 #undef IMM_RATA
637 #undef IMM_AA
638 #undef IMM_BA
639 #undef IMM_OA_IMPL
640 #undef IMM_OA
641 #undef IMM_VSA
642 #undef IMM_KA
643 #undef IMM_LAR
644 #undef IMM_FCA
646 #undef IMM_NA
647 #undef IMM_ONE
648 #undef IMM_TWO
649 #undef IMM_THREE
650 #undef IMM_FOUR
651 #undef IMM_FIVE
652 #undef IMM_SIX
654 #undef POP_NOV
655 #undef POP_ONE
656 #undef POP_TWO
657 #undef POP_THREE
659 #undef POP_CMANY
660 #undef POP_SMANY
661 #undef POP_CUMANY
662 #undef POP_CMANY_U3
663 #undef POP_CALLNATIVE
664 #undef POP_FCALL
665 #undef POP_MFINAL
666 #undef POP_C_MFINAL
668 #undef PUSH_NOV
669 #undef PUSH_ONE
670 #undef PUSH_TWO
671 #undef PUSH_THREE
672 #undef PUSH_CMANY
673 #undef PUSH_FCALL
674 #undef PUSH_CALLNATIVE
676 #define O(opcode, ...) \
677 case Op::opcode: \
678 if (Op::opcode != Op::Nop) emit_##opcode(inst.opcode); \
679 break;
680 switch (inst.op) { OPCODES }
681 #undef O
684 ret.blockOrder = order_blocks(func);
685 auto blockIt = begin(ret.blockOrder);
686 auto const endBlockIt = end(ret.blockOrder);
687 for (; blockIt != endBlockIt; ++blockIt) {
688 auto bid = *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();
708 auto flip = false;
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 {}));
718 } else {
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();
724 auto const target =
725 bc.op == Op::JmpNZ ? bc.JmpNZ.target1 : bc.JmpZ.target1;
726 if (std::next(blockIt) != endBlockIt && blockIt[1] == target) {
727 fallthrough = target;
728 --end;
729 flip = true;
733 for (auto iit = b->hhbcs.begin(); iit != end; ++iit) emit_inst(*iit);
734 if (flip) {
735 if (end->op == Op::JmpNZ) {
736 emit_inst(bc_with_loc(end->srcLoc, bc::JmpZ { b->fallthrough }));
737 } else {
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 });
751 } else {
752 emit_inst(bc::Jmp { fallthrough });
755 auto const parent = commonParent(func,
756 func.blocks[fallthrough]->exnNodeId,
757 b->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);
779 if (traceBc) {
780 FTRACE(0, "TraceBytecode (emit): {}::{} in {}\n",
781 func.cls ? func.cls->name->data() : "",
782 func.name, func.unit->filename);
785 return ret;
788 void emit_locals_and_params(FuncEmitter& fe,
789 const php::Func& func,
790 const EmitBcInfo& info) {
791 Id id = 0;
793 for (auto& loc : func.locals) {
794 if (loc.id < func.params.size()) {
795 assert(!loc.killed);
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;
811 ++id;
812 } else if (!loc.killed) {
813 if (loc.name) {
814 fe.allocVarId(loc.name);
815 assert(fe.lookupVarId(loc.name) == id);
816 assert(loc.id == id);
817 } else {
818 fe.allocUnnamedLocal();
820 ++id;
823 assert(fe.numLocals() == id);
824 fe.setNumIterators(func.numIters);
827 struct EHRegion {
828 const php::ExnNode* node;
829 EHRegion* parent;
830 Offset start;
831 Offset past;
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");
848 return;
849 } else if (unreachable(*region->node)) {
850 FTRACE(2, " Unreachable\n");
851 return;
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;
866 if (parent) {
867 auto parentIt = parentIndexMap.find(parent);
868 assert(parentIt != end(parentIndexMap));
869 eh.m_parentIndex = parentIt->second;
870 } else {
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,
885 ExnNodeId id) {
886 if (id == NoExnNodeId) return;
887 auto const& n = func.exnNodes[id];
888 exn_path(func, ret, n.parent);
889 ret.push_back(&n);
892 // Return the count of shared elements in the front of two forward
893 // ranges.
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;
904 return ret;
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) {
918 hphp_fast_map<
919 const php::ExnNode*,
920 std::vector<std::unique_ptr<EHRegion>>
921 > exnMap;
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()
939 ? nullptr
940 : exnMap[activeList.back()].back().get();
941 exnMap[p].push_back(
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);
961 continue;
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) {
969 pop_active(offset);
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()) {
982 current.clear();
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
999 * here.
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());
1013 std::sort(
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
1020 // child.
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.
1026 if (debug) {
1027 auto p = a->parent;
1028 for (; p != b && p != nullptr; p = p->parent) continue;
1029 assert(p == b);
1031 return false;
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()) {
1050 case T::OptBool:
1051 case T::OptInt:
1052 case T::OptSStr:
1053 case T::OptStr:
1054 case T::OptDbl:
1055 case T::OptRes:
1056 case T::OptObj:
1057 case T::OptFunc:
1058 case T::OptCls:
1059 case T::OptClsMeth:
1060 case T::OptRecord:
1061 case T::OptUncArrKey:
1062 case T::OptArrKey:
1063 case T::OptUncStrLike:
1064 case T::OptStrLike:
1065 case T::Null:
1066 case T::Cell:
1067 case T::InitUnc:
1068 case T::Unc:
1069 case T::UncArrKey:
1070 case T::ArrKey:
1071 case T::UncStrLike:
1072 case T::StrLike:
1073 case T::InitCell:
1074 case T::Uninit:
1075 case T::InitNull:
1076 case T::Bool:
1077 case T::Int:
1078 case T::Dbl:
1079 case T::Res:
1080 case T::SStr:
1081 case T::Str:
1082 case T::Obj:
1083 case T::Func:
1084 case T::Cls:
1085 case T::ClsMeth:
1086 case T::Record:
1087 return;
1089 case T::OptSArr:
1090 case T::OptArr:
1091 case T::SArr:
1092 case T::Arr:
1093 case T::OptSVArr:
1094 case T::OptVArr:
1095 case T::SVArr:
1096 case T::VArr:
1097 case T::OptSDArr:
1098 case T::OptDArr:
1099 case T::SDArr:
1100 case T::DArr:
1101 case T::OptSVec:
1102 case T::OptVec:
1103 case T::SVec:
1104 case T::Vec:
1105 case T::OptSDict:
1106 case T::OptDict:
1107 case T::SDict:
1108 case T::Dict:
1109 case T::OptSKeyset:
1110 case T::OptKeyset:
1111 case T::SKeyset:
1112 case T::Keyset:
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));
1125 break;
1126 case RepoAuthType::Array::Tag::PackedN:
1127 merge_repo_auth_type(ue, arr->elemType());
1128 break;
1131 return;
1133 case T::OptSubObj:
1134 case T::OptExactObj:
1135 case T::SubObj:
1136 case T::ExactObj:
1137 case T::OptSubCls:
1138 case T::OptExactCls:
1139 case T::SubCls:
1140 case T::ExactCls:
1141 ue.mergeLitstr(rat.clsName());
1142 return;
1146 void emit_finish_func(EmitUnitState& state,
1147 const php::Func& func,
1148 FuncEmitter& fe,
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(),
1185 awaitedTy
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 +
1198 fe.numLocals() +
1199 fe.numIterators() * kNumIterCells;
1201 fe.finish(fe.ue().bcPos());
1204 void renumber_locals(const php::Func& func) {
1205 Id id = 0;
1207 for (auto& loc : const_cast<php::Func&>(func).locals) {
1208 if (loc.killed) {
1209 // make sure its out of range, in case someone tries to read it.
1210 loc.id = INT_MAX;
1211 } else {
1212 loc.id = id++;
1217 void emit_init_func(FuncEmitter& fe, const php::Func& func) {
1218 renumber_locals(func);
1219 fe.init(
1220 std::get<0>(func.srcInfo.loc),
1221 std::get<1>(func.srcInfo.loc),
1222 fe.ue().bcPos(),
1223 func.attrs,
1224 func.top,
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,
1238 UnitEmitter& ue,
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;
1251 } else {
1252 ue.m_mergeOnly =
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());
1261 re->init(
1262 std::get<0>(rec.srcInfo.loc),
1263 std::get<1>(rec.srcInfo.loc),
1264 rec.attrs,
1265 rec.parentName ? rec.parentName : staticEmptyString(),
1266 rec.srcInfo.docComment
1268 re->setUserAttributes(rec.userAttributes);
1269 for (auto&& f : rec.fields) {
1270 re->addField(
1271 f.name,
1272 f.attrs,
1273 f.userType,
1274 f.typeConstraint,
1275 f.docComment,
1276 &f.val,
1277 RepoAuthType{},
1278 f.userAttributes
1283 void emit_class(EmitUnitState& state,
1284 UnitEmitter& ue,
1285 PreClassEmitter* pce,
1286 Offset offset,
1287 const php::Class& cls) {
1288 FTRACE(2, " class: {}\n", cls.name->data());
1289 pce->init(
1290 std::get<0>(cls.srcInfo.loc),
1291 std::get<1>(cls.srcInfo.loc),
1292 offset == kInvalidOffset ? ue.bcPos() : offset,
1293 cls.attrs,
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)) {
1314 break;
1316 if (!cconst.val.hasValue()) {
1317 pce->addAbstractConstant(
1318 cconst.name,
1319 cconst.typeConstraint,
1320 cconst.isTypeconst
1322 } else {
1323 needs86cinit |= cconst.val->m_type == KindOfUninit;
1325 pce->addConstant(
1326 cconst.name,
1327 cconst.typeConstraint,
1328 &cconst.val.value(),
1329 cconst.phpCode,
1330 cconst.isTypeconst
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);
1340 pce->addMethod(fe);
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);
1367 return 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++;
1375 return Type{};
1378 auto it = ps.find(prop.name);
1379 if (it == end(ps)) return Type{};
1380 return it->second.ty;
1383 Type propTy;
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);
1391 pce->addProperty(
1392 prop.name,
1393 prop.attrs,
1394 prop.userType,
1395 prop.typeConstraint,
1396 prop.docComment,
1397 &prop.val,
1398 makeRat(propTy),
1399 prop.userAttributes
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) {
1421 Trace::Bump bumper{
1422 Trace::hhbbc_emit, kSystemLibBump, is_systemlib_part(unit)
1425 assert(check(unit));
1427 auto ue = std::make_unique<UnitEmitter>(unit.sha1,
1428 SHA1{},
1429 Native::s_noNativeFuncs,
1430 true);
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
1458 * reachable.
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) {
1465 continue;
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;
1474 do {
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));
1504 return ue;
1507 //////////////////////////////////////////////////////////////////////