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 value.isa<BlockArgument>() ||
137  (definingOp && isa<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 (!cond.isa<BlockArgument>() && 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 (!cond.isa<BlockArgument>() && 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 = portNames[i].cast<StringAttr>();
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{portNamesAttr[i].cast<StringAttr>(),
670  portTypes[i],
671  direction::get(portDirectionsAttr[i]),
672  portAttrs[i].cast<DictionaryAttr>()});
673  }
674  return results;
675 }
676 
677 /// A helper function to return a filtered subset of a component's ports.
678 template <typename Pred>
679 static SmallVector<PortInfo> getFilteredPorts(ComponentOp op, Pred p) {
680  SmallVector<PortInfo> ports = op.getPortInfo();
681  llvm::erase_if(ports, p);
682  return ports;
683 }
684 
685 SmallVector<PortInfo> ComponentOp::getInputPortInfo() {
686  return getFilteredPorts(
687  *this, [](const PortInfo &port) { return port.direction == Output; });
688 }
689 
690 SmallVector<PortInfo> ComponentOp::getOutputPortInfo() {
691  return getFilteredPorts(
692  *this, [](const PortInfo &port) { return port.direction == Input; });
693 }
694 
695 void ComponentOp::print(OpAsmPrinter &p) {
696  printComponentInterface<ComponentOp>(p, *this);
697 }
698 
699 ParseResult ComponentOp::parse(OpAsmParser &parser, OperationState &result) {
700  return parseComponentInterface<ComponentOp>(parser, result);
701 }
702 
703 /// Determines whether the given ComponentOp has all the required ports.
704 static LogicalResult hasRequiredPorts(ComponentOp op) {
705  // Get all identifiers from the component ports.
706  llvm::SmallVector<StringRef, 4> identifiers;
707  for (PortInfo &port : op.getPortInfo()) {
708  auto portIds = port.getAllIdentifiers();
709  identifiers.append(portIds.begin(), portIds.end());
710  }
711  // Sort the identifiers: a pre-condition for std::set_intersection.
712  std::sort(identifiers.begin(), identifiers.end());
713 
714  llvm::SmallVector<StringRef, 4> intersection,
715  interfacePorts{clkPort, donePort, goPort, resetPort};
716  // Find the intersection between all identifiers and required ports.
717  std::set_intersection(interfacePorts.begin(), interfacePorts.end(),
718  identifiers.begin(), identifiers.end(),
719  std::back_inserter(intersection));
720 
721  if (intersection.size() == interfacePorts.size())
722  return success();
723 
724  SmallVector<StringRef, 4> difference;
725  std::set_difference(interfacePorts.begin(), interfacePorts.end(),
726  intersection.begin(), intersection.end(),
727  std::back_inserter(difference));
728  return op->emitOpError()
729  << "is missing the following required port attribute identifiers: "
730  << difference;
731 }
732 
733 LogicalResult ComponentOp::verify() {
734  // Verify there is exactly one of each the wires and control operations.
735  auto wIt = getBodyBlock()->getOps<WiresOp>();
736  auto cIt = getBodyBlock()->getOps<ControlOp>();
737  if (std::distance(wIt.begin(), wIt.end()) +
738  std::distance(cIt.begin(), cIt.end()) !=
739  2)
740  return emitOpError() << "requires exactly one of each: '"
741  << WiresOp::getOperationName() << "', '"
742  << ControlOp::getOperationName() << "'.";
743 
744  if (failed(hasRequiredPorts(*this)))
745  return failure();
746 
747  // Verify the component actually does something: has a non-empty Control
748  // region, or continuous assignments.
749  bool hasNoControlConstructs =
750  getControlOp().getBodyBlock()->getOperations().empty();
751  bool hasNoAssignments =
752  getWiresOp().getBodyBlock()->getOps<AssignOp>().empty();
753  if (hasNoControlConstructs && hasNoAssignments)
754  return emitOpError(
755  "The component currently does nothing. It needs to either have "
756  "continuous assignments in the Wires region or control constructs in "
757  "the Control region.");
758 
759  return success();
760 }
761 
762 void ComponentOp::build(OpBuilder &builder, OperationState &result,
763  StringAttr name, ArrayRef<PortInfo> ports) {
764  buildComponentLike(builder, result, name, ports, /*combinational=*/false);
765 }
766 
767 void ComponentOp::getAsmBlockArgumentNames(
768  mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
769  if (region.empty())
770  return;
771  auto ports = getPortNames();
772  auto *block = &getRegion()->front();
773  for (size_t i = 0, e = block->getNumArguments(); i != e; ++i)
774  setNameFn(block->getArgument(i), ports[i].cast<StringAttr>().getValue());
775 }
776 
777 //===----------------------------------------------------------------------===//
778 // CombComponentOp
779 //===----------------------------------------------------------------------===//
780 
781 SmallVector<PortInfo> CombComponentOp::getPortInfo() {
782  auto portTypes = getArgumentTypes();
783  ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
784  APInt portDirectionsAttr = getPortDirections();
785 
786  SmallVector<PortInfo> results;
787  for (size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
788  results.push_back(PortInfo{portNamesAttr[i].cast<StringAttr>(),
789  portTypes[i],
790  direction::get(portDirectionsAttr[i]),
791  portAttrs[i].cast<DictionaryAttr>()});
792  }
793  return results;
794 }
795 
796 WiresOp calyx::CombComponentOp::getWiresOp() {
797  auto *body = getBodyBlock();
798  auto opIt = body->getOps<WiresOp>().begin();
799  return *opIt;
800 }
801 
802 /// A helper function to return a filtered subset of a comb component's ports.
803 template <typename Pred>
804 static SmallVector<PortInfo> getFilteredPorts(CombComponentOp op, Pred p) {
805  SmallVector<PortInfo> ports = op.getPortInfo();
806  llvm::erase_if(ports, p);
807  return ports;
808 }
809 
810 SmallVector<PortInfo> CombComponentOp::getInputPortInfo() {
811  return getFilteredPorts(
812  *this, [](const PortInfo &port) { return port.direction == Output; });
813 }
814 
815 SmallVector<PortInfo> CombComponentOp::getOutputPortInfo() {
816  return getFilteredPorts(
817  *this, [](const PortInfo &port) { return port.direction == Input; });
818 }
819 
820 void CombComponentOp::print(OpAsmPrinter &p) {
821  printComponentInterface<CombComponentOp>(p, *this);
822 }
823 
824 ParseResult CombComponentOp::parse(OpAsmParser &parser,
825  OperationState &result) {
826  return parseComponentInterface<CombComponentOp>(parser, result);
827 }
828 
829 LogicalResult CombComponentOp::verify() {
830  // Verify there is exactly one wires operation.
831  auto wIt = getBodyBlock()->getOps<WiresOp>();
832  if (std::distance(wIt.begin(), wIt.end()) != 1)
833  return emitOpError() << "requires exactly one "
834  << WiresOp::getOperationName() << " op.";
835 
836  // Verify there is not a control operation.
837  auto cIt = getBodyBlock()->getOps<ControlOp>();
838  if (std::distance(cIt.begin(), cIt.end()) != 0)
839  return emitOpError() << "must not have a `" << ControlOp::getOperationName()
840  << "` op.";
841 
842  // Verify the component actually does something: has continuous assignments.
843  bool hasNoAssignments =
844  getWiresOp().getBodyBlock()->getOps<AssignOp>().empty();
845  if (hasNoAssignments)
846  return emitOpError(
847  "The component currently does nothing. It needs to either have "
848  "continuous assignments in the Wires region or control constructs in "
849  "the Control region.");
850 
851  // Check that all cells are combinational
852  auto cells = getOps<CellInterface>();
853  for (auto cell : cells) {
854  if (!cell.isCombinational())
855  return emitOpError() << "contains non-combinational cell "
856  << cell.instanceName();
857  }
858 
859  // Check that the component has no groups
860  auto groups = getWiresOp().getOps<GroupOp>();
861  if (!groups.empty())
862  return emitOpError() << "contains group " << (*groups.begin()).getSymName();
863 
864  // Combinational groups aren't allowed in combinational components either.
865  // For more information see here:
866  // https://docs.calyxir.org/lang/ref.html#comb-group-definitions
867  auto combGroups = getWiresOp().getOps<CombGroupOp>();
868  if (!combGroups.empty())
869  return emitOpError() << "contains comb group "
870  << (*combGroups.begin()).getSymName();
871 
872  return success();
873 }
874 
875 void CombComponentOp::build(OpBuilder &builder, OperationState &result,
876  StringAttr name, ArrayRef<PortInfo> ports) {
877  buildComponentLike(builder, result, name, ports, /*combinational=*/true);
878 }
879 
880 void CombComponentOp::getAsmBlockArgumentNames(
881  mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
882  if (region.empty())
883  return;
884  auto ports = getPortNames();
885  auto *block = &getRegion()->front();
886  for (size_t i = 0, e = block->getNumArguments(); i != e; ++i)
887  setNameFn(block->getArgument(i), ports[i].cast<StringAttr>().getValue());
888 }
889 
890 //===----------------------------------------------------------------------===//
891 // ControlOp
892 //===----------------------------------------------------------------------===//
893 LogicalResult ControlOp::verify() { return verifyControlBody(*this); }
894 
895 // Get the InvokeOps of this ControlOp.
896 SmallVector<InvokeOp, 4> ControlOp::getInvokeOps() {
897  SmallVector<InvokeOp, 4> ret;
898  this->walk([&](InvokeOp invokeOp) { ret.push_back(invokeOp); });
899  return ret;
900 }
901 
902 //===----------------------------------------------------------------------===//
903 // SeqOp
904 //===----------------------------------------------------------------------===//
905 
906 void SeqOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
907  MLIRContext *context) {
908  patterns.add(collapseControl<SeqOp>);
909  patterns.add(emptyControl<SeqOp>);
910  patterns.insert<CollapseUnaryControl<SeqOp>>(context);
911 }
912 
913 //===----------------------------------------------------------------------===//
914 // StaticSeqOp
915 //===----------------------------------------------------------------------===//
916 
917 LogicalResult StaticSeqOp::verify() {
918  // StaticSeqOp should only have static control in it
919  auto &ops = (*this).getBodyBlock()->getOperations();
920  if (!llvm::all_of(ops, [&](Operation &op) { return isStaticControl(&op); })) {
921  return emitOpError("StaticSeqOp has non static control within it");
922  }
923 
924  return success();
925 }
926 
927 void StaticSeqOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
928  MLIRContext *context) {
929  patterns.add(collapseControl<StaticSeqOp>);
930  patterns.add(emptyControl<StaticSeqOp>);
932 }
933 
934 //===----------------------------------------------------------------------===//
935 // ParOp
936 //===----------------------------------------------------------------------===//
937 
938 LogicalResult ParOp::verify() {
939  llvm::SmallSet<StringRef, 8> groupNames;
940 
941  // Add loose requirement that the body of a ParOp may not enable the same
942  // Group more than once, e.g. calyx.par { calyx.enable @G calyx.enable @G }
943  for (EnableOp op : getBodyBlock()->getOps<EnableOp>()) {
944  StringRef groupName = op.getGroupName();
945  if (groupNames.count(groupName))
946  return emitOpError() << "cannot enable the same group: \"" << groupName
947  << "\" more than once.";
948  groupNames.insert(groupName);
949  }
950 
951  return success();
952 }
953 
954 void ParOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
955  MLIRContext *context) {
956  patterns.add(collapseControl<ParOp>);
957  patterns.add(emptyControl<ParOp>);
958  patterns.insert<CollapseUnaryControl<ParOp>>(context);
959 }
960 
961 //===----------------------------------------------------------------------===//
962 // StaticParOp
963 //===----------------------------------------------------------------------===//
964 
965 LogicalResult StaticParOp::verify() {
966  llvm::SmallSet<StringRef, 8> groupNames;
967 
968  // Add loose requirement that the body of a ParOp may not enable the same
969  // Group more than once, e.g. calyx.par { calyx.enable @G calyx.enable @G }
970  for (EnableOp op : getBodyBlock()->getOps<EnableOp>()) {
971  StringRef groupName = op.getGroupName();
972  if (groupNames.count(groupName))
973  return emitOpError() << "cannot enable the same group: \"" << groupName
974  << "\" more than once.";
975  groupNames.insert(groupName);
976  }
977 
978  // static par must only have static control in it
979  auto &ops = (*this).getBodyBlock()->getOperations();
980  for (Operation &op : ops) {
981  if (!isStaticControl(&op)) {
982  return op.emitOpError("StaticParOp has non static control within it");
983  }
984  }
985 
986  return success();
987 }
988 
989 void StaticParOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
990  MLIRContext *context) {
991  patterns.add(collapseControl<StaticParOp>);
992  patterns.add(emptyControl<StaticParOp>);
994 }
995 
996 //===----------------------------------------------------------------------===//
997 // WiresOp
998 //===----------------------------------------------------------------------===//
999 LogicalResult WiresOp::verify() {
1000  auto componentInterface = (*this)->getParentOfType<ComponentInterface>();
1001  if (llvm::isa<ComponentOp>(componentInterface)) {
1002  auto component = llvm::cast<ComponentOp>(componentInterface);
1003  auto control = component.getControlOp();
1004 
1005  // Verify each group is referenced in the control section.
1006  for (auto &&op : *getBodyBlock()) {
1007  if (!isa<GroupInterface>(op))
1008  continue;
1009  auto group = cast<GroupInterface>(op);
1010  auto groupName = group.symName();
1011  if (mlir::SymbolTable::symbolKnownUseEmpty(groupName, control))
1012  return op.emitOpError()
1013  << "with name: " << groupName
1014  << " is unused in the control execution schedule";
1015  }
1016  }
1017 
1018  // Verify that:
1019  // - At most one continuous assignment exists for any given value
1020  // - A continuously assigned wire has no assignments inside groups.
1021  for (auto thisAssignment : getBodyBlock()->getOps<AssignOp>()) {
1022  // Always assume guarded assignments will not be driven simultaneously. We
1023  // liberally assume that guards are mutually exclusive (more elaborate
1024  // static and dynamic checking can be performed to validate such cases).
1025  if (thisAssignment.getGuard())
1026  continue;
1027 
1028  Value dest = thisAssignment.getDest();
1029  for (Operation *user : dest.getUsers()) {
1030  auto assignUser = dyn_cast<AssignOp>(user);
1031  if (!assignUser || assignUser.getDest() != dest ||
1032  assignUser == thisAssignment)
1033  continue;
1034 
1035  return user->emitOpError() << "destination is already continuously "
1036  "driven. Other assignment is "
1037  << thisAssignment;
1038  }
1039  }
1040 
1041  return success();
1042 }
1043 
1044 //===----------------------------------------------------------------------===//
1045 // CombGroupOp
1046 //===----------------------------------------------------------------------===//
1047 
1048 /// Verifies the defining operation of a value is combinational.
1049 static LogicalResult isCombinational(Value value, GroupInterface group) {
1050  Operation *definingOp = value.getDefiningOp();
1051  if (definingOp == nullptr || definingOp->hasTrait<Combinational>())
1052  // This is a port of the parent component or combinational.
1053  return success();
1054 
1055  // For now, assumes all component instances are combinational. Once
1056  // combinational components are supported, this can be strictly enforced.
1057  if (isa<InstanceOp>(definingOp))
1058  return success();
1059 
1060  // Constants and logical operations are OK.
1061  if (isa<comb::CombDialect, hw::HWDialect>(definingOp->getDialect()))
1062  return success();
1063 
1064  // Reads to MemoryOp and RegisterOp are combinational. Writes are not.
1065  if (auto r = dyn_cast<RegisterOp>(definingOp)) {
1066  return value == r.getOut()
1067  ? success()
1068  : group->emitOpError()
1069  << "with register: \"" << r.instanceName()
1070  << "\" is conducting a memory store. This is not "
1071  "combinational.";
1072  } else if (auto m = dyn_cast<MemoryOp>(definingOp)) {
1073  auto writePorts = {m.writeData(), m.writeEn()};
1074  return (llvm::none_of(writePorts, [&](Value p) { return p == value; }))
1075  ? success()
1076  : group->emitOpError()
1077  << "with memory: \"" << m.instanceName()
1078  << "\" is conducting a memory store. This "
1079  "is not combinational.";
1080  }
1081 
1082  std::string portName =
1083  valueName(group->getParentOfType<ComponentOp>(), value);
1084  return group->emitOpError() << "with port: " << portName
1085  << ". This operation is not combinational.";
1086 }
1087 
1088 /// Verifies a combinational group may contain only combinational primitives or
1089 /// perform combinational logic.
1090 LogicalResult CombGroupOp::verify() {
1091  for (auto &&op : *getBodyBlock()) {
1092  auto assign = dyn_cast<AssignOp>(op);
1093  if (assign == nullptr)
1094  continue;
1095  Value dst = assign.getDest(), src = assign.getSrc();
1096  if (failed(isCombinational(dst, *this)) ||
1097  failed(isCombinational(src, *this)))
1098  return failure();
1099  }
1100  return success();
1101 }
1102 
1103 //===----------------------------------------------------------------------===//
1104 // GroupGoOp
1105 //===----------------------------------------------------------------------===//
1106 GroupGoOp GroupOp::getGoOp() {
1107  auto goOps = getBodyBlock()->getOps<GroupGoOp>();
1108  size_t nOps = std::distance(goOps.begin(), goOps.end());
1109  return nOps ? *goOps.begin() : GroupGoOp();
1110 }
1111 
1112 GroupDoneOp GroupOp::getDoneOp() {
1113  auto body = this->getBodyBlock();
1114  return cast<GroupDoneOp>(body->getTerminator());
1115 }
1116 
1117 //===----------------------------------------------------------------------===//
1118 // CycleOp
1119 //===----------------------------------------------------------------------===//
1120 void CycleOp::print(OpAsmPrinter &p) {
1121  p << " ";
1122  // The guard is optional.
1123  auto start = this->getStart();
1124  auto end = this->getEnd();
1125  if (end.has_value()) {
1126  p << "[" << start << ":" << end.value() << "]";
1127  } else {
1128  p << start;
1129  }
1130 }
1131 
1132 ParseResult CycleOp::parse(OpAsmParser &parser, OperationState &result) {
1133  SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
1134 
1135  uint32_t startLiteral;
1136  uint32_t endLiteral;
1137 
1138  auto hasEnd = succeeded(parser.parseOptionalLSquare());
1139 
1140  if (parser.parseInteger(startLiteral)) {
1141  parser.emitError(parser.getNameLoc(), "Could not parse start cycle");
1142  return failure();
1143  }
1144 
1145  auto start = parser.getBuilder().getI32IntegerAttr(startLiteral);
1146  result.addAttribute(getStartAttrName(result.name), start);
1147 
1148  if (hasEnd) {
1149  if (parser.parseColon())
1150  return failure();
1151 
1152  if (auto res = parser.parseOptionalInteger(endLiteral); res.has_value()) {
1153  auto end = parser.getBuilder().getI32IntegerAttr(endLiteral);
1154  result.addAttribute(getEndAttrName(result.name), end);
1155  }
1156 
1157  if (parser.parseRSquare())
1158  return failure();
1159  }
1160 
1161  result.addTypes(parser.getBuilder().getI1Type());
1162 
1163  return success();
1164 }
1165 
1166 LogicalResult CycleOp::verify() {
1167  uint32_t latency = this->getGroupLatency();
1168 
1169  if (this->getStart() >= latency) {
1170  emitOpError("start cycle must be less than the group latency");
1171  return failure();
1172  }
1173 
1174  if (this->getEnd().has_value()) {
1175  if (this->getStart() >= this->getEnd().value()) {
1176  emitOpError("start cycle must be less than end cycle");
1177  return failure();
1178  }
1179 
1180  if (this->getEnd() >= latency) {
1181  emitOpError("end cycle must be less than the group latency");
1182  return failure();
1183  }
1184  }
1185 
1186  return success();
1187 }
1188 
1189 uint32_t CycleOp::getGroupLatency() {
1190  auto group = (*this)->getParentOfType<StaticGroupOp>();
1191  return group.getLatency();
1192 }
1193 
1194 //===----------------------------------------------------------------------===//
1195 // GroupInterface
1196 //===----------------------------------------------------------------------===//
1197 
1198 /// Determines whether the given port is used in the group. Its use depends on
1199 /// the `isDriven` value; if true, then the port should be a destination in an
1200 /// AssignOp. Otherwise, it should be the source, i.e. a read.
1201 static bool portIsUsedInGroup(GroupInterface group, Value port, bool isDriven) {
1202  return llvm::any_of(port.getUses(), [&](auto &&use) {
1203  auto assignOp = dyn_cast<AssignOp>(use.getOwner());
1204  if (assignOp == nullptr)
1205  return false;
1206 
1207  Operation *parent = assignOp->getParentOp();
1208  if (isa<WiresOp>(parent))
1209  // This is a continuous assignment.
1210  return false;
1211 
1212  // A port is used if it meet the criteria:
1213  // (1) it is a {source, destination} of an assignment.
1214  // (2) that assignment is found in the provided group.
1215 
1216  // If not driven, then read.
1217  Value expected = isDriven ? assignOp.getDest() : assignOp.getSrc();
1218  return expected == port && group == parent;
1219  });
1220 }
1221 
1222 /// Checks whether `port` is driven from within `groupOp`.
1223 static LogicalResult portDrivenByGroup(GroupInterface groupOp, Value port) {
1224  // Check if the port is driven by an assignOp from within `groupOp`.
1225  if (portIsUsedInGroup(groupOp, port, /*isDriven=*/true))
1226  return success();
1227 
1228  // If `port` is an output of a cell then we conservatively enforce that at
1229  // least one input port of the cell must be driven by the group.
1230  if (auto cell = dyn_cast<CellInterface>(port.getDefiningOp());
1231  cell && cell.direction(port) == calyx::Direction::Output)
1232  return groupOp.drivesAnyPort(cell.getInputPorts());
1233 
1234  return failure();
1235 }
1236 
1237 LogicalResult GroupOp::drivesPort(Value port) {
1238  return portDrivenByGroup(*this, port);
1239 }
1240 
1241 LogicalResult CombGroupOp::drivesPort(Value port) {
1242  return portDrivenByGroup(*this, port);
1243 }
1244 
1245 LogicalResult StaticGroupOp::drivesPort(Value port) {
1246  return portDrivenByGroup(*this, port);
1247 }
1248 
1249 /// Checks whether all ports are driven within the group.
1250 static LogicalResult allPortsDrivenByGroup(GroupInterface group,
1251  ValueRange ports) {
1252  return success(llvm::all_of(ports, [&](Value port) {
1253  return portIsUsedInGroup(group, port, /*isDriven=*/true);
1254  }));
1255 }
1256 
1257 LogicalResult GroupOp::drivesAllPorts(ValueRange ports) {
1258  return allPortsDrivenByGroup(*this, ports);
1259 }
1260 
1261 LogicalResult CombGroupOp::drivesAllPorts(ValueRange ports) {
1262  return allPortsDrivenByGroup(*this, ports);
1263 }
1264 
1265 LogicalResult StaticGroupOp::drivesAllPorts(ValueRange ports) {
1266  return allPortsDrivenByGroup(*this, ports);
1267 }
1268 
1269 /// Checks whether any ports are driven within the group.
1270 static LogicalResult anyPortsDrivenByGroup(GroupInterface group,
1271  ValueRange ports) {
1272  return success(llvm::any_of(ports, [&](Value port) {
1273  return portIsUsedInGroup(group, port, /*isDriven=*/true);
1274  }));
1275 }
1276 
1277 LogicalResult GroupOp::drivesAnyPort(ValueRange ports) {
1278  return anyPortsDrivenByGroup(*this, ports);
1279 }
1280 
1281 LogicalResult CombGroupOp::drivesAnyPort(ValueRange ports) {
1282  return anyPortsDrivenByGroup(*this, ports);
1283 }
1284 
1285 LogicalResult StaticGroupOp::drivesAnyPort(ValueRange ports) {
1286  return anyPortsDrivenByGroup(*this, ports);
1287 }
1288 
1289 /// Checks whether any ports are read within the group.
1290 static LogicalResult anyPortsReadByGroup(GroupInterface group,
1291  ValueRange ports) {
1292  return success(llvm::any_of(ports, [&](Value port) {
1293  return portIsUsedInGroup(group, port, /*isDriven=*/false);
1294  }));
1295 }
1296 
1297 LogicalResult GroupOp::readsAnyPort(ValueRange ports) {
1298  return anyPortsReadByGroup(*this, ports);
1299 }
1300 
1301 LogicalResult CombGroupOp::readsAnyPort(ValueRange ports) {
1302  return anyPortsReadByGroup(*this, ports);
1303 }
1304 
1305 LogicalResult StaticGroupOp::readsAnyPort(ValueRange ports) {
1306  return anyPortsReadByGroup(*this, ports);
1307 }
1308 
1309 /// Verifies that certain ports of primitives are either driven or read
1310 /// together.
1311 static LogicalResult verifyPrimitivePortDriving(AssignOp assign,
1312  GroupInterface group) {
1313  Operation *destDefiningOp = assign.getDest().getDefiningOp();
1314  if (destDefiningOp == nullptr)
1315  return success();
1316  auto destCell = dyn_cast<CellInterface>(destDefiningOp);
1317  if (destCell == nullptr)
1318  return success();
1319 
1320  LogicalResult verifyWrites =
1321  TypeSwitch<Operation *, LogicalResult>(destCell)
1322  .Case<RegisterOp>([&](auto op) {
1323  // We only want to verify this is written to if the {write enable,
1324  // in} port is driven.
1325  return succeeded(group.drivesAnyPort({op.getWriteEn(), op.getIn()}))
1326  ? group.drivesAllPorts({op.getWriteEn(), op.getIn()})
1327  : success();
1328  })
1329  .Case<MemoryOp>([&](auto op) {
1330  SmallVector<Value> requiredWritePorts;
1331  // If writing to memory, write_en, write_data, and all address ports
1332  // should be driven.
1333  requiredWritePorts.push_back(op.writeEn());
1334  requiredWritePorts.push_back(op.writeData());
1335  for (Value address : op.addrPorts())
1336  requiredWritePorts.push_back(address);
1337 
1338  // We only want to verify the write ports if either write_data or
1339  // write_en is driven.
1340  return succeeded(
1341  group.drivesAnyPort({op.writeData(), op.writeEn()}))
1342  ? group.drivesAllPorts(requiredWritePorts)
1343  : success();
1344  })
1345  .Case<AndLibOp, OrLibOp, XorLibOp, AddLibOp, SubLibOp, GtLibOp,
1346  LtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp,
1347  RshLibOp, SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp,
1348  SleLibOp, SrshLibOp>([&](auto op) {
1349  Value lhs = op.getLeft(), rhs = op.getRight();
1350  return succeeded(group.drivesAnyPort({lhs, rhs}))
1351  ? group.drivesAllPorts({lhs, rhs})
1352  : success();
1353  })
1354  .Default([&](auto op) { return success(); });
1355 
1356  if (failed(verifyWrites))
1357  return group->emitOpError()
1358  << "with cell: " << destCell->getName() << " \""
1359  << destCell.instanceName()
1360  << "\" is performing a write and failed to drive all necessary "
1361  "ports.";
1362 
1363  Operation *srcDefiningOp = assign.getSrc().getDefiningOp();
1364  if (srcDefiningOp == nullptr)
1365  return success();
1366  auto srcCell = dyn_cast<CellInterface>(srcDefiningOp);
1367  if (srcCell == nullptr)
1368  return success();
1369 
1370  LogicalResult verifyReads =
1371  TypeSwitch<Operation *, LogicalResult>(srcCell)
1372  .Case<MemoryOp>([&](auto op) {
1373  // If reading memory, all address ports should be driven. Note that
1374  // we only want to verify the read ports if read_data is used in the
1375  // group.
1376  return succeeded(group.readsAnyPort({op.readData()}))
1377  ? group.drivesAllPorts(op.addrPorts())
1378  : success();
1379  })
1380  .Default([&](auto op) { return success(); });
1381 
1382  if (failed(verifyReads))
1383  return group->emitOpError() << "with cell: " << srcCell->getName() << " \""
1384  << srcCell.instanceName()
1385  << "\" is having a read performed upon it, and "
1386  "failed to drive all necessary ports.";
1387 
1388  return success();
1389 }
1390 
1391 LogicalResult calyx::verifyGroupInterface(Operation *op) {
1392  auto group = dyn_cast<GroupInterface>(op);
1393  if (group == nullptr)
1394  return success();
1395 
1396  for (auto &&groupOp : *group.getBody()) {
1397  auto assign = dyn_cast<AssignOp>(groupOp);
1398  if (assign == nullptr)
1399  continue;
1400  if (failed(verifyPrimitivePortDriving(assign, group)))
1401  return failure();
1402  }
1403 
1404  return success();
1405 }
1406 
1407 //===----------------------------------------------------------------------===//
1408 // Utilities for operations with the Cell trait.
1409 //===----------------------------------------------------------------------===//
1410 
1411 /// Gives each result of the cell a meaningful name in the form:
1412 /// <instance-name>.<port-name>
1413 static void getCellAsmResultNames(OpAsmSetValueNameFn setNameFn, Operation *op,
1414  ArrayRef<StringRef> portNames) {
1415  auto cellInterface = dyn_cast<CellInterface>(op);
1416  assert(cellInterface && "must implement the Cell interface");
1417 
1418  std::string prefix = cellInterface.instanceName().str() + ".";
1419  for (size_t i = 0, e = portNames.size(); i != e; ++i)
1420  setNameFn(op->getResult(i), prefix + portNames[i].str());
1421 }
1422 
1423 //===----------------------------------------------------------------------===//
1424 // AssignOp
1425 //===----------------------------------------------------------------------===//
1426 
1427 /// Determines whether the given direction is valid with the given inputs. The
1428 /// `isDestination` boolean is used to distinguish whether the value is a source
1429 /// or a destination.
1430 static LogicalResult verifyPortDirection(Operation *op, Value value,
1431  bool isDestination) {
1432  Operation *definingOp = value.getDefiningOp();
1433  bool isComponentPort = value.isa<BlockArgument>(),
1434  isCellInterfacePort = definingOp && isa<CellInterface>(definingOp);
1435  assert((isComponentPort || isCellInterfacePort) && "Not a port.");
1436 
1437  PortInfo port = isComponentPort
1438  ? getPortInfo(value.cast<BlockArgument>())
1439  : cast<CellInterface>(definingOp).portInfo(value);
1440 
1441  bool isSource = !isDestination;
1442  // Component output ports and cell interface input ports should be driven.
1443  Direction validDirection =
1444  (isDestination && isComponentPort) || (isSource && isCellInterfacePort)
1446  : Direction::Input;
1447 
1448  return port.direction == validDirection
1449  ? success()
1450  : op->emitOpError()
1451  << "has a " << (isComponentPort ? "component" : "cell")
1452  << " port as the "
1453  << (isDestination ? "destination" : "source")
1454  << " with the incorrect direction.";
1455 }
1456 
1457 /// Verifies the value of a given assignment operation. The boolean
1458 /// `isDestination` is used to distinguish whether the destination
1459 /// or source of the AssignOp is to be verified.
1460 static LogicalResult verifyAssignOpValue(AssignOp op, bool isDestination) {
1461  bool isSource = !isDestination;
1462  Value value = isDestination ? op.getDest() : op.getSrc();
1463  if (isPort(value))
1464  return verifyPortDirection(op, value, isDestination);
1465 
1466  // A destination may also be the Go or Done hole of a GroupOp.
1467  if (isDestination && !isa<GroupGoOp, GroupDoneOp>(value.getDefiningOp()))
1468  return op->emitOpError(
1469  "has an invalid destination port. It must be drive-able.");
1470  else if (isSource)
1471  return verifyNotComplexSource(op);
1472 
1473  return success();
1474 }
1475 
1476 LogicalResult AssignOp::verify() {
1477  bool isDestination = true, isSource = false;
1478  if (failed(verifyAssignOpValue(*this, isDestination)))
1479  return failure();
1480  if (failed(verifyAssignOpValue(*this, isSource)))
1481  return failure();
1482 
1483  return success();
1484 }
1485 
1486 ParseResult AssignOp::parse(OpAsmParser &parser, OperationState &result) {
1487  OpAsmParser::UnresolvedOperand destination;
1488  if (parser.parseOperand(destination) || parser.parseEqual())
1489  return failure();
1490 
1491  // An AssignOp takes one of the two following forms:
1492  // (1) %<dest> = %<src> : <type>
1493  // (2) %<dest> = %<guard> ? %<src> : <type>
1494  OpAsmParser::UnresolvedOperand guardOrSource;
1495  if (parser.parseOperand(guardOrSource))
1496  return failure();
1497 
1498  // Since the guard is optional, we need to check if there is an accompanying
1499  // `?` symbol.
1500  OpAsmParser::UnresolvedOperand source;
1501  bool hasGuard = succeeded(parser.parseOptionalQuestion());
1502  if (hasGuard) {
1503  // The guard exists. Parse the source.
1504  if (parser.parseOperand(source))
1505  return failure();
1506  }
1507 
1508  Type type;
1509  if (parser.parseColonType(type) ||
1510  parser.resolveOperand(destination, type, result.operands))
1511  return failure();
1512 
1513  if (hasGuard) {
1514  Type i1Type = parser.getBuilder().getI1Type();
1515  // Since the guard is optional, it is listed last in the arguments of the
1516  // AssignOp. Therefore, we must parse the source first.
1517  if (parser.resolveOperand(source, type, result.operands) ||
1518  parser.resolveOperand(guardOrSource, i1Type, result.operands))
1519  return failure();
1520  } else {
1521  // This is actually a source.
1522  if (parser.resolveOperand(guardOrSource, type, result.operands))
1523  return failure();
1524  }
1525 
1526  return success();
1527 }
1528 
1529 void AssignOp::print(OpAsmPrinter &p) {
1530  p << " " << getDest() << " = ";
1531 
1532  Value bguard = getGuard(), source = getSrc();
1533  // The guard is optional.
1534  if (bguard)
1535  p << bguard << " ? ";
1536 
1537  // We only need to print a single type; the destination and source are
1538  // guaranteed to be the same type.
1539  p << source << " : " << source.getType();
1540 }
1541 
1542 //===----------------------------------------------------------------------===//
1543 // InstanceOp
1544 //===----------------------------------------------------------------------===//
1545 
1546 /// Lookup the component for the symbol. This returns null on
1547 /// invalid IR.
1548 ComponentInterface InstanceOp::getReferencedComponent() {
1549  auto module = (*this)->getParentOfType<ModuleOp>();
1550  if (!module)
1551  return nullptr;
1552 
1553  return module.lookupSymbol<ComponentInterface>(getComponentName());
1554 }
1555 
1556 /// Verifies the port information in comparison with the referenced component
1557 /// of an instance. This helper function avoids conducting a lookup for the
1558 /// referenced component twice.
1559 static LogicalResult
1560 verifyInstanceOpType(InstanceOp instance,
1561  ComponentInterface referencedComponent) {
1562  auto module = instance->getParentOfType<ModuleOp>();
1563  StringRef entryPointName =
1564  module->getAttrOfType<StringAttr>("calyx.entrypoint");
1565  if (instance.getComponentName() == entryPointName)
1566  return instance.emitOpError()
1567  << "cannot reference the entry-point component: '" << entryPointName
1568  << "'.";
1569 
1570  // Verify the instance result ports with those of its referenced component.
1571  SmallVector<PortInfo> componentPorts = referencedComponent.getPortInfo();
1572  size_t numPorts = componentPorts.size();
1573 
1574  size_t numResults = instance.getNumResults();
1575  if (numResults != numPorts)
1576  return instance.emitOpError()
1577  << "has a wrong number of results; expected: " << numPorts
1578  << " but got " << numResults;
1579 
1580  for (size_t i = 0; i != numResults; ++i) {
1581  auto resultType = instance.getResult(i).getType();
1582  auto expectedType = componentPorts[i].type;
1583  if (resultType == expectedType)
1584  continue;
1585  return instance.emitOpError()
1586  << "result type for " << componentPorts[i].name << " must be "
1587  << expectedType << ", but got " << resultType;
1588  }
1589  return success();
1590 }
1591 
1592 LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1593  Operation *op = *this;
1594  auto module = op->getParentOfType<ModuleOp>();
1595  Operation *referencedComponent =
1596  symbolTable.lookupNearestSymbolFrom(module, getComponentNameAttr());
1597  if (referencedComponent == nullptr)
1598  return emitError() << "referencing component: '" << getComponentName()
1599  << "', which does not exist.";
1600 
1601  Operation *shadowedComponentName =
1602  symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1603  if (shadowedComponentName != nullptr)
1604  return emitError() << "instance symbol: '" << instanceName()
1605  << "' is already a symbol for another component.";
1606 
1607  // Verify the referenced component is not instantiating itself.
1608  auto parentComponent = op->getParentOfType<ComponentOp>();
1609  if (parentComponent == referencedComponent)
1610  return emitError() << "recursive instantiation of its parent component: '"
1611  << getComponentName() << "'";
1612 
1613  assert(isa<ComponentInterface>(referencedComponent) &&
1614  "Should be a ComponentInterface.");
1615  return verifyInstanceOpType(*this,
1616  cast<ComponentInterface>(referencedComponent));
1617 }
1618 
1619 /// Provide meaningful names to the result values of an InstanceOp.
1621  getCellAsmResultNames(setNameFn, *this, this->portNames());
1622 }
1623 
1624 SmallVector<StringRef> InstanceOp::portNames() {
1625  SmallVector<StringRef> portNames;
1626  for (Attribute name : getReferencedComponent().getPortNames())
1627  portNames.push_back(name.cast<StringAttr>().getValue());
1628  return portNames;
1629 }
1630 
1631 SmallVector<Direction> InstanceOp::portDirections() {
1632  SmallVector<Direction> portDirections;
1633  for (const PortInfo &port : getReferencedComponent().getPortInfo())
1634  portDirections.push_back(port.direction);
1635  return portDirections;
1636 }
1637 
1638 SmallVector<DictionaryAttr> InstanceOp::portAttributes() {
1639  SmallVector<DictionaryAttr> portAttributes;
1640  for (const PortInfo &port : getReferencedComponent().getPortInfo())
1641  portAttributes.push_back(port.attributes);
1642  return portAttributes;
1643 }
1644 
1646  return isa<CombComponentOp>(getReferencedComponent());
1647 }
1648 
1649 //===----------------------------------------------------------------------===//
1650 // PrimitiveOp
1651 //===----------------------------------------------------------------------===//
1652 
1653 /// Lookup the component for the symbol. This returns null on
1654 /// invalid IR.
1655 hw::HWModuleExternOp PrimitiveOp::getReferencedPrimitive() {
1656  auto module = (*this)->getParentOfType<ModuleOp>();
1657  if (!module)
1658  return nullptr;
1659 
1660  return module.lookupSymbol<hw::HWModuleExternOp>(getPrimitiveName());
1661 }
1662 
1663 /// Verifies the port information in comparison with the referenced component
1664 /// of an instance. This helper function avoids conducting a lookup for the
1665 /// referenced component twice.
1666 static LogicalResult
1667 verifyPrimitiveOpType(PrimitiveOp instance,
1668  hw::HWModuleExternOp referencedPrimitive) {
1669  auto module = instance->getParentOfType<ModuleOp>();
1670  StringRef entryPointName =
1671  module->getAttrOfType<StringAttr>("calyx.entrypoint");
1672  if (instance.getPrimitiveName() == entryPointName)
1673  return instance.emitOpError()
1674  << "cannot reference the entry-point component: '" << entryPointName
1675  << "'.";
1676 
1677  // Verify the instance result ports with those of its referenced component.
1678  auto primitivePorts = referencedPrimitive.getPortList();
1679  size_t numPorts = primitivePorts.size();
1680 
1681  size_t numResults = instance.getNumResults();
1682  if (numResults != numPorts)
1683  return instance.emitOpError()
1684  << "has a wrong number of results; expected: " << numPorts
1685  << " but got " << numResults;
1686 
1687  // Verify parameters match up
1688  ArrayAttr modParameters = referencedPrimitive.getParameters();
1689  ArrayAttr parameters = instance.getParameters().value_or(ArrayAttr());
1690  size_t numExpected = modParameters.size();
1691  size_t numParams = parameters.size();
1692  if (numParams != numExpected)
1693  return instance.emitOpError()
1694  << "has the wrong number of parameters; expected: " << numExpected
1695  << " but got " << numParams;
1696 
1697  for (size_t i = 0; i != numExpected; ++i) {
1698  auto param = parameters[i].cast<circt::hw::ParamDeclAttr>();
1699  auto modParam = modParameters[i].cast<circt::hw::ParamDeclAttr>();
1700 
1701  auto paramName = param.getName();
1702  if (paramName != modParam.getName())
1703  return instance.emitOpError()
1704  << "parameter #" << i << " should have name " << modParam.getName()
1705  << " but has name " << paramName;
1706 
1707  if (param.getType() != modParam.getType())
1708  return instance.emitOpError()
1709  << "parameter " << paramName << " should have type "
1710  << modParam.getType() << " but has type " << param.getType();
1711 
1712  // All instance parameters must have a value. Specify the same value as
1713  // a module's default value if you want the default.
1714  if (!param.getValue())
1715  return instance.emitOpError("parameter ")
1716  << paramName << " must have a value";
1717  }
1718 
1719  for (size_t i = 0; i != numResults; ++i) {
1720  auto resultType = instance.getResult(i).getType();
1721  auto expectedType = primitivePorts[i].type;
1722  auto replacedType = hw::evaluateParametricType(
1723  instance.getLoc(), instance.getParametersAttr(), expectedType);
1724  if (failed(replacedType))
1725  return failure();
1726  if (resultType == replacedType)
1727  continue;
1728  return instance.emitOpError()
1729  << "result type for " << primitivePorts[i].name << " must be "
1730  << expectedType << ", but got " << resultType;
1731  }
1732  return success();
1733 }
1734 
1735 LogicalResult
1736 PrimitiveOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1737  Operation *op = *this;
1738  auto module = op->getParentOfType<ModuleOp>();
1739  Operation *referencedPrimitive =
1740  symbolTable.lookupNearestSymbolFrom(module, getPrimitiveNameAttr());
1741  if (referencedPrimitive == nullptr)
1742  return emitError() << "referencing primitive: '" << getPrimitiveName()
1743  << "', which does not exist.";
1744 
1745  Operation *shadowedPrimitiveName =
1746  symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1747  if (shadowedPrimitiveName != nullptr)
1748  return emitError() << "instance symbol: '" << instanceName()
1749  << "' is already a symbol for another primitive.";
1750 
1751  // Verify the referenced primitive is not instantiating itself.
1752  auto parentPrimitive = op->getParentOfType<hw::HWModuleExternOp>();
1753  if (parentPrimitive == referencedPrimitive)
1754  return emitError() << "recursive instantiation of its parent primitive: '"
1755  << getPrimitiveName() << "'";
1756 
1757  assert(isa<hw::HWModuleExternOp>(referencedPrimitive) &&
1758  "Should be a HardwareModuleExternOp.");
1759 
1760  return verifyPrimitiveOpType(*this,
1761  cast<hw::HWModuleExternOp>(referencedPrimitive));
1762 }
1763 
1764 /// Provide meaningful names to the result values of an PrimitiveOp.
1766  getCellAsmResultNames(setNameFn, *this, this->portNames());
1767 }
1768 
1769 SmallVector<StringRef> PrimitiveOp::portNames() {
1770  SmallVector<StringRef> portNames;
1771  auto ports = getReferencedPrimitive().getPortList();
1772  for (auto port : ports)
1773  portNames.push_back(port.name.getValue());
1774 
1775  return portNames;
1776 }
1777 
1779  switch (direction) {
1781  return Direction::Input;
1783  return Direction::Output;
1785  llvm_unreachable("InOut ports not supported by Calyx");
1786  }
1787  llvm_unreachable("Impossible port type");
1788 }
1789 
1790 SmallVector<Direction> PrimitiveOp::portDirections() {
1791  SmallVector<Direction> portDirections;
1792  auto ports = getReferencedPrimitive().getPortList();
1793  for (hw::PortInfo port : ports)
1794  portDirections.push_back(convertHWDirectionToCalyx(port.dir));
1795  return portDirections;
1796 }
1797 
1798 bool PrimitiveOp::isCombinational() { return false; }
1799 
1800 /// Returns a new DictionaryAttr containing only the calyx dialect attrs
1801 /// in the input DictionaryAttr. Also strips the 'calyx.' prefix from these
1802 /// attrs.
1803 static DictionaryAttr cleanCalyxPortAttrs(OpBuilder builder,
1804  DictionaryAttr dict) {
1805  if (!dict) {
1806  return dict;
1807  }
1808  llvm::SmallVector<NamedAttribute> attrs;
1809  for (NamedAttribute attr : dict) {
1810  Dialect *dialect = attr.getNameDialect();
1811  if (dialect == nullptr || !isa<CalyxDialect>(*dialect))
1812  continue;
1813  StringRef name = attr.getName().strref();
1814  StringAttr newName = builder.getStringAttr(std::get<1>(name.split(".")));
1815  attr.setName(newName);
1816  attrs.push_back(attr);
1817  }
1818  return builder.getDictionaryAttr(attrs);
1819 }
1820 
1821 // Grabs calyx port attributes from the HWModuleExternOp arg/result attributes.
1822 SmallVector<DictionaryAttr> PrimitiveOp::portAttributes() {
1823  SmallVector<DictionaryAttr> portAttributes;
1824  OpBuilder builder(getContext());
1825  hw::HWModuleExternOp prim = getReferencedPrimitive();
1826  auto argAttrs = prim.getAllInputAttrs();
1827  auto resAttrs = prim.getAllOutputAttrs();
1828  for (auto a : argAttrs)
1829  portAttributes.push_back(
1830  cleanCalyxPortAttrs(builder, cast_or_null<DictionaryAttr>(a)));
1831  for (auto a : resAttrs)
1832  portAttributes.push_back(
1833  cleanCalyxPortAttrs(builder, cast_or_null<DictionaryAttr>(a)));
1834  return portAttributes;
1835 }
1836 
1837 /// Parse an parameter list if present. Same format as HW dialect.
1838 /// module-parameter-list ::= `<` parameter-decl (`,` parameter-decl)* `>`
1839 /// parameter-decl ::= identifier `:` type
1840 /// parameter-decl ::= identifier `:` type `=` attribute
1841 ///
1842 static ParseResult parseParameterList(OpAsmParser &parser,
1843  SmallVector<Attribute> &parameters) {
1844 
1845  return parser.parseCommaSeparatedList(
1846  OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1847  std::string name;
1848  Type type;
1849  Attribute value;
1850 
1851  if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1852  return failure();
1853 
1854  // Parse the default value if present.
1855  if (succeeded(parser.parseOptionalEqual())) {
1856  if (parser.parseAttribute(value, type))
1857  return failure();
1858  }
1859 
1860  auto &builder = parser.getBuilder();
1861  parameters.push_back(hw::ParamDeclAttr::get(
1862  builder.getContext(), builder.getStringAttr(name), type, value));
1863  return success();
1864  });
1865 }
1866 
1867 /// Shim to also use this for the InstanceOp custom parser.
1868 static ParseResult parseParameterList(OpAsmParser &parser,
1869  ArrayAttr &parameters) {
1870  SmallVector<Attribute> parseParameters;
1871  if (failed(parseParameterList(parser, parseParameters)))
1872  return failure();
1873 
1874  parameters = ArrayAttr::get(parser.getContext(), parseParameters);
1875 
1876  return success();
1877 }
1878 
1879 /// Print a parameter list for a module or instance. Same format as HW dialect.
1880 static void printParameterList(OpAsmPrinter &p, Operation *op,
1881  ArrayAttr parameters) {
1882  if (parameters.empty())
1883  return;
1884 
1885  p << '<';
1886  llvm::interleaveComma(parameters, p, [&](Attribute param) {
1887  auto paramAttr = param.cast<hw::ParamDeclAttr>();
1888  p << paramAttr.getName().getValue() << ": " << paramAttr.getType();
1889  if (auto value = paramAttr.getValue()) {
1890  p << " = ";
1891  p.printAttributeWithoutType(value);
1892  }
1893  });
1894  p << '>';
1895 }
1896 
1897 //===----------------------------------------------------------------------===//
1898 // GroupGoOp
1899 //===----------------------------------------------------------------------===//
1900 
1901 LogicalResult GroupGoOp::verify() { return verifyNotComplexSource(*this); }
1902 
1903 /// Provide meaningful names to the result value of a GroupGoOp.
1904 void GroupGoOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1905  auto parent = (*this)->getParentOfType<GroupOp>();
1906  StringRef name = parent.getSymName();
1907  std::string resultName = name.str() + ".go";
1908  setNameFn(getResult(), resultName);
1909 }
1910 
1911 void GroupGoOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1912 
1913 ParseResult GroupGoOp::parse(OpAsmParser &parser, OperationState &result) {
1914  if (parseGroupPort(parser, result))
1915  return failure();
1916 
1917  result.addTypes(parser.getBuilder().getI1Type());
1918  return success();
1919 }
1920 
1921 //===----------------------------------------------------------------------===//
1922 // GroupDoneOp
1923 //===----------------------------------------------------------------------===//
1924 
1925 LogicalResult GroupDoneOp::verify() {
1926  Operation *srcOp = getSrc().getDefiningOp();
1927  Value optionalGuard = getGuard();
1928  Operation *guardOp = optionalGuard ? optionalGuard.getDefiningOp() : nullptr;
1929  bool noGuard = (guardOp == nullptr);
1930 
1931  if (srcOp == nullptr)
1932  // This is a port of the parent component.
1933  return success();
1934 
1935  if (isa<hw::ConstantOp>(srcOp) && (noGuard || isa<hw::ConstantOp>(guardOp)))
1936  return emitOpError() << "with constant source"
1937  << (noGuard ? "" : " and constant guard")
1938  << ". This should be a combinational group.";
1939 
1940  return verifyNotComplexSource(*this);
1941 }
1942 
1943 void GroupDoneOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1944 
1945 ParseResult GroupDoneOp::parse(OpAsmParser &parser, OperationState &result) {
1946  return parseGroupPort(parser, result);
1947 }
1948 
1949 //===----------------------------------------------------------------------===//
1950 // RegisterOp
1951 //===----------------------------------------------------------------------===//
1952 
1953 /// Provide meaningful names to the result values of a RegisterOp.
1954 void RegisterOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1955  getCellAsmResultNames(setNameFn, *this, this->portNames());
1956 }
1957 
1958 SmallVector<StringRef> RegisterOp::portNames() {
1959  return {"in", "write_en", clkPort, resetPort, "out", donePort};
1960 }
1961 
1962 SmallVector<Direction> RegisterOp::portDirections() {
1963  return {Input, Input, Input, Input, Output, Output};
1964 }
1965 
1966 SmallVector<DictionaryAttr> RegisterOp::portAttributes() {
1967  MLIRContext *context = getContext();
1968  IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
1969  NamedAttrList writeEn, clk, reset, done;
1970  writeEn.append(goPort, isSet);
1971  clk.append(clkPort, isSet);
1972  reset.append(resetPort, isSet);
1973  done.append(donePort, isSet);
1974  return {
1975  DictionaryAttr::get(context), // In
1976  writeEn.getDictionary(context), // Write enable
1977  clk.getDictionary(context), // Clk
1978  reset.getDictionary(context), // Reset
1979  DictionaryAttr::get(context), // Out
1980  done.getDictionary(context) // Done
1981  };
1982 }
1983 
1984 bool RegisterOp::isCombinational() { return false; }
1985 
1986 //===----------------------------------------------------------------------===//
1987 // MemoryOp
1988 //===----------------------------------------------------------------------===//
1989 
1990 /// Provide meaningful names to the result values of a MemoryOp.
1991 void MemoryOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1992  getCellAsmResultNames(setNameFn, *this, this->portNames());
1993 }
1994 
1995 SmallVector<StringRef> MemoryOp::portNames() {
1996  SmallVector<StringRef> portNames;
1997  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
1998  auto nameAttr =
1999  StringAttr::get(this->getContext(), "addr" + std::to_string(i));
2000  portNames.push_back(nameAttr.getValue());
2001  }
2002  portNames.append({"write_data", "write_en", clkPort, "read_data", donePort});
2003  return portNames;
2004 }
2005 
2006 SmallVector<Direction> MemoryOp::portDirections() {
2007  SmallVector<Direction> portDirections;
2008  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2009  portDirections.push_back(Input);
2010  portDirections.append({Input, Input, Input, Output, Output});
2011  return portDirections;
2012 }
2013 
2014 SmallVector<DictionaryAttr> MemoryOp::portAttributes() {
2015  SmallVector<DictionaryAttr> portAttributes;
2016  MLIRContext *context = getContext();
2017  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2018  portAttributes.push_back(DictionaryAttr::get(context)); // Addresses
2019 
2020  // Use a boolean to indicate this attribute is used.
2021  IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
2022  NamedAttrList writeEn, clk, reset, done;
2023  writeEn.append(goPort, isSet);
2024  clk.append(clkPort, isSet);
2025  done.append(donePort, isSet);
2026  portAttributes.append({DictionaryAttr::get(context), // In
2027  writeEn.getDictionary(context), // Write enable
2028  clk.getDictionary(context), // Clk
2029  DictionaryAttr::get(context), // Out
2030  done.getDictionary(context)} // Done
2031  );
2032  return portAttributes;
2033 }
2034 
2035 void MemoryOp::build(OpBuilder &builder, OperationState &state,
2036  StringRef instanceName, int64_t width,
2037  ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2038  state.addAttribute(SymbolTable::getSymbolAttrName(),
2039  builder.getStringAttr(instanceName));
2040  state.addAttribute("width", builder.getI64IntegerAttr(width));
2041  state.addAttribute("sizes", builder.getI64ArrayAttr(sizes));
2042  state.addAttribute("addrSizes", builder.getI64ArrayAttr(addrSizes));
2043  SmallVector<Type> types;
2044  for (int64_t size : addrSizes)
2045  types.push_back(builder.getIntegerType(size)); // Addresses
2046  types.push_back(builder.getIntegerType(width)); // Write data
2047  types.push_back(builder.getI1Type()); // Write enable
2048  types.push_back(builder.getI1Type()); // Clk
2049  types.push_back(builder.getIntegerType(width)); // Read data
2050  types.push_back(builder.getI1Type()); // Done
2051  state.addTypes(types);
2052 }
2053 
2054 LogicalResult MemoryOp::verify() {
2055  ArrayRef<Attribute> opSizes = getSizes().getValue();
2056  ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2057  size_t numDims = getSizes().size();
2058  size_t numAddrs = getAddrSizes().size();
2059  if (numDims != numAddrs)
2060  return emitOpError("mismatched number of dimensions (")
2061  << numDims << ") and address sizes (" << numAddrs << ")";
2062 
2063  size_t numExtraPorts = 5; // write data/enable, clk, and read data/done.
2064  if (getNumResults() != numAddrs + numExtraPorts)
2065  return emitOpError("incorrect number of address ports, expected ")
2066  << numAddrs;
2067 
2068  for (size_t i = 0; i < numDims; ++i) {
2069  int64_t size = opSizes[i].cast<IntegerAttr>().getInt();
2070  int64_t addrSize = opAddrSizes[i].cast<IntegerAttr>().getInt();
2071  if (llvm::Log2_64_Ceil(size) > addrSize)
2072  return emitOpError("address size (")
2073  << addrSize << ") for dimension " << i
2074  << " can't address the entire range (" << size << ")";
2075  }
2076 
2077  return success();
2078 }
2079 
2080 //===----------------------------------------------------------------------===//
2081 // SeqMemoryOp
2082 //===----------------------------------------------------------------------===//
2083 
2084 /// Provide meaningful names to the result values of a SeqMemoryOp.
2085 void SeqMemoryOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
2086  getCellAsmResultNames(setNameFn, *this, this->portNames());
2087 }
2088 
2089 SmallVector<StringRef> SeqMemoryOp::portNames() {
2090  SmallVector<StringRef> portNames;
2091  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2092  auto nameAttr =
2093  StringAttr::get(this->getContext(), "addr" + std::to_string(i));
2094  portNames.push_back(nameAttr.getValue());
2095  }
2096  portNames.append({clkPort, "reset", "content_en", "write_en", "write_data",
2097  "read_data", "done"});
2098  return portNames;
2099 }
2100 
2101 SmallVector<Direction> SeqMemoryOp::portDirections() {
2102  SmallVector<Direction> portDirections;
2103  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2104  portDirections.push_back(Input);
2105  portDirections.append({Input, Input, Input, Input, Input, Output, Output});
2106  return portDirections;
2107 }
2108 
2109 SmallVector<DictionaryAttr> SeqMemoryOp::portAttributes() {
2110  SmallVector<DictionaryAttr> portAttributes;
2111  MLIRContext *context = getContext();
2112  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2113  portAttributes.push_back(DictionaryAttr::get(context)); // Addresses
2114 
2115  OpBuilder builder(context);
2116  // Use a boolean to indicate this attribute is used.
2117  IntegerAttr isSet = IntegerAttr::get(builder.getIndexType(), 1);
2118  IntegerAttr isTwo = IntegerAttr::get(builder.getIndexType(), 2);
2119  NamedAttrList done, clk, reset, contentEn;
2120  done.append(donePort, isSet);
2121  clk.append(clkPort, isSet);
2122  clk.append(resetPort, isSet);
2123  contentEn.append(goPort, isTwo);
2124  portAttributes.append({clk.getDictionary(context), // Clk
2125  reset.getDictionary(context), // Reset
2126  contentEn.getDictionary(context), // Content enable
2127  DictionaryAttr::get(context), // Write enable
2128  DictionaryAttr::get(context), // Write data
2129  DictionaryAttr::get(context), // Read data
2130  done.getDictionary(context)} // Done
2131  );
2132  return portAttributes;
2133 }
2134 
2135 void SeqMemoryOp::build(OpBuilder &builder, OperationState &state,
2136  StringRef instanceName, int64_t width,
2137  ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2138  state.addAttribute(SymbolTable::getSymbolAttrName(),
2139  builder.getStringAttr(instanceName));
2140  state.addAttribute("width", builder.getI64IntegerAttr(width));
2141  state.addAttribute("sizes", builder.getI64ArrayAttr(sizes));
2142  state.addAttribute("addrSizes", builder.getI64ArrayAttr(addrSizes));
2143  SmallVector<Type> types;
2144  for (int64_t size : addrSizes)
2145  types.push_back(builder.getIntegerType(size)); // Addresses
2146  types.push_back(builder.getI1Type()); // Clk
2147  types.push_back(builder.getI1Type()); // Reset
2148  types.push_back(builder.getI1Type()); // Content enable
2149  types.push_back(builder.getI1Type()); // Write enable
2150  types.push_back(builder.getIntegerType(width)); // Write data
2151  types.push_back(builder.getIntegerType(width)); // Read data
2152  types.push_back(builder.getI1Type()); // Done
2153  state.addTypes(types);
2154 }
2155 
2156 LogicalResult SeqMemoryOp::verify() {
2157  ArrayRef<Attribute> opSizes = getSizes().getValue();
2158  ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2159  size_t numDims = getSizes().size();
2160  size_t numAddrs = getAddrSizes().size();
2161  if (numDims != numAddrs)
2162  return emitOpError("mismatched number of dimensions (")
2163  << numDims << ") and address sizes (" << numAddrs << ")";
2164 
2165  size_t numExtraPorts =
2166  7; // write data/enable, clk, reset, read data, content enable, and done.
2167  if (getNumResults() != numAddrs + numExtraPorts)
2168  return emitOpError("incorrect number of address ports, expected ")
2169  << numAddrs;
2170 
2171  for (size_t i = 0; i < numDims; ++i) {
2172  int64_t size = opSizes[i].cast<IntegerAttr>().getInt();
2173  int64_t addrSize = opAddrSizes[i].cast<IntegerAttr>().getInt();
2174  if (llvm::Log2_64_Ceil(size) > addrSize)
2175  return emitOpError("address size (")
2176  << addrSize << ") for dimension " << i
2177  << " can't address the entire range (" << size << ")";
2178  }
2179 
2180  return success();
2181 }
2182 
2183 //===----------------------------------------------------------------------===//
2184 // EnableOp
2185 //===----------------------------------------------------------------------===//
2186 LogicalResult EnableOp::verify() {
2187  auto component = (*this)->getParentOfType<ComponentOp>();
2188  auto wiresOp = component.getWiresOp();
2189  StringRef name = getGroupName();
2190 
2191  auto groupOp = wiresOp.lookupSymbol<GroupInterface>(name);
2192  if (!groupOp)
2193  return emitOpError() << "with group '" << name
2194  << "', which does not exist.";
2195 
2196  if (isa<CombGroupOp>(groupOp))
2197  return emitOpError() << "with group '" << name
2198  << "', which is a combinational group.";
2199 
2200  return success();
2201 }
2202 
2203 //===----------------------------------------------------------------------===//
2204 // IfOp
2205 //===----------------------------------------------------------------------===//
2206 
2207 LogicalResult IfOp::verify() {
2208  std::optional<StringRef> optGroupName = getGroupName();
2209  if (!optGroupName) {
2210  // No combinational group was provided.
2211  return success();
2212  }
2213  auto component = (*this)->getParentOfType<ComponentOp>();
2214  WiresOp wiresOp = component.getWiresOp();
2215  StringRef groupName = *optGroupName;
2216  auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2217  if (!groupOp)
2218  return emitOpError() << "with group '" << groupName
2219  << "', which does not exist.";
2220 
2221  if (isa<GroupOp>(groupOp))
2222  return emitOpError() << "with group '" << groupName
2223  << "', which is not a combinational group.";
2224 
2225  if (failed(groupOp.drivesPort(getCond())))
2226  return emitError() << "with conditional op: '"
2227  << valueName(component, getCond())
2228  << "' expected to be driven from group: '" << groupName
2229  << "' but no driver was found.";
2230 
2231  return success();
2232 }
2233 
2234 /// Returns the last EnableOp within the child tree of 'parentSeqOp' or
2235 /// `parentStaticSeqOp.` If no EnableOp was found (e.g. a "calyx.par" operation
2236 /// is present), returns None.
2237 template <typename OpTy>
2238 static std::optional<EnableOp> getLastEnableOp(OpTy parent) {
2239  static_assert(IsAny<OpTy, SeqOp, StaticSeqOp>(),
2240  "Should be a StaticSeqOp or SeqOp.");
2241  auto &lastOp = parent.getBodyBlock()->back();
2242  if (auto enableOp = dyn_cast<EnableOp>(lastOp))
2243  return enableOp;
2244  if (auto seqOp = dyn_cast<SeqOp>(lastOp))
2245  return getLastEnableOp(seqOp);
2246  if (auto staticSeqOp = dyn_cast<StaticSeqOp>(lastOp))
2247  return getLastEnableOp(staticSeqOp);
2248 
2249  return std::nullopt;
2250 }
2251 
2252 /// Returns a mapping of {enabled Group name, EnableOp} for all EnableOps within
2253 /// the immediate ParOp's body.
2254 template <typename OpTy>
2255 static llvm::StringMap<EnableOp> getAllEnableOpsInImmediateBody(OpTy parent) {
2256  static_assert(IsAny<OpTy, ParOp, StaticParOp>(),
2257  "Should be a StaticParOp or ParOp.");
2258 
2259  llvm::StringMap<EnableOp> enables;
2260  Block *body = parent.getBodyBlock();
2261  for (EnableOp op : body->getOps<EnableOp>())
2262  enables.insert(std::pair(op.getGroupName(), op));
2263 
2264  return enables;
2265 }
2266 
2267 /// Checks preconditions for the common tail pattern. This canonicalization is
2268 /// stringent about not entering nested control operations, as this may cause
2269 /// unintentional changes in behavior.
2270 /// We only look for two cases: (1) both regions are ParOps, and
2271 /// (2) both regions are SeqOps. The case when these are different, e.g. ParOp
2272 /// and SeqOp, will only produce less optimal code, or even worse, change the
2273 /// behavior.
2274 template <typename IfOpTy, typename TailOpTy>
2275 static bool hasCommonTailPatternPreConditions(IfOpTy op) {
2276  static_assert(IsAny<TailOpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
2277  "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp.");
2278  static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2279  "Should be a IfOp or StaticIfOp.");
2280 
2281  if (!op.thenBodyExists() || !op.elseBodyExists())
2282  return false;
2283  if (op.getThenBody()->empty() || op.getElseBody()->empty())
2284  return false;
2285 
2286  Block *thenBody = op.getThenBody(), *elseBody = op.getElseBody();
2287  return isa<TailOpTy>(thenBody->front()) && isa<TailOpTy>(elseBody->front());
2288 }
2289 
2290 /// seq {
2291 /// if %a with @G { if %a with @G {
2292 /// seq { ... calyx.enable @A } seq { ... }
2293 /// else { -> } else {
2294 /// seq { ... calyx.enable @A } seq { ... }
2295 /// } }
2296 /// calyx.enable @A
2297 /// }
2298 template <typename IfOpTy, typename SeqOpTy>
2299 static LogicalResult commonTailPatternWithSeq(IfOpTy ifOp,
2300  PatternRewriter &rewriter) {
2301  static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2302  "Should be an IfOp or StaticIfOp.");
2303  static_assert(IsAny<SeqOpTy, SeqOp, StaticSeqOp>(),
2304  "Branches should be checking for an SeqOp or StaticSeqOp");
2305  if (!hasCommonTailPatternPreConditions<IfOpTy, SeqOpTy>(ifOp))
2306  return failure();
2307  auto thenControl = cast<SeqOpTy>(ifOp.getThenBody()->front()),
2308  elseControl = cast<SeqOpTy>(ifOp.getElseBody()->front());
2309 
2310  std::optional<EnableOp> lastThenEnableOp = getLastEnableOp(thenControl),
2311  lastElseEnableOp = getLastEnableOp(elseControl);
2312 
2313  if (!lastThenEnableOp || !lastElseEnableOp)
2314  return failure();
2315  if (lastThenEnableOp->getGroupName() != lastElseEnableOp->getGroupName())
2316  return failure();
2317 
2318  // Place the IfOp and pulled EnableOp inside a sequential region, in case
2319  // this IfOp is nested in a ParOp. This avoids unintentionally
2320  // parallelizing the pulled out EnableOps.
2321  rewriter.setInsertionPointAfter(ifOp);
2322  SeqOpTy seqOp = rewriter.create<SeqOpTy>(ifOp.getLoc());
2323  Block *body = seqOp.getBodyBlock();
2324  ifOp->remove();
2325  body->push_back(ifOp);
2326  rewriter.setInsertionPointToEnd(body);
2327  rewriter.create<EnableOp>(seqOp.getLoc(), lastThenEnableOp->getGroupName());
2328 
2329  // Erase the common EnableOp from the Then and Else regions.
2330  rewriter.eraseOp(*lastThenEnableOp);
2331  rewriter.eraseOp(*lastElseEnableOp);
2332  return success();
2333 }
2334 
2335 /// if %a with @G { par {
2336 /// par { if %a with @G {
2337 /// ... par { ... }
2338 /// calyx.enable @A } else {
2339 /// calyx.enable @B -> par { ... }
2340 /// } }
2341 /// } else { calyx.enable @A
2342 /// par { calyx.enable @B
2343 /// ... }
2344 /// calyx.enable @A
2345 /// calyx.enable @B
2346 /// }
2347 /// }
2348 template <typename OpTy, typename ParOpTy>
2349 static LogicalResult commonTailPatternWithPar(OpTy controlOp,
2350  PatternRewriter &rewriter) {
2351  static_assert(IsAny<OpTy, IfOp, StaticIfOp>(),
2352  "Should be an IfOp or StaticIfOp.");
2353  static_assert(IsAny<ParOpTy, ParOp, StaticParOp>(),
2354  "Branches should be checking for an ParOp or StaticParOp");
2355  if (!hasCommonTailPatternPreConditions<OpTy, ParOpTy>(controlOp))
2356  return failure();
2357  auto thenControl = cast<ParOpTy>(controlOp.getThenBody()->front()),
2358  elseControl = cast<ParOpTy>(controlOp.getElseBody()->front());
2359 
2360  llvm::StringMap<EnableOp> a = getAllEnableOpsInImmediateBody(thenControl),
2361  b = getAllEnableOpsInImmediateBody(elseControl);
2362  // Compute the intersection between `A` and `B`.
2363  SmallVector<StringRef> groupNames;
2364  for (auto aIndex = a.begin(); aIndex != a.end(); ++aIndex) {
2365  StringRef groupName = aIndex->getKey();
2366  auto bIndex = b.find(groupName);
2367  if (bIndex == b.end())
2368  continue;
2369  // This is also an element in B.
2370  groupNames.push_back(groupName);
2371  // Since these are being pulled out, erase them.
2372  rewriter.eraseOp(aIndex->getValue());
2373  rewriter.eraseOp(bIndex->getValue());
2374  }
2375 
2376  // Place the IfOp and EnableOp(s) inside a parallel region, in case this
2377  // IfOp is nested in a SeqOp. This avoids unintentionally sequentializing
2378  // the pulled out EnableOps.
2379  rewriter.setInsertionPointAfter(controlOp);
2380 
2381  ParOpTy parOp = rewriter.create<ParOpTy>(controlOp.getLoc());
2382  Block *body = parOp.getBodyBlock();
2383  controlOp->remove();
2384  body->push_back(controlOp);
2385  // Pull out the intersection between these two sets, and erase their
2386  // counterparts in the Then and Else regions.
2387  rewriter.setInsertionPointToEnd(body);
2388  for (StringRef groupName : groupNames)
2389  rewriter.create<EnableOp>(parOp.getLoc(), groupName);
2390 
2391  return success();
2392 }
2393 
2394 /// This pattern checks for one of two cases that will lead to IfOp deletion:
2395 /// (1) Then and Else bodies are both empty.
2396 /// (2) Then body is empty and Else body does not exist.
2397 struct EmptyIfBody : mlir::OpRewritePattern<IfOp> {
2398  using mlir::OpRewritePattern<IfOp>::OpRewritePattern;
2399  LogicalResult matchAndRewrite(IfOp ifOp,
2400  PatternRewriter &rewriter) const override {
2401  if (!ifOp.getThenBody()->empty())
2402  return failure();
2403  if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2404  return failure();
2405 
2406  eraseControlWithGroupAndConditional(ifOp, rewriter);
2407 
2408  return success();
2409  }
2410 };
2411 
2412 void IfOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2413  MLIRContext *context) {
2414  patterns.add<EmptyIfBody>(context);
2415  patterns.add(commonTailPatternWithPar<IfOp, ParOp>);
2416  patterns.add(commonTailPatternWithSeq<IfOp, SeqOp>);
2417 }
2418 
2419 //===----------------------------------------------------------------------===//
2420 // StaticIfOp
2421 //===----------------------------------------------------------------------===//
2422 LogicalResult StaticIfOp::verify() {
2423  if (elseBodyExists()) {
2424  auto *elseBod = getElseBody();
2425  auto &elseOps = elseBod->getOperations();
2426  // should only have one Operation, static, in the else branch
2427  for (Operation &op : elseOps) {
2428  if (!isStaticControl(&op)) {
2429  return op.emitOpError(
2430  "static if's else branch has non static control within it");
2431  }
2432  }
2433  }
2434 
2435  auto *thenBod = getThenBody();
2436  auto &thenOps = thenBod->getOperations();
2437  for (Operation &op : thenOps) {
2438  // should only have one, static, Operation in the then branch
2439  if (!isStaticControl(&op)) {
2440  return op.emitOpError(
2441  "static if's then branch has non static control within it");
2442  }
2443  }
2444 
2445  return success();
2446 }
2447 
2448 /// This pattern checks for one of two cases that will lead to StaticIfOp
2449 /// deletion: (1) Then and Else bodies are both empty. (2) Then body is empty
2450 /// and Else body does not exist.
2451 struct EmptyStaticIfBody : mlir::OpRewritePattern<StaticIfOp> {
2452  using mlir::OpRewritePattern<StaticIfOp>::OpRewritePattern;
2453  LogicalResult matchAndRewrite(StaticIfOp ifOp,
2454  PatternRewriter &rewriter) const override {
2455  if (!ifOp.getThenBody()->empty())
2456  return failure();
2457  if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2458  return failure();
2459 
2460  eraseControlWithConditional(ifOp, rewriter);
2461 
2462  return success();
2463  }
2464 };
2465 
2466 void StaticIfOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2467  MLIRContext *context) {
2468  patterns.add<EmptyStaticIfBody>(context);
2469  patterns.add(commonTailPatternWithPar<StaticIfOp, StaticParOp>);
2470  patterns.add(commonTailPatternWithSeq<StaticIfOp, StaticSeqOp>);
2471 }
2472 
2473 //===----------------------------------------------------------------------===//
2474 // WhileOp
2475 //===----------------------------------------------------------------------===//
2476 LogicalResult WhileOp::verify() {
2477  auto component = (*this)->getParentOfType<ComponentOp>();
2478  auto wiresOp = component.getWiresOp();
2479 
2480  std::optional<StringRef> optGroupName = getGroupName();
2481  if (!optGroupName) {
2482  /// No combinational group was provided
2483  return success();
2484  }
2485  StringRef groupName = *optGroupName;
2486  auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2487  if (!groupOp)
2488  return emitOpError() << "with group '" << groupName
2489  << "', which does not exist.";
2490 
2491  if (isa<GroupOp>(groupOp))
2492  return emitOpError() << "with group '" << groupName
2493  << "', which is not a combinational group.";
2494 
2495  if (failed(groupOp.drivesPort(getCond())))
2496  return emitError() << "conditional op: '" << valueName(component, getCond())
2497  << "' expected to be driven from group: '" << groupName
2498  << "' but no driver was found.";
2499 
2500  return success();
2501 }
2502 
2503 LogicalResult WhileOp::canonicalize(WhileOp whileOp,
2504  PatternRewriter &rewriter) {
2505  if (whileOp.getBodyBlock()->empty()) {
2506  eraseControlWithGroupAndConditional(whileOp, rewriter);
2507  return success();
2508  }
2509 
2510  return failure();
2511 }
2512 
2513 //===----------------------------------------------------------------------===//
2514 // StaticRepeatOp
2515 //===----------------------------------------------------------------------===//
2516 LogicalResult StaticRepeatOp::verify() {
2517  for (auto &&bodyOp : (*this).getRegion().front()) {
2518  // there should only be one bodyOp for each StaticRepeatOp
2519  if (!isStaticControl(&bodyOp)) {
2520  return bodyOp.emitOpError(
2521  "static repeat has non static control within it");
2522  }
2523  }
2524 
2525  return success();
2526 }
2527 
2528 template <typename OpTy>
2529 static LogicalResult zeroRepeat(OpTy op, PatternRewriter &rewriter) {
2530  static_assert(IsAny<OpTy, RepeatOp, StaticRepeatOp>(),
2531  "Should be a RepeatOp or StaticPRepeatOp");
2532  if (op.getCount() == 0) {
2533  Block *controlBody = op.getBodyBlock();
2534  for (auto &op : make_early_inc_range(*controlBody))
2535  op.erase();
2536 
2537  rewriter.eraseOp(op);
2538  return success();
2539  }
2540 
2541  return failure();
2542 }
2543 
2544 void StaticRepeatOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2545  MLIRContext *context) {
2546  patterns.add(emptyControl<StaticRepeatOp>);
2547  patterns.add(zeroRepeat<StaticRepeatOp>);
2548 }
2549 
2550 //===----------------------------------------------------------------------===//
2551 // RepeatOp
2552 //===----------------------------------------------------------------------===//
2553 void RepeatOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2554  MLIRContext *context) {
2555  patterns.add(emptyControl<RepeatOp>);
2556  patterns.add(zeroRepeat<RepeatOp>);
2557 }
2558 
2559 //===----------------------------------------------------------------------===//
2560 // InvokeOp
2561 //===----------------------------------------------------------------------===//
2562 
2563 // Parse the parameter list of invoke.
2564 static ParseResult
2565 parseParameterList(OpAsmParser &parser, OperationState &result,
2566  SmallVectorImpl<OpAsmParser::UnresolvedOperand> &ports,
2567  SmallVectorImpl<OpAsmParser::UnresolvedOperand> &inputs,
2568  SmallVectorImpl<Attribute> &portNames,
2569  SmallVectorImpl<Attribute> &inputNames,
2570  SmallVectorImpl<Type> &types) {
2571  OpAsmParser::UnresolvedOperand port;
2572  OpAsmParser::UnresolvedOperand input;
2573  Type type;
2574  auto parseParameter = [&]() -> ParseResult {
2575  if (parser.parseOperand(port) || parser.parseEqual() ||
2576  parser.parseOperand(input))
2577  return failure();
2578  ports.push_back(port);
2579  portNames.push_back(StringAttr::get(parser.getContext(), port.name));
2580  inputs.push_back(input);
2581  inputNames.push_back(StringAttr::get(parser.getContext(), input.name));
2582  return success();
2583  };
2584  if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2585  parseParameter))
2586  return failure();
2587  if (parser.parseArrow())
2588  return failure();
2589  auto parseType = [&]() -> ParseResult {
2590  if (parser.parseType(type))
2591  return failure();
2592  types.push_back(type);
2593  return success();
2594  };
2595  return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2596  parseType);
2597 }
2598 
2599 ParseResult InvokeOp::parse(OpAsmParser &parser, OperationState &result) {
2600  StringAttr componentName;
2601  SmallVector<OpAsmParser::UnresolvedOperand, 4> ports;
2602  SmallVector<OpAsmParser::UnresolvedOperand, 4> inputs;
2603  SmallVector<Attribute> portNames;
2604  SmallVector<Attribute> inputNames;
2605  SmallVector<Type, 4> types;
2606  if (parser.parseSymbolName(componentName))
2607  return failure();
2608  FlatSymbolRefAttr callee = FlatSymbolRefAttr::get(componentName);
2609  SMLoc loc = parser.getCurrentLocation();
2610  result.addAttribute("callee", callee);
2611  if (parseParameterList(parser, result, ports, inputs, portNames, inputNames,
2612  types))
2613  return failure();
2614  if (parser.resolveOperands(ports, types, loc, result.operands))
2615  return failure();
2616  if (parser.resolveOperands(inputs, types, loc, result.operands))
2617  return failure();
2618  result.addAttribute("portNames",
2619  ArrayAttr::get(parser.getContext(), portNames));
2620  result.addAttribute("inputNames",
2621  ArrayAttr::get(parser.getContext(), inputNames));
2622  return success();
2623 }
2624 
2625 void InvokeOp::print(OpAsmPrinter &p) {
2626  p << " @" << getCallee() << "(";
2627  auto ports = getPorts();
2628  auto inputs = getInputs();
2629  llvm::interleaveComma(llvm::zip(ports, inputs), p, [&](auto arg) {
2630  p << std::get<0>(arg) << " = " << std::get<1>(arg);
2631  });
2632  p << ") -> (";
2633  llvm::interleaveComma(ports, p, [&](auto port) { p << port.getType(); });
2634  p << ")";
2635 }
2636 
2637 // Check the direction of one of the ports in one of the connections of an
2638 // InvokeOp.
2639 static LogicalResult verifyInvokeOpValue(InvokeOp &op, Value &value,
2640  bool isDestination) {
2641  if (isPort(value))
2642  return verifyPortDirection(op, value, isDestination);
2643  return success();
2644 }
2645 
2646 // Checks if the value comes from complex logic.
2647 static LogicalResult verifyComplexLogic(InvokeOp &op, Value &value) {
2648  // Refer to the above function verifyNotComplexSource for its role.
2649  Operation *operation = value.getDefiningOp();
2650  if (operation == nullptr)
2651  return success();
2652  if (auto *dialect = operation->getDialect(); isa<comb::CombDialect>(dialect))
2653  return failure();
2654  return success();
2655 }
2656 
2657 // Get the go port of the invoked component.
2658 Value InvokeOp::getInstGoValue() {
2659  ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2660  Operation *operation = componentOp.lookupSymbol(getCallee());
2661  Value ret = nullptr;
2662  llvm::TypeSwitch<Operation *>(operation)
2663  .Case<RegisterOp>([&](auto op) { ret = operation->getResult(1); })
2664  .Case<MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2665  RemSPipeLibOp, RemUPipeLibOp>(
2666  [&](auto op) { ret = operation->getResult(2); })
2667  .Case<InstanceOp>([&](auto op) {
2668  auto portInfo = op.getReferencedComponent().getPortInfo();
2669  for (auto [portInfo, res] :
2670  llvm::zip(portInfo, operation->getResults())) {
2671  if (portInfo.hasAttribute(goPort))
2672  ret = res;
2673  }
2674  })
2675  .Case<PrimitiveOp>([&](auto op) {
2676  auto moduleExternOp = op.getReferencedPrimitive();
2677  auto argAttrs = moduleExternOp.getAllInputAttrs();
2678  for (auto [attr, res] : llvm::zip(argAttrs, op.getResults())) {
2679  if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2680  if (!dictAttr.empty()) {
2681  if (dictAttr.begin()->getName().getValue() == "calyx.go")
2682  ret = res;
2683  }
2684  }
2685  }
2686  });
2687  return ret;
2688 }
2689 
2690 // Get the done port of the invoked component.
2691 Value InvokeOp::getInstDoneValue() {
2692  ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2693  Operation *operation = componentOp.lookupSymbol(getCallee());
2694  Value ret = nullptr;
2695  llvm::TypeSwitch<Operation *>(operation)
2696  .Case<RegisterOp, MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2697  RemSPipeLibOp, RemUPipeLibOp>([&](auto op) {
2698  size_t doneIdx = operation->getResults().size() - 1;
2699  ret = operation->getResult(doneIdx);
2700  })
2701  .Case<InstanceOp>([&](auto op) {
2702  InstanceOp instanceOp = cast<InstanceOp>(operation);
2703  auto portInfo = instanceOp.getReferencedComponent().getPortInfo();
2704  for (auto [portInfo, res] :
2705  llvm::zip(portInfo, operation->getResults())) {
2706  if (portInfo.hasAttribute(donePort))
2707  ret = res;
2708  }
2709  })
2710  .Case<PrimitiveOp>([&](auto op) {
2711  PrimitiveOp primOp = cast<PrimitiveOp>(operation);
2712  auto moduleExternOp = primOp.getReferencedPrimitive();
2713  auto resAttrs = moduleExternOp.getAllOutputAttrs();
2714  for (auto [attr, res] : llvm::zip(resAttrs, primOp.getResults())) {
2715  if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2716  if (!dictAttr.empty()) {
2717  if (dictAttr.begin()->getName().getValue() == "calyx.done")
2718  ret = res;
2719  }
2720  }
2721  }
2722  });
2723  return ret;
2724 }
2725 
2726 // A helper function that gets the number of go or done ports in
2727 // hw.module.extern.
2728 static size_t
2729 getHwModuleExtGoOrDonePortNumber(hw::HWModuleExternOp &moduleExternOp,
2730  bool isGo) {
2731  size_t ret = 0;
2732  std::string str = isGo ? "calyx.go" : "calyx.done";
2733  for (Attribute attr : moduleExternOp.getAllInputAttrs()) {
2734  if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2735  ret = llvm::count_if(dictAttr, [&](NamedAttribute iter) {
2736  return iter.getName().getValue() == str;
2737  });
2738  }
2739  }
2740  return ret;
2741 }
2742 
2743 LogicalResult InvokeOp::verify() {
2744  ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2745  StringRef callee = getCallee();
2746  Operation *operation = componentOp.lookupSymbol(callee);
2747  // The referenced symbol does not exist.
2748  if (!operation)
2749  return emitOpError() << "with instance '@" << callee
2750  << "', which does not exist.";
2751  // The argument list of invoke is empty.
2752  if (getInputs().empty())
2753  return emitOpError() << "'@" << callee
2754  << "' has zero input and output port connections; "
2755  "expected at least one.";
2756  size_t goPortNum = 0, donePortNum = 0;
2757  // They both have a go port and a done port, but the "go" port for
2758  // registers and memrey should be "write_en" port.
2759  llvm::TypeSwitch<Operation *>(operation)
2760  .Case<RegisterOp, DivSPipeLibOp, DivUPipeLibOp, MemoryOp, MultPipeLibOp,
2761  RemSPipeLibOp, RemUPipeLibOp>(
2762  [&](auto op) { goPortNum = 1, donePortNum = 1; })
2763  .Case<InstanceOp>([&](auto op) {
2764  auto portInfo = op.getReferencedComponent().getPortInfo();
2765  for (PortInfo info : portInfo) {
2766  if (info.hasAttribute(goPort))
2767  ++goPortNum;
2768  if (info.hasAttribute(donePort))
2769  ++donePortNum;
2770  }
2771  })
2772  .Case<PrimitiveOp>([&](auto op) {
2773  auto moduleExternOp = op.getReferencedPrimitive();
2774  // Get the number of go ports and done ports by their attrubutes.
2775  goPortNum = getHwModuleExtGoOrDonePortNumber(moduleExternOp, true);
2776  donePortNum = getHwModuleExtGoOrDonePortNumber(moduleExternOp, false);
2777  });
2778  // If the number of go ports and done ports is wrong.
2779  if (goPortNum != 1 && donePortNum != 1)
2780  return emitOpError()
2781  << "'@" << callee << "'"
2782  << " is a combinational component and cannot be invoked, which must "
2783  "have single go port and single done port.";
2784 
2785  auto ports = getPorts();
2786  auto inputs = getInputs();
2787  // We have verified earlier that the instance has a go and a done port.
2788  Value goValue = getInstGoValue();
2789  Value doneValue = getInstDoneValue();
2790  for (auto [port, input, portName, inputName] :
2791  llvm::zip(ports, inputs, getPortNames(), getInputNames())) {
2792  // Check the direction of these destination ports.
2793  // 'calyx.invoke' op '@r0' has input '%r.out', which is a source port. The
2794  // inputs are required to be destination ports.
2795  if (failed(verifyInvokeOpValue(*this, port, true)))
2796  return emitOpError() << "'@" << callee << "' has input '"
2797  << portName.cast<StringAttr>().getValue()
2798  << "', which is a source port. The inputs are "
2799  "required to be destination ports.";
2800  // The go port should not appear in the parameter list.
2801  if (port == goValue)
2802  return emitOpError() << "the go or write_en port of '@" << callee
2803  << "' cannot appear here.";
2804  // Check the direction of these source ports.
2805  if (failed(verifyInvokeOpValue(*this, input, false)))
2806  return emitOpError() << "'@" << callee << "' has output '"
2807  << inputName.cast<StringAttr>().getValue()
2808  << "', which is a destination port. The inputs are "
2809  "required to be source ports.";
2810  if (failed(verifyComplexLogic(*this, input)))
2811  return emitOpError() << "'@" << callee << "' has '"
2812  << inputName.cast<StringAttr>().getValue()
2813  << "', which is not a port or constant. Complex "
2814  "logic should be conducted in the guard.";
2815  if (input == doneValue)
2816  return emitOpError() << "the done port of '@" << callee
2817  << "' cannot appear here.";
2818  // Check if the connection uses the callee's port.
2819  if (port.getDefiningOp() != operation && input.getDefiningOp() != operation)
2820  return emitOpError() << "the connection "
2821  << portName.cast<StringAttr>().getValue() << " = "
2822  << inputName.cast<StringAttr>().getValue()
2823  << " is not defined as an input port of '@" << callee
2824  << "'.";
2825  }
2826  return success();
2827 }
2828 
2829 //===----------------------------------------------------------------------===//
2830 // Calyx library ops
2831 //===----------------------------------------------------------------------===//
2832 
2833 LogicalResult PadLibOp::verify() {
2834  unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2835  unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2836  if (inBits >= outBits)
2837  return emitOpError("expected input bits (")
2838  << inBits << ')' << " to be less than output bits (" << outBits
2839  << ')';
2840  return success();
2841 }
2842 
2843 LogicalResult SliceLibOp::verify() {
2844  unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2845  unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2846  if (inBits <= outBits)
2847  return emitOpError("expected input bits (")
2848  << inBits << ')' << " to be greater than output bits (" << outBits
2849  << ')';
2850  return success();
2851 }
2852 
2853 #define ImplBinPipeOpCellInterface(OpType, outName) \
2854  SmallVector<StringRef> OpType::portNames() { \
2855  return {clkPort, resetPort, goPort, "left", "right", outName, donePort}; \
2856  } \
2857  \
2858  SmallVector<Direction> OpType::portDirections() { \
2859  return {Input, Input, Input, Input, Input, Output, Output}; \
2860  } \
2861  \
2862  void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2863  getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2864  } \
2865  \
2866  SmallVector<DictionaryAttr> OpType::portAttributes() { \
2867  MLIRContext *context = getContext(); \
2868  IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1); \
2869  NamedAttrList go, clk, reset, done; \
2870  go.append(goPort, isSet); \
2871  clk.append(clkPort, isSet); \
2872  reset.append(resetPort, isSet); \
2873  done.append(donePort, isSet); \
2874  return { \
2875  clk.getDictionary(context), /* Clk */ \
2876  reset.getDictionary(context), /* Reset */ \
2877  go.getDictionary(context), /* Go */ \
2878  DictionaryAttr::get(context), /* Lhs */ \
2879  DictionaryAttr::get(context), /* Rhs */ \
2880  DictionaryAttr::get(context), /* Out */ \
2881  done.getDictionary(context) /* Done */ \
2882  }; \
2883  } \
2884  \
2885  bool OpType::isCombinational() { return false; }
2886 
2887 #define ImplUnaryOpCellInterface(OpType) \
2888  SmallVector<StringRef> OpType::portNames() { return {"in", "out"}; } \
2889  SmallVector<Direction> OpType::portDirections() { return {Input, Output}; } \
2890  SmallVector<DictionaryAttr> OpType::portAttributes() { \
2891  return {DictionaryAttr::get(getContext()), \
2892  DictionaryAttr::get(getContext())}; \
2893  } \
2894  bool OpType::isCombinational() { return true; } \
2895  void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2896  getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2897  }
2898 
2899 #define ImplBinOpCellInterface(OpType) \
2900  SmallVector<StringRef> OpType::portNames() { \
2901  return {"left", "right", "out"}; \
2902  } \
2903  SmallVector<Direction> OpType::portDirections() { \
2904  return {Input, Input, Output}; \
2905  } \
2906  void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2907  getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2908  } \
2909  bool OpType::isCombinational() { return true; } \
2910  SmallVector<DictionaryAttr> OpType::portAttributes() { \
2911  return {DictionaryAttr::get(getContext()), \
2912  DictionaryAttr::get(getContext()), \
2913  DictionaryAttr::get(getContext())}; \
2914  }
2915 
2916 // clang-format off
2917 ImplBinPipeOpCellInterface(MultPipeLibOp, "out")
2918 ImplBinPipeOpCellInterface(DivUPipeLibOp, "out_quotient")
2919 ImplBinPipeOpCellInterface(DivSPipeLibOp, "out_quotient")
2920 ImplBinPipeOpCellInterface(RemUPipeLibOp, "out_remainder")
2921 ImplBinPipeOpCellInterface(RemSPipeLibOp, "out_remainder")
2922 
2923 ImplUnaryOpCellInterface(PadLibOp)
2924 ImplUnaryOpCellInterface(SliceLibOp)
2925 ImplUnaryOpCellInterface(NotLibOp)
2926 ImplUnaryOpCellInterface(WireLibOp)
2927 ImplUnaryOpCellInterface(ExtSILibOp)
2928 
2929 ImplBinOpCellInterface(LtLibOp)
2930 ImplBinOpCellInterface(GtLibOp)
2931 ImplBinOpCellInterface(EqLibOp)
2932 ImplBinOpCellInterface(NeqLibOp)
2933 ImplBinOpCellInterface(GeLibOp)
2934 ImplBinOpCellInterface(LeLibOp)
2935 ImplBinOpCellInterface(SltLibOp)
2936 ImplBinOpCellInterface(SgtLibOp)
2937 ImplBinOpCellInterface(SeqLibOp)
2938 ImplBinOpCellInterface(SneqLibOp)
2939 ImplBinOpCellInterface(SgeLibOp)
2940 ImplBinOpCellInterface(SleLibOp)
2941 
2942 ImplBinOpCellInterface(AddLibOp)
2943 ImplBinOpCellInterface(SubLibOp)
2944 ImplBinOpCellInterface(ShruLibOp)
2945 ImplBinOpCellInterface(RshLibOp)
2946 ImplBinOpCellInterface(SrshLibOp)
2947 ImplBinOpCellInterface(LshLibOp)
2948 ImplBinOpCellInterface(AndLibOp)
2949 ImplBinOpCellInterface(OrLibOp)
2950 ImplBinOpCellInterface(XorLibOp)
2951 // clang-format on
2952 
2953 //===----------------------------------------------------------------------===//
2954 // TableGen generated logic.
2955 //===----------------------------------------------------------------------===//
2956 
2957 #include "circt/Dialect/Calyx/CalyxInterfaces.cpp.inc"
2958 
2959 // Provide the autogenerated implementation guts for the Op classes.
2960 #define GET_OP_CLASSES
2961 #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:1667
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:1460
static ParseResult parseParameterList(OpAsmParser &parser, SmallVector< Attribute > &parameters)
Parse an parameter list if present.
Definition: CalyxOps.cpp:1842
static SmallVector< PortInfo > getFilteredPorts(ComponentOp op, Pred p)
A helper function to return a filtered subset of a component's ports.
Definition: CalyxOps.cpp:679
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:1311
#define ImplBinPipeOpCellInterface(OpType, outName)
Definition: CalyxOps.cpp:2853
static bool portIsUsedInGroup(GroupInterface group, Value port, bool isDriven)
Determines whether the given port is used in the group.
Definition: CalyxOps.cpp:1201
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:1560
static LogicalResult collapseControl(OpTy controlOp, PatternRewriter &rewriter)
Definition: CalyxOps.cpp:311
Direction convertHWDirectionToCalyx(hw::ModulePort::Direction direction)
Definition: CalyxOps.cpp:1778
static LogicalResult anyPortsReadByGroup(GroupInterface group, ValueRange ports)
Checks whether any ports are read within the group.
Definition: CalyxOps.cpp:1290
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:704
static LogicalResult anyPortsDrivenByGroup(GroupInterface group, ValueRange ports)
Checks whether any ports are driven within the group.
Definition: CalyxOps.cpp:1270
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:2899
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:1223
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:1413
static LogicalResult allPortsDrivenByGroup(GroupInterface group, ValueRange ports)
Checks whether all ports are driven within the group.
Definition: CalyxOps.cpp:1250
static LogicalResult verifyPortDirection(Operation *op, Value value, bool isDestination)
Determines whether the given direction is valid with the given inputs.
Definition: CalyxOps.cpp:1430
static LogicalResult isCombinational(Value value, GroupInterface group)
Verifies the defining operation of a value is combinational.
Definition: CalyxOps.cpp:1049
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:1880
static DictionaryAttr cleanCalyxPortAttrs(OpBuilder builder, DictionaryAttr dict)
Returns a new DictionaryAttr containing only the calyx dialect attrs in the input DictionaryAttr.
Definition: CalyxOps.cpp:1803
static void printGroupPort(OpAsmPrinter &p, GroupPortType op)
Definition: CalyxOps.cpp:296
#define ImplUnaryOpCellInterface(OpType)
Definition: CalyxOps.cpp:2887
static ParseResult parseGroupPort(OpAsmParser &parser, OperationState &result)
Definition: CalyxOps.cpp:268
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:
Builder builder
Signals that the following operation is combinational.
Definition: CalyxOps.h:55
def type(self)
Definition: hw.py:191
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:1391
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:186
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