mirror of https://github.com/llvm/circt.git
[Scheduling] Define modulo scheduling problem. (#1886)
This commit is contained in:
parent
caa1f0be41
commit
bff070fa32
|
@ -287,6 +287,28 @@ protected:
|
|||
virtual LogicalResult verifyOperatorType(OperatorType opr) override;
|
||||
};
|
||||
|
||||
/// This class models the modulo scheduling problem as the composition of the
|
||||
/// cyclic problem and the resource-constrained problem with fully-pipelined
|
||||
/// shared operators.
|
||||
///
|
||||
/// A solution to this problem comprises an integer II and integer start times
|
||||
/// for all registered operations, and is feasible iff:
|
||||
/// (1) The precedence constraints implied by the `CyclicProblem`'s dependence
|
||||
/// edges are satisfied, and
|
||||
/// (2) The number of operations that use a certain limited operator type,
|
||||
/// and start in the same congruence class (= start time *mod* II), does
|
||||
/// not exceed the operator type's limit.
|
||||
class ModuloProblem : public virtual CyclicProblem,
|
||||
public virtual SharedPipelinedOperatorsProblem {
|
||||
public:
|
||||
ModuloProblem(Operation *containingOp)
|
||||
: Problem(containingOp), CyclicProblem(containingOp),
|
||||
SharedPipelinedOperatorsProblem(containingOp) {}
|
||||
|
||||
protected:
|
||||
virtual LogicalResult verifyOperatorType(OperatorType opr) override;
|
||||
};
|
||||
|
||||
} // namespace scheduling
|
||||
} // namespace circt
|
||||
|
||||
|
|
|
@ -213,6 +213,35 @@ SharedPipelinedOperatorsProblem::verifyOperatorType(OperatorType opr) {
|
|||
return success();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ModuloProblem
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
LogicalResult ModuloProblem::verifyOperatorType(OperatorType opr) {
|
||||
// fail early if II is not set or invalid
|
||||
if (!getInitiationInterval() || *getInitiationInterval() == 0)
|
||||
return getContainingOp()->emitError("Invalid initiation interval");
|
||||
|
||||
auto limit = getLimit(opr);
|
||||
if (!limit)
|
||||
return success();
|
||||
|
||||
unsigned ii = *getInitiationInterval();
|
||||
llvm::SmallDenseMap<unsigned, unsigned> nOpsPerCongruenceClass;
|
||||
for (auto *op : getOperations())
|
||||
if (opr == *getLinkedOperatorType(op))
|
||||
++nOpsPerCongruenceClass[*getStartTime(op) % ii];
|
||||
|
||||
for (auto &kv : nOpsPerCongruenceClass)
|
||||
if (kv.second > *limit)
|
||||
return getContainingOp()->emitError()
|
||||
<< "Operator type '" << opr << "' is oversubscribed."
|
||||
<< "\n congruence class: " << kv.first
|
||||
<< "\n #operations: " << kv.second << "\n limit: " << *limit;
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Dependence
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -86,8 +86,6 @@ static void constructProblem(Problem &prob, FuncOp func) {
|
|||
}
|
||||
|
||||
static void constructCyclicProblem(CyclicProblem &prob, FuncOp func) {
|
||||
constructProblem(prob, func);
|
||||
|
||||
// parse auxiliary dependences in the testcase (again), in order to set the
|
||||
// optional distance in the cyclic problem
|
||||
if (auto attr = func->getAttrOfType<ArrayAttr>("auxdeps")) {
|
||||
|
@ -105,8 +103,6 @@ static void constructCyclicProblem(CyclicProblem &prob, FuncOp func) {
|
|||
|
||||
static void constructSPOProblem(SharedPipelinedOperatorsProblem &prob,
|
||||
FuncOp func) {
|
||||
constructProblem(prob, func);
|
||||
|
||||
// parse operator type info (again) to extract optional operator limit
|
||||
if (auto attr = func->getAttrOfType<ArrayAttr>("operatortypes")) {
|
||||
for (auto &elem : parseArrayOfDicts(attr, "limit")) {
|
||||
|
@ -179,6 +175,7 @@ void TestCyclicProblemPass::runOnFunction() {
|
|||
auto func = getFunction();
|
||||
|
||||
CyclicProblem prob(func);
|
||||
constructProblem(prob, func);
|
||||
constructCyclicProblem(prob, func);
|
||||
|
||||
if (failed(prob.check())) {
|
||||
|
@ -221,6 +218,7 @@ void TestSPOProblemPass::runOnFunction() {
|
|||
auto func = getFunction();
|
||||
|
||||
SharedPipelinedOperatorsProblem prob(func);
|
||||
constructProblem(prob, func);
|
||||
constructSPOProblem(prob, func);
|
||||
|
||||
if (failed(prob.check())) {
|
||||
|
@ -239,6 +237,49 @@ void TestSPOProblemPass::runOnFunction() {
|
|||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ModuloProblem
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
struct TestModuloProblemPass
|
||||
: public PassWrapper<TestModuloProblemPass, FunctionPass> {
|
||||
void runOnFunction() override;
|
||||
StringRef getArgument() const override { return "test-modulo-problem"; }
|
||||
StringRef getDescription() const override {
|
||||
return "Import a solution for the modulo problem encoded as attributes";
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void TestModuloProblemPass::runOnFunction() {
|
||||
auto func = getFunction();
|
||||
|
||||
ModuloProblem prob(func);
|
||||
constructProblem(prob, func);
|
||||
constructCyclicProblem(prob, func);
|
||||
constructSPOProblem(prob, func);
|
||||
|
||||
if (failed(prob.check())) {
|
||||
func->emitError("problem check failed");
|
||||
return signalPassFailure();
|
||||
}
|
||||
|
||||
// get II from the test case
|
||||
if (auto attr = func->getAttrOfType<IntegerAttr>("problemInitiationInterval"))
|
||||
prob.setInitiationInterval(attr.getInt());
|
||||
|
||||
// get schedule from the test case
|
||||
for (auto *op : prob.getOperations())
|
||||
if (auto startTimeAttr = op->getAttrOfType<IntegerAttr>("problemStartTime"))
|
||||
prob.setStartTime(op, startTimeAttr.getInt());
|
||||
|
||||
if (failed(prob.verify())) {
|
||||
func->emitError("problem verification failed");
|
||||
return signalPassFailure();
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ASAPScheduler
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -319,6 +360,7 @@ void TestSimplexSchedulerPass::runOnFunction() {
|
|||
|
||||
if (problemToTest == "CyclicProblem") {
|
||||
CyclicProblem prob(func);
|
||||
constructProblem(prob, func);
|
||||
constructCyclicProblem(prob, func);
|
||||
assert(succeeded(prob.check()));
|
||||
|
||||
|
@ -377,6 +419,9 @@ void registerSchedulingTestPasses() {
|
|||
mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
|
||||
return std::make_unique<TestSPOProblemPass>();
|
||||
});
|
||||
mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
|
||||
return std::make_unique<TestModuloProblemPass>();
|
||||
});
|
||||
mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
|
||||
return std::make_unique<TestASAPSchedulerPass>();
|
||||
});
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
// RUN: circt-opt %s -test-modulo-problem -verify-diagnostics -split-input-file
|
||||
|
||||
// expected-error@+2 {{Operator type 'limited' is oversubscribed}}
|
||||
// expected-error@+1 {{problem verification failed}}
|
||||
func @oversubscribed(%a0 : i32, %a1 : i32, %a2 : i32) -> i32 attributes {
|
||||
problemInitiationInterval = 2,
|
||||
operatortypes = [ { name = "limited", latency = 1, limit = 2} ]
|
||||
} {
|
||||
%0 = addi %a0, %a0 { problemStartTime = 0 } : i32
|
||||
%1 = addi %a1, %0 { opr = "limited", problemStartTime = 1 } : i32
|
||||
%2 = addi %0, %a2 { opr = "limited", problemStartTime = 3 } : i32
|
||||
%3 = addi %0, %0 { opr = "limited", problemStartTime = 5 } : i32
|
||||
return { problemStartTime = 6 } %3 : i32
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// RUN: circt-opt %s -test-modulo-problem -allow-unregistered-dialect
|
||||
|
||||
func @canis14_fig2() attributes {
|
||||
problemInitiationInterval = 3,
|
||||
auxdeps = [ [2,0,1], [3,4] ],
|
||||
operatortypes = [
|
||||
{ name = "mem_port", latency = 1, limit = 1 },
|
||||
{ name = "add", latency = 1 }
|
||||
] } {
|
||||
%0 = "dummy.load_A"() { opr = "mem_port", problemStartTime = 2 } : () -> i32
|
||||
%1 = "dummy.load_B"() { opr = "mem_port", problemStartTime = 0 } : () -> i32
|
||||
%2 = addi %0, %1 { opr = "add", problemStartTime = 3 } : i32
|
||||
"dummy.store_A"(%2) { opr = "mem_port", problemStartTime = 4 } : (i32) -> ()
|
||||
return { problemStartTime = 5 }
|
||||
}
|
||||
|
||||
func @minII_feasible() attributes {
|
||||
problemInitiationInterval = 3,
|
||||
auxdeps = [ [6,1,5], [5,2,3], [6,7] ],
|
||||
operatortypes = [
|
||||
{ name = "const", latency = 0 },
|
||||
{ name = "phi", latency = 2 },
|
||||
{ name = "xor", latency = 1 },
|
||||
{ name = "sub", latency = 3, limit = 1}
|
||||
] } {
|
||||
%0 = constant { opr = "const", problemStartTime = 0 } -1 : i32
|
||||
%1 = "dummy.phi"() { opr = "phi", problemStartTime = 0 } : () -> i32
|
||||
%2 = "dummy.phi"() { opr = "phi", problemStartTime = 1 } : () -> i32
|
||||
%3 = xor %1, %0 { opr = "xor", problemStartTime = 2 } : i32
|
||||
%4 = subi %3, %2 { opr = "sub", problemStartTime = 3 } : i32
|
||||
%5 = subi %0, %4 { opr = "sub", problemStartTime = 7 } : i32
|
||||
%6 = subi %4, %5 { opr = "sub", problemStartTime = 11 } : i32
|
||||
return { problemStartTime = 14 }
|
||||
}
|
||||
|
||||
func @minII_infeasible() -> i32 attributes {
|
||||
problemInitiationInterval = 4,
|
||||
auxdeps = [ [0,1], [5,1,1] ],
|
||||
operatortypes = [
|
||||
{ name = "unlimited", latency = 1 },
|
||||
{ name = "limited", latency = 1, limit = 2 }
|
||||
] } {
|
||||
%0 = constant { opr = "unlimited", problemStartTime = 0 } 42 : i32
|
||||
%1 = "dummy.phi"() { opr = "unlimited", problemStartTime = 1 } : () -> i32
|
||||
%2 = "dummy.op"(%1) { opr = "limited", problemStartTime = 2 } : (i32) -> i32
|
||||
%3 = "dummy.op"(%1) { opr = "limited", problemStartTime = 3 } : (i32) -> i32
|
||||
%4 = "dummy.op"(%1) { opr = "limited", problemStartTime = 2 } : (i32) -> i32
|
||||
%5 = "dummy.mux"(%2, %3, %4) { opr = "unlimited", problemStartTime = 4 } : (i32, i32, i32) -> i32
|
||||
return { opr = "unlimited", problemStartTime = 5 } %5 : i32
|
||||
}
|
Loading…
Reference in New Issue