IterInit, IterInitK, IterNet and IterNextK specializations and guards
Specialize IterInit and IterInitK after collection gurads and specialize IterNext and IterNextK after iter guard for collections and array
Esse commit está contido em:
@@ -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<IterType> 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<T,offset> 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<Iter<IterType>,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<IterType>; if not, make a fallback
|
||||
jump. (A jump to a service request that chains to a retranslation for
|
||||
this tracelet.)
|
||||
|
||||
D:StkPtr = CheckStk<T,offset> 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.
|
||||
|
||||
@@ -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<class Tuplish>
|
||||
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<class Vectorish>
|
||||
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<class Mappish>
|
||||
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<class Tuplish>
|
||||
inline ALWAYS_INLINE
|
||||
bool ArrayIter::iterNext(Fixed) {
|
||||
return ++m_pos < static_cast<Tuplish*>(getObject())->size();
|
||||
int64_t ArrayIter::iterInit(Fixed, TypedValue* valOut) {
|
||||
return static_cast<Tuplish*>(getObject())->iterInit(valOut);
|
||||
}
|
||||
|
||||
template<class Vectorish>
|
||||
inline ALWAYS_INLINE
|
||||
bool ArrayIter::iterNext(Versionable) {
|
||||
int64_t ArrayIter::iterInit(Versionable, TypedValue* valOut) {
|
||||
return static_cast<Vectorish*>(getObject())->iterInit(valOut);
|
||||
}
|
||||
|
||||
template<class Mappish>
|
||||
inline ALWAYS_INLINE
|
||||
int64_t ArrayIter::iterInit(VersionableSparse, TypedValue* valOut) {
|
||||
m_pos = static_cast<Mappish*>(getObject())->iterInit(valOut);
|
||||
return m_pos;
|
||||
}
|
||||
|
||||
template<class Tuplish>
|
||||
inline ALWAYS_INLINE
|
||||
int64_t ArrayIter::iterInitK(Fixed, TypedValue* valOut, TypedValue* keyOut) {
|
||||
return static_cast<Tuplish*>(getObject())->iterInitK(valOut, keyOut);
|
||||
}
|
||||
|
||||
template<class Vectorish>
|
||||
inline ALWAYS_INLINE
|
||||
int64_t ArrayIter::iterInitK(
|
||||
Versionable, TypedValue* valOut, TypedValue* keyOut) {
|
||||
return static_cast<Vectorish*>(getObject())->iterInitK(valOut, keyOut);
|
||||
}
|
||||
|
||||
template<class Mappish>
|
||||
inline ALWAYS_INLINE
|
||||
int64_t ArrayIter::iterInitK(
|
||||
VersionableSparse, TypedValue* valOut, TypedValue* keyOut) {
|
||||
m_pos = static_cast<Mappish*>(getObject())->iterInitK(valOut, keyOut);
|
||||
return m_pos;
|
||||
}
|
||||
|
||||
template<class Tuplish>
|
||||
inline ALWAYS_INLINE
|
||||
int64_t ArrayIter::iterNext(Fixed, TypedValue* valOut) {
|
||||
int64_t pos = static_cast<Tuplish*>(getObject())->iterNext(m_pos, valOut);
|
||||
if (pos != 0) {
|
||||
m_pos = pos;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
template<class Vectorish>
|
||||
inline ALWAYS_INLINE
|
||||
int64_t ArrayIter::iterNext(Versionable, TypedValue* valOut) {
|
||||
Vectorish* vec = static_cast<Vectorish*>(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<class Mappish>
|
||||
inline ALWAYS_INLINE
|
||||
bool ArrayIter::iterNext(VersionableSparse) {
|
||||
int64_t ArrayIter::iterNext(VersionableSparse, TypedValue* valOut) {
|
||||
Mappish* coll = static_cast<Mappish*>(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<class Tuplish>
|
||||
inline ALWAYS_INLINE
|
||||
Variant ArrayIter::iterKey(Fixed) {
|
||||
return m_pos;
|
||||
int64_t ArrayIter::iterNextKey(
|
||||
Fixed, TypedValue* valOut, TypedValue* keyOut) {
|
||||
auto pos = static_cast<Tuplish*>(getObject())->iterNextK(
|
||||
m_pos, valOut, keyOut);
|
||||
if (pos != 0) {
|
||||
m_pos = pos;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
template<class Vectorish>
|
||||
inline ALWAYS_INLINE
|
||||
Variant ArrayIter::iterKey(Versionable) {
|
||||
int64_t ArrayIter::iterNextKey(
|
||||
Versionable, TypedValue* valOut, TypedValue* keyOut) {
|
||||
Vectorish* vec = static_cast<Vectorish*>(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<class Mappish>
|
||||
inline ALWAYS_INLINE
|
||||
int64_t ArrayIter::iterNextKey(
|
||||
VersionableSparse, TypedValue* valOut, TypedValue* keyOut) {
|
||||
Mappish* coll = static_cast<Mappish*>(getObject());
|
||||
if (UNLIKELY(m_version != coll->getVersion())) {
|
||||
throw_collection_modified();
|
||||
}
|
||||
m_pos = coll->iterNextK(m_pos, valOut, keyOut);
|
||||
return m_pos;
|
||||
}
|
||||
|
||||
template<class Mappish>
|
||||
inline ALWAYS_INLINE
|
||||
Variant ArrayIter::iterKey(VersionableSparse) {
|
||||
return static_cast<Mappish*>(getObject())->iter_key(m_pos);
|
||||
}
|
||||
|
||||
template<class Tuplish>
|
||||
inline ALWAYS_INLINE
|
||||
Variant ArrayIter::iterValue(Fixed) {
|
||||
return tvAsCVarRef(static_cast<Tuplish*>(getObject())->get(m_pos));
|
||||
}
|
||||
|
||||
template<class Vectorish>
|
||||
inline ALWAYS_INLINE
|
||||
Variant ArrayIter::iterValue(Versionable) {
|
||||
return tvAsCVarRef(static_cast<Vectorish*>(getObject())->get(m_pos));
|
||||
}
|
||||
|
||||
template<class Mappish>
|
||||
inline ALWAYS_INLINE
|
||||
Variant ArrayIter::iterValue(VersionableSparse) {
|
||||
return tvAsCVarRef(static_cast<Mappish*>(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<class Coll, class Style>
|
||||
HOT_FUNC static
|
||||
void iterValue(ArrayIter* iter, TypedValue* out) {
|
||||
Variant val = iter->iterValue<Coll>(Style());
|
||||
assert(val.getRawType() != KindOfRef);
|
||||
cellDup(*val.asTypedValue(), *out);
|
||||
}
|
||||
|
||||
template<class Coll, class Style>
|
||||
HOT_FUNC static
|
||||
void iterKey(ArrayIter* iter, TypedValue* out) {
|
||||
Variant key = iter->iterKey<Coll>(Style());
|
||||
cellDup(*key.asTypedValue(), *out);
|
||||
}
|
||||
//
|
||||
// JIT helper functions for IterInit and IterInitK instructions over
|
||||
// collection iterators.
|
||||
//
|
||||
|
||||
template<class Coll, class Style>
|
||||
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<class Coll, class Style, ArrayIter::IterKind iterKind>
|
||||
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<Coll>(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<Coll, Style>(&dest->arr(), valOut);
|
||||
tvRefcountedDecRefHelper(vType, vDatum);
|
||||
|
||||
if (keyOut) {
|
||||
DataType kType = keyOut->m_type;
|
||||
uint64_t kDatum = keyOut->m_data.num;
|
||||
iterKey<Coll, Style>(&dest->arr(), keyOut);
|
||||
tvRefcountedDecRefHelper(kType, kDatum);
|
||||
}
|
||||
return 1LL;
|
||||
}
|
||||
|
||||
template int64_t iterInit<c_Pair,
|
||||
ArrayIter::Fixed,
|
||||
ArrayIter::IterKind::Pair>(
|
||||
Iter* dest, c_Pair* p, TypedValue* valOut);
|
||||
template int64_t iterInit<c_Vector,
|
||||
ArrayIter::Versionable,
|
||||
ArrayIter::IterKind::Vector>(
|
||||
Iter* dest, c_Vector* v, TypedValue* valOut);
|
||||
template int64_t iterInit<c_Map,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::IterKind::Map>(
|
||||
Iter* dest, c_Map* m, TypedValue* valOut);
|
||||
template int64_t iterInit<c_StableMap,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::IterKind::StableMap>(
|
||||
Iter* dest, c_StableMap* sm, TypedValue* valOut);
|
||||
template int64_t iterInit<c_Set,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::IterKind::Set>(
|
||||
Iter* dest, c_Set* s, TypedValue* valOut);
|
||||
|
||||
template<class Coll, class Style, ArrayIter::IterKind iterKind>
|
||||
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<Coll>(Style(), valOut, keyOut);
|
||||
} else {
|
||||
res = dest->arr().iterInit<Coll>(Style(), valOut);
|
||||
}
|
||||
if (res == 0LL) {
|
||||
dest->arr().~ArrayIter();
|
||||
return 0LL;
|
||||
}
|
||||
return 1LL;
|
||||
}
|
||||
|
||||
template int64_t iterInitK<c_Pair,
|
||||
ArrayIter::Fixed,
|
||||
ArrayIter::IterKind::Pair>(
|
||||
Iter* dest, c_Pair* coll,
|
||||
TypedValue* valOut, TypedValue* keyOut);
|
||||
template int64_t iterInitK<c_Vector,
|
||||
ArrayIter::Versionable,
|
||||
ArrayIter::IterKind::Vector>(
|
||||
Iter* dest, c_Vector* coll,
|
||||
TypedValue* valOut, TypedValue* keyOut);
|
||||
template int64_t iterInitK<c_Map,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::IterKind::Map>(
|
||||
Iter* dest, c_Map* coll,
|
||||
TypedValue* valOut, TypedValue* keyOut);
|
||||
template int64_t iterInitK<c_StableMap,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::IterKind::StableMap>(
|
||||
Iter* dest, c_StableMap* coll,
|
||||
TypedValue* valOut, TypedValue* keyOut);
|
||||
template int64_t iterInitK<c_Set,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::IterKind::Set>(
|
||||
Iter* dest, c_Set* coll,
|
||||
TypedValue* valOut, TypedValue* keyOut);
|
||||
|
||||
//
|
||||
// JIT helpers function for IterNext and IterNextK instruction over
|
||||
// collection iterators.
|
||||
//
|
||||
|
||||
template<class Coll, class Style>
|
||||
HOT_FUNC static
|
||||
int64_t iterNext(ArrayIter* iter, TypedValue* valOut, TypedValue* keyOut) {
|
||||
if (!iter->iterNext<Coll>(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<Coll>(Style(), valOut)) {
|
||||
iter->~ArrayIter();
|
||||
return 0LL;
|
||||
}
|
||||
tvRefcountedDecRefHelper(vType, vDatum);
|
||||
return 1LL;
|
||||
}
|
||||
|
||||
template int64_t iterNext<c_Vector, ArrayIter::Versionable>(
|
||||
ArrayIter* iter, TypedValue* valOut);
|
||||
template int64_t iterNext<c_Map, ArrayIter::VersionableSparse>(
|
||||
ArrayIter* iter, TypedValue* valOut);
|
||||
template int64_t iterNext<c_StableMap, ArrayIter::VersionableSparse>(
|
||||
ArrayIter* iter, TypedValue* valOut);
|
||||
template int64_t iterNext<c_Set, ArrayIter::VersionableSparse>(
|
||||
ArrayIter* iter, TypedValue* valOut);
|
||||
template int64_t iterNext<c_Pair, ArrayIter::Fixed>(
|
||||
ArrayIter* iter, TypedValue* valOut);
|
||||
|
||||
template<class Coll, class Style, ArrayIter::RefCountKey refCountKey>
|
||||
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<Coll, Style>(iter, valOut);
|
||||
DataType kType = keyOut->m_type;
|
||||
uint64_t kDatum = keyOut->m_data.num;
|
||||
if (!iter->iterNextKey<Coll>(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<Coll, Style>(iter, keyOut);
|
||||
if (refCountKey == ArrayIter::RefCountKey::Refcount) {
|
||||
tvRefcountedDecRefHelper(kType, kDatum);
|
||||
}
|
||||
return 1LL;
|
||||
}
|
||||
|
||||
template int64_t iterNextK<c_Vector,
|
||||
ArrayIter::Versionable,
|
||||
ArrayIter::RefCountKey::DontRefcount>(
|
||||
ArrayIter* iter, TypedValue* valOut, TypedValue* keyOut);
|
||||
template int64_t iterNextK<c_Map,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::RefCountKey::Refcount>(
|
||||
ArrayIter* iter, TypedValue* valOut, TypedValue* keyOut);
|
||||
template int64_t iterNextK<c_StableMap,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::RefCountKey::Refcount>(
|
||||
ArrayIter* iter, TypedValue* valOut, TypedValue* keyOut);
|
||||
template int64_t iterNextK<c_Set,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::RefCountKey::DontRefcount>(
|
||||
ArrayIter* iter, TypedValue* valOut, TypedValue* keyOut);
|
||||
template int64_t iterNextK<c_Pair,
|
||||
ArrayIter::Fixed,
|
||||
ArrayIter::RefCountKey::DontRefcount>(
|
||||
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<true, withRef>(dest, valOut);
|
||||
if (keyOut) {
|
||||
iter_key_cell_local_impl<true, withRef>(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<false>(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<withRef>(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<false, false>(dest, valOut);
|
||||
if (keyOut) {
|
||||
iter_key_cell_local_impl<false, false>(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<c_Vector, ArrayIter::Versionable>(
|
||||
return iterInitK<c_Vector,
|
||||
ArrayIter::Versionable,
|
||||
ArrayIter::IterKind::Vector>(
|
||||
dest, static_cast<c_Vector*>(obj),
|
||||
valOut, keyOut);
|
||||
case Collection::MapType:
|
||||
return iterInit<c_Map, ArrayIter::VersionableSparse>(
|
||||
return iterInitK<c_Map,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::IterKind::Map>(
|
||||
dest,
|
||||
static_cast<c_Map*>(obj),
|
||||
valOut, keyOut);
|
||||
case Collection::StableMapType:
|
||||
return iterInit<c_StableMap, ArrayIter::VersionableSparse>(
|
||||
return iterInitK<c_StableMap,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::IterKind::StableMap>(
|
||||
dest,
|
||||
static_cast<c_StableMap*>(obj),
|
||||
valOut, keyOut);
|
||||
case Collection::SetType:
|
||||
return iterInit<c_Set, ArrayIter::VersionableSparse>(
|
||||
return iterInitK<c_Set,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::IterKind::Set>(
|
||||
dest,
|
||||
static_cast<c_Set*>(obj),
|
||||
valOut, keyOut);
|
||||
case Collection::PairType:
|
||||
return iterInit<c_Pair, ArrayIter::Fixed>(
|
||||
return iterInitK<c_Pair,
|
||||
ArrayIter::Fixed,
|
||||
ArrayIter::IterKind::Pair>(
|
||||
dest,
|
||||
static_cast<c_Pair*>(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 <bool withRef>
|
||||
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<true, withRef>(iter, valOut);
|
||||
if (keyOut) {
|
||||
iter_key_cell_local_impl<true, withRef>(iter, keyOut);
|
||||
@@ -1166,49 +1338,31 @@ int64_t iter_next_cold(Iter* iter, TypedValue* valOut, TypedValue* keyOut) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <bool withRef>
|
||||
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<false>(
|
||||
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<c_Vector, ArrayIter::Versionable>(
|
||||
ai, valOut, keyOut);
|
||||
case Collection::MapType:
|
||||
return iterNext<c_Map, ArrayIter::VersionableSparse>(
|
||||
ai, valOut, keyOut);
|
||||
case Collection::StableMapType:
|
||||
return iterNext<c_StableMap, ArrayIter::VersionableSparse>(
|
||||
ai, valOut, keyOut);
|
||||
case Collection::SetType:
|
||||
return iterNext<c_Set, ArrayIter::VersionableSparse>(
|
||||
ai, valOut, keyOut);
|
||||
case Collection::PairType:
|
||||
return iterNext<c_Pair, ArrayIter::Fixed>(
|
||||
ai, valOut, keyOut);
|
||||
default:
|
||||
return iter_next_cold<withRef>(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<false>(iter, valOut, nullptr);
|
||||
return iter_next_any<false>(iter, valOut, nullptr);
|
||||
}
|
||||
|
||||
// Invoked for IterNextKArray and the WIterNext* instructions
|
||||
template <bool withRef>
|
||||
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<withRef>(iter, valOut, keyOut);
|
||||
return iter_next_any<withRef>(iter, valOut, keyOut);
|
||||
}
|
||||
|
||||
template int64_t iter_next_key<false>(Iter* dest,
|
||||
|
||||
@@ -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<class Tuplish>
|
||||
ArrayIter(Tuplish* coll, Fixed);
|
||||
ArrayIter(Tuplish* coll, IterKind iterKind, Fixed);
|
||||
template<class Vectorish>
|
||||
ArrayIter(Vectorish* coll, Versionable);
|
||||
ArrayIter(Vectorish* coll, IterKind iterKind, Versionable);
|
||||
template<class Mappish>
|
||||
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<class Tuplish>
|
||||
bool iterNext(Fixed);
|
||||
int64_t iterInit(Fixed, TypedValue* valOut);
|
||||
template<class Vectorish>
|
||||
bool iterNext(Versionable);
|
||||
int64_t iterInit(Versionable, TypedValue* valOut);
|
||||
template<class Mappish>
|
||||
bool iterNext(VersionableSparse);
|
||||
int64_t iterInit(VersionableSparse, TypedValue* valOut);
|
||||
template<class Tuplish>
|
||||
int64_t iterInitK(Fixed, TypedValue* valOut, TypedValue* keyOut);
|
||||
template<class Vectorish>
|
||||
int64_t iterInitK(Versionable, TypedValue* valOut, TypedValue* keyOut);
|
||||
template<class Mappish>
|
||||
int64_t iterInitK(
|
||||
VersionableSparse, TypedValue* valOut, TypedValue* keyOut);
|
||||
|
||||
template<class Tuplish>
|
||||
Variant iterValue(Fixed);
|
||||
int64_t iterNext(Fixed, TypedValue* valOut);
|
||||
template<class Vectorish>
|
||||
Variant iterValue(Versionable);
|
||||
int64_t iterNext(Versionable, TypedValue* valOut);
|
||||
template<class Mappish>
|
||||
Variant iterValue(VersionableSparse);
|
||||
|
||||
int64_t iterNext(VersionableSparse, TypedValue* valOut);
|
||||
template<class Tuplish>
|
||||
Variant iterKey(Fixed);
|
||||
int64_t iterNextKey(Fixed, TypedValue* valOut, TypedValue* keyOut);
|
||||
template<class Vectorish>
|
||||
Variant iterKey(Versionable);
|
||||
int64_t iterNextKey(Versionable, TypedValue* valOut, TypedValue* keyOut);
|
||||
template<class Mappish>
|
||||
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 <bool withRef>
|
||||
int64_t iter_next_key(Iter* dest, TypedValue* val, TypedValue* key);
|
||||
template <bool withRef>
|
||||
int64_t iter_next_any(Iter* dest, TypedValue* val, TypedValue* key);
|
||||
template<class Coll, class Style, ArrayIter::IterKind iterKind>
|
||||
int64_t iterInit(Iter* dest, Coll* coll, TypedValue* valOut);
|
||||
template<class Coll, class Style, ArrayIter::IterKind iterKind>
|
||||
int64_t iterInitK(Iter* dest, Coll* coll,
|
||||
TypedValue* valOut, TypedValue* keyOut);
|
||||
template<class Coll, class Style>
|
||||
int64_t iterNext(ArrayIter* iter, TypedValue* valOut);
|
||||
template<class Coll, class Style, ArrayIter::RefCountKey refCountKey>
|
||||
int64_t iterNextK(ArrayIter* iter, TypedValue* valOut, TypedValue* keyOut);
|
||||
|
||||
|
||||
int64_t new_miter_array_key(Iter* dest, RefData* arr, TypedValue* val,
|
||||
|
||||
@@ -167,6 +167,11 @@ class c_Vector : public ExtObjectDataFlags<ObjectData::VectorAttrInit|
|
||||
int64_t sz, char type);
|
||||
|
||||
private:
|
||||
int64_t iterInit(TypedValue* valOut);
|
||||
int64_t iterInitK(TypedValue* valOut, TypedValue* keyOut);
|
||||
int64_t iterNext(ssize_t pos, TypedValue* valOut);
|
||||
int64_t iterNextK(ssize_t pos, TypedValue* valOut, TypedValue* keyOut);
|
||||
|
||||
void grow();
|
||||
static void throwBadKeyType() ATTRIBUTE_COLD ATTRIBUTE_NORETURN;
|
||||
|
||||
@@ -393,6 +398,11 @@ class c_Map : public ExtObjectDataFlags<ObjectData::MapAttrInit|
|
||||
};
|
||||
|
||||
private:
|
||||
int64_t iterInit(TypedValue* valOut);
|
||||
int64_t iterInitK(TypedValue* valOut, TypedValue* keyOut);
|
||||
int64_t iterNext(ssize_t key, TypedValue* valOut);
|
||||
int64_t iterNextK(ssize_t key, TypedValue* valOut, TypedValue* keyOut);
|
||||
|
||||
/**
|
||||
* Map uses a power of two for the table size and quadratic probing to
|
||||
* resolve hash collisions.
|
||||
@@ -714,6 +724,11 @@ class c_StableMap : public ExtObjectDataFlags<ObjectData::StableMapAttrInit|
|
||||
void uksort(CVarRef cmp_function);
|
||||
|
||||
private:
|
||||
int64_t iterInit(TypedValue* valOut);
|
||||
int64_t iterInitK(TypedValue* valOut, TypedValue* keyOut);
|
||||
int64_t iterNext(ssize_t key, TypedValue* valOut);
|
||||
int64_t iterNextK(ssize_t key, TypedValue* valOut, TypedValue* keyOut);
|
||||
|
||||
uint m_size;
|
||||
uint m_nTableSize;
|
||||
uint m_nTableMask;
|
||||
@@ -909,6 +924,11 @@ class c_Set : public ExtObjectDataFlags<ObjectData::SetAttrInit|
|
||||
};
|
||||
|
||||
private:
|
||||
int64_t iterInit(TypedValue* valOut);
|
||||
int64_t iterInitK(TypedValue* valOut, TypedValue* keyOut);
|
||||
int64_t iterNext(ssize_t key, TypedValue* valOut);
|
||||
int64_t iterNextK(ssize_t key, TypedValue* valOut, TypedValue* keyOut);
|
||||
|
||||
/**
|
||||
* Set uses a power of two for the table size and quadratic probing to
|
||||
* resolve hash collisions, similar to the Map class. See the comments
|
||||
@@ -1105,6 +1125,11 @@ class c_Pair : public ExtObjectDataFlags<ObjectData::PairAttrInit|
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t iterInit(TypedValue* valOut);
|
||||
int64_t iterInitK(TypedValue* valOut, TypedValue* keyOut);
|
||||
int64_t iterNext(ssize_t pos, TypedValue* valOut);
|
||||
int64_t iterNextK(ssize_t pos, TypedValue* valOut, TypedValue* keyOut);
|
||||
|
||||
static void throwBadKeyType() ATTRIBUTE_COLD ATTRIBUTE_NORETURN;
|
||||
|
||||
uint m_size;
|
||||
@@ -1223,6 +1248,11 @@ inline bool isOptimizableCollectionClass(const Class* klass) {
|
||||
klass == c_StableMap::s_cls || klass == c_Pair::s_cls;
|
||||
}
|
||||
|
||||
inline bool isCollectionClass(const Class* klass) {
|
||||
return klass == c_Vector::s_cls || klass == c_Map::s_cls ||
|
||||
klass == c_StableMap::s_cls || klass == c_Pair::s_cls ||
|
||||
klass == c_Set::s_cls;
|
||||
}
|
||||
|
||||
void collectionSerialize(ObjectData* obj, VariableSerializer* serializer);
|
||||
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| HipHop for PHP |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
|
||||
| Copyright (c) 1997-2010 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef incl_HPHP_EXT_COLLECTION_DEF_H_
|
||||
#define incl_HPHP_EXT_COLLECTION_DEF_H_
|
||||
|
||||
#include "hphp/runtime/ext/ext_collections.h"
|
||||
|
||||
namespace HPHP {
|
||||
|
||||
// Vector JIT helpers
|
||||
inline int64_t c_Vector::iterInit(TypedValue* valOut) {
|
||||
if (UNLIKELY(0 >= 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<Bucket*>(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<Bucket*>(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<Bucket*>(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<Bucket*>(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<Bucket*>(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<Bucket*>(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<Bucket*>(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<Bucket*>(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<Bucket*>(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<Bucket*>(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<Bucket*>(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<Bucket*>(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_
|
||||
@@ -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:?";
|
||||
|
||||
@@ -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<GuardIter>()->iterId);
|
||||
auto const loc = rFP[baseOff + ArrayIter::getOffsetOfIterKind()];
|
||||
auto const iterKind = inst->typeParam().getIterKind();
|
||||
assert(sizeof(ArrayIter::IterKind) == 4);
|
||||
m_as.cmpl(static_cast<int32_t>(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<GuardLoc>()->locId);
|
||||
@@ -5563,22 +5575,84 @@ void CodeGenerator::cgIterInitCommon(IRInstruction* inst) {
|
||||
isInitK ? (TCA)new_iter_array_key<false> : (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<c_Pair,
|
||||
ArrayIter::Fixed,
|
||||
ArrayIter::IterKind::Pair> :
|
||||
(TCA)iterInit<c_Pair,
|
||||
ArrayIter::Fixed,
|
||||
ArrayIter::IterKind::Pair>;
|
||||
} else if (klass == c_Vector::s_cls) {
|
||||
helperAddr = inst->op() == IterInitK ?
|
||||
(TCA)iterInitK<c_Vector,
|
||||
ArrayIter::Versionable,
|
||||
ArrayIter::IterKind::Vector> :
|
||||
(TCA)iterInit<c_Vector,
|
||||
ArrayIter::Versionable,
|
||||
ArrayIter::IterKind::Vector>;
|
||||
} else if (klass == c_Map::s_cls) {
|
||||
helperAddr = inst->op() == IterInitK ?
|
||||
(TCA)iterInitK<c_Map,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::IterKind::Map> :
|
||||
(TCA)iterInit<c_Map,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::IterKind::Map>;
|
||||
} else if (klass == c_StableMap::s_cls) {
|
||||
helperAddr = inst->op() == IterInitK ?
|
||||
(TCA)iterInitK<c_StableMap,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::IterKind::StableMap> :
|
||||
(TCA)iterInit<c_StableMap,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::IterKind::StableMap>;
|
||||
} else if (klass == c_Set::s_cls) {
|
||||
helperAddr = inst->op() == IterInitK ?
|
||||
(TCA)iterInitK<c_Set,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::IterKind::Set> :
|
||||
(TCA)iterInit<c_Set,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::IterKind::Set>;
|
||||
}
|
||||
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<true> :
|
||||
isNextK ? (TCA)iter_next_key<false> : (TCA)iter_next;
|
||||
TCA helperAddr;
|
||||
if (isWNext) {
|
||||
helperAddr = (TCA)iter_next_key<true>;
|
||||
} else {
|
||||
if (isArray) {
|
||||
helperAddr = isNextK ? (TCA)iter_next_key<false>
|
||||
: (TCA)iter_next;
|
||||
} else {
|
||||
args.imm(0);
|
||||
helperAddr = (TCA)iter_next_any<false>;
|
||||
}
|
||||
}
|
||||
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<c_Vector, ArrayIter::Versionable>,
|
||||
inst->dst(),
|
||||
SyncOptions::kSyncPoint,
|
||||
args);
|
||||
}
|
||||
|
||||
void CodeGenerator::cgIterNextKVector(IRInstruction* inst) {
|
||||
ArgGroup args(m_regs);
|
||||
loadIterNextArg(args, inst, true);
|
||||
cgCallHelper(m_as,
|
||||
(TCA)iterNextK<c_Vector,
|
||||
ArrayIter::Versionable,
|
||||
ArrayIter::RefCountKey::DontRefcount>,
|
||||
inst->dst(),
|
||||
SyncOptions::kSyncPoint,
|
||||
args);
|
||||
}
|
||||
|
||||
void CodeGenerator::cgIterNextMap(IRInstruction* inst) {
|
||||
ArgGroup args(m_regs);
|
||||
loadIterNextArg(args, inst, false);
|
||||
cgCallHelper(m_as,
|
||||
(TCA)iterNext<c_Map, ArrayIter::VersionableSparse>,
|
||||
inst->dst(),
|
||||
SyncOptions::kSyncPoint,
|
||||
args);
|
||||
}
|
||||
|
||||
void CodeGenerator::cgIterNextKMap(IRInstruction* inst) {
|
||||
ArgGroup args(m_regs);
|
||||
loadIterNextArg(args, inst, true);
|
||||
cgCallHelper(m_as,
|
||||
(TCA)iterNextK<c_Map,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::RefCountKey::Refcount>,
|
||||
inst->dst(),
|
||||
SyncOptions::kSyncPoint,
|
||||
args);
|
||||
}
|
||||
|
||||
void CodeGenerator::cgIterNextStableMap(IRInstruction* inst) {
|
||||
ArgGroup args(m_regs);
|
||||
loadIterNextArg(args, inst, false);
|
||||
cgCallHelper(m_as,
|
||||
(TCA)iterNext<c_StableMap, ArrayIter::VersionableSparse>,
|
||||
inst->dst(),
|
||||
SyncOptions::kSyncPoint,
|
||||
args);
|
||||
}
|
||||
|
||||
void CodeGenerator::cgIterNextKStableMap(IRInstruction* inst) {
|
||||
ArgGroup args(m_regs);
|
||||
loadIterNextArg(args, inst, true);
|
||||
cgCallHelper(m_as,
|
||||
(TCA)iterNextK<c_StableMap,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::RefCountKey::Refcount>,
|
||||
inst->dst(),
|
||||
SyncOptions::kSyncPoint,
|
||||
args);
|
||||
}
|
||||
|
||||
void CodeGenerator::cgIterNextSet(IRInstruction* inst) {
|
||||
ArgGroup args(m_regs);
|
||||
loadIterNextArg(args, inst, false);
|
||||
cgCallHelper(m_as,
|
||||
(TCA)iterNext<c_Set, ArrayIter::VersionableSparse>,
|
||||
inst->dst(),
|
||||
SyncOptions::kSyncPoint,
|
||||
args);
|
||||
}
|
||||
|
||||
void CodeGenerator::cgIterNextKSet(IRInstruction* inst) {
|
||||
ArgGroup args(m_regs);
|
||||
loadIterNextArg(args, inst, true);
|
||||
cgCallHelper(m_as,
|
||||
(TCA)iterNextK<c_Set,
|
||||
ArrayIter::VersionableSparse,
|
||||
ArrayIter::RefCountKey::DontRefcount>,
|
||||
inst->dst(),
|
||||
SyncOptions::kSyncPoint,
|
||||
args);
|
||||
}
|
||||
|
||||
void CodeGenerator::cgIterNextPair(IRInstruction* inst) {
|
||||
ArgGroup args(m_regs);
|
||||
loadIterNextArg(args, inst, false);
|
||||
cgCallHelper(m_as,
|
||||
(TCA)iterNext<c_Pair, ArrayIter::Fixed>,
|
||||
inst->dst(),
|
||||
SyncOptions::kSyncPoint,
|
||||
args);
|
||||
}
|
||||
|
||||
void CodeGenerator::cgIterNextKPair(IRInstruction* inst) {
|
||||
ArgGroup args(m_regs);
|
||||
loadIterNextArg(args, inst, true);
|
||||
cgCallHelper(m_as,
|
||||
(TCA)iterNextK<c_Pair,
|
||||
ArrayIter::Fixed,
|
||||
ArrayIter::RefCountKey::DontRefcount>,
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -871,7 +871,7 @@ template<class Lambda>
|
||||
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));
|
||||
|
||||
@@ -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<bool>& 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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<std::string> types;
|
||||
# define IRT(name, ...) if (name.subtypeOf(*this)) types.push_back(#name);
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário