diff --git a/hphp/doc/ir.specification b/hphp/doc/ir.specification index aba9c8d99..483ece63f 100755 --- a/hphp/doc/ir.specification +++ b/hphp/doc/ir.specification @@ -168,6 +168,8 @@ above PHP-facing types. FramePtr Pointer to a frame on the VM execution stack TCA Machine code address Nullptr Represents a null pointer + Iter Represents an iterator type instantiated by IterInit* + Iter Specific iterator type There is also one special type which represents all the possible types that can be on the stack. @@ -395,6 +397,13 @@ D:StkPtr = GuardStk S0:StkPtr Returns a new StkPtr that represents the same stack but with the knowledge that the slot at the index S1 has type T. +GuardIter,iterId> S0:FramePtr + + Guard that the type of the given iterator in iterId on the frame S0 is + a subtype of the iterator type Iter; if not, make a fallback + jump. (A jump to a service request that chains to a retranslation for + this tracelet.) + D:StkPtr = CheckStk S0:StkPtr -> L Check that the type of the cell on the stack pointed to by S0 at @@ -1660,7 +1669,19 @@ D:Bool = MIterInitK S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt S4:ConstInt into the new iterator. D:Bool = IterNext S0:FramePtr S1:ConstInt S2:ConstInt +D:Bool = IterNextArray S0:FramePtr S1:ConstInt S2:ConstInt +D:Bool = IterNextPair S0:FramePtr S1:ConstInt S2:ConstInt +D:Bool = IterNextVector S0:FramePtr S1:ConstInt S2:ConstInt +D:Bool = IterNextMap S0:FramePtr S1:ConstInt S2:ConstInt +D:Bool = IterNextStableMap S0:FramePtr S1:ConstInt S2:ConstInt +D:Bool = IterNextSet S0:FramePtr S1:ConstInt S2:ConstInt D:Bool = IterNextK S0:FramePtr S1:ConstInt S2:ConstInt S3:ConstInt +D:Bool = IterNextKArray S0:FramePtr S1:ConstInt S2:ConstInt +D:Bool = IterNextKPair S0:FramePtr S1:ConstInt S2:ConstInt +D:Bool = IterNextKVector S0:FramePtr S1:ConstInt S2:ConstInt +D:Bool = IterNextKMap S0:FramePtr S1:ConstInt S2:ConstInt +D:Bool = IterNextKStableMap S0:FramePtr S1:ConstInt S2:ConstInt +D:Bool = IterNextKSet S0:FramePtr S1:ConstInt S2:ConstInt D:Bool = WIterNext S0:FramePtr S1:ConstInt S2:ConstInt D:Bool = WIterNextK S0:FramePtr S1:ConstInt S2:ConstInt S3:ConstInt D:Bool = MIterNext S0:FramePtr S1:ConstInt S2:ConstInt @@ -1676,9 +1697,9 @@ D:Bool = MIterNextK S0:FramePtr S1:ConstInt S2:ConstInt S3:ConstInt the iterator's next value (and key) into the local variable S3 (and S4, respectively). - The IterInit and IterInitK instructions always copy the array + The IterNext* and IterNextK* instructions always copy the array element by value. - The WIterInit and WIterInitK instructions copy referenced array + The WIterNext and WIterNextK instructions copy referenced array elements by reference, and non-referenced array elements by value. The MIterNext and MIterNextK instructions always copy the array element by reference. diff --git a/hphp/runtime/base/array_iterator.cpp b/hphp/runtime/base/array_iterator.cpp index 0b45166a5..fa6fee793 100644 --- a/hphp/runtime/base/array_iterator.cpp +++ b/hphp/runtime/base/array_iterator.cpp @@ -21,6 +21,7 @@ #include "hphp/runtime/base/complex_types.h" #include "hphp/runtime/base/object_data.h" #include "hphp/runtime/ext/ext_collections.h" +#include "hphp/runtime/ext/ext_collections_def.h" // inline methods of HphpArray. #include "hphp/runtime/base/hphp_array-defs.h" @@ -152,7 +153,7 @@ ArrayIter::~ArrayIter() { decRefObj(obj); } if (debug) { - m_itype = TypeUndefined; + m_ikind = IterKind::Undefined; } } @@ -367,92 +368,142 @@ CVarRef ArrayIter::secondRef() { // template -ArrayIter::ArrayIter(Tuplish* coll, Fixed) - : m_pos(0), m_itype(ArrayIter::TypeIterator) { +ArrayIter::ArrayIter(Tuplish* coll, IterKind iterKind, Fixed) + : m_pos(0), m_ikind(iterKind) { assert(coll); setObject(coll); } template -ArrayIter::ArrayIter(Vectorish* coll, Versionable) - : m_pos(0), m_itype(ArrayIter::TypeIterator) { +ArrayIter::ArrayIter(Vectorish* coll, IterKind iterKind, Versionable) + : m_pos(0), m_ikind(iterKind) { assert(coll && coll->size() > 0); setObject(coll); m_version = coll->getVersion(); } template -ArrayIter::ArrayIter(Mappish* coll, VersionableSparse) - : m_itype(ArrayIter::TypeIterator) { +ArrayIter::ArrayIter(Mappish* coll, IterKind iterKind, VersionableSparse) + : m_pos(0), m_ikind(iterKind) { 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(); +int64_t ArrayIter::iterInit(Fixed, TypedValue* valOut) { + return static_cast(getObject())->iterInit(valOut); } template inline ALWAYS_INLINE -bool ArrayIter::iterNext(Versionable) { +int64_t ArrayIter::iterInit(Versionable, TypedValue* valOut) { + return static_cast(getObject())->iterInit(valOut); +} + +template +inline ALWAYS_INLINE +int64_t ArrayIter::iterInit(VersionableSparse, TypedValue* valOut) { + m_pos = static_cast(getObject())->iterInit(valOut); + return m_pos; +} + +template +inline ALWAYS_INLINE +int64_t ArrayIter::iterInitK(Fixed, TypedValue* valOut, TypedValue* keyOut) { + return static_cast(getObject())->iterInitK(valOut, keyOut); +} + +template +inline ALWAYS_INLINE +int64_t ArrayIter::iterInitK( + Versionable, TypedValue* valOut, TypedValue* keyOut) { + return static_cast(getObject())->iterInitK(valOut, keyOut); +} + +template +inline ALWAYS_INLINE +int64_t ArrayIter::iterInitK( + VersionableSparse, TypedValue* valOut, TypedValue* keyOut) { + m_pos = static_cast(getObject())->iterInitK(valOut, keyOut); + return m_pos; +} + +template +inline ALWAYS_INLINE +int64_t ArrayIter::iterNext(Fixed, TypedValue* valOut) { + int64_t pos = static_cast(getObject())->iterNext(m_pos, valOut); + if (pos != 0) { + m_pos = pos; + } + return pos; +} + +template +inline ALWAYS_INLINE +int64_t ArrayIter::iterNext(Versionable, TypedValue* valOut) { Vectorish* vec = static_cast(getObject()); if (UNLIKELY(m_version != vec->getVersion())) { throw_collection_modified(); } - return ++m_pos < vec->size(); + int pos = vec->iterNext(m_pos, valOut); + if (LIKELY(pos != 0)) { + m_pos = pos; + } + return pos; } template inline ALWAYS_INLINE -bool ArrayIter::iterNext(VersionableSparse) { +int64_t ArrayIter::iterNext(VersionableSparse, TypedValue* valOut) { 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; + m_pos = coll->iterNext(m_pos, valOut); + return m_pos; } template inline ALWAYS_INLINE -Variant ArrayIter::iterKey(Fixed) { - return m_pos; +int64_t ArrayIter::iterNextKey( + Fixed, TypedValue* valOut, TypedValue* keyOut) { + auto pos = static_cast(getObject())->iterNextK( + m_pos, valOut, keyOut); + if (pos != 0) { + m_pos = pos; + } + return pos; } template inline ALWAYS_INLINE -Variant ArrayIter::iterKey(Versionable) { +int64_t ArrayIter::iterNextKey( + Versionable, TypedValue* valOut, TypedValue* keyOut) { + Vectorish* vec = static_cast(getObject()); + if (UNLIKELY(m_version != vec->getVersion())) { + throw_collection_modified(); + } + auto pos = vec->iterNextK(m_pos, valOut, keyOut); + if (LIKELY(pos != 0)) { + m_pos = pos; + } + return pos; +} + +template +inline ALWAYS_INLINE +int64_t ArrayIter::iterNextKey( + VersionableSparse, TypedValue* valOut, TypedValue* keyOut) { + Mappish* coll = static_cast(getObject()); + if (UNLIKELY(m_version != coll->getVersion())) { + throw_collection_modified(); + } + m_pos = coll->iterNextK(m_pos, valOut, keyOut); 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 @@ -669,26 +720,56 @@ CufIter::~CufIter() { if (m_name) decRefStr(m_name); } +static +ArrayIter::IterKind getCollectionIterKind(ObjectData* obj) { + ArrayIter::IterKind iterKind = ArrayIter::IterKind::Undefined; + Collection::Type collType = obj->getCollectionType(); + switch (collType) { + case Collection::VectorType: + iterKind = ArrayIter::IterKind::Vector; + break; + case Collection::MapType: + iterKind = ArrayIter::IterKind::Map; + break; + case Collection::StableMapType: + iterKind = ArrayIter::IterKind::StableMap; + break; + case Collection::SetType: + iterKind = ArrayIter::IterKind::Set; + break; + case Collection::PairType: + iterKind = ArrayIter::IterKind::Pair; + break; + default: + iterKind = ArrayIter::IterKind::Undefined; + break; + } + return iterKind; +} + bool Iter::init(TypedValue* c1) { assert(c1->m_type != KindOfRef); bool hasElems = true; if (c1->m_type == KindOfArray) { if (!c1->m_data.parr->empty()) { (void) new (&arr()) ArrayIter(c1->m_data.parr); - arr().setIterType(ArrayIter::TypeArray); + arr().setIterKind(ArrayIter::IterKind::Array); } else { hasElems = false; } } else if (c1->m_type == KindOfObject) { - bool isIterator; + ArrayIter::IterKind iterKind = ArrayIter::IterKind::Undefined; if (c1->m_data.pobj->isCollection()) { - isIterator = true; (void) new (&arr()) ArrayIter(c1->m_data.pobj); + iterKind = getCollectionIterKind(c1->m_data.pobj); } else { + bool isIterator; Object obj = c1->m_data.pobj->iterableObject(isIterator); if (isIterator) { + iterKind = ArrayIter::IterKind::Iterator; (void) new (&arr()) ArrayIter(obj, ArrayIter::transferOwner); } else { + iterKind = ArrayIter::IterKind::Array; Class* ctx = arGetContextClass(g_vmContext->getFP()); CStrRef ctxStr = ctx ? ctx->nameRef() : null_string; Array iterArray(obj->o_toIterArray(ctxStr)); @@ -703,8 +784,7 @@ bool Iter::init(TypedValue* c1) { arr().~ArrayIter(); hasElems = false; } else { - arr().setIterType( - isIterator ? ArrayIter::TypeIterator : ArrayIter::TypeArray); + arr().setIterKind(iterKind); } } catch (...) { arr().~ArrayIter(); @@ -718,8 +798,7 @@ bool Iter::init(TypedValue* c1) { } bool Iter::next() { - assert(arr().getIterType() == ArrayIter::TypeArray || - arr().getIterType() == ArrayIter::TypeIterator); + assert(arr().getIterKind() != ArrayIter::IterKind::Undefined); // The emitter should never generate bytecode where the iterator // is at the end before IterNext is executed. However, even if // the iterator is at the end, it is safe to call next(). @@ -738,8 +817,7 @@ bool Iter::next() { } void Iter::free() { - assert(arr().getIterType() == ArrayIter::TypeArray || - arr().getIterType() == ArrayIter::TypeIterator); + assert(arr().getIterKind() != ArrayIter::IterKind::Undefined); arr().~ArrayIter(); } @@ -761,70 +839,159 @@ void Iter::cfree() { * 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); - cellDup(*val.asTypedValue(), *out); -} -template -HOT_FUNC static -void iterKey(ArrayIter* iter, TypedValue* out) { - Variant key = iter->iterKey(Style()); - cellDup(*key.asTypedValue(), *out); -} +// +// JIT helper functions for IterInit and IterInitK instructions over +// collection iterators. +// -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); +template +HOT_FUNC +int64_t iterInit(Iter* dest, Coll* coll, TypedValue* valOut) { + (void) new (&dest->arr()) ArrayIter(coll, iterKind, Style()); + int64_t res = dest->arr().iterInit(Style(), valOut); + if (res == 0LL) { + dest->arr().~ArrayIter(); 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 int64_t iterInit( + Iter* dest, c_Pair* p, TypedValue* valOut); +template int64_t iterInit( + Iter* dest, c_Vector* v, TypedValue* valOut); +template int64_t iterInit( + Iter* dest, c_Map* m, TypedValue* valOut); +template int64_t iterInit( + Iter* dest, c_StableMap* sm, TypedValue* valOut); +template int64_t iterInit( + Iter* dest, c_Set* s, TypedValue* valOut); + +template +HOT_FUNC +int64_t iterInitK(Iter* dest, Coll* coll, + TypedValue* valOut, TypedValue* keyOut) { + (void) new (&dest->arr()) ArrayIter(coll, iterKind, Style()); + int64_t res; + if (keyOut != nullptr) { + res = dest->arr().iterInitK(Style(), valOut, keyOut); + } else { + res = dest->arr().iterInit(Style(), valOut); + } + if (res == 0LL) { + dest->arr().~ArrayIter(); + return 0LL; + } + return 1LL; +} + +template int64_t iterInitK( + Iter* dest, c_Pair* coll, + TypedValue* valOut, TypedValue* keyOut); +template int64_t iterInitK( + Iter* dest, c_Vector* coll, + TypedValue* valOut, TypedValue* keyOut); +template int64_t iterInitK( + Iter* dest, c_Map* coll, + TypedValue* valOut, TypedValue* keyOut); +template int64_t iterInitK( + Iter* dest, c_StableMap* coll, + TypedValue* valOut, TypedValue* keyOut); +template int64_t iterInitK( + Iter* dest, c_Set* coll, + TypedValue* valOut, TypedValue* keyOut); + +// +// JIT helpers function for IterNext and IterNextK instruction over +// collection iterators. +// + template -HOT_FUNC static -int64_t iterNext(ArrayIter* iter, TypedValue* valOut, TypedValue* keyOut) { - if (!iter->iterNext(Style())) { +HOT_FUNC +int64_t iterNext(ArrayIter* iter, TypedValue* valOut) { + DataType vType = valOut->m_type; + assert(vType != KindOfRef); + uint64_t vDatum = valOut->m_data.num; + if (!iter->iterNext(Style(), valOut)) { iter->~ArrayIter(); return 0LL; } + tvRefcountedDecRefHelper(vType, vDatum); + return 1LL; +} +template int64_t iterNext( + ArrayIter* iter, TypedValue* valOut); +template int64_t iterNext( + ArrayIter* iter, TypedValue* valOut); +template int64_t iterNext( + ArrayIter* iter, TypedValue* valOut); +template int64_t iterNext( + ArrayIter* iter, TypedValue* valOut); +template int64_t iterNext( + ArrayIter* iter, TypedValue* valOut); + +template +HOT_FUNC +int64_t iterNextK(ArrayIter* iter, TypedValue* valOut, TypedValue* keyOut) { DataType vType = valOut->m_type; assert(vType != KindOfRef); uint64_t vDatum = valOut->m_data.num; - iterValue(iter, valOut); + DataType kType = keyOut->m_type; + uint64_t kDatum = keyOut->m_data.num; + if (!iter->iterNextKey(Style(), valOut, keyOut)) { + iter->~ArrayIter(); + return 0LL; + } tvRefcountedDecRefHelper(vType, vDatum); - - if (keyOut) { - DataType kType = keyOut->m_type; - uint64_t kDatum = keyOut->m_data.num; - iterKey(iter, keyOut); + if (refCountKey == ArrayIter::RefCountKey::Refcount) { tvRefcountedDecRefHelper(kType, kDatum); } return 1LL; } +template int64_t iterNextK( + ArrayIter* iter, TypedValue* valOut, TypedValue* keyOut); +template int64_t iterNextK( + ArrayIter* iter, TypedValue* valOut, TypedValue* keyOut); +template int64_t iterNextK( + ArrayIter* iter, TypedValue* valOut, TypedValue* keyOut); +template int64_t iterNextK( + ArrayIter* iter, TypedValue* valOut, TypedValue* keyOut); +template int64_t iterNextK( + ArrayIter* iter, TypedValue* valOut, TypedValue* keyOut); + /* * 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 @@ -847,8 +1014,10 @@ static inline void iter_value_cell_local_impl(Iter* iter, TypedValue* out) { uint64_t oldDatum = out->m_data.num; TRACE(2, "%s: typeArray: %s, I %p, out %p\n", __func__, typeArray ? "true" : "false", iter, out); - assert((typeArray && iter->arr().getIterType() == ArrayIter::TypeArray) || - (!typeArray && iter->arr().getIterType() == ArrayIter::TypeIterator)); + assert((typeArray && iter->arr().getIterKind() + == ArrayIter::IterKind::Array) || + (!typeArray && iter->arr().getIterKind() + != ArrayIter::IterKind::Array)); ArrayIter& arrIter = iter->arr(); if (typeArray) { TypedValue* cur = arrIter.nvSecond(); @@ -872,8 +1041,10 @@ static inline void iter_key_cell_local_impl(Iter* iter, TypedValue* out) { assert(withRef || oldType != KindOfRef); uint64_t oldDatum = out->m_data.num; TRACE(2, "%s: I %p, out %p\n", __func__, iter, out); - assert((typeArray && iter->arr().getIterType() == ArrayIter::TypeArray) || - (!typeArray && iter->arr().getIterType() == ArrayIter::TypeIterator)); + assert((typeArray && iter->arr().getIterKind() + == ArrayIter::IterKind::Array) || + (!typeArray && iter->arr().getIterKind() + != ArrayIter::IterKind::Array)); ArrayIter& arr = iter->arr(); if (typeArray) { arr.nvFirst(out); @@ -899,7 +1070,7 @@ int64_t new_iter_array_cold(Iter* dest, ArrayData* arr, TypedValue* valOut, // We are transferring ownership of the array to the iterator, therefore // we do not need to adjust the refcount. (void) new (&dest->arr()) ArrayIter(arr, ArrayIter::noInc); - dest->arr().setIterType(ArrayIter::TypeArray); + dest->arr().setIterKind(ArrayIter::IterKind::Array); iter_value_cell_local_impl(dest, valOut); if (keyOut) { iter_key_cell_local_impl(dest, keyOut); @@ -929,7 +1100,7 @@ int64_t new_iter_array(Iter* dest, ArrayData* ad, TypedValue* valOut) { // We are transferring ownership of the array to the iterator, therefore // we do not need to adjust the refcount. (void) new (&dest->arr()) ArrayIter(arr, ArrayIter::noIncNonNull); - dest->arr().setIterType(ArrayIter::TypeArray); + dest->arr().setIterKind(ArrayIter::IterKind::Array); arr->getArrayElm(dest->arr().m_pos, valOut, nullptr); return 1LL; } @@ -971,7 +1142,7 @@ int64_t new_iter_array_key(Iter* dest, ArrayData* ad, // We are transferring ownership of the array to the iterator, therefore // we do not need to adjust the refcount. (void) new (&dest->arr()) ArrayIter(arr, ArrayIter::noIncNonNull); - dest->arr().setIterType(ArrayIter::TypeArray); + dest->arr().setIterKind(ArrayIter::IterKind::Array); arr->getArrayElm(dest->arr().m_pos, valOut, keyOut); return 1LL; } @@ -1020,7 +1191,7 @@ static int64_t new_iter_object_any(Iter* dest, ObjectData* obj, Class* ctx, if (keyOut) { keyOut = tvToCell(keyOut); } - ArrayIter::Type itType; + ArrayIter::IterKind itKind; { FreeObj fo; if (obj->implementsIterator()) { @@ -1032,7 +1203,7 @@ static int64_t new_iter_object_any(Iter* dest, ObjectData* obj, Class* ctx, decRefObj(obj); throw; } - itType = ArrayIter::TypeIterator; + itKind = ArrayIter::IterKind::Iterator; } else { bool isIteratorAggregate; /* @@ -1050,7 +1221,7 @@ static int64_t new_iter_object_any(Iter* dest, ObjectData* obj, Class* ctx, TRACE(2, "%s: I %p, obj %p, ctx %p, IteratorAggregate\n", __func__, dest, obj, ctx); (void) new (&dest->arr()) ArrayIter(itObj, ArrayIter::transferOwner); - itType = ArrayIter::TypeIterator; + itKind = ArrayIter::IterKind::Iterator; } else { TRACE(2, "%s: I %p, obj %p, ctx %p, iterate as array\n", __func__, dest, obj, ctx); @@ -1058,7 +1229,7 @@ static int64_t new_iter_object_any(Iter* dest, ObjectData* obj, Class* ctx, Array iterArray(itObj->o_toIterArray(ctxStr)); ArrayData* ad = iterArray.get(); (void) new (&dest->arr()) ArrayIter(ad); - itType = ArrayIter::TypeArray; + itKind = ArrayIter::IterKind::Array; } } try { @@ -1074,8 +1245,8 @@ static int64_t new_iter_object_any(Iter* dest, ObjectData* obj, Class* ctx, } } - dest->arr().setIterType(itType); - if (itType == ArrayIter::TypeIterator) { + dest->arr().setIterKind(itKind); + if (itKind == ArrayIter::IterKind::Iterator) { iter_value_cell_local_impl(dest, valOut); if (keyOut) { iter_key_cell_local_impl(dest, keyOut); @@ -1097,26 +1268,36 @@ int64_t new_iter_object(Iter* dest, ObjectData* obj, Class* ctx, Collection::Type type = obj->getCollectionType(); switch (type) { case Collection::VectorType: - return iterInit( + return iterInitK( dest, static_cast(obj), valOut, keyOut); case Collection::MapType: - return iterInit( + return iterInitK( dest, static_cast(obj), valOut, keyOut); case Collection::StableMapType: - return iterInit( + return iterInitK( dest, static_cast(obj), valOut, keyOut); case Collection::SetType: - return iterInit( + return iterInitK( dest, static_cast(obj), valOut, keyOut); case Collection::PairType: - return iterInit( + return iterInitK( dest, static_cast(obj), valOut, keyOut); @@ -1125,26 +1306,17 @@ int64_t new_iter_object(Iter* dest, ObjectData* obj, Class* ctx, } } -/** - * iter_next will advance the iterator to point to the next element. - * If the iterator reaches the end, iter_next will free the iterator - * and will decRef the array. - * This function has been split into hot and cold parts. The hot part has - * been carefully crafted so that it's a leaf function (after all functions - * it calls have been trivially inlined) that then tail calls a cold - * version of itself (iter_next_array_cold). The hot part should cover the - * common case, which occurs when the array parameter is an HphpArray. - * If you make any changes to this function, please keep the hot/cold - * splitting in mind, and disasemble the optimized version of the binary - * to make sure the hot part is a good-looking leaf function; otherwise, - * you're likely to get a performance regression. - */ template NEVER_INLINE -int64_t iter_next_cold(Iter* iter, TypedValue* valOut, TypedValue* keyOut) { - TRACE(2, "iter_next_cold: I %p\n", iter); - assert(iter->arr().getIterType() == ArrayIter::TypeArray || - iter->arr().getIterType() == ArrayIter::TypeIterator); +int64_t iter_next_any( + Iter* iter, TypedValue* valOut, TypedValue* keyOut) { + TRACE(2, "iter_next_any: I %p\n", iter); + + valOut = tvToCell(valOut); + if (keyOut != nullptr) { + keyOut = tvToCell(keyOut); + } + ArrayIter* ai = &iter->arr(); ai->next(); if (ai->end()) { @@ -1152,7 +1324,7 @@ int64_t iter_next_cold(Iter* iter, TypedValue* valOut, TypedValue* keyOut) { ai->~ArrayIter(); return 0; } - if (iter->arr().getIterType() == ArrayIter::TypeArray) { + if (iter->arr().getIterKind() == ArrayIter::IterKind::Array) { iter_value_cell_local_impl(iter, valOut); if (keyOut) { iter_key_cell_local_impl(iter, keyOut); @@ -1166,49 +1338,31 @@ 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); +template int64_t iter_next_any( + Iter* iter, TypedValue* valOut, TypedValue* keyOut); - 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); - } -} +/** + * iter_next will advance the iterator to point to the next element. + * If the iterator reaches the end, iter_next will free the iterator + * and will decRef the array. + * This function has been split into hot and cold parts. The hot part has + * been carefully crafted so that it's a leaf function (after all functions + * it calls have been trivially inlined) that then tail calls a cold + * version of itself (iter_next_cold). The hot part should cover the + * common case, which occurs when the array parameter is an HphpArray. + * If you make any changes to this function, please keep the hot/cold + * splitting in mind, and disasemble the optimized version of the binary + * to make sure the hot part is a good-looking leaf function; otherwise, + * you're likely to get a performance regression. + */ +// Invoked for IterNextKArray HOT_FUNC int64_t iter_next(Iter* iter, TypedValue* valOut) { TRACE(2, "iter_next: I %p\n", iter); - assert(iter->arr().getIterType() == ArrayIter::TypeArray || - iter->arr().getIterType() == ArrayIter::TypeIterator); + assert(iter->arr().getIterKind() == ArrayIter::IterKind::Array); ArrayIter* arrIter = &iter->arr(); valOut = tvToCell(valOut); - if (UNLIKELY(!arrIter->hasArrayData())) { - goto cold; - } { const ArrayData* ad = arrIter->getArrayData(); if (UNLIKELY(!ad->isHphpArray())) { @@ -1223,7 +1377,7 @@ int64_t iter_next(Iter* iter, TypedValue* valOut) { } arr->decRefCount(); if (debug) { - iter->arr().setIterType(ArrayIter::TypeUndefined); + iter->arr().setIterKind(ArrayIter::IterKind::Undefined); } return 0; } @@ -1237,15 +1391,15 @@ int64_t iter_next(Iter* iter, TypedValue* valOut) { return 1; } cold: - return iter_next_collection(iter, valOut, nullptr); + return iter_next_any(iter, valOut, nullptr); } +// Invoked for IterNextKArray and the WIterNext* instructions template HOT_FUNC int64_t iter_next_key(Iter* iter, TypedValue* valOut, TypedValue* keyOut) { TRACE(2, "iter_next_key: I %p\n", iter); - assert(iter->arr().getIterType() == ArrayIter::TypeArray || - iter->arr().getIterType() == ArrayIter::TypeIterator); + assert(iter->arr().getIterKind() != ArrayIter::IterKind::Undefined); ArrayIter* arrIter = &iter->arr(); if (!withRef) { valOut = tvToCell(valOut); @@ -1269,7 +1423,7 @@ int64_t iter_next_key(Iter* iter, TypedValue* valOut, TypedValue* keyOut) { } arr->decRefCount(); if (debug) { - iter->arr().setIterType(ArrayIter::TypeUndefined); + iter->arr().setIterKind(ArrayIter::IterKind::Undefined); } return 0; } @@ -1289,7 +1443,7 @@ int64_t iter_next_key(Iter* iter, TypedValue* valOut, TypedValue* keyOut) { return 1; } cold: - return iter_next_collection(iter, valOut, keyOut); + return iter_next_any(iter, valOut, keyOut); } template int64_t iter_next_key(Iter* dest, diff --git a/hphp/runtime/base/array_iterator.h b/hphp/runtime/base/array_iterator.h index 0c2aa14ed..a6f049739 100644 --- a/hphp/runtime/base/array_iterator.h +++ b/hphp/runtime/base/array_iterator.h @@ -48,13 +48,46 @@ struct Iter; */ class ArrayIter { public: - enum Type { - TypeUndefined = 0, - TypeArray, - TypeIterator // for objects that implement Iterator or - // IteratorAggregate + enum class IterKind { + Undefined = 0, + Array, + Iterator, // for objects that implement Iterator or + // IteratorAggregate + Pair, + Vector, + Map, + StableMap, + Set }; + static const std::string typeAsString(IterKind iterKind) { + switch (iterKind) { + case IterKind::Undefined: + return "Undefined"; + case IterKind::Array: + return "Array"; + case IterKind::Iterator: + return "Iterator"; + case IterKind::Vector: + return "Vector"; + case IterKind::Map: + return "Map"; + case IterKind::StableMap: + return "StableMap"; + case IterKind::Set: + return "Set"; + case IterKind::Pair: + return "Pair"; + } + assert(false); + return "Unknown"; + } + + static size_t getOffsetOfIterKind() { + // For assembly linkage. + return offsetof(ArrayIter, m_ikind); + } + /** * Constructors. */ @@ -169,57 +202,71 @@ class ArrayIter { /** * 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(). + * Moreover the collection elements are accessed via an iterator exposed + * by the collection class. */ enum class VersionableSparse {}; + /** + * Whether the key needs to be refcount'ed or not. For Vector, Pair and + * Set there is no reason to refcount the key as that is an int (Pair, + * Vecotr) or null (Set). + */ + enum class RefCountKey { DontRefcount, Refcount }; + // Constructors template - ArrayIter(Tuplish* coll, Fixed); + ArrayIter(Tuplish* coll, IterKind iterKind, Fixed); template - ArrayIter(Vectorish* coll, Versionable); + ArrayIter(Vectorish* coll, IterKind iterKind, Versionable); template - ArrayIter(Mappish* coll, VersionableSparse); + ArrayIter(Mappish* coll, IterKind iterKind, VersionableSparse); - // iterator "next", "value", "key" functions + // Iterator init and next functions. These methods are modeled after the + // corresponding HHIR opcodes IterInit, IterIntK, and so on. The idea is + // to have ArrayIter construct an iterator, and to map the HHIR instructions + // directly to these methods. + // Todo (#2624480) - We want to create specialized IterInit and IterNext + // opcodes for every kind of collection and array shape, and possibly teach + // the JIT to inline some of them. + // For now only collections go through these helpers. template - bool iterNext(Fixed); + int64_t iterInit(Fixed, TypedValue* valOut); template - bool iterNext(Versionable); + int64_t iterInit(Versionable, TypedValue* valOut); template - bool iterNext(VersionableSparse); + int64_t iterInit(VersionableSparse, TypedValue* valOut); + template + int64_t iterInitK(Fixed, TypedValue* valOut, TypedValue* keyOut); + template + int64_t iterInitK(Versionable, TypedValue* valOut, TypedValue* keyOut); + template + int64_t iterInitK( + VersionableSparse, TypedValue* valOut, TypedValue* keyOut); template - Variant iterValue(Fixed); + int64_t iterNext(Fixed, TypedValue* valOut); template - Variant iterValue(Versionable); + int64_t iterNext(Versionable, TypedValue* valOut); template - Variant iterValue(VersionableSparse); - + int64_t iterNext(VersionableSparse, TypedValue* valOut); template - Variant iterKey(Fixed); + int64_t iterNextKey(Fixed, TypedValue* valOut, TypedValue* keyOut); template - Variant iterKey(Versionable); + int64_t iterNextKey(Versionable, TypedValue* valOut, TypedValue* keyOut); template - Variant iterKey(VersionableSparse); + int64_t iterNextKey( + VersionableSparse, TypedValue* valOut, TypedValue* keyOut); public: const ArrayData* getArrayData() { @@ -232,11 +279,11 @@ class ArrayIter { void setPos(ssize_t newPos) { m_pos = newPos; } - Type getIterType() const { - return m_itype; + IterKind getIterKind() const { + return m_ikind; } - void setIterType(Type iterType) { - m_itype = iterType; + void setIterKind(IterKind iterKind) { + m_ikind = iterKind; } ObjectData* getObject() { @@ -296,7 +343,7 @@ class ArrayIter { ssize_t m_pos; private: int m_version; - Type m_itype; + IterKind m_ikind; friend struct Iter; }; @@ -565,6 +612,17 @@ int64_t new_iter_object(Iter* dest, ObjectData* obj, Class* ctx, int64_t iter_next(Iter* dest, TypedValue* val); template int64_t iter_next_key(Iter* dest, TypedValue* val, TypedValue* key); +template +int64_t iter_next_any(Iter* dest, TypedValue* val, TypedValue* key); +template +int64_t iterInit(Iter* dest, Coll* coll, TypedValue* valOut); +template +int64_t iterInitK(Iter* dest, Coll* coll, + TypedValue* valOut, TypedValue* keyOut); +template +int64_t iterNext(ArrayIter* iter, TypedValue* valOut); +template +int64_t iterNextK(ArrayIter* iter, TypedValue* valOut, TypedValue* keyOut); int64_t new_miter_array_key(Iter* dest, RefData* arr, TypedValue* val, diff --git a/hphp/runtime/ext/ext_collections.h b/hphp/runtime/ext/ext_collections.h index 3e67cf028..12edcade0 100644 --- a/hphp/runtime/ext/ext_collections.h +++ b/hphp/runtime/ext/ext_collections.h @@ -167,6 +167,11 @@ class c_Vector : public ExtObjectDataFlags= m_size)) { + return 0LL; + } + cellDup(m_data[0], *valOut); + return 1LL; +} + +inline int64_t c_Vector::iterInitK(TypedValue* valOut, TypedValue* keyOut) { + if (UNLIKELY(0 >= m_size)) { + return 0LL; + } + cellDup(m_data[0], *valOut); + keyOut->m_data.num = 0; + keyOut->m_type = KindOfInt64; + return 1LL; +} + +inline int64_t c_Vector::iterNext(ssize_t pos, TypedValue* valOut) { + if (UNLIKELY(uint64_t(++pos) >= m_size)) { + return 0LL; + } + cellDup(m_data[pos], *valOut); + return pos; +} + +inline int64_t c_Vector::iterNextK( + ssize_t pos, TypedValue* valOut, TypedValue* keyOut) { + if (UNLIKELY(uint64_t(++pos) >= m_size)) { + return 0LL; + } + cellDup(m_data[pos], *valOut); + keyOut->m_data.num = pos; + keyOut->m_type = KindOfInt64; + return pos; +} + +// Map JIT helpers +inline int64_t c_Map::iterInit(TypedValue* valOut) { + ssize_t key = iter_begin(); + if (UNLIKELY(key == 0)) { + return 0; + } + Bucket* p = reinterpret_cast(key); + cellDup(p->data, *valOut); + return key; +} + +inline int64_t c_Map::iterInitK(TypedValue* valOut, TypedValue* keyOut) { + ssize_t key = iter_begin(); + if (UNLIKELY(key == 0)) { + return 0; + } + Bucket* p = reinterpret_cast(key); + cellDup(p->data, *valOut); + if (p->hasStrKey()) { + Variant v(p->skey); + cellDup(*v.asTypedValue(), *keyOut); + } else { + keyOut->m_data.num = (int64_t)p->ikey; + keyOut->m_type = KindOfInt64; + } + return key; +} + +inline int64_t c_Map::iterNext(ssize_t key, TypedValue* valOut) { + key = iter_next(key); + if (UNLIKELY(key == 0)) { + return 0; + } + Bucket* p = reinterpret_cast(key); + cellDup(p->data, *valOut); + return key; +} + +inline int64_t c_Map::iterNextK( + ssize_t key, TypedValue* valOut, TypedValue* keyOut) { + key = iter_next(key); + if (UNLIKELY(key == 0)) { + return 0; + } + Bucket* p = reinterpret_cast(key); + cellDup(p->data, *valOut); + if (p->hasStrKey()) { + Variant v(p->skey); + cellDup(*v.asTypedValue(), *keyOut); + } else { + keyOut->m_data.num = (int64_t)p->ikey; + keyOut->m_type = KindOfInt64; + } + return key; +} + +// StableMap JIT helpers +inline int64_t c_StableMap::iterInit(TypedValue* valOut) { + ssize_t key = iter_begin(); + if (UNLIKELY(key == 0)) { + return 0; + } + Bucket* p = reinterpret_cast(key); + cellDup(p->data, *valOut); + return key; +} + +inline int64_t c_StableMap::iterInitK(TypedValue* valOut, TypedValue* keyOut) { + ssize_t key = iter_begin(); + if (UNLIKELY(key == 0)) { + return 0; + } + Bucket* p = reinterpret_cast(key); + cellDup(p->data, *valOut); + if (p->hasStrKey()) { + Variant v(p->skey); + cellDup(*v.asTypedValue(), *keyOut); + } else { + keyOut->m_data.num = (int64_t)p->ikey; + keyOut->m_type = KindOfInt64; + } + return key; +} + +inline int64_t c_StableMap::iterNext(ssize_t key, TypedValue* valOut) { + key = iter_next(key); + if (UNLIKELY(key == 0)) { + return 0; + } + Bucket* p = reinterpret_cast(key); + cellDup(p->data, *valOut); + return key; +} + +inline int64_t c_StableMap::iterNextK( + ssize_t key, TypedValue* valOut, TypedValue* keyOut) { + key = iter_next(key); + if (UNLIKELY(key == 0)) { + return 0; + } + Bucket* p = reinterpret_cast(key); + cellDup(p->data, *valOut); + if (p->hasStrKey()) { + Variant v(p->skey); + cellDup(*v.asTypedValue(), *keyOut); + } else { + keyOut->m_data.num = (int64_t)p->ikey; + keyOut->m_type = KindOfInt64; + } + return key; +} + +// Set JIT helpers +inline int64_t c_Set::iterInit(TypedValue* valOut) { + ssize_t key = iter_begin(); + if (UNLIKELY(key == 0)) { + return 0; + } + Bucket* p = reinterpret_cast(key); + cellDup(p->data, *valOut); + return key; +} + +inline int64_t c_Set::iterInitK(TypedValue* valOut, TypedValue* keyOut) { + ssize_t key = iter_begin(); + if (UNLIKELY(key == 0)) { + return 0; + } + Bucket* p = reinterpret_cast(key); + cellDup(p->data, *valOut); + keyOut->m_type = KindOfUninit; + return key; +} + +inline int64_t c_Set::iterNext(ssize_t key, TypedValue* valOut) { + key = iter_next(key); + if (UNLIKELY(key == 0)) { + return 0; + } + Bucket* p = reinterpret_cast(key); + cellDup(p->data, *valOut); + return key; +} + +inline int64_t c_Set::iterNextK( + ssize_t key, TypedValue* valOut, TypedValue* keyOut) { + key = iter_next(key); + if (UNLIKELY(key != 0)) { + return 0; + } + Bucket* p = reinterpret_cast(key); + cellDup(p->data, *valOut); + keyOut->m_type = KindOfUninit; + return key; +} + +// Pair JIT helpers +inline int64_t c_Pair::iterInit(TypedValue* valOut) { + cellDup(getElms()[0], *valOut); + return 1LL; +} + +inline int64_t c_Pair::iterInitK(TypedValue* valOut, TypedValue* keyOut) { + cellDup(getElms()[0], *valOut); + keyOut->m_data.num = 0; + keyOut->m_type = KindOfInt64; + return 1LL; +} + +inline int64_t c_Pair::iterNext(ssize_t pos, TypedValue* valOut) { + if (uint64_t(++pos) >= uint64_t(2)) { + return 0LL; + } + cellDup(getElms()[pos], *valOut); + return pos; +} + +inline int64_t c_Pair::iterNextK( + ssize_t pos, TypedValue* valOut, TypedValue* keyOut) { + if (uint64_t(++pos) >= uint64_t(2)) { + return 0LL; + } + cellDup(getElms()[pos], *valOut); + keyOut->m_data.num = pos; + keyOut->m_type = KindOfInt64; + return pos; +} + +} + +#endif // incl_HPHP_EXT_COLLECTION_DEF_H_ diff --git a/hphp/runtime/vm/bytecode.cpp b/hphp/runtime/vm/bytecode.cpp index 94f9d3d06..1ee7c3f0c 100644 --- a/hphp/runtime/vm/bytecode.cpp +++ b/hphp/runtime/vm/bytecode.cpp @@ -740,13 +740,23 @@ static std::string toStringIter(const Iter* it, bool itRef) { // TODO(#2458166): it might be a CufIter, but we're just lucky that // the bit pattern for the CufIter is going to have a 0 in // getIterType for now. - switch (it->arr().getIterType()) { - case ArrayIter::TypeUndefined: - return "I:Undefined"; - case ArrayIter::TypeArray: - return "I:Array"; - case ArrayIter::TypeIterator: - return "I:Iterator"; + switch (it->arr().getIterKind()) { + case ArrayIter::IterKind::Undefined: + return "I:Undefined"; + case ArrayIter::IterKind::Array: + return "I:Array"; + case ArrayIter::IterKind::Iterator: + return "I:Iterator"; + case ArrayIter::IterKind::Pair: + return "I:Pair"; + case ArrayIter::IterKind::Vector: + return "I:Vector"; + case ArrayIter::IterKind::Map: + return "I:Map"; + case ArrayIter::IterKind::StableMap: + return "I:StableMap"; + case ArrayIter::IterKind::Set: + return "I:Set"; } assert(false); return "I:?"; diff --git a/hphp/runtime/vm/jit/code-gen.cpp b/hphp/runtime/vm/jit/code-gen.cpp index 289466a3b..1d6d4d4df 100755 --- a/hphp/runtime/vm/jit/code-gen.cpp +++ b/hphp/runtime/vm/jit/code-gen.cpp @@ -4359,6 +4359,18 @@ void CodeGenerator::cgCheckStk(IRInstruction* inst) { rbase[baseOff + TVOFF(m_data)], inst->taken()); } +void CodeGenerator::cgGuardIter(IRInstruction* inst) { + auto const rFP = m_regs[inst->src(0)].reg(); + auto const baseOff = iterOffset(inst->extra()->iterId); + auto const loc = rFP[baseOff + ArrayIter::getOffsetOfIterKind()]; + auto const iterKind = inst->typeParam().getIterKind(); + assert(sizeof(ArrayIter::IterKind) == 4); + m_as.cmpl(static_cast(iterKind), loc); + auto const destSK = SrcKey(curFunc(), m_curTrace->bcOff()); + auto const destSR = m_tx64->getSrcRec(destSK); + m_tx64->emitFallbackCondJmp(m_as, *destSR, CC_NE); +} + void CodeGenerator::cgGuardLoc(IRInstruction* inst) { auto const rFP = m_regs[inst->src(0)].reg(); auto const baseOff = localOffset(inst->extra()->locId); @@ -5563,22 +5575,84 @@ void CodeGenerator::cgIterInitCommon(IRInstruction* inst) { isInitK ? (TCA)new_iter_array_key : (TCA)new_iter_array; cgCallHelper(m_as, helperAddr, inst->dst(), SyncOptions::kSyncPoint, args); } else { - assert(src->type() == Type::Obj); - args.imm(uintptr_t(curClass())).addr(fpReg, valLocalOffset); - if (isInitK) { - args.addr(fpReg, localOffset(inst->src(4))); + Type srcType = src->type(); + assert(srcType.subtypeOf(Type::Obj)); + + const Class* klass = srcType.getClass(); + if (!isWInit && isCollectionClass(klass)) { + args.addr(fpReg, valLocalOffset); + if (isInitK) { + args.addr(fpReg, localOffset(inst->src(4))); + } + cgIterInitCommonCollection(inst, klass, args); } else { - args.imm(0); + args.imm(uintptr_t(curClass())).addr(fpReg, valLocalOffset); + if (isInitK) { + args.addr(fpReg, localOffset(inst->src(4))); + } else { + args.imm(0); + } + // new_iter_object decrefs its src object if it propagates an + // exception out, so we use kSyncPointAdjustOne, which adjusts the + // stack pointer by 1 stack element on an unwind, skipping over + // the src object. + cgCallHelper(m_as, (TCA)new_iter_object, inst->dst(), + SyncOptions::kSyncPointAdjustOne, args); } - // new_iter_object decrefs its src object if it propagates an - // exception out, so we use kSyncPointAdjustOne, which adjusts the - // stack pointer by 1 stack element on an unwind, skipping over - // the src object. - cgCallHelper(m_as, (TCA)new_iter_object, inst->dst(), - SyncOptions::kSyncPointAdjustOne, args); } } +void CodeGenerator::cgIterInitCommonCollection(IRInstruction* inst, + const Class* klass, + ArgGroup& args) { + assert(isCollectionClass(klass)); + TCA helperAddr = nullptr; + if (klass == c_Pair::s_cls) { + helperAddr = inst->op() == IterInitK ? + (TCA)iterInitK : + (TCA)iterInit; + } else if (klass == c_Vector::s_cls) { + helperAddr = inst->op() == IterInitK ? + (TCA)iterInitK : + (TCA)iterInit; + } else if (klass == c_Map::s_cls) { + helperAddr = inst->op() == IterInitK ? + (TCA)iterInitK : + (TCA)iterInit; + } else if (klass == c_StableMap::s_cls) { + helperAddr = inst->op() == IterInitK ? + (TCA)iterInitK : + (TCA)iterInit; + } else if (klass == c_Set::s_cls) { + helperAddr = inst->op() == IterInitK ? + (TCA)iterInitK : + (TCA)iterInit; + } + assert(helperAddr != nullptr); + cgCallHelper(m_as, helperAddr, inst->dst(), + SyncOptions::kSyncPoint, args); +} + void CodeGenerator::cgMIterInit(IRInstruction* inst) { cgMIterInitCommon(inst); } @@ -5628,6 +5702,14 @@ void CodeGenerator::cgMIterInitCommon(IRInstruction* inst) { } } +void CodeGenerator::cgIterNextArray(IRInstruction* inst) { + cgIterNextCommon(inst); +} + +void CodeGenerator::cgIterNextKArray(IRInstruction* inst) { + cgIterNextCommon(inst); +} + void CodeGenerator::cgIterNext(IRInstruction* inst) { cgIterNextCommon(inst); } @@ -5645,8 +5727,10 @@ void CodeGenerator::cgWIterNextK(IRInstruction* inst) { } void CodeGenerator::cgIterNextCommon(IRInstruction* inst) { - bool isNextK = inst->op() == IterNextK || inst->op() == WIterNextK; + bool isNextK = inst->op() == IterNextK || inst->op() == WIterNextK || + inst->op() == IterNextKArray; bool isWNext = inst->op() == WIterNext || inst->op() == WIterNextK; + bool isArray = inst->op() == IterNextArray || inst->op() == IterNextKArray; PhysReg fpReg = m_regs[inst->src(0)].reg(); ArgGroup args(m_regs); args.addr(fpReg, iterOffset(inst->src(1))) @@ -5656,11 +5740,142 @@ void CodeGenerator::cgIterNextCommon(IRInstruction* inst) { } else if (isWNext) { args.imm(0); } - TCA helperAddr = isWNext ? (TCA)iter_next_key : - isNextK ? (TCA)iter_next_key : (TCA)iter_next; + TCA helperAddr; + if (isWNext) { + helperAddr = (TCA)iter_next_key; + } else { + if (isArray) { + helperAddr = isNextK ? (TCA)iter_next_key + : (TCA)iter_next; + } else { + args.imm(0); + helperAddr = (TCA)iter_next_any; + } + } cgCallHelper(m_as, helperAddr, inst->dst(), SyncOptions::kSyncPoint, args); } + +void CodeGenerator::cgIterNextVector(IRInstruction* inst) { + ArgGroup args(m_regs); + loadIterNextArg(args, inst, false); + cgCallHelper(m_as, + (TCA)iterNext, + inst->dst(), + SyncOptions::kSyncPoint, + args); +} + +void CodeGenerator::cgIterNextKVector(IRInstruction* inst) { + ArgGroup args(m_regs); + loadIterNextArg(args, inst, true); + cgCallHelper(m_as, + (TCA)iterNextK, + inst->dst(), + SyncOptions::kSyncPoint, + args); +} + +void CodeGenerator::cgIterNextMap(IRInstruction* inst) { + ArgGroup args(m_regs); + loadIterNextArg(args, inst, false); + cgCallHelper(m_as, + (TCA)iterNext, + inst->dst(), + SyncOptions::kSyncPoint, + args); +} + +void CodeGenerator::cgIterNextKMap(IRInstruction* inst) { + ArgGroup args(m_regs); + loadIterNextArg(args, inst, true); + cgCallHelper(m_as, + (TCA)iterNextK, + inst->dst(), + SyncOptions::kSyncPoint, + args); +} + +void CodeGenerator::cgIterNextStableMap(IRInstruction* inst) { + ArgGroup args(m_regs); + loadIterNextArg(args, inst, false); + cgCallHelper(m_as, + (TCA)iterNext, + inst->dst(), + SyncOptions::kSyncPoint, + args); +} + +void CodeGenerator::cgIterNextKStableMap(IRInstruction* inst) { + ArgGroup args(m_regs); + loadIterNextArg(args, inst, true); + cgCallHelper(m_as, + (TCA)iterNextK, + inst->dst(), + SyncOptions::kSyncPoint, + args); +} + +void CodeGenerator::cgIterNextSet(IRInstruction* inst) { + ArgGroup args(m_regs); + loadIterNextArg(args, inst, false); + cgCallHelper(m_as, + (TCA)iterNext, + inst->dst(), + SyncOptions::kSyncPoint, + args); +} + +void CodeGenerator::cgIterNextKSet(IRInstruction* inst) { + ArgGroup args(m_regs); + loadIterNextArg(args, inst, true); + cgCallHelper(m_as, + (TCA)iterNextK, + inst->dst(), + SyncOptions::kSyncPoint, + args); +} + +void CodeGenerator::cgIterNextPair(IRInstruction* inst) { + ArgGroup args(m_regs); + loadIterNextArg(args, inst, false); + cgCallHelper(m_as, + (TCA)iterNext, + inst->dst(), + SyncOptions::kSyncPoint, + args); +} + +void CodeGenerator::cgIterNextKPair(IRInstruction* inst) { + ArgGroup args(m_regs); + loadIterNextArg(args, inst, true); + cgCallHelper(m_as, + (TCA)iterNextK, + inst->dst(), + SyncOptions::kSyncPoint, + args); +} + +void CodeGenerator::loadIterNextArg( + ArgGroup& args, IRInstruction* inst, bool isNextK) { + PhysReg fpReg = m_regs[inst->src(0)].reg(); + args.addr(fpReg, iterOffset(inst->src(1))) + .addr(fpReg, localOffset(inst->src(2))); + if (isNextK) { + args.addr(fpReg, localOffset(inst->src(3))); + } +} + void CodeGenerator::cgMIterNext(IRInstruction* inst) { cgMIterNextCommon(inst); } diff --git a/hphp/runtime/vm/jit/code-gen.h b/hphp/runtime/vm/jit/code-gen.h index 41bfcb312..ac49e5056 100644 --- a/hphp/runtime/vm/jit/code-gen.h +++ b/hphp/runtime/vm/jit/code-gen.h @@ -308,7 +308,11 @@ private: Block* exit); void cgIterNextCommon(IRInstruction* inst); + void loadIterNextArg(ArgGroup& args, IRInstruction* inst, bool isNextK); void cgIterInitCommon(IRInstruction* inst); + void cgIterInitCommonCollection(IRInstruction* inst, + const Class* klass, + ArgGroup& args); void cgMIterNextCommon(IRInstruction* inst); void cgMIterInitCommon(IRInstruction* inst); void cgLdFuncCachedCommon(IRInstruction* inst); diff --git a/hphp/runtime/vm/jit/extra-data.h b/hphp/runtime/vm/jit/extra-data.h index c4cd5d2f5..3e65d087e 100644 --- a/hphp/runtime/vm/jit/extra-data.h +++ b/hphp/runtime/vm/jit/extra-data.h @@ -355,6 +355,7 @@ X(DecRefLoc, LocalId); X(LdLoc, LocalId); X(StLoc, LocalId); X(StLocNT, LocalId); +X(GuardIter, IterId); X(IterFree, IterId); X(MIterFree, IterId); X(CIterFree, IterId); diff --git a/hphp/runtime/vm/jit/hhbc-translator.cpp b/hphp/runtime/vm/jit/hhbc-translator.cpp index 69a10963f..cc10c654d 100755 --- a/hphp/runtime/vm/jit/hhbc-translator.cpp +++ b/hphp/runtime/vm/jit/hhbc-translator.cpp @@ -871,7 +871,7 @@ template SSATmp* HhbcTranslator::emitIterInitCommon(int offset, Lambda genFunc) { SSATmp* src = popC(); Type type = src->type(); - if (!type.isArray() && type != Type::Obj) { + if (!type.isArray() && !type.subtypeOf(Type::Obj)) { PUNT(IterInit); } SSATmp* res = genFunc(src); @@ -927,13 +927,38 @@ void HhbcTranslator::emitIterInitK(uint32_t iterId, void HhbcTranslator::emitIterNext(uint32_t iterId, int offset, - uint32_t valLocalId) { + uint32_t valLocalId, + ArrayIter::IterKind iterKind) { + Opcode op; + switch (iterKind) { + case ArrayIter::IterKind::Array: + op = IterNextArray; + break; + case ArrayIter::IterKind::Vector: + op = IterNextVector; + break; + case ArrayIter::IterKind::Map: + op = IterNextMap; + break; + case ArrayIter::IterKind::StableMap: + op = IterNextStableMap; + break; + case ArrayIter::IterKind::Set: + op = IterNextSet; + break; + case ArrayIter::IterKind::Pair: + op = IterNextPair; + break; + default: + op = IterNext; + break; + } SSATmp* res = gen( - IterNext, - Type::Bool, - m_tb->fp(), - cns(iterId), - cns(valLocalId) + op, + Type::Bool, + m_tb->fp(), + cns(iterId), + cns(valLocalId) ); emitJmpCondHelper(offset, false, res); } @@ -941,14 +966,39 @@ void HhbcTranslator::emitIterNext(uint32_t iterId, void HhbcTranslator::emitIterNextK(uint32_t iterId, int offset, uint32_t valLocalId, - uint32_t keyLocalId) { + uint32_t keyLocalId, + ArrayIter::IterKind iterKind) { + Opcode op; + switch (iterKind) { + case ArrayIter::IterKind::Array: + op = IterNextKArray; + break; + case ArrayIter::IterKind::Vector: + op = IterNextKVector; + break; + case ArrayIter::IterKind::Map: + op = IterNextKMap; + break; + case ArrayIter::IterKind::StableMap: + op = IterNextKStableMap; + break; + case ArrayIter::IterKind::Set: + op = IterNextKSet; + break; + case ArrayIter::IterKind::Pair: + op = IterNextKPair; + break; + default: + op = IterNextK; + break; + } SSATmp* res = gen( - IterNextK, - Type::Bool, - m_tb->fp(), - cns(iterId), - cns(valLocalId), - cns(keyLocalId) + op, + Type::Bool, + m_tb->fp(), + cns(iterId), + cns(valLocalId), + cns(keyLocalId) ); emitJmpCondHelper(offset, false, res); } @@ -2596,6 +2646,10 @@ void HhbcTranslator::guardTypeLocal(uint32_t locId, Type type) { gen(GuardLoc, type, LocalId(locId), m_tb->fp()); } +void HhbcTranslator::guardTypeIter(uint32_t iterId, Type type) { + gen(GuardIter, type, IterId(iterId), m_tb->fp()); +} + void HhbcTranslator::guardTypeLocation(const RegionDesc::Location& loc, Type type) { assert(type.subtypeOf(Type::Gen | Type::Cls)); diff --git a/hphp/runtime/vm/jit/hhbc-translator.h b/hphp/runtime/vm/jit/hhbc-translator.h index cfcd5fae4..6ba5e1213 100755 --- a/hphp/runtime/vm/jit/hhbc-translator.h +++ b/hphp/runtime/vm/jit/hhbc-translator.h @@ -121,6 +121,7 @@ struct HhbcTranslator { // Tracelet guards. void guardTypeStack(uint32_t stackIndex, Type type); void guardTypeLocal(uint32_t locId, Type type); + void guardTypeIter(uint32_t iterId, Type type); void guardTypeLocation(const RegionDesc::Location& loc, Type type); void guardRefs(int64_t entryArDelta, const vector& mask, @@ -360,11 +361,15 @@ struct HhbcTranslator { int targetOffset, uint32_t valLocalId, uint32_t keyLocalId); - void emitIterNext(uint32_t iterId, int targetOffset, uint32_t valLocalId); + void emitIterNext(uint32_t iterId, + int targetOffset, + uint32_t valLocalId, + ArrayIter::IterKind iterKind); void emitIterNextK(uint32_t iterId, int targetOffset, uint32_t valLocalId, - uint32_t keyLocalId); + uint32_t keyLocalId, + ArrayIter::IterKind iterKind); void emitMIterInit(uint32_t iterId, int targetOffset, uint32_t valLocalId); void emitMIterInitK(uint32_t iterId, int targetOffset, diff --git a/hphp/runtime/vm/jit/ir-translator.cpp b/hphp/runtime/vm/jit/ir-translator.cpp index 39fd9409a..04b7b53c0 100644 --- a/hphp/runtime/vm/jit/ir-translator.cpp +++ b/hphp/runtime/vm/jit/ir-translator.cpp @@ -127,6 +127,9 @@ void IRTranslator::checkType(const Transl::Location& l, break; case Location::Iter: + m_hhbcTrans.guardTypeIter(l.offset, Type::fromRuntimeType(rtt)); + break; + case Location::Invalid: case Location::Litstr: case Location::Litint: @@ -1402,20 +1405,24 @@ IRTranslator::translateIterInitK(const NormalizedInstruction& i) { void IRTranslator::translateIterNext(const NormalizedInstruction& i) { + assert(i.inputs[0]->isIter()); HHIR_EMIT(IterNext, i.imm[0].u_IVA, i.offset() + i.imm[1].u_BA, - i.imm[2].u_IVA); + i.imm[2].u_IVA, + i.inputs[0]->rtt.iterKind()); } void IRTranslator::translateIterNextK(const NormalizedInstruction& i) { + assert(i.inputs[0]->isIter()); HHIR_EMIT(IterNextK, i.imm[0].u_IVA, i.offset() + i.imm[1].u_BA, i.imm[2].u_IVA, - i.imm[3].u_IVA); + i.imm[3].u_IVA, + i.inputs[0]->rtt.iterKind()); } void diff --git a/hphp/runtime/vm/jit/ir.cpp b/hphp/runtime/vm/jit/ir.cpp index 058273691..d37316b7a 100755 --- a/hphp/runtime/vm/jit/ir.cpp +++ b/hphp/runtime/vm/jit/ir.cpp @@ -57,8 +57,10 @@ std::string Type::toString() const { if (strictSubtypeOf(Type::Obj)) { return folly::format("Obj<{}>", m_class->name()->data()).str(); + } else if (subtypeOf(Type::Iter)) { + return folly::format("Iter<{}>", + ArrayIter::typeAsString(m_iterKind)).str(); } - // Concat all of the primitive types in the custom union type std::vector types; # define IRT(name, ...) if (name.subtypeOf(*this)) types.push_back(#name); diff --git a/hphp/runtime/vm/jit/ir.h b/hphp/runtime/vm/jit/ir.h index c25a55bb7..e6929d149 100755 --- a/hphp/runtime/vm/jit/ir.h +++ b/hphp/runtime/vm/jit/ir.h @@ -188,6 +188,7 @@ O(AssertType, DParam, S(Gen,Nullptr,Cls), C|E|CRc|PRc|P) \ O(CheckTypeMem, ND, S(PtrToGen), E) \ O(GuardLoc, ND, S(FramePtr), E) \ O(GuardStk, D(StkPtr), S(StkPtr), E) \ +O(GuardIter, ND, S(FramePtr), E) \ O(CheckLoc, ND, S(FramePtr), E) \ O(CheckStk, D(StkPtr), S(StkPtr), E) \ O(CastStk, D(StkPtr), S(StkPtr), Mem|N|Er) \ @@ -502,8 +503,32 @@ O(IterInitK, D(Bool), S(Arr,Obj) \ C(Int), E|N|Mem|Refs|CRc) \ O(IterNext, D(Bool), S(FramePtr) \ C(Int) C(Int), E|N|Mem|Refs) \ +O(IterNextArray, D(Bool), S(FramePtr) \ + C(Int) C(Int), E|N|Mem|Refs) \ +O(IterNextVector, D(Bool), S(FramePtr) \ + C(Int) C(Int), E|N|Mem|Refs) \ +O(IterNextMap, D(Bool), S(FramePtr) \ + C(Int) C(Int), E|N|Mem|Refs) \ +O(IterNextStableMap, D(Bool), S(FramePtr) \ + C(Int) C(Int), E|N|Mem|Refs) \ +O(IterNextSet, D(Bool), S(FramePtr) \ + C(Int) C(Int), E|N|Mem|Refs) \ +O(IterNextPair, D(Bool), S(FramePtr) \ + C(Int) C(Int), E|N|Mem|Refs) \ O(IterNextK, D(Bool), S(FramePtr) \ C(Int) C(Int) C(Int), E|N|Mem|Refs) \ +O(IterNextKArray, D(Bool), S(FramePtr) \ + C(Int) C(Int) C(Int), E|N|Mem|Refs) \ +O(IterNextKVector, D(Bool), S(FramePtr) \ + C(Int) C(Int) C(Int), E|N|Mem|Refs) \ +O(IterNextKMap, D(Bool), S(FramePtr) \ + C(Int) C(Int) C(Int), E|N|Mem|Refs) \ +O(IterNextKStableMap, D(Bool), S(FramePtr) \ + C(Int) C(Int) C(Int), E|N|Mem|Refs) \ +O(IterNextKSet, D(Bool), S(FramePtr) \ + C(Int) C(Int) C(Int), E|N|Mem|Refs) \ +O(IterNextKPair, D(Bool), S(FramePtr) \ + C(Int) C(Int) C(Int), E|N|Mem|Refs) \ O(WIterInit, D(Bool), S(Arr,Obj) \ S(FramePtr) \ C(Int) \ diff --git a/hphp/runtime/vm/jit/runtime-type.cpp b/hphp/runtime/vm/jit/runtime-type.cpp index 791d5622c..113ccdf39 100644 --- a/hphp/runtime/vm/jit/runtime-type.cpp +++ b/hphp/runtime/vm/jit/runtime-type.cpp @@ -103,10 +103,12 @@ RuntimeType::RuntimeType() : RuntimeType::RuntimeType(const Iter* it) : m_kind(ITER) { + m_value.iterKind = ArrayIter::IterKind::Undefined; } -RuntimeType::RuntimeType(ArrayIter::Type type) : +RuntimeType::RuntimeType(ArrayIter::IterKind iterKind) : m_kind(ITER) { + m_value.iterKind = iterKind; } RuntimeType RuntimeType::box() const { @@ -204,6 +206,12 @@ RuntimeType::knownClass() const { return m_value.knownClass; } +ArrayIter::IterKind +RuntimeType::iterKind() const { + consistencyCheck(); + return m_value.iterKind; +} + RuntimeType RuntimeType::setValueType(DataType newInner) const { assert(m_kind == VALUE); @@ -234,6 +242,15 @@ RuntimeType::setKnownClass(const Class* klass) const { return rtt; } +RuntimeType +RuntimeType::setIterKind(ArrayIter::IterKind iterKind) const { + assert(isIter()); + RuntimeType rtt; + rtt.m_kind = this->m_kind; + rtt.m_value.iterKind = iterKind; + return rtt; +} + // Accessors DataType RuntimeType::outerType() const { consistencyCheck(); @@ -312,6 +329,10 @@ bool RuntimeType::hasKnownType() const { return isObject() && m_value.knownClass != nullptr; } +bool RuntimeType::hasIterKind() const { + return isIter() && m_value.iterKind != ArrayIter::IterKind::Undefined; +} + bool RuntimeType::isArray() const { return isValue() && outerType() == KindOfArray; } diff --git a/hphp/runtime/vm/jit/runtime-type.h b/hphp/runtime/vm/jit/runtime-type.h index 01bd5a675..f7f10ef7b 100644 --- a/hphp/runtime/vm/jit/runtime-type.h +++ b/hphp/runtime/vm/jit/runtime-type.h @@ -200,7 +200,10 @@ class RuntimeType { DataType innerType; // Set when we want to transfer the type information to the // IR type system (Type object) - const Class* knownClass; + union { + const Class* knownClass; + ArrayIter::IterKind iterKind; + }; union { // We may have even more precise data about this set of values. const StringData* string; // KindOfString: The exact value. @@ -239,6 +242,7 @@ class RuntimeType { assert(m_value.innerType != KindOfStaticString && m_value.outerType != KindOfStaticString); assert(m_value.knownClass == nullptr || + m_kind == ITER || m_value.outerType == KindOfObject || (m_value.outerType == KindOfRef && m_value.innerType == KindOfObject)); @@ -256,7 +260,7 @@ class RuntimeType { RuntimeType(const RuntimeType& copy) = default; RuntimeType(); explicit RuntimeType(const Iter* iter); - explicit RuntimeType(ArrayIter::Type type); + explicit RuntimeType(ArrayIter::IterKind kind); static const int UnknownBool = -1; @@ -265,6 +269,7 @@ class RuntimeType { RuntimeType unbox() const; RuntimeType setValueType(DataType vt) const; RuntimeType setKnownClass(const Class* klass) const; + RuntimeType setIterKind(ArrayIter::IterKind iterKind) const; // Accessors DataType outerType() const; @@ -278,6 +283,7 @@ class RuntimeType { int64_t valueInt() const; int64_t valueGeneric() const; const Class* knownClass() const; + ArrayIter::IterKind iterKind() const; // Helpers for typechecking DataType typeCheckValue() const; @@ -299,6 +305,7 @@ class RuntimeType { bool isObject() const; bool isClass() const; bool hasKnownType() const; + bool hasIterKind() const; bool operator==(const RuntimeType& r) const; RuntimeType &operator=(const RuntimeType& r) = default; size_t operator()(const RuntimeType& r) const; // hash function diff --git a/hphp/runtime/vm/jit/test/type.cpp b/hphp/runtime/vm/jit/test/type.cpp index ec201fa7f..b81eaea3e 100644 --- a/hphp/runtime/vm/jit/test/type.cpp +++ b/hphp/runtime/vm/jit/test/type.cpp @@ -18,7 +18,7 @@ #include "hphp/util/base.h" #include "hphp/runtime/vm/jit/ir.h" -// for specialized object tests to get some real VM::Class +// for specialized object tests to get some real VM::Class and Iter types #include "hphp/system/systemlib.h" namespace std { namespace tr1 { @@ -168,6 +168,25 @@ TEST(Type, RuntimeType) { EXPECT_FALSE(t.subtypeOf(t1)); EXPECT_FALSE(t.subtypeOf(Type::Str)); EXPECT_FALSE(Type::Int.subtypeOf(t)); + HPHP::Transl::RuntimeType rtItArr = + HPHP::Transl::RuntimeType(ArrayIter::IterKind::Array); + Type tItArr = Type::fromRuntimeType(rtItArr); + HPHP::Transl::RuntimeType rtItVec = + HPHP::Transl::RuntimeType(ArrayIter::IterKind::Vector); + Type tItVec = Type::fromRuntimeType(rtItVec); + EXPECT_TRUE(tItArr.subtypeOf(Type::Iter)); + EXPECT_TRUE(tItVec.subtypeOf(Type::Iter)); + EXPECT_TRUE(tItVec.strictSubtypeOf(Type::Iter)); + EXPECT_FALSE(tItArr.subtypeOf(tItVec)); + EXPECT_FALSE(tItVec.subtypeOf(tItArr)); + EXPECT_FALSE(tItArr.subtypeOf(Type::Str)); + EXPECT_FALSE(tItArr.subtypeOf(Type::Obj)); + EXPECT_FALSE(tItVec.subtypeOf(Type::Int)); + EXPECT_FALSE(tItVec.subtypeOf(Type::Obj)); + EXPECT_FALSE(tItArr.subtypeOf(t)); + EXPECT_FALSE(tItVec.subtypeOf(t1)); + EXPECT_FALSE(t.subtypeOf(tItArr)); + EXPECT_FALSE(t1.subtypeOf(tItVec)); } TEST(Type, CanRunDtor) { diff --git a/hphp/runtime/vm/jit/trace-builder.cpp b/hphp/runtime/vm/jit/trace-builder.cpp index 78db6a1e6..2c7d99387 100644 --- a/hphp/runtime/vm/jit/trace-builder.cpp +++ b/hphp/runtime/vm/jit/trace-builder.cpp @@ -313,6 +313,12 @@ void TraceBuilder::updateTrackedState(IRInstruction* inst) { break; case IterNextK: + case IterNextKArray: + case IterNextKVector: + case IterNextKMap: + case IterNextKStableMap: + case IterNextKSet: + case IterNextKPair: case WIterNextK: // kill the locals to which this instruction stores iter's key and value killLocalValue(inst->src(2)->getValInt()); @@ -320,6 +326,12 @@ void TraceBuilder::updateTrackedState(IRInstruction* inst) { break; case IterNext: + case IterNextArray: + case IterNextVector: + case IterNextMap: + case IterNextStableMap: + case IterNextSet: + case IterNextPair: case WIterNext: // kill the local to which this instruction stores iter's value killLocalValue(inst->src(2)->getValInt()); diff --git a/hphp/runtime/vm/jit/translator-x64.cpp b/hphp/runtime/vm/jit/translator-x64.cpp index 500e76197..52b07eda4 100644 --- a/hphp/runtime/vm/jit/translator-x64.cpp +++ b/hphp/runtime/vm/jit/translator-x64.cpp @@ -71,7 +71,6 @@ #include "hphp/runtime/base/execution_context.h" #include "hphp/runtime/base/runtime_option.h" #include "hphp/runtime/base/strings.h" -#include "hphp/runtime/base/strings.h" #include "hphp/runtime/server/source_root_info.h" #include "hphp/runtime/base/zend_string.h" #include "hphp/runtime/ext/ext_closure.h" diff --git a/hphp/runtime/vm/jit/translator.cpp b/hphp/runtime/vm/jit/translator.cpp index c45815dfb..ceb1297ba 100755 --- a/hphp/runtime/vm/jit/translator.cpp +++ b/hphp/runtime/vm/jit/translator.cpp @@ -243,6 +243,9 @@ RuntimeType Translator::liveType(Location l, const Iter *it = frame_iter(curFrame(), l.offset); TRACE(1, "Iter input: fp %p, iter %p, offset %" PRId64 "\n", vmfp(), it, l.offset); + if (specialize) { + return RuntimeType(it->arr().getIterKind()); + } return RuntimeType(it); } break; case Location::Litstr: { @@ -1228,10 +1231,10 @@ static const struct { { OpIterInitK, {Stack1, Local, OutUnknown, -1 }}, { OpMIterInitK, {Stack1, Local, OutUnknown, -1 }}, { OpWIterInitK, {Stack1, Local, OutUnknown, -1 }}, - { OpIterNext, {None, Local, OutUnknown, 0 }}, + { OpIterNext, {Iter, Local, OutUnknown, 0 }}, { OpMIterNext, {None, Local, OutUnknown, 0 }}, { OpWIterNext, {None, Local, OutUnknown, 0 }}, - { OpIterNextK, {None, Local, OutUnknown, 0 }}, + { OpIterNextK, {Iter, Local, OutUnknown, 0 }}, { OpMIterNextK, {None, Local, OutUnknown, 0 }}, { OpWIterNextK, {None, Local, OutUnknown, 0 }}, { OpIterFree, {None, None, OutNone, 0 }}, @@ -2561,10 +2564,15 @@ GuardType::GuardType(DataType outer, DataType inner) } GuardType::GuardType(const RuntimeType& rtt) { - assert(rtt.isValue()); - outerType = rtt.outerType(); - innerType = rtt.innerType(); - klass = rtt.hasKnownType() ? rtt.knownClass() : nullptr; + if (rtt.isIter()) { + iterKind = rtt.hasIterKind() ? rtt.iterKind() + : ArrayIter::IterKind::Undefined; + } else { + assert(rtt.isValue()); + outerType = rtt.outerType(); + innerType = rtt.innerType(); + klass = rtt.hasKnownType() ? rtt.knownClass() : nullptr; + } } GuardType::GuardType(const GuardType& other) { @@ -2667,9 +2675,12 @@ GuardType GuardType::dropSpecialization() const { } RuntimeType GuardType::getRuntimeType() const { - if (klass != nullptr) { + if (outerType == KindOfObject && klass != nullptr) { return RuntimeType(outerType, innerType).setKnownClass(klass); } + if (iterKind != ArrayIter::IterKind::Undefined) { + return RuntimeType(iterKind); + } return RuntimeType(outerType, innerType); } @@ -2796,7 +2807,18 @@ Translator::getOperandConstraintCategory(NormalizedInstruction* instr, } } return DataTypeSpecific; + case OpIterInit: + case OpIterInitK: { + const Class* klass = specType.getSpecializedClass(); + if (klass != nullptr && isCollectionClass(klass)) { + return DataTypeSpecialized; + } + return DataTypeSpecific; + } + case OpIterNext: + case OpIterNextK: + return DataTypeSpecialized; default: return DataTypeSpecific; } diff --git a/hphp/runtime/vm/jit/translator.h b/hphp/runtime/vm/jit/translator.h index 86a64d3ed..03e45b464 100644 --- a/hphp/runtime/vm/jit/translator.h +++ b/hphp/runtime/vm/jit/translator.h @@ -143,6 +143,9 @@ struct DynLocation { bool isArray() const { return rtt.isArray(); } + bool isIter() const { + return rtt.isIter(); + } DataType valueType() const { return rtt.valueType(); } @@ -385,27 +388,31 @@ class GuardType { DataType inner = KindOfInvalid); explicit GuardType(const RuntimeType& rtt); GuardType(const GuardType& other); - const DataType getOuterType() const; - const DataType getInnerType() const; - const Class* getSpecializedClass() const; - bool isSpecific() const; - bool isSpecialized() const; - bool isRelaxed() const; - bool isGeneric() const; - bool isCounted() const; - bool isMoreRefinedThan(const GuardType& other) const; - bool mayBeUninit() const; - GuardType getCountness() const; - GuardType getCountnessInit() const; - DataTypeCategory getCategory() const; - GuardType dropSpecialization() const; - RuntimeType getRuntimeType() const; - bool isEqual(GuardType other) const; + const DataType getOuterType() const; + const DataType getInnerType() const; + const Class* getSpecializedClass() const; + const ArrayIter::IterKind getSpecializedIterKind() const; + bool isSpecific() const; + bool isSpecialized() const; + bool isRelaxed() const; + bool isGeneric() const; + bool isCounted() const; + bool isMoreRefinedThan(const GuardType& other) const; + bool mayBeUninit() const; + GuardType getCountness() const; + GuardType getCountnessInit() const; + DataTypeCategory getCategory() const; + GuardType dropSpecialization() const; + RuntimeType getRuntimeType() const; + bool isEqual(GuardType other) const; private: DataType outerType; DataType innerType; - const Class* klass; + union { + const Class* klass; + ArrayIter::IterKind iterKind; + }; }; /* diff --git a/hphp/runtime/vm/jit/type.cpp b/hphp/runtime/vm/jit/type.cpp index d4e193909..dca2fcf2a 100644 --- a/hphp/runtime/vm/jit/type.cpp +++ b/hphp/runtime/vm/jit/type.cpp @@ -104,7 +104,11 @@ Type Type::fromDataType(DataType outerType, } Type Type::fromRuntimeType(const RuntimeType& rtt) { - return fromDataType(rtt.outerType(), rtt.innerType(), rtt.knownClass()); + if (rtt.isIter()) { + return Iter.specialize(rtt.iterKind()); + } else { + return fromDataType(rtt.outerType(), rtt.innerType(), rtt.knownClass()); + } } Type Type::fromDynLocation(const Transl::DynLocation* dynLoc) { diff --git a/hphp/runtime/vm/jit/type.h b/hphp/runtime/vm/jit/type.h index 4302a3c00..554c6074b 100644 --- a/hphp/runtime/vm/jit/type.h +++ b/hphp/runtime/vm/jit/type.h @@ -17,6 +17,8 @@ #ifndef incl_HPHP_JIT_TYPE_H_ #define incl_HPHP_JIT_TYPE_H_ +#include "hphp/runtime/base/array_iterator.h" + namespace HPHP { namespace Transl { struct DynLocation; @@ -72,7 +74,8 @@ using Transl::RuntimeType; IRT(ActRec, 1ULL << 51) \ IRT(None, 1ULL << 52) \ IRT(CacheHandle, 1ULL << 53) /* TargetCache::CacheHandle */ \ - IRT(Nullptr, 1ULL << 54) + IRT(Nullptr, 1ULL << 54) \ + IRT(Iter, 1ULL << 55) // The definitions for these are in ir.cpp #define IRT_UNIONS \ @@ -121,13 +124,21 @@ class Type { bits_t m_bits; TypeBits m_typedBits; }; - const Class* m_class; + union { + const Class* m_class; + ArrayIter::IterKind m_iterKind; + }; // private ctor to build a specialized type explicit Type(bits_t bits, const Class* klass) : m_bits(bits), m_class(klass) {} + // private ctor to build a specialized iterator type + explicit Type(bits_t bits, ArrayIter::IterKind iterKind) + : m_bits(bits), m_iterKind(iterKind) + {} + public: # define IRT(name, ...) static const Type name; IR_TYPES @@ -158,7 +169,7 @@ public: } Type operator&(Type other) const { - if (m_class != nullptr && other.m_class != nullptr) { + if (strictSubtypeOf(Obj) && other.strictSubtypeOf(Obj)) { if (m_class->classof(other.m_class)) { return Type(m_bits & other.m_bits).specialize(other.m_class); } else if (other.m_class->classof(m_class)) { @@ -280,7 +291,7 @@ public: bool subtypeOf(Type t2) const { return (m_bits & t2.m_bits) == m_bits && (t2.m_class == nullptr - || (m_class != nullptr + || (m_class != nullptr && !subtypeOf(Iter) && m_class->classof(t2.m_class))); } @@ -360,11 +371,20 @@ public: return subtypeOf(Cls); } + bool isIter() const { + return subtypeOf(Iter); + } + const Class* getClass() const { assert(isObj()); return m_class; } + ArrayIter::IterKind getIterKind() const { + assert(isIter()); + return m_iterKind; + } + Type innerType() const { assert(isBoxed()); return Type(m_bits >> kBoxShift, m_class); @@ -439,8 +459,13 @@ public: return Type(m_bits, klass); } + Type specialize(ArrayIter::IterKind iterKind) const { + assert(isIter() && m_iterKind == ArrayIter::IterKind::Undefined); + return Type(m_bits, iterKind); + } + Type unspecialize() const { - assert(isObj() && m_class != nullptr); + assert((isObj() || isIter()) && m_class != nullptr); return Type(m_bits, nullptr); }