Add sub-controls for Hack array compat runtime checks
[hiphop-php.git] / hphp / runtime / vm / jit / ir-unit.cpp
blob7202c007eea3a29acdacb9597ee1bf1471420081
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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/ir-unit.h"
19 #include "hphp/runtime/vm/jit/block.h"
20 #include "hphp/runtime/vm/jit/cfg.h"
21 #include "hphp/runtime/vm/jit/frame-state.h"
22 #include "hphp/util/timer.h"
24 namespace HPHP { namespace jit {
25 ///////////////////////////////////////////////////////////////////////////////
27 TRACE_SET_MOD(hhir);
29 ///////////////////////////////////////////////////////////////////////////////
31 IRUnit::IRUnit(TransContext context) : m_context(context)
33 // Setup m_entry after property initialization, since it depends on
34 // the value of m_defHint.
35 // For Optimize translations, the entry block's profCount is
36 // adjusted later in translateRegion.
37 m_entry = defBlock();
38 m_startNanos = HPHP::Timer::GetThreadCPUTimeNanos();
41 void IRUnit::initLogEntry(const Func* func) {
42 if (func ? func->shouldSampleJit() :
43 StructuredLog::coinflip(RuntimeOption::EvalJitSampleRate)) {
44 m_logEntry.emplace();
48 IRInstruction* IRUnit::defLabel(unsigned numDst, BCContext bcctx) {
49 IRInstruction inst(DefLabel, bcctx);
50 auto const label = clone(&inst);
51 if (numDst > 0) {
52 auto const dstsPtr = new (m_arena) SSATmp*[numDst];
53 for (unsigned i = 0; i < numDst; ++i) {
54 dstsPtr[i] = newSSATmp(label);
56 label->setDsts(numDst, dstsPtr);
58 return label;
61 void IRUnit::expandLabel(IRInstruction* label, unsigned extraDst) {
62 assertx(label->is(DefLabel));
63 assertx(extraDst > 0);
64 auto const dstsPtr = new (m_arena) SSATmp*[extraDst + label->numDsts()];
65 unsigned i = 0;
66 for (auto dst : label->dsts()) {
67 dstsPtr[i++] = dst;
69 for (unsigned j = 0; j < extraDst; j++) {
70 dstsPtr[i++] = newSSATmp(label);
72 label->setDsts(i, dstsPtr);
75 void IRUnit::expandJmp(IRInstruction* jmp, SSATmp* value) {
76 assertx(jmp->is(Jmp));
77 auto const newSrcs = new (m_arena) SSATmp*[jmp->numSrcs() + 1];
78 size_t i = 0;
79 for (auto src : jmp->srcs()) {
80 newSrcs[i++] = src;
82 newSrcs[i++] = value;
83 jmp->setSrcs(i, newSrcs);
86 Block* IRUnit::defBlock(uint64_t profCount /* =1 */,
87 Block::Hint hint /* =Neither */ ) {
88 FTRACE(2, "IRUnit defining B{}\n", m_nextBlockId);
89 auto const block = new (m_arena) Block(m_nextBlockId++, profCount);
90 if (hint == Block::Hint::Neither) {
91 hint = m_defHint;
93 block->setHint(hint);
94 return block;
97 SSATmp* IRUnit::cns(Type type) {
98 assertx(type.hasConstVal() ||
99 type.subtypeOfAny(TUninit, TInitNull, TNullptr));
100 IRInstruction inst(DefConst, BCContext{});
101 inst.setTypeParam(type);
102 if (SSATmp* tmp = m_constTable.lookup(&inst)) {
103 assertx(tmp->type() == type);
104 return tmp;
106 return m_constTable.insert(clone(&inst)->dst());
109 ///////////////////////////////////////////////////////////////////////////////
112 * Returns true iff `block' ends the IR unit after finishing execution
113 * of the bytecode instruction at `sk'.
115 static bool endsUnitAtSrcKey(const Block* block, SrcKey sk) {
116 if (!block->isExitNoThrow()) return false;
118 const auto& inst = block->back();
119 const auto instSk = inst.marker().sk();
121 switch (inst.op()) {
122 // These instructions end a unit after executing the bytecode
123 // instruction they correspond to.
124 case InterpOneCF:
125 case JmpSSwitchDest:
126 case JmpSwitchDest:
127 case RaiseError:
128 case ThrowOutOfBounds:
129 case ThrowInvalidArrayKey:
130 case ThrowInvalidOperation:
131 case ThrowArithmeticError:
132 case ThrowDivisionByZeroError:
133 case VerifyParamFailHard:
134 case VerifyRetFailHard:
135 case Unreachable:
136 case EndBlock:
137 case FatalMissingThis:
138 return instSk == sk;
140 // The RetCtrl is generally ending a bytecode instruction, with the
141 // exception being in an Await bytecode instruction, where we consider the
142 // end of the bytecode instruction to be the non-suspending path.
143 case RetCtrl: {
144 auto const op = inst.marker().sk().op();
145 return op != Op::Await && op != Op::AwaitAll && op != Op::FCallAwait;
148 case AsyncFuncRet:
149 case AsyncFuncRetSlow:
150 return true;
152 // A ReqBindJmp ends a unit and it jumps to the next instruction to
153 // execute.
154 case ReqBindJmp: {
155 auto destOffset = inst.extra<ReqBindJmp>()->target.offset();
156 return sk.succOffsets().count(destOffset);
159 default:
160 return false;
164 Block* findMainExitBlock(const IRUnit& unit, SrcKey lastSk) {
165 bool unreachable = false;
166 Block* mainExit = nullptr;
168 FTRACE(5, "findMainExitBlock: looking for exit at {} in unit:\n{}\n",
169 showShort(lastSk), show(unit));
171 for (auto block : rpoSortCfg(unit)) {
172 if (block->back().is(Unreachable)) unreachable = true;
174 if (endsUnitAtSrcKey(block, lastSk)) {
175 if (mainExit == nullptr) {
176 mainExit = block;
177 continue;
180 always_assert_flog(
181 mainExit->hint() == Block::Hint::Unlikely ||
182 block->hint() == Block::Hint::Unlikely,
183 "findMainExit: 2 likely exits found: B{} and B{}\nlastSk = {}",
184 mainExit->id(), block->id(), showShort(lastSk)
187 if (mainExit->hint() == Block::Hint::Unlikely) mainExit = block;
191 always_assert_flog(
192 mainExit || unreachable,
193 "findMainExit: no exit found for lastSk = {}",
194 showShort(lastSk)
197 FTRACE(5, "findMainExitBlock: mainExit = B{}\n", mainExit->id());
199 return mainExit;
202 ///////////////////////////////////////////////////////////////////////////////