[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:
Jiahan Xie 2025-05-13 08:52:11 -04:00 committed by GitHub
parent 58897cd827
commit 3c51e75ed6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 914 additions and 212 deletions

View File

@ -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> {

View File

@ -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";
}

View File

@ -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;

View File

@ -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 =

View File

@ -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

View File

@ -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())

View File

@ -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 =

View File

@ -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.
//===----------------------------------------------------------------------===//

View File

@ -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.

View File

@ -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();

View File

@ -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(

View File

@ -5,6 +5,7 @@ ssp.instance @Foo of "Problem" {
library {
operator_type @Bar [latency<1>]
}
resource {}
graph {
// CHECK: op0 -> op1
// CHECK: op0 -> op2

View File

@ -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)

View File

@ -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>]
}
}

View File

@ -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]
}
}

View File

@ -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)

View File

@ -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>])

View File

@ -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>]

View File

@ -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>]

View File

@ -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>]

View File

@ -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>()
}

View File

@ -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>]

View File

@ -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>]

View File

@ -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)

View File

@ -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>]
}
}

View File

@ -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>]

View File

@ -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()

View File

@ -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>]

View File

@ -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>]

View File

@ -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>]
}
}

View File

@ -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>]