Allocate HphpArrays in a flat mode instead of using m_inline_data

Gets HphpArrays into a flat mode using MM().objMalloc()
instead of SmartAllocator.  Various optimizations were needed to the
Make functions to get this to work out ok.  Growth still creates a
non-flat HphpArray (leaving a PromotedPayload behind so we know how
big the original allocation was).

Reviewed By: @edwinsmith

Differential Revision: D969431
Esse commit está contido em:
Jordan DeLong
2013-09-08 19:30:13 -07:00
commit de Sara Golemon
commit 38fd595342
32 arquivos alterados com 589 adições e 354 exclusões
+386 -202
Ver Arquivo
@@ -17,6 +17,7 @@
#define INLINE_VARIANT_HELPER 1
#include "hphp/runtime/base/hphp-array.h"
#include "hphp/runtime/base/array-init.h"
#include "hphp/runtime/base/array-iterator.h"
#include "hphp/runtime/base/complex-types.h"
@@ -38,19 +39,9 @@
namespace HPHP {
static_assert(
sizeof(HphpArray) == 152,
"Performance is sensitive to sizeof(HphpArray)."
" Make sure you changed it with good reason and then update this assert."
);
static_assert(
sizeof(ArrayData) == 24,
"ArrayData is expected to take 3 q-words"
);
TRACE_SET_MOD(runtime);
///////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/*
* Allocation of HphpArray buffers works like this: the smallest buffer
@@ -74,19 +65,39 @@ TRACE_SET_MOD(runtime);
* separately. Even larger tables allocate the hashtable and slots
* contiguously.
*/
void* HphpArray::SmaAllocatorInitSetup = SmartAllocatorInitSetup<HphpArray>();
//=============================================================================
// Static members.
std::aligned_storage<
sizeof(HphpArray),
sizeof(HphpArray) +
sizeof(HphpArray::Elm) * HphpArray::SmallSize,
// No need for space for the hash because the empty array is
// kPackedKind.
alignof(HphpArray)
>::type s_theEmptyArray;
struct HphpArray::EmptyArrayInitializer {
EmptyArrayInitializer() {
new (HphpArray::GetStaticEmptyArray()) HphpArray(StaticEmptyArray);
EmptyArrayInitializer() ATTRIBUTE_COLD {
void* vpEmpty = &s_theEmptyArray;
auto const ad = static_cast<HphpArray*>(vpEmpty);
ad->m_kind = kPackedKind;
ad->m_allocMode = AllocationMode::smart;
ad->m_size = 0;
ad->m_pos = ArrayData::invalid_index;
ad->m_count = 0;
ad->m_strongIterators = nullptr;
ad->m_used = 0;
ad->m_tableMask = SmallHashSize - 1;
ad->m_cap = SmallSize;
ad->m_data = reinterpret_cast<Elm*>(ad + 1);
ad->m_hash = nullptr;
ad->setStatic();
assert(ad->checkInvariants());
}
};
HphpArray::EmptyArrayInitializer HphpArray::s_arrayInitializer;
@@ -94,101 +105,219 @@ HphpArray::EmptyArrayInitializer HphpArray::s_arrayInitializer;
//=============================================================================
// Helpers.
static inline uint32_t computeMaskFromNumElms(const uint32_t n) {
namespace {
ALWAYS_INLINE
uint32_t computeMaskFromNumElms(uint32_t n) {
assert(n <= 0x7fffffffU);
auto lgSize = HphpArray::MinLgTableSize;
auto maxElms = HphpArray::SmallSize;
assert(lgSize >= 2);
// Note: it's tempting to convert this loop into something involving
// x64 bsr and a shift. Naive attempts currently actually add more
// branches, because we need to initially check whether `n' is less
// than SmallSize, and after finding the next power of two we need a
// branch to see if it was big enough for the desired load factor.
// This is probably still worth revisiting (e.g., MakeReserve could
// have a precondition that n is at least SmallSize).
while (maxElms < n) {
++lgSize;
maxElms <<= 1;
}
assert(lgSize <= 32);
// return 2^lgSize - 1
return ((size_t(1U)) << lgSize) - 1;
static_assert(HphpArray::MinLgTableSize >= 2,
"lower limit for 0.75 load factor");
}
//=============================================================================
// Construction/destruction.
HphpArray::HphpArray(uint capacity)
: ArrayData(kPackedKind, AllocationMode::smart, 0)
, m_used(0) {
assert(m_size == 0);
const auto mask = computeMaskFromNumElms(capacity);
m_tableMask = mask;
allocData(computeMaxElms(mask), computeTableSize(mask));
assert(checkInvariants());
ALWAYS_INLINE
std::pair<uint32_t,uint32_t> computeCapAndMask(uint32_t minimumMaxElms) {
auto const mask = computeMaskFromNumElms(minimumMaxElms);
auto const cap = HphpArray::computeMaxElms(mask);
return std::make_pair(cap, mask);
}
HphpArray::HphpArray(uint size, const TypedValue* values)
: ArrayData(kPackedKind, AllocationMode::smart, size)
, m_used(size) {
const auto mask = computeMaskFromNumElms(size);
m_tableMask = mask;
allocData(computeMaxElms(mask), computeTableSize(mask));
// append values by moving -- Caller assumes we update refcount. Values
// are in reverse order since they come from the stack, which grows down.
Elm* data = m_data;
ALWAYS_INLINE
uint32_t computeAllocBytesPromoted(uint32_t cap, uint32_t mask) {
auto const tabSize = mask + 1;
auto const tabBytes = tabSize * sizeof(int32_t);
auto const dataBytes = cap * sizeof(HphpArray::Elm);
return tabBytes + dataBytes;
}
ALWAYS_INLINE
uint32_t computeAllocBytesFlat(uint32_t cap, uint32_t mask) {
return sizeof(HphpArray) + computeAllocBytesPromoted(cap, mask);
}
ALWAYS_INLINE
HphpArray* smartAllocFlat(uint32_t cap, uint32_t mask) {
/*
* Note: we're currently still allocating the memory for the hash
* for a packed array even if we aren't going to use it yet.
*/
auto const allocBytes = computeAllocBytesFlat(cap, mask);
return static_cast<HphpArray*>(MM().objMalloc(allocBytes));
}
ALWAYS_INLINE
HphpArray* mallocFlat(uint32_t cap, uint32_t mask) {
auto const allocBytes = computeAllocBytesFlat(cap, mask);
return static_cast<HphpArray*>(std::malloc(allocBytes));
}
}
//=============================================================================
// Construction
HOT_FUNC_VM
HphpArray* HphpArray::MakeReserve(uint32_t capacity) {
auto const cmret = computeCapAndMask(capacity);
auto const cap = cmret.first;
auto const mask = cmret.second;
auto const ad = smartAllocFlat(cap, mask);
ad->m_kindModeAndSize = static_cast<uint32_t>(AllocationMode::smart) << 8 |
kPackedKind; // 0
ad->m_posAndCount = uint64_t{1} << 32 |
static_cast<uint32_t>(ArrayData::invalid_index);
ad->m_strongIterators = nullptr;
ad->m_data = reinterpret_cast<Elm*>(ad + 1);
ad->m_capAndUsed = cap;
ad->m_tableMask = mask;
assert(ad->m_kind == kPackedKind);
assert(ad->m_allocMode == AllocationMode::smart);
assert(ad->m_size == 0);
assert(ad->m_pos == ArrayData::invalid_index);
assert(ad->m_count == 1);
assert(ad->m_cap == cap);
assert(ad->m_used == 0);
assert(ad->isFlat());
assert(ad->checkInvariants());
return ad;
}
HOT_FUNC_VM
HphpArray* HphpArray::MakeTuple(uint32_t size, const TypedValue* values) {
assert(size > 0);
auto const cmret = computeCapAndMask(size);
auto const cap = cmret.first;
auto const mask = cmret.second;
auto const ad = smartAllocFlat(cap, mask);
auto const shiftedSize = uint64_t{size} << 32;
ad->m_kindModeAndSize = shiftedSize |
static_cast<uint32_t>(AllocationMode::smart) << 8 |
kPackedKind;
ad->m_posAndCount = uint64_t{1} << 32;
ad->m_strongIterators = nullptr;
ad->m_capAndUsed = shiftedSize | cap;
ad->m_tableMask = mask;
auto const data = reinterpret_cast<Elm*>(ad + 1);
ad->m_data = data;
// Append values by moving -- Caller assumes we update refcount.
// Values are in reverse order since they come from the stack, which
// grows down.
for (uint32_t i = 0; i < size; i++) {
const auto& tv = values[size - i - 1];
data[i].data.m_data = tv.m_data;
data[i].data.m_type = tv.m_type;
}
assert(size == 0 || m_pos == 0);
assert(checkInvariants());
}
HphpArray::HphpArray(EmptyMode)
: ArrayData(kPackedKind, AllocationMode::smart, 0)
, m_used(0)
, m_tableMask(SmallHashSize - 1) {
allocData(SmallSize, SmallHashSize);
setStatic();
assert(checkInvariants());
assert(ad->m_kind == kPackedKind);
assert(ad->m_allocMode == AllocationMode::smart);
assert(ad->m_size == size);
assert(ad->m_pos == 0);
assert(ad->m_count == 1);
assert(ad->m_cap == cap);
assert(ad->m_used == size);
assert(ad->isFlat());
assert(ad->checkInvariants());
return ad;
}
// for internal use by nonSmartCopy() and copyPacked()
ALWAYS_INLINE
HphpArray::HphpArray(const HphpArray& other, AllocationMode mode, ClonePacked)
: ArrayData(other.m_kind, mode, other.m_size)
, m_used(other.m_used)
, m_tableMask(other.m_tableMask) {
HphpArray* HphpArray::CopyPacked(const HphpArray& other, AllocationMode mode) {
assert(other.isPacked());
m_pos = other.m_pos;
allocData(other.m_cap, computeTableSize(m_tableMask));
auto const cap = other.m_cap;
auto const mask = other.m_tableMask;
auto const ad = mode == AllocationMode::smart
? smartAllocFlat(cap, mask)
: mallocFlat(cap, mask);
ad->m_kindModeAndSize = uint64_t{other.m_size} << 32 |
static_cast<uint32_t>(mode) << 8 |
kPackedKind;
ad->m_posAndCount = static_cast<uint32_t>(other.m_pos);
ad->m_strongIterators = nullptr;
ad->m_capAndUsed = uint64_t{other.m_used} << 32 | cap;
ad->m_tableMask = mask;
auto const targetElms = reinterpret_cast<Elm*>(ad + 1);
ad->m_data = targetElms;
// Copy the elements and bump up refcounts as needed.
Elm* elms = other.m_data;
Elm* targetElms = m_data;
for (uint32_t i = 0, limit = m_used; i < limit; ++i) {
auto const elms = other.m_data;
for (uint32_t i = 0, limit = ad->m_used; i < limit; ++i) {
tvDupFlattenVars(&elms[i].data, &targetElms[i].data, &other);
}
assert(checkInvariants());
assert(ad->m_kind == kPackedKind);
assert(ad->m_allocMode == mode);
assert(ad->m_size == other.m_size);
assert(ad->m_pos == other.m_pos);
assert(ad->m_count == 0);
assert(ad->m_used == other.m_used);
assert(ad->m_cap == cap);
assert(ad->m_tableMask == mask);
assert(ad->isFlat());
assert(ad->checkInvariants());
return ad;
}
// For internal use by nonSmartCopy() and copyMixed()
ALWAYS_INLINE
HphpArray::HphpArray(const HphpArray& other, AllocationMode mode, CloneMixed)
: ArrayData(other.m_kind, mode, other.m_size)
, m_used(other.m_used)
, m_tableMask(other.m_tableMask)
, m_hLoad(other.m_hLoad)
, m_nextKI(other.m_nextKI) {
assert(!other.isPacked());
m_pos = other.m_pos;
auto maxElms = other.m_cap;
auto tableSize = computeTableSize(m_tableMask);
m_hash = allocData(maxElms, tableSize);
// Copy the hash.
memcpy(m_hash, other.m_hash, tableSize * sizeof(*m_hash));
HphpArray* HphpArray::CopyMixed(const HphpArray& other, AllocationMode mode) {
assert(other.m_kind == kMixedKind);
auto const cap = other.m_cap;
auto const mask = other.m_tableMask;
auto const ad = mode == AllocationMode::smart
? smartAllocFlat(cap, mask)
: mallocFlat(cap, mask);
ad->m_kindModeAndSize = uint64_t{other.m_size} << 32 |
static_cast<uint32_t>(mode) << 8 |
kMixedKind;
ad->m_posAndCount = static_cast<uint32_t>(other.m_pos);
ad->m_strongIterators = nullptr;
ad->m_capAndUsed = uint64_t{other.m_used} << 32 | cap;
ad->m_maskAndLoad = uint64_t{other.m_hLoad} << 32 | mask;
ad->m_nextKI = other.m_nextKI;
auto const data = reinterpret_cast<Elm*>(ad + 1);
auto const hash = reinterpret_cast<int32_t*>(data + cap);
ad->m_data = data;
ad->m_hash = static_cast<int32_t*>(
memcpy(hash, other.m_hash, (mask + 1) * sizeof *hash)
);
// Copy the elements and bump up refcounts as needed.
auto elms = other.m_data;
auto targetElms = m_data;
for (uint32_t i = 0, limit = m_used; i < limit; ++i) {
const auto& e = elms[i];
auto& te = targetElms[i];
auto const elms = other.m_data;
for (uint32_t i = 0, limit = ad->m_used; i < limit; ++i) {
auto const& e = elms[i];
auto& te = data[i];
if (!isTombstone(e.data.m_type)) {
te.key = e.key;
te.data.hash() = e.data.hash();
@@ -200,76 +329,146 @@ HphpArray::HphpArray(const HphpArray& other, AllocationMode mode, CloneMixed)
te.data.m_type = KindOfInvalid;
}
}
// If the element density dropped below 50% due to indirect elements
// being converted into tombstones, we should do a compaction
if (m_size < m_used / 2) {
compact(false);
if (ad->m_size < ad->m_used / 2) {
ad->compact(false);
}
assert(ad->m_kind == other.m_kind);
assert(ad->m_allocMode == mode);
assert(ad->m_size == other.m_size);
assert(ad->m_pos == other.m_pos);
assert(ad->m_count == 0);
assert(ad->m_used == other.m_used);
assert(ad->m_cap == cap);
assert(ad->m_tableMask == mask);
assert(ad->m_hLoad == other.m_hLoad);
assert(ad->isFlat());
assert(ad->checkInvariants());
return ad;
}
NEVER_INLINE ArrayData* HphpArray::NonSmartCopy(const ArrayData* in) {
auto a = asHphpArray(in);
assert(a->checkInvariants());
return a->isPacked()
? CopyPacked(*a, AllocationMode::nonSmart)
: CopyMixed(*a, AllocationMode::nonSmart);
}
NEVER_INLINE HphpArray* HphpArray::copyPacked() const {
assert(checkInvariants());
return CopyPacked(*this, AllocationMode::smart);
}
inline void HphpArray::destroyPacked() {
auto const elms = m_data;
for (uint32_t i = 0, n = m_used; i < n; ++i) {
tvRefcountedDecRef(&elms[i].data);
}
if (elms != m_inline_data.slots) modeFree(elms);
NEVER_INLINE HphpArray* HphpArray::copyMixed() const {
assert(checkInvariants());
return CopyMixed(*this, AllocationMode::smart);
}
inline void HphpArray::destroy() {
auto const elms = m_data;
for (uint32_t i = 0, n = m_used; i < n; ++i) {
auto& e = elms[i];
if (isTombstone(e.data.m_type)) continue;
if (e.hasStrKey()) decRefStr(e.key);
tvRefcountedDecRef(&e.data);
//=============================================================================
// Destruction
HOT_FUNC_VM
void HphpArray::ReleasePacked(ArrayData* in) {
auto const ad = asPacked(in);
auto const data = ad->m_data;
auto const stop = data + ad->m_used;
for (auto ptr = data; ptr != stop; ++ptr) {
tvRefcountedDecRef(ptr->data);
}
if (elms != m_inline_data.slots) modeFree(elms);
ad->ArrayData::destroy();
auto const cap = ad->m_cap;
auto const mask = ad->m_tableMask;
// Calling isFlat() here instead of manually inlining it does a
// re-load from m_data (the compiler doesn't know whether
// ArrayData::destroy modified it).
if (UNLIKELY(data != static_cast<void*>(ad + 1))) {
MM().objFree(data, computeAllocBytesPromoted(cap, mask));
auto& payload = ad->promotedPayload();
MM().objFree(
ad,
computeAllocBytesFlat(payload.oldCap, payload.oldMask)
);
return;
}
MM().objFree(ad, computeAllocBytesFlat(cap, mask));
}
HOT_FUNC_VM
void HphpArray::ReleasePacked(ArrayData* ad) {
auto a = asPacked(ad);
a->destroyPacked();
a->ArrayData::destroy();
HphpArray::AllocatorType::getNoCheck()->dealloc(a);
void HphpArray::Release(ArrayData* in) {
auto const ad = asMixed(in);
auto const data = ad->m_data;
auto const stop = data + ad->m_used;
for (auto ptr = data; ptr != stop; ++ptr) {
if (isTombstone(ptr->data.m_type)) continue;
if (ptr->hasStrKey()) decRefStr(ptr->key);
tvRefcountedDecRef(&ptr->data);
}
ad->ArrayData::destroy();
auto const cap = ad->m_cap;
auto const mask = ad->m_tableMask;
// Calling isFlat() here instead of manually inlining it does a
// re-load from m_data. (The compiler doesn't know whether
// ArrayData::destroy modified it.)
if (UNLIKELY(data != static_cast<void*>(ad + 1))) {
MM().objFree(data, computeAllocBytesPromoted(cap, mask));
auto& payload = ad->promotedPayload();
MM().objFree(
ad,
computeAllocBytesFlat(payload.oldCap, payload.oldMask)
);
return;
}
MM().objFree(ad, computeAllocBytesFlat(cap, mask));
}
HOT_FUNC_VM
void HphpArray::Release(ArrayData* ad) {
auto a = asMixed(ad);
a->destroy();
a->ArrayData::destroy();
HphpArray::AllocatorType::getNoCheck()->dealloc(a);
}
//=============================================================================
// State transitions
NEVER_INLINE HOT_FUNC_VM
HphpArray* HphpArray::packedToMixed() {
assert(isPacked());
if (m_data == m_inline_data.slots) {
m_hash = m_inline_data.hash;
} else {
auto dataSize = m_cap * sizeof(*m_data);
auto hashSize = computeTableSize(m_tableMask) * sizeof(*m_hash);
m_hash = hashSize <= sizeof(m_inline_hash) ? m_inline_hash :
(int32_t*)(uintptr_t(m_data) + dataSize);
}
auto const data = m_data;
auto const hash = reinterpret_cast<int32_t*>(data + m_cap);
m_kind = kMixedKind;
m_hash = hash;
auto const size = m_size;
m_hLoad = size;
m_nextKI = size;
uint32_t i = 0;
auto size = m_size;
for (; i < size; ++i) {
m_data[i].setIntKey(i);
m_hash[i] = i;
data[i].setIntKey(i);
hash[i] = i;
}
for (; i <= m_tableMask; ++i) {
m_hash[i] = Empty;
hash[i] = Empty;
}
m_hLoad = size;
m_nextKI = size;
assert(checkInvariants());
return this;
}
//=============================================================================
// Invariants:
//
// m_size <= m_used; m_used <= m_cap
@@ -295,7 +494,13 @@ HphpArray* HphpArray::packedToMixed() {
// no KindOfInvalid tombstones
//
bool HphpArray::checkInvariants() const {
static_assert(sizeof(*m_data) == 24, "");
static_assert(sizeof *m_data == 24, "");
static_assert(sizeof(ArrayData) == 3 * sizeof(uint64_t), "");
static_assert(
sizeof(HphpArray) == sizeof(ArrayData) + 5 * sizeof(uint64_t),
"Performance is sensitive to sizeof(HphpArray)."
" Make sure you changed it with good reason and then update this assert."
);
assert(m_size <= m_used);
assert(m_used <= m_cap);
@@ -699,39 +904,6 @@ HphpArray* HphpArray::moveVal(TypedValue& tv, TypedValue v) {
return this;
}
int32_t* HphpArray::allocData(size_t maxElms, size_t tableSize) {
m_cap = maxElms;
if (maxElms <= SmallSize) {
m_data = m_inline_data.slots;
return m_inline_data.hash;
}
size_t hashSize = tableSize * sizeof(*m_hash);
size_t dataSize = maxElms * sizeof(*m_data);
size_t allocSize = hashSize <= sizeof(m_inline_hash) ? dataSize :
dataSize + hashSize;
m_data = (Elm*) modeAlloc(allocSize);
return hashSize <= sizeof(m_inline_hash) ? m_inline_hash :
(int32_t*)(uintptr_t(m_data) + dataSize);
}
int32_t* HphpArray::reallocData(size_t maxElms, size_t tableSize) {
assert(m_data && m_cap > 0 && maxElms > SmallSize);
size_t hashSize = tableSize * sizeof(*m_hash);
size_t dataSize = maxElms * sizeof(*m_data);
size_t allocSize = hashSize <= sizeof(m_inline_hash) ? dataSize :
dataSize + hashSize;
size_t oldDataSize = m_cap * sizeof(*m_data); // slots only.
if (m_data == m_inline_data.slots) {
m_data = (Elm*) modeAlloc(allocSize);
memcpy(m_data, m_inline_data.slots, oldDataSize);
} else {
m_data = (Elm*) modeRealloc(m_data, allocSize);
}
m_cap = maxElms;
return hashSize <= sizeof(m_inline_hash) ? m_inline_hash :
(int32_t*)(uintptr_t(m_data) + dataSize);
}
ALWAYS_INLINE void HphpArray::resizeIfNeeded() {
if (isFull()) resize();
}
@@ -753,30 +925,80 @@ NEVER_INLINE void HphpArray::resize() {
void HphpArray::grow() {
assert(!isPacked());
assert(m_tableMask <= 0x7fffffffU);
m_tableMask = 2 * m_tableMask + 1;
auto tableSize = computeTableSize(m_tableMask);
auto maxElms = computeMaxElms(m_tableMask);
m_hash = reallocData(maxElms, tableSize);
// All the elements have been copied and their offsets from the base are
// still the same, so we just need to build the new hash table.
initHash(tableSize);
auto elms = m_data;
auto const oldMask = m_tableMask;
auto const mask = oldMask * 2 + 1;
auto const cap = computeMaxElms(mask);
auto const allocBytes = computeAllocBytesPromoted(cap, mask);
auto data = static_cast<Elm*>(MM().objMalloc(allocBytes));
auto const oldData = m_data;
m_data = data;
m_tableMask = mask;
m_hash = reinterpret_cast<int32_t*>(data + cap);
// Load oldCap down here instead of earlier to keep its lifetime
// small. (This reduces the amount of spills vs doing it up near
// oldMask.)
auto const oldCap = m_cap;
m_cap = cap;
// Preserve data without using a register.
data = static_cast<Elm*>(
memcpy(data, oldData, oldCap * sizeof(Elm))
);
// If we call isFlat() here it will reload from m_data---use our
// oldData.
if (oldData == static_cast<void*>(this + 1)) {
promotedPayload().oldCap = oldCap;
promotedPayload().oldMask = oldMask;
} else {
MM().objFree(
oldData,
computeAllocBytesPromoted(oldCap, oldMask)
);
}
initHash(mask + 1);
for (uint32_t i = 0, limit = m_used; i < limit; ++i) {
auto& e = elms[i];
auto& e = data[i];
if (isTombstone(e.data.m_type)) continue;
auto* ei = findForNewInsert(e.hasIntKey() ? e.ikey : e.hash());
*ei = i;
}
m_hLoad = m_size;
}
NEVER_INLINE
void HphpArray::growPacked() {
assert(isPacked());
auto maxElms = m_cap * 2;
auto mask = m_tableMask * 2 + 1;
auto const oldCap = m_cap;
auto const oldMask = m_tableMask;
auto const cap = oldCap * 2;
auto const mask = oldMask * 2 + 1;
auto const allocBytes = computeAllocBytesPromoted(cap, mask);
auto const ptr = MM().objMalloc(allocBytes);
auto const oldData = m_data;
m_data = static_cast<Elm*>(ptr);
m_tableMask = mask;
reallocData(maxElms, computeTableSize(mask));
m_cap = cap;
memcpy(ptr, oldData, oldCap * sizeof(Elm));
if (oldData == static_cast<void*>(this + 1)) {
promotedPayload().oldCap = oldCap;
promotedPayload().oldMask = oldMask;
} else {
MM().objFree(
oldData,
computeAllocBytesPromoted(oldCap, oldMask)
);
}
}
void HphpArray::compact(bool renumber /* = false */) {
@@ -1708,44 +1930,6 @@ bool HphpArray::AdvanceFullPos(ArrayData* ad, FullPos& fp) {
return true;
}
//=============================================================================
//////////////////////////////////////////////////////////////////////
NEVER_INLINE ArrayData* HphpArray::NonSmartCopy(const ArrayData* ad) {
auto a = asHphpArray(ad);
return a->isPacked() ?
new HphpArray(*a, AllocationMode::nonSmart, ClonePacked()) :
new HphpArray(*a, AllocationMode::nonSmart, CloneMixed());
}
NEVER_INLINE HphpArray* HphpArray::copyPacked() const {
assert(checkInvariants());
return new (HphpArray::AllocatorType::getNoCheck()->alloc(sizeof(*this)))
HphpArray(*this, AllocationMode::smart, ClonePacked());
}
NEVER_INLINE HphpArray* HphpArray::copyMixed() const {
assert(checkInvariants());
return new (HphpArray::AllocatorType::getNoCheck()->alloc(sizeof(*this)))
HphpArray(*this, AllocationMode::smart, CloneMixed());
}
//=============================================================================
HOT_FUNC_VM
HphpArray* ArrayData::MakeTuple(uint size, const TypedValue* data) {
auto a = HphpArray::Make(size, data);
a->setRefCount(1);
TRACE(2, "MakeTuple: size %d\n", size);
return a;
}
HOT_FUNC_VM
HphpArray* ArrayData::MakeReserve(uint capacity) {
auto a = HphpArray::Make(capacity);
a->setRefCount(1);
TRACE(2, "MakeReserve: capacity %d\n", capacity);
return a;
}
///////////////////////////////////////////////////////////////////////////////
}