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) {