mirror of https://github.com/llvm/circt.git
[SSP] Allow standalone use of `OperatorLibraryOp`. (#4222)
This commit is contained in:
parent
6ed5055917
commit
3e65bb13d1
|
@ -126,16 +126,25 @@ problem instances?
|
|||
|
||||
### Use of container-like operations instead of regions in `InstanceOp`
|
||||
|
||||
This dialect defines the `OperatorLibraryOp` and `DependenceGraphOp` to
|
||||
serve exclusively as the first and second operation in an `InstanceOp`'s region.
|
||||
The alternative of using two regions on the `InstanceOp` is not applicable,
|
||||
because the `InstanceOp` then needs to provide a symbol table, but the upstream
|
||||
This dialect defines the `OperatorLibraryOp` and `DependenceGraphOp` to serve as
|
||||
the first and second operation in an `InstanceOp`'s region. The alternative of
|
||||
using two regions on the `InstanceOp` is not applicable, because the
|
||||
`InstanceOp` then needs to provide a symbol table, but the upstream
|
||||
`SymbolTable` trait enforces single-region ops. Lastly, we also considered using
|
||||
a single graph region to hold both `OperatorTypeOp`s and `OperationOp`s, but
|
||||
discarded that design because it cannot be safely roundtripped via a
|
||||
`circt::scheduling::Problem` (internally, registered operator types and
|
||||
operations are separate lists).
|
||||
|
||||
### Stand-alone use of the `OperatorLibraryOp`
|
||||
|
||||
The `OperatorLibraryOp` can be named and used outside of an `InstanceOp`. This
|
||||
is useful to share operator type definitions across multiple instances. In
|
||||
addition, until CIRCT gains better infrastructure to manage predefined hardware
|
||||
modules and their properties, such a stand-alone `OperatorLibraryOp` can also
|
||||
act as an interim solution to represent operator libraries for scheduling
|
||||
clients.
|
||||
|
||||
### Use of SSA operands _and_ symbol references to encode dependences
|
||||
|
||||
This is required to faithfully reproduce the internal modeling in the scheduling
|
||||
|
|
|
@ -45,7 +45,7 @@ include "PropertyBase.td"
|
|||
|
||||
// Problem
|
||||
def LinkedOperatorTypeProp : OperationProperty<SSPDialect,
|
||||
"LinkedOperatorType", "::mlir::FlatSymbolRefAttr", "::circt::scheduling::Problem"> {
|
||||
"LinkedOperatorType", "::mlir::SymbolRefAttr", "::circt::scheduling::Problem"> {
|
||||
let mnemonic = "opr";
|
||||
let unwrapValue = [{ getValue().getLeafReference() }];
|
||||
let wrapValue = [{ ::mlir::FlatSymbolRefAttr::get(ctx, value) }];
|
||||
|
|
|
@ -19,7 +19,7 @@ class SSPOp<string mnemonic, list<Trait> traits = []> :
|
|||
|
||||
def InstanceOp : SSPOp<"instance",
|
||||
[NoRegionArguments, SingleBlock, NoTerminator,
|
||||
IsolatedFromAbove, OpAsmOpInterface]> {
|
||||
IsolatedFromAbove, OpAsmOpInterface, SymbolTable, Symbol]> {
|
||||
let summary = "Instance of a static scheduling problem.";
|
||||
let description = [{
|
||||
This operation represents an instance of a static scheduling problem,
|
||||
|
@ -33,7 +33,7 @@ def InstanceOp : SSPOp<"instance",
|
|||
|
||||
**Example**
|
||||
```mlir
|
||||
ssp.instance "canis14_fig2" of "ModuloProblem" [II<3>] {
|
||||
ssp.instance @canis14_fig2 of "ModuloProblem" [II<3>] {
|
||||
library {
|
||||
operator_type @MemPort [latency<1>, limit<1>]
|
||||
operator_type @Add [latency<1>]
|
||||
|
@ -48,11 +48,11 @@ def InstanceOp : SSPOp<"instance",
|
|||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins StrAttr:$instanceName, StrAttr:$problemName,
|
||||
let arguments = (ins SymbolNameAttr:$sym_name, StrAttr:$problemName,
|
||||
OptionalAttr<ArrayAttr>:$properties);
|
||||
let regions = (region SizedRegion<1>:$body);
|
||||
let assemblyFormat = [{
|
||||
$instanceName `of` $problemName custom<Properties>($properties) $body attr-dict
|
||||
$sym_name `of` $problemName custom<Properties>($properties) $body attr-dict
|
||||
}];
|
||||
|
||||
let hasVerifier = true;
|
||||
|
@ -66,6 +66,9 @@ def InstanceOp : SSPOp<"instance",
|
|||
return &getBody().getBlocks().front();
|
||||
}
|
||||
|
||||
// The symbol is actually the "instance name"
|
||||
::mlir::StringAttr getInstanceNameAttr() { return getSymNameAttr(); }
|
||||
|
||||
// Access to container ops
|
||||
::circt::ssp::OperatorLibraryOp getOperatorLibrary();
|
||||
::circt::ssp::DependenceGraphOp getDependenceGraph();
|
||||
|
@ -76,7 +79,7 @@ def InstanceOp : SSPOp<"instance",
|
|||
OpBuilder<(ins "::mlir::StringAttr":$instanceName,
|
||||
"::mlir::StringAttr":$problemName,
|
||||
CArg<"::mlir::ArrayAttr", "::mlir::ArrayAttr()">:$properties), [{
|
||||
$_state.addAttribute($_builder.getStringAttr("instanceName"), instanceName);
|
||||
$_state.addAttribute(::mlir::SymbolTable::getSymbolAttrName(), instanceName);
|
||||
$_state.addAttribute($_builder.getStringAttr("problemName"), problemName);
|
||||
if (properties)
|
||||
$_state.addAttribute($_builder.getStringAttr("properties"), properties);
|
||||
|
@ -86,11 +89,55 @@ def InstanceOp : SSPOp<"instance",
|
|||
];
|
||||
}
|
||||
|
||||
class ContainerOp<string mnemonic, list<Trait> traits = []>
|
||||
: SSPOp<mnemonic, traits # [NoRegionArguments, SingleBlock, NoTerminator,
|
||||
SymbolTable, OpAsmOpInterface, HasParent<"InstanceOp">]> {
|
||||
def OperatorLibraryOp : SSPOp<"library",
|
||||
[NoRegionArguments, SingleBlock,
|
||||
NoTerminator, OpAsmOpInterface, SymbolTable, Symbol]> {
|
||||
let summary = "Container for operator types.";
|
||||
let description = [{
|
||||
The operator library abstracts the characteristics of the target
|
||||
architecture/IR (onto which the source graph is scheduled), represented by
|
||||
the individual `OperatorTypeOp`s. This operation may be used outside of an
|
||||
`InstanceOp`.
|
||||
}];
|
||||
|
||||
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"; }
|
||||
|
||||
// SymbolUserOpInterface
|
||||
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,
|
||||
HasParent<"InstanceOp">]> {
|
||||
let summary = "Container for (scheduling) operations.";
|
||||
let description = [{
|
||||
The dependence graph is spanned by `OperationOp`s (vertices) and a
|
||||
combination of MLIR value uses and symbol references (edges).
|
||||
}];
|
||||
|
||||
let assemblyFormat = "$body attr-dict";
|
||||
let regions = (region SizedRegion<1>:$body);
|
||||
|
||||
let extraClassDeclaration = [{
|
||||
// OpAsmOpInterface
|
||||
|
@ -111,24 +158,6 @@ class ContainerOp<string mnemonic, list<Trait> traits = []>
|
|||
];
|
||||
}
|
||||
|
||||
def OperatorLibraryOp : ContainerOp<"library"> {
|
||||
let summary = "Container for operator types.";
|
||||
let description = [{
|
||||
The operator library abstracts the characteristics of the target
|
||||
architecture/IR (onto which the source graph is scheduled), represented by
|
||||
the individual `OperatorTypeOp`s.
|
||||
}];
|
||||
}
|
||||
|
||||
def DependenceGraphOp : ContainerOp<"graph",
|
||||
[RegionKindInterface, HasOnlyGraphRegion]> {
|
||||
let summary = "Container for (scheduling) operations.";
|
||||
let description = [{
|
||||
The dependence graph is spanned by `OperationOp`s (vertices) and a
|
||||
combination of MLIR value uses and symbol references (edges).
|
||||
}];
|
||||
}
|
||||
|
||||
def OperatorTypeOp : SSPOp<"operator_type",
|
||||
[Symbol, HasParent<"OperatorLibraryOp">]> {
|
||||
let summary = "Element of the target architecture/IR.";
|
||||
|
@ -165,7 +194,9 @@ def OperationOp : SSPOp<"operation",
|
|||
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.
|
||||
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.
|
||||
|
||||
**Examples**
|
||||
```mlir
|
||||
|
@ -180,6 +211,9 @@ def OperationOp : SSPOp<"operation",
|
|||
|
||||
// dependence properties
|
||||
operation<@Barrier>(%2 [dist<1>], %5#1, @store_A [dist<3>])
|
||||
|
||||
// operator type in stand-alone library
|
||||
%7 = operation<@MathLib::@Sqrt>(%6)
|
||||
```
|
||||
}];
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "circt/Support/ValueMapper.h"
|
||||
|
||||
#include "mlir/IR/ImplicitLocOpBuilder.h"
|
||||
#include "mlir/IR/SymbolTable.h"
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/TypeSwitch.h"
|
||||
|
@ -92,12 +93,37 @@ void loadInstanceProperties(ProblemT &prob, ArrayAttr props) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Load the operator type represented by \p oprOp into \p prob under a unique
|
||||
/// name informed by \p oprIds, 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... OperatorTypePropertyTs>
|
||||
OperatorType loadOperatorType(ProblemT &prob, OperatorTypeOp oprOp,
|
||||
SmallDenseMap<StringAttr, unsigned> &oprIds) {
|
||||
OperatorType opr = oprOp.getNameAttr();
|
||||
unsigned &id = oprIds[opr];
|
||||
if (id > 0)
|
||||
opr = StringAttr::get(opr.getContext(),
|
||||
opr.getValue() + Twine('_') + Twine(id));
|
||||
++id;
|
||||
assert(!prob.hasOperatorType(opr));
|
||||
prob.insertOperatorType(opr);
|
||||
loadOperatorTypeProperties<ProblemT, OperatorTypePropertyTs...>(
|
||||
prob, opr, oprOp.getPropertiesAttr());
|
||||
return opr;
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// elements may therefore be unitialized objects. The template instantiation
|
||||
/// fails if properties are incompatible with \p ProblemT.
|
||||
///
|
||||
/// Operations may link to operator types in other libraries, but the origin of
|
||||
/// an operator type will not be preserved in the problem instance. As this
|
||||
/// could lead to conflicts, operator types will be automatically renamed in the
|
||||
/// returned instance.
|
||||
///
|
||||
/// Example: To load an instance of the `circt::scheduling::CyclicProblem` with
|
||||
/// all its input and solution properties, call this as follows:
|
||||
///
|
||||
|
@ -121,11 +147,19 @@ ProblemT loadProblem(InstanceOp instOp,
|
|||
loadInstanceProperties<ProblemT, InstancePropertyTs...>(
|
||||
prob, instOp.getPropertiesAttr());
|
||||
|
||||
instOp.getOperatorLibrary().walk([&](OperatorTypeOp oprOp) {
|
||||
OperatorType opr = oprOp.getNameAttr();
|
||||
prob.insertOperatorType(opr);
|
||||
loadOperatorTypeProperties<ProblemT, OperatorTypePropertyTs...>(
|
||||
prob, opr, oprOp.getPropertiesAttr());
|
||||
// Use IDs to disambiguate operator types with the same name defined in
|
||||
// different libraries.
|
||||
SmallDenseMap<OperatorType, unsigned> operatorTypeIds;
|
||||
// Map `OperatorTypeOp`s to their (possibly uniqued) name in the problem
|
||||
// instance.
|
||||
SmallDenseMap<Operation *, OperatorType> operatorTypes;
|
||||
|
||||
// Register all operator types in the instance's library.
|
||||
auto libraryOp = instOp.getOperatorLibrary();
|
||||
libraryOp.walk([&](OperatorTypeOp oprOp) {
|
||||
operatorTypes[oprOp] =
|
||||
loadOperatorType<ProblemT, OperatorTypePropertyTs...>(prob, oprOp,
|
||||
operatorTypeIds);
|
||||
});
|
||||
|
||||
// Register all operations first, in order to retain their original order.
|
||||
|
@ -134,6 +168,38 @@ ProblemT loadProblem(InstanceOp instOp,
|
|||
prob.insertOperation(opOp);
|
||||
loadOperationProperties<ProblemT, OperationPropertyTs...>(
|
||||
prob, opOp, opOp.getPropertiesAttr());
|
||||
|
||||
// Nothing else to check if no linked operator type is set for `opOp`,
|
||||
// because the operation doesn't carry a `LinkedOperatorTypeAttr`, or that
|
||||
// class is not part of the `OperationPropertyTs` to load.
|
||||
if (!prob.getLinkedOperatorType(opOp).has_value())
|
||||
return;
|
||||
|
||||
// Otherwise, inspect the corresponding attribute to make sure the operator
|
||||
// type is available.
|
||||
SymbolRefAttr oprRef = opOp.getLinkedOperatorTypeAttr().getValue();
|
||||
|
||||
Operation *oprOp;
|
||||
// 1) Look in the instance's library.
|
||||
oprOp = SymbolTable::lookupSymbolIn(libraryOp, oprRef);
|
||||
// 2) Try to resolve a nested reference to the instance's library.
|
||||
if (!oprOp)
|
||||
oprOp = SymbolTable::lookupSymbolIn(instOp, oprRef);
|
||||
// 3) Look outside of the instance.
|
||||
if (!oprOp)
|
||||
oprOp =
|
||||
SymbolTable::lookupNearestSymbolFrom(instOp->getParentOp(), oprRef);
|
||||
|
||||
assert(oprOp && isa<OperatorTypeOp>(oprOp)); // checked by verifier
|
||||
|
||||
// Load the operator type from `oprOp` if needed.
|
||||
auto &opr = operatorTypes[oprOp];
|
||||
if (!opr)
|
||||
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);
|
||||
});
|
||||
|
||||
// Then walk them again, and load auxiliary dependences as well as any
|
||||
|
|
|
@ -61,7 +61,7 @@ ParseResult OperationOp::parse(OpAsmParser &parser, OperationState &result) {
|
|||
if (parser.parseLess())
|
||||
return failure();
|
||||
|
||||
FlatSymbolRefAttr oprRef;
|
||||
SymbolRefAttr oprRef;
|
||||
auto parseSymbolResult = parser.parseOptionalAttribute(oprRef);
|
||||
if (parseSymbolResult.has_value()) {
|
||||
assert(succeeded(*parseSymbolResult));
|
||||
|
@ -276,8 +276,18 @@ OperationOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
|
|||
// If a linkedOperatorType property is present, verify that it references a
|
||||
// valid operator type.
|
||||
if (auto linkedOpr = getLinkedOperatorTypeAttr()) {
|
||||
FlatSymbolRefAttr oprRef = linkedOpr.getValue();
|
||||
Operation *oprOp = symbolTable.lookupSymbolIn(libraryOp, oprRef);
|
||||
SymbolRefAttr oprRef = linkedOpr.getValue();
|
||||
Operation *oprOp;
|
||||
// 1) Look in the instance's library.
|
||||
oprOp = symbolTable.lookupSymbolIn(libraryOp, oprRef);
|
||||
// 2) Try to resolve a nested reference to the instance's library.
|
||||
if (!oprOp)
|
||||
oprOp = symbolTable.lookupSymbolIn(instanceOp, oprRef);
|
||||
// 3) Look outside of the instance.
|
||||
if (!oprOp)
|
||||
oprOp = symbolTable.lookupNearestSymbolFrom(instanceOp->getParentOp(),
|
||||
oprRef);
|
||||
|
||||
if (!oprOp || !isa<OperatorTypeOp>(oprOp))
|
||||
return emitError("Linked operator type property references invalid "
|
||||
"operator type: ")
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
// RUN: circt-opt %s -split-input-file -verify-diagnostics
|
||||
|
||||
// expected-error @+1 {{must contain exactly one 'library' op and one 'graph' op}}
|
||||
ssp.instance "containers_empty" of "Problem" {}
|
||||
ssp.instance @containers_empty of "Problem" {}
|
||||
|
||||
// -----
|
||||
|
||||
// expected-error @+1 {{must contain the 'library' op followed by the 'graph' op}}
|
||||
ssp.instance "containers_wrong_order" of "Problem" {
|
||||
ssp.instance @containers_wrong_order of "Problem" {
|
||||
graph {}
|
||||
library {}
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
ssp.instance "deps_out_of_bounds" of "Problem" {
|
||||
ssp.instance @deps_out_of_bounds of "Problem" {
|
||||
library {}
|
||||
graph {
|
||||
// expected-error @+1 {{Operand index is out of bounds for def-use dependence attribute}}
|
||||
|
@ -23,7 +23,7 @@ ssp.instance "deps_out_of_bounds" of "Problem" {
|
|||
|
||||
// -----
|
||||
|
||||
ssp.instance "deps_defuse_not_increasing" of "Problem" {
|
||||
ssp.instance @deps_defuse_not_increasing of "Problem" {
|
||||
library {}
|
||||
graph {
|
||||
%0:2 = ssp.operation<>()
|
||||
|
@ -34,7 +34,7 @@ ssp.instance "deps_defuse_not_increasing" of "Problem" {
|
|||
|
||||
// -----
|
||||
|
||||
ssp.instance "deps_interleaved" of "Problem" {
|
||||
ssp.instance @deps_interleaved of "Problem" {
|
||||
library {}
|
||||
graph {
|
||||
%0 = operation<> @Op()
|
||||
|
@ -45,7 +45,7 @@ ssp.instance "deps_interleaved" of "Problem" {
|
|||
|
||||
// -----
|
||||
|
||||
ssp.instance "deps_aux_not_consecutive" of "Problem" {
|
||||
ssp.instance @deps_aux_not_consecutive of "Problem" {
|
||||
library {}
|
||||
graph {
|
||||
operation<> @Op()
|
||||
|
@ -56,7 +56,7 @@ ssp.instance "deps_aux_not_consecutive" of "Problem" {
|
|||
|
||||
// -----
|
||||
|
||||
ssp.instance "deps_aux_invalid" of "Problem" {
|
||||
ssp.instance @deps_aux_invalid of "Problem" {
|
||||
library {}
|
||||
graph {
|
||||
// expected-error @+1 {{Auxiliary dependence references invalid source operation: @InvalidOp}}
|
||||
|
@ -66,10 +66,21 @@ ssp.instance "deps_aux_invalid" of "Problem" {
|
|||
|
||||
// -----
|
||||
|
||||
ssp.instance "linked_opr_invalid" of "Problem" {
|
||||
ssp.instance @linked_opr_invalid of "Problem" {
|
||||
library {}
|
||||
graph {
|
||||
// expected-error @+1 {{Linked operator type property references invalid operator type: @InvalidOpr}}
|
||||
operation<@InvalidOpr>()
|
||||
}
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
ssp.library @standalone {}
|
||||
ssp.instance @standalone_opr_invalid of "Problem" {
|
||||
library {}
|
||||
graph {
|
||||
// expected-error @+1 {{Linked operator type property references invalid operator type: @standalone::@InvalidOpr}}
|
||||
operation<@standalone::@InvalidOpr>()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// 1) tests the plain parser/printer roundtrip.
|
||||
// 2) roundtrips via the scheduling infra (i.e. populates a `Problem` instance and reconstructs the SSP IR from it.)
|
||||
|
||||
// CHECK: ssp.instance "no properties" of "Problem" {
|
||||
// CHECK: ssp.instance @"no properties" of "Problem" {
|
||||
// CHECK: library {
|
||||
// CHECK: operator_type @NoProps
|
||||
// CHECK: }
|
||||
|
@ -15,7 +15,7 @@
|
|||
// CHECK: operation<>(%[[op_0]], @Op0)
|
||||
// CHECK: }
|
||||
// CHECK: }
|
||||
ssp.instance "no properties" of "Problem" {
|
||||
ssp.instance @"no properties" of "Problem" {
|
||||
library {
|
||||
operator_type @NoProps
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ ssp.instance "no properties" of "Problem" {
|
|||
}
|
||||
}
|
||||
|
||||
// CHECK: ssp.instance "arbitrary_latencies" of "Problem" {
|
||||
// CHECK: ssp.instance @arbitrary_latencies of "Problem" {
|
||||
// CHECK: library {
|
||||
// CHECK: operator_type @unit [latency<1>]
|
||||
// CHECK: operator_type @extr [latency<0>]
|
||||
|
@ -45,7 +45,7 @@ ssp.instance "no properties" of "Problem" {
|
|||
// CHECK: operation<@unit>(%[[op_5]]) [t<60>]
|
||||
// CHECK: }
|
||||
// CHECK: }
|
||||
ssp.instance "arbitrary_latencies" of "Problem" {
|
||||
ssp.instance @arbitrary_latencies of "Problem" {
|
||||
library {
|
||||
operator_type @unit [latency<1>]
|
||||
operator_type @extr [latency<0>]
|
||||
|
@ -64,7 +64,7 @@ ssp.instance "arbitrary_latencies" of "Problem" {
|
|||
}
|
||||
}
|
||||
|
||||
// CHECK: ssp.instance "self_arc" of "CyclicProblem" [II<3>] {
|
||||
// CHECK: ssp.instance @self_arc of "CyclicProblem" [II<3>] {
|
||||
// CHECK: library {
|
||||
// CHECK: operator_type @unit [latency<1>]
|
||||
// CHECK: operator_type @_3 [latency<3>]
|
||||
|
@ -75,7 +75,7 @@ ssp.instance "arbitrary_latencies" of "Problem" {
|
|||
// CHECK: operation<@unit>(%[[op_1]]) [t<4>]
|
||||
// CHECK: }
|
||||
// CHECK: }
|
||||
ssp.instance "self_arc" of "CyclicProblem" [II<3>] {
|
||||
ssp.instance @self_arc of "CyclicProblem" [II<3>] {
|
||||
library {
|
||||
operator_type @unit [latency<1>]
|
||||
operator_type @_3 [latency<3>]
|
||||
|
@ -87,7 +87,7 @@ ssp.instance "self_arc" of "CyclicProblem" [II<3>] {
|
|||
}
|
||||
}
|
||||
|
||||
// CHECK: ssp.instance "multiple_oprs" of "SharedOperatorsProblem" {
|
||||
// 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>]
|
||||
|
@ -104,7 +104,7 @@ ssp.instance "self_arc" of "CyclicProblem" [II<3>] {
|
|||
// CHECK: operation<@_1>() [t<10>]
|
||||
// CHECK: }
|
||||
// CHECK: }
|
||||
ssp.instance "multiple_oprs" of "SharedOperatorsProblem" {
|
||||
ssp.instance @multiple_oprs of "SharedOperatorsProblem" {
|
||||
library {
|
||||
operator_type @slowAdd [latency<3>, limit<2>]
|
||||
operator_type @fastAdd [latency<1>, limit<1>]
|
||||
|
@ -122,7 +122,7 @@ ssp.instance "multiple_oprs" of "SharedOperatorsProblem" {
|
|||
}
|
||||
}
|
||||
|
||||
// CHECK: ssp.instance "canis14_fig2" of "ModuloProblem" [II<3>] {
|
||||
// CHECK: ssp.instance @canis14_fig2 of "ModuloProblem" [II<3>] {
|
||||
// CHECK: library {
|
||||
// CHECK: operator_type @MemPort [latency<1>, limit<1>]
|
||||
// CHECK: operator_type @Add [latency<1>]
|
||||
|
@ -136,7 +136,7 @@ ssp.instance "multiple_oprs" of "SharedOperatorsProblem" {
|
|||
// CHECK: operation<@Implicit> @last(@store_A) [t<5>]
|
||||
// CHECK: }
|
||||
// CHECK: }
|
||||
ssp.instance "canis14_fig2" of "ModuloProblem" [II<3>] {
|
||||
ssp.instance @canis14_fig2 of "ModuloProblem" [II<3>] {
|
||||
library {
|
||||
operator_type @MemPort [latency<1>, limit<1>]
|
||||
operator_type @Add [latency<1>]
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
// RUN: circt-opt %s | circt-opt | FileCheck %s
|
||||
// RUN: circt-opt %s -test-ssp-roundtrip | circt-opt | FileCheck %s --check-prefix=INFRA
|
||||
|
||||
// 1) tests the plain parser/printer roundtrip.
|
||||
// CHECK: ssp.library @Lib {
|
||||
// CHECK: operator_type @Opr [latency<1>, limit<1>]
|
||||
// CHECK: }
|
||||
// CHECK: module @SomeModule {
|
||||
// CHECK: ssp.library @Lib {
|
||||
// CHECK: operator_type @Opr [latency<2>, limit<2>]
|
||||
// CHECK: }
|
||||
// CHECK: }
|
||||
// CHECK: ssp.instance @SomeInstance of "ModuloProblem" {
|
||||
// CHECK: library @InternalLib {
|
||||
// CHECK: operator_type @Opr [latency<3>, 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: }
|
||||
// CHECK: }
|
||||
|
||||
// 2) Import/export via the scheduling infra (i.e. populates a `Problem` instance and reconstructs the SSP IR from it.)
|
||||
// Operator types from stand-alone libraries are appended to the instance's internal library, whose name is not preserved.
|
||||
// INFRA: ssp.instance @SomeInstance of "ModuloProblem" {
|
||||
// INFRA: library {
|
||||
// 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: }
|
||||
// INFRA: graph {
|
||||
// INFRA: operation<@Opr>()
|
||||
// INFRA: operation<@Opr>()
|
||||
// INFRA: operation<@Opr>()
|
||||
// INFRA: operation<@Opr_1>()
|
||||
// INFRA: operation<@Opr_2>()
|
||||
// INFRA: }
|
||||
// INFRA: }
|
||||
|
||||
ssp.library @Lib {
|
||||
operator_type @Opr [latency<1>, limit<1>]
|
||||
}
|
||||
module @SomeModule {
|
||||
ssp.library @Lib {
|
||||
operator_type @Opr [latency<2>, limit<2>]
|
||||
}
|
||||
}
|
||||
ssp.instance @SomeInstance of "ModuloProblem" {
|
||||
library @InternalLib {
|
||||
operator_type @Opr [latency<3>, limit<3>]
|
||||
}
|
||||
graph {
|
||||
operation<@Opr>()
|
||||
operation<@InternalLib::@Opr>()
|
||||
operation<@SomeInstance::@InternalLib::@Opr>()
|
||||
operation<@Lib::@Opr>()
|
||||
operation<@SomeModule::@Lib::@Opr>()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue