CIRCT  18.0.0git
IbisOps.cpp
Go to the documentation of this file.
1 //===- IbisOps.cpp - Implementation of Ibis dialect ops -------------------===//
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 
12 
13 #include "mlir/IR/BuiltinOps.h"
14 #include "mlir/IR/DialectImplementation.h"
15 #include "mlir/IR/PatternMatch.h"
16 #include "mlir/IR/SymbolTable.h"
17 #include "mlir/Interfaces/FunctionImplementation.h"
18 #include "llvm/ADT/TypeSwitch.h"
19 
20 using namespace mlir;
21 using namespace circt;
22 using namespace ibis;
23 
24 // Looks up a `sym`-symbol defining operation of type T in the `mlir::ModuleOp`
25 // parent scope of the provided `base` operation.
26 template <typename T>
27 static T lookupInModule(Operation *base, FlatSymbolRefAttr sym,
28  SymbolTable *symbolTable) {
29  auto mod = base->getParentOfType<mlir::ModuleOp>();
30  if (symbolTable)
31  return dyn_cast<T>(symbolTable->lookupSymbolIn(mod, sym));
32 
33  return mod.lookupSymbol<T>(sym);
34 }
35 
36 template <typename TSymAttr>
37 ParseResult parseScopeRefFromName(OpAsmParser &parser, Type &scopeRefType,
38  TSymAttr sym) {
39  // Nothing to parse, since this is already encoded in the child symbol.
40  scopeRefType = ScopeRefType::get(parser.getContext(), sym);
41  return success();
42 }
43 
44 template <typename TSymAttr>
45 void printScopeRefFromName(OpAsmPrinter &p, Operation *op, Type type,
46  TSymAttr sym) {
47  // Nothing to print since this information is already encoded in the child
48  // symbol.
49 }
50 
51 // Generates a name for Ibis values.
52 // NOLINTBEGIN(misc-no-recursion)
53 static llvm::raw_string_ostream &genValueName(llvm::raw_string_ostream &os,
54  Value value) {
55  auto *definingOp = value.getDefiningOp();
56  assert(definingOp && "scoperef should always be defined by some op");
57  llvm::TypeSwitch<Operation *, void>(definingOp)
58  .Case<ThisOp>([&](auto op) { os << "this"; })
59  .Case<InstanceOp, ContainerInstanceOp>(
60  [&](auto op) { os << op.getInstanceNameAttr().strref(); })
61  .Case<PortOpInterface>([&](auto op) { os << op.getPortName().strref(); })
62  .Case<PathOp>([&](auto op) {
63  llvm::interleave(
64  op.getPathAsRange(), os,
65  [&](PathStepAttr step) {
66  if (step.getDirection() == PathDirection::Parent)
67  os << "parent";
68  else
69  os << step.getChild().getAttr().strref();
70  },
71  ".");
72  })
73  .Case<GetPortOp>([&](auto op) {
74  genValueName(os, op.getInstance())
75  << "." << op.getPortSymbol() << ".ref";
76  })
77  .Case<PortReadOp>(
78  [&](auto op) { genValueName(os, op.getPort()) << ".val"; })
79  .Default([&](auto op) {
80  op->emitOpError() << "unhandled value type";
81  assert(false && "unhandled value type");
82  });
83  return os;
84 }
85 // NOLINTEND(misc-no-recursion)
86 
87 // Generates a name for Ibis values, and returns a StringAttr.
88 static StringAttr genValueNameAttr(Value v) {
89  std::string s;
90  llvm::raw_string_ostream os(s);
91  genValueName(os, v);
92  return StringAttr::get(v.getContext(), s);
93 }
94 
95 //===----------------------------------------------------------------------===//
96 // ScopeOpInterface
97 //===----------------------------------------------------------------------===//
98 
101  auto scopeOp = cast<ScopeOpInterface>(op);
102  auto thisOps = scopeOp.getBodyBlock()->getOps<ibis::ThisOp>();
103  if (thisOps.empty())
104  return op->emitOpError("must contain a 'ibis.this' operation");
105 
106  if (std::next(thisOps.begin()) != thisOps.end())
107  return op->emitOpError("must contain only one 'ibis.this' operation");
108 
109  return {*thisOps.begin()};
110 }
111 
112 LogicalResult circt::ibis::detail::verifyScopeOpInterface(Operation *op) {
113  if (failed(getThisFromScope(op)))
114  return failure();
115 
116  if (!isa<SymbolOpInterface>(op))
117  return op->emitOpError("must implement 'SymbolOpInterface'");
118 
119  return success();
120 }
121 
122 //===----------------------------------------------------------------------===//
123 // MethodOp
124 //===----------------------------------------------------------------------===//
125 
126 template <typename TOp>
127 ParseResult parseMethodLikeOp(OpAsmParser &parser, OperationState &result) {
128  // Parse the name as a symbol.
129  StringAttr nameAttr;
130  if (parser.parseSymbolName(nameAttr))
131  return failure();
132 
133  result.attributes.append(hw::InnerSymbolTable::getInnerSymbolAttrName(),
134  hw::InnerSymAttr::get(nameAttr));
135 
136  // Parse the function signature.
137  SmallVector<OpAsmParser::Argument, 4> args;
138  SmallVector<Attribute> argNames;
139  SmallVector<Type> resultTypes;
140  TypeAttr functionType;
141 
142  using namespace mlir::function_interface_impl;
143  auto *context = parser.getContext();
144 
145  // Parse the argument list.
146  if (parser.parseArgumentList(args, OpAsmParser::Delimiter::Paren,
147  /*allowType=*/true, /*allowAttrs=*/false))
148  return failure();
149 
150  // Parse the result types
151  if (parser.parseOptionalArrowTypeList(resultTypes))
152  return failure();
153 
154  // Process the ssa args for the information we're looking for.
155  SmallVector<Type> argTypes;
156  for (auto &arg : args) {
157  argNames.push_back(parsing_util::getNameFromSSA(context, arg.ssaName.name));
158  argTypes.push_back(arg.type);
159  if (!arg.sourceLoc)
160  arg.sourceLoc = parser.getEncodedSourceLoc(arg.ssaName.location);
161  }
162 
163  functionType =
164  TypeAttr::get(FunctionType::get(context, argTypes, resultTypes));
165 
166  // Parse the attribute dict.
167  if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
168  return failure();
169 
170  result.addAttribute("argNames", ArrayAttr::get(context, argNames));
171  result.addAttribute(TOp::getFunctionTypeAttrName(result.name), functionType);
172 
173  // Parse the function body.
174  auto *body = result.addRegion();
175  if (parser.parseRegion(*body, args))
176  return failure();
177 
178  return success();
179 }
180 
181 template <typename TOp>
182 void printMethodLikeOp(TOp op, OpAsmPrinter &p) {
183  FunctionType funcTy = op.getFunctionType();
184  p << ' ';
185  p.printSymbolName(op.getInnerSym().getSymName());
186  Region &body = op.getBody();
187  p << "(";
188  llvm::interleaveComma(body.getArguments(), p,
189  [&](BlockArgument arg) { p.printRegionArgument(arg); });
190  p << ") ";
191  p.printArrowTypeList(funcTy.getResults());
192  p.printOptionalAttrDictWithKeyword(op.getOperation()->getAttrs(),
193  op.getAttributeNames());
194  if (!body.empty()) {
195  p << ' ';
196  p.printRegion(body, /*printEntryBlockArgs=*/false,
197  /*printBlockTerminators=*/true);
198  }
199 }
200 
201 ParseResult MethodOp::parse(OpAsmParser &parser, OperationState &result) {
202  return parseMethodLikeOp<MethodOp>(parser, result);
203 }
204 
205 void MethodOp::print(OpAsmPrinter &p) { return printMethodLikeOp(*this, p); }
206 
207 void MethodOp::getAsmBlockArgumentNames(mlir::Region &region,
208  OpAsmSetValueNameFn setNameFn) {
209  if (region.empty())
210  return;
211 
212  auto func = cast<MethodOp>(region.getParentOp());
213  auto argNames = func.getArgNames().getAsRange<StringAttr>();
214  auto *block = &region.front();
215 
216  for (auto [idx, argName] : llvm::enumerate(argNames))
217  if (!argName.getValue().empty())
218  setNameFn(block->getArgument(idx), argName);
219 }
220 
221 //===----------------------------------------------------------------------===//
222 // DataflowMethodOp
223 //===----------------------------------------------------------------------===//
224 
225 ParseResult DataflowMethodOp::parse(OpAsmParser &parser,
226  OperationState &result) {
227  return parseMethodLikeOp<DataflowMethodOp>(parser, result);
228 }
229 
230 void DataflowMethodOp::print(OpAsmPrinter &p) {
231  return printMethodLikeOp(*this, p);
232 }
233 
234 //===----------------------------------------------------------------------===//
235 // ReturnOp
236 //===----------------------------------------------------------------------===//
237 
238 void ReturnOp::build(OpBuilder &odsBuilder, OperationState &odsState) {}
239 
240 LogicalResult ReturnOp::verify() {
241  // Check that the return operand type matches the function return type.
242  auto methodLike = cast<MethodLikeOpInterface>((*this)->getParentOp());
243  ArrayRef<Type> resTypes = methodLike.getResultTypes();
244 
245  if (getNumOperands() != resTypes.size())
246  return emitOpError(
247  "must have the same number of operands as the method has results");
248 
249  for (auto [arg, resType] : llvm::zip(getOperands(), resTypes))
250  if (arg.getType() != resType)
251  return emitOpError("operand type (")
252  << arg.getType() << ") must match function return type ("
253  << resType << ")";
254 
255  return success();
256 }
257 
258 //===----------------------------------------------------------------------===//
259 // GetVarOp
260 //===----------------------------------------------------------------------===//
261 
262 LogicalResult GetVarOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
263  ScopeRefType parentType = getInstance().getType().cast<ScopeRefType>();
264  auto varOp = ns.lookupOp<VarOp>(hw::InnerRefAttr::get(
265  parentType.getScopeRef().getAttr(), getVarNameAttr().getAttr()));
266 
267  if (!varOp)
268  return failure();
269 
270  // Ensure that the dereferenced type is the same type as the variable type.
271  if (varOp.getType() != getType())
272  return emitOpError() << "dereferenced type (" << getType()
273  << ") must match variable type (" << varOp.getType()
274  << ")";
275 
276  return success();
277 }
278 
279 FailureOr<VarOp> GetVarOp::getTarget(SymbolTable *symbolTable) {
280  auto targetClassSym =
281  getInstance().getType().cast<ScopeRefType>().getScopeRef();
282  auto targetClass =
283  lookupInModule<ClassOp>(getOperation(), targetClassSym, symbolTable);
284 
285  if (!targetClass)
286  return emitOpError() << "'" << targetClassSym << "' does not exist";
287 
288  // Lookup the variable inside the class scope.
289  auto varName = getVarName();
290  // @teqdruid TODO: make this more efficient using
291  // innersymtablecollection when that's available to non-firrtl dialects.
292  auto var = dyn_cast_or_null<VarOp>(
293  symbolTable->lookupSymbolIn(targetClass.getOperation(), varName));
294  if (!var)
295  return emitOpError() << "'" << varName << "' does not exist in '"
296  << targetClassSym << "'";
297  return {var};
298 }
299 
300 //===----------------------------------------------------------------------===//
301 // InstanceOp
302 //===----------------------------------------------------------------------===//
303 
304 LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
305  auto targetClass = getClass(&symbolTable.getSymbolTable(
306  getOperation()->getParentOfType<mlir::ModuleOp>()));
307  if (!targetClass)
308  return emitOpError() << "'" << getTargetName() << "' does not exist";
309 
310  return success();
311 }
312 
313 ClassOp InstanceOp::getClass(SymbolTable *symbolTable) {
314  return lookupInModule<ClassOp>(getOperation(), getTargetNameAttr(),
315  symbolTable);
316 }
317 
319  setNameFn(getResult(), genValueNameAttr(getResult()));
320 }
321 
322 //===----------------------------------------------------------------------===//
323 // GetPortOp
324 //===----------------------------------------------------------------------===//
325 
326 LogicalResult GetPortOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
327  // Lookup the target module type of the instance class reference.
328  ScopeRefType crt = getInstance().getType().cast<ScopeRefType>();
329  Operation *targetOp = ns.lookupOp(hw::InnerRefAttr::get(
330  crt.getScopeRef().getAttr(), getPortSymbolAttr().getAttr()));
331 
332  if (!targetOp)
333  return emitOpError() << "port '" << getPortSymbolAttr()
334  << "' does not exist in " << crt.getScopeRef();
335 
336  auto portOp = dyn_cast<PortOpInterface>(targetOp);
337  if (!portOp)
338  return emitOpError() << "symbol '" << getPortSymbolAttr()
339  << "' does not refer to a port";
340 
341  Type targetPortType = portOp.getPortType();
342  Type thisPortType = getType().getPortType();
343  if (targetPortType != thisPortType)
344  return emitOpError() << "symbol '" << getPortSymbolAttr()
345  << "' refers to a port of type " << targetPortType
346  << ", but this op has type " << thisPortType;
347 
348  return success();
349 }
350 
351 LogicalResult GetPortOp::canonicalize(GetPortOp op, PatternRewriter &rewriter) {
352  // Canonicalize away get_port on %this in favor of using the port SSA value
353  // directly.
354  // get_port(%this, @P) -> ibis.port.#
355  auto parentScope = dyn_cast<ScopeOpInterface>(op->getParentOp());
356  if (parentScope) {
357  auto scopeThis = parentScope.getThis();
358  if (op.getInstance() == scopeThis) {
359  auto definingPort = parentScope.lookupPort(op.getPortSymbol());
360  rewriter.replaceOp(op, {definingPort.getPort()});
361  return success();
362  }
363  }
364 
365  return failure();
366 }
367 
369  setNameFn(getResult(), genValueNameAttr(getResult()));
370 }
371 
372 //===----------------------------------------------------------------------===//
373 // ThisOp
374 //===----------------------------------------------------------------------===//
375 
376 LogicalResult ThisOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
377  // A thisOp should always refer to the parent operation, which in turn should
378  // be an Ibis ScopeOpInterface.
379  auto parentScope =
380  dyn_cast_or_null<ScopeOpInterface>(getOperation()->getParentOp());
381  if (!parentScope)
382  return emitOpError() << "thisOp must be nested in a scope op";
383 
384  if (parentScope.getScopeName() != getScopeName())
385  return emitOpError() << "thisOp refers to a parent scope of name "
386  << getScopeName() << ", but the parent scope is named "
387  << parentScope.getScopeName();
388 
389  return success();
390 }
391 
393  setNameFn(getResult(), "this");
394 }
395 
396 //===----------------------------------------------------------------------===//
397 // PortReadOp
398 //===----------------------------------------------------------------------===//
399 
401  setNameFn(getResult(), genValueNameAttr(getResult()));
402 }
403 
404 //===----------------------------------------------------------------------===//
405 // ContainerInstanceOp
406 //===----------------------------------------------------------------------===//
407 
408 ContainerOp ContainerInstanceOp::getContainer(SymbolTable *symbolTable) {
409  auto mod = getOperation()->getParentOfType<mlir::ModuleOp>();
410  if (symbolTable)
411  return dyn_cast_or_null<ContainerOp>(
412  symbolTable->lookupSymbolIn(mod, getTargetNameAttr()));
413 
414  return mod.lookupSymbol<ContainerOp>(getTargetNameAttr());
415 }
416 
417 LogicalResult
418 ContainerInstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
419  auto targetContainer = getContainer(&symbolTable.getSymbolTable(
420  getOperation()->getParentOfType<mlir::ModuleOp>()));
421  if (!targetContainer)
422  return emitOpError() << "'" << getTargetName() << "' does not exist";
423 
424  return success();
425 }
426 
428  setNameFn(getResult(), genValueNameAttr(getResult()));
429 }
430 
431 //===----------------------------------------------------------------------===//
432 // PathOp
433 //===----------------------------------------------------------------------===//
434 
435 /// Infer the return types of this operation.
436 LogicalResult PathOp::inferReturnTypes(
437  MLIRContext *context, std::optional<Location> loc, ValueRange operands,
438  DictionaryAttr attrs, mlir::OpaqueProperties properties,
439  mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
440  auto path = attrs.get("path").cast<ArrayAttr>();
441  if (path.empty())
442  return failure();
443 
444  auto lastStep = path.getValue().back().cast<PathStepAttr>();
445  results.push_back(lastStep.getType());
446  return success();
447 }
448 
449 LogicalResult PathStepAttr::verify(function_ref<InFlightDiagnostic()> emitError,
450  PathDirection direction, mlir::Type type,
451  mlir::FlatSymbolRefAttr instance) {
452  // 'parent' should never have an instance name specified.
453  if (direction == PathDirection::Parent && instance)
454  return emitError() << "ibis.step 'parent' may not specify an instance name";
455 
456  if (direction == PathDirection::Child && !instance)
457  return emitError() << "ibis.step 'child' must specify an instance name";
458 
459  // Only allow scoperefs
460  auto scoperefType = type.dyn_cast<ScopeRefType>();
461  if (!scoperefType)
462  return emitError() << "ibis.step type must be an !ibis.scoperef type";
463 
464  return success();
465 }
466 
467 LogicalResult PathOp::verify() {
468  auto pathRange = getPathAsRange();
469  if (pathRange.empty())
470  return emitOpError() << "ibis.path must have at least one step";
471 
472  // Verify that each referenced child symbol actually exists at the module
473  // level.
474  auto mod = getOperation()->getParentOfType<mlir::ModuleOp>();
475  for (PathStepAttr step : getPathAsRange()) {
476  auto scoperefType = step.getType().cast<ScopeRefType>();
477  FlatSymbolRefAttr scopeRefSym = scoperefType.getScopeRef();
478  if (!scopeRefSym)
479  continue;
480 
481  auto *targetScope = mod.lookupSymbol(scopeRefSym);
482  if (!targetScope)
483  return emitOpError() << "ibis.step scoperef symbol '" << scopeRefSym
484  << "' does not exist";
485  }
486 
487  // Verify that the last step is fully typed.
488  PathStepAttr lastStep = *std::prev(getPathAsRange().end());
489  ScopeRefType lastStepType = lastStep.getType().cast<ScopeRefType>();
490  if (!lastStepType.getScopeRef())
491  return emitOpError()
492  << "last ibis.step in path must specify a symbol for the scoperef";
493 
494  return success();
495 }
496 
497 LogicalResult PathOp::canonicalize(PathOp op, PatternRewriter &rewriter) {
498  // Canonicalize away ibis.path [ibis.child] to just referencing the instance
499  // in the current scope.
500  auto range = op.getPathAsRange();
501  size_t pathSize = std::distance(range.begin(), range.end());
502  PathStepAttr firstStep = *range.begin();
503  if (pathSize == 1 && firstStep.getDirection() == PathDirection::Child) {
504  auto parentScope = cast<ScopeOpInterface>(op->getParentOp());
505  auto childInstance = dyn_cast_or_null<ContainerInstanceOp>(
506  parentScope.lookupInnerSym(firstStep.getChild().getValue()));
507  assert(childInstance && "should have been verified by the op verifier");
508  rewriter.replaceOp(op, {childInstance.getResult()});
509  return success();
510  }
511 
512  return failure();
513 }
514 
516  setNameFn(getResult(), genValueNameAttr(getResult()));
517 }
518 
519 //===----------------------------------------------------------------------===//
520 // OutputPortOp
521 //===----------------------------------------------------------------------===//
522 
523 LogicalResult OutputPortOp::canonicalize(OutputPortOp op,
524  PatternRewriter &rewriter) {
525  // Replace any reads of an output port op that is written to from the same
526  // scope, with the value that is written to it.
527  PortWriteOp writer;
528  llvm::SmallVector<PortReadOp, 4> readers;
529  for (auto *user : op.getResult().getUsers()) {
530  if (auto read = dyn_cast<PortReadOp>(user)) {
531  readers.push_back(read);
532  } else if (auto write = dyn_cast<PortWriteOp>(user);
533  write && write.getPort() == op.getPort()) {
534  assert(!writer && "should only have one writer");
535  writer = write;
536  }
537  }
538 
539  if (!readers.empty()) {
540  for (auto reader : readers)
541  rewriter.replaceOp(reader, writer.getValue());
542  return success();
543  }
544 
545  return failure();
546 }
547 
548 //===----------------------------------------------------------------------===//
549 // InputWireOp
550 //===----------------------------------------------------------------------===//
551 
552 LogicalResult InputWireOp::canonicalize(InputWireOp op,
553  PatternRewriter &rewriter) {
554  // Canonicalize away wires which are assigned within this scope.
555  auto portUsers = op.getPort().getUsers();
556  size_t nPortUsers = std::distance(portUsers.begin(), portUsers.end());
557  for (auto *portUser : op.getPort().getUsers()) {
558  auto writer = dyn_cast<PortWriteOp>(portUser);
559  if (writer && writer.getPort() == op.getPort() && nPortUsers == 1) {
560  rewriter.replaceAllUsesWith(op.getOutput(), writer.getValue());
561  rewriter.eraseOp(writer);
562  rewriter.eraseOp(op);
563  return success();
564  }
565  }
566 
567  return failure();
568 }
569 
570 //===----------------------------------------------------------------------===//
571 // OutputWireOp
572 //===----------------------------------------------------------------------===//
573 
574 LogicalResult OutputWireOp::canonicalize(OutputWireOp op,
575  PatternRewriter &rewriter) {
576  // Canonicalize away wires which are read (and nothing else) within this
577  // scope. Assume that duplicate reads have been CSE'd away and just look
578  // for a single reader.
579  auto portUsers = op.getPort().getUsers();
580  size_t nPortUsers = std::distance(portUsers.begin(), portUsers.end());
581  for (auto *portUser : op.getPort().getUsers()) {
582  auto reader = dyn_cast<PortReadOp>(portUser);
583  if (reader && reader.getPort() == op.getPort() && nPortUsers == 1) {
584  rewriter.replaceOp(reader, op.getInput());
585  rewriter.eraseOp(op);
586  return success();
587  }
588  }
589 
590  return failure();
591 }
592 
593 //===----------------------------------------------------------------------===//
594 // StaticBlockOp
595 //===----------------------------------------------------------------------===//
596 
597 template <typename TOp>
598 static ParseResult parseBlockLikeOp(
599  OpAsmParser &parser, OperationState &result,
600  llvm::function_ref<ParseResult(OpAsmParser::Argument &)> argAdjuster = {}) {
601  // Parse the argument initializer list.
602  llvm::SmallVector<OpAsmParser::UnresolvedOperand> inputOperands;
603  llvm::SmallVector<OpAsmParser::Argument> inputArguments;
604  llvm::SmallVector<Type> inputTypes;
605  ArrayAttr inputNames;
606  if (parsing_util::parseInitializerList(parser, inputArguments, inputOperands,
607  inputTypes, inputNames))
608  return failure();
609 
610  // Parse the result types.
611  llvm::SmallVector<Type> resultTypes;
612  if (parser.parseOptionalArrowTypeList(resultTypes))
613  return failure();
614  result.addTypes(resultTypes);
615 
616  // Parse the attribute dict.
617  if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
618  return failure();
619 
620  // All operands have been parsed - resolve.
621  if (parser.resolveOperands(inputOperands, inputTypes, parser.getNameLoc(),
622  result.operands))
623  return failure();
624 
625  // If the user provided an arg adjuster, apply it to each argument.
626  if (argAdjuster) {
627  for (auto &arg : inputArguments)
628  if (failed(argAdjuster(arg)))
629  return failure();
630  }
631 
632  // Parse the body region.
633  Region *body = result.addRegion();
634  if (parser.parseRegion(*body, inputArguments))
635  return failure();
636 
637  TOp::ensureTerminator(*body, parser.getBuilder(), result.location);
638  return success();
639 }
640 
641 template <typename T>
642 static void printBlockLikeOp(T op, OpAsmPrinter &p) {
643  p << ' ';
644  parsing_util::printInitializerList(p, op.getInputs(),
645  op.getBodyBlock()->getArguments());
646  p.printOptionalArrowTypeList(op.getResultTypes());
647  p.printOptionalAttrDictWithKeyword(op.getOperation()->getAttrs());
648  p << ' ';
649  p.printRegion(op.getBody(), /*printEntryBlockArgs=*/false);
650 }
651 
652 LogicalResult StaticBlockOp::verify() {
653  if (getInputs().size() != getBodyBlock()->getNumArguments())
654  return emitOpError("number of inputs must match number of block arguments");
655 
656  for (auto [arg, barg] :
657  llvm::zip(getInputs(), getBodyBlock()->getArguments())) {
658  if (arg.getType() != barg.getType())
659  return emitOpError("block argument type must match input type");
660  }
661 
662  return success();
663 }
664 
665 ParseResult StaticBlockOp::parse(OpAsmParser &parser, OperationState &result) {
666  return parseBlockLikeOp<StaticBlockOp>(parser, result);
667 }
668 
669 void StaticBlockOp::print(OpAsmPrinter &p) {
670  return printBlockLikeOp(*this, p);
671 }
672 
673 //===----------------------------------------------------------------------===//
674 // IsolatedStaticBlockOp
675 //===----------------------------------------------------------------------===//
676 
677 LogicalResult IsolatedStaticBlockOp::verify() {
678  if (getInputs().size() != getBodyBlock()->getNumArguments())
679  return emitOpError("number of inputs must match number of block arguments");
680 
681  for (auto [arg, barg] :
682  llvm::zip(getInputs(), getBodyBlock()->getArguments())) {
683  if (arg.getType() != barg.getType())
684  return emitOpError("block argument type must match input type");
685  }
686 
687  return success();
688 }
689 
690 ParseResult IsolatedStaticBlockOp::parse(OpAsmParser &parser,
691  OperationState &result) {
692  return parseBlockLikeOp<IsolatedStaticBlockOp>(parser, result);
693 }
694 
695 void IsolatedStaticBlockOp::print(OpAsmPrinter &p) {
696  return printBlockLikeOp(*this, p);
697 }
698 
699 //===----------------------------------------------------------------------===//
700 // DCBlockOp
701 //===----------------------------------------------------------------------===//
702 
703 void DCBlockOp::build(OpBuilder &odsBuilder, OperationState &odsState,
704  TypeRange outputs, ValueRange inputs,
705  IntegerAttr maxThreads) {
706  odsState.addOperands(inputs);
707  if (maxThreads)
708  odsState.addAttribute(getMaxThreadsAttrName(odsState.name), maxThreads);
709  auto *region = odsState.addRegion();
710  llvm::SmallVector<Type> resTypes;
711  for (auto output : outputs) {
712  dc::ValueType dcType = output.dyn_cast<dc::ValueType>();
713  assert(dcType && "DCBlockOp outputs must be dc::ValueType");
714  resTypes.push_back(dcType);
715  }
716  odsState.addTypes(resTypes);
717  ensureTerminator(*region, odsBuilder, odsState.location);
718  llvm::SmallVector<Location> argLocs;
719  llvm::SmallVector<Type> argTypes;
720  for (auto input : inputs) {
721  argLocs.push_back(input.getLoc());
722  dc::ValueType dcType = input.getType().dyn_cast<dc::ValueType>();
723  assert(dcType && "DCBlockOp inputs must be dc::ValueType");
724  argTypes.push_back(dcType.getInnerType());
725  }
726  region->front().addArguments(argTypes, argLocs);
727 }
728 
729 LogicalResult DCBlockOp::verify() {
730  if (getInputs().size() != getBodyBlock()->getNumArguments())
731  return emitOpError("number of inputs must match number of block arguments");
732 
733  for (auto [arg, barg] :
734  llvm::zip(getInputs(), getBodyBlock()->getArguments())) {
735  dc::ValueType dcType = arg.getType().dyn_cast<dc::ValueType>();
736  if (!dcType)
737  return emitOpError("DCBlockOp inputs must be dc::ValueType but got ")
738  << arg.getType();
739 
740  if (dcType.getInnerType() != barg.getType())
741  return emitOpError("block argument type must match input type. Got ")
742  << barg.getType() << " expected " << dcType.getInnerType();
743  }
744 
745  return success();
746 }
747 
748 ParseResult DCBlockOp::parse(OpAsmParser &parser, OperationState &result) {
749  return parseBlockLikeOp<DCBlockOp>(
750  parser, result, [&](OpAsmParser::Argument &arg) -> LogicalResult {
751  dc::ValueType valueType = arg.type.dyn_cast<dc::ValueType>();
752  if (!valueType)
753  return parser.emitError(parser.getCurrentLocation(),
754  "DCBlockOp inputs must be dc::ValueType");
755  arg.type = valueType.getInnerType();
756  return success();
757  });
758 }
759 
760 void DCBlockOp::print(OpAsmPrinter &p) { return printBlockLikeOp(*this, p); }
761 
762 //===----------------------------------------------------------------------===//
763 // BlockReturnOp
764 //===----------------------------------------------------------------------===//
765 
766 LogicalResult BlockReturnOp::verify() {
767  Operation *parent = getOperation()->getParentOp();
768  auto parentBlock = dyn_cast<BlockOpInterface>(parent);
769  if (!parentBlock)
770  return emitOpError("must be nested in a block");
771 
772  if (getNumOperands() != parent->getNumResults())
773  return emitOpError("number of operands must match number of block outputs");
774 
775  for (auto [op, out] :
776  llvm::zip(getOperands(), parentBlock.getInternalResultTypes())) {
777  if (op.getType() != out)
778  return emitOpError(
779  "operand type must match parent block output type. Expected ")
780  << out << " got " << op.getType();
781  }
782 
783  return success();
784 }
785 
786 //===----------------------------------------------------------------------===//
787 // InlineStaticBlockEndOp
788 //===----------------------------------------------------------------------===//
789 
790 InlineStaticBlockBeginOp InlineStaticBlockEndOp::getBeginOp() {
791  auto curr = getOperation()->getReverseIterator();
792  Operation *firstOp = &getOperation()->getBlock()->front();
793  while (true) {
794  if (auto beginOp = dyn_cast<InlineStaticBlockBeginOp>(*curr))
795  return beginOp;
796  if (curr.getNodePtr() == firstOp)
797  break;
798  ++curr;
799  }
800  return nullptr;
801 }
802 
803 //===----------------------------------------------------------------------===//
804 // InlineStaticBlockBeginOp
805 //===----------------------------------------------------------------------===//
806 
807 InlineStaticBlockEndOp InlineStaticBlockBeginOp::getEndOp() {
808  auto curr = getOperation()->getIterator();
809  auto end = getOperation()->getBlock()->end();
810  while (curr != end) {
811  if (auto endOp = dyn_cast<InlineStaticBlockEndOp>(*curr))
812  return endOp;
813 
814  ++curr;
815  }
816  return nullptr;
817 }
818 
819 //===----------------------------------------------------------------------===//
820 // TableGen generated logic
821 //===----------------------------------------------------------------------===//
822 
823 #include "circt/Dialect/Ibis/IbisInterfaces.cpp.inc"
824 
825 // Provide the autogenerated implementation guts for the Op classes.
826 #define GET_OP_CLASSES
827 #include "circt/Dialect/Ibis/Ibis.cpp.inc"
lowerAnnotationsNoRefTypePorts FirtoolPreserveValuesMode value
Definition: Firtool.cpp:95
assert(baseType &&"element must be base type")
static Attribute getAttr(ArrayRef< NamedAttribute > attrs, StringRef name)
Get an attribute by name from a list of named attributes.
Definition: FIRRTLOps.cpp:3429
ParseResult parseMethodLikeOp(OpAsmParser &parser, OperationState &result)
Definition: IbisOps.cpp:127
void printScopeRefFromName(OpAsmPrinter &p, Operation *op, Type type, TSymAttr sym)
Definition: IbisOps.cpp:45
static llvm::raw_string_ostream & genValueName(llvm::raw_string_ostream &os, Value value)
Definition: IbisOps.cpp:53
void printMethodLikeOp(TOp op, OpAsmPrinter &p)
Definition: IbisOps.cpp:182
static T lookupInModule(Operation *base, FlatSymbolRefAttr sym, SymbolTable *symbolTable)
Definition: IbisOps.cpp:27
static StringAttr genValueNameAttr(Value v)
Definition: IbisOps.cpp:88
ParseResult parseScopeRefFromName(OpAsmParser &parser, Type &scopeRefType, TSymAttr sym)
Definition: IbisOps.cpp:37
static void printBlockLikeOp(T op, OpAsmPrinter &p)
Definition: IbisOps.cpp:642
static ParseResult parseBlockLikeOp(OpAsmParser &parser, OperationState &result, llvm::function_ref< ParseResult(OpAsmParser::Argument &)> argAdjuster={})
Definition: IbisOps.cpp:598
llvm::SmallVector< StringAttr > inputs
llvm::SmallVector< StringAttr > outputs
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
LogicalResult inferReturnTypes(MLIRContext *context, std::optional< Location > loc, ValueRange operands, DictionaryAttr attrs, mlir::OpaqueProperties properties, mlir::RegionRange regions, SmallVectorImpl< Type > &results, llvm::function_ref< FIRRTLType(ValueRange, ArrayRef< NamedAttribute >, std::optional< Location >)> callback)
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
LogicalResult verifyScopeOpInterface(Operation *op)
Definition: IbisOps.cpp:112
mlir::FailureOr< mlir::TypedValue< ScopeRefType > > getThisFromScope(Operation *op)
Definition: IbisOps.cpp:100
ParseResult parseInitializerList(mlir::OpAsmParser &parser, llvm::SmallVector< mlir::OpAsmParser::Argument > &inputArguments, llvm::SmallVector< mlir::OpAsmParser::UnresolvedOperand > &inputOperands, llvm::SmallVector< Type > &inputTypes, ArrayAttr &inputNames)
Parses an initializer.
void printInitializerList(OpAsmPrinter &p, ValueRange ins, ArrayRef< BlockArgument > args)
static StringAttr getNameFromSSA(MLIRContext *context, StringRef name)
Get a name from an SSA value string, if said value name is not a number.
Definition: ParsingUtils.h:25
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition: LLVM.h:186