Eliminate ArrayData::plusEq
[hiphop-php.git] / hphp / runtime / vm / jit / irlower-array.cpp
blobde0fa42b2b2a02bac0c1a4ae6306af213cc68050
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/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();
68 auto& v = vmain(env);
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};
82 return size;
83 }();
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 ////////////////////////////////////////////////////////////////////////////////
92 namespace {
94 ArrayData* setLegacyHelper(ArrayData* arr) {
95 if (arr->cowCheck()) {
96 auto ad = arr->copy();
97 arr->decRefCount();
98 ad->setLegacyArray(true);
99 return ad;
100 } else {
101 arr->setLegacyArray(true);
102 return arr;
106 void setLegacyImpl(IRLS& env, const IRInstruction* inst) {
107 auto const args = argGroup(env, inst).ssa(0);
109 cgCallHelper(vmain(env),
110 env,
111 CallSpec::direct(setLegacyHelper),
112 callDest(env, inst),
113 SyncOptions::None,
114 args);
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 ///////////////////////////////////////////////////////////////////////////////
129 namespace {
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 ///////////////////////////////////////////////////////////////////////////////
159 // AKExists.
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())) {
165 return false;
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())) {
175 return false;
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 ///////////////////////////////////////////////////////////////////////////////
237 // Array creation.
239 namespace {
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);
272 namespace {
274 void newStructImpl(
275 IRLS& env,
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)
288 .imm(extra->numKeys)
289 .dataPtr(table)
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) {
302 newStructImpl(
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)
314 .imm(extra->size)
315 .addr(sp, cellsToBytes(extra->offset.offset));
317 cgCallHelper(v, env, CallSpec::direct(SetArray::MakeSet),
318 callDest(env, inst), SyncOptions::Sync, args);
321 namespace {
323 template<typename ArrayInit>
324 void allocStructImpl(
325 IRLS& env,
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.
405 assertx(count);
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.
420 v << lea{i1[2], i2};
421 v << subqi{2, j1, j2, sf};
422 return sf;
424 count
428 namespace {
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)
441 .reg(rec)
442 .imm(extra->numKeys)
443 .dataPtr(table)
444 .addr(sp, cellsToBytes(extra->offset.offset));
446 cgCallHelper(v, env, CallSpec::direct(creatorFn),
447 callDest(env, inst),
448 SyncOptions::Sync, args);
453 void cgNewRecord(IRLS& env, const IRInstruction* inst) {
454 newRecordImpl(env, inst, RecordData::newRecord);
457 ///////////////////////////////////////////////////////////////////////////////