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/vm/jit/irlower-internal.h"
19 #include "hphp/runtime/base/array-data.h"
20 #include "hphp/runtime/base/collections.h"
21 #include "hphp/runtime/base/mixed-array.h"
22 #include "hphp/runtime/base/object-data.h"
23 #include "hphp/runtime/base/packed-array.h"
24 #include "hphp/runtime/base/set-array.h"
25 #include "hphp/runtime/base/string-data.h"
26 #include "hphp/runtime/base/type-array.h"
27 #include "hphp/runtime/base/type-variant.h"
28 #include "hphp/runtime/base/typed-value.h"
30 #include "hphp/runtime/vm/jit/types.h"
31 #include "hphp/runtime/vm/jit/abi.h"
32 #include "hphp/runtime/vm/jit/arg-group.h"
33 #include "hphp/runtime/vm/jit/array-kind-profile.h"
34 #include "hphp/runtime/vm/jit/call-spec.h"
35 #include "hphp/runtime/vm/jit/code-gen-cf.h"
36 #include "hphp/runtime/vm/jit/code-gen-helpers.h"
37 #include "hphp/runtime/vm/jit/code-gen-internal.h"
38 #include "hphp/runtime/vm/jit/ir-instruction.h"
39 #include "hphp/runtime/vm/jit/ir-opcode.h"
40 #include "hphp/runtime/vm/jit/ssa-tmp.h"
41 #include "hphp/runtime/vm/jit/translator-inline.h"
42 #include "hphp/runtime/vm/jit/type.h"
43 #include "hphp/runtime/vm/jit/vasm-gen.h"
44 #include "hphp/runtime/vm/jit/vasm-instr.h"
45 #include "hphp/runtime/vm/jit/vasm-reg.h"
47 #include "hphp/runtime/ext/std/ext_std_closure.h"
49 #include "hphp/util/asm-x64.h"
50 #include "hphp/util/trace.h"
52 namespace HPHP
{ namespace jit
{ namespace irlower
{
54 TRACE_SET_MOD(irlower
);
56 ///////////////////////////////////////////////////////////////////////////////
58 void profileArrayKindHelper(ArrayKindProfile
* profile
, ArrayData
* arr
) {
59 profile
->report(arr
->kind());
62 void cgProfileArrayKind(IRLS
& env
, const IRInstruction
* inst
) {
63 auto const extra
= inst
->extra
<RDSHandleData
>();
64 assertx(!rds::isPersistentHandle(extra
->handle
));
67 auto const profile
= v
.makeReg();
68 v
<< lea
{rvmtl()[extra
->handle
], profile
};
69 cgCallHelper(v
, env
, CallSpec::direct(profileArrayKindHelper
), kVoidDest
,
70 SyncOptions::None
, argGroup(env
, inst
).reg(profile
).ssa(0));
73 void cgCheckPackedArrayDataBounds(IRLS
& env
, const IRInstruction
* inst
) {
74 static_assert(ArrayData::sizeofSize() == 4, "");
76 // We may check packed array bounds on profiled arrays that we do not
77 // statically know have kPackedKind.
78 assertx(inst
->taken());
79 auto arr
= srcLoc(env
, inst
, 0).reg();
80 auto idx
= srcLoc(env
, inst
, 1).reg();
83 auto const size
= [&]{
84 auto const arrTmp
= inst
->src(0);
85 if (arrTmp
->hasConstVal(TArr
)) return v
.cns(arrTmp
->arrVal()->size());
86 if (arrTmp
->hasConstVal(TVec
)) return v
.cns(arrTmp
->vecVal()->size());
87 auto const at
= arrTmp
->type().arrSpec().type();
88 using A
= RepoAuthType::Array
;
89 if (at
&& at
->tag() == A::Tag::Packed
&& at
->emptiness() == A::Empty::No
) {
90 return v
.cns(at
->size());
92 // ArrayData::m_size is a uint32_t but we need to do a 64-bit comparison
93 // since idx is KindOfInt64.
94 auto const size
= v
.makeReg();
95 v
<< loadzlq
{arr
[ArrayData::offsetofSize()], size
};
99 auto const sf
= v
.makeReg();
100 v
<< cmpq
{idx
, size
, sf
};
101 v
<< jcc
{CC_BE
, sf
, {label(env
, inst
->next()), label(env
, inst
->taken())}};
104 IMPL_OPCODE_CALL(ArrayAdd
);
106 ///////////////////////////////////////////////////////////////////////////////
110 void implCountArrayLike(IRLS
& env
, const IRInstruction
* inst
) {
111 static_assert(ArrayData::sizeofSize() == 4, "");
112 auto const src
= srcLoc(env
, inst
, 0).reg();
113 auto const dst
= dstLoc(env
, inst
, 0).reg();
114 vmain(env
) << loadzlq
{src
[ArrayData::offsetofSize()], dst
};
119 IMPL_OPCODE_CALL(Count
)
121 void cgCountArray(IRLS
& env
, const IRInstruction
* inst
) {
122 auto const src
= srcLoc(env
, inst
, 0).reg();
123 auto const dst
= dstLoc(env
, inst
, 0).reg();
124 auto& v
= vmain(env
);
126 auto const d
= v
.makeReg();
127 auto const sf
= v
.makeReg();
128 v
<< loadl
{src
[ArrayData::offsetofSize()], d
};
129 v
<< testl
{d
, d
, sf
};
132 v
, vcold(env
), CC_S
, sf
, dst
,
134 auto const d
= v
.makeReg();
135 cgCallHelper(v
, env
, CallSpec::array(&g_array_funcs
.vsize
),
136 callDest(d
), SyncOptions::None
,
137 argGroup(env
, inst
).ssa(0));
140 [&](Vout
& /*v*/) { return d
; });
143 void cgCountArrayFast(IRLS
& env
, const IRInstruction
* inst
) {
144 implCountArrayLike(env
, inst
);
147 void cgCountVec(IRLS
& env
, const IRInstruction
* inst
) {
148 implCountArrayLike(env
, inst
);
151 void cgCountDict(IRLS
& env
, const IRInstruction
* inst
) {
152 implCountArrayLike(env
, inst
);
155 void cgCountShape(IRLS
& env
, const IRInstruction
* inst
) {
156 implCountArrayLike(env
, inst
);
159 void cgCountKeyset(IRLS
& env
, const IRInstruction
* inst
) {
160 implCountArrayLike(env
, inst
);
163 ///////////////////////////////////////////////////////////////////////////////
168 template <bool intishWarn
>
170 bool ak_exist_string_impl(const ArrayData
* arr
, const StringData
* key
) {
172 if (arr
->convertKey(key
, n
, intishWarn
)) {
173 return arr
->exists(n
);
175 return arr
->exists(key
);
180 template <bool intishWarn
>
181 bool ak_exist_string(const ArrayData
* arr
, const StringData
* key
) {
182 return ak_exist_string_impl
<intishWarn
>(arr
, key
);
185 bool ak_exist_int_obj(ObjectData
* obj
, int64_t key
) {
186 if (obj
->isCollection()) {
187 return collections::contains(obj
, key
);
188 } else if (obj
->instanceof(c_Closure::classof())) {
191 auto const arr
= obj
->toArray(false, true);
192 return arr
.get()->exists(key
);
195 bool ak_exist_string_obj(ObjectData
* obj
, StringData
* key
) {
196 if (obj
->isCollection()) {
197 return collections::contains(obj
, Variant
{key
});
198 } else if (obj
->instanceof(c_Closure::classof())) {
201 auto const arr
= obj
->toArray(false, true);
202 return ak_exist_string_impl
<false>(arr
.get(), key
);
205 void cgAKExistsArr(IRLS
& env
, const IRInstruction
* inst
) {
206 auto const arrTy
= inst
->src(0)->type();
207 auto const keyTy
= inst
->src(1)->type();
208 auto& v
= vmain(env
);
210 auto const keyInfo
= checkStrictlyInteger(arrTy
, keyTy
);
213 ? (checkHACIntishCast()
214 ? CallSpec::direct(ak_exist_string
<true>)
215 : CallSpec::direct(ak_exist_string
<false>))
216 : (keyInfo
.type
== KeyType::Int
217 ? CallSpec::array(&g_array_funcs
.existsInt
)
218 : CallSpec::array(&g_array_funcs
.existsStr
));
220 auto args
= argGroup(env
, inst
).ssa(0);
221 if (keyInfo
.converted
) {
222 args
.imm(keyInfo
.convertedInt
);
228 v
, env
, target
, callDest(env
, inst
),
229 RuntimeOption::EvalHackArrCompatNotices
230 ? SyncOptions::Sync
: SyncOptions::None
,
235 void cgAKExistsDict(IRLS
& env
, const IRInstruction
* inst
) {
236 auto const keyTy
= inst
->src(1)->type();
237 auto& v
= vmain(env
);
239 auto const target
= (keyTy
<= TInt
)
240 ? CallSpec::direct(MixedArray::ExistsIntDict
)
241 : CallSpec::direct(MixedArray::ExistsStrDict
);
243 cgCallHelper(v
, env
, target
, callDest(env
, inst
), SyncOptions::None
,
244 argGroup(env
, inst
).ssa(0).ssa(1));
247 void cgAKExistsKeyset(IRLS
& env
, const IRInstruction
* inst
) {
248 auto const keyTy
= inst
->src(1)->type();
249 auto& v
= vmain(env
);
251 auto const target
= (keyTy
<= TInt
)
252 ? CallSpec::direct(SetArray::ExistsInt
)
253 : CallSpec::direct(SetArray::ExistsStr
);
255 cgCallHelper(v
, env
, target
, callDest(env
, inst
), SyncOptions::None
,
256 argGroup(env
, inst
).ssa(0).ssa(1));
259 void cgAKExistsObj(IRLS
& env
, const IRInstruction
* inst
) {
260 auto const keyTy
= inst
->src(1)->type();
261 auto& v
= vmain(env
);
263 auto const target
= (keyTy
<= TInt
)
264 ? CallSpec::direct(ak_exist_int_obj
)
265 : CallSpec::direct(ak_exist_string_obj
);
267 cgCallHelper(v
, env
, target
, callDest(env
, inst
), SyncOptions::Sync
,
268 argGroup(env
, inst
).ssa(0).ssa(1));
271 ///////////////////////////////////////////////////////////////////////////////
274 IMPL_OPCODE_CALL(NewArray
)
275 IMPL_OPCODE_CALL(NewMixedArray
)
276 IMPL_OPCODE_CALL(NewLikeArray
)
277 IMPL_OPCODE_CALL(NewDictArray
)
278 IMPL_OPCODE_CALL(AllocPackedArray
)
279 IMPL_OPCODE_CALL(AllocVecArray
)
281 void cgNewDArray(IRLS
& env
, const IRInstruction
* inst
) {
285 CallSpec::direct(MixedArray::MakeReserveDArray
),
288 argGroup(env
, inst
).ssa(0)
292 void cgAllocVArray(IRLS
& env
, const IRInstruction
* inst
) {
293 auto const extra
= inst
->extra
<PackedArrayData
>();
297 CallSpec::direct(PackedArray::MakeUninitializedVArray
),
300 argGroup(env
, inst
).imm(extra
->size
)
306 void newStructImpl(IRLS
& env
,
307 const IRInstruction
* inst
,
308 MixedArray
* (*f
)(uint32_t,
309 const StringData
* const*,
312 auto const sp
= srcLoc(env
, inst
, 0).reg();
313 auto const extra
= inst
->extra
<NewStructData
>();
314 auto& v
= vmain(env
);
316 auto table
= v
.allocData
<const StringData
*>(extra
->numKeys
);
317 memcpy(table
, extra
->keys
, extra
->numKeys
* sizeof(*extra
->keys
));
319 auto const args
= argGroup(env
, inst
)
322 .addr(sp
, cellsToBytes(extra
->offset
.offset
));
324 cgCallHelper(v
, env
, CallSpec::direct(f
), callDest(env
, inst
),
325 SyncOptions::None
, args
);
330 void cgNewStructArray(IRLS
& env
, const IRInstruction
* inst
) {
331 newStructImpl(env
, inst
, MixedArray::MakeStruct
);
334 void cgNewStructDArray(IRLS
& env
, const IRInstruction
* inst
) {
335 newStructImpl(env
, inst
, MixedArray::MakeStructDArray
);
338 void cgNewStructDict(IRLS
& env
, const IRInstruction
* inst
) {
339 newStructImpl(env
, inst
, MixedArray::MakeStructDict
);
342 void cgNewKeysetArray(IRLS
& env
, const IRInstruction
* inst
) {
343 auto const sp
= srcLoc(env
, inst
, 0).reg();
344 auto const extra
= inst
->extra
<NewKeysetArray
>();
345 auto& v
= vmain(env
);
347 auto const args
= argGroup(env
, inst
)
349 .addr(sp
, cellsToBytes(extra
->offset
.offset
));
351 cgCallHelper(v
, env
, CallSpec::direct(SetArray::MakeSet
),
352 callDest(env
, inst
), SyncOptions::Sync
, args
);
355 void cgInitPackedLayoutArray(IRLS
& env
, const IRInstruction
* inst
) {
356 auto const arr
= srcLoc(env
, inst
, 0).reg();
357 auto const index
= inst
->extra
<InitPackedLayoutArray
>()->index
;
359 auto const slot_off
= PackedArray::entriesOffset() +
360 index
* sizeof(TypedValue
);
361 storeTV(vmain(env
), arr
[slot_off
], srcLoc(env
, inst
, 1), inst
->src(1));
364 void cgInitPackedLayoutArrayLoop(IRLS
& env
, const IRInstruction
* inst
) {
365 auto const arr
= srcLoc(env
, inst
, 0).reg();
366 auto const spIn
= srcLoc(env
, inst
, 1).reg();
367 auto const extra
= inst
->extra
<InitPackedLayoutArrayLoop
>();
368 auto const count
= safe_cast
<int>(extra
->size
);
369 auto& v
= vmain(env
);
371 auto const sp
= v
.makeReg();
372 v
<< lea
{spIn
[cellsToBytes(extra
->offset
.offset
)], sp
};
374 auto const i
= v
.cns(0);
375 auto const j
= v
.cns((count
- 1) * 2);
377 // We know that we have at least one element in the array so we don't have to
378 // do an initial bounds check.
381 doWhile(v
, CC_GE
, {i
, j
},
382 [&] (const VregList
& in
, const VregList
& out
) {
383 auto const i1
= in
[0], j1
= in
[1];
384 auto const i2
= out
[0], j2
= out
[1];
385 auto const sf
= v
.makeReg();
386 auto const value
= v
.makeReg();
388 // Load the value from the stack and store into the array. It's safe to
389 // copy all 16 bytes of the TV because packed arrays don't use m_aux.
390 v
<< loadups
{sp
[j1
* 8], value
};
391 v
<< storeups
{value
, arr
[i1
* 8] + PackedArray::entriesOffset()};
393 // Add 2 to the loop variable because we can only scale by at most 8.
395 v
<< subqi
{2, j1
, j2
, sf
};
401 ///////////////////////////////////////////////////////////////////////////////