Rename RefcountProfile to IncRefProfile
[hiphop-php.git] / hphp / runtime / vm / jit / irlower-array.cpp
blob4e0d4bc2844f21b8b77f2ece3d2f7e2eff26cfe9
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/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));
65 auto& v = vmain(env);
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();
81 auto& v = vmain(env);
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};
96 return size;
97 }();
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 ///////////////////////////////////////////////////////////////////////////////
108 namespace {
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};
131 unlikelyCond(
132 v, vcold(env), CC_S, sf, dst,
133 [&](Vout& v) {
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));
138 return d;
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 ///////////////////////////////////////////////////////////////////////////////
164 // AKExists.
166 namespace {
168 template <bool intishWarn>
169 ALWAYS_INLINE
170 bool ak_exist_string_impl(const ArrayData* arr, const StringData* key) {
171 int64_t n;
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())) {
189 return false;
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())) {
199 return false;
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);
211 auto const target =
212 keyInfo.checkForInt
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);
223 } else {
224 args.ssa(1);
227 cgCallHelper(
228 v, env, target, callDest(env, inst),
229 RuntimeOption::EvalHackArrCompatNotices
230 ? SyncOptions::Sync : SyncOptions::None,
231 args
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 ///////////////////////////////////////////////////////////////////////////////
272 // Array creation.
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) {
282 cgCallHelper(
283 vmain(env),
284 env,
285 CallSpec::direct(MixedArray::MakeReserveDArray),
286 callDest(env, inst),
287 SyncOptions::None,
288 argGroup(env, inst).ssa(0)
292 void cgAllocVArray(IRLS& env, const IRInstruction* inst) {
293 auto const extra = inst->extra<PackedArrayData>();
294 cgCallHelper(
295 vmain(env),
296 env,
297 CallSpec::direct(PackedArray::MakeUninitializedVArray),
298 callDest(env, inst),
299 SyncOptions::None,
300 argGroup(env, inst).imm(extra->size)
304 namespace {
306 void newStructImpl(IRLS& env,
307 const IRInstruction* inst,
308 MixedArray* (*f)(uint32_t,
309 const StringData* const*,
310 const TypedValue*)
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)
320 .imm(extra->numKeys)
321 .dataPtr(table)
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)
348 .imm(extra->size)
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.
379 assertx(count);
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.
394 v << lea{i1[2], i2};
395 v << subqi{2, j1, j2, sf};
396 return sf;
401 ///////////////////////////////////////////////////////////////////////////////