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