Remove genAssertStk/genCastStk and move their optimizations to Simplifier
The elimination of these is stateless, so it's going to simplifier (along with getStackValue). Other stack functions are still here until I move EvalStack to tracebuilder.
Esse commit está contido em:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<T> 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;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
}}}
|
||||
|
||||
@@ -117,6 +117,8 @@ private:
|
||||
SSATmp* simplifyCondJmp(IRInstruction*);
|
||||
SSATmp* simplifyQueryJmp(IRInstruction*);
|
||||
SSATmp* simplifyExitOnVarEnv(IRInstruction*);
|
||||
SSATmp* simplifyCastStk(IRInstruction*);
|
||||
SSATmp* simplifyAssertStk(IRInstruction*);
|
||||
|
||||
private: // tracebuilder forwarders
|
||||
template<class... Args> 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.
|
||||
|
||||
@@ -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<T> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário