diff --git a/hphp/doc/ir.specification b/hphp/doc/ir.specification index 20bd44642..1fea0d607 100644 --- a/hphp/doc/ir.specification +++ b/hphp/doc/ir.specification @@ -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. diff --git a/hphp/runtime/base/ref_data.h b/hphp/runtime/base/ref_data.h index 83abe75ad..3fa219740 100644 --- a/hphp/runtime/base/ref_data.h +++ b/hphp/runtime/base/ref_data.h @@ -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(); diff --git a/hphp/runtime/base/tv_helpers.h b/hphp/runtime/base/tv_helpers.h index d3f4439db..a4f13f369 100644 --- a/hphp/runtime/base/tv_helpers.h +++ b/hphp/runtime/base/tv_helpers.h @@ -34,9 +34,12 @@ class Stack; namespace HPHP { /////////////////////////////////////////////////////////////////////////////// -inline TypedValue tv(DataType type, intptr_t data) { +template +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); diff --git a/hphp/runtime/vm/bytecode.cpp b/hphp/runtime/vm/bytecode.cpp index 30a0b18b7..8b2cd681a 100644 --- a/hphp/runtime/vm/bytecode.cpp +++ b/hphp/runtime/vm/bytecode.cpp @@ -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); } diff --git a/hphp/runtime/vm/instance.cpp b/hphp/runtime/vm/instance.cpp index eb8351eff..d0e45276c 100644 --- a/hphp/runtime/vm/instance.cpp +++ b/hphp/runtime/vm/instance.cpp @@ -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); diff --git a/hphp/runtime/vm/translator/hopt/codegen.cpp b/hphp/runtime/vm/translator/hopt/codegen.cpp index 487156c22..92a4ec0c7 100644 --- a/hphp/runtime/vm/translator/hopt/codegen.cpp +++ b/hphp/runtime/vm/translator/hopt/codegen.cpp @@ -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); diff --git a/hphp/runtime/vm/translator/hopt/hhbctranslator.h b/hphp/runtime/vm/translator/hopt/hhbctranslator.h index f3fbec8f7..d25017aa0 100644 --- a/hphp/runtime/vm/translator/hopt/hhbctranslator.h +++ b/hphp/runtime/vm/translator/hopt/hhbctranslator.h @@ -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); diff --git a/hphp/runtime/vm/translator/hopt/ir.cpp b/hphp/runtime/vm/translator/hopt/ir.cpp index 5dd3e1931..491b70c1b 100644 --- a/hphp/runtime/vm/translator/hopt/ir.cpp +++ b/hphp/runtime/vm/translator/hopt/ir.cpp @@ -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 diff --git a/hphp/runtime/vm/translator/hopt/ir.h b/hphp/runtime/vm/translator/hopt/ir.h index 687bb3278..8b712c920 100644 --- a/hphp/runtime/vm/translator/hopt/ir.h +++ b/hphp/runtime/vm/translator/hopt/ir.h @@ -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; diff --git a/hphp/runtime/vm/translator/hopt/irtranslator.cpp b/hphp/runtime/vm/translator/hopt/irtranslator.cpp index c7f495425..f176d5ab3 100644 --- a/hphp/runtime/vm/translator/hopt/irtranslator.cpp +++ b/hphp/runtime/vm/translator/hopt/irtranslator.cpp @@ -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; } } diff --git a/hphp/runtime/vm/translator/hopt/nativecalls.cpp b/hphp/runtime/vm/translator/hopt/nativecalls.cpp index 188811828..bd4ad6071 100644 --- a/hphp/runtime/vm/translator/hopt/nativecalls.cpp +++ b/hphp/runtime/vm/translator/hopt/nativecalls.cpp @@ -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, diff --git a/hphp/runtime/vm/translator/hopt/tracebuilder.cpp b/hphp/runtime/vm/translator/hopt/tracebuilder.cpp index 3ff2ab6b3..edc7ff265 100644 --- a/hphp/runtime/vm/translator/hopt/tracebuilder.cpp +++ b/hphp/runtime/vm/translator/hopt/tracebuilder.cpp @@ -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); } diff --git a/hphp/runtime/vm/translator/hopt/type.cpp b/hphp/runtime/vm/translator/hopt/type.cpp index 6530c7ffc..dc0cf5040 100644 --- a/hphp/runtime/vm/translator/hopt/type.cpp +++ b/hphp/runtime/vm/translator/hopt/type.cpp @@ -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); diff --git a/hphp/runtime/vm/translator/hopt/vectortranslator.cpp b/hphp/runtime/vm/translator/hopt/vectortranslator.cpp index 737a0a3e2..eab8851fa 100644 --- a/hphp/runtime/vm/translator/hopt/vectortranslator.cpp +++ b/hphp/runtime/vm/translator/hopt/vectortranslator.cpp @@ -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(); 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(); } // 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(); } // 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(); } } @@ -1026,16 +1014,110 @@ void HhbcTranslator::VectorTranslator::emitCGetProp() { } #undef HELPER_TABLE -void HhbcTranslator::VectorTranslator::emitVGetProp() { - SPUNT(__func__); +template +static inline TypedValue vGetPropImpl(Class* ctx, TypedValue* base, + TypedValue keyVal, MInstrState* mis) { + TypedValue* key = keyPtr(keyVal); + key = unbox(key); + base = VM::Prop( + 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 +static inline bool issetEmptyPropImpl(Class* ctx, TypedValue* base, + TypedValue keyVal) { + TypedValue* key = keyPtr(keyVal); + key = unbox(key); + return VM::IssetEmptyProp(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 @@ -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 +static inline TypedValue setOpPropImpl(TypedValue* base, TypedValue keyVal, + Cell val, MInstrState* mis) { + TypedValue* key = keyPtr(keyVal); + key = unbox(key); + TypedValue* result = VM::SetOpProp( + 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 +static inline TypedValue incDecPropImpl(Class* ctx, TypedValue* base, + TypedValue keyVal, MInstrState* mis) { + TypedValue* key = keyPtr(keyVal); + key = unbox(key); + TypedValue result; + result.m_type = KindOfUninit; + VM::IncDecProp( + 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 +static inline void bindPropImpl(Class* ctx, TypedValue* base, TypedValue keyVal, + RefData* val, MInstrState* mis) { + TypedValue* key = keyPtr(keyVal); + key = unbox(key); + TypedValue* prop = VM::Prop( + 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 +static inline TypedValue vGetElemImpl(TypedValue* base, TypedValue keyVal, + MInstrState* mis) { + TypedValue* key = keyPtr(keyVal); + key = unbox(key); + base = VM::ElemD(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 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 +static inline TypedValue setOpElemImpl(TypedValue* base, TypedValue keyVal, + Cell val, MInstrState* mis) { + TypedValue* key = keyPtr(keyVal); + key = unbox(key); + TypedValue* result = + VM::SetOpElem(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 +static inline TypedValue incDecElemImpl(TypedValue* base, TypedValue keyVal, + MInstrState* mis) { + TypedValue* key = keyPtr(keyVal); + key = unbox(key); + TypedValue result; + VM::IncDecElem( + 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 +static inline void bindElemImpl(TypedValue* base, TypedValue keyVal, + RefData* val, MInstrState* mis) { + TypedValue* key = keyPtr(keyVal); + key = unbox(key); + base = VM::ElemD(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: { diff --git a/hphp/runtime/vm/translator/translator-runtime.cpp b/hphp/runtime/vm/translator/translator-runtime.cpp index 6f3ec6f89..798917eb3 100644 --- a/hphp/runtime/vm/translator/translator-runtime.cpp +++ b/hphp/runtime/vm/translator/translator-runtime.cpp @@ -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); diff --git a/hphp/runtime/vm/translator/translator-runtime.h b/hphp/runtime/vm/translator/translator-runtime.h index f0cf63f5c..4de397652 100644 --- a/hphp/runtime/vm/translator/translator-runtime.h +++ b/hphp/runtime/vm/translator/translator-runtime.h @@ -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); diff --git a/hphp/runtime/vm/translator/translator-x64-vector.cpp b/hphp/runtime/vm/translator/translator-x64-vector.cpp index 18dd43b42..1a5ff0817 100644 --- a/hphp/runtime/vm/translator/translator-x64-vector.cpp +++ b/hphp/runtime/vm/translator/translator-x64-vector.cpp @@ -1713,10 +1713,7 @@ static inline void setOpElemImpl(TypedValue* base, TypedValue* key, Cell* val, TypedValue* result = SetOpElem(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(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); } } diff --git a/hphp/runtime/vm/translator/translator.cpp b/hphp/runtime/vm/translator/translator.cpp index e6aa2f34d..4c25460be 100644 --- a/hphp/runtime/vm/translator/translator.cpp +++ b/hphp/runtime/vm/translator/translator.cpp @@ -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) { diff --git a/hphp/runtime/vm/translator/translator.h b/hphp/runtime/vm/translator/translator.h index 7f0c8d87d..f11a78952 100644 --- a/hphp/runtime/vm/translator/translator.h +++ b/hphp/runtime/vm/translator/translator.h @@ -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 { diff --git a/hphp/test/vm/setop-incdec-refs.php b/hphp/test/vm/setop-incdec-refs.php new file mode 100644 index 000000000..ba17cd71b --- /dev/null +++ b/hphp/test/vm/setop-incdec-refs.php @@ -0,0 +1,33 @@ +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(); + diff --git a/hphp/test/vm/setop-incdec-refs.php.exp b/hphp/test/vm/setop-incdec-refs.php.exp new file mode 100644 index 000000000..7236a2251 --- /dev/null +++ b/hphp/test/vm/setop-incdec-refs.php.exp @@ -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" diff --git a/hphp/util/assertions.h b/hphp/util/assertions.h index f64b87a57..3bad8539a 100644 --- a/hphp/util/assertions.h +++ b/hphp/util/assertions.h @@ -51,6 +51,11 @@ } while (true) #endif +template +T bad_value() { + not_reached(); +} + #define NOT_REACHED not_reached #define not_implemented() do { \