2 +----------------------------------------------------------------------+
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
) {
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
:
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
}};
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 ///////////////////////////////////////////////////////////////////////////////
124 ///////////////////////////////////////////////////////////////////////////////
127 * Materialize `data' into a Vreg and return it.
129 inline Vreg
materialize(Vout
& v
, Vptr data
) {
130 auto const t
= v
.makeReg();
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();
143 case LayoutTest::And1Byte
:
144 v
<< testbim
{imm
, r
[ArrayData::offsetOfBespokeIndex() + 1], sf
};
146 case LayoutTest::And2Byte
:
147 v
<< testwim
{imm
, r
[ArrayData::offsetOfBespokeIndex()], sf
};
149 case LayoutTest::Cmp1Byte
:
150 v
<< cmpbim
{imm
, r
[ArrayData::offsetOfBespokeIndex() + 1], sf
};
152 case LayoutTest::Cmp2Byte
:
153 v
<< cmpwim
{imm
, r
[ArrayData::offsetOfBespokeIndex()], sf
};
158 auto const doneBlock
= v
.makeBlock();
164 * Test whether the value given by `dataSrc' has the same type specialization
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
,
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
);
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();
192 emitCmpLowPtr(v
, sf
, type
.clsSpec().cls(),
193 data
[ObjectData::getVMClassOffset()]);
195 v
<< cmpq
{v
.cns(type
.clsSpec().cls()), data
, sf
};
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
};
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());
230 !type
.subtypeOfAny(TCountedStr
, TPersistentArrLike
),
231 "Unsupported type in emitTypeTest(): {}", type
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
);
251 auto const cmp
= [&] (DataType kind
, ConditionCode cc
) {
252 emitCmpTVType(v
, sf
, kind
, typeSrc
);
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
);
269 v
<< cmpbi
{static_cast<data_type_t
>(KindOfUninit
), rtype
, sf
};
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
};
283 // All other valid types must not be unions.
284 always_assert_flog(false, "Uncheckable type: {}", type
);
289 if (type
.isSpecialized() || type
== TStaticStr
) {
290 detail::emitSpecializedTypeTest(v
, env
, type
, dataSrc
, doJcc
);
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 ///////////////////////////////////////////////////////////////////////////////