CIRCT  20.0.0git
MooreOps.cpp
Go to the documentation of this file.
1 //===- MooreOps.cpp - Implement the Moore operations ----------------------===//
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 file implements the Moore dialect operations.
10 //
11 //===----------------------------------------------------------------------===//
12 
18 #include "mlir/IR/Builders.h"
19 #include "llvm/ADT/SmallString.h"
20 #include "llvm/ADT/TypeSwitch.h"
21 
22 using namespace circt;
23 using namespace circt::moore;
24 using namespace mlir;
25 
26 //===----------------------------------------------------------------------===//
27 // SVModuleOp
28 //===----------------------------------------------------------------------===//
29 
30 void SVModuleOp::build(mlir::OpBuilder &builder, mlir::OperationState &state,
31  llvm::StringRef name, hw::ModuleType type) {
32  state.addAttribute(SymbolTable::getSymbolAttrName(),
33  builder.getStringAttr(name));
34  state.addAttribute(getModuleTypeAttrName(state.name), TypeAttr::get(type));
35  state.addRegion();
36 }
37 
38 void SVModuleOp::print(OpAsmPrinter &p) {
39  p << " ";
40 
41  // Print the visibility of the module.
42  StringRef visibilityAttrName = SymbolTable::getVisibilityAttrName();
43  if (auto visibility = (*this)->getAttrOfType<StringAttr>(visibilityAttrName))
44  p << visibility.getValue() << ' ';
45 
46  p.printSymbolName(SymbolTable::getSymbolName(*this).getValue());
48  getModuleType(), {}, {});
49  p << " ";
50  p.printRegion(getBodyRegion(), /*printEntryBlockArgs=*/false,
51  /*printBlockTerminators=*/true);
52 
53  p.printOptionalAttrDictWithKeyword(getOperation()->getAttrs(),
54  getAttributeNames());
55 }
56 
57 ParseResult SVModuleOp::parse(OpAsmParser &parser, OperationState &result) {
58  // Parse the visibility attribute.
59  (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
60 
61  // Parse the module name.
62  StringAttr nameAttr;
63  if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
64  result.attributes))
65  return failure();
66 
67  // Parse the ports.
68  SmallVector<hw::module_like_impl::PortParse> ports;
69  TypeAttr modType;
70  if (failed(
71  hw::module_like_impl::parseModuleSignature(parser, ports, modType)))
72  return failure();
73  result.addAttribute(getModuleTypeAttrName(result.name), modType);
74 
75  // Parse the attributes.
76  if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
77  return failure();
78 
79  // Add the entry block arguments.
80  SmallVector<OpAsmParser::Argument, 4> entryArgs;
81  for (auto &port : ports)
82  if (port.direction != hw::ModulePort::Direction::Output)
83  entryArgs.push_back(port);
84 
85  // Parse the optional function body.
86  auto &bodyRegion = *result.addRegion();
87  if (parser.parseRegion(bodyRegion, entryArgs))
88  return failure();
89 
90  ensureTerminator(bodyRegion, parser.getBuilder(), result.location);
91  return success();
92 }
93 
94 void SVModuleOp::getAsmBlockArgumentNames(mlir::Region &region,
95  mlir::OpAsmSetValueNameFn setNameFn) {
96  if (&region != &getBodyRegion())
97  return;
98  auto moduleType = getModuleType();
99  for (auto [index, arg] : llvm::enumerate(region.front().getArguments()))
100  setNameFn(arg, moduleType.getInputNameAttr(index));
101 }
102 
103 OutputOp SVModuleOp::getOutputOp() {
104  return cast<OutputOp>(getBody()->getTerminator());
105 }
106 
107 OperandRange SVModuleOp::getOutputs() { return getOutputOp().getOperands(); }
108 
109 //===----------------------------------------------------------------------===//
110 // OutputOp
111 //===----------------------------------------------------------------------===//
112 
113 LogicalResult OutputOp::verify() {
114  auto module = getParentOp();
115 
116  // Check that the number of operands matches the number of output ports.
117  auto outputTypes = module.getModuleType().getOutputTypes();
118  if (outputTypes.size() != getNumOperands())
119  return emitOpError("has ")
120  << getNumOperands() << " operands, but enclosing module @"
121  << module.getSymName() << " has " << outputTypes.size()
122  << " outputs";
123 
124  // Check that the operand types match the output ports.
125  for (unsigned i = 0, e = outputTypes.size(); i != e; ++i)
126  if (outputTypes[i] != getOperand(i).getType())
127  return emitOpError() << "operand " << i << " (" << getOperand(i).getType()
128  << ") does not match output type (" << outputTypes[i]
129  << ") of module @" << module.getSymName();
130 
131  return success();
132 }
133 
134 //===----------------------------------------------------------------------===//
135 // InstanceOp
136 //===----------------------------------------------------------------------===//
137 
138 LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
139  // Resolve the target symbol.
140  auto *symbol =
141  symbolTable.lookupNearestSymbolFrom(*this, getModuleNameAttr());
142  if (!symbol)
143  return emitOpError("references unknown symbol @") << getModuleName();
144 
145  // Check that the symbol is a SVModuleOp.
146  auto module = dyn_cast<SVModuleOp>(symbol);
147  if (!module)
148  return emitOpError("must reference a 'moore.module', but @")
149  << getModuleName() << " is a '" << symbol->getName() << "'";
150 
151  // Check that the input ports match.
152  auto moduleType = module.getModuleType();
153  auto inputTypes = moduleType.getInputTypes();
154 
155  if (inputTypes.size() != getNumOperands())
156  return emitOpError("has ")
157  << getNumOperands() << " operands, but target module @"
158  << module.getSymName() << " has " << inputTypes.size() << " inputs";
159 
160  for (unsigned i = 0, e = inputTypes.size(); i != e; ++i)
161  if (inputTypes[i] != getOperand(i).getType())
162  return emitOpError() << "operand " << i << " (" << getOperand(i).getType()
163  << ") does not match input type (" << inputTypes[i]
164  << ") of module @" << module.getSymName();
165 
166  // Check that the output ports match.
167  auto outputTypes = moduleType.getOutputTypes();
168 
169  if (outputTypes.size() != getNumResults())
170  return emitOpError("has ")
171  << getNumOperands() << " results, but target module @"
172  << module.getSymName() << " has " << outputTypes.size()
173  << " outputs";
174 
175  for (unsigned i = 0, e = outputTypes.size(); i != e; ++i)
176  if (outputTypes[i] != getResult(i).getType())
177  return emitOpError() << "result " << i << " (" << getResult(i).getType()
178  << ") does not match output type (" << outputTypes[i]
179  << ") of module @" << module.getSymName();
180 
181  return success();
182 }
183 
184 void InstanceOp::print(OpAsmPrinter &p) {
185  p << " ";
186  p.printAttributeWithoutType(getInstanceNameAttr());
187  p << " ";
188  p.printAttributeWithoutType(getModuleNameAttr());
189  printInputPortList(p, getOperation(), getInputs(), getInputs().getTypes(),
190  getInputNames());
191  p << " -> ";
192  printOutputPortList(p, getOperation(), getOutputs().getTypes(),
193  getOutputNames());
194  p.printOptionalAttrDict(getOperation()->getAttrs(), getAttributeNames());
195 }
196 
197 ParseResult InstanceOp::parse(OpAsmParser &parser, OperationState &result) {
198  // Parse the instance name.
199  StringAttr instanceName;
200  if (parser.parseAttribute(instanceName, "instanceName", result.attributes))
201  return failure();
202 
203  // Parse the module name.
204  FlatSymbolRefAttr moduleName;
205  if (parser.parseAttribute(moduleName, "moduleName", result.attributes))
206  return failure();
207 
208  // Parse the input port list.
209  auto loc = parser.getCurrentLocation();
210  SmallVector<OpAsmParser::UnresolvedOperand> inputs;
211  SmallVector<Type> types;
212  ArrayAttr names;
213  if (parseInputPortList(parser, inputs, types, names))
214  return failure();
215  if (parser.resolveOperands(inputs, types, loc, result.operands))
216  return failure();
217  result.addAttribute("inputNames", names);
218 
219  // Parse `->`.
220  if (parser.parseArrow())
221  return failure();
222 
223  // Parse the output port list.
224  types.clear();
225  if (parseOutputPortList(parser, types, names))
226  return failure();
227  result.addAttribute("outputNames", names);
228  result.addTypes(types);
229 
230  // Parse the attributes.
231  if (parser.parseOptionalAttrDict(result.attributes))
232  return failure();
233 
234  return success();
235 }
236 
238  SmallString<32> name;
239  name += getInstanceName();
240  name += '.';
241  auto baseLen = name.size();
242 
243  for (auto [result, portName] :
244  llvm::zip(getOutputs(), getOutputNames().getAsRange<StringAttr>())) {
245  if (!portName || portName.empty())
246  continue;
247  name.resize(baseLen);
248  name += portName.getValue();
249  setNameFn(result, name);
250  }
251 }
252 
253 //===----------------------------------------------------------------------===//
254 // VariableOp
255 //===----------------------------------------------------------------------===//
256 
258  if (getName() && !getName()->empty())
259  setNameFn(getResult(), *getName());
260 }
261 
262 LogicalResult VariableOp::canonicalize(VariableOp op,
263  PatternRewriter &rewriter) {
264  // If the variable is embedded in an SSACFG region, move the initial value
265  // into an assignment immediately after the variable op. This allows the
266  // mem2reg pass which cannot handle variables with initial values.
267  auto initial = op.getInitial();
268  if (initial && mlir::mayHaveSSADominance(*op->getParentRegion())) {
269  rewriter.modifyOpInPlace(op, [&] { op.getInitialMutable().clear(); });
270  rewriter.setInsertionPointAfter(op);
271  rewriter.create<BlockingAssignOp>(initial.getLoc(), op, initial);
272  return success();
273  }
274 
275  // Check if the variable has one unique continuous assignment to it, all other
276  // uses are reads, and that all uses are in the same block as the variable
277  // itself.
278  auto *block = op->getBlock();
279  ContinuousAssignOp uniqueAssignOp;
280  for (auto *user : op->getUsers()) {
281  // Ensure that all users of the variable are in the same block.
282  if (user->getBlock() != block)
283  return failure();
284 
285  // Ensure there is at most one unique continuous assignment to the variable.
286  if (auto assignOp = dyn_cast<ContinuousAssignOp>(user)) {
287  if (uniqueAssignOp)
288  return failure();
289  uniqueAssignOp = assignOp;
290  continue;
291  }
292 
293  // Ensure all other users are reads.
294  if (!isa<ReadOp>(user))
295  return failure();
296  }
297  if (!uniqueAssignOp)
298  return failure();
299 
300  // If the original variable had a name, create an `AssignedVariableOp` as a
301  // replacement. Otherwise substitute the assigned value directly.
302  Value assignedValue = uniqueAssignOp.getSrc();
303  if (auto name = op.getNameAttr(); name && !name.empty())
304  assignedValue = rewriter.create<AssignedVariableOp>(
305  op.getLoc(), name, uniqueAssignOp.getSrc());
306 
307  // Remove the assign op and replace all reads with the new assigned var op.
308  rewriter.eraseOp(uniqueAssignOp);
309  for (auto *user : llvm::make_early_inc_range(op->getUsers())) {
310  auto readOp = cast<ReadOp>(user);
311  rewriter.replaceOp(readOp, assignedValue);
312  }
313 
314  // Remove the original variable.
315  rewriter.eraseOp(op);
316  return success();
317 }
318 
319 SmallVector<MemorySlot> VariableOp::getPromotableSlots() {
320  // We cannot promote variables with an initial value, since that value may not
321  // dominate the location where the default value needs to be constructed.
322  if (mlir::mayBeGraphRegion(*getOperation()->getParentRegion()) ||
323  getInitial())
324  return {};
325 
326  // Ensure that `getDefaultValue` can conjure up a default value for the
327  // variable's type.
328  if (!isa<PackedType>(getType().getNestedType()))
329  return {};
330 
331  return {MemorySlot{getResult(), getType().getNestedType()}};
332 }
333 
334 Value VariableOp::getDefaultValue(const MemorySlot &slot, OpBuilder &builder) {
335  auto packedType = dyn_cast<PackedType>(slot.elemType);
336  if (!packedType)
337  return {};
338  auto bitWidth = packedType.getBitSize();
339  if (!bitWidth)
340  return {};
341  auto fvint = packedType.getDomain() == Domain::FourValued
342  ? FVInt::getAllX(*bitWidth)
343  : FVInt::getZero(*bitWidth);
344  Value value = builder.create<ConstantOp>(
345  getLoc(), IntType::get(getContext(), *bitWidth, packedType.getDomain()),
346  fvint);
347  if (value.getType() != packedType)
348  builder.create<ConversionOp>(getLoc(), packedType, value);
349  return value;
350 }
351 
352 void VariableOp::handleBlockArgument(const MemorySlot &slot,
353  BlockArgument argument,
354  OpBuilder &builder) {}
355 
356 std::optional<mlir::PromotableAllocationOpInterface>
357 VariableOp::handlePromotionComplete(const MemorySlot &slot, Value defaultValue,
358  OpBuilder &builder) {
359  if (defaultValue && defaultValue.use_empty())
360  defaultValue.getDefiningOp()->erase();
361  this->erase();
362  return {};
363 }
364 
365 SmallVector<DestructurableMemorySlot> VariableOp::getDestructurableSlots() {
366  if (isa<SVModuleOp>(getOperation()->getParentOp()))
367  return {};
368  if (getInitial())
369  return {};
370 
371  auto refType = getType();
372  auto destructurable = llvm::dyn_cast<DestructurableTypeInterface>(refType);
373  if (!destructurable)
374  return {};
375 
376  auto destructuredType = destructurable.getSubelementIndexMap();
377  if (!destructuredType)
378  return {};
379 
380  return {DestructurableMemorySlot{{getResult(), refType}, *destructuredType}};
381 }
382 
383 DenseMap<Attribute, MemorySlot> VariableOp::destructure(
384  const DestructurableMemorySlot &slot,
385  const SmallPtrSetImpl<Attribute> &usedIndices, OpBuilder &builder,
386  SmallVectorImpl<DestructurableAllocationOpInterface> &newAllocators) {
387  assert(slot.ptr == getResult());
388  assert(!getInitial());
389  builder.setInsertionPointAfter(*this);
390 
391  auto destructurableType = cast<DestructurableTypeInterface>(getType());
392  DenseMap<Attribute, MemorySlot> slotMap;
393  for (Attribute index : usedIndices) {
394  auto elemType = cast<RefType>(destructurableType.getTypeAtIndex(index));
395  assert(elemType && "used index must exist");
396  StringAttr varName;
397  if (auto name = getName(); name && !name->empty())
398  varName = StringAttr::get(
399  getContext(), (*name) + "." + cast<StringAttr>(index).getValue());
400  auto varOp =
401  builder.create<VariableOp>(getLoc(), elemType, varName, Value());
402  newAllocators.push_back(varOp);
403  slotMap.try_emplace<MemorySlot>(index, {varOp.getResult(), elemType});
404  }
405 
406  return slotMap;
407 }
408 
409 std::optional<DestructurableAllocationOpInterface>
410 VariableOp::handleDestructuringComplete(const DestructurableMemorySlot &slot,
411  OpBuilder &builder) {
412  assert(slot.ptr == getResult());
413  this->erase();
414  return std::nullopt;
415 }
416 
417 //===----------------------------------------------------------------------===//
418 // NetOp
419 //===----------------------------------------------------------------------===//
420 
422  if (getName() && !getName()->empty())
423  setNameFn(getResult(), *getName());
424 }
425 
426 LogicalResult NetOp::canonicalize(NetOp op, PatternRewriter &rewriter) {
427  bool modified = false;
428 
429  // Check if the net has one unique continuous assignment to it, and
430  // additionally if all other users are reads.
431  auto *block = op->getBlock();
432  ContinuousAssignOp uniqueAssignOp;
433  bool allUsesAreReads = true;
434  for (auto *user : op->getUsers()) {
435  // Ensure that all users of the net are in the same block.
436  if (user->getBlock() != block)
437  return failure();
438 
439  // Ensure there is at most one unique continuous assignment to the net.
440  if (auto assignOp = dyn_cast<ContinuousAssignOp>(user)) {
441  if (uniqueAssignOp)
442  return failure();
443  uniqueAssignOp = assignOp;
444  continue;
445  }
446 
447  // Ensure all other users are reads.
448  if (!isa<ReadOp>(user))
449  allUsesAreReads = false;
450  }
451 
452  // If there was one unique assignment, and the `NetOp` does not yet have an
453  // assigned value set, fold the assignment into the net.
454  if (uniqueAssignOp && !op.getAssignment()) {
455  rewriter.modifyOpInPlace(
456  op, [&] { op.getAssignmentMutable().assign(uniqueAssignOp.getSrc()); });
457  rewriter.eraseOp(uniqueAssignOp);
458  modified = true;
459  uniqueAssignOp = {};
460  }
461 
462  // If all users of the net op are reads, and any potential unique assignment
463  // has been folded into the net op itself, directly replace the reads with the
464  // net's assigned value.
465  if (!uniqueAssignOp && allUsesAreReads && op.getAssignment()) {
466  // If the original net had a name, create an `AssignedVariableOp` as a
467  // replacement. Otherwise substitute the assigned value directly.
468  auto assignedValue = op.getAssignment();
469  if (auto name = op.getNameAttr(); name && !name.empty())
470  assignedValue =
471  rewriter.create<AssignedVariableOp>(op.getLoc(), name, assignedValue);
472 
473  // Replace all reads with the new assigned var op and remove the original
474  // net op.
475  for (auto *user : llvm::make_early_inc_range(op->getUsers())) {
476  auto readOp = cast<ReadOp>(user);
477  rewriter.replaceOp(readOp, assignedValue);
478  }
479  rewriter.eraseOp(op);
480  modified = true;
481  }
482 
483  return success(modified);
484 }
485 
486 //===----------------------------------------------------------------------===//
487 // AssignedVariableOp
488 //===----------------------------------------------------------------------===//
489 
491  if (getName() && !getName()->empty())
492  setNameFn(getResult(), *getName());
493 }
494 
495 LogicalResult AssignedVariableOp::canonicalize(AssignedVariableOp op,
496  PatternRewriter &rewriter) {
497  // Eliminate chained variables with the same name.
498  // var(name, var(name, x)) -> var(name, x)
499  if (auto otherOp = op.getInput().getDefiningOp<AssignedVariableOp>()) {
500  if (otherOp.getNameAttr() == op.getNameAttr()) {
501  rewriter.replaceOp(op, otherOp);
502  return success();
503  }
504  }
505 
506  // Eliminate variables that alias an input port of the same name.
507  if (auto blockArg = dyn_cast<BlockArgument>(op.getInput())) {
508  if (auto moduleOp =
509  dyn_cast<SVModuleOp>(blockArg.getOwner()->getParentOp())) {
510  auto moduleType = moduleOp.getModuleType();
511  auto portName = moduleType.getInputNameAttr(blockArg.getArgNumber());
512  if (portName == op.getNameAttr()) {
513  rewriter.replaceOp(op, blockArg);
514  return success();
515  }
516  }
517  }
518 
519  // Eliminate variables that feed an output port of the same name.
520  for (auto &use : op->getUses()) {
521  auto *useOwner = use.getOwner();
522  if (auto outputOp = dyn_cast<OutputOp>(useOwner)) {
523  if (auto moduleOp = dyn_cast<SVModuleOp>(outputOp->getParentOp())) {
524  auto moduleType = moduleOp.getModuleType();
525  auto portName = moduleType.getOutputNameAttr(use.getOperandNumber());
526  if (portName == op.getNameAttr()) {
527  rewriter.replaceOp(op, op.getInput());
528  return success();
529  }
530  } else
531  break;
532  }
533  }
534 
535  return failure();
536 }
537 
538 //===----------------------------------------------------------------------===//
539 // ConstantOp
540 //===----------------------------------------------------------------------===//
541 
542 void ConstantOp::print(OpAsmPrinter &p) {
543  p << " ";
544  printFVInt(p, getValue());
545  p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"value"});
546  p << " : ";
547  p.printStrippedAttrOrType(getType());
548 }
549 
550 ParseResult ConstantOp::parse(OpAsmParser &parser, OperationState &result) {
551  // Parse the constant value.
552  FVInt value;
553  auto valueLoc = parser.getCurrentLocation();
554  if (parseFVInt(parser, value))
555  return failure();
556 
557  // Parse any optional attributes and the `:`.
558  if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon())
559  return failure();
560 
561  // Parse the result type.
562  IntType type;
563  if (parser.parseCustomTypeWithFallback(type))
564  return failure();
565 
566  // Extend or truncate the constant value to match the size of the type.
567  if (type.getWidth() > value.getBitWidth()) {
568  // sext is always safe here, even for unsigned values, because the
569  // parseOptionalInteger method will return something with a zero in the
570  // top bits if it is a positive number.
571  value = value.sext(type.getWidth());
572  } else if (type.getWidth() < value.getBitWidth()) {
573  // The parser can return an unnecessarily wide result with leading
574  // zeros. This isn't a problem, but truncating off bits is bad.
575  unsigned neededBits =
576  value.isNegative() ? value.getSignificantBits() : value.getActiveBits();
577  if (type.getWidth() < neededBits)
578  return parser.emitError(valueLoc)
579  << "value requires " << neededBits
580  << " bits, but result type only has " << type.getWidth();
581  value = value.trunc(type.getWidth());
582  }
583 
584  // If the constant contains any X or Z bits, the result type must be
585  // four-valued.
586  if (value.hasUnknown() && type.getDomain() != Domain::FourValued)
587  return parser.emitError(valueLoc)
588  << "value contains X or Z bits, but result type " << type
589  << " only allows two-valued bits";
590 
591  // Build the attribute and op.
592  auto attrValue = FVIntegerAttr::get(parser.getContext(), value);
593  result.addAttribute("value", attrValue);
594  result.addTypes(type);
595  return success();
596 }
597 
598 LogicalResult ConstantOp::verify() {
599  auto attrWidth = getValue().getBitWidth();
600  auto typeWidth = getType().getWidth();
601  if (attrWidth != typeWidth)
602  return emitError("attribute width ")
603  << attrWidth << " does not match return type's width " << typeWidth;
604  return success();
605 }
606 
607 void ConstantOp::build(OpBuilder &builder, OperationState &result, IntType type,
608  const FVInt &value) {
609  assert(type.getWidth() == value.getBitWidth() &&
610  "FVInt width must match type width");
611  build(builder, result, type, FVIntegerAttr::get(builder.getContext(), value));
612 }
613 
614 void ConstantOp::build(OpBuilder &builder, OperationState &result, IntType type,
615  const APInt &value) {
616  assert(type.getWidth() == value.getBitWidth() &&
617  "APInt width must match type width");
618  build(builder, result, type, FVInt(value));
619 }
620 
621 /// This builder allows construction of small signed integers like 0, 1, -1
622 /// matching a specified MLIR type. This shouldn't be used for general constant
623 /// folding because it only works with values that can be expressed in an
624 /// `int64_t`.
625 void ConstantOp::build(OpBuilder &builder, OperationState &result, IntType type,
626  int64_t value) {
627  build(builder, result, type,
628  APInt(type.getWidth(), (uint64_t)value, /*isSigned=*/true));
629 }
630 
631 OpFoldResult ConstantOp::fold(FoldAdaptor adaptor) {
632  assert(adaptor.getOperands().empty() && "constant has no operands");
633  return getValueAttr();
634 }
635 
636 //===----------------------------------------------------------------------===//
637 // ConcatOp
638 //===----------------------------------------------------------------------===//
639 
640 LogicalResult ConcatOp::inferReturnTypes(
641  MLIRContext *context, std::optional<Location> loc, ValueRange operands,
642  DictionaryAttr attrs, mlir::OpaqueProperties properties,
643  mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
644  Domain domain = Domain::TwoValued;
645  unsigned width = 0;
646  for (auto operand : operands) {
647  auto type = cast<IntType>(operand.getType());
648  if (type.getDomain() == Domain::FourValued)
649  domain = Domain::FourValued;
650  width += type.getWidth();
651  }
652  results.push_back(IntType::get(context, width, domain));
653  return success();
654 }
655 
656 //===----------------------------------------------------------------------===//
657 // ConcatRefOp
658 //===----------------------------------------------------------------------===//
659 
660 LogicalResult ConcatRefOp::inferReturnTypes(
661  MLIRContext *context, std::optional<Location> loc, ValueRange operands,
662  DictionaryAttr attrs, mlir::OpaqueProperties properties,
663  mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
664  Domain domain = Domain::TwoValued;
665  unsigned width = 0;
666  for (auto operand : operands) {
667  auto type = cast<IntType>(cast<RefType>(operand.getType()).getNestedType());
668  if (type.getDomain() == Domain::FourValued)
669  domain = Domain::FourValued;
670  width += type.getWidth();
671  }
672  results.push_back(RefType::get(IntType::get(context, width, domain)));
673  return success();
674 }
675 
676 //===----------------------------------------------------------------------===//
677 // ArrayCreateOp
678 //===----------------------------------------------------------------------===//
679 
680 static std::pair<unsigned, UnpackedType> getArrayElements(Type type) {
681  if (auto arrayType = dyn_cast<ArrayType>(type))
682  return {arrayType.getSize(), arrayType.getElementType()};
683  if (auto arrayType = dyn_cast<UnpackedArrayType>(type))
684  return {arrayType.getSize(), arrayType.getElementType()};
685  assert(0 && "expected ArrayType or UnpackedArrayType");
686  return {};
687 }
688 
689 LogicalResult ArrayCreateOp::verify() {
690  auto [size, elementType] = getArrayElements(getType());
691 
692  // Check that the number of operands matches the array size.
693  if (getElements().size() != size)
694  return emitOpError() << "has " << getElements().size()
695  << " operands, but result type requires " << size;
696 
697  // Check that the operand types match the array element type. We only need to
698  // check one of the operands, since the `SameTypeOperands` trait ensures all
699  // operands have the same type.
700  if (size > 0) {
701  auto value = getElements()[0];
702  if (value.getType() != elementType)
703  return emitOpError() << "operands have type " << value.getType()
704  << ", but array requires " << elementType;
705  }
706  return success();
707 }
708 
709 //===----------------------------------------------------------------------===//
710 // StructCreateOp
711 //===----------------------------------------------------------------------===//
712 
713 static std::optional<uint32_t> getStructFieldIndex(Type type, StringAttr name) {
714  if (auto structType = dyn_cast<StructType>(type))
715  return structType.getFieldIndex(name);
716  if (auto structType = dyn_cast<UnpackedStructType>(type))
717  return structType.getFieldIndex(name);
718  assert(0 && "expected StructType or UnpackedStructType");
719  return {};
720 }
721 
722 static ArrayRef<StructLikeMember> getStructMembers(Type type) {
723  if (auto structType = dyn_cast<StructType>(type))
724  return structType.getMembers();
725  if (auto structType = dyn_cast<UnpackedStructType>(type))
726  return structType.getMembers();
727  assert(0 && "expected StructType or UnpackedStructType");
728  return {};
729 }
730 
731 static UnpackedType getStructFieldType(Type type, StringAttr name) {
732  if (auto index = getStructFieldIndex(type, name))
733  return getStructMembers(type)[*index].type;
734  return {};
735 }
736 
737 LogicalResult StructCreateOp::verify() {
738  auto members = getStructMembers(getType());
739 
740  // Check that the number of operands matches the number of struct fields.
741  if (getFields().size() != members.size())
742  return emitOpError() << "has " << getFields().size()
743  << " operands, but result type requires "
744  << members.size();
745 
746  // Check that the operand types match the struct field types.
747  for (auto [index, pair] : llvm::enumerate(llvm::zip(getFields(), members))) {
748  auto [value, member] = pair;
749  if (value.getType() != member.type)
750  return emitOpError() << "operand #" << index << " has type "
751  << value.getType() << ", but struct field "
752  << member.name << " requires " << member.type;
753  }
754  return success();
755 }
756 
757 OpFoldResult StructCreateOp::fold(FoldAdaptor adaptor) {
758  SmallVector<NamedAttribute> fields;
759  for (auto [member, field] :
760  llvm::zip(getStructMembers(getType()), adaptor.getFields())) {
761  if (!field)
762  return {};
763  fields.push_back(NamedAttribute(member.name, field));
764  }
765  return DictionaryAttr::get(getContext(), fields);
766 }
767 
768 //===----------------------------------------------------------------------===//
769 // StructExtractOp
770 //===----------------------------------------------------------------------===//
771 
772 LogicalResult StructExtractOp::verify() {
773  auto type = getStructFieldType(getInput().getType(), getFieldNameAttr());
774  if (!type)
775  return emitOpError() << "extracts field " << getFieldNameAttr()
776  << " which does not exist in " << getInput().getType();
777  if (type != getType())
778  return emitOpError() << "result type " << getType()
779  << " must match struct field type " << type;
780  return success();
781 }
782 
783 OpFoldResult StructExtractOp::fold(FoldAdaptor adaptor) {
784  // Extract on a constant struct input.
785  if (auto fields = dyn_cast_or_null<DictionaryAttr>(adaptor.getInput()))
786  if (auto value = fields.get(getFieldNameAttr()))
787  return value;
788 
789  // extract(inject(s, "field", v), "field") -> v
790  if (auto inject = getInput().getDefiningOp<StructInjectOp>()) {
791  if (inject.getFieldNameAttr() == getFieldNameAttr())
792  return inject.getNewValue();
793  return {};
794  }
795 
796  // extract(create({"field": v, ...}), "field") -> v
797  if (auto create = getInput().getDefiningOp<StructCreateOp>()) {
798  if (auto index = getStructFieldIndex(create.getType(), getFieldNameAttr()))
799  return create.getFields()[*index];
800  return {};
801  }
802 
803  return {};
804 }
805 
806 //===----------------------------------------------------------------------===//
807 // StructExtractRefOp
808 //===----------------------------------------------------------------------===//
809 
810 LogicalResult StructExtractRefOp::verify() {
811  auto type = getStructFieldType(
812  cast<RefType>(getInput().getType()).getNestedType(), getFieldNameAttr());
813  if (!type)
814  return emitOpError() << "extracts field " << getFieldNameAttr()
815  << " which does not exist in " << getInput().getType();
816  if (type != getType().getNestedType())
817  return emitOpError() << "result ref of type " << getType().getNestedType()
818  << " must match struct field type " << type;
819  return success();
820 }
821 
822 bool StructExtractRefOp::canRewire(
823  const DestructurableMemorySlot &slot,
824  SmallPtrSetImpl<Attribute> &usedIndices,
825  SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
826  const DataLayout &dataLayout) {
827  if (slot.ptr != getInput())
828  return false;
829  auto index = getFieldNameAttr();
830  if (!index || !slot.subelementTypes.contains(index))
831  return false;
832  usedIndices.insert(index);
833  return true;
834 }
835 
836 DeletionKind
837 StructExtractRefOp::rewire(const DestructurableMemorySlot &slot,
838  DenseMap<Attribute, MemorySlot> &subslots,
839  OpBuilder &builder, const DataLayout &dataLayout) {
840  auto index = getFieldNameAttr();
841  const MemorySlot &memorySlot = subslots.at(index);
842  replaceAllUsesWith(memorySlot.ptr);
843  getInputMutable().drop();
844  erase();
845  return DeletionKind::Keep;
846 }
847 
848 //===----------------------------------------------------------------------===//
849 // StructInjectOp
850 //===----------------------------------------------------------------------===//
851 
852 LogicalResult StructInjectOp::verify() {
853  auto type = getStructFieldType(getInput().getType(), getFieldNameAttr());
854  if (!type)
855  return emitOpError() << "injects field " << getFieldNameAttr()
856  << " which does not exist in " << getInput().getType();
857  if (type != getNewValue().getType())
858  return emitOpError() << "injected value " << getNewValue().getType()
859  << " must match struct field type " << type;
860  return success();
861 }
862 
863 OpFoldResult StructInjectOp::fold(FoldAdaptor adaptor) {
864  auto input = adaptor.getInput();
865  auto newValue = adaptor.getNewValue();
866  if (!input || !newValue)
867  return {};
868  NamedAttrList fields(cast<DictionaryAttr>(input));
869  fields.set(getFieldNameAttr(), newValue);
870  return fields.getDictionary(getContext());
871 }
872 
873 LogicalResult StructInjectOp::canonicalize(StructInjectOp op,
874  PatternRewriter &rewriter) {
875  auto members = getStructMembers(op.getType());
876 
877  // Chase a chain of `struct_inject` ops, with an optional final
878  // `struct_create`, and take note of the values assigned to each field.
879  SmallPtrSet<Operation *, 4> injectOps;
880  DenseMap<StringAttr, Value> fieldValues;
881  Value input = op;
882  while (auto injectOp = input.getDefiningOp<StructInjectOp>()) {
883  if (!injectOps.insert(injectOp).second)
884  return failure();
885  fieldValues.insert({injectOp.getFieldNameAttr(), injectOp.getNewValue()});
886  input = injectOp.getInput();
887  }
888  if (auto createOp = input.getDefiningOp<StructCreateOp>())
889  for (auto [value, member] : llvm::zip(createOp.getFields(), members))
890  fieldValues.insert({member.name, value});
891 
892  // If the inject chain sets all fields, canonicalize to a `struct_create`.
893  if (fieldValues.size() == members.size()) {
894  SmallVector<Value> values;
895  values.reserve(fieldValues.size());
896  for (auto member : members)
897  values.push_back(fieldValues.lookup(member.name));
898  rewriter.replaceOpWithNewOp<StructCreateOp>(op, op.getType(), values);
899  return success();
900  }
901 
902  // If each inject op in the chain assigned to a unique field, there is nothing
903  // to canonicalize.
904  if (injectOps.size() == fieldValues.size())
905  return failure();
906 
907  // Otherwise we can eliminate overwrites by creating new injects. The hash map
908  // of field values contains the last assigned value for each field.
909  for (auto member : members)
910  if (auto value = fieldValues.lookup(member.name))
911  input = rewriter.create<StructInjectOp>(op.getLoc(), op.getType(), input,
912  member.name, value);
913  rewriter.replaceOp(op, input);
914  return success();
915 }
916 
917 //===----------------------------------------------------------------------===//
918 // UnionCreateOp
919 //===----------------------------------------------------------------------===//
920 
921 LogicalResult UnionCreateOp::verify() {
922  /// checks if the types of the input is exactly equal to the union field
923  /// type
924  return TypeSwitch<Type, LogicalResult>(getType())
925  .Case<UnionType, UnpackedUnionType>([this](auto &type) {
926  auto members = type.getMembers();
927  auto resultType = getType();
928  auto fieldName = getFieldName();
929  for (const auto &member : members)
930  if (member.name == fieldName && member.type == resultType)
931  return success();
932  emitOpError("input type must match the union field type");
933  return failure();
934  })
935  .Default([this](auto &) {
936  emitOpError("input type must be UnionType or UnpackedUnionType");
937  return failure();
938  });
939 }
940 
941 //===----------------------------------------------------------------------===//
942 // UnionExtractOp
943 //===----------------------------------------------------------------------===//
944 
945 LogicalResult UnionExtractOp::verify() {
946  /// checks if the types of the input is exactly equal to the one of the
947  /// types of the result union fields
948  return TypeSwitch<Type, LogicalResult>(getInput().getType())
949  .Case<UnionType, UnpackedUnionType>([this](auto &type) {
950  auto members = type.getMembers();
951  auto fieldName = getFieldName();
952  auto resultType = getType();
953  for (const auto &member : members)
954  if (member.name == fieldName && member.type == resultType)
955  return success();
956  emitOpError("result type must match the union field type");
957  return failure();
958  })
959  .Default([this](auto &) {
960  emitOpError("input type must be UnionType or UnpackedUnionType");
961  return failure();
962  });
963 }
964 
965 //===----------------------------------------------------------------------===//
966 // UnionExtractOp
967 //===----------------------------------------------------------------------===//
968 
969 LogicalResult UnionExtractRefOp::verify() {
970  /// checks if the types of the result is exactly equal to the type of the
971  /// refe union field
972  return TypeSwitch<Type, LogicalResult>(getInput().getType().getNestedType())
973  .Case<UnionType, UnpackedUnionType>([this](auto &type) {
974  auto members = type.getMembers();
975  auto fieldName = getFieldName();
976  auto resultType = getType().getNestedType();
977  for (const auto &member : members)
978  if (member.name == fieldName && member.type == resultType)
979  return success();
980  emitOpError("result type must match the union field type");
981  return failure();
982  })
983  .Default([this](auto &) {
984  emitOpError("input type must be UnionType or UnpackedUnionType");
985  return failure();
986  });
987 }
988 
989 //===----------------------------------------------------------------------===//
990 // YieldOp
991 //===----------------------------------------------------------------------===//
992 
993 LogicalResult YieldOp::verify() {
994  // Check that YieldOp's parent operation is ConditionalOp.
995  auto cond = dyn_cast<ConditionalOp>(*(*this).getParentOp());
996  if (!cond) {
997  emitOpError("must have a conditional parent");
998  return failure();
999  }
1000 
1001  // Check that the operand matches the parent operation's result.
1002  auto condType = cond.getType();
1003  auto yieldType = getOperand().getType();
1004  if (condType != yieldType) {
1005  emitOpError("yield type must match conditional. Expected ")
1006  << condType << ", but got " << yieldType << ".";
1007  return failure();
1008  }
1009 
1010  return success();
1011 }
1012 
1013 //===----------------------------------------------------------------------===//
1014 // ConversionOp
1015 //===----------------------------------------------------------------------===//
1016 
1017 OpFoldResult ConversionOp::fold(FoldAdaptor adaptor) {
1018  // Fold away no-op casts.
1019  if (getInput().getType() == getResult().getType())
1020  return getInput();
1021 
1022  // Convert domains of constant integer inputs.
1023  auto intInput = dyn_cast_or_null<FVIntegerAttr>(adaptor.getInput());
1024  auto fromIntType = dyn_cast<IntType>(getInput().getType());
1025  auto toIntType = dyn_cast<IntType>(getResult().getType());
1026  if (intInput && fromIntType && toIntType &&
1027  fromIntType.getWidth() == toIntType.getWidth()) {
1028  // If we are going *to* a four-valued type, simply pass through the
1029  // constant.
1030  if (toIntType.getDomain() == Domain::FourValued)
1031  return intInput;
1032 
1033  // Otherwise map all unknown bits to zero (the default in SystemVerilog) and
1034  // return a new constant.
1035  return FVIntegerAttr::get(getContext(), intInput.getValue().toAPInt(false));
1036  }
1037 
1038  return {};
1039 }
1040 
1041 //===----------------------------------------------------------------------===//
1042 // BoolCastOp
1043 //===----------------------------------------------------------------------===//
1044 
1045 OpFoldResult BoolCastOp::fold(FoldAdaptor adaptor) {
1046  // Fold away no-op casts.
1047  if (getInput().getType() == getResult().getType())
1048  return getInput();
1049  return {};
1050 }
1051 
1052 //===----------------------------------------------------------------------===//
1053 // BlockingAssignOp
1054 //===----------------------------------------------------------------------===//
1055 
1056 bool BlockingAssignOp::loadsFrom(const MemorySlot &slot) { return false; }
1057 
1058 bool BlockingAssignOp::storesTo(const MemorySlot &slot) {
1059  return getDst() == slot.ptr;
1060 }
1061 
1062 Value BlockingAssignOp::getStored(const MemorySlot &slot, OpBuilder &builder,
1063  Value reachingDef,
1064  const DataLayout &dataLayout) {
1065  return getSrc();
1066 }
1067 
1068 bool BlockingAssignOp::canUsesBeRemoved(
1069  const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1070  SmallVectorImpl<OpOperand *> &newBlockingUses,
1071  const DataLayout &dataLayout) {
1072 
1073  if (blockingUses.size() != 1)
1074  return false;
1075  Value blockingUse = (*blockingUses.begin())->get();
1076  return blockingUse == slot.ptr && getDst() == slot.ptr &&
1077  getSrc() != slot.ptr && getSrc().getType() == slot.elemType;
1078 }
1079 
1080 DeletionKind BlockingAssignOp::removeBlockingUses(
1081  const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1082  OpBuilder &builder, Value reachingDefinition,
1083  const DataLayout &dataLayout) {
1084  return DeletionKind::Delete;
1085 }
1086 
1087 //===----------------------------------------------------------------------===//
1088 // ReadOp
1089 //===----------------------------------------------------------------------===//
1090 
1091 bool ReadOp::loadsFrom(const MemorySlot &slot) {
1092  return getInput() == slot.ptr;
1093 }
1094 
1095 bool ReadOp::storesTo(const MemorySlot &slot) { return false; }
1096 
1097 Value ReadOp::getStored(const MemorySlot &slot, OpBuilder &builder,
1098  Value reachingDef, const DataLayout &dataLayout) {
1099  llvm_unreachable("getStored should not be called on ReadOp");
1100 }
1101 
1102 bool ReadOp::canUsesBeRemoved(const MemorySlot &slot,
1103  const SmallPtrSetImpl<OpOperand *> &blockingUses,
1104  SmallVectorImpl<OpOperand *> &newBlockingUses,
1105  const DataLayout &dataLayout) {
1106 
1107  if (blockingUses.size() != 1)
1108  return false;
1109  Value blockingUse = (*blockingUses.begin())->get();
1110  return blockingUse == slot.ptr && getOperand() == slot.ptr &&
1111  getResult().getType() == slot.elemType;
1112 }
1113 
1114 DeletionKind
1115 ReadOp::removeBlockingUses(const MemorySlot &slot,
1116  const SmallPtrSetImpl<OpOperand *> &blockingUses,
1117  OpBuilder &builder, Value reachingDefinition,
1118  const DataLayout &dataLayout) {
1119  getResult().replaceAllUsesWith(reachingDefinition);
1120  return DeletionKind::Delete;
1121 }
1122 
1123 //===----------------------------------------------------------------------===//
1124 // PowSOp
1125 //===----------------------------------------------------------------------===//
1126 
1127 static OpFoldResult powCommonFolding(MLIRContext *ctxt, Attribute lhs,
1128  Attribute rhs) {
1129  auto lhsValue = dyn_cast_or_null<FVIntegerAttr>(lhs);
1130  if (lhsValue && lhsValue.getValue() == 1)
1131  return lhs;
1132 
1133  auto rhsValue = dyn_cast_or_null<FVIntegerAttr>(rhs);
1134  if (rhsValue && rhsValue.getValue().isZero())
1135  return FVIntegerAttr::get(ctxt,
1136  FVInt(rhsValue.getValue().getBitWidth(), 1));
1137 
1138  return {};
1139 }
1140 
1141 OpFoldResult PowSOp::fold(FoldAdaptor adaptor) {
1142  return powCommonFolding(getContext(), adaptor.getLhs(), adaptor.getRhs());
1143 }
1144 
1145 LogicalResult PowSOp::canonicalize(PowSOp op, PatternRewriter &rewriter) {
1146  Location loc = op.getLoc();
1147  auto intType = cast<IntType>(op.getRhs().getType());
1148  if (auto baseOp = op.getLhs().getDefiningOp<ConstantOp>()) {
1149  if (baseOp.getValue() == 2) {
1150  Value constOne = rewriter.create<ConstantOp>(loc, intType, 1);
1151  Value constZero = rewriter.create<ConstantOp>(loc, intType, 0);
1152  Value shift = rewriter.create<ShlOp>(loc, constOne, op.getRhs());
1153  Value isNegative = rewriter.create<SltOp>(loc, op.getRhs(), constZero);
1154  auto condOp = rewriter.replaceOpWithNewOp<ConditionalOp>(
1155  op, op.getLhs().getType(), isNegative);
1156  Block *thenBlock = rewriter.createBlock(&condOp.getTrueRegion());
1157  rewriter.setInsertionPointToStart(thenBlock);
1158  rewriter.create<YieldOp>(loc, constZero);
1159  Block *elseBlock = rewriter.createBlock(&condOp.getFalseRegion());
1160  rewriter.setInsertionPointToStart(elseBlock);
1161  rewriter.create<YieldOp>(loc, shift);
1162  return success();
1163  }
1164  }
1165 
1166  return failure();
1167 }
1168 
1169 //===----------------------------------------------------------------------===//
1170 // PowUOp
1171 //===----------------------------------------------------------------------===//
1172 
1173 OpFoldResult PowUOp::fold(FoldAdaptor adaptor) {
1174  return powCommonFolding(getContext(), adaptor.getLhs(), adaptor.getRhs());
1175 }
1176 
1177 LogicalResult PowUOp::canonicalize(PowUOp op, PatternRewriter &rewriter) {
1178  Location loc = op.getLoc();
1179  auto intType = cast<IntType>(op.getRhs().getType());
1180  if (auto baseOp = op.getLhs().getDefiningOp<ConstantOp>()) {
1181  if (baseOp.getValue() == 2) {
1182  Value constOne = rewriter.create<ConstantOp>(loc, intType, 1);
1183  rewriter.replaceOpWithNewOp<ShlOp>(op, constOne, op.getRhs());
1184  return success();
1185  }
1186  }
1187 
1188  return failure();
1189 }
1190 
1191 //===----------------------------------------------------------------------===//
1192 // TableGen generated logic.
1193 //===----------------------------------------------------------------------===//
1194 
1195 // Provide the autogenerated implementation guts for the Op classes.
1196 #define GET_OP_CLASSES
1197 #include "circt/Dialect/Moore/Moore.cpp.inc"
1198 #include "circt/Dialect/Moore/MooreEnums.cpp.inc"
assert(baseType &&"element must be base type")
MlirType elementType
Definition: CHIRRTL.cpp:29
@ Output
Definition: HW.h:35
static bool getFieldName(const FieldRef &fieldRef, SmallString< 32 > &string)
static InstancePath empty
static ArrayRef< StructLikeMember > getStructMembers(Type type)
Definition: MooreOps.cpp:722
static std::optional< uint32_t > getStructFieldIndex(Type type, StringAttr name)
Definition: MooreOps.cpp:713
static OpFoldResult powCommonFolding(MLIRContext *ctxt, Attribute lhs, Attribute rhs)
Definition: MooreOps.cpp:1127
static std::pair< unsigned, UnpackedType > getArrayElements(Type type)
Definition: MooreOps.cpp:680
static UnpackedType getStructFieldType(Type type, StringAttr name)
Definition: MooreOps.cpp:731
Four-valued arbitrary precision integers.
Definition: FVInt.h:37
bool isNegative() const
Determine whether the integer interpreted as a signed number would be negative.
Definition: FVInt.h:181
FVInt sext(unsigned bitWidth) const
Sign-extend the integer to a new bit width.
Definition: FVInt.h:144
unsigned getSignificantBits() const
Compute the minimum bit width necessary to accurately represent this integer's value and sign.
Definition: FVInt.h:98
static FVInt getAllX(unsigned numBits)
Construct an FVInt with all bits set to X.
Definition: FVInt.h:71
bool hasUnknown() const
Determine if any bits are X or Z.
Definition: FVInt.h:164
unsigned getActiveBits() const
Compute the number of active bits in the value.
Definition: FVInt.h:88
unsigned getBitWidth() const
Return the number of bits this integer has.
Definition: FVInt.h:81
FVInt trunc(unsigned bitWidth) const
Truncate the integer to a smaller bit width.
Definition: FVInt.h:128
An unpacked SystemVerilog type.
Definition: MooreTypes.h:82
static LogicalResult canonicalize(Op op, PatternRewriter &rewriter)
Definition: VerifOps.cpp:66
static LogicalResult verify(Value clock, bool eventExists, mlir::Location loc)
Definition: SVOps.cpp:2467
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
std::string getInstanceName(mlir::func::CallOp callOp)
A helper function to get the instance name.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
ParseResult parseModuleSignature(OpAsmParser &parser, SmallVectorImpl< PortParse > &args, TypeAttr &modType)
New Style parsing.
void printModuleSignatureNew(OpAsmPrinter &p, Region &body, hw::ModuleType modType, ArrayRef< Attribute > portAttrs, ArrayRef< Location > locAttrs)
FunctionType getModuleType(Operation *module)
Return the signature for the specified module as a function type.
Definition: HWOps.cpp:521
Domain
The number of values each bit of a type can assume.
Definition: MooreTypes.h:47
@ FourValued
Four-valued types such as logic or integer.
@ TwoValued
Two-valued types such as bit or int.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
ParseResult parseInputPortList(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &inputs, SmallVectorImpl< Type > &inputTypes, ArrayAttr &inputNames)
Parse a list of instance input ports.
void printOutputPortList(OpAsmPrinter &p, Operation *op, TypeRange resultTypes, ArrayAttr resultNames)
Print a list of instance output ports.
void printFVInt(AsmPrinter &p, const FVInt &value)
Print a four-valued integer usign an AsmPrinter.
Definition: FVInt.cpp:147
ParseResult parseFVInt(AsmParser &p, FVInt &result)
Parse a four-valued integer using an AsmParser.
Definition: FVInt.cpp:162
void printInputPortList(OpAsmPrinter &p, Operation *op, OperandRange inputs, TypeRange inputTypes, ArrayAttr inputNames)
Print a list of instance input ports.
ParseResult parseOutputPortList(OpAsmParser &parser, SmallVectorImpl< Type > &resultTypes, ArrayAttr &resultNames)
Parse a list of instance output ports.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition: LLVM.h:182