diff --git a/hphp/runtime/base/array/array_iterator.cpp b/hphp/runtime/base/array/array_iterator.cpp index fb04f613b..6fb3860a3 100644 --- a/hphp/runtime/base/array/array_iterator.cpp +++ b/hphp/runtime/base/array/array_iterator.cpp @@ -155,7 +155,7 @@ ArrayIter::~ArrayIter() { bool ArrayIter::endHelper() { switch (getCollectionType()) { case Collection::VectorType: { - return m_pos >= getVector()->t_count(); + return m_pos >= getVector()->size(); } case Collection::MapType: { return m_pos == 0; @@ -167,7 +167,7 @@ bool ArrayIter::endHelper() { return m_pos == 0; } case Collection::PairType: { - return m_pos >= getPair()->t_count(); + return m_pos >= getPair()->size(); } default: { ObjectData* obj = getIteratorObj(); @@ -358,6 +358,97 @@ CVarRef ArrayIter::secondRef() { return ad->getValueRef(m_pos); } +// +// Collection iterator specialized functions. +// + +template +ArrayIter::ArrayIter(Tuplish* coll, Fixed) + : m_pos(0), m_itype(ArrayIter::TypeIterator) { + assert(coll); + setObject(coll); +} + +template +ArrayIter::ArrayIter(Vectorish* coll, Versionable) + : m_pos(0), m_itype(ArrayIter::TypeIterator) { + assert(coll && coll->size() > 0); + setObject(coll); + m_version = coll->getVersion(); +} + +template +ArrayIter::ArrayIter(Mappish* coll, VersionableSparse) + : m_itype(ArrayIter::TypeIterator) { + assert(coll && coll->size() > 0); + setObject(coll); + m_version = coll->getVersion(); + m_pos = coll->iter_begin(); +} + +template +inline ALWAYS_INLINE +bool ArrayIter::iterNext(Fixed) { + return ++m_pos < static_cast(getObject())->size(); +} + +template +inline ALWAYS_INLINE +bool ArrayIter::iterNext(Versionable) { + Vectorish* vec = static_cast(getObject()); + if (UNLIKELY(m_version != vec->getVersion())) { + throw_collection_modified(); + } + return ++m_pos < vec->size(); +} + +template +inline ALWAYS_INLINE +bool ArrayIter::iterNext(VersionableSparse) { + Mappish* coll = static_cast(getObject()); + if (UNLIKELY(m_version != coll->getVersion())) { + throw_collection_modified(); + } + m_pos = coll->iter_next(m_pos); + return m_pos != 0; +} + +template +inline ALWAYS_INLINE +Variant ArrayIter::iterKey(Fixed) { + return m_pos; +} + +template +inline ALWAYS_INLINE +Variant ArrayIter::iterKey(Versionable) { + return m_pos; +} + +template +inline ALWAYS_INLINE +Variant ArrayIter::iterKey(VersionableSparse) { + return static_cast(getObject())->iter_key(m_pos); +} + +template +inline ALWAYS_INLINE +Variant ArrayIter::iterValue(Fixed) { + return tvAsCVarRef(static_cast(getObject())->get(m_pos)); +} + +template +inline ALWAYS_INLINE +Variant ArrayIter::iterValue(Versionable) { + return tvAsCVarRef(static_cast(getObject())->get(m_pos)); +} + +template +inline ALWAYS_INLINE +Variant ArrayIter::iterValue(VersionableSparse) { + return tvAsCVarRef(static_cast(getObject())->iter_value(m_pos)); +} + /////////////////////////////////////////////////////////////////////////////// // FullPos @@ -712,6 +803,80 @@ void Iter::cfree() { cuf().~CufIter(); } +/** + * Helper functions for collection style iterators. + * Iterators over collections are never by-ref so there is no reason to + * unbox any value. + * Templates are instantiated over the collection class and the iterator + * style. See the definition of Fixed, Versionable and VersionableSparse + * in the header for details. + * IterInit and IterNext can be called directly from the JIT for specialized + * iterators. + */ +template +HOT_FUNC static +void iterValue(ArrayIter* iter, TypedValue* out) { + Variant val = iter->iterValue(Style()); + assert(val.getRawType() != KindOfRef); + tvDupCell(val.asTypedValue(), out); +} + +template +HOT_FUNC static +void iterKey(ArrayIter* iter, TypedValue* out) { + Variant key = iter->iterKey(Style()); + tvDupCell(key.asTypedValue(), out); +} + +template +HOT_FUNC static +int64_t iterInit(Iter* dest, Coll* coll, + TypedValue* valOut, TypedValue* keyOut) { + int64_t size = coll->size(); + if (UNLIKELY(size == 0)) { + decRefObj(coll); + return 0LL; + } + (void) new (&dest->arr()) ArrayIter(coll, Style()); + + DataType vType = valOut->m_type; + assert(vType != KindOfRef); + uint64_t vDatum = valOut->m_data.num; + iterValue(&dest->arr(), valOut); + tvRefcountedDecRefHelper(vType, vDatum); + + if (keyOut) { + DataType kType = keyOut->m_type; + uint64_t kDatum = keyOut->m_data.num; + iterKey(&dest->arr(), keyOut); + tvRefcountedDecRefHelper(kType, kDatum); + } + return 1LL; +} + +template +HOT_FUNC static +int64_t iterNext(ArrayIter* iter, TypedValue* valOut, TypedValue* keyOut) { + if (!iter->iterNext(Style())) { + iter->~ArrayIter(); + return 0LL; + } + + DataType vType = valOut->m_type; + assert(vType != KindOfRef); + uint64_t vDatum = valOut->m_data.num; + iterValue(iter, valOut); + tvRefcountedDecRefHelper(vType, vDatum); + + if (keyOut) { + DataType kType = keyOut->m_type; + uint64_t kDatum = keyOut->m_data.num; + iterKey(iter, keyOut); + tvRefcountedDecRefHelper(kType, kDatum); + } + return 1LL; +} + /* * iter_value_cell* will store a copy of the current value at the address * given by 'out'. iter_value_cell* will increment the refcount of the current @@ -748,7 +913,7 @@ static inline void iter_value_cell_local_impl(Iter* iter, TypedValue* out) { } else { Variant val = arrIter.second(); assert(val.getRawType() != KindOfRef); - tvDupCell((TypedValue*)&val, out); + tvDupCell(val.asTypedValue(), out); } tvRefcountedDecRefHelper(oldType, oldDatum); } @@ -766,7 +931,7 @@ static inline void iter_key_cell_local_impl(Iter* iter, TypedValue* out) { arr.nvFirst(out); } else { Variant key = arr.first(); - tvDupCell((TypedValue*)&key, out); + tvDupCell(key.asTypedValue(), out); } tvRefcountedDecRefHelper(oldType, oldDatum); } @@ -914,15 +1079,17 @@ class FreeObj { }; /** - * new_iter_object creates an iterator for the specified object if the object - * is iterable and it is non-empty (has properties). If new_iter_object creates - * an iterator, it does not increment the refcount of the specified object. If - * new_iter_object does not create an iterator, it decRefs the object. + * new_iter_object_any creates an iterator for the specified object if the + * object is iterable and it is non-empty (has properties). If + * new_iter_object_any creates an iterator, it does not increment the refcount + * of the specified object. If new_iter_object does not create an iterator, + * it decRefs the object. * - * If exceptions are thrown, new_iter_object takes care of decRefing the object. + * If exceptions are thrown, new_iter_object_any takes care of decRefing the + * object. */ HOT_FUNC -int64_t new_iter_object(Iter* dest, ObjectData* obj, Class* ctx, +static int64_t new_iter_object_any(Iter* dest, ObjectData* obj, Class* ctx, TypedValue* valOut, TypedValue* keyOut) { valOut = tvToCell(valOut); if (keyOut) { @@ -931,7 +1098,7 @@ int64_t new_iter_object(Iter* dest, ObjectData* obj, Class* ctx, ArrayIter::Type itType; { FreeObj fo; - if (obj->isCollection() || obj->implementsIterator()) { + if (obj->implementsIterator()) { TRACE(2, "%s: I %p, obj %p, ctx %p, collection or Iterator\n", __func__, dest, obj, ctx); try { @@ -997,6 +1164,42 @@ int64_t new_iter_object(Iter* dest, ObjectData* obj, Class* ctx, return 1LL; } +HOT_FUNC +int64_t new_iter_object(Iter* dest, ObjectData* obj, Class* ctx, + TypedValue* valOut, TypedValue* keyOut) { + TRACE(2, "%s: I %p, obj %p, ctx %p, collection or Iterator or Object\n", + __func__, dest, obj, ctx); + Collection::Type type = obj->getCollectionType(); + switch (type) { + case Collection::VectorType: + return iterInit( + dest, static_cast(obj), + valOut, keyOut); + case Collection::MapType: + return iterInit( + dest, + static_cast(obj), + valOut, keyOut); + case Collection::StableMapType: + return iterInit( + dest, + static_cast(obj), + valOut, keyOut); + case Collection::SetType: + return iterInit( + dest, + static_cast(obj), + valOut, keyOut); + case Collection::PairType: + return iterInit( + dest, + static_cast(obj), + valOut, keyOut); + default: + return new_iter_object_any(dest, obj, ctx, valOut, keyOut); + } +} + /** * iter_next will advance the iterator to point to the next element. * If the iterator reaches the end, iter_next will free the iterator @@ -1038,6 +1241,39 @@ int64_t iter_next_cold(Iter* iter, TypedValue* valOut, TypedValue* keyOut) { return 1; } +template +HOT_FUNC +static int64_t iter_next_collection( + Iter* iter, TypedValue* valOut, TypedValue* keyOut) { + assert(iter->arr().getIterType() == ArrayIter::TypeArray || + iter->arr().getIterType() == ArrayIter::TypeIterator); + TRACE(2, "iter_next_collection: I %p\n", iter); + + ArrayIter* ai = &iter->arr(); + Collection::Type type = (ai->hasArrayData()) ? + Collection::InvalidType : + ai->getObject()->getCollectionType(); + switch (type) { + case Collection::VectorType: + return iterNext( + ai, valOut, keyOut); + case Collection::MapType: + return iterNext( + ai, valOut, keyOut); + case Collection::StableMapType: + return iterNext( + ai, valOut, keyOut); + case Collection::SetType: + return iterNext( + ai, valOut, keyOut); + case Collection::PairType: + return iterNext( + ai, valOut, keyOut); + default: + return iter_next_cold(iter, valOut, keyOut); + } +} + HOT_FUNC int64_t iter_next(Iter* iter, TypedValue* valOut) { TRACE(2, "iter_next: I %p\n", iter); @@ -1078,7 +1314,7 @@ int64_t iter_next(Iter* iter, TypedValue* valOut) { return 1; } cold: - return iter_next_cold(iter, valOut, nullptr); + return iter_next_collection(iter, valOut, nullptr); } template @@ -1132,7 +1368,7 @@ int64_t iter_next_key(Iter* iter, TypedValue* valOut, TypedValue* keyOut) { return 1; } cold: - return iter_next_cold(iter, valOut, keyOut); + return iter_next_collection(iter, valOut, keyOut); } template int64_t iter_next_key(Iter* dest, diff --git a/hphp/runtime/base/array/array_iterator.h b/hphp/runtime/base/array/array_iterator.h index 7888b9ea6..7696c3dde 100644 --- a/hphp/runtime/base/array/array_iterator.h +++ b/hphp/runtime/base/array/array_iterator.h @@ -162,7 +162,65 @@ class ArrayIter { return (!hasArrayData() && !getObject()->isCollection()); } - public: + // + // Specialized iterator for collections. Used via JIT + // + + /** + * Fixed is used for collections that are immutable in size. + * Templatized Fixed functions expect the collection to implement + * size() and get(). + * The key is the current position of the iterator. + */ + enum class Fixed {}; + /** + * Versionable is used for collections that are mutable and throw if + * an insertion or deletion is made to the collection while iterating. + * Templatized Versionable functions expect the collection to implement + * size(), getVersion() and get(). + * The key is the current position of the iterator. + */ + enum class Versionable {}; + /** + * VersionableSparse is used for collections that are mutable and throw if + * an insertion or deletion is made to the collection while iterating. + * Moreover the collection elements are accessed via an iterator. + * Templatized VersionableSparse functions expect the collection to implement + * getVersion(), iter_begin(), iter_next(), iter_value() and iter_key(). + */ + enum class VersionableSparse {}; + + // Constructors + template + ArrayIter(Tuplish* coll, Fixed); + template + ArrayIter(Vectorish* coll, Versionable); + template + ArrayIter(Mappish* coll, VersionableSparse); + + // iterator "next", "value", "key" functions + template + bool iterNext(Fixed); + template + bool iterNext(Versionable); + template + bool iterNext(VersionableSparse); + + template + Variant iterValue(Fixed); + template + Variant iterValue(Versionable); + template + Variant iterValue(VersionableSparse); + + template + Variant iterKey(Fixed); + template + Variant iterKey(Versionable); + template + Variant iterKey(VersionableSparse); + + public: const ArrayData* getArrayData() { assert(hasArrayData()); return m_data; @@ -180,7 +238,12 @@ class ArrayIter { m_itype = iterType; } - private: + ObjectData* getObject() { + assert(!hasArrayData()); + return (ObjectData*)((intptr_t)m_obj & ~1); + } + + private: c_Vector* getVector() { assert(hasCollection() && getCollectionType() == Collection::VectorType); return (c_Vector*)((intptr_t)m_obj & ~1); @@ -201,10 +264,6 @@ class ArrayIter { assert(hasCollection() && getCollectionType() == Collection::PairType); return (c_Pair*)((intptr_t)m_obj & ~1); } - ObjectData* getObject() { - assert(!hasArrayData()); - return (ObjectData*)((intptr_t)m_obj & ~1); - } Collection::Type getCollectionType() { ObjectData* obj = getObject(); return obj->getCollectionType(); diff --git a/hphp/runtime/ext/ext_collections.h b/hphp/runtime/ext/ext_collections.h index fddab2c3f..e5dd024df 100644 --- a/hphp/runtime/ext/ext_collections.h +++ b/hphp/runtime/ext/ext_collections.h @@ -132,10 +132,10 @@ class c_Vector : public ExtObjectDataFlags