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