CIRCT 21.0.0git
Loading...
Searching...
No Matches
SSPOps.cpp
Go to the documentation of this file.
1//===- SSPOps.cpp - SSP operation implementation --------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the SSP (static scheduling problem) dialect operations.
10//
11//===----------------------------------------------------------------------===//
12
14#include "circt/Support/LLVM.h"
15
16#include "mlir/IR/Builders.h"
17
18using namespace circt;
19using namespace circt::ssp;
20
21//===----------------------------------------------------------------------===//
22// InstanceOp
23//===----------------------------------------------------------------------===//
24
25LogicalResult InstanceOp::verify() {
26 auto *body = getBodyBlock();
27 auto libraryOps = body->getOps<OperatorLibraryOp>();
28 auto graphOps = body->getOps<DependenceGraphOp>();
29
30 if (std::distance(libraryOps.begin(), libraryOps.end()) != 1 ||
31 std::distance(graphOps.begin(), graphOps.end()) != 1)
32 return emitOpError()
33 << "must contain exactly one 'library' op and one 'graph' op";
34
35 if ((*graphOps.begin())->isBeforeInBlock(*libraryOps.begin()))
36 return emitOpError()
37 << "must contain the 'library' op followed by the 'graph' op";
38
39 return success();
40}
41
42// The verifier checks that exactly one of each of the container ops is present.
43OperatorLibraryOp InstanceOp::getOperatorLibrary() {
44 return *getOps<OperatorLibraryOp>().begin();
45}
46
47DependenceGraphOp InstanceOp::getDependenceGraph() {
48 return *getOps<DependenceGraphOp>().begin();
49}
50
51//===----------------------------------------------------------------------===//
52// OperationOp
53//===----------------------------------------------------------------------===//
54
55ParseResult OperationOp::parse(OpAsmParser &parser, OperationState &result) {
56 auto &builder = parser.getBuilder();
57
58 // Special handling for the linked operator type property
59 SmallVector<Attribute> alreadyParsed;
60
61 if (parser.parseLess())
62 return failure();
63
64 SymbolRefAttr oprRef;
65 auto parseSymbolResult = parser.parseOptionalAttribute(oprRef);
66 if (parseSymbolResult.has_value()) {
67 assert(succeeded(*parseSymbolResult));
68 alreadyParsed.push_back(builder.getAttr<LinkedOperatorTypeAttr>(oprRef));
69 }
70
71 if (parser.parseGreater())
72 return failure();
73
74 // (Scheduling) operation's name
75 StringAttr opName;
76 (void)parser.parseOptionalSymbolName(opName, SymbolTable::getSymbolAttrName(),
77 result.attributes);
78
79 // Dependences
80 SmallVector<OpAsmParser::UnresolvedOperand> unresolvedOperands;
81 SmallVector<Attribute> dependences;
82 unsigned operandIdx = 0;
83 auto parseDependenceSourceWithAttrDict = [&]() -> ParseResult {
84 llvm::SMLoc loc = parser.getCurrentLocation();
85 FlatSymbolRefAttr sourceRef;
86 ArrayAttr properties;
87
88 // Try to parse either symbol reference...
89 auto parseSymbolResult = parser.parseOptionalAttribute(sourceRef);
90 if (parseSymbolResult.has_value())
91 assert(succeeded(*parseSymbolResult));
92 else {
93 // ...or an SSA operand.
94 OpAsmParser::UnresolvedOperand operand;
95 if (parser.parseOperand(operand))
96 return parser.emitError(loc, "expected SSA value or symbol reference");
97
98 unresolvedOperands.push_back(operand);
99 }
100
101 // Parse the properties, if present.
102 parseOptionalPropertyArray(properties, parser);
103
104 // No need to explicitly store SSA deps without properties.
105 if (sourceRef || properties)
106 dependences.push_back(
107 builder.getAttr<DependenceAttr>(operandIdx, sourceRef, properties));
108
109 ++operandIdx;
110 return success();
111 };
112
113 if (parser.parseCommaSeparatedList(AsmParser::Delimiter::Paren,
114 parseDependenceSourceWithAttrDict))
115 return failure();
116
117 if (!dependences.empty())
118 result.addAttribute(builder.getStringAttr("dependences"),
119 builder.getArrayAttr(dependences));
120
121 // Properties
122 ArrayAttr properties;
123 auto parsePropertiesResult =
124 parseOptionalPropertyArray(properties, parser, alreadyParsed);
125 if (parsePropertiesResult.has_value()) {
126 if (failed(*parsePropertiesResult))
127 return failure();
128 result.addAttribute(builder.getStringAttr("sspProperties"), properties);
129 }
130
131 // Parse default attr-dict
132 if (parser.parseOptionalAttrDict(result.attributes))
133 return failure();
134
135 // Resolve operands
136 SmallVector<Value> operands;
137 if (parser.resolveOperands(unresolvedOperands, builder.getNoneType(),
138 operands))
139 return failure();
140 result.addOperands(operands);
141
142 // Mockup results
143 SmallVector<Type> types(parser.getNumResults(), builder.getNoneType());
144 result.addTypes(types);
145
146 return success();
147}
148
149void OperationOp::print(OpAsmPrinter &p) {
150 // Special handling for the linked operator type
151 SmallVector<Attribute> alreadyPrinted;
152
153 p << '<';
154 if (auto linkedOpr = getLinkedOperatorTypeAttr()) {
155 p.printAttribute(linkedOpr.getValue());
156 alreadyPrinted.push_back(linkedOpr);
157 }
158 p << '>';
159
160 // (Scheduling) operation's name
161 if (StringAttr symName = getSymNameAttr()) {
162 p << ' ';
163 p.printSymbolName(symName);
164 }
165
166 // Dependences = SSA operands + other OperationOps via symbol references.
167 // Emitted format looks like this:
168 // (%0, %1 [#ssp.some_property<42>, ...], %2, ...,
169 // @op0, @op1 [#ssp.some_property<17>, ...], ...)
170 SmallVector<DependenceAttr> defUseDeps(getNumOperands()), auxDeps;
171 if (ArrayAttr dependences = getDependencesAttr()) {
172 for (auto dep : dependences.getAsRange<DependenceAttr>()) {
173 if (dep.getSourceRef())
174 auxDeps.push_back(dep);
175 else
176 defUseDeps[dep.getOperandIdx()] = dep;
177 }
178 }
179
180 p << '(';
181 llvm::interleaveComma((*this)->getOpOperands(), p, [&](OpOperand &operand) {
182 p.printOperand(operand.get());
183 if (DependenceAttr dep = defUseDeps[operand.getOperandNumber()]) {
184 p << ' ';
185 p.printAttribute(dep.getProperties());
186 }
187 });
188 if (!auxDeps.empty()) {
189 if (!defUseDeps.empty())
190 p << ", ";
191 llvm::interleaveComma(auxDeps, p, [&](DependenceAttr dep) {
192 p.printAttribute(dep.getSourceRef());
193 if (ArrayAttr depProps = dep.getProperties()) {
194 p << ' ';
195 printPropertyArray(depProps, p);
196 }
197 });
198 }
199 p << ')';
200
201 // Properties
202 if (ArrayAttr properties = getSspPropertiesAttr()) {
203 p << ' ';
204 printPropertyArray(properties, p, alreadyPrinted);
205 }
206
207 // Default attr-dict
208 SmallVector<StringRef> elidedAttrs = {
209 SymbolTable::getSymbolAttrName(),
210 OperationOp::getDependencesAttrName().getValue(),
211 OperationOp::getSspPropertiesAttrName().getValue()};
212 p.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
213}
214
215LogicalResult OperationOp::verify() {
216 ArrayAttr dependences = getDependencesAttr();
217 if (!dependences)
218 return success();
219
220 int nOperands = getNumOperands();
221 int lastIdx = -1;
222 for (auto dep : dependences.getAsRange<DependenceAttr>()) {
223 int idx = dep.getOperandIdx();
224 FlatSymbolRefAttr sourceRef = dep.getSourceRef();
225
226 if (!sourceRef) {
227 // Def-use deps use the index to refer to one of the SSA operands.
228 if (idx >= nOperands)
229 return emitError(
230 "Operand index is out of bounds for def-use dependence attribute");
231
232 // Indices may be sparse, but shall be sorted and unique.
233 if (idx <= lastIdx)
234 return emitError("Def-use operand indices in dependence attribute are "
235 "not monotonically increasing");
236 } else {
237 // Auxiliary deps are expected to follow the def-use deps (if present),
238 // and hence use indices >= #operands.
239 if (idx < nOperands)
240 return emitError() << "Auxiliary dependence from " << sourceRef
241 << " is interleaved with SSA operands";
242
243 // Indices shall be consecutive (special case: the first aux dep)
244 if (!((idx == lastIdx + 1) || (idx > lastIdx && idx == nOperands)))
245 return emitError("Auxiliary operand indices in dependence attribute "
246 "are not consecutive");
247 }
248
249 lastIdx = idx;
250 }
251 return success();
252}
253
254LogicalResult
255OperationOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
256 auto instanceOp = (*this)->getParentOfType<InstanceOp>();
257 auto libraryOp = instanceOp.getOperatorLibrary();
258 auto graphOp = instanceOp.getDependenceGraph();
259
260 // Verify that all auxiliary dependences reference valid named operations
261 // inside the dependence graph.
262 if (ArrayAttr dependences = getDependencesAttr())
263 for (auto dep : dependences.getAsRange<DependenceAttr>()) {
264 FlatSymbolRefAttr sourceRef = dep.getSourceRef();
265 if (!sourceRef)
266 continue;
267
268 Operation *sourceOp = symbolTable.lookupSymbolIn(graphOp, sourceRef);
269 if (!sourceOp || !isa<OperationOp>(sourceOp)) {
270 return emitError(
271 "Auxiliary dependence references invalid source operation: ")
272 << sourceRef;
273 }
274 }
275
276 // If a linkedOperatorType property is present, verify that it references a
277 // valid operator type.
278 if (auto linkedOpr = getLinkedOperatorTypeAttr()) {
279 SymbolRefAttr oprRef = linkedOpr.getValue();
280 Operation *oprOp;
281 // 1) Look in the instance's library.
282 oprOp = symbolTable.lookupSymbolIn(libraryOp, oprRef);
283 // 2) Try to resolve a nested reference to the instance's library.
284 if (!oprOp)
285 oprOp = symbolTable.lookupSymbolIn(instanceOp, oprRef);
286 // 3) Look outside of the instance.
287 if (!oprOp)
288 oprOp = symbolTable.lookupNearestSymbolFrom(instanceOp->getParentOp(),
289 oprRef);
290
291 if (!oprOp || !isa<OperatorTypeOp>(oprOp))
292 return emitError("Linked operator type property references invalid "
293 "operator type: ")
294 << oprRef;
295 }
296
297 return success();
298}
299
300LinkedOperatorTypeAttr OperationOp::getLinkedOperatorTypeAttr() {
301 if (ArrayAttr properties = getSspPropertiesAttr()) {
302 const auto *it = llvm::find_if(
303 properties, [](Attribute a) { return isa<LinkedOperatorTypeAttr>(a); });
304 if (it != properties.end())
305 return cast<LinkedOperatorTypeAttr>(*it);
306 }
307 return {};
308}
309
310//===----------------------------------------------------------------------===//
311// Wrappers for the `custom<Properties>` ODS directive.
312//===----------------------------------------------------------------------===//
313
314static ParseResult parseSSPProperties(OpAsmParser &parser, ArrayAttr &attr) {
315 auto result = parseOptionalPropertyArray(attr, parser);
316 if (!result.has_value() || succeeded(*result))
317 return success();
318 return failure();
319}
320
321static void printSSPProperties(OpAsmPrinter &p, Operation *op, ArrayAttr attr) {
322 if (!attr)
323 return;
324 printPropertyArray(attr, p);
325}
326
327//===----------------------------------------------------------------------===//
328// TableGen'ed code
329//===----------------------------------------------------------------------===//
330
331#define GET_OP_CLASSES
332#include "circt/Dialect/SSP/SSP.cpp.inc"
assert(baseType &&"element must be base type")
static Block * getBodyBlock(FModuleLike mod)
static void printSSPProperties(OpAsmPrinter &p, Operation *op, ArrayAttr attr)
Definition SSPOps.cpp:321
static ParseResult parseSSPProperties(OpAsmParser &parser, ArrayAttr &attr)
Definition SSPOps.cpp:314
void printPropertyArray(ArrayAttr attr, AsmPrinter &p, ArrayRef< Attribute > alreadyPrinted={})
Print an array attribute, suppressing the #ssp.
mlir::OptionalParseResult parseOptionalPropertyArray(ArrayAttr &attr, AsmParser &parser, ArrayRef< Attribute > alreadyParsed={})
Parse an array of attributes while recognizing the properties of the SSP dialect even without a #ssp.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.