mirror of https://github.com/llvm/circt.git
[SSP] Separate `ResourceType` from `OperatorType` (#8444)
* We separate 'resource' as a standalone concept from 'operator type' so that we can associate different kinds of resource to an operation. * The test cases are modified to reflect the new changes accordingly. * Similar to operators, resources are also printed as the first class in operations.
This commit is contained in:
parent
58897cd827
commit
3c51e75ed6
|
@ -85,6 +85,26 @@ class OperatorTypeProperty<Dialect dialect, string name, string type, string pro
|
|||
}];
|
||||
}
|
||||
|
||||
class ResourceTypeProperty<Dialect dialect, string name, string type, string problem,
|
||||
list<Trait> traits = []>
|
||||
: PropertyBase<dialect, name, type, problem, traits> {
|
||||
let extraClassDeclaration = [{
|
||||
void setInProblem(}] # problemClassName # [{ &prob,
|
||||
::circt::scheduling::Problem::ResourceType rsrc) {
|
||||
prob.set}] # propertyName # [{(rsrc, }] # unwrapValue # [{);
|
||||
}
|
||||
static ::mlir::Attribute getFromProblem(}] # problemClassName # [{ &prob,
|
||||
::circt::scheduling::Problem::ResourceType rsrc,
|
||||
::mlir::MLIRContext *ctx) {
|
||||
if (auto optValue = prob.get}] # propertyName # [{(rsrc)) {
|
||||
auto value = *optValue;
|
||||
return }] # cppClassName # [{::get(ctx, }] # wrapValue # [{);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
class DependenceProperty<Dialect dialect, string name, string type, string problem,
|
||||
list<Trait> traits = []>
|
||||
: PropertyBase<dialect, name, type, problem, traits> {
|
||||
|
|
|
@ -48,7 +48,53 @@ def LinkedOperatorTypeProp : OperationProperty<SSPDialect,
|
|||
"LinkedOperatorType", "::mlir::SymbolRefAttr", "::circt::scheduling::Problem"> {
|
||||
let mnemonic = "opr";
|
||||
let unwrapValue = [{ getValue().getLeafReference() }];
|
||||
let wrapValue = [{ ::mlir::FlatSymbolRefAttr::get(ctx, value) }];
|
||||
let wrapValue = [{ ::mlir::FlatSymbolRefAttr::get(ctx, value.getAttr()) }];
|
||||
}
|
||||
def LinkedResourceTypesProp : OperationProperty<SSPDialect,
|
||||
"LinkedResourceTypes", "::mlir::ArrayAttr", "::circt::scheduling::Problem"> {
|
||||
let mnemonic = "rsrcs";
|
||||
let extraClassDeclaration = [{
|
||||
static SmallVector<::circt::scheduling::Problem::ResourceType>
|
||||
arrayAttrToResourceTypes(::mlir::ArrayAttr attr) {
|
||||
SmallVector<::circt::scheduling::Problem::ResourceType> result;
|
||||
for (auto a : attr.getAsRange<::mlir::SymbolRefAttr>())
|
||||
result.push_back(::circt::scheduling::Problem::ResourceType(a.getLeafReference()));
|
||||
return result;
|
||||
}
|
||||
|
||||
static ::mlir::ArrayAttr
|
||||
resourceTypesToArrayAttr(::mlir::MLIRContext *ctx,
|
||||
::llvm::ArrayRef<::circt::scheduling::Problem::ResourceType> rsrcs) {
|
||||
SmallVector<::mlir::Attribute> attrs;
|
||||
for (auto r : rsrcs) {
|
||||
auto value = r.getAttr().getValue();
|
||||
auto strAttr = mlir::StringAttr::get(ctx, value);
|
||||
attrs.push_back(::mlir::FlatSymbolRefAttr::get(strAttr));
|
||||
}
|
||||
return ::mlir::ArrayAttr::get(ctx, attrs);
|
||||
}
|
||||
|
||||
void setInProblem(::circt::scheduling::Problem &prob, ::mlir::Operation *op) {
|
||||
SmallVector<::circt::scheduling::Problem::ResourceType> result;
|
||||
for (auto a : getValue().getAsRange<::mlir::SymbolRefAttr>()) {
|
||||
auto attr = a.getLeafReference();
|
||||
result.push_back(::circt::scheduling::Problem::ResourceType(attr));
|
||||
}
|
||||
prob.setLinkedResourceTypes(op, result);
|
||||
}
|
||||
|
||||
static ::mlir::Attribute getFromProblem(::circt::scheduling::Problem &prob,
|
||||
::mlir::Operation *op,
|
||||
::mlir::MLIRContext *context) {
|
||||
if (auto optValue = prob.getLinkedResourceTypes(op)) {
|
||||
const SmallVector<::circt::scheduling::Problem::ResourceType> &vec = *optValue;
|
||||
return LinkedResourceTypesAttr::get(context, resourceTypesToArrayAttr(context, vec));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}];
|
||||
let unwrapValue = [{ arrayAttrToResourceTypes(getValue()) }];
|
||||
let wrapValue = [{ resourceTypesToArrayAttr(context, value) }];
|
||||
}
|
||||
def StartTimeProp : OperationProperty<SSPDialect,
|
||||
"StartTime", "unsigned", "::circt::scheduling::Problem"> {
|
||||
|
@ -89,7 +135,7 @@ in {
|
|||
}
|
||||
|
||||
// SharedOperatorsProblem
|
||||
def LimitProp : OperatorTypeProperty<SSPDialect,
|
||||
def LimitProp : ResourceTypeProperty<SSPDialect,
|
||||
"Limit", "unsigned", "::circt::scheduling::SharedOperatorsProblem"> {
|
||||
let mnemonic = "limit";
|
||||
}
|
||||
|
|
|
@ -24,25 +24,30 @@ def InstanceOp : SSPOp<"instance",
|
|||
let description = [{
|
||||
This operation represents an instance of a static scheduling problem,
|
||||
comprised of an operator library (`OperatorLibraryOp`, a container for
|
||||
`OperatorTypeOp`s) and the dependence graph (`DependenceGraphOp`, a
|
||||
`OperatorTypeOp`s), an resource library (`ResourceLibraryOp`, a container
|
||||
for `ResourceTypeOp`s), and the dependence graph (`DependenceGraphOp`, a
|
||||
container for `OperationOp`s). The instance and its components (operations,
|
||||
operator types and dependences) can carry properties, i.e. special MLIR
|
||||
attributes inheriting from the TableGen classes in `PropertyBase.td`. The
|
||||
`ssp` dialect provides attribute definitions (and short-form
|
||||
pretty-printing) for CIRCT's built-in scheduling problems.
|
||||
operator types, resource types and dependences) can carry properties,
|
||||
i.e. special MLIR attributes inheriting from the TableGen classes in
|
||||
`PropertyBase.td`. The `ssp` dialect provides attribute definitions (and
|
||||
short-form pretty-printing) for CIRCT's built-in scheduling problems.
|
||||
|
||||
**Example**
|
||||
```mlir
|
||||
ssp.instance @canis14_fig2 of "ModuloProblem" [II<3>] {
|
||||
library {
|
||||
operator_type @MemPort [latency<1>, limit<1>]
|
||||
operator_type @Memory [latency<1>]
|
||||
operator_type @Add [latency<1>]
|
||||
}
|
||||
resource {
|
||||
resource_type @ReadPort [limit<1>]
|
||||
resource_type @WritePort [limit<1>]
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@MemPort> @load_A(@store_A [dist<1>]) [t<2>]
|
||||
%1 = operation<@MemPort> @load_B() [t<0>]
|
||||
%2 = operation<@Add> @add(%0, %1) [t<3>]
|
||||
operation<@MemPort> @store_A(%2) [t<4>]
|
||||
%0 = operation<@Memory> @load_A(@store_A [dist<1>]) uses[@ReadPort] [t<2>]
|
||||
%1 = operation<@Memory> @load_B() uses[@ReadPort] [t<0>]
|
||||
%2 = operation<@Add> @add(%0, %1) [t<3>] // no `resource_type` needed
|
||||
operation<@Memory> @store_A(%2) uses[@WritePort] [t<4>]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -71,6 +76,7 @@ def InstanceOp : SSPOp<"instance",
|
|||
|
||||
// Access to container ops
|
||||
::circt::ssp::OperatorLibraryOp getOperatorLibrary();
|
||||
::circt::ssp::ResourceLibraryOp getResourceLibrary();
|
||||
::circt::ssp::DependenceGraphOp getDependenceGraph();
|
||||
}];
|
||||
|
||||
|
@ -124,6 +130,44 @@ def OperatorLibraryOp : SSPOp<"library",
|
|||
];
|
||||
}
|
||||
|
||||
def ResourceLibraryOp : SSPOp<"resource",
|
||||
[NoRegionArguments, SingleBlock,
|
||||
NoTerminator, OpAsmOpInterface, SymbolTable, Symbol]> {
|
||||
let summary = "Container for resource types.";
|
||||
let description = [{
|
||||
The resource library represents different kinds of resource of desired
|
||||
usage on the target architecture/IR. Each resource will be represented by
|
||||
the individual `ResourceTypeOp`s. An `OperationOp` can be associated with
|
||||
zero, one, or multiple resources. This operation may be used outside of
|
||||
an `InstanceOp` so different problems can share the same resource constraints.
|
||||
}];
|
||||
|
||||
let arguments = (ins OptionalAttr<SymbolNameAttr>:$sym_name);
|
||||
let assemblyFormat = "($sym_name^)? $body attr-dict";
|
||||
let regions = (region SizedRegion<1>:$body);
|
||||
|
||||
let extraClassDeclaration = [{
|
||||
// OpAsmOpInterface
|
||||
static ::llvm::StringRef getDefaultDialect() { return "ssp"; }
|
||||
|
||||
// SymbolOpInterface
|
||||
static bool isOptionalSymbol() { return true; }
|
||||
|
||||
// Convenience
|
||||
::mlir::Block *getBodyBlock() {
|
||||
return &getBody().getBlocks().front();
|
||||
}
|
||||
}];
|
||||
|
||||
let skipDefaultBuilders = true;
|
||||
let builders = [
|
||||
OpBuilder<(ins ), [{
|
||||
::mlir::Region* region = $_state.addRegion();
|
||||
region->push_back(new ::mlir::Block());
|
||||
}]>
|
||||
];
|
||||
}
|
||||
|
||||
def DependenceGraphOp : SSPOp<"graph",
|
||||
[HasOnlyGraphRegion, NoRegionArguments,
|
||||
SingleBlock, NoTerminator, OpAsmOpInterface, SymbolTable,
|
||||
|
@ -165,7 +209,24 @@ def OperatorTypeOp : SSPOp<"operator_type",
|
|||
|
||||
**Example**
|
||||
```mlir
|
||||
operator_type @MemPort [latency<1>, limit<1>]
|
||||
operator_type @MemPort [latency<1>]
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins SymbolNameAttr:$sym_name, OptionalAttr<ArrayAttr>:$sspProperties);
|
||||
let assemblyFormat = "$sym_name custom<SSPProperties>($sspProperties) attr-dict";
|
||||
}
|
||||
|
||||
def ResourceTypeOp : SSPOp<"resource_type",
|
||||
[Symbol, HasParent<"ResourceLibraryOp">]> {
|
||||
let summary = "Resource of desired usage on the target architecture/IR.";
|
||||
let description = [{
|
||||
This resource represents a resource type, which can be augmented with a
|
||||
set of problem-specific properties, and is identified through a unique name.
|
||||
|
||||
**Example**
|
||||
```mlir
|
||||
resource_type @MemPort [limit<1>]
|
||||
```
|
||||
}];
|
||||
|
||||
|
@ -189,12 +250,13 @@ def OperationOp : SSPOp<"operation",
|
|||
operands/results. The operation and the incoming dependences can carry
|
||||
properties.
|
||||
|
||||
The `linkedOperatorType` property in the root `Problem` class is central to
|
||||
the problem models, because it links operations to their properties in the
|
||||
target IR. Therefore, the referenced operator type symbol is parsed/printed
|
||||
right after the operation keyword in the custom assembly syntax. Flat symbol
|
||||
references are resolved by name in the surrounding instance's operator
|
||||
library. Nested references can point to arbitrary operator libraries.
|
||||
The `linkedOperatorType` and `linkedResourceType` property in the root
|
||||
`Problem` class are central to the problem models, because it links operations
|
||||
to their properties in the target IR. Therefore, the referenced operator/resource
|
||||
type symbol is parsed/printed right after the operation keyword in the custom
|
||||
assembly syntax. Flat symbol references are resolved by name in the surrounding
|
||||
instance's operator/resource library. Nested references can point to arbitrary
|
||||
operator/resource libraries.
|
||||
|
||||
**Examples**
|
||||
```mlir
|
||||
|
@ -205,7 +267,7 @@ def OperationOp : SSPOp<"operation",
|
|||
%5:2 = operation<@Div>(%3, %4) // multiple results
|
||||
|
||||
// named, mix of def-use and auxiliary dependences
|
||||
operation<@MemPort> @store_A(%2, @store_B, @load_A)
|
||||
operation<@MemAccess> @store_A(%2, @store_B, @load_A) uses[@MemPort]
|
||||
|
||||
// dependence properties
|
||||
operation<@Barrier>(%2 [dist<1>], %5#1, @store_A [dist<3>])
|
||||
|
@ -230,6 +292,9 @@ def OperationOp : SSPOp<"operation",
|
|||
|
||||
// Find the attribute modeling the `linkedOperatorType` property
|
||||
::circt::ssp::LinkedOperatorTypeAttr getLinkedOperatorTypeAttr();
|
||||
|
||||
// Find the attribute modeling the `linkedResourceTypes` property
|
||||
::circt::ssp::LinkedResourceTypesAttr getLinkedResourceTypesAttr();
|
||||
}];
|
||||
|
||||
let skipDefaultBuilders = true;
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace circt {
|
|||
namespace ssp {
|
||||
|
||||
using OperatorType = scheduling::Problem::OperatorType;
|
||||
using ResourceType = scheduling::Problem::ResourceType;
|
||||
using Dependence = scheduling::Problem::Dependence;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -66,6 +67,21 @@ void loadOperatorTypeProperties(ProblemT &prob, OperatorType opr,
|
|||
}
|
||||
}
|
||||
|
||||
template <typename ProblemT>
|
||||
void loadResourceTypeProperties(ProblemT &, ResourceType, ArrayAttr) {}
|
||||
template <typename ProblemT, typename ResourceTypePropertyT,
|
||||
typename... ResourceTypePropertyTs>
|
||||
void loadResourceTypeProperties(ProblemT &prob, ResourceType rsrc,
|
||||
ArrayAttr props) {
|
||||
if (!props)
|
||||
return;
|
||||
for (auto prop : props) {
|
||||
TypeSwitch<Attribute>(prop)
|
||||
.Case<ResourceTypePropertyT, ResourceTypePropertyTs...>(
|
||||
[&](auto p) { p.setInProblem(prob, rsrc); });
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ProblemT>
|
||||
void loadDependenceProperties(ProblemT &, Dependence, ArrayAttr) {}
|
||||
template <typename ProblemT, typename DependencePropertyT,
|
||||
|
@ -98,12 +114,13 @@ void loadInstanceProperties(ProblemT &prob, ArrayAttr props) {
|
|||
/// given attribute classes. The registered name is returned. The template
|
||||
/// instantiation fails if properties are incompatible with \p ProblemT.
|
||||
template <typename ProblemT, typename... OperatorTypePropertyTs>
|
||||
OperatorType loadOperatorType(ProblemT &prob, OperatorTypeOp oprOp,
|
||||
SmallDenseMap<StringAttr, unsigned> &oprIds) {
|
||||
typename ProblemT::OperatorType loadOperatorType(
|
||||
ProblemT &prob, OperatorTypeOp oprOp,
|
||||
SmallDenseMap<typename ProblemT::OperatorType, unsigned> &oprIds) {
|
||||
OperatorType opr = oprOp.getNameAttr();
|
||||
unsigned &id = oprIds[opr];
|
||||
if (id > 0)
|
||||
opr = StringAttr::get(opr.getContext(),
|
||||
opr = StringAttr::get(oprOp.getContext(),
|
||||
opr.getValue() + Twine('_') + Twine(id));
|
||||
++id;
|
||||
assert(!prob.hasOperatorType(opr));
|
||||
|
@ -113,6 +130,27 @@ OperatorType loadOperatorType(ProblemT &prob, OperatorTypeOp oprOp,
|
|||
return opr;
|
||||
}
|
||||
|
||||
/// Load the resource type represented by \p rsrcOp into \p prob under a unique
|
||||
/// name informed by \p rsrcIds, and attempt to set its properties from the
|
||||
/// given attribute classes. The registered name is returned. The template
|
||||
/// instantiation fails if properties are incompatible with \p ProblemT.
|
||||
template <typename ProblemT, typename... ResourceTypePropertyTs>
|
||||
typename ProblemT::ResourceType loadResourceType(
|
||||
ProblemT &prob, ResourceTypeOp rsrcOp,
|
||||
SmallDenseMap<typename ProblemT::ResourceType, unsigned> &rsrcIds) {
|
||||
ResourceType rsrc = rsrcOp.getNameAttr();
|
||||
unsigned &id = rsrcIds[rsrc];
|
||||
if (id > 0)
|
||||
rsrc = StringAttr::get(rsrcOp.getContext(),
|
||||
rsrc.getValue() + Twine('_') + Twine(id));
|
||||
++id;
|
||||
assert(!prob.hasResourceType(rsrc));
|
||||
prob.insertResourceType(rsrc);
|
||||
loadResourceTypeProperties<ProblemT, ResourceTypePropertyTs...>(
|
||||
prob, rsrc, rsrcOp.getSspPropertiesAttr());
|
||||
return rsrc;
|
||||
}
|
||||
|
||||
/// Construct an instance of \p ProblemT from \p instOp, and attempt to set
|
||||
/// properties from the given attribute classes. The attribute tuples are used
|
||||
/// solely for grouping/inferring the template parameter packs. The tuple
|
||||
|
@ -135,11 +173,13 @@ OperatorType loadOperatorType(ProblemT &prob, OperatorTypeOp oprOp,
|
|||
/// std::make_tuple(InitiationIntervalAttr()));
|
||||
/// ```
|
||||
template <typename ProblemT, typename... OperationPropertyTs,
|
||||
typename... OperatorTypePropertyTs, typename... DependencePropertyTs,
|
||||
typename... OperatorTypePropertyTs,
|
||||
typename... ResourceTypePropertyTs, typename... DependencePropertyTs,
|
||||
typename... InstancePropertyTs>
|
||||
ProblemT loadProblem(InstanceOp instOp,
|
||||
std::tuple<OperationPropertyTs...> opProps,
|
||||
std::tuple<OperatorTypePropertyTs...> oprProps,
|
||||
std::tuple<ResourceTypePropertyTs...> rsrcProps,
|
||||
std::tuple<DependencePropertyTs...> depProps,
|
||||
std::tuple<InstancePropertyTs...> instProps) {
|
||||
ProblemT prob(instOp);
|
||||
|
@ -166,6 +206,24 @@ ProblemT loadProblem(InstanceOp instOp,
|
|||
if (auto libName = libraryOp.getSymNameAttr())
|
||||
prob.setLibraryName(libName);
|
||||
|
||||
// Use IDs to disambiguate resource types with the same name defined in
|
||||
// different resource libraries.
|
||||
SmallDenseMap<ResourceType, unsigned> resourceTypeIds;
|
||||
// Map `ResourceTypeOp`s to their (possibly uniqued) name in the problem
|
||||
// instance.
|
||||
SmallDenseMap<Operation *, ResourceType> resourceTypes;
|
||||
|
||||
// Register all resource types in the instance's resource library.
|
||||
auto rsrcLibraryOp = instOp.getResourceLibrary();
|
||||
rsrcLibraryOp.walk([&](ResourceTypeOp rsrcOp) {
|
||||
resourceTypes[rsrcOp] =
|
||||
loadResourceType<ProblemT, ResourceTypePropertyTs...>(prob, rsrcOp,
|
||||
resourceTypeIds);
|
||||
});
|
||||
|
||||
if (auto rsrcLibName = rsrcLibraryOp.getSymNameAttr())
|
||||
prob.setRsrcLibraryName(rsrcLibName);
|
||||
|
||||
// Register all operations first, in order to retain their original order.
|
||||
auto graphOp = instOp.getDependenceGraph();
|
||||
graphOp.walk([&](OperationOp opOp) {
|
||||
|
@ -200,12 +258,52 @@ ProblemT loadProblem(InstanceOp instOp,
|
|||
|
||||
// Load the operator type from `oprOp` if needed.
|
||||
auto &opr = operatorTypes[oprOp];
|
||||
if (!opr)
|
||||
if (!opr.getAttr())
|
||||
opr = loadOperatorType<ProblemT, OperatorTypePropertyTs...>(
|
||||
prob, cast<OperatorTypeOp>(oprOp), operatorTypeIds);
|
||||
|
||||
// Update `opOp`'s property (may be a no-op if `opr` wasn't renamed).
|
||||
prob.setLinkedOperatorType(opOp, opr);
|
||||
|
||||
// Nothing else to check if no linked resource type is set for `opOp`,
|
||||
// because the operation doesn't carry a `LinkedResourceTypeAttr`, or that
|
||||
// class is not part of the `OperationPropertyTs` to load.
|
||||
if (!prob.getLinkedResourceTypes(opOp).has_value())
|
||||
return;
|
||||
|
||||
// Otherwise, inspect the corresponding attribute to make sure the resource
|
||||
// type is available.
|
||||
SmallVector<ResourceType> loadedRsrcs;
|
||||
for (auto attr : opOp.getLinkedResourceTypesAttr().getValue()) {
|
||||
SymbolRefAttr rsrcRef = dyn_cast<SymbolRefAttr>(attr);
|
||||
assert(rsrcRef &&
|
||||
"expected SymbolRefAttr inside LinkedResourceTypesAttr");
|
||||
|
||||
Operation *rsrcOp;
|
||||
// 1) Look in the instance's resource library.
|
||||
rsrcOp = SymbolTable::lookupSymbolIn(rsrcLibraryOp, rsrcRef);
|
||||
// 2) Try to resolve a nested reference to the instance's resource
|
||||
// library.
|
||||
if (!rsrcOp)
|
||||
rsrcOp = SymbolTable::lookupSymbolIn(instOp, rsrcRef);
|
||||
// 3) Look outside of the instance.
|
||||
if (!rsrcOp)
|
||||
rsrcOp = SymbolTable::lookupNearestSymbolFrom(instOp->getParentOp(),
|
||||
rsrcRef);
|
||||
|
||||
assert(rsrcOp && isa<ResourceTypeOp>(rsrcOp)); // checked by verifier
|
||||
|
||||
// Load the resource type from `rsrcOp` if needed.
|
||||
auto &rsrc = resourceTypes[rsrcOp];
|
||||
if (!rsrc.getAttr())
|
||||
rsrc = loadResourceType<ProblemT, ResourceTypePropertyTs...>(
|
||||
prob, cast<ResourceTypeOp>(rsrcOp), resourceTypeIds);
|
||||
|
||||
loadedRsrcs.push_back(rsrc);
|
||||
}
|
||||
|
||||
// Update `opOp`'s property (may be a no-op if `rsrc` wasn't renamed).
|
||||
prob.setLinkedResourceTypes(opOp, loadedRsrcs);
|
||||
});
|
||||
|
||||
// Then walk them again, and load auxiliary dependences as well as any
|
||||
|
@ -265,6 +363,19 @@ ArrayAttr saveOperatorTypeProperties(ProblemT &prob, OperatorType opr,
|
|||
return props.empty() ? ArrayAttr() : b.getArrayAttr(props);
|
||||
}
|
||||
|
||||
template <typename ProblemT, typename... ResourceTypePropertyTs>
|
||||
ArrayAttr saveResourceTypeProperties(ProblemT &prob, ResourceType rsrc,
|
||||
ImplicitLocOpBuilder &b) {
|
||||
SmallVector<Attribute> props;
|
||||
Attribute prop;
|
||||
// Fold expression: Expands to a `getFromProblem` and a conditional
|
||||
// `push_back` call for each of the `ResourceTypePropertyTs`.
|
||||
((prop = ResourceTypePropertyTs::getFromProblem(prob, rsrc, b.getContext()),
|
||||
prop ? props.push_back(prop) : (void)prop),
|
||||
...);
|
||||
return props.empty() ? ArrayAttr() : b.getArrayAttr(props);
|
||||
}
|
||||
|
||||
template <typename ProblemT, typename... DependencePropertyTs>
|
||||
ArrayAttr saveDependenceProperties(ProblemT &prob, Dependence dep,
|
||||
ImplicitLocOpBuilder &b) {
|
||||
|
@ -314,11 +425,13 @@ ArrayAttr saveInstanceProperties(ProblemT &prob, ImplicitLocOpBuilder &b) {
|
|||
/// builder);
|
||||
/// ```
|
||||
template <typename ProblemT, typename... OperationPropertyTs,
|
||||
typename... OperatorTypePropertyTs, typename... DependencePropertyTs,
|
||||
typename... OperatorTypePropertyTs,
|
||||
typename... ResourceTypePropertyTs, typename... DependencePropertyTs,
|
||||
typename... InstancePropertyTs>
|
||||
InstanceOp
|
||||
saveProblem(ProblemT &prob, std::tuple<OperationPropertyTs...> opProps,
|
||||
std::tuple<OperatorTypePropertyTs...> oprProps,
|
||||
std::tuple<ResourceTypePropertyTs...> rsrcProps,
|
||||
std::tuple<DependencePropertyTs...> depProps,
|
||||
std::tuple<InstancePropertyTs...> instProps, OpBuilder &builder) {
|
||||
ImplicitLocOpBuilder b(builder.getUnknownLoc(), builder);
|
||||
|
@ -339,8 +452,22 @@ saveProblem(ProblemT &prob, std::tuple<OperationPropertyTs...> opProps,
|
|||
|
||||
for (auto opr : prob.getOperatorTypes())
|
||||
b.create<OperatorTypeOp>(
|
||||
opr, saveOperatorTypeProperties<ProblemT, OperatorTypePropertyTs...>(
|
||||
prob, opr, b));
|
||||
opr.getAttr(),
|
||||
saveOperatorTypeProperties<ProblemT, OperatorTypePropertyTs...>(
|
||||
prob, opr, b));
|
||||
|
||||
// Emit resource types.
|
||||
b.setInsertionPointToEnd(instOp.getBodyBlock());
|
||||
auto rsrcLibraryOp = b.create<ResourceLibraryOp>();
|
||||
if (auto rsrcLibName = prob.getRsrcLibraryName())
|
||||
rsrcLibraryOp.setSymNameAttr(rsrcLibName);
|
||||
b.setInsertionPointToStart(rsrcLibraryOp.getBodyBlock());
|
||||
|
||||
for (auto rsrc : prob.getResourceTypes())
|
||||
b.create<ResourceTypeOp>(
|
||||
rsrc.getAttr(),
|
||||
saveResourceTypeProperties<ProblemT, ResourceTypePropertyTs...>(
|
||||
prob, rsrc, b));
|
||||
|
||||
// Determine which operations act as source ops for auxiliary dependences, and
|
||||
// therefore need a name. Also, honor names provided by the client.
|
||||
|
@ -437,6 +564,7 @@ template <typename ProblemT>
|
|||
ProblemT loadProblem(InstanceOp instOp) {
|
||||
return loadProblem<ProblemT>(instOp, Default<ProblemT>::operationProperties,
|
||||
Default<ProblemT>::operatorTypeProperties,
|
||||
Default<ProblemT>::resourceTypeProperties,
|
||||
Default<ProblemT>::dependenceProperties,
|
||||
Default<ProblemT>::instanceProperties);
|
||||
}
|
||||
|
@ -450,6 +578,7 @@ template <typename ProblemT>
|
|||
InstanceOp saveProblem(ProblemT &prob, OpBuilder &builder) {
|
||||
return saveProblem<ProblemT>(prob, Default<ProblemT>::operationProperties,
|
||||
Default<ProblemT>::operatorTypeProperties,
|
||||
Default<ProblemT>::resourceTypeProperties,
|
||||
Default<ProblemT>::dependenceProperties,
|
||||
Default<ProblemT>::instanceProperties, builder);
|
||||
}
|
||||
|
@ -460,9 +589,10 @@ InstanceOp saveProblem(ProblemT &prob, OpBuilder &builder) {
|
|||
|
||||
template <>
|
||||
struct Default<scheduling::Problem> {
|
||||
static constexpr auto operationProperties =
|
||||
std::make_tuple(LinkedOperatorTypeAttr(), StartTimeAttr());
|
||||
static constexpr auto operationProperties = std::make_tuple(
|
||||
LinkedOperatorTypeAttr(), LinkedResourceTypesAttr(), StartTimeAttr());
|
||||
static constexpr auto operatorTypeProperties = std::make_tuple(LatencyAttr());
|
||||
static constexpr auto resourceTypeProperties = std::make_tuple();
|
||||
static constexpr auto dependenceProperties = std::make_tuple();
|
||||
static constexpr auto instanceProperties = std::make_tuple();
|
||||
};
|
||||
|
@ -473,6 +603,8 @@ struct Default<scheduling::CyclicProblem> {
|
|||
Default<scheduling::Problem>::operationProperties;
|
||||
static constexpr auto operatorTypeProperties =
|
||||
Default<scheduling::Problem>::operatorTypeProperties;
|
||||
static constexpr auto resourceTypeProperties =
|
||||
Default<scheduling::Problem>::resourceTypeProperties;
|
||||
static constexpr auto dependenceProperties =
|
||||
std::tuple_cat(Default<scheduling::Problem>::dependenceProperties,
|
||||
std::make_tuple(DistanceAttr()));
|
||||
|
@ -489,6 +621,8 @@ struct Default<scheduling::ChainingProblem> {
|
|||
static constexpr auto operatorTypeProperties =
|
||||
std::tuple_cat(Default<scheduling::Problem>::operatorTypeProperties,
|
||||
std::make_tuple(IncomingDelayAttr(), OutgoingDelayAttr()));
|
||||
static constexpr auto resourceTypeProperties =
|
||||
Default<scheduling::Problem>::resourceTypeProperties;
|
||||
static constexpr auto dependenceProperties =
|
||||
Default<scheduling::Problem>::dependenceProperties;
|
||||
static constexpr auto instanceProperties =
|
||||
|
@ -500,8 +634,8 @@ struct Default<scheduling::SharedOperatorsProblem> {
|
|||
static constexpr auto operationProperties =
|
||||
Default<scheduling::Problem>::operationProperties;
|
||||
static constexpr auto operatorTypeProperties =
|
||||
std::tuple_cat(Default<scheduling::Problem>::operatorTypeProperties,
|
||||
std::make_tuple(LimitAttr()));
|
||||
Default<scheduling::Problem>::operatorTypeProperties;
|
||||
static constexpr auto resourceTypeProperties = std::make_tuple(LimitAttr());
|
||||
static constexpr auto dependenceProperties =
|
||||
Default<scheduling::Problem>::dependenceProperties;
|
||||
static constexpr auto instanceProperties =
|
||||
|
@ -514,6 +648,8 @@ struct Default<scheduling::ModuloProblem> {
|
|||
Default<scheduling::Problem>::operationProperties;
|
||||
static constexpr auto operatorTypeProperties =
|
||||
Default<scheduling::SharedOperatorsProblem>::operatorTypeProperties;
|
||||
static constexpr auto resourceTypeProperties =
|
||||
Default<scheduling::SharedOperatorsProblem>::resourceTypeProperties;
|
||||
static constexpr auto dependenceProperties =
|
||||
Default<scheduling::CyclicProblem>::dependenceProperties;
|
||||
static constexpr auto instanceProperties =
|
||||
|
@ -526,6 +662,8 @@ struct Default<scheduling::ChainingCyclicProblem> {
|
|||
Default<scheduling::ChainingProblem>::operationProperties;
|
||||
static constexpr auto operatorTypeProperties =
|
||||
Default<scheduling::ChainingProblem>::operatorTypeProperties;
|
||||
static constexpr auto resourceTypeProperties =
|
||||
Default<scheduling::ChainingProblem>::resourceTypeProperties;
|
||||
static constexpr auto dependenceProperties =
|
||||
Default<scheduling::CyclicProblem>::dependenceProperties;
|
||||
static constexpr auto instanceProperties =
|
||||
|
|
|
@ -95,7 +95,49 @@ public:
|
|||
using Dependence = detail::Dependence;
|
||||
|
||||
/// Operator types are distinguished by name (chosen by the client).
|
||||
using OperatorType = mlir::StringAttr;
|
||||
struct OperatorType {
|
||||
mlir::StringAttr attr;
|
||||
|
||||
OperatorType() = default;
|
||||
OperatorType(mlir::StringAttr attr) : attr(attr) {}
|
||||
|
||||
static OperatorType get(mlir::MLIRContext *ctx, llvm::StringRef name) {
|
||||
return OperatorType{mlir::StringAttr::get(ctx, name)};
|
||||
}
|
||||
|
||||
mlir::StringAttr getAttr() const { return attr; }
|
||||
|
||||
mlir::StringRef getValue() const { return attr.getValue(); }
|
||||
|
||||
std::string str() const { return attr.str(); }
|
||||
|
||||
friend bool operator==(const OperatorType &lhs, const OperatorType &rhs) {
|
||||
return lhs.attr == rhs.attr;
|
||||
}
|
||||
};
|
||||
|
||||
/// Resource types are distinguished by name (chosen by the client).
|
||||
struct ResourceType {
|
||||
mlir::StringAttr attr;
|
||||
|
||||
ResourceType() = default;
|
||||
ResourceType(mlir::StringAttr attr) : attr(attr) {}
|
||||
|
||||
static ResourceType get(mlir::MLIRContext *ctx, llvm::StringRef name) {
|
||||
return ResourceType{mlir::StringAttr::get(ctx, name)};
|
||||
}
|
||||
|
||||
mlir::StringAttr getAttr() const { return attr; }
|
||||
|
||||
mlir::StringRef getValue() const { return attr.getValue(); }
|
||||
|
||||
std::string str() const { return attr.str(); }
|
||||
|
||||
friend bool operator==(const ResourceType &lhs, const ResourceType &rhs) {
|
||||
return lhs.attr == rhs.attr;
|
||||
}
|
||||
bool operator!=(const ResourceType &rhs) const { return !(*this == rhs); }
|
||||
};
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Aliases for containers storing the problem components and properties
|
||||
|
@ -104,6 +146,7 @@ public:
|
|||
using OperationSet = llvm::SetVector<Operation *>;
|
||||
using DependenceRange = llvm::iterator_range<detail::DependenceIterator>;
|
||||
using OperatorTypeSet = llvm::SetVector<OperatorType>;
|
||||
using ResourceTypeSet = llvm::SetVector<ResourceType>;
|
||||
|
||||
protected:
|
||||
using AuxDependenceMap =
|
||||
|
@ -116,6 +159,8 @@ protected:
|
|||
template <typename T>
|
||||
using OperatorTypeProperty = llvm::DenseMap<OperatorType, std::optional<T>>;
|
||||
template <typename T>
|
||||
using ResourceTypeProperty = llvm::DenseMap<ResourceType, std::optional<T>>;
|
||||
template <typename T>
|
||||
using InstanceProperty = std::optional<T>;
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
@ -130,9 +175,11 @@ private:
|
|||
OperationSet operations;
|
||||
AuxDependenceMap auxDependences;
|
||||
OperatorTypeSet operatorTypes;
|
||||
ResourceTypeSet resourceTypes;
|
||||
|
||||
// Operation properties
|
||||
OperationProperty<OperatorType> linkedOperatorType;
|
||||
OperationProperty<SmallVector<ResourceType>> linkedResourceTypes;
|
||||
OperationProperty<unsigned> startTime;
|
||||
|
||||
// Operator type properties
|
||||
|
@ -153,10 +200,17 @@ public:
|
|||
/// Include \p opr in this scheduling problem.
|
||||
void insertOperatorType(OperatorType opr) { operatorTypes.insert(opr); }
|
||||
|
||||
/// Include \p rsrc in this scheduling problem.
|
||||
void insertResourceType(ResourceType rsrc) { resourceTypes.insert(rsrc); }
|
||||
|
||||
/// Retrieves the operator type identified by the client-specific \p name. The
|
||||
/// operator type is automatically registered in the scheduling problem.
|
||||
OperatorType getOrInsertOperatorType(StringRef name);
|
||||
|
||||
/// Retrieves the resource type identified by the client-specific \p name. The
|
||||
/// resource type is automatically registered in the scheduling problem.
|
||||
ResourceType getOrInsertResourceType(StringRef name);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Access to problem components
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
@ -188,6 +242,12 @@ public:
|
|||
/// Return the set of operator types.
|
||||
const OperatorTypeSet &getOperatorTypes() { return operatorTypes; }
|
||||
|
||||
/// Return true if \p rsrc is part of this problem.
|
||||
bool hasResourceType(ResourceType rsrc) {
|
||||
return resourceTypes.contains(rsrc);
|
||||
}
|
||||
/// Return the set of resource types.
|
||||
const ResourceTypeSet &getResourceTypes() { return resourceTypes; }
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Access to properties
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
@ -200,6 +260,15 @@ public:
|
|||
linkedOperatorType[op] = opr;
|
||||
}
|
||||
|
||||
/// The linked resource type provides the available resources for \p op.
|
||||
std::optional<SmallVector<ResourceType>>
|
||||
getLinkedResourceTypes(Operation *op) {
|
||||
return linkedResourceTypes.lookup(op);
|
||||
}
|
||||
void setLinkedResourceTypes(Operation *op, SmallVector<ResourceType> rsrc) {
|
||||
linkedResourceTypes[op] = rsrc;
|
||||
}
|
||||
|
||||
/// The latency is the number of cycles \p opr needs to compute its result.
|
||||
std::optional<unsigned> getLatency(OperatorType opr) {
|
||||
return latency.lookup(opr);
|
||||
|
@ -227,7 +296,7 @@ public:
|
|||
// Optional names (for exporting and debugging instances)
|
||||
//===--------------------------------------------------------------------===//
|
||||
private:
|
||||
StringAttr instanceName, libraryName;
|
||||
StringAttr instanceName, libraryName, rsrcLibraryName;
|
||||
SmallDenseMap<Operation *, StringAttr> operationNames;
|
||||
|
||||
public:
|
||||
|
@ -237,6 +306,9 @@ public:
|
|||
StringAttr getLibraryName() { return libraryName; }
|
||||
void setLibraryName(StringAttr name) { libraryName = name; }
|
||||
|
||||
StringAttr getRsrcLibraryName() { return rsrcLibraryName; }
|
||||
void setRsrcLibraryName(StringAttr name) { rsrcLibraryName = name; }
|
||||
|
||||
StringAttr getOperationName(Operation *op) {
|
||||
return operationNames.lookup(op);
|
||||
}
|
||||
|
@ -256,14 +328,15 @@ public:
|
|||
virtual PropertyStringVector getProperties(OperatorType opr);
|
||||
virtual PropertyStringVector getProperties();
|
||||
|
||||
virtual PropertyStringVector getProperties(ResourceType rsrc);
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Property-specific validators
|
||||
//===--------------------------------------------------------------------===//
|
||||
protected:
|
||||
/// \p op is linked to a registered operator type.
|
||||
virtual LogicalResult checkLinkedOperatorType(Operation *op);
|
||||
/// \p opr has a latency.
|
||||
virtual LogicalResult checkLatency(OperatorType opr);
|
||||
/// \p op has a latency.
|
||||
virtual LogicalResult checkLatency(Operation *op);
|
||||
/// \p op has a start time.
|
||||
virtual LogicalResult verifyStartTime(Operation *op);
|
||||
/// \p dep's source operation is available before \p dep's destination
|
||||
|
@ -400,7 +473,7 @@ public:
|
|||
};
|
||||
|
||||
/// This class models a resource-constrained scheduling problem. An optional,
|
||||
/// non-zero *limit* marks operator types to be *shared* by the operations using
|
||||
/// non-zero *limit* marks resource types to be *shared* by the operations using
|
||||
/// them. In an HLS setting, this corresponds to multiplexing multiple
|
||||
/// operations onto a pre-allocated number of operator instances. These
|
||||
/// instances are assumed to be *fully pipelined*, meaning each instance can
|
||||
|
@ -419,23 +492,23 @@ protected:
|
|||
SharedOperatorsProblem() = default;
|
||||
|
||||
private:
|
||||
OperatorTypeProperty<unsigned> limit;
|
||||
ResourceTypeProperty<unsigned> limit;
|
||||
|
||||
public:
|
||||
/// The limit is the maximum number of operations using \p opr that are
|
||||
/// allowed to start in the same time step.
|
||||
std::optional<unsigned> getLimit(OperatorType opr) {
|
||||
return limit.lookup(opr);
|
||||
/// The limit is the maximum number of operations using \p rsrc that are
|
||||
/// available in the target hardware.
|
||||
std::optional<unsigned> getLimit(ResourceType rsrc) {
|
||||
return limit.lookup(rsrc);
|
||||
}
|
||||
void setLimit(OperatorType opr, unsigned val) { limit[opr] = val; }
|
||||
void setLimit(ResourceType rsrc, unsigned val) { limit[rsrc] = val; }
|
||||
|
||||
virtual PropertyStringVector getProperties(OperatorType opr) override;
|
||||
virtual PropertyStringVector getProperties(ResourceType rsrc) override;
|
||||
|
||||
protected:
|
||||
/// If \p opr is limited, it has a non-zero latency.
|
||||
virtual LogicalResult checkLatency(OperatorType opr) override;
|
||||
/// \p opr is not oversubscribed in any time step.
|
||||
virtual LogicalResult verifyUtilization(OperatorType opr);
|
||||
/// If \p op is limited, it has a non-zero latency.
|
||||
virtual LogicalResult checkLatency(Operation *op) override;
|
||||
/// \p rsrc is not oversubscribed in any time step.
|
||||
virtual LogicalResult verifyUtilization(ResourceType rsrc);
|
||||
|
||||
public:
|
||||
virtual LogicalResult verify() override;
|
||||
|
@ -462,7 +535,7 @@ protected:
|
|||
ModuloProblem() = default;
|
||||
|
||||
/// \p opr is not oversubscribed in any congruence class modulo II.
|
||||
virtual LogicalResult verifyUtilization(OperatorType opr) override;
|
||||
virtual LogicalResult verifyUtilization(ResourceType rsrc) override;
|
||||
|
||||
public:
|
||||
virtual LogicalResult verify() override;
|
||||
|
@ -499,4 +572,54 @@ public:
|
|||
} // namespace scheduling
|
||||
} // namespace circt
|
||||
|
||||
namespace llvm {
|
||||
|
||||
template <>
|
||||
struct DenseMapInfo<circt::scheduling::Problem::OperatorType> {
|
||||
static inline circt::scheduling::Problem::OperatorType getEmptyKey() {
|
||||
return circt::scheduling::Problem::OperatorType(
|
||||
DenseMapInfo<mlir::StringAttr>::getEmptyKey());
|
||||
}
|
||||
|
||||
static inline circt::scheduling::Problem::OperatorType getTombstoneKey() {
|
||||
return circt::scheduling::Problem::OperatorType{
|
||||
DenseMapInfo<mlir::StringAttr>::getTombstoneKey()};
|
||||
}
|
||||
|
||||
static unsigned
|
||||
getHashValue(const circt::scheduling::Problem::OperatorType &val) {
|
||||
return DenseMapInfo<mlir::StringAttr>::getHashValue(val.attr);
|
||||
}
|
||||
|
||||
static bool isEqual(const circt::scheduling::Problem::OperatorType &lhs,
|
||||
const circt::scheduling::Problem::OperatorType &rhs) {
|
||||
return DenseMapInfo<mlir::StringAttr>::isEqual(lhs.attr, rhs.attr);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DenseMapInfo<circt::scheduling::Problem::ResourceType> {
|
||||
static inline circt::scheduling::Problem::ResourceType getEmptyKey() {
|
||||
return circt::scheduling::Problem::ResourceType(
|
||||
DenseMapInfo<mlir::StringAttr>::getEmptyKey());
|
||||
}
|
||||
|
||||
static inline circt::scheduling::Problem::ResourceType getTombstoneKey() {
|
||||
return circt::scheduling::Problem::ResourceType(
|
||||
DenseMapInfo<mlir::StringAttr>::getTombstoneKey());
|
||||
}
|
||||
|
||||
static unsigned
|
||||
getHashValue(const circt::scheduling::Problem::ResourceType &val) {
|
||||
return DenseMapInfo<mlir::StringAttr>::getHashValue(val.attr);
|
||||
}
|
||||
|
||||
static bool isEqual(const circt::scheduling::Problem::ResourceType &lhs,
|
||||
const circt::scheduling::Problem::ResourceType &rhs) {
|
||||
return DenseMapInfo<mlir::StringAttr>::isEqual(lhs.attr, rhs.attr);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif // CIRCT_SCHEDULING_PROBLEMS_H
|
||||
|
|
|
@ -87,6 +87,9 @@ ModuloProblem AffineToLoopSchedule::getModuloProblem(CyclicProblem &prob) {
|
|||
if (latency.has_value())
|
||||
modProb.setLatency(opr.value(), latency.value());
|
||||
}
|
||||
auto rsrc = prob.getLinkedResourceTypes(op);
|
||||
if (rsrc.has_value())
|
||||
modProb.setLinkedResourceTypes(op, rsrc.value());
|
||||
modProb.insertOperation(op);
|
||||
}
|
||||
|
||||
|
@ -323,8 +326,14 @@ LogicalResult AffineToLoopSchedule::populateOperatorTypes(
|
|||
Problem::OperatorType memOpr = problem.getOrInsertOperatorType(
|
||||
"mem_" + std::to_string(hash_value(memRef)));
|
||||
problem.setLatency(memOpr, 1);
|
||||
problem.setLimit(memOpr, 1);
|
||||
problem.setLinkedOperatorType(memOp, memOpr);
|
||||
|
||||
auto memRsrc = problem.getOrInsertResourceType(
|
||||
"mem_" + std::to_string(hash_value(memRef)) + "_rsrc");
|
||||
problem.setLimit(memRsrc, 1);
|
||||
problem.setLinkedResourceTypes(
|
||||
memOp, SmallVector<Problem::ResourceType>{memRsrc});
|
||||
|
||||
return WalkResult::advance();
|
||||
})
|
||||
.Case<AffineLoadOp, memref::LoadOp>([&](Operation *memOp) {
|
||||
|
@ -337,8 +346,14 @@ LogicalResult AffineToLoopSchedule::populateOperatorTypes(
|
|||
Problem::OperatorType memOpr = problem.getOrInsertOperatorType(
|
||||
"mem_" + std::to_string(hash_value(memRef)));
|
||||
problem.setLatency(memOpr, 1);
|
||||
problem.setLimit(memOpr, 1);
|
||||
problem.setLinkedOperatorType(memOp, memOpr);
|
||||
|
||||
auto memRsrc = problem.getOrInsertResourceType(
|
||||
"mem_" + std::to_string(hash_value(memRef)) + "_rsrc");
|
||||
problem.setLimit(memRsrc, 1);
|
||||
problem.setLinkedResourceTypes(
|
||||
memOp, SmallVector<Problem::ResourceType>{memRsrc});
|
||||
|
||||
return WalkResult::advance();
|
||||
})
|
||||
.Case<MulIOp>([&](Operation *mcOp) {
|
||||
|
@ -368,7 +383,7 @@ LogicalResult AffineToLoopSchedule::solveSchedulingProblem(
|
|||
LLVM_DEBUG(forOp.getBody()->walk<WalkOrder::PreOrder>([&](Operation *op) {
|
||||
llvm::dbgs() << "Scheduling inputs for " << *op;
|
||||
auto opr = problem.getLinkedOperatorType(op);
|
||||
llvm::dbgs() << "\n opr = " << opr;
|
||||
llvm::dbgs() << "\n opr = " << opr->getAttr();
|
||||
llvm::dbgs() << "\n latency = " << problem.getLatency(*opr);
|
||||
for (auto dep : problem.getDependences(op))
|
||||
if (dep.isAuxiliary())
|
||||
|
|
|
@ -72,7 +72,7 @@ ScheduleLinearPipelinePass::schedulePipeline(UnscheduledPipelineOp pipeline) {
|
|||
Problem problem(pipeline);
|
||||
|
||||
DenseMap<SymbolRefAttr, Problem::OperatorType> operatorTypes;
|
||||
SmallDenseMap<StringAttr, unsigned> oprIds;
|
||||
SmallDenseMap<ssp::OperatorType, unsigned> oprIds;
|
||||
|
||||
// Set operation operator types.
|
||||
auto returnOp =
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "circt/Support/LLVM.h"
|
||||
|
||||
#include "mlir/IR/Builders.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace circt;
|
||||
using namespace circt::ssp;
|
||||
|
@ -44,6 +45,10 @@ OperatorLibraryOp InstanceOp::getOperatorLibrary() {
|
|||
return *getOps<OperatorLibraryOp>().begin();
|
||||
}
|
||||
|
||||
ResourceLibraryOp InstanceOp::getResourceLibrary() {
|
||||
return *getOps<ResourceLibraryOp>().begin();
|
||||
}
|
||||
|
||||
DependenceGraphOp InstanceOp::getDependenceGraph() {
|
||||
return *getOps<DependenceGraphOp>().begin();
|
||||
}
|
||||
|
@ -114,6 +119,26 @@ ParseResult OperationOp::parse(OpAsmParser &parser, OperationState &result) {
|
|||
parseDependenceSourceWithAttrDict))
|
||||
return failure();
|
||||
|
||||
if (succeeded(parser.parseOptionalKeyword("uses"))) {
|
||||
SmallVector<Attribute> rsrcRefs;
|
||||
auto parseOne = [&]() -> ParseResult {
|
||||
SymbolRefAttr rsrcRef;
|
||||
if (parser.parseAttribute(rsrcRef))
|
||||
return parser.emitError(parser.getCurrentLocation(),
|
||||
"expected symbol reference inside uses[...]");
|
||||
rsrcRefs.push_back(rsrcRef);
|
||||
return success();
|
||||
};
|
||||
if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Square,
|
||||
parseOne))
|
||||
return failure();
|
||||
|
||||
auto linkedRsrcsAttr = builder.getAttr<LinkedResourceTypesAttr>(
|
||||
builder.getArrayAttr(rsrcRefs));
|
||||
|
||||
alreadyParsed.push_back(linkedRsrcsAttr);
|
||||
}
|
||||
|
||||
if (!dependences.empty())
|
||||
result.addAttribute(builder.getStringAttr("dependences"),
|
||||
builder.getArrayAttr(dependences));
|
||||
|
@ -198,6 +223,21 @@ void OperationOp::print(OpAsmPrinter &p) {
|
|||
}
|
||||
p << ')';
|
||||
|
||||
if (ArrayAttr properties = getSspPropertiesAttr()) {
|
||||
for (auto attr : properties) {
|
||||
if (auto linkedRsrcs = dyn_cast<LinkedResourceTypesAttr>(attr)) {
|
||||
auto rsrcList = linkedRsrcs.getValue();
|
||||
if (!rsrcList.empty()) {
|
||||
p << " uses[";
|
||||
llvm::interleaveComma(
|
||||
rsrcList, p, [&](Attribute rsrc) { p.printAttribute(rsrc); });
|
||||
p << "]";
|
||||
}
|
||||
alreadyPrinted.push_back(linkedRsrcs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Properties
|
||||
if (ArrayAttr properties = getSspPropertiesAttr()) {
|
||||
p << ' ';
|
||||
|
@ -307,6 +347,17 @@ LinkedOperatorTypeAttr OperationOp::getLinkedOperatorTypeAttr() {
|
|||
return {};
|
||||
}
|
||||
|
||||
LinkedResourceTypesAttr OperationOp::getLinkedResourceTypesAttr() {
|
||||
if (ArrayAttr properties = getSspPropertiesAttr()) {
|
||||
const auto *it = llvm::find_if(properties, [](Attribute a) {
|
||||
return isa<LinkedResourceTypesAttr>(a);
|
||||
});
|
||||
if (it != properties.end())
|
||||
return cast<LinkedResourceTypesAttr>(*it);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Wrappers for the `custom<Properties>` ODS directive.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -49,7 +49,7 @@ LogicalResult scheduling::scheduleCPSAT(SharedOperatorsProblem &prob,
|
|||
|
||||
DenseMap<Operation *, IntVar> taskStarts;
|
||||
DenseMap<Operation *, IntVar> taskEnds;
|
||||
DenseMap<Problem::OperatorType, SmallVector<IntervalVar, 4>>
|
||||
DenseMap<Problem::ResourceType, SmallVector<IntervalVar, 4>>
|
||||
resourcesToTaskIntervals;
|
||||
|
||||
// First get horizon, i.e., the time taken if all operations were executed
|
||||
|
@ -75,14 +75,19 @@ LogicalResult scheduling::scheduleCPSAT(SharedOperatorsProblem &prob,
|
|||
.WithName((Twine("end_of_task_") + Twine(i)).str());
|
||||
taskStarts[task] = startVar;
|
||||
taskEnds[task] = endVar;
|
||||
auto resource = prob.getLinkedOperatorType(task);
|
||||
unsigned duration = *prob.getLatency(*resource);
|
||||
auto opr = prob.getLinkedOperatorType(task);
|
||||
unsigned duration = *prob.getLatency(*opr);
|
||||
IntervalVar taskInterval =
|
||||
cpModel.NewIntervalVar(startVar, duration, endVar)
|
||||
.WithName((Twine("task_interval_") + Twine(i)).str());
|
||||
|
||||
if (prob.getLimit(*resource))
|
||||
resourcesToTaskIntervals[resource.value()].emplace_back(taskInterval);
|
||||
auto resourceListOpt = prob.getLinkedResourceTypes(task);
|
||||
if (resourceListOpt) {
|
||||
for (const auto &resource : *resourceListOpt) {
|
||||
if (auto limitOpt = prob.getLimit(resource))
|
||||
resourcesToTaskIntervals[resource].push_back(taskInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for cycles and otherwise establish operation ordering
|
||||
|
@ -100,7 +105,7 @@ LogicalResult scheduling::scheduleCPSAT(SharedOperatorsProblem &prob,
|
|||
// Establish "cumulative" constraints in order to constrain maximum
|
||||
// concurrent usage of operators.
|
||||
for (auto resourceToTaskIntervals : resourcesToTaskIntervals) {
|
||||
Problem::OperatorType &resource = resourceToTaskIntervals.getFirst();
|
||||
Problem::ResourceType &resource = resourceToTaskIntervals.getFirst();
|
||||
auto capacity = prob.getLimit(resource);
|
||||
SmallVector<IntervalVar, 4> &taskIntervals =
|
||||
resourceToTaskIntervals.getSecond();
|
||||
|
@ -115,7 +120,8 @@ LogicalResult scheduling::scheduleCPSAT(SharedOperatorsProblem &prob,
|
|||
auto i = item.index();
|
||||
auto taskInterval = item.value();
|
||||
IntVar demandVar = cpModel.NewIntVar(Domain(1)).WithName(
|
||||
(Twine("demand_") + Twine(i) + Twine("_") + Twine(resource.strref()))
|
||||
(Twine("demand_") + Twine(i) + Twine("_") +
|
||||
Twine(resource.getAttr().strref()))
|
||||
.str());
|
||||
// Conventional formulation for SharedOperatorsProblem;
|
||||
// interval during which the resource is occupied has size 1.
|
||||
|
|
|
@ -50,6 +50,12 @@ Problem::OperatorType Problem::getOrInsertOperatorType(StringRef name) {
|
|||
return opr;
|
||||
}
|
||||
|
||||
Problem::ResourceType Problem::getOrInsertResourceType(StringRef name) {
|
||||
auto rsrc = ResourceType::get(containingOp->getContext(), name);
|
||||
resourceTypes.insert(rsrc);
|
||||
return rsrc;
|
||||
}
|
||||
|
||||
Problem::DependenceRange Problem::getDependences(Operation *op) {
|
||||
return DependenceRange(DependenceIterator(*this, op),
|
||||
DependenceIterator(*this, op, /*end=*/true));
|
||||
|
@ -77,6 +83,10 @@ Problem::PropertyStringVector Problem::getProperties(OperatorType opr) {
|
|||
|
||||
Problem::PropertyStringVector Problem::getProperties() { return {}; }
|
||||
|
||||
Problem::PropertyStringVector Problem::getProperties(ResourceType rsrc) {
|
||||
return {};
|
||||
}
|
||||
|
||||
LogicalResult Problem::checkLinkedOperatorType(Operation *op) {
|
||||
if (!getLinkedOperatorType(op))
|
||||
return op->emitError("Operation is not linked to an operator type");
|
||||
|
@ -85,22 +95,27 @@ LogicalResult Problem::checkLinkedOperatorType(Operation *op) {
|
|||
return success();
|
||||
}
|
||||
|
||||
LogicalResult Problem::checkLatency(OperatorType opr) {
|
||||
if (!getLatency(opr))
|
||||
LogicalResult Problem::checkLatency(Operation *op) {
|
||||
auto maybeOpr = getLinkedOperatorType(op);
|
||||
if (!maybeOpr)
|
||||
return getContainingOp()->emitError()
|
||||
<< "Operator type '" << opr.getValue() << "' has no latency";
|
||||
<< "Operation is missing a linked operator type";
|
||||
|
||||
if (!getLatency(*maybeOpr))
|
||||
return getContainingOp()->emitError()
|
||||
<< "Operator type '" << maybeOpr->getValue() << "' has no latency";
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult Problem::check() {
|
||||
for (auto *op : getOperations())
|
||||
for (auto *op : getOperations()) {
|
||||
if (failed(checkLinkedOperatorType(op)))
|
||||
return failure();
|
||||
|
||||
for (auto opr : getOperatorTypes())
|
||||
if (failed(checkLatency(opr)))
|
||||
if (failed(checkLatency(op)))
|
||||
return failure();
|
||||
}
|
||||
|
||||
return success();
|
||||
}
|
||||
|
@ -227,20 +242,20 @@ LogicalResult ChainingProblem::checkDelays(OperatorType opr) {
|
|||
|
||||
if (!incomingDelay || !outgoingDelay)
|
||||
return getContainingOp()->emitError()
|
||||
<< "Missing delays for operator type '" << opr << "'";
|
||||
<< "Missing delays for operator type '" << opr.getAttr() << "'";
|
||||
|
||||
float iDel = *incomingDelay;
|
||||
float oDel = *outgoingDelay;
|
||||
|
||||
if (iDel < 0.0f || oDel < 0.0f)
|
||||
return getContainingOp()->emitError()
|
||||
<< "Negative delays for operator type '" << opr << "'";
|
||||
<< "Negative delays for operator type '" << opr.getAttr() << "'";
|
||||
|
||||
if (*getLatency(opr) == 0 && iDel != oDel)
|
||||
return getContainingOp()->emitError()
|
||||
<< "Incoming & outgoing delay must be equal for zero-latency "
|
||||
"operator type '"
|
||||
<< opr << "'";
|
||||
<< opr.getAttr() << "'";
|
||||
|
||||
return success();
|
||||
}
|
||||
|
@ -319,39 +334,59 @@ LogicalResult ChainingProblem::verify() {
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
Problem::PropertyStringVector
|
||||
SharedOperatorsProblem::getProperties(OperatorType opr) {
|
||||
auto psv = Problem::getProperties(opr);
|
||||
if (auto limit = getLimit(opr))
|
||||
SharedOperatorsProblem::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(OperatorType opr) {
|
||||
if (failed(Problem::checkLatency(opr)))
|
||||
LogicalResult SharedOperatorsProblem::checkLatency(Operation *op) {
|
||||
if (failed(Problem::checkLatency(op)))
|
||||
return failure();
|
||||
|
||||
auto limit = getLimit(opr);
|
||||
if (limit && *limit > 0 && *getLatency(opr) == 0)
|
||||
return getContainingOp()->emitError()
|
||||
<< "Limited operator type '" << opr.getValue()
|
||||
<< "' has zero latency.";
|
||||
auto maybeRsrcs = getLinkedResourceTypes(op);
|
||||
if (!maybeRsrcs)
|
||||
return success();
|
||||
|
||||
// `linkedOprType` is not null since it must have been checked by the base
|
||||
// class' `checkLatency`.
|
||||
OperatorType linkedOprType = *getLinkedOperatorType(op);
|
||||
|
||||
for (auto rsrc : *maybeRsrcs) {
|
||||
auto limit = getLimit(rsrc);
|
||||
if (limit && *limit > 0 && *getLatency(linkedOprType) == 0)
|
||||
return getContainingOp()->emitError()
|
||||
<< "Operator type '" << linkedOprType.getValue()
|
||||
<< "' using limited resource '" << rsrc.getValue()
|
||||
<< "' has zero latency.";
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult SharedOperatorsProblem::verifyUtilization(OperatorType opr) {
|
||||
auto limit = getLimit(opr);
|
||||
LogicalResult SharedOperatorsProblem::verifyUtilization(ResourceType rsrc) {
|
||||
auto limit = getLimit(rsrc);
|
||||
if (!limit)
|
||||
return success();
|
||||
|
||||
llvm::SmallDenseMap<unsigned, unsigned> nOpsPerTimeStep;
|
||||
for (auto *op : getOperations())
|
||||
if (opr == *getLinkedOperatorType(op))
|
||||
++nOpsPerTimeStep[*getStartTime(op)];
|
||||
for (auto *op : getOperations()) {
|
||||
auto maybeRsrcs = getLinkedResourceTypes(op);
|
||||
if (!maybeRsrcs)
|
||||
continue;
|
||||
|
||||
if (llvm::none_of(*maybeRsrcs, [&](ResourceType linkedRsrc) {
|
||||
return linkedRsrc == rsrc;
|
||||
}))
|
||||
continue;
|
||||
|
||||
++nOpsPerTimeStep[*getStartTime(op)];
|
||||
}
|
||||
|
||||
for (auto &kv : nOpsPerTimeStep)
|
||||
if (kv.second > *limit)
|
||||
return getContainingOp()->emitError()
|
||||
<< "Operator type '" << opr.getValue() << "' is oversubscribed."
|
||||
<< "Resource type '" << rsrc.getValue() << "' is oversubscribed."
|
||||
<< "\n time step: " << kv.first
|
||||
<< "\n #operations: " << kv.second << "\n limit: " << *limit;
|
||||
|
||||
|
@ -362,8 +397,8 @@ LogicalResult SharedOperatorsProblem::verify() {
|
|||
if (failed(Problem::verify()))
|
||||
return failure();
|
||||
|
||||
for (auto opr : getOperatorTypes())
|
||||
if (failed(verifyUtilization(opr)))
|
||||
for (auto rsrc : getResourceTypes())
|
||||
if (failed(verifyUtilization(rsrc)))
|
||||
return failure();
|
||||
|
||||
return success();
|
||||
|
@ -373,21 +408,30 @@ LogicalResult SharedOperatorsProblem::verify() {
|
|||
// ModuloProblem
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
LogicalResult ModuloProblem::verifyUtilization(OperatorType opr) {
|
||||
auto limit = getLimit(opr);
|
||||
LogicalResult ModuloProblem::verifyUtilization(ResourceType rsrc) {
|
||||
auto limit = getLimit(rsrc);
|
||||
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 *op : getOperations()) {
|
||||
auto maybeRsrcs = getLinkedResourceTypes(op);
|
||||
if (!maybeRsrcs)
|
||||
continue;
|
||||
|
||||
if (llvm::none_of(*maybeRsrcs, [&](ResourceType linkedRsrc) {
|
||||
return linkedRsrc == rsrc;
|
||||
}))
|
||||
continue;
|
||||
|
||||
++nOpsPerCongruenceClass[*getStartTime(op) % ii];
|
||||
}
|
||||
|
||||
for (auto &kv : nOpsPerCongruenceClass)
|
||||
if (kv.second > *limit)
|
||||
return getContainingOp()->emitError()
|
||||
<< "Operator type '" << opr.getValue() << "' is oversubscribed."
|
||||
<< "Resource type '" << rsrc.getValue() << "' is oversubscribed."
|
||||
<< "\n congruence class: " << kv.first
|
||||
<< "\n #operations: " << kv.second << "\n limit: " << *limit;
|
||||
|
||||
|
@ -400,8 +444,8 @@ LogicalResult ModuloProblem::verify() {
|
|||
|
||||
// Don't call SharedOperatorsProblem::verify() here to prevent redundant
|
||||
// verification of the base problem.
|
||||
for (auto opr : getOperatorTypes())
|
||||
if (failed(verifyUtilization(opr)))
|
||||
for (auto rsrc : getResourceTypes())
|
||||
if (failed(verifyUtilization(rsrc)))
|
||||
return failure();
|
||||
|
||||
return success();
|
||||
|
|
|
@ -243,8 +243,8 @@ private:
|
|||
|
||||
using TableType = SmallDenseMap<unsigned, DenseSet<Operation *>>;
|
||||
using ReverseTableType = SmallDenseMap<Operation *, unsigned>;
|
||||
SmallDenseMap<Problem::OperatorType, TableType> tables;
|
||||
SmallDenseMap<Problem::OperatorType, ReverseTableType> reverseTables;
|
||||
SmallDenseMap<Problem::ResourceType, TableType> tables;
|
||||
SmallDenseMap<Problem::ResourceType, ReverseTableType> reverseTables;
|
||||
|
||||
explicit MRT(ModuloSimplexScheduler &sched) : sched(sched) {}
|
||||
LogicalResult enter(Operation *op, unsigned timeStep);
|
||||
|
@ -874,7 +874,12 @@ LogicalResult CyclicSimplexScheduler::schedule() {
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static bool isLimited(Operation *op, SharedOperatorsProblem &prob) {
|
||||
return prob.getLimit(*prob.getLinkedOperatorType(op)).value_or(0) > 0;
|
||||
auto maybeRsrcs = prob.getLinkedResourceTypes(op);
|
||||
if (!maybeRsrcs)
|
||||
return false;
|
||||
return llvm::any_of(*maybeRsrcs, [&](Problem::ResourceType rsrc) {
|
||||
return prob.getLimit(rsrc).value_or(0) > 0;
|
||||
});
|
||||
}
|
||||
|
||||
LogicalResult SharedOperatorsSimplexScheduler::schedule() {
|
||||
|
@ -923,21 +928,28 @@ LogicalResult SharedOperatorsSimplexScheduler::schedule() {
|
|||
getStartTime(startTimeVariables[b]);
|
||||
});
|
||||
|
||||
// Store the number of operations using an operator type in a particular time
|
||||
// Store the number of operations using a resource type in a particular time
|
||||
// step.
|
||||
SmallDenseMap<Problem::OperatorType, SmallDenseMap<unsigned, unsigned>>
|
||||
SmallDenseMap<Problem::ResourceType, SmallDenseMap<unsigned, unsigned>>
|
||||
reservationTable;
|
||||
|
||||
for (auto *op : limitedOps) {
|
||||
auto opr = *prob.getLinkedOperatorType(op);
|
||||
unsigned limit = prob.getLimit(opr).value_or(0);
|
||||
auto maybeRsrcs = prob.getLinkedResourceTypes(op);
|
||||
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");
|
||||
|
||||
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.
|
||||
unsigned startTimeVar = startTimeVariables[op];
|
||||
unsigned candTime = getStartTime(startTimeVar);
|
||||
while (reservationTable[opr].lookup(candTime) == limit)
|
||||
while (reservationTable[rsrc].lookup(candTime) == limit)
|
||||
++candTime;
|
||||
|
||||
// Fix the start time. As explained above, this cannot make the problem
|
||||
|
@ -947,7 +959,7 @@ LogicalResult SharedOperatorsSimplexScheduler::schedule() {
|
|||
(void)fixed;
|
||||
|
||||
// Record the operator use.
|
||||
++reservationTable[opr][candTime];
|
||||
++reservationTable[rsrc][candTime];
|
||||
|
||||
LLVM_DEBUG(dbgs() << "After scheduling " << startTimeVar
|
||||
<< " to t=" << candTime << ":\n";
|
||||
|
@ -993,15 +1005,22 @@ LogicalResult ModuloSimplexScheduler::checkLastOp() {
|
|||
|
||||
LogicalResult ModuloSimplexScheduler::MRT::enter(Operation *op,
|
||||
unsigned timeStep) {
|
||||
auto opr = *sched.prob.getLinkedOperatorType(op);
|
||||
auto lim = *sched.prob.getLimit(opr);
|
||||
auto maybeRsrcs = sched.prob.getLinkedResourceTypes(op);
|
||||
assert(maybeRsrcs && "Operation must have linked resource types");
|
||||
|
||||
auto &rsrcs = *maybeRsrcs;
|
||||
assert(rsrcs.size() == 1 &&
|
||||
"Multi-resource operations are not yet supported by MRT");
|
||||
|
||||
auto rsrc = rsrcs[0];
|
||||
auto lim = *sched.prob.getLimit(rsrc);
|
||||
assert(lim > 0);
|
||||
|
||||
auto &revTab = reverseTables[opr];
|
||||
auto &revTab = reverseTables[rsrc];
|
||||
assert(!revTab.count(op));
|
||||
|
||||
unsigned slot = timeStep % sched.parameterT;
|
||||
auto &cell = tables[opr][slot];
|
||||
auto &cell = tables[rsrc][slot];
|
||||
if (cell.size() < lim) {
|
||||
cell.insert(op);
|
||||
revTab[op] = slot;
|
||||
|
@ -1011,11 +1030,18 @@ LogicalResult ModuloSimplexScheduler::MRT::enter(Operation *op,
|
|||
}
|
||||
|
||||
void ModuloSimplexScheduler::MRT::release(Operation *op) {
|
||||
auto opr = *sched.prob.getLinkedOperatorType(op);
|
||||
auto &revTab = reverseTables[opr];
|
||||
auto maybeRsrcs = sched.prob.getLinkedResourceTypes(op);
|
||||
assert(maybeRsrcs && "Operation must have linked resource types");
|
||||
|
||||
auto &rsrcs = *maybeRsrcs;
|
||||
assert(rsrcs.size() == 1 &&
|
||||
"Multi-resource operations are not yet supported by MRT");
|
||||
|
||||
auto rsrc = rsrcs[0];
|
||||
auto &revTab = reverseTables[rsrc];
|
||||
auto it = revTab.find(op);
|
||||
assert(it != revTab.end());
|
||||
tables[opr][it->second].erase(op);
|
||||
tables[rsrc][it->second].erase(op);
|
||||
revTab.erase(it);
|
||||
}
|
||||
|
||||
|
@ -1167,10 +1193,17 @@ void ModuloSimplexScheduler::scheduleOperation(Operation *n) {
|
|||
|
||||
unsigned ModuloSimplexScheduler::computeResMinII() {
|
||||
unsigned resMinII = 1;
|
||||
SmallDenseMap<Problem::OperatorType, unsigned> uses;
|
||||
for (auto *op : prob.getOperations())
|
||||
if (isLimited(op, prob))
|
||||
++uses[*prob.getLinkedOperatorType(op)];
|
||||
SmallDenseMap<Problem::ResourceType, unsigned> uses;
|
||||
for (auto *op : prob.getOperations()) {
|
||||
auto maybeRsrcs = prob.getLinkedResourceTypes(op);
|
||||
if (!maybeRsrcs)
|
||||
continue;
|
||||
|
||||
for (auto rsrc : *maybeRsrcs) {
|
||||
if (prob.getLimit(rsrc).value_or(0) > 0)
|
||||
++uses[rsrc];
|
||||
}
|
||||
}
|
||||
|
||||
for (auto pair : uses)
|
||||
resMinII = std::max(
|
||||
|
|
|
@ -5,6 +5,7 @@ ssp.instance @Foo of "Problem" {
|
|||
library {
|
||||
operator_type @Bar [latency<1>]
|
||||
}
|
||||
resource {}
|
||||
graph {
|
||||
// CHECK: op0 -> op1
|
||||
// CHECK: op0 -> op2
|
||||
|
|
|
@ -16,6 +16,7 @@ ssp.instance @self_arc of "CyclicProblem" {
|
|||
operator_type @unit [latency<1>]
|
||||
operator_type @_3 [latency<3>]
|
||||
}
|
||||
resource {}
|
||||
graph {
|
||||
%0 = operation<@unit>()
|
||||
%1 = operation<@_3> @self(%0, %0, @self [dist<1>])
|
||||
|
@ -42,6 +43,7 @@ ssp.instance @mco_outgoing_delays of "ChainingProblem" {
|
|||
operator_type @mul [latency<3>, incDelay<5.000000e+00 : f32>, outDelay<1.000000e-01 : f32>]
|
||||
operator_type @ret [latency<0>, incDelay<0.000000e+00 : f32>, outDelay<0.000000e+00 : f32>]
|
||||
}
|
||||
resource {}
|
||||
graph {
|
||||
%0 = operation<@add>()
|
||||
%1 = operation<@mul>(%0, %0)
|
||||
|
|
|
@ -7,12 +7,16 @@
|
|||
// CHECK: ssp.instance of "Problem" {
|
||||
// CHECK: library {
|
||||
// CHECK: }
|
||||
// CHECK: resource {
|
||||
// CHECK: }
|
||||
// CHECK: graph {
|
||||
// CHECK: }
|
||||
// CHECK: }
|
||||
ssp.instance of "Problem" {
|
||||
library {
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
}
|
||||
}
|
||||
|
@ -20,12 +24,16 @@ ssp.instance of "Problem" {
|
|||
// CHECK: ssp.instance @named_library of "Problem" {
|
||||
// CHECK: library @myLib {
|
||||
// CHECK: }
|
||||
// CHECK: resource {
|
||||
// CHECK: }
|
||||
// CHECK: graph {
|
||||
// CHECK: }
|
||||
// CHECK: }
|
||||
ssp.instance @named_library of "Problem" {
|
||||
library @myLib {
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +42,8 @@ ssp.instance @named_library of "Problem" {
|
|||
// CHECK: library {
|
||||
// CHECK: operator_type @NoProps
|
||||
// CHECK: }
|
||||
// CHECK: resource {
|
||||
// CHECK: }
|
||||
// CHECK: graph {
|
||||
// CHECK: %[[op_0:.*]] = operation<> @Op0()
|
||||
// CHECK: operation<>(%[[op_0]])
|
||||
|
@ -45,6 +55,8 @@ ssp.instance @"no properties" of "Problem" {
|
|||
library {
|
||||
operator_type @NoProps
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<> @Op0()
|
||||
operation<>(%0)
|
||||
|
@ -61,6 +73,8 @@ ssp.instance @"no properties" of "Problem" {
|
|||
// CHECK: operator_type @mult [latency<6>]
|
||||
// CHECK: operator_type @sqrt [latency<10>]
|
||||
// CHECK: }
|
||||
// CHECK: resource {
|
||||
// CHECK: }
|
||||
// CHECK: graph {
|
||||
// CHECK: %[[op_0:.*]] = operation<@extr>() [t<0>]
|
||||
// CHECK: %[[op_1:.*]] = operation<@extr>() [t<10>]
|
||||
|
@ -79,6 +93,8 @@ ssp.instance @arbitrary_latencies of "Problem" {
|
|||
operator_type @mult [latency<6>]
|
||||
operator_type @sqrt [latency<10>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@extr>() [t<0>]
|
||||
%1 = operation<@extr>() [t<10>]
|
||||
|
@ -95,6 +111,8 @@ ssp.instance @arbitrary_latencies of "Problem" {
|
|||
// CHECK: operator_type @unit [latency<1>]
|
||||
// CHECK: operator_type @_3 [latency<3>]
|
||||
// CHECK: }
|
||||
// CHECK: resource {
|
||||
// CHECK: }
|
||||
// CHECK: graph {
|
||||
// CHECK: %[[op_0:.*]] = operation<@unit>() [t<0>]
|
||||
// CHECK: %[[op_1:.*]] = operation<@_3> @self(%[[op_0]], %[[op_0]], @self [dist<1>]) [t<1>]
|
||||
|
@ -106,6 +124,8 @@ ssp.instance @self_arc of "CyclicProblem" [II<3>] {
|
|||
operator_type @unit [latency<1>]
|
||||
operator_type @_3 [latency<3>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@unit>() [t<0>]
|
||||
%1 = operation<@_3> @self(%0, %0, @self [dist<1>]) [t<1>]
|
||||
|
@ -119,6 +139,8 @@ ssp.instance @self_arc of "CyclicProblem" [II<3>] {
|
|||
// CHECK: operator_type @mul [latency<3>, incDelay<5.000000e+00 : f32>, outDelay<1.000000e-01 : f32>]
|
||||
// CHECK: operator_type @ret [latency<0>, incDelay<0.000000e+00 : f32>, outDelay<0.000000e+00 : f32>]
|
||||
// CHECK: }
|
||||
// CHECK: resource {
|
||||
// CHECK: }
|
||||
// CHECK: graph {
|
||||
// CHECK: %[[op_0:.*]] = operation<@add>() [t<0>, z<0.000000e+00 : f32>]
|
||||
// CHECK: %[[op_1:.*]] = operation<@mul>(%[[op_0]], %[[op_0]]) [t<3>, z<0.000000e+00 : f32>]
|
||||
|
@ -132,6 +154,8 @@ ssp.instance @mco_outgoing_delays of "ChainingProblem" {
|
|||
operator_type @mul [latency<3>, incDelay<5.000000e+00 : f32>, outDelay<1.000000e-01 : f32>]
|
||||
operator_type @ret [latency<0>, incDelay<0.000000e+00 : f32>, outDelay<0.000000e+00 : f32>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@add>() [t<0>, z<0.000000e+00 : f32>]
|
||||
%1 = operation<@mul>(%0, %0) [t<3>, z<0.000000e+00 : f32>]
|
||||
|
@ -142,34 +166,42 @@ ssp.instance @mco_outgoing_delays of "ChainingProblem" {
|
|||
|
||||
// CHECK: ssp.instance @multiple_oprs of "SharedOperatorsProblem" {
|
||||
// CHECK: library {
|
||||
// CHECK: operator_type @slowAdd [latency<3>, limit<2>]
|
||||
// CHECK: operator_type @fastAdd [latency<1>, limit<1>]
|
||||
// CHECK: operator_type @slowAdd [latency<3>]
|
||||
// CHECK: operator_type @fastAdd [latency<1>]
|
||||
// CHECK: operator_type @_0 [latency<0>]
|
||||
// CHECK: operator_type @_1 [latency<1>]
|
||||
// CHECK: }
|
||||
// CHECK: resource {
|
||||
// CHECK: resource_type @slowAdder [limit<1>]
|
||||
// CHECK: resource_type @fastAdder [limit<1>]
|
||||
// CHECK: }
|
||||
// CHECK: graph {
|
||||
// CHECK: %[[op_0:.*]] = operation<@slowAdd>() [t<0>]
|
||||
// CHECK: %[[op_1:.*]] = operation<@slowAdd>() [t<1>]
|
||||
// CHECK: %[[op_2:.*]] = operation<@fastAdd>() [t<0>]
|
||||
// CHECK: %[[op_3:.*]] = operation<@slowAdd>() [t<1>]
|
||||
// CHECK: %[[op_4:.*]] = operation<@fastAdd>() [t<1>]
|
||||
// CHECK: %[[op_0:.*]] = operation<@slowAdd>() uses[@slowAdder] [t<0>]
|
||||
// CHECK: %[[op_1:.*]] = operation<@slowAdd>() uses[@slowAdder] [t<1>]
|
||||
// CHECK: %[[op_2:.*]] = operation<@fastAdd>() uses[@fastAdder] [t<0>]
|
||||
// CHECK: %[[op_3:.*]] = operation<@slowAdd>() uses[@slowAdder] [t<1>]
|
||||
// CHECK: %[[op_4:.*]] = operation<@fastAdd>() uses[@fastAdder] [t<1>]
|
||||
// CHECK: %[[op_5:.*]] = operation<@_0>(%[[op_0]], %[[op_1]], %[[op_2]], %[[op_3]], %[[op_4]]) [t<10>]
|
||||
// CHECK: operation<@_1>() [t<10>]
|
||||
// CHECK: }
|
||||
// CHECK: }
|
||||
ssp.instance @multiple_oprs of "SharedOperatorsProblem" {
|
||||
library {
|
||||
operator_type @slowAdd [latency<3>, limit<2>]
|
||||
operator_type @fastAdd [latency<1>, limit<1>]
|
||||
operator_type @slowAdd [latency<3>]
|
||||
operator_type @fastAdd [latency<1>]
|
||||
operator_type @_0 [latency<0>]
|
||||
operator_type @_1 [latency<1>]
|
||||
}
|
||||
resource {
|
||||
resource_type @slowAdder [limit<1>]
|
||||
resource_type @fastAdder [limit<1>]
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@slowAdd>() [t<0>]
|
||||
%1 = operation<@slowAdd>() [t<1>]
|
||||
%2 = operation<@fastAdd>() [t<0>]
|
||||
%3 = operation<@slowAdd>() [t<1>]
|
||||
%4 = operation<@fastAdd>() [t<1>]
|
||||
%0 = operation<@slowAdd>() uses[@slowAdder] [t<0>]
|
||||
%1 = operation<@slowAdd>() uses[@slowAdder] [t<1>]
|
||||
%2 = operation<@fastAdd>() uses[@fastAdder] [t<0>]
|
||||
%3 = operation<@slowAdd>() uses[@slowAdder] [t<1>]
|
||||
%4 = operation<@fastAdd>() uses[@fastAdder] [t<1>]
|
||||
%5 = operation<@_0>(%0, %1, %2, %3, %4) [t<10>]
|
||||
operation<@_1>() [t<10>]
|
||||
}
|
||||
|
@ -177,29 +209,37 @@ ssp.instance @multiple_oprs of "SharedOperatorsProblem" {
|
|||
|
||||
// CHECK: ssp.instance @canis14_fig2 of "ModuloProblem" [II<3>] {
|
||||
// CHECK: library {
|
||||
// CHECK: operator_type @MemPort [latency<1>, limit<1>]
|
||||
// CHECK: operator_type @MemAccess [latency<1>]
|
||||
// CHECK: operator_type @Add [latency<1>]
|
||||
// CHECK: operator_type @Implicit [latency<0>]
|
||||
// CHECK: }
|
||||
// CHECK: resource {
|
||||
// CHECK: resource_type @ReadPort [limit<1>]
|
||||
// CHECK: resource_type @WritePort [limit<1>]
|
||||
// CHECK: }
|
||||
// CHECK: graph {
|
||||
// CHECK: %[[op_0:.*]] = operation<@MemPort> @load_A(@store_A [dist<1>]) [t<2>]
|
||||
// CHECK: %[[op_1:.*]] = operation<@MemPort> @load_B() [t<0>]
|
||||
// CHECK: %[[op_0:.*]] = operation<@MemAccess> @load_A(@store_A [dist<1>]) uses[@ReadPort] [t<2>]
|
||||
// CHECK: %[[op_1:.*]] = operation<@MemAccess> @load_B() uses[@ReadPort] [t<0>]
|
||||
// CHECK: %[[op_2:.*]] = operation<@Add> @add(%[[op_0]], %[[op_1]]) [t<3>]
|
||||
// CHECK: operation<@MemPort> @store_A(%[[op_2]]) [t<4>]
|
||||
// CHECK: operation<@MemAccess> @store_A(%[[op_2]]) uses[@WritePort] [t<4>]
|
||||
// CHECK: operation<@Implicit> @last(@store_A) [t<5>]
|
||||
// CHECK: }
|
||||
// CHECK: }
|
||||
ssp.instance @canis14_fig2 of "ModuloProblem" [II<3>] {
|
||||
library {
|
||||
operator_type @MemPort [latency<1>, limit<1>]
|
||||
operator_type @MemAccess [latency<1>]
|
||||
operator_type @Add [latency<1>]
|
||||
operator_type @Implicit [latency<0>]
|
||||
}
|
||||
resource {
|
||||
resource_type @ReadPort [limit<1>]
|
||||
resource_type @WritePort [limit<1>]
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@MemPort> @load_A(@store_A [dist<1>]) [t<2>]
|
||||
%1 = operation<@MemPort> @load_B() [t<0>]
|
||||
%0 = operation<@MemAccess> @load_A(@store_A [dist<1>]) uses[@ReadPort] [t<2>]
|
||||
%1 = operation<@MemAccess> @load_B() uses[@ReadPort] [t<0>]
|
||||
%2 = operation<@Add> @add(%0, %1) [t<3>]
|
||||
operation<@MemPort> @store_A(%2) [t<4>]
|
||||
operation<@MemAccess> @store_A(%2) uses[@WritePort] [t<4>]
|
||||
operation<@Implicit> @last(@store_A) [t<5>]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,23 +3,32 @@
|
|||
|
||||
// 1) tests the plain parser/printer roundtrip.
|
||||
// CHECK: ssp.library @Lib {
|
||||
// CHECK: operator_type @Opr [latency<1>, limit<1>]
|
||||
// CHECK: operator_type @Opr [latency<1>]
|
||||
// CHECK: }
|
||||
// CHECK: ssp.resource @RsrcLib {
|
||||
// CHECK: resource_type @Rsrc [limit<1>]
|
||||
// CHECK: }
|
||||
// CHECK: module @SomeModule {
|
||||
// CHECK: ssp.library @Lib {
|
||||
// CHECK: operator_type @Opr [latency<2>, limit<2>]
|
||||
// CHECK: operator_type @Opr [latency<2>]
|
||||
// CHECK: }
|
||||
// CHECK: ssp.resource @RsrcLib {
|
||||
// CHECK: resource_type @Rsrc [limit<2>]
|
||||
// CHECK: }
|
||||
// CHECK: }
|
||||
// CHECK: ssp.instance @SomeInstance of "ModuloProblem" {
|
||||
// CHECK: library @InternalLib {
|
||||
// CHECK: operator_type @Opr [latency<3>, limit<3>]
|
||||
// CHECK: operator_type @Opr [latency<3>]
|
||||
// CHECK: }
|
||||
// CHECK: resource @InternalRsrc {
|
||||
// CHECK: resource_type @Rsrc [limit<3>]
|
||||
// CHECK: }
|
||||
// CHECK: graph {
|
||||
// CHECK: operation<@Opr>()
|
||||
// CHECK: operation<@InternalLib::@Opr>()
|
||||
// CHECK: operation<@SomeInstance::@InternalLib::@Opr>()
|
||||
// CHECK: operation<@Lib::@Opr>()
|
||||
// CHECK: operation<@SomeModule::@Lib::@Opr>()
|
||||
// CHECK: operation<@Opr>() uses[@Rsrc]
|
||||
// CHECK: operation<@InternalLib::@Opr>() uses[@InternalRsrc::@Rsrc]
|
||||
// CHECK: operation<@SomeInstance::@InternalLib::@Opr>() uses[@SomeInstance::@InternalRsrc::@Rsrc]
|
||||
// CHECK: operation<@Lib::@Opr>() uses[@RsrcLib::@Rsrc]
|
||||
// CHECK: operation<@SomeModule::@Lib::@Opr>() uses[@SomeModule::@RsrcLib::@Rsrc]
|
||||
// CHECK: }
|
||||
// CHECK: }
|
||||
|
||||
|
@ -27,36 +36,50 @@
|
|||
// Operator types from stand-alone libraries are appended to the instance's internal library.
|
||||
// INFRA: ssp.instance @SomeInstance of "ModuloProblem" {
|
||||
// INFRA: library @InternalLib {
|
||||
// INFRA: operator_type @Opr [latency<3>, limit<3>]
|
||||
// INFRA: operator_type @Opr_1 [latency<1>, limit<1>]
|
||||
// INFRA: operator_type @Opr_2 [latency<2>, limit<2>]
|
||||
// INFRA: operator_type @Opr [latency<3>]
|
||||
// INFRA: operator_type @Opr_1 [latency<1>]
|
||||
// INFRA: operator_type @Opr_2 [latency<2>]
|
||||
// INFRA: }
|
||||
// INFRA: resource @InternalRsrc {
|
||||
// INFRA: resource_type @Rsrc [limit<3>]
|
||||
// INFRA: resource_type @Rsrc_1 [limit<1>]
|
||||
// INFRA: resource_type @Rsrc_2 [limit<2>]
|
||||
// INFRA: }
|
||||
// INFRA: graph {
|
||||
// INFRA: operation<@Opr>()
|
||||
// INFRA: operation<@Opr>()
|
||||
// INFRA: operation<@Opr>()
|
||||
// INFRA: operation<@Opr_1>()
|
||||
// INFRA: operation<@Opr_2>()
|
||||
// INFRA: operation<@Opr>() uses[@Rsrc]
|
||||
// INFRA: operation<@Opr>() uses[@Rsrc]
|
||||
// INFRA: operation<@Opr>() uses[@Rsrc]
|
||||
// INFRA: operation<@Opr_1>() uses[@Rsrc_1]
|
||||
// INFRA: operation<@Opr_2>() uses[@Rsrc_2]
|
||||
// INFRA: }
|
||||
// INFRA: }
|
||||
|
||||
ssp.library @Lib {
|
||||
operator_type @Opr [latency<1>, limit<1>]
|
||||
operator_type @Opr [latency<1>]
|
||||
}
|
||||
ssp.resource @RsrcLib {
|
||||
resource_type @Rsrc [limit<1>]
|
||||
}
|
||||
module @SomeModule {
|
||||
ssp.library @Lib {
|
||||
operator_type @Opr [latency<2>, limit<2>]
|
||||
operator_type @Opr [latency<2>]
|
||||
}
|
||||
ssp.resource @RsrcLib {
|
||||
resource_type @Rsrc [limit<2>]
|
||||
}
|
||||
}
|
||||
ssp.instance @SomeInstance of "ModuloProblem" {
|
||||
library @InternalLib {
|
||||
operator_type @Opr [latency<3>, limit<3>]
|
||||
operator_type @Opr [latency<3>]
|
||||
}
|
||||
resource @InternalRsrc {
|
||||
resource_type @Rsrc [limit<3>]
|
||||
}
|
||||
graph {
|
||||
operation<@Opr>()
|
||||
operation<@InternalLib::@Opr>()
|
||||
operation<@SomeInstance::@InternalLib::@Opr>()
|
||||
operation<@Lib::@Opr>()
|
||||
operation<@SomeModule::@Lib::@Opr>()
|
||||
operation<@Opr>() uses[@Rsrc]
|
||||
operation<@InternalLib::@Opr>() uses[@InternalRsrc::@Rsrc]
|
||||
operation<@SomeInstance::@InternalLib::@Opr>() uses[@SomeInstance::@InternalRsrc::@Rsrc]
|
||||
operation<@Lib::@Opr>() uses[@RsrcLib::@Rsrc]
|
||||
operation<@SomeModule::@Lib::@Opr>() uses[@SomeModule::@RsrcLib::@Rsrc]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ ssp.instance @cyclic_graph of "Problem" {
|
|||
library {
|
||||
operator_type @_1 [latency<1>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_1>(@op2)
|
||||
%1 = operation<@_1>(%0)
|
||||
|
|
|
@ -5,6 +5,8 @@ ssp.instance @defUse_distance of "ChainingCyclicProblem" {
|
|||
library {
|
||||
operator_type @_0 [latency<0>, incDelay<1.0>, outDelay<1.0>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = ssp.operation<@_0> ()
|
||||
%1 = ssp.operation<@_0> (%0 [dist<3>])
|
||||
|
|
|
@ -11,6 +11,8 @@ ssp.instance @cyclic of "ChainingCyclicProblem" [II<2>] {
|
|||
operator_type @_1 [latency<1>, incDelay<0.0>, outDelay<0.0>]
|
||||
operator_type @_2 [latency<2>, incDelay<0.0>, outDelay<0.0>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_1>() [t<0>, z<0.000000e+00 : f32>]
|
||||
%1 = operation<@_0>(@op4 [dist<1>]) [t<1>, z<0.000000e+00 : f32>]
|
||||
|
@ -29,6 +31,8 @@ ssp.instance @mobility of "ChainingCyclicProblem" [II<3>] {
|
|||
operator_type @_1 [latency<1>, incDelay<0.0>, outDelay<0.0>]
|
||||
operator_type @_4 [latency<4>, incDelay<0.0>, outDelay<0.0>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_1>() [t<0>, z<0.000000e+00 : f32>]
|
||||
%1 = operation<@_4>(%0) [t<1>, z<0.000000e+00 : f32>]
|
||||
|
@ -48,6 +52,8 @@ ssp.instance @interleaved_cycles of "ChainingCyclicProblem" [II<4>] {
|
|||
operator_type @_1 [latency<1>, incDelay<0.0>, outDelay<0.0>]
|
||||
operator_type @_10 [latency<10>, incDelay<0.0>, outDelay<0.0>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_1>() [t<0>, z<0.000000e+00 : f32>]
|
||||
%1 = operation<@_10>(%0) [t<1>, z<0.000000e+00 : f32>]
|
||||
|
@ -71,6 +77,8 @@ ssp.instance @self_arc of "ChainingCyclicProblem" [II<3>] {
|
|||
operator_type @_1 [latency<1>, incDelay<0.0>, outDelay<0.0>]
|
||||
operator_type @_3 [latency<3>, incDelay<0.0>, outDelay<0.0>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_1>() [t<0>, z<0.000000e+00 : f32>]
|
||||
%1 = operation<@_3> @op1(%0, @op1 [dist<1>]) [t<1>, z<0.000000e+00 : f32>]
|
||||
|
@ -87,6 +95,8 @@ ssp.instance @adder_chain of "ChainingCyclicProblem" [II<1>] {
|
|||
operator_type @_0 [latency<0>, incDelay<2.34>, outDelay<2.34>]
|
||||
operator_type @_1 [latency<1>, incDelay<0.0>, outDelay<0.0>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_0>() [t<0>, z<0.0>]
|
||||
%1 = operation<@_0>(%0) [t<0>, z<2.34>]
|
||||
|
@ -105,6 +115,8 @@ ssp.instance @multi_cycle of "ChainingCyclicProblem" [II<1>] {
|
|||
operator_type @_1 [latency<1>, incDelay<0.0>, outDelay<0.0>]
|
||||
operator_type @_3 [latency<3>, incDelay<2.5>, outDelay<3.75>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_0>() [t<0>, z<0.0>]
|
||||
%1 = operation<@_0>(%0) [t<0>, z<2.34>]
|
||||
|
@ -122,6 +134,8 @@ ssp.instance @mco_outgoing_delays of "ChainingCyclicProblem" [II<1>] {
|
|||
operator_type @_2 [latency<2>, incDelay<0.1>, outDelay<0.1>]
|
||||
operator_type @_3 [latency<3>, incDelay<5.0>, outDelay<0.1>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
// SIMPLEX: graph
|
||||
graph {
|
||||
// SIMPLEX-NEXT: [t<0>, z<0.000000e+00 : f32>]
|
||||
|
@ -152,7 +166,8 @@ ssp.instance @chaining_and_cyclic of "ChainingCyclicProblem" [II<2>] {
|
|||
operator_type @_4 [latency<1>, incDelay<1.2>, outDelay<1.2>]
|
||||
operator_type @_5 [latency<0>, incDelay<3.8>, outDelay<3.8>]
|
||||
}
|
||||
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_0>(@op2 [dist<1>]) [t<0>, z<0.0>]
|
||||
%1 = operation<@_1>(%0) [t<0>, z<1.0>]
|
||||
|
@ -170,6 +185,8 @@ ssp.instance @backedge_delay_propagation of "ChainingCyclicProblem" [II<1>] {
|
|||
library {
|
||||
operator_type @_0 [latency<0>, incDelay<1.0>, outDelay<1.0>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
// SIMPLEX: @last(@last [dist<1>]) [t<0>, z<0.000000e+00 : f32>]
|
||||
%0 = operation<@_0> @last(@last [dist<1>]) [t<0>, z<0.000000e+00 : f32>]
|
||||
|
|
|
@ -5,6 +5,7 @@ ssp.instance @missing_delay of "ChainingProblem" {
|
|||
library {
|
||||
operator_type @_0 [latency<0>]
|
||||
}
|
||||
resource {}
|
||||
graph {}
|
||||
}
|
||||
|
||||
|
@ -15,6 +16,7 @@ ssp.instance @negative_delay of "ChainingProblem" {
|
|||
library {
|
||||
operator_type @_0 [latency<0>, incDelay<-1.0>, outDelay<-1.0>]
|
||||
}
|
||||
resource {}
|
||||
graph {}
|
||||
}
|
||||
|
||||
|
@ -25,6 +27,7 @@ ssp.instance @inc_out_mismatch of "ChainingProblem" {
|
|||
library {
|
||||
operator_type @_0 [latency<0>, incDelay<1.0>, outDelay<2.0>]
|
||||
}
|
||||
resource {}
|
||||
graph {}
|
||||
}
|
||||
|
||||
|
@ -34,6 +37,7 @@ ssp.instance @no_stic of "ChainingProblem" {
|
|||
library {
|
||||
operator_type @_0 [latency<0>, incDelay<1.0>, outDelay<1.0>]
|
||||
}
|
||||
resource {}
|
||||
graph {
|
||||
operation<@_0>() [t<0>] // expected-error {{Operation has no non-negative start time in cycle}}
|
||||
}
|
||||
|
@ -46,6 +50,7 @@ ssp.instance @precedence1 of "ChainingProblem" {
|
|||
library {
|
||||
operator_type @_0 [latency<0>, incDelay<1.0>, outDelay<1.0>]
|
||||
}
|
||||
resource {}
|
||||
graph {
|
||||
%0 = operation<@_0>() [t<0>, z<1.1>]
|
||||
operation<@_0>(%0) [t<0>, z<2.0>]
|
||||
|
@ -60,6 +65,7 @@ ssp.instance @precedence2 of "ChainingProblem" {
|
|||
operator_type @_0 [latency<0>, incDelay<1.0>, outDelay<1.0>]
|
||||
operator_type @_3 [latency<3>, incDelay<2.5>, outDelay<3.75>]
|
||||
}
|
||||
resource {}
|
||||
graph {
|
||||
%0 = operation<@_3>() [t<0>, z<0.0>]
|
||||
operation<@_0>(%0) [t<3>, z<3.0>]
|
||||
|
|
|
@ -9,6 +9,8 @@ ssp.instance @adder_chain of "ChainingProblem" {
|
|||
operator_type @_0 [latency<0>, incDelay<2.34>, outDelay<2.34>]
|
||||
operator_type @_1 [latency<1>, incDelay<0.0>, outDelay<0.0>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_0>() [t<0>, z<0.0>]
|
||||
%1 = operation<@_0>(%0) [t<0>, z<2.34>]
|
||||
|
@ -27,6 +29,8 @@ ssp.instance @multi_cycle of "ChainingProblem" {
|
|||
operator_type @_1 [latency<1>, incDelay<0.0>, outDelay<0.0>]
|
||||
operator_type @_3 [latency<3>, incDelay<2.5>, outDelay<3.75>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_0>() [t<0>, z<0.0>]
|
||||
%1 = operation<@_0>(%0) [t<0>, z<2.34>]
|
||||
|
@ -44,6 +48,8 @@ ssp.instance @mco_outgoing_delays of "ChainingProblem" {
|
|||
operator_type @_2 [latency<2>, incDelay<0.1>, outDelay<0.1>]
|
||||
operator_type @_3 [latency<3>, incDelay<5.0>, outDelay<0.1>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
// SIMPLEX: graph
|
||||
graph {
|
||||
// SIMPLEX-NEXT: [t<0>, z<0.000000e+00 : f32>]
|
||||
|
|
|
@ -5,6 +5,8 @@ ssp.instance @invalid_delay of "ChainingProblem" {
|
|||
library {
|
||||
operator_type @inv [latency<0>, incDelay<2.34>, outDelay<2.34>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
operation<@inv>()
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// expected-error@+1 {{Invalid initiation interval}}
|
||||
ssp.instance @no_II of "CyclicProblem" {
|
||||
library {}
|
||||
resource {}
|
||||
graph {}
|
||||
}
|
||||
|
||||
|
@ -13,6 +14,7 @@ ssp.instance @backedge_violated of "CyclicProblem" [II<2>] {
|
|||
library {
|
||||
operator_type @_1 [latency<1>]
|
||||
}
|
||||
resource {}
|
||||
graph {
|
||||
%0 = operation<@_1>(@op2 [dist<1>]) [t<0>]
|
||||
%1 = operation<@_1>(%0) [t<1>]
|
||||
|
|
|
@ -11,6 +11,8 @@ ssp.instance @cyclic of "CyclicProblem" [II<2>] {
|
|||
operator_type @_1 [latency<1>]
|
||||
operator_type @_2 [latency<2>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_1>() [t<0>]
|
||||
%1 = operation<@_0>(@op4 [dist<1>]) [t<2>]
|
||||
|
@ -31,6 +33,8 @@ ssp.instance @mobility of "CyclicProblem" [II<3>] {
|
|||
operator_type @_1 [latency<1>]
|
||||
operator_type @_4 [latency<4>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_1>() [t<0>]
|
||||
%1 = operation<@_4>(%0) [t<1>]
|
||||
|
@ -52,6 +56,8 @@ ssp.instance @interleaved_cycles of "CyclicProblem" [II<4>] {
|
|||
operator_type @_1 [latency<1>]
|
||||
operator_type @_10 [latency<10>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_1>() [t<0>]
|
||||
%1 = operation<@_10>(%0) [t<1>]
|
||||
|
@ -77,6 +83,8 @@ ssp.instance @self_arc of "CyclicProblem" [II<3>] {
|
|||
operator_type @_1 [latency<1>]
|
||||
operator_type @_3 [latency<3>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_1>() [t<0>]
|
||||
%1 = operation<@_3> @op1(%0, @op1 [dist<1>]) [t<1>]
|
||||
|
|
|
@ -5,6 +5,8 @@ ssp.instance @cyclic_graph of "CyclicProblem" {
|
|||
library {
|
||||
operator_type @_1 [latency<1>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_1>(@op2)
|
||||
%1 = operation<@_1>(%0)
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
// RUN: circt-opt %s -ssp-roundtrip=verify -verify-diagnostics -split-input-file
|
||||
|
||||
// expected-error@+1 {{Operator type 'limited' is oversubscribed}}
|
||||
// expected-error@+1 {{Resource type 'limited_rsrc' is oversubscribed}}
|
||||
ssp.instance @oversubscribed of "ModuloProblem" [II<2>] {
|
||||
library {
|
||||
operator_type @limited [latency<1>, limit<2>]
|
||||
operator_type @limited [latency<1>]
|
||||
}
|
||||
resource {
|
||||
resource_type @limited_rsrc [limit<2>]
|
||||
}
|
||||
graph {
|
||||
operation<@limited>() [t<1>]
|
||||
operation<@limited>() [t<3>]
|
||||
operation<@limited>() [t<5>]
|
||||
operation<@limited>() uses[@limited_rsrc] [t<1>]
|
||||
operation<@limited>() uses[@limited_rsrc] [t<3>]
|
||||
operation<@limited>() uses[@limited_rsrc] [t<5>]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,15 +5,17 @@
|
|||
// SIMPLEX-SAME: [II<4>]
|
||||
ssp.instance @canis14_fig2 of "ModuloProblem" [II<3>] {
|
||||
library {
|
||||
operator_type @L1_1 [latency<1>, limit<1>]
|
||||
operator_type @L1_1 [latency<1>]
|
||||
operator_type @_1 [latency<1>]
|
||||
}
|
||||
resource {
|
||||
resource_type @L1_rsrc [limit<1>]
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@L1_1>(@op3 [dist<1>]) [t<2>]
|
||||
%1 = operation<@L1_1>() [t<0>]
|
||||
%0 = operation<@L1_1>(@op3 [dist<1>]) uses[@L1_rsrc] [t<2>]
|
||||
%1 = operation<@L1_1>() uses[@L1_rsrc] [t<0>]
|
||||
%2 = operation<@_1>(%0, %1) [t<3>]
|
||||
%3 = operation<@L1_1> @op3(%2) [t<4>]
|
||||
// SIMPLEX: @last(%{{.*}}) [t<4>]
|
||||
%3 = operation<@L1_1> @op3(%2) uses[@L1_rsrc] [t<4>]
|
||||
operation<@_1> @last(%3) [t<5>]
|
||||
}
|
||||
}
|
||||
|
@ -25,16 +27,19 @@ ssp.instance @minII_feasible of "ModuloProblem" [II<3>] {
|
|||
operator_type @_0 [latency<0>]
|
||||
operator_type @_1 [latency<1>]
|
||||
operator_type @_2 [latency<2>]
|
||||
operator_type @L1_3 [latency<3>, limit<1>]
|
||||
operator_type @L1_3 [latency<3>]
|
||||
}
|
||||
resource {
|
||||
resource_type @L1_rsrc [limit<1>]
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_0>() [t<0>]
|
||||
%1 = operation<@_2>(@op6 [dist<5>]) [t<0>]
|
||||
%2 = operation<@_2>(@op5 [dist<3>]) [t<1>]
|
||||
%3 = operation<@_1>(%1, %0) [t<2>]
|
||||
%4 = operation<@L1_3>(%3, %2) [t<3>]
|
||||
%5 = operation<@L1_3> @op5(%0, %4) [t<7>]
|
||||
%6 = operation<@L1_3> @op6(%4, %5) [t<11>]
|
||||
%4 = operation<@L1_3>(%3, %2) uses[@L1_rsrc] [t<3>]
|
||||
%5 = operation<@L1_3> @op5(%0, %4) uses[@L1_rsrc] [t<7>]
|
||||
%6 = operation<@L1_3> @op6(%4, %5) uses[@L1_rsrc] [t<11>]
|
||||
// SIMPLEX: @last(%{{.*}}) [t<14>]
|
||||
operation<@_1> @last(%6) [t<14>]
|
||||
}
|
||||
|
@ -47,12 +52,15 @@ ssp.instance @minII_infeasible of "ModuloProblem" [II<4>] {
|
|||
operator_type @_1 [latency<1>]
|
||||
operator_type @L2_1 [latency<1>, limit<2>]
|
||||
}
|
||||
resource {
|
||||
resource_type @L2_rsrc [limit<2>]
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_1>() [t<0>]
|
||||
%1 = operation<@_1>(%0, @op5 [dist<1>]) [t<1>]
|
||||
%2 = operation<@L2_1>(%1) [t<2>]
|
||||
%3 = operation<@L2_1>(%1) [t<3>]
|
||||
%4 = operation<@L2_1>(%1) [t<2>]
|
||||
%2 = operation<@L2_1>(%1) uses[@L2_rsrc] [t<2>]
|
||||
%3 = operation<@L2_1>(%1) uses[@L2_rsrc] [t<3>]
|
||||
%4 = operation<@L2_1>(%1) uses[@L2_rsrc] [t<2>]
|
||||
%5 = operation<@_1> @op5(%2, %3, %4) [t<4>]
|
||||
// SIMPLEX: @last(%{{.*}}) [t<5>]
|
||||
operation<@_1> @last(%5) [t<5>]
|
||||
|
@ -64,15 +72,18 @@ ssp.instance @minII_infeasible of "ModuloProblem" [II<4>] {
|
|||
ssp.instance @four_read_pipeline of "ModuloProblem" [II<4>] {
|
||||
library {
|
||||
operator_type @_1 [latency<1>]
|
||||
operator_type @L1_1 [latency<1>, limit<1>]
|
||||
operator_type @L1_1 [latency<1>]
|
||||
}
|
||||
resource {
|
||||
resource_type @L1_rsrc [limit<1>]
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@_1>() [t<0>]
|
||||
%1 = operation<@_1>(%0) [t<1>]
|
||||
%2 = operation<@L1_1>(%1) [t<2>]
|
||||
%3 = operation<@L1_1>(%1) [t<3>]
|
||||
%4 = operation<@L1_1>(%1) [t<4>]
|
||||
%5 = operation<@L1_1>(%1) [t<5>]
|
||||
%2 = operation<@L1_1>(%1) uses[@L1_rsrc] [t<2>]
|
||||
%3 = operation<@L1_1>(%1) uses[@L1_rsrc] [t<3>]
|
||||
%4 = operation<@L1_1>(%1) uses[@L1_rsrc] [t<4>]
|
||||
%5 = operation<@L1_1>(%1) uses[@L1_rsrc] [t<5>]
|
||||
%6 = operation<@_1>(%2, %3) [t<4>]
|
||||
%7 = operation<@_1>(%4, %5) [t<6>]
|
||||
%8 = operation<@_1>(%6, %7) [t<7>]
|
||||
|
|
|
@ -5,6 +5,8 @@ ssp.instance of "ModuloProblem" {
|
|||
library {
|
||||
operator_type @_1 [latency<1>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
operation<@_1> @last()
|
||||
operation<@_1>(@last)
|
||||
|
@ -18,6 +20,8 @@ ssp.instance of "ModuloProblem" {
|
|||
library {
|
||||
operator_type @_1 [latency<1>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
graph {
|
||||
operation<@_1>()
|
||||
operation<@_1> @last()
|
||||
|
|
|
@ -5,7 +5,10 @@ ssp.instance @no_latency of "Problem" {
|
|||
library {
|
||||
operator_type @foo
|
||||
}
|
||||
graph {}
|
||||
resource {}
|
||||
graph {
|
||||
operation<@foo>()
|
||||
}
|
||||
}
|
||||
|
||||
// -----
|
||||
|
@ -14,6 +17,7 @@ ssp.instance @no_starttime of "Problem" {
|
|||
library {
|
||||
operator_type @_1 [latency<1>]
|
||||
}
|
||||
resource {}
|
||||
graph {
|
||||
operation<@_1>() // expected-error {{Operation has no start time}}
|
||||
}
|
||||
|
@ -26,6 +30,7 @@ ssp.instance @ssa_dep_violated of "Problem" {
|
|||
library {
|
||||
operator_type @_1 [latency<1>]
|
||||
}
|
||||
resource {}
|
||||
graph {
|
||||
%0 = operation<@_1>() [t<0>]
|
||||
%1 = operation<@_1>(%0) [t<0>]
|
||||
|
@ -39,6 +44,7 @@ ssp.instance @aux_dep_violated of "Problem" {
|
|||
library {
|
||||
operator_type @_1 [latency<1>]
|
||||
}
|
||||
resource {}
|
||||
graph {
|
||||
operation<@_1> @op0() [t<0>]
|
||||
operation<@_1> @op1(@op0) [t<0>]
|
||||
|
|
|
@ -8,6 +8,8 @@ ssp.instance @unit_latencies of "Problem" {
|
|||
library {
|
||||
operator_type @_1 [latency<1>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
// ASAP: graph
|
||||
graph {
|
||||
// ASAP-NEXT: [t<0>]
|
||||
|
@ -40,6 +42,8 @@ ssp.instance @arbitrary_latencies of "Problem" {
|
|||
operator_type @_6 [latency<6>]
|
||||
operator_type @_10 [latency<10>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
// ASAP: graph
|
||||
graph {
|
||||
// ASAP-NEXT: [t<0>]
|
||||
|
@ -66,6 +70,8 @@ ssp.instance @auxiliary_dependences of "Problem" {
|
|||
library {
|
||||
operator_type @_1 [latency<1>]
|
||||
}
|
||||
resource {
|
||||
}
|
||||
// ASAP: graph
|
||||
graph {
|
||||
// ASAP-NEXT: [t<0>]
|
||||
|
|
|
@ -1,23 +1,31 @@
|
|||
// RUN: circt-opt %s -ssp-roundtrip=verify -verify-diagnostics -split-input-file
|
||||
|
||||
// expected-error@+1 {{Limited operator type 'limited' has zero latency}}
|
||||
// expected-error@+1 {{Operator type 'limited' using limited resource 'limited_rsrc' has zero latency.}}
|
||||
ssp.instance @limited_but_zero_latency of "SharedOperatorsProblem" {
|
||||
library {
|
||||
operator_type @limited [latency<0>, limit<1>]
|
||||
operator_type @limited [latency<0>]
|
||||
}
|
||||
resource {
|
||||
resource_type @limited_rsrc [limit<1>]
|
||||
}
|
||||
graph {
|
||||
operation<@limited>() uses[@limited_rsrc] [t<0>]
|
||||
}
|
||||
graph {}
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// expected-error@+1 {{Operator type 'limited' is oversubscribed}}
|
||||
// expected-error@+1 {{Resource type 'limited_rsrc' is oversubscribed}}
|
||||
ssp.instance @oversubscribed of "SharedOperatorsProblem" {
|
||||
library {
|
||||
operator_type @limited [latency<1>, limit<2>]
|
||||
operator_type @limited [latency<1>]
|
||||
}
|
||||
resource {
|
||||
resource_type @limited_rsrc [limit<2>]
|
||||
}
|
||||
graph {
|
||||
operation<@limited>() [t<0>]
|
||||
operation<@limited>() [t<0>]
|
||||
operation<@limited>() [t<0>]
|
||||
operation<@limited>() uses[@limited_rsrc] [t<0>]
|
||||
operation<@limited>() uses[@limited_rsrc] [t<0>]
|
||||
operation<@limited>() uses[@limited_rsrc] [t<0>]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,15 +5,18 @@
|
|||
// CHECK-LABEL: full_load
|
||||
ssp.instance @full_load of "SharedOperatorsProblem" {
|
||||
library {
|
||||
operator_type @L1_3 [latency<3>, limit<1>]
|
||||
operator_type @L1_3 [latency<3>]
|
||||
operator_type @_1 [latency<1>]
|
||||
}
|
||||
resource {
|
||||
resource_type @L1_rsrc [limit<1>]
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@L1_3>() [t<0>]
|
||||
%1 = operation<@L1_3>() [t<1>]
|
||||
%2 = operation<@L1_3>() [t<2>]
|
||||
%3 = operation<@L1_3>() [t<3>]
|
||||
%4 = operation<@L1_3>() [t<4>]
|
||||
%0 = operation<@L1_3>() uses[@L1_rsrc] [t<0>]
|
||||
%1 = operation<@L1_3>() uses[@L1_rsrc] [t<1>]
|
||||
%2 = operation<@L1_3>() uses[@L1_rsrc] [t<2>]
|
||||
%3 = operation<@L1_3>() uses[@L1_rsrc] [t<3>]
|
||||
%4 = operation<@L1_3>() uses[@L1_rsrc] [t<4>]
|
||||
%5 = operation<@_1>(%0, %1, %2, %3, %4) [t<7>]
|
||||
// SIMPLEX: @last(%{{.*}}) [t<8>]
|
||||
// CPSAT: @last(%{{.*}}) [t<8>]
|
||||
|
@ -24,15 +27,18 @@ ssp.instance @full_load of "SharedOperatorsProblem" {
|
|||
// CHECK-LABEL: partial_load
|
||||
ssp.instance @partial_load of "SharedOperatorsProblem" {
|
||||
library {
|
||||
operator_type @L3_3 [latency<3>, limit<3>]
|
||||
operator_type @L3_3 [latency<3>]
|
||||
operator_type @_1 [latency<1>]
|
||||
}
|
||||
resource {
|
||||
resource_type @L3_rsrc [limit<3>]
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@L3_3>() [t<0>]
|
||||
%1 = operation<@L3_3>() [t<1>]
|
||||
%2 = operation<@L3_3>() [t<0>]
|
||||
%3 = operation<@L3_3>() [t<2>]
|
||||
%4 = operation<@L3_3>() [t<1>]
|
||||
%0 = operation<@L3_3>() uses[@L3_rsrc] [t<0>]
|
||||
%1 = operation<@L3_3>() uses[@L3_rsrc] [t<1>]
|
||||
%2 = operation<@L3_3>() uses[@L3_rsrc] [t<0>]
|
||||
%3 = operation<@L3_3>() uses[@L3_rsrc] [t<2>]
|
||||
%4 = operation<@L3_3>() uses[@L3_rsrc] [t<1>]
|
||||
%5 = operation<@_1>(%0, %1, %2, %3, %4) [t<10>]
|
||||
// SIMPLEX: @last(%{{.*}}) [t<5>]
|
||||
// CPSAT: @last(%{{.*}}) [t<5>]
|
||||
|
@ -43,16 +49,20 @@ ssp.instance @partial_load of "SharedOperatorsProblem" {
|
|||
// CHECK-LABEL: multiple
|
||||
ssp.instance @multiple of "SharedOperatorsProblem" {
|
||||
library {
|
||||
operator_type @L3_2 [latency<3>, limit<2>]
|
||||
operator_type @L1_1 [latency<1>, limit<1>]
|
||||
operator_type @L3_2 [latency<3>]
|
||||
operator_type @L1_1 [latency<1>]
|
||||
operator_type @_1 [latency<1>]
|
||||
}
|
||||
resource {
|
||||
resource_type @L3_rsrc [limit<2>]
|
||||
resource_type @L1_rsrc [limit<1>]
|
||||
}
|
||||
graph {
|
||||
%0 = operation<@L3_2>() [t<0>]
|
||||
%1 = operation<@L3_2>() [t<1>]
|
||||
%2 = operation<@L1_1>() [t<0>]
|
||||
%3 = operation<@L3_2>() [t<1>]
|
||||
%4 = operation<@L1_1>() [t<1>]
|
||||
%0 = operation<@L3_2>() uses[@L3_rsrc] [t<0>]
|
||||
%1 = operation<@L3_2>() uses[@L3_rsrc] [t<1>]
|
||||
%2 = operation<@L1_1>() uses[@L1_rsrc] [t<0>]
|
||||
%3 = operation<@L3_2>() uses[@L3_rsrc] [t<1>]
|
||||
%4 = operation<@L1_1>() uses[@L1_rsrc] [t<1>]
|
||||
%5 = operation<@_1>(%0, %1, %2, %3, %4) [t<10>]
|
||||
// SIMPLEX: @last(%{{.*}}) [t<5>]
|
||||
// CPSAT: @last(%{{.*}}) [t<5>]
|
||||
|
|
Loading…
Reference in New Issue