CIRCT  18.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/BuiltinTypes.h"
21 #include "mlir/IR/Diagnostics.h"
22 #include "mlir/IR/DialectImplementation.h"
23 #include "mlir/IR/PatternMatch.h"
24 #include "mlir/IR/SymbolTable.h"
25 #include "mlir/Interfaces/FunctionImplementation.h"
26 #include "mlir/Support/LLVM.h"
27 #include "llvm/ADT/DenseMap.h"
28 #include "llvm/ADT/PriorityQueue.h"
29 #include "llvm/ADT/STLExtras.h"
30 #include "llvm/ADT/SmallSet.h"
31 #include "llvm/ADT/StringExtras.h"
32 #include "llvm/ADT/TypeSwitch.h"
33 #include "llvm/Support/Casting.h"
34 
35 using namespace circt;
36 using namespace circt::calyx;
37 using namespace mlir;
38 
39 namespace {
40 
41 // A struct to enforce that the LHS template is one of the RHS templates.
42 // For example:
43 // std::is_any<uint32_t, uint16_t, float, int32_t>::value is false.
44 template <class T, class... Ts>
45 struct IsAny : std::disjunction<std::is_same<T, Ts>...> {};
46 
47 } // namespace
48 
49 //===----------------------------------------------------------------------===//
50 // Utilities related to Direction
51 //===----------------------------------------------------------------------===//
52 
53 Direction direction::get(bool isOutput) {
54  return static_cast<Direction>(isOutput);
55 }
56 
57 IntegerAttr direction::packAttribute(MLIRContext *ctx, size_t nIns,
58  size_t nOuts) {
59  // Pack the array of directions into an APInt. Input direction is zero,
60  // output direction is one.
61  size_t numDirections = nIns + nOuts;
62  APInt portDirections(/*width=*/numDirections, /*value=*/0);
63  for (size_t i = nIns, e = numDirections; i != e; ++i)
64  portDirections.setBit(i);
65 
66  return IntegerAttr::get(IntegerType::get(ctx, numDirections), portDirections);
67 }
68 
69 //===----------------------------------------------------------------------===//
70 // Utilities
71 //===----------------------------------------------------------------------===//
72 
73 /// This pattern collapses a calyx.seq or calyx.par operation when it
74 /// contains exactly one calyx.enable operation.
75 template <typename CtrlOp>
78  LogicalResult matchAndRewrite(CtrlOp ctrlOp,
79  PatternRewriter &rewriter) const override {
80  auto &ops = ctrlOp.getBodyBlock()->getOperations();
81  bool isUnaryControl =
82  (ops.size() == 1) && isa<EnableOp>(ops.front()) &&
83  isa<SeqOp, ParOp, StaticSeqOp, StaticParOp>(ctrlOp->getParentOp());
84  if (!isUnaryControl)
85  return failure();
86 
87  ops.front().moveBefore(ctrlOp);
88  rewriter.eraseOp(ctrlOp);
89  return success();
90  }
91 };
92 
93 /// Verify that the value is not a "complex" value. For example, the source
94 /// of an AssignOp should be a constant or port, e.g.
95 /// %and = comb.and %a, %b : i1
96 /// calyx.assign %port = %c1_i1 ? %and : i1 // Incorrect
97 /// calyx.assign %port = %and ? %c1_i1 : i1 // Correct
98 /// TODO(Calyx): This is useful to verify current MLIR can be lowered to the
99 /// native compiler. Remove this when Calyx supports wire declarations.
100 /// See: https://github.com/llvm/circt/pull/1774 for context.
101 template <typename Op>
102 static LogicalResult verifyNotComplexSource(Op op) {
103  Operation *definingOp = op.getSrc().getDefiningOp();
104  if (definingOp == nullptr)
105  // This is a port of the parent component.
106  return success();
107 
108  // Currently, we use the Combinational dialect to perform logical operations
109  // on wires, i.e. comb::AndOp, comb::OrOp, comb::XorOp.
110  if (auto dialect = definingOp->getDialect(); isa<comb::CombDialect>(dialect))
111  return op->emitOpError("has source that is not a port or constant. "
112  "Complex logic should be conducted in the guard.");
113 
114  return success();
115 }
116 
117 /// Convenience function for getting the SSA name of `v` under the scope of
118 /// operation `scopeOp`.
119 static std::string valueName(Operation *scopeOp, Value v) {
120  std::string s;
121  llvm::raw_string_ostream os(s);
122  // CAVEAT: Since commit 27df7158fe MLIR prefers verifiers to print errors for
123  // operations in generic form, and the printer by default runs a verification.
124  // `valueName` is used in some of these verifiers where preferably the generic
125  // operand form should be used instead.
126  AsmState asmState(scopeOp, OpPrintingFlags().assumeVerified());
127  v.printAsOperand(os, asmState);
128  return s;
129 }
130 
131 /// Returns whether this value is either (1) a port on a ComponentOp or (2) a
132 /// port on a cell interface.
133 static bool isPort(Value value) {
134  Operation *definingOp = value.getDefiningOp();
135  return value.isa<BlockArgument>() ||
136  (definingOp && isa<CellInterface>(definingOp));
137 }
138 
139 /// Gets the port for a given BlockArgument.
140 PortInfo calyx::getPortInfo(BlockArgument arg) {
141  Operation *op = arg.getOwner()->getParentOp();
142  assert(isa<ComponentInterface>(op) &&
143  "Only ComponentInterface should support lookup by BlockArgument.");
144  return cast<ComponentInterface>(op).getPortInfo()[arg.getArgNumber()];
145 }
146 
147 /// Returns whether the given operation has a control region.
148 static bool hasControlRegion(Operation *op) {
149  return isa<ControlOp, SeqOp, IfOp, RepeatOp, WhileOp, ParOp, StaticRepeatOp,
150  StaticParOp, StaticSeqOp, StaticIfOp>(op);
151 }
152 
153 /// Returns whether the given operation is a static control operator
154 static bool isStaticControl(Operation *op) {
155  if (isa<EnableOp>(op)) {
156  // for enables, we need to check whether its corresponding group is static
157  auto component = op->getParentOfType<ComponentOp>();
158  auto enableOp = llvm::cast<EnableOp>(op);
159  StringRef groupName = enableOp.getGroupName();
160  auto group = component.getWiresOp().lookupSymbol<GroupInterface>(groupName);
161  return isa<StaticGroupOp>(group);
162  }
163  return isa<StaticIfOp, StaticSeqOp, StaticRepeatOp, StaticParOp>(op);
164 }
165 
166 /// Verifies the body of a ControlLikeOp.
167 static LogicalResult verifyControlBody(Operation *op) {
168  if (isa<SeqOp, ParOp, StaticSeqOp, StaticParOp>(op))
169  // This does not apply to sequential and parallel regions.
170  return success();
171 
172  // Some ControlLike operations have (possibly) multiple regions, e.g. IfOp.
173  for (auto &region : op->getRegions()) {
174  auto opsIt = region.getOps();
175  size_t numOperations = std::distance(opsIt.begin(), opsIt.end());
176  // A body of a ControlLike operation may have a single EnableOp within it.
177  // However, that must be the only operation.
178  // E.g. Allowed: calyx.control { calyx.enable @A }
179  // Not Allowed: calyx.control { calyx.enable @A calyx.seq { ... } }
180  bool usesEnableAsCompositionOperator =
181  numOperations > 1 && llvm::any_of(region.front(), [](auto &&bodyOp) {
182  return isa<EnableOp>(bodyOp);
183  });
184  if (usesEnableAsCompositionOperator)
185  return op->emitOpError(
186  "EnableOp is not a composition operator. It should be nested "
187  "in a control flow operation, such as \"calyx.seq\"");
188 
189  // Verify that multiple control flow operations are nested inside a single
190  // control operator. See: https://github.com/llvm/circt/issues/1723
191  size_t numControlFlowRegions =
192  llvm::count_if(opsIt, [](auto &&op) { return hasControlRegion(&op); });
193  if (numControlFlowRegions > 1)
194  return op->emitOpError(
195  "has an invalid control sequence. Multiple control flow operations "
196  "must all be nested in a single calyx.seq or calyx.par");
197  }
198  return success();
199 }
200 
201 LogicalResult calyx::verifyComponent(Operation *op) {
202  auto *opParent = op->getParentOp();
203  if (!isa<ModuleOp>(opParent))
204  return op->emitOpError()
205  << "has parent: " << opParent << ", expected ModuleOp.";
206  return success();
207 }
208 
209 LogicalResult calyx::verifyCell(Operation *op) {
210  auto opParent = op->getParentOp();
211  if (!isa<ComponentInterface>(opParent))
212  return op->emitOpError()
213  << "has parent: " << opParent << ", expected ComponentInterface.";
214  return success();
215 }
216 
217 LogicalResult calyx::verifyControlLikeOp(Operation *op) {
218  auto parent = op->getParentOp();
219 
220  if (isa<calyx::EnableOp>(op) &&
221  !isa<calyx::CalyxDialect>(parent->getDialect())) {
222  // Allow embedding calyx.enable ops within other dialects. This is motivated
223  // by allowing experimentation with new styles of Calyx lowering. For more
224  // info and the historical discussion, see:
225  // https://github.com/llvm/circt/pull/3211
226  return success();
227  }
228 
229  if (!hasControlRegion(parent))
230  return op->emitOpError()
231  << "has parent: " << parent
232  << ", which is not allowed for a control-like operation.";
233 
234  if (op->getNumRegions() == 0)
235  return success();
236 
237  auto &region = op->getRegion(0);
238  // Operations that are allowed in the body of a ControlLike op.
239  auto isValidBodyOp = [](Operation *operation) {
240  return isa<EnableOp, InvokeOp, SeqOp, IfOp, RepeatOp, WhileOp, ParOp,
241  StaticParOp, StaticRepeatOp, StaticSeqOp, StaticIfOp>(operation);
242  };
243  for (auto &&bodyOp : region.front()) {
244  if (isValidBodyOp(&bodyOp))
245  continue;
246 
247  return op->emitOpError()
248  << "has operation: " << bodyOp.getName()
249  << ", which is not allowed in this control-like operation";
250  }
251  return verifyControlBody(op);
252 }
253 
254 LogicalResult calyx::verifyIf(Operation *op) {
255  auto ifOp = dyn_cast<IfInterface>(op);
256 
257  if (ifOp.elseBodyExists() && ifOp.getElseBody()->empty())
258  return ifOp->emitOpError() << "empty 'else' region.";
259 
260  return success();
261 }
262 
263 // Helper function for parsing a group port operation, i.e. GroupDoneOp and
264 // GroupPortOp. These may take one of two different forms:
265 // (1) %<guard> ? %<src> : i1
266 // (2) %<src> : i1
267 static ParseResult parseGroupPort(OpAsmParser &parser, OperationState &result) {
268  SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
269  OpAsmParser::UnresolvedOperand guardOrSource;
270  if (parser.parseOperand(guardOrSource))
271  return failure();
272 
273  if (succeeded(parser.parseOptionalQuestion())) {
274  OpAsmParser::UnresolvedOperand source;
275  // The guard exists.
276  if (parser.parseOperand(source))
277  return failure();
278  operandInfos.push_back(source);
279  }
280  // No matter if this is the source or guard, it should be last.
281  operandInfos.push_back(guardOrSource);
282 
283  Type type;
284  // Resolving the operands with the same type works here since the source and
285  // guard of a group port is always i1.
286  if (parser.parseColonType(type) ||
287  parser.resolveOperands(operandInfos, type, result.operands))
288  return failure();
289 
290  return success();
291 }
292 
293 // A helper function for printing group ports, i.e. GroupGoOp and GroupDoneOp.
294 template <typename GroupPortType>
295 static void printGroupPort(OpAsmPrinter &p, GroupPortType op) {
296  static_assert(IsAny<GroupPortType, GroupGoOp, GroupDoneOp>(),
297  "Should be a Calyx Group port.");
298 
299  p << " ";
300  // The guard is optional.
301  Value guard = op.getGuard(), source = op.getSrc();
302  if (guard)
303  p << guard << " ? ";
304  p << source << " : " << source.getType();
305 }
306 
307 // Collapse nested control of the same type for SeqOp and ParOp, e.g.
308 // calyx.seq { calyx.seq { ... } } -> calyx.seq { ... }
309 template <typename OpTy>
310 static LogicalResult collapseControl(OpTy controlOp,
311  PatternRewriter &rewriter) {
312  static_assert(IsAny<OpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
313  "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp");
314 
315  if (isa<OpTy>(controlOp->getParentOp())) {
316  Block *controlBody = controlOp.getBodyBlock();
317  for (auto &op : make_early_inc_range(*controlBody))
318  op.moveBefore(controlOp);
319 
320  rewriter.eraseOp(controlOp);
321  return success();
322  }
323 
324  return failure();
325 }
326 
327 template <typename OpTy>
328 static LogicalResult emptyControl(OpTy controlOp, PatternRewriter &rewriter) {
329  if (controlOp.getBodyBlock()->empty()) {
330  rewriter.eraseOp(controlOp);
331  return success();
332  }
333  return failure();
334 }
335 
336 /// A helper function to check whether the conditional and group (if it exists)
337 /// needs to be erased to maintain a valid state of a Calyx program. If these
338 /// have no more uses, they will be erased.
339 template <typename OpTy>
341  PatternRewriter &rewriter) {
342  static_assert(IsAny<OpTy, IfOp, WhileOp>(),
343  "This is only applicable to WhileOp and IfOp.");
344 
345  // Save information about the operation, and erase it.
346  Value cond = op.getCond();
347  std::optional<StringRef> groupName = op.getGroupName();
348  auto component = op->template getParentOfType<ComponentOp>();
349  rewriter.eraseOp(op);
350 
351  // Clean up the attached conditional and combinational group (if it exists).
352  if (groupName) {
353  auto group = component.getWiresOp().template lookupSymbol<GroupInterface>(
354  *groupName);
355  if (SymbolTable::symbolKnownUseEmpty(group, component.getRegion()))
356  rewriter.eraseOp(group);
357  }
358  // Check the conditional after the Group, since it will be driven within.
359  if (!cond.isa<BlockArgument>() && cond.getDefiningOp()->use_empty())
360  rewriter.eraseOp(cond.getDefiningOp());
361 }
362 
363 /// A helper function to check whether the conditional needs to be erased
364 /// to maintain a valid state of a Calyx program. If these
365 /// have no more uses, they will be erased.
366 template <typename OpTy>
367 static void eraseControlWithConditional(OpTy op, PatternRewriter &rewriter) {
368  static_assert(std::is_same<OpTy, StaticIfOp>(),
369  "This is only applicable to StatifIfOp.");
370 
371  // Save information about the operation, and erase it.
372  Value cond = op.getCond();
373  rewriter.eraseOp(op);
374 
375  // Check if conditional is still needed, and remove if it isn't
376  if (!cond.isa<BlockArgument>() && cond.getDefiningOp()->use_empty())
377  rewriter.eraseOp(cond.getDefiningOp());
378 }
379 
380 //===----------------------------------------------------------------------===//
381 // ComponentInterface
382 //===----------------------------------------------------------------------===//
383 
384 template <typename ComponentTy>
385 static void printComponentInterface(OpAsmPrinter &p, ComponentInterface comp) {
386  auto componentName = comp->template getAttrOfType<StringAttr>(
387  ::mlir::SymbolTable::getSymbolAttrName())
388  .getValue();
389  p << " ";
390  p.printSymbolName(componentName);
391 
392  // Print the port definition list for input and output ports.
393  auto printPortDefList = [&](auto ports) {
394  p << "(";
395  llvm::interleaveComma(ports, p, [&](const PortInfo &port) {
396  p << "%" << port.name.getValue() << ": " << port.type;
397  if (!port.attributes.empty()) {
398  p << " ";
399  p.printAttributeWithoutType(port.attributes);
400  }
401  });
402  p << ")";
403  };
404  printPortDefList(comp.getInputPortInfo());
405  p << " -> ";
406  printPortDefList(comp.getOutputPortInfo());
407 
408  p << " ";
409  p.printRegion(*comp.getRegion(), /*printEntryBlockArgs=*/false,
410  /*printBlockTerminators=*/false,
411  /*printEmptyBlock=*/false);
412 
413  SmallVector<StringRef> elidedAttrs = {
414  "portAttributes",
415  "portNames",
416  "portDirections",
417  "sym_name",
418  ComponentTy::getFunctionTypeAttrName(comp->getName()),
419  ComponentTy::getArgAttrsAttrName(comp->getName()),
420  ComponentTy::getResAttrsAttrName(comp->getName())};
421  p.printOptionalAttrDict(comp->getAttrs(), elidedAttrs);
422 }
423 
424 /// Parses the ports of a Calyx component signature, and adds the corresponding
425 /// port names to `attrName`.
426 static ParseResult
427 parsePortDefList(OpAsmParser &parser, OperationState &result,
428  SmallVectorImpl<OpAsmParser::Argument> &ports,
429  SmallVectorImpl<Type> &portTypes,
430  SmallVectorImpl<NamedAttrList> &portAttrs) {
431  auto parsePort = [&]() -> ParseResult {
432  OpAsmParser::Argument port;
433  Type portType;
434  // Expect each port to have the form `%<ssa-name> : <type>`.
435  if (parser.parseArgument(port) || parser.parseColon() ||
436  parser.parseType(portType))
437  return failure();
438  port.type = portType;
439  ports.push_back(port);
440  portTypes.push_back(portType);
441 
442  NamedAttrList portAttr;
443  portAttrs.push_back(succeeded(parser.parseOptionalAttrDict(portAttr))
444  ? portAttr
445  : NamedAttrList());
446  return success();
447  };
448 
449  return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
450  parsePort);
451 }
452 
453 /// Parses the signature of a Calyx component.
454 static ParseResult
455 parseComponentSignature(OpAsmParser &parser, OperationState &result,
456  SmallVectorImpl<OpAsmParser::Argument> &ports,
457  SmallVectorImpl<Type> &portTypes) {
458  SmallVector<OpAsmParser::Argument> inPorts, outPorts;
459  SmallVector<Type> inPortTypes, outPortTypes;
460  SmallVector<NamedAttrList> portAttributes;
461 
462  if (parsePortDefList(parser, result, inPorts, inPortTypes, portAttributes))
463  return failure();
464 
465  if (parser.parseArrow() ||
466  parsePortDefList(parser, result, outPorts, outPortTypes, portAttributes))
467  return failure();
468 
469  auto *context = parser.getBuilder().getContext();
470  // Add attribute for port names; these are currently
471  // just inferred from the SSA names of the component.
472  SmallVector<Attribute> portNames;
473  auto getPortName = [context](const auto &port) -> StringAttr {
474  StringRef name = port.ssaName.name;
475  if (name.startswith("%"))
476  name = name.drop_front();
477  return StringAttr::get(context, name);
478  };
479  llvm::transform(inPorts, std::back_inserter(portNames), getPortName);
480  llvm::transform(outPorts, std::back_inserter(portNames), getPortName);
481 
482  result.addAttribute("portNames", ArrayAttr::get(context, portNames));
483  result.addAttribute(
484  "portDirections",
485  direction::packAttribute(context, inPorts.size(), outPorts.size()));
486 
487  ports.append(inPorts);
488  ports.append(outPorts);
489  portTypes.append(inPortTypes);
490  portTypes.append(outPortTypes);
491 
492  SmallVector<Attribute> portAttrs;
493  llvm::transform(portAttributes, std::back_inserter(portAttrs),
494  [&](auto attr) { return attr.getDictionary(context); });
495  result.addAttribute("portAttributes", ArrayAttr::get(context, portAttrs));
496 
497  return success();
498 }
499 
500 template <typename ComponentTy>
501 static ParseResult parseComponentInterface(OpAsmParser &parser,
502  OperationState &result) {
503  using namespace mlir::function_interface_impl;
504 
505  StringAttr componentName;
506  if (parser.parseSymbolName(componentName,
507  ::mlir::SymbolTable::getSymbolAttrName(),
508  result.attributes))
509  return failure();
510 
511  SmallVector<mlir::OpAsmParser::Argument> ports;
512 
513  SmallVector<Type> portTypes;
514  if (parseComponentSignature(parser, result, ports, portTypes))
515  return failure();
516 
517  // Build the component's type for FunctionLike trait. All ports are listed
518  // as arguments so they may be accessed within the component.
519  auto type = parser.getBuilder().getFunctionType(portTypes, /*results=*/{});
520  result.addAttribute(ComponentTy::getFunctionTypeAttrName(result.name),
521  TypeAttr::get(type));
522 
523  auto *body = result.addRegion();
524  if (parser.parseRegion(*body, ports))
525  return failure();
526 
527  if (body->empty())
528  body->push_back(new Block());
529 
530  if (parser.parseOptionalAttrDict(result.attributes))
531  return failure();
532 
533  return success();
534 }
535 
536 /// Returns a new vector containing the concatenation of vectors `a` and `b`.
537 template <typename T>
538 static SmallVector<T> concat(const SmallVectorImpl<T> &a,
539  const SmallVectorImpl<T> &b) {
540  SmallVector<T> out;
541  out.append(a);
542  out.append(b);
543  return out;
544 }
545 
546 static void buildComponentLike(OpBuilder &builder, OperationState &result,
547  StringAttr name, ArrayRef<PortInfo> ports,
548  bool combinational) {
549  using namespace mlir::function_interface_impl;
550 
551  result.addAttribute(::mlir::SymbolTable::getSymbolAttrName(), name);
552 
553  std::pair<SmallVector<Type, 8>, SmallVector<Type, 8>> portIOTypes;
554  std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>> portIONames;
555  std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>>
556  portIOAttributes;
557  SmallVector<Direction, 8> portDirections;
558  // Avoid using llvm::partition or llvm::sort to preserve relative ordering
559  // between individual inputs and outputs.
560  for (auto &&port : ports) {
561  bool isInput = port.direction == Direction::Input;
562  (isInput ? portIOTypes.first : portIOTypes.second).push_back(port.type);
563  (isInput ? portIONames.first : portIONames.second).push_back(port.name);
564  (isInput ? portIOAttributes.first : portIOAttributes.second)
565  .push_back(port.attributes);
566  }
567  auto portTypes = concat(portIOTypes.first, portIOTypes.second);
568  auto portNames = concat(portIONames.first, portIONames.second);
569  auto portAttributes = concat(portIOAttributes.first, portIOAttributes.second);
570 
571  // Build the function type of the component.
572  auto functionType = builder.getFunctionType(portTypes, {});
573  if (combinational) {
574  result.addAttribute(CombComponentOp::getFunctionTypeAttrName(result.name),
575  TypeAttr::get(functionType));
576  } else {
577  result.addAttribute(ComponentOp::getFunctionTypeAttrName(result.name),
578  TypeAttr::get(functionType));
579  }
580 
581  // Record the port names and number of input ports of the component.
582  result.addAttribute("portNames", builder.getArrayAttr(portNames));
583  result.addAttribute("portDirections",
584  direction::packAttribute(builder.getContext(),
585  portIOTypes.first.size(),
586  portIOTypes.second.size()));
587  // Record the attributes of the ports.
588  result.addAttribute("portAttributes", builder.getArrayAttr(portAttributes));
589 
590  // Create a single-blocked region.
591  Region *region = result.addRegion();
592  Block *body = new Block();
593  region->push_back(body);
594 
595  // Add all ports to the body.
596  body->addArguments(portTypes, SmallVector<Location, 4>(
597  portTypes.size(), builder.getUnknownLoc()));
598 
599  // Insert the WiresOp and ControlOp.
600  IRRewriter::InsertionGuard guard(builder);
601  builder.setInsertionPointToStart(body);
602  builder.create<WiresOp>(result.location);
603  if (!combinational)
604  builder.create<ControlOp>(result.location);
605 }
606 
607 //===----------------------------------------------------------------------===//
608 // ComponentOp
609 //===----------------------------------------------------------------------===//
610 
611 /// This is a helper function that should only be used to get the WiresOp or
612 /// ControlOp of a ComponentOp, which are guaranteed to exist and generally at
613 /// the end of a component's body. In the worst case, this will run in linear
614 /// time with respect to the number of instances within the component.
615 template <typename Op>
616 static Op getControlOrWiresFrom(ComponentOp op) {
617  auto *body = op.getBodyBlock();
618  // We verify there is a single WiresOp and ControlOp,
619  // so this is safe.
620  auto opIt = body->getOps<Op>().begin();
621  return *opIt;
622 }
623 
624 /// Returns the Block argument with the given name from a ComponentOp.
625 /// If the name doesn't exist, returns an empty Value.
626 static Value getBlockArgumentWithName(StringRef name, ComponentOp op) {
627  ArrayAttr portNames = op.getPortNames();
628 
629  for (size_t i = 0, e = portNames.size(); i != e; ++i) {
630  auto portName = portNames[i].cast<StringAttr>();
631  if (portName.getValue() == name)
632  return op.getBodyBlock()->getArgument(i);
633  }
634  return Value{};
635 }
636 
637 WiresOp calyx::ComponentOp::getWiresOp() {
638  return getControlOrWiresFrom<WiresOp>(*this);
639 }
640 
641 ControlOp calyx::ComponentOp::getControlOp() {
642  return getControlOrWiresFrom<ControlOp>(*this);
643 }
644 
645 Value calyx::ComponentOp::getGoPort() {
646  return getBlockArgumentWithName("go", *this);
647 }
648 
649 Value calyx::ComponentOp::getDonePort() {
650  return getBlockArgumentWithName("done", *this);
651 }
652 
653 Value calyx::ComponentOp::getClkPort() {
654  return getBlockArgumentWithName("clk", *this);
655 }
656 
657 Value calyx::ComponentOp::getResetPort() {
658  return getBlockArgumentWithName("reset", *this);
659 }
660 
661 SmallVector<PortInfo> ComponentOp::getPortInfo() {
662  auto portTypes = getArgumentTypes();
663  ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
664  APInt portDirectionsAttr = getPortDirections();
665 
666  SmallVector<PortInfo> results;
667  for (size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
668  results.push_back(PortInfo{portNamesAttr[i].cast<StringAttr>(),
669  portTypes[i],
670  direction::get(portDirectionsAttr[i]),
671  portAttrs[i].cast<DictionaryAttr>()});
672  }
673  return results;
674 }
675 
676 /// A helper function to return a filtered subset of a component's ports.
677 template <typename Pred>
678 static SmallVector<PortInfo> getFilteredPorts(ComponentOp op, Pred p) {
679  SmallVector<PortInfo> ports = op.getPortInfo();
680  llvm::erase_if(ports, p);
681  return ports;
682 }
683 
684 SmallVector<PortInfo> ComponentOp::getInputPortInfo() {
685  return getFilteredPorts(
686  *this, [](const PortInfo &port) { return port.direction == Output; });
687 }
688 
689 SmallVector<PortInfo> ComponentOp::getOutputPortInfo() {
690  return getFilteredPorts(
691  *this, [](const PortInfo &port) { return port.direction == Input; });
692 }
693 
694 void ComponentOp::print(OpAsmPrinter &p) {
695  printComponentInterface<ComponentOp>(p, *this);
696 }
697 
698 ParseResult ComponentOp::parse(OpAsmParser &parser, OperationState &result) {
699  return parseComponentInterface<ComponentOp>(parser, result);
700 }
701 
702 /// Determines whether the given ComponentOp has all the required ports.
703 static LogicalResult hasRequiredPorts(ComponentOp op) {
704  // Get all identifiers from the component ports.
705  llvm::SmallVector<StringRef, 4> identifiers;
706  for (PortInfo &port : op.getPortInfo()) {
707  auto portIds = port.getAllIdentifiers();
708  identifiers.append(portIds.begin(), portIds.end());
709  }
710  // Sort the identifiers: a pre-condition for std::set_intersection.
711  std::sort(identifiers.begin(), identifiers.end());
712 
713  llvm::SmallVector<StringRef, 4> intersection,
714  interfacePorts{"clk", "done", "go", "reset"};
715  // Find the intersection between all identifiers and required ports.
716  std::set_intersection(interfacePorts.begin(), interfacePorts.end(),
717  identifiers.begin(), identifiers.end(),
718  std::back_inserter(intersection));
719 
720  if (intersection.size() == interfacePorts.size())
721  return success();
722 
723  SmallVector<StringRef, 4> difference;
724  std::set_difference(interfacePorts.begin(), interfacePorts.end(),
725  intersection.begin(), intersection.end(),
726  std::back_inserter(difference));
727  return op->emitOpError()
728  << "is missing the following required port attribute identifiers: "
729  << difference;
730 }
731 
732 LogicalResult ComponentOp::verify() {
733  // Verify there is exactly one of each the wires and control operations.
734  auto wIt = getBodyBlock()->getOps<WiresOp>();
735  auto cIt = getBodyBlock()->getOps<ControlOp>();
736  if (std::distance(wIt.begin(), wIt.end()) +
737  std::distance(cIt.begin(), cIt.end()) !=
738  2)
739  return emitOpError() << "requires exactly one of each: '"
740  << WiresOp::getOperationName() << "', '"
741  << ControlOp::getOperationName() << "'.";
742 
743  if (failed(hasRequiredPorts(*this)))
744  return failure();
745 
746  // Verify the component actually does something: has a non-empty Control
747  // region, or continuous assignments.
748  bool hasNoControlConstructs =
749  getControlOp().getBodyBlock()->getOperations().empty();
750  bool hasNoAssignments =
751  getWiresOp().getBodyBlock()->getOps<AssignOp>().empty();
752  if (hasNoControlConstructs && hasNoAssignments)
753  return emitOpError(
754  "The component currently does nothing. It needs to either have "
755  "continuous assignments in the Wires region or control constructs in "
756  "the Control region.");
757 
758  return success();
759 }
760 
761 void ComponentOp::build(OpBuilder &builder, OperationState &result,
762  StringAttr name, ArrayRef<PortInfo> ports) {
763  buildComponentLike(builder, result, name, ports, /*combinational=*/false);
764 }
765 
766 void ComponentOp::getAsmBlockArgumentNames(
767  mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
768  if (region.empty())
769  return;
770  auto ports = getPortNames();
771  auto *block = &getRegion()->front();
772  for (size_t i = 0, e = block->getNumArguments(); i != e; ++i)
773  setNameFn(block->getArgument(i), ports[i].cast<StringAttr>().getValue());
774 }
775 
776 //===----------------------------------------------------------------------===//
777 // CombComponentOp
778 //===----------------------------------------------------------------------===//
779 
780 SmallVector<PortInfo> CombComponentOp::getPortInfo() {
781  auto portTypes = getArgumentTypes();
782  ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
783  APInt portDirectionsAttr = getPortDirections();
784 
785  SmallVector<PortInfo> results;
786  for (size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
787  results.push_back(PortInfo{portNamesAttr[i].cast<StringAttr>(),
788  portTypes[i],
789  direction::get(portDirectionsAttr[i]),
790  portAttrs[i].cast<DictionaryAttr>()});
791  }
792  return results;
793 }
794 
795 WiresOp calyx::CombComponentOp::getWiresOp() {
796  auto *body = getBodyBlock();
797  auto opIt = body->getOps<WiresOp>().begin();
798  return *opIt;
799 }
800 
801 /// A helper function to return a filtered subset of a comb component's ports.
802 template <typename Pred>
803 static SmallVector<PortInfo> getFilteredPorts(CombComponentOp op, Pred p) {
804  SmallVector<PortInfo> ports = op.getPortInfo();
805  llvm::erase_if(ports, p);
806  return ports;
807 }
808 
809 SmallVector<PortInfo> CombComponentOp::getInputPortInfo() {
810  return getFilteredPorts(
811  *this, [](const PortInfo &port) { return port.direction == Output; });
812 }
813 
814 SmallVector<PortInfo> CombComponentOp::getOutputPortInfo() {
815  return getFilteredPorts(
816  *this, [](const PortInfo &port) { return port.direction == Input; });
817 }
818 
819 void CombComponentOp::print(OpAsmPrinter &p) {
820  printComponentInterface<CombComponentOp>(p, *this);
821 }
822 
823 ParseResult CombComponentOp::parse(OpAsmParser &parser,
824  OperationState &result) {
825  return parseComponentInterface<CombComponentOp>(parser, result);
826 }
827 
828 LogicalResult CombComponentOp::verify() {
829  // Verify there is exactly one wires operation.
830  auto wIt = getBodyBlock()->getOps<WiresOp>();
831  if (std::distance(wIt.begin(), wIt.end()) != 1)
832  return emitOpError() << "requires exactly one "
833  << WiresOp::getOperationName() << " op.";
834 
835  // Verify there is not a control operation.
836  auto cIt = getBodyBlock()->getOps<ControlOp>();
837  if (std::distance(cIt.begin(), cIt.end()) != 0)
838  return emitOpError() << "must not have a `" << ControlOp::getOperationName()
839  << "` op.";
840 
841  // Verify the component actually does something: has continuous assignments.
842  bool hasNoAssignments =
843  getWiresOp().getBodyBlock()->getOps<AssignOp>().empty();
844  if (hasNoAssignments)
845  return emitOpError(
846  "The component currently does nothing. It needs to either have "
847  "continuous assignments in the Wires region or control constructs in "
848  "the Control region.");
849 
850  // Check that all cells are combinational
851  auto cells = getOps<CellInterface>();
852  for (auto cell : cells) {
853  if (!cell.isCombinational())
854  return emitOpError() << "contains non-combinational cell "
855  << cell.instanceName();
856  }
857 
858  // Check that the component has no groups
859  auto groups = getWiresOp().getOps<GroupOp>();
860  if (!groups.empty())
861  return emitOpError() << "contains group " << (*groups.begin()).getSymName();
862 
863  // Combinational groups aren't allowed in combinational components either.
864  // For more information see here:
865  // https://docs.calyxir.org/lang/ref.html#comb-group-definitions
866  auto combGroups = getWiresOp().getOps<CombGroupOp>();
867  if (!combGroups.empty())
868  return emitOpError() << "contains comb group "
869  << (*combGroups.begin()).getSymName();
870 
871  return success();
872 }
873 
874 void CombComponentOp::build(OpBuilder &builder, OperationState &result,
875  StringAttr name, ArrayRef<PortInfo> ports) {
876  buildComponentLike(builder, result, name, ports, /*combinational=*/true);
877 }
878 
879 void CombComponentOp::getAsmBlockArgumentNames(
880  mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
881  if (region.empty())
882  return;
883  auto ports = getPortNames();
884  auto *block = &getRegion()->front();
885  for (size_t i = 0, e = block->getNumArguments(); i != e; ++i)
886  setNameFn(block->getArgument(i), ports[i].cast<StringAttr>().getValue());
887 }
888 
889 //===----------------------------------------------------------------------===//
890 // ControlOp
891 //===----------------------------------------------------------------------===//
892 LogicalResult ControlOp::verify() { return verifyControlBody(*this); }
893 
894 // Get the InvokeOps of this ControlOp.
895 SmallVector<InvokeOp, 4> ControlOp::getInvokeOps() {
896  SmallVector<InvokeOp, 4> ret;
897  this->walk([&](InvokeOp invokeOp) { ret.push_back(invokeOp); });
898  return ret;
899 }
900 
901 //===----------------------------------------------------------------------===//
902 // SeqOp
903 //===----------------------------------------------------------------------===//
904 
905 void SeqOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
906  MLIRContext *context) {
907  patterns.add(collapseControl<SeqOp>);
908  patterns.add(emptyControl<SeqOp>);
909  patterns.insert<CollapseUnaryControl<SeqOp>>(context);
910 }
911 
912 //===----------------------------------------------------------------------===//
913 // StaticSeqOp
914 //===----------------------------------------------------------------------===//
915 
916 LogicalResult StaticSeqOp::verify() {
917  // StaticSeqOp should only have static control in it
918  auto &ops = (*this).getBodyBlock()->getOperations();
919  if (!llvm::all_of(ops, [&](Operation &op) { return isStaticControl(&op); })) {
920  return emitOpError("StaticSeqOp has non static control within it");
921  }
922 
923  return success();
924 }
925 
926 void StaticSeqOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
927  MLIRContext *context) {
928  patterns.add(collapseControl<StaticSeqOp>);
929  patterns.add(emptyControl<StaticSeqOp>);
931 }
932 
933 //===----------------------------------------------------------------------===//
934 // ParOp
935 //===----------------------------------------------------------------------===//
936 
937 LogicalResult ParOp::verify() {
938  llvm::SmallSet<StringRef, 8> groupNames;
939 
940  // Add loose requirement that the body of a ParOp may not enable the same
941  // Group more than once, e.g. calyx.par { calyx.enable @G calyx.enable @G }
942  for (EnableOp op : getBodyBlock()->getOps<EnableOp>()) {
943  StringRef groupName = op.getGroupName();
944  if (groupNames.count(groupName))
945  return emitOpError() << "cannot enable the same group: \"" << groupName
946  << "\" more than once.";
947  groupNames.insert(groupName);
948  }
949 
950  return success();
951 }
952 
953 void ParOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
954  MLIRContext *context) {
955  patterns.add(collapseControl<ParOp>);
956  patterns.add(emptyControl<ParOp>);
957  patterns.insert<CollapseUnaryControl<ParOp>>(context);
958 }
959 
960 //===----------------------------------------------------------------------===//
961 // StaticParOp
962 //===----------------------------------------------------------------------===//
963 
964 LogicalResult StaticParOp::verify() {
965  llvm::SmallSet<StringRef, 8> groupNames;
966 
967  // Add loose requirement that the body of a ParOp may not enable the same
968  // Group more than once, e.g. calyx.par { calyx.enable @G calyx.enable @G }
969  for (EnableOp op : getBodyBlock()->getOps<EnableOp>()) {
970  StringRef groupName = op.getGroupName();
971  if (groupNames.count(groupName))
972  return emitOpError() << "cannot enable the same group: \"" << groupName
973  << "\" more than once.";
974  groupNames.insert(groupName);
975  }
976 
977  // static par must only have static control in it
978  auto &ops = (*this).getBodyBlock()->getOperations();
979  for (Operation &op : ops) {
980  if (!isStaticControl(&op)) {
981  return op.emitOpError("StaticParOp has non static control within it");
982  }
983  }
984 
985  return success();
986 }
987 
988 void StaticParOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
989  MLIRContext *context) {
990  patterns.add(collapseControl<StaticParOp>);
991  patterns.add(emptyControl<StaticParOp>);
993 }
994 
995 //===----------------------------------------------------------------------===//
996 // WiresOp
997 //===----------------------------------------------------------------------===//
998 LogicalResult WiresOp::verify() {
999  auto componentInterface = (*this)->getParentOfType<ComponentInterface>();
1000  if (llvm::isa<ComponentOp>(componentInterface)) {
1001  auto component = llvm::cast<ComponentOp>(componentInterface);
1002  auto control = component.getControlOp();
1003 
1004  // Verify each group is referenced in the control section.
1005  for (auto &&op : *getBodyBlock()) {
1006  if (!isa<GroupInterface>(op))
1007  continue;
1008  auto group = cast<GroupInterface>(op);
1009  auto groupName = group.symName();
1010  if (mlir::SymbolTable::symbolKnownUseEmpty(groupName, control))
1011  return op.emitOpError()
1012  << "with name: " << groupName
1013  << " is unused in the control execution schedule";
1014  }
1015  }
1016 
1017  // Verify that:
1018  // - At most one continuous assignment exists for any given value
1019  // - A continuously assigned wire has no assignments inside groups.
1020  for (auto thisAssignment : getBodyBlock()->getOps<AssignOp>()) {
1021  // Always assume guarded assignments will not be driven simultaneously. We
1022  // liberally assume that guards are mutually exclusive (more elaborate
1023  // static and dynamic checking can be performed to validate such cases).
1024  if (thisAssignment.getGuard())
1025  continue;
1026 
1027  Value dest = thisAssignment.getDest();
1028  for (Operation *user : dest.getUsers()) {
1029  auto assignUser = dyn_cast<AssignOp>(user);
1030  if (!assignUser || assignUser.getDest() != dest ||
1031  assignUser == thisAssignment)
1032  continue;
1033 
1034  return user->emitOpError() << "destination is already continuously "
1035  "driven. Other assignment is "
1036  << thisAssignment;
1037  }
1038  }
1039 
1040  return success();
1041 }
1042 
1043 //===----------------------------------------------------------------------===//
1044 // CombGroupOp
1045 //===----------------------------------------------------------------------===//
1046 
1047 /// Verifies the defining operation of a value is combinational.
1048 static LogicalResult isCombinational(Value value, GroupInterface group) {
1049  Operation *definingOp = value.getDefiningOp();
1050  if (definingOp == nullptr || definingOp->hasTrait<Combinational>())
1051  // This is a port of the parent component or combinational.
1052  return success();
1053 
1054  // For now, assumes all component instances are combinational. Once
1055  // combinational components are supported, this can be strictly enforced.
1056  if (isa<InstanceOp>(definingOp))
1057  return success();
1058 
1059  // Constants and logical operations are OK.
1060  if (isa<comb::CombDialect, hw::HWDialect>(definingOp->getDialect()))
1061  return success();
1062 
1063  // Reads to MemoryOp and RegisterOp are combinational. Writes are not.
1064  if (auto r = dyn_cast<RegisterOp>(definingOp)) {
1065  return value == r.getOut()
1066  ? success()
1067  : group->emitOpError()
1068  << "with register: \"" << r.instanceName()
1069  << "\" is conducting a memory store. This is not "
1070  "combinational.";
1071  } else if (auto m = dyn_cast<MemoryOp>(definingOp)) {
1072  auto writePorts = {m.writeData(), m.writeEn()};
1073  return (llvm::none_of(writePorts, [&](Value p) { return p == value; }))
1074  ? success()
1075  : group->emitOpError()
1076  << "with memory: \"" << m.instanceName()
1077  << "\" is conducting a memory store. This "
1078  "is not combinational.";
1079  }
1080 
1081  std::string portName =
1082  valueName(group->getParentOfType<ComponentOp>(), value);
1083  return group->emitOpError() << "with port: " << portName
1084  << ". This operation is not combinational.";
1085 }
1086 
1087 /// Verifies a combinational group may contain only combinational primitives or
1088 /// perform combinational logic.
1089 LogicalResult CombGroupOp::verify() {
1090  for (auto &&op : *getBodyBlock()) {
1091  auto assign = dyn_cast<AssignOp>(op);
1092  if (assign == nullptr)
1093  continue;
1094  Value dst = assign.getDest(), src = assign.getSrc();
1095  if (failed(isCombinational(dst, *this)) ||
1096  failed(isCombinational(src, *this)))
1097  return failure();
1098  }
1099  return success();
1100 }
1101 
1102 //===----------------------------------------------------------------------===//
1103 // GroupGoOp
1104 //===----------------------------------------------------------------------===//
1105 GroupGoOp GroupOp::getGoOp() {
1106  auto goOps = getBodyBlock()->getOps<GroupGoOp>();
1107  size_t nOps = std::distance(goOps.begin(), goOps.end());
1108  return nOps ? *goOps.begin() : GroupGoOp();
1109 }
1110 
1111 GroupDoneOp GroupOp::getDoneOp() {
1112  auto body = this->getBodyBlock();
1113  return cast<GroupDoneOp>(body->getTerminator());
1114 }
1115 
1116 //===----------------------------------------------------------------------===//
1117 // CycleOp
1118 //===----------------------------------------------------------------------===//
1119 void CycleOp::print(OpAsmPrinter &p) {
1120  p << " ";
1121  // The guard is optional.
1122  auto start = this->getStart();
1123  auto end = this->getEnd();
1124  if (end.has_value()) {
1125  p << "[" << start << ":" << end.value() << "]";
1126  } else {
1127  p << start;
1128  }
1129 }
1130 
1131 ParseResult CycleOp::parse(OpAsmParser &parser, OperationState &result) {
1132  SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
1133 
1134  uint32_t startLiteral;
1135  uint32_t endLiteral;
1136 
1137  auto hasEnd = succeeded(parser.parseOptionalLSquare());
1138 
1139  if (parser.parseInteger(startLiteral)) {
1140  parser.emitError(parser.getNameLoc(), "Could not parse start cycle");
1141  return failure();
1142  }
1143 
1144  auto start = parser.getBuilder().getI32IntegerAttr(startLiteral);
1145  result.addAttribute(getStartAttrName(result.name), start);
1146 
1147  if (hasEnd) {
1148  if (parser.parseColon())
1149  return failure();
1150 
1151  if (auto res = parser.parseOptionalInteger(endLiteral); res.has_value()) {
1152  auto end = parser.getBuilder().getI32IntegerAttr(endLiteral);
1153  result.addAttribute(getEndAttrName(result.name), end);
1154  }
1155 
1156  if (parser.parseRSquare())
1157  return failure();
1158  }
1159 
1160  result.addTypes(parser.getBuilder().getI1Type());
1161 
1162  return success();
1163 }
1164 
1165 LogicalResult CycleOp::verify() {
1166  uint32_t latency = this->getGroupLatency();
1167 
1168  if (this->getStart() >= latency) {
1169  emitOpError("start cycle must be less than the group latency");
1170  return failure();
1171  }
1172 
1173  if (this->getEnd().has_value()) {
1174  if (this->getStart() >= this->getEnd().value()) {
1175  emitOpError("start cycle must be less than end cycle");
1176  return failure();
1177  }
1178 
1179  if (this->getEnd() >= latency) {
1180  emitOpError("end cycle must be less than the group latency");
1181  return failure();
1182  }
1183  }
1184 
1185  return success();
1186 }
1187 
1188 uint32_t CycleOp::getGroupLatency() {
1189  auto group = (*this)->getParentOfType<StaticGroupOp>();
1190  return group.getLatency();
1191 }
1192 
1193 //===----------------------------------------------------------------------===//
1194 // GroupInterface
1195 //===----------------------------------------------------------------------===//
1196 
1197 /// Determines whether the given port is used in the group. Its use depends on
1198 /// the `isDriven` value; if true, then the port should be a destination in an
1199 /// AssignOp. Otherwise, it should be the source, i.e. a read.
1200 static bool portIsUsedInGroup(GroupInterface group, Value port, bool isDriven) {
1201  return llvm::any_of(port.getUses(), [&](auto &&use) {
1202  auto assignOp = dyn_cast<AssignOp>(use.getOwner());
1203  if (assignOp == nullptr)
1204  return false;
1205 
1206  Operation *parent = assignOp->getParentOp();
1207  if (isa<WiresOp>(parent))
1208  // This is a continuous assignment.
1209  return false;
1210 
1211  // A port is used if it meet the criteria:
1212  // (1) it is a {source, destination} of an assignment.
1213  // (2) that assignment is found in the provided group.
1214 
1215  // If not driven, then read.
1216  Value expected = isDriven ? assignOp.getDest() : assignOp.getSrc();
1217  return expected == port && group == parent;
1218  });
1219 }
1220 
1221 /// Checks whether `port` is driven from within `groupOp`.
1222 static LogicalResult portDrivenByGroup(GroupInterface groupOp, Value port) {
1223  // Check if the port is driven by an assignOp from within `groupOp`.
1224  if (portIsUsedInGroup(groupOp, port, /*isDriven=*/true))
1225  return success();
1226 
1227  // If `port` is an output of a cell then we conservatively enforce that at
1228  // least one input port of the cell must be driven by the group.
1229  if (auto cell = dyn_cast<CellInterface>(port.getDefiningOp());
1230  cell && cell.direction(port) == calyx::Direction::Output)
1231  return groupOp.drivesAnyPort(cell.getInputPorts());
1232 
1233  return failure();
1234 }
1235 
1236 LogicalResult GroupOp::drivesPort(Value port) {
1237  return portDrivenByGroup(*this, port);
1238 }
1239 
1240 LogicalResult CombGroupOp::drivesPort(Value port) {
1241  return portDrivenByGroup(*this, port);
1242 }
1243 
1244 LogicalResult StaticGroupOp::drivesPort(Value port) {
1245  return portDrivenByGroup(*this, port);
1246 }
1247 
1248 /// Checks whether all ports are driven within the group.
1249 static LogicalResult allPortsDrivenByGroup(GroupInterface group,
1250  ValueRange ports) {
1251  return success(llvm::all_of(ports, [&](Value port) {
1252  return portIsUsedInGroup(group, port, /*isDriven=*/true);
1253  }));
1254 }
1255 
1256 LogicalResult GroupOp::drivesAllPorts(ValueRange ports) {
1257  return allPortsDrivenByGroup(*this, ports);
1258 }
1259 
1260 LogicalResult CombGroupOp::drivesAllPorts(ValueRange ports) {
1261  return allPortsDrivenByGroup(*this, ports);
1262 }
1263 
1264 LogicalResult StaticGroupOp::drivesAllPorts(ValueRange ports) {
1265  return allPortsDrivenByGroup(*this, ports);
1266 }
1267 
1268 /// Checks whether any ports are driven within the group.
1269 static LogicalResult anyPortsDrivenByGroup(GroupInterface group,
1270  ValueRange ports) {
1271  return success(llvm::any_of(ports, [&](Value port) {
1272  return portIsUsedInGroup(group, port, /*isDriven=*/true);
1273  }));
1274 }
1275 
1276 LogicalResult GroupOp::drivesAnyPort(ValueRange ports) {
1277  return anyPortsDrivenByGroup(*this, ports);
1278 }
1279 
1280 LogicalResult CombGroupOp::drivesAnyPort(ValueRange ports) {
1281  return anyPortsDrivenByGroup(*this, ports);
1282 }
1283 
1284 LogicalResult StaticGroupOp::drivesAnyPort(ValueRange ports) {
1285  return anyPortsDrivenByGroup(*this, ports);
1286 }
1287 
1288 /// Checks whether any ports are read within the group.
1289 static LogicalResult anyPortsReadByGroup(GroupInterface group,
1290  ValueRange ports) {
1291  return success(llvm::any_of(ports, [&](Value port) {
1292  return portIsUsedInGroup(group, port, /*isDriven=*/false);
1293  }));
1294 }
1295 
1296 LogicalResult GroupOp::readsAnyPort(ValueRange ports) {
1297  return anyPortsReadByGroup(*this, ports);
1298 }
1299 
1300 LogicalResult CombGroupOp::readsAnyPort(ValueRange ports) {
1301  return anyPortsReadByGroup(*this, ports);
1302 }
1303 
1304 LogicalResult StaticGroupOp::readsAnyPort(ValueRange ports) {
1305  return anyPortsReadByGroup(*this, ports);
1306 }
1307 
1308 /// Verifies that certain ports of primitives are either driven or read
1309 /// together.
1310 static LogicalResult verifyPrimitivePortDriving(AssignOp assign,
1311  GroupInterface group) {
1312  Operation *destDefiningOp = assign.getDest().getDefiningOp();
1313  if (destDefiningOp == nullptr)
1314  return success();
1315  auto destCell = dyn_cast<CellInterface>(destDefiningOp);
1316  if (destCell == nullptr)
1317  return success();
1318 
1319  LogicalResult verifyWrites =
1320  TypeSwitch<Operation *, LogicalResult>(destCell)
1321  .Case<RegisterOp>([&](auto op) {
1322  // We only want to verify this is written to if the {write enable,
1323  // in} port is driven.
1324  return succeeded(group.drivesAnyPort({op.getWriteEn(), op.getIn()}))
1325  ? group.drivesAllPorts({op.getWriteEn(), op.getIn()})
1326  : success();
1327  })
1328  .Case<MemoryOp>([&](auto op) {
1329  SmallVector<Value> requiredWritePorts;
1330  // If writing to memory, write_en, write_data, and all address ports
1331  // should be driven.
1332  requiredWritePorts.push_back(op.writeEn());
1333  requiredWritePorts.push_back(op.writeData());
1334  for (Value address : op.addrPorts())
1335  requiredWritePorts.push_back(address);
1336 
1337  // We only want to verify the write ports if either write_data or
1338  // write_en is driven.
1339  return succeeded(
1340  group.drivesAnyPort({op.writeData(), op.writeEn()}))
1341  ? group.drivesAllPorts(requiredWritePorts)
1342  : success();
1343  })
1344  .Case<AndLibOp, OrLibOp, XorLibOp, AddLibOp, SubLibOp, GtLibOp,
1345  LtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp,
1346  RshLibOp, SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp,
1347  SleLibOp, SrshLibOp>([&](auto op) {
1348  Value lhs = op.getLeft(), rhs = op.getRight();
1349  return succeeded(group.drivesAnyPort({lhs, rhs}))
1350  ? group.drivesAllPorts({lhs, rhs})
1351  : success();
1352  })
1353  .Default([&](auto op) { return success(); });
1354 
1355  if (failed(verifyWrites))
1356  return group->emitOpError()
1357  << "with cell: " << destCell->getName() << " \""
1358  << destCell.instanceName()
1359  << "\" is performing a write and failed to drive all necessary "
1360  "ports.";
1361 
1362  Operation *srcDefiningOp = assign.getSrc().getDefiningOp();
1363  if (srcDefiningOp == nullptr)
1364  return success();
1365  auto srcCell = dyn_cast<CellInterface>(srcDefiningOp);
1366  if (srcCell == nullptr)
1367  return success();
1368 
1369  LogicalResult verifyReads =
1370  TypeSwitch<Operation *, LogicalResult>(srcCell)
1371  .Case<MemoryOp>([&](auto op) {
1372  // If reading memory, all address ports should be driven. Note that
1373  // we only want to verify the read ports if read_data is used in the
1374  // group.
1375  return succeeded(group.readsAnyPort({op.readData()}))
1376  ? group.drivesAllPorts(op.addrPorts())
1377  : success();
1378  })
1379  .Default([&](auto op) { return success(); });
1380 
1381  if (failed(verifyReads))
1382  return group->emitOpError() << "with cell: " << srcCell->getName() << " \""
1383  << srcCell.instanceName()
1384  << "\" is having a read performed upon it, and "
1385  "failed to drive all necessary ports.";
1386 
1387  return success();
1388 }
1389 
1390 LogicalResult calyx::verifyGroupInterface(Operation *op) {
1391  auto group = dyn_cast<GroupInterface>(op);
1392  if (group == nullptr)
1393  return success();
1394 
1395  for (auto &&groupOp : *group.getBody()) {
1396  auto assign = dyn_cast<AssignOp>(groupOp);
1397  if (assign == nullptr)
1398  continue;
1399  if (failed(verifyPrimitivePortDriving(assign, group)))
1400  return failure();
1401  }
1402 
1403  return success();
1404 }
1405 
1406 //===----------------------------------------------------------------------===//
1407 // Utilities for operations with the Cell trait.
1408 //===----------------------------------------------------------------------===//
1409 
1410 /// Gives each result of the cell a meaningful name in the form:
1411 /// <instance-name>.<port-name>
1412 static void getCellAsmResultNames(OpAsmSetValueNameFn setNameFn, Operation *op,
1413  ArrayRef<StringRef> portNames) {
1414  auto cellInterface = dyn_cast<CellInterface>(op);
1415  assert(cellInterface && "must implement the Cell interface");
1416 
1417  std::string prefix = cellInterface.instanceName().str() + ".";
1418  for (size_t i = 0, e = portNames.size(); i != e; ++i)
1419  setNameFn(op->getResult(i), prefix + portNames[i].str());
1420 }
1421 
1422 //===----------------------------------------------------------------------===//
1423 // AssignOp
1424 //===----------------------------------------------------------------------===//
1425 
1426 /// Determines whether the given direction is valid with the given inputs. The
1427 /// `isDestination` boolean is used to distinguish whether the value is a source
1428 /// or a destination.
1429 static LogicalResult verifyPortDirection(Operation *op, Value value,
1430  bool isDestination) {
1431  Operation *definingOp = value.getDefiningOp();
1432  bool isComponentPort = value.isa<BlockArgument>(),
1433  isCellInterfacePort = definingOp && isa<CellInterface>(definingOp);
1434  assert((isComponentPort || isCellInterfacePort) && "Not a port.");
1435 
1436  PortInfo port = isComponentPort
1437  ? getPortInfo(value.cast<BlockArgument>())
1438  : cast<CellInterface>(definingOp).portInfo(value);
1439 
1440  bool isSource = !isDestination;
1441  // Component output ports and cell interface input ports should be driven.
1442  Direction validDirection =
1443  (isDestination && isComponentPort) || (isSource && isCellInterfacePort)
1445  : Direction::Input;
1446 
1447  return port.direction == validDirection
1448  ? success()
1449  : op->emitOpError()
1450  << "has a " << (isComponentPort ? "component" : "cell")
1451  << " port as the "
1452  << (isDestination ? "destination" : "source")
1453  << " with the incorrect direction.";
1454 }
1455 
1456 /// Verifies the value of a given assignment operation. The boolean
1457 /// `isDestination` is used to distinguish whether the destination
1458 /// or source of the AssignOp is to be verified.
1459 static LogicalResult verifyAssignOpValue(AssignOp op, bool isDestination) {
1460  bool isSource = !isDestination;
1461  Value value = isDestination ? op.getDest() : op.getSrc();
1462  if (isPort(value))
1463  return verifyPortDirection(op, value, isDestination);
1464 
1465  // A destination may also be the Go or Done hole of a GroupOp.
1466  if (isDestination && !isa<GroupGoOp, GroupDoneOp>(value.getDefiningOp()))
1467  return op->emitOpError(
1468  "has an invalid destination port. It must be drive-able.");
1469  else if (isSource)
1470  return verifyNotComplexSource(op);
1471 
1472  return success();
1473 }
1474 
1475 LogicalResult AssignOp::verify() {
1476  bool isDestination = true, isSource = false;
1477  if (failed(verifyAssignOpValue(*this, isDestination)))
1478  return failure();
1479  if (failed(verifyAssignOpValue(*this, isSource)))
1480  return failure();
1481 
1482  return success();
1483 }
1484 
1485 ParseResult AssignOp::parse(OpAsmParser &parser, OperationState &result) {
1486  OpAsmParser::UnresolvedOperand destination;
1487  if (parser.parseOperand(destination) || parser.parseEqual())
1488  return failure();
1489 
1490  // An AssignOp takes one of the two following forms:
1491  // (1) %<dest> = %<src> : <type>
1492  // (2) %<dest> = %<guard> ? %<src> : <type>
1493  OpAsmParser::UnresolvedOperand guardOrSource;
1494  if (parser.parseOperand(guardOrSource))
1495  return failure();
1496 
1497  // Since the guard is optional, we need to check if there is an accompanying
1498  // `?` symbol.
1499  OpAsmParser::UnresolvedOperand source;
1500  bool hasGuard = succeeded(parser.parseOptionalQuestion());
1501  if (hasGuard) {
1502  // The guard exists. Parse the source.
1503  if (parser.parseOperand(source))
1504  return failure();
1505  }
1506 
1507  Type type;
1508  if (parser.parseColonType(type) ||
1509  parser.resolveOperand(destination, type, result.operands))
1510  return failure();
1511 
1512  if (hasGuard) {
1513  Type i1Type = parser.getBuilder().getI1Type();
1514  // Since the guard is optional, it is listed last in the arguments of the
1515  // AssignOp. Therefore, we must parse the source first.
1516  if (parser.resolveOperand(source, type, result.operands) ||
1517  parser.resolveOperand(guardOrSource, i1Type, result.operands))
1518  return failure();
1519  } else {
1520  // This is actually a source.
1521  if (parser.resolveOperand(guardOrSource, type, result.operands))
1522  return failure();
1523  }
1524 
1525  return success();
1526 }
1527 
1528 void AssignOp::print(OpAsmPrinter &p) {
1529  p << " " << getDest() << " = ";
1530 
1531  Value bguard = getGuard(), source = getSrc();
1532  // The guard is optional.
1533  if (bguard)
1534  p << bguard << " ? ";
1535 
1536  // We only need to print a single type; the destination and source are
1537  // guaranteed to be the same type.
1538  p << source << " : " << source.getType();
1539 }
1540 
1541 //===----------------------------------------------------------------------===//
1542 // InstanceOp
1543 //===----------------------------------------------------------------------===//
1544 
1545 /// Lookup the component for the symbol. This returns null on
1546 /// invalid IR.
1547 ComponentInterface InstanceOp::getReferencedComponent() {
1548  auto module = (*this)->getParentOfType<ModuleOp>();
1549  if (!module)
1550  return nullptr;
1551 
1552  return module.lookupSymbol<ComponentInterface>(getComponentName());
1553 }
1554 
1555 /// Verifies the port information in comparison with the referenced component
1556 /// of an instance. This helper function avoids conducting a lookup for the
1557 /// referenced component twice.
1558 static LogicalResult
1559 verifyInstanceOpType(InstanceOp instance,
1560  ComponentInterface referencedComponent) {
1561  auto module = instance->getParentOfType<ModuleOp>();
1562  StringRef entryPointName =
1563  module->getAttrOfType<StringAttr>("calyx.entrypoint");
1564  if (instance.getComponentName() == entryPointName)
1565  return instance.emitOpError()
1566  << "cannot reference the entry-point component: '" << entryPointName
1567  << "'.";
1568 
1569  // Verify the instance result ports with those of its referenced component.
1570  SmallVector<PortInfo> componentPorts = referencedComponent.getPortInfo();
1571  size_t numPorts = componentPorts.size();
1572 
1573  size_t numResults = instance.getNumResults();
1574  if (numResults != numPorts)
1575  return instance.emitOpError()
1576  << "has a wrong number of results; expected: " << numPorts
1577  << " but got " << numResults;
1578 
1579  for (size_t i = 0; i != numResults; ++i) {
1580  auto resultType = instance.getResult(i).getType();
1581  auto expectedType = componentPorts[i].type;
1582  if (resultType == expectedType)
1583  continue;
1584  return instance.emitOpError()
1585  << "result type for " << componentPorts[i].name << " must be "
1586  << expectedType << ", but got " << resultType;
1587  }
1588  return success();
1589 }
1590 
1591 LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1592  Operation *op = *this;
1593  auto module = op->getParentOfType<ModuleOp>();
1594  Operation *referencedComponent =
1595  symbolTable.lookupNearestSymbolFrom(module, getComponentNameAttr());
1596  if (referencedComponent == nullptr)
1597  return emitError() << "referencing component: '" << getComponentName()
1598  << "', which does not exist.";
1599 
1600  Operation *shadowedComponentName =
1601  symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1602  if (shadowedComponentName != nullptr)
1603  return emitError() << "instance symbol: '" << instanceName()
1604  << "' is already a symbol for another component.";
1605 
1606  // Verify the referenced component is not instantiating itself.
1607  auto parentComponent = op->getParentOfType<ComponentOp>();
1608  if (parentComponent == referencedComponent)
1609  return emitError() << "recursive instantiation of its parent component: '"
1610  << getComponentName() << "'";
1611 
1612  assert(isa<ComponentInterface>(referencedComponent) &&
1613  "Should be a ComponentInterface.");
1614  return verifyInstanceOpType(*this,
1615  cast<ComponentInterface>(referencedComponent));
1616 }
1617 
1618 /// Provide meaningful names to the result values of an InstanceOp.
1620  getCellAsmResultNames(setNameFn, *this, this->portNames());
1621 }
1622 
1623 SmallVector<StringRef> InstanceOp::portNames() {
1624  SmallVector<StringRef> portNames;
1625  for (Attribute name : getReferencedComponent().getPortNames())
1626  portNames.push_back(name.cast<StringAttr>().getValue());
1627  return portNames;
1628 }
1629 
1630 SmallVector<Direction> InstanceOp::portDirections() {
1631  SmallVector<Direction> portDirections;
1632  for (const PortInfo &port : getReferencedComponent().getPortInfo())
1633  portDirections.push_back(port.direction);
1634  return portDirections;
1635 }
1636 
1637 SmallVector<DictionaryAttr> InstanceOp::portAttributes() {
1638  SmallVector<DictionaryAttr> portAttributes;
1639  for (const PortInfo &port : getReferencedComponent().getPortInfo())
1640  portAttributes.push_back(port.attributes);
1641  return portAttributes;
1642 }
1643 
1645  return isa<CombComponentOp>(getReferencedComponent());
1646 }
1647 
1648 //===----------------------------------------------------------------------===//
1649 // PrimitiveOp
1650 //===----------------------------------------------------------------------===//
1651 
1652 /// Lookup the component for the symbol. This returns null on
1653 /// invalid IR.
1654 hw::HWModuleExternOp PrimitiveOp::getReferencedPrimitive() {
1655  auto module = (*this)->getParentOfType<ModuleOp>();
1656  if (!module)
1657  return nullptr;
1658 
1659  return module.lookupSymbol<hw::HWModuleExternOp>(getPrimitiveName());
1660 }
1661 
1662 /// Verifies the port information in comparison with the referenced component
1663 /// of an instance. This helper function avoids conducting a lookup for the
1664 /// referenced component twice.
1665 static LogicalResult
1666 verifyPrimitiveOpType(PrimitiveOp instance,
1667  hw::HWModuleExternOp referencedPrimitive) {
1668  auto module = instance->getParentOfType<ModuleOp>();
1669  StringRef entryPointName =
1670  module->getAttrOfType<StringAttr>("calyx.entrypoint");
1671  if (instance.getPrimitiveName() == entryPointName)
1672  return instance.emitOpError()
1673  << "cannot reference the entry-point component: '" << entryPointName
1674  << "'.";
1675 
1676  // Verify the instance result ports with those of its referenced component.
1677  auto primitivePorts = referencedPrimitive.getPortList();
1678  size_t numPorts = primitivePorts.size();
1679 
1680  size_t numResults = instance.getNumResults();
1681  if (numResults != numPorts)
1682  return instance.emitOpError()
1683  << "has a wrong number of results; expected: " << numPorts
1684  << " but got " << numResults;
1685 
1686  // Verify parameters match up
1687  ArrayAttr modParameters = referencedPrimitive.getParameters();
1688  ArrayAttr parameters = instance.getParameters().value_or(ArrayAttr());
1689  size_t numExpected = modParameters.size();
1690  size_t numParams = parameters.size();
1691  if (numParams != numExpected)
1692  return instance.emitOpError()
1693  << "has the wrong number of parameters; expected: " << numExpected
1694  << " but got " << numParams;
1695 
1696  for (size_t i = 0; i != numExpected; ++i) {
1697  auto param = parameters[i].cast<circt::hw::ParamDeclAttr>();
1698  auto modParam = modParameters[i].cast<circt::hw::ParamDeclAttr>();
1699 
1700  auto paramName = param.getName();
1701  if (paramName != modParam.getName())
1702  return instance.emitOpError()
1703  << "parameter #" << i << " should have name " << modParam.getName()
1704  << " but has name " << paramName;
1705 
1706  if (param.getType() != modParam.getType())
1707  return instance.emitOpError()
1708  << "parameter " << paramName << " should have type "
1709  << modParam.getType() << " but has type " << param.getType();
1710 
1711  // All instance parameters must have a value. Specify the same value as
1712  // a module's default value if you want the default.
1713  if (!param.getValue())
1714  return instance.emitOpError("parameter ")
1715  << paramName << " must have a value";
1716  }
1717 
1718  for (size_t i = 0; i != numResults; ++i) {
1719  auto resultType = instance.getResult(i).getType();
1720  auto expectedType = primitivePorts[i].type;
1721  auto replacedType = hw::evaluateParametricType(
1722  instance.getLoc(), instance.getParametersAttr(), expectedType);
1723  if (failed(replacedType))
1724  return failure();
1725  if (resultType == replacedType)
1726  continue;
1727  return instance.emitOpError()
1728  << "result type for " << primitivePorts[i].name << " must be "
1729  << expectedType << ", but got " << resultType;
1730  }
1731  return success();
1732 }
1733 
1734 LogicalResult
1735 PrimitiveOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1736  Operation *op = *this;
1737  auto module = op->getParentOfType<ModuleOp>();
1738  Operation *referencedPrimitive =
1739  symbolTable.lookupNearestSymbolFrom(module, getPrimitiveNameAttr());
1740  if (referencedPrimitive == nullptr)
1741  return emitError() << "referencing primitive: '" << getPrimitiveName()
1742  << "', which does not exist.";
1743 
1744  Operation *shadowedPrimitiveName =
1745  symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1746  if (shadowedPrimitiveName != nullptr)
1747  return emitError() << "instance symbol: '" << instanceName()
1748  << "' is already a symbol for another primitive.";
1749 
1750  // Verify the referenced primitive is not instantiating itself.
1751  auto parentPrimitive = op->getParentOfType<hw::HWModuleExternOp>();
1752  if (parentPrimitive == referencedPrimitive)
1753  return emitError() << "recursive instantiation of its parent primitive: '"
1754  << getPrimitiveName() << "'";
1755 
1756  assert(isa<hw::HWModuleExternOp>(referencedPrimitive) &&
1757  "Should be a HardwareModuleExternOp.");
1758 
1759  return verifyPrimitiveOpType(*this,
1760  cast<hw::HWModuleExternOp>(referencedPrimitive));
1761 }
1762 
1763 /// Provide meaningful names to the result values of an PrimitiveOp.
1765  getCellAsmResultNames(setNameFn, *this, this->portNames());
1766 }
1767 
1768 SmallVector<StringRef> PrimitiveOp::portNames() {
1769  SmallVector<StringRef> portNames;
1770  auto ports = getReferencedPrimitive().getPortList();
1771  for (auto port : ports)
1772  portNames.push_back(port.name.getValue());
1773 
1774  return portNames;
1775 }
1776 
1778  switch (direction) {
1780  return Direction::Input;
1782  return Direction::Output;
1784  llvm_unreachable("InOut ports not supported by Calyx");
1785  }
1786  llvm_unreachable("Impossible port type");
1787 }
1788 
1789 SmallVector<Direction> PrimitiveOp::portDirections() {
1790  SmallVector<Direction> portDirections;
1791  auto ports = getReferencedPrimitive().getPortList();
1792  for (hw::PortInfo port : ports)
1793  portDirections.push_back(convertHWDirectionToCalyx(port.dir));
1794  return portDirections;
1795 }
1796 
1797 bool PrimitiveOp::isCombinational() { return false; }
1798 
1799 /// Returns a new DictionaryAttr containing only the calyx dialect attrs
1800 /// in the input DictionaryAttr. Also strips the 'calyx.' prefix from these
1801 /// attrs.
1802 static DictionaryAttr cleanCalyxPortAttrs(OpBuilder builder,
1803  DictionaryAttr dict) {
1804  if (!dict) {
1805  return dict;
1806  }
1807  llvm::SmallVector<NamedAttribute> attrs;
1808  for (NamedAttribute attr : dict) {
1809  Dialect *dialect = attr.getNameDialect();
1810  if (dialect == nullptr || !isa<CalyxDialect>(*dialect))
1811  continue;
1812  StringRef name = attr.getName().strref();
1813  StringAttr newName = builder.getStringAttr(std::get<1>(name.split(".")));
1814  attr.setName(newName);
1815  attrs.push_back(attr);
1816  }
1817  return builder.getDictionaryAttr(attrs);
1818 }
1819 
1820 // Grabs calyx port attributes from the HWModuleExternOp arg/result attributes.
1821 SmallVector<DictionaryAttr> PrimitiveOp::portAttributes() {
1822  SmallVector<DictionaryAttr> portAttributes;
1823  OpBuilder builder(getContext());
1824  hw::HWModuleExternOp prim = getReferencedPrimitive();
1825  auto argAttrs = prim.getAllInputAttrs();
1826  auto resAttrs = prim.getAllOutputAttrs();
1827  for (auto a : argAttrs)
1828  portAttributes.push_back(
1829  cleanCalyxPortAttrs(builder, cast_or_null<DictionaryAttr>(a)));
1830  for (auto a : resAttrs)
1831  portAttributes.push_back(
1832  cleanCalyxPortAttrs(builder, cast_or_null<DictionaryAttr>(a)));
1833  return portAttributes;
1834 }
1835 
1836 /// Parse an parameter list if present. Same format as HW dialect.
1837 /// module-parameter-list ::= `<` parameter-decl (`,` parameter-decl)* `>`
1838 /// parameter-decl ::= identifier `:` type
1839 /// parameter-decl ::= identifier `:` type `=` attribute
1840 ///
1841 static ParseResult parseParameterList(OpAsmParser &parser,
1842  SmallVector<Attribute> &parameters) {
1843 
1844  return parser.parseCommaSeparatedList(
1845  OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1846  std::string name;
1847  Type type;
1848  Attribute value;
1849 
1850  if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1851  return failure();
1852 
1853  // Parse the default value if present.
1854  if (succeeded(parser.parseOptionalEqual())) {
1855  if (parser.parseAttribute(value, type))
1856  return failure();
1857  }
1858 
1859  auto &builder = parser.getBuilder();
1860  parameters.push_back(hw::ParamDeclAttr::get(
1861  builder.getContext(), builder.getStringAttr(name), type, value));
1862  return success();
1863  });
1864 }
1865 
1866 /// Shim to also use this for the InstanceOp custom parser.
1867 static ParseResult parseParameterList(OpAsmParser &parser,
1868  ArrayAttr &parameters) {
1869  SmallVector<Attribute> parseParameters;
1870  if (failed(parseParameterList(parser, parseParameters)))
1871  return failure();
1872 
1873  parameters = ArrayAttr::get(parser.getContext(), parseParameters);
1874 
1875  return success();
1876 }
1877 
1878 /// Print a parameter list for a module or instance. Same format as HW dialect.
1879 static void printParameterList(OpAsmPrinter &p, Operation *op,
1880  ArrayAttr parameters) {
1881  if (parameters.empty())
1882  return;
1883 
1884  p << '<';
1885  llvm::interleaveComma(parameters, p, [&](Attribute param) {
1886  auto paramAttr = param.cast<hw::ParamDeclAttr>();
1887  p << paramAttr.getName().getValue() << ": " << paramAttr.getType();
1888  if (auto value = paramAttr.getValue()) {
1889  p << " = ";
1890  p.printAttributeWithoutType(value);
1891  }
1892  });
1893  p << '>';
1894 }
1895 
1896 //===----------------------------------------------------------------------===//
1897 // GroupGoOp
1898 //===----------------------------------------------------------------------===//
1899 
1900 LogicalResult GroupGoOp::verify() { return verifyNotComplexSource(*this); }
1901 
1902 /// Provide meaningful names to the result value of a GroupGoOp.
1903 void GroupGoOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1904  auto parent = (*this)->getParentOfType<GroupOp>();
1905  StringRef name = parent.getSymName();
1906  std::string resultName = name.str() + ".go";
1907  setNameFn(getResult(), resultName);
1908 }
1909 
1910 void GroupGoOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1911 
1912 ParseResult GroupGoOp::parse(OpAsmParser &parser, OperationState &result) {
1913  if (parseGroupPort(parser, result))
1914  return failure();
1915 
1916  result.addTypes(parser.getBuilder().getI1Type());
1917  return success();
1918 }
1919 
1920 //===----------------------------------------------------------------------===//
1921 // GroupDoneOp
1922 //===----------------------------------------------------------------------===//
1923 
1924 LogicalResult GroupDoneOp::verify() {
1925  Operation *srcOp = getSrc().getDefiningOp();
1926  Value optionalGuard = getGuard();
1927  Operation *guardOp = optionalGuard ? optionalGuard.getDefiningOp() : nullptr;
1928  bool noGuard = (guardOp == nullptr);
1929 
1930  if (srcOp == nullptr)
1931  // This is a port of the parent component.
1932  return success();
1933 
1934  if (isa<hw::ConstantOp>(srcOp) && (noGuard || isa<hw::ConstantOp>(guardOp)))
1935  return emitOpError() << "with constant source"
1936  << (noGuard ? "" : " and constant guard")
1937  << ". This should be a combinational group.";
1938 
1939  return verifyNotComplexSource(*this);
1940 }
1941 
1942 void GroupDoneOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1943 
1944 ParseResult GroupDoneOp::parse(OpAsmParser &parser, OperationState &result) {
1945  return parseGroupPort(parser, result);
1946 }
1947 
1948 //===----------------------------------------------------------------------===//
1949 // RegisterOp
1950 //===----------------------------------------------------------------------===//
1951 
1952 /// Provide meaningful names to the result values of a RegisterOp.
1953 void RegisterOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1954  getCellAsmResultNames(setNameFn, *this, this->portNames());
1955 }
1956 
1957 SmallVector<StringRef> RegisterOp::portNames() {
1958  return {"in", "write_en", "clk", "reset", "out", "done"};
1959 }
1960 
1961 SmallVector<Direction> RegisterOp::portDirections() {
1962  return {Input, Input, Input, Input, Output, Output};
1963 }
1964 
1965 SmallVector<DictionaryAttr> RegisterOp::portAttributes() {
1966  MLIRContext *context = getContext();
1967  IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
1968  NamedAttrList writeEn, clk, reset, done;
1969  writeEn.append("go", isSet);
1970  clk.append("clk", isSet);
1971  reset.append("reset", isSet);
1972  done.append("done", isSet);
1973  return {
1974  DictionaryAttr::get(context), // In
1975  writeEn.getDictionary(context), // Write enable
1976  clk.getDictionary(context), // Clk
1977  reset.getDictionary(context), // Reset
1978  DictionaryAttr::get(context), // Out
1979  done.getDictionary(context) // Done
1980  };
1981 }
1982 
1983 bool RegisterOp::isCombinational() { return false; }
1984 
1985 //===----------------------------------------------------------------------===//
1986 // MemoryOp
1987 //===----------------------------------------------------------------------===//
1988 
1989 /// Provide meaningful names to the result values of a MemoryOp.
1990 void MemoryOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1991  getCellAsmResultNames(setNameFn, *this, this->portNames());
1992 }
1993 
1994 SmallVector<StringRef> MemoryOp::portNames() {
1995  SmallVector<StringRef> portNames;
1996  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
1997  auto nameAttr =
1998  StringAttr::get(this->getContext(), "addr" + std::to_string(i));
1999  portNames.push_back(nameAttr.getValue());
2000  }
2001  portNames.append({"write_data", "write_en", "clk", "read_data", "done"});
2002  return portNames;
2003 }
2004 
2005 SmallVector<Direction> MemoryOp::portDirections() {
2006  SmallVector<Direction> portDirections;
2007  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2008  portDirections.push_back(Input);
2009  portDirections.append({Input, Input, Input, Output, Output});
2010  return portDirections;
2011 }
2012 
2013 SmallVector<DictionaryAttr> MemoryOp::portAttributes() {
2014  SmallVector<DictionaryAttr> portAttributes;
2015  MLIRContext *context = getContext();
2016  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2017  portAttributes.push_back(DictionaryAttr::get(context)); // Addresses
2018 
2019  // Use a boolean to indicate this attribute is used.
2020  IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
2021  NamedAttrList writeEn, clk, reset, done;
2022  writeEn.append("go", isSet);
2023  clk.append("clk", isSet);
2024  done.append("done", isSet);
2025  portAttributes.append({DictionaryAttr::get(context), // In
2026  writeEn.getDictionary(context), // Write enable
2027  clk.getDictionary(context), // Clk
2028  DictionaryAttr::get(context), // Out
2029  done.getDictionary(context)} // Done
2030  );
2031  return portAttributes;
2032 }
2033 
2034 void MemoryOp::build(OpBuilder &builder, OperationState &state,
2035  StringRef instanceName, int64_t width,
2036  ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2037  state.addAttribute(SymbolTable::getSymbolAttrName(),
2038  builder.getStringAttr(instanceName));
2039  state.addAttribute("width", builder.getI64IntegerAttr(width));
2040  state.addAttribute("sizes", builder.getI64ArrayAttr(sizes));
2041  state.addAttribute("addrSizes", builder.getI64ArrayAttr(addrSizes));
2042  SmallVector<Type> types;
2043  for (int64_t size : addrSizes)
2044  types.push_back(builder.getIntegerType(size)); // Addresses
2045  types.push_back(builder.getIntegerType(width)); // Write data
2046  types.push_back(builder.getI1Type()); // Write enable
2047  types.push_back(builder.getI1Type()); // Clk
2048  types.push_back(builder.getIntegerType(width)); // Read data
2049  types.push_back(builder.getI1Type()); // Done
2050  state.addTypes(types);
2051 }
2052 
2053 LogicalResult MemoryOp::verify() {
2054  ArrayRef<Attribute> opSizes = getSizes().getValue();
2055  ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2056  size_t numDims = getSizes().size();
2057  size_t numAddrs = getAddrSizes().size();
2058  if (numDims != numAddrs)
2059  return emitOpError("mismatched number of dimensions (")
2060  << numDims << ") and address sizes (" << numAddrs << ")";
2061 
2062  size_t numExtraPorts = 5; // write data/enable, clk, and read data/done.
2063  if (getNumResults() != numAddrs + numExtraPorts)
2064  return emitOpError("incorrect number of address ports, expected ")
2065  << numAddrs;
2066 
2067  for (size_t i = 0; i < numDims; ++i) {
2068  int64_t size = opSizes[i].cast<IntegerAttr>().getInt();
2069  int64_t addrSize = opAddrSizes[i].cast<IntegerAttr>().getInt();
2070  if (llvm::Log2_64_Ceil(size) > addrSize)
2071  return emitOpError("address size (")
2072  << addrSize << ") for dimension " << i
2073  << " can't address the entire range (" << size << ")";
2074  }
2075 
2076  return success();
2077 }
2078 
2079 //===----------------------------------------------------------------------===//
2080 // SeqMemoryOp
2081 //===----------------------------------------------------------------------===//
2082 
2083 /// Provide meaningful names to the result values of a SeqMemoryOp.
2084 void SeqMemoryOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
2085  getCellAsmResultNames(setNameFn, *this, this->portNames());
2086 }
2087 
2088 SmallVector<StringRef> SeqMemoryOp::portNames() {
2089  SmallVector<StringRef> portNames;
2090  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2091  auto nameAttr =
2092  StringAttr::get(this->getContext(), "addr" + std::to_string(i));
2093  portNames.push_back(nameAttr.getValue());
2094  }
2095  portNames.append({"write_data", "write_en", "write_done", "clk", "read_data",
2096  "read_en", "read_done"});
2097  return portNames;
2098 }
2099 
2100 SmallVector<Direction> SeqMemoryOp::portDirections() {
2101  SmallVector<Direction> portDirections;
2102  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2103  portDirections.push_back(Input);
2104  portDirections.append({Input, Input, Output, Input, Output, Input, Output});
2105  return portDirections;
2106 }
2107 
2108 SmallVector<DictionaryAttr> SeqMemoryOp::portAttributes() {
2109  SmallVector<DictionaryAttr> portAttributes;
2110  MLIRContext *context = getContext();
2111  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2112  portAttributes.push_back(DictionaryAttr::get(context)); // Addresses
2113 
2114  OpBuilder builder(context);
2115  // Use a boolean to indicate this attribute is used.
2116  IntegerAttr isSet = IntegerAttr::get(builder.getIndexType(), 1);
2117  IntegerAttr isTwo = IntegerAttr::get(builder.getIndexType(), 2);
2118  NamedAttrList writeEn, writeDone, clk, reset, readEn, readDone;
2119  writeEn.append("go", isSet);
2120  writeDone.append("done", isSet);
2121  clk.append("clk", isSet);
2122  readEn.append("go", isTwo);
2123  readDone.append("done", isTwo);
2124  portAttributes.append({DictionaryAttr::get(context), // Write Data
2125  writeEn.getDictionary(context), // Write enable
2126  writeDone.getDictionary(context), // Write done
2127  clk.getDictionary(context), // Clk
2128  DictionaryAttr::get(context), // Out
2129  readEn.getDictionary(context), // Read enable
2130  readDone.getDictionary(context)} // Read 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.getIntegerType(width)); // Write data
2147  types.push_back(builder.getI1Type()); // Write enable
2148  types.push_back(builder.getI1Type()); // Write done
2149  types.push_back(builder.getI1Type()); // Clk
2150  types.push_back(builder.getIntegerType(width)); // Read data
2151  types.push_back(builder.getI1Type()); // Read enable
2152  types.push_back(builder.getI1Type()); // Read 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/done, clk, and read data/enable/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("go"))
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("done"))
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("go"))
2767  ++goPortNum;
2768  if (info.hasAttribute("done"))
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 {"clk", "reset", "go", "left", "right", outName, "done"}; \
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("go", isSet); \
2871  clk.append("clk", isSet); \
2872  reset.append("reset", isSet); \
2873  done.append("done", 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"
lowerAnnotationsNoRefTypePorts FirtoolPreserveValuesMode value
Definition: Firtool.cpp:95
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:1666
static ParseResult parseComponentSignature(OpAsmParser &parser, OperationState &result, SmallVectorImpl< OpAsmParser::Argument > &ports, SmallVectorImpl< Type > &portTypes)
Parses the signature of a Calyx component.
Definition: CalyxOps.cpp:455
static LogicalResult verifyAssignOpValue(AssignOp op, bool isDestination)
Verifies the value of a given assignment operation.
Definition: CalyxOps.cpp:1459
static ParseResult parseParameterList(OpAsmParser &parser, SmallVector< Attribute > &parameters)
Parse an parameter list if present.
Definition: CalyxOps.cpp:1841
static SmallVector< PortInfo > getFilteredPorts(ComponentOp op, Pred p)
A helper function to return a filtered subset of a component's ports.
Definition: CalyxOps.cpp:678
static Op getControlOrWiresFrom(ComponentOp op)
This is a helper function that should only be used to get the WiresOp or ControlOp of a ComponentOp,...
Definition: CalyxOps.cpp:616
static LogicalResult verifyPrimitivePortDriving(AssignOp assign, GroupInterface group)
Verifies that certain ports of primitives are either driven or read together.
Definition: CalyxOps.cpp:1310
#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:1200
static Value getBlockArgumentWithName(StringRef name, ComponentOp op)
Returns the Block argument with the given name from a ComponentOp.
Definition: CalyxOps.cpp:626
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:427
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:119
static LogicalResult verifyNotComplexSource(Op op)
Verify that the value is not a "complex" value.
Definition: CalyxOps.cpp:102
static LogicalResult verifyInstanceOpType(InstanceOp instance, ComponentInterface referencedComponent)
Verifies the port information in comparison with the referenced component of an instance.
Definition: CalyxOps.cpp:1559
static LogicalResult collapseControl(OpTy controlOp, PatternRewriter &rewriter)
Definition: CalyxOps.cpp:310
Direction convertHWDirectionToCalyx(hw::ModulePort::Direction direction)
Definition: CalyxOps.cpp:1777
static LogicalResult anyPortsReadByGroup(GroupInterface group, ValueRange ports)
Checks whether any ports are read within the group.
Definition: CalyxOps.cpp:1289
static bool hasControlRegion(Operation *op)
Returns whether the given operation has a control region.
Definition: CalyxOps.cpp:148
static LogicalResult emptyControl(OpTy controlOp, PatternRewriter &rewriter)
Definition: CalyxOps.cpp:328
static LogicalResult verifyControlBody(Operation *op)
Verifies the body of a ControlLikeOp.
Definition: CalyxOps.cpp:167
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:367
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:340
static ParseResult parseComponentInterface(OpAsmParser &parser, OperationState &result)
Definition: CalyxOps.cpp:501
static void printComponentInterface(OpAsmPrinter &p, ComponentInterface comp)
Definition: CalyxOps.cpp:385
static LogicalResult hasRequiredPorts(ComponentOp op)
Determines whether the given ComponentOp has all the required ports.
Definition: CalyxOps.cpp:703
static LogicalResult anyPortsDrivenByGroup(GroupInterface group, ValueRange ports)
Checks whether any ports are driven within the group.
Definition: CalyxOps.cpp:1269
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:133
#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:538
static LogicalResult portDrivenByGroup(GroupInterface groupOp, Value port)
Checks whether port is driven from within groupOp.
Definition: CalyxOps.cpp:1222
static void buildComponentLike(OpBuilder &builder, OperationState &result, StringAttr name, ArrayRef< PortInfo > ports, bool combinational)
Definition: CalyxOps.cpp:546
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:1412
static LogicalResult allPortsDrivenByGroup(GroupInterface group, ValueRange ports)
Checks whether all ports are driven within the group.
Definition: CalyxOps.cpp:1249
static LogicalResult verifyPortDirection(Operation *op, Value value, bool isDestination)
Determines whether the given direction is valid with the given inputs.
Definition: CalyxOps.cpp:1429
static LogicalResult isCombinational(Value value, GroupInterface group)
Verifies the defining operation of a value is combinational.
Definition: CalyxOps.cpp:1048
static bool isStaticControl(Operation *op)
Returns whether the given operation is a static control operator.
Definition: CalyxOps.cpp:154
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:1879
static DictionaryAttr cleanCalyxPortAttrs(OpBuilder builder, DictionaryAttr dict)
Returns a new DictionaryAttr containing only the calyx dialect attrs in the input DictionaryAttr.
Definition: CalyxOps.cpp:1802
static void printGroupPort(OpAsmPrinter &p, GroupPortType op)
Definition: CalyxOps.cpp:295
#define ImplUnaryOpCellInterface(OpType)
Definition: CalyxOps.cpp:2887
static ParseResult parseGroupPort(OpAsmParser &parser, OperationState &result)
Definition: CalyxOps.cpp:267
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:27
@ Input
Definition: HW.h:32
@ Output
Definition: HW.h:32
@ InOut
Definition: HW.h:32
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:48
def type(self)
Definition: hw.py:195
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
IntegerAttr packAttribute(MLIRContext *context, size_t nIns, size_t nOuts)
Returns an IntegerAttr containing the packed representation of the direction counts.
Definition: CalyxOps.cpp:57
LogicalResult verifyComponent(Operation *op)
A helper function to verify each operation with the Ccomponent trait.
Definition: CalyxOps.cpp:201
LogicalResult verifyControlLikeOp(Operation *op)
A helper function to verify each control-like operation has a valid parent and, if applicable,...
Definition: CalyxOps.cpp:217
LogicalResult verifyGroupInterface(Operation *op)
A helper function to verify each operation with the Group Interface trait.
Definition: CalyxOps.cpp:1390
LogicalResult verifyCell(Operation *op)
A helper function to verify each operation with the Cell trait.
Definition: CalyxOps.cpp:209
Direction
The direction of a Component or Cell port.
Definition: CalyxOps.h:65
LogicalResult verifyIf(Operation *op)
A helper function to verify each operation with the If trait.
Definition: CalyxOps.cpp:254
PortInfo getPortInfo(BlockArgument arg)
Returns port information for the block argument provided.
Definition: CalyxOps.cpp:140
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 ...
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition: LLVM.h:186
This pattern collapses a calyx.seq or calyx.par operation when it contains exactly one calyx....
Definition: CalyxOps.cpp:76
LogicalResult matchAndRewrite(CtrlOp ctrlOp, PatternRewriter &rewriter) const override
Definition: CalyxOps.cpp:78
This holds information about the port for either a Component or Cell.
Definition: CalyxOps.h:78
Direction direction
Definition: CalyxOps.h:81
DictionaryAttr attributes
Definition: CalyxOps.h:82