fix typo serializing additionalTextEdits
[hiphop-php.git] / hphp / hhbbc / emit.cpp
blob3a780971d332fc1d2dc109c7c20dfd469969de07
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/representation.h"
36 #include "hphp/hhbbc/unit-util.h"
38 #include "hphp/runtime/base/repo-auth-type-array.h"
39 #include "hphp/runtime/base/repo-auth-type-codec.h"
40 #include "hphp/runtime/base/repo-auth-type.h"
41 #include "hphp/runtime/base/tv-comparisons.h"
43 #include "hphp/runtime/vm/bytecode.h"
44 #include "hphp/runtime/vm/func-emitter.h"
45 #include "hphp/runtime/vm/native.h"
46 #include "hphp/runtime/vm/preclass-emitter.h"
47 #include "hphp/runtime/vm/record-emitter.h"
48 #include "hphp/runtime/vm/unit-emitter.h"
50 namespace HPHP { namespace HHBBC {
52 TRACE_SET_MOD(hhbbc_emit);
54 namespace {
56 //////////////////////////////////////////////////////////////////////
58 const StaticString s_empty("");
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;
98 * Some bytecodes need to be mutated before being emitted. Pass those
99 * bytecodes by value to their respective emit_op functions.
101 template<typename T>
102 struct OpInfoHelper {
103 static constexpr bool by_value =
104 T::op == Op::DefCls ||
105 T::op == Op::DefClsNop ||
106 T::op == Op::CreateCl ||
107 T::op == Op::DefTypeAlias;
109 using type = typename std::conditional<by_value, T, const T&>::type;
112 template<typename T>
113 using OpInfo = typename OpInfoHelper<T>::type;
116 * Helper to conditionally call fun on data provided data is of the
117 * given type.
119 template<Op op, typename F, typename Bc>
120 std::enable_if_t<std::remove_reference_t<Bc>::op == op>
121 caller(F&& fun, Bc&& data) { fun(std::forward<Bc>(data)); }
123 template<Op op, typename F, typename Bc>
124 std::enable_if_t<std::remove_reference_t<Bc>::op != op>
125 caller(F&&, Bc&&) {}
127 Id recordClass(EmitUnitState& euState, UnitEmitter& ue, Id id) {
128 auto cls = euState.unit->classes[id].get();
129 euState.pceInfo.push_back(
130 { ue.newPreClassEmitter(cls->name->toCppString(), cls->hoistability), id }
132 return euState.pceInfo.back().pce->id();
135 //////////////////////////////////////////////////////////////////////
137 php::SrcLoc srcLoc(const php::Func& func, int32_t ix) {
138 if (ix < 0) return php::SrcLoc{};
139 auto const unit = func.originalUnit ? func.originalUnit : func.unit;
140 return unit->srcLocs[ix];
144 * We need to ensure that all blocks in an fpi region are
145 * contiguous. This will normally be guaranteed by rpo order (since
146 * the blocks are dominated by the FPush and post-dominated by the
147 * FCall). If there are exceptional edges from the fpi region,
148 * however, but the FCall is still reachable its possible that the
149 * block(s) ending in the exceptional edge get sorted after the block
150 * containing the FCall.
152 * To solve this, we need to find all exceptional exits from fpi
153 * regions, and insert "fake" edges to the block containing the
154 * corresponding call.
156 std::vector<BlockId> initial_sort(const php::Func& f) {
157 auto sorted = rpoSortFromMain(f);
159 FTRACE(4, "Initial sort {}\n", f.name);
161 // We give each fpi region a unique id
162 auto nextFpi = 0;
163 // Map from fpi region id to the block containing its FCall.
164 // Needs value stability because extraEdges holds pointers into this map.
165 hphp_hash_map<int, BlockId> fpiToCallBlkMap;
166 // Map from the ids of terminal blocks within fpi regions to the
167 // entries in fpiToCallBlkMap corresponding to the active fpi regions.
168 hphp_fast_map<BlockId, std::vector<BlockId*>> extraEdges;
169 // The fpi state at the start of each block
170 std::vector<folly::Optional<std::vector<int>>> blkFpiState(f.blocks.size());
171 for (auto const bid : sorted) {
172 auto& fpiState = blkFpiState[bid];
173 if (!fpiState) fpiState.emplace();
174 auto curState = *fpiState;
175 for (auto const& bc : f.blocks[bid]->hhbcs) {
176 if (isLegacyFPush(bc.op)) {
177 FTRACE(4, "blk:{} legacy FPush {} (nesting {})\n",
178 bid, nextFpi, curState.size());
179 curState.push_back(nextFpi++);
180 } else if (isLegacyFCall(bc.op)) {
181 FTRACE(4, "blk:{} legacy FCall {} (nesting {})\n",
182 bid, curState.back(), curState.size() - 1);
183 fpiToCallBlkMap[curState.back()] = bid;
184 curState.pop_back();
187 auto hasNormalSucc = false;
188 forEachNormalSuccessor(
189 *f.blocks[bid],
190 [&] (BlockId id) {
191 hasNormalSucc = true;
192 auto &succState = blkFpiState[id];
193 FTRACE(4, "blk:{} propagate state to {}\n",
194 bid, id);
195 if (!succState) {
196 succState = curState;
197 } else {
198 assertx(succState == curState);
202 if (!hasNormalSucc) {
203 for (auto fpi : curState) {
204 // We may or may not have seen the FCall yet; if we've not
205 // seen it, either there is none (in which case there's
206 // nothing to do), or the blocks happen to be in the right
207 // order anyway (so again there's apparently nothing to
208 // do). But in the latter case its possible that adding edges
209 // for another fpi region (or another exceptional exit in this
210 // fpi region) might perturb the order; so we record a pointer
211 // to the unordered_map element, in case it gets filled in
212 // later.
213 auto const res = fpiToCallBlkMap.emplace(fpi, NoBlockId);
214 extraEdges[bid].push_back(&res.first->second);
219 if (!extraEdges.empty()) {
220 hphp_fast_map<BlockId, std::vector<BlockId>> extraIds;
221 for (auto& elm : extraEdges) {
222 for (auto const bidPtr : elm.second) {
223 if (*bidPtr == NoBlockId) {
224 // There was no FCall, so no need to do anything
225 continue;
227 FTRACE(4, "blk:{} add extra edge to {}\n",
228 elm.first, *bidPtr);
229 extraIds[elm.first].push_back(*bidPtr);
232 if (extraIds.size()) {
233 // redo the sort with the extra edges in place
234 sorted = rpoSortFromMain(f, &extraIds);
238 return sorted;
242 * Order the blocks for bytecode emission.
244 * Rules about block order:
246 * - All bytecodes corresponding to a given FPI region must be
247 * contiguous. Note that an FPI region can start or end part way
248 * through a block, so this constraint is on bytecodes, not blocks
250 * - Each DV funclet must have all of its blocks contiguous, with the
251 * entry block first.
253 * - Main entry point must be the first block.
255 * It is not a requirement, but we attempt to locate all the DV entry
256 * points after the rest of the primary function body. The normal
257 * case for DV initializers is that each one falls through to the
258 * next, with the block jumping back to the main entry point.
260 std::vector<BlockId> order_blocks(const php::Func& f) {
261 auto sorted = initial_sort(f);
263 // Get the DV blocks, without the rest of the primary function body,
264 // and then add them to the end of sorted.
265 auto const dvBlocks = [&] {
266 auto withDVs = rpoSortAddDVs(f);
267 withDVs.erase(
268 std::find(begin(withDVs), end(withDVs), sorted.front()),
269 end(withDVs)
271 return withDVs;
272 }();
273 sorted.insert(end(sorted), begin(dvBlocks), end(dvBlocks));
275 FTRACE(2, " block order:{}\n",
276 [&] {
277 std::string ret;
278 for (auto const bid : sorted) {
279 ret += " ";
280 ret += folly::to<std::string>(bid);
282 return ret;
285 return sorted;
288 // While emitting bytecode, we learn about some metadata that will
289 // need to be registered in the FuncEmitter.
290 struct EmitBcInfo {
291 struct FPI {
292 Offset fpushOff;
293 Offset fpiEndOff;
294 int32_t fpDelta;
297 struct JmpFixup { Offset instrOff; Offset jmpImmedOff; };
299 struct BlockInfo {
300 BlockInfo()
301 : offset(kInvalidOffset)
302 , past(kInvalidOffset)
303 , regionsToPop(0)
306 // The offset of the block, if we've already emitted it.
307 // Otherwise kInvalidOffset.
308 Offset offset;
310 // The offset past the end of this block.
311 Offset past;
313 // How many catch regions the jump at the end of this block is leaving.
314 // 0 if there is no jump or if the jump is to the same catch region or a
315 // child
316 int regionsToPop;
318 // When we emit a forward jump to a block we haven't seen yet, we
319 // write down where the jump was so we can fix it up when we get
320 // to that block.
321 std::vector<JmpFixup> forwardJumps;
323 // When we see a forward jump to a block, we record the stack
324 // depth at the jump site here. This is needed to track
325 // currentStackDepth correctly (and we also assert all the jumps
326 // have the same depth).
327 folly::Optional<uint32_t> expectedStackDepth;
329 // Similar to expectedStackDepth, for the fpi stack. Needed to deal with
330 // terminal instructions that end an fpi region.
331 folly::Optional<uint32_t> expectedFPIDepth;
334 std::vector<BlockId> blockOrder;
335 uint32_t maxStackDepth;
336 bool containsCalls;
337 std::vector<FPI> fpiRegions;
338 std::vector<BlockInfo> blockInfo;
341 using ExnNodePtr = php::ExnNode*;
343 bool handleEquivalent(const php::Func& func, ExnNodeId eh1, ExnNodeId eh2) {
344 auto entry = [&] (ExnNodeId eid) {
345 return func.exnNodes[eid].region.catchEntry;
348 while (eh1 != eh2) {
349 assertx(eh1 != NoExnNodeId &&
350 eh2 != NoExnNodeId &&
351 func.exnNodes[eh1].depth == func.exnNodes[eh2].depth);
352 if (entry(eh1) != entry(eh2)) return false;
353 eh1 = func.exnNodes[eh1].parent;
354 eh2 = func.exnNodes[eh2].parent;
357 return true;
360 // The common parent P of eh1 and eh2 is the deepest region such that
361 // eh1 and eh2 are both handle-equivalent to P or a child of P
362 ExnNodeId commonParent(const php::Func& func, ExnNodeId eh1, ExnNodeId eh2) {
363 if (eh1 == NoExnNodeId || eh2 == NoExnNodeId) return NoExnNodeId;
364 while (func.exnNodes[eh1].depth > func.exnNodes[eh2].depth) {
365 eh1 = func.exnNodes[eh1].parent;
367 while (func.exnNodes[eh2].depth > func.exnNodes[eh1].depth) {
368 eh2 = func.exnNodes[eh2].parent;
370 while (!handleEquivalent(func, eh1, eh2)) {
371 eh1 = func.exnNodes[eh1].parent;
372 eh2 = func.exnNodes[eh2].parent;
374 return eh1;
377 const StaticString
378 s_hhbbc_fail_verification("__hhvm_intrinsics\\hhbbc_fail_verification");
380 EmitBcInfo emit_bytecode(EmitUnitState& euState,
381 UnitEmitter& ue,
382 const php::Func& func) {
383 EmitBcInfo ret = {};
384 auto& blockInfo = ret.blockInfo;
385 blockInfo.resize(func.blocks.size());
387 // Track the stack depth while emitting to determine maxStackDepth.
388 int32_t currentStackDepth { 0 };
390 // Stack of in-progress fpi regions.
391 std::vector<EmitBcInfo::FPI> fpiStack;
393 // Temporary buffer for vector immediates. (Hoisted so it's not
394 // allocated in the loop.)
395 std::vector<uint8_t> immVec;
397 // Offset of the last emitted bytecode.
398 Offset lastOff { 0 };
400 bool traceBc = false;
402 Type tos{};
404 auto const pseudomain = is_pseudomain(&func);
405 auto process_mergeable = [&] (const Bytecode& bc) {
406 if (!pseudomain) return;
407 switch (bc.op) {
408 case Op::DefCls:
409 case Op::DefClsNop:
410 if (!ue.m_returnSeen) {
411 auto const& cls = euState.unit->classes[
412 bc.op == Op::DefCls ? bc.DefCls.arg1 : bc.DefClsNop.arg1];
413 if (cls->hoistability == PreClass::NotHoistable) {
414 cls->hoistability = PreClass::Mergeable;
417 return;
418 case Op::AssertRATL:
419 case Op::AssertRATStk:
420 case Op::Nop:
421 return;
423 case Op::DefCns: {
424 if (ue.m_returnSeen || tos.subtypeOf(BBottom)) break;
425 auto top = tv(tos);
426 assertx(top);
427 auto val = euState.index.lookup_persistent_constant(bc.DefCns.str1);
428 // If there's a persistent constant with the same name, either
429 // this is the one and only definition, or the persistent
430 // definition is in systemlib (and this one will always fail).
431 auto const kind = val && cellSame(*val, *top) ?
432 Unit::MergeKind::PersistentDefine : Unit::MergeKind::Define;
433 ue.pushMergeableDef(kind, bc.DefCns.str1, *top);
434 return;
436 case Op::DefTypeAlias:
437 ue.pushMergeableTypeAlias(Unit::MergeKind::TypeAlias,
438 bc.DefTypeAlias.arg1);
439 return;
441 case Op::Null: tos = TInitNull; return;
442 case Op::True: tos = TTrue; return;
443 case Op::False: tos = TFalse; return;
444 case Op::Int: tos = ival(bc.Int.arg1); return;
445 case Op::Double: tos = dval(bc.Double.dbl1); return;
446 case Op::String: tos = sval(bc.String.str1); return;
447 case Op::Vec: tos = vec_val(bc.Vec.arr1); return;
448 case Op::Dict: tos = dict_val(bc.Dict.arr1); return;
449 case Op::Keyset: tos = keyset_val(bc.Keyset.arr1); return;
450 case Op::Array: tos = aval(bc.Array.arr1); return;
451 case Op::PopC:
452 tos = TBottom;
453 return;
454 case Op::RetC: {
455 if (ue.m_returnSeen || tos.subtypeOf(BBottom)) break;
456 auto top = tv(tos);
457 assertx(top);
458 ue.m_returnSeen = true;
459 ue.m_mainReturn = *top;
460 tos = TBottom;
461 return;
463 default:
464 break;
466 ue.m_returnSeen = true;
467 ue.m_mainReturn = make_tv<KindOfUninit>();
468 tos = TBottom;
471 auto map_local = [&] (LocalId id) {
472 auto const loc = func.locals[id];
473 assert(!loc.killed);
474 assert(loc.id <= id);
475 return loc.id;
478 auto end_fpi = [&] (Offset off) {
479 auto& fpi = fpiStack.back();
480 fpi.fpiEndOff = off;
481 ret.fpiRegions.push_back(fpi);
482 fpiStack.pop_back();
485 auto set_expected_depth = [&] (BlockId block) {
486 auto& info = blockInfo[block];
488 if (info.expectedStackDepth) {
489 assert(*info.expectedStackDepth == currentStackDepth);
490 } else {
491 info.expectedStackDepth = currentStackDepth;
494 if (info.expectedFPIDepth) {
495 assert(*info.expectedFPIDepth == fpiStack.size());
496 } else {
497 info.expectedFPIDepth = fpiStack.size();
501 auto make_member_key = [&] (MKey mkey) {
502 switch (mkey.mcode) {
503 case MEC: case MPC:
504 return MemberKey{mkey.mcode, mkey.idx};
505 case MEL: case MPL:
506 return MemberKey{
507 mkey.mcode, static_cast<int32_t>(map_local(mkey.local))
509 case MET: case MPT: case MQT:
510 return MemberKey{mkey.mcode, mkey.litstr};
511 case MEI:
512 return MemberKey{mkey.mcode, mkey.int64};
513 case MW:
514 return MemberKey{};
516 not_reached();
519 auto emit_inst = [&] (const Bytecode& inst) {
520 process_mergeable(inst);
521 auto const startOffset = ue.bcPos();
522 lastOff = startOffset;
524 FTRACE(4, " emit: {} -- {} @ {}\n", currentStackDepth, show(&func, inst),
525 show(srcLoc(func, inst.srcLoc)));
527 if (options.TraceBytecodes.count(inst.op)) traceBc = true;
529 auto emit_vsa = [&] (const CompactVector<LSString>& keys) {
530 auto n = keys.size();
531 ue.emitIVA(n);
532 for (size_t i = 0; i < n; ++i) {
533 ue.emitInt32(ue.mergeLitstr(keys[i]));
537 auto emit_branch = [&] (BlockId id) {
538 auto& info = blockInfo[id];
539 if (info.offset != kInvalidOffset) {
540 ue.emitInt32(info.offset - startOffset);
541 } else {
542 info.forwardJumps.push_back({ startOffset, ue.bcPos() });
543 ue.emitInt32(0);
547 auto emit_switch = [&] (const SwitchTab& targets) {
548 ue.emitIVA(targets.size());
549 for (auto t : targets) {
550 set_expected_depth(t);
551 emit_branch(t);
555 auto emit_sswitch = [&] (const SSwitchTab& targets) {
556 ue.emitIVA(targets.size());
557 for (size_t i = 0; i < targets.size() - 1; ++i) {
558 set_expected_depth(targets[i].second);
559 ue.emitInt32(ue.mergeLitstr(targets[i].first));
560 emit_branch(targets[i].second);
562 ue.emitInt32(-1);
563 set_expected_depth(targets[targets.size() - 1].second);
564 emit_branch(targets[targets.size() - 1].second);
567 auto emit_itertab = [&] (const IterTab& iterTab) {
568 ue.emitIVA(iterTab.size());
569 for (auto const& kv : iterTab) {
570 ue.emitIVA(kv.kind);
571 ue.emitIVA(kv.id);
572 if (kv.kind == KindOfLIter) {
573 always_assert(kv.local != NoLocalId);
574 ue.emitIVA(map_local(kv.local));
575 } else {
576 always_assert(kv.local == NoLocalId);
581 auto emit_argvec32 = [&] (const CompactVector<uint32_t>& argv) {
582 ue.emitIVA(argv.size());
583 for (auto i : argv) {
584 ue.emitInt32(i);
588 auto emit_srcloc = [&] {
589 auto const sl = srcLoc(func, inst.srcLoc);
590 if (!sl.isValid()) return;
591 Location::Range loc(sl.start.line, sl.start.col,
592 sl.past.line, sl.past.col);
593 ue.recordSourceLocation(loc, startOffset);
596 auto pop = [&] (int32_t n) {
597 currentStackDepth -= n;
598 assert(currentStackDepth >= 0);
600 auto push = [&] (int32_t n) {
601 currentStackDepth += n;
602 auto const depth = currentStackDepth + fpiStack.size() * kNumActRecCells;
603 ret.maxStackDepth = std::max<uint32_t>(ret.maxStackDepth, depth);
606 auto begin_fpi = [&] {
607 fpiStack.push_back({startOffset, kInvalidOffset, currentStackDepth});
608 auto const depth = currentStackDepth + fpiStack.size() * kNumActRecCells;
609 ret.maxStackDepth = std::max<uint32_t>(ret.maxStackDepth, depth);
612 auto ret_assert = [&] { assert(currentStackDepth == inst.numPop()); };
614 auto clsid_impl = [&] (uint32_t& id, bool closure) {
615 if (euState.classOffsets[id] != kInvalidOffset) {
616 always_assert(closure);
617 for (auto const& elm : euState.pceInfo) {
618 if (elm.origId == id) {
619 id = elm.pce->id();
620 return;
623 always_assert(false);
625 euState.classOffsets[id] = startOffset;
626 id = recordClass(euState, ue, id);
628 auto defcls = [&] (auto& data) { clsid_impl(data.arg1, false); };
629 auto defclsnop = [&] (auto& data) { clsid_impl(data.arg1, false); };
630 auto createcl = [&] (auto& data) { clsid_impl(data.arg2, true); };
631 auto deftype = [&] (auto& data) {
632 euState.typeAliasInfo.push_back(data.arg1);
633 data.arg1 = euState.typeAliasInfo.size() - 1;
636 auto emit_lar = [&](const LocalRange& range) {
637 always_assert(range.first + range.count <= func.locals.size());
638 auto const first = (range.count > 0) ? map_local(range.first) : 0;
639 encodeLocalRange(ue, HPHP::LocalRange{first, range.count});
642 #define IMM_BLA(n) emit_switch(data.targets);
643 #define IMM_SLA(n) emit_sswitch(data.targets);
644 #define IMM_ILA(n) emit_itertab(data.iterTab);
645 #define IMM_I32LA(n) emit_argvec32(data.argv);
646 #define IMM_IVA(n) ue.emitIVA(data.arg##n);
647 #define IMM_I64A(n) ue.emitInt64(data.arg##n);
648 #define IMM_LA(n) ue.emitIVA(map_local(data.loc##n));
649 #define IMM_IA(n) ue.emitIVA(data.iter##n);
650 #define IMM_CAR(n) ue.emitIVA(data.slot);
651 #define IMM_CAW(n) ue.emitIVA(data.slot);
652 #define IMM_DA(n) ue.emitDouble(data.dbl##n);
653 #define IMM_SA(n) ue.emitInt32(ue.mergeLitstr(data.str##n));
654 #define IMM_RATA(n) encodeRAT(ue, data.rat);
655 #define IMM_AA(n) ue.emitInt32(ue.mergeArray(data.arr##n));
656 #define IMM_OA_IMPL(n) ue.emitByte(static_cast<uint8_t>(data.subop##n));
657 #define IMM_OA(type) IMM_OA_IMPL
658 #define IMM_BA(n) targets[numTargets++] = data.target##n; \
659 emit_branch(data.target##n);
660 #define IMM_VSA(n) emit_vsa(data.keys);
661 #define IMM_KA(n) encode_member_key(make_member_key(data.mkey), ue);
662 #define IMM_LAR(n) emit_lar(data.locrange);
663 #define IMM_FCA(n) encodeFCallArgs( \
664 ue, data.fca, data.fca.byRefs.get(), \
665 data.fca.asyncEagerTarget != NoBlockId, \
666 [&] { \
667 set_expected_depth(data.fca.asyncEagerTarget); \
668 emit_branch(data.fca.asyncEagerTarget); \
669 }); \
670 if (!data.fca.hasUnpack()) ret.containsCalls = true;\
672 #define IMM_NA
673 #define IMM_ONE(x) IMM_##x(1)
674 #define IMM_TWO(x, y) IMM_##x(1); IMM_##y(2);
675 #define IMM_THREE(x, y, z) IMM_TWO(x, y); IMM_##z(3);
676 #define IMM_FOUR(x, y, z, n) IMM_THREE(x, y, z); IMM_##n(4);
677 #define IMM_FIVE(x, y, z, n, m) IMM_FOUR(x, y, z, n); IMM_##m(5);
679 #define POP_NOV
680 #define POP_ONE(x) pop(1);
681 #define POP_TWO(x, y) pop(2);
682 #define POP_THREE(x, y, z) pop(3);
684 #define POP_MFINAL pop(data.arg1);
685 #define POP_C_MFINAL(n) pop(n); pop(data.arg1);
686 #define POP_CMANY pop(data.arg##1);
687 #define POP_SMANY pop(data.keys.size());
688 #define POP_CUMANY pop(data.arg##1);
689 #define POP_CVUMANY pop(data.arg##1);
690 #define POP_FPUSH(nin, nobj) \
691 pop(data.arg1 + nin + 3);
692 #define POP_FCALL(nin, nobj) \
693 pop(nin + data.fca.numArgsInclUnpack() + 2 + \
694 data.fca.numRets);
695 #define POP_FCALLO pop(data.fca.numArgsInclUnpack() + data.fca.numRets - 1);
697 #define PUSH_NOV
698 #define PUSH_ONE(x) push(1);
699 #define PUSH_TWO(x, y) push(2);
700 #define PUSH_THREE(x, y, z) push(3);
701 #define PUSH_FPUSH push(data.arg1);
702 #define PUSH_FCALL push(data.fca.numRets);
704 #define O(opcode, imms, inputs, outputs, flags) \
705 auto emit_##opcode = [&] (OpInfo<bc::opcode> data) { \
706 if (RuntimeOption::EnableIntrinsicsExtension) { \
707 if (Op::opcode == Op::FCallBuiltin && \
708 inst.FCallBuiltin.str3->isame( \
709 s_hhbbc_fail_verification.get())) { \
710 ue.emitOp(Op::CheckProp); \
711 ue.emitInt32( \
712 ue.mergeLitstr(inst.FCallBuiltin.str3)); \
713 ue.emitOp(Op::PopC); \
716 caller<Op::DefCls>(defcls, data); \
717 caller<Op::DefClsNop>(defclsnop, data); \
718 caller<Op::CreateCl>(createcl, data); \
719 caller<Op::DefTypeAlias>(deftype, data); \
721 if (isRet(Op::opcode)) ret_assert(); \
722 ue.emitOp(Op::opcode); \
723 POP_##inputs \
724 if (isLegacyFPush(Op::opcode)) begin_fpi(); \
725 if (isLegacyFCall(Op::opcode)) end_fpi(startOffset); \
727 size_t numTargets = 0; \
728 std::array<BlockId, kMaxHhbcImms> targets; \
730 if (Op::opcode == Op::MemoGet) { \
731 IMM_##imms \
732 assertx(numTargets == 1); \
733 set_expected_depth(targets[0]); \
734 PUSH_##outputs \
735 } else if (Op::opcode == Op::MemoGetEager) { \
736 IMM_##imms \
737 assertx(numTargets == 2); \
738 set_expected_depth(targets[0]); \
739 PUSH_##outputs \
740 set_expected_depth(targets[1]); \
741 } else { \
742 PUSH_##outputs \
743 IMM_##imms \
744 for (size_t i = 0; i < numTargets; ++i) { \
745 set_expected_depth(targets[i]); \
749 if (flags & TF) currentStackDepth = 0; \
750 emit_srcloc(); \
753 OPCODES
755 #undef O
757 #undef IMM_MA
758 #undef IMM_BLA
759 #undef IMM_SLA
760 #undef IMM_ILA
761 #undef IMM_I32LA
762 #undef IMM_IVA
763 #undef IMM_I64A
764 #undef IMM_LA
765 #undef IMM_IA
766 #undef IMM_CAR
767 #undef IMM_CAW
768 #undef IMM_DA
769 #undef IMM_SA
770 #undef IMM_RATA
771 #undef IMM_AA
772 #undef IMM_BA
773 #undef IMM_OA_IMPL
774 #undef IMM_OA
775 #undef IMM_VSA
776 #undef IMM_KA
777 #undef IMM_LAR
778 #undef IMM_FCA
780 #undef IMM_NA
781 #undef IMM_ONE
782 #undef IMM_TWO
783 #undef IMM_THREE
784 #undef IMM_FOUR
785 #undef IMM_FIVE
787 #undef POP_NOV
788 #undef POP_ONE
789 #undef POP_TWO
790 #undef POP_THREE
792 #undef POP_CMANY
793 #undef POP_SMANY
794 #undef POP_CUMANY
795 #undef POP_CVUMANY
796 #undef POP_FPUSH
797 #undef POP_FCALL
798 #undef POP_FCALLO
799 #undef POP_MFINAL
800 #undef POP_C_MFINAL
802 #undef PUSH_NOV
803 #undef PUSH_ONE
804 #undef PUSH_TWO
805 #undef PUSH_THREE
806 #undef PUSH_FPUSH
807 #undef PUSH_FCALL
809 #define O(opcode, ...) \
810 case Op::opcode: \
811 if (Op::opcode != Op::Nop) emit_##opcode(inst.opcode); \
812 break;
813 switch (inst.op) { OPCODES }
814 #undef O
817 ret.blockOrder = order_blocks(func);
818 auto blockIt = begin(ret.blockOrder);
819 auto const endBlockIt = end(ret.blockOrder);
820 for (; blockIt != endBlockIt; ++blockIt) {
821 auto bid = *blockIt;
822 auto& info = blockInfo[bid];
823 auto const b = func.blocks[bid].get();
825 info.offset = ue.bcPos();
826 FTRACE(2, " block {}: {}\n", bid, info.offset);
828 for (auto& fixup : info.forwardJumps) {
829 ue.emitInt32(info.offset - fixup.instrOff, fixup.jmpImmedOff);
832 if (!info.expectedStackDepth) {
833 // unreachable, or entry block
834 info.expectedStackDepth = b->catchEntry ? 1 : 0;
837 currentStackDepth = *info.expectedStackDepth;
839 if (!info.expectedFPIDepth) {
840 // unreachable, or an entry block
841 info.expectedFPIDepth = 0;
844 // deal with fpiRegions that were ended by terminal instructions
845 assert(*info.expectedFPIDepth <= fpiStack.size());
846 while (*info.expectedFPIDepth < fpiStack.size()) end_fpi(lastOff);
848 auto fallthrough = b->fallthrough;
849 auto end = b->hhbcs.end();
850 auto flip = false;
852 if (is_single_nop(*b)) {
853 if (blockIt == begin(ret.blockOrder)) {
854 // If the first block is just a Nop, this means that there is
855 // a jump to the second block from somewhere in the
856 // function. We don't want this, so we change this nop to an
857 // EntryNop so it doesn't get optimized away
858 emit_inst(bc_with_loc(b->hhbcs.front().srcLoc, bc::EntryNop {}));
860 } else {
861 // If the block ends with JmpZ or JmpNZ to the next block, flip
862 // the condition to make the fallthrough the next block
863 if (b->hhbcs.back().op == Op::JmpZ ||
864 b->hhbcs.back().op == Op::JmpNZ) {
865 auto const& bc = b->hhbcs.back();
866 auto const target =
867 bc.op == Op::JmpNZ ? bc.JmpNZ.target1 : bc.JmpZ.target1;
868 if (std::next(blockIt) != endBlockIt && blockIt[1] == target) {
869 fallthrough = target;
870 --end;
871 flip = true;
875 for (auto iit = b->hhbcs.begin(); iit != end; ++iit) emit_inst(*iit);
876 if (flip) {
877 if (end->op == Op::JmpNZ) {
878 emit_inst(bc_with_loc(end->srcLoc, bc::JmpZ { b->fallthrough }));
879 } else {
880 emit_inst(bc_with_loc(end->srcLoc, bc::JmpNZ { b->fallthrough }));
885 info.past = ue.bcPos();
887 if (fallthrough != NoBlockId) {
888 set_expected_depth(fallthrough);
889 if (std::next(blockIt) == endBlockIt ||
890 blockIt[1] != fallthrough) {
891 if (b->fallthroughNS) {
892 emit_inst(bc::JmpNS { fallthrough });
893 } else {
894 emit_inst(bc::Jmp { fallthrough });
897 auto const parent = commonParent(func,
898 func.blocks[fallthrough]->exnNodeId,
899 b->exnNodeId);
901 auto depth = [&] (ExnNodeId eid) {
902 return eid == NoExnNodeId ? 0 : func.exnNodes[eid].depth;
904 // If we are in an exn region we pop from the current region to the
905 // common parent. If the common parent is null, we pop all regions
906 info.regionsToPop = depth(b->exnNodeId) - depth(parent);
907 assert(info.regionsToPop >= 0);
908 FTRACE(4, " popped catch regions: {}\n", info.regionsToPop);
912 if (b->throwExit != NoBlockId) {
913 FTRACE(4, " throw: {}\n", b->throwExit);
915 if (fallthrough != NoBlockId) {
916 FTRACE(4, " fallthrough: {}\n", fallthrough);
918 FTRACE(2, " block {} end: {}\n", bid, info.past);
921 while (fpiStack.size()) end_fpi(lastOff);
923 if (traceBc) {
924 FTRACE(0, "TraceBytecode (emit): {}::{} in {}\n",
925 func.cls ? func.cls->name->data() : "",
926 func.name, func.unit->filename);
929 return ret;
932 void emit_locals_and_params(FuncEmitter& fe,
933 const php::Func& func,
934 const EmitBcInfo& info) {
935 Id id = 0;
937 for (auto& loc : func.locals) {
938 if (loc.id < func.params.size()) {
939 assert(!loc.killed);
940 auto& param = func.params[id];
941 FuncEmitter::ParamInfo pinfo;
942 pinfo.defaultValue = param.defaultValue;
943 pinfo.typeConstraint = param.typeConstraint;
944 pinfo.userType = param.userTypeConstraint;
945 pinfo.phpCode = param.phpCode;
946 pinfo.userAttributes = param.userAttributes;
947 pinfo.builtinType = param.builtinType;
948 pinfo.byRef = param.byRef;
949 pinfo.inout = param.inout;
950 pinfo.variadic = param.isVariadic;
951 fe.appendParam(func.locals[id].name, pinfo);
952 auto const dv = param.dvEntryPoint;
953 if (dv != NoBlockId) {
954 fe.params[id].funcletOff = info.blockInfo[dv].offset;
956 ++id;
957 } else if (!loc.killed) {
958 if (loc.name) {
959 fe.allocVarId(loc.name);
960 assert(fe.lookupVarId(loc.name) == id);
961 assert(loc.id == id);
962 } else {
963 fe.allocUnnamedLocal();
965 ++id;
968 assert(fe.numLocals() == id);
969 fe.setNumIterators(func.numIters);
970 fe.setNumClsRefSlots(func.numClsRefSlots);
973 struct EHRegion {
974 const php::ExnNode* node;
975 EHRegion* parent;
976 Offset start;
977 Offset past;
980 template<class BlockInfo, class ParentIndexMap>
981 void emit_eh_region(FuncEmitter& fe,
982 const EHRegion* region,
983 const BlockInfo& blockInfo,
984 ParentIndexMap& parentIndexMap) {
985 FTRACE(2, " func {}: ExnNode {}\n", fe.name, region->node->idx);
987 auto const unreachable = [&] (const php::ExnNode& node) {
988 return blockInfo[node.region.catchEntry].offset == kInvalidOffset;
991 // A region on a single empty block.
992 if (region->start == region->past) {
993 FTRACE(2, " Skipping\n");
994 return;
995 } else if (unreachable(*region->node)) {
996 FTRACE(2, " Unreachable\n");
997 return;
1000 FTRACE(2, " Process @ {}-{}\n", region->start, region->past);
1002 auto& eh = fe.addEHEnt();
1003 eh.m_base = region->start;
1004 eh.m_past = region->past;
1005 assert(eh.m_past >= eh.m_base);
1006 assert(eh.m_base != kInvalidOffset && eh.m_past != kInvalidOffset);
1008 // An unreachable parent won't be emitted (and thus its offset won't be set),
1009 // so find the closest reachable one.
1010 auto parent = region->parent;
1011 while (parent && unreachable(*parent->node)) parent = parent->parent;
1012 if (parent) {
1013 auto parentIt = parentIndexMap.find(parent);
1014 assert(parentIt != end(parentIndexMap));
1015 eh.m_parentIndex = parentIt->second;
1016 } else {
1017 eh.m_parentIndex = -1;
1019 parentIndexMap[region] = fe.ehtab.size() - 1;
1021 auto const& cr = region->node->region;
1022 eh.m_handler = blockInfo[cr.catchEntry].offset;
1023 eh.m_end = kInvalidOffset;
1024 eh.m_iterId = cr.iterId;
1026 assert(eh.m_handler != kInvalidOffset);
1029 void exn_path(const php::Func& func,
1030 std::vector<const php::ExnNode*>& ret,
1031 ExnNodeId id) {
1032 if (id == NoExnNodeId) return;
1033 auto const& n = func.exnNodes[id];
1034 exn_path(func, ret, n.parent);
1035 ret.push_back(&n);
1038 // Return the count of shared elements in the front of two forward
1039 // ranges.
1040 template<class ForwardRange1, class ForwardRange2>
1041 size_t shared_prefix(ForwardRange1& r1, ForwardRange2& r2) {
1042 auto r1it = begin(r1);
1043 auto r2it = begin(r2);
1044 auto const r1end = end(r1);
1045 auto const r2end = end(r2);
1046 auto ret = size_t{0};
1047 while (r1it != r1end && r2it != r2end && *r1it == *r2it) {
1048 ++ret; ++r1it; ++r2it;
1050 return ret;
1054 * Traverse the actual block layout, and find out the intervals for
1055 * each exception region in the tree.
1057 * The basic idea here is that we haven't constrained block layout
1058 * based on the exception tree, but adjacent blocks are still
1059 * reasonably likely to have the same ExnNode. Try to coalesce the EH
1060 * regions we create for in those cases.
1062 void emit_ehent_tree(FuncEmitter& fe, const php::Func& func,
1063 const EmitBcInfo& info) {
1064 hphp_fast_map<
1065 const php::ExnNode*,
1066 std::vector<std::unique_ptr<EHRegion>>
1067 > exnMap;
1070 * While walking over the blocks in layout order, we track the set
1071 * of "active" exnNodes. This are a list of exnNodes that inherit
1072 * from each other. When a new active node is pushed, begin an
1073 * EHEnt, and when it's popped, it's done.
1075 std::vector<const php::ExnNode*> activeList;
1077 auto pop_active = [&] (Offset past) {
1078 auto p = activeList.back();
1079 activeList.pop_back();
1080 exnMap[p].back()->past = past;
1083 auto push_active = [&] (const php::ExnNode* p, Offset start) {
1084 auto const parent = activeList.empty()
1085 ? nullptr
1086 : exnMap[activeList.back()].back().get();
1087 exnMap[p].push_back(
1088 std::make_unique<EHRegion>(
1089 EHRegion { p, parent, start, kInvalidOffset }
1092 activeList.push_back(p);
1096 * Walk over the blocks, and compare the new block's exnNode path to
1097 * the active one. Find the least common ancestor of the two paths,
1098 * then modify the active list by popping and then pushing nodes to
1099 * set it to the new block's path.
1101 for (auto const bid : info.blockOrder) {
1102 auto const b = func.blocks[bid].get();
1103 auto const offset = info.blockInfo[bid].offset;
1105 if (b->exnNodeId == NoExnNodeId) {
1106 while (!activeList.empty()) pop_active(offset);
1107 continue;
1110 std::vector<const php::ExnNode*> current;
1111 exn_path(func, current, b->exnNodeId);
1113 auto const prefix = shared_prefix(current, activeList);
1114 for (size_t i = prefix, sz = activeList.size(); i < sz; ++i) {
1115 pop_active(offset);
1117 for (size_t i = prefix, sz = current.size(); i < sz; ++i) {
1118 push_active(current[i], offset);
1121 for (int i = 0; i < info.blockInfo[bid].regionsToPop; i++) {
1122 // If the block ended in a jump out of the catch region, this effectively
1123 // ends all catch regions deeper than the one we are jumping to
1124 pop_active(info.blockInfo[bid].past);
1127 if (debug && !activeList.empty()) {
1128 current.clear();
1129 exn_path(func, current, activeList.back()->idx);
1130 assert(current == activeList);
1134 while (!activeList.empty()) {
1135 pop_active(info.blockInfo[info.blockOrder.back()].past);
1139 * We've created all our regions, but we need to sort them instead
1140 * of trying to get the UnitEmitter to do it.
1142 * The UnitEmitter expects EH regions that look a certain way
1143 * (basically the way emitter.cpp likes them). There are some rules
1144 * about the order it needs to have at runtime, which we set up
1145 * here.
1147 * Essentially, an entry a is less than an entry b iff:
1149 * - a starts before b
1150 * - a starts at the same place, but encloses b entirely
1151 * - a has the same extents as b, but is a parent of b
1153 std::vector<EHRegion*> regions;
1154 for (auto& mapEnt : exnMap) {
1155 for (auto& region : mapEnt.second) {
1156 regions.push_back(region.get());
1159 std::sort(
1160 begin(regions), end(regions),
1161 [&] (const EHRegion* a, const EHRegion* b) {
1162 if (a == b) return false;
1163 if (a->start == b->start) {
1164 if (a->past == b->past) {
1165 // When regions exactly overlap, the parent is less than the
1166 // child.
1167 for (auto p = b->parent; p != nullptr; p = p->parent) {
1168 if (p == a) return true;
1170 // If a is not a parent of b, and they have the same region;
1171 // then b better be a parent of a.
1172 if (debug) {
1173 auto p = a->parent;
1174 for (; p != b && p != nullptr; p = p->parent) continue;
1175 assert(p == b);
1177 return false;
1179 return a->past > b->past;
1181 return a->start < b->start;
1185 hphp_fast_map<const EHRegion*,uint32_t> parentIndexMap;
1186 for (auto& r : regions) {
1187 emit_eh_region(fe, r, info.blockInfo, parentIndexMap);
1189 fe.setEHTabIsSorted();
1192 void merge_repo_auth_type(UnitEmitter& ue, RepoAuthType rat) {
1193 using T = RepoAuthType::Tag;
1195 switch (rat.tag()) {
1196 case T::OptBool:
1197 case T::OptInt:
1198 case T::OptSStr:
1199 case T::OptStr:
1200 case T::OptDbl:
1201 case T::OptRes:
1202 case T::OptObj:
1203 case T::OptFunc:
1204 case T::OptCls:
1205 case T::OptClsMeth:
1206 case T::OptUncArrKey:
1207 case T::OptArrKey:
1208 case T::OptUncStrLike:
1209 case T::OptStrLike:
1210 case T::Null:
1211 case T::Cell:
1212 case T::Ref:
1213 case T::InitUnc:
1214 case T::Unc:
1215 case T::UncArrKey:
1216 case T::ArrKey:
1217 case T::UncStrLike:
1218 case T::StrLike:
1219 case T::InitCell:
1220 case T::InitGen:
1221 case T::Gen:
1222 case T::Uninit:
1223 case T::InitNull:
1224 case T::Bool:
1225 case T::Int:
1226 case T::Dbl:
1227 case T::Res:
1228 case T::SStr:
1229 case T::Str:
1230 case T::Obj:
1231 case T::Func:
1232 case T::Cls:
1233 case T::ClsMeth:
1234 return;
1236 case T::OptSArr:
1237 case T::OptArr:
1238 case T::SArr:
1239 case T::Arr:
1240 case T::OptSVArr:
1241 case T::OptVArr:
1242 case T::SVArr:
1243 case T::VArr:
1244 case T::OptSDArr:
1245 case T::OptDArr:
1246 case T::SDArr:
1247 case T::DArr:
1248 case T::OptSVec:
1249 case T::OptVec:
1250 case T::SVec:
1251 case T::Vec:
1252 case T::OptSDict:
1253 case T::OptDict:
1254 case T::SDict:
1255 case T::Dict:
1256 case T::OptSKeyset:
1257 case T::OptKeyset:
1258 case T::SKeyset:
1259 case T::Keyset:
1260 // NOTE: In repo mode, RAT's in Array's might only contain global litstr
1261 // id's. No need to merge. In non-repo mode, RAT's in Array's might contain
1262 // local litstr id's.
1263 if (RuntimeOption::RepoAuthoritative) return;
1265 if (rat.hasArrData()) {
1266 auto arr = rat.array();
1267 switch (arr->tag()) {
1268 case RepoAuthType::Array::Tag::Packed:
1269 for (uint32_t i = 0; i < arr->size(); ++i) {
1270 merge_repo_auth_type(ue, arr->packedElem(i));
1272 break;
1273 case RepoAuthType::Array::Tag::PackedN:
1274 merge_repo_auth_type(ue, arr->elemType());
1275 break;
1278 return;
1280 case T::OptSubObj:
1281 case T::OptExactObj:
1282 case T::SubObj:
1283 case T::ExactObj:
1284 ue.mergeLitstr(rat.clsName());
1285 return;
1289 void emit_finish_func(EmitUnitState& state,
1290 const php::Func& func,
1291 FuncEmitter& fe,
1292 const EmitBcInfo& info) {
1293 if (info.containsCalls) fe.containsCalls = true;;
1295 for (auto& fpi : info.fpiRegions) {
1296 auto& e = fe.addFPIEnt();
1297 e.m_fpushOff = fpi.fpushOff;
1298 e.m_fpiEndOff = fpi.fpiEndOff;
1299 e.m_fpOff = fpi.fpDelta;
1302 emit_locals_and_params(fe, func, info);
1303 emit_ehent_tree(fe, func, info);
1305 // Nothing should look at the bytecode from now on. Free it up to
1306 // compensate for the UnitEmitter we're creating.
1307 const_cast<php::Func&>(func).blocks.clear();
1309 fe.userAttributes = func.userAttributes;
1310 fe.retUserType = func.returnUserType;
1311 fe.originalFilename =
1312 func.originalFilename ? func.originalFilename :
1313 func.originalUnit ? func.originalUnit->filename : nullptr;
1314 fe.isClosureBody = func.isClosureBody;
1315 fe.isAsync = func.isAsync;
1316 fe.isGenerator = func.isGenerator;
1317 fe.isPairGenerator = func.isPairGenerator;
1318 fe.isNative = func.nativeInfo != nullptr;
1319 fe.isMemoizeWrapper = func.isMemoizeWrapper;
1320 fe.isMemoizeWrapperLSB = func.isMemoizeWrapperLSB;
1321 fe.isRxDisabled = func.isRxDisabled;
1323 auto const retTy = state.index.lookup_return_type_and_clear(&func);
1324 if (!retTy.subtypeOf(BBottom)) {
1325 auto const rat = make_repo_type(*state.index.array_table_builder(), retTy);
1326 merge_repo_auth_type(fe.ue(), rat);
1327 fe.repoReturnType = rat;
1330 if (is_specialized_wait_handle(retTy)) {
1331 auto const awaitedTy = wait_handle_inner(retTy);
1332 if (!awaitedTy.subtypeOf(BBottom)) {
1333 auto const rat = make_repo_type(
1334 *state.index.array_table_builder(),
1335 awaitedTy
1337 merge_repo_auth_type(fe.ue(), rat);
1338 fe.repoAwaitedReturnType = rat;
1342 if (func.nativeInfo) {
1343 fe.hniReturnType = func.nativeInfo->returnType;
1345 fe.retTypeConstraint = func.retTypeConstraint;
1347 fe.maxStackCells = info.maxStackDepth +
1348 fe.numLocals() +
1349 fe.numIterators() * kNumIterCells +
1350 clsRefCountToCells(fe.numClsRefSlots());
1352 fe.finish(fe.ue().bcPos(), false /* load */);
1355 void renumber_locals(const php::Func& func) {
1356 Id id = 0;
1358 for (auto& loc : const_cast<php::Func&>(func).locals) {
1359 if (loc.killed) {
1360 // make sure its out of range, in case someone tries to read it.
1361 loc.id = INT_MAX;
1362 } else {
1363 loc.id = id++;
1368 void emit_init_func(FuncEmitter& fe, const php::Func& func) {
1369 renumber_locals(func);
1370 fe.init(
1371 std::get<0>(func.srcInfo.loc),
1372 std::get<1>(func.srcInfo.loc),
1373 fe.ue().bcPos(),
1374 func.attrs,
1375 func.top,
1376 func.srcInfo.docComment
1380 void emit_func(EmitUnitState& state, UnitEmitter& ue,
1381 FuncEmitter* fe, const php::Func& func) {
1382 FTRACE(2, " func {}\n", func.name->data());
1383 emit_init_func(*fe, func);
1384 auto const info = emit_bytecode(state, ue, func);
1385 emit_finish_func(state, func, *fe, info);
1388 void emit_pseudomain(EmitUnitState& state,
1389 UnitEmitter& ue,
1390 const php::Unit& unit) {
1391 FTRACE(2, " pseudomain\n");
1392 auto& pm = *unit.pseudomain;
1393 renumber_locals(pm);
1394 ue.initMain(std::get<0>(pm.srcInfo.loc),
1395 std::get<1>(pm.srcInfo.loc));
1396 auto const fe = ue.getMain();
1397 auto const info = emit_bytecode(state, ue, pm);
1398 if (is_systemlib_part(unit)) {
1399 ue.m_mergeOnly = true;
1400 auto const tv = make_tv<KindOfInt64>(1);
1401 ue.m_mainReturn = tv;
1402 } else {
1403 ue.m_mergeOnly =
1404 ue.m_returnSeen && ue.m_mainReturn.m_type != KindOfUninit;
1407 emit_finish_func(state, pm, *fe, info);
1410 void emit_record(UnitEmitter& ue, const php::Record& rec) {
1411 auto const re = ue.newRecordEmitter(rec.name->toCppString());
1412 re->init(
1413 std::get<0>(rec.srcInfo.loc),
1414 std::get<1>(rec.srcInfo.loc),
1415 rec.attrs,
1416 rec.srcInfo.docComment
1418 re->setUserAttributes(rec.userAttributes);
1419 for (auto&& f : rec.fields) {
1420 re->addField(
1421 f.name,
1422 f.attrs,
1423 f.userType,
1424 f.typeConstraint,
1425 f.docComment,
1426 &f.val,
1427 RepoAuthType{},
1428 f.userAttributes
1433 void emit_class(EmitUnitState& state,
1434 UnitEmitter& ue,
1435 PreClassEmitter* pce,
1436 Offset offset,
1437 const php::Class& cls) {
1438 FTRACE(2, " class: {}\n", cls.name->data());
1439 pce->init(
1440 std::get<0>(cls.srcInfo.loc),
1441 std::get<1>(cls.srcInfo.loc),
1442 offset == kInvalidOffset ? ue.bcPos() : offset,
1443 cls.attrs,
1444 cls.parentName ? cls.parentName : s_empty.get(),
1445 cls.srcInfo.docComment
1447 pce->setUserAttributes(cls.userAttributes);
1449 for (auto& x : cls.interfaceNames) pce->addInterface(x);
1450 for (auto& x : cls.usedTraitNames) pce->addUsedTrait(x);
1451 for (auto& x : cls.requirements) pce->addClassRequirement(x);
1452 for (auto& x : cls.traitPrecRules) pce->addTraitPrecRule(x);
1453 for (auto& x : cls.traitAliasRules) pce->addTraitAliasRule(x);
1455 pce->setIfaceVtableSlot(state.index.lookup_iface_vtable_slot(&cls));
1457 bool needs86cinit = false;
1459 auto const nativeConsts = cls.attrs & AttrBuiltin ?
1460 Native::getClassConstants(cls.name) : nullptr;
1462 for (auto& cconst : cls.constants) {
1463 if (nativeConsts && nativeConsts->count(cconst.name)) {
1464 break;
1466 if (!cconst.val.hasValue()) {
1467 pce->addAbstractConstant(
1468 cconst.name,
1469 cconst.typeConstraint,
1470 cconst.isTypeconst
1472 } else {
1473 needs86cinit |= cconst.val->m_type == KindOfUninit;
1475 pce->addConstant(
1476 cconst.name,
1477 cconst.typeConstraint,
1478 &cconst.val.value(),
1479 cconst.phpCode,
1480 cconst.isTypeconst
1485 for (auto& m : cls.methods) {
1486 if (!needs86cinit && m->name == s_86cinit.get()) continue;
1487 FTRACE(2, " method: {}\n", m->name->data());
1488 auto const fe = ue.newMethodEmitter(m->name, pce);
1489 emit_init_func(*fe, *m);
1490 pce->addMethod(fe);
1491 auto const info = emit_bytecode(state, ue, *m);
1492 emit_finish_func(state, *m, *fe, info);
1495 CompactVector<Type> useVars;
1496 if (is_closure(cls)) {
1497 auto f = find_method(&cls, s_invoke.get());
1498 useVars = state.index.lookup_closure_use_vars(f, true);
1500 auto uvIt = useVars.begin();
1502 auto const privateProps = state.index.lookup_private_props(&cls, true);
1503 auto const privateStatics = state.index.lookup_private_statics(&cls, true);
1504 for (auto& prop : cls.properties) {
1505 auto const makeRat = [&] (const Type& ty) -> RepoAuthType {
1506 if (!ty.subtypeOf(BGen)) return RepoAuthType{};
1507 if (ty.subtypeOf(BBottom)) {
1508 // A property can be TBottom if no sets (nor its initial value) is
1509 // compatible with its type-constraint, or if its LateInit and there's
1510 // no sets to it. The repo auth type here doesn't particularly matter,
1511 // since such a prop will be inaccessible.
1512 return RepoAuthType{};
1515 auto const rat = make_repo_type(*state.index.array_table_builder(), ty);
1516 merge_repo_auth_type(ue, rat);
1517 return rat;
1520 auto const privPropTy = [&] (const PropState& ps) -> Type {
1521 if (is_closure(cls)) {
1522 // For closures use variables will be the first properties of the
1523 // closure object, in declaration order
1524 if (uvIt != useVars.end()) return *uvIt++;
1525 return Type{};
1528 auto it = ps.find(prop.name);
1529 if (it == end(ps)) return Type{};
1530 return it->second.ty;
1533 Type propTy;
1534 auto const attrs = prop.attrs;
1535 if (attrs & AttrPrivate) {
1536 propTy = privPropTy((attrs & AttrStatic) ? privateStatics : privateProps);
1537 } else if ((attrs & AttrPublic) && (attrs & AttrStatic)) {
1538 propTy = state.index.lookup_public_static(Context{}, &cls, prop.name);
1541 pce->addProperty(
1542 prop.name,
1543 prop.attrs,
1544 prop.userType,
1545 prop.typeConstraint,
1546 prop.docComment,
1547 &prop.val,
1548 makeRat(propTy),
1549 prop.userAttributes
1552 assert(uvIt == useVars.end());
1554 pce->setEnumBaseTy(cls.enumBaseTy);
1557 void emit_typealias(UnitEmitter& ue, const php::TypeAlias& alias) {
1558 auto const id = ue.addTypeAlias(alias);
1559 ue.pushMergeableTypeAlias(HPHP::Unit::MergeKind::TypeAlias, id);
1562 //////////////////////////////////////////////////////////////////////
1566 std::unique_ptr<UnitEmitter> emit_unit(const Index& index,
1567 const php::Unit& unit) {
1568 Trace::Bump bumper{
1569 Trace::hhbbc_emit, kSystemLibBump, is_systemlib_part(unit)
1572 assert(check(unit));
1574 auto ue = std::make_unique<UnitEmitter>(unit.sha1,
1575 Native::s_noNativeFuncs,
1576 true);
1577 FTRACE(1, " unit {}\n", unit.filename->data());
1578 ue->m_filepath = unit.filename;
1579 ue->m_isHHFile = unit.isHHFile;
1580 ue->m_useStrictTypes = unit.useStrictTypes;
1581 ue->m_metaData = unit.metaData;
1582 ue->m_fileAttributes = unit.fileAttributes;
1584 EmitUnitState state { index, &unit };
1585 state.classOffsets.resize(unit.classes.size(), kInvalidOffset);
1587 emit_pseudomain(state, *ue, unit);
1589 std::vector<std::unique_ptr<FuncEmitter> > top_fes;
1591 * Top level funcs are always defined when the unit is loaded, and
1592 * don't have a DefFunc bytecode. Process them up front.
1594 for (size_t id = 0; id < unit.funcs.size(); ++id) {
1595 auto const f = unit.funcs[id].get();
1596 assertx(f != unit.pseudomain.get());
1597 if (!f->top) continue;
1598 top_fes.push_back(std::make_unique<FuncEmitter>(*ue, -1, -1, f->name));
1599 emit_func(state, *ue, top_fes.back().get(), *f);
1603 * Find any top-level classes that need to be included due to
1604 * hoistability, even though the corresponding DefCls was not
1605 * reachable.
1607 for (size_t id = 0; id < unit.classes.size(); ++id) {
1608 if (state.classOffsets[id] != kInvalidOffset) continue;
1609 auto const c = unit.classes[id].get();
1610 if (c->hoistability != PreClass::MaybeHoistable &&
1611 c->hoistability != PreClass::AlwaysHoistable) {
1612 continue;
1614 // Closures are AlwaysHoistable; but there's no need to include
1615 // them unless there's a reachable CreateCl.
1616 if (is_closure(*c)) continue;
1617 recordClass(state, *ue, id);
1620 size_t pceId = 0, feId = 0;
1621 do {
1622 // Note that state.pceInfo can grow inside the loop
1623 while (pceId < state.pceInfo.size()) {
1624 auto const& pceInfo = state.pceInfo[pceId++];
1625 auto const& c = unit.classes[pceInfo.origId];
1626 emit_class(state, *ue, pceInfo.pce,
1627 state.classOffsets[pceInfo.origId], *c);
1630 while (feId < state.feInfo.size()) {
1631 auto const& feInfo = state.feInfo[feId++];
1632 // DefFunc ids are off by one wrt unit.funcs because we don't
1633 // store the pseudomain there.
1634 auto const& f = unit.funcs[feInfo.origId - 1];
1635 emit_func(state, *ue, feInfo.fe, *f);
1637 } while (pceId < state.pceInfo.size());
1639 for (auto tid : state.typeAliasInfo) {
1640 emit_typealias(*ue, *unit.typeAliases[tid]);
1643 for (size_t id = 0; id < unit.records.size(); ++id) {
1644 emit_record(*ue, *unit.records[id]);
1647 // Top level funcs need to go after any non-top level funcs. See
1648 // Unit::merge for details.
1649 for (auto& fe : top_fes) ue->appendTopEmitter(std::move(fe));
1651 return ue;
1654 //////////////////////////////////////////////////////////////////////