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