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:
bsimmers
2013-03-20 16:44:56 -07:00
commit de Sara Golemon
commit 7219dec255
22 arquivos alterados com 761 adições e 116 exclusões
+90 -8
Ver Arquivo
@@ -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.
+7
Ver Arquivo
@@ -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();
+24 -5
Ver Arquivo
@@ -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);
+1 -4
Ver Arquivo
@@ -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);
}
+5 -3
Ver Arquivo
@@ -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);
+14 -2
Ver Arquivo
@@ -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);
+4
Ver Arquivo
@@ -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
+70 -4
Ver Arquivo
@@ -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);
}
+2 -4
Ver Arquivo
@@ -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);
}
}
+4
Ver Arquivo
@@ -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) {
+4 -1
Ver Arquivo
@@ -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 {
+33
Ver Arquivo
@@ -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();
+28
Ver Arquivo
@@ -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"
+5
Ver Arquivo
@@ -51,6 +51,11 @@
} while (true)
#endif
template<typename T>
T bad_value() {
not_reached();
}
#define NOT_REACHED not_reached
#define not_implemented() do { \