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/array-init.h"
21 #include "hphp/runtime/base/array-provenance.h"
22 #include "hphp/runtime/base/collections.h"
23 #include "hphp/runtime/base/mixed-array.h"
24 #include "hphp/runtime/base/object-data.h"
25 #include "hphp/runtime/base/packed-array.h"
26 #include "hphp/runtime/base/packed-array-defs.h"
27 #include "hphp/runtime/base/record-data.h"
28 #include "hphp/runtime/base/set-array.h"
29 #include "hphp/runtime/base/string-data.h"
30 #include "hphp/runtime/base/type-array.h"
31 #include "hphp/runtime/base/type-variant.h"
32 #include "hphp/runtime/base/typed-value.h"
34 #include "hphp/runtime/vm/jit/types.h"
35 #include "hphp/runtime/vm/jit/abi.h"
36 #include "hphp/runtime/vm/jit/arg-group.h"
37 #include "hphp/runtime/vm/jit/call-spec.h"
38 #include "hphp/runtime/vm/jit/code-gen-cf.h"
39 #include "hphp/runtime/vm/jit/code-gen-helpers.h"
40 #include "hphp/runtime/vm/jit/ir-instruction.h"
41 #include "hphp/runtime/vm/jit/ir-opcode.h"
42 #include "hphp/runtime/vm/jit/ssa-tmp.h"
43 #include "hphp/runtime/vm/jit/translator-inline.h"
44 #include "hphp/runtime/vm/jit/type.h"
45 #include "hphp/runtime/vm/jit/vasm-gen.h"
46 #include "hphp/runtime/vm/jit/vasm-instr.h"
47 #include "hphp/runtime/vm/jit/vasm-reg.h"
49 #include "hphp/runtime/ext/std/ext_std_closure.h"
51 #include "hphp/util/asm-x64.h"
52 #include "hphp/util/trace.h"
54 namespace HPHP
{ namespace jit
{ namespace irlower
{
56 TRACE_SET_MOD(irlower
);
58 ///////////////////////////////////////////////////////////////////////////////
60 void cgCheckPackedArrayDataBounds(IRLS
& env
, const IRInstruction
* inst
) {
61 static_assert(ArrayData::sizeofSize() == 4, "");
63 // We may check packed array bounds on profiled arrays that we do not
64 // statically know have kPackedKind.
65 assertx(inst
->taken());
66 auto arr
= srcLoc(env
, inst
, 0).reg();
67 auto idx
= srcLoc(env
, inst
, 1).reg();
70 auto const size
= [&]{
71 auto const arrTmp
= inst
->src(0);
72 if (arrTmp
->hasConstVal()) return v
.cns(arrTmp
->arrLikeVal()->size());
73 auto const at
= arrTmp
->type().arrSpec().type();
74 using A
= RepoAuthType::Array
;
75 if (at
&& at
->tag() == A::Tag::Packed
&& at
->emptiness() == A::Empty::No
) {
76 return v
.cns(at
->size());
78 // ArrayData::m_size is a uint32_t but we need to do a 64-bit comparison
79 // since idx is KindOfInt64.
80 auto const size
= v
.makeReg();
81 v
<< loadzlq
{arr
[ArrayData::offsetofSize()], size
};
85 auto const sf
= v
.makeReg();
86 v
<< cmpq
{idx
, size
, sf
};
87 v
<< jcc
{CC_BE
, sf
, {label(env
, inst
->next()), label(env
, inst
->taken())}};
90 ////////////////////////////////////////////////////////////////////////////////
94 ArrayData
* setLegacyHelper(ArrayData
* arr
) {
95 if (arr
->cowCheck()) {
96 auto ad
= arr
->copy();
98 ad
->setLegacyArray(true);
101 arr
->setLegacyArray(true);
106 void setLegacyImpl(IRLS
& env
, const IRInstruction
* inst
) {
107 auto const args
= argGroup(env
, inst
).ssa(0);
109 cgCallHelper(vmain(env
),
111 CallSpec::direct(setLegacyHelper
),
119 void cgSetLegacyVec(IRLS
& env
, const IRInstruction
* inst
) {
120 setLegacyImpl(env
, inst
);
123 void cgSetLegacyDict(IRLS
& env
, const IRInstruction
* inst
) {
124 setLegacyImpl(env
, inst
);
127 ///////////////////////////////////////////////////////////////////////////////
131 void implCountArrayLike(IRLS
& env
, const IRInstruction
* inst
) {
132 static_assert(ArrayData::sizeofSize() == 4, "");
133 auto const src
= srcLoc(env
, inst
, 0).reg();
134 auto const dst
= dstLoc(env
, inst
, 0).reg();
135 vmain(env
) << loadzlq
{src
[ArrayData::offsetofSize()], dst
};
140 IMPL_OPCODE_CALL(Count
)
142 void cgCountArray(IRLS
& env
, const IRInstruction
* inst
) {
143 implCountArrayLike(env
, inst
);
146 void cgCountVec(IRLS
& env
, const IRInstruction
* inst
) {
147 implCountArrayLike(env
, inst
);
150 void cgCountDict(IRLS
& env
, const IRInstruction
* inst
) {
151 implCountArrayLike(env
, inst
);
154 void cgCountKeyset(IRLS
& env
, const IRInstruction
* inst
) {
155 implCountArrayLike(env
, inst
);
158 ///////////////////////////////////////////////////////////////////////////////
161 bool ak_exist_int_obj(ObjectData
* obj
, int64_t key
) {
162 if (obj
->isCollection()) {
163 return collections::contains(obj
, key
);
164 } else if (obj
->instanceof(c_Closure::classof())) {
167 auto const arr
= obj
->toArray(false, true);
168 return arr
.get()->exists(key
);
171 bool ak_exist_string_obj(ObjectData
* obj
, StringData
* key
) {
172 if (obj
->isCollection()) {
173 return collections::contains(obj
, Variant
{key
});
174 } else if (obj
->instanceof(c_Closure::classof())) {
177 auto const arr
= obj
->toArray(false, true);
178 return arr
.get()->exists(key
);
181 void cgAKExistsArr(IRLS
& env
, const IRInstruction
* inst
) {
182 auto const& arr_type
= inst
->src(0)->type();
183 auto const& key_type
= inst
->src(1)->type();
184 auto& v
= vmain(env
);
186 using ExistsInt
= bool (ArrayData::*)(int64_t) const;
187 using ExistsStr
= bool (ArrayData::*)(const StringData
*) const;
189 assertx(key_type
.subtypeOfAny(TInt
, TStr
));
190 auto const target
= (key_type
<= TInt
)
191 ? CallSpec::array(arr_type
, &g_array_funcs
.existsInt
,
192 static_cast<ExistsInt
>(&ArrayData::exists
))
193 : CallSpec::array(arr_type
, &g_array_funcs
.existsStr
,
194 static_cast<ExistsStr
>(&ArrayData::exists
));
196 cgCallHelper(v
, env
, target
, callDest(env
, inst
), SyncOptions::None
,
197 argGroup(env
, inst
).ssa(0).ssa(1));
200 void cgAKExistsDict(IRLS
& env
, const IRInstruction
* inst
) {
201 auto const keyTy
= inst
->src(1)->type();
202 auto& v
= vmain(env
);
204 auto const target
= (keyTy
<= TInt
)
205 ? CallSpec::direct(MixedArray::ExistsIntDict
)
206 : CallSpec::direct(MixedArray::ExistsStrDict
);
208 cgCallHelper(v
, env
, target
, callDest(env
, inst
), SyncOptions::None
,
209 argGroup(env
, inst
).ssa(0).ssa(1));
212 void cgAKExistsKeyset(IRLS
& env
, const IRInstruction
* inst
) {
213 auto const keyTy
= inst
->src(1)->type();
214 auto& v
= vmain(env
);
216 auto const target
= (keyTy
<= TInt
)
217 ? CallSpec::direct(SetArray::ExistsInt
)
218 : CallSpec::direct(SetArray::ExistsStr
);
220 cgCallHelper(v
, env
, target
, callDest(env
, inst
), SyncOptions::None
,
221 argGroup(env
, inst
).ssa(0).ssa(1));
224 void cgAKExistsObj(IRLS
& env
, const IRInstruction
* inst
) {
225 auto const keyTy
= inst
->src(1)->type();
226 auto& v
= vmain(env
);
228 auto const target
= (keyTy
<= TInt
)
229 ? CallSpec::direct(ak_exist_int_obj
)
230 : CallSpec::direct(ak_exist_string_obj
);
232 cgCallHelper(v
, env
, target
, callDest(env
, inst
), SyncOptions::Sync
,
233 argGroup(env
, inst
).ssa(0).ssa(1));
236 ///////////////////////////////////////////////////////////////////////////////
241 using MakeArrayFn
= ArrayData
*(uint32_t);
243 void implNewArray(IRLS
& env
, const IRInstruction
* inst
, MakeArrayFn target
,
244 SyncOptions sync
= SyncOptions::None
) {
245 cgCallHelper(vmain(env
), env
, CallSpec::direct(target
), callDest(env
, inst
),
246 sync
, argGroup(env
, inst
).ssa(0));
249 void implAllocArray(IRLS
& env
, const IRInstruction
* inst
, MakeArrayFn target
,
250 SyncOptions sync
= SyncOptions::None
) {
251 auto const extra
= inst
->extra
<PackedArrayData
>();
252 cgCallHelper(vmain(env
), env
, CallSpec::direct(target
), callDest(env
, inst
),
253 sync
, argGroup(env
, inst
).imm(extra
->size
));
258 void cgNewDictArray(IRLS
& env
, const IRInstruction
* inst
) {
259 implNewArray(env
, inst
, MixedArray::MakeReserveDict
);
261 void cgNewDArray(IRLS
& env
, const IRInstruction
* inst
) {
262 implNewArray(env
, inst
, MixedArray::MakeReserveDArray
);
265 void cgAllocVec(IRLS
& env
, const IRInstruction
* inst
) {
266 implAllocArray(env
, inst
, PackedArray::MakeUninitializedVec
);
268 void cgAllocVArray(IRLS
& env
, const IRInstruction
* inst
) {
269 implAllocArray(env
, inst
, PackedArray::MakeUninitializedVArray
);
276 const IRInstruction
* inst
,
277 MixedArray
* (*f
)(uint32_t, const StringData
* const*, const TypedValue
*),
278 SyncOptions sync
= SyncOptions::None
280 auto const sp
= srcLoc(env
, inst
, 0).reg();
281 auto const extra
= inst
->extra
<NewStructData
>();
282 auto& v
= vmain(env
);
284 auto table
= v
.allocData
<const StringData
*>(extra
->numKeys
);
285 memcpy(table
, extra
->keys
, extra
->numKeys
* sizeof(*extra
->keys
));
287 auto const args
= argGroup(env
, inst
)
290 .addr(sp
, cellsToBytes(extra
->offset
.offset
));
292 cgCallHelper(v
, env
, CallSpec::direct(f
), callDest(env
, inst
), sync
, args
);
297 void cgNewStructDArray(IRLS
& env
, const IRInstruction
* inst
) {
298 newStructImpl(env
, inst
, MixedArray::MakeStructDArray
);
301 void cgNewStructDict(IRLS
& env
, const IRInstruction
* inst
) {
303 env
, inst
, MixedArray::MakeStructDict
,
304 RuntimeOption::EvalArrayProvenance
? SyncOptions::Sync
: SyncOptions::None
308 void cgNewKeysetArray(IRLS
& env
, const IRInstruction
* inst
) {
309 auto const sp
= srcLoc(env
, inst
, 0).reg();
310 auto const extra
= inst
->extra
<NewKeysetArray
>();
311 auto& v
= vmain(env
);
313 auto const args
= argGroup(env
, inst
)
315 .addr(sp
, cellsToBytes(extra
->offset
.offset
));
317 cgCallHelper(v
, env
, CallSpec::direct(SetArray::MakeSet
),
318 callDest(env
, inst
), SyncOptions::Sync
, args
);
323 template<typename ArrayInit
>
324 void allocStructImpl(
326 const IRInstruction
* inst
,
327 MixedArray
* (*f
)(uint32_t, const int32_t*),
328 SyncOptions sync
= SyncOptions::None
330 arrprov::TagOverride ap_override
{arrprov::tagFromSK(inst
->marker().sk())};
331 auto const extra
= inst
->extra
<NewStructData
>();
332 auto init
= ArrayInit
{extra
->numKeys
};
333 for (auto i
= 0; i
< extra
->numKeys
; ++i
) {
334 init
.set(extra
->keys
[i
], make_tv
<KindOfNull
>());
336 auto const array
= init
.toArray();
337 auto const ad
= MixedArray::asMixed(array
.get());
339 auto const scale
= MixedArray::computeScaleFromSize(extra
->numKeys
);
340 always_assert(MixedArray::HashSize(scale
) == ad
->hashSize());
342 using HashTableEntry
= std::remove_pointer_t
<decltype(ad
->hashTab())>;
344 auto& v
= vmain(env
);
345 auto table
= v
.allocData
<HashTableEntry
>(ad
->hashSize());
346 memcpy(table
, ad
->hashTab(), ad
->hashSize() * sizeof(HashTableEntry
));
348 auto const args
= argGroup(env
, inst
).imm(extra
->numKeys
).dataPtr(table
);
349 cgCallHelper(v
, env
, CallSpec::direct(f
), callDest(env
, inst
), sync
, args
);
354 void cgAllocStructDArray(IRLS
& env
, const IRInstruction
* inst
) {
355 allocStructImpl
<DArrayInit
>(env
, inst
, MixedArray::AllocStructDArray
);
358 void cgAllocStructDict(IRLS
& env
, const IRInstruction
* inst
) {
359 allocStructImpl
<DictInit
>(
360 env
, inst
, MixedArray::AllocStructDict
,
361 RuntimeOption::EvalArrayProvenance
? SyncOptions::Sync
: SyncOptions::None
365 void cgInitMixedLayoutArray(IRLS
& env
, const IRInstruction
* inst
) {
366 auto const arr
= srcLoc(env
, inst
, 0).reg();
367 auto const key
= inst
->extra
<InitMixedLayoutArray
>()->key
;
368 auto const idx
= inst
->extra
<InitMixedLayoutArray
>()->index
;
370 auto const elm_off
= MixedArray::elmOff(idx
);
371 auto const key_ptr
= arr
[elm_off
+ MixedArrayElm::keyOff()];
372 auto const data_ptr
= arr
[elm_off
+ MixedArrayElm::dataOff()];
373 auto const hash_ptr
= arr
[elm_off
+ MixedArrayElm::hashOff()];
375 auto& v
= vmain(env
);
376 storeTV(v
, data_ptr
, srcLoc(env
, inst
, 1), inst
->src(1));
377 v
<< storeli
{ key
->hash(), hash_ptr
};
378 v
<< store
{ v
.cns(key
), key_ptr
};
381 void cgInitPackedLayoutArray(IRLS
& env
, const IRInstruction
* inst
) {
382 auto const arr
= srcLoc(env
, inst
, 0).reg();
383 auto const index
= inst
->extra
<InitPackedLayoutArray
>()->index
;
385 auto const slot_off
= PackedArray::entriesOffset() +
386 index
* sizeof(TypedValue
);
387 storeTV(vmain(env
), arr
[slot_off
], srcLoc(env
, inst
, 1), inst
->src(1));
390 void cgInitPackedLayoutArrayLoop(IRLS
& env
, const IRInstruction
* inst
) {
391 auto const arr
= srcLoc(env
, inst
, 0).reg();
392 auto const spIn
= srcLoc(env
, inst
, 1).reg();
393 auto const extra
= inst
->extra
<InitPackedLayoutArrayLoop
>();
394 auto const count
= safe_cast
<int>(extra
->size
);
395 auto& v
= vmain(env
);
397 auto const sp
= v
.makeReg();
398 v
<< lea
{spIn
[cellsToBytes(extra
->offset
.offset
)], sp
};
400 auto const i
= v
.cns(0);
401 auto const j
= v
.cns((count
- 1) * 2);
403 // We know that we have at least one element in the array so we don't have to
404 // do an initial bounds check.
407 doWhile(v
, CC_GE
, {i
, j
},
408 [&] (const VregList
& in
, const VregList
& out
) {
409 auto const i1
= in
[0], j1
= in
[1];
410 auto const i2
= out
[0], j2
= out
[1];
411 auto const sf
= v
.makeReg();
412 auto const value
= v
.makeReg();
414 // Load the value from the stack and store into the array. It's safe to
415 // copy all 16 bytes of the TV because packed arrays don't use m_aux.
416 v
<< loadups
{sp
[j1
* 8], value
};
417 v
<< storeups
{value
, arr
[i1
* 8] + PackedArray::entriesOffset()};
419 // Add 2 to the loop variable because we can only scale by at most 8.
421 v
<< subqi
{2, j1
, j2
, sf
};
430 template<typename Fn
>
431 void newRecordImpl(IRLS
& env
, const IRInstruction
* inst
, Fn creatorFn
) {
432 auto const rec
= srcLoc(env
, inst
, 0).reg();
433 auto const sp
= srcLoc(env
, inst
, 1).reg();
434 auto const extra
= inst
->extra
<NewStructData
>();
435 auto& v
= vmain(env
);
437 auto table
= v
.allocData
<const StringData
*>(extra
->numKeys
);
438 memcpy(table
, extra
->keys
, extra
->numKeys
* sizeof(*extra
->keys
));
440 auto const args
= argGroup(env
, inst
)
444 .addr(sp
, cellsToBytes(extra
->offset
.offset
));
446 cgCallHelper(v
, env
, CallSpec::direct(creatorFn
),
448 SyncOptions::Sync
, args
);
453 void cgNewRecord(IRLS
& env
, const IRInstruction
* inst
) {
454 newRecordImpl(env
, inst
, RecordData::newRecord
);
457 ///////////////////////////////////////////////////////////////////////////////