diff --git a/include/circt/Dialect/SSP/SSPAttributes.td b/include/circt/Dialect/SSP/SSPAttributes.td index 31dacd08ee7a..55d3ba915e48 100644 --- a/include/circt/Dialect/SSP/SSPAttributes.td +++ b/include/circt/Dialect/SSP/SSPAttributes.td @@ -134,8 +134,8 @@ in { } } -// SharedOperatorsProblem +// SharedResourcesProblem def LimitProp : ResourceTypeProperty { + "Limit", "unsigned", "::circt::scheduling::SharedResourcesProblem"> { let mnemonic = "limit"; } diff --git a/include/circt/Dialect/SSP/Utilities.h b/include/circt/Dialect/SSP/Utilities.h index 9e8815c6ef8b..6fb45904b4dc 100644 --- a/include/circt/Dialect/SSP/Utilities.h +++ b/include/circt/Dialect/SSP/Utilities.h @@ -630,7 +630,7 @@ struct Default { }; template <> -struct Default { +struct Default { static constexpr auto operationProperties = Default::operationProperties; static constexpr auto operatorTypeProperties = @@ -647,9 +647,9 @@ struct Default { static constexpr auto operationProperties = Default::operationProperties; static constexpr auto operatorTypeProperties = - Default::operatorTypeProperties; + Default::operatorTypeProperties; static constexpr auto resourceTypeProperties = - Default::resourceTypeProperties; + Default::resourceTypeProperties; static constexpr auto dependenceProperties = Default::dependenceProperties; static constexpr auto instanceProperties = diff --git a/include/circt/Scheduling/Algorithms.h b/include/circt/Scheduling/Algorithms.h index 59bc269f0c56..7885ee7187dd 100644 --- a/include/circt/Scheduling/Algorithms.h +++ b/include/circt/Scheduling/Algorithms.h @@ -38,11 +38,11 @@ LogicalResult scheduleSimplex(Problem &prob, Operation *lastOp); /// \p prob does not include \p lastOp. LogicalResult scheduleSimplex(CyclicProblem &prob, Operation *lastOp); -/// Solve the acyclic problem with shared operators using a linear +/// Solve the acyclic problem with shared resources using a linear /// programming-based heuristic. The approach tries to minimize the start time /// of the given \p lastOp, but optimality is not guaranteed. Fails if the /// dependence graph contains cycles, or \p prob does not include \p lastOp. -LogicalResult scheduleSimplex(SharedOperatorsProblem &prob, Operation *lastOp); +LogicalResult scheduleSimplex(SharedResourcesProblem &prob, Operation *lastOp); /// Solve the modulo scheduling problem using a linear programming-based /// heuristic. The approach tries to determine the smallest feasible initiation @@ -87,11 +87,11 @@ LogicalResult scheduleLP(Problem &prob, Operation *lastOp); /// \p lastOp. LogicalResult scheduleLP(CyclicProblem &prob, Operation *lastOp); -/// Solve the acyclic problem with shared operators using constraint programming +/// Solve the acyclic problem with shared resources using constraint programming /// and an external SAT solver. The objective is to minimize the start time of /// the given \p lastOp. Fails if the dependence graph contains cycles, or \p /// prob does not include \p lastOp. -LogicalResult scheduleCPSAT(SharedOperatorsProblem &prob, Operation *lastOp); +LogicalResult scheduleCPSAT(SharedResourcesProblem &prob, Operation *lastOp); } // namespace scheduling } // namespace circt diff --git a/include/circt/Scheduling/Problems.h b/include/circt/Scheduling/Problems.h index bacdb4abbd5d..2481088d7cfa 100644 --- a/include/circt/Scheduling/Problems.h +++ b/include/circt/Scheduling/Problems.h @@ -480,16 +480,16 @@ class ChainingProblem : public virtual Problem { /// accept new operands (coming from a distinct operation) in each time step. /// /// A solution to this problem is feasible iff the number of operations that use -/// a certain limited operator type, and start in the same time step, does not -/// exceed the operator type's limit. These constraints do not apply to operator +/// a certain limited resource type, and start in the same time step, does not +/// exceed the resource type's limit. These constraints do not apply to resource /// types without a limit (not set, or 0). -class SharedOperatorsProblem : public virtual Problem { +class SharedResourcesProblem : public virtual Problem { public: - static constexpr auto name = "SharedOperatorsProblem"; + static constexpr auto name = "SharedResourcesProblem"; using Problem::Problem; protected: - SharedOperatorsProblem() = default; + SharedResourcesProblem() = default; private: ResourceTypeProperty limit; @@ -526,7 +526,7 @@ class SharedOperatorsProblem : public virtual Problem { /// 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 SharedOperatorsProblem { + public virtual SharedResourcesProblem { public: static constexpr auto name = "ModuloProblem"; using CyclicProblem::CyclicProblem; diff --git a/lib/Dialect/SSP/Transforms/Print.cpp b/lib/Dialect/SSP/Transforms/Print.cpp index 3b0f5e7d86ec..9fa1a7e3f42d 100644 --- a/lib/Dialect/SSP/Transforms/Print.cpp +++ b/lib/Dialect/SSP/Transforms/Print.cpp @@ -55,8 +55,8 @@ void PrintPass::runOnOperation() { printInstance(instOp, os); else if (probName == "ChainingProblem") printInstance(instOp, os); - else if (probName == "SharedOperatorsProblem") - printInstance(instOp, os); + else if (probName == "SharedResourcesProblem") + printInstance(instOp, os); else if (probName == "ModuloProblem") printInstance(instOp, os); else if (probName == "ChainingCyclicProblem") diff --git a/lib/Dialect/SSP/Transforms/Roundtrip.cpp b/lib/Dialect/SSP/Transforms/Roundtrip.cpp index 21825bc28940..0f40c430e3a5 100644 --- a/lib/Dialect/SSP/Transforms/Roundtrip.cpp +++ b/lib/Dialect/SSP/Transforms/Roundtrip.cpp @@ -50,8 +50,8 @@ static InstanceOp roundtrip(InstanceOp instOp, bool check, bool verify, return roundtripAs(instOp, check, verify, builder); if (problemName == "CyclicProblem") return roundtripAs(instOp, check, verify, builder); - if (problemName == "SharedOperatorsProblem") - return roundtripAs(instOp, check, verify, builder); + if (problemName == "SharedResourcesProblem") + return roundtripAs(instOp, check, verify, builder); if (problemName == "ModuloProblem") return roundtripAs(instOp, check, verify, builder); if (problemName == "ChainingProblem") diff --git a/lib/Dialect/SSP/Transforms/Schedule.cpp b/lib/Dialect/SSP/Transforms/Schedule.cpp index fa078cf3d15d..ebdbb76059e3 100644 --- a/lib/Dialect/SSP/Transforms/Schedule.cpp +++ b/lib/Dialect/SSP/Transforms/Schedule.cpp @@ -138,8 +138,8 @@ static InstanceOp scheduleWithSimplex(InstanceOp instOp, StringRef options, return scheduleProblemTWithSimplex(instOp, lastOp, builder); if (problemName == "CyclicProblem") return scheduleProblemTWithSimplex(instOp, lastOp, builder); - if (problemName == "SharedOperatorsProblem") - return scheduleProblemTWithSimplex(instOp, lastOp, + if (problemName == "SharedResourcesProblem") + return scheduleProblemTWithSimplex(instOp, lastOp, builder); if (problemName == "ModuloProblem") return scheduleProblemTWithSimplex(instOp, lastOp, builder); @@ -219,13 +219,13 @@ static InstanceOp scheduleWithCPSAT(InstanceOp instOp, StringRef options, } auto problemName = instOp.getProblemName(); - if (problemName != "SharedOperatorsProblem") { + if (problemName != "SharedResourcesProblem") { llvm::errs() << "ssp-schedule: Unsupported problem '" << problemName << "' for CPSAT scheduler\n"; return {}; } - auto prob = loadProblem(instOp); + auto prob = loadProblem(instOp); if (failed(prob.check()) || failed(scheduling::scheduleCPSAT(prob, lastOp)) || failed(prob.verify())) return {}; diff --git a/lib/Scheduling/CPSATSchedulers.cpp b/lib/Scheduling/CPSATSchedulers.cpp index 950c33fa5bf7..85565a05236b 100644 --- a/lib/Scheduling/CPSATSchedulers.cpp +++ b/lib/Scheduling/CPSATSchedulers.cpp @@ -38,7 +38,7 @@ using llvm::format; /// https://github.com/google/or-tools/blob/stable/examples/python/rcpsp_sat.py /// but a gentler introduction (though with differing formulation) is /// https://python-mip.readthedocs.io/en/latest/examples.html -LogicalResult scheduling::scheduleCPSAT(SharedOperatorsProblem &prob, +LogicalResult scheduling::scheduleCPSAT(SharedResourcesProblem &prob, Operation *lastOp) { Operation *containingOp = prob.getContainingOp(); if (!prob.hasOperation(lastOp)) @@ -123,7 +123,7 @@ LogicalResult scheduling::scheduleCPSAT(SharedOperatorsProblem &prob, (Twine("demand_") + Twine(i) + Twine("_") + Twine(resource.getAttr().strref())) .str()); - // Conventional formulation for SharedOperatorsProblem; + // Conventional formulation for SharedResourcesProblem; // interval during which the resource is occupied has size 1. IntervalVar start = cpModel.NewFixedSizeIntervalVar(taskInterval.StartExpr(), 1); diff --git a/lib/Scheduling/Problems.cpp b/lib/Scheduling/Problems.cpp index 579b1e2534ab..436b671918cd 100644 --- a/lib/Scheduling/Problems.cpp +++ b/lib/Scheduling/Problems.cpp @@ -330,18 +330,18 @@ LogicalResult ChainingProblem::verify() { } //===----------------------------------------------------------------------===// -// SharedOperatorsProblem +// SharedResourcesProblem //===----------------------------------------------------------------------===// Problem::PropertyStringVector -SharedOperatorsProblem::getProperties(ResourceType rsrc) { +SharedResourcesProblem::getProperties(ResourceType rsrc) { auto psv = Problem::getProperties(rsrc); if (auto limit = getLimit(rsrc)) psv.emplace_back("limit", std::to_string(*limit)); return psv; } -LogicalResult SharedOperatorsProblem::checkLatency(Operation *op) { +LogicalResult SharedResourcesProblem::checkLatency(Operation *op) { if (failed(Problem::checkLatency(op))) return failure(); @@ -364,7 +364,7 @@ LogicalResult SharedOperatorsProblem::checkLatency(Operation *op) { return success(); } -LogicalResult SharedOperatorsProblem::verifyUtilization(ResourceType rsrc) { +LogicalResult SharedResourcesProblem::verifyUtilization(ResourceType rsrc) { auto limit = getLimit(rsrc); if (!limit) return success(); @@ -393,7 +393,7 @@ LogicalResult SharedOperatorsProblem::verifyUtilization(ResourceType rsrc) { return success(); } -LogicalResult SharedOperatorsProblem::verify() { +LogicalResult SharedResourcesProblem::verify() { if (failed(Problem::verify())) return failure(); @@ -442,7 +442,7 @@ LogicalResult ModuloProblem::verify() { if (failed(CyclicProblem::verify())) return failure(); - // Don't call SharedOperatorsProblem::verify() here to prevent redundant + // Don't call SharedResourcesProblem::verify() here to prevent redundant // verification of the base problem. for (auto rsrc : getResourceTypes()) if (failed(verifyUtilization(rsrc))) diff --git a/lib/Scheduling/SimplexSchedulers.cpp b/lib/Scheduling/SimplexSchedulers.cpp index f0235c9326d4..e9936a3833d2 100644 --- a/lib/Scheduling/SimplexSchedulers.cpp +++ b/lib/Scheduling/SimplexSchedulers.cpp @@ -218,17 +218,17 @@ class CyclicSimplexScheduler : public SimplexSchedulerBase { LogicalResult schedule() override; }; -// This class solves acyclic, resource-constrained `SharedOperatorsProblem` with +// This class solves acyclic, resource-constrained `SharedResourcesProblem` with // a simplified version of the iterative heuristic presented in [2]. -class SharedOperatorsSimplexScheduler : public SimplexSchedulerBase { +class SharedResourcesSimplexScheduler : public SimplexSchedulerBase { private: - SharedOperatorsProblem &prob; + SharedResourcesProblem &prob; protected: Problem &getProblem() override { return prob; } public: - SharedOperatorsSimplexScheduler(SharedOperatorsProblem &prob, + SharedResourcesSimplexScheduler(SharedResourcesProblem &prob, Operation *lastOp) : SimplexSchedulerBase(lastOp), prob(prob) {} LogicalResult schedule() override; @@ -870,10 +870,10 @@ LogicalResult CyclicSimplexScheduler::schedule() { } //===----------------------------------------------------------------------===// -// SharedOperatorsSimplexScheduler +// SharedResourcesSimplexScheduler //===----------------------------------------------------------------------===// -static bool isLimited(Operation *op, SharedOperatorsProblem &prob) { +static bool isLimited(Operation *op, SharedResourcesProblem &prob) { auto maybeRsrcs = prob.getLinkedResourceTypes(op); if (!maybeRsrcs) return false; @@ -882,7 +882,7 @@ static bool isLimited(Operation *op, SharedOperatorsProblem &prob) { }); } -LogicalResult SharedOperatorsSimplexScheduler::schedule() { +LogicalResult SharedResourcesSimplexScheduler::schedule() { if (failed(checkLastOp())) return failure(); @@ -938,18 +938,26 @@ LogicalResult SharedOperatorsSimplexScheduler::schedule() { assert(maybeRsrcs && "Limited operation must have linked resource types"); auto &rsrcs = *maybeRsrcs; - assert(rsrcs.size() == 1 && - "Multi-resource operations are not yet supported by this scheduler"); + for (auto rsrc : rsrcs) { + assert(prob.getLimit(rsrc).value_or(0) > 0 && + "All resources must have non-zero limits"); + } auto rsrc = rsrcs[0]; unsigned limit = prob.getLimit(rsrc).value_or(0); assert(limit > 0); // Find the first time step (beginning at the current start time in the - // partial schedule) in which an operator instance is available. + // partial schedule) in which all required resources are available for the + // operation. unsigned startTimeVar = startTimeVariables[op]; unsigned candTime = getStartTime(startTimeVar); - while (reservationTable[rsrc].lookup(candTime) == limit) + + while (std::any_of(rsrcs.begin(), rsrcs.end(), + [&](Problem::ResourceType rsrc) { + return reservationTable[rsrc].lookup(candTime) == + prob.getLimit(rsrc).value_or(0); + })) ++candTime; // Fix the start time. As explained above, this cannot make the problem @@ -958,8 +966,9 @@ LogicalResult SharedOperatorsSimplexScheduler::schedule() { assert(succeeded(fixed)); (void)fixed; - // Record the operator use. - ++reservationTable[rsrc][candTime]; + // Record the resource uses. + for (auto rsrc : rsrcs) + ++reservationTable[rsrc][candTime]; LLVM_DEBUG(dbgs() << "After scheduling " << startTimeVar << " to t=" << candTime << ":\n"; @@ -1375,9 +1384,9 @@ LogicalResult scheduling::scheduleSimplex(CyclicProblem &prob, return simplex.schedule(); } -LogicalResult scheduling::scheduleSimplex(SharedOperatorsProblem &prob, +LogicalResult scheduling::scheduleSimplex(SharedResourcesProblem &prob, Operation *lastOp) { - SharedOperatorsSimplexScheduler simplex(prob, lastOp); + SharedResourcesSimplexScheduler simplex(prob, lastOp); return simplex.schedule(); } diff --git a/test/Dialect/SSP/roundtrip.mlir b/test/Dialect/SSP/roundtrip.mlir index ebad3093f01a..7d7e90372edb 100644 --- a/test/Dialect/SSP/roundtrip.mlir +++ b/test/Dialect/SSP/roundtrip.mlir @@ -164,7 +164,7 @@ ssp.instance @mco_outgoing_delays of "ChainingProblem" { } } -// CHECK: ssp.instance @multiple_oprs of "SharedOperatorsProblem" { +// CHECK: ssp.instance @multiple_oprs of "SharedResourcesProblem" { // CHECK: library { // CHECK: operator_type @slowAdd [latency<3>] // CHECK: operator_type @fastAdd [latency<1>] @@ -185,7 +185,7 @@ ssp.instance @mco_outgoing_delays of "ChainingProblem" { // CHECK: operation<@_1>() [t<10>] // CHECK: } // CHECK: } -ssp.instance @multiple_oprs of "SharedOperatorsProblem" { +ssp.instance @multiple_oprs of "SharedResourcesProblem" { library { operator_type @slowAdd [latency<3>] operator_type @fastAdd [latency<1>] diff --git a/test/Scheduling/shared-operators-problem-errors.mlir b/test/Scheduling/shared-resources-problem-errors.mlir similarity index 85% rename from test/Scheduling/shared-operators-problem-errors.mlir rename to test/Scheduling/shared-resources-problem-errors.mlir index 49cda354a4a0..f226e875d4b6 100644 --- a/test/Scheduling/shared-operators-problem-errors.mlir +++ b/test/Scheduling/shared-resources-problem-errors.mlir @@ -1,7 +1,7 @@ // RUN: circt-opt %s -ssp-roundtrip=verify -verify-diagnostics -split-input-file // expected-error@+1 {{Operator type 'limited' using limited resource 'limited_rsrc' has zero latency.}} -ssp.instance @limited_but_zero_latency of "SharedOperatorsProblem" { +ssp.instance @limited_but_zero_latency of "SharedResourcesProblem" { library { operator_type @limited [latency<0>] } @@ -16,7 +16,7 @@ ssp.instance @limited_but_zero_latency of "SharedOperatorsProblem" { // ----- // expected-error@+1 {{Resource type 'limited_rsrc' is oversubscribed}} -ssp.instance @oversubscribed of "SharedOperatorsProblem" { +ssp.instance @oversubscribed of "SharedResourcesProblem" { library { operator_type @limited [latency<1>] } diff --git a/test/Scheduling/shared-operators-problems.mlir b/test/Scheduling/shared-resources-problems.mlir similarity index 68% rename from test/Scheduling/shared-operators-problems.mlir rename to test/Scheduling/shared-resources-problems.mlir index 1bcfc90da465..371979c96293 100644 --- a/test/Scheduling/shared-operators-problems.mlir +++ b/test/Scheduling/shared-resources-problems.mlir @@ -3,7 +3,7 @@ // RUN: %if or-tools %{ circt-opt %s -ssp-schedule=scheduler=cpsat | FileCheck %s -check-prefixes=CHECK,CPSAT %} // CHECK-LABEL: full_load -ssp.instance @full_load of "SharedOperatorsProblem" { +ssp.instance @full_load of "SharedResourcesProblem" { library { operator_type @L1_3 [latency<3>] operator_type @_1 [latency<1>] @@ -25,7 +25,7 @@ ssp.instance @full_load of "SharedOperatorsProblem" { } // CHECK-LABEL: partial_load -ssp.instance @partial_load of "SharedOperatorsProblem" { +ssp.instance @partial_load of "SharedResourcesProblem" { library { operator_type @L3_3 [latency<3>] operator_type @_1 [latency<1>] @@ -47,7 +47,7 @@ ssp.instance @partial_load of "SharedOperatorsProblem" { } // CHECK-LABEL: multiple -ssp.instance @multiple of "SharedOperatorsProblem" { +ssp.instance @multiple of "SharedResourcesProblem" { library { operator_type @L3_2 [latency<3>] operator_type @L1_1 [latency<1>] @@ -69,3 +69,26 @@ ssp.instance @multiple of "SharedOperatorsProblem" { operation<@_1> @last(%5) [t<11>] } } + +// CHECK-LABEL: if_else +ssp.instance @if_else_exclusive of "SharedResourcesProblem" { + library { + operator_type @ThenOperator [latency<2>] + operator_type @ElseOperator [latency<3>] + operator_type @_1 [latency<2>] + } + resource { + resource_type @adder_then_br [limit<1>] + resource_type @mem_port_then_br [limit<1>] + resource_type @adder_else_br [limit<1>] + resource_type @mem_port_else_br [limit<1>] + } + graph { + %0 = operation<@ThenOperator> @compute_then_br() uses[@adder_then_br, @mem_port_then_br] [t<0>] + %1 = operation<@ElseOperator> @compute_else_br() uses[@adder_else_br, @mem_port_else_br] [t<0>] + %2 = operation<@_1>(%0, %1) [t<4>] + // SIMPLEX: @last(%{{.*}}) [t<5>] + // CPSAT: @last(%{{.*}}) [t<5>] + operation<@_1> @last(%2) [t<7>] + } +}