diff --git a/hphp/runtime/base/array/array_data.cpp b/hphp/runtime/base/array/array_data.cpp index 75d770dab..433553fbb 100644 --- a/hphp/runtime/base/array/array_data.cpp +++ b/hphp/runtime/base/array/array_data.cpp @@ -360,7 +360,7 @@ Variant ArrayData::key() const { return uninit_null(); } -Variant ArrayData::value(ssize_t &pos) const { +Variant ArrayData::value(int32_t &pos) const { if (size_t(pos) < size_t(size())) { return getValue(pos); } diff --git a/hphp/runtime/base/array/array_data.h b/hphp/runtime/base/array/array_data.h index 7bdd54e43..c1395d377 100644 --- a/hphp/runtime/base/array/array_data.h +++ b/hphp/runtime/base/array/array_data.h @@ -55,14 +55,35 @@ class ArrayData : public Countable { public: static const ssize_t invalid_index = -1; - explicit ArrayData(ArrayKind kind, bool nonsmart = false) : - m_size(-1), m_pos(0), m_strongIterators(0), m_kind(kind), - m_nonsmart(nonsmart) { + explicit ArrayData(ArrayKind kind) + : m_size(-1) + , m_pos(0) + , m_kind(kind) + , m_nonsmart(false) + , m_strongIterators(nullptr) { } - ArrayData(const ArrayData *src, ArrayKind kind, - bool nonsmart = false) : - m_pos(src->m_pos), m_strongIterators(0), m_kind(src->m_kind), - m_nonsmart(nonsmart) { + + ArrayData(ArrayKind kind, bool nonsmart) + : m_size(-1) + , m_pos(0) + , m_kind(kind) + , m_nonsmart(nonsmart) + , m_strongIterators(nullptr) { + } + + ArrayData(ArrayKind kind, bool nonsmart, uint size) + : m_size(size) + , m_pos(size ? 0 : ArrayData::invalid_index) + , m_kind(kind) + , m_nonsmart(nonsmart) + , m_strongIterators(nullptr) { + } + + ArrayData(const ArrayData *src, ArrayKind kind, bool nonsmart = false) + : m_pos(src->m_pos) + , m_kind(src->m_kind) + , m_nonsmart(nonsmart) + , m_strongIterators(nullptr) { } static HphpArray* Make(uint capacity); @@ -182,7 +203,7 @@ class ArrayData : public Countable { virtual Variant next(); virtual Variant end(); virtual Variant key() const; - virtual Variant value(ssize_t &pos) const; + virtual Variant value(int32_t &pos) const; virtual Variant each(); bool isHead() const { return m_pos == iter_begin(); } @@ -479,16 +500,17 @@ class ArrayData : public Countable { static bool IsValidKey(const StringData* k) { return k; } protected: + // Layout starts with 64 bits for vtable, then 32 bits for m_count + // from Countable base, then... uint m_size; - ssize_t m_pos; - private: - FullPos* m_strongIterators; // head of linked list + int32_t m_pos; protected: const ArrayKind m_kind; AllocMode m_allocMode; const bool m_nonsmart; // never use smartalloc to allocate Elms - /* The 4 bytes of padding here are available to subclasses if their - * first field is also <= 4 bytes. */ + uint8_t m_slack8; // use this for whatever you wanna + private: + FullPos* m_strongIterators; // head of linked list public: // for the JIT static uint32_t getKindOff() { diff --git a/hphp/runtime/base/array/hphp_array.cpp b/hphp/runtime/base/array/hphp_array.cpp index e4dca6f29..6a0a92d0e 100644 --- a/hphp/runtime/base/array/hphp_array.cpp +++ b/hphp/runtime/base/array/hphp_array.cpp @@ -66,7 +66,13 @@ static const Trace::Module TRACEMOD = Trace::runtime; * separately. Even larger tables allocate the hashtable and slots * contiguously. */ -IMPLEMENT_SMART_ALLOCATION(HphpArray); +void *HphpArray::SmaAllocatorInitSetup = SmartAllocatorInitSetup(); + +void HphpArray::release() { + assert(typeid(*this) == typeid(HphpArray)); + this->HphpArray::~HphpArray(); + HphpArray::AllocatorType::getNoCheck()->dealloc(this); +} //============================================================================= // Static members. @@ -76,12 +82,12 @@ HphpArray HphpArray::s_theEmptyArray(StaticEmptyArray); //============================================================================= // Helpers. -static inline size_t computeMaskFromNumElms(uint32_t numElms) { - assert(numElms <= 0x7fffffffU); +static inline size_t computeMaskFromNumElms(const uint32_t n) { + assert(n <= 0x7fffffffU); size_t lgSize = HphpArray::MinLgTableSize; - size_t maxElms = (size_t(3U)) << (lgSize-2); + size_t maxElms = (size_t(3U)) << (HphpArray::MinLgTableSize - 2); assert(lgSize >= 2); - while (maxElms < numElms) { + while (maxElms < n) { ++lgSize; maxElms <<= 1; } @@ -95,60 +101,72 @@ static inline size_t computeMaskFromNumElms(uint32_t numElms) { //============================================================================= // Construction/destruction. -inline void HphpArray::init(uint size) { - m_size = 0; - m_tableMask = computeMaskFromNumElms(size); - size_t tableSize = computeTableSize(m_tableMask); - size_t maxElms = computeMaxElms(m_tableMask); - allocData(maxElms, tableSize); +inline uint32_t HphpArray::initWithoutHash(uint capacity) { + m_tableMask = computeMaskFromNumElms(capacity); + auto const tableSize = computeTableSize(m_tableMask); + allocData(computeMaxElms(m_tableMask), tableSize); + return tableSize; +} + +inline void HphpArray::init(uint capacity) { + assert(m_size == 0); + const auto tableSize = initWithoutHash(capacity); initHash(m_hash, tableSize); - m_pos = ArrayData::invalid_index; } -HphpArray::HphpArray(uint size) : ArrayData(kHphpArray), - m_lastE(ElmIndEmpty), m_data(nullptr), m_nextKI(0), m_hLoad(0) { +HphpArray::HphpArray(uint size) : ArrayData(kHphpArray, false, 0), + m_lastE(ElmIndEmpty), m_hLoad(0), m_nextKI(0) { #ifdef PEDANTIC if (size > 0x7fffffffU) { raise_error("Cannot create an array with more than 2^31 - 1 elements"); } #endif + assert(m_size == 0); init(size); } -HphpArray::HphpArray(uint size, const TypedValue* values) : - ArrayData(kHphpArray), m_lastE(ElmIndEmpty), m_data(nullptr), - m_nextKI(0), m_hLoad(0) - { +HphpArray::HphpArray(uint size, const TypedValue* values) + : ArrayData(kHphpArray, false, size) + , m_lastE(size - 1) + , m_hLoad(size) + , m_nextKI(size) { #ifdef PEDANTIC if (size > 0x7fffffffU) { raise_error("Cannot create an array with more than 2^31 - 1 elements"); } #endif - init(size); + initWithoutHash(size); assert(size <= m_tableMask + 1); // append values by moving -- Caller assumes we update refcount. Values // are in reverse order since they come from the stack, which grows down. // This code is hand-specialized from nextInsert(). - assert(m_size == 0 && m_hLoad == 0 && m_nextKI == 0); + assert(m_size == size && m_hLoad == size && m_nextKI == size); ElmInd* hash = m_hash; Elm* data = m_data; - for (uint i = 0; i < size; i++) { - assert(hash[i] == HphpArray::ElmIndEmpty); + uint i = 0; + for (; i < size; i++) { const TypedValue& tv = values[size - i - 1]; data[i].data.m_data = tv.m_data; data[i].data.m_type = tv.m_type; data[i].setIntKey(i); hash[i] = i; } - m_size = size; - m_hLoad = size; - m_lastE = size - 1; - m_nextKI = size; - if (size > 0) m_pos = 0; + // Initialize the leftover hash + for (; i <= m_tableMask; i++) { + hash[i] = ElmIndEmpty; + } + assert(m_size == size); + assert(m_hLoad == size); + assert(m_lastE == size - 1); + assert(m_nextKI == size); + assert(size == 0 || m_pos == 0); } -HphpArray::HphpArray(EmptyMode) : ArrayData(kHphpArray), - m_lastE(ElmIndEmpty), m_data(nullptr), m_nextKI(0), m_hLoad(0) { +HphpArray::HphpArray(EmptyMode) + : ArrayData(kHphpArray, false, 0) + , m_lastE(ElmIndEmpty) + , m_hLoad(0) + , m_nextKI(0) { init(0); setStatic(); } @@ -189,10 +207,10 @@ void HphpArray::dumpDebugInfo() const { "--- dumpDebugInfo(this=0x%08zx) ----------------------------\n", uintptr_t(this)); fprintf(stderr, "m_data = %p\tm_hash = %p\n" - "m_tableMask = %u\tm_size = %d\tm_hLoad = %d\n" - "m_nextKI = %" PRId64 "\t\tm_lastE = %d\tm_pos = %zd\n", - m_data, m_hash, m_tableMask, m_size, m_hLoad, - m_nextKI, m_lastE, m_pos); + "m_tableMask = %u\tm_size = %d\tm_hLoad = %d\n" + "m_nextKI = %" PRId64 "\t\tm_lastE = %d\tm_pos = %zd\n", + m_data, m_hash, m_tableMask, m_size, m_hLoad, + m_nextKI, m_lastE, size_t(m_pos)); fprintf(stderr, "Elements:\n"); ssize_t lastE = m_lastE; Elm* elms = m_data; @@ -394,7 +412,7 @@ Variant HphpArray::key() const { return uninit_null(); } -Variant HphpArray::value(ssize_t& pos) const { +Variant HphpArray::value(int32_t& pos) const { if (pos != ArrayData::invalid_index) { Elm* e = &m_data[pos]; assert(e->data.m_type != KindOfTombstone); @@ -703,7 +721,6 @@ void HphpArray::newElmStr(ElmInd* ei, strhash_t h, StringData* key, } void HphpArray::allocData(size_t maxElms, size_t tableSize) { - assert(!m_data); if (maxElms <= SmallSize) { m_data = m_inline_data.slots; m_hash = m_inline_data.hash; diff --git a/hphp/runtime/base/array/hphp_array.h b/hphp/runtime/base/array/hphp_array.h index 304753d74..96984fd25 100644 --- a/hphp/runtime/base/array/hphp_array.h +++ b/hphp/runtime/base/array/hphp_array.h @@ -111,7 +111,7 @@ public: Variant next(); Variant end(); Variant key() const; - Variant value(ssize_t& pos) const; + Variant value(int32_t& pos) const; Variant each(); // implements ArrayData @@ -297,6 +297,7 @@ public: // Use a minimum of an 4-element hash table. Valid range: [2..32] static const uint32_t MinLgTableSize = 2; static const uint32_t SmallHashSize = 1 << MinLgTableSize; + static const uint32_t SmallMask = SmallHashSize - 1; static const uint32_t SmallSize = SmallHashSize - SmallHashSize / LoadScale; struct InlineSlots { @@ -358,11 +359,11 @@ private: // +--------------------+ ElmInd m_lastE; // Index of last used element. + uint32_t m_tableMask; // Bitmask used when indexing into the hash table. + uint32_t m_hLoad; // Hash table load (# of non-empty slots). + int64_t m_nextKI; // Next integer key to use for append. Elm* m_data; // Contains elements and hash table. ElmInd* m_hash; // Hash table. - int64_t m_nextKI; // Next integer key to use for append. - uint32_t m_tableMask; // Bitmask used when indexing into the hash table. - uint32_t m_hLoad; // Hash table load (# of non-empty slots). union { InlineSlots m_inline_data; ElmInd m_inline_hash[sizeof(m_inline_data) / sizeof(ElmInd)]; @@ -439,9 +440,11 @@ private: void reallocData(size_t maxElms, size_t tableSize, uint oldMask); /** - * init(size) allocates space for size elements but initializes - * as an empty array + * init(size) allocates space for size elements but initializes as + * an empty array. The "WithoutHash" version does not initialize the + * hash table and returns the hash table size. */ + uint32_t initWithoutHash(uint size); void init(uint size); /** diff --git a/hphp/runtime/base/array/policy_array.cpp b/hphp/runtime/base/array/policy_array.cpp index bb55c9b0d..2e3bd45a3 100644 --- a/hphp/runtime/base/array/policy_array.cpp +++ b/hphp/runtime/base/array/policy_array.cpp @@ -284,7 +284,7 @@ Variant ArrayShell::key() const { return key(toPos(m_pos)); } -Variant ArrayShell::value(ssize_t &pos) const { +Variant ArrayShell::value(int32_t &pos) const { NOT_IMPLEMENTED(); } diff --git a/hphp/runtime/base/array/policy_array.h b/hphp/runtime/base/array/policy_array.h index 4d33154ad..0b706cc34 100644 --- a/hphp/runtime/base/array/policy_array.h +++ b/hphp/runtime/base/array/policy_array.h @@ -54,6 +54,10 @@ static uint32_t toUint32(PosType pos) { } // Signed to pos, no change in representation. +inline PosType toPos(int32_t n) { + static_assert(sizeof(int64_t) == sizeof(PosType), ""); + return static_cast(n); +} static PosType toPos(int64_t n) { static_assert(sizeof(int64_t) == sizeof(PosType), "can't"); return static_cast(n); @@ -379,7 +383,7 @@ public: virtual Variant next() FOLLY_OVERRIDE; virtual Variant end() FOLLY_OVERRIDE; virtual Variant key() const FOLLY_OVERRIDE; - virtual Variant value(ssize_t &pos) const FOLLY_OVERRIDE; + virtual Variant value(int32_t &pos) const FOLLY_OVERRIDE; virtual Variant each() FOLLY_OVERRIDE; virtual bool isInvalid() const FOLLY_OVERRIDE { diff --git a/hphp/runtime/vm/name_value_table_wrapper.cpp b/hphp/runtime/vm/name_value_table_wrapper.cpp index 58eff0905..5f5621c05 100644 --- a/hphp/runtime/vm/name_value_table_wrapper.cpp +++ b/hphp/runtime/vm/name_value_table_wrapper.cpp @@ -252,7 +252,7 @@ Variant NameValueTableWrapper::key() const { return null_variant; } -Variant NameValueTableWrapper::value(ssize_t& pos) const { +Variant NameValueTableWrapper::value(int32_t& pos) const { return current(); } diff --git a/hphp/runtime/vm/name_value_table_wrapper.h b/hphp/runtime/vm/name_value_table_wrapper.h index b57ee6811..8f275bd01 100644 --- a/hphp/runtime/vm/name_value_table_wrapper.h +++ b/hphp/runtime/vm/name_value_table_wrapper.h @@ -130,7 +130,7 @@ public: // ArrayData implementation virtual Variant end(); virtual CVarRef endRef(); virtual Variant key() const; - virtual Variant value(ssize_t &pos) const; + virtual Variant value(int32_t &pos) const; virtual Variant each(); virtual bool validFullPos(const FullPos & fp) const; virtual bool advanceFullPos(FullPos&); @@ -149,7 +149,7 @@ private: class GlobalNameValueTableWrapper : public NameValueTableWrapper { public: - GlobalNameValueTableWrapper(NameValueTable* tab); + explicit GlobalNameValueTableWrapper(NameValueTable* tab); Variant gvm_argc; Variant gvm_argv; Variant gvm__SERVER;