CIRCT  19.0.0git
CalyxOps.cpp
Go to the documentation of this file.
1 //===- CalyxOps.cpp - Calyx op code defs ------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This is where op definitions live.
10 //
11 //===----------------------------------------------------------------------===//
12 
16 #include "circt/Dialect/HW/HWOps.h"
18 #include "mlir/IR/AsmState.h"
19 #include "mlir/IR/Builders.h"
20 #include "mlir/IR/BuiltinAttributes.h"
21 #include "mlir/IR/BuiltinTypes.h"
22 #include "mlir/IR/Diagnostics.h"
23 #include "mlir/IR/DialectImplementation.h"
24 #include "mlir/IR/PatternMatch.h"
25 #include "mlir/IR/SymbolTable.h"
26 #include "mlir/Interfaces/FunctionImplementation.h"
27 #include "mlir/Support/LLVM.h"
28 #include "llvm/ADT/DenseMap.h"
29 #include "llvm/ADT/PriorityQueue.h"
30 #include "llvm/ADT/STLExtras.h"
31 #include "llvm/ADT/SmallSet.h"
32 #include "llvm/ADT/StringExtras.h"
33 #include "llvm/ADT/TypeSwitch.h"
34 #include "llvm/Support/Casting.h"
35 
36 using namespace circt;
37 using namespace circt::calyx;
38 using namespace mlir;
39 
40 namespace {
41 
42 // A struct to enforce that the LHS template is one of the RHS templates.
43 // For example:
44 // std::is_any<uint32_t, uint16_t, float, int32_t>::value is false.
45 template <class T, class... Ts>
46 struct IsAny : std::disjunction<std::is_same<T, Ts>...> {};
47 
48 } // namespace
49 
50 //===----------------------------------------------------------------------===//
51 // Utilities related to Direction
52 //===----------------------------------------------------------------------===//
53 
54 Direction direction::get(bool isOutput) {
55  return static_cast<Direction>(isOutput);
56 }
57 
58 IntegerAttr direction::packAttribute(MLIRContext *ctx, size_t nIns,
59  size_t nOuts) {
60  // Pack the array of directions into an APInt. Input direction is zero,
61  // output direction is one.
62  size_t numDirections = nIns + nOuts;
63  APInt portDirections(/*width=*/numDirections, /*value=*/0);
64  for (size_t i = nIns, e = numDirections; i != e; ++i)
65  portDirections.setBit(i);
66 
67  return IntegerAttr::get(IntegerType::get(ctx, numDirections), portDirections);
68 }
69 
70 //===----------------------------------------------------------------------===//
71 // Utilities
72 //===----------------------------------------------------------------------===//
73 
74 /// This pattern collapses a calyx.seq or calyx.par operation when it
75 /// contains exactly one calyx.enable operation.
76 template <typename CtrlOp>
79  LogicalResult matchAndRewrite(CtrlOp ctrlOp,
80  PatternRewriter &rewriter) const override {
81  auto &ops = ctrlOp.getBodyBlock()->getOperations();
82  bool isUnaryControl =
83  (ops.size() == 1) && isa<EnableOp>(ops.front()) &&
84  isa<SeqOp, ParOp, StaticSeqOp, StaticParOp>(ctrlOp->getParentOp());
85  if (!isUnaryControl)
86  return failure();
87 
88  ops.front().moveBefore(ctrlOp);
89  rewriter.eraseOp(ctrlOp);
90  return success();
91  }
92 };
93 
94 /// Verify that the value is not a "complex" value. For example, the source
95 /// of an AssignOp should be a constant or port, e.g.
96 /// %and = comb.and %a, %b : i1
97 /// calyx.assign %port = %c1_i1 ? %and : i1 // Incorrect
98 /// calyx.assign %port = %and ? %c1_i1 : i1 // Correct
99 /// TODO(Calyx): This is useful to verify current MLIR can be lowered to the
100 /// native compiler. Remove this when Calyx supports wire declarations.
101 /// See: https://github.com/llvm/circt/pull/1774 for context.
102 template <typename Op>
103 static LogicalResult verifyNotComplexSource(Op op) {
104  Operation *definingOp = op.getSrc().getDefiningOp();
105  if (definingOp == nullptr)
106  // This is a port of the parent component.
107  return success();
108 
109  // Currently, we use the Combinational dialect to perform logical operations
110  // on wires, i.e. comb::AndOp, comb::OrOp, comb::XorOp.
111  if (auto dialect = definingOp->getDialect(); isa<comb::CombDialect>(dialect))
112  return op->emitOpError("has source that is not a port or constant. "
113  "Complex logic should be conducted in the guard.");
114 
115  return success();
116 }
117 
118 /// Convenience function for getting the SSA name of `v` under the scope of
119 /// operation `scopeOp`.
120 static std::string valueName(Operation *scopeOp, Value v) {
121  std::string s;
122  llvm::raw_string_ostream os(s);
123  // CAVEAT: Since commit 27df7158fe MLIR prefers verifiers to print errors for
124  // operations in generic form, and the printer by default runs a verification.
125  // `valueName` is used in some of these verifiers where preferably the generic
126  // operand form should be used instead.
127  AsmState asmState(scopeOp, OpPrintingFlags().assumeVerified());
128  v.printAsOperand(os, asmState);
129  return s;
130 }
131 
132 /// Returns whether this value is either (1) a port on a ComponentOp or (2) a
133 /// port on a cell interface.
134 static bool isPort(Value value) {
135  Operation *definingOp = value.getDefiningOp();
136  return isa<BlockArgument>(value) ||
137  isa_and_nonnull<CellInterface>(definingOp);
138 }
139 
140 /// Gets the port for a given BlockArgument.
141 PortInfo calyx::getPortInfo(BlockArgument arg) {
142  Operation *op = arg.getOwner()->getParentOp();
143  assert(isa<ComponentInterface>(op) &&
144  "Only ComponentInterface should support lookup by BlockArgument.");
145  return cast<ComponentInterface>(op).getPortInfo()[arg.getArgNumber()];
146 }
147 
148 /// Returns whether the given operation has a control region.
149 static bool hasControlRegion(Operation *op) {
150  return isa<ControlOp, SeqOp, IfOp, RepeatOp, WhileOp, ParOp, StaticRepeatOp,
151  StaticParOp, StaticSeqOp, StaticIfOp>(op);
152 }
153 
154 /// Returns whether the given operation is a static control operator
155 static bool isStaticControl(Operation *op) {
156  if (isa<EnableOp>(op)) {
157  // for enables, we need to check whether its corresponding group is static
158  auto component = op->getParentOfType<ComponentOp>();
159  auto enableOp = llvm::cast<EnableOp>(op);
160  StringRef groupName = enableOp.getGroupName();
161  auto group = component.getWiresOp().lookupSymbol<GroupInterface>(groupName);
162  return isa<StaticGroupOp>(group);
163  }
164  return isa<StaticIfOp, StaticSeqOp, StaticRepeatOp, StaticParOp>(op);
165 }
166 
167 /// Verifies the body of a ControlLikeOp.
168 static LogicalResult verifyControlBody(Operation *op) {
169  if (isa<SeqOp, ParOp, StaticSeqOp, StaticParOp>(op))
170  // This does not apply to sequential and parallel regions.
171  return success();
172 
173  // Some ControlLike operations have (possibly) multiple regions, e.g. IfOp.
174  for (auto &region : op->getRegions()) {
175  auto opsIt = region.getOps();
176  size_t numOperations = std::distance(opsIt.begin(), opsIt.end());
177  // A body of a ControlLike operation may have a single EnableOp within it.
178  // However, that must be the only operation.
179  // E.g. Allowed: calyx.control { calyx.enable @A }
180  // Not Allowed: calyx.control { calyx.enable @A calyx.seq { ... } }
181  bool usesEnableAsCompositionOperator =
182  numOperations > 1 && llvm::any_of(region.front(), [](auto &&bodyOp) {
183  return isa<EnableOp>(bodyOp);
184  });
185  if (usesEnableAsCompositionOperator)
186  return op->emitOpError(
187  "EnableOp is not a composition operator. It should be nested "
188  "in a control flow operation, such as \"calyx.seq\"");
189 
190  // Verify that multiple control flow operations are nested inside a single
191  // control operator. See: https://github.com/llvm/circt/issues/1723
192  size_t numControlFlowRegions =
193  llvm::count_if(opsIt, [](auto &&op) { return hasControlRegion(&op); });
194  if (numControlFlowRegions > 1)
195  return op->emitOpError(
196  "has an invalid control sequence. Multiple control flow operations "
197  "must all be nested in a single calyx.seq or calyx.par");
198  }
199  return success();
200 }
201 
202 LogicalResult calyx::verifyComponent(Operation *op) {
203  auto *opParent = op->getParentOp();
204  if (!isa<ModuleOp>(opParent))
205  return op->emitOpError()
206  << "has parent: " << opParent << ", expected ModuleOp.";
207  return success();
208 }
209 
210 LogicalResult calyx::verifyCell(Operation *op) {
211  auto opParent = op->getParentOp();
212  if (!isa<ComponentInterface>(opParent))
213  return op->emitOpError()
214  << "has parent: " << opParent << ", expected ComponentInterface.";
215  return success();
216 }
217 
218 LogicalResult calyx::verifyControlLikeOp(Operation *op) {
219  auto parent = op->getParentOp();
220 
221  if (isa<calyx::EnableOp>(op) &&
222  !isa<calyx::CalyxDialect>(parent->getDialect())) {
223  // Allow embedding calyx.enable ops within other dialects. This is motivated
224  // by allowing experimentation with new styles of Calyx lowering. For more
225  // info and the historical discussion, see:
226  // https://github.com/llvm/circt/pull/3211
227  return success();
228  }
229 
230  if (!hasControlRegion(parent))
231  return op->emitOpError()
232  << "has parent: " << parent
233  << ", which is not allowed for a control-like operation.";
234 
235  if (op->getNumRegions() == 0)
236  return success();
237 
238  auto &region = op->getRegion(0);
239  // Operations that are allowed in the body of a ControlLike op.
240  auto isValidBodyOp = [](Operation *operation) {
241  return isa<EnableOp, InvokeOp, SeqOp, IfOp, RepeatOp, WhileOp, ParOp,
242  StaticParOp, StaticRepeatOp, StaticSeqOp, StaticIfOp>(operation);
243  };
244  for (auto &&bodyOp : region.front()) {
245  if (isValidBodyOp(&bodyOp))
246  continue;
247 
248  return op->emitOpError()
249  << "has operation: " << bodyOp.getName()
250  << ", which is not allowed in this control-like operation";
251  }
252  return verifyControlBody(op);
253 }
254 
255 LogicalResult calyx::verifyIf(Operation *op) {
256  auto ifOp = dyn_cast<IfInterface>(op);
257 
258  if (ifOp.elseBodyExists() && ifOp.getElseBody()->empty())
259  return ifOp->emitOpError() << "empty 'else' region.";
260 
261  return success();
262 }
263 
264 // Helper function for parsing a group port operation, i.e. GroupDoneOp and
265 // GroupPortOp. These may take one of two different forms:
266 // (1) %<guard> ? %<src> : i1
267 // (2) %<src> : i1
268 static ParseResult parseGroupPort(OpAsmParser &parser, OperationState &result) {
269  SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
270  OpAsmParser::UnresolvedOperand guardOrSource;
271  if (parser.parseOperand(guardOrSource))
272  return failure();
273 
274  if (succeeded(parser.parseOptionalQuestion())) {
275  OpAsmParser::UnresolvedOperand source;
276  // The guard exists.
277  if (parser.parseOperand(source))
278  return failure();
279  operandInfos.push_back(source);
280  }
281  // No matter if this is the source or guard, it should be last.
282  operandInfos.push_back(guardOrSource);
283 
284  Type type;
285  // Resolving the operands with the same type works here since the source and
286  // guard of a group port is always i1.
287  if (parser.parseColonType(type) ||
288  parser.resolveOperands(operandInfos, type, result.operands))
289  return failure();
290 
291  return success();
292 }
293 
294 // A helper function for printing group ports, i.e. GroupGoOp and GroupDoneOp.
295 template <typename GroupPortType>
296 static void printGroupPort(OpAsmPrinter &p, GroupPortType op) {
297  static_assert(IsAny<GroupPortType, GroupGoOp, GroupDoneOp>(),
298  "Should be a Calyx Group port.");
299 
300  p << " ";
301  // The guard is optional.
302  Value guard = op.getGuard(), source = op.getSrc();
303  if (guard)
304  p << guard << " ? ";
305  p << source << " : " << source.getType();
306 }
307 
308 // Collapse nested control of the same type for SeqOp and ParOp, e.g.
309 // calyx.seq { calyx.seq { ... } } -> calyx.seq { ... }
310 template <typename OpTy>
311 static LogicalResult collapseControl(OpTy controlOp,
312  PatternRewriter &rewriter) {
313  static_assert(IsAny<OpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
314  "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp");
315 
316  if (isa<OpTy>(controlOp->getParentOp())) {
317  Block *controlBody = controlOp.getBodyBlock();
318  for (auto &op : make_early_inc_range(*controlBody))
319  op.moveBefore(controlOp);
320 
321  rewriter.eraseOp(controlOp);
322  return success();
323  }
324 
325  return failure();
326 }
327 
328 template <typename OpTy>
329 static LogicalResult emptyControl(OpTy controlOp, PatternRewriter &rewriter) {
330  if (controlOp.getBodyBlock()->empty()) {
331  rewriter.eraseOp(controlOp);
332  return success();
333  }
334  return failure();
335 }
336 
337 /// A helper function to check whether the conditional and group (if it exists)
338 /// needs to be erased to maintain a valid state of a Calyx program. If these
339 /// have no more uses, they will be erased.
340 template <typename OpTy>
342  PatternRewriter &rewriter) {
343  static_assert(IsAny<OpTy, IfOp, WhileOp>(),
344  "This is only applicable to WhileOp and IfOp.");
345 
346  // Save information about the operation, and erase it.
347  Value cond = op.getCond();
348  std::optional<StringRef> groupName = op.getGroupName();
349  auto component = op->template getParentOfType<ComponentOp>();
350  rewriter.eraseOp(op);
351 
352  // Clean up the attached conditional and combinational group (if it exists).
353  if (groupName) {
354  auto group = component.getWiresOp().template lookupSymbol<GroupInterface>(
355  *groupName);
356  if (SymbolTable::symbolKnownUseEmpty(group, component.getRegion()))
357  rewriter.eraseOp(group);
358  }
359  // Check the conditional after the Group, since it will be driven within.
360  if (!isa<BlockArgument>(cond) && cond.getDefiningOp()->use_empty())
361  rewriter.eraseOp(cond.getDefiningOp());
362 }
363 
364 /// A helper function to check whether the conditional needs to be erased
365 /// to maintain a valid state of a Calyx program. If these
366 /// have no more uses, they will be erased.
367 template <typename OpTy>
368 static void eraseControlWithConditional(OpTy op, PatternRewriter &rewriter) {
369  static_assert(std::is_same<OpTy, StaticIfOp>(),
370  "This is only applicable to StatifIfOp.");
371 
372  // Save information about the operation, and erase it.
373  Value cond = op.getCond();
374  rewriter.eraseOp(op);
375 
376  // Check if conditional is still needed, and remove if it isn't
377  if (!isa<BlockArgument>(cond) && cond.getDefiningOp()->use_empty())
378  rewriter.eraseOp(cond.getDefiningOp());
379 }
380 
381 //===----------------------------------------------------------------------===//
382 // ComponentInterface
383 //===----------------------------------------------------------------------===//
384 
385 template <typename ComponentTy>
386 static void printComponentInterface(OpAsmPrinter &p, ComponentInterface comp) {
387  auto componentName = comp->template getAttrOfType<StringAttr>(
388  ::mlir::SymbolTable::getSymbolAttrName())
389  .getValue();
390  p << " ";
391  p.printSymbolName(componentName);
392 
393  // Print the port definition list for input and output ports.
394  auto printPortDefList = [&](auto ports) {
395  p << "(";
396  llvm::interleaveComma(ports, p, [&](const PortInfo &port) {
397  p << "%" << port.name.getValue() << ": " << port.type;
398  if (!port.attributes.empty()) {
399  p << " ";
400  p.printAttributeWithoutType(port.attributes);
401  }
402  });
403  p << ")";
404  };
405  printPortDefList(comp.getInputPortInfo());
406  p << " -> ";
407  printPortDefList(comp.getOutputPortInfo());
408 
409  p << " ";
410  p.printRegion(*comp.getRegion(), /*printEntryBlockArgs=*/false,
411  /*printBlockTerminators=*/false,
412  /*printEmptyBlock=*/false);
413 
414  SmallVector<StringRef> elidedAttrs = {
415  "portAttributes",
416  "portNames",
417  "portDirections",
418  "sym_name",
419  ComponentTy::getFunctionTypeAttrName(comp->getName()),
420  ComponentTy::getArgAttrsAttrName(comp->getName()),
421  ComponentTy::getResAttrsAttrName(comp->getName())};
422  p.printOptionalAttrDict(comp->getAttrs(), elidedAttrs);
423 }
424 
425 /// Parses the ports of a Calyx component signature, and adds the corresponding
426 /// port names to `attrName`.
427 static ParseResult
428 parsePortDefList(OpAsmParser &parser, OperationState &result,
429  SmallVectorImpl<OpAsmParser::Argument> &ports,
430  SmallVectorImpl<Type> &portTypes,
431  SmallVectorImpl<NamedAttrList> &portAttrs) {
432  auto parsePort = [&]() -> ParseResult {
433  OpAsmParser::Argument port;
434  Type portType;
435  // Expect each port to have the form `%<ssa-name> : <type>`.
436  if (parser.parseArgument(port) || parser.parseColon() ||
437  parser.parseType(portType))
438  return failure();
439  port.type = portType;
440  ports.push_back(port);
441  portTypes.push_back(portType);
442 
443  NamedAttrList portAttr;
444  portAttrs.push_back(succeeded(parser.parseOptionalAttrDict(portAttr))
445  ? portAttr
446  : NamedAttrList());
447  return success();
448  };
449 
450  return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
451  parsePort);
452 }
453 
454 /// Parses the signature of a Calyx component.
455 static ParseResult
456 parseComponentSignature(OpAsmParser &parser, OperationState &result,
457  SmallVectorImpl<OpAsmParser::Argument> &ports,
458  SmallVectorImpl<Type> &portTypes) {
459  SmallVector<OpAsmParser::Argument> inPorts, outPorts;
460  SmallVector<Type> inPortTypes, outPortTypes;
461  SmallVector<NamedAttrList> portAttributes;
462 
463  if (parsePortDefList(parser, result, inPorts, inPortTypes, portAttributes))
464  return failure();
465 
466  if (parser.parseArrow() ||
467  parsePortDefList(parser, result, outPorts, outPortTypes, portAttributes))
468  return failure();
469 
470  auto *context = parser.getBuilder().getContext();
471  // Add attribute for port names; these are currently
472  // just inferred from the SSA names of the component.
473  SmallVector<Attribute> portNames;
474  auto getPortName = [context](const auto &port) -> StringAttr {
475  StringRef name = port.ssaName.name;
476  if (name.starts_with("%"))
477  name = name.drop_front();
478  return StringAttr::get(context, name);
479  };
480  llvm::transform(inPorts, std::back_inserter(portNames), getPortName);
481  llvm::transform(outPorts, std::back_inserter(portNames), getPortName);
482 
483  result.addAttribute("portNames", ArrayAttr::get(context, portNames));
484  result.addAttribute(
485  "portDirections",
486  direction::packAttribute(context, inPorts.size(), outPorts.size()));
487 
488  ports.append(inPorts);
489  ports.append(outPorts);
490  portTypes.append(inPortTypes);
491  portTypes.append(outPortTypes);
492 
493  SmallVector<Attribute> portAttrs;
494  llvm::transform(portAttributes, std::back_inserter(portAttrs),
495  [&](auto attr) { return attr.getDictionary(context); });
496  result.addAttribute("portAttributes", ArrayAttr::get(context, portAttrs));
497 
498  return success();
499 }
500 
501 template <typename ComponentTy>
502 static ParseResult parseComponentInterface(OpAsmParser &parser,
503  OperationState &result) {
504  using namespace mlir::function_interface_impl;
505 
506  StringAttr componentName;
507  if (parser.parseSymbolName(componentName,
508  ::mlir::SymbolTable::getSymbolAttrName(),
509  result.attributes))
510  return failure();
511 
512  SmallVector<mlir::OpAsmParser::Argument> ports;
513 
514  SmallVector<Type> portTypes;
515  if (parseComponentSignature(parser, result, ports, portTypes))
516  return failure();
517 
518  // Build the component's type for FunctionLike trait. All ports are listed
519  // as arguments so they may be accessed within the component.
520  auto type = parser.getBuilder().getFunctionType(portTypes, /*results=*/{});
521  result.addAttribute(ComponentTy::getFunctionTypeAttrName(result.name),
522  TypeAttr::get(type));
523 
524  auto *body = result.addRegion();
525  if (parser.parseRegion(*body, ports))
526  return failure();
527 
528  if (body->empty())
529  body->push_back(new Block());
530 
531  if (parser.parseOptionalAttrDict(result.attributes))
532  return failure();
533 
534  return success();
535 }
536 
537 /// Returns a new vector containing the concatenation of vectors `a` and `b`.
538 template <typename T>
539 static SmallVector<T> concat(const SmallVectorImpl<T> &a,
540  const SmallVectorImpl<T> &b) {
541  SmallVector<T> out;
542  out.append(a);
543  out.append(b);
544  return out;
545 }
546 
547 static void buildComponentLike(OpBuilder &builder, OperationState &result,
548  StringAttr name, ArrayRef<PortInfo> ports,
549  bool combinational) {
550  using namespace mlir::function_interface_impl;
551 
552  result.addAttribute(::mlir::SymbolTable::getSymbolAttrName(), name);
553 
554  std::pair<SmallVector<Type, 8>, SmallVector<Type, 8>> portIOTypes;
555  std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>> portIONames;
556  std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>>
557  portIOAttributes;
558  SmallVector<Direction, 8> portDirections;
559  // Avoid using llvm::partition or llvm::sort to preserve relative ordering
560  // between individual inputs and outputs.
561  for (auto &&port : ports) {
562  bool isInput = port.direction == Direction::Input;
563  (isInput ? portIOTypes.first : portIOTypes.second).push_back(port.type);
564  (isInput ? portIONames.first : portIONames.second).push_back(port.name);
565  (isInput ? portIOAttributes.first : portIOAttributes.second)
566  .push_back(port.attributes);
567  }
568  auto portTypes = concat(portIOTypes.first, portIOTypes.second);
569  auto portNames = concat(portIONames.first, portIONames.second);
570  auto portAttributes = concat(portIOAttributes.first, portIOAttributes.second);
571 
572  // Build the function type of the component.
573  auto functionType = builder.getFunctionType(portTypes, {});
574  if (combinational) {
575  result.addAttribute(CombComponentOp::getFunctionTypeAttrName(result.name),
576  TypeAttr::get(functionType));
577  } else {
578  result.addAttribute(ComponentOp::getFunctionTypeAttrName(result.name),
579  TypeAttr::get(functionType));
580  }
581 
582  // Record the port names and number of input ports of the component.
583  result.addAttribute("portNames", builder.getArrayAttr(portNames));
584  result.addAttribute("portDirections",
585  direction::packAttribute(builder.getContext(),
586  portIOTypes.first.size(),
587  portIOTypes.second.size()));
588  // Record the attributes of the ports.
589  result.addAttribute("portAttributes", builder.getArrayAttr(portAttributes));
590 
591  // Create a single-blocked region.
592  Region *region = result.addRegion();
593  Block *body = new Block();
594  region->push_back(body);
595 
596  // Add all ports to the body.
597  body->addArguments(portTypes, SmallVector<Location, 4>(
598  portTypes.size(), builder.getUnknownLoc()));
599 
600  // Insert the WiresOp and ControlOp.
601  IRRewriter::InsertionGuard guard(builder);
602  builder.setInsertionPointToStart(body);
603  builder.create<WiresOp>(result.location);
604  if (!combinational)
605  builder.create<ControlOp>(result.location);
606 }
607 
608 //===----------------------------------------------------------------------===//
609 // ComponentOp
610 //===----------------------------------------------------------------------===//
611 
612 /// This is a helper function that should only be used to get the WiresOp or
613 /// ControlOp of a ComponentOp, which are guaranteed to exist and generally at
614 /// the end of a component's body. In the worst case, this will run in linear
615 /// time with respect to the number of instances within the component.
616 template <typename Op>
617 static Op getControlOrWiresFrom(ComponentOp op) {
618  auto *body = op.getBodyBlock();
619  // We verify there is a single WiresOp and ControlOp,
620  // so this is safe.
621  auto opIt = body->getOps<Op>().begin();
622  return *opIt;
623 }
624 
625 /// Returns the Block argument with the given name from a ComponentOp.
626 /// If the name doesn't exist, returns an empty Value.
627 static Value getBlockArgumentWithName(StringRef name, ComponentOp op) {
628  ArrayAttr portNames = op.getPortNames();
629 
630  for (size_t i = 0, e = portNames.size(); i != e; ++i) {
631  auto portName = cast<StringAttr>(portNames[i]);
632  if (portName.getValue() == name)
633  return op.getBodyBlock()->getArgument(i);
634  }
635  return Value{};
636 }
637 
638 WiresOp calyx::ComponentOp::getWiresOp() {
639  return getControlOrWiresFrom<WiresOp>(*this);
640 }
641 
642 ControlOp calyx::ComponentOp::getControlOp() {
643  return getControlOrWiresFrom<ControlOp>(*this);
644 }
645 
646 Value calyx::ComponentOp::getGoPort() {
647  return getBlockArgumentWithName(goPort, *this);
648 }
649 
650 Value calyx::ComponentOp::getDonePort() {
651  return getBlockArgumentWithName(donePort, *this);
652 }
653 
654 Value calyx::ComponentOp::getClkPort() {
655  return getBlockArgumentWithName(clkPort, *this);
656 }
657 
658 Value calyx::ComponentOp::getResetPort() {
659  return getBlockArgumentWithName(resetPort, *this);
660 }
661 
662 SmallVector<PortInfo> ComponentOp::getPortInfo() {
663  auto portTypes = getArgumentTypes();
664  ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
665  APInt portDirectionsAttr = getPortDirections();
666 
667  SmallVector<PortInfo> results;
668  for (size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
669  results.push_back(PortInfo{cast<StringAttr>(portNamesAttr[i]), portTypes[i],
670  direction::get(portDirectionsAttr[i]),
671  cast<DictionaryAttr>(portAttrs[i])});
672  }
673  return results;
674 }
675 
676 /// A helper function to return a filtered subset of a component's ports.
677 template <typename Pred>
678 static SmallVector<PortInfo> getFilteredPorts(ComponentOp op, Pred p) {
679  SmallVector<PortInfo> ports = op.getPortInfo();
680  llvm::erase_if(ports, p);
681  return ports;
682 }
683 
684 SmallVector<PortInfo> ComponentOp::getInputPortInfo() {
685  return getFilteredPorts(
686  *this, [](const PortInfo &port) { return port.direction == Output; });
687 }
688 
689 SmallVector<PortInfo> ComponentOp::getOutputPortInfo() {
690  return getFilteredPorts(
691  *this, [](const PortInfo &port) { return port.direction == Input; });
692 }
693 
694 void ComponentOp::print(OpAsmPrinter &p) {
695  printComponentInterface<ComponentOp>(p, *this);
696 }
697 
698 ParseResult ComponentOp::parse(OpAsmParser &parser, OperationState &result) {
699  return parseComponentInterface<ComponentOp>(parser, result);
700 }
701 
702 /// Determines whether the given ComponentOp has all the required ports.
703 static LogicalResult hasRequiredPorts(ComponentOp op) {
704  // Get all identifiers from the component ports.
705  llvm::SmallVector<StringRef, 4> identifiers;
706  for (PortInfo &port : op.getPortInfo()) {
707  auto portIds = port.getAllIdentifiers();
708  identifiers.append(portIds.begin(), portIds.end());
709  }
710  // Sort the identifiers: a pre-condition for std::set_intersection.
711  std::sort(identifiers.begin(), identifiers.end());
712 
713  llvm::SmallVector<StringRef, 4> intersection,
714  interfacePorts{clkPort, donePort, goPort, resetPort};
715  // Find the intersection between all identifiers and required ports.
716  std::set_intersection(interfacePorts.begin(), interfacePorts.end(),
717  identifiers.begin(), identifiers.end(),
718  std::back_inserter(intersection));
719 
720  if (intersection.size() == interfacePorts.size())
721  return success();
722 
723  SmallVector<StringRef, 4> difference;
724  std::set_difference(interfacePorts.begin(), interfacePorts.end(),
725  intersection.begin(), intersection.end(),
726  std::back_inserter(difference));
727  return op->emitOpError()
728  << "is missing the following required port attribute identifiers: "
729  << difference;
730 }
731 
732 LogicalResult ComponentOp::verify() {
733  // Verify there is exactly one of each the wires and control operations.
734  auto wIt = getBodyBlock()->getOps<WiresOp>();
735  auto cIt = getBodyBlock()->getOps<ControlOp>();
736  if (std::distance(wIt.begin(), wIt.end()) +
737  std::distance(cIt.begin(), cIt.end()) !=
738  2)
739  return emitOpError() << "requires exactly one of each: '"
740  << WiresOp::getOperationName() << "', '"
741  << ControlOp::getOperationName() << "'.";
742 
743  if (failed(hasRequiredPorts(*this)))
744  return failure();
745 
746  // Verify the component actually does something: has a non-empty Control
747  // region, or continuous assignments.
748  bool hasNoControlConstructs =
749  getControlOp().getBodyBlock()->getOperations().empty();
750  bool hasNoAssignments =
751  getWiresOp().getBodyBlock()->getOps<AssignOp>().empty();
752  if (hasNoControlConstructs && hasNoAssignments)
753  return emitOpError(
754  "The component currently does nothing. It needs to either have "
755  "continuous assignments in the Wires region or control constructs in "
756  "the Control region.");
757 
758  return success();
759 }
760 
761 void ComponentOp::build(OpBuilder &builder, OperationState &result,
762  StringAttr name, ArrayRef<PortInfo> ports) {
763  buildComponentLike(builder, result, name, ports, /*combinational=*/false);
764 }
765 
766 void ComponentOp::getAsmBlockArgumentNames(
767  mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
768  if (region.empty())
769  return;
770  auto ports = getPortNames();
771  auto *block = &getRegion()->front();
772  for (size_t i = 0, e = block->getNumArguments(); i != e; ++i)
773  setNameFn(block->getArgument(i), cast<StringAttr>(ports[i]).getValue());
774 }
775 
776 //===----------------------------------------------------------------------===//
777 // CombComponentOp
778 //===----------------------------------------------------------------------===//
779 
780 SmallVector<PortInfo> CombComponentOp::getPortInfo() {
781  auto portTypes = getArgumentTypes();
782  ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
783  APInt portDirectionsAttr = getPortDirections();
784 
785  SmallVector<PortInfo> results;
786  for (size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
787  results.push_back(PortInfo{cast<StringAttr>(portNamesAttr[i]), portTypes[i],
788  direction::get(portDirectionsAttr[i]),
789  cast<DictionaryAttr>(portAttrs[i])});
790  }
791  return results;
792 }
793 
794 WiresOp calyx::CombComponentOp::getWiresOp() {
795  auto *body = getBodyBlock();
796  auto opIt = body->getOps<WiresOp>().begin();
797  return *opIt;
798 }
799 
800 /// A helper function to return a filtered subset of a comb component's ports.
801 template <typename Pred>
802 static SmallVector<PortInfo> getFilteredPorts(CombComponentOp op, Pred p) {
803  SmallVector<PortInfo> ports = op.getPortInfo();
804  llvm::erase_if(ports, p);
805  return ports;
806 }
807 
808 SmallVector<PortInfo> CombComponentOp::getInputPortInfo() {
809  return getFilteredPorts(
810  *this, [](const PortInfo &port) { return port.direction == Output; });
811 }
812 
813 SmallVector<PortInfo> CombComponentOp::getOutputPortInfo() {
814  return getFilteredPorts(
815  *this, [](const PortInfo &port) { return port.direction == Input; });
816 }
817 
818 void CombComponentOp::print(OpAsmPrinter &p) {
819  printComponentInterface<CombComponentOp>(p, *this);
820 }
821 
822 ParseResult CombComponentOp::parse(OpAsmParser &parser,
823  OperationState &result) {
824  return parseComponentInterface<CombComponentOp>(parser, result);
825 }
826 
827 LogicalResult CombComponentOp::verify() {
828  // Verify there is exactly one wires operation.
829  auto wIt = getBodyBlock()->getOps<WiresOp>();
830  if (std::distance(wIt.begin(), wIt.end()) != 1)
831  return emitOpError() << "requires exactly one "
832  << WiresOp::getOperationName() << " op.";
833 
834  // Verify there is not a control operation.
835  auto cIt = getBodyBlock()->getOps<ControlOp>();
836  if (std::distance(cIt.begin(), cIt.end()) != 0)
837  return emitOpError() << "must not have a `" << ControlOp::getOperationName()
838  << "` op.";
839 
840  // Verify the component actually does something: has continuous assignments.
841  bool hasNoAssignments =
842  getWiresOp().getBodyBlock()->getOps<AssignOp>().empty();
843  if (hasNoAssignments)
844  return emitOpError(
845  "The component currently does nothing. It needs to either have "
846  "continuous assignments in the Wires region or control constructs in "
847  "the Control region.");
848 
849  // Check that all cells are combinational
850  auto cells = getOps<CellInterface>();
851  for (auto cell : cells) {
852  if (!cell.isCombinational())
853  return emitOpError() << "contains non-combinational cell "
854  << cell.instanceName();
855  }
856 
857  // Check that the component has no groups
858  auto groups = getWiresOp().getOps<GroupOp>();
859  if (!groups.empty())
860  return emitOpError() << "contains group " << (*groups.begin()).getSymName();
861 
862  // Combinational groups aren't allowed in combinational components either.
863  // For more information see here:
864  // https://docs.calyxir.org/lang/ref.html#comb-group-definitions
865  auto combGroups = getWiresOp().getOps<CombGroupOp>();
866  if (!combGroups.empty())
867  return emitOpError() << "contains comb group "
868  << (*combGroups.begin()).getSymName();
869 
870  return success();
871 }
872 
873 void CombComponentOp::build(OpBuilder &builder, OperationState &result,
874  StringAttr name, ArrayRef<PortInfo> ports) {
875  buildComponentLike(builder, result, name, ports, /*combinational=*/true);
876 }
877 
878 void CombComponentOp::getAsmBlockArgumentNames(
879  mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
880  if (region.empty())
881  return;
882  auto ports = getPortNames();
883  auto *block = &getRegion()->front();
884  for (size_t i = 0, e = block->getNumArguments(); i != e; ++i)
885  setNameFn(block->getArgument(i), cast<StringAttr>(ports[i]).getValue());
886 }
887 
888 //===----------------------------------------------------------------------===//
889 // ControlOp
890 //===----------------------------------------------------------------------===//
891 LogicalResult ControlOp::verify() { return verifyControlBody(*this); }
892 
893 // Get the InvokeOps of this ControlOp.
894 SmallVector<InvokeOp, 4> ControlOp::getInvokeOps() {
895  SmallVector<InvokeOp, 4> ret;
896  this->walk([&](InvokeOp invokeOp) { ret.push_back(invokeOp); });
897  return ret;
898 }
899 
900 //===----------------------------------------------------------------------===//
901 // SeqOp
902 //===----------------------------------------------------------------------===//
903 
904 void SeqOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
905  MLIRContext *context) {
906  patterns.add(collapseControl<SeqOp>);
907  patterns.add(emptyControl<SeqOp>);
908  patterns.insert<CollapseUnaryControl<SeqOp>>(context);
909 }
910 
911 //===----------------------------------------------------------------------===//
912 // StaticSeqOp
913 //===----------------------------------------------------------------------===//
914 
915 LogicalResult StaticSeqOp::verify() {
916  // StaticSeqOp should only have static control in it
917  auto &ops = (*this).getBodyBlock()->getOperations();
918  if (!llvm::all_of(ops, [&](Operation &op) { return isStaticControl(&op); })) {
919  return emitOpError("StaticSeqOp has non static control within it");
920  }
921 
922  return success();
923 }
924 
925 void StaticSeqOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
926  MLIRContext *context) {
927  patterns.add(collapseControl<StaticSeqOp>);
928  patterns.add(emptyControl<StaticSeqOp>);
930 }
931 
932 //===----------------------------------------------------------------------===//
933 // ParOp
934 //===----------------------------------------------------------------------===//
935 
936 LogicalResult ParOp::verify() {
937  llvm::SmallSet<StringRef, 8> groupNames;
938 
939  // Add loose requirement that the body of a ParOp may not enable the same
940  // Group more than once, e.g. calyx.par { calyx.enable @G calyx.enable @G }
941  for (EnableOp op : getBodyBlock()->getOps<EnableOp>()) {
942  StringRef groupName = op.getGroupName();
943  if (groupNames.count(groupName))
944  return emitOpError() << "cannot enable the same group: \"" << groupName
945  << "\" more than once.";
946  groupNames.insert(groupName);
947  }
948 
949  return success();
950 }
951 
952 void ParOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
953  MLIRContext *context) {
954  patterns.add(collapseControl<ParOp>);
955  patterns.add(emptyControl<ParOp>);
956  patterns.insert<CollapseUnaryControl<ParOp>>(context);
957 }
958 
959 //===----------------------------------------------------------------------===//
960 // StaticParOp
961 //===----------------------------------------------------------------------===//
962 
963 LogicalResult StaticParOp::verify() {
964  llvm::SmallSet<StringRef, 8> groupNames;
965 
966  // Add loose requirement that the body of a ParOp may not enable the same
967  // Group more than once, e.g. calyx.par { calyx.enable @G calyx.enable @G }
968  for (EnableOp op : getBodyBlock()->getOps<EnableOp>()) {
969  StringRef groupName = op.getGroupName();
970  if (groupNames.count(groupName))
971  return emitOpError() << "cannot enable the same group: \"" << groupName
972  << "\" more than once.";
973  groupNames.insert(groupName);
974  }
975 
976  // static par must only have static control in it
977  auto &ops = (*this).getBodyBlock()->getOperations();
978  for (Operation &op : ops) {
979  if (!isStaticControl(&op)) {
980  return op.emitOpError("StaticParOp has non static control within it");
981  }
982  }
983 
984  return success();
985 }
986 
987 void StaticParOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
988  MLIRContext *context) {
989  patterns.add(collapseControl<StaticParOp>);
990  patterns.add(emptyControl<StaticParOp>);
992 }
993 
994 //===----------------------------------------------------------------------===//
995 // WiresOp
996 //===----------------------------------------------------------------------===//
997 LogicalResult WiresOp::verify() {
998  auto componentInterface = (*this)->getParentOfType<ComponentInterface>();
999  if (llvm::isa<ComponentOp>(componentInterface)) {
1000  auto component = llvm::cast<ComponentOp>(componentInterface);
1001  auto control = component.getControlOp();
1002 
1003  // Verify each group is referenced in the control section.
1004  for (auto &&op : *getBodyBlock()) {
1005  if (!isa<GroupInterface>(op))
1006  continue;
1007  auto group = cast<GroupInterface>(op);
1008  auto groupName = group.symName();
1009  if (mlir::SymbolTable::symbolKnownUseEmpty(groupName, control))
1010  return op.emitOpError()
1011  << "with name: " << groupName
1012  << " is unused in the control execution schedule";
1013  }
1014  }
1015 
1016  // Verify that:
1017  // - At most one continuous assignment exists for any given value
1018  // - A continuously assigned wire has no assignments inside groups.
1019  for (auto thisAssignment : getBodyBlock()->getOps<AssignOp>()) {
1020  // Always assume guarded assignments will not be driven simultaneously. We
1021  // liberally assume that guards are mutually exclusive (more elaborate
1022  // static and dynamic checking can be performed to validate such cases).
1023  if (thisAssignment.getGuard())
1024  continue;
1025 
1026  Value dest = thisAssignment.getDest();
1027  for (Operation *user : dest.getUsers()) {
1028  auto assignUser = dyn_cast<AssignOp>(user);
1029  if (!assignUser || assignUser.getDest() != dest ||
1030  assignUser == thisAssignment)
1031  continue;
1032 
1033  return user->emitOpError() << "destination is already continuously "
1034  "driven. Other assignment is "
1035  << thisAssignment;
1036  }
1037  }
1038 
1039  return success();
1040 }
1041 
1042 //===----------------------------------------------------------------------===//
1043 // CombGroupOp
1044 //===----------------------------------------------------------------------===//
1045 
1046 /// Verifies the defining operation of a value is combinational.
1047 static LogicalResult isCombinational(Value value, GroupInterface group) {
1048  Operation *definingOp = value.getDefiningOp();
1049  if (definingOp == nullptr || definingOp->hasTrait<Combinational>())
1050  // This is a port of the parent component or combinational.
1051  return success();
1052 
1053  // For now, assumes all component instances are combinational. Once
1054  // combinational components are supported, this can be strictly enforced.
1055  if (isa<InstanceOp>(definingOp))
1056  return success();
1057 
1058  // Constants and logical operations are OK.
1059  if (isa<comb::CombDialect, hw::HWDialect>(definingOp->getDialect()))
1060  return success();
1061 
1062  // Reads to MemoryOp and RegisterOp are combinational. Writes are not.
1063  if (auto r = dyn_cast<RegisterOp>(definingOp)) {
1064  return value == r.getOut()
1065  ? success()
1066  : group->emitOpError()
1067  << "with register: \"" << r.instanceName()
1068  << "\" is conducting a memory store. This is not "
1069  "combinational.";
1070  } else if (auto m = dyn_cast<MemoryOp>(definingOp)) {
1071  auto writePorts = {m.writeData(), m.writeEn()};
1072  return (llvm::none_of(writePorts, [&](Value p) { return p == value; }))
1073  ? success()
1074  : group->emitOpError()
1075  << "with memory: \"" << m.instanceName()
1076  << "\" is conducting a memory store. This "
1077  "is not combinational.";
1078  }
1079 
1080  std::string portName =
1081  valueName(group->getParentOfType<ComponentOp>(), value);
1082  return group->emitOpError() << "with port: " << portName
1083  << ". This operation is not combinational.";
1084 }
1085 
1086 /// Verifies a combinational group may contain only combinational primitives or
1087 /// perform combinational logic.
1088 LogicalResult CombGroupOp::verify() {
1089  for (auto &&op : *getBodyBlock()) {
1090  auto assign = dyn_cast<AssignOp>(op);
1091  if (assign == nullptr)
1092  continue;
1093  Value dst = assign.getDest(), src = assign.getSrc();
1094  if (failed(isCombinational(dst, *this)) ||
1095  failed(isCombinational(src, *this)))
1096  return failure();
1097  }
1098  return success();
1099 }
1100 
1101 //===----------------------------------------------------------------------===//
1102 // GroupGoOp
1103 //===----------------------------------------------------------------------===//
1104 GroupGoOp GroupOp::getGoOp() {
1105  auto goOps = getBodyBlock()->getOps<GroupGoOp>();
1106  size_t nOps = std::distance(goOps.begin(), goOps.end());
1107  return nOps ? *goOps.begin() : GroupGoOp();
1108 }
1109 
1110 GroupDoneOp GroupOp::getDoneOp() {
1111  auto body = this->getBodyBlock();
1112  return cast<GroupDoneOp>(body->getTerminator());
1113 }
1114 
1115 //===----------------------------------------------------------------------===//
1116 // CycleOp
1117 //===----------------------------------------------------------------------===//
1118 void CycleOp::print(OpAsmPrinter &p) {
1119  p << " ";
1120  // The guard is optional.
1121  auto start = this->getStart();
1122  auto end = this->getEnd();
1123  if (end.has_value()) {
1124  p << "[" << start << ":" << end.value() << "]";
1125  } else {
1126  p << start;
1127  }
1128 }
1129 
1130 ParseResult CycleOp::parse(OpAsmParser &parser, OperationState &result) {
1131  SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
1132 
1133  uint32_t startLiteral;
1134  uint32_t endLiteral;
1135 
1136  auto hasEnd = succeeded(parser.parseOptionalLSquare());
1137 
1138  if (parser.parseInteger(startLiteral)) {
1139  parser.emitError(parser.getNameLoc(), "Could not parse start cycle");
1140  return failure();
1141  }
1142 
1143  auto start = parser.getBuilder().getI32IntegerAttr(startLiteral);
1144  result.addAttribute(getStartAttrName(result.name), start);
1145 
1146  if (hasEnd) {
1147  if (parser.parseColon())
1148  return failure();
1149 
1150  if (auto res = parser.parseOptionalInteger(endLiteral); res.has_value()) {
1151  auto end = parser.getBuilder().getI32IntegerAttr(endLiteral);
1152  result.addAttribute(getEndAttrName(result.name), end);
1153  }
1154 
1155  if (parser.parseRSquare())
1156  return failure();
1157  }
1158 
1159  result.addTypes(parser.getBuilder().getI1Type());
1160 
1161  return success();
1162 }
1163 
1164 LogicalResult CycleOp::verify() {
1165  uint32_t latency = this->getGroupLatency();
1166 
1167  if (this->getStart() >= latency) {
1168  emitOpError("start cycle must be less than the group latency");
1169  return failure();
1170  }
1171 
1172  if (this->getEnd().has_value()) {
1173  if (this->getStart() >= this->getEnd().value()) {
1174  emitOpError("start cycle must be less than end cycle");
1175  return failure();
1176  }
1177 
1178  if (this->getEnd() >= latency) {
1179  emitOpError("end cycle must be less than the group latency");
1180  return failure();
1181  }
1182  }
1183 
1184  return success();
1185 }
1186 
1187 uint32_t CycleOp::getGroupLatency() {
1188  auto group = (*this)->getParentOfType<StaticGroupOp>();
1189  return group.getLatency();
1190 }
1191 
1192 //===----------------------------------------------------------------------===//
1193 // GroupInterface
1194 //===----------------------------------------------------------------------===//
1195 
1196 /// Determines whether the given port is used in the group. Its use depends on
1197 /// the `isDriven` value; if true, then the port should be a destination in an
1198 /// AssignOp. Otherwise, it should be the source, i.e. a read.
1199 static bool portIsUsedInGroup(GroupInterface group, Value port, bool isDriven) {
1200  return llvm::any_of(port.getUses(), [&](auto &&use) {
1201  auto assignOp = dyn_cast<AssignOp>(use.getOwner());
1202  if (assignOp == nullptr)
1203  return false;
1204 
1205  Operation *parent = assignOp->getParentOp();
1206  if (isa<WiresOp>(parent))
1207  // This is a continuous assignment.
1208  return false;
1209 
1210  // A port is used if it meet the criteria:
1211  // (1) it is a {source, destination} of an assignment.
1212  // (2) that assignment is found in the provided group.
1213 
1214  // If not driven, then read.
1215  Value expected = isDriven ? assignOp.getDest() : assignOp.getSrc();
1216  return expected == port && group == parent;
1217  });
1218 }
1219 
1220 /// Checks whether `port` is driven from within `groupOp`.
1221 static LogicalResult portDrivenByGroup(GroupInterface groupOp, Value port) {
1222  // Check if the port is driven by an assignOp from within `groupOp`.
1223  if (portIsUsedInGroup(groupOp, port, /*isDriven=*/true))
1224  return success();
1225 
1226  // If `port` is an output of a cell then we conservatively enforce that at
1227  // least one input port of the cell must be driven by the group.
1228  if (auto cell = dyn_cast<CellInterface>(port.getDefiningOp());
1229  cell && cell.direction(port) == calyx::Direction::Output)
1230  return groupOp.drivesAnyPort(cell.getInputPorts());
1231 
1232  return failure();
1233 }
1234 
1235 LogicalResult GroupOp::drivesPort(Value port) {
1236  return portDrivenByGroup(*this, port);
1237 }
1238 
1239 LogicalResult CombGroupOp::drivesPort(Value port) {
1240  return portDrivenByGroup(*this, port);
1241 }
1242 
1243 LogicalResult StaticGroupOp::drivesPort(Value port) {
1244  return portDrivenByGroup(*this, port);
1245 }
1246 
1247 /// Checks whether all ports are driven within the group.
1248 static LogicalResult allPortsDrivenByGroup(GroupInterface group,
1249  ValueRange ports) {
1250  return success(llvm::all_of(ports, [&](Value port) {
1251  return portIsUsedInGroup(group, port, /*isDriven=*/true);
1252  }));
1253 }
1254 
1255 LogicalResult GroupOp::drivesAllPorts(ValueRange ports) {
1256  return allPortsDrivenByGroup(*this, ports);
1257 }
1258 
1259 LogicalResult CombGroupOp::drivesAllPorts(ValueRange ports) {
1260  return allPortsDrivenByGroup(*this, ports);
1261 }
1262 
1263 LogicalResult StaticGroupOp::drivesAllPorts(ValueRange ports) {
1264  return allPortsDrivenByGroup(*this, ports);
1265 }
1266 
1267 /// Checks whether any ports are driven within the group.
1268 static LogicalResult anyPortsDrivenByGroup(GroupInterface group,
1269  ValueRange ports) {
1270  return success(llvm::any_of(ports, [&](Value port) {
1271  return portIsUsedInGroup(group, port, /*isDriven=*/true);
1272  }));
1273 }
1274 
1275 LogicalResult GroupOp::drivesAnyPort(ValueRange ports) {
1276  return anyPortsDrivenByGroup(*this, ports);
1277 }
1278 
1279 LogicalResult CombGroupOp::drivesAnyPort(ValueRange ports) {
1280  return anyPortsDrivenByGroup(*this, ports);
1281 }
1282 
1283 LogicalResult StaticGroupOp::drivesAnyPort(ValueRange ports) {
1284  return anyPortsDrivenByGroup(*this, ports);
1285 }
1286 
1287 /// Checks whether any ports are read within the group.
1288 static LogicalResult anyPortsReadByGroup(GroupInterface group,
1289  ValueRange ports) {
1290  return success(llvm::any_of(ports, [&](Value port) {
1291  return portIsUsedInGroup(group, port, /*isDriven=*/false);
1292  }));
1293 }
1294 
1295 LogicalResult GroupOp::readsAnyPort(ValueRange ports) {
1296  return anyPortsReadByGroup(*this, ports);
1297 }
1298 
1299 LogicalResult CombGroupOp::readsAnyPort(ValueRange ports) {
1300  return anyPortsReadByGroup(*this, ports);
1301 }
1302 
1303 LogicalResult StaticGroupOp::readsAnyPort(ValueRange ports) {
1304  return anyPortsReadByGroup(*this, ports);
1305 }
1306 
1307 /// Verifies that certain ports of primitives are either driven or read
1308 /// together.
1309 static LogicalResult verifyPrimitivePortDriving(AssignOp assign,
1310  GroupInterface group) {
1311  Operation *destDefiningOp = assign.getDest().getDefiningOp();
1312  if (destDefiningOp == nullptr)
1313  return success();
1314  auto destCell = dyn_cast<CellInterface>(destDefiningOp);
1315  if (destCell == nullptr)
1316  return success();
1317 
1318  LogicalResult verifyWrites =
1319  TypeSwitch<Operation *, LogicalResult>(destCell)
1320  .Case<RegisterOp>([&](auto op) {
1321  // We only want to verify this is written to if the {write enable,
1322  // in} port is driven.
1323  return succeeded(group.drivesAnyPort({op.getWriteEn(), op.getIn()}))
1324  ? group.drivesAllPorts({op.getWriteEn(), op.getIn()})
1325  : success();
1326  })
1327  .Case<MemoryOp>([&](auto op) {
1328  SmallVector<Value> requiredWritePorts;
1329  // If writing to memory, write_en, write_data, and all address ports
1330  // should be driven.
1331  requiredWritePorts.push_back(op.writeEn());
1332  requiredWritePorts.push_back(op.writeData());
1333  for (Value address : op.addrPorts())
1334  requiredWritePorts.push_back(address);
1335 
1336  // We only want to verify the write ports if either write_data or
1337  // write_en is driven.
1338  return succeeded(
1339  group.drivesAnyPort({op.writeData(), op.writeEn()}))
1340  ? group.drivesAllPorts(requiredWritePorts)
1341  : success();
1342  })
1343  .Case<AndLibOp, OrLibOp, XorLibOp, AddLibOp, SubLibOp, GtLibOp,
1344  LtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp,
1345  RshLibOp, SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp,
1346  SleLibOp, SrshLibOp>([&](auto op) {
1347  Value lhs = op.getLeft(), rhs = op.getRight();
1348  return succeeded(group.drivesAnyPort({lhs, rhs}))
1349  ? group.drivesAllPorts({lhs, rhs})
1350  : success();
1351  })
1352  .Default([&](auto op) { return success(); });
1353 
1354  if (failed(verifyWrites))
1355  return group->emitOpError()
1356  << "with cell: " << destCell->getName() << " \""
1357  << destCell.instanceName()
1358  << "\" is performing a write and failed to drive all necessary "
1359  "ports.";
1360 
1361  Operation *srcDefiningOp = assign.getSrc().getDefiningOp();
1362  if (srcDefiningOp == nullptr)
1363  return success();
1364  auto srcCell = dyn_cast<CellInterface>(srcDefiningOp);
1365  if (srcCell == nullptr)
1366  return success();
1367 
1368  LogicalResult verifyReads =
1369  TypeSwitch<Operation *, LogicalResult>(srcCell)
1370  .Case<MemoryOp>([&](auto op) {
1371  // If reading memory, all address ports should be driven. Note that
1372  // we only want to verify the read ports if read_data is used in the
1373  // group.
1374  return succeeded(group.readsAnyPort({op.readData()}))
1375  ? group.drivesAllPorts(op.addrPorts())
1376  : success();
1377  })
1378  .Default([&](auto op) { return success(); });
1379 
1380  if (failed(verifyReads))
1381  return group->emitOpError() << "with cell: " << srcCell->getName() << " \""
1382  << srcCell.instanceName()
1383  << "\" is having a read performed upon it, and "
1384  "failed to drive all necessary ports.";
1385 
1386  return success();
1387 }
1388 
1389 LogicalResult calyx::verifyGroupInterface(Operation *op) {
1390  auto group = dyn_cast<GroupInterface>(op);
1391  if (group == nullptr)
1392  return success();
1393 
1394  for (auto &&groupOp : *group.getBody()) {
1395  auto assign = dyn_cast<AssignOp>(groupOp);
1396  if (assign == nullptr)
1397  continue;
1398  if (failed(verifyPrimitivePortDriving(assign, group)))
1399  return failure();
1400  }
1401 
1402  return success();
1403 }
1404 
1405 //===----------------------------------------------------------------------===//
1406 // Utilities for operations with the Cell trait.
1407 //===----------------------------------------------------------------------===//
1408 
1409 /// Gives each result of the cell a meaningful name in the form:
1410 /// <instance-name>.<port-name>
1411 static void getCellAsmResultNames(OpAsmSetValueNameFn setNameFn, Operation *op,
1412  ArrayRef<StringRef> portNames) {
1413  auto cellInterface = dyn_cast<CellInterface>(op);
1414  assert(cellInterface && "must implement the Cell interface");
1415 
1416  std::string prefix = cellInterface.instanceName().str() + ".";
1417  for (size_t i = 0, e = portNames.size(); i != e; ++i)
1418  setNameFn(op->getResult(i), prefix + portNames[i].str());
1419 }
1420 
1421 //===----------------------------------------------------------------------===//
1422 // AssignOp
1423 //===----------------------------------------------------------------------===//
1424 
1425 /// Determines whether the given direction is valid with the given inputs. The
1426 /// `isDestination` boolean is used to distinguish whether the value is a source
1427 /// or a destination.
1428 static LogicalResult verifyPortDirection(Operation *op, Value value,
1429  bool isDestination) {
1430  Operation *definingOp = value.getDefiningOp();
1431  bool isComponentPort = isa<BlockArgument>(value),
1432  isCellInterfacePort = isa_and_nonnull<CellInterface>(definingOp);
1433  assert((isComponentPort || isCellInterfacePort) && "Not a port.");
1434 
1435  PortInfo port = isComponentPort
1436  ? getPortInfo(cast<BlockArgument>(value))
1437  : cast<CellInterface>(definingOp).portInfo(value);
1438 
1439  bool isSource = !isDestination;
1440  // Component output ports and cell interface input ports should be driven.
1441  Direction validDirection =
1442  (isDestination && isComponentPort) || (isSource && isCellInterfacePort)
1444  : Direction::Input;
1445 
1446  return port.direction == validDirection
1447  ? success()
1448  : op->emitOpError()
1449  << "has a " << (isComponentPort ? "component" : "cell")
1450  << " port as the "
1451  << (isDestination ? "destination" : "source")
1452  << " with the incorrect direction.";
1453 }
1454 
1455 /// Verifies the value of a given assignment operation. The boolean
1456 /// `isDestination` is used to distinguish whether the destination
1457 /// or source of the AssignOp is to be verified.
1458 static LogicalResult verifyAssignOpValue(AssignOp op, bool isDestination) {
1459  bool isSource = !isDestination;
1460  Value value = isDestination ? op.getDest() : op.getSrc();
1461  if (isPort(value))
1462  return verifyPortDirection(op, value, isDestination);
1463 
1464  // A destination may also be the Go or Done hole of a GroupOp.
1465  if (isDestination && !isa<GroupGoOp, GroupDoneOp>(value.getDefiningOp()))
1466  return op->emitOpError(
1467  "has an invalid destination port. It must be drive-able.");
1468  else if (isSource)
1469  return verifyNotComplexSource(op);
1470 
1471  return success();
1472 }
1473 
1474 LogicalResult AssignOp::verify() {
1475  bool isDestination = true, isSource = false;
1476  if (failed(verifyAssignOpValue(*this, isDestination)))
1477  return failure();
1478  if (failed(verifyAssignOpValue(*this, isSource)))
1479  return failure();
1480 
1481  return success();
1482 }
1483 
1484 ParseResult AssignOp::parse(OpAsmParser &parser, OperationState &result) {
1485  OpAsmParser::UnresolvedOperand destination;
1486  if (parser.parseOperand(destination) || parser.parseEqual())
1487  return failure();
1488 
1489  // An AssignOp takes one of the two following forms:
1490  // (1) %<dest> = %<src> : <type>
1491  // (2) %<dest> = %<guard> ? %<src> : <type>
1492  OpAsmParser::UnresolvedOperand guardOrSource;
1493  if (parser.parseOperand(guardOrSource))
1494  return failure();
1495 
1496  // Since the guard is optional, we need to check if there is an accompanying
1497  // `?` symbol.
1498  OpAsmParser::UnresolvedOperand source;
1499  bool hasGuard = succeeded(parser.parseOptionalQuestion());
1500  if (hasGuard) {
1501  // The guard exists. Parse the source.
1502  if (parser.parseOperand(source))
1503  return failure();
1504  }
1505 
1506  Type type;
1507  if (parser.parseColonType(type) ||
1508  parser.resolveOperand(destination, type, result.operands))
1509  return failure();
1510 
1511  if (hasGuard) {
1512  Type i1Type = parser.getBuilder().getI1Type();
1513  // Since the guard is optional, it is listed last in the arguments of the
1514  // AssignOp. Therefore, we must parse the source first.
1515  if (parser.resolveOperand(source, type, result.operands) ||
1516  parser.resolveOperand(guardOrSource, i1Type, result.operands))
1517  return failure();
1518  } else {
1519  // This is actually a source.
1520  if (parser.resolveOperand(guardOrSource, type, result.operands))
1521  return failure();
1522  }
1523 
1524  return success();
1525 }
1526 
1527 void AssignOp::print(OpAsmPrinter &p) {
1528  p << " " << getDest() << " = ";
1529 
1530  Value bguard = getGuard(), source = getSrc();
1531  // The guard is optional.
1532  if (bguard)
1533  p << bguard << " ? ";
1534 
1535  // We only need to print a single type; the destination and source are
1536  // guaranteed to be the same type.
1537  p << source << " : " << source.getType();
1538 }
1539 
1540 //===----------------------------------------------------------------------===//
1541 // InstanceOp
1542 //===----------------------------------------------------------------------===//
1543 
1544 /// Lookup the component for the symbol. This returns null on
1545 /// invalid IR.
1546 ComponentInterface InstanceOp::getReferencedComponent() {
1547  auto module = (*this)->getParentOfType<ModuleOp>();
1548  if (!module)
1549  return nullptr;
1550 
1551  return module.lookupSymbol<ComponentInterface>(getComponentName());
1552 }
1553 
1554 /// Verifies the port information in comparison with the referenced component
1555 /// of an instance. This helper function avoids conducting a lookup for the
1556 /// referenced component twice.
1557 static LogicalResult
1558 verifyInstanceOpType(InstanceOp instance,
1559  ComponentInterface referencedComponent) {
1560  auto module = instance->getParentOfType<ModuleOp>();
1561  StringRef entryPointName =
1562  module->getAttrOfType<StringAttr>("calyx.entrypoint");
1563  if (instance.getComponentName() == entryPointName)
1564  return instance.emitOpError()
1565  << "cannot reference the entry-point component: '" << entryPointName
1566  << "'.";
1567 
1568  // Verify the instance result ports with those of its referenced component.
1569  SmallVector<PortInfo> componentPorts = referencedComponent.getPortInfo();
1570  size_t numPorts = componentPorts.size();
1571 
1572  size_t numResults = instance.getNumResults();
1573  if (numResults != numPorts)
1574  return instance.emitOpError()
1575  << "has a wrong number of results; expected: " << numPorts
1576  << " but got " << numResults;
1577 
1578  for (size_t i = 0; i != numResults; ++i) {
1579  auto resultType = instance.getResult(i).getType();
1580  auto expectedType = componentPorts[i].type;
1581  if (resultType == expectedType)
1582  continue;
1583  return instance.emitOpError()
1584  << "result type for " << componentPorts[i].name << " must be "
1585  << expectedType << ", but got " << resultType;
1586  }
1587  return success();
1588 }
1589 
1590 LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1591  Operation *op = *this;
1592  auto module = op->getParentOfType<ModuleOp>();
1593  Operation *referencedComponent =
1594  symbolTable.lookupNearestSymbolFrom(module, getComponentNameAttr());
1595  if (referencedComponent == nullptr)
1596  return emitError() << "referencing component: '" << getComponentName()
1597  << "', which does not exist.";
1598 
1599  Operation *shadowedComponentName =
1600  symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1601  if (shadowedComponentName != nullptr)
1602  return emitError() << "instance symbol: '" << instanceName()
1603  << "' is already a symbol for another component.";
1604 
1605  // Verify the referenced component is not instantiating itself.
1606  auto parentComponent = op->getParentOfType<ComponentOp>();
1607  if (parentComponent == referencedComponent)
1608  return emitError() << "recursive instantiation of its parent component: '"
1609  << getComponentName() << "'";
1610 
1611  assert(isa<ComponentInterface>(referencedComponent) &&
1612  "Should be a ComponentInterface.");
1613  return verifyInstanceOpType(*this,
1614  cast<ComponentInterface>(referencedComponent));
1615 }
1616 
1617 /// Provide meaningful names to the result values of an InstanceOp.
1619  getCellAsmResultNames(setNameFn, *this, this->portNames());
1620 }
1621 
1622 SmallVector<StringRef> InstanceOp::portNames() {
1623  SmallVector<StringRef> portNames;
1624  for (Attribute name : getReferencedComponent().getPortNames())
1625  portNames.push_back(cast<StringAttr>(name).getValue());
1626  return portNames;
1627 }
1628 
1629 SmallVector<Direction> InstanceOp::portDirections() {
1630  SmallVector<Direction> portDirections;
1631  for (const PortInfo &port : getReferencedComponent().getPortInfo())
1632  portDirections.push_back(port.direction);
1633  return portDirections;
1634 }
1635 
1636 SmallVector<DictionaryAttr> InstanceOp::portAttributes() {
1637  SmallVector<DictionaryAttr> portAttributes;
1638  for (const PortInfo &port : getReferencedComponent().getPortInfo())
1639  portAttributes.push_back(port.attributes);
1640  return portAttributes;
1641 }
1642 
1644  return isa<CombComponentOp>(getReferencedComponent());
1645 }
1646 
1647 //===----------------------------------------------------------------------===//
1648 // PrimitiveOp
1649 //===----------------------------------------------------------------------===//
1650 
1651 /// Lookup the component for the symbol. This returns null on
1652 /// invalid IR.
1653 hw::HWModuleExternOp PrimitiveOp::getReferencedPrimitive() {
1654  auto module = (*this)->getParentOfType<ModuleOp>();
1655  if (!module)
1656  return nullptr;
1657 
1658  return module.lookupSymbol<hw::HWModuleExternOp>(getPrimitiveName());
1659 }
1660 
1661 /// Verifies the port information in comparison with the referenced component
1662 /// of an instance. This helper function avoids conducting a lookup for the
1663 /// referenced component twice.
1664 static LogicalResult
1665 verifyPrimitiveOpType(PrimitiveOp instance,
1666  hw::HWModuleExternOp referencedPrimitive) {
1667  auto module = instance->getParentOfType<ModuleOp>();
1668  StringRef entryPointName =
1669  module->getAttrOfType<StringAttr>("calyx.entrypoint");
1670  if (instance.getPrimitiveName() == entryPointName)
1671  return instance.emitOpError()
1672  << "cannot reference the entry-point component: '" << entryPointName
1673  << "'.";
1674 
1675  // Verify the instance result ports with those of its referenced component.
1676  auto primitivePorts = referencedPrimitive.getPortList();
1677  size_t numPorts = primitivePorts.size();
1678 
1679  size_t numResults = instance.getNumResults();
1680  if (numResults != numPorts)
1681  return instance.emitOpError()
1682  << "has a wrong number of results; expected: " << numPorts
1683  << " but got " << numResults;
1684 
1685  // Verify parameters match up
1686  ArrayAttr modParameters = referencedPrimitive.getParameters();
1687  ArrayAttr parameters = instance.getParameters().value_or(ArrayAttr());
1688  size_t numExpected = modParameters.size();
1689  size_t numParams = parameters.size();
1690  if (numParams != numExpected)
1691  return instance.emitOpError()
1692  << "has the wrong number of parameters; expected: " << numExpected
1693  << " but got " << numParams;
1694 
1695  for (size_t i = 0; i != numExpected; ++i) {
1696  auto param = cast<circt::hw::ParamDeclAttr>(parameters[i]);
1697  auto modParam = cast<circt::hw::ParamDeclAttr>(modParameters[i]);
1698 
1699  auto paramName = param.getName();
1700  if (paramName != modParam.getName())
1701  return instance.emitOpError()
1702  << "parameter #" << i << " should have name " << modParam.getName()
1703  << " but has name " << paramName;
1704 
1705  if (param.getType() != modParam.getType())
1706  return instance.emitOpError()
1707  << "parameter " << paramName << " should have type "
1708  << modParam.getType() << " but has type " << param.getType();
1709 
1710  // All instance parameters must have a value. Specify the same value as
1711  // a module's default value if you want the default.
1712  if (!param.getValue())
1713  return instance.emitOpError("parameter ")
1714  << paramName << " must have a value";
1715  }
1716 
1717  for (size_t i = 0; i != numResults; ++i) {
1718  auto resultType = instance.getResult(i).getType();
1719  auto expectedType = primitivePorts[i].type;
1720  auto replacedType = hw::evaluateParametricType(
1721  instance.getLoc(), instance.getParametersAttr(), expectedType);
1722  if (failed(replacedType))
1723  return failure();
1724  if (resultType == replacedType)
1725  continue;
1726  return instance.emitOpError()
1727  << "result type for " << primitivePorts[i].name << " must be "
1728  << expectedType << ", but got " << resultType;
1729  }
1730  return success();
1731 }
1732 
1733 LogicalResult
1734 PrimitiveOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1735  Operation *op = *this;
1736  auto module = op->getParentOfType<ModuleOp>();
1737  Operation *referencedPrimitive =
1738  symbolTable.lookupNearestSymbolFrom(module, getPrimitiveNameAttr());
1739  if (referencedPrimitive == nullptr)
1740  return emitError() << "referencing primitive: '" << getPrimitiveName()
1741  << "', which does not exist.";
1742 
1743  Operation *shadowedPrimitiveName =
1744  symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1745  if (shadowedPrimitiveName != nullptr)
1746  return emitError() << "instance symbol: '" << instanceName()
1747  << "' is already a symbol for another primitive.";
1748 
1749  // Verify the referenced primitive is not instantiating itself.
1750  auto parentPrimitive = op->getParentOfType<hw::HWModuleExternOp>();
1751  if (parentPrimitive == referencedPrimitive)
1752  return emitError() << "recursive instantiation of its parent primitive: '"
1753  << getPrimitiveName() << "'";
1754 
1755  assert(isa<hw::HWModuleExternOp>(referencedPrimitive) &&
1756  "Should be a HardwareModuleExternOp.");
1757 
1758  return verifyPrimitiveOpType(*this,
1759  cast<hw::HWModuleExternOp>(referencedPrimitive));
1760 }
1761 
1762 /// Provide meaningful names to the result values of an PrimitiveOp.
1764  getCellAsmResultNames(setNameFn, *this, this->portNames());
1765 }
1766 
1767 SmallVector<StringRef> PrimitiveOp::portNames() {
1768  SmallVector<StringRef> portNames;
1769  auto ports = getReferencedPrimitive().getPortList();
1770  for (auto port : ports)
1771  portNames.push_back(port.name.getValue());
1772 
1773  return portNames;
1774 }
1775 
1777  switch (direction) {
1779  return Direction::Input;
1781  return Direction::Output;
1783  llvm_unreachable("InOut ports not supported by Calyx");
1784  }
1785  llvm_unreachable("Impossible port type");
1786 }
1787 
1788 SmallVector<Direction> PrimitiveOp::portDirections() {
1789  SmallVector<Direction> portDirections;
1790  auto ports = getReferencedPrimitive().getPortList();
1791  for (hw::PortInfo port : ports)
1792  portDirections.push_back(convertHWDirectionToCalyx(port.dir));
1793  return portDirections;
1794 }
1795 
1796 bool PrimitiveOp::isCombinational() { return false; }
1797 
1798 /// Returns a new DictionaryAttr containing only the calyx dialect attrs
1799 /// in the input DictionaryAttr. Also strips the 'calyx.' prefix from these
1800 /// attrs.
1801 static DictionaryAttr cleanCalyxPortAttrs(OpBuilder builder,
1802  DictionaryAttr dict) {
1803  if (!dict) {
1804  return dict;
1805  }
1806  llvm::SmallVector<NamedAttribute> attrs;
1807  for (NamedAttribute attr : dict) {
1808  Dialect *dialect = attr.getNameDialect();
1809  if (dialect == nullptr || !isa<CalyxDialect>(*dialect))
1810  continue;
1811  StringRef name = attr.getName().strref();
1812  StringAttr newName = builder.getStringAttr(std::get<1>(name.split(".")));
1813  attr.setName(newName);
1814  attrs.push_back(attr);
1815  }
1816  return builder.getDictionaryAttr(attrs);
1817 }
1818 
1819 // Grabs calyx port attributes from the HWModuleExternOp arg/result attributes.
1820 SmallVector<DictionaryAttr> PrimitiveOp::portAttributes() {
1821  SmallVector<DictionaryAttr> portAttributes;
1822  OpBuilder builder(getContext());
1823  hw::HWModuleExternOp prim = getReferencedPrimitive();
1824  auto argAttrs = prim.getAllInputAttrs();
1825  auto resAttrs = prim.getAllOutputAttrs();
1826  for (auto a : argAttrs)
1827  portAttributes.push_back(
1828  cleanCalyxPortAttrs(builder, cast_or_null<DictionaryAttr>(a)));
1829  for (auto a : resAttrs)
1830  portAttributes.push_back(
1831  cleanCalyxPortAttrs(builder, cast_or_null<DictionaryAttr>(a)));
1832  return portAttributes;
1833 }
1834 
1835 /// Parse an parameter list if present. Same format as HW dialect.
1836 /// module-parameter-list ::= `<` parameter-decl (`,` parameter-decl)* `>`
1837 /// parameter-decl ::= identifier `:` type
1838 /// parameter-decl ::= identifier `:` type `=` attribute
1839 ///
1840 static ParseResult parseParameterList(OpAsmParser &parser,
1841  SmallVector<Attribute> &parameters) {
1842 
1843  return parser.parseCommaSeparatedList(
1844  OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1845  std::string name;
1846  Type type;
1847  Attribute value;
1848 
1849  if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1850  return failure();
1851 
1852  // Parse the default value if present.
1853  if (succeeded(parser.parseOptionalEqual())) {
1854  if (parser.parseAttribute(value, type))
1855  return failure();
1856  }
1857 
1858  auto &builder = parser.getBuilder();
1859  parameters.push_back(hw::ParamDeclAttr::get(
1860  builder.getContext(), builder.getStringAttr(name), type, value));
1861  return success();
1862  });
1863 }
1864 
1865 /// Shim to also use this for the InstanceOp custom parser.
1866 static ParseResult parseParameterList(OpAsmParser &parser,
1867  ArrayAttr &parameters) {
1868  SmallVector<Attribute> parseParameters;
1869  if (failed(parseParameterList(parser, parseParameters)))
1870  return failure();
1871 
1872  parameters = ArrayAttr::get(parser.getContext(), parseParameters);
1873 
1874  return success();
1875 }
1876 
1877 /// Print a parameter list for a module or instance. Same format as HW dialect.
1878 static void printParameterList(OpAsmPrinter &p, Operation *op,
1879  ArrayAttr parameters) {
1880  if (parameters.empty())
1881  return;
1882 
1883  p << '<';
1884  llvm::interleaveComma(parameters, p, [&](Attribute param) {
1885  auto paramAttr = cast<hw::ParamDeclAttr>(param);
1886  p << paramAttr.getName().getValue() << ": " << paramAttr.getType();
1887  if (auto value = paramAttr.getValue()) {
1888  p << " = ";
1889  p.printAttributeWithoutType(value);
1890  }
1891  });
1892  p << '>';
1893 }
1894 
1895 //===----------------------------------------------------------------------===//
1896 // GroupGoOp
1897 //===----------------------------------------------------------------------===//
1898 
1899 LogicalResult GroupGoOp::verify() { return verifyNotComplexSource(*this); }
1900 
1901 /// Provide meaningful names to the result value of a GroupGoOp.
1902 void GroupGoOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1903  auto parent = (*this)->getParentOfType<GroupOp>();
1904  StringRef name = parent.getSymName();
1905  std::string resultName = name.str() + ".go";
1906  setNameFn(getResult(), resultName);
1907 }
1908 
1909 void GroupGoOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1910 
1911 ParseResult GroupGoOp::parse(OpAsmParser &parser, OperationState &result) {
1912  if (parseGroupPort(parser, result))
1913  return failure();
1914 
1915  result.addTypes(parser.getBuilder().getI1Type());
1916  return success();
1917 }
1918 
1919 //===----------------------------------------------------------------------===//
1920 // GroupDoneOp
1921 //===----------------------------------------------------------------------===//
1922 
1923 LogicalResult GroupDoneOp::verify() {
1924  Operation *srcOp = getSrc().getDefiningOp();
1925  Value optionalGuard = getGuard();
1926  Operation *guardOp = optionalGuard ? optionalGuard.getDefiningOp() : nullptr;
1927  bool noGuard = (guardOp == nullptr);
1928 
1929  if (srcOp == nullptr)
1930  // This is a port of the parent component.
1931  return success();
1932 
1933  if (isa<hw::ConstantOp>(srcOp) && (noGuard || isa<hw::ConstantOp>(guardOp)))
1934  return emitOpError() << "with constant source"
1935  << (noGuard ? "" : " and constant guard")
1936  << ". This should be a combinational group.";
1937 
1938  return verifyNotComplexSource(*this);
1939 }
1940 
1941 void GroupDoneOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1942 
1943 ParseResult GroupDoneOp::parse(OpAsmParser &parser, OperationState &result) {
1944  return parseGroupPort(parser, result);
1945 }
1946 
1947 //===----------------------------------------------------------------------===//
1948 // RegisterOp
1949 //===----------------------------------------------------------------------===//
1950 
1951 /// Provide meaningful names to the result values of a RegisterOp.
1952 void RegisterOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1953  getCellAsmResultNames(setNameFn, *this, this->portNames());
1954 }
1955 
1956 SmallVector<StringRef> RegisterOp::portNames() {
1957  return {"in", "write_en", clkPort, resetPort, "out", donePort};
1958 }
1959 
1960 SmallVector<Direction> RegisterOp::portDirections() {
1961  return {Input, Input, Input, Input, Output, Output};
1962 }
1963 
1964 SmallVector<DictionaryAttr> RegisterOp::portAttributes() {
1965  MLIRContext *context = getContext();
1966  IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
1967  NamedAttrList writeEn, clk, reset, done;
1968  writeEn.append(goPort, isSet);
1969  clk.append(clkPort, isSet);
1970  reset.append(resetPort, isSet);
1971  done.append(donePort, isSet);
1972  return {
1973  DictionaryAttr::get(context), // In
1974  writeEn.getDictionary(context), // Write enable
1975  clk.getDictionary(context), // Clk
1976  reset.getDictionary(context), // Reset
1977  DictionaryAttr::get(context), // Out
1978  done.getDictionary(context) // Done
1979  };
1980 }
1981 
1982 bool RegisterOp::isCombinational() { return false; }
1983 
1984 //===----------------------------------------------------------------------===//
1985 // MemoryOp
1986 //===----------------------------------------------------------------------===//
1987 
1988 /// Provide meaningful names to the result values of a MemoryOp.
1989 void MemoryOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1990  getCellAsmResultNames(setNameFn, *this, this->portNames());
1991 }
1992 
1993 SmallVector<StringRef> MemoryOp::portNames() {
1994  SmallVector<StringRef> portNames;
1995  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
1996  auto nameAttr =
1997  StringAttr::get(this->getContext(), "addr" + std::to_string(i));
1998  portNames.push_back(nameAttr.getValue());
1999  }
2000  portNames.append({"write_data", "write_en", clkPort, "read_data", donePort});
2001  return portNames;
2002 }
2003 
2004 SmallVector<Direction> MemoryOp::portDirections() {
2005  SmallVector<Direction> portDirections;
2006  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2007  portDirections.push_back(Input);
2008  portDirections.append({Input, Input, Input, Output, Output});
2009  return portDirections;
2010 }
2011 
2012 SmallVector<DictionaryAttr> MemoryOp::portAttributes() {
2013  SmallVector<DictionaryAttr> portAttributes;
2014  MLIRContext *context = getContext();
2015  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2016  portAttributes.push_back(DictionaryAttr::get(context)); // Addresses
2017 
2018  // Use a boolean to indicate this attribute is used.
2019  IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
2020  NamedAttrList writeEn, clk, reset, done;
2021  writeEn.append(goPort, isSet);
2022  clk.append(clkPort, isSet);
2023  done.append(donePort, isSet);
2024  portAttributes.append({DictionaryAttr::get(context), // In
2025  writeEn.getDictionary(context), // Write enable
2026  clk.getDictionary(context), // Clk
2027  DictionaryAttr::get(context), // Out
2028  done.getDictionary(context)} // Done
2029  );
2030  return portAttributes;
2031 }
2032 
2033 void MemoryOp::build(OpBuilder &builder, OperationState &state,
2034  StringRef instanceName, int64_t width,
2035  ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2036  state.addAttribute(SymbolTable::getSymbolAttrName(),
2037  builder.getStringAttr(instanceName));
2038  state.addAttribute("width", builder.getI64IntegerAttr(width));
2039  state.addAttribute("sizes", builder.getI64ArrayAttr(sizes));
2040  state.addAttribute("addrSizes", builder.getI64ArrayAttr(addrSizes));
2041  SmallVector<Type> types;
2042  for (int64_t size : addrSizes)
2043  types.push_back(builder.getIntegerType(size)); // Addresses
2044  types.push_back(builder.getIntegerType(width)); // Write data
2045  types.push_back(builder.getI1Type()); // Write enable
2046  types.push_back(builder.getI1Type()); // Clk
2047  types.push_back(builder.getIntegerType(width)); // Read data
2048  types.push_back(builder.getI1Type()); // Done
2049  state.addTypes(types);
2050 }
2051 
2052 LogicalResult MemoryOp::verify() {
2053  ArrayRef<Attribute> opSizes = getSizes().getValue();
2054  ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2055  size_t numDims = getSizes().size();
2056  size_t numAddrs = getAddrSizes().size();
2057  if (numDims != numAddrs)
2058  return emitOpError("mismatched number of dimensions (")
2059  << numDims << ") and address sizes (" << numAddrs << ")";
2060 
2061  size_t numExtraPorts = 5; // write data/enable, clk, and read data/done.
2062  if (getNumResults() != numAddrs + numExtraPorts)
2063  return emitOpError("incorrect number of address ports, expected ")
2064  << numAddrs;
2065 
2066  for (size_t i = 0; i < numDims; ++i) {
2067  int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2068  int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2069  if (llvm::Log2_64_Ceil(size) > addrSize)
2070  return emitOpError("address size (")
2071  << addrSize << ") for dimension " << i
2072  << " can't address the entire range (" << size << ")";
2073  }
2074 
2075  return success();
2076 }
2077 
2078 //===----------------------------------------------------------------------===//
2079 // SeqMemoryOp
2080 //===----------------------------------------------------------------------===//
2081 
2082 /// Provide meaningful names to the result values of a SeqMemoryOp.
2083 void SeqMemoryOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
2084  getCellAsmResultNames(setNameFn, *this, this->portNames());
2085 }
2086 
2087 SmallVector<StringRef> SeqMemoryOp::portNames() {
2088  SmallVector<StringRef> portNames;
2089  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2090  auto nameAttr =
2091  StringAttr::get(this->getContext(), "addr" + std::to_string(i));
2092  portNames.push_back(nameAttr.getValue());
2093  }
2094  portNames.append({clkPort, "reset", "content_en", "write_en", "write_data",
2095  "read_data", "done"});
2096  return portNames;
2097 }
2098 
2099 SmallVector<Direction> SeqMemoryOp::portDirections() {
2100  SmallVector<Direction> portDirections;
2101  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2102  portDirections.push_back(Input);
2103  portDirections.append({Input, Input, Input, Input, Input, Output, Output});
2104  return portDirections;
2105 }
2106 
2107 SmallVector<DictionaryAttr> SeqMemoryOp::portAttributes() {
2108  SmallVector<DictionaryAttr> portAttributes;
2109  MLIRContext *context = getContext();
2110  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2111  portAttributes.push_back(DictionaryAttr::get(context)); // Addresses
2112 
2113  OpBuilder builder(context);
2114  // Use a boolean to indicate this attribute is used.
2115  IntegerAttr isSet = IntegerAttr::get(builder.getIndexType(), 1);
2116  IntegerAttr isTwo = IntegerAttr::get(builder.getIndexType(), 2);
2117  NamedAttrList done, clk, reset, contentEn;
2118  done.append(donePort, isSet);
2119  clk.append(clkPort, isSet);
2120  clk.append(resetPort, isSet);
2121  contentEn.append(goPort, isTwo);
2122  portAttributes.append({clk.getDictionary(context), // Clk
2123  reset.getDictionary(context), // Reset
2124  contentEn.getDictionary(context), // Content enable
2125  DictionaryAttr::get(context), // Write enable
2126  DictionaryAttr::get(context), // Write data
2127  DictionaryAttr::get(context), // Read data
2128  done.getDictionary(context)} // Done
2129  );
2130  return portAttributes;
2131 }
2132 
2133 void SeqMemoryOp::build(OpBuilder &builder, OperationState &state,
2134  StringRef instanceName, int64_t width,
2135  ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2136  state.addAttribute(SymbolTable::getSymbolAttrName(),
2137  builder.getStringAttr(instanceName));
2138  state.addAttribute("width", builder.getI64IntegerAttr(width));
2139  state.addAttribute("sizes", builder.getI64ArrayAttr(sizes));
2140  state.addAttribute("addrSizes", builder.getI64ArrayAttr(addrSizes));
2141  SmallVector<Type> types;
2142  for (int64_t size : addrSizes)
2143  types.push_back(builder.getIntegerType(size)); // Addresses
2144  types.push_back(builder.getI1Type()); // Clk
2145  types.push_back(builder.getI1Type()); // Reset
2146  types.push_back(builder.getI1Type()); // Content enable
2147  types.push_back(builder.getI1Type()); // Write enable
2148  types.push_back(builder.getIntegerType(width)); // Write data
2149  types.push_back(builder.getIntegerType(width)); // Read data
2150  types.push_back(builder.getI1Type()); // Done
2151  state.addTypes(types);
2152 }
2153 
2154 LogicalResult SeqMemoryOp::verify() {
2155  ArrayRef<Attribute> opSizes = getSizes().getValue();
2156  ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2157  size_t numDims = getSizes().size();
2158  size_t numAddrs = getAddrSizes().size();
2159  if (numDims != numAddrs)
2160  return emitOpError("mismatched number of dimensions (")
2161  << numDims << ") and address sizes (" << numAddrs << ")";
2162 
2163  size_t numExtraPorts =
2164  7; // write data/enable, clk, reset, read data, content enable, and done.
2165  if (getNumResults() != numAddrs + numExtraPorts)
2166  return emitOpError("incorrect number of address ports, expected ")
2167  << numAddrs;
2168 
2169  for (size_t i = 0; i < numDims; ++i) {
2170  int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2171  int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2172  if (llvm::Log2_64_Ceil(size) > addrSize)
2173  return emitOpError("address size (")
2174  << addrSize << ") for dimension " << i
2175  << " can't address the entire range (" << size << ")";
2176  }
2177 
2178  return success();
2179 }
2180 
2181 //===----------------------------------------------------------------------===//
2182 // EnableOp
2183 //===----------------------------------------------------------------------===//
2184 LogicalResult EnableOp::verify() {
2185  auto component = (*this)->getParentOfType<ComponentOp>();
2186  auto wiresOp = component.getWiresOp();
2187  StringRef name = getGroupName();
2188 
2189  auto groupOp = wiresOp.lookupSymbol<GroupInterface>(name);
2190  if (!groupOp)
2191  return emitOpError() << "with group '" << name
2192  << "', which does not exist.";
2193 
2194  if (isa<CombGroupOp>(groupOp))
2195  return emitOpError() << "with group '" << name
2196  << "', which is a combinational group.";
2197 
2198  return success();
2199 }
2200 
2201 //===----------------------------------------------------------------------===//
2202 // IfOp
2203 //===----------------------------------------------------------------------===//
2204 
2205 LogicalResult IfOp::verify() {
2206  std::optional<StringRef> optGroupName = getGroupName();
2207  if (!optGroupName) {
2208  // No combinational group was provided.
2209  return success();
2210  }
2211  auto component = (*this)->getParentOfType<ComponentOp>();
2212  WiresOp wiresOp = component.getWiresOp();
2213  StringRef groupName = *optGroupName;
2214  auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2215  if (!groupOp)
2216  return emitOpError() << "with group '" << groupName
2217  << "', which does not exist.";
2218 
2219  if (isa<GroupOp>(groupOp))
2220  return emitOpError() << "with group '" << groupName
2221  << "', which is not a combinational group.";
2222 
2223  if (failed(groupOp.drivesPort(getCond())))
2224  return emitError() << "with conditional op: '"
2225  << valueName(component, getCond())
2226  << "' expected to be driven from group: '" << groupName
2227  << "' but no driver was found.";
2228 
2229  return success();
2230 }
2231 
2232 /// Returns the last EnableOp within the child tree of 'parentSeqOp' or
2233 /// `parentStaticSeqOp.` If no EnableOp was found (e.g. a "calyx.par" operation
2234 /// is present), returns None.
2235 template <typename OpTy>
2236 static std::optional<EnableOp> getLastEnableOp(OpTy parent) {
2237  static_assert(IsAny<OpTy, SeqOp, StaticSeqOp>(),
2238  "Should be a StaticSeqOp or SeqOp.");
2239  auto &lastOp = parent.getBodyBlock()->back();
2240  if (auto enableOp = dyn_cast<EnableOp>(lastOp))
2241  return enableOp;
2242  if (auto seqOp = dyn_cast<SeqOp>(lastOp))
2243  return getLastEnableOp(seqOp);
2244  if (auto staticSeqOp = dyn_cast<StaticSeqOp>(lastOp))
2245  return getLastEnableOp(staticSeqOp);
2246 
2247  return std::nullopt;
2248 }
2249 
2250 /// Returns a mapping of {enabled Group name, EnableOp} for all EnableOps within
2251 /// the immediate ParOp's body.
2252 template <typename OpTy>
2253 static llvm::StringMap<EnableOp> getAllEnableOpsInImmediateBody(OpTy parent) {
2254  static_assert(IsAny<OpTy, ParOp, StaticParOp>(),
2255  "Should be a StaticParOp or ParOp.");
2256 
2257  llvm::StringMap<EnableOp> enables;
2258  Block *body = parent.getBodyBlock();
2259  for (EnableOp op : body->getOps<EnableOp>())
2260  enables.insert(std::pair(op.getGroupName(), op));
2261 
2262  return enables;
2263 }
2264 
2265 /// Checks preconditions for the common tail pattern. This canonicalization is
2266 /// stringent about not entering nested control operations, as this may cause
2267 /// unintentional changes in behavior.
2268 /// We only look for two cases: (1) both regions are ParOps, and
2269 /// (2) both regions are SeqOps. The case when these are different, e.g. ParOp
2270 /// and SeqOp, will only produce less optimal code, or even worse, change the
2271 /// behavior.
2272 template <typename IfOpTy, typename TailOpTy>
2273 static bool hasCommonTailPatternPreConditions(IfOpTy op) {
2274  static_assert(IsAny<TailOpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
2275  "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp.");
2276  static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2277  "Should be a IfOp or StaticIfOp.");
2278 
2279  if (!op.thenBodyExists() || !op.elseBodyExists())
2280  return false;
2281  if (op.getThenBody()->empty() || op.getElseBody()->empty())
2282  return false;
2283 
2284  Block *thenBody = op.getThenBody(), *elseBody = op.getElseBody();
2285  return isa<TailOpTy>(thenBody->front()) && isa<TailOpTy>(elseBody->front());
2286 }
2287 
2288 /// seq {
2289 /// if %a with @G { if %a with @G {
2290 /// seq { ... calyx.enable @A } seq { ... }
2291 /// else { -> } else {
2292 /// seq { ... calyx.enable @A } seq { ... }
2293 /// } }
2294 /// calyx.enable @A
2295 /// }
2296 template <typename IfOpTy, typename SeqOpTy>
2297 static LogicalResult commonTailPatternWithSeq(IfOpTy ifOp,
2298  PatternRewriter &rewriter) {
2299  static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2300  "Should be an IfOp or StaticIfOp.");
2301  static_assert(IsAny<SeqOpTy, SeqOp, StaticSeqOp>(),
2302  "Branches should be checking for an SeqOp or StaticSeqOp");
2303  if (!hasCommonTailPatternPreConditions<IfOpTy, SeqOpTy>(ifOp))
2304  return failure();
2305  auto thenControl = cast<SeqOpTy>(ifOp.getThenBody()->front()),
2306  elseControl = cast<SeqOpTy>(ifOp.getElseBody()->front());
2307 
2308  std::optional<EnableOp> lastThenEnableOp = getLastEnableOp(thenControl),
2309  lastElseEnableOp = getLastEnableOp(elseControl);
2310 
2311  if (!lastThenEnableOp || !lastElseEnableOp)
2312  return failure();
2313  if (lastThenEnableOp->getGroupName() != lastElseEnableOp->getGroupName())
2314  return failure();
2315 
2316  // Place the IfOp and pulled EnableOp inside a sequential region, in case
2317  // this IfOp is nested in a ParOp. This avoids unintentionally
2318  // parallelizing the pulled out EnableOps.
2319  rewriter.setInsertionPointAfter(ifOp);
2320  SeqOpTy seqOp = rewriter.create<SeqOpTy>(ifOp.getLoc());
2321  Block *body = seqOp.getBodyBlock();
2322  ifOp->remove();
2323  body->push_back(ifOp);
2324  rewriter.setInsertionPointToEnd(body);
2325  rewriter.create<EnableOp>(seqOp.getLoc(), lastThenEnableOp->getGroupName());
2326 
2327  // Erase the common EnableOp from the Then and Else regions.
2328  rewriter.eraseOp(*lastThenEnableOp);
2329  rewriter.eraseOp(*lastElseEnableOp);
2330  return success();
2331 }
2332 
2333 /// if %a with @G { par {
2334 /// par { if %a with @G {
2335 /// ... par { ... }
2336 /// calyx.enable @A } else {
2337 /// calyx.enable @B -> par { ... }
2338 /// } }
2339 /// } else { calyx.enable @A
2340 /// par { calyx.enable @B
2341 /// ... }
2342 /// calyx.enable @A
2343 /// calyx.enable @B
2344 /// }
2345 /// }
2346 template <typename OpTy, typename ParOpTy>
2347 static LogicalResult commonTailPatternWithPar(OpTy controlOp,
2348  PatternRewriter &rewriter) {
2349  static_assert(IsAny<OpTy, IfOp, StaticIfOp>(),
2350  "Should be an IfOp or StaticIfOp.");
2351  static_assert(IsAny<ParOpTy, ParOp, StaticParOp>(),
2352  "Branches should be checking for an ParOp or StaticParOp");
2353  if (!hasCommonTailPatternPreConditions<OpTy, ParOpTy>(controlOp))
2354  return failure();
2355  auto thenControl = cast<ParOpTy>(controlOp.getThenBody()->front()),
2356  elseControl = cast<ParOpTy>(controlOp.getElseBody()->front());
2357 
2358  llvm::StringMap<EnableOp> a = getAllEnableOpsInImmediateBody(thenControl),
2359  b = getAllEnableOpsInImmediateBody(elseControl);
2360  // Compute the intersection between `A` and `B`.
2361  SmallVector<StringRef> groupNames;
2362  for (auto aIndex = a.begin(); aIndex != a.end(); ++aIndex) {
2363  StringRef groupName = aIndex->getKey();
2364  auto bIndex = b.find(groupName);
2365  if (bIndex == b.end())
2366  continue;
2367  // This is also an element in B.
2368  groupNames.push_back(groupName);
2369  // Since these are being pulled out, erase them.
2370  rewriter.eraseOp(aIndex->getValue());
2371  rewriter.eraseOp(bIndex->getValue());
2372  }
2373 
2374  // Place the IfOp and EnableOp(s) inside a parallel region, in case this
2375  // IfOp is nested in a SeqOp. This avoids unintentionally sequentializing
2376  // the pulled out EnableOps.
2377  rewriter.setInsertionPointAfter(controlOp);
2378 
2379  ParOpTy parOp = rewriter.create<ParOpTy>(controlOp.getLoc());
2380  Block *body = parOp.getBodyBlock();
2381  controlOp->remove();
2382  body->push_back(controlOp);
2383  // Pull out the intersection between these two sets, and erase their
2384  // counterparts in the Then and Else regions.
2385  rewriter.setInsertionPointToEnd(body);
2386  for (StringRef groupName : groupNames)
2387  rewriter.create<EnableOp>(parOp.getLoc(), groupName);
2388 
2389  return success();
2390 }
2391 
2392 /// This pattern checks for one of two cases that will lead to IfOp deletion:
2393 /// (1) Then and Else bodies are both empty.
2394 /// (2) Then body is empty and Else body does not exist.
2395 struct EmptyIfBody : mlir::OpRewritePattern<IfOp> {
2396  using mlir::OpRewritePattern<IfOp>::OpRewritePattern;
2397  LogicalResult matchAndRewrite(IfOp ifOp,
2398  PatternRewriter &rewriter) const override {
2399  if (!ifOp.getThenBody()->empty())
2400  return failure();
2401  if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2402  return failure();
2403 
2404  eraseControlWithGroupAndConditional(ifOp, rewriter);
2405 
2406  return success();
2407  }
2408 };
2409 
2410 void IfOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2411  MLIRContext *context) {
2412  patterns.add<EmptyIfBody>(context);
2413  patterns.add(commonTailPatternWithPar<IfOp, ParOp>);
2414  patterns.add(commonTailPatternWithSeq<IfOp, SeqOp>);
2415 }
2416 
2417 //===----------------------------------------------------------------------===//
2418 // StaticIfOp
2419 //===----------------------------------------------------------------------===//
2420 LogicalResult StaticIfOp::verify() {
2421  if (elseBodyExists()) {
2422  auto *elseBod = getElseBody();
2423  auto &elseOps = elseBod->getOperations();
2424  // should only have one Operation, static, in the else branch
2425  for (Operation &op : elseOps) {
2426  if (!isStaticControl(&op)) {
2427  return op.emitOpError(
2428  "static if's else branch has non static control within it");
2429  }
2430  }
2431  }
2432 
2433  auto *thenBod = getThenBody();
2434  auto &thenOps = thenBod->getOperations();
2435  for (Operation &op : thenOps) {
2436  // should only have one, static, Operation in the then branch
2437  if (!isStaticControl(&op)) {
2438  return op.emitOpError(
2439  "static if's then branch has non static control within it");
2440  }
2441  }
2442 
2443  return success();
2444 }
2445 
2446 /// This pattern checks for one of two cases that will lead to StaticIfOp
2447 /// deletion: (1) Then and Else bodies are both empty. (2) Then body is empty
2448 /// and Else body does not exist.
2449 struct EmptyStaticIfBody : mlir::OpRewritePattern<StaticIfOp> {
2450  using mlir::OpRewritePattern<StaticIfOp>::OpRewritePattern;
2451  LogicalResult matchAndRewrite(StaticIfOp ifOp,
2452  PatternRewriter &rewriter) const override {
2453  if (!ifOp.getThenBody()->empty())
2454  return failure();
2455  if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2456  return failure();
2457 
2458  eraseControlWithConditional(ifOp, rewriter);
2459 
2460  return success();
2461  }
2462 };
2463 
2464 void StaticIfOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2465  MLIRContext *context) {
2466  patterns.add<EmptyStaticIfBody>(context);
2467  patterns.add(commonTailPatternWithPar<StaticIfOp, StaticParOp>);
2468  patterns.add(commonTailPatternWithSeq<StaticIfOp, StaticSeqOp>);
2469 }
2470 
2471 //===----------------------------------------------------------------------===//
2472 // WhileOp
2473 //===----------------------------------------------------------------------===//
2474 LogicalResult WhileOp::verify() {
2475  auto component = (*this)->getParentOfType<ComponentOp>();
2476  auto wiresOp = component.getWiresOp();
2477 
2478  std::optional<StringRef> optGroupName = getGroupName();
2479  if (!optGroupName) {
2480  /// No combinational group was provided
2481  return success();
2482  }
2483  StringRef groupName = *optGroupName;
2484  auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2485  if (!groupOp)
2486  return emitOpError() << "with group '" << groupName
2487  << "', which does not exist.";
2488 
2489  if (isa<GroupOp>(groupOp))
2490  return emitOpError() << "with group '" << groupName
2491  << "', which is not a combinational group.";
2492 
2493  if (failed(groupOp.drivesPort(getCond())))
2494  return emitError() << "conditional op: '" << valueName(component, getCond())
2495  << "' expected to be driven from group: '" << groupName
2496  << "' but no driver was found.";
2497 
2498  return success();
2499 }
2500 
2501 LogicalResult WhileOp::canonicalize(WhileOp whileOp,
2502  PatternRewriter &rewriter) {
2503  if (whileOp.getBodyBlock()->empty()) {
2504  eraseControlWithGroupAndConditional(whileOp, rewriter);
2505  return success();
2506  }
2507 
2508  return failure();
2509 }
2510 
2511 //===----------------------------------------------------------------------===//
2512 // StaticRepeatOp
2513 //===----------------------------------------------------------------------===//
2514 LogicalResult StaticRepeatOp::verify() {
2515  for (auto &&bodyOp : (*this).getRegion().front()) {
2516  // there should only be one bodyOp for each StaticRepeatOp
2517  if (!isStaticControl(&bodyOp)) {
2518  return bodyOp.emitOpError(
2519  "static repeat has non static control within it");
2520  }
2521  }
2522 
2523  return success();
2524 }
2525 
2526 template <typename OpTy>
2527 static LogicalResult zeroRepeat(OpTy op, PatternRewriter &rewriter) {
2528  static_assert(IsAny<OpTy, RepeatOp, StaticRepeatOp>(),
2529  "Should be a RepeatOp or StaticPRepeatOp");
2530  if (op.getCount() == 0) {
2531  Block *controlBody = op.getBodyBlock();
2532  for (auto &op : make_early_inc_range(*controlBody))
2533  op.erase();
2534 
2535  rewriter.eraseOp(op);
2536  return success();
2537  }
2538 
2539  return failure();
2540 }
2541 
2542 void StaticRepeatOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2543  MLIRContext *context) {
2544  patterns.add(emptyControl<StaticRepeatOp>);
2545  patterns.add(zeroRepeat<StaticRepeatOp>);
2546 }
2547 
2548 //===----------------------------------------------------------------------===//
2549 // RepeatOp
2550 //===----------------------------------------------------------------------===//
2551 void RepeatOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2552  MLIRContext *context) {
2553  patterns.add(emptyControl<RepeatOp>);
2554  patterns.add(zeroRepeat<RepeatOp>);
2555 }
2556 
2557 //===----------------------------------------------------------------------===//
2558 // InvokeOp
2559 //===----------------------------------------------------------------------===//
2560 
2561 // Parse the parameter list of invoke.
2562 static ParseResult
2563 parseParameterList(OpAsmParser &parser, OperationState &result,
2564  SmallVectorImpl<OpAsmParser::UnresolvedOperand> &ports,
2565  SmallVectorImpl<OpAsmParser::UnresolvedOperand> &inputs,
2566  SmallVectorImpl<Attribute> &portNames,
2567  SmallVectorImpl<Attribute> &inputNames,
2568  SmallVectorImpl<Type> &types) {
2569  OpAsmParser::UnresolvedOperand port;
2570  OpAsmParser::UnresolvedOperand input;
2571  Type type;
2572  auto parseParameter = [&]() -> ParseResult {
2573  if (parser.parseOperand(port) || parser.parseEqual() ||
2574  parser.parseOperand(input))
2575  return failure();
2576  ports.push_back(port);
2577  portNames.push_back(StringAttr::get(parser.getContext(), port.name));
2578  inputs.push_back(input);
2579  inputNames.push_back(StringAttr::get(parser.getContext(), input.name));
2580  return success();
2581  };
2582  if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2583  parseParameter))
2584  return failure();
2585  if (parser.parseArrow())
2586  return failure();
2587  auto parseType = [&]() -> ParseResult {
2588  if (parser.parseType(type))
2589  return failure();
2590  types.push_back(type);
2591  return success();
2592  };
2593  return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2594  parseType);
2595 }
2596 
2597 ParseResult InvokeOp::parse(OpAsmParser &parser, OperationState &result) {
2598  StringAttr componentName;
2599  SmallVector<OpAsmParser::UnresolvedOperand, 4> ports;
2600  SmallVector<OpAsmParser::UnresolvedOperand, 4> inputs;
2601  SmallVector<Attribute> portNames;
2602  SmallVector<Attribute> inputNames;
2603  SmallVector<Type, 4> types;
2604  if (parser.parseSymbolName(componentName))
2605  return failure();
2606  FlatSymbolRefAttr callee = FlatSymbolRefAttr::get(componentName);
2607  SMLoc loc = parser.getCurrentLocation();
2608  result.addAttribute("callee", callee);
2609  if (parseParameterList(parser, result, ports, inputs, portNames, inputNames,
2610  types))
2611  return failure();
2612  if (parser.resolveOperands(ports, types, loc, result.operands))
2613  return failure();
2614  if (parser.resolveOperands(inputs, types, loc, result.operands))
2615  return failure();
2616  result.addAttribute("portNames",
2617  ArrayAttr::get(parser.getContext(), portNames));
2618  result.addAttribute("inputNames",
2619  ArrayAttr::get(parser.getContext(), inputNames));
2620  return success();
2621 }
2622 
2623 void InvokeOp::print(OpAsmPrinter &p) {
2624  p << " @" << getCallee() << "(";
2625  auto ports = getPorts();
2626  auto inputs = getInputs();
2627  llvm::interleaveComma(llvm::zip(ports, inputs), p, [&](auto arg) {
2628  p << std::get<0>(arg) << " = " << std::get<1>(arg);
2629  });
2630  p << ") -> (";
2631  llvm::interleaveComma(ports, p, [&](auto port) { p << port.getType(); });
2632  p << ")";
2633 }
2634 
2635 // Check the direction of one of the ports in one of the connections of an
2636 // InvokeOp.
2637 static LogicalResult verifyInvokeOpValue(InvokeOp &op, Value &value,
2638  bool isDestination) {
2639  if (isPort(value))
2640  return verifyPortDirection(op, value, isDestination);
2641  return success();
2642 }
2643 
2644 // Checks if the value comes from complex logic.
2645 static LogicalResult verifyComplexLogic(InvokeOp &op, Value &value) {
2646  // Refer to the above function verifyNotComplexSource for its role.
2647  Operation *operation = value.getDefiningOp();
2648  if (operation == nullptr)
2649  return success();
2650  if (auto *dialect = operation->getDialect(); isa<comb::CombDialect>(dialect))
2651  return failure();
2652  return success();
2653 }
2654 
2655 // Get the go port of the invoked component.
2656 Value InvokeOp::getInstGoValue() {
2657  ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2658  Operation *operation = componentOp.lookupSymbol(getCallee());
2659  Value ret = nullptr;
2660  llvm::TypeSwitch<Operation *>(operation)
2661  .Case<RegisterOp>([&](auto op) { ret = operation->getResult(1); })
2662  .Case<MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2663  RemSPipeLibOp, RemUPipeLibOp>(
2664  [&](auto op) { ret = operation->getResult(2); })
2665  .Case<InstanceOp>([&](auto op) {
2666  auto portInfo = op.getReferencedComponent().getPortInfo();
2667  for (auto [portInfo, res] :
2668  llvm::zip(portInfo, operation->getResults())) {
2669  if (portInfo.hasAttribute(goPort))
2670  ret = res;
2671  }
2672  })
2673  .Case<PrimitiveOp>([&](auto op) {
2674  auto moduleExternOp = op.getReferencedPrimitive();
2675  auto argAttrs = moduleExternOp.getAllInputAttrs();
2676  for (auto [attr, res] : llvm::zip(argAttrs, op.getResults())) {
2677  if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2678  if (!dictAttr.empty()) {
2679  if (dictAttr.begin()->getName().getValue() == "calyx.go")
2680  ret = res;
2681  }
2682  }
2683  }
2684  });
2685  return ret;
2686 }
2687 
2688 // Get the done port of the invoked component.
2689 Value InvokeOp::getInstDoneValue() {
2690  ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2691  Operation *operation = componentOp.lookupSymbol(getCallee());
2692  Value ret = nullptr;
2693  llvm::TypeSwitch<Operation *>(operation)
2694  .Case<RegisterOp, MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2695  RemSPipeLibOp, RemUPipeLibOp>([&](auto op) {
2696  size_t doneIdx = operation->getResults().size() - 1;
2697  ret = operation->getResult(doneIdx);
2698  })
2699  .Case<InstanceOp>([&](auto op) {
2700  InstanceOp instanceOp = cast<InstanceOp>(operation);
2701  auto portInfo = instanceOp.getReferencedComponent().getPortInfo();
2702  for (auto [portInfo, res] :
2703  llvm::zip(portInfo, operation->getResults())) {
2704  if (portInfo.hasAttribute(donePort))
2705  ret = res;
2706  }
2707  })
2708  .Case<PrimitiveOp>([&](auto op) {
2709  PrimitiveOp primOp = cast<PrimitiveOp>(operation);
2710  auto moduleExternOp = primOp.getReferencedPrimitive();
2711  auto resAttrs = moduleExternOp.getAllOutputAttrs();
2712  for (auto [attr, res] : llvm::zip(resAttrs, primOp.getResults())) {
2713  if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2714  if (!dictAttr.empty()) {
2715  if (dictAttr.begin()->getName().getValue() == "calyx.done")
2716  ret = res;
2717  }
2718  }
2719  }
2720  });
2721  return ret;
2722 }
2723 
2724 // A helper function that gets the number of go or done ports in
2725 // hw.module.extern.
2726 static size_t
2727 getHwModuleExtGoOrDonePortNumber(hw::HWModuleExternOp &moduleExternOp,
2728  bool isGo) {
2729  size_t ret = 0;
2730  std::string str = isGo ? "calyx.go" : "calyx.done";
2731  for (Attribute attr : moduleExternOp.getAllInputAttrs()) {
2732  if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2733  ret = llvm::count_if(dictAttr, [&](NamedAttribute iter) {
2734  return iter.getName().getValue() == str;
2735  });
2736  }
2737  }
2738  return ret;
2739 }
2740 
2741 LogicalResult InvokeOp::verify() {
2742  ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2743  StringRef callee = getCallee();
2744  Operation *operation = componentOp.lookupSymbol(callee);
2745  // The referenced symbol does not exist.
2746  if (!operation)
2747  return emitOpError() << "with instance '@" << callee
2748  << "', which does not exist.";
2749  // The argument list of invoke is empty.
2750  if (getInputs().empty())
2751  return emitOpError() << "'@" << callee
2752  << "' has zero input and output port connections; "
2753  "expected at least one.";
2754  size_t goPortNum = 0, donePortNum = 0;
2755  // They both have a go port and a done port, but the "go" port for
2756  // registers and memrey should be "write_en" port.
2757  llvm::TypeSwitch<Operation *>(operation)
2758  .Case<RegisterOp, DivSPipeLibOp, DivUPipeLibOp, MemoryOp, MultPipeLibOp,
2759  RemSPipeLibOp, RemUPipeLibOp>(
2760  [&](auto op) { goPortNum = 1, donePortNum = 1; })
2761  .Case<InstanceOp>([&](auto op) {
2762  auto portInfo = op.getReferencedComponent().getPortInfo();
2763  for (PortInfo info : portInfo) {
2764  if (info.hasAttribute(goPort))
2765  ++goPortNum;
2766  if (info.hasAttribute(donePort))
2767  ++donePortNum;
2768  }
2769  })
2770  .Case<PrimitiveOp>([&](auto op) {
2771  auto moduleExternOp = op.getReferencedPrimitive();
2772  // Get the number of go ports and done ports by their attrubutes.
2773  goPortNum = getHwModuleExtGoOrDonePortNumber(moduleExternOp, true);
2774  donePortNum = getHwModuleExtGoOrDonePortNumber(moduleExternOp, false);
2775  });
2776  // If the number of go ports and done ports is wrong.
2777  if (goPortNum != 1 && donePortNum != 1)
2778  return emitOpError()
2779  << "'@" << callee << "'"
2780  << " is a combinational component and cannot be invoked, which must "
2781  "have single go port and single done port.";
2782 
2783  auto ports = getPorts();
2784  auto inputs = getInputs();
2785  // We have verified earlier that the instance has a go and a done port.
2786  Value goValue = getInstGoValue();
2787  Value doneValue = getInstDoneValue();
2788  for (auto [port, input, portName, inputName] :
2789  llvm::zip(ports, inputs, getPortNames(), getInputNames())) {
2790  // Check the direction of these destination ports.
2791  // 'calyx.invoke' op '@r0' has input '%r.out', which is a source port. The
2792  // inputs are required to be destination ports.
2793  if (failed(verifyInvokeOpValue(*this, port, true)))
2794  return emitOpError() << "'@" << callee << "' has input '"
2795  << cast<StringAttr>(portName).getValue()
2796  << "', which is a source port. The inputs are "
2797  "required to be destination ports.";
2798  // The go port should not appear in the parameter list.
2799  if (port == goValue)
2800  return emitOpError() << "the go or write_en port of '@" << callee
2801  << "' cannot appear here.";
2802  // Check the direction of these source ports.
2803  if (failed(verifyInvokeOpValue(*this, input, false)))
2804  return emitOpError() << "'@" << callee << "' has output '"
2805  << cast<StringAttr>(inputName).getValue()
2806  << "', which is a destination port. The inputs are "
2807  "required to be source ports.";
2808  if (failed(verifyComplexLogic(*this, input)))
2809  return emitOpError() << "'@" << callee << "' has '"
2810  << cast<StringAttr>(inputName).getValue()
2811  << "', which is not a port or constant. Complex "
2812  "logic should be conducted in the guard.";
2813  if (input == doneValue)
2814  return emitOpError() << "the done port of '@" << callee
2815  << "' cannot appear here.";
2816  // Check if the connection uses the callee's port.
2817  if (port.getDefiningOp() != operation && input.getDefiningOp() != operation)
2818  return emitOpError() << "the connection "
2819  << cast<StringAttr>(portName).getValue() << " = "
2820  << cast<StringAttr>(inputName).getValue()
2821  << " is not defined as an input port of '@" << callee
2822  << "'.";
2823  }
2824  return success();
2825 }
2826 
2827 //===----------------------------------------------------------------------===//
2828 // Calyx library ops
2829 //===----------------------------------------------------------------------===//
2830 
2831 LogicalResult PadLibOp::verify() {
2832  unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2833  unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2834  if (inBits >= outBits)
2835  return emitOpError("expected input bits (")
2836  << inBits << ')' << " to be less than output bits (" << outBits
2837  << ')';
2838  return success();
2839 }
2840 
2841 LogicalResult SliceLibOp::verify() {
2842  unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2843  unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2844  if (inBits <= outBits)
2845  return emitOpError("expected input bits (")
2846  << inBits << ')' << " to be greater than output bits (" << outBits
2847  << ')';
2848  return success();
2849 }
2850 
2851 #define ImplBinPipeOpCellInterface(OpType, outName) \
2852  SmallVector<StringRef> OpType::portNames() { \
2853  return {clkPort, resetPort, goPort, "left", "right", outName, donePort}; \
2854  } \
2855  \
2856  SmallVector<Direction> OpType::portDirections() { \
2857  return {Input, Input, Input, Input, Input, Output, Output}; \
2858  } \
2859  \
2860  void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2861  getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2862  } \
2863  \
2864  SmallVector<DictionaryAttr> OpType::portAttributes() { \
2865  MLIRContext *context = getContext(); \
2866  IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1); \
2867  NamedAttrList go, clk, reset, done; \
2868  go.append(goPort, isSet); \
2869  clk.append(clkPort, isSet); \
2870  reset.append(resetPort, isSet); \
2871  done.append(donePort, isSet); \
2872  return { \
2873  clk.getDictionary(context), /* Clk */ \
2874  reset.getDictionary(context), /* Reset */ \
2875  go.getDictionary(context), /* Go */ \
2876  DictionaryAttr::get(context), /* Lhs */ \
2877  DictionaryAttr::get(context), /* Rhs */ \
2878  DictionaryAttr::get(context), /* Out */ \
2879  done.getDictionary(context) /* Done */ \
2880  }; \
2881  } \
2882  \
2883  bool OpType::isCombinational() { return false; }
2884 
2885 #define ImplUnaryOpCellInterface(OpType) \
2886  SmallVector<StringRef> OpType::portNames() { return {"in", "out"}; } \
2887  SmallVector<Direction> OpType::portDirections() { return {Input, Output}; } \
2888  SmallVector<DictionaryAttr> OpType::portAttributes() { \
2889  return {DictionaryAttr::get(getContext()), \
2890  DictionaryAttr::get(getContext())}; \
2891  } \
2892  bool OpType::isCombinational() { return true; } \
2893  void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2894  getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2895  }
2896 
2897 #define ImplBinOpCellInterface(OpType) \
2898  SmallVector<StringRef> OpType::portNames() { \
2899  return {"left", "right", "out"}; \
2900  } \
2901  SmallVector<Direction> OpType::portDirections() { \
2902  return {Input, Input, Output}; \
2903  } \
2904  void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2905  getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2906  } \
2907  bool OpType::isCombinational() { return true; } \
2908  SmallVector<DictionaryAttr> OpType::portAttributes() { \
2909  return {DictionaryAttr::get(getContext()), \
2910  DictionaryAttr::get(getContext()), \
2911  DictionaryAttr::get(getContext())}; \
2912  }
2913 
2914 // clang-format off
2915 ImplBinPipeOpCellInterface(MultPipeLibOp, "out")
2916 ImplBinPipeOpCellInterface(DivUPipeLibOp, "out_quotient")
2917 ImplBinPipeOpCellInterface(DivSPipeLibOp, "out_quotient")
2918 ImplBinPipeOpCellInterface(RemUPipeLibOp, "out_remainder")
2919 ImplBinPipeOpCellInterface(RemSPipeLibOp, "out_remainder")
2920 
2921 ImplUnaryOpCellInterface(PadLibOp)
2922 ImplUnaryOpCellInterface(SliceLibOp)
2923 ImplUnaryOpCellInterface(NotLibOp)
2924 ImplUnaryOpCellInterface(WireLibOp)
2925 ImplUnaryOpCellInterface(ExtSILibOp)
2926 
2927 ImplBinOpCellInterface(LtLibOp)
2928 ImplBinOpCellInterface(GtLibOp)
2929 ImplBinOpCellInterface(EqLibOp)
2930 ImplBinOpCellInterface(NeqLibOp)
2931 ImplBinOpCellInterface(GeLibOp)
2932 ImplBinOpCellInterface(LeLibOp)
2933 ImplBinOpCellInterface(SltLibOp)
2934 ImplBinOpCellInterface(SgtLibOp)
2935 ImplBinOpCellInterface(SeqLibOp)
2936 ImplBinOpCellInterface(SneqLibOp)
2937 ImplBinOpCellInterface(SgeLibOp)
2938 ImplBinOpCellInterface(SleLibOp)
2939 
2940 ImplBinOpCellInterface(AddLibOp)
2941 ImplBinOpCellInterface(SubLibOp)
2942 ImplBinOpCellInterface(ShruLibOp)
2943 ImplBinOpCellInterface(RshLibOp)
2944 ImplBinOpCellInterface(SrshLibOp)
2945 ImplBinOpCellInterface(LshLibOp)
2946 ImplBinOpCellInterface(AndLibOp)
2947 ImplBinOpCellInterface(OrLibOp)
2948 ImplBinOpCellInterface(XorLibOp)
2949 // clang-format on
2950 
2951 //===----------------------------------------------------------------------===//
2952 // TableGen generated logic.
2953 //===----------------------------------------------------------------------===//
2954 
2955 #include "circt/Dialect/Calyx/CalyxInterfaces.cpp.inc"
2956 
2957 // Provide the autogenerated implementation guts for the Op classes.
2958 #define GET_OP_CLASSES
2959 #include "circt/Dialect/Calyx/Calyx.cpp.inc"
assert(baseType &&"element must be base type")
static LogicalResult verifyPrimitiveOpType(PrimitiveOp instance, hw::HWModuleExternOp referencedPrimitive)
Verifies the port information in comparison with the referenced component of an instance.
Definition: CalyxOps.cpp:1665
static ParseResult parseComponentSignature(OpAsmParser &parser, OperationState &result, SmallVectorImpl< OpAsmParser::Argument > &ports, SmallVectorImpl< Type > &portTypes)
Parses the signature of a Calyx component.
Definition: CalyxOps.cpp:456
static LogicalResult verifyAssignOpValue(AssignOp op, bool isDestination)
Verifies the value of a given assignment operation.
Definition: CalyxOps.cpp:1458
static ParseResult parseParameterList(OpAsmParser &parser, SmallVector< Attribute > &parameters)
Parse an parameter list if present.
Definition: CalyxOps.cpp:1840
static SmallVector< PortInfo > getFilteredPorts(ComponentOp op, Pred p)
A helper function to return a filtered subset of a component's ports.
Definition: CalyxOps.cpp:678
static Op getControlOrWiresFrom(ComponentOp op)
This is a helper function that should only be used to get the WiresOp or ControlOp of a ComponentOp,...
Definition: CalyxOps.cpp:617
static LogicalResult verifyPrimitivePortDriving(AssignOp assign, GroupInterface group)
Verifies that certain ports of primitives are either driven or read together.
Definition: CalyxOps.cpp:1309
#define ImplBinPipeOpCellInterface(OpType, outName)
Definition: CalyxOps.cpp:2851
static bool portIsUsedInGroup(GroupInterface group, Value port, bool isDriven)
Determines whether the given port is used in the group.
Definition: CalyxOps.cpp:1199
static Value getBlockArgumentWithName(StringRef name, ComponentOp op)
Returns the Block argument with the given name from a ComponentOp.
Definition: CalyxOps.cpp:627
static ParseResult parsePortDefList(OpAsmParser &parser, OperationState &result, SmallVectorImpl< OpAsmParser::Argument > &ports, SmallVectorImpl< Type > &portTypes, SmallVectorImpl< NamedAttrList > &portAttrs)
Parses the ports of a Calyx component signature, and adds the corresponding port names to attrName.
Definition: CalyxOps.cpp:428
static std::string valueName(Operation *scopeOp, Value v)
Convenience function for getting the SSA name of v under the scope of operation scopeOp.
Definition: CalyxOps.cpp:120
static LogicalResult verifyNotComplexSource(Op op)
Verify that the value is not a "complex" value.
Definition: CalyxOps.cpp:103
static LogicalResult verifyInstanceOpType(InstanceOp instance, ComponentInterface referencedComponent)
Verifies the port information in comparison with the referenced component of an instance.
Definition: CalyxOps.cpp:1558
static LogicalResult collapseControl(OpTy controlOp, PatternRewriter &rewriter)
Definition: CalyxOps.cpp:311
Direction convertHWDirectionToCalyx(hw::ModulePort::Direction direction)
Definition: CalyxOps.cpp:1776
static LogicalResult anyPortsReadByGroup(GroupInterface group, ValueRange ports)
Checks whether any ports are read within the group.
Definition: CalyxOps.cpp:1288
static bool hasControlRegion(Operation *op)
Returns whether the given operation has a control region.
Definition: CalyxOps.cpp:149
static LogicalResult emptyControl(OpTy controlOp, PatternRewriter &rewriter)
Definition: CalyxOps.cpp:329
static LogicalResult verifyControlBody(Operation *op)
Verifies the body of a ControlLikeOp.
Definition: CalyxOps.cpp:168
static void eraseControlWithConditional(OpTy op, PatternRewriter &rewriter)
A helper function to check whether the conditional needs to be erased to maintain a valid state of a ...
Definition: CalyxOps.cpp:368
static void eraseControlWithGroupAndConditional(OpTy op, PatternRewriter &rewriter)
A helper function to check whether the conditional and group (if it exists) needs to be erased to mai...
Definition: CalyxOps.cpp:341
static ParseResult parseComponentInterface(OpAsmParser &parser, OperationState &result)
Definition: CalyxOps.cpp:502
static void printComponentInterface(OpAsmPrinter &p, ComponentInterface comp)
Definition: CalyxOps.cpp:386
static LogicalResult hasRequiredPorts(ComponentOp op)
Determines whether the given ComponentOp has all the required ports.
Definition: CalyxOps.cpp:703
static LogicalResult anyPortsDrivenByGroup(GroupInterface group, ValueRange ports)
Checks whether any ports are driven within the group.
Definition: CalyxOps.cpp:1268
static bool isPort(Value value)
Returns whether this value is either (1) a port on a ComponentOp or (2) a port on a cell interface.
Definition: CalyxOps.cpp:134
#define ImplBinOpCellInterface(OpType)
Definition: CalyxOps.cpp:2897
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
Definition: CalyxOps.cpp:539
static LogicalResult portDrivenByGroup(GroupInterface groupOp, Value port)
Checks whether port is driven from within groupOp.
Definition: CalyxOps.cpp:1221
static void buildComponentLike(OpBuilder &builder, OperationState &result, StringAttr name, ArrayRef< PortInfo > ports, bool combinational)
Definition: CalyxOps.cpp:547
static void getCellAsmResultNames(OpAsmSetValueNameFn setNameFn, Operation *op, ArrayRef< StringRef > portNames)
Gives each result of the cell a meaningful name in the form: <instance-name>.
Definition: CalyxOps.cpp:1411
static LogicalResult allPortsDrivenByGroup(GroupInterface group, ValueRange ports)
Checks whether all ports are driven within the group.
Definition: CalyxOps.cpp:1248
static LogicalResult verifyPortDirection(Operation *op, Value value, bool isDestination)
Determines whether the given direction is valid with the given inputs.
Definition: CalyxOps.cpp:1428
static LogicalResult isCombinational(Value value, GroupInterface group)
Verifies the defining operation of a value is combinational.
Definition: CalyxOps.cpp:1047
static bool isStaticControl(Operation *op)
Returns whether the given operation is a static control operator.
Definition: CalyxOps.cpp:155
static void printParameterList(OpAsmPrinter &p, Operation *op, ArrayAttr parameters)
Print a parameter list for a module or instance. Same format as HW dialect.
Definition: CalyxOps.cpp:1878
static DictionaryAttr cleanCalyxPortAttrs(OpBuilder builder, DictionaryAttr dict)
Returns a new DictionaryAttr containing only the calyx dialect attrs in the input DictionaryAttr.
Definition: CalyxOps.cpp:1801
static void printGroupPort(OpAsmPrinter &p, GroupPortType op)
Definition: CalyxOps.cpp:296
#define ImplUnaryOpCellInterface(OpType)
Definition: CalyxOps.cpp:2885
static ParseResult parseGroupPort(OpAsmParser &parser, OperationState &result)
Definition: CalyxOps.cpp:268
std::map< std::string, WriteChannelPort & > writePorts
static SmallVector< Block *, 8 > intersection(SmallVectorImpl< Block * > &v1, SmallVectorImpl< Block * > &v2)
Calculate intersection of two vectors, returns a new vector.
int32_t width
Definition: FIRRTL.cpp:36
@ Input
Definition: HW.h:35
@ Output
Definition: HW.h:35
@ InOut
Definition: HW.h:35
static InstancePath empty
static ParseResult parsePort(OpAsmParser &p, module_like_impl::PortParse &result)
Parse a single argument with the following syntax:
static Block * getBodyBlock(FModuleLike mod)
Signals that the following operation is combinational.
Definition: CalyxOps.h:55
static LogicalResult verify(Value clock, bool eventExists, mlir::Location loc)
Definition: SVOps.cpp:2443
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
IntegerAttr packAttribute(MLIRContext *context, size_t nIns, size_t nOuts)
Returns an IntegerAttr containing the packed representation of the direction counts.
Definition: CalyxOps.cpp:58
static constexpr std::string_view clkPort
Definition: CalyxOps.h:34
LogicalResult verifyComponent(Operation *op)
A helper function to verify each operation with the Ccomponent trait.
Definition: CalyxOps.cpp:202
static constexpr std::string_view donePort
Definition: CalyxOps.h:32
LogicalResult verifyControlLikeOp(Operation *op)
A helper function to verify each control-like operation has a valid parent and, if applicable,...
Definition: CalyxOps.cpp:218
LogicalResult verifyGroupInterface(Operation *op)
A helper function to verify each operation with the Group Interface trait.
Definition: CalyxOps.cpp:1389
LogicalResult verifyCell(Operation *op)
A helper function to verify each operation with the Cell trait.
Definition: CalyxOps.cpp:210
static constexpr std::string_view resetPort
Definition: CalyxOps.h:33
Direction
The direction of a Component or Cell port.
Definition: CalyxOps.h:72
LogicalResult verifyIf(Operation *op)
A helper function to verify each operation with the If trait.
Definition: CalyxOps.cpp:255
PortInfo getPortInfo(BlockArgument arg)
Returns port information for the block argument provided.
Definition: CalyxOps.cpp:141
static constexpr std::string_view goPort
Definition: CalyxOps.h:31
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
enum PEO uint32_t mlir::FailureOr< mlir::Type > evaluateParametricType(mlir::Location loc, mlir::ArrayAttr parameters, mlir::Type type, bool emitErrors=true)
Returns a resolved version of 'type' wherein any parameter reference has been evaluated based on the ...
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition: LLVM.h:182
This pattern collapses a calyx.seq or calyx.par operation when it contains exactly one calyx....
Definition: CalyxOps.cpp:77
LogicalResult matchAndRewrite(CtrlOp ctrlOp, PatternRewriter &rewriter) const override
Definition: CalyxOps.cpp:79
This holds information about the port for either a Component or Cell.
Definition: CalyxOps.h:85
Direction direction
Definition: CalyxOps.h:88
DictionaryAttr attributes
Definition: CalyxOps.h:89