Remove dead includes in hphp/runtime/vm
[hiphop-php.git] / hphp / runtime / vm / jit / reg-alloc.cpp
blob41c26fd295c49637ea1a5f1085b63ad05ae48db5
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/reg-alloc.h"
19 #include "hphp/runtime/vm/jit/abi.h"
20 #include "hphp/runtime/vm/jit/abi-arm.h"
21 #include "hphp/runtime/vm/jit/irlower.h"
22 #include "hphp/runtime/vm/jit/minstr-effects.h"
23 #include "hphp/runtime/vm/jit/native-calls.h"
24 #include "hphp/runtime/vm/jit/vasm-instr.h"
25 #include "hphp/runtime/vm/jit/vasm-print.h"
26 #include "hphp/runtime/vm/jit/vasm-unit.h"
27 #include "hphp/runtime/vm/jit/vasm-util.h"
29 #include "hphp/runtime/base/packed-array.h"
31 #include "hphp/util/arch.h"
33 #include <boost/dynamic_bitset.hpp>
35 namespace HPHP { namespace jit {
38 TRACE_SET_MOD(hhir);
40 //////////////////////////////////////////////////////////////////////
42 namespace {
45 * Return true if this instruction can load a TypedValue using a 16-byte load
46 * into a SIMD register.
48 bool loadsCell(const IRInstruction& inst) {
49 auto const arch_allows = [] {
50 switch (arch()) {
51 case Arch::X64: return true;
52 case Arch::ARM: return true;
53 case Arch::PPC64: return true;
55 not_reached();
56 }();
58 switch (inst.op()) {
59 case LdMem:
60 return arch_allows && (!wide_tv_val || inst.src(0)->isA(TPtrToCell));
62 case LdVecElem:
63 case LdPackedElem:
64 static_assert(PackedArray::stores_typed_values, "");
65 return arch_allows;
67 case LdStk:
68 case LdLoc:
69 case LdContField:
70 case InitClsCns:
71 case CGetProp:
72 case ArrayGet:
73 case DictGet:
74 case DictGetQuiet:
75 case DictGetK:
76 case KeysetGet:
77 case KeysetGetQuiet:
78 case KeysetGetK:
79 case MapGet:
80 case CGetElem:
81 case ArrayIdx:
82 case DictIdx:
83 case KeysetIdx:
84 case MemoGetStaticValue:
85 case MemoGetStaticCache:
86 case MemoGetLSBValue:
87 case MemoGetLSBCache:
88 case MemoGetInstanceValue:
89 case MemoGetInstanceCache:
90 return arch_allows;
92 default:
93 return false;
98 * Returns true if the instruction can store source operand srcIdx to
99 * memory as a cell using a 16-byte store. (implying its okay to
100 * clobber TypedValue.m_aux)
102 bool storesCell(const IRInstruction& inst, uint32_t srcIdx) {
103 switch (arch()) {
104 case Arch::X64: break;
105 case Arch::ARM: break;
106 case Arch::PPC64: return false;
109 // If this function returns true for an operand, then the register allocator
110 // may give it an XMM register, and the instruction will store the whole 16
111 // bytes into memory. Therefore it's important *not* to return true if the
112 // TypedValue.m_aux field in memory has important data. This is the case for
113 // MixedArray elements, and Map elements.
114 switch (inst.op()) {
115 case StLoc:
116 return srcIdx == 1;
117 case StStk:
118 return srcIdx == 1;
119 case StClsInitElem:
120 return srcIdx == 1;
121 case InitPackedLayoutArray:
122 return srcIdx == 1;
123 case StMem:
124 return srcIdx == 1 && (!wide_tv_val || inst.src(0)->isA(TPtrToCell));
125 default:
126 return false;
132 //////////////////////////////////////////////////////////////////////
134 PhysReg forceAlloc(const SSATmp& tmp) {
135 if (tmp.type() <= TBottom) return InvalidReg;
137 auto inst = tmp.inst();
138 auto opc = inst->op();
140 // LdContActRec and LdAFWHActRec, loading a generator's AR, is the only time
141 // we have a pointer to an AR that is not in rvmfp().
142 if (opc != LdContActRec && opc != LdAFWHActRec && tmp.isA(TFramePtr)) {
143 return rvmfp();
146 return InvalidReg;
149 // Assign virtual registers to all SSATmps used or defined in reachable
150 // blocks. This assigns a value register to constants defined by DefConst,
151 // because some HHIR instructions require them. Ordinary Gen values with
152 // a known DataType only get one register. Assign "wide" locations when
153 // possible (when all uses and defs can be wide). These will be assigned
154 // SIMD registers later.
155 void assignRegs(const IRUnit& unit, Vunit& vunit, irlower::IRLS& state,
156 const BlockList& blocks) {
157 // visit instructions to find tmps eligible to use SIMD registers
158 auto const try_wide = RuntimeOption::EvalHHIRAllocSIMDRegs;
159 boost::dynamic_bitset<> not_wide(unit.numTmps());
160 StateVector<SSATmp,SSATmp*> tmps(unit, nullptr);
161 for (auto block : blocks) {
162 for (auto& inst : *block) {
163 for (uint32_t i = 0, n = inst.numSrcs(); i < n; i++) {
164 auto s = inst.src(i);
165 tmps[s] = s;
166 if (!try_wide || !storesCell(inst, i)) {
167 not_wide.set(s->id());
170 for (auto& d : inst.dsts()) {
171 tmps[d] = d;
172 if (!try_wide || inst.isControlFlow() || !loadsCell(inst)) {
173 not_wide.set(d->id());
178 // visit each tmp, assign 1 or 2 registers to each.
179 for (auto tmp : tmps) {
180 if (!tmp) continue;
181 auto forced = forceAlloc(*tmp);
182 if (forced != InvalidReg) {
183 state.locs[tmp] = Vloc{forced};
184 UNUSED Reg64 r = forced;
185 FTRACE(kVasmRegAllocDetailLevel,
186 "force t{} in {}\n", tmp->id(), reg::regname(r));
187 continue;
189 if (tmp->inst()->is(DefConst)) {
190 auto const loc = make_const(vunit, tmp->type());
191 state.locs[tmp] = loc;
192 FTRACE(kVasmRegAllocDetailLevel, "const t{} in %{}\n", tmp->id(),
193 size_t(loc.reg(0)), size_t(loc.reg(1)));
194 } else {
195 if (tmp->numWords() == 2) {
196 if (!not_wide.test(tmp->id())) {
197 auto r = vunit.makeReg();
198 state.locs[tmp] = Vloc{Vloc::kWide, r};
199 FTRACE(kVasmRegAllocDetailLevel,
200 "def t{} in wide %{}\n", tmp->id(), size_t(r));
201 } else {
202 auto data = vunit.makeReg();
203 auto type = vunit.makeReg();
204 state.locs[tmp] = Vloc{data, type};
205 FTRACE(kVasmRegAllocDetailLevel,
206 "def t{} in %{},%{}\n", tmp->id(),
207 size_t(data), size_t(type));
209 } else {
210 auto data = vunit.makeReg();
211 state.locs[tmp] = Vloc{data};
212 FTRACE(kVasmRegAllocDetailLevel,
213 "def t{} in %{}\n", tmp->id(), size_t(data));
219 void getEffects(const Abi& abi, const Vinstr& i,
220 RegSet& uses, RegSet& across, RegSet& defs) {
221 uses = defs = across = RegSet();
222 switch (i.op) {
223 case Vinstr::call:
224 case Vinstr::callm:
225 case Vinstr::callr:
226 case Vinstr::calls:
227 defs = abi.all() - (abi.calleeSaved | rvmfp());
228 break;
230 case Vinstr::callstub:
231 defs =
232 (abi.all() - (abi.calleeSaved | rvmfp()))
233 | jit::abi(CodeKind::CrossTrace).unreserved();
234 break;
236 case Vinstr::callfaststub:
237 defs = abi.all() - abi.calleeSaved - abi.gpUnreserved;
238 break;
240 case Vinstr::callphp:
241 defs = abi.all() - RegSet(rvmtl());
242 break;
244 case Vinstr::callunpack:
245 case Vinstr::contenter:
246 defs = abi.all() - (rvmfp() | rvmtl());
247 break;
249 case Vinstr::cqo:
250 uses = RegSet(reg::rax);
251 defs = reg::rax | reg::rdx;
252 break;
253 case Vinstr::idiv:
254 uses = defs = reg::rax | reg::rdx;
255 break;
256 case Vinstr::shlq:
257 case Vinstr::sarq:
258 across = RegSet(reg::rcx);
259 break;
261 case Vinstr::vcall:
262 case Vinstr::vinvoke:
263 case Vinstr::vcallunpack:
264 always_assert(false && "Unsupported instruction in vxls");
266 default:
267 break;
271 //////////////////////////////////////////////////////////////////////