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.getNameHint(); })
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 
522  setNameFn(getPort(), genValueNameAttr(getPort()));
523 }
524 
525 //===----------------------------------------------------------------------===//
526 // InputPortOp
527 //===----------------------------------------------------------------------===//
528 
530  setNameFn(getPort(), genValueNameAttr(getPort()));
531 }
532 
533 //===----------------------------------------------------------------------===//
534 // InputWireOp
535 //===----------------------------------------------------------------------===//
536 
537 LogicalResult InputWireOp::canonicalize(InputWireOp op,
538  PatternRewriter &rewriter) {
539  // Canonicalize away wires which are assigned within this scope.
540  auto portUsers = op.getPort().getUsers();
541  size_t nPortUsers = std::distance(portUsers.begin(), portUsers.end());
542  for (auto *portUser : op.getPort().getUsers()) {
543  auto writer = dyn_cast<PortWriteOp>(portUser);
544  if (writer && writer.getPort() == op.getPort() && nPortUsers == 1) {
545  rewriter.replaceAllUsesWith(op.getOutput(), writer.getValue());
546  rewriter.eraseOp(writer);
547  rewriter.eraseOp(op);
548  return success();
549  }
550  }
551 
552  return failure();
553 }
554 
556  auto name = genValueNameAttr(getOutput());
557  setNameFn(getPort(), name);
558  setNameFn(getOutput(),
559  StringAttr::get(getContext(), name.strref() + ".out").strref());
560 }
561 
562 //===----------------------------------------------------------------------===//
563 // OutputWireOp
564 //===----------------------------------------------------------------------===//
565 
566 LogicalResult OutputWireOp::canonicalize(OutputWireOp op,
567  PatternRewriter &rewriter) {
568  // Canonicalize away wires which are read (and nothing else) within this
569  // scope. Assume that duplicate reads have been CSE'd away and just look
570  // for a single reader.
571  auto portUsers = op.getPort().getUsers();
572  size_t nPortUsers = std::distance(portUsers.begin(), portUsers.end());
573  for (auto *portUser : op.getPort().getUsers()) {
574  auto reader = dyn_cast<PortReadOp>(portUser);
575  if (reader && reader.getPort() == op.getPort() && nPortUsers == 1) {
576  rewriter.replaceOp(reader, op.getInput());
577  rewriter.eraseOp(op);
578  return success();
579  }
580  }
581 
582  return failure();
583 }
584 
586  setNameFn(getPort(), genValueNameAttr(getPort()));
587 }
588 
589 //===----------------------------------------------------------------------===//
590 // StaticBlockOp
591 //===----------------------------------------------------------------------===//
592 
593 template <typename TOp>
594 static ParseResult parseBlockLikeOp(
595  OpAsmParser &parser, OperationState &result,
596  llvm::function_ref<ParseResult(OpAsmParser::Argument &)> argAdjuster = {}) {
597  // Parse the argument initializer list.
598  llvm::SmallVector<OpAsmParser::UnresolvedOperand> inputOperands;
599  llvm::SmallVector<OpAsmParser::Argument> inputArguments;
600  llvm::SmallVector<Type> inputTypes;
601  ArrayAttr inputNames;
602  if (parsing_util::parseInitializerList(parser, inputArguments, inputOperands,
603  inputTypes, inputNames))
604  return failure();
605 
606  // Parse the result types.
607  llvm::SmallVector<Type> resultTypes;
608  if (parser.parseOptionalArrowTypeList(resultTypes))
609  return failure();
610  result.addTypes(resultTypes);
611 
612  // Parse the attribute dict.
613  if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
614  return failure();
615 
616  // All operands have been parsed - resolve.
617  if (parser.resolveOperands(inputOperands, inputTypes, parser.getNameLoc(),
618  result.operands))
619  return failure();
620 
621  // If the user provided an arg adjuster, apply it to each argument.
622  if (argAdjuster) {
623  for (auto &arg : inputArguments)
624  if (failed(argAdjuster(arg)))
625  return failure();
626  }
627 
628  // Parse the body region.
629  Region *body = result.addRegion();
630  if (parser.parseRegion(*body, inputArguments))
631  return failure();
632 
633  TOp::ensureTerminator(*body, parser.getBuilder(), result.location);
634  return success();
635 }
636 
637 template <typename T>
638 static void printBlockLikeOp(T op, OpAsmPrinter &p) {
639  p << ' ';
640  parsing_util::printInitializerList(p, op.getInputs(),
641  op.getBodyBlock()->getArguments());
642  p.printOptionalArrowTypeList(op.getResultTypes());
643  p.printOptionalAttrDictWithKeyword(op.getOperation()->getAttrs());
644  p << ' ';
645  p.printRegion(op.getBody(), /*printEntryBlockArgs=*/false);
646 }
647 
648 LogicalResult StaticBlockOp::verify() {
649  if (getInputs().size() != getBodyBlock()->getNumArguments())
650  return emitOpError("number of inputs must match number of block arguments");
651 
652  for (auto [arg, barg] :
653  llvm::zip(getInputs(), getBodyBlock()->getArguments())) {
654  if (arg.getType() != barg.getType())
655  return emitOpError("block argument type must match input type");
656  }
657 
658  return success();
659 }
660 
661 ParseResult StaticBlockOp::parse(OpAsmParser &parser, OperationState &result) {
662  return parseBlockLikeOp<StaticBlockOp>(parser, result);
663 }
664 
665 void StaticBlockOp::print(OpAsmPrinter &p) {
666  return printBlockLikeOp(*this, p);
667 }
668 
669 //===----------------------------------------------------------------------===//
670 // IsolatedStaticBlockOp
671 //===----------------------------------------------------------------------===//
672 
673 LogicalResult IsolatedStaticBlockOp::verify() {
674  if (getInputs().size() != getBodyBlock()->getNumArguments())
675  return emitOpError("number of inputs must match number of block arguments");
676 
677  for (auto [arg, barg] :
678  llvm::zip(getInputs(), getBodyBlock()->getArguments())) {
679  if (arg.getType() != barg.getType())
680  return emitOpError("block argument type must match input type");
681  }
682 
683  return success();
684 }
685 
686 ParseResult IsolatedStaticBlockOp::parse(OpAsmParser &parser,
687  OperationState &result) {
688  return parseBlockLikeOp<IsolatedStaticBlockOp>(parser, result);
689 }
690 
691 void IsolatedStaticBlockOp::print(OpAsmPrinter &p) {
692  return printBlockLikeOp(*this, p);
693 }
694 
695 //===----------------------------------------------------------------------===//
696 // DCBlockOp
697 //===----------------------------------------------------------------------===//
698 
699 void DCBlockOp::build(OpBuilder &odsBuilder, OperationState &odsState,
700  TypeRange outputs, ValueRange inputs,
701  IntegerAttr maxThreads) {
702  odsState.addOperands(inputs);
703  if (maxThreads)
704  odsState.addAttribute(getMaxThreadsAttrName(odsState.name), maxThreads);
705  auto *region = odsState.addRegion();
706  llvm::SmallVector<Type> resTypes;
707  for (auto output : outputs) {
708  dc::ValueType dcType = dyn_cast<dc::ValueType>(output);
709  assert(dcType && "DCBlockOp outputs must be dc::ValueType");
710  resTypes.push_back(dcType);
711  }
712  odsState.addTypes(resTypes);
713  ensureTerminator(*region, odsBuilder, odsState.location);
714  llvm::SmallVector<Location> argLocs;
715  llvm::SmallVector<Type> argTypes;
716  for (auto input : inputs) {
717  argLocs.push_back(input.getLoc());
718  dc::ValueType dcType = dyn_cast<dc::ValueType>(input.getType());
719  assert(dcType && "DCBlockOp inputs must be dc::ValueType");
720  argTypes.push_back(dcType.getInnerType());
721  }
722  region->front().addArguments(argTypes, argLocs);
723 }
724 
725 LogicalResult DCBlockOp::verify() {
726  if (getInputs().size() != getBodyBlock()->getNumArguments())
727  return emitOpError("number of inputs must match number of block arguments");
728 
729  for (auto [arg, barg] :
730  llvm::zip(getInputs(), getBodyBlock()->getArguments())) {
731  dc::ValueType dcType = dyn_cast<dc::ValueType>(arg.getType());
732  if (!dcType)
733  return emitOpError("DCBlockOp inputs must be dc::ValueType but got ")
734  << arg.getType();
735 
736  if (dcType.getInnerType() != barg.getType())
737  return emitOpError("block argument type must match input type. Got ")
738  << barg.getType() << " expected " << dcType.getInnerType();
739  }
740 
741  return success();
742 }
743 
744 ParseResult DCBlockOp::parse(OpAsmParser &parser, OperationState &result) {
745  return parseBlockLikeOp<DCBlockOp>(
746  parser, result, [&](OpAsmParser::Argument &arg) -> LogicalResult {
747  dc::ValueType valueType = dyn_cast<dc::ValueType>(arg.type);
748  if (!valueType)
749  return parser.emitError(parser.getCurrentLocation(),
750  "DCBlockOp inputs must be dc::ValueType");
751  arg.type = valueType.getInnerType();
752  return success();
753  });
754 }
755 
756 void DCBlockOp::print(OpAsmPrinter &p) { return printBlockLikeOp(*this, p); }
757 
758 //===----------------------------------------------------------------------===//
759 // BlockReturnOp
760 //===----------------------------------------------------------------------===//
761 
762 LogicalResult BlockReturnOp::verify() {
763  Operation *parent = getOperation()->getParentOp();
764  auto parentBlock = dyn_cast<BlockOpInterface>(parent);
765  if (!parentBlock)
766  return emitOpError("must be nested in a block");
767 
768  if (getNumOperands() != parent->getNumResults())
769  return emitOpError("number of operands must match number of block outputs");
770 
771  for (auto [op, out] :
772  llvm::zip(getOperands(), parentBlock.getInternalResultTypes())) {
773  if (op.getType() != out)
774  return emitOpError(
775  "operand type must match parent block output type. Expected ")
776  << out << " got " << op.getType();
777  }
778 
779  return success();
780 }
781 
782 //===----------------------------------------------------------------------===//
783 // InlineStaticBlockEndOp
784 //===----------------------------------------------------------------------===//
785 
786 InlineStaticBlockBeginOp InlineStaticBlockEndOp::getBeginOp() {
787  auto curr = getOperation()->getReverseIterator();
788  Operation *firstOp = &getOperation()->getBlock()->front();
789  while (true) {
790  if (auto beginOp = dyn_cast<InlineStaticBlockBeginOp>(*curr))
791  return beginOp;
792  if (curr.getNodePtr() == firstOp)
793  break;
794  ++curr;
795  }
796  return nullptr;
797 }
798 
799 //===----------------------------------------------------------------------===//
800 // InlineStaticBlockBeginOp
801 //===----------------------------------------------------------------------===//
802 
803 InlineStaticBlockEndOp InlineStaticBlockBeginOp::getEndOp() {
804  auto curr = getOperation()->getIterator();
805  auto end = getOperation()->getBlock()->end();
806  while (curr != end) {
807  if (auto endOp = dyn_cast<InlineStaticBlockEndOp>(*curr))
808  return endOp;
809 
810  ++curr;
811  }
812  return nullptr;
813 }
814 
815 //===----------------------------------------------------------------------===//
816 // TableGen generated logic
817 //===----------------------------------------------------------------------===//
818 
819 #include "circt/Dialect/Ibis/IbisInterfaces.cpp.inc"
820 
821 // Provide the autogenerated implementation guts for the Op classes.
822 #define GET_OP_CLASSES
823 #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:638
static ParseResult parseBlockLikeOp(OpAsmParser &parser, OperationState &result, llvm::function_ref< ParseResult(OpAsmParser::Argument &)> argAdjuster={})
Definition: IbisOps.cpp:594
llvm::SmallVector< StringAttr > inputs
llvm::SmallVector< StringAttr > outputs
static LogicalResult canonicalize(Op op, PatternRewriter &rewriter)
Definition: VerifOps.cpp:64
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