diff --git a/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp b/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp index a11225d4f..fe9be5990 100644 --- a/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp +++ b/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp @@ -1825,8 +1825,12 @@ void HhbcTranslator::emitFCallBuiltin(uint32_t numArgs, case KindOfArray: case KindOfObject: case KindOfString: - m_tb->genCastStk(numArgs - i - 1, - Type::fromDataType(pi.builtinType(), KindOfInvalid)); + gen( + CastStk, + Type::fromDataType(pi.builtinType(), KindOfInvalid), + m_tb->getSp(), + cns(numArgs - i - 1) + ); break; case KindOfDouble: not_reached(); case KindOfUnknown: break; @@ -2185,7 +2189,7 @@ void HhbcTranslator::checkTypeTopOfStack(Type type, void HhbcTranslator::assertTypeStack(uint32_t stackIndex, Type type) { SSATmp* tmp = m_evalStack.top(stackIndex); if (!tmp) { - m_tb->genAssertStk(stackIndex, type); + gen(AssertStk, type, m_tb->getSp(), cns(stackIndex)); return; } diff --git a/hphp/runtime/vm/translator/hopt/hhbctranslator.h b/hphp/runtime/vm/translator/hopt/hhbctranslator.h index c25aea256..00a049964 100644 --- a/hphp/runtime/vm/translator/hopt/hhbctranslator.h +++ b/hphp/runtime/vm/translator/hopt/hhbctranslator.h @@ -583,7 +583,7 @@ private: * Eval stack helpers */ SSATmp* push(SSATmp* tmp); - SSATmp* pushIncRef(SSATmp* tmp) { return push(m_tb->gen(IncRef, tmp)); } + SSATmp* pushIncRef(SSATmp* tmp) { return push(gen(IncRef, tmp)); } SSATmp* pop(Type type); void popDecRef(Type type); void discard(unsigned n); diff --git a/hphp/runtime/vm/translator/hopt/simplifier.cpp b/hphp/runtime/vm/translator/hopt/simplifier.cpp index 977c8fa7d..92051b0ff 100644 --- a/hphp/runtime/vm/translator/hopt/simplifier.cpp +++ b/hphp/runtime/vm/translator/hopt/simplifier.cpp @@ -29,6 +29,152 @@ namespace JIT { TRACE_SET_MOD(hhir); +////////////////////////////////////////////////////////////////////// + +SSATmp* getStackValue(SSATmp* sp, + uint32_t index, + bool& spansCall, + Type& type) { + IRInstruction* inst = sp->inst(); + switch (inst->op()) { + case DefSP: + return nullptr; + + case ReDefGeneratorSP: { + auto srcInst = inst->getSrc(0)->inst(); + assert(srcInst->op() == StashGeneratorSP); + return getStackValue(srcInst->getSrc(0), index, spansCall, type); + } + case ReDefSP: + return getStackValue(inst->getSrc(1), index, spansCall, type); + + case ExceptionBarrier: + return getStackValue(inst->getSrc(0), index, spansCall, type); + + case AssertStk: + // fallthrough + case CastStk: + // fallthrough + case GuardStk: { + // sp = GuardStk sp, offset + // We don't have a value, but we may know the type due to guarding + // on it. + if (inst->getSrc(1)->getValInt() == index) { + type = inst->getTypeParam(); + return nullptr; + } + return getStackValue(inst->getSrc(0), + index, + spansCall, + type); + } + + case Call: + // sp = call(actrec, bcoffset, func, args...) + if (index == 0) { + // return value from call + return nullptr; + } + spansCall = true; + // search recursively on the actrec argument + return getStackValue(inst->getSrc(0), // sp = actrec argument to call + index - + (1 /* pushed */ - kNumActRecCells /* popped */), + spansCall, + type); + + case SpillStack: { + int64_t numPushed = 0; + int32_t numSpillSrcs = inst->getNumSrcs() - 2; + + for (int i = 0; i < numSpillSrcs; ++i) { + SSATmp* tmp = inst->getSrc(i + 2); + if (index == numPushed) { + if (tmp->inst()->op() == IncRef) { + tmp = tmp->inst()->getSrc(0); + } + type = tmp->type(); + if (!type.equals(Type::None)) { + return tmp; + } + } + ++numPushed; + } + + // This is not one of the values pushed onto the stack by this + // spillstack instruction, so continue searching. + SSATmp* prevSp = inst->getSrc(0); + int64_t numPopped = inst->getSrc(1)->getValInt(); + return getStackValue(prevSp, + // pop values pushed by spillstack + index - (numPushed - numPopped), + spansCall, + type); + } + + case InterpOne: { + SSATmp* prevSp = inst->getSrc(1); + int64_t spAdjustment = inst->getSrc(3)->getValInt(); // # popped - # pushed + Type resultType = inst->getTypeParam(); + if (index == 0 && resultType != Type::None) { + type = resultType; + return nullptr; + } + return getStackValue(prevSp, index + spAdjustment, spansCall, type); + } + + case SpillFrame: + return getStackValue(inst->getSrc(0), + // pushes an ActRec + index - kNumActRecCells, + spansCall, + type); + + case NewObj: + case NewObjCached: + if (index == kNumActRecCells) { + // newly allocated object, which we unfortunately don't have any + // kind of handle to :-( + type = Type::Obj; + return nullptr; + } + + return getStackValue(sp->inst()->getSrc(2), + // NewObj pushes an object and an ActRec + index - (1 + kNumActRecCells), + spansCall, + type); + + case NewObjNoCtorCached: + if (index == 0) { + type = Type::Obj; + return nullptr; + } + + return getStackValue(sp->inst()->getSrc(1), + index - 1, + spansCall, + type); + + default: { + SSATmp* value; + if (VectorEffects::getStackValue(sp->inst(), index, + value, type)) { + return value; + } else { + // If VectorEffects::getStackValue failed, it returns the next + // sp to search in value. + return getStackValue(value, index, spansCall, type); + } + } + } + + // Should not get here! + not_reached(); +} + +////////////////////////////////////////////////////////////////////// + static void copyPropSrc(IRInstruction* inst, int index) { auto tmp = inst->getSrc(index); auto srcInst = tmp->inst(); @@ -170,14 +316,14 @@ SSATmp* Simplifier::simplify(IRInstruction* inst) { case LdCls: return simplifyLdCls(inst); case LdThis: return simplifyLdThis(inst); - case LdCtx: return simplifyLdCtx(inst); case LdClsCtx: return simplifyLdClsCtx(inst); case GetCtxFwdCall:return simplifyGetCtxFwdCall(inst); case SpillStack: return simplifySpillStack(inst); case Call: return simplifyCall(inst); - + case CastStk: return simplifyCastStk(inst); + case AssertStk: return simplifyAssertStk(inst); case ExitOnVarEnv: return simplifyExitOnVarEnv(inst); default: @@ -1435,4 +1581,38 @@ SSATmp* Simplifier::simplifyCondJmp(IRInstruction* inst) { return nullptr; } -}}} // namespace HPHP::VM::JIT +SSATmp* Simplifier::simplifyCastStk(IRInstruction* inst) { + bool spansCall = false; + Type knownType = Type::None; + getStackValue(inst->getSrc(0), + inst->getSrc(1)->getValInt(), + spansCall, + knownType); + if (knownType.subtypeOf(inst->getTypeParam())) { + // No need to cast---the type was as good or better. + inst->convertToNop(); + } + return nullptr; +} + +SSATmp* Simplifier::simplifyAssertStk(IRInstruction* inst) { + Type knownType = Type::None; + bool spansCall = false; + UNUSED SSATmp* tmp = getStackValue(inst->getSrc(0), + inst->getSrc(1)->getValInt(), + spansCall, + knownType); + + // AssertStk indicated that we knew the type from static analysis, + // so this assert just double checks. + if (tmp) assert(tmp->isA(inst->getTypeParam())); + + if (knownType.subtypeOf(inst->getTypeParam())) { + inst->convertToNop(); + } + return nullptr; +} + +////////////////////////////////////////////////////////////////////// + +}}} diff --git a/hphp/runtime/vm/translator/hopt/simplifier.h b/hphp/runtime/vm/translator/hopt/simplifier.h index fdfcbafc5..760cb6445 100644 --- a/hphp/runtime/vm/translator/hopt/simplifier.h +++ b/hphp/runtime/vm/translator/hopt/simplifier.h @@ -117,6 +117,8 @@ private: SSATmp* simplifyCondJmp(IRInstruction*); SSATmp* simplifyQueryJmp(IRInstruction*); SSATmp* simplifyExitOnVarEnv(IRInstruction*); + SSATmp* simplifyCastStk(IRInstruction*); + SSATmp* simplifyAssertStk(IRInstruction*); private: // tracebuilder forwarders template SSATmp* cns(Args&&...); @@ -128,6 +130,20 @@ private: ////////////////////////////////////////////////////////////////////// +/* + * Track down a value using the StkPtr chain. + * + * The spansCall parameter tracks whether the returned value's + * lifetime on the stack spans a call. This search bottoms out on + * hitting either the initial DefSP instruction (failure), or some + * instruction that produced a view of the stack with the requested + * value. + */ +SSATmp* getStackValue(SSATmp* stack, + uint32_t index, + bool& spansCall, + Type& type); + /* * Propagate very simple copies on the given instruction. * Specifically, Movs, and also IncRefs of non-refcounted types. diff --git a/hphp/runtime/vm/translator/hopt/tracebuilder.cpp b/hphp/runtime/vm/translator/hopt/tracebuilder.cpp index 1651c23e0..952bd7edf 100644 --- a/hphp/runtime/vm/translator/hopt/tracebuilder.cpp +++ b/hphp/runtime/vm/translator/hopt/tracebuilder.cpp @@ -437,186 +437,6 @@ SSATmp* TraceBuilder::genStLoc(uint32_t id, return retVal; } -/* - * Track down a value that was previously spilled onto the stack - * The spansCall parameter tracks whether the returned value's - * lifetime on the stack spans a call. This search bottoms out - * on hitting either a DefSP instruction (failure), a SpillStack - * instruction that has the spilled location, or a call that returns - * the value. - */ -static SSATmp* getStackValue(SSATmp* sp, - uint32_t index, - bool& spansCall, - Type& type) { - IRInstruction* inst = sp->inst(); - switch (inst->op()) { - case DefSP: - return nullptr; - - case ReDefGeneratorSP: { - auto srcInst = inst->getSrc(0)->inst(); - assert(srcInst->op() == StashGeneratorSP); - return getStackValue(srcInst->getSrc(0), index, spansCall, type); - } - case ReDefSP: - return getStackValue(inst->getSrc(1), index, spansCall, type); - - case ExceptionBarrier: - return getStackValue(inst->getSrc(0), index, spansCall, type); - - case AssertStk: - // fallthrough - case CastStk: - // fallthrough - case GuardStk: { - // sp = GuardStk sp, offset - // We don't have a value, but we may know the type due to guarding - // on it. - if (inst->getSrc(1)->getValInt() == index) { - type = inst->getTypeParam(); - return nullptr; - } - return getStackValue(inst->getSrc(0), - index, - spansCall, - type); - } - - case Call: - // sp = call(actrec, bcoffset, func, args...) - if (index == 0) { - // return value from call - return nullptr; - } - spansCall = true; - // search recursively on the actrec argument - return getStackValue(inst->getSrc(0), // sp = actrec argument to call - index - - (1 /* pushed */ - kNumActRecCells /* popped */), - spansCall, - type); - - case SpillStack: { - int64_t numPushed = 0; - int32_t numSpillSrcs = inst->getNumSrcs() - 2; - - for (int i = 0; i < numSpillSrcs; ++i) { - SSATmp* tmp = inst->getSrc(i + 2); - if (index == numPushed) { - if (tmp->inst()->op() == IncRef) { - tmp = tmp->inst()->getSrc(0); - } - type = tmp->type(); - if (!type.equals(Type::None)) { - return tmp; - } - } - ++numPushed; - } - - // This is not one of the values pushed onto the stack by this - // spillstack instruction, so continue searching. - SSATmp* prevSp = inst->getSrc(0); - int64_t numPopped = inst->getSrc(1)->getValInt(); - return getStackValue(prevSp, - // pop values pushed by spillstack - index - (numPushed - numPopped), - spansCall, - type); - } - - case InterpOne: { - SSATmp* prevSp = inst->getSrc(1); - int64_t spAdjustment = inst->getSrc(3)->getValInt(); // # popped - # pushed - Type resultType = inst->getTypeParam(); - if (index == 0 && resultType != Type::None) { - type = resultType; - return nullptr; - } - return getStackValue(prevSp, index + spAdjustment, spansCall, type); - } - - case SpillFrame: - return getStackValue(inst->getSrc(0), - // pushes an ActRec - index - kNumActRecCells, - spansCall, - type); - - case NewObj: - case NewObjCached: - if (index == kNumActRecCells) { - // newly allocated object, which we unfortunately don't have any - // kind of handle to :-( - type = Type::Obj; - return nullptr; - } - - return getStackValue(sp->inst()->getSrc(2), - // NewObj pushes an object and an ActRec - index - (1 + kNumActRecCells), - spansCall, - type); - - case NewObjNoCtorCached: - if (index == 0) { - type = Type::Obj; - return nullptr; - } - - return getStackValue(sp->inst()->getSrc(1), - index - 1, - spansCall, - type); - - default: { - SSATmp* value; - if (VectorEffects::getStackValue(sp->inst(), index, - value, type)) { - return value; - } else { - // If VectorEffects::getStackValue failed, it returns the next - // sp to search in value. - return getStackValue(value, index, spansCall, type); - } - } - } - - // Should not get here! - not_reached(); -} - -void TraceBuilder::genAssertStk(uint32_t id, Type type) { - Type knownType = Type::None; - bool spansCall = false; - UNUSED SSATmp* tmp = getStackValue(m_spValue, id, spansCall, knownType); - - // We may have found a value if there was an inlined call. - // AssertStk indicated that we knew the type from static analysis, - // so let's double check. - if (tmp) { - assert(tmp->isA(type)); - } - - if (knownType == Type::None || type.strictSubtypeOf(knownType)) { - gen(AssertStk, type, m_spValue, cns(id)); - } -} - -SSATmp* TraceBuilder::genCastStk(uint32_t id, Type type) { - bool spansCall = false; - Type knownType = Type::None; - getStackValue(m_spValue, id, spansCall, knownType); - if (knownType.subtypeOf(Type::None) || !knownType.subtypeOf(type)) { - SSATmp* off = cns(id); - gen(CastStk, m_spValue, off); - IRInstruction* inst = m_spValue->inst(); - inst->setTypeParam(type); - } - return m_spValue; -} - SSATmp* TraceBuilder::genLdStackAddr(SSATmp* sp, int64_t index) { Type type; bool spansCall; @@ -1118,11 +938,10 @@ SSATmp* TraceBuilder::preOptimizeAssertLoc(IRInstruction* inst) { auto const prevType = getLocalType(locId); auto const typeParam = inst->getTypeParam(); - if (prevType != Type::None && !typeParam.strictSubtypeOf(prevType)) { + if (!prevType.equals(Type::None) && !typeParam.strictSubtypeOf(prevType)) { assert(prevType.subtypeOf(typeParam)); inst->convertToNop(); } - return nullptr; } diff --git a/hphp/runtime/vm/translator/hopt/tracebuilder.h b/hphp/runtime/vm/translator/hopt/tracebuilder.h index 3f1130c2c..67d835beb 100644 --- a/hphp/runtime/vm/translator/hopt/tracebuilder.h +++ b/hphp/runtime/vm/translator/hopt/tracebuilder.h @@ -186,7 +186,6 @@ struct TraceBuilder { ////////////////////////////////////////////////////////////////////// // stack - void genAssertStk(uint32_t id, Type type); SSATmp* genSpillStack(uint32_t stackAdjustment, uint32_t numOpnds, SSATmp** opnds); @@ -233,7 +232,6 @@ struct TraceBuilder { // TODO(#2058865): we should have a real not opcode SSATmp* genNot(SSATmp* src); - SSATmp* genCastStk(uint32_t id, Type type); SSATmp* genConvToBool(SSATmp* src); //////////////////////////////////////////////////////////////////////