Add bytecodes for some "withRef" operations

Functions like array_filter and array_map need "withRef" semantics,
and while it can be simulated in php via copy-on-write, doing so
is very inefficient.

This adds

  SetWithRefLM
  SetWithRefRM
    - similar to SetM but binds the value if it was a reference. L reads a local, R reads a return value

  WIterInit
  WIterInitK
  WIterNext
  WIterNextK
    - essentially the same as the corresponding opcodes without W, but the value local is set by reference if the array element was a reference.
Esse commit está contido em:
mwilliams
2013-05-19 12:09:30 -07:00
commit de sgolemon
commit c12714e2bb
32 arquivos alterados com 1012 adições e 220 exclusões
+14 -4
Ver Arquivo
@@ -283,6 +283,7 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
#define COUNT_FOUR(t1,t2,t3,t4) 4
#define COUNT_LMANY() 0
#define COUNT_C_LMANY() 0
#define COUNT_R_LMANY() 0
#define COUNT_V_LMANY() 0
#define COUNT_FMANY 0
#define COUNT_CMANY 0
@@ -332,6 +333,9 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
#define POP_V_LMANY() \
getEmitterVisitor().popEvalStack(StackSym::V); \
getEmitterVisitor().popEvalStackLMany()
#define POP_R_LMANY() \
getEmitterVisitor().popEvalStack(StackSym::R); \
getEmitterVisitor().popEvalStackLMany()
#define POP_FMANY \
getEmitterVisitor().popEvalStackMany(a1, StackSym::F)
#define POP_CMANY \
@@ -549,6 +553,7 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
#undef POP_LMANY
#undef POP_C_LMANY
#undef POP_V_LMANY
#undef POP_R_LMANY
#undef POP_CV
#undef POP_VV
#undef POP_HV
@@ -7231,9 +7236,13 @@ static Unit* emitHHBCNativeClassUnit(const HhbcExtClassInfo* builtinClasses,
const ClassInfo::PropertyInfo* propInfo = propVec[i];
assert(propInfo);
int attr = AttrNone;
if (propInfo->attribute & ClassInfo::IsProtected) attr |= AttrProtected;
else if (propInfo->attribute & ClassInfo::IsPrivate) attr |= AttrPrivate;
else attr |= AttrPublic;
if (propInfo->attribute & ClassInfo::IsProtected) {
attr |= AttrProtected;
} else if (propInfo->attribute & ClassInfo::IsPrivate) {
attr |= AttrPrivate;
} else {
attr |= AttrPublic;
}
if (propInfo->attribute & ClassInfo::IsStatic) attr |= AttrStatic;
TypedValue tvNull;
@@ -7242,7 +7251,8 @@ static Unit* emitHHBCNativeClassUnit(const HhbcExtClassInfo* builtinClasses,
propInfo->name.get(),
Attr(attr),
nullptr,
propInfo->docComment ? StringData::GetStaticString(propInfo->docComment) : nullptr,
propInfo->docComment ?
StringData::GetStaticString(propInfo->docComment) : nullptr,
&tvNull,
KindOfInvalid
);
+45 -4
Ver Arquivo
@@ -3150,6 +3150,40 @@ SetM <loc-desc/M-vector> [C..C C] -> [C]
SC | BaseSC
SL | BaseSL
SetWithRefLM <loc-desc/M-vector> <local variable id> [C..C] -> []
Set member preserving the reffiness of the local.
location Base* member intermediate final
descriptor operation code operation operation
-----------+---------- -------+--------------+-----------
C | BaseC EC | ElemCD | SetWithRefElemC
R | BaseR PC | PropCD | SetPropC
L | BaseLD EL | ElemLD | SetWithRefElemL
NC | BaseNCD PL | PropLD | SetPropL
NL | BaseNLD W | NewElem | SetWithRefNewElem
GC | BaseGCD
GL | BaseGLD
SC | BaseSC
SL | BaseSL
SetWithRefRM <loc-desc/M-vector> [C..C R] -> []
Set member preserving the reffiness of the stack element.
location Base* member intermediate final
descriptor operation code operation operation
-----------+---------- -------+--------------+-----------
C | BaseC EC | ElemCD | SetWithRefElemC
R | BaseR PC | PropCD | SetPropC
L | BaseLD EL | ElemLD | SetWithRefElemL
NC | BaseNCD PL | PropLD | SetPropL
NL | BaseNLD W | NewElem | SetWithRefNewElem
GC | BaseGCD
GL | BaseGLD
SC | BaseSC
SL | BaseSL
SetOpM <op> <loc-desc/M-vector> [C..C C] -> [C]
Set op member.
@@ -3224,6 +3258,8 @@ UnsetM <loc-desc/M-vector> [C..C] -> []
IterInit <iterator id> <rel offset> <local id> [C] -> []
IterInitK <iterator id> <rel offset> <local id> <local id> [C] -> []
WIterInit <iterator id> <rel offset> <local id> [C] -> []
WIterInitK <iterator id> <rel offset> <local id> <local id> [C] -> []
Initialize iterator. If $1 is an array, these instructions create an array
iterator, rewind the array iterator to point to the beginning of the array,
@@ -3264,8 +3300,9 @@ IterInitK <iterator id> <rel offset> <local id> <local id> [C] -> []
transfer control to the location specified by %2.
If the iterator specified by %1 is a non-mutable array iterator or an
extension class iterator, these instructions store a copy of the current
value in %3 as a cell.
extension class iterator, IterInit and IterInitK store a copy of the current
value in %3 as a cell; WIterInit and WIterInitK will store a reference
if the current element is a reference, or a value otherwise in %3
If the iterator specified by %1 is a user class iterator for object $x, these
instructions store the return value of $x->current() in %3 as a cell.
@@ -3331,6 +3368,8 @@ MIterInitK <iterator id> <rel offset> <local id> <local id> [V] -> []
IterNext <iterator id> <rel offset> <local id> [] -> []
IterNextK <iterator id> <rel offset> <local id> <local id> [] -> []
WIterNext <iterator id> <rel offset> <local id> [] -> []
WIterNextK <iterator id> <rel offset> <local id> <local id> [] -> []
Iterator next. If the specified iterator is a non-mutable array iterator
or an extension class iterator, advance the iterator to point to the next
@@ -3353,8 +3392,10 @@ IterNextK <iterator id> <rel offset> <local id> <local id> [] -> []
If the specified iterator is not at the end, retrieve the key and value:
If the iterator specified by %1 is a non-mutable array iterator or an
extension class iterator, these instructions store a copy of the new current
value in %3 as a cell.
extension class iterator, IterNext and IterNextK store a copy of the new current
value in %3 as a cell; WIterNext and WIterNextK store a reference to the
new current value in %3 if the current value is a reference, otherwise
they store the new current value in %3 as a cell.
If the iterator specified by %1 is a user class iterator for object $x, these
instructions store the return value of $x->current() in %3 as a cell.
+27
Ver Arquivo
@@ -1549,6 +1549,8 @@ DbgAssertType<T> S0:Cell
D:Bool = IterInit S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt
D:Bool = IterInitK S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt S4:ConstInt
D:Bool = WIterInit S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt
D:Bool = WIterInitK S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt S4:ConstInt
Initializes the iterator variable whose index is given by S2. This
instruction creates the appropriate iterator for the array or object
@@ -1563,12 +1565,20 @@ D:Bool = IterInitK S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt S4:ConstInt
instruction stores the iterator's first value (and key) into the
local variable S3 (and S4, respectively).
The IterInit and IterInitK instructions always copy the array
element by value.
The WIterInit and WIterInitK instructions copy referenced array
elements by reference, and non-referenced array elements by value.
This instruction has the ConsumesRC property because it either
decrements the reference count of s0 or stores a reference to S0
into the new iterator.
D:Bool = IterNext S0:FramePtr S1:ConstInt S2:ConstInt
D:Bool = IterNextK S0:FramePtr S1:ConstInt S2:ConstInt S3:ConstInt
D:Bool = WIterNext S0:FramePtr S1:ConstInt S2:ConstInt
D:Bool = WIterNextK S0:FramePtr S1:ConstInt S2:ConstInt S3:ConstInt
Advances the iterator variable whose index is given by S1. S2 and S3
are local variable indices. S0 points to the stack frame containing
@@ -1580,6 +1590,11 @@ D:Bool = IterNextK 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
element by value.
The WIterInit and WIterInitK instructions copy referenced array
elements by reference, and non-referenced array elements by value.
IterFree S0:FramePtr S1:ConstInt
Free the iterator variable whose index is given by S1 in the stack
@@ -1729,6 +1744,12 @@ D:Vector, D:StkPtr = SetElemStk S0:ConstTCA S1:PtrToGen S2:Gen S3:Cell
Set element with key S2 in S1 to S3.
SetWithRefElem S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToGen S4:PtrToCell
D:StkPtr = SetWithRefElemStk S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToGen S4:PtrToCell
Set element with key S2 in S1 to S3. S4 should point to
an MInstrState struct.
UnsetElem S0:ConstTCA S1:PtrToGen S2:Gen
D:StkPtr = UnsetElemStk S0:ConstTCA S1:PtrToGen S2:Gen
@@ -1752,6 +1773,12 @@ D:Vector, D:StkPtr = SetNewElemStk S0:PtrToGen S1:Cell
Append the value in S1 to S0.
SetWithRefNewElem S0:ConstTCA S1:PtrToGen S2:PtrToGen S3:PtrToCell
D:StkPtr = SetWithRefNewElemStk S0:ConstTCA S1:PtrToGen S2:PtrToGen
S3:PtrToCell
AppendWithRef the value in S2 to S1. S3 should point to an MInstrState struct.
BindNewElem S0:ConstTCA S0:PtrToGen S1:BoxedCell S2:PtrToCell
D:StkPtr = BindNewElemStk S0:PtrToGen S1:BoxedCell S2:PtrToCell
+123 -79
Ver Arquivo
@@ -716,10 +716,10 @@ void Iter::mfree() {
* to make sure the hot part is a good-looking leaf function; otherwise,
* you're likely to get a performance regression.
*/
template <bool typeArray>
template <bool typeArray, bool withRef>
static inline void iter_value_cell_local_impl(Iter* iter, TypedValue* out) {
DataType oldType = out->m_type;
assert(oldType != KindOfRef);
assert(withRef || oldType != KindOfRef);
uint64_t oldDatum = out->m_data.num;
TRACE(2, "%s: typeArray: %s, I %p, out %p\n",
__func__, typeArray ? "true" : "false", iter, out);
@@ -727,8 +727,13 @@ static inline void iter_value_cell_local_impl(Iter* iter, TypedValue* out) {
(!typeArray && iter->arr().getIterType() == ArrayIter::TypeIterator));
ArrayIter& arrIter = iter->arr();
if (typeArray) {
TypedValue* cur = tvToCell(arrIter.nvSecond());
tvDupCell(cur, out);
TypedValue* cur = arrIter.nvSecond();
if (cur->m_type == KindOfRef) {
if (!withRef || cur->m_data.pref->getCount() == 1) {
cur = cur->m_data.pref->tv();
}
}
tvDup(cur, out);
} else {
Variant val = arrIter.second();
assert(val.getRawType() != KindOfRef);
@@ -737,10 +742,10 @@ static inline void iter_value_cell_local_impl(Iter* iter, TypedValue* out) {
tvRefcountedDecRefHelper(oldType, oldDatum);
}
template <bool typeArray>
template <bool typeArray, bool withRef>
static inline void iter_key_cell_local_impl(Iter* iter, TypedValue* out) {
DataType oldType = out->m_type;
assert(oldType != KindOfRef);
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) ||
@@ -761,18 +766,19 @@ static inline void iter_key_cell_local_impl(Iter* iter, TypedValue* out) {
* refcount of the specified array. If new_iter_array does not create an
* iterator, it decRefs the array.
*/
static NEVER_INLINE
template <bool withRef>
NEVER_INLINE
int64_t new_iter_array_cold(Iter* dest, ArrayData* arr, TypedValue* valOut,
TypedValue* keyOut) {
TypedValue* keyOut) {
TRACE(2, "%s: I %p, arr %p\n", __func__, dest, arr);
if (!arr->empty()) {
// 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);
iter_value_cell_local_impl<true>(dest, valOut);
iter_value_cell_local_impl<true, withRef>(dest, valOut);
if (keyOut) {
iter_key_cell_local_impl<true>(dest, keyOut);
iter_key_cell_local_impl<true, withRef>(dest, keyOut);
}
return 1LL;
}
@@ -782,12 +788,24 @@ int64_t new_iter_array_cold(Iter* dest, ArrayData* arr, TypedValue* valOut,
return 0LL;
}
static inline void getHphpArrayElm(HphpArray::Elm* elm, TypedValue* valOut,
TypedValue* keyOut) {
TypedValue* cur = tvToCell(&elm->data);
tvDupCell(cur, valOut);
if (keyOut) {
HphpArray::getElmKey(elm, keyOut);
template <bool withRef>
inline ALWAYS_INLINE
void getHphpArrayElm(HphpArray::Elm* elm, TypedValue* valOut,
TypedValue* keyOut) {
if (withRef) {
tvAsVariant(valOut) = withRefBind(tvAsVariant(&elm->data));
if (LIKELY(keyOut != nullptr)) {
DataType t = keyOut->m_type;
uint64_t d = keyOut->m_data.num;
HphpArray::getElmKey(elm, keyOut);
tvRefcountedDecRefHelper(t, d);
}
} else {
TypedValue* cur = tvToCell(&elm->data);
tvDupCell(cur, valOut);
if (keyOut) {
HphpArray::getElmKey(elm, keyOut);
}
}
}
@@ -810,7 +828,7 @@ int64_t new_iter_array(Iter* dest, ArrayData* ad, TypedValue* valOut) {
(void) new (&dest->arr()) ArrayIter(arr, ArrayIter::noIncNonNull);
dest->arr().setIterType(ArrayIter::TypeArray);
HphpArray::Elm* elm = arr->getElm(dest->arr().m_pos);
getHphpArrayElm(elm, valOut, nullptr);
getHphpArrayElm<false>(elm, valOut, nullptr);
return 1LL;
}
// We did not transfer ownership of the array to an iterator, so we need
@@ -822,33 +840,38 @@ int64_t new_iter_array(Iter* dest, ArrayData* ad, TypedValue* valOut) {
return 0LL;
}
cold:
return new_iter_array_cold(dest, ad, valOut, nullptr);
return new_iter_array_cold<false>(dest, ad, valOut, nullptr);
}
template <bool withRef>
HOT_FUNC
int64_t new_iter_array_key(Iter* dest, ArrayData* ad, TypedValue* valOut,
TypedValue* keyOut) {
int64_t new_iter_array_key(Iter* dest, ArrayData* ad,
TypedValue* valOut, TypedValue* keyOut) {
TRACE(2, "%s: I %p, ad %p\n", __func__, dest, ad);
valOut = tvToCell(valOut);
keyOut = tvToCell(keyOut);
if (!withRef) {
valOut = tvToCell(valOut);
keyOut = tvToCell(keyOut);
}
if (UNLIKELY(!ad->isHphpArray())) {
goto cold;
}
{
HphpArray* arr = (HphpArray*)ad;
if (LIKELY(arr->getSize() != 0)) {
if (UNLIKELY(tvWillBeReleased(valOut)) ||
UNLIKELY(tvWillBeReleased(keyOut))) {
goto cold;
if (!withRef) {
if (UNLIKELY(tvWillBeReleased(valOut)) ||
UNLIKELY(tvWillBeReleased(keyOut))) {
goto cold;
}
tvDecRefOnly(valOut);
tvDecRefOnly(keyOut);
}
tvDecRefOnly(valOut);
tvDecRefOnly(keyOut);
// 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);
HphpArray::Elm* elm = arr->getElm(dest->arr().m_pos);
getHphpArrayElm(elm, valOut, keyOut);
getHphpArrayElm<withRef>(elm, valOut, keyOut);
return 1LL;
}
// We did not transfer ownership of the array to an iterator, so we need
@@ -860,9 +883,16 @@ int64_t new_iter_array_key(Iter* dest, ArrayData* ad, TypedValue* valOut,
return 0LL;
}
cold:
return new_iter_array_cold(dest, ad, valOut, keyOut);
return new_iter_array_cold<withRef>(dest, ad, valOut, keyOut);
}
template int64_t new_iter_array_key<false>(Iter* dest, ArrayData* ad,
TypedValue* valOut,
TypedValue* keyOut);
template int64_t new_iter_array_key<true>(Iter* dest, ArrayData* ad,
TypedValue* valOut,
TypedValue* keyOut);
class FreeObj {
public:
FreeObj() : m_obj(0) {}
@@ -943,14 +973,14 @@ int64_t new_iter_object(Iter* dest, ObjectData* obj, Class* ctx,
dest->arr().setIterType(itType);
if (itType == ArrayIter::TypeIterator) {
iter_value_cell_local_impl<false>(dest, valOut);
iter_value_cell_local_impl<false, false>(dest, valOut);
if (keyOut) {
iter_key_cell_local_impl<false>(dest, keyOut);
iter_key_cell_local_impl<false, false>(dest, keyOut);
}
} else {
iter_value_cell_local_impl<true>(dest, valOut);
iter_value_cell_local_impl<true, false>(dest, valOut);
if (keyOut) {
iter_key_cell_local_impl<true>(dest, keyOut);
iter_key_cell_local_impl<true, false>(dest, keyOut);
}
}
return 1LL;
@@ -970,7 +1000,8 @@ int64_t new_iter_object(Iter* dest, ObjectData* obj, Class* ctx,
* to make sure the hot part is a good-looking leaf function; otherwise,
* you're likely to get a performance regression.
*/
static NEVER_INLINE
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 ||
@@ -983,14 +1014,14 @@ int64_t iter_next_cold(Iter* iter, TypedValue* valOut, TypedValue* keyOut) {
return 0;
}
if (iter->arr().getIterType() == ArrayIter::TypeArray) {
iter_value_cell_local_impl<true>(iter, valOut);
iter_value_cell_local_impl<true, withRef>(iter, valOut);
if (keyOut) {
iter_key_cell_local_impl<true>(iter, keyOut);
iter_key_cell_local_impl<true, withRef>(iter, keyOut);
}
} else {
iter_value_cell_local_impl<false>(iter, valOut);
iter_value_cell_local_impl<false, withRef>(iter, valOut);
if (keyOut) {
iter_key_cell_local_impl<false>(iter, keyOut);
iter_key_cell_local_impl<false, withRef>(iter, keyOut);
}
}
return 1;
@@ -1013,41 +1044,45 @@ int64_t iter_next(Iter* iter, TypedValue* valOut) {
}
const HphpArray* arr = (HphpArray*)ad;
ssize_t pos = arrIter->getPos();
if (size_t(pos) >= size_t(arr->getLastE())) {
if (UNLIKELY(arr->getCount() == 1)) {
goto cold;
HphpArray::Elm* elm;
do {
if (size_t(pos) >= size_t(arr->getLastE())) {
if (UNLIKELY(arr->getCount() == 1)) {
goto cold;
}
arr->decRefCount();
if (debug) {
iter->arr().setIterType(ArrayIter::TypeUndefined);
}
return 0;
}
arr->decRefCount();
if (debug) {
iter->arr().setIterType(ArrayIter::TypeUndefined);
}
return 0;
}
pos = pos + 1;
HphpArray::Elm* elm = arr->getElm(pos);
if (UNLIKELY(elm->data.m_type >= HphpArray::KindOfTombstone)) {
goto cold;
}
pos = pos + 1;
elm = arr->getElm(pos);
} while (elm->data.m_type >= HphpArray::KindOfTombstone);
if (UNLIKELY(tvWillBeReleased(valOut))) {
goto cold;
}
tvDecRefOnly(valOut);
arrIter->setPos(pos);
getHphpArrayElm(elm, valOut, nullptr);
getHphpArrayElm<false>(elm, valOut, nullptr);
return 1;
}
cold:
return iter_next_cold(iter, valOut, nullptr);
return iter_next_cold<false>(iter, valOut, nullptr);
}
template <bool withRef>
HOT_FUNC
int64_t iter_next_key(Iter* iter, TypedValue* valOut, TypedValue* keyOut) {
TRACE(2, "iter_next: I %p\n", iter);
TRACE(2, "iter_next_key: I %p\n", iter);
assert(iter->arr().getIterType() == ArrayIter::TypeArray ||
iter->arr().getIterType() == ArrayIter::TypeIterator);
ArrayIter* arrIter = &iter->arr();
valOut = tvToCell(valOut);
keyOut = tvToCell(keyOut);
if (!withRef) {
valOut = tvToCell(valOut);
keyOut = tvToCell(keyOut);
}
if (UNLIKELY(!arrIter->hasArrayData())) {
goto cold;
}
@@ -1058,36 +1093,45 @@ int64_t iter_next_key(Iter* iter, TypedValue* valOut, TypedValue* keyOut) {
}
const HphpArray* arr = (HphpArray*)ad;
ssize_t pos = arrIter->getPos();
if (size_t(pos) >= size_t(arr->getLastE())) {
if (UNLIKELY(arr->getCount() == 1)) {
HphpArray::Elm* elm;
do {
if (size_t(pos) >= size_t(arr->getLastE())) {
if (UNLIKELY(arr->getCount() == 1)) {
goto cold;
}
arr->decRefCount();
if (debug) {
iter->arr().setIterType(ArrayIter::TypeUndefined);
}
return 0;
}
pos = pos + 1;
elm = arr->getElm(pos);
} while (elm->data.m_type >= HphpArray::KindOfTombstone);
if (!withRef) {
if (UNLIKELY(tvWillBeReleased(valOut))) {
goto cold;
}
arr->decRefCount();
if (debug) {
iter->arr().setIterType(ArrayIter::TypeUndefined);
if (UNLIKELY(tvWillBeReleased(keyOut))) {
goto cold;
}
return 0;
tvDecRefOnly(valOut);
tvDecRefOnly(keyOut);
}
pos = pos + 1;
HphpArray::Elm* elm = arr->getElm(pos);
if (UNLIKELY(elm->data.m_type >= HphpArray::KindOfTombstone)) {
goto cold;
}
if (UNLIKELY(tvWillBeReleased(valOut))) {
goto cold;
}
if (UNLIKELY(tvWillBeReleased(keyOut))) {
goto cold;
}
tvDecRefOnly(valOut);
tvDecRefOnly(keyOut);
arrIter->setPos(pos);
getHphpArrayElm(elm, valOut, keyOut);
getHphpArrayElm<withRef>(elm, valOut, keyOut);
return 1;
}
cold:
return iter_next_cold(iter, valOut, keyOut);
cold:
return iter_next_cold<withRef>(iter, valOut, keyOut);
}
template int64_t iter_next_key<false>(Iter* dest,
TypedValue* valOut,
TypedValue* keyOut);
template int64_t iter_next_key<true>(Iter* dest,
TypedValue* valOut,
TypedValue* keyOut);
///////////////////////////////////////////////////////////////////////////////
}
+2
Ver Arquivo
@@ -469,11 +469,13 @@ bool interp_iter_next(Iter* it);
bool interp_iter_next_m(Iter* it);
int64_t new_iter_array(Iter* dest, ArrayData* arr, TypedValue* val);
template <bool withRef>
int64_t new_iter_array_key(Iter* dest, ArrayData* arr, TypedValue* val,
TypedValue* key);
int64_t new_iter_object(Iter* dest, ObjectData* obj, Class* ctx,
TypedValue* val, TypedValue* key);
int64_t iter_next(Iter* dest, TypedValue* val);
template <bool withRef>
int64_t iter_next_key(Iter* dest, TypedValue* val, TypedValue* key);
///////////////////////////////////////////////////////////////////////////////
+2 -1
Ver Arquivo
@@ -280,7 +280,8 @@ string FileRepository::unitMd5(const string& fileMd5) {
string t = fileMd5 + '\0'
+ (RuntimeOption::EnableHipHopSyntax ? '1' : '0')
+ (RuntimeOption::EnableEmitSwitch ? '1' : '0')
+ (RuntimeOption::EvalJitEnableRenameFunction ? '1' : '0');
+ (RuntimeOption::EvalJitEnableRenameFunction ? '1' : '0')
+ (RuntimeOption::EvalAllowHhas ? '1' : '0');
md5str = string_md5(t.c_str(), t.size(), false, md5len);
string s = string(md5str, md5len);
free(md5str);
+2
Ver Arquivo
@@ -1020,6 +1020,7 @@ OpcodeParserMap opcode_parsers;
#define NUM_POP_THREE(a,b,c) 3
#define NUM_POP_LMANY() vecImmStackValues
#define NUM_POP_V_LMANY() (1 + vecImmStackValues)
#define NUM_POP_R_LMANY() (1 + vecImmStackValues)
#define NUM_POP_C_LMANY() (1 + vecImmStackValues)
#define NUM_POP_FMANY immIVA /* number of arguments */
#define NUM_POP_CMANY immIVA /* number of arguments */
@@ -1094,6 +1095,7 @@ OPCODES
#undef NUM_POP_POS_N
#undef NUM_POP_LMANY
#undef NUM_POP_V_LMANY
#undef NUM_POP_R_LMANY
#undef NUM_POP_C_LMANY
#undef NUM_POP_FMANY
#undef NUM_POP_CMANY
+88
Ver Arquivo
@@ -5246,6 +5246,32 @@ inline void OPTBLD_INLINE VMExecutionContext::iopSetM(PC& pc) {
setHelperPost<1>(SETHELPERPOST_ARGS);
}
inline void OPTBLD_INLINE VMExecutionContext::iopSetWithRefLM(PC& pc) {
NEXT();
DECLARE_SETHELPER_ARGS
bool skip = setHelperPre<false, true, false, false, 0,
ConsumeAll>(MEMBERHELPERPRE_ARGS);
DECODE_HA(local);
if (!skip) {
TypedValue* from = frame_local(m_fp, local);
tvAsVariant(base) = withRefBind(tvAsVariant(from));
}
setHelperPost<0>(SETHELPERPOST_ARGS);
}
inline void OPTBLD_INLINE VMExecutionContext::iopSetWithRefRM(PC& pc) {
NEXT();
DECLARE_SETHELPER_ARGS
bool skip = setHelperPre<false, true, false, false, 1,
ConsumeAll>(MEMBERHELPERPRE_ARGS);
if (!skip) {
TypedValue* from = m_stack.top();
tvAsVariant(base) = withRefBind(tvAsVariant(from));
}
m_stack.popTV();
setHelperPost<1>(SETHELPERPOST_ARGS);
}
inline void OPTBLD_INLINE VMExecutionContext::iopSetOpL(PC& pc) {
NEXT();
DECODE_HA(local);
@@ -6460,6 +6486,37 @@ inline void OPTBLD_INLINE VMExecutionContext::iopIterInitK(PC& pc) {
}
}
inline void OPTBLD_INLINE VMExecutionContext::iopWIterInit(PC& pc) {
PC origPc = pc;
NEXT();
DECODE_IA(itId);
DECODE(Offset, offset);
DECODE_HA(val);
Cell* c1 = m_stack.topC();
Iter* it = frame_iter(m_fp, itId);
TypedValue* tv1 = frame_local(m_fp, val);
if (initIterator(pc, origPc, it, offset, c1)) {
tvAsVariant(tv1) = withRefBind(it->arr().secondRef());
}
}
inline void OPTBLD_INLINE VMExecutionContext::iopWIterInitK(PC& pc) {
PC origPc = pc;
NEXT();
DECODE_IA(itId);
DECODE(Offset, offset);
DECODE_HA(val);
DECODE_HA(key);
Cell* c1 = m_stack.topC();
Iter* it = frame_iter(m_fp, itId);
TypedValue* tv1 = frame_local(m_fp, val);
TypedValue* tv2 = frame_local(m_fp, key);
if (initIterator(pc, origPc, it, offset, c1)) {
tvAsVariant(tv1) = withRefBind(it->arr().secondRef());
tvAsVariant(tv2) = it->arr().first();
}
}
inline bool VMExecutionContext::initIteratorM(PC& pc, PC& origPc, Iter* it,
Offset offset, Var* v1) {
bool hasElems = it->minit(v1);
@@ -6534,6 +6591,37 @@ inline void OPTBLD_INLINE VMExecutionContext::iopIterNextK(PC& pc) {
}
}
inline void OPTBLD_INLINE VMExecutionContext::iopWIterNext(PC& pc) {
PC origPc = pc;
NEXT();
DECODE_IA(itId);
DECODE(Offset, offset);
DECODE_HA(val);
Iter* it = frame_iter(m_fp, itId);
TypedValue* tv1 = frame_local(m_fp, val);
if (it->next()) {
ITER_SKIP(offset);
tvAsVariant(tv1) = withRefBind(it->arr().secondRef());
}
}
inline void OPTBLD_INLINE VMExecutionContext::iopWIterNextK(PC& pc) {
PC origPc = pc;
NEXT();
DECODE_IA(itId);
DECODE(Offset, offset);
DECODE_HA(val);
DECODE_HA(key);
Iter* it = frame_iter(m_fp, itId);
TypedValue* tv1 = frame_local(m_fp, val);
TypedValue* tv2 = frame_local(m_fp, key);
if (it->next()) {
ITER_SKIP(offset);
tvAsVariant(tv1) = withRefBind(it->arr().secondRef());
tvAsVariant(tv2) = it->arr().first();
}
}
inline void OPTBLD_INLINE VMExecutionContext::iopMIterNext(PC& pc) {
PC origPc = pc;
NEXT();
+3 -1
Ver Arquivo
@@ -14,8 +14,8 @@
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/ext/ext_variable.h"
#include "hphp/runtime/vm/hhbc.h"
#include "hphp/runtime/ext/ext_variable.h"
#include "hphp/runtime/vm/unit.h"
#include "hphp/runtime/base/stats.h"
#include <sstream>
@@ -364,6 +364,7 @@ int instrNumPops(const Opcode* opcode) {
#define LMANY(...) -1
#define C_LMANY(...) -2
#define V_LMANY(...) -2
#define R_LMANY(...) -2
#define FMANY -3
#define CMANY -3
#define O(name, imm, pop, push, flags) pop,
@@ -376,6 +377,7 @@ int instrNumPops(const Opcode* opcode) {
#undef LMANY
#undef C_LMANY
#undef V_LMANY
#undef R_LMANY
#undef FMANY
#undef CMANY
#undef O
+15 -5
Ver Arquivo
@@ -204,18 +204,22 @@ enum MInstrAttr {
// fN) final new element operation name
#define MINSTRS \
MII(CGet, MIA_warn|MIA_final_get, W, W, 0, NotSuppNewElem) \
MII(VGet, MIA_define|MIA_reffy|MIA_new|MIA_final_get, \
MII(VGet, MIA_define|MIA_reffy|MIA_new|MIA_final_get, \
D, D, 0, VGetNewElem) \
MII(Isset, MIA_final_get, , , 0, NotSuppNewElem) \
MII(Empty, MIA_final_get, , , 0, NotSuppNewElem) \
MII(Set, MIA_define|MIA_new, D, D, 1, SetNewElem) \
MII(SetOp, MIA_more_warn|MIA_define|MIA_new|MIA_final_get, \
MII(Set, MIA_define|MIA_new, D, D, 1, SetNewElem) \
MII(SetOp, MIA_more_warn|MIA_define|MIA_new|MIA_final_get, \
WD, WD, 1, SetOpNewElem) \
MII(IncDec, MIA_more_warn|MIA_define|MIA_new|MIA_final_get, \
MII(IncDec, MIA_more_warn|MIA_define|MIA_new|MIA_final_get, \
WD, WD, 0, IncDecNewElem) \
MII(Bind, MIA_define|MIA_reffy|MIA_new|MIA_final_get, \
MII(Bind, MIA_define|MIA_reffy|MIA_new|MIA_final_get, \
D, D, 1, BindNewElem) \
MII(Unset, MIA_unset, , U, 0, NotSuppNewElem) \
MII(SetWithRefL,MIA_define|MIA_reffy|MIA_new|MIA_final_get, \
D, D, 1, SetWithRefNewElem) \
MII(SetWithRefR,MIA_define|MIA_reffy|MIA_new|MIA_final_get, \
D, D, 1, SetWithRefNewElem)
enum MInstr {
#define MII(instr, attrs, bS, iS, vC, fN) \
@@ -469,6 +473,8 @@ enum SetOpOp {
O(SetG, NA, TWO(CV,CV), ONE(CV), NF) \
O(SetS, NA, THREE(CV,AV,CV), ONE(CV), NF) \
O(SetM, ONE(MA), C_LMANY(), ONE(CV), NF) \
O(SetWithRefLM, TWO(MA, HA), LMANY(), NOV, NF) \
O(SetWithRefRM, ONE(MA), R_LMANY(), NOV, NF) \
O(SetOpL, TWO(HA, OA), ONE(CV), ONE(CV), NF) \
O(SetOpN, ONE(OA), TWO(CV,CV), ONE(CV), NF) \
O(SetOpG, ONE(OA), TWO(CV,CV), ONE(CV), NF) \
@@ -521,12 +527,16 @@ enum SetOpOp {
O(CufSafeReturn, NA, THREE(RV,CV,CV), ONE(RV), NF) \
O(IterInit, THREE(IA,BA,HA), ONE(CV), NOV, CF) \
O(MIterInit, THREE(IA,BA,HA), ONE(VV), NOV, CF) \
O(WIterInit, THREE(IA,BA,HA), ONE(CV), NOV, CF) \
O(IterInitK, FOUR(IA,BA,HA,HA),ONE(CV), NOV, CF) \
O(MIterInitK, FOUR(IA,BA,HA,HA),ONE(VV), NOV, CF) \
O(WIterInitK, FOUR(IA,BA,HA,HA),ONE(CV), NOV, CF) \
O(IterNext, THREE(IA,BA,HA), NOV, NOV, CF) \
O(MIterNext, THREE(IA,BA,HA), NOV, NOV, CF) \
O(WIterNext, THREE(IA,BA,HA), NOV, NOV, CF) \
O(IterNextK, FOUR(IA,BA,HA,HA),NOV, NOV, CF) \
O(MIterNextK, FOUR(IA,BA,HA,HA),NOV, NOV, CF) \
O(WIterNextK, FOUR(IA,BA,HA,HA),NOV, NOV, CF) \
O(IterFree, ONE(IA), NOV, NOV, NF) \
O(MIterFree, ONE(IA), NOV, NOV, NF) \
O(Incl, NA, ONE(CV), ONE(CV), CF) \
+64 -35
Ver Arquivo
@@ -435,6 +435,8 @@ CALL_OPCODE(ArrayGet)
CALL_OPCODE(CGetElem)
CALL_STK_OPCODE(VGetElem)
CALL_STK_OPCODE(BindElem)
CALL_STK_OPCODE(SetWithRefElem)
CALL_STK_OPCODE(SetWithRefNewElem)
CALL_OPCODE(ArraySet)
CALL_OPCODE(ArraySetRef)
CALL_STK_OPCODE(SetElem)
@@ -5049,46 +5051,26 @@ void CodeGenerator::cgContStartedCheck(IRInstruction* inst) {
emitFwdJcc(CC_L, inst->taken());
}
void CodeGenerator::cgIterNextK(IRInstruction* inst) {
cgIterNextCommon(inst, true);
}
void CodeGenerator::cgIterNext(IRInstruction* inst) {
cgIterNextCommon(inst, false);
}
void CodeGenerator::cgIterNextCommon(IRInstruction* inst, bool isNextK) {
PhysReg fpReg = m_regs[inst->src(0)].getReg();
ArgGroup args(m_regs);
args.addr(fpReg, getIterOffset(inst->src(1)))
.addr(fpReg, getLocalOffset(inst->src(2)));
if (isNextK) {
args.addr(fpReg, getLocalOffset(inst->src(3)));
}
TCA helperAddr = isNextK ? (TCA)iter_next_key : (TCA)iter_next;
cgCallHelper(m_as, helperAddr, inst->dst(), kSyncPoint, args);
}
void CodeGenerator::cgIterInit(IRInstruction* inst) {
cgIterInitCommon(inst, false);
}
void iterFreeHelper(Iter* iter) {
iter->free();
}
void CodeGenerator::cgIterFree(IRInstruction* inst) {
PhysReg fpReg = m_regs[inst->src(0)].getReg();
int64_t offset = getIterOffset(inst->src(1));
cgCallHelper(m_as, (TCA)iterFreeHelper, InvalidReg, kSyncPoint,
ArgGroup(m_regs).addr(fpReg, offset));
cgIterInitCommon(inst);
}
void CodeGenerator::cgIterInitK(IRInstruction* inst) {
cgIterInitCommon(inst, true);
cgIterInitCommon(inst);
}
void CodeGenerator::cgIterInitCommon(IRInstruction* inst, bool isInitK) {
void CodeGenerator::cgWIterInit(IRInstruction* inst) {
cgIterInitCommon(inst);
}
void CodeGenerator::cgWIterInitK(IRInstruction* inst) {
cgIterInitCommon(inst);
}
void CodeGenerator::cgIterInitCommon(IRInstruction* inst) {
bool isInitK = inst->op() == IterInitK || inst->op() == WIterInitK;
bool isWInit = inst->op() == WIterInit || inst->op() == WIterInitK;
PhysReg fpReg = m_regs[inst->src(1)].getReg();
int64_t iterOffset = getIterOffset(inst->src(2));
int64_t valLocalOffset = getLocalOffset(inst->src(3));
@@ -5099,8 +5081,11 @@ void CodeGenerator::cgIterInitCommon(IRInstruction* inst, bool isInitK) {
args.addr(fpReg, valLocalOffset);
if (isInitK) {
args.addr(fpReg, getLocalOffset(inst->src(4)));
} else if (isWInit) {
args.imm(0);
}
TCA helperAddr = isInitK ? (TCA)new_iter_array_key : (TCA)new_iter_array;
TCA helperAddr = isWInit ? (TCA)new_iter_array_key<true> :
isInitK ? (TCA)new_iter_array_key<false> : (TCA)new_iter_array;
cgCallHelper(m_as, helperAddr, inst->dst(), kSyncPoint, args);
} else {
assert(src->type() == Type::Obj);
@@ -5119,6 +5104,50 @@ void CodeGenerator::cgIterInitCommon(IRInstruction* inst, bool isInitK) {
}
}
void CodeGenerator::cgIterNext(IRInstruction* inst) {
cgIterNextCommon(inst);
}
void CodeGenerator::cgIterNextK(IRInstruction* inst) {
cgIterNextCommon(inst);
}
void CodeGenerator::cgWIterNext(IRInstruction* inst) {
cgIterNextCommon(inst);
}
void CodeGenerator::cgWIterNextK(IRInstruction* inst) {
cgIterNextCommon(inst);
}
void CodeGenerator::cgIterNextCommon(IRInstruction* inst) {
bool isNextK = inst->op() == IterNextK || inst->op() == WIterNextK;
bool isWNext = inst->op() == WIterNext || inst->op() == WIterNextK;
PhysReg fpReg = m_regs[inst->src(0)].getReg();
ArgGroup args(m_regs);
args.addr(fpReg, getIterOffset(inst->src(1)))
.addr(fpReg, getLocalOffset(inst->src(2)));
if (isNextK) {
args.addr(fpReg, getLocalOffset(inst->src(3)));
} else if (isWNext) {
args.imm(0);
}
TCA helperAddr = isWNext ? (TCA)iter_next_key<true> :
isNextK ? (TCA)iter_next_key<false> : (TCA)iter_next;
cgCallHelper(m_as, helperAddr, inst->dst(), kSyncPoint, args);
}
void iterFreeHelper(Iter* iter) {
iter->free();
}
void CodeGenerator::cgIterFree(IRInstruction* inst) {
PhysReg fpReg = m_regs[inst->src(0)].getReg();
int64_t offset = getIterOffset(inst->src(1));
cgCallHelper(m_as, (TCA)iterFreeHelper, InvalidReg, kSyncPoint,
ArgGroup(m_regs).addr(fpReg, offset));
}
void CodeGenerator::cgIncStat(IRInstruction *inst) {
Stats::emitInc(m_as,
Stats::StatCounter(inst->src(0)->getValInt()),
+2 -2
Ver Arquivo
@@ -310,8 +310,8 @@ private:
int64_t offset,
Block* exit);
void cgIterNextCommon(IRInstruction* inst, bool isNextK);
void cgIterInitCommon(IRInstruction* inst, bool isInitK);
void cgIterNextCommon(IRInstruction* inst);
void cgIterInitCommon(IRInstruction* inst);
void cgLdFuncCachedCommon(IRInstruction* inst);
TargetCache::CacheHandle cgLdClsCachedCommon(IRInstruction* inst);
void emitFwdJcc(ConditionCode cc, Block* target);
@@ -892,6 +892,70 @@ void HhbcTranslator::emitIterNextK(uint32_t iterId,
emitJmpCondHelper(offset, false, res);
}
void HhbcTranslator::emitWIterInit(uint32_t iterId,
int offset,
uint32_t valLocalId) {
emitIterInitCommon(
offset, [=] (SSATmp* src) {
return gen(
WIterInit,
Type::Bool,
src,
m_tb->getFp(),
cns(iterId),
cns(valLocalId)
);
}
);
}
void HhbcTranslator::emitWIterInitK(uint32_t iterId,
int offset,
uint32_t valLocalId,
uint32_t keyLocalId) {
emitIterInitCommon(
offset, [=] (SSATmp* src) {
return gen(
WIterInitK,
Type::Bool,
src,
m_tb->getFp(),
cns(iterId),
cns(valLocalId),
cns(keyLocalId)
);
}
);
}
void HhbcTranslator::emitWIterNext(uint32_t iterId,
int offset,
uint32_t valLocalId) {
SSATmp* res = gen(
WIterNext,
Type::Bool,
m_tb->getFp(),
cns(iterId),
cns(valLocalId)
);
emitJmpCondHelper(offset, false, res);
}
void HhbcTranslator::emitWIterNextK(uint32_t iterId,
int offset,
uint32_t valLocalId,
uint32_t keyLocalId) {
SSATmp* res = gen(
WIterNextK,
Type::Bool,
m_tb->getFp(),
cns(iterId),
cns(valLocalId),
cns(keyLocalId)
);
emitJmpCondHelper(offset, false, res);
}
void HhbcTranslator::emitIterFree(uint32_t iterId) {
gen(IterFree, m_tb->getFp(), cns(iterId));
}
@@ -339,6 +339,16 @@ struct HhbcTranslator {
int targetOffset,
uint32_t valLocalId,
uint32_t keyLocalId);
void emitWIterInit(uint32_t iterId, int targetOffset, uint32_t valLocalId);
void emitWIterInitK(uint32_t iterId,
int targetOffset,
uint32_t valLocalId,
uint32_t keyLocalId);
void emitWIterNext(uint32_t iterId, int targetOffset, uint32_t valLocalId);
void emitWIterNextK(uint32_t iterId,
int targetOffset,
uint32_t valLocalId,
uint32_t keyLocalId);
void emitIterFree(uint32_t iterId);
void emitVerifyParamType(uint32_t paramId);
@@ -412,6 +422,7 @@ private:
void emitNotSuppNewElem();
void emitVGetNewElem();
void emitSetNewElem();
void emitSetWithRefNewElem();
void emitSetOpNewElem();
void emitIncDecNewElem();
void emitBindNewElem();
@@ -427,6 +438,7 @@ private:
SSATmp* getBase();
SSATmp* getKey();
SSATmp* getValue();
SSATmp* getValAddr();
SSATmp* checkInitProp(SSATmp* baseAsObj,
SSATmp* propAddr,
Transl::PropInfo propOffset,
+22
Ver Arquivo
@@ -458,6 +458,19 @@ O(IterNext, 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(WIterInit, D(Bool), S(Arr,Obj) \
S(FramePtr) \
C(Int) \
C(Int), E|N|Mem|Refs|CRc) \
O(WIterInitK, D(Bool), S(Arr,Obj) \
S(FramePtr) \
C(Int) \
C(Int) \
C(Int), E|N|Mem|Refs|CRc) \
O(WIterNext, D(Bool), S(FramePtr) \
C(Int) C(Int), E|N|Mem|Refs) \
O(WIterNextK, D(Bool), S(FramePtr) \
C(Int) C(Int) C(Int), E|N|Mem|Refs) \
O(IterFree, ND, S(FramePtr) C(Int), E|N|Mem|Refs) \
O(DefMIStateBase, D(PtrToCell), NA, NF) \
O(BaseG, D(PtrToGen), C(TCA) \
@@ -558,6 +571,11 @@ O_STK(SetElem, DVector, C(TCA) \
S(PtrToGen) \
S(Cell) \
S(Cell), VElem|E|N|Mem|Refs|Er) \
O_STK(SetWithRefElem, ND, C(TCA) \
S(PtrToGen) \
S(Cell) \
S(PtrToGen) \
S(PtrToCell),VElem|E|N|Mem|Refs|Er) \
O_STK(UnsetElem, ND, C(TCA) \
S(PtrToGen) \
S(Cell), VElem|E|N|Mem|Refs|Er) \
@@ -572,6 +590,10 @@ O_STK(IncDecElem, D(Cell), C(TCA) \
S(PtrToCell),VElem|E|N|Mem|Refs|Er) \
O_STK(SetNewElem, DVector, S(PtrToGen) \
S(Cell), VElem|E|N|Mem|Refs|Er) \
O_STK(SetWithRefNewElem, ND, C(TCA) \
S(PtrToGen) \
S(PtrToGen) \
S(PtrToCell),VElem|E|N|Mem|Refs|Er) \
O_STK(BindNewElem, ND, S(PtrToGen) \
S(BoxedCell) \
S(PtrToCell),VElem|E|N|Mem|Refs|Er) \
+40 -1
Ver Arquivo
@@ -799,7 +799,6 @@ TranslatorX64::irTranslateLateBoundCls(const Tracelet&,
HHIR_EMIT(LateBoundCls);
}
void TranslatorX64::irTranslateFPassL(const Tracelet& t,
const NormalizedInstruction& ni) {
if (ni.preppedByRef) {
@@ -1403,6 +1402,46 @@ TranslatorX64::irTranslateIterNextK(const Tracelet& t,
i.imm[3].u_IVA);
}
void
TranslatorX64::irTranslateWIterInit(const Tracelet& t,
const NormalizedInstruction& i) {
HHIR_EMIT(WIterInit,
i.imm[0].u_IVA,
i.offset() + i.imm[1].u_BA,
i.imm[2].u_IVA);
}
void
TranslatorX64::irTranslateWIterInitK(const Tracelet& t,
const NormalizedInstruction& i) {
HHIR_EMIT(WIterInitK,
i.imm[0].u_IVA,
i.offset() + i.imm[1].u_BA,
i.imm[2].u_IVA,
i.imm[3].u_IVA);
}
void
TranslatorX64::irTranslateWIterNext(const Tracelet& t,
const NormalizedInstruction& i) {
HHIR_EMIT(WIterNext,
i.imm[0].u_IVA,
i.offset() + i.imm[1].u_BA,
i.imm[2].u_IVA);
}
void
TranslatorX64::irTranslateWIterNextK(const Tracelet& t,
const NormalizedInstruction& i) {
HHIR_EMIT(WIterNextK,
i.imm[0].u_IVA,
i.offset() + i.imm[1].u_BA,
i.imm[2].u_IVA,
i.imm[3].u_IVA);
}
void
TranslatorX64::irTranslateIterFree(const Tracelet& t,
const NormalizedInstruction& i) {
+5 -4
Ver Arquivo
@@ -837,6 +837,7 @@ void LinearScan::computePreColoringHint() {
}
break;
case IterInit:
case WIterInit:
{
m_preColoringHint.add(inst->src(0), 0, 1);
}
@@ -1397,14 +1398,14 @@ void LinearScan::rematerializeAux() {
}
// Other instructions that may have side effects on locals must
// kill the local variable values.
else if (opc == IterInit) {
else if (opc == IterInit || opc == WIterInit) {
killLocal(inst, 3);
} else if (opc == IterInitK) {
} else if (opc == IterInitK || opc == WIterInitK) {
killLocal(inst, 3);
killLocal(inst, 4);
} else if (opc == IterNext) {
} else if (opc == IterNext || opc == WIterNext) {
killLocal(inst, 2);
} else if (opc == IterNextK) {
} else if (opc == IterNextK || opc == WIterNextK) {
killLocal(inst, 2);
killLocal(inst, 3);
}
@@ -221,6 +221,8 @@ static CallMap s_callMap({
{{SSA, 1}, {VecKeyIS, 2}, {SSA, 3}}},
{BindElem, {FSSA, 0}, DNone, SSync,
{{SSA, 1}, {TV, 2}, {SSA, 3}, {SSA, 4}}},
{SetWithRefElem, {FSSA, 0}, DNone, SSync,
{{SSA, 1}, {TV, 2}, {SSA, 3}, {SSA, 4}}},
{ArraySet, {FSSA, 0}, DSSA, SSync,
{{SSA, 1}, {SSA, 2}, {TV, 3}}},
{ArraySetRef, {FSSA, 0}, DSSA, SSync,
@@ -234,6 +236,8 @@ static CallMap s_callMap({
{IncDecElem, {FSSA, 0}, DTV, SSync,
{{SSA, 1}, {TV, 2}, {SSA, 3}}},
{SetNewElem, (TCA)setNewElem, DTV, SSync, {{SSA, 0}, {TV, 1}}},
{SetWithRefNewElem, {FSSA, 0}, DNone, SSync,
{{SSA, 1}, {SSA, 2}, {SSA, 3}}},
{BindNewElem, (TCA)bindNewElemIR, DNone, SSync,
{{SSA, 0}, {SSA, 1}, {SSA, 2}}},
{ArrayIsset, {FSSA, 0}, DSSA, SSync, {{SSA, 1}, {SSA, 2}}},
@@ -288,23 +288,27 @@ void TraceBuilder::updateTrackedState(IRInstruction* inst) {
break;
case IterInitK:
case WIterInitK:
// kill the locals to which this instruction stores iter's key and value
killLocalValue(inst->src(3)->getValInt());
killLocalValue(inst->src(4)->getValInt());
break;
case IterInit:
case WIterInit:
// kill the local to which this instruction stores iter's value
killLocalValue(inst->src(3)->getValInt());
break;
case IterNextK:
case WIterNextK:
// kill the locals to which this instruction stores iter's key and value
killLocalValue(inst->src(2)->getValInt());
killLocalValue(inst->src(3)->getValInt());
break;
case IterNext:
case WIterNext:
// kill the local to which this instruction stores iter's value
killLocalValue(inst->src(2)->getValInt());
break;
@@ -84,6 +84,12 @@ Opcode canonicalOp(Opcode op) {
op == UnsetElem || op == UnsetElemStk) {
return UnsetElem;
}
if (op == SetWithRefElem ||
op == SetWithRefElemStk ||
op == SetWithRefNewElem ||
op == SetWithRefNewElemStk) {
return SetWithRefElem;
}
return opcodeHasFlags(op, VectorProp) ? SetProp
: opcodeHasFlags(op, VectorElem) || op == ArraySet ? SetElem
: bad_value<Opcode>();
@@ -132,7 +138,7 @@ void VectorEffects::init(Opcode op, const Type origBase,
valType = origVal;
baseTypeChanged = baseValChanged = valTypeChanged = false;
// Canonicalize the op to SetProp or SetElem
// Canonicalize the op to SetProp/SetElem/UnsetElem/SetWithRefElem
op = canonicalOp(op);
// We're not expecting types other than specific known data types
@@ -141,9 +147,13 @@ void VectorEffects::init(Opcode op, const Type origBase,
// since this shouldn't actually happen.)
assert(key.equals(Type::None) || key.isKnownDataType() ||
key.equals(Type::Cell));
assert(origVal.equals(Type::None) || origVal.isKnownDataType());
if (op == SetWithRefElem) {
assert(origVal.isPtr());
} else {
assert(origVal.equals(Type::None) || origVal.isKnownDataType());
}
if ((op == SetElem || op == SetProp) &&
if ((op == SetElem || op == SetProp || op == SetWithRefElem) &&
baseType.maybe(Type::Null | Type::Bool | Type::Str)) {
// stdClass or array promotion might happen
auto newBase = op == SetElem ? Type::Arr : Type::Obj;
@@ -163,17 +173,19 @@ void VectorEffects::init(Opcode op, const Type origBase,
}
baseValChanged = !baseBoxed;
}
if ((op == SetElem || op == UnsetElem) && baseType.maybe(Type::Arr)) {
// possible COW when modifying an array, unless the base was boxed. If the
// base is a box then the value of the box itself won't change, just what
// it points to.
baseValChanged = !baseBoxed;
}
if ((op == SetElem || op == UnsetElem) && baseType.maybe(Type::StaticArr)) {
// the base might get promoted to a CountedArr. We can ignore the change if
// the base is boxed, (for the same reasons as above).
baseType = baseType | Type::CountedArr;
baseValChanged = !baseBoxed;
if (op == SetElem || op == UnsetElem || op == SetWithRefElem) {
if (baseType.maybe(Type::Arr)) {
// possible COW when modifying an array, unless the base was boxed. If the
// base is a box then the value of the box itself won't change, just what
// it points to.
baseValChanged = !baseBoxed;
}
if (baseType.maybe(Type::StaticArr)) {
// the base might get promoted to a CountedArr. We can ignore the change
// if the base is boxed, (for the same reasons as above).
baseType = baseType | Type::CountedArr;
baseValChanged = !baseBoxed;
}
}
// Setting an element with a base of one of these types will either succeed
@@ -233,6 +245,7 @@ int vectorBaseIdx(const IRInstruction* inst) {
// vectorKeyIdx returns the src index for inst's key operand.
int vectorKeyIdx(Opcode opc) {
return opc == SetNewElem || opc == SetNewElemStk ? -1
: opc == SetWithRefNewElem || opc == SetWithRefNewElemStk ? -1
: opc == BindNewElem || opc == BindNewElem ? -1
: opc == ArraySet ? 2
: opc == SetOpProp || opc == SetOpPropStk ? 2
@@ -259,6 +272,7 @@ int vectorValIdx(Opcode opc) {
case ArraySet: return 3;
case SetNewElem: case SetNewElemStk: return 1;
case SetWithRefNewElem: case SetWithRefNewElemStk: return 2;
case BindNewElem: case BindNewElemStk: return 1;
case SetOpProp: case SetOpPropStk: return 3;
@@ -454,7 +468,9 @@ void HhbcTranslator::VectorTranslator::numberStackInputs() {
// higher offsets in the stack. m_mii.valCount() tells us how many
// rvals the instruction takes on the stack; they're pushed after
// any vector elements and we want to ignore them here.
int stackIdx = m_mii.valCount() + m_ni.immVec.numStackValues() - 1;
bool stackRhs = m_mii.valCount() &&
m_ni.inputs[0]->location.space == Location::Stack;
int stackIdx = (int)stackRhs + m_ni.immVec.numStackValues() - 1;
for (unsigned i = m_mii.valCount(); i < m_ni.inputs.size(); ++i) {
const Location& l = m_ni.inputs[i]->location;
switch (l.space) {
@@ -467,9 +483,9 @@ void HhbcTranslator::VectorTranslator::numberStackInputs() {
break;
}
}
assert(stackIdx == (m_mii.valCount() - 1));
assert(stackIdx == (stackRhs ? 0 : -1));
if (m_mii.valCount()) {
if (stackRhs) {
// If this instruction does have an RHS, it will be input 0 at
// stack offset 0.
assert(m_mii.valCount() == 1);
@@ -499,6 +515,20 @@ SSATmp* HhbcTranslator::VectorTranslator::getValue() {
return getInput(kValIdx);
}
SSATmp* HhbcTranslator::VectorTranslator::getValAddr() {
assert(m_mii.valCount() == 1);
const DynLocation& dl = *m_ni.inputs[0];
const Location& l = dl.location;
if (l.space == Location::Local) {
assert(!mapContains(m_stackInputs, 0));
return m_ht.ldLocAddr(l.offset);
} else {
assert(l.space == Location::Stack);
assert(mapContains(m_stackInputs, 0));
return m_ht.ldStackAddr(m_stackInputs[0]);
}
}
SSATmp* HhbcTranslator::VectorTranslator::getInput(unsigned i) {
const DynLocation& dl = *m_ni.inputs[i];
const Location& l = dl.location;
@@ -1779,6 +1809,65 @@ void HhbcTranslator::VectorTranslator::emitArraySet(SSATmp* key,
}
#undef HELPER_TABLE
namespace VectorHelpers {
void setWithRefElemC(TypedValue* base, TypedValue keyVal, TypedValue* val,
MInstrState* mis) {
base = HPHP::ElemD<false, false>(mis->tvScratch, mis->tvRef, base, &keyVal);
if (!(base == &mis->tvScratch && base->m_type == KindOfUninit)) {
tvDup(val, base);
}
}
void setWithRefNewElem(TypedValue* base, TypedValue* val,
MInstrState* mis) {
base = NewElem(mis->tvScratch, mis->tvRef, base);
if (!(base == &mis->tvScratch && base->m_type == KindOfUninit)) {
tvDup(val, base);
}
}
}
void HhbcTranslator::VectorTranslator::emitSetWithRefLElem() {
SSATmp* key = getKey();
SSATmp* locAddr = getValAddr();
m_ht.exceptionBarrier();
if (m_base->type().strip().subtypeOf(Type::Arr) &&
!locAddr->type().deref().maybeBoxed()) {
emitSetElem();
m_predictedResult = nullptr;
} else {
genStk(SetWithRefElem, cns((TCA)VectorHelpers::setWithRefElemC),
m_base, key, locAddr, genMisPtr());
}
m_result = nullptr;
}
void HhbcTranslator::VectorTranslator::emitSetWithRefLProp() {
SPUNT(__func__);
}
void HhbcTranslator::VectorTranslator::emitSetWithRefRElem() {
emitSetWithRefLElem();
}
void HhbcTranslator::VectorTranslator::emitSetWithRefRProp() {
emitSetWithRefLProp();
}
void HhbcTranslator::VectorTranslator::emitSetWithRefNewElem() {
SSATmp* locAddr = getValAddr();
m_ht.exceptionBarrier();
if (m_base->type().strip().subtypeOf(Type::Arr) &&
!locAddr->type().deref().maybeBoxed()) {
emitSetNewElem();
m_predictedResult = nullptr;
} else {
genStk(SetWithRefNewElem, cns((TCA)VectorHelpers::setWithRefNewElem),
m_base, locAddr, genMisPtr());
}
m_result = nullptr;
}
template <KeyType keyType>
static inline TypedValue setElemImpl(TypedValue* base, TypedValue keyVal,
Cell val) {
@@ -2029,7 +2118,9 @@ void HhbcTranslator::VectorTranslator::emitMPost() {
if (m_result) {
m_ht.push(usePredictedResult() ? m_predictedResult : m_result);
} else {
assert(m_ni.mInstrOp() == OpUnsetM);
assert(m_ni.mInstrOp() == OpUnsetM ||
m_ni.mInstrOp() == OpSetWithRefLM ||
m_ni.mInstrOp() == OpSetWithRefRM);
}
// Clean up tvRef(2)
+77 -10
Ver Arquivo
@@ -10398,6 +10398,18 @@ TranslatorX64::analyzeIterInitK(Tracelet& t, NormalizedInstruction& ni) {
ni.m_txFlags = supportedPlan(inType == KindOfArray || inType == KindOfObject);
}
void
TranslatorX64::analyzeWIterInit(Tracelet& t, NormalizedInstruction& ni) {
DataType inType = ni.inputs[0]->valueType();
ni.m_txFlags = supportedPlan(inType == KindOfArray || inType == KindOfObject);
}
void
TranslatorX64::analyzeWIterInitK(Tracelet& t, NormalizedInstruction& ni) {
DataType inType = ni.inputs[0]->valueType();
ni.m_txFlags = supportedPlan(inType == KindOfArray || inType == KindOfObject);
}
void TranslatorX64::translateBasicIterInit(const Tracelet& t,
const NormalizedInstruction& ni) {
const int kValIdx = 0;
@@ -10416,11 +10428,15 @@ void TranslatorX64::translateBasicIterInit(const Tracelet& t,
TypedValue *val = nullptr;
TypedValue *key = nullptr;
new_iter_array(dest, arr, val);
new_iter_array_key(dest, arr, val, key);
new_iter_array_key<false>(dest, arr, val, key);
new_iter_array_key<true>(dest, arr, val, key);
}
if (ni.outLocal2) {
EMIT_RCALL(a, ni, new_iter_array_key, A(iterLoc), R(src),
A(ni.outLocal->location), A(ni.outLocal2->location));
if (ni.outLocal2 || ni.op() == OpWIterInit) {
EMIT_RCALL(a, ni,
ni.op() == OpIterInitK ?
(TCA)new_iter_array_key<false> : (TCA)new_iter_array_key<true>,
A(iterLoc), R(src), A(ni.outLocal->location),
ni.outLocal2 ? A(ni.outLocal2->location) : IMM(0));
} else {
EMIT_RCALL(a, ni, new_iter_array, A(iterLoc), R(src),
A(ni.outLocal->location));
@@ -10473,6 +10489,22 @@ void TranslatorX64::translateIterInitK(const Tracelet& t,
translateBasicIterInit(t, ni);
}
void TranslatorX64::translateWIterInit(const Tracelet& t,
const NormalizedInstruction& ni) {
assert(ni.inputs.size() == 1);
assert(ni.outLocal);
assert(!ni.outStack && !ni.outLocal2);
translateBasicIterInit(t, ni);
}
void TranslatorX64::translateWIterInitK(const Tracelet& t,
const NormalizedInstruction& ni) {
assert(ni.inputs.size() == 1);
assert(ni.outLocal && ni.outLocal2);
assert(!ni.outStack);
translateBasicIterInit(t, ni);
}
void
TranslatorX64::analyzeIterNext(Tracelet& t, NormalizedInstruction& i) {
assert(i.inputs.size() == 0);
@@ -10485,6 +10517,17 @@ TranslatorX64::analyzeIterNextK(Tracelet& t, NormalizedInstruction& i) {
i.m_txFlags = Supported;
}
void
TranslatorX64::analyzeWIterNext(Tracelet& t, NormalizedInstruction& i) {
i.m_txFlags = Supported;
}
void
TranslatorX64::analyzeWIterNextK(Tracelet& t, NormalizedInstruction& i) {
i.m_txFlags = Supported;
}
void
TranslatorX64::translateBasicIterNext(const Tracelet& t,
const NormalizedInstruction& i) {
@@ -10493,19 +10536,23 @@ TranslatorX64::translateBasicIterNext(const Tracelet& t,
TypedValue* val = nullptr;
TypedValue* key = nullptr;
int64_t ret = iter_next(it, val);
ret = iter_next_key(it, val, key);
ret = iter_next_key<false>(it, val, key);
ret = iter_next_key<true>(it, val, key);
if (ret) printf("\n");
}
m_regMap.cleanAll(); // input might be in-flight
// If the iterator reaches the end, iter_next will handle
// freeing the iterator and it will decRef the array
Location iterLoc(Location::Iter, i.imm[0].u_IVA);
if (i.outLocal2) {
EMIT_CALL(a, iter_next_key, A(iterLoc),
A(i.outLocal->location), A(i.outLocal2->location));
if (i.outLocal2 || i.op() == OpWIterNext) {
EMIT_CALL(a,
i.op() == OpIterNextK ?
(TCA)iter_next_key<false> : (TCA)iter_next_key<true>,
A(iterLoc),
A(i.outLocal->location),
i.op() == OpWIterNext ? IMM(0) : A(i.outLocal2->location));
} else {
EMIT_CALL(a, iter_next, A(iterLoc),
A(i.outLocal->location));
EMIT_CALL(a, iter_next, A(iterLoc), A(i.outLocal->location));
}
recordReentrantCall(a, i);
ScratchReg raxScratch(m_regMap, rax);
@@ -10538,6 +10585,24 @@ TranslatorX64::translateIterNextK(const Tracelet& t,
translateBasicIterNext(t, i);
}
void
TranslatorX64::translateWIterNext(const Tracelet& t,
const NormalizedInstruction& i) {
assert(i.inputs.size() == 0);
assert(!i.outStack && !i.outLocal2);
assert(i.outLocal);
translateBasicIterNext(t, i);
}
void
TranslatorX64::translateWIterNextK(const Tracelet& t,
const NormalizedInstruction& i) {
assert(i.inputs.size() == 0);
assert(!i.outStack);
assert(i.outLocal && i.outLocal2);
translateBasicIterNext(t, i);
}
// PSEUDOINSTR_DISPATCH is a switch() fragment that routes opcodes to their
// shared handlers, as per the PSEUDOINSTRS macro.
#define PSEUDOINSTR_DISPATCH(func) \
@@ -11866,6 +11931,8 @@ bool TranslatorX64::dumpTCData() {
* Always-interp instructions,
*/ \
INTERP_OP(ContHandle) \
INTERP_OP(SetWithRefLM) \
INTERP_OP(SetWithRefRM) \
INTERP_OP(ArrayIdx) \
INTERP_OP(CGetM) \
INTERP_OP(FPassM) \
@@ -490,6 +490,8 @@ private:
CASE(SetS) \
CASE(SetG) \
CASE(SetM) \
CASE(SetWithRefLM) \
CASE(SetWithRefRM) \
CASE(SetOpL) \
CASE(SetOpM) \
CASE(IncDecL) \
@@ -523,6 +525,10 @@ private:
CASE(IterInitK) \
CASE(IterNext) \
CASE(IterNextK) \
CASE(WIterInit) \
CASE(WIterInitK) \
CASE(WIterNext) \
CASE(WIterNextK) \
CASE(ReqDoc) \
CASE(DefCls) \
CASE(DefFunc) \
+77 -53
Ver Arquivo
@@ -1242,6 +1242,8 @@ static const struct {
{ OpSetG, {StackTop2, Stack1|Local, OutSameAsInput, -1 }},
{ OpSetS, {StackTop3, Stack1, OutSameAsInput, -2 }},
{ OpSetM, {MVector|Stack1, Stack1|Local, OutSameAsInput, 0 }},
{ OpSetWithRefLM,{MVector|Local , Local, OutNone, 0 }},
{ OpSetWithRefRM,{MVector|Stack1, Local, OutNone, -1 }},
{ OpSetOpL, {Stack1|Local, Stack1|Local, OutSetOp, 0 }},
{ OpSetOpN, {StackTop2, Stack1|Local, OutUnknown, -1 }},
{ OpSetOpG, {StackTop2, Stack1|Local, OutUnknown, -1 }},
@@ -1328,12 +1330,16 @@ static const struct {
{ OpIterInit, {Stack1, Local, OutUnknown, -1 }},
{ OpMIterInit, {Stack1, Local, OutUnknown, -1 }},
{ OpWIterInit, {Stack1, Local, OutUnknown, -1 }},
{ OpIterInitK, {Stack1, Local, OutUnknown, -1 }},
{ OpMIterInitK, {Stack1, Local, OutUnknown, -1 }},
{ OpWIterInitK, {Stack1, Local, OutUnknown, -1 }},
{ OpIterNext, {None, Local, OutUnknown, 0 }},
{ OpMIterNext, {None, Local, OutUnknown, 0 }},
{ OpWIterNext, {None, Local, OutUnknown, 0 }},
{ OpIterNextK, {None, Local, OutUnknown, 0 }},
{ OpMIterNextK, {None, Local, OutUnknown, 0 }},
{ OpWIterNextK, {None, Local, OutUnknown, 0 }},
{ OpIterFree, {None, None, OutNone, 0 }},
{ OpMIterFree, {None, None, OutNone, 0 }},
@@ -2137,6 +2143,7 @@ void Translator::getInputs(Tracelet& t,
// instructions that take a Local have its index at their first
// immediate.
int loc;
auto insertAt = inputs.end();
switch (ni->op()) {
case OpUnpackCont:
case OpPackCont:
@@ -2147,6 +2154,9 @@ void Translator::getInputs(Tracelet& t,
loc = 0;
break;
case OpSetWithRefLM:
insertAt = inputs.begin();
// fallthrough
case OpFPassL:
loc = ni->imm[1].u_IVA;
break;
@@ -2156,7 +2166,7 @@ void Translator::getInputs(Tracelet& t,
break;
}
SKTRACE(1, sk, "getInputs: local %d\n", loc);
inputs.emplace_back(Location(Location::Local, loc));
inputs.emplace(insertAt, Location(Location::Local, loc));
if (input & DontGuardLocal) inputs.back().dontGuard = true;
if (input & DontBreakLocal) inputs.back().dontBreak = true;
}
@@ -2360,6 +2370,7 @@ void Translator::getOutputs(/*inout*/ Tracelet& t,
ASSERT_NOT_IMPLEMENTED(op == OpSetOpL ||
op == OpSetM || op == OpSetOpM ||
op == OpBindM ||
op == OpSetWithRefLM || op == OpSetWithRefRM ||
op == OpIncDecL || op == OpIncDecG ||
op == OpUnsetG || op == OpBindG ||
op == OpSetG || op == OpSetOpG ||
@@ -2369,8 +2380,10 @@ void Translator::getOutputs(/*inout*/ Tracelet& t,
op == OpUnsetL ||
op == OpIterInit || op == OpIterInitK ||
op == OpMIterInit || op == OpMIterInitK ||
op == OpWIterInit || op == OpWIterInitK ||
op == OpIterNext || op == OpIterNextK ||
op == OpMIterNext || op == OpMIterNextK);
op == OpMIterNext || op == OpMIterNextK ||
op == OpWIterNext || op == OpWIterNextK);
if (op == OpIncDecL) {
assert(ni->inputs.size() == 1);
const RuntimeType &inRtt = ni->inputs[0]->rtt;
@@ -2407,49 +2420,62 @@ void Translator::getOutputs(/*inout*/ Tracelet& t,
KindOfInvalid);
continue;
}
if (op == OpSetM || op == OpSetOpM || op == OpVGetM || op == OpBindM) {
// TODO(#1069330): This code assumes that the location is
// LH. We need to figure out how to handle cases where the
// location is LN or LG or LR.
// XXX: analogous garbage needed for OpSetOpM.
if (ni->immVec.locationCode() == LL) {
const int kVecStart = (op == OpSetM ||
op == OpSetOpM ||
op == OpBindM) ?
1 : 0; // 0 is rhs for SetM/SetOpM
DynLocation* inLoc = ni->inputs[kVecStart];
assert(inLoc->location.isLocal());
Location locLoc = inLoc->location;
if (inLoc->rtt.isString() ||
inLoc->rtt.valueType() == KindOfBoolean) {
// Strings and bools produce value-dependent results; "" and
// false upgrade to an array successfully, while other values
// fail and leave the lhs unmodified.
DynLocation* baseLoc = t.newDynLocation(locLoc, KindOfInvalid);
assert(baseLoc->isLocal());
ni->outLocal = baseLoc;
} else if (inLoc->rtt.valueType() == KindOfUninit ||
inLoc->rtt.valueType() == KindOfNull) {
RuntimeType newLhsRtt = inLoc->rtt.setValueType(
mcodeMaybePropName(ni->immVecM[0]) ?
KindOfObject : KindOfArray);
SKTRACE(2, ni->source, "(%s, %d) <- type %d\n",
locLoc.spaceName(), locLoc.offset, newLhsRtt.valueType());
DynLocation* baseLoc = t.newDynLocation(locLoc, newLhsRtt);
assert(baseLoc->location.isLocal());
ni->outLocal = baseLoc;
if (op == OpSetM || op == OpSetOpM ||
op == OpVGetM || op == OpBindM ||
op == OpSetWithRefLM || op == OpSetWithRefRM) {
switch (ni->immVec.locationCode()) {
case LL: {
const int kVecStart = (op == OpSetM ||
op == OpSetOpM ||
op == OpBindM ||
op == OpSetWithRefLM ||
op == OpSetWithRefRM) ?
1 : 0; // 0 is rhs for SetM/SetOpM
DynLocation* inLoc = ni->inputs[kVecStart];
assert(inLoc->location.isLocal());
Location locLoc = inLoc->location;
if (inLoc->rtt.isString() ||
inLoc->rtt.valueType() == KindOfBoolean) {
// Strings and bools produce value-dependent results; "" and
// false upgrade to an array successfully, while other values
// fail and leave the lhs unmodified.
DynLocation* baseLoc = t.newDynLocation(locLoc, KindOfInvalid);
assert(baseLoc->isLocal());
ni->outLocal = baseLoc;
} else if (inLoc->rtt.valueType() == KindOfUninit ||
inLoc->rtt.valueType() == KindOfNull) {
RuntimeType newLhsRtt = inLoc->rtt.setValueType(
mcodeMaybePropName(ni->immVecM[0]) ?
KindOfObject : KindOfArray);
SKTRACE(2, ni->source, "(%s, %d) <- type %d\n",
locLoc.spaceName(), locLoc.offset,
newLhsRtt.valueType());
DynLocation* baseLoc = t.newDynLocation(locLoc, newLhsRtt);
assert(baseLoc->location.isLocal());
ni->outLocal = baseLoc;
}
// Note (if we start translating pseudo-mains):
//
// A SetM in pseudo-main might alias a local whose type we're
// remembering:
//
// $GLOBALS['a'] = 123; // $a :: Int
//
// and more deviously:
//
// $loc['b'][17] = $GLOBALS; $x = 'b'; $y = 17;
// $loc[$x][$y]['a'] = 123; // $a :: Int
break;
}
// Note (if we start translating pseudo-mains):
//
// A SetM in pseudo-main might alias a local whose type we're
// remembering:
//
// $GLOBALS['a'] = 123; // $a :: Int
//
// and more deviously:
//
// $loc['b'][17] = $GLOBALS; $x = 'b'; $y = 17;
// $loc[$x][$y]['a'] = 123; // $a :: Int
case LNL:
case LNC:
varEnvTaint = true;
break;
case LGL:
case LGC:
break;
default:
break;
}
continue;
}
@@ -2470,11 +2496,13 @@ void Translator::getOutputs(/*inout*/ Tracelet& t,
ni->outLocal = dl;
continue;
}
if (op >= OpIterInit && op <= OpMIterNextK) {
if (op >= OpIterInit && op <= OpWIterNextK) {
assert(op == OpIterInit || op == OpIterInitK ||
op == OpMIterInit || op == OpMIterInitK ||
op == OpWIterInit || op == OpWIterInitK ||
op == OpIterNext || op == OpIterNextK ||
op == OpMIterNext || op == OpMIterNextK);
op == OpMIterNext || op == OpMIterNextK ||
op == OpWIterNext || op == OpWIterNextK);
const int kValImmIdx = 2;
const int kKeyImmIdx = 3;
DynLocation* outVal = t.newDynLocation();
@@ -2487,18 +2515,14 @@ void Translator::getOutputs(/*inout*/ Tracelet& t,
outVal->rtt = RuntimeType(KindOfInvalid);
}
ni->outLocal = outVal;
if (op == OpIterInitK || op == OpIterNextK) {
if (op == OpIterInitK || op == OpIterNextK ||
op == OpWIterInitK || op == OpWIterNextK ||
op == OpMIterInitK || op == OpMIterNextK) {
DynLocation* outKey = t.newDynLocation();
int keyOff = getImm(ni->pc(), kKeyImmIdx).u_IVA;
outKey->location = Location(Location::Local, keyOff);
outKey->rtt = RuntimeType(KindOfInvalid);
ni->outLocal2 = outKey;
} else if (op == OpMIterInitK || op == OpMIterNextK) {
DynLocation* outKey = t.newDynLocation();
int keyOff = getImm(ni->pc(), kKeyImmIdx).u_IVA;
outKey->location = Location(Location::Local, keyOff);
outKey->rtt = RuntimeType(KindOfRef, KindOfInvalid);
ni->outLocal2 = outKey;
}
continue;
}
+2
Ver Arquivo
@@ -1079,6 +1079,8 @@ opcodeControlFlowInfo(const Opcode instr) {
case OpIterInitK: // Ditto
case OpMIterInit: // Ditto
case OpMIterInitK: // Ditto
case OpWIterInit: // Ditto
case OpWIterInitK: // Ditto
case OpThrow:
case OpUnwind:
case OpEval:
+12 -2
Ver Arquivo
@@ -310,7 +310,7 @@ Offset decodeOffset(PC* ppc) {
*/
class ImmVecRange {
public:
ImmVecRange(const Opcode* instr) : v(getImmVector(instr)),
explicit ImmVecRange(const Opcode* instr) : v(getImmVector(instr)),
vecp(v.vec() + 1), // skip location code
loc(v.locationCode()),
loc_local(numLocationCodeImms(loc) ? decodeVariableSizeImm(&vecp) : -1) {
@@ -574,11 +574,13 @@ const FlavorDesc* FuncChecker::sig(PC pc) {
#define LMANY() { },
#define C_LMANY() { },
#define V_LMANY() { },
#define R_LMANY() { },
#define O(name, imm, pop, push, flags) pop
OPCODES
#undef O
#undef C_LMANY
#undef V_LMANY
#undef R_LMANY
#undef LMANY
#undef FMANY
#undef CMANY
@@ -602,6 +604,10 @@ const FlavorDesc* FuncChecker::sig(PC pc) {
case OpSetM: // ONE(LA), C_LMANY(), ONE(CV)
case OpSetOpM: // TWO(OA,LA), C_LMANY(), ONE(CV)
return vectorSig(pc, CV);
case OpSetWithRefLM://TWO(MA, HA), LMANY(), NOV
return vectorSig(pc, NOV);
case OpSetWithRefRM://ONE(MA), R_LMANY(), NOV
return vectorSig(pc, RV);
case OpFCall: // ONE(IVA), FMANY, ONE(RV)
case OpFCallArray:// NA, ONE(FV), ONE(RV)
case OpFCallBuiltin: //TWO(IVA, SA) FMANY, ONE(RV)
@@ -700,6 +706,7 @@ bool FuncChecker::checkIter(State* cur, PC pc) {
int id = getImmIva(pc);
bool ok = true;
if (Op(*pc) == OpIterInit || Op(*pc) == OpIterInitK ||
Op(*pc) == OpWIterInit || Op(*pc) == OpWIterInitK ||
Op(*pc) == OpMIterInit || Op(*pc) == OpMIterInitK) {
if (cur->iters[id]) {
verify_error(
@@ -732,11 +739,13 @@ bool FuncChecker::checkOutputs(State* cur, PC pc, Block* b) {
#define LMANY() { },
#define C_LMANY() { },
#define V_LMANY() { },
#define R_LMANY() { },
#define O(name, imm, pop, push, flags) push
OPCODES
#undef O
#undef C_LMANY
#undef V_LMANY
#undef R_LMANY
#undef LMANY
#undef FMANY
#undef CMANY
@@ -911,7 +920,8 @@ bool FuncChecker::checkSuccEdges(Block* b, State* cur) {
int id = getImmIva(b->last);
bool taken_state =
(Op(*b->last) == OpIterNext || Op(*b->last) == OpIterNextK ||
Op(*b->last) == OpMIterNext || Op(*b->last) == OpMIterNextK);
Op(*b->last) == OpMIterNext || Op(*b->last) == OpMIterNextK ||
Op(*b->last) == OpWIterNext || Op(*b->last) == OpWIterNextK);
bool save = cur->iters[id];
cur->iters[id] = taken_state;
if (m_verbose) {
@@ -0,0 +1,32 @@
<?php
require_once "support.hhas";
class X {
function __destruct() { var_dump(__METHOD__); }
static function test($x) { return $x !== true; }
}
function test() {
$a = array();
var_dump(test2($a, "foo", new X));
$a = array();
var_dump(test2($a, "foo", "bar"));
$a = array();
$b = "bar";
var_dump(test3($a, $b));
$a = array(1, 'foo' => 'bar', 0, true, false);
$b =& $a['foo'];
var_dump(fast_array_filter($a, null));
var_dump(fast_array_filter($a, function($a) { return !$a; }));
var_dump(fast_array_filter($a, array('X', 'test')));
var_dump(fast_array_map(function($a) { return $a.$a; }, $a));
var_dump(fast_array_map(function&(&$a) { return $a; }, $a));
}
test();
@@ -0,0 +1,62 @@
array(1) {
["foo"]=>
object(X)#1 (0) {
}
}
string(13) "X::__destruct"
array(1) {
["foo"]=>
string(3) "bar"
}
array(1) {
[0]=>
&string(3) "bar"
}
array(3) {
[0]=>
int(1)
["foo"]=>
&string(3) "bar"
[2]=>
bool(true)
}
array(2) {
[1]=>
int(0)
[3]=>
bool(false)
}
array(4) {
[0]=>
int(1)
["foo"]=>
&string(3) "bar"
[1]=>
int(0)
[3]=>
bool(false)
}
array(5) {
[0]=>
string(2) "11"
["foo"]=>
string(6) "barbar"
[1]=>
string(2) "00"
[2]=>
string(2) "11"
[3]=>
string(0) ""
}
array(5) {
[0]=>
int(1)
["foo"]=>
&string(3) "bar"
[1]=>
int(0)
[2]=>
bool(true)
[3]=>
bool(false)
}
+93
Ver Arquivo
@@ -0,0 +1,93 @@
.main {
Int 1
RetC
}
.function test1($arr, $k, $v) {
SetWithRefLM <L:$arr EL:$k> $v
CGetL $arr
RetC
}
.function test2(&$arr, $k, $v) {
CGetL $v
SetM <L:$arr EL:$k>
PopC
CGetL $arr
RetC
}
.function test3($arr, &$v) {
SetWithRefLM <L:$arr W> $v
CGetL $arr
RetC
}
.function fast_array_filter($arr, $func) {
.numiters 1;
NewArray
SetL $res
PopC
CGetL $arr
WIterInitK 0 endloop $v $k
IssetL $func
JmpZ loop_n
IsArrayL $func
JmpNZ loop_a
loop_s: CGetL $func
FPushFunc 1
FPassL 0 $v
FCall 1
UnboxR
JmpZ skip_s
SetWithRefLM <L:$res EL:$k> $v
skip_s: WIterNextK 0 loop_s $v $k
Jmp endloop
loop_n: CGetL $v
JmpZ skip_n
SetWithRefLM <L:$res EL:$k> $v
skip_n: WIterNextK 0 loop_n $v $k
Jmp endloop
loop_a: CGetL $func
FPushCuf 1
FPassL 0 $v
FCall 1
UnboxR
JmpZ skip_a
SetWithRefLM <L:$res EL:$k> $v
skip_a: WIterNextK 0 loop_a $v $k
endloop: CGetL $res
RetC
}
.function fast_array_map($func, $arr) {
.numiters 1;
IssetL $func
JmpZ ident
NewArray
SetL $res
PopC
CGetL $arr
WIterInitK 0 endloop $v $k
loop_x: CGetL $func
FPushCuf 1
FPassL 0 $v
FCall 1
SetWithRefRM <L:$res EL:$k>
WIterNextK 0 loop_x $v $k
endloop: CGetL $res
RetC
ident: CGetL $arr
RetC
}
+3
Ver Arquivo
@@ -1,3 +1,6 @@
Eval {
AllowHhas = true
}
Log {
AlwaysLogUnhandledExceptions = false
+1 -1
Ver Arquivo
@@ -378,7 +378,7 @@ sub run_test
if ($opt_repo) {
if ($test =~ m/\.hhas/) {
if ($test =~ m/\.hhas/ || -e $test . ".norepo") {
# We don't have a way to skip, I guess run non-repo?
} else {
my ($hphp_opts) = "";