Remove empty from slow runtime tests
[hiphop-php.git] / hphp / runtime / base / empty-array.cpp
blob94ee2d28e6ff5da05f0f6a2d3a38144b0af5c369
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/base/empty-array.h"
19 #include <type_traits>
21 #include "hphp/util/assertions.h"
23 #include "hphp/runtime/base/array-data.h"
24 #include "hphp/runtime/base/array-init.h"
25 #include "hphp/runtime/base/tv-val.h"
26 #include "hphp/runtime/base/mixed-array-defs.h"
27 #include "hphp/runtime/base/mixed-array.h"
28 #include "hphp/runtime/base/packed-array-defs.h"
29 #include "hphp/runtime/base/set-array.h"
30 #include "hphp/runtime/base/tv-mutate.h"
31 #include "hphp/runtime/base/tv-type.h"
32 #include "hphp/runtime/base/tv-variant.h"
33 #include "hphp/runtime/base/type-variant.h"
35 namespace HPHP {
37 //////////////////////////////////////////////////////////////////////
39 std::aligned_storage<sizeof(ArrayData), 16>::type s_theEmptyArray;
41 struct EmptyArray::Initializer {
42 Initializer() {
43 auto const ad = reinterpret_cast<ArrayData*>(&s_theEmptyArray);
44 ad->m_sizeAndPos = 0;
45 ad->initHeader(HeaderKind::Empty, StaticValue);
48 EmptyArray::Initializer EmptyArray::s_initializer;
50 //////////////////////////////////////////////////////////////////////
52 void EmptyArray::Release(ArrayData*) {
53 always_assert(!"never try to free the empty array");
56 Cell EmptyArray::NvGetKey(const ArrayData*, ssize_t /*pos*/) {
57 // We have no valid positions---no one should call this function.
58 not_reached();
61 size_t EmptyArray::Vsize(const ArrayData*) { not_reached(); }
63 tv_rval EmptyArray::GetValueRef(const ArrayData* /*ad*/, ssize_t /*pos*/) {
64 // We have no valid positions---no one should call this function.
65 not_reached();
68 // EmptyArray::IterAdvance() is reachable; see ArrayData::next() for details
69 ssize_t EmptyArray::IterAdvance(const ArrayData*, ssize_t /*prev*/) {
70 return 0;
73 // EmptyArray::IterRewind() is NOT reachable; see ArrayData::prev() for details
74 ssize_t EmptyArray::IterRewind(const ArrayData*, ssize_t /*prev*/) {
75 not_reached();
78 // We're always already a static array.
79 void EmptyArray::OnSetEvalScalar(ArrayData*) { not_reached(); }
80 ArrayData* EmptyArray::CopyStatic(const ArrayData* /*ad*/) {
81 not_reached();
84 //////////////////////////////////////////////////////////////////////
86 NEVER_INLINE
87 ArrayData* EmptyArray::Copy(const ArrayData*) { return staticEmptyArray(); }
89 //////////////////////////////////////////////////////////////////////
92 * Note: if you try to tail-call these helper routines, gcc will
93 * unfortunately still generate functions with frames and makes a
94 * call instead of a jump. It's because of std::pair (and is still
95 * the case if you return a custom struct).
97 * For now we're leaving this, because it's essentially free for these
98 * routines to leave the lval pointer in the second return register,
99 * and it seems questionable to clone the whole function just to avoid
100 * the frame creation in these callers. (It works to reinterpret_cast
101 * these functions to one that returns ArrayData* instead of a pair in
102 * the cases we don't need the second value, but this seems a tad too
103 * sketchy for probably-unmeasurable benefits. I'll admit I didn't
104 * try to measure it though... ;)
108 * Helper for empty array -> packed transitions. Creates an array
109 * with one element. The element is transferred into the array (should
110 * already be incref'd).
112 ALWAYS_INLINE
113 arr_lval EmptyArray::MakePackedInl(TypedValue tv) {
114 auto const ad = static_cast<ArrayData*>(
115 tl_heap->objMallocIndex(PackedArray::SmallSizeIndex)
117 ad->initHeader_16(
118 HeaderKind::Packed,
119 OneReference,
120 PackedArray::packSizeIndexAndAuxBits(
121 PackedArray::SmallSizeIndex,
122 ArrayData::kNotDVArray
125 ad->m_sizeAndPos = 1; // size=1, pos=0
127 auto elem = PackedArray::LvalUncheckedInt(ad, 0);
128 tvCopy(tv, elem);
130 assertx(ad->kind() == ArrayData::kPackedKind);
131 assertx(ad->dvArray() == ArrayData::kNotDVArray);
132 assertx(ad->m_size == 1);
133 assertx(ad->m_pos == 0);
134 assertx(ad->hasExactlyOneRef());
135 assertx(PackedArray::checkInvariants(ad));
136 return arr_lval { ad, elem };
139 NEVER_INLINE
140 arr_lval EmptyArray::MakePacked(TypedValue tv) {
141 return MakePackedInl(tv);
145 * Helper for creating a single-element mixed array with a string key.
147 * Note: the key is not already incref'd, but the value must be.
149 NEVER_INLINE
150 arr_lval EmptyArray::MakeMixed(StringData* key, TypedValue val) {
151 auto const ad = MixedArray::reqAlloc(MixedArray::SmallScale);
152 MixedArray::InitSmall(ad, 1/*size*/, 0/*nextIntKey*/);
153 auto const data = ad->data();
154 auto const hash = reinterpret_cast<int32_t*>(data + MixedArray::SmallSize);
155 auto const khash = key->hash();
156 auto const mask = MixedArray::SmallMask;
157 hash[khash & mask] = 0;
158 data[0].setStrKey(key, khash);
160 auto& elem = data[0].data;
161 elem.m_data = val.m_data;
162 elem.m_type = val.m_type;
164 assertx(ad->m_size == 1);
165 assertx(ad->m_pos == 0);
166 assertx(ad->m_scale == MixedArray::SmallScale);
167 assertx(ad->kind() == ArrayData::kMixedKind);
168 assertx(ad->hasExactlyOneRef());
169 assertx(ad->m_used == 1);
170 assertx(ad->checkInvariants());
171 return arr_lval { ad, &elem };
175 * Creating a single-element mixed array with a integer key. The
176 * value is already incref'd.
178 arr_lval EmptyArray::MakeMixed(int64_t key, TypedValue val) {
179 auto const ad = MixedArray::reqAlloc(MixedArray::SmallScale);
180 MixedArray::InitSmall(ad, 1/*size*/, (key >= 0) ? key + uint64_t{1} : 0);
181 auto const data = ad->data();
182 auto const hash = reinterpret_cast<int32_t*>(data + MixedArray::SmallSize);
184 auto const mask = MixedArray::SmallMask;
185 auto h = hash_int64(key);
186 hash[h & mask] = 0;
187 data[0].setIntKey(key, h);
189 auto& elem = data[0].data;
190 elem.m_data = val.m_data;
191 elem.m_type = val.m_type;
193 assertx(ad->kind() == ArrayData::kMixedKind);
194 assertx(ad->m_size == 1);
195 assertx(ad->m_pos == 0);
196 assertx(ad->hasExactlyOneRef());
197 assertx(ad->m_scale == MixedArray::SmallScale);
198 assertx(ad->m_used == 1);
199 assertx(ad->checkInvariants());
200 return arr_lval { ad, &elem };
203 //////////////////////////////////////////////////////////////////////
205 template<bool warn> ALWAYS_INLINE
206 arr_lval EmptyArray::LvalIntImpl(ArrayData*, int64_t k, bool) {
207 if (warn && checkHACFalseyPromote()) {
208 raise_hac_falsey_promote_notice("Lval on missing array element");
210 return k == 0 ? EmptyArray::MakePacked(make_tv<KindOfNull>())
211 : EmptyArray::MakeMixed(k, make_tv<KindOfNull>());
214 template<bool warn> ALWAYS_INLINE
215 arr_lval EmptyArray::LvalStrImpl(ArrayData*, StringData* k, bool) {
216 if (warn && checkHACFalseyPromote()) {
217 raise_hac_falsey_promote_notice("Lval on missing array element");
219 return EmptyArray::MakeMixed(k, make_tv<KindOfNull>());
222 ArrayData* EmptyArray::SetInt(ArrayData*, int64_t k, Cell v) {
223 // TODO(#3888164): we should make it so we don't need KindOfUninit checks
224 if (v.m_type == KindOfUninit) v.m_type = KindOfNull;
225 tvIncRefGen(v);
226 auto const lval = k == 0 ? EmptyArray::MakePacked(v)
227 : EmptyArray::MakeMixed(k, v);
228 return lval.arr;
231 ArrayData*
232 EmptyArray::SetStr(ArrayData*, StringData* k, Cell v) {
233 tvIncRefGen(v);
234 // TODO(#3888164): we should make it so we don't need KindOfUninit checks
235 if (v.m_type == KindOfUninit) v.m_type = KindOfNull;
236 return EmptyArray::MakeMixed(k, v).arr;
239 ArrayData* EmptyArray::SetWithRefInt(ArrayData* ad, int64_t k, TypedValue v) {
240 if (checkHACRefBind() && tvIsReferenced(v)) {
241 raiseHackArrCompatRefBind(k);
243 auto const lval = LvalIntImpl<false>(ad, k, ad->cowCheck());
244 tvSetWithRef(v, lval);
245 return lval.arr;
248 ArrayData* EmptyArray::SetWithRefStr(ArrayData* ad, StringData* k,
249 TypedValue v) {
250 if (checkHACRefBind() && tvIsReferenced(v)) {
251 raiseHackArrCompatRefBind(k);
253 auto const lval = LvalStrImpl<false>(ad, k, ad->cowCheck());
254 tvSetWithRef(v, lval);
255 return lval.arr;
258 ArrayData* EmptyArray::RemoveInt(ArrayData* ad, int64_t) {
259 return ad;
262 ArrayData* EmptyArray::RemoveStr(ArrayData* ad, const StringData*) {
263 return ad;
266 arr_lval EmptyArray::LvalInt(ArrayData* ad, int64_t k, bool copy) {
267 return LvalIntImpl<true>(ad, k, copy);
270 arr_lval EmptyArray::LvalIntRef(ArrayData* ad, int64_t k, bool copy) {
271 if (checkHACRefBind()) raiseHackArrCompatRefBind(k);
272 return LvalInt(ad, k, copy);
275 arr_lval EmptyArray::LvalStr(ArrayData* ad, StringData* k, bool copy) {
276 return LvalStrImpl<true>(ad, k, copy);
279 arr_lval EmptyArray::LvalStrRef(ArrayData* ad, StringData* k, bool copy) {
280 if (checkHACRefBind()) raiseHackArrCompatRefBind(k);
281 return LvalStr(ad, k, copy);
284 arr_lval EmptyArray::LvalNew(ArrayData*, bool) {
285 if (checkHACFalseyPromote()) {
286 raise_hac_falsey_promote_notice("Lval on missing array element");
288 return EmptyArray::MakePacked(make_tv<KindOfNull>());
291 arr_lval EmptyArray::LvalNewRef(ArrayData* ad, bool copy) {
292 if (checkHACRefBind()) raiseHackArrCompatRefNew();
293 return LvalNew(ad, copy);
296 ArrayData* EmptyArray::Append(ArrayData*, Cell v) {
297 tvIncRefGen(v);
298 return EmptyArray::MakePackedInl(v).arr;
301 ArrayData* EmptyArray::AppendWithRef(ArrayData*, TypedValue v) {
302 if (checkHACRefBind() && tvIsReferenced(v)) {
303 raiseHackArrCompatRefNew();
305 auto tv = make_tv<KindOfNull>();
306 tvAsVariant(&tv).setWithRef(v);
307 return EmptyArray::MakePacked(tv).arr;
310 //////////////////////////////////////////////////////////////////////
312 ArrayData* EmptyArray::PlusEq(ArrayData*, const ArrayData* elems) {
313 if (!elems->isPHPArray()) throwInvalidAdditionException(elems);
314 elems->incRefCount();
315 return const_cast<ArrayData*>(elems);
318 ArrayData* EmptyArray::Merge(ArrayData*, const ArrayData* elems) {
319 if (elems->isNotDVArray()) {
320 // Packed arrays don't need renumbering, so don't make a copy.
321 if (elems->isPacked()) {
322 elems->incRefCount();
323 return const_cast<ArrayData*>(elems);
325 // Fast path the common case that elems is mixed.
326 if (elems->isMixed()) {
327 auto const copy = MixedArray::Copy(elems);
328 assertx(copy != elems);
329 MixedArray::Renumber(copy);
330 return copy;
333 auto copy = const_cast<ArrayData*>(elems)->toPHPArray(true);
334 copy = copy == elems ? elems->copy() : copy;
335 assertx(copy != elems);
336 copy->renumber();
337 return copy;
340 ArrayData* EmptyArray::PopOrDequeue(ArrayData* ad, Variant& value) {
341 value = uninit_null();
342 return ad;
345 ArrayData* EmptyArray::Prepend(ArrayData*, Cell v) {
346 tvIncRefGen(v);
347 return EmptyArray::MakePacked(v).arr;
350 ArrayData* EmptyArray::ToDict(ArrayData*, bool) {
351 return staticEmptyDictArray();
354 ArrayData* EmptyArray::ToShape(ArrayData*, bool) {
355 return staticEmptyShapeArray();
358 ArrayData* EmptyArray::ToVec(ArrayData*, bool) {
359 return staticEmptyVecArray();
362 ArrayData* EmptyArray::ToKeyset(ArrayData*, bool) {
363 return staticEmptyKeysetArray();
366 //////////////////////////////////////////////////////////////////////