CIRCT  19.0.0git
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 
18 using namespace circt;
19 using namespace circt::ssp;
20 
21 //===----------------------------------------------------------------------===//
22 // InstanceOp
23 //===----------------------------------------------------------------------===//
24 
25 LogicalResult 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.
43 OperatorLibraryOp InstanceOp::getOperatorLibrary() {
44  return *getOps<OperatorLibraryOp>().begin();
45 }
46 
47 DependenceGraphOp InstanceOp::getDependenceGraph() {
48  return *getOps<DependenceGraphOp>().begin();
49 }
50 
51 //===----------------------------------------------------------------------===//
52 // OperationOp
53 //===----------------------------------------------------------------------===//
54 
55 ParseResult 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 
149 void 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 
215 LogicalResult 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 
254 LogicalResult
255 OperationOp::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 
300 LinkedOperatorTypeAttr OperationOp::getLinkedOperatorTypeAttr() {
301  if (ArrayAttr properties = getSspPropertiesAttr()) {
302  const auto *it = llvm::find_if(properties, [](Attribute a) {
303  return a.isa<LinkedOperatorTypeAttr>();
304  });
305  if (it != properties.end())
306  return (*it).cast<LinkedOperatorTypeAttr>();
307  }
308  return {};
309 }
310 
311 //===----------------------------------------------------------------------===//
312 // Wrappers for the `custom<Properties>` ODS directive.
313 //===----------------------------------------------------------------------===//
314 
315 static ParseResult parseSSPProperties(OpAsmParser &parser, ArrayAttr &attr) {
316  auto result = parseOptionalPropertyArray(attr, parser);
317  if (!result.has_value() || succeeded(*result))
318  return success();
319  return failure();
320 }
321 
322 static void printSSPProperties(OpAsmPrinter &p, Operation *op, ArrayAttr attr) {
323  if (!attr)
324  return;
325  printPropertyArray(attr, p);
326 }
327 
328 //===----------------------------------------------------------------------===//
329 // TableGen'ed code
330 //===----------------------------------------------------------------------===//
331 
332 #define GET_OP_CLASSES
333 #include "circt/Dialect/SSP/SSP.cpp.inc"
assert(baseType &&"element must be base type")
Builder builder
static void printSSPProperties(OpAsmPrinter &p, Operation *op, ArrayAttr attr)
Definition: SSPOps.cpp:322
static ParseResult parseSSPProperties(OpAsmParser &parser, ArrayAttr &attr)
Definition: SSPOps.cpp:315
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.
Definition: DebugAnalysis.h:21