diff options
| author | Yong He <yonghe@outlook.com> | 2022-02-25 20:49:31 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-02-25 20:49:31 -0800 |
| commit | c31577953d5041c82375c22d847c2eba06106c58 (patch) | |
| tree | bc685a8b63fc13cb85d160ae13df950056ca6e91 /source/slang/slang-ir-sccp.cpp | |
| parent | 8990d270e3a0c01b1f7abbf4f79556c5ef82a096 (diff) | |
Improved SCCP, inlining and resource specialization passes, legalize `ImageSubscript` for GLSL (#2146)
Diffstat (limited to 'source/slang/slang-ir-sccp.cpp')
| -rw-r--r-- | source/slang/slang-ir-sccp.cpp | 729 |
1 files changed, 704 insertions, 25 deletions
diff --git a/source/slang/slang-ir-sccp.cpp b/source/slang/slang-ir-sccp.cpp index 159cf9abc..463de29ca 100644 --- a/source/slang/slang-ir-sccp.cpp +++ b/source/slang/slang-ir-sccp.cpp @@ -109,6 +109,41 @@ struct SCCPContext } }; + static bool isEvaluableOpCode(IROp op) + { + switch (op) + { + case kIROp_IntLit: + case kIROp_BoolLit: + case kIROp_FloatLit: + case kIROp_StringLit: + case kIROp_Add: + case kIROp_Sub: + case kIROp_Mul: + case kIROp_Div: + case kIROp_Neg: + case kIROp_Not: + case kIROp_Eql: + case kIROp_Neq: + case kIROp_Leq: + case kIROp_Geq: + case kIROp_Less: + case kIROp_Greater: + case kIROp_Lsh: + case kIROp_Rsh: + case kIROp_BitAnd: + case kIROp_BitOr: + case kIROp_BitXor: + case kIROp_BitNot: + case kIROp_BitCast: + case kIROp_Construct: + case kIROp_Select: + return true; + default: + return false; + } + } + // If we imagine a variable (actually an SSA phi node...) that // might be assigned lattice value A at one point in the code, // and lattice value B at another point, we need a way to @@ -204,20 +239,29 @@ struct SCCPContext break; } - // We might be asked for the lattice value of an instruction - // not contained in the current function. When that happens, - // we will treat it as having potentially any value, rather - // than the default of none. - // - auto parentBlock = as<IRBlock>(inst->getParent()); - if(!parentBlock || parentBlock->getParent() != code) return LatticeVal::getAny(); - - // Once the special cases are dealt with, we can look up in - // the dictionary and just return the value we get from it, - // or default to the `None` (empty set) case. + // Look up in the dictionary and just return the value we get from it. LatticeVal latticeVal; if(mapInstToLatticeVal.TryGetValue(inst, latticeVal)) return latticeVal; + + // If we can't find the value from dictionary, we want to return None if this is a value + // in the same function as the one we are working with right now. If it is defined + // elsewhere, we return Any. + auto parentBlock = as<IRBlock>(inst->getParent()); + bool isProcessingGlobalScope = (code == nullptr); + if (!parentBlock && isProcessingGlobalScope) + { + // We are folding constant in the global scope, continue registering the inst as Any. + } + else + { + // If we are processing a function and asked for the lattice value of an instruction + // not contained in the current function, we will treat it as having potentially any + // value, rather than the default of none. + // + if(!parentBlock || parentBlock->getParent() != code) return LatticeVal::getAny(); + } + return LatticeVal::getNone(); } @@ -228,6 +272,460 @@ struct SCCPContext IRBuilder builderStorage; IRBuilder* getBuilder() { return &builderStorage; } + // LatticeVal constant evaluation methods. +#define SLANG_SCCP_RETURN_IF_NONE_OR_ANY(v) \ + switch (v.flavor) \ + { \ + case LatticeVal::Flavor::None: \ + return LatticeVal::getNone(); \ + case LatticeVal::Flavor::Any: \ + return LatticeVal::getAny(); \ + default: \ + break; \ + } + + LatticeVal evalConstruct(IRType* type, LatticeVal v0) + { + SLANG_SCCP_RETURN_IF_NONE_OR_ANY(v0) + auto irConstant = as<IRConstant>(v0.value); + IRInst* resultVal = nullptr; + switch (type->getOp()) + { + case kIROp_Int8Type: + case kIROp_Int16Type: + case kIROp_IntType: + case kIROp_Int64Type: + case kIROp_UInt8Type: + case kIROp_UInt16Type: + case kIROp_UIntType: + case kIROp_UInt64Type: + switch (irConstant->getOp()) + { + case kIROp_FloatLit: + resultVal = + getBuilder()->getIntValue(type, (IRIntegerValue)irConstant->value.floatVal); + break; + case kIROp_IntLit: + case kIROp_BoolLit: + { + IRIntegerValue intVal = irConstant->value.intVal; + switch (type->getOp()) + { + case kIROp_Int8Type: + case kIROp_UInt8Type: + intVal = intVal & 0xFF; + break; + case kIROp_Int16Type: + case kIROp_UInt16Type: + intVal = intVal & 0xFFFF; + break; + case kIROp_IntType: + case kIROp_UIntType: + case kIROp_BoolType: + intVal = intVal & 0xFFFFFFFF; + break; + default: + break; + } + resultVal = getBuilder()->getIntValue(type, (IRIntegerValue)intVal); + } + break; + default: + return LatticeVal::getAny(); + } + break; + case kIROp_FloatType: + case kIROp_DoubleType: + case kIROp_HalfType: + switch (irConstant->getOp()) + { + case kIROp_FloatLit: + resultVal = getBuilder()->getFloatValue( + type, (IRFloatingPointValue)irConstant->value.floatVal); + break; + case kIROp_IntLit: + case kIROp_BoolLit: + resultVal = getBuilder()->getFloatValue( + type, (IRFloatingPointValue)irConstant->value.intVal); + break; + default: + return LatticeVal::getAny(); + } + break; + case kIROp_BoolType: + switch (irConstant->getOp()) + { + case kIROp_FloatLit: + resultVal = getBuilder()->getBoolValue(irConstant->value.floatVal != 0); + break; + case kIROp_IntLit: + case kIROp_BoolLit: + { + resultVal = getBuilder()->getBoolValue(irConstant->value.intVal != 0); + } + break; + default: + return LatticeVal::getAny(); + } + } + if (!resultVal) + return LatticeVal::getAny(); + return LatticeVal::getConstant(resultVal); + } + + template<typename TIntFunc, typename TFloatFunc> + LatticeVal evalBinaryImpl( + IRType* type, + LatticeVal v0, + LatticeVal v1, + const TIntFunc& intFunc, + const TFloatFunc& floatFunc) + { + SLANG_SCCP_RETURN_IF_NONE_OR_ANY(v0) + auto c0 = as<IRConstant>(v0.value); + SLANG_SCCP_RETURN_IF_NONE_OR_ANY(v1) + auto c1 = as<IRConstant>(v1.value); + IRInst* resultVal = nullptr; + switch (type->getOp()) + { + case kIROp_Int8Type: + case kIROp_Int16Type: + case kIROp_IntType: + case kIROp_Int64Type: + case kIROp_UInt8Type: + case kIROp_UInt16Type: + case kIROp_UIntType: + case kIROp_UInt64Type: + case kIROp_BoolType: + resultVal = getBuilder()->getIntValue(type, intFunc(c0->value.intVal, c1->value.intVal)); + break; + case kIROp_FloatType: + case kIROp_DoubleType: + case kIROp_HalfType: + resultVal = getBuilder()->getFloatValue(type, floatFunc(c0->value.floatVal, c1->value.floatVal)); + break; + default: + break; + } + if (!resultVal) + return LatticeVal::getAny(); + return LatticeVal::getConstant(resultVal); + } + + template <typename TIntFunc> + LatticeVal evalBinaryIntImpl( + IRType* type, + LatticeVal v0, + LatticeVal v1, + const TIntFunc& intFunc) + { + SLANG_SCCP_RETURN_IF_NONE_OR_ANY(v0) + auto c0 = as<IRConstant>(v0.value); + SLANG_SCCP_RETURN_IF_NONE_OR_ANY(v1) + auto c1 = as<IRConstant>(v1.value); + IRInst* resultVal = nullptr; + switch (type->getOp()) + { + case kIROp_Int8Type: + case kIROp_Int16Type: + case kIROp_IntType: + case kIROp_Int64Type: + case kIROp_UInt8Type: + case kIROp_UInt16Type: + case kIROp_UIntType: + case kIROp_UInt64Type: + case kIROp_BoolType: + resultVal = + getBuilder()->getIntValue(type, intFunc(c0->value.intVal, c1->value.intVal)); + break; + default: + break; + } + if (!resultVal) + return LatticeVal::getAny(); + return LatticeVal::getConstant(resultVal); + } + + template <typename TIntFunc> + LatticeVal evalUnaryIntImpl( + IRType* type, LatticeVal v0, const TIntFunc& intFunc) + { + SLANG_SCCP_RETURN_IF_NONE_OR_ANY(v0) + auto c0 = as<IRConstant>(v0.value); + IRInst* resultVal = nullptr; + switch (type->getOp()) + { + case kIROp_Int8Type: + case kIROp_Int16Type: + case kIROp_IntType: + case kIROp_Int64Type: + case kIROp_UInt8Type: + case kIROp_UInt16Type: + case kIROp_UIntType: + case kIROp_UInt64Type: + case kIROp_BoolType: + resultVal = + getBuilder()->getIntValue(type, intFunc(c0->value.intVal)); + break; + default: + break; + } + if (!resultVal) + return LatticeVal::getAny(); + return LatticeVal::getConstant(resultVal); + } + + template <typename TIntFunc, typename TFloatFunc> + LatticeVal evalComparisonImpl( + IRType* type, + LatticeVal v0, + LatticeVal v1, + const TIntFunc& intFunc, + const TFloatFunc& floatFunc) + { + SLANG_SCCP_RETURN_IF_NONE_OR_ANY(v0) + auto c0 = as<IRConstant>(v0.value); + SLANG_SCCP_RETURN_IF_NONE_OR_ANY(v1) + auto c1 = as<IRConstant>(v1.value); + IRInst* resultVal = nullptr; + switch (type->getOp()) + { + case kIROp_Int8Type: + case kIROp_Int16Type: + case kIROp_IntType: + case kIROp_Int64Type: + case kIROp_UInt8Type: + case kIROp_UInt16Type: + case kIROp_UIntType: + case kIROp_UInt64Type: + case kIROp_BoolType: + resultVal = + getBuilder()->getBoolValue(intFunc(c0->value.intVal, c1->value.intVal)); + break; + case kIROp_FloatType: + case kIROp_DoubleType: + case kIROp_HalfType: + resultVal = + getBuilder()->getBoolValue(floatFunc(c0->value.floatVal, c1->value.floatVal)); + break; + default: + break; + } + if (!resultVal) + return LatticeVal::getAny(); + return LatticeVal::getConstant(resultVal); + } + + LatticeVal evalAdd(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalBinaryImpl( + type, + v0, + v1, + [](IRIntegerValue c0, IRIntegerValue c1) { return c0 + c1; }, + [](IRFloatingPointValue c0, IRFloatingPointValue c1) { return c0 + c1; }); + } + LatticeVal evalSub(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalBinaryImpl( + type, + v0, + v1, + [](IRIntegerValue c0, IRIntegerValue c1) { return c0 - c1; }, + [](IRFloatingPointValue c0, IRFloatingPointValue c1) { return c0 - c1; }); + } + LatticeVal evalMul(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalBinaryImpl( + type, + v0, + v1, + [](IRIntegerValue c0, IRIntegerValue c1) { return c0 * c1; }, + [](IRFloatingPointValue c0, IRFloatingPointValue c1) { return c0 * c1; }); + } + LatticeVal evalDiv(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalBinaryImpl( + type, + v0, + v1, + [](IRIntegerValue c0, IRIntegerValue c1) { return c0 / c1; }, + [](IRFloatingPointValue c0, IRFloatingPointValue c1) { return c0 / c1; }); + } + LatticeVal evalEql(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalComparisonImpl( + type, + v0, + v1, + [](IRIntegerValue c0, IRIntegerValue c1) { return c0 == c1; }, + [](IRFloatingPointValue c0, IRFloatingPointValue c1) { return c0 == c1; }); + } + LatticeVal evalNeq(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalComparisonImpl( + type, + v0, + v1, + [](IRIntegerValue c0, IRIntegerValue c1) { return c0 != c1; }, + [](IRFloatingPointValue c0, IRFloatingPointValue c1) { return c0 != c1; }); + } + LatticeVal evalGeq(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalComparisonImpl( + type, + v0, + v1, + [](IRIntegerValue c0, IRIntegerValue c1) { return c0 >= c1; }, + [](IRFloatingPointValue c0, IRFloatingPointValue c1) { return c0 >= c1; }); + } + LatticeVal evalLeq(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalComparisonImpl( + type, + v0, + v1, + [](IRIntegerValue c0, IRIntegerValue c1) { return c0 <= c1; }, + [](IRFloatingPointValue c0, IRFloatingPointValue c1) { return c0 <= c1; }); + } + LatticeVal evalGreater(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalComparisonImpl( + type, + v0, + v1, + [](IRIntegerValue c0, IRIntegerValue c1) { return c0 > c1; }, + [](IRFloatingPointValue c0, IRFloatingPointValue c1) { return c0 > c1; }); + } + LatticeVal evalLess(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalComparisonImpl( + type, + v0, + v1, + [](IRIntegerValue c0, IRIntegerValue c1) { return c0 < c1; }, + [](IRFloatingPointValue c0, IRFloatingPointValue c1) { return c0 < c1; }); + } + LatticeVal evalAnd(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalBinaryIntImpl( + type, + v0, + v1, + [](IRIntegerValue c0, IRIntegerValue c1) { return c0 != 0 && c1 != 0; }); + } + LatticeVal evalOr(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalBinaryIntImpl( + type, v0, v1, [](IRIntegerValue c0, IRIntegerValue c1) { return c0 != 0 || c1 != 0; }); + } + LatticeVal evalNot(IRType* type, LatticeVal v0) + { + return evalUnaryIntImpl(type, v0, [](IRIntegerValue c0) { return c0 == 0; }); + } + LatticeVal evalBitAnd(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalBinaryIntImpl( + type, v0, v1, [](IRIntegerValue c0, IRIntegerValue c1) { return c0 & c1; }); + } + LatticeVal evalBitOr(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalBinaryIntImpl( + type, v0, v1, [](IRIntegerValue c0, IRIntegerValue c1) { return c0 | c1; }); + } + LatticeVal evalBitNot(IRType* type, LatticeVal v0) + { + return evalUnaryIntImpl(type, v0, [](IRIntegerValue c0) { return ~c0; }); + } + LatticeVal evalBitXor(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalBinaryIntImpl( + type, v0, v1, [](IRIntegerValue c0, IRIntegerValue c1) { return c0 ^ c1; }); + } + LatticeVal evalLsh(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalBinaryIntImpl( + type, v0, v1, [](IRIntegerValue c0, IRIntegerValue c1) { return c0 << c1; }); + } + LatticeVal evalRsh(IRType* type, LatticeVal v0, LatticeVal v1) + { + return evalBinaryIntImpl( + type, v0, v1, [](IRIntegerValue c0, IRIntegerValue c1) { return c0 >> c1; }); + } + LatticeVal evalNeg(IRType* type, LatticeVal v0) + { + SLANG_SCCP_RETURN_IF_NONE_OR_ANY(v0) + auto c0 = as<IRConstant>(v0.value); + IRInst* resultVal = nullptr; + switch (type->getOp()) + { + case kIROp_Int8Type: + case kIROp_Int16Type: + case kIROp_IntType: + case kIROp_Int64Type: + case kIROp_UInt8Type: + case kIROp_UInt16Type: + case kIROp_UIntType: + case kIROp_UInt64Type: + resultVal = getBuilder()->getIntValue(type, -c0->value.intVal); + break; + case kIROp_FloatType: + case kIROp_DoubleType: + case kIROp_HalfType: + resultVal = getBuilder()->getFloatValue(type, -c0->value.floatVal); + break; + default: + break; + } + if (!resultVal) + return LatticeVal::getAny(); + return LatticeVal::getConstant(resultVal); + } + + LatticeVal evalBitCast(IRType* type, LatticeVal v0) + { + SLANG_SCCP_RETURN_IF_NONE_OR_ANY(v0) + auto c0 = as<IRConstant>(v0.value); + IRInst* resultVal = nullptr; + switch (type->getOp()) + { + case kIROp_Int64Type: + case kIROp_UInt64Type: + resultVal = getBuilder()->getIntValue(type, c0->value.intVal); + break; + case kIROp_IntType: + case kIROp_UIntType: + { + float val = (float)c0->value.floatVal; + uint32_t intVal = (uint32_t)FloatAsInt(val); + resultVal = getBuilder()->getIntValue(type, intVal); + } + break; + case kIROp_FloatType: + { + uint32_t val = (uint32_t)c0->value.intVal; + float floatVal = IntAsFloat((int)val); + resultVal = getBuilder()->getFloatValue(type, floatVal); + } + break; + case kIROp_DoubleType: + resultVal = getBuilder()->getFloatValue(type, Int64AsDouble(c0->value.intVal)); + break; + default: + break; + } + if (!resultVal) + return LatticeVal::getAny(); + return LatticeVal::getConstant(resultVal); + } + + LatticeVal evalSelect(LatticeVal v0, LatticeVal v1, LatticeVal v2) + { + SLANG_SCCP_RETURN_IF_NONE_OR_ANY(v0) + auto c0 = as<IRConstant>(v0.value); + return c0->value.intVal != 0 ? v1 : v2; + } + // In order to perform constant folding, we need to be able to // interpret an instruction over the lattice values. // @@ -245,11 +743,17 @@ struct SCCPContext case kIROp_BoolLit: return LatticeVal::getConstant(inst); - // TODO: we might also want to special-case certain + // We might also want to special-case certain // instructions where we shouldn't bother trying to // constant-fold them and should just default to the // `Any` value right away. - + case kIROp_Call: + case kIROp_ByteAddressBufferLoad: + case kIROp_ByteAddressBufferStore: + case kIROp_Alloca: + case kIROp_Store: + case kIROp_Load: + return LatticeVal::getAny(); default: break; } @@ -288,10 +792,116 @@ struct SCCPContext // `None` inputs as producing `Any` to make sure we don't // optimize the code based on non-obvious assumptions. // - // For now we aren't implementing *any* folding logic here, - // for simplicity. This is the right place to add folding - // optimizations if/when we need them. - // + // For now we implement only basic folding operations for + // scalar values. + if (!as<IRBasicType>(inst->getDataType())) + return LatticeVal::getAny(); + + switch (inst->getOp()) + { + case kIROp_Construct: + return evalConstruct(inst->getDataType(), getLatticeVal(inst->getOperand(0))); + case kIROp_Add: + return evalAdd( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_Sub: + return evalSub( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_Mul: + return evalMul( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_Div: + return evalDiv( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_Eql: + return evalEql( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_Neq: + return evalNeq( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_Greater: + return evalGreater( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_Less: + return evalLess( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_Leq: + return evalLeq( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_Geq: + return evalGeq( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_And: + return evalAnd( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_Or: + return evalOr( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_Not: + return evalNot(inst->getDataType(), getLatticeVal(inst->getOperand(0))); + case kIROp_BitAnd: + return evalBitAnd( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_BitOr: + return evalBitOr( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_BitNot: + return evalBitNot(inst->getDataType(), getLatticeVal(inst->getOperand(0))); + case kIROp_BitXor: + return evalBitXor( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_BitCast: + return evalBitCast(inst->getDataType(), getLatticeVal(inst->getOperand(0))); + case kIROp_Neg: + return evalNeg(inst->getDataType(), getLatticeVal(inst->getOperand(0))); + case kIROp_Lsh: + return evalLsh( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_Rsh: + return evalRsh( + inst->getDataType(), + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1))); + case kIROp_Select: + return evalSelect( + getLatticeVal(inst->getOperand(0)), + getLatticeVal(inst->getOperand(1)), + getLatticeVal(inst->getOperand(2))); + default: + break; + } // A safe default is to assume that every instruction not // handled by one of the cases above could produce *any* @@ -567,10 +1177,60 @@ struct SCCPContext } } + // Run the constant folding on global scope only. + bool applyOnGlobalScope(IRModule* module) + { + builderStorage.init(shared->sharedBuilder); + for (auto child : module->getModuleInst()->getChildren()) + { + // Only consider evaluable opcodes. + if (!isEvaluableOpCode(child->getOp())) + continue; + + updateValueForInst(child); + } + while (ssaWorkList.getCount()) + { + auto inst = ssaWorkList[0]; + ssaWorkList.fastRemoveAt(0); + // Only consider evaluable opcodes and insts at global scope. + if (!isEvaluableOpCode(inst->getOp()) || inst->getParent() != module->getModuleInst()) + continue; + updateValueForInst(inst); + } + + bool changed = false; + // Replace the insts with their values. + List<IRInst*> instsToRemove; + for (auto child : module->getModuleInst()->getChildren()) + { + if (!isEvaluableOpCode(child->getOp())) + continue; + + auto latticeVal = getLatticeVal(child); + if (latticeVal.flavor == LatticeVal::Flavor::Constant && latticeVal.value != child) + { + child->replaceUsesWith(latticeVal.value); + instsToRemove.add(child); + } + } + + if (instsToRemove.getCount()) + { + changed = true; + for (auto inst : instsToRemove) + inst->removeAndDeallocate(); + // Rebuild global value map. + builderStorage.getSharedBuilder()->deduplicateAndRebuildGlobalNumberingMap(); + } + return changed; + } + // The `apply()` function will run the full algorithm. // - void apply() + bool apply() { + bool changed = false; // We start with the busy-work of setting up our IR builder. // builderStorage.init(shared->sharedBuilder); @@ -733,6 +1393,9 @@ struct SCCPContext } } + if (instsToRemove.getCount() != 0) + changed = true; + // Once we've replaced the uses of instructions that evaluate // to constants, we make a second pass to remove the instructions // themselves (or at least those without side effects). @@ -786,6 +1449,7 @@ struct SCCPContext builder->setInsertBefore(terminator); builder->emitBranch(target); terminator->removeAndDeallocate(); + changed = true; } } else if(auto condBranchInst = as<IRConditionalBranch>(terminator)) @@ -800,6 +1464,7 @@ struct SCCPContext builder->setInsertBefore(terminator); builder->emitBranch(target); terminator->removeAndDeallocate(); + changed = true; } } @@ -911,38 +1576,52 @@ struct SCCPContext builder->emitUnreachable(); } } + return changed; } }; -static void applySparseConditionalConstantPropagationRec( - SharedSCCPContext* shared, +static bool applySparseConditionalConstantPropagationRec( + const SCCPContext& globalContext, IRInst* inst) { + bool changed = false; if( auto code = as<IRGlobalValueWithCode>(inst) ) { if( code->getFirstBlock() ) { SCCPContext context; - context.shared = shared; + context.shared = globalContext.shared; context.code = code; - context.apply(); + context.mapInstToLatticeVal = globalContext.mapInstToLatticeVal; + changed |= context.apply(); } } for( auto childInst : inst->getDecorationsAndChildren() ) { - applySparseConditionalConstantPropagationRec(shared, childInst); + changed |= applySparseConditionalConstantPropagationRec(globalContext, childInst); } + return changed; } -void applySparseConditionalConstantPropagation( +bool applySparseConditionalConstantPropagation( IRModule* module) { SharedSCCPContext shared; shared.module = module; shared.sharedBuilder.init(module); + shared.sharedBuilder.deduplicateAndRebuildGlobalNumberingMap(); + + // First we fold constants at global scope. + SCCPContext globalContext; + globalContext.shared = &shared; + globalContext.code = nullptr; + bool changed = globalContext.applyOnGlobalScope(module); + + // Now run recursive SCCP passes on each child code block. + changed |= applySparseConditionalConstantPropagationRec(globalContext, module->getModuleInst()); - applySparseConditionalConstantPropagationRec(&shared, module->getModuleInst()); + return changed; } } |
