Implement more final operations in VectorTranslator
Most of this is pretty boring and mechanical. I added VectorProp and VectorElem flags to help deal with the increasing number of vector-related opcodes.
Esse commit está contido em:
@@ -297,18 +297,27 @@ KillsSource
|
||||
The instruction might destroy one or more of its sources, so they
|
||||
cannot be safely used after it has executed.
|
||||
|
||||
HasStackVersion
|
||||
|
||||
This instruction has a counterpart that returns a StkPtr in addition to any
|
||||
primary destination. The behavior of the stack-modifying version is otherwise
|
||||
identical.
|
||||
|
||||
ModifiesStack
|
||||
|
||||
The instruction modifies the in-memory evaluation stack in the process of
|
||||
performing its primary work. It will have a StkPtr destination in addition to
|
||||
its primary destination.
|
||||
|
||||
HasStackVersion
|
||||
|
||||
This instruction has a counterpart that returns a StkPtr in addition to any
|
||||
primary destination. The behavior of the stack-modifying version is otherwise
|
||||
identical.
|
||||
|
||||
VectorProp
|
||||
|
||||
The instruction may affect the type and/or value of its base operand,
|
||||
operating on object properties.
|
||||
|
||||
VectorElem
|
||||
|
||||
The instruction may affect the type and/or value of its base operand,
|
||||
operating on array elements.
|
||||
|
||||
Instruction set
|
||||
---------------
|
||||
@@ -1142,7 +1151,7 @@ IterFree S0:StkPtr S1:ConstInt
|
||||
|
||||
D:PtrToCell = DefMIStateBase
|
||||
|
||||
Declares a base register for MInstrState. Currently this is always %rsp
|
||||
Declares a base register for MInstrState. Currently this is always %rsp.
|
||||
|
||||
All of the remaining opcodes in this section are simple wrappers
|
||||
around helper functions (specified in S0) to perform the corresponding
|
||||
@@ -1160,6 +1169,9 @@ SetProp/SetOpProp/IncDecProp:
|
||||
with a warning and the return value is null. Otherwise the return
|
||||
value is the same as the value input.
|
||||
|
||||
Any instructions that take a pointer to an MInstrState struct use the various
|
||||
fields of that struct for holding intermediate values.
|
||||
|
||||
D:PtrToGen = PropX S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen S4:PtrToCell
|
||||
|
||||
Lookup intermediate property in S2, with key S3. S4 should point to
|
||||
@@ -1170,11 +1182,51 @@ D:Cell = CGetProp S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen S4:PtrToCell
|
||||
Get property with key S3 from S2. S4 should point to an MInstrState
|
||||
struct.
|
||||
|
||||
D:BoxedCell = VGetProp S0:ConstTCA S1:ConstCls S2:{Obj|PtrtoGen} S3:Gen
|
||||
S4:PtrToCell
|
||||
D:BoxedCell, D:StkPtr = VGetPropStk S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen}
|
||||
S3:Gen S4:PtrToCell
|
||||
|
||||
Get property with key S3 from base S2 as a reference. S4 should point to an
|
||||
MInstrState struct.
|
||||
|
||||
BindProp S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen S4:BoxedCell
|
||||
S5:PtrToCell
|
||||
D:StkPtr = BindPropStk S0:ConstTCA S1:ConstCls S2:{Obj|PtrtoGen} S3:Gen
|
||||
S4:BoxedCell S5:PtrToCell
|
||||
|
||||
Bind property with key S3 in base S2 to the reference in S4. S5 should point
|
||||
to an MInstrState struct.
|
||||
|
||||
D:Vector = SetProp S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen S4:Cell
|
||||
D:Vector, D:StkPtr = SetPropStk S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen S4:Cell
|
||||
D:Vector, D:StkPtr = SetPropStk S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen
|
||||
S4:Cell
|
||||
|
||||
Set property with key S3 in S2 to S4.
|
||||
|
||||
D:Cell = SetOpProp S0:ConstTCA S1:{Obj|PtrToGen} S2:Gen S3:Cell S4:PtrToCell
|
||||
D:Cell, D:StkPtr = SetOpPropStk S0:ConstTCA S1:{Obj|PtrtoGen} S2:Gen S3:Cell
|
||||
S4:PtrtoCell
|
||||
|
||||
Set op propery with key S2 in base S1, using S3 as the right hand side. S4
|
||||
should point to an MInstrState struct.
|
||||
|
||||
D:Cell = IncDecProp S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen
|
||||
S4:PtrToCell
|
||||
D:Cell, D:StkPtr = IncDecPropStk S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen}
|
||||
S3:Gen S4:PtrToCell
|
||||
|
||||
Increment/decrement property with key S3 in base S2. S4 should point to an
|
||||
MInstrState struct.
|
||||
|
||||
D:Bool = EmptyProp S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen
|
||||
|
||||
Returns true iff the property with key S3 in base S2 is empty.
|
||||
|
||||
D:Bool = IssetProp S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen
|
||||
|
||||
Returns true iff the property with key S3 in base S2 is set.
|
||||
|
||||
D:PtrToGen = ElemX S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell
|
||||
|
||||
Get intermediate element with key S2 from base S1. The base will not be
|
||||
@@ -1191,6 +1243,18 @@ D:Cell = CGetElem S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell
|
||||
Get element with key S2 from S1. S3 should point to an MInstrState
|
||||
struct.
|
||||
|
||||
D:BoxedCell = VGetElem S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell
|
||||
D:BoxedCell, D:StkPtr = VGetElemStk S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell
|
||||
|
||||
Get element with key S2 from base S1 as a reference. S3 should point to an
|
||||
MInstrState struct.
|
||||
|
||||
BindElem S0:ConstTCA S1:PtrToGen S2:Gen S3:BoxedCell S4:PtrToCell
|
||||
D:StkPtr = BindElemStk S0:ConstTCA S1:PtrToGen S2:Gen S3:BoxedCell S4:PtrToCell
|
||||
|
||||
Bind element with key S2 in base S1 to the reference S3. S4 should point to
|
||||
an MInstrState struct.
|
||||
|
||||
D:Arr = ArraySet S0:ConstTCA S1:Arr S2:{Int|Str} S3:Cell
|
||||
|
||||
Set element with key S2 in S1 to S3. The dest will be a new Array
|
||||
@@ -1207,11 +1271,29 @@ D:Vector, D:StkPtr = SetElemStk S0:ConstTCA S1:PtrToGen S2:Gen S3:Cell
|
||||
|
||||
Set element with key S2 in S1 to S3.
|
||||
|
||||
D:Cell = SetOpElem S0:ConstTCA S1:PtrToGen S2:Gen S3:Cell S4:PtrToCell
|
||||
D:Cell, D:StkPtr = SetOpElemStk S0:ConstTCA S1:PtrToGen S2:Gen S3:Cell
|
||||
S4:PtrToCell
|
||||
|
||||
Set op elem with key S2 in base S1, using S3 as the right hand side. S4
|
||||
should point to an MInstrState struct.
|
||||
|
||||
D:Cell = IncDecElem S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell
|
||||
D:Cell, D:StkPtr = IncDecElemStk S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell
|
||||
|
||||
Increment/decrement element with key S2 in base S1. S3 should point to an
|
||||
MInstrState struct.
|
||||
|
||||
D:Vector = SetNewElem S0:PtrToGen S1:Cell
|
||||
D:Vector, D:StkPtr = SetNewElemStk S0:PtrToGen S1:Cell
|
||||
|
||||
Append the value in S1 to S0.
|
||||
|
||||
BindNewElem S0:ConstTCA S0:PtrToGen S1:BoxedCell S2:PtrToCell
|
||||
D:StkPtr = BindNewElemStk S0:PtrToGen S1:BoxedCell S2:PtrToCell
|
||||
|
||||
Append the reference in S1 to S0. S2 should point to an MInstrState struct.
|
||||
|
||||
D:Bool = IssetElem S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell
|
||||
|
||||
Returns true iff the element at key S2 in S1 is set.
|
||||
|
||||
@@ -31,7 +31,14 @@ namespace HPHP {
|
||||
*/
|
||||
class RefData {
|
||||
public:
|
||||
enum NullInit {
|
||||
nullinit,
|
||||
};
|
||||
RefData() {}
|
||||
RefData(NullInit) {
|
||||
_count = 1;
|
||||
m_tv.m_type = KindOfNull;
|
||||
}
|
||||
RefData(DataType t, int64_t datum) { init(t, datum); }
|
||||
~RefData();
|
||||
|
||||
|
||||
@@ -34,9 +34,12 @@ class Stack;
|
||||
namespace HPHP {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline TypedValue tv(DataType type, intptr_t data) {
|
||||
template<typename Data>
|
||||
inline TypedValue tv(DataType type, Data data) {
|
||||
static_assert(sizeof(Data) == sizeof(int64_t),
|
||||
"Data type in tv() not proper size");
|
||||
TypedValue v;
|
||||
v.m_data.num = data;
|
||||
v.m_data.num = (int64_t)data;
|
||||
v.m_type = type;
|
||||
return v;
|
||||
}
|
||||
@@ -179,18 +182,18 @@ inline void tvUnbox(TypedValue* tv) {
|
||||
assert(tvIsPlausible(tv));
|
||||
}
|
||||
|
||||
// Assumes 'fr' is live and 'to' is dead
|
||||
// Assumes 'fr' is live and 'to' is dead. Store a reference to 'fr',
|
||||
// as a Cell, into 'to'.
|
||||
inline void tvReadCell(const TypedValue* fr, TypedValue* to) {
|
||||
assert(tvIsPlausible(fr));
|
||||
if (fr->m_type != KindOfRef) {
|
||||
memcpy(to, fr, sizeof(TypedValue));
|
||||
tvRefcountedIncRef(to);
|
||||
} else {
|
||||
TypedValue* fr2 = fr->m_data.pref->tv();
|
||||
to->m_data.num = fr2->m_data.num;
|
||||
to->m_type = fr2->m_type;
|
||||
tvRefcountedIncRef(to);
|
||||
}
|
||||
tvRefcountedIncRef(to);
|
||||
}
|
||||
|
||||
// Assumes 'fr' is live and 'to' is dead
|
||||
@@ -215,6 +218,14 @@ inline void tvDupVar(const TypedValue* fr, TypedValue* to) {
|
||||
tvIncRefNotShared(to);
|
||||
}
|
||||
|
||||
// Assumes 'fr' is live and 'to' is dead
|
||||
inline void tvDupRef(RefData* fr, TypedValue* to) {
|
||||
assert(tvIsPlausible(fr->tv()));
|
||||
to->m_data.pref = fr;
|
||||
to->m_type = KindOfRef;
|
||||
fr->incRefCount();
|
||||
}
|
||||
|
||||
// Assumes 'fr' is live and 'to' is dead
|
||||
// NOTE: this helper does not modify to->_count
|
||||
inline void tvDup(const TypedValue* fr, TypedValue* to) {
|
||||
@@ -328,6 +339,14 @@ inline void tvBind(TypedValue * fr, TypedValue * to) {
|
||||
tvRefcountedDecRefHelper(oldType, oldDatum);
|
||||
}
|
||||
|
||||
// Assumes 'to' and 'fr' are live
|
||||
inline void tvBindRef(RefData* fr, TypedValue* to) {
|
||||
DataType oldType = to->m_type;
|
||||
uint64_t oldDatum = to->m_data.num;
|
||||
tvDupRef(fr, to);
|
||||
tvRefcountedDecRefHelper(oldType, oldDatum);
|
||||
}
|
||||
|
||||
// Assumes 'to' is live
|
||||
inline void tvUnset(TypedValue * to) {
|
||||
tvRefcountedDecRef(to);
|
||||
|
||||
@@ -5326,11 +5326,8 @@ inline void OPTBLD_INLINE VMExecutionContext::iopSetOpM(PC& pc) {
|
||||
}
|
||||
}
|
||||
|
||||
if (result->m_type == KindOfRef) {
|
||||
tvUnbox(result);
|
||||
}
|
||||
tvRefcountedDecRef(rhs);
|
||||
tvDup(result, rhs);
|
||||
tvReadCell(result, rhs);
|
||||
}
|
||||
setHelperPost<1>(SETHELPERPOST_ARGS);
|
||||
}
|
||||
|
||||
@@ -393,12 +393,14 @@ TypedValue* Instance::setOpProp(TypedValue& tvRef, Class* ctx,
|
||||
invokeGet(&tvResult, key);
|
||||
SETOP_BODY(&tvResult, op, val);
|
||||
if (getAttribute(UseSet)) {
|
||||
assert(tvRef.m_type == KindOfUninit);
|
||||
memcpy(&tvRef, &tvResult, sizeof(TypedValue));
|
||||
TypedValue ignored;
|
||||
invokeSet(&ignored, key, &tvResult);
|
||||
invokeSet(&ignored, key, &tvRef);
|
||||
tvRefcountedDecRef(&ignored);
|
||||
propVal = &tvResult;
|
||||
propVal = &tvRef;
|
||||
} else {
|
||||
memcpy((void *)propVal, (void *)&tvResult, sizeof(TypedValue));
|
||||
memcpy(propVal, &tvResult, sizeof(TypedValue));
|
||||
}
|
||||
} else {
|
||||
SETOP_BODY(propVal, op, val);
|
||||
|
||||
@@ -346,14 +346,25 @@ CALL_OPCODE(RaiseUndefProp)
|
||||
CALL_OPCODE(BaseG)
|
||||
CALL_OPCODE(PropX)
|
||||
CALL_OPCODE(CGetProp)
|
||||
CALL_STK_OPCODE(VGetProp)
|
||||
CALL_STK_OPCODE(BindProp)
|
||||
CALL_STK_OPCODE(SetProp)
|
||||
CALL_STK_OPCODE(SetOpProp)
|
||||
CALL_STK_OPCODE(IncDecProp)
|
||||
CALL_OPCODE(EmptyProp);
|
||||
CALL_OPCODE(IssetProp);
|
||||
CALL_OPCODE(ElemX)
|
||||
CALL_STK_OPCODE(ElemDX)
|
||||
CALL_OPCODE(CGetElem)
|
||||
CALL_STK_OPCODE(VGetElem)
|
||||
CALL_STK_OPCODE(BindElem)
|
||||
CALL_OPCODE(ArraySet)
|
||||
CALL_OPCODE(ArraySetRef)
|
||||
CALL_STK_OPCODE(SetElem)
|
||||
CALL_STK_OPCODE(SetOpElem)
|
||||
CALL_STK_OPCODE(IncDecElem)
|
||||
CALL_STK_OPCODE(SetNewElem)
|
||||
CALL_STK_OPCODE(BindNewElem)
|
||||
CALL_OPCODE(IssetElem)
|
||||
CALL_OPCODE(EmptyElem)
|
||||
|
||||
@@ -3444,15 +3455,16 @@ void CodeGenerator::cgStRaw(IRInstruction* inst) {
|
||||
SSATmp* value = inst->getSrc(2);
|
||||
|
||||
RawMemSlot& slot = RawMemSlot::Get(RawMemSlot::Kind(kind));
|
||||
assert(value->getType().equals(slot.getType()));
|
||||
int stSize = slot.getSize();
|
||||
int64_t off = slot.getOffset();
|
||||
auto dest = baseReg[off];
|
||||
|
||||
if (value->isConst()) {
|
||||
if (stSize == sz::qword) {
|
||||
m_as.storeq(value->getValInt(), dest);
|
||||
m_as.storeq(value->getValRawInt(), dest);
|
||||
} else if (stSize == sz::dword) {
|
||||
m_as.storel(value->getValInt(), dest);
|
||||
m_as.storel(value->getValRawInt(), dest);
|
||||
} else {
|
||||
assert(stSize == sz::byte);
|
||||
m_as.storeb(value->getValBool(), dest);
|
||||
|
||||
@@ -438,6 +438,11 @@ private:
|
||||
void setNoMIState() { m_needMIS = false; }
|
||||
SSATmp* genMisPtr();
|
||||
SSATmp* getInput(unsigned i);
|
||||
SSATmp* getValue() {
|
||||
// If an instruction takes an rhs, it's always input 0.
|
||||
const int kValIdx = 0;
|
||||
return getInput(kValIdx);
|
||||
}
|
||||
SSATmp* checkInitProp(SSATmp* baseAsObj, SSATmp* propAddr, int propOffset,
|
||||
bool warn, bool define);
|
||||
|
||||
|
||||
@@ -93,6 +93,8 @@ namespace {
|
||||
#define P Passthrough
|
||||
#define K KillsSources
|
||||
#define StkFlags(f) HasStackVersion|(f)
|
||||
#define VProp VectorProp
|
||||
#define VElem VectorElem
|
||||
|
||||
#define ND 0
|
||||
#define D(n) HasDest
|
||||
@@ -133,6 +135,8 @@ struct {
|
||||
#undef P
|
||||
#undef K
|
||||
#undef StkFlags
|
||||
#undef VProp
|
||||
#undef VElem
|
||||
|
||||
#undef ND
|
||||
#undef D
|
||||
|
||||
@@ -143,6 +143,8 @@ static const TCA kIRDirectGuardActive = (TCA)0x03;
|
||||
* T isTerminal
|
||||
* P passthrough
|
||||
* K killsSource
|
||||
* VProp VectorProp
|
||||
* VElem VectorElem
|
||||
*/
|
||||
|
||||
#define O_STK(name, dst, src, flags) \
|
||||
@@ -388,11 +390,40 @@ O(CGetProp, D(Cell), C(TCA) \
|
||||
S(Obj,PtrToGen) \
|
||||
S(Gen) \
|
||||
S(PtrToCell), E|N|Mem|Refs|Er) \
|
||||
O_STK(VGetProp, D(BoxedCell), C(TCA) \
|
||||
C(Cls) \
|
||||
S(Obj,PtrToGen) \
|
||||
S(Gen) \
|
||||
S(PtrToCell),VProp|E|N|Mem|Refs|Er) \
|
||||
O_STK(BindProp, ND, C(TCA) \
|
||||
C(Cls) \
|
||||
S(Obj,PtrToGen) \
|
||||
S(Gen) \
|
||||
S(BoxedCell) \
|
||||
S(PtrToCell),VProp|E|N|Mem|Refs|Er) \
|
||||
O_STK(SetProp, DVector, C(TCA) \
|
||||
C(Cls) \
|
||||
S(Obj,PtrToGen) \
|
||||
S(Gen) \
|
||||
S(Cell), E|N|Mem|Refs|Er) \
|
||||
S(Cell), VProp|E|N|Mem|Refs|Er) \
|
||||
O_STK(SetOpProp, D(Cell), C(TCA) \
|
||||
S(Obj,PtrToGen) \
|
||||
S(Gen) \
|
||||
S(Cell) \
|
||||
S(PtrToCell),VProp|E|N|Mem|Refs|Er) \
|
||||
O_STK(IncDecProp, D(Cell), C(TCA) \
|
||||
C(Cls) \
|
||||
S(Obj,PtrToGen) \
|
||||
S(Gen) \
|
||||
S(PtrToCell),VProp|E|N|Mem|Refs|Er) \
|
||||
O(EmptyProp, D(Bool), C(TCA) \
|
||||
C(Cls) \
|
||||
S(Obj,PtrToGen) \
|
||||
S(Gen), E|N|Mem|Refs|Er) \
|
||||
O(IssetProp, D(Bool), C(TCA) \
|
||||
C(Cls) \
|
||||
S(Obj,PtrToGen) \
|
||||
S(Gen), E|N|Mem|Refs|Er) \
|
||||
O(ElemX, D(PtrToGen), C(TCA) \
|
||||
S(PtrToGen) \
|
||||
S(Gen) \
|
||||
@@ -400,11 +431,20 @@ O(ElemX, D(PtrToGen), C(TCA) \
|
||||
O_STK(ElemDX, D(PtrToGen), C(TCA) \
|
||||
S(PtrToGen) \
|
||||
S(Gen) \
|
||||
S(PtrToCell), E|N|Mem|Refs|Er) \
|
||||
S(PtrToCell),VElem|E|N|Mem|Refs|Er) \
|
||||
O(CGetElem, D(Cell), C(TCA) \
|
||||
S(PtrToGen) \
|
||||
S(Gen) \
|
||||
S(PtrToCell), E|N|Mem|Refs|Er) \
|
||||
O_STK(VGetElem, D(BoxedCell), C(TCA) \
|
||||
S(PtrToGen) \
|
||||
S(Gen) \
|
||||
S(PtrToCell),VElem|E|N|Mem|Refs|Er) \
|
||||
O_STK(BindElem, ND, C(TCA) \
|
||||
S(PtrToGen) \
|
||||
S(Gen) \
|
||||
S(BoxedCell) \
|
||||
S(PtrToCell),VElem|E|N|Mem|Refs|Er) \
|
||||
O(ArraySet, D(Arr), C(TCA) \
|
||||
S(Arr) \
|
||||
S(Int,Str) \
|
||||
@@ -417,8 +457,21 @@ O(ArraySetRef, ND, C(TCA) \
|
||||
O_STK(SetElem, DVector, C(TCA) \
|
||||
S(PtrToGen) \
|
||||
S(Gen) \
|
||||
S(Cell), E|N|Mem|Refs|Er) \
|
||||
O_STK(SetNewElem, DVector, S(PtrToGen) S(Gen), E|N|Mem|Refs|Er) \
|
||||
S(Cell), VElem|E|N|Mem|Refs|Er) \
|
||||
O_STK(SetOpElem, D(Cell), C(TCA) \
|
||||
S(PtrToGen) \
|
||||
S(Gen) \
|
||||
S(Cell) \
|
||||
S(PtrToCell),VElem|E|N|Mem|Refs|Er) \
|
||||
O_STK(IncDecElem, D(Cell), C(TCA) \
|
||||
S(PtrToGen) \
|
||||
S(Gen) \
|
||||
S(PtrToCell),VElem|E|N|Mem|Refs|Er) \
|
||||
O_STK(SetNewElem, DVector, S(PtrToGen) \
|
||||
S(Gen), VElem|E|N|Mem|Refs|Er) \
|
||||
O_STK(BindNewElem, ND, S(PtrToGen) \
|
||||
S(BoxedCell) \
|
||||
S(PtrToCell),VElem|E|N|Mem|Refs|Er) \
|
||||
O(IssetElem, D(Bool), C(TCA) \
|
||||
S(PtrToGen) \
|
||||
S(Gen) \
|
||||
@@ -780,6 +833,8 @@ enum OpcodeFlag : uint64_t {
|
||||
KillsSources = 1ULL << 14,
|
||||
ModifiesStack = 1ULL << 15,
|
||||
HasStackVersion = 1ULL << 16,
|
||||
VectorProp = 1ULL << 17,
|
||||
VectorElem = 1ULL << 18,
|
||||
};
|
||||
|
||||
bool opcodeHasFlags(Opcode opc, uint64_t flags);
|
||||
@@ -1278,6 +1333,7 @@ class RawMemSlot {
|
||||
enum Kind {
|
||||
ContLabel, ContDone, ContShouldThrow, ContRunning, ContARPtr,
|
||||
StrLen, FuncNumParams, FuncRefBitVec, FuncBody, MisBaseStrOff,
|
||||
MisCtx,
|
||||
MaxKind
|
||||
};
|
||||
|
||||
@@ -1293,6 +1349,7 @@ class RawMemSlot {
|
||||
case FuncRefBitVec: return GetFuncRefBitVec();
|
||||
case FuncBody: return GetFuncBody();
|
||||
case MisBaseStrOff: return GetMisBaseStrOff();
|
||||
case MisCtx: return GetMisCtx();
|
||||
default: not_reached();
|
||||
}
|
||||
}
|
||||
@@ -1346,6 +1403,10 @@ class RawMemSlot {
|
||||
static RawMemSlot m(HHIR_MISOFF(baseStrOff), sz::byte, Type::Bool);
|
||||
return m;
|
||||
}
|
||||
static RawMemSlot& GetMisCtx() {
|
||||
static RawMemSlot m(HHIR_MISOFF(ctx), sz::qword, Type::Cls);
|
||||
return m;
|
||||
}
|
||||
|
||||
int64_t m_offset;
|
||||
int32_t m_size;
|
||||
@@ -1912,6 +1973,11 @@ struct VectorEffects {
|
||||
VectorEffects(Opcode op, Type base, Type key, Type val) {
|
||||
init(op, base, key, val);
|
||||
}
|
||||
VectorEffects(Opcode op, SSATmp* base, SSATmp* key, SSATmp* val) {
|
||||
auto typeOrNone =
|
||||
[](SSATmp* val){ return val ? val->getType() : Type::None; };
|
||||
init(op, typeOrNone(base), typeOrNone(key), typeOrNone(val));
|
||||
}
|
||||
Type baseType;
|
||||
Type valType;
|
||||
bool baseTypeChanged;
|
||||
|
||||
@@ -1011,6 +1011,7 @@ TranslatorX64::irTranslateIncDecM(const Tracelet& t,
|
||||
const DynLocation& prop = *i.inputs[1];
|
||||
const Location& propLoc = prop.location;
|
||||
HHIR_EMIT(IncDecProp, pre, inc, offset, propLoc.isStack());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,21 +103,43 @@ static CallMap s_callMap({
|
||||
{{SSA, 1}, {SSA, 2}, {VecKeyS, 3}, {SSA, 4}}},
|
||||
{CGetProp, {FSSA, 0}, DTV, SSync,
|
||||
{{SSA, 1}, {SSA, 2}, {VecKeyS, 3}, {SSA, 4}}},
|
||||
{VGetProp, {FSSA, 0}, DTV, SSync,
|
||||
{{SSA, 1}, {SSA, 2}, {VecKeyS, 3}, {SSA, 4}}},
|
||||
{BindProp, {FSSA, 0}, DNone, SSync,
|
||||
{{SSA, 1}, {SSA, 2}, {VecKeyS, 3}, {SSA, 4}, {SSA, 5}}},
|
||||
{SetProp, {FSSA, 0}, DTV, SSync,
|
||||
{{SSA, 1}, {SSA, 2}, {VecKeyS, 3}, {TV, 4}}},
|
||||
{SetOpProp,{FSSA, 0}, DTV, SSync,
|
||||
{{SSA, 1}, {VecKeyS, 2}, {TV, 3}, {SSA, 4}}},
|
||||
{IncDecProp,{FSSA, 0}, DTV, SSync,
|
||||
{{SSA, 1}, {SSA, 2}, {VecKeyS, 3}, {SSA, 4}}},
|
||||
{EmptyProp,{FSSA, 0}, DSSA, SSync,
|
||||
{{SSA, 1}, {SSA, 2}, {VecKeyS, 3}}},
|
||||
{IssetProp,{FSSA, 0}, DSSA, SSync,
|
||||
{{SSA, 1}, {SSA, 2}, {VecKeyS, 3}}},
|
||||
{ElemX, {FSSA, 0}, DSSA, SSync,
|
||||
{{SSA, 1}, {VecKeyIS, 2}, {SSA, 3}}},
|
||||
{ElemDX, {FSSA, 0}, DSSA, SSync,
|
||||
{{SSA, 1}, {VecKeyIS, 2}, {SSA, 3}}},
|
||||
{CGetElem, {FSSA, 0}, DTV, SSync,
|
||||
{{SSA, 1}, {VecKeyIS, 2}, {SSA, 3}}},
|
||||
{VGetElem, {FSSA, 0}, DTV, SSync,
|
||||
{{SSA, 1}, {VecKeyIS, 2}, {SSA, 3}}},
|
||||
{BindElem, {FSSA, 0}, DNone, SSync,
|
||||
{{SSA, 1}, {VecKeyIS, 2}, {SSA, 3}, {SSA, 4}}},
|
||||
{ArraySet, {FSSA, 0}, DSSA, SSync,
|
||||
{{SSA, 1}, {SSA, 2}, {TV, 3}}},
|
||||
{ArraySetRef, {FSSA, 0}, DSSA, SSync,
|
||||
{{SSA, 1}, {SSA, 2}, {TV, 3}, {SSA, 4}}},
|
||||
{SetElem, {FSSA, 0}, DTV, SSync,
|
||||
{{SSA, 1}, {VecKeyIS, 2}, {TV, 3}}},
|
||||
{SetOpElem,{FSSA, 0}, DTV, SSync,
|
||||
{{SSA, 1}, {VecKeyIS, 2}, {TV, 3}, {SSA, 4}}},
|
||||
{IncDecElem,{FSSA, 0}, DTV, SSync,
|
||||
{{SSA, 1}, {VecKeyIS, 2}, {SSA, 3}}},
|
||||
{SetNewElem, (TCA)setNewElem, DTV, SSync, {{SSA, 0}, {TV, 1}}},
|
||||
{BindNewElem, (TCA)bindNewElemIR, DNone, SSync,
|
||||
{{SSA, 0}, {SSA, 1}, {SSA, 2}}},
|
||||
{IssetElem,{FSSA, 0}, DSSA, SSync,
|
||||
{{SSA, 1}, {VecKeyIS, 2}, {SSA, 3}}},
|
||||
{EmptyElem,{FSSA, 0}, DSSA, SSync,
|
||||
|
||||
@@ -270,7 +270,6 @@ SSATmp* TraceBuilder::genLdRaw(SSATmp* base, RawMemSlot::Kind kind,
|
||||
|
||||
void TraceBuilder::genStRaw(SSATmp* base, RawMemSlot::Kind kind,
|
||||
SSATmp* value) {
|
||||
assert(value->getType() == Type::Int || value->getType() == Type::Bool);
|
||||
gen(StRaw, base, genDefConst(int64_t(kind)), value);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,9 +30,6 @@ TRACE_SET_MOD(hhir);
|
||||
namespace {
|
||||
|
||||
Type vectorReturn(const IRInstruction* inst) {
|
||||
assert(inst->getOpcode() == SetProp ||
|
||||
inst->getOpcode() == SetElem ||
|
||||
inst->getOpcode() == SetNewElem);
|
||||
return VectorEffects(inst).valType;
|
||||
}
|
||||
|
||||
@@ -79,7 +76,8 @@ Type outputType(const IRInstruction* inst, int dstId) {
|
||||
#define DBox(n) return boxReturn(inst, n);
|
||||
#define DParam return inst->getTypeParam();
|
||||
#define DMulti return Type::None;
|
||||
#define DStk(in) return stkReturn(inst, dstId, [&]{ in not_reached(); });
|
||||
#define DStk(in) return stkReturn(inst, dstId, \
|
||||
[&]() -> Type { in not_reached(); });
|
||||
#define DVector return vectorReturn(inst);
|
||||
#define ND assert(0 && "outputType requires HasDest or NaryDest");
|
||||
#define DBuiltin return builtinReturn(inst);
|
||||
|
||||
@@ -32,16 +32,7 @@ using Transl::MInstrState;
|
||||
using Transl::mInstrHasUnknownOffsets;
|
||||
|
||||
bool VectorEffects::supported(Opcode op) {
|
||||
switch (op) {
|
||||
case SetProp: case SetPropStk:
|
||||
case SetElem: case SetElemStk:
|
||||
case SetNewElem: case SetNewElemStk:
|
||||
case ElemDX: case ElemDXStk:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return opcodeHasFlags(op, VectorProp | VectorElem);
|
||||
}
|
||||
|
||||
void VectorEffects::get(const IRInstruction* inst,
|
||||
@@ -112,20 +103,9 @@ void VectorEffects::init(Opcode op, const Type origBase,
|
||||
baseTypeChanged = baseValChanged = valTypeChanged = false;
|
||||
|
||||
// Canonicalize the op to SetProp or SetElem
|
||||
switch (op) {
|
||||
case SetProp: case SetPropStk:
|
||||
break;
|
||||
|
||||
case SetElem: case SetElemStk:
|
||||
case SetNewElem: case SetNewElemStk:
|
||||
case ElemDX: case ElemDXStk:
|
||||
case ArraySet:
|
||||
op = SetElem;
|
||||
break;
|
||||
|
||||
default:
|
||||
not_implemented();
|
||||
}
|
||||
op = opcodeHasFlags(op, VectorProp) ? SetProp
|
||||
: opcodeHasFlags(op, VectorElem) || op == ArraySet ? SetElem
|
||||
: bad_value<Opcode>();
|
||||
assert(op == SetProp || op == SetElem);
|
||||
assert(key.equals(Type::None) || key.isKnownDataType());
|
||||
assert(origVal.equals(Type::None) || origVal.isKnownDataType());
|
||||
@@ -190,37 +170,45 @@ void VectorEffects::init(Opcode op, const Type origBase,
|
||||
|
||||
// vectorBaseIdx returns the src index for inst's base operand.
|
||||
int vectorBaseIdx(Opcode opc) {
|
||||
switch (opc) {
|
||||
case SetProp: case SetPropStk: return 2;
|
||||
case SetElem: case SetElemStk: return 1;
|
||||
case ElemDX: case ElemDXStk: return 1;
|
||||
case SetNewElem: case SetNewElemStk: return 0;
|
||||
case ArraySet: return 1;
|
||||
default: not_reached();
|
||||
}
|
||||
return opc == SetNewElem || opc == SetNewElemStk ? 0
|
||||
: opc == BindNewElem || opc == BindNewElemStk ? 0
|
||||
: opc == ArraySet ? 1
|
||||
: opc == SetOpProp || opc == SetOpPropStk ? 1
|
||||
: opcodeHasFlags(opc, VectorProp) ? 2
|
||||
: opcodeHasFlags(opc, VectorElem) ? 1
|
||||
: bad_value<int>();
|
||||
}
|
||||
|
||||
// vectorKeyIdx returns the src index for inst's key operand.
|
||||
int vectorKeyIdx(Opcode opc) {
|
||||
switch (opc) {
|
||||
case SetProp: case SetPropStk: return 3;
|
||||
case SetElem: case SetElemStk: return 2;
|
||||
case ElemDX: case ElemDXStk: return 2;
|
||||
case SetNewElem: case SetNewElemStk: return -1;
|
||||
case ArraySet: return 2;
|
||||
default: not_reached();
|
||||
}
|
||||
return opc == SetNewElem || opc == SetNewElemStk ? -1
|
||||
: opc == BindNewElem || opc == BindNewElem ? -1
|
||||
: opc == ArraySet ? 2
|
||||
: opc == SetOpProp || opc == SetOpPropStk ? 2
|
||||
: opcodeHasFlags(opc, VectorProp) ? 3
|
||||
: opcodeHasFlags(opc, VectorElem) ? 2
|
||||
: bad_value<int>();
|
||||
}
|
||||
|
||||
// vectorValIdx returns the src index for inst's value operand.
|
||||
int vectorValIdx(Opcode opc) {
|
||||
switch (opc) {
|
||||
case SetProp: case SetPropStk: return 4;
|
||||
case SetElem: case SetElemStk: return 3;
|
||||
case ElemDX: case ElemDXStk: return -1;
|
||||
case SetNewElem: case SetNewElemStk: return 1;
|
||||
case VGetProp: case VGetPropStk:
|
||||
case IncDecProp: case IncDecPropStk:
|
||||
case VGetElem: case VGetElemStk:
|
||||
case IncDecElem: case IncDecElemStk:
|
||||
case ElemDX: case ElemDXStk:
|
||||
return -1;
|
||||
|
||||
case ArraySet: return 3;
|
||||
default: not_reached();
|
||||
case SetNewElem: case SetNewElemStk: return 1;
|
||||
case BindNewElem: case BindNewElemStk: return 1;
|
||||
case SetOpProp: case SetOpPropStk: return 3;
|
||||
|
||||
default:
|
||||
return opcodeHasFlags(opc, VectorProp) ? 4
|
||||
: opcodeHasFlags(opc, VectorElem) ? 3
|
||||
: bad_value<int>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1026,16 +1014,110 @@ void HhbcTranslator::VectorTranslator::emitCGetProp() {
|
||||
}
|
||||
#undef HELPER_TABLE
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitVGetProp() {
|
||||
SPUNT(__func__);
|
||||
template <KeyType keyType, bool unboxKey, bool isObj>
|
||||
static inline TypedValue vGetPropImpl(Class* ctx, TypedValue* base,
|
||||
TypedValue keyVal, MInstrState* mis) {
|
||||
TypedValue* key = keyPtr<keyType>(keyVal);
|
||||
key = unbox<keyType, unboxKey>(key);
|
||||
base = VM::Prop<false, true, false, isObj, keyType>(
|
||||
mis->tvScratch, mis->tvRef, ctx, base, key);
|
||||
|
||||
if (base == &mis->tvScratch && base->m_type == KindOfUninit) {
|
||||
// Error (no result was set).
|
||||
return tv(KindOfRef, NEW(RefData)(RefData::nullinit));
|
||||
} else {
|
||||
if (base->m_type != KindOfRef) {
|
||||
tvBox(base);
|
||||
}
|
||||
assert(base->m_type == KindOfRef);
|
||||
base->m_data.pref->incRefCount();
|
||||
return *base;
|
||||
}
|
||||
}
|
||||
|
||||
#define HELPER_TABLE(m) \
|
||||
/* name hot key unboxKey isObj */ \
|
||||
m(vGetPropC, , AnyKey, false, false) \
|
||||
m(vGetPropCO, , AnyKey, false, true) \
|
||||
m(vGetPropL, , AnyKey, true, false) \
|
||||
m(vGetPropLO, , AnyKey, true, true) \
|
||||
m(vGetPropLS, , StrKey, true, false) \
|
||||
m(vGetPropLSO, , StrKey, true, true) \
|
||||
m(vGetPropS, , StrKey, false, false) \
|
||||
m(vGetPropSO, HOT_FUNC_VM, StrKey, false, true)
|
||||
|
||||
#define PROP(nm, hot, ...) \
|
||||
hot \
|
||||
static TypedValue nm(Class* ctx, TypedValue* base, TypedValue key, \
|
||||
MInstrState* mis) { \
|
||||
return vGetPropImpl<__VA_ARGS__>(ctx, base, key, mis); \
|
||||
}
|
||||
HELPER_TABLE(PROP)
|
||||
#undef PROP
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitVGetProp() {
|
||||
SSATmp* key = getInput(m_iInd);
|
||||
typedef TypedValue (*OpFunc)(Class*, TypedValue*, TypedValue, MInstrState*);
|
||||
BUILD_OPTAB_HOT(getKeyTypeS(key), key->isBoxed(), m_base->isA(Type::Obj));
|
||||
m_ht.spillStack();
|
||||
m_result = genStk(VGetProp, cns((TCA)opFunc), CTX(),
|
||||
m_base, key, genMisPtr());
|
||||
}
|
||||
#undef HELPER_TABLE
|
||||
|
||||
template <KeyType keyType, bool unboxKey, bool useEmpty, bool isObj>
|
||||
static inline bool issetEmptyPropImpl(Class* ctx, TypedValue* base,
|
||||
TypedValue keyVal) {
|
||||
TypedValue* key = keyPtr<keyType>(keyVal);
|
||||
key = unbox<keyType, unboxKey>(key);
|
||||
return VM::IssetEmptyProp<useEmpty, isObj, keyType>(ctx, base, key);
|
||||
}
|
||||
|
||||
#define HELPER_TABLE(m) \
|
||||
/* name hot key unboxKey useEmpty isObj */ \
|
||||
m(issetPropC, , AnyKey, false, false, false) \
|
||||
m(issetPropCE, , AnyKey, false, true, false) \
|
||||
m(issetPropCEO, , AnyKey, false, true, true) \
|
||||
m(issetPropCO, , AnyKey, false, false, true) \
|
||||
m(issetPropL, , AnyKey, true, false, false) \
|
||||
m(issetPropLE, , AnyKey, true, true, false) \
|
||||
m(issetPropLEO, , AnyKey, true, true, true) \
|
||||
m(issetPropLO, , AnyKey, true, false, true) \
|
||||
m(issetPropLS, , StrKey, true, false, false) \
|
||||
m(issetPropLSE, , StrKey, true, true, false) \
|
||||
m(issetPropLSEO, , StrKey, true, true, true) \
|
||||
m(issetPropLSO, , StrKey, true, false, true) \
|
||||
m(issetPropS, , StrKey, false, false, false) \
|
||||
m(issetPropSE, , StrKey, false, true, false) \
|
||||
m(issetPropSEO, , StrKey, false, true, true) \
|
||||
m(issetPropSO, HOT_FUNC_VM, StrKey, false, false, true)
|
||||
|
||||
#define ISSET(nm, hot, ...) \
|
||||
hot \
|
||||
/* This returns int64_t to ensure all 64 bits of rax are valid */ \
|
||||
static int64_t nm(Class* ctx, TypedValue* base, TypedValue key) { \
|
||||
return issetEmptyPropImpl<__VA_ARGS__>(ctx, base, key); \
|
||||
}
|
||||
HELPER_TABLE(ISSET)
|
||||
#undef ISSET
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitIssetEmptyProp(bool isEmpty) {
|
||||
SSATmp* key = getInput(m_iInd);
|
||||
typedef bool (*OpFunc)(Class*, TypedValue*, TypedValue);
|
||||
BUILD_OPTAB_HOT(getKeyTypeS(key), key->isBoxed(), isEmpty,
|
||||
m_base->isA(Type::Obj));
|
||||
m_ht.spillStack();
|
||||
m_result = m_tb.gen(isEmpty ? EmptyProp : IssetProp, cns((TCA)opFunc),
|
||||
CTX(), m_base, key);
|
||||
}
|
||||
#undef HELPER_TABLE
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitIssetProp() {
|
||||
SPUNT(__func__);
|
||||
emitIssetEmptyProp(false);
|
||||
}
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitEmptyProp() {
|
||||
SPUNT(__func__);
|
||||
emitIssetEmptyProp(true);
|
||||
}
|
||||
|
||||
template <KeyType keyType, bool unboxKey, bool isObj>
|
||||
@@ -1066,8 +1148,7 @@ HELPER_TABLE(PROP)
|
||||
#undef PROP
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitSetProp() {
|
||||
const int kValIdx = 0;
|
||||
SSATmp* value = getInput(kValIdx);
|
||||
SSATmp* value = getValue();
|
||||
|
||||
/* If we know the class for the current base, emit a direct property set. */
|
||||
const Class* knownCls = nullptr;
|
||||
@@ -1097,17 +1178,141 @@ void HhbcTranslator::VectorTranslator::emitSetProp() {
|
||||
}
|
||||
#undef HELPER_TABLE
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitSetOpProp() {
|
||||
SPUNT(__func__);
|
||||
template <KeyType keyType, bool unboxKey, SetOpOp op, bool isObj>
|
||||
static inline TypedValue setOpPropImpl(TypedValue* base, TypedValue keyVal,
|
||||
Cell val, MInstrState* mis) {
|
||||
TypedValue* key = keyPtr<keyType>(keyVal);
|
||||
key = unbox<keyType, unboxKey>(key);
|
||||
TypedValue* result = VM::SetOpProp<isObj, keyType>(
|
||||
mis->tvScratch, mis->tvRef, mis->ctx, op, base, key, &val);
|
||||
|
||||
TypedValue ret;
|
||||
tvReadCell(result, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define OPPROP_TABLE(m, nm, op) \
|
||||
/* name keyType unboxKey op isObj */ \
|
||||
m(nm##op##PropC, AnyKey, false, op, false) \
|
||||
m(nm##op##PropCO, AnyKey, false, op, true) \
|
||||
m(nm##op##PropL, AnyKey, true, op, false) \
|
||||
m(nm##op##PropLO, AnyKey, true, op, true) \
|
||||
m(nm##op##PropLS, StrKey, true, op, false) \
|
||||
m(nm##op##PropLSO, StrKey, true, op, true) \
|
||||
m(nm##op##PropS, StrKey, false, op, false) \
|
||||
m(nm##op##PropSO, StrKey, false, op, true)
|
||||
|
||||
#define HELPER_TABLE(m, op) OPPROP_TABLE(m, setOp, SetOp##op)
|
||||
#define SETOP(nm, ...) \
|
||||
static TypedValue nm(TypedValue* base, TypedValue key, \
|
||||
Cell val, MInstrState* mis) { \
|
||||
return setOpPropImpl<__VA_ARGS__>(base, key, val, mis); \
|
||||
}
|
||||
#define SETOP_OP(op, bcOp) HELPER_TABLE(SETOP, op)
|
||||
SETOP_OPS
|
||||
#undef SETOP_OP
|
||||
#undef SETOP
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitSetOpProp() {
|
||||
SetOpOp op = SetOpOp(m_ni.imm[0].u_OA);
|
||||
SSATmp* key = getInput(m_iInd);
|
||||
SSATmp* value = getValue();
|
||||
typedef TypedValue (*OpFunc)(TypedValue*, TypedValue,
|
||||
Cell, MInstrState*);
|
||||
# define SETOP_OP(op, bcOp) HELPER_TABLE(FILL_ROW, op)
|
||||
BUILD_OPTAB_ARG(SETOP_OPS,
|
||||
getKeyTypeS(key), key->isBoxed(), op,
|
||||
m_base->isA(Type::Obj));
|
||||
# undef SETOP_OP
|
||||
m_tb.genStRaw(m_misBase, RawMemSlot::MisCtx, CTX());
|
||||
m_ht.spillStack();
|
||||
m_result =
|
||||
genStk(SetOpProp, cns((TCA)opFunc), m_base, key, value, genMisPtr());
|
||||
}
|
||||
#undef HELPER_TABLE
|
||||
|
||||
template <KeyType keyType, bool unboxKey, IncDecOp op, bool isObj>
|
||||
static inline TypedValue incDecPropImpl(Class* ctx, TypedValue* base,
|
||||
TypedValue keyVal, MInstrState* mis) {
|
||||
TypedValue* key = keyPtr<keyType>(keyVal);
|
||||
key = unbox<keyType, unboxKey>(key);
|
||||
TypedValue result;
|
||||
result.m_type = KindOfUninit;
|
||||
VM::IncDecProp<true, isObj, keyType>(
|
||||
mis->tvScratch, mis->tvRef, ctx, op, base, key, result);
|
||||
assert(result.m_type != KindOfRef);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#define HELPER_TABLE(m, op) OPPROP_TABLE(m, incDec, op)
|
||||
#define INCDEC(nm, ...) \
|
||||
static TypedValue nm(Class* ctx, TypedValue* base, TypedValue key, \
|
||||
MInstrState* mis) { \
|
||||
return incDecPropImpl<__VA_ARGS__>(ctx, base, key, mis); \
|
||||
}
|
||||
#define INCDEC_OP(op) HELPER_TABLE(INCDEC, op)
|
||||
INCDEC_OPS
|
||||
#undef INCDEC_OP
|
||||
#undef INCDEC
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitIncDecProp() {
|
||||
SPUNT(__func__);
|
||||
IncDecOp op = IncDecOp(m_ni.imm[0].u_OA);
|
||||
SSATmp* key = getInput(m_iInd);
|
||||
typedef TypedValue (*OpFunc)(Class*, TypedValue*, TypedValue, MInstrState*);
|
||||
# define INCDEC_OP(op) HELPER_TABLE(FILL_ROW, op)
|
||||
BUILD_OPTAB_ARG(INCDEC_OPS,
|
||||
getKeyTypeS(key), key->isBoxed(), op,
|
||||
m_base->isA(Type::Obj));
|
||||
# undef INCDEC_OP
|
||||
m_ht.spillStack();
|
||||
m_result =
|
||||
genStk(IncDecProp, cns((TCA)opFunc), CTX(), m_base, key, genMisPtr());
|
||||
}
|
||||
#undef HELPER_TABLE
|
||||
|
||||
template <KeyType keyType, bool unboxKey, bool isObj>
|
||||
static inline void bindPropImpl(Class* ctx, TypedValue* base, TypedValue keyVal,
|
||||
RefData* val, MInstrState* mis) {
|
||||
TypedValue* key = keyPtr<keyType>(keyVal);
|
||||
key = unbox<keyType, unboxKey>(key);
|
||||
TypedValue* prop = VM::Prop<false, true, false, isObj, keyType>(
|
||||
mis->tvScratch, mis->tvRef, ctx, base, key);
|
||||
if (!(prop == &mis->tvScratch && prop->m_type == KindOfUninit)) {
|
||||
tvBindRef(val, prop);
|
||||
}
|
||||
}
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitBindProp() {
|
||||
SPUNT(__func__);
|
||||
#define HELPER_TABLE(m) \
|
||||
/* name key unboxKey isObj */ \
|
||||
m(bindPropC, AnyKey, false, false) \
|
||||
m(bindPropCO, AnyKey, false, true) \
|
||||
m(bindPropL, AnyKey, true, false) \
|
||||
m(bindPropLO, AnyKey, true, true) \
|
||||
m(bindPropLS, StrKey, true, false) \
|
||||
m(bindPropLSO, StrKey, true, true) \
|
||||
m(bindPropS, StrKey, false, false) \
|
||||
m(bindPropSO, StrKey, false, true)
|
||||
|
||||
#define PROP(nm, ...) \
|
||||
static inline void nm(Class* ctx, TypedValue* base, TypedValue key, \
|
||||
RefData* val, MInstrState* mis) { \
|
||||
bindPropImpl<__VA_ARGS__>(ctx, base, key, val, mis); \
|
||||
}
|
||||
HELPER_TABLE(PROP)
|
||||
#undef PROP
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitBindProp() {
|
||||
SSATmp* key = getInput(m_iInd);
|
||||
SSATmp* box = getValue();
|
||||
typedef void (*OpFunc)(Class*, TypedValue*, TypedValue*, RefData*,
|
||||
MInstrState*);
|
||||
BUILD_OPTAB(getKeyTypeS(key), key->isBoxed(), m_base->isA(Type::Obj));
|
||||
m_ht.spillStack();
|
||||
genStk(BindProp, cns((TCA)opFunc), CTX(), m_base, key, box, genMisPtr());
|
||||
m_result = box;
|
||||
}
|
||||
#undef HELPER_TABLE
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitUnsetProp() {
|
||||
SPUNT(__func__);
|
||||
@@ -1157,10 +1362,53 @@ void HhbcTranslator::VectorTranslator::emitCGetElem() {
|
||||
}
|
||||
#undef HELPER_TABLE
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitVGetElem() {
|
||||
SPUNT(__func__);
|
||||
template <KeyType keyType, bool unboxKey>
|
||||
static inline TypedValue vGetElemImpl(TypedValue* base, TypedValue keyVal,
|
||||
MInstrState* mis) {
|
||||
TypedValue* key = keyPtr<keyType>(keyVal);
|
||||
key = unbox<keyType, unboxKey>(key);
|
||||
base = VM::ElemD<false, true, keyType>(mis->tvScratch, mis->tvRef, base, key);
|
||||
|
||||
TypedValue result;
|
||||
if (base == &mis->tvScratch && base->m_type == KindOfUninit) {
|
||||
// Error (no result was set).
|
||||
tvWriteNull(&result);
|
||||
tvBox(&result);
|
||||
} else {
|
||||
if (base->m_type != KindOfRef) {
|
||||
tvBox(base);
|
||||
}
|
||||
tvDupVar(base, &result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#define HELPER_TABLE(m) \
|
||||
/* name hot keyType unboxKey */ \
|
||||
m(vGetElemC, , AnyKey, false) \
|
||||
m(vGetElemI, HOT_FUNC_VM, IntKey, false) \
|
||||
m(vGetElemL, , AnyKey, true) \
|
||||
m(vGetElemLI, , IntKey, true) \
|
||||
m(vGetElemLS, , StrKey, true) \
|
||||
m(vGetElemS, , StrKey, false)
|
||||
|
||||
#define ELEM(nm, hot, ...) \
|
||||
hot \
|
||||
static TypedValue nm(TypedValue* base, TypedValue key, MInstrState* mis) { \
|
||||
return vGetElemImpl<__VA_ARGS__>(base, key, mis); \
|
||||
}
|
||||
HELPER_TABLE(ELEM)
|
||||
#undef ELEM
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitVGetElem() {
|
||||
SSATmp* key = getInput(m_iInd);
|
||||
typedef TypedValue (*OpFunc)(TypedValue*, TypedValue, MInstrState*);
|
||||
BUILD_OPTAB_HOT(getKeyTypeIS(key), key->isBoxed());
|
||||
m_ht.spillStack();
|
||||
m_result = genStk(VGetElem, cns((TCA)opFunc), m_base, key, genMisPtr());
|
||||
}
|
||||
#undef HELPER_TABLE
|
||||
|
||||
template <KeyType keyType, bool unboxKey, bool isEmpty>
|
||||
static inline bool issetEmptyElemImpl(TypedValue* base, TypedValue keyVal,
|
||||
MInstrState* mis) {
|
||||
@@ -1341,8 +1589,7 @@ void HhbcTranslator::VectorTranslator::emitSimpleArraySet(SSATmp* key,
|
||||
}
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitSetElem() {
|
||||
const int kValIdx = 0;
|
||||
SSATmp* value = getInput(kValIdx);
|
||||
SSATmp* value = getValue();
|
||||
SSATmp* key = getInput(m_iInd);
|
||||
|
||||
if (isSimpleArraySet()) {
|
||||
@@ -1361,17 +1608,124 @@ void HhbcTranslator::VectorTranslator::emitSetElem() {
|
||||
#undef HELPER_TABLE
|
||||
#undef HELPER_TABLE_ARRAY_SET
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitSetOpElem() {
|
||||
SPUNT(__func__);
|
||||
template <KeyType keyType, bool unboxKey, SetOpOp op>
|
||||
static inline TypedValue setOpElemImpl(TypedValue* base, TypedValue keyVal,
|
||||
Cell val, MInstrState* mis) {
|
||||
TypedValue* key = keyPtr<keyType>(keyVal);
|
||||
key = unbox<keyType, unboxKey>(key);
|
||||
TypedValue* result =
|
||||
VM::SetOpElem<keyType>(mis->tvScratch, mis->tvRef, op, base, key, &val);
|
||||
|
||||
TypedValue ret;
|
||||
tvReadCell(result, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define OPELEM_TABLE(m, nm, op) \
|
||||
/* name keyType unboxKey op */ \
|
||||
m(nm##op##ElemC, AnyKey, false, op) \
|
||||
m(nm##op##ElemI, IntKey, false, op) \
|
||||
m(nm##op##ElemL, AnyKey, true, op) \
|
||||
m(nm##op##ElemLI, IntKey, true, op) \
|
||||
m(nm##op##ElemLS, StrKey, true, op) \
|
||||
m(nm##op##ElemS, StrKey, false, op)
|
||||
|
||||
#define HELPER_TABLE(m, op) OPELEM_TABLE(m, setOp, SetOp##op)
|
||||
#define SETOP(nm, ...) \
|
||||
static TypedValue nm(TypedValue* base, TypedValue key, Cell val, \
|
||||
MInstrState* mis) { \
|
||||
return setOpElemImpl<__VA_ARGS__>(base, key, val, mis); \
|
||||
}
|
||||
#define SETOP_OP(op, bcOp) HELPER_TABLE(SETOP, op)
|
||||
SETOP_OPS
|
||||
#undef SETOP_OP
|
||||
#undef SETOP
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitSetOpElem() {
|
||||
SetOpOp op = SetOpOp(m_ni.imm[0].u_OA);
|
||||
SSATmp* key = getInput(m_iInd);
|
||||
typedef TypedValue (*OpFunc)(TypedValue*, TypedValue, Cell, MInstrState*);
|
||||
# define SETOP_OP(op, bcOp) HELPER_TABLE(FILL_ROW, op)
|
||||
BUILD_OPTAB_ARG(SETOP_OPS, getKeyTypeIS(key), key->isBoxed(), op);
|
||||
# undef SETOP_OP
|
||||
m_ht.spillStack();
|
||||
m_result =
|
||||
genStk(SetOpElem, cns((TCA)opFunc), m_base, key, getValue(), genMisPtr());
|
||||
}
|
||||
#undef HELPER_TABLE
|
||||
|
||||
template <KeyType keyType, bool unboxKey, IncDecOp op>
|
||||
static inline TypedValue incDecElemImpl(TypedValue* base, TypedValue keyVal,
|
||||
MInstrState* mis) {
|
||||
TypedValue* key = keyPtr<keyType>(keyVal);
|
||||
key = unbox<keyType, unboxKey>(key);
|
||||
TypedValue result;
|
||||
VM::IncDecElem<true, keyType>(
|
||||
mis->tvScratch, mis->tvRef, op, base, key, result);
|
||||
assert(result.m_type != KindOfRef);
|
||||
return result;
|
||||
}
|
||||
|
||||
#define HELPER_TABLE(m, op) OPELEM_TABLE(m, incDec, op)
|
||||
#define INCDEC(nm, ...) \
|
||||
static TypedValue nm(TypedValue* base, TypedValue key, MInstrState* mis) { \
|
||||
return incDecElemImpl<__VA_ARGS__>(base, key, mis); \
|
||||
}
|
||||
#define INCDEC_OP(op) HELPER_TABLE(INCDEC, op)
|
||||
INCDEC_OPS
|
||||
#undef INCDEC_OP
|
||||
#undef INCDEC
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitIncDecElem() {
|
||||
SPUNT(__func__);
|
||||
IncDecOp op = IncDecOp(m_ni.imm[0].u_OA);
|
||||
SSATmp* key = getInput(m_iInd);
|
||||
typedef TypedValue (*OpFunc)(TypedValue*, TypedValue, MInstrState*);
|
||||
# define INCDEC_OP(op) HELPER_TABLE(FILL_ROW, op)
|
||||
BUILD_OPTAB_ARG(INCDEC_OPS, getKeyTypeIS(key), key->isBoxed(), op);
|
||||
# undef INCDEC_OP
|
||||
m_ht.spillStack();
|
||||
m_result = genStk(IncDecElem, cns((TCA)opFunc), m_base, key, genMisPtr());
|
||||
}
|
||||
#undef HELPER_TABLE
|
||||
|
||||
template <KeyType keyType, bool unboxKey>
|
||||
static inline void bindElemImpl(TypedValue* base, TypedValue keyVal,
|
||||
RefData* val, MInstrState* mis) {
|
||||
TypedValue* key = keyPtr<keyType>(keyVal);
|
||||
key = unbox<keyType, unboxKey>(key);
|
||||
base = VM::ElemD<false, true, keyType>(mis->tvScratch, mis->tvRef, base, key);
|
||||
if (!(base == &mis->tvScratch && base->m_type == KindOfUninit)) {
|
||||
tvBindRef(val, base);
|
||||
}
|
||||
}
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitBindElem() {
|
||||
SPUNT(__func__);
|
||||
#define HELPER_TABLE(m) \
|
||||
/* name keyType unboxKey */ \
|
||||
m(bindElemC, AnyKey, false) \
|
||||
m(bindElemI, IntKey, false) \
|
||||
m(bindElemL, AnyKey, true) \
|
||||
m(bindElemLI, IntKey, true) \
|
||||
m(bindElemLS, StrKey, true) \
|
||||
m(bindElemS, StrKey, false)
|
||||
|
||||
#define ELEM(nm, ...) \
|
||||
static void nm(TypedValue* base, TypedValue key, RefData* val, \
|
||||
MInstrState* mis) { \
|
||||
bindElemImpl<__VA_ARGS__>(base, key, val, mis); \
|
||||
}
|
||||
HELPER_TABLE(ELEM)
|
||||
#undef ELEM
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitBindElem() {
|
||||
SSATmp* key = getInput(m_iInd);
|
||||
SSATmp* box = getValue();
|
||||
typedef void (*OpFunc)(TypedValue*, TypedValue, RefData*, MInstrState*);
|
||||
BUILD_OPTAB(getKeyTypeIS(key), key->isBoxed());
|
||||
m_ht.spillStack();
|
||||
genStk(BindElem, cns((TCA)opFunc), m_base, key, box, genMisPtr());
|
||||
m_result = box;
|
||||
}
|
||||
#undef HELPER_TABLE
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitUnsetElem() {
|
||||
SPUNT(__func__);
|
||||
@@ -1386,9 +1740,7 @@ void HhbcTranslator::VectorTranslator::emitVGetNewElem() {
|
||||
}
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitSetNewElem() {
|
||||
const int kValIdx = 0;
|
||||
SSATmp* value = getInput(kValIdx);
|
||||
|
||||
SSATmp* value = getValue();
|
||||
m_ht.spillStack();
|
||||
SSATmp* result = m_tb.gen(SetNewElem, ptr(m_base), value);
|
||||
VectorEffects ve(result->getInstruction());
|
||||
@@ -1404,17 +1756,21 @@ void HhbcTranslator::VectorTranslator::emitIncDecNewElem() {
|
||||
}
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitBindNewElem() {
|
||||
SPUNT(__func__);
|
||||
SSATmp* box = getValue();
|
||||
m_ht.spillStack();
|
||||
genStk(BindNewElem, m_base, box, genMisPtr());
|
||||
m_result = box;
|
||||
}
|
||||
|
||||
void HhbcTranslator::VectorTranslator::emitMPost() {
|
||||
// Decref stack inputs. If we have an rhs (valCount() == 1), don't
|
||||
// decref it since it's also our output. There are cases where the
|
||||
// rhs stack cell gets clobbered to be null, but the helper that
|
||||
// does that decrefs the existing value so we still don't have to do
|
||||
// anything here.
|
||||
unsigned nStack = m_mii.valCount();
|
||||
for (unsigned i = m_mii.valCount(); i < m_ni.inputs.size(); ++i) {
|
||||
// Decref stack inputs. If we're translating a SetM or BindM, then input 0 is
|
||||
// both our input and output so leave its refcount alone. The helpers for
|
||||
// SetM can overwrite this value with InitNull if the operation fails, but
|
||||
// they also decref the old value so it's still safe to leave its refcount
|
||||
// alone.
|
||||
unsigned nStack =
|
||||
(m_ni.mInstrOp() == OpSetM || m_ni.mInstrOp() == OpBindM) ? 1 : 0;
|
||||
for (unsigned i = nStack; i < m_ni.inputs.size(); ++i) {
|
||||
const DynLocation& input = *m_ni.inputs[i];
|
||||
switch (input.location.space) {
|
||||
case Location::Stack: {
|
||||
|
||||
@@ -39,6 +39,13 @@ HOT_FUNC_VM TypedValue setNewElem(TypedValue* base, Cell val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
void bindNewElemIR(TypedValue* base, RefData* val, MInstrState* mis) {
|
||||
base = NewElem(mis->tvScratch, mis->tvRef, base);
|
||||
if (!(base == &mis->tvScratch && base->m_type == KindOfUninit)) {
|
||||
tvBindRef(val, base);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Kill this #2031980
|
||||
HOT_FUNC_VM RefData* box_value(TypedValue tv) {
|
||||
return tvBoxHelper(tv.m_type, tv.m_data.num);
|
||||
|
||||
@@ -55,6 +55,7 @@ ArrayData* addElemIntKeyHelper(ArrayData* ad, int64_t key, TypedValue val);
|
||||
ArrayData* addElemStringKeyHelper(ArrayData* ad, StringData* key,
|
||||
TypedValue val);
|
||||
TypedValue setNewElem(TypedValue* base, Cell val);
|
||||
void bindNewElemIR(TypedValue* base, RefData* val, MInstrState* mis);
|
||||
RefData* box_value(TypedValue tv);
|
||||
void raisePropertyOnNonObject();
|
||||
void raiseUndefProp(ObjectData* base, const StringData* name);
|
||||
|
||||
@@ -1713,10 +1713,7 @@ static inline void setOpElemImpl(TypedValue* base, TypedValue* key, Cell* val,
|
||||
TypedValue* result = SetOpElem<keyType>(mis->tvScratch, mis->tvRef, op, base,
|
||||
key, val);
|
||||
if (setResult) {
|
||||
if (result->m_type == KindOfRef) {
|
||||
tvUnbox(result);
|
||||
}
|
||||
tvDup(result, tvRes); // tvRes may or may not be &mis->tvResult.
|
||||
tvReadCell(result, tvRes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1804,10 +1801,7 @@ static inline void setOpPropImpl(Class* ctx, TypedValue* base, TypedValue* key,
|
||||
TypedValue* result = SetOpProp<isObj, keyType>(mis->tvScratch, mis->tvRef,
|
||||
ctx, op, base, key, val);
|
||||
if (setResult) {
|
||||
if (result->m_type == KindOfRef) {
|
||||
tvUnbox(result);
|
||||
}
|
||||
tvDup(result, tvRes); // tvRes may or may not be &mis->tvResult.
|
||||
tvReadCell(result, tvRes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -150,6 +150,10 @@ DynLocation* Tracelet::newDynLocation() {
|
||||
return dl;
|
||||
}
|
||||
|
||||
void Tracelet::print() const {
|
||||
print(std::cerr);
|
||||
}
|
||||
|
||||
void Tracelet::print(std::ostream& out) const {
|
||||
const NormalizedInstruction* i = m_instrStream.first;
|
||||
if (i == nullptr) {
|
||||
|
||||
@@ -677,7 +677,10 @@ struct Tracelet : private boost::noncopyable {
|
||||
DynLocation* newDynLocation(Location l, RuntimeType t);
|
||||
DynLocation* newDynLocation();
|
||||
|
||||
void print(std::ostream& out = std::cerr) const;
|
||||
/* These aren't merged into a single method with a default argument
|
||||
* to make gdb happy. */
|
||||
void print() const;
|
||||
void print(std::ostream& out) const;
|
||||
};
|
||||
|
||||
struct TraceletContext {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
function properties() {
|
||||
$c = new stdclass;
|
||||
$a = 5;
|
||||
$c->foo =& $a;
|
||||
var_dump($c);
|
||||
$x = $c->foo += 10;
|
||||
var_dump($c);
|
||||
var_dump($a);
|
||||
|
||||
$x = --$c->foo;
|
||||
var_dump($c);
|
||||
var_dump($a);
|
||||
}
|
||||
|
||||
function elements() {
|
||||
$a = array();
|
||||
$v = 'a string';
|
||||
$a['ref'] =& $v;
|
||||
var_dump($a);
|
||||
$x = $a['ref'] .= ' tail';
|
||||
var_dump($a);
|
||||
var_dump($v);
|
||||
|
||||
$x = $a['ref']++;
|
||||
var_dump($a);
|
||||
var_dump($v);
|
||||
}
|
||||
properties();
|
||||
elements();
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
object(stdClass)#1 (1) {
|
||||
["foo"]=>
|
||||
&int(5)
|
||||
}
|
||||
object(stdClass)#1 (1) {
|
||||
["foo"]=>
|
||||
&int(15)
|
||||
}
|
||||
int(15)
|
||||
object(stdClass)#1 (1) {
|
||||
["foo"]=>
|
||||
&int(14)
|
||||
}
|
||||
int(14)
|
||||
array(1) {
|
||||
["ref"]=>
|
||||
&string(8) "a string"
|
||||
}
|
||||
array(1) {
|
||||
["ref"]=>
|
||||
&string(13) "a string tail"
|
||||
}
|
||||
string(13) "a string tail"
|
||||
array(1) {
|
||||
["ref"]=>
|
||||
&string(13) "a string taim"
|
||||
}
|
||||
string(13) "a string taim"
|
||||
@@ -51,6 +51,11 @@
|
||||
} while (true)
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
T bad_value() {
|
||||
not_reached();
|
||||
}
|
||||
|
||||
#define NOT_REACHED not_reached
|
||||
|
||||
#define not_implemented() do { \
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário