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:
Dario Russi
2013-07-17 15:31:39 -07:00
commit de Sara Golemon
commit f2f631c163
23 arquivos alterados com 1238 adições e 283 exclusões
+23 -2
Ver Arquivo
@@ -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.
+327 -173
Ver Arquivo
@@ -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,
+91 -33
Ver Arquivo
@@ -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,
+30
Ver Arquivo
@@ -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);
+253
Ver Arquivo
@@ -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_
+17 -7
Ver Arquivo
@@ -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:?";
+229 -14
Ver Arquivo
@@ -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);
}
+4
Ver Arquivo
@@ -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);
+1
Ver Arquivo
@@ -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);
+68 -14
Ver Arquivo
@@ -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));
+7 -2
Ver Arquivo
@@ -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,
+9 -2
Ver Arquivo
@@ -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
+3 -1
Ver Arquivo
@@ -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);
+25
Ver Arquivo
@@ -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) \
+22 -1
Ver Arquivo
@@ -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;
}
+9 -2
Ver Arquivo
@@ -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
+20 -1
Ver Arquivo
@@ -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) {
+12
Ver Arquivo
@@ -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());
-1
Ver Arquivo
@@ -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"
+29 -7
Ver Arquivo
@@ -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;
}
+24 -17
Ver Arquivo
@@ -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;
};
};
/*
+5 -1
Ver Arquivo
@@ -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) {
+30 -5
Ver Arquivo
@@ -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);
}