From da37a0ae8f74873fe0b0741173c865c7a31a82a2 Mon Sep 17 00:00:00 2001 From: ottoni Date: Tue, 9 Apr 2013 15:06:39 -0700 Subject: [PATCH] interpOne instructions that fail the HHBC->HHIR translation This diff adds full support for doing general interpOnes in HHIR. Whenever a bytecode instruction fails (punts) in the HHBC->HHIR translation, the NormalizedInstruction is marked with an 'interp' flag and the HHIR translation for the whole tracelet is retried. This forces an InterpOne of such instruction, preventing HHIR from attempting to translate it again, which then allows the HHBC->HHIR translation to make further progress. If another instruction in the sequence punts again, the process is repeat and so on. --- hphp/doc/ir.specification | 18 +++-- hphp/runtime/vm/hhbc.cpp | 20 +++++ hphp/runtime/vm/hhbc.h | 1 + hphp/runtime/vm/translator/hopt/codegen.cpp | 61 ++++++++------- .../vm/translator/hopt/hhbctranslator.cpp | 71 ++++++++++++------ .../vm/translator/hopt/hhbctranslator.h | 8 +- hphp/runtime/vm/translator/hopt/ir.h | 18 ++++- .../vm/translator/hopt/irtranslator.cpp | 74 ++++++++++++++++++- .../vm/translator/hopt/tracebuilder.cpp | 33 ++++++--- .../runtime/vm/translator/hopt/tracebuilder.h | 7 +- hphp/runtime/vm/translator/translator-x64.cpp | 36 ++++----- hphp/runtime/vm/translator/translator-x64.h | 10 ++- hphp/runtime/vm/translator/translator.h | 7 ++ 13 files changed, 262 insertions(+), 102 deletions(-) diff --git a/hphp/doc/ir.specification b/hphp/doc/ir.specification index 76df56e8f..ef10ac5e5 100644 --- a/hphp/doc/ir.specification +++ b/hphp/doc/ir.specification @@ -1163,14 +1163,20 @@ D:Int LdSwitchObjIndex S0:Obj S1:Int S2:Int in the range [S1:S1 + S2), and if so return the value S1 - (Int)S0. Else, they return the target of the default target, S2 + 1. -D:StkPtr InterpOne S0:StkPtr S1:StkPtr S2:ConstInt S3:ConstInt - S4:ConstInt [ -> L ] +D:StkPtr InterpOne S0:StkPtr S1:StkPtr S2:ConstInt S3:ConstInt Call the interpreter implementation function for one opcode. S0 and - S1 are the VM frame and stack pointers, respectively. S2 is the bytecode - offset. S3 is the number of cells to adjust the stack pointer before - running the interpreter function. S4 is the VM DataType of the return - type for the opcode. Returns the updated VM stack pointer. + S1 are, respectively, the VM frame and stack pointers before this + instruction. S2 is the bytecode offset. S3 is the stack adjustment + performed by this instruction: number of cells popped minus number + of cells pushed. This instruction returns the updated VM stack + pointer. + +InterpOneCF S0:StkPtr S1:StkPtr S2:ConstInt S3:ConstInt + + Call the interpreter implementation function for one control-flow + opcode. S0 and S1 are, respectively, the VM frame and stack + pointers before this instruction. S2 is the bytecode offset. 15. Register allocation diff --git a/hphp/runtime/vm/hhbc.cpp b/hphp/runtime/vm/hhbc.cpp index 577550aa6..3101bd3e1 100644 --- a/hphp/runtime/vm/hhbc.cpp +++ b/hphp/runtime/vm/hhbc.cpp @@ -486,6 +486,26 @@ StackTransInfo instrStackTransInfo(const Opcode* opcode) { } } +bool pushesActRec(Opcode opcode) { + switch (opcode) { + case OpFPushFunc: + case OpFPushFuncD: + case OpFPushObjMethod: + case OpFPushObjMethodD: + case OpFPushClsMethod: + case OpFPushClsMethodF: + case OpFPushClsMethodD: + case OpFPushCtor: + case OpFPushCtorD: + case OpFPushCuf: + case OpFPushCufF: + case OpFPushCufSafe: + return true; + default: + return false; + } +} + static void staticArrayStreamer(ArrayData* ad, std::stringstream& out) { out << "array("; if (!ad->empty()) { diff --git a/hphp/runtime/vm/hhbc.h b/hphp/runtime/vm/hhbc.h index 7c2c5b196..d9fa0fb5e 100644 --- a/hphp/runtime/vm/hhbc.h +++ b/hphp/runtime/vm/hhbc.h @@ -716,6 +716,7 @@ static inline bool isTypePred(const Opcode op) { int instrLen(const Opcode* opcode); InstrFlags instrFlags(Opcode opcode); int numSuccs(const Opcode* opcode); +bool pushesActRec(Opcode opcode); // The returned struct has normalized variable-sized immediates ArgUnion getImm(const Opcode* opcode, int idx); diff --git a/hphp/runtime/vm/translator/hopt/codegen.cpp b/hphp/runtime/vm/translator/hopt/codegen.cpp index 949298c1b..99a845662 100644 --- a/hphp/runtime/vm/translator/hopt/codegen.cpp +++ b/hphp/runtime/vm/translator/hopt/codegen.cpp @@ -4749,47 +4749,46 @@ void CodeGenerator::cgInterpOne(IRInstruction* inst) { SSATmp* pcOffTmp = inst->getSrc(2); SSATmp* spAdjustmentTmp = inst->getSrc(3); Type resultType = inst->getTypeParam(); - Block* label = inst->getTaken(); - - assert(pcOffTmp->isConst()); - assert(spAdjustmentTmp->isConst()); - assert(fp->getType() == Type::StkPtr); - assert(sp->getType() == Type::StkPtr); - int64_t pcOff = pcOffTmp->getValInt(); - void* interpOneHelper = - interpOneEntryPoints[*(getCurFunc()->unit()->at(pcOff))]; + auto opc = *(getCurFunc()->unit()->at(pcOff)); + void* interpOneHelper = interpOneEntryPoints[opc]; auto dstReg = InvalidReg; - if (label) { - dstReg = rScratch; - } cgCallHelper(m_as, (TCA)interpOneHelper, dstReg, kSyncPoint, ArgGroup().ssa(fp).ssa(sp).imm(pcOff)); - if (label) { - // compare the pc in the returned execution context with the - // bytecode offset of the label - Trace* targetTrace = label->getTrace(); - assert(targetTrace); - uint32_t targetBcOff = targetTrace->getBcOff(); - // compare the pc with the target bc offset - m_as.cmp_imm64_disp_reg64(targetBcOff, - offsetof(VMExecutionContext, m_pc), - dstReg); -// emitFwdJcc(CC_E, label); - } + auto newSpReg = inst->getDst()->getReg(); - DEBUG_ONLY auto spReg = sp->getReg(); - int64_t spAdjustment = spAdjustmentTmp->getValInt(); - int64_t adjustment = - (spAdjustment - (resultType == Type::None ? 0 : 1)) * sizeof(Cell); - assert(newSpReg == spReg); - if (adjustment != 0) { - m_as.add_imm32_reg64(adjustment, newSpReg); + assert(newSpReg == sp->getReg()); + + int64_t spAdjustBytes = cellsToBytes(spAdjustmentTmp->getValInt()); + if (spAdjustBytes != 0) { + m_as.addq(spAdjustBytes, newSpReg); } } +void CodeGenerator::cgInterpOneCF(IRInstruction* inst) { + SSATmp* fp = inst->getSrc(0); + SSATmp* sp = inst->getSrc(1); + int64_t pcOff = inst->getSrc(2)->getValInt(); + + auto opc = *(getCurFunc()->unit()->at(pcOff)); + void* interpOneHelper = interpOneEntryPoints[opc]; + + auto dstReg = InvalidReg; + cgCallHelper(m_as, (TCA)interpOneHelper, dstReg, kSyncPoint, + ArgGroup().ssa(fp).ssa(sp).imm(pcOff)); + + // The interpOne method returns a pointer to the current ExecutionContext + // in rax. Use it read the 'm_fp' and 'm_stack.m_top' fields into the + // rVmFp and rVmSp registers. + m_as.loadq(rax[offsetof(VMExecutionContext, m_fp)], rVmFp); + m_as.loadq(rax[offsetof(VMExecutionContext, m_stack) + + Stack::topOfStackOffset()], rVmSp); + + m_tx64->emitServiceReq(TranslatorX64::SRFlags::SREmitInA, REQ_RESUME, 0ull); +} + void CodeGenerator::cgDefFunc(IRInstruction* inst) { SSATmp* dst = inst->getDst(); SSATmp* func = inst->getSrc(0); diff --git a/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp b/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp index 302cd299c..655c1f35c 100644 --- a/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp +++ b/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp @@ -111,7 +111,7 @@ SSATmp* HhbcTranslator::pop(Type type) { void HhbcTranslator::discard(unsigned n) { for (unsigned i = 0; i < n; ++i) { - pop(Type::Gen); + pop(Type::Gen | Type::Cls); } } @@ -324,7 +324,7 @@ void HhbcTranslator::emitAddNewElemC() { } void HhbcTranslator::emitNewCol(int type, int numElems) { - emitInterpOneOrPunt(Type::Obj); + emitInterpOneOrPunt(Type::Obj, 0); } void HhbcTranslator::emitColAddElemC() { @@ -336,7 +336,7 @@ void HhbcTranslator::emitColAddNewElemC() { } void HhbcTranslator::emitCns(uint32_t id) { - emitInterpOneOrPunt(Type::Cell); + emitInterpOneOrPunt(Type::Cell, 0); } void HhbcTranslator::emitDefCns(uint32_t id) { @@ -355,19 +355,19 @@ void HhbcTranslator::emitConcat() { void HhbcTranslator::emitDefCls(int cid, Offset after) { // m_tb->genDefCls(lookupPreClassId(cid), getCurUnit()->at(after)); - emitInterpOneOrPunt(Type::None); + emitInterpOneOrPunt(Type::None, 0); } void HhbcTranslator::emitDefFunc(int fid) { // m_tb->genDefFunc(lookupFuncId(fid)); - emitInterpOneOrPunt(Type::None); + emitInterpOneOrPunt(Type::None, 0); } void HhbcTranslator::emitLateBoundCls() { Class* clss = getCurClass(); if (!clss) { // no static context class, so this will raise an error - emitInterpOne(Type::Cls); + emitInterpOne(Type::Cls, 0); return; } push(m_tb->gen(LdClsCtx, m_tb->genLdCtx(getCurFunc()))); @@ -376,7 +376,7 @@ void HhbcTranslator::emitLateBoundCls() { void HhbcTranslator::emitSelf() { Class* clss = getCurClass(); if (clss == NULL) { - emitInterpOne(Type::Cls); + emitInterpOne(Type::Cls, 0); } else { push(m_tb->genDefConst(clss)); } @@ -385,7 +385,7 @@ void HhbcTranslator::emitSelf() { void HhbcTranslator::emitParent() { const Class* clss = getCurClass()->parent(); if (clss == NULL || clss->parent() == NULL) { - emitInterpOne(Type::Cls); + emitInterpOne(Type::Cls, 0); } else { push(m_tb->genDefConst(clss->parent())); } @@ -853,8 +853,7 @@ void HhbcTranslator::emitContStopped() { } void HhbcTranslator::emitContHandle() { - // No reason to punt, translator-x64 does emitInterpOne as well - emitInterpOne(Type::None, 1); + emitInterpOneCF(1); } void HhbcTranslator::emitStrlen() { @@ -1826,7 +1825,16 @@ void HhbcTranslator::checkTypeLocal(uint32_t locId, Type type) { } void HhbcTranslator::assertTypeLocal(uint32_t localIndex, Type type) { - m_tb->genAssertLoc(localIndex, type); + m_tb->genAssertLoc(localIndex, type, false); +} + +void HhbcTranslator::overrideTypeLocal(uint32_t localIndex, Type type) { + // if changing the inner type of a boxed local, also drop the + // information about inner types for any other boxed local + if (type.isBoxed()) { + m_tb->dropLocalRefsInnerTypes(); + } + m_tb->genAssertLoc(localIndex, type, true); } Trace* HhbcTranslator::guardTypeStack(uint32_t stackIndex, @@ -1967,7 +1975,7 @@ void HhbcTranslator::emitVerifyParamType(int32_t paramId) { * For now we just interp that case. */ if (!locType.isObj()) { - emitInterpOneOrPunt(Type::None); + emitInterpOneOrPunt(Type::None, 0); return; } @@ -2544,26 +2552,45 @@ void HhbcTranslator::emitXor() { m_tb->genDecRef(btr); } -void HhbcTranslator::emitInterpOne(Type type, - int numDiscard, /* = 0 */ - Trace* target /* = NULL */) { +/** + * Emit InterpOne instruction. + * - 'type' is the return type of the value the instruction pushes on + * the stack if any (or Type:None if none) + * - 'numPopped' is the number of cells that this instruction pops + * - 'numExtraPushed' is the number of cells this instruction pushes on + * the stack, in addition to the cell corresponding to 'type' + */ +void HhbcTranslator::emitInterpOne(Type type, int numPopped, + int numExtraPushed) { spillStack(); - // discard the top elements of the stack - discard(numDiscard); - m_tb->genInterpOne(m_bcOff, m_stackDeficit, type, target); + // discard the top elements of the stack, which are consumed by this instr + discard(numPopped); + assert(numPopped == m_stackDeficit); + int numPushed = (type == Type::None ? 0 : 1) + numExtraPushed; + m_tb->genInterpOne(m_bcOff, numPopped - numPushed, type); m_stackDeficit = 0; } -void HhbcTranslator::emitInterpOneOrPunt(Type type, - int numDiscard, /* = 0 */ - Trace* target /* = NULL */) { +void HhbcTranslator::emitInterpOneCF(int numPopped) { + spillStack(); + // discard the top elements of the stack, which are consumed by this instr + discard(numPopped); + assert(numPopped == m_stackDeficit); + m_tb->gen(InterpOneCF, m_tb->getFp(), m_tb->getSp(), + m_tb->genDefConst(m_bcOff)); + m_stackDeficit = 0; + m_hasExit = true; +} + +void HhbcTranslator::emitInterpOneOrPunt(Type type, int numPopped, + int numExtraPushed) { if (RuntimeOption::EvalIRPuntDontInterp) { Op op = *(Op*)(getCurUnit()->entry() + m_bcOff); const char* name = StringData::GetStaticString( std::string("PuntDontInterp-") + opcodeToName(op))->data(); SPUNT(name); } else { - emitInterpOne(type, numDiscard, target); + emitInterpOne(type, numPopped, numExtraPushed); } } diff --git a/hphp/runtime/vm/translator/hopt/hhbctranslator.h b/hphp/runtime/vm/translator/hopt/hhbctranslator.h index 58a697146..4219580d2 100644 --- a/hphp/runtime/vm/translator/hopt/hhbctranslator.h +++ b/hphp/runtime/vm/translator/hopt/hhbctranslator.h @@ -352,8 +352,11 @@ struct HhbcTranslator { void assertTypeStack(uint32_t stackIndex, Type type); void checkTypeLocal(uint32_t localIndex, Type type); void checkTypeTopOfStack(Type type, Offset nextByteCode); + void overrideTypeLocal(uint32_t localIndex, Type type); void setThisAvailable(); void emitLoadDeps(); + void emitInterpOne(Type type, int numPopped, int numExtraPushed = 0); + void emitInterpOneCF(int numPopped); private: /* @@ -534,10 +537,7 @@ private: Trace* getExitSlowTrace(); Trace* getGuardExit(); SSATmp* emitLdLocWarn(uint32_t id, Trace* target); - void emitInterpOne(Type type, int numDiscard = 0, Trace* target = nullptr); - void emitInterpOneOrPunt(Type type, - int numDiscard = 0, - Trace* target = nullptr); + void emitInterpOneOrPunt(Type type, int numPopped, int numExtraPushed = 0); void emitBinaryArith(Opcode); template SSATmp* emitIterInitCommon(int offset, Lambda genFunc); diff --git a/hphp/runtime/vm/translator/hopt/ir.h b/hphp/runtime/vm/translator/hopt/ir.h index 08e9b0385..2c821c42d 100644 --- a/hphp/runtime/vm/translator/hopt/ir.h +++ b/hphp/runtime/vm/translator/hopt/ir.h @@ -389,7 +389,10 @@ O(ArrayAdd, D(Arr), SUnk, N|Mem|CRc|PRc) \ O(DefCls, ND, SUnk, C|E|N) \ O(DefFunc, ND, SUnk, C|E|N|Er) \ O(AKExists, D(Bool), S(Cell) S(Cell), C|N) \ -O(InterpOne, D(StkPtr), SUnk, E|N|Mem|Refs|Er) \ +O(InterpOne, D(StkPtr), S(StkPtr) S(StkPtr) \ + C(Int) C(Int), E|N|Mem|Refs|Er) \ +O(InterpOneCF, ND, S(StkPtr) S(StkPtr) \ + C(Int), T|E|N|Mem|Refs|Er) \ O(Spill, DofS(0), SUnk, Mem) \ O(Reload, DofS(0), SUnk, Mem) \ O(AllocSpill, ND, C(Int), E|Mem) \ @@ -405,7 +408,7 @@ O(FillContLocals, ND, S(StkPtr) \ S(Obj), E|N|Mem) \ O(FillContThis, ND, S(Obj) \ S(PtrToCell) C(Int), E|Mem) \ -O(ContEnter, ND, S(StkPtr) \ +O(ContEnter, ND, S(StkPtr) \ S(TCA) C(Int) S(StkPtr), E|Mem) \ O(UnlinkContVarEnv, ND, S(StkPtr), E|N|Mem) \ O(LinkContVarEnv, ND, S(StkPtr), E|N|Mem) \ @@ -1391,6 +1394,17 @@ public: static Type fromRuntimeType(const Transl::RuntimeType& rtt) { return fromDataType(rtt.outerType(), rtt.innerType()); } + + static Type fromDynLocation(const Transl::DynLocation* dynLoc) { + if (!dynLoc) { + return JIT::Type::None; + } + DataType dt = dynLoc->rtt.outerType(); + if (dt == KindOfUnknown) { + return JIT::Type::Gen; + } + return JIT::Type::fromDataType(dt, dynLoc->rtt.innerType()); + } }; // class Type static_assert(sizeof(Type) <= sizeof(uint64_t), diff --git a/hphp/runtime/vm/translator/hopt/irtranslator.cpp b/hphp/runtime/vm/translator/hopt/irtranslator.cpp index b324a1539..bc182731a 100644 --- a/hphp/runtime/vm/translator/hopt/irtranslator.cpp +++ b/hphp/runtime/vm/translator/hopt/irtranslator.cpp @@ -1389,9 +1389,49 @@ TranslatorX64::irPassPredictedAndInferredTypes(const NormalizedInstruction& i) { } } -void -TranslatorX64::irTranslateInstr(const Tracelet& t, - const NormalizedInstruction& i) { +/** + * Returns the number of cells that instruction i pops from the stack. + */ +static int getNumPopped(const NormalizedInstruction& i) { + return -getStackDelta(i) + // getStackDelta includes the output left on the stack, so discount it + + (i.outStack ? 1 : 0) + // getStackDelta includes ActRec cells pushed on the stack, so discount them + + (pushesActRec(i.op()) ? kNumActRecCells : 0); +} + +/** + * Returns the number of Act-Rec cells that instruction i pushes onto the stack. + */ +static int getNumARCellsPushed(const NormalizedInstruction& i) { + return pushesActRec(i.op()) ? kNumActRecCells : 0; +} + +void TranslatorX64::irInterpretInstr(const NormalizedInstruction& i) { + JIT::Type outStkType = JIT::Type::fromDynLocation(i.outStack); + int poppedCells = getNumPopped(i); + int arPushedCells = getNumARCellsPushed(i); + + FTRACE(5, "HHIR: BC Instr {} Popped = {} ARCellsPushed = {}\n", + i.toString(), poppedCells, arPushedCells); + + if (i.changesPC) { + m_hhbcTrans->emitInterpOneCF(poppedCells); + } else { + m_hhbcTrans->emitInterpOne(outStkType, poppedCells, arPushedCells); + if (i.outLocal) { + // HHIR tracks local values and types, so we should inform it about + // the new local type. This is done via an overriding type assertion. + assert(i.outLocal->isLocal()); + int32_t locId = i.outLocal->location.offset; + JIT::Type newType = JIT::Type::fromRuntimeType(i.outLocal->rtt); + m_hhbcTrans->overrideTypeLocal(locId, newType); + } + } +} + +void TranslatorX64::irTranslateInstr(const Tracelet& t, + const NormalizedInstruction& i) { assert(m_useHHIR); assert(!i.outStack || i.outStack->isStack()); assert(!i.outLocal || i.outLocal->isLocal()); @@ -1423,7 +1463,12 @@ TranslatorX64::irTranslateInstr(const Tracelet& t, m_hhbcTrans->emitIncStat(Stats::opcodeToIRPostStatCounter(i.op()), 1, true); } - irTranslateInstrWork(t, i); + + if (i.interp) { + irInterpretInstr(i); + } else { + irTranslateInstrWork(t, i); + } irPassPredictedAndInferredTypes(i); } @@ -1474,6 +1519,19 @@ void TranslatorX64::irEmitResolvedDeps(const ChangeMap& resolvedDeps) { } } +static bool supportedInterpOne(const NormalizedInstruction* i) { + switch (i->op()) { + // Instructions that do function return are not supported yet + case OpRetC: + case OpRetV: + case OpContRetC: + case OpNativeImpl: + return false; + default: + return true; + } +} + TranslatorX64::TranslateTraceletResult TranslatorX64::irTranslateTracelet(Tracelet& t, const TCA start, @@ -1508,6 +1566,14 @@ TranslatorX64::irTranslateTracelet(Tracelet& t, m_curNI = ni; irTranslateInstr(t, *ni); } catch (JIT::FailedIRGen& fcg) { + // If we haven't tried interpreting ni yet, flag it to be interpreted + // and retry + if (RuntimeOption::EvalHHIRDisableTx64 && !ni->interp && + supportedInterpOne(ni)) { + ni->interp = true; + transResult = Retry; + break; + } if (!RuntimeOption::EvalHHIRDisableTx64 || !ni->prev) { // Let tx64 handle the entire tracelet. throw; diff --git a/hphp/runtime/vm/translator/hopt/tracebuilder.cpp b/hphp/runtime/vm/translator/hopt/tracebuilder.cpp index 23767b457..30d39e451 100644 --- a/hphp/runtime/vm/translator/hopt/tracebuilder.cpp +++ b/hphp/runtime/vm/translator/hopt/tracebuilder.cpp @@ -461,8 +461,14 @@ void TraceBuilder::genGuardLoc(uint32_t id, Type type, Trace* exitTrace) { } } -void TraceBuilder::genAssertLoc(uint32_t id, Type type) { - Type prevType = getLocalType(id); +/** + * Generates an AssertLoc instruction for the given local 'id' and 'type'. + * If the 'override' flag is not set, then 'type' must be a subtype of the + * previous tracked type for this local. + */ +void TraceBuilder::genAssertLoc(uint32_t id, Type type, + bool overrideType /* =false */) { + Type prevType = overrideType ? Type::None : getLocalType(id); if (prevType == Type::None || type.strictSubtypeOf(prevType)) { LocalId local(id); gen(AssertLoc, type, &local, m_fpValue); @@ -871,15 +877,13 @@ static SSATmp* getStackValue(SSATmp* sp, case InterpOne: { // sp = InterpOne(fp, sp, bcOff, stackAdjustment, resultType) SSATmp* prevSp = inst->getSrc(1); - int64_t numPopped = inst->getSrc(3)->getValInt(); + int64_t spAdjustment = inst->getSrc(3)->getValInt(); // # popped - # pushed Type resultType = inst->getTypeParam(); - int64_t numPushed = resultType == Type::None ? 0 : 1; - if (index == 0 && numPushed == 1) { + if (index == 0 && resultType != Type::None) { type = resultType; return nullptr; } - return getStackValue(prevSp, index - (numPushed - numPopped), - spansCall, type); + return getStackValue(prevSp, index + spAdjustment, spansCall, type); } case NewObj: @@ -973,11 +977,9 @@ void TraceBuilder::genNativeImpl() { SSATmp* TraceBuilder::genInterpOne(uint32_t pcOff, uint32_t stackAdjustment, - Type resultType, - Trace* target) { + Type resultType) { return gen(InterpOne, resultType, - getFirstBlock(target), m_fpValue, m_spValue, genDefConst(pcOff), @@ -1701,6 +1703,17 @@ void TraceBuilder::updateLocalRefValues(SSATmp* oldRef, SSATmp* newRef) { } } +/** + * This method changes any boxed local into a BoxedCell type. + */ +void TraceBuilder::dropLocalRefsInnerTypes() { + for (size_t id = 0; id < m_localTypes.size(); id++) { + if (m_localTypes[id].isBoxed()) { + m_localTypes[id] = Type::BoxedCell; + } + } +} + /** * Called to clear out the tracked local values at a call site. * Calls kill all registers, so we don't want to keep locals in diff --git a/hphp/runtime/vm/translator/hopt/tracebuilder.h b/hphp/runtime/vm/translator/hopt/tracebuilder.h index e06c8075a..8ace4a0f5 100644 --- a/hphp/runtime/vm/translator/hopt/tracebuilder.h +++ b/hphp/runtime/vm/translator/hopt/tracebuilder.h @@ -52,6 +52,7 @@ public: void setThisAvailable() { m_thisIsAvailable = true; } + void dropLocalRefsInnerTypes(); // Run one more pass of simplification on this builder's trace. void optimizeTrace(); @@ -153,7 +154,9 @@ public: SSATmp* mask64, SSATmp* vals64, Trace* exitTrace); - void genAssertLoc(uint32_t id, Type type); + void genAssertLoc(uint32_t id, + Type type, + bool override = false); // ignores conflict w/ prev type SSATmp* genUnboxPtr(SSATmp* ptr); SSATmp* genLdRef(SSATmp* ref, Type type, Trace* exit); @@ -242,7 +245,7 @@ public: SSATmp* genIterFree(uint32_t iterId); SSATmp* genInterpOne(uint32_t pcOff, uint32_t stackAdjustment, - Type resultType, Trace* target); + Type resultType); Trace* getExitSlowTrace(uint32_t bcOff, int32_t stackDeficit, uint32_t numOpnds, diff --git a/hphp/runtime/vm/translator/translator-x64.cpp b/hphp/runtime/vm/translator/translator-x64.cpp index f639fd175..f04defa7c 100644 --- a/hphp/runtime/vm/translator/translator-x64.cpp +++ b/hphp/runtime/vm/translator/translator-x64.cpp @@ -3561,10 +3561,12 @@ class ConditionalCodeCursor { TCA TranslatorX64::emitServiceReqVA(SRFlags flags, ServiceRequest req, int numArgs, va_list args) { - bool align = bool(flags & SRFlags::SRAlign); - bool inLine = bool(flags & SRFlags::SRInline); - TCA start = getFreeStub(inLine); - ConditionalCodeCursor cg(astubs, start); + bool emitInA = bool(flags & SRFlags::SREmitInA); + bool align = bool(flags & SRFlags::SRAlign) && !emitInA; + bool inLine = bool(flags & SRFlags::SRInline); + Asm& as = emitInA ? a : astubs; + TCA start = emitInA ? a.code.frontier : getFreeStub(inLine); + ConditionalCodeCursor cg(as, start); /* max space for moving to align, saving VM regs plus emitting args */ static const int kVMRegSpace = 0x14; static const int kMovSize = 0xa; @@ -3573,10 +3575,10 @@ TranslatorX64::emitServiceReqVA(SRFlags flags, ServiceRequest req, int numArgs, + kVMRegSpace + kNumServiceRegs * kMovSize; if (align) { - moveToAlign(astubs); + moveToAlign(as); } - TCA retval = astubs.code.frontier; - emitEagerVMRegSave(astubs, SaveFP); + TCA retval = as.code.frontier; + emitEagerVMRegSave(as, SaveFP); /* * Move args into appropriate regs. */ @@ -3584,20 +3586,20 @@ TranslatorX64::emitServiceReqVA(SRFlags flags, ServiceRequest req, int numArgs, for (int i = 0; i < numArgs; i++) { uint64_t argVal = va_arg(args, uint64_t); TRACE(3, "%p,", (void*)argVal); - emitImmReg(astubs, argVal, serviceReqArgRegs[i]); + emitImmReg(as, argVal, serviceReqArgRegs[i]); } if (!inLine) { /* make sure that the stub has enough space that it can be * reused for other service requests, with different number of * arguments, alignment, etc. */ - astubs. emitNop(start + kMaxStubSpace - astubs.code.frontier); - emitImmReg(astubs, (uint64_t)start, rScratch); + as.emitNop(start + kMaxStubSpace - as.code.frontier); + emitImmReg(as, (uint64_t)start, rScratch); } else { - emitImmReg(astubs, 0, rScratch); + emitImmReg(as, 0, rScratch); } TRACE(3, ")\n"); - emitImmReg(astubs, req, rdi); + emitImmReg(as, req, rdi); /* * Weird hand-shaking with enterTC: reverse-call a service routine. * @@ -3607,13 +3609,13 @@ TranslatorX64::emitServiceReqVA(SRFlags flags, ServiceRequest req, int numArgs, * SRJmpInsteadOfRet indicates to fake the return. */ if (flags & SRFlags::SRJmpInsteadOfRet) { - astubs. pop (rax); - astubs. jmp (rax); + as.pop(rax); + as.jmp(rax); } else { - astubs. ret(); + as.ret(); } - recordBCInstr(OpServiceRequest, astubs, retval); - translator_not_reached(astubs); + recordBCInstr(OpServiceRequest, as, retval); + translator_not_reached(as); return retval; } diff --git a/hphp/runtime/vm/translator/translator-x64.h b/hphp/runtime/vm/translator/translator-x64.h index f127ecac7..df4665ff6 100644 --- a/hphp/runtime/vm/translator/translator-x64.h +++ b/hphp/runtime/vm/translator/translator-x64.h @@ -813,6 +813,7 @@ private: TCA getInterceptHelper(); void translateInstr(const Tracelet& t, const NormalizedInstruction& i); void translateInstrWork(const Tracelet& t, const NormalizedInstruction& i); + void irInterpretInstr(const NormalizedInstruction& i); void irTranslateInstr(const Tracelet& t, const NormalizedInstruction& i); void irTranslateInstrWork(const Tracelet& t, const NormalizedInstruction& i); void irTranslateInstrDefault(const Tracelet& t, @@ -926,10 +927,11 @@ private: SrcRec& fail); enum SRFlags { - SRNone = 0, - SRAlign = 1, - SRInline = 2, - SRJmpInsteadOfRet = 4 + SRNone = 0, + SRAlign = 1, + SRInline = 2, + SRJmpInsteadOfRet = 4, + SREmitInA = 8, }; TCA emitServiceReq(ServiceRequest, int numArgs, ...); TCA emitServiceReq(SRFlags flags, ServiceRequest, int numArgs, ...); diff --git a/hphp/runtime/vm/translator/translator.h b/hphp/runtime/vm/translator/translator.h index c16ca49e5..3347fc969 100644 --- a/hphp/runtime/vm/translator/translator.h +++ b/hphp/runtime/vm/translator/translator.h @@ -360,6 +360,12 @@ class NormalizedInstruction { */ bool noOp:1; + /* + * Used with HHIR. Instruction shoud be interpreted, because previous attempt + * to translate it has failed. + */ + bool interp:1; + /* * This is an FPush* that will be directly bound to a Func* */ @@ -407,6 +413,7 @@ class NormalizedInstruction { noSurprise(false), noCtor(false), noOp(false), + interp(false), directCall(false), inlineReturn(false), m_txFlags(Interp) {