Store num args instead of offset in prologue and func entry SrcKeys
[hiphop-php.git] / hphp / runtime / vm / jit / irlower-internal-inl.h
blob31ca61a0c239ca84db303d88b3202024efc394c2
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/base/array-data.h"
18 #include "hphp/runtime/base/attr.h"
19 #include "hphp/runtime/base/bespoke-array.h"
20 #include "hphp/runtime/base/datatype.h"
21 #include "hphp/runtime/base/header-kind.h"
22 #include "hphp/runtime/base/object-data.h"
24 #include "hphp/runtime/vm/jit/arg-group.h"
25 #include "hphp/runtime/vm/jit/code-gen-helpers.h"
26 #include "hphp/runtime/vm/jit/ir-instruction.h"
27 #include "hphp/runtime/vm/jit/ir-opcode.h"
28 #include "hphp/runtime/vm/jit/irlower.h"
29 #include "hphp/runtime/vm/jit/guard-type-profile.h"
30 #include "hphp/runtime/vm/jit/type.h"
31 #include "hphp/runtime/vm/jit/type-specialization.h"
32 #include "hphp/runtime/vm/jit/vasm.h"
33 #include "hphp/runtime/vm/jit/vasm-gen.h"
34 #include "hphp/runtime/vm/jit/vasm-instr.h"
35 #include "hphp/runtime/vm/jit/vasm-reg.h"
37 #include "hphp/util/asm-x64.h"
39 namespace HPHP::jit::irlower {
41 ///////////////////////////////////////////////////////////////////////////////
43 inline Vout& vmain(IRLS& env) { assertx(env.vmain); return *env.vmain; }
44 inline Vout& vcold(IRLS& env) { assertx(env.vcold); return *env.vcold; }
46 inline Vlabel label(IRLS& env, Block* b) { return env.labels[b]; }
48 inline Vloc tmpLoc(IRLS& env, const SSATmp* tmp) {
49 return env.locs[tmp];
52 inline Vloc srcLoc(IRLS& env, const IRInstruction* inst, unsigned i) {
53 return tmpLoc(env, inst->src(i));
56 inline Vloc dstLoc(IRLS& env, const IRInstruction* inst, unsigned i) {
57 return tmpLoc(env, inst->dst(i));
60 inline ArgGroup argGroup(IRLS& env, const IRInstruction* inst) {
61 return ArgGroup(inst, env.locs);
64 inline CallDest callDest(Vreg reg0) {
65 return { DestType::SSA, reg0 };
68 inline CallDest callDest(Vreg reg0, Vreg reg1) {
69 return { DestType::TV, reg0, reg1 };
72 inline CallDest callDest(IRLS& env, const IRInstruction* inst) {
73 if (inst->numDsts() == 0) return kVoidDest;
74 assertx(inst->numDsts() == 1);
75 if (inst->dst()->isA(TBottom)) return kVoidDest;
77 auto const loc = dstLoc(env, inst, 0);
78 DEBUG_ONLY auto const maybe_lval = inst->dst()->type().maybe(TLval);
79 assertx(loc.numAllocated() == 1 || (maybe_lval && loc.numAllocated() == 2));
80 assertx(IMPLIES(maybe_lval, inst->dst()->isA(TLval|TNullptr)));
82 auto const dst = inst->dst();
83 auto const kind = dst->isA(TBool) ? DestType::Byte :
84 dst->isA(TDbl) ? DestType::Dbl :
85 DestType::SSA;
87 return { kind, dst->type(), loc.reg(0), loc.reg(1) };
90 inline CallDest callDestTV(IRLS& env, const IRInstruction* inst) {
91 assertx(inst->numDsts() == 1);
93 auto const loc = dstLoc(env, inst, 0);
94 assertx(loc.numAllocated() == 1 || loc.numAllocated() == 2);
96 if (loc.isFullSIMD()) {
97 assertx(loc.numAllocated() == 1);
98 return { DestType::SIMD, TCell, loc.reg(0) };
101 // loc.reg(1) may be InvalidReg, if the type is statically known. This is
102 // expected and handled by users of CallDest.
103 return { DestType::TV, TCell, loc.reg(0), loc.reg(1) };
106 ///////////////////////////////////////////////////////////////////////////////
108 inline void fwdJcc(Vout& v, IRLS& env, ConditionCode cc,
109 Vreg sf, Vlabel target) {
110 auto const next = v.makeBlock();
111 v << jcc{cc, sf, {next, target}};
112 v = next;
115 inline void fwdJcc(Vout& v, IRLS& env, ConditionCode cc,
116 Vreg sf, Block* target) {
117 fwdJcc(v, env, cc, sf, label(env, target));
120 ///////////////////////////////////////////////////////////////////////////////
122 namespace detail {
124 ///////////////////////////////////////////////////////////////////////////////
127 * Materialize `data' into a Vreg and return it.
129 inline Vreg materialize(Vout& v, Vptr data) {
130 auto const t = v.makeReg();
131 v << load{data, t};
132 return t;
134 inline Vreg materialize(Vout&, Vreg data) { return data; }
136 template <class JmpFn>
137 void emitBespokeLayoutTest(Vout& v, ArrayLayout layout, Vreg r, JmpFn doJcc) {
138 auto const test = layout.bespokeLayoutTest();
139 auto const imm = static_cast<int16_t>(test.imm);
140 auto const sf = v.makeReg();
142 switch (test.mode) {
143 case LayoutTest::And1Byte:
144 v << testbim{imm, r[ArrayData::offsetOfBespokeIndex() + 1], sf};
145 break;
146 case LayoutTest::And2Byte:
147 v << testwim{imm, r[ArrayData::offsetOfBespokeIndex()], sf};
148 break;
149 case LayoutTest::Cmp1Byte:
150 v << cmpbim{imm, r[ArrayData::offsetOfBespokeIndex() + 1], sf};
151 break;
152 case LayoutTest::Cmp2Byte:
153 v << cmpwim{imm, r[ArrayData::offsetOfBespokeIndex()], sf};
154 break;
157 doJcc(CC_Z, sf);
158 auto const doneBlock = v.makeBlock();
159 v << jmp{doneBlock};
160 v = doneBlock;
164 * Test whether the value given by `dataSrc' has the same type specialization
165 * as `type' does.
167 * Assumes that the DataType corresponding to `dataSrc' already matches `type'.
169 template <class Loc, class JmpFn>
170 void emitSpecializedTypeTest(Vout& v, IRLS& /*env*/, Type type, Loc dataSrc,
171 JmpFn doJcc) {
172 if (type < TRes) {
173 // No cls field in Resource.
174 always_assert(false && "unexpected guard on specialized Resource");
177 if (type == TStaticStr) {
178 auto const sf = emitCmpRefCount(v, StaticValue, dataSrc);
179 doJcc(CC_E, sf);
180 return;
183 if (type < TObj || type < TCls) {
184 // Emit the specific class test.
185 assertx(type.clsSpec());
186 assertx(type.clsSpec().exact() ||
187 type.clsSpec().cls()->attrs() & AttrNoOverride);
189 auto const data = materialize(v, dataSrc);
190 auto const sf = v.makeReg();
191 if (type < TObj) {
192 emitCmpLowPtr(v, sf, type.clsSpec().cls(),
193 data[ObjectData::getVMClassOffset()]);
194 } else {
195 v << cmpq{v.cns(type.clsSpec().cls()), data, sf};
197 doJcc(CC_E, sf);
198 return;
201 auto const spec = type.arrSpec();
202 assertx(allowBespokeArrayLikes());
203 assertx(!spec.type());
205 auto const r = materialize(v, dataSrc);
206 auto const layout = spec.layout();
207 if (layout == ArrayLayout::Vanilla() || layout == ArrayLayout::Bespoke()) {
208 auto const sf = v.makeReg();
209 auto const cc = layout == ArrayLayout::Vanilla() ? CC_Z : CC_NZ;
210 v << testbim{ArrayData::kBespokeKindMask, r[HeaderKindOffset], sf};
211 doJcc(cc, sf);
212 } else {
213 emitBespokeLayoutTest(v, layout, r, doJcc);
217 ///////////////////////////////////////////////////////////////////////////////
221 ///////////////////////////////////////////////////////////////////////////////
223 template<class Loc, class JmpFn>
224 void emitTypeTest(Vout& v, IRLS& env, Type type,
225 Loc typeSrc, Loc dataSrc, Vreg sf, JmpFn doJcc) {
226 // Note: If you add new supported type tests, you should update
227 // negativeCheckType() to indicate whether it is precise or not.
228 always_assert(!type.hasConstVal());
229 always_assert_flog(
230 !type.subtypeOfAny(TCountedStr, TPersistentArrLike),
231 "Unsupported type in emitTypeTest(): {}", type
234 // Nothing to check.
235 if (type == TCell) return;
237 // Profile the type being guarded. We skip TUncounted here because that's
238 // handled in emitIsTVTypeRefCounted, which has a number of other callers.
239 if (RuntimeOption::EvalJitProfileGuardTypes && type != TUncounted) {
240 emitProfileGuardType(v, type);
243 auto const cc = [&] {
244 auto const test = [&] (DataType kind) {
245 assertx(kind == dt_modulo_persistence(kind));
246 auto const mask = ~static_cast<data_type_t>(kind);
247 emitTestTVType(v, sf, mask, typeSrc);
248 return CC_E;
251 auto const cmp = [&] (DataType kind, ConditionCode cc) {
252 emitCmpTVType(v, sf, kind, typeSrc);
253 return cc;
256 auto const base = type.unspecialize();
257 if (type == TStaticStr) return test(KindOfString);
258 if (type.isKnownDataType()) return test(type.toDataType());
259 if (base == TArrLike) return cmp(KindOfKeyset, CC_BE);
260 if (type == (TVec|TDict)) return cmp(KindOfVec, CC_BE);
261 if (type == TNull) return cmp(KindOfUninit, CC_AE);
263 if (type == TUncountedInit) {
264 auto const rtype = emitGetTVType(v, typeSrc);
265 auto const sf2 = v.makeReg();
266 emitTestTVType(v, sf2, kRefCountedBit, rtype);
267 doJcc(CC_Z, sf2);
269 v << cmpbi{static_cast<data_type_t>(KindOfUninit), rtype, sf};
270 return CC_NZ;
273 if (type == TUncounted) {
274 return ccNegate(emitIsTVTypeRefCounted(v, sf, typeSrc));
277 if (type == TInitCell) {
278 auto const rtype = emitGetTVType(v, typeSrc);
279 v << cmpbi{static_cast<data_type_t>(KindOfUninit), rtype, sf};
280 return CC_NZ;
283 // All other valid types must not be unions.
284 always_assert_flog(false, "Uncheckable type: {}", type);
285 }();
287 doJcc(cc, sf);
289 if (type.isSpecialized() || type == TStaticStr) {
290 detail::emitSpecializedTypeTest(v, env, type, dataSrc, doJcc);
294 template<class Loc>
295 void emitTypeCheck(Vout& v, IRLS& env, Type type,
296 Loc typeSrc, Loc dataSrc, Block* taken) {
297 emitTypeTest(v, env, type, typeSrc, dataSrc, v.makeReg(),
298 [&] (ConditionCode cc, Vreg sf) {
299 fwdJcc(v, env, ccNegate(cc), sf, taken);
304 ///////////////////////////////////////////////////////////////////////////////