23 #include "mlir/IR/BuiltinTypes.h"
24 #include "mlir/IR/Diagnostics.h"
25 #include "mlir/IR/DialectImplementation.h"
26 #include "mlir/IR/PatternMatch.h"
27 #include "mlir/IR/SymbolTable.h"
28 #include "mlir/Interfaces/FunctionImplementation.h"
29 #include "llvm/ADT/BitVector.h"
30 #include "llvm/ADT/DenseMap.h"
31 #include "llvm/ADT/DenseSet.h"
32 #include "llvm/ADT/STLExtras.h"
33 #include "llvm/ADT/StringExtras.h"
34 #include "llvm/ADT/TypeSwitch.h"
35 #include "llvm/Support/FormatVariadic.h"
37 using llvm::SmallDenseSet;
38 using mlir::RegionRange;
39 using namespace circt;
40 using namespace firrtl;
41 using namespace chirrtl;
52 const llvm::BitVector &indicesToDrop) {
55 int lastIndex = indicesToDrop.find_last();
57 assert((
size_t)lastIndex < input.size() &&
"index out of range");
67 size_t lastCopied = 0;
68 SmallVector<T> result;
69 result.reserve(input.size() - indicesToDrop.count());
71 for (
unsigned indexToDrop : indicesToDrop.set_bits()) {
73 if (indexToDrop > lastCopied) {
74 result.append(input.begin() + lastCopied, input.begin() + indexToDrop);
75 lastCopied = indexToDrop;
82 if (lastCopied < input.size())
83 result.append(input.begin() + lastCopied, input.end());
89 template <
typename RetTy =
FIRRTLType,
typename... Args>
91 const Twine &message, Args &&...args) {
93 (mlir::emitError(*loc, message) << ... << std::forward<Args>(args));
99 while (Operation *op = val.getDefiningOp()) {
101 TypeSwitch<Operation *, std::optional<bool>>(op)
102 .Case<SubfieldOp, SubindexOp, SubaccessOp>([&val](
auto op) {
106 .Case<RegOp, RegResetOp, WireOp>([](
auto) {
return true; })
107 .Default([](
auto) {
return false; });
116 constexpr
unsigned int addr = 1 << 0;
117 constexpr
unsigned int en = 1 << 1;
118 constexpr
unsigned int clk = 1 << 2;
119 constexpr
unsigned int data = 1 << 3;
120 constexpr
unsigned int mask = 1 << 4;
121 constexpr
unsigned int rdata = 1 << 5;
122 constexpr
unsigned int wdata = 1 << 6;
123 constexpr
unsigned int wmask = 1 << 7;
124 constexpr
unsigned int wmode = 1 << 8;
125 constexpr
unsigned int def = 1 << 9;
127 auto portType = type_dyn_cast<BundleType>(type);
129 return MemOp::PortKind::Debug;
132 for (
auto elem : portType.getElements()) {
133 fields |= llvm::StringSwitch<unsigned>(elem.name.getValue())
139 .Case(
"rdata",
rdata)
140 .Case(
"wdata",
wdata)
141 .Case(
"wmask",
wmask)
142 .Case(
"wmode",
wmode)
146 return MemOp::PortKind::Read;
148 return MemOp::PortKind::Write;
150 return MemOp::PortKind::ReadWrite;
151 return MemOp::PortKind::Debug;
172 return "source flow";
176 return "duplex flow";
181 auto swap = [&accumulatedFlow]() ->
Flow {
185 if (
auto blockArg = dyn_cast<BlockArgument>(val)) {
186 auto *op = val.getParentBlock()->getParentOp();
187 if (
auto moduleLike = dyn_cast<FModuleLike>(op)) {
188 auto direction = moduleLike.getPortDirection(blockArg.getArgNumber());
192 return accumulatedFlow;
195 Operation *op = val.getDefiningOp();
197 return TypeSwitch<Operation *, Flow>(op)
198 .Case<SubfieldOp, OpenSubfieldOp>([&](
auto op) {
200 op.isFieldFlipped() ? swap() : accumulatedFlow);
202 .Case<SubindexOp, SubaccessOp, OpenSubindexOp, RefSubOp>(
203 [&](
auto op) {
return foldFlow(op.getInput(), accumulatedFlow); })
205 .Case<RegOp, RegResetOp, WireOp, MemoryPortOp>(
207 .Case<InstanceOp>([&](
auto inst) {
208 auto resultNo = cast<OpResult>(val).getResultNumber();
210 return accumulatedFlow;
213 .Case<MemOp>([&](
auto op) {
215 if (type_isa<RefType>(val.getType()))
219 .Case<ObjectSubfieldOp>([&](ObjectSubfieldOp op) {
220 auto input = op.getInput();
221 auto *inputOp = input.getDefiningOp();
224 if (
auto objectOp = dyn_cast_or_null<ObjectOp>(inputOp)) {
225 auto classType = input.getType();
226 auto direction = classType.getElement(op.getIndex()).direction;
238 auto classType = input.getType();
239 auto direction = classType.getElement(op.getIndex()).direction;
243 op = dyn_cast_or_null<ObjectSubfieldOp>(inputOp);
245 input = op.getInput();
246 inputOp = input.getDefiningOp();
250 return accumulatedFlow;
254 .Default([&](
auto) {
return accumulatedFlow; });
260 Operation *op = val.getDefiningOp();
264 return TypeSwitch<Operation *, DeclKind>(op)
266 .Case<SubfieldOp, SubindexOp, SubaccessOp, OpenSubfieldOp, OpenSubindexOp,
272 if (
auto module = dyn_cast<FModuleLike>(op))
273 return module.getNumPorts();
274 return op->getNumResults();
288 if (
auto *op = value.getDefiningOp())
290 auto arg = dyn_cast<BlockArgument>(value);
291 auto module = cast<FModuleOp>(arg.getOwner()->getParentOp());
292 return (module.getPortSymbolAttr(arg.getArgNumber())) ||
303 auto *block = ®ion.front();
306 auto argAttr = parentOp->getAttrOfType<ArrayAttr>(
"portNames");
308 if (!argAttr || argAttr.size() != block->getNumArguments())
311 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i) {
312 auto str = cast<StringAttr>(argAttr[i]).getValue();
314 setNameFn(block->getArgument(i), str);
320 firrtl::NameKindEnumAttr &result);
326 void CircuitOp::build(OpBuilder &
builder, OperationState &result,
327 StringAttr name, ArrayAttr annotations) {
329 result.addAttribute(
builder.getStringAttr(
"name"), name);
332 annotations =
builder.getArrayAttr({});
333 result.addAttribute(
"annotations", annotations);
336 Region *bodyRegion = result.addRegion();
338 bodyRegion->push_back(body);
342 FModuleLike CircuitOp::getMainModule(mlir::SymbolTable *symtbl) {
344 return symtbl->lookup<FModuleLike>(
getName());
345 return dyn_cast_or_null<FModuleLike>(lookupSymbol(
getName()));
349 NamedAttrList &resultAttrs) {
350 auto result = parser.parseOptionalAttrDictWithKeyword(resultAttrs);
351 if (!resultAttrs.get(
"annotations"))
352 resultAttrs.append(
"annotations", parser.getBuilder().getArrayAttr({}));
358 DictionaryAttr attr) {
360 SmallVector<StringRef> elidedAttrs = {
"name"};
362 auto annotationsAttr = op->getAttrOfType<ArrayAttr>(
"annotations");
363 if (annotationsAttr.empty())
364 elidedAttrs.push_back(
"annotations");
366 p.printOptionalAttrDictWithKeyword(op->getAttrs(), elidedAttrs);
369 LogicalResult CircuitOp::verifyRegions() {
374 emitOpError(
"must have a non-empty name");
378 mlir::SymbolTable symtbl(getOperation());
381 auto mainModule = getMainModule(&symtbl);
383 return emitOpError(
"must contain one module that matches main name '" +
388 if (isa<ClassOp>(mainModule))
389 return emitOpError(
"must have a non-class top module");
392 if (!mainModule.isPublic())
393 return emitOpError(
"main module '" +
main +
"' must be public");
398 llvm::DenseMap<Attribute, FExtModuleOp> defnameMap;
400 auto verifyExtModule = [&](FExtModuleOp extModule) -> LogicalResult {
404 auto defname = extModule.getDefnameAttr();
410 if (
auto collidingModule = symtbl.lookup<FModuleOp>(defname.getValue()))
411 return extModule.emitOpError()
412 .append(
"attribute 'defname' with value ", defname,
413 " conflicts with the name of another module in the circuit")
414 .attachNote(collidingModule.getLoc())
415 .append(
"previous module declared here");
423 FExtModuleOp collidingExtModule;
424 if (
auto &value = defnameMap[defname]) {
425 collidingExtModule = value;
426 if (!value.getParameters().empty() && extModule.getParameters().empty())
436 SmallVector<PortInfo> ports = extModule.getPorts();
437 SmallVector<PortInfo> collidingPorts = collidingExtModule.getPorts();
439 if (ports.size() != collidingPorts.size())
440 return extModule.emitOpError()
441 .append(
"with 'defname' attribute ", defname,
" has ", ports.size(),
442 " ports which is different from a previously defined "
443 "extmodule with the same 'defname' which has ",
444 collidingPorts.size(),
" ports")
445 .attachNote(collidingExtModule.getLoc())
446 .append(
"previous extmodule definition occurred here");
452 for (
auto p : llvm::zip(ports, collidingPorts)) {
453 StringAttr aName = std::get<0>(p).name, bName = std::get<1>(p).name;
454 Type aType = std::get<0>(p).type, bType = std::get<1>(p).type;
457 return extModule.emitOpError()
458 .append(
"with 'defname' attribute ", defname,
459 " has a port with name ", aName,
460 " which does not match the name of the port in the same "
461 "position of a previously defined extmodule with the same "
462 "'defname', expected port to have name ",
464 .attachNote(collidingExtModule.getLoc())
465 .append(
"previous extmodule definition occurred here");
467 if (!extModule.getParameters().empty() ||
468 !collidingExtModule.getParameters().empty()) {
470 if (
auto base = type_dyn_cast<FIRRTLBaseType>(aType))
471 aType = base.getWidthlessType();
472 if (
auto base = type_dyn_cast<FIRRTLBaseType>(bType))
473 bType = base.getWidthlessType();
476 return extModule.emitOpError()
477 .append(
"with 'defname' attribute ", defname,
478 " has a port with name ", aName,
479 " which has a different type ", aType,
480 " which does not match the type of the port in the same "
481 "position of a previously defined extmodule with the same "
482 "'defname', expected port to have type ",
484 .attachNote(collidingExtModule.getLoc())
485 .append(
"previous extmodule definition occurred here");
490 for (
auto &op : *getBodyBlock()) {
492 if (
auto extModule = dyn_cast<FExtModuleOp>(op)) {
493 if (verifyExtModule(extModule).failed())
501 Block *CircuitOp::getBodyBlock() {
return &getBody().front(); }
508 SmallVector<PortInfo> results;
509 for (
unsigned i = 0, e = module.getNumPorts(); i < e; ++i) {
510 results.push_back({module.getPortNameAttr(i), module.getPortType(i),
511 module.getPortDirection(i), module.getPortSymbolAttr(i),
512 module.getPortLocation(i),
531 assert(0 &&
"invalid direction");
536 SmallVector<hw::PortInfo> results;
537 for (
unsigned i = 0, e =
getNumPorts(module); i < e; ++i) {
538 results.push_back({{module.getPortNameAttr(i), module.getPortType(i),
539 dirFtoH(module.getPortDirection(i))},
541 module.getPortSymbolAttr(i),
543 module.getPortLocation(i)});
545 return hw::ModulePortInfo(results);
563 BlockArgument FModuleOp::getArgument(
size_t portNumber) {
564 return getBodyBlock()->getArgument(portNumber);
571 ArrayRef<std::pair<unsigned, PortInfo>> ports,
572 bool supportsInternalPaths =
false) {
575 unsigned oldNumArgs = op.getNumPorts();
576 unsigned newNumArgs = oldNumArgs + ports.size();
579 SmallVector<Direction> existingDirections =
581 ArrayRef<Attribute> existingNames = op.getPortNames();
582 ArrayRef<Attribute> existingTypes = op.getPortTypes();
583 ArrayRef<Attribute> existingLocs = op.getPortLocations();
584 assert(existingDirections.size() == oldNumArgs);
585 assert(existingNames.size() == oldNumArgs);
586 assert(existingTypes.size() == oldNumArgs);
587 assert(existingLocs.size() == oldNumArgs);
588 SmallVector<Attribute> internalPaths;
590 if (supportsInternalPaths) {
591 if (
auto internalPathsAttr = op->getAttrOfType<ArrayAttr>(
"internalPaths"))
592 llvm::append_range(internalPaths, internalPathsAttr);
594 internalPaths.resize(oldNumArgs, emptyInternalPath);
595 assert(internalPaths.size() == oldNumArgs);
598 SmallVector<Direction> newDirections;
599 SmallVector<Attribute> newNames, newTypes, newAnnos, newSyms, newLocs,
601 newDirections.reserve(newNumArgs);
602 newNames.reserve(newNumArgs);
603 newTypes.reserve(newNumArgs);
604 newAnnos.reserve(newNumArgs);
605 newSyms.reserve(newNumArgs);
606 newLocs.reserve(newNumArgs);
607 newInternalPaths.reserve(newNumArgs);
612 auto migrateOldPorts = [&](
unsigned untilOldIdx) {
613 while (oldIdx < oldNumArgs && oldIdx < untilOldIdx) {
614 newDirections.push_back(existingDirections[oldIdx]);
615 newNames.push_back(existingNames[oldIdx]);
616 newTypes.push_back(existingTypes[oldIdx]);
617 newAnnos.push_back(op.getAnnotationsAttrForPort(oldIdx));
618 newSyms.push_back(op.getPortSymbolAttr(oldIdx));
619 newLocs.push_back(existingLocs[oldIdx]);
620 if (supportsInternalPaths)
621 newInternalPaths.push_back(internalPaths[oldIdx]);
625 for (
auto pair : llvm::enumerate(ports)) {
626 auto idx = pair.value().first;
627 auto &port = pair.value().second;
628 migrateOldPorts(idx);
629 newDirections.push_back(port.direction);
630 newNames.push_back(port.name);
632 auto annos = port.annotations.getArrayAttr();
633 newAnnos.push_back(annos ? annos : emptyArray);
634 newSyms.push_back(port.sym);
635 newLocs.push_back(port.loc);
636 if (supportsInternalPaths)
637 newInternalPaths.push_back(emptyInternalPath);
639 migrateOldPorts(oldNumArgs);
643 if (llvm::all_of(newAnnos, [](Attribute attr) {
644 return cast<ArrayAttr>(attr).empty();
649 op->setAttr(
"portDirections",
651 op->setAttr(
"portNames",
ArrayAttr::get(op.getContext(), newNames));
652 op->setAttr(
"portTypes",
ArrayAttr::get(op.getContext(), newTypes));
653 op->setAttr(
"portAnnotations",
ArrayAttr::get(op.getContext(), newAnnos));
654 op.setPortSymbols(newSyms);
655 op->setAttr(
"portLocations",
ArrayAttr::get(op.getContext(), newLocs));
656 if (supportsInternalPaths) {
658 auto empty = llvm::all_of(newInternalPaths, [](Attribute attr) {
659 return !cast<InternalPathAttr>(attr).getPath();
662 op->removeAttr(
"internalPaths");
664 op->setAttr(
"internalPaths",
670 static void erasePorts(FModuleLike op,
const llvm::BitVector &portIndices) {
671 if (portIndices.none())
675 SmallVector<Direction> portDirections =
677 ArrayRef<Attribute> portNames = op.getPortNames();
678 ArrayRef<Attribute> portTypes = op.getPortTypes();
679 ArrayRef<Attribute> portAnnos = op.getPortAnnotations();
680 ArrayRef<Attribute> portSyms = op.getPortSymbols();
681 ArrayRef<Attribute> portLocs = op.getPortLocations();
682 auto numPorts = op.getNumPorts();
684 assert(portDirections.size() == numPorts);
685 assert(portNames.size() == numPorts);
686 assert(portAnnos.size() == numPorts || portAnnos.empty());
687 assert(portTypes.size() == numPorts);
688 assert(portSyms.size() == numPorts || portSyms.empty());
689 assert(portLocs.size() == numPorts);
691 SmallVector<Direction> newPortDirections =
692 removeElementsAtIndices<Direction>(portDirections, portIndices);
693 SmallVector<Attribute> newPortNames, newPortTypes, newPortAnnos, newPortSyms,
700 op->setAttr(
"portDirections",
702 op->setAttr(
"portNames",
ArrayAttr::get(op.getContext(), newPortNames));
703 op->setAttr(
"portAnnotations",
ArrayAttr::get(op.getContext(), newPortAnnos));
704 op->setAttr(
"portTypes",
ArrayAttr::get(op.getContext(), newPortTypes));
705 FModuleLike::fixupPortSymsArray(newPortSyms, op.getContext());
706 op->setAttr(
"portSyms",
ArrayAttr::get(op.getContext(), newPortSyms));
707 op->setAttr(
"portLocations",
ArrayAttr::get(op.getContext(), newPortLocs));
710 template <
typename T>
713 auto internalPaths = op.getInternalPaths();
721 auto empty = llvm::all_of(newPaths, [](Attribute attr) {
722 return !cast<InternalPathAttr>(attr).getPath();
725 op.removeInternalPathsAttr();
727 op.setInternalPathsAttr(
ArrayAttr::get(op.getContext(), newPaths));
731 ::erasePorts(cast<FModuleLike>((Operation *)*
this), portIndices);
736 ::erasePorts(cast<FModuleLike>((Operation *)*
this), portIndices);
741 ::erasePorts(cast<FModuleLike>((Operation *)*
this), portIndices);
745 ::erasePorts(cast<FModuleLike>((Operation *)*
this), portIndices);
746 getBodyBlock()->eraseArguments(portIndices);
756 auto *body = getBodyBlock();
757 for (
size_t i = 0, e = ports.size(); i < e; ++i) {
760 auto &[index, port] = ports[i];
761 body->insertArgument(index + i, port.type, port.loc);
783 StringAttr name, ArrayRef<PortInfo> ports,
784 ArrayAttr annotations,
bool withAnnotations =
true) {
786 result.addAttribute(::mlir::SymbolTable::getSymbolAttrName(), name);
789 SmallVector<Direction, 4> portDirections;
790 SmallVector<Attribute, 4> portNames;
791 SmallVector<Attribute, 4> portTypes;
792 SmallVector<Attribute, 4> portAnnotations;
793 SmallVector<Attribute, 4> portSyms;
794 SmallVector<Attribute, 4> portLocs;
795 for (
const auto &port : ports) {
796 portDirections.push_back(port.direction);
797 portNames.push_back(port.name);
799 portAnnotations.push_back(port.annotations.getArrayAttr());
800 portSyms.push_back(port.sym);
801 portLocs.push_back(port.loc);
806 if (llvm::all_of(portAnnotations, [](Attribute attr) {
807 return cast<ArrayAttr>(attr).empty();
809 portAnnotations.clear();
811 FModuleLike::fixupPortSymsArray(portSyms,
builder.getContext());
816 result.addAttribute(
"portNames",
builder.getArrayAttr(portNames));
817 result.addAttribute(
"portTypes",
builder.getArrayAttr(portTypes));
818 result.addAttribute(
"portSyms",
builder.getArrayAttr(portSyms));
819 result.addAttribute(
"portLocations",
builder.getArrayAttr(portLocs));
821 if (withAnnotations) {
823 annotations =
builder.getArrayAttr({});
824 result.addAttribute(
"annotations", annotations);
825 result.addAttribute(
"portAnnotations",
826 builder.getArrayAttr(portAnnotations));
833 StringAttr name, ArrayRef<PortInfo> ports) {
838 void FModuleOp::build(OpBuilder &
builder, OperationState &result,
839 StringAttr name, ConventionAttr convention,
840 ArrayRef<PortInfo> ports, ArrayAttr annotations) {
842 result.addAttribute(
"convention", convention);
845 auto *bodyRegion = result.regions[0].get();
847 bodyRegion->push_back(body);
850 for (
auto &elt : ports)
851 body->addArgument(elt.type, elt.loc);
854 void FExtModuleOp::build(OpBuilder &
builder, OperationState &result,
855 StringAttr name, ConventionAttr convention,
856 ArrayRef<PortInfo> ports, StringRef defnameAttr,
857 ArrayAttr annotations, ArrayAttr parameters,
858 ArrayAttr internalPaths) {
860 result.addAttribute(
"convention", convention);
861 if (!defnameAttr.empty())
862 result.addAttribute(
"defname",
builder.getStringAttr(defnameAttr));
864 parameters =
builder.getArrayAttr({});
865 result.addAttribute(getParametersAttrName(result.name), parameters);
866 if (internalPaths && !internalPaths.empty())
867 result.addAttribute(getInternalPathsAttrName(result.name), internalPaths);
870 void FIntModuleOp::build(OpBuilder &
builder, OperationState &result,
871 StringAttr name, ArrayRef<PortInfo> ports,
872 StringRef intrinsicNameAttr, ArrayAttr annotations,
873 ArrayAttr parameters, ArrayAttr internalPaths) {
875 result.addAttribute(
"intrinsic",
builder.getStringAttr(intrinsicNameAttr));
877 parameters =
builder.getArrayAttr({});
878 result.addAttribute(getParametersAttrName(result.name), parameters);
879 if (internalPaths && !internalPaths.empty())
880 result.addAttribute(getInternalPathsAttrName(result.name), internalPaths);
883 void FMemModuleOp::build(OpBuilder &
builder, OperationState &result,
884 StringAttr name, ArrayRef<PortInfo> ports,
885 uint32_t numReadPorts, uint32_t numWritePorts,
886 uint32_t numReadWritePorts, uint32_t dataWidth,
887 uint32_t maskBits, uint32_t readLatency,
888 uint32_t writeLatency, uint64_t depth,
889 ArrayAttr annotations) {
890 auto *context =
builder.getContext();
894 result.addAttribute(
"numReadPorts",
IntegerAttr::get(ui32Type, numReadPorts));
895 result.addAttribute(
"numWritePorts",
897 result.addAttribute(
"numReadWritePorts",
901 result.addAttribute(
"readLatency",
IntegerAttr::get(ui32Type, readLatency));
902 result.addAttribute(
"writeLatency",
IntegerAttr::get(ui32Type, writeLatency));
920 ArrayRef<Direction> portDirections,
921 ArrayRef<Attribute> portNames,
922 ArrayRef<Attribute> portTypes,
923 ArrayRef<Attribute> portAnnotations,
924 ArrayRef<Attribute> portSyms,
925 ArrayRef<Attribute> portLocs) {
928 bool printedNamesDontMatch =
false;
930 mlir::OpPrintingFlags flags;
934 SmallString<32> resultNameStr;
936 for (
unsigned i = 0, e = portTypes.size(); i < e; ++i) {
941 p << portDirections[i] <<
" ";
947 resultNameStr.clear();
948 llvm::raw_svector_ostream tmpStream(resultNameStr);
949 p.printOperand(block->getArgument(i), tmpStream);
952 auto portName = cast<StringAttr>(portNames[i]).getValue();
953 if (!portName.empty() && tmpStream.str().drop_front() != portName)
954 printedNamesDontMatch =
true;
955 p << tmpStream.str();
957 p.printKeywordOrString(cast<StringAttr>(portNames[i]).getValue());
962 auto portType = cast<TypeAttr>(portTypes[i]).getValue();
963 p.printType(portType);
966 if (!portSyms.empty()) {
967 if (!portSyms[i].cast<hw::InnerSymAttr>().
empty()) {
969 portSyms[i].cast<hw::InnerSymAttr>().print(p);
975 if (!portAnnotations.empty() &&
976 !cast<ArrayAttr>(portAnnotations[i]).empty()) {
978 p.printAttribute(portAnnotations[i]);
985 if (flags.shouldPrintDebugInfo() && !portLocs.empty())
986 p.printOptionalLocationSpecifier(cast<LocationAttr>(portLocs[i]));
990 return printedNamesDontMatch;
997 bool supportsSymbols,
998 SmallVectorImpl<OpAsmParser::Argument> &entryArgs,
999 SmallVectorImpl<Direction> &portDirections,
1000 SmallVectorImpl<Attribute> &portNames,
1001 SmallVectorImpl<Attribute> &portTypes,
1002 SmallVectorImpl<Attribute> &portAnnotations,
1003 SmallVectorImpl<Attribute> &portSyms,
1004 SmallVectorImpl<Attribute> &portLocs) {
1005 auto *context = parser.getContext();
1007 auto parseArgument = [&]() -> ParseResult {
1009 if (succeeded(parser.parseOptionalKeyword(
"out")))
1011 else if (succeeded(parser.parseKeyword(
"in",
"or 'out'")))
1020 if (hasSSAIdentifiers) {
1021 OpAsmParser::Argument arg;
1022 if (parser.parseArgument(arg))
1024 entryArgs.push_back(arg);
1028 assert(arg.ssaName.name.size() > 1 && arg.ssaName.name[0] ==
'%' &&
1029 "Unknown MLIR name");
1030 if (
isdigit(arg.ssaName.name[1]))
1033 portNames.push_back(
1037 irLoc = arg.ssaName.location;
1041 irLoc = parser.getCurrentLocation();
1042 std::string portName;
1043 if (parser.parseKeywordOrString(&portName))
1050 if (parser.parseColonType(portType))
1054 if (hasSSAIdentifiers)
1055 entryArgs.back().type = portType;
1058 if (supportsSymbols) {
1059 hw::InnerSymAttr innerSymAttr;
1060 if (succeeded(parser.parseOptionalKeyword(
"sym"))) {
1061 NamedAttrList dummyAttrs;
1062 if (parser.parseCustomAttributeWithFallback(
1063 innerSymAttr, ::mlir::Type{},
1065 return ::mlir::failure();
1068 portSyms.push_back(innerSymAttr);
1073 auto parseResult = parser.parseOptionalAttribute(annos);
1074 if (!parseResult.has_value())
1075 annos = parser.getBuilder().getArrayAttr({});
1076 else if (failed(*parseResult))
1078 portAnnotations.push_back(annos);
1081 std::optional<Location> maybeLoc;
1082 if (failed(parser.parseOptionalLocationSpecifier(maybeLoc)))
1084 Location loc = maybeLoc ? *maybeLoc : parser.getEncodedSourceLoc(irLoc);
1085 portLocs.push_back(loc);
1086 if (hasSSAIdentifiers)
1087 entryArgs.back().sourceLoc = loc;
1093 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
1099 if (!parameters || parameters.empty())
1103 llvm::interleaveComma(parameters, p, [&](Attribute param) {
1104 auto paramAttr = cast<ParamDeclAttr>(param);
1105 p << paramAttr.getName().getValue() <<
": " << paramAttr.getType();
1106 if (
auto value = paramAttr.getValue()) {
1108 p.printAttributeWithoutType(value);
1114 static void printFModuleLikeOp(OpAsmPrinter &p, FModuleLike op) {
1117 // Print the visibility of the module.
1118 StringRef visibilityAttrName = SymbolTable::getVisibilityAttrName();
1119 if (auto visibility = op->getAttrOfType<StringAttr>(visibilityAttrName))
1120 p << visibility.getValue() << ' ';
1122 // Print the operation and the function name.
1123 p.printSymbolName(op.getModuleName());
1125 // Print the parameter list (if non-empty).
1126 printParameterList(op->getAttrOfType<ArrayAttr>("parameters"), p);
1128 // Both modules and external modules have a body, but it is always empty for
1129 // external modules.
1130 Block *body = nullptr;
1131 if (!op->getRegion(0).empty())
1132 body = &op->getRegion(0).front();
1134 auto portDirections = direction::unpackAttribute(op.getPortDirectionsAttr());
1136 auto needPortNamesAttr = printModulePorts(
1137 p, body, portDirections, op.getPortNames(), op.getPortTypes(),
1138 op.getPortAnnotations(), op.getPortSymbols(), op.getPortLocations());
1140 SmallVector<StringRef, 12> omittedAttrs = {
1141 "sym_name", "portDirections", "portTypes", "portAnnotations",
1142 "portSyms", "portLocations", "parameters", visibilityAttrName};
1144 if (op.getConvention() == Convention::Internal)
1145 omittedAttrs.push_back("convention");
1147 // We can omit the portNames if they were able to be printed as properly as
1149 if (!needPortNamesAttr)
1150 omittedAttrs.push_back("portNames");
1152 // If there are no annotations we can omit the empty array.
1153 if (op->getAttrOfType<ArrayAttr>("annotations").empty())
1154 omittedAttrs.push_back("annotations");
1156 p.printOptionalAttrDictWithKeyword(op->getAttrs(), omittedAttrs);
1159 void FExtModuleOp::print(OpAsmPrinter &p) { printFModuleLikeOp(p, *this); }
1161 void FIntModuleOp::print(OpAsmPrinter &p) { printFModuleLikeOp(p, *this); }
1163 void FMemModuleOp::print(OpAsmPrinter &p) { printFModuleLikeOp(p, *this); }
1165 void FModuleOp::print(OpAsmPrinter &p) {
1166 printFModuleLikeOp(p, *this);
1168 // Print the body if this is not an external function. Since this block does
1169 // not have terminators, printing the terminator actually just prints the last
1171 Region &fbody = getBody();
1172 if (!fbody.empty()) {
1174 p.printRegion(fbody, /*printEntryBlockArgs=*/false,
1175 /*printBlockTerminators=*/true);
1185 parseOptionalParameters(OpAsmParser &parser,
1186 SmallVectorImpl<Attribute> ¶meters) {
1188 return parser.parseCommaSeparatedList(
1189 OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1194 if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1197 // Parse the default value if present.
1198 if (succeeded(parser.parseOptionalEqual())) {
1199 if (parser.parseAttribute(value, type))
1203 auto &builder = parser.getBuilder();
1204 parameters.push_back(ParamDeclAttr::get(
1205 builder.getContext(), builder.getStringAttr(name), type, value));
1210 static ParseResult parseFModuleLikeOp(OpAsmParser &parser,
1211 OperationState &result,
1212 bool hasSSAIdentifiers) {
1213 auto *context = result.getContext();
1214 auto &builder = parser.getBuilder();
1216 // Parse the visibility attribute.
1217 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
1219 // Parse the name as a symbol.
1220 StringAttr nameAttr;
1221 if (parser.parseSymbolName(nameAttr, ::mlir::SymbolTable::getSymbolAttrName(),
1225 // Parse optional parameters.
1226 SmallVector<Attribute, 4> parameters;
1227 if (parseOptionalParameters(parser, parameters))
1229 result.addAttribute("parameters", builder.getArrayAttr(parameters));
1231 // Parse the module ports.
1232 SmallVector<OpAsmParser::Argument> entryArgs;
1233 SmallVector<Direction, 4> portDirections;
1234 SmallVector<Attribute, 4> portNames;
1235 SmallVector<Attribute, 4> portTypes;
1236 SmallVector<Attribute, 4> portAnnotations;
1237 SmallVector<Attribute, 4> portSyms;
1238 SmallVector<Attribute, 4> portLocs;
1239 if (parseModulePorts(parser, hasSSAIdentifiers, /*supportsSymbols=*/true,
1240 entryArgs, portDirections, portNames, portTypes,
1241 portAnnotations, portSyms, portLocs))
1244 // If module attributes are present, parse them.
1245 if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
1248 assert(portNames.size() == portTypes.size());
1250 // Record the argument and result types as an attribute. This is necessary
1251 // for external modules.
1253 // Add port directions.
1254 if (!result.attributes.get("portDirections"))
1255 result.addAttribute("portDirections",
1256 direction::packAttribute(context, portDirections));
1259 if (!result.attributes.get("portNames"))
1260 result.addAttribute("portNames", builder.getArrayAttr(portNames));
1262 // Add the port types.
1263 if (!result.attributes.get("portTypes"))
1264 result.addAttribute("portTypes", ArrayAttr::get(context, portTypes));
1266 // Add the port annotations.
1267 if (!result.attributes.get("portAnnotations")) {
1268 // If there are no portAnnotations, don't add the attribute.
1269 if (llvm::any_of(portAnnotations, [&](Attribute anno) {
1270 return !cast<ArrayAttr>(anno).empty();
1272 result.addAttribute(
"portAnnotations",
1277 if (!result.attributes.get(
"portSyms")) {
1278 FModuleLike::fixupPortSymsArray(portSyms,
builder.getContext());
1279 result.addAttribute(
"portSyms",
builder.getArrayAttr(portSyms));
1283 if (!result.attributes.get(
"portLocations"))
1284 result.addAttribute(
"portLocations",
ArrayAttr::get(context, portLocs));
1287 if (!result.attributes.get(
"annotations"))
1288 result.addAttribute(
"annotations",
builder.getArrayAttr({}));
1292 if (!result.attributes.get(
"portAnnotations"))
1293 result.addAttribute(
"portAnnotations",
builder.getArrayAttr({}));
1296 auto *body = result.addRegion();
1298 if (hasSSAIdentifiers) {
1299 if (parser.parseRegion(*body, entryArgs))
1302 body->push_back(
new Block());
1307 ParseResult FModuleOp::parse(OpAsmParser &parser, OperationState &result) {
1310 if (!result.attributes.get(
"convention"))
1311 result.addAttribute(
1317 ParseResult FExtModuleOp::parse(OpAsmParser &parser, OperationState &result) {
1320 if (!result.attributes.get(
"convention"))
1321 result.addAttribute(
1327 ParseResult FIntModuleOp::parse(OpAsmParser &parser, OperationState &result) {
1331 ParseResult FMemModuleOp::parse(OpAsmParser &parser, OperationState &result) {
1335 LogicalResult FModuleOp::verify() {
1337 auto *body = getBodyBlock();
1338 auto portTypes = getPortTypes();
1339 auto portLocs = getPortLocations();
1340 auto numPorts = portTypes.size();
1343 if (body->getNumArguments() != numPorts)
1344 return emitOpError(
"entry block must have ")
1345 << numPorts <<
" arguments to match module signature";
1348 for (
auto [arg, type, loc] : zip(body->getArguments(), portTypes, portLocs)) {
1349 if (arg.getType() != cast<TypeAttr>(type).getValue())
1350 return emitOpError(
"block argument types should match signature types");
1351 if (arg.getLoc() != cast<LocationAttr>(loc))
1353 "block argument locations should match signature locations");
1359 static LogicalResult
1361 std::optional<::mlir::ArrayAttr> internalPaths) {
1366 if (internalPaths->size() != op.getNumPorts())
1367 return op.emitError(
"module has inconsistent internal path array with ")
1368 << internalPaths->size() <<
" entries for " << op.getNumPorts()
1372 for (
auto [idx, path, typeattr] : llvm::enumerate(
1373 internalPaths->getAsRange<InternalPathAttr>(), op.getPortTypes())) {
1374 if (path.getPath() &&
1375 !type_isa<RefType>(cast<TypeAttr>(typeattr).getValue())) {
1377 op.emitError(
"module has internal path for non-ref-type port ")
1378 << op.getPortNameAttr(idx);
1379 return diag.attachNote(op.getPortLocation(idx)) <<
"this port";
1386 LogicalResult FExtModuleOp::verify() {
1390 auto params = getParameters();
1394 auto checkParmValue = [&](Attribute elt) ->
bool {
1395 auto param = cast<ParamDeclAttr>(elt);
1396 auto value = param.getValue();
1397 if (isa<IntegerAttr, StringAttr, FloatAttr, hw::ParamVerbatimAttr>(value))
1399 emitError() <<
"has unknown extmodule parameter value '"
1400 << param.getName().getValue() <<
"' = " << value;
1404 if (!llvm::all_of(params, checkParmValue))
1410 LogicalResult FIntModuleOp::verify() {
1414 auto params = getParameters();
1418 auto checkParmValue = [&](Attribute elt) ->
bool {
1419 auto param = cast<ParamDeclAttr>(elt);
1420 auto value = param.getValue();
1421 if (isa<IntegerAttr, StringAttr, FloatAttr>(value))
1423 emitError() <<
"has unknown intmodule parameter value '"
1424 << param.getName().getValue() <<
"' = " << value;
1428 if (!llvm::all_of(params, checkParmValue))
1435 SymbolTableCollection &symbolTable) {
1436 auto circuitOp = module->getParentOfType<CircuitOp>();
1439 for (
size_t i = 0, e = module.getNumPorts(); i < e; ++i) {
1440 auto type = module.getPortType(i);
1441 auto classType = dyn_cast<ClassType>(type);
1446 auto className = classType.getNameAttr();
1447 auto classOp = dyn_cast_or_null<ClassLike>(
1448 symbolTable.lookupSymbolIn(circuitOp, className));
1450 return module.emitOpError() <<
"references unknown class " << className;
1453 if (failed(classOp.verifyType(classType,
1454 [&]() { return module.emitOpError(); })))
1461 LogicalResult FModuleOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1466 FExtModuleOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1471 FIntModuleOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1476 FMemModuleOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1480 void FModuleOp::getAsmBlockArgumentNames(mlir::Region ®ion,
1485 void FExtModuleOp::getAsmBlockArgumentNames(
1490 void FIntModuleOp::getAsmBlockArgumentNames(
1495 void FMemModuleOp::getAsmBlockArgumentNames(
1500 ArrayAttr FMemModuleOp::getParameters() {
return {}; }
1502 ArrayAttr FModuleOp::getParameters() {
return {}; }
1504 Convention FIntModuleOp::getConvention() {
return Convention::Internal; }
1506 ConventionAttr FIntModuleOp::getConventionAttr() {
1510 Convention FMemModuleOp::getConvention() {
return Convention::Internal; }
1512 ConventionAttr FMemModuleOp::getConventionAttr() {
1521 ClassLike classOp, ClassType type,
1522 function_ref<InFlightDiagnostic()> emitError) {
1524 auto name = type.getNameAttr().getAttr();
1525 auto expectedName = classOp.getModuleNameAttr();
1526 if (name != expectedName)
1527 return emitError() <<
"type has wrong name, got " << name <<
", expected "
1530 auto elements = type.getElements();
1532 auto expectedNumElements = classOp.getNumPorts();
1534 return emitError() <<
"has wrong number of ports, got " <<
numElements
1535 <<
", expected " << expectedNumElements;
1537 auto portNames = classOp.getPortNames();
1538 auto portDirections = classOp.getPortDirections();
1539 auto portTypes = classOp.getPortTypes();
1542 auto element = elements[i];
1544 auto name = element.name;
1545 auto expectedName = portNames[i];
1546 if (name != expectedName)
1547 return emitError() <<
"port #" << i <<
" has wrong name, got " << name
1548 <<
", expected " << expectedName;
1550 auto direction = element.direction;
1551 auto expectedDirection =
Direction(portDirections[i]);
1552 if (direction != expectedDirection)
1553 return emitError() <<
"port " << name <<
" has wrong direction, got "
1557 auto type = element.type;
1558 auto expectedType = cast<TypeAttr>(portTypes[i]).getValue();
1559 if (type != expectedType)
1560 return emitError() <<
"port " << name <<
" has wrong type, got " << type
1561 <<
", expected " << expectedType;
1568 auto n = classOp.getNumPorts();
1569 SmallVector<ClassElement> elements;
1570 elements.reserve(n);
1571 for (
size_t i = 0; i < n; ++i)
1572 elements.push_back({classOp.getPortNameAttr(i), classOp.getPortType(i),
1573 classOp.getPortDirection(i)});
1579 bool hasSSAIdentifiers) {
1580 auto *context = result.getContext();
1581 auto &
builder = parser.getBuilder();
1584 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
1587 StringAttr nameAttr;
1588 if (parser.parseSymbolName(nameAttr, ::mlir::SymbolTable::getSymbolAttrName(),
1593 SmallVector<OpAsmParser::Argument> entryArgs;
1594 SmallVector<Direction, 4> portDirections;
1595 SmallVector<Attribute, 4> portNames;
1596 SmallVector<Attribute, 4> portTypes;
1597 SmallVector<Attribute, 4> portAnnotations;
1598 SmallVector<Attribute, 4> portSyms;
1599 SmallVector<Attribute, 4> portLocs;
1601 false, entryArgs, portDirections,
1602 portNames, portTypes, portAnnotations, portSyms,
1607 for (
auto annos : portAnnotations)
1608 if (!cast<ArrayAttr>(annos).empty())
1612 if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
1615 assert(portNames.size() == portTypes.size());
1621 if (!result.attributes.get(
"portDirections"))
1622 result.addAttribute(
"portDirections",
1626 if (!result.attributes.get(
"portNames"))
1627 result.addAttribute(
"portNames",
builder.getArrayAttr(portNames));
1630 if (!result.attributes.get(
"portTypes"))
1631 result.addAttribute(
"portTypes",
builder.getArrayAttr(portTypes));
1634 if (!result.attributes.get(
"portSyms")) {
1635 FModuleLike::fixupPortSymsArray(portSyms,
builder.getContext());
1636 result.addAttribute(
"portSyms",
builder.getArrayAttr(portSyms));
1640 if (!result.attributes.get(
"portLocations"))
1641 result.addAttribute(
"portLocations",
ArrayAttr::get(context, portLocs));
1647 auto *bodyRegion = result.addRegion();
1649 if (hasSSAIdentifiers) {
1650 if (parser.parseRegion(*bodyRegion, entryArgs))
1652 if (bodyRegion->empty())
1653 bodyRegion->push_back(
new Block());
1663 StringRef visibilityAttrName = SymbolTable::getVisibilityAttrName();
1664 if (
auto visibility = op->getAttrOfType<StringAttr>(visibilityAttrName))
1665 p << visibility.getValue() <<
' ';
1668 p.printSymbolName(op.getName());
1672 Region ®ion = op->getRegion(0);
1673 Block *body =
nullptr;
1674 if (!region.empty())
1675 body = ®ion.front();
1680 p, body, portDirections, op.getPortNames(), op.getPortTypes(), {},
1681 op.getPortSymbols(), op.getPortLocations());
1684 SmallVector<StringRef, 8> omittedAttrs = {
1685 "sym_name",
"portNames",
"portTypes",
"portDirections",
1686 "portSyms",
"portLocations", visibilityAttrName};
1690 if (!needPortNamesAttr)
1691 omittedAttrs.push_back(
"portNames");
1693 p.printOptionalAttrDictWithKeyword(op->getAttrs(), omittedAttrs);
1696 if (!region.empty()) {
1698 auto printEntryBlockArgs =
false;
1699 auto printBlockTerminators =
false;
1700 p.printRegion(region, printEntryBlockArgs, printBlockTerminators);
1708 void ClassOp::build(OpBuilder &
builder, OperationState &result, StringAttr name,
1709 ArrayRef<PortInfo> ports) {
1712 [](
const auto &port) {
return port.annotations.empty(); }) &&
1713 "class ports may not have annotations");
1718 auto *bodyRegion = result.regions[0].get();
1720 bodyRegion->push_back(body);
1723 for (
auto &elt : ports)
1724 body->addArgument(elt.type, elt.loc);
1727 void ClassOp::print(OpAsmPrinter &p) {
1731 ParseResult ClassOp::parse(OpAsmParser &parser, OperationState &result) {
1732 auto hasSSAIdentifiers =
true;
1736 LogicalResult ClassOp::verify() {
1737 for (
auto operand : getBodyBlock()->getArguments()) {
1738 auto type = operand.getType();
1739 if (!isa<PropertyType>(type)) {
1740 emitOpError(
"ports on a class must be properties");
1749 ClassOp::verifySymbolUses(::mlir::SymbolTableCollection &symbolTable) {
1753 void ClassOp::getAsmBlockArgumentNames(mlir::Region ®ion,
1758 SmallVector<PortInfo> ClassOp::getPorts() {
1763 ::erasePorts(cast<FModuleLike>((Operation *)*
this), portIndices);
1764 getBodyBlock()->eraseArguments(portIndices);
1768 ::insertPorts(cast<FModuleLike>((Operation *)*
this), ports);
1771 Convention ClassOp::getConvention() {
return Convention::Internal; }
1773 ConventionAttr ClassOp::getConventionAttr() {
1777 ArrayAttr ClassOp::getParameters() {
return {}; }
1779 ArrayAttr ClassOp::getPortAnnotationsAttr() {
1785 BlockArgument ClassOp::getArgument(
size_t portNumber) {
1786 return getBodyBlock()->getArgument(portNumber);
1789 bool ClassOp::canDiscardOnUseEmpty() {
1800 void ExtClassOp::build(OpBuilder &
builder, OperationState &result,
1801 StringAttr name, ArrayRef<PortInfo> ports) {
1804 [](
const auto &port) {
return port.annotations.empty(); }) &&
1805 "class ports may not have annotations");
1810 void ExtClassOp::print(OpAsmPrinter &p) {
1814 ParseResult ExtClassOp::parse(OpAsmParser &parser, OperationState &result) {
1815 auto hasSSAIdentifiers =
false;
1820 ExtClassOp::verifySymbolUses(::mlir::SymbolTableCollection &symbolTable) {
1824 void ExtClassOp::getAsmBlockArgumentNames(mlir::Region ®ion,
1829 SmallVector<PortInfo> ExtClassOp::getPorts() {
1834 ::erasePorts(cast<FModuleLike>((Operation *)*
this), portIndices);
1838 ::insertPorts(cast<FModuleLike>((Operation *)*
this), ports);
1841 Convention ExtClassOp::getConvention() {
return Convention::Internal; }
1843 ConventionAttr ExtClassOp::getConventionAttr() {
1847 ArrayAttr ExtClassOp::getParameters() {
return {}; }
1849 ArrayAttr ExtClassOp::getPortAnnotationsAttr() {
1857 bool ExtClassOp::canDiscardOnUseEmpty() {
1870 Operation *InstanceOp::getReferencedModuleSlow() {
1871 auto circuit = (*this)->getParentOfType<CircuitOp>();
1875 return circuit.lookupSymbol<FModuleLike>(getModuleNameAttr());
1879 return cast<hw::PortList>(getReferencedModuleSlow()).getPortList();
1882 void InstanceOp::build(OpBuilder &
builder, OperationState &result,
1883 TypeRange resultTypes, StringRef moduleName,
1884 StringRef name, NameKindEnum nameKind,
1885 ArrayRef<Direction> portDirections,
1886 ArrayRef<Attribute> portNames,
1887 ArrayRef<Attribute> annotations,
1888 ArrayRef<Attribute> portAnnotations,
bool lowerToBind,
1889 StringAttr innerSym) {
1890 build(
builder, result, resultTypes, moduleName, name, nameKind,
1891 portDirections, portNames, annotations, portAnnotations, lowerToBind,
1895 void InstanceOp::build(OpBuilder &
builder, OperationState &result,
1896 TypeRange resultTypes, StringRef moduleName,
1897 StringRef name, NameKindEnum nameKind,
1898 ArrayRef<Direction> portDirections,
1899 ArrayRef<Attribute> portNames,
1900 ArrayRef<Attribute> annotations,
1901 ArrayRef<Attribute> portAnnotations,
bool lowerToBind,
1902 hw::InnerSymAttr innerSym) {
1903 result.addTypes(resultTypes);
1904 result.addAttribute(
"moduleName",
1906 result.addAttribute(
"name",
builder.getStringAttr(name));
1907 result.addAttribute(
1910 result.addAttribute(
"portNames",
builder.getArrayAttr(portNames));
1911 result.addAttribute(
"annotations",
builder.getArrayAttr(annotations));
1913 result.addAttribute(
"lowerToBind",
builder.getUnitAttr());
1915 result.addAttribute(
"inner_sym", innerSym);
1916 result.addAttribute(
"nameKind",
1919 if (portAnnotations.empty()) {
1920 SmallVector<Attribute, 16> portAnnotationsVec(resultTypes.size(),
1922 result.addAttribute(
"portAnnotations",
1923 builder.getArrayAttr(portAnnotationsVec));
1925 assert(portAnnotations.size() == resultTypes.size());
1926 result.addAttribute(
"portAnnotations",
1927 builder.getArrayAttr(portAnnotations));
1931 void InstanceOp::build(OpBuilder &
builder, OperationState &result,
1932 FModuleLike module, StringRef name,
1933 NameKindEnum nameKind, ArrayRef<Attribute> annotations,
1934 ArrayRef<Attribute> portAnnotations,
bool lowerToBind,
1935 StringAttr innerSym) {
1938 SmallVector<Type> resultTypes;
1939 resultTypes.reserve(module.getNumPorts());
1941 module.getPortTypes(), std::back_inserter(resultTypes),
1942 [](Attribute typeAttr) { return cast<TypeAttr>(typeAttr).getValue(); });
1945 ArrayAttr portAnnotationsAttr;
1946 if (portAnnotations.empty()) {
1947 portAnnotationsAttr =
builder.getArrayAttr(SmallVector<Attribute, 16>(
1948 resultTypes.size(),
builder.getArrayAttr({})));
1950 portAnnotationsAttr =
builder.getArrayAttr(portAnnotations);
1958 module.getPortDirectionsAttr(), module.getPortNamesAttr(),
1959 builder.getArrayAttr(annotations), portAnnotationsAttr,
1960 lowerToBind ?
builder.getUnitAttr() : UnitAttr(),
1967 const llvm::BitVector &portIndices) {
1968 assert(portIndices.size() >= getNumResults() &&
1969 "portIndices is not at least as large as getNumResults()");
1971 if (portIndices.none())
1974 SmallVector<Type> newResultTypes = removeElementsAtIndices<Type>(
1975 SmallVector<Type>(result_type_begin(), result_type_end()), portIndices);
1976 SmallVector<Direction> newPortDirections = removeElementsAtIndices<Direction>(
1978 SmallVector<Attribute> newPortNames =
1980 SmallVector<Attribute> newPortAnnotations =
1983 auto newOp =
builder.create<InstanceOp>(
1984 getLoc(), newResultTypes, getModuleName(),
getName(), getNameKind(),
1985 newPortDirections, newPortNames, getAnnotations().getValue(),
1986 newPortAnnotations, getLowerToBind(), getInnerSymAttr());
1988 for (
unsigned oldIdx = 0, newIdx = 0, numOldPorts = getNumResults();
1989 oldIdx != numOldPorts; ++oldIdx) {
1990 if (portIndices.test(oldIdx)) {
1991 assert(getResult(oldIdx).use_empty() &&
"removed instance port has uses");
1994 getResult(oldIdx).replaceAllUsesWith(newOp.getResult(newIdx));
2002 if (
auto outputFile = (*this)->getAttr(
"output_file"))
2003 newOp->setAttr(
"output_file", outputFile);
2008 ArrayAttr InstanceOp::getPortAnnotation(
unsigned portIdx) {
2009 assert(portIdx < getNumResults() &&
2010 "index should be smaller than result number");
2011 return cast<ArrayAttr>(getPortAnnotations()[portIdx]);
2014 void InstanceOp::setAllPortAnnotations(ArrayRef<Attribute> annotations) {
2015 assert(annotations.size() == getNumResults() &&
2016 "number of annotations is not equal to result number");
2017 (*this)->setAttr(
"portAnnotations",
2022 InstanceOp::cloneAndInsertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
2023 auto portSize = ports.size();
2024 auto newPortCount = getNumResults() + portSize;
2025 SmallVector<Direction> newPortDirections;
2026 newPortDirections.reserve(newPortCount);
2027 SmallVector<Attribute> newPortNames;
2028 newPortNames.reserve(newPortCount);
2029 SmallVector<Type> newPortTypes;
2030 newPortTypes.reserve(newPortCount);
2031 SmallVector<Attribute> newPortAnnos;
2032 newPortAnnos.reserve(newPortCount);
2034 unsigned oldIndex = 0;
2035 unsigned newIndex = 0;
2036 while (oldIndex + newIndex < newPortCount) {
2038 if (newIndex < portSize && ports[newIndex].first == oldIndex) {
2039 auto &newPort = ports[newIndex].second;
2040 newPortDirections.push_back(newPort.direction);
2041 newPortNames.push_back(newPort.name);
2042 newPortTypes.push_back(newPort.type);
2043 newPortAnnos.push_back(newPort.annotations.getArrayAttr());
2047 newPortDirections.push_back(getPortDirection(oldIndex));
2048 newPortNames.push_back(getPortName(oldIndex));
2049 newPortTypes.push_back(getType(oldIndex));
2050 newPortAnnos.push_back(getPortAnnotation(oldIndex));
2056 return OpBuilder(*this).create<InstanceOp>(
2057 getLoc(), newPortTypes, getModuleName(),
getName(), getNameKind(),
2058 newPortDirections, newPortNames, getAnnotations().getValue(),
2059 newPortAnnos, getLowerToBind(), getInnerSymAttr());
2062 LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2063 auto module = (*this)->getParentOfType<FModuleOp>();
2064 auto referencedModule = symbolTable.lookupNearestSymbolFrom<FModuleLike>(
2065 *
this, getModuleNameAttr());
2066 if (!referencedModule) {
2067 return emitOpError(
"invalid symbol reference");
2071 if (isa<ClassOp /* ClassLike */>(referencedModule))
2072 return emitOpError(
"must instantiate a module not a class")
2073 .attachNote(referencedModule.getLoc())
2074 <<
"class declared here";
2078 if (referencedModule == module) {
2079 auto diag = emitOpError()
2080 <<
"is a recursive instantiation of its containing module";
2081 return diag.attachNote(module.getLoc())
2082 <<
"containing module declared here";
2086 auto emitNote = [&](InFlightDiagnostic &&diag) -> InFlightDiagnostic && {
2087 diag.attachNote(referencedModule->getLoc())
2088 <<
"original module declared here";
2089 return std::move(diag);
2094 size_t numResults = getNumResults();
2095 size_t numExpected = referencedModule.getNumPorts();
2096 if (numResults != numExpected) {
2097 return emitNote(emitOpError() <<
"has a wrong number of results; expected "
2098 << numExpected <<
" but got " << numResults);
2100 if (getPortDirections().
getBitWidth() != numExpected)
2101 return emitNote(emitOpError(
"the number of port directions should be "
2102 "equal to the number of results"));
2103 if (getPortNames().
size() != numExpected)
2104 return emitNote(emitOpError(
"the number of port names should be "
2105 "equal to the number of results"));
2106 if (getPortAnnotations().
size() != numExpected)
2107 return emitNote(emitOpError(
"the number of result annotations should be "
2108 "equal to the number of results"));
2111 if (getPortNamesAttr() != referencedModule.getPortNamesAttr()) {
2113 auto instanceNames = getPortNames();
2114 auto moduleNames = referencedModule.getPortNamesAttr();
2116 if (instanceNames.size() != moduleNames.size()) {
2117 return emitNote(emitOpError()
2118 <<
"has a wrong number of directions; expected "
2119 << moduleNames.size() <<
" but got "
2120 << instanceNames.size());
2123 for (
size_t i = 0; i != numResults; ++i) {
2124 if (instanceNames[i] != moduleNames[i]) {
2125 return emitNote(emitOpError()
2126 <<
"name for port " << i <<
" must be "
2127 << moduleNames[i] <<
", but got " << instanceNames[i]);
2130 llvm_unreachable(
"should have found something wrong");
2134 for (
size_t i = 0; i != numResults; i++) {
2135 auto resultType = getResult(i).getType();
2136 auto expectedType = referencedModule.getPortType(i);
2137 if (resultType != expectedType) {
2138 return emitNote(emitOpError()
2139 <<
"result type for " << getPortName(i) <<
" must be "
2140 << expectedType <<
", but got " << resultType);
2145 if (getPortDirectionsAttr() != referencedModule.getPortDirectionsAttr()) {
2147 auto instanceDirectionAttr = getPortDirectionsAttr();
2148 auto moduleDirectionAttr = referencedModule.getPortDirectionsAttr();
2150 auto expectedWidth = moduleDirectionAttr.getValue().getBitWidth();
2151 auto actualWidth = instanceDirectionAttr.getValue().getBitWidth();
2152 if (expectedWidth != actualWidth) {
2153 return emitNote(emitOpError()
2154 <<
"has a wrong number of directions; expected "
2155 << expectedWidth <<
" but got " << actualWidth);
2160 for (
size_t i = 0; i != numResults; ++i) {
2161 if (instanceDirs[i] != moduleDirs[i]) {
2162 return emitNote(emitOpError()
2163 <<
"direction for " << getPortName(i) <<
" must be \""
2169 llvm_unreachable(
"should have found something wrong");
2177 StringAttr InstanceOp::getInstanceNameAttr() {
return getNameAttr(); }
2179 void InstanceOp::print(OpAsmPrinter &p) {
2182 p.printKeywordOrString(
getName());
2183 if (
auto attr = getInnerSymAttr()) {
2185 p.printSymbolName(attr.getSymName());
2187 if (getNameKindAttr().getValue() != NameKindEnum::DroppableName)
2188 p <<
' ' << stringifyNameKindEnum(getNameKindAttr().getValue());
2191 SmallVector<StringRef, 9> omittedAttrs = {
"moduleName",
"name",
2192 "portDirections",
"portNames",
2193 "portTypes",
"portAnnotations",
2194 "inner_sym",
"nameKind"};
2195 if (getAnnotations().
empty())
2196 omittedAttrs.push_back(
"annotations");
2197 p.printOptionalAttrDict((*this)->getAttrs(), omittedAttrs);
2201 p.printSymbolName(getModuleName());
2204 SmallVector<Attribute> portTypes;
2205 portTypes.reserve(getNumResults());
2206 llvm::transform(getResultTypes(), std::back_inserter(portTypes),
2210 getPortNames().getValue(), portTypes,
2211 getPortAnnotations().getValue(), {}, {});
2214 ParseResult InstanceOp::parse(OpAsmParser &parser, OperationState &result) {
2215 auto *context = parser.getContext();
2216 auto &resultAttrs = result.attributes;
2219 hw::InnerSymAttr innerSymAttr;
2220 FlatSymbolRefAttr moduleName;
2221 SmallVector<OpAsmParser::Argument> entryArgs;
2222 SmallVector<Direction, 4> portDirections;
2223 SmallVector<Attribute, 4> portNames;
2224 SmallVector<Attribute, 4> portTypes;
2225 SmallVector<Attribute, 4> portAnnotations;
2226 SmallVector<Attribute, 4> portSyms;
2227 SmallVector<Attribute, 4> portLocs;
2228 NameKindEnumAttr nameKind;
2230 if (parser.parseKeywordOrString(&name))
2232 if (succeeded(parser.parseOptionalKeyword(
"sym"))) {
2233 if (parser.parseCustomAttributeWithFallback(
2234 innerSymAttr, ::mlir::Type{},
2236 result.attributes)) {
2237 return ::mlir::failure();
2241 parser.parseOptionalAttrDict(result.attributes) ||
2242 parser.parseAttribute(moduleName,
"moduleName", resultAttrs) ||
2244 false, entryArgs, portDirections,
2245 portNames, portTypes, portAnnotations, portSyms,
2251 if (!resultAttrs.get(
"moduleName"))
2252 result.addAttribute(
"moduleName", moduleName);
2253 if (!resultAttrs.get(
"name"))
2255 result.addAttribute(
"nameKind", nameKind);
2256 if (!resultAttrs.get(
"portDirections"))
2257 result.addAttribute(
"portDirections",
2259 if (!resultAttrs.get(
"portNames"))
2260 result.addAttribute(
"portNames",
ArrayAttr::get(context, portNames));
2261 if (!resultAttrs.get(
"portAnnotations"))
2262 result.addAttribute(
"portAnnotations",
2267 if (!resultAttrs.get(
"annotations"))
2268 resultAttrs.append(
"annotations", parser.getBuilder().getArrayAttr({}));
2271 result.types.reserve(portTypes.size());
2273 portTypes, std::back_inserter(result.types),
2274 [](Attribute typeAttr) { return cast<TypeAttr>(typeAttr).getValue(); });
2284 for (
size_t i = 0, e = (*this)->getNumResults(); i != e; ++i) {
2285 setNameFn(getResult(i), (base +
"_" + getPortNameStr(i)).str());
2289 std::optional<size_t> InstanceOp::getTargetResultIndex() {
2291 return std::nullopt;
2294 void MemOp::build(OpBuilder &
builder, OperationState &result,
2295 TypeRange resultTypes, uint32_t readLatency,
2296 uint32_t writeLatency, uint64_t depth, RUWAttr ruw,
2297 ArrayRef<Attribute> portNames, StringRef name,
2298 NameKindEnum nameKind, ArrayRef<Attribute> annotations,
2299 ArrayRef<Attribute> portAnnotations,
2300 hw::InnerSymAttr innerSym) {
2301 result.addAttribute(
2303 builder.getIntegerAttr(
builder.getIntegerType(32), readLatency));
2304 result.addAttribute(
2306 builder.getIntegerAttr(
builder.getIntegerType(32), writeLatency));
2307 result.addAttribute(
2308 "depth",
builder.getIntegerAttr(
builder.getIntegerType(64), depth));
2310 result.addAttribute(
"portNames",
builder.getArrayAttr(portNames));
2311 result.addAttribute(
"name",
builder.getStringAttr(name));
2312 result.addAttribute(
"nameKind",
2314 result.addAttribute(
"annotations",
builder.getArrayAttr(annotations));
2316 result.addAttribute(
"inner_sym", innerSym);
2317 result.addTypes(resultTypes);
2319 if (portAnnotations.empty()) {
2320 SmallVector<Attribute, 16> portAnnotationsVec(resultTypes.size(),
2322 result.addAttribute(
"portAnnotations",
2323 builder.getArrayAttr(portAnnotationsVec));
2325 assert(portAnnotations.size() == resultTypes.size());
2326 result.addAttribute(
"portAnnotations",
2327 builder.getArrayAttr(portAnnotations));
2331 void MemOp::build(OpBuilder &
builder, OperationState &result,
2332 TypeRange resultTypes, uint32_t readLatency,
2333 uint32_t writeLatency, uint64_t depth, RUWAttr ruw,
2334 ArrayRef<Attribute> portNames, StringRef name,
2335 NameKindEnum nameKind, ArrayRef<Attribute> annotations,
2336 ArrayRef<Attribute> portAnnotations, StringAttr innerSym) {
2337 build(
builder, result, resultTypes, readLatency, writeLatency, depth, ruw,
2338 portNames, name, nameKind, annotations, portAnnotations,
2342 ArrayAttr MemOp::getPortAnnotation(
unsigned portIdx) {
2343 assert(portIdx < getNumResults() &&
2344 "index should be smaller than result number");
2345 return cast<ArrayAttr>(getPortAnnotations()[portIdx]);
2348 void MemOp::setAllPortAnnotations(ArrayRef<Attribute> annotations) {
2349 assert(annotations.size() == getNumResults() &&
2350 "number of annotations is not equal to result number");
2351 (*this)->setAttr(
"portAnnotations",
2357 size_t &numReadWritePorts,
size_t &numDbgsPorts) {
2360 numReadWritePorts = 0;
2362 for (
size_t i = 0, e = getNumResults(); i != e; ++i) {
2363 auto portKind = getPortKind(i);
2364 if (portKind == MemOp::PortKind::Debug)
2366 else if (portKind == MemOp::PortKind::Read)
2368 else if (portKind == MemOp::PortKind::Write) {
2371 ++numReadWritePorts;
2376 LogicalResult MemOp::verify() {
2380 llvm::SmallDenseSet<Attribute, 8> portNamesSet;
2386 for (
size_t i = 0, e = getNumResults(); i != e; ++i) {
2387 auto portName = getPortName(i);
2392 BundleType portBundleType =
2393 type_dyn_cast<BundleType>(getResult(i).getType());
2396 if (!portNamesSet.insert(portName).second) {
2397 emitOpError() <<
"has non-unique port name " << portName;
2405 auto elt = getPortNamed(portName);
2407 emitOpError() <<
"could not get port with name " << portName;
2410 auto firrtlType = type_cast<FIRRTLType>(elt.getType());
2413 if (portKind == MemOp::PortKind::Debug &&
2414 !type_isa<RefType>(getResult(i).getType()))
2415 return emitOpError() <<
"has an invalid type on port " << portName
2416 <<
" (expected Read/Write/ReadWrite/Debug)";
2417 if (type_isa<RefType>(firrtlType) && e == 1)
2418 return emitOpError()
2419 <<
"cannot have only one port of debug type. Debug port can only "
2420 "exist alongside other read/write/read-write port";
2425 if (portKind == MemOp::PortKind::Debug) {
2426 auto resType = type_cast<RefType>(getResult(i).getType());
2427 if (!(resType && type_isa<FVectorType>(resType.getType())))
2428 return emitOpError() <<
"debug ports must be a RefType of FVectorType";
2429 dataType = type_cast<FVectorType>(resType.getType()).getElementType();
2431 auto dataTypeOption = portBundleType.getElement(
"data");
2432 if (!dataTypeOption && portKind == MemOp::PortKind::ReadWrite)
2433 dataTypeOption = portBundleType.getElement(
"wdata");
2434 if (!dataTypeOption) {
2435 emitOpError() <<
"has no data field on port " << portName
2436 <<
" (expected to see \"data\" for a read or write "
2437 "port or \"rdata\" for a read/write port)";
2440 dataType = dataTypeOption->type;
2442 if (portKind == MemOp::PortKind::Read) {
2449 emitOpError() <<
"has non-passive data type on port " << portName
2450 <<
" (memory types must be passive)";
2455 if (dataType.containsAnalog()) {
2456 emitOpError() <<
"has a data type that contains an analog type on port "
2458 <<
" (memory types cannot contain analog types)";
2466 getTypeForPort(getDepth(), dataType, portKind,
2467 dataType.isGround() ? getMaskBits() : 0);
2470 auto originalType = getResult(i).getType();
2471 if (originalType != expectedType) {
2472 StringRef portKindName;
2474 case MemOp::PortKind::Read:
2475 portKindName =
"read";
2477 case MemOp::PortKind::Write:
2478 portKindName =
"write";
2480 case MemOp::PortKind::ReadWrite:
2481 portKindName =
"readwrite";
2483 case MemOp::PortKind::Debug:
2484 portKindName =
"dbg";
2487 emitOpError() <<
"has an invalid type for port " << portName
2488 <<
" of determined kind \"" << portKindName
2489 <<
"\" (expected " << expectedType <<
", but got "
2490 << originalType <<
")";
2496 if (oldDataType && oldDataType != dataType) {
2497 emitOpError() <<
"port " << getPortName(i)
2498 <<
" has a different type than port " << getPortName(i - 1)
2499 <<
" (expected " << oldDataType <<
", but got " << dataType
2504 oldDataType = dataType;
2507 auto maskWidth = getMaskBits();
2509 auto dataWidth = getDataType().getBitWidthOrSentinel();
2510 if (dataWidth > 0 && maskWidth > (
size_t)dataWidth)
2511 return emitOpError(
"the mask width cannot be greater than "
2514 if (getPortAnnotations().
size() != getNumResults())
2515 return emitOpError(
"the number of result annotations should be "
2516 "equal to the number of results");
2522 return std::max(1U, llvm::Log2_64_Ceil(depth));
2528 PortKind portKind,
size_t maskBits) {
2530 auto *context = dataType.getContext();
2531 if (portKind == PortKind::Debug)
2540 auto getId = [&](StringRef name) -> StringAttr {
2544 SmallVector<BundleType::BundleElement, 7> portFields;
2548 portFields.push_back({getId(
"addr"),
false, addressType});
2549 portFields.push_back({getId(
"en"),
false,
UIntType::get(context, 1)});
2550 portFields.push_back({getId(
"clk"),
false,
ClockType::get(context)});
2553 case PortKind::Read:
2554 portFields.push_back({getId(
"data"),
true, dataType});
2557 case PortKind::Write:
2558 portFields.push_back({getId(
"data"),
false, dataType});
2559 portFields.push_back({getId(
"mask"),
false, maskType});
2562 case PortKind::ReadWrite:
2563 portFields.push_back({getId(
"rdata"),
true, dataType});
2564 portFields.push_back({getId(
"wmode"),
false,
UIntType::get(context, 1)});
2565 portFields.push_back({getId(
"wdata"),
false, dataType});
2566 portFields.push_back({getId(
"wmask"),
false, maskType});
2569 llvm::report_fatal_error(
"memory port kind not handled");
2577 SmallVector<MemOp::NamedPort> MemOp::getPorts() {
2578 SmallVector<MemOp::NamedPort> result;
2580 for (
size_t i = 0, e = getNumResults(); i != e; ++i) {
2582 auto portType = type_cast<FIRRTLType>(getResult(i).getType());
2589 MemOp::PortKind MemOp::getPortKind(StringRef portName) {
2591 type_cast<FIRRTLType>(getPortNamed(portName).getType()));
2595 MemOp::PortKind MemOp::getPortKind(
size_t resultNo) {
2597 type_cast<FIRRTLType>(getResult(resultNo).getType()));
2601 size_t MemOp::getMaskBits() {
2603 for (
auto res : getResults()) {
2604 if (type_isa<RefType>(res.getType()))
2606 auto firstPortType = type_cast<FIRRTLBaseType>(res.getType());
2612 for (
auto t : type_cast<BundleType>(firstPortType.getPassiveType())) {
2613 if (t.name.getValue().contains(
"mask"))
2616 if (type_isa<UIntType>(mType))
2626 assert(getNumResults() != 0 &&
"Mems with no read/write ports are illegal");
2628 if (
auto refType = type_dyn_cast<RefType>(getResult(0).getType()))
2629 return type_cast<FVectorType>(refType.getType()).getElementType();
2630 auto firstPortType = type_cast<FIRRTLBaseType>(getResult(0).getType());
2632 StringRef dataFieldName =
"data";
2634 dataFieldName =
"rdata";
2636 return type_cast<BundleType>(firstPortType.getPassiveType())
2637 .getElementType(dataFieldName);
2640 StringAttr MemOp::getPortName(
size_t resultNo) {
2641 return cast<StringAttr>(getPortNames()[resultNo]);
2645 return type_cast<FIRRTLBaseType>(getResults()[resultNo].getType());
2648 Value MemOp::getPortNamed(StringAttr name) {
2649 auto namesArray = getPortNames();
2650 for (
size_t i = 0, e = namesArray.size(); i != e; ++i) {
2651 if (namesArray[i] == name) {
2652 assert(i < getNumResults() &&
" names array out of sync with results");
2653 return getResult(i);
2662 size_t numReadPorts = 0;
2663 size_t numWritePorts = 0;
2664 size_t numReadWritePorts = 0;
2666 SmallVector<int32_t> writeClockIDs;
2668 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
2669 auto portKind = op.getPortKind(i);
2670 if (portKind == MemOp::PortKind::Read)
2672 else if (portKind == MemOp::PortKind::Write) {
2673 for (
auto *a : op.getResult(i).getUsers()) {
2674 auto subfield = dyn_cast<SubfieldOp>(a);
2675 if (!subfield || subfield.getFieldIndex() != 2)
2677 auto clockPort = a->getResult(0);
2678 for (
auto *b : clockPort.getUsers()) {
2679 if (
auto connect = dyn_cast<FConnectLike>(b)) {
2680 if (
connect.getDest() == clockPort) {
2683 connect.getSrc(),
true,
true,
true),
2685 if (result.second) {
2686 writeClockIDs.push_back(numWritePorts);
2688 writeClockIDs.push_back(result.first->second);
2697 ++numReadWritePorts;
2704 op.emitError(
"'firrtl.mem' should have simple type and known width");
2705 MemoryInitAttr init = op->getAttrOfType<MemoryInitAttr>(
"init");
2707 if (op->hasAttr(
"modName"))
2708 modName = op->getAttrOfType<StringAttr>(
"modName");
2710 SmallString<8> clocks;
2711 for (
auto a : writeClockIDs)
2712 clocks.append(Twine((
char)(a +
'a')).str());
2713 SmallString<32> initStr;
2718 for (
auto c : init.getFilename().getValue())
2719 if ((c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') ||
2720 (c >=
'0' && c <=
'9'))
2721 initStr.push_back(c);
2722 initStr.push_back(
'_');
2723 initStr.push_back(init.getIsBinary() ?
't' :
'f');
2724 initStr.push_back(
'_');
2725 initStr.push_back(init.getIsInline() ?
't' :
'f');
2730 "{0}FIRRTLMem_{1}_{2}_{3}_{4}_{5}_{6}_{7}_{8}_{9}_{10}{11}{12}",
2731 op.getPrefix().value_or(
""), numReadPorts, numWritePorts,
2732 numReadWritePorts, (
size_t)
width, op.getDepth(),
2733 op.getReadLatency(), op.getWriteLatency(), op.getMaskBits(),
2734 (
unsigned)op.getRuw(), (
unsigned)seq::WUW::PortOrder,
2735 clocks.empty() ?
"" :
"_" + clocks, init ? initStr.str() :
""));
2737 return {numReadPorts,
2742 op.getReadLatency(),
2743 op.getWriteLatency(),
2745 *seq::symbolizeRUW(
unsigned(op.getRuw())),
2746 seq::WUW::PortOrder,
2749 op.getMaskBits() > 1,
2760 for (
size_t i = 0, e = (*this)->getNumResults(); i != e; ++i) {
2761 setNameFn(getResult(i), (base +
"_" + getPortNameStr(i)).str());
2765 std::optional<size_t> MemOp::getTargetResultIndex() {
2767 return std::nullopt;
2778 setNameFn(op.getDataRaw(), name);
2779 if (op.isForceable())
2780 setNameFn(op.getDataRef(), (name +
"_ref").str());
2788 mlir::MLIRContext *context, std::optional<mlir::Location> location,
2789 ::mlir::ValueRange operands, ::mlir::DictionaryAttr attributes,
2790 ::mlir::OpaqueProperties properties, ::mlir::RegionRange regions,
2791 ::llvm::SmallVectorImpl<::mlir::Type> &inferredReturnTypes) {
2792 if (operands.empty())
2794 inferredReturnTypes.push_back(operands[0].getType());
2795 for (
auto &attr : attributes)
2796 if (attr.getName() == Forceable::getForceableAttrName()) {
2797 auto forceableType =
2799 if (!forceableType) {
2801 ::mlir::emitError(*location,
"cannot force a node of type ")
2802 << operands[0].getType();
2805 inferredReturnTypes.push_back(forceableType);
2810 std::optional<size_t> NodeOp::getTargetResultIndex() {
return 0; }
2816 std::optional<size_t> RegOp::getTargetResultIndex() {
return 0; }
2818 LogicalResult RegResetOp::verify() {
2819 auto reset = getResetValue();
2826 return emitError(
"type mismatch between register ")
2827 << regType <<
" and reset value " << resetType;
2832 std::optional<size_t> RegResetOp::getTargetResultIndex() {
return 0; }
2842 std::optional<size_t> WireOp::getTargetResultIndex() {
return 0; }
2844 void ObjectOp::build(OpBuilder &
builder, OperationState &state, ClassLike klass,
2846 build(
builder, state, klass.getInstanceType(),
2850 LogicalResult ObjectOp::verify() {
return success(); }
2852 LogicalResult ObjectOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2853 auto circuitOp = getOperation()->getParentOfType<CircuitOp>();
2854 auto classType = getType();
2855 auto className = classType.getNameAttr();
2858 auto classOp = dyn_cast_or_null<ClassLike>(
2859 symbolTable.lookupSymbolIn(circuitOp, className));
2861 return emitOpError() <<
"references unknown class " << className;
2864 if (failed(classOp.verifyType(classType, [&]() { return emitOpError(); })))
2870 StringAttr ObjectOp::getClassNameAttr() {
2871 return getType().getNameAttr().getAttr();
2874 StringRef ObjectOp::getClassName() {
return getType().getName(); }
2876 ClassLike ObjectOp::getReferencedClass(SymbolTable &symbolTable) {
2877 auto symRef = getType().getNameAttr();
2878 return symbolTable.lookup<ClassLike>(symRef.getLeafReference());
2882 return getReferencedClass(symtbl);
2885 Operation *ObjectOp::getReferencedModuleSlow() {
2886 auto circuit = (*this)->getParentOfType<CircuitOp>();
2890 return circuit.lookupSymbol<ClassLike>(getClassNameAttr());
2895 StringAttr ObjectOp::getInstanceNameAttr() {
return getNameAttr(); }
2897 StringRef ObjectOp::getReferencedModuleName() {
return getClassName(); }
2899 StringAttr ObjectOp::getReferencedModuleNameAttr() {
2900 return getClassNameAttr();
2904 setNameFn(getResult(),
getName());
2911 LogicalResult AttachOp::verify() {
2913 std::optional<int32_t> commonWidth;
2914 for (
auto operand : getOperands()) {
2915 auto thisWidth = type_cast<AnalogType>(operand.getType()).getWidth();
2919 commonWidth = thisWidth;
2922 if (commonWidth != thisWidth)
2923 return emitOpError(
"is inavlid as not all known operand widths match");
2930 Value dst =
connect->getOperand(0);
2931 Value src =
connect->getOperand(1);
2942 auto diag = emitError(
connect->getLoc());
2943 diag <<
"connect has invalid flow: the source expression ";
2945 diag <<
"\"" << srcName <<
"\" ";
2946 diag <<
"has " <<
toString(srcFlow) <<
", expected source or duplex flow";
2947 return diag.attachNote(srcRef.getLoc()) <<
"the source was defined here";
2955 auto diag = emitError(
connect->getLoc());
2956 diag <<
"connect has invalid flow: the destination expression ";
2958 diag <<
"\"" << dstName <<
"\" ";
2959 diag <<
"has " <<
toString(dstFlow) <<
", expected sink or duplex flow";
2960 return diag.attachNote(dstRef.getLoc())
2961 <<
"the destination was defined here";
2970 bool outerTypeIsConst =
false) {
2971 auto typeIsConst = outerTypeIsConst || type.
isConst();
2976 if (
auto bundleType = type_dyn_cast<BundleType>(type))
2977 return llvm::any_of(bundleType.getElements(), [&](
auto &element) {
2978 return isConstFieldDriven(element.type, isFlip ^ element.isFlip,
2982 if (
auto vectorType = type_dyn_cast<FVectorType>(type))
2994 auto dest =
connect.getDest();
2995 auto destType = type_dyn_cast<FIRRTLBaseType>(dest.getType());
2997 auto srcType = type_dyn_cast<FIRRTLBaseType>(src.getType());
2998 if (!destType || !srcType)
3001 auto destRefinedType = destType;
3002 auto srcRefinedType = srcType;
3007 auto findFieldDeclarationRefiningFieldType =
3009 while (
auto *definingOp = value.getDefiningOp()) {
3010 bool shouldContinue =
true;
3011 TypeSwitch<Operation *>(definingOp)
3012 .Case<SubfieldOp, SubindexOp>([&](
auto op) { value = op.getInput(); })
3013 .Case<SubaccessOp>([&](SubaccessOp op) {
3017 .getElementTypePreservingConst()
3019 originalFieldType = originalFieldType.getConstType(
true);
3020 value = op.getInput();
3022 .Default([&](Operation *) { shouldContinue =
false; });
3023 if (!shouldContinue)
3029 auto destDeclaration =
3030 findFieldDeclarationRefiningFieldType(dest, destRefinedType);
3031 auto srcDeclaration =
3032 findFieldDeclarationRefiningFieldType(src, srcRefinedType);
3034 auto checkConstConditionality = [&](Value value,
FIRRTLBaseType type,
3035 Value declaration) -> LogicalResult {
3036 auto *declarationBlock = declaration.getParentBlock();
3037 auto *block =
connect->getBlock();
3038 while (block && block != declarationBlock) {
3039 auto *parentOp = block->getParentOp();
3041 if (
auto whenOp = dyn_cast<WhenOp>(parentOp);
3042 whenOp && !whenOp.getCondition().getType().isConst()) {
3045 <<
"assignment to 'const' type " << type
3046 <<
" is dependent on a non-'const' condition";
3048 <<
"assignment to nested 'const' member of type " << type
3049 <<
" is dependent on a non-'const' condition";
3052 block = parentOp->getBlock();
3057 auto emitSubaccessError = [&] {
3059 "assignment to non-'const' subaccess of 'const' type is disallowed");
3065 if (destType != destRefinedType)
3066 return emitSubaccessError();
3068 if (failed(checkConstConditionality(dest, destType, destDeclaration)))
3073 if (srcRefinedType.containsConst() &&
3076 if (srcType != srcRefinedType)
3077 return emitSubaccessError();
3078 if (failed(checkConstConditionality(src, srcType, srcDeclaration)))
3085 LogicalResult ConnectOp::verify() {
3086 auto dstType = getDest().getType();
3087 auto srcType = getSrc().getType();
3088 auto dstBaseType = type_dyn_cast<FIRRTLBaseType>(dstType);
3089 auto srcBaseType = type_dyn_cast<FIRRTLBaseType>(srcType);
3090 if (!dstBaseType || !srcBaseType) {
3091 if (dstType != srcType)
3092 return emitError(
"may not connect different non-base types");
3095 if (dstBaseType.containsAnalog() || srcBaseType.containsAnalog())
3096 return emitError(
"analog types may not be connected");
3100 return emitError(
"type mismatch between destination ")
3101 << dstBaseType <<
" and source " << srcBaseType;
3106 return emitError(
"destination ")
3107 << dstBaseType <<
" is not as wide as the source " << srcBaseType;
3120 LogicalResult StrictConnectOp::verify() {
3121 if (
auto type = type_dyn_cast<FIRRTLType>(getDest().getType())) {
3122 auto baseType = type_cast<FIRRTLBaseType>(type);
3125 if (baseType && baseType.containsAnalog())
3126 return emitError(
"analog types may not be connected");
3131 "`SameAnonTypeOperands` trait should have already rejected "
3132 "structurally non-equivalent types");
3145 LogicalResult RefDefineOp::verify() {
3155 for (
auto *user : getDest().getUsers()) {
3156 if (
auto conn = dyn_cast<FConnectLike>(user);
3157 conn && conn.getDest() == getDest() && conn != *
this)
3158 return emitError(
"destination reference cannot be reused by multiple "
3159 "operations, it can only capture a unique dataflow");
3163 if (
auto *op = getDest().getDefiningOp()) {
3165 if (isa<RefSubOp>(op))
3167 "destination reference cannot be a sub-element of a reference");
3168 if (isa<RefCastOp>(op))
3170 "destination reference cannot be a cast of another reference");
3176 LogicalResult PropAssignOp::verify() {
3182 for (
auto *user : getDest().getUsers()) {
3183 if (
auto conn = dyn_cast<FConnectLike>(user);
3184 conn && conn.getDest() == getDest() && conn != *
this)
3185 return emitError(
"destination property cannot be reused by multiple "
3186 "operations, it can only capture a unique dataflow");
3192 void WhenOp::createElseRegion() {
3193 assert(!hasElseRegion() &&
"already has an else region");
3194 getElseRegion().push_back(
new Block());
3197 void WhenOp::build(OpBuilder &
builder, OperationState &result, Value condition,
3198 bool withElseRegion, std::function<
void()> thenCtor,
3199 std::function<
void()> elseCtor) {
3200 OpBuilder::InsertionGuard guard(
builder);
3201 result.addOperands(condition);
3204 builder.createBlock(result.addRegion());
3209 Region *elseRegion = result.addRegion();
3210 if (withElseRegion) {
3211 builder.createBlock(elseRegion);
3221 LogicalResult MatchOp::verify() {
3222 FEnumType type = getInput().getType();
3225 auto numCases = getTags().size();
3226 auto numRegions = getNumRegions();
3227 if (numRegions != numCases)
3228 return emitOpError(
"expected ")
3229 << numRegions <<
" tags but got " << numCases;
3231 auto numTags = type.getNumElements();
3233 SmallDenseSet<int64_t> seen;
3234 for (
const auto &[tag, region] : llvm::zip(getTags(), getRegions())) {
3235 auto tagIndex = size_t(cast<IntegerAttr>(tag).
getInt());
3238 if (region.getNumArguments() != 1)
3239 return emitOpError(
"region should have exactly one argument");
3242 if (tagIndex >= numTags)
3243 return emitOpError(
"the tag index ")
3244 << tagIndex <<
" is out of the range of valid tags in " << type;
3247 auto [it, inserted] = seen.insert(tagIndex);
3249 return emitOpError(
"the tag ") << type.getElementNameAttr(tagIndex)
3250 <<
" is matched more than once";
3253 auto expectedType = type.getElementTypePreservingConst(tagIndex);
3254 auto regionType = region.getArgument(0).getType();
3255 if (regionType != expectedType)
3256 return emitOpError(
"region type ")
3257 << regionType <<
" does not match the expected type "
3262 for (
size_t i = 0, e = type.getNumElements(); i < e; ++i)
3263 if (!seen.contains(i))
3264 return emitOpError(
"missing case for tag ") << type.getElementNameAttr(i);
3269 void MatchOp::print(OpAsmPrinter &p) {
3270 auto input = getInput();
3271 FEnumType type = input.getType();
3272 auto regions = getRegions();
3273 p <<
" " << input <<
" : " << type;
3274 SmallVector<StringRef> elided = {
"tags"};
3275 p.printOptionalAttrDictWithKeyword((*this)->getAttrs(), elided);
3278 for (
const auto &[tag, region] : llvm::zip(getTags(), regions)) {
3281 p.printKeywordOrString(
3282 type.getElementName(cast<IntegerAttr>(tag).getInt()));
3284 p.printRegionArgument(region.front().getArgument(0), {},
3287 p.printRegion(region,
false);
3294 ParseResult MatchOp::parse(OpAsmParser &parser, OperationState &result) {
3295 auto *context = parser.getContext();
3296 OpAsmParser::UnresolvedOperand input;
3297 if (parser.parseOperand(input) || parser.parseColon())
3300 auto loc = parser.getCurrentLocation();
3302 if (parser.parseType(type))
3304 auto enumType = type_dyn_cast<FEnumType>(type);
3306 return parser.emitError(loc,
"expected enumeration type but got") << type;
3308 if (parser.resolveOperand(input, type, result.operands) ||
3309 parser.parseOptionalAttrDictWithKeyword(result.attributes) ||
3310 parser.parseLBrace())
3314 SmallVector<Attribute> tags;
3317 if (failed(parser.parseOptionalKeyword(
"case")))
3321 auto nameLoc = parser.getCurrentLocation();
3323 OpAsmParser::Argument arg;
3324 auto *region = result.addRegion();
3325 if (parser.parseKeywordOrString(&name) || parser.parseLParen() ||
3326 parser.parseArgument(arg) || parser.parseRParen())
3330 auto index = enumType.getElementIndex(name);
3332 return parser.emitError(nameLoc,
"the tag \"")
3333 << name <<
"\" is not a member of the enumeration " << enumType;
3337 arg.type = enumType.getElementTypePreservingConst(*index);
3338 if (parser.parseRegion(*region, arg))
3343 return parser.parseRBrace();
3346 void MatchOp::build(OpBuilder &
builder, OperationState &result, Value input,
3348 MutableArrayRef<std::unique_ptr<Region>> regions) {
3349 result.addOperands(input);
3350 result.addAttribute(
"tags", tags);
3351 result.addRegions(regions);
3364 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
3365 DictionaryAttr attrs, mlir::OpaqueProperties properties,
3366 RegionRange regions, SmallVectorImpl<Type> &results,
3367 llvm::function_ref<
FIRRTLType(ValueRange, ArrayRef<NamedAttribute>,
3368 std::optional<Location>)>
3370 auto type = callback(
3371 operands, attrs ? attrs.getValue() : ArrayRef<NamedAttribute>{}, loc);
3373 results.push_back(type);
3381 static Attribute
getAttr(ArrayRef<NamedAttribute> attrs, StringRef name) {
3382 for (
auto attr : attrs)
3383 if (attr.getName() == name)
3384 return attr.getValue();
3385 llvm::report_fatal_error(
"attribute '" + name +
"' not found");
3389 template <
typename AttrClass>
3390 AttrClass
getAttr(ArrayRef<NamedAttribute> attrs, StringRef name) {
3391 return cast<AttrClass>(
getAttr(attrs, name));
3396 struct IsExprClassifier :
public ExprVisitor<IsExprClassifier, bool> {
3397 bool visitInvalidExpr(Operation *op) {
return false; }
3398 bool visitUnhandledExpr(Operation *op) {
return true; }
3407 if (
auto ty = type_dyn_cast<IntType>(getType())) {
3408 const char *base = ty.isSigned() ?
"invalid_si" :
"invalid_ui";
3409 auto width = ty.getWidthOrSentinel();
3413 name = (Twine(base) + Twine(
width)).str();
3414 }
else if (
auto ty = type_dyn_cast<AnalogType>(getType())) {
3415 auto width = ty.getWidthOrSentinel();
3417 name =
"invalid_analog";
3419 name = (
"invalid_analog" + Twine(
width)).str();
3420 }
else if (type_isa<AsyncResetType>(getType()))
3421 name =
"invalid_asyncreset";
3422 else if (type_isa<ResetType>(getType()))
3423 name =
"invalid_reset";
3424 else if (type_isa<ClockType>(getType()))
3425 name =
"invalid_clock";
3429 setNameFn(getResult(), name);
3432 void ConstantOp::print(OpAsmPrinter &p) {
3434 p.printAttributeWithoutType(getValueAttr());
3436 p.printType(getType());
3437 p.printOptionalAttrDict((*this)->getAttrs(), {
"value"});
3440 ParseResult ConstantOp::parse(OpAsmParser &parser, OperationState &result) {
3443 auto loc = parser.getCurrentLocation();
3444 auto valueResult = parser.parseOptionalInteger(value);
3445 if (!valueResult.has_value())
3446 return parser.emitError(loc,
"expected integer value");
3450 if (failed(*valueResult) || parser.parseColonType(resultType) ||
3451 parser.parseOptionalAttrDict(result.attributes))
3453 result.addTypes(resultType);
3459 if (
width > value.getBitWidth()) {
3463 value = value.sext(
width);
3464 }
else if (
width < value.getBitWidth()) {
3467 if (value.getNumSignBits() < value.getBitWidth() -
width)
3468 return parser.emitError(loc,
"constant too large for result type ")
3470 value = value.trunc(
width);
3474 auto intType = parser.getBuilder().getIntegerType(value.getBitWidth(),
3476 auto valueAttr = parser.getBuilder().getIntegerAttr(intType, value);
3477 result.addAttribute(
"value", valueAttr);
3481 LogicalResult ConstantOp::verify() {
3487 "firrtl.constant attribute bitwidth doesn't match return type");
3490 auto attrType = type_cast<IntegerType>(getValueAttr().getType());
3491 if (attrType.isSignless() || attrType.isSigned() != intType.
isSigned())
3492 return emitError(
"firrtl.constant attribute has wrong sign");
3499 void ConstantOp::build(OpBuilder &
builder, OperationState &result,
IntType type,
3500 const APInt &value) {
3504 "incorrect attribute bitwidth for firrtl.constant");
3508 return build(
builder, result, type, attr);
3513 void ConstantOp::build(OpBuilder &
builder, OperationState &result,
3514 const APSInt &value) {
3518 return build(
builder, result, type, attr);
3528 SmallString<32> specialNameBuffer;
3529 llvm::raw_svector_ostream specialName(specialNameBuffer);
3531 getValue().print(specialName, intTy.
isSigned());
3533 specialName << (intTy.
isSigned() ?
"_si" :
"_ui");
3536 specialName <<
width;
3537 setNameFn(getResult(), specialName.str());
3540 void SpecialConstantOp::print(OpAsmPrinter &p) {
3543 p << static_cast<unsigned>(getValue());
3545 p.printType(getType());
3546 p.printOptionalAttrDict((*this)->getAttrs(), {
"value"});
3549 ParseResult SpecialConstantOp::parse(OpAsmParser &parser,
3550 OperationState &result) {
3554 auto loc = parser.getCurrentLocation();
3555 auto valueResult = parser.parseOptionalInteger(value);
3556 if (!valueResult.has_value())
3557 return parser.emitError(loc,
"expected integer value");
3560 if (value != 0 && value != 1)
3561 return parser.emitError(loc,
"special constants can only be 0 or 1.");
3565 if (failed(*valueResult) || parser.parseColonType(resultType) ||
3566 parser.parseOptionalAttrDict(result.attributes))
3568 result.addTypes(resultType);
3571 auto valueAttr = parser.getBuilder().getBoolAttr(value == 1);
3572 result.addAttribute(
"value", valueAttr);
3577 SmallString<32> specialNameBuffer;
3578 llvm::raw_svector_ostream specialName(specialNameBuffer);
3580 specialName << static_cast<unsigned>(getValue());
3581 auto type = getType();
3582 if (type_isa<ClockType>(type)) {
3583 specialName <<
"_clock";
3584 }
else if (type_isa<ResetType>(type)) {
3585 specialName <<
"_reset";
3586 }
else if (type_isa<AsyncResetType>(type)) {
3587 specialName <<
"_asyncreset";
3589 setNameFn(getResult(), specialName.str());
3596 if (type.isGround()) {
3597 if (!isa<IntegerAttr>(attr)) {
3598 op->emitOpError(
"Ground type is not an integer attribute");
3603 auto attrlist = dyn_cast<ArrayAttr>(attr);
3605 op->emitOpError(
"expected array attribute for aggregate constant");
3608 if (
auto array = type_dyn_cast<FVectorType>(type)) {
3609 if (array.getNumElements() != attrlist.size()) {
3610 op->emitOpError(
"array attribute (")
3611 << attrlist.size() <<
") has wrong size for vector constant ("
3612 << array.getNumElements() <<
")";
3615 return llvm::all_of(attrlist, [&array, op](Attribute attr) {
3619 if (
auto bundle = type_dyn_cast<BundleType>(type)) {
3620 if (bundle.getNumElements() != attrlist.size()) {
3621 op->emitOpError(
"array attribute (")
3622 << attrlist.size() <<
") has wrong size for bundle constant ("
3623 << bundle.getNumElements() <<
")";
3626 for (
size_t i = 0; i < bundle.getNumElements(); ++i) {
3627 if (bundle.getElement(i).isFlip) {
3628 op->emitOpError(
"Cannot have constant bundle type with flip");
3636 op->emitOpError(
"Unknown aggregate type");
3640 LogicalResult AggregateConstantOp::verify() {
3646 Attribute AggregateConstantOp::getAttributeFromFieldID(uint64_t fieldID) {
3648 Attribute value = getFields();
3649 while (fieldID != 0) {
3650 if (
auto bundle = type_dyn_cast<BundleType>(type)) {
3651 auto index = bundle.getIndexForFieldID(fieldID);
3652 fieldID -= bundle.getFieldID(index);
3653 type = bundle.getElementType(index);
3654 value = cast<ArrayAttr>(value)[index];
3656 auto vector = type_cast<FVectorType>(type);
3657 auto index = vector.getIndexForFieldID(fieldID);
3658 fieldID -= vector.getFieldID(index);
3659 type = vector.getElementType();
3660 value = cast<ArrayAttr>(value)[index];
3666 void FIntegerConstantOp::print(OpAsmPrinter &p) {
3668 p.printAttributeWithoutType(getValueAttr());
3669 p.printOptionalAttrDict((*this)->getAttrs(), {
"value"});
3672 ParseResult FIntegerConstantOp::parse(OpAsmParser &parser,
3673 OperationState &result) {
3674 auto *context = parser.getContext();
3676 if (parser.parseInteger(value) ||
3677 parser.parseOptionalAttrDict(result.attributes))
3682 auto valueAttr = parser.getBuilder().getIntegerAttr(intType, value);
3683 result.addAttribute(
"value", valueAttr);
3687 ParseResult ListCreateOp::parse(OpAsmParser &parser, OperationState &result) {
3688 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 16> operands;
3691 if (parser.parseOperandList(operands) ||
3692 parser.parseOptionalAttrDict(result.attributes) ||
3693 parser.parseColonType(type))
3695 result.addTypes(type);
3697 return parser.resolveOperands(operands, type.getElementType(),
3701 void ListCreateOp::print(OpAsmPrinter &p) {
3703 p.printOperands(getElements());
3704 p.printOptionalAttrDict((*this)->getAttrs());
3705 p <<
" : " << getType();
3708 LogicalResult ListCreateOp::verify() {
3709 if (getElements().
empty())
3712 auto elementType = getElements().front().getType();
3713 auto listElementType = getType().getElementType();
3715 return emitOpError(
"has elements of type ")
3716 <<
elementType <<
" instead of " << listElementType;
3721 ParseResult MapCreateOp::parse(OpAsmParser &parser, OperationState &result) {
3722 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 16> keys;
3723 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 16> values;
3727 auto parsePair = [&]() {
3728 OpAsmParser::UnresolvedOperand key, value;
3729 if (parser.parseOperand(key) || parser.parseArrow() ||
3730 parser.parseOperand(value))
3732 keys.push_back(key);
3733 values.push_back(value);
3736 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::OptionalParen,
3738 parser.parseOptionalAttrDict(result.attributes) ||
3739 parser.parseColonType(type))
3741 result.addTypes(type);
3743 if (parser.resolveOperands(keys, type.getKeyType(), result.operands) ||
3744 parser.resolveOperands(values, type.getValueType(), result.operands))
3750 void MapCreateOp::print(OpAsmPrinter &p) {
3752 if (!getKeys().
empty()) {
3754 llvm::interleaveComma(llvm::zip_equal(getKeys(), getValues()), p,
3756 auto &[key, value] = pair;
3757 p.printOperand(key);
3759 p.printOperand(value);
3763 p.printOptionalAttrDict((*this)->getAttrs());
3764 p <<
" : " << getType();
3767 LogicalResult MapCreateOp::verify() {
3768 if (getKeys().
empty())
3771 if (!llvm::all_equal(getKeys().getTypes()))
3772 return emitOpError(
"has keys that are not all the same type");
3773 if (!llvm::all_equal(getValues().getTypes()))
3774 return emitOpError(
"has values that are not all the same type");
3776 if (getKeys().front().getType() != getType().getKeyType())
3777 return emitOpError(
"has unexpected key type ")
3778 << getKeys().front().getType() <<
" instead of map key type "
3779 << getType().getKeyType();
3781 if (getValues().front().getType() != getType().getValueType())
3782 return emitOpError(
"has unexpected value type ")
3783 << getValues().front().getType() <<
" instead of map value type "
3784 << getType().getValueType();
3788 LogicalResult BundleCreateOp::verify() {
3789 BundleType resultType = getType();
3790 if (resultType.getNumElements() != getFields().
size())
3791 return emitOpError(
"number of fields doesn't match type");
3792 for (
size_t i = 0; i < resultType.getNumElements(); ++i)
3794 resultType.getElementTypePreservingConst(i),
3795 type_cast<FIRRTLBaseType>(getOperand(i).getType())))
3796 return emitOpError(
"type of element doesn't match bundle for field ")
3797 << resultType.getElement(i).name;
3802 LogicalResult VectorCreateOp::verify() {
3803 FVectorType resultType = getType();
3804 if (resultType.getNumElements() != getFields().
size())
3805 return emitOpError(
"number of fields doesn't match type");
3806 auto elemTy = resultType.getElementTypePreservingConst();
3807 for (
size_t i = 0; i < resultType.getNumElements(); ++i)
3809 elemTy, type_cast<FIRRTLBaseType>(getOperand(i).getType())))
3810 return emitOpError(
"type of element doesn't match vector element");
3819 LogicalResult FEnumCreateOp::verify() {
3820 FEnumType resultType = getResult().getType();
3821 auto elementIndex = resultType.getElementIndex(
getFieldName());
3823 return emitOpError(
"label ")
3824 <<
getFieldName() <<
" is not a member of the enumeration type "
3827 resultType.getElementTypePreservingConst(*elementIndex),
3828 getInput().getType()))
3829 return emitOpError(
"type of element doesn't match enum element");
3833 void FEnumCreateOp::print(OpAsmPrinter &printer) {
3836 printer <<
'(' << getInput() <<
')';
3837 SmallVector<StringRef> elidedAttrs = {
"fieldIndex"};
3838 printer.printOptionalAttrDictWithKeyword((*this)->getAttrs(), elidedAttrs);
3840 printer.printFunctionalType(ArrayRef<Type>{getInput().getType()},
3841 ArrayRef<Type>{getResult().getType()});
3844 ParseResult FEnumCreateOp::parse(OpAsmParser &parser, OperationState &result) {
3845 auto *context = parser.getContext();
3847 OpAsmParser::UnresolvedOperand input;
3848 std::string fieldName;
3849 mlir::FunctionType functionType;
3850 if (parser.parseKeywordOrString(&fieldName) || parser.parseLParen() ||
3851 parser.parseOperand(input) || parser.parseRParen() ||
3852 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
3853 parser.parseType(functionType))
3856 if (functionType.getNumInputs() != 1)
3857 return parser.emitError(parser.getNameLoc(),
"single input type required");
3858 if (functionType.getNumResults() != 1)
3859 return parser.emitError(parser.getNameLoc(),
"single result type required");
3861 auto inputType = functionType.getInput(0);
3862 if (parser.resolveOperand(input, inputType, result.operands))
3865 auto outputType = functionType.getResult(0);
3866 auto enumType = type_dyn_cast<FEnumType>(outputType);
3868 return parser.emitError(parser.getNameLoc(),
3869 "output must be enum type, got ")
3871 auto fieldIndex = enumType.getElementIndex(fieldName);
3873 return parser.emitError(parser.getNameLoc(),
3874 "unknown field " + fieldName +
" in enum type ")
3877 result.addAttribute(
3881 result.addTypes(enumType);
3890 LogicalResult IsTagOp::verify() {
3891 if (getFieldIndex() >= getInput().getType().
get().getNumElements())
3892 return emitOpError(
"element index is greater than the number of fields in "
3897 void IsTagOp::print(::mlir::OpAsmPrinter &printer) {
3898 printer <<
' ' << getInput() <<
' ';
3900 SmallVector<::llvm::StringRef, 1> elidedAttrs = {
"fieldIndex"};
3901 printer.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
3902 printer <<
" : " << getInput().getType();
3905 ParseResult IsTagOp::parse(OpAsmParser &parser, OperationState &result) {
3906 auto *context = parser.getContext();
3908 OpAsmParser::UnresolvedOperand input;
3909 std::string fieldName;
3911 if (parser.parseOperand(input) || parser.parseKeywordOrString(&fieldName) ||
3912 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
3913 parser.parseType(inputType))
3916 if (parser.resolveOperand(input, inputType, result.operands))
3919 auto enumType = type_dyn_cast<FEnumType>(inputType);
3921 return parser.emitError(parser.getNameLoc(),
3922 "input must be enum type, got ")
3924 auto fieldIndex = enumType.getElementIndex(fieldName);
3926 return parser.emitError(parser.getNameLoc(),
3927 "unknown field " + fieldName +
" in enum type ")
3930 result.addAttribute(
3939 FIRRTLType IsTagOp::inferReturnType(ValueRange operands,
3940 ArrayRef<NamedAttribute> attrs,
3941 std::optional<Location> loc) {
3943 isConst(operands[0].getType()));
3946 template <
typename OpTy>
3948 auto *context = parser.getContext();
3950 OpAsmParser::UnresolvedOperand input;
3951 std::string fieldName;
3953 if (parser.parseOperand(input) || parser.parseLSquare() ||
3954 parser.parseKeywordOrString(&fieldName) || parser.parseRSquare() ||
3955 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
3956 parser.parseType(inputType))
3959 if (parser.resolveOperand(input, inputType, result.operands))
3962 auto bundleType = type_dyn_cast<typename OpTy::InputType>(inputType);
3964 return parser.emitError(parser.getNameLoc(),
3965 "input must be bundle type, got ")
3967 auto fieldIndex = bundleType.getElementIndex(fieldName);
3969 return parser.emitError(parser.getNameLoc(),
3970 "unknown field " + fieldName +
" in bundle type ")
3973 result.addAttribute(
3977 SmallVector<Type> inferredReturnTypes;
3979 result.attributes.getDictionary(context),
3980 result.getRawProperties(), result.regions,
3981 inferredReturnTypes)))
3983 result.addTypes(inferredReturnTypes);
3988 ParseResult SubtagOp::parse(OpAsmParser &parser, OperationState &result) {
3989 auto *context = parser.getContext();
3991 OpAsmParser::UnresolvedOperand input;
3992 std::string fieldName;
3994 if (parser.parseOperand(input) || parser.parseLSquare() ||
3995 parser.parseKeywordOrString(&fieldName) || parser.parseRSquare() ||
3996 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
3997 parser.parseType(inputType))
4000 if (parser.resolveOperand(input, inputType, result.operands))
4003 auto enumType = type_dyn_cast<FEnumType>(inputType);
4005 return parser.emitError(parser.getNameLoc(),
4006 "input must be enum type, got ")
4008 auto fieldIndex = enumType.getElementIndex(fieldName);
4010 return parser.emitError(parser.getNameLoc(),
4011 "unknown field " + fieldName +
" in enum type ")
4014 result.addAttribute(
4018 SmallVector<Type> inferredReturnTypes;
4020 context, result.location, result.operands,
4021 result.attributes.getDictionary(context), result.getRawProperties(),
4022 result.regions, inferredReturnTypes)))
4024 result.addTypes(inferredReturnTypes);
4029 ParseResult SubfieldOp::parse(OpAsmParser &parser, OperationState &result) {
4030 return parseSubfieldLikeOp<SubfieldOp>(parser, result);
4032 ParseResult OpenSubfieldOp::parse(OpAsmParser &parser, OperationState &result) {
4033 return parseSubfieldLikeOp<OpenSubfieldOp>(parser, result);
4036 template <
typename OpTy>
4038 printer <<
' ' << op.getInput() <<
'[';
4039 printer.printKeywordOrString(op.getFieldName());
4041 ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;
4042 elidedAttrs.push_back(
"fieldIndex");
4043 printer.printOptionalAttrDict(op->getAttrs(), elidedAttrs);
4044 printer <<
" : " << op.getInput().getType();
4046 void SubfieldOp::print(::mlir::OpAsmPrinter &printer) {
4047 return printSubfieldLikeOp<SubfieldOp>(*
this, printer);
4049 void OpenSubfieldOp::print(::mlir::OpAsmPrinter &printer) {
4050 return printSubfieldLikeOp<OpenSubfieldOp>(*
this, printer);
4053 void SubtagOp::print(::mlir::OpAsmPrinter &printer) {
4054 printer <<
' ' << getInput() <<
'[';
4057 ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;
4058 elidedAttrs.push_back(
"fieldIndex");
4059 printer.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
4060 printer <<
" : " << getInput().getType();
4063 template <
typename OpTy>
4065 if (op.getFieldIndex() >=
4066 firrtl::type_cast<typename OpTy::InputType>(op.getInput().getType())
4068 return op.emitOpError(
"subfield element index is greater than the number "
4069 "of fields in the bundle type");
4072 LogicalResult SubfieldOp::verify() {
4073 return verifySubfieldLike<SubfieldOp>(*
this);
4075 LogicalResult OpenSubfieldOp::verify() {
4076 return verifySubfieldLike<OpenSubfieldOp>(*
this);
4079 LogicalResult SubtagOp::verify() {
4080 if (getFieldIndex() >= getInput().getType().
get().getNumElements())
4081 return emitOpError(
"subfield element index is greater than the number "
4082 "of fields in the bundle type");
4092 SmallVector<Operation *, 8> worklist({op});
4096 bool constant =
true;
4102 while (constant && !(worklist.empty()))
4103 TypeSwitch<Operation *>(worklist.pop_back_val())
4104 .Case<NodeOp, AsSIntPrimOp, AsUIntPrimOp>([&](
auto op) {
4105 if (
auto definingOp = op.getInput().getDefiningOp())
4106 worklist.push_back(definingOp);
4109 .Case<WireOp, SubindexOp, SubfieldOp>([&](
auto op) {
4110 for (
auto &use : op.getResult().getUses())
4111 worklist.push_back(use.getOwner());
4113 .Case<ConstantOp, SpecialConstantOp, AggregateConstantOp>([](
auto) {})
4114 .Default([&](
auto) { constant =
false; });
4123 if (
auto *op = value.getDefiningOp())
4128 LogicalResult ConstCastOp::verify() {
4130 return emitOpError() << getInput().getType()
4131 <<
" is not 'const'-castable to "
4132 << getResult().getType();
4136 FIRRTLType SubfieldOp::inferReturnType(ValueRange operands,
4137 ArrayRef<NamedAttribute> attrs,
4138 std::optional<Location> loc) {
4139 auto inType = type_cast<BundleType>(operands[0].getType());
4141 getAttr<IntegerAttr>(attrs,
"fieldIndex").getValue().getZExtValue();
4143 if (fieldIndex >= inType.getNumElements())
4145 "subfield element index is greater than the "
4146 "number of fields in the bundle type");
4150 return inType.getElementTypePreservingConst(fieldIndex);
4153 FIRRTLType OpenSubfieldOp::inferReturnType(ValueRange operands,
4154 ArrayRef<NamedAttribute> attrs,
4155 std::optional<Location> loc) {
4156 auto inType = type_cast<OpenBundleType>(operands[0].getType());
4158 getAttr<IntegerAttr>(attrs,
"fieldIndex").getValue().getZExtValue();
4160 if (fieldIndex >= inType.getNumElements())
4162 "subfield element index is greater than the "
4163 "number of fields in the bundle type");
4167 return inType.getElementTypePreservingConst(fieldIndex);
4170 bool SubfieldOp::isFieldFlipped() {
4171 BundleType bundle = getInput().getType();
4172 return bundle.getElement(getFieldIndex()).isFlip;
4174 bool OpenSubfieldOp::isFieldFlipped() {
4175 auto bundle = getInput().getType();
4176 return bundle.getElement(getFieldIndex()).isFlip;
4179 FIRRTLType SubindexOp::inferReturnType(ValueRange operands,
4180 ArrayRef<NamedAttribute> attrs,
4181 std::optional<Location> loc) {
4182 Type inType = operands[0].getType();
4184 getAttr<IntegerAttr>(attrs,
"index").getValue().getZExtValue();
4186 if (
auto vectorType = type_dyn_cast<FVectorType>(inType)) {
4187 if (fieldIdx < vectorType.getNumElements())
4188 return vectorType.getElementTypePreservingConst();
4190 "' in vector type ", inType);
4196 FIRRTLType OpenSubindexOp::inferReturnType(ValueRange operands,
4197 ArrayRef<NamedAttribute> attrs,
4198 std::optional<Location> loc) {
4199 Type inType = operands[0].getType();
4201 getAttr<IntegerAttr>(attrs,
"index").getValue().getZExtValue();
4203 if (
auto vectorType = type_dyn_cast<OpenVectorType>(inType)) {
4204 if (fieldIdx < vectorType.getNumElements())
4205 return vectorType.getElementTypePreservingConst();
4207 "' in vector type ", inType);
4213 FIRRTLType SubtagOp::inferReturnType(ValueRange operands,
4214 ArrayRef<NamedAttribute> attrs,
4215 std::optional<Location> loc) {
4216 auto inType = type_cast<FEnumType>(operands[0].getType());
4218 getAttr<IntegerAttr>(attrs,
"fieldIndex").getValue().getZExtValue();
4220 if (fieldIndex >= inType.getNumElements())
4222 "subtag element index is greater than the "
4223 "number of fields in the enum type");
4227 auto elementType = inType.getElement(fieldIndex).type;
4231 FIRRTLType SubaccessOp::inferReturnType(ValueRange operands,
4232 ArrayRef<NamedAttribute> attrs,
4233 std::optional<Location> loc) {
4234 auto inType = operands[0].getType();
4235 auto indexType = operands[1].getType();
4237 if (!type_isa<UIntType>(indexType))
4241 if (
auto vectorType = type_dyn_cast<FVectorType>(inType)) {
4243 return vectorType.getElementTypePreservingConst();
4244 return vectorType.getElementType().getAllConstDroppedType();
4251 FIRRTLType TagExtractOp::inferReturnType(ValueRange operands,
4252 ArrayRef<NamedAttribute> attrs,
4253 std::optional<Location> loc) {
4254 auto inType = type_cast<FEnumType>(operands[0].getType());
4255 auto i = llvm::Log2_32_Ceil(inType.getNumElements());
4259 ParseResult MultibitMuxOp::parse(OpAsmParser &parser, OperationState &result) {
4260 OpAsmParser::UnresolvedOperand index;
4261 SmallVector<OpAsmParser::UnresolvedOperand, 16>
inputs;
4262 Type indexType, elemType;
4264 if (parser.parseOperand(index) || parser.parseComma() ||
4265 parser.parseOperandList(
inputs) ||
4266 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
4267 parser.parseType(indexType) || parser.parseComma() ||
4268 parser.parseType(elemType))
4271 if (parser.resolveOperand(index, indexType, result.operands))
4274 result.addTypes(elemType);
4276 return parser.resolveOperands(
inputs, elemType, result.operands);
4279 void MultibitMuxOp::print(OpAsmPrinter &p) {
4280 p <<
" " << getIndex() <<
", ";
4281 p.printOperands(getInputs());
4282 p.printOptionalAttrDict((*this)->getAttrs());
4283 p <<
" : " << getIndex().getType() <<
", " << getType();
4286 FIRRTLType MultibitMuxOp::inferReturnType(ValueRange operands,
4287 ArrayRef<NamedAttribute> attrs,
4288 std::optional<Location> loc) {
4289 if (operands.size() < 2)
4293 if (!llvm::all_of(operands.drop_front(2), [&](
auto op) {
4294 return operands[1].getType() == op.getType();
4298 return type_cast<FIRRTLType>(operands[1].getType());
4306 MLIRContext *context, std::optional<mlir::Location> location,
4307 ValueRange operands, DictionaryAttr attributes, OpaqueProperties properties,
4308 RegionRange regions, llvm::SmallVectorImpl<Type> &inferredReturnTypes) {
4309 auto type = inferReturnType(operands, attributes.getValue(), location);
4312 inferredReturnTypes.push_back(type);
4316 Type ObjectSubfieldOp::inferReturnType(ValueRange operands,
4317 ArrayRef<NamedAttribute> attrs,
4318 std::optional<Location> loc) {
4319 auto classType = dyn_cast<ClassType>(operands[0].getType());
4323 auto index = getAttr<IntegerAttr>(attrs,
"index").getValue().getZExtValue();
4324 if (classType.getNumElements() <= index)
4326 "number of fields in the object");
4328 return classType.getElement(index).type;
4331 void ObjectSubfieldOp::print(OpAsmPrinter &p) {
4332 auto input = getInput();
4333 auto classType = input.getType();
4334 p <<
' ' << input <<
"[";
4335 p.printKeywordOrString(classType.getElement(getIndex()).name);
4337 p.printOptionalAttrDict((*this)->getAttrs(), std::array{StringRef(
"index")});
4338 p <<
" : " << classType;
4341 ParseResult ObjectSubfieldOp::parse(OpAsmParser &parser,
4342 OperationState &result) {
4343 auto *context = parser.getContext();
4345 OpAsmParser::UnresolvedOperand input;
4346 std::string fieldName;
4347 ClassType inputType;
4348 if (parser.parseOperand(input) || parser.parseLSquare() ||
4349 parser.parseKeywordOrString(&fieldName) || parser.parseRSquare() ||
4350 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
4351 parser.parseType(inputType) ||
4352 parser.resolveOperand(input, inputType, result.operands))
4355 auto index = inputType.getElementIndex(fieldName);
4357 return parser.emitError(parser.getNameLoc(),
4358 "unknown field " + fieldName +
" in class type ")
4360 result.addAttribute(
"index",
4363 SmallVector<Type> inferredReturnTypes;
4365 result.attributes.getDictionary(context),
4366 result.getRawProperties(), result.regions,
4367 inferredReturnTypes)))
4369 result.addTypes(inferredReturnTypes);
4386 int32_t &rhsWidth,
bool &isConstResult,
4387 std::optional<Location> loc) {
4389 auto lhsi = type_dyn_cast<IntType>(lhs);
4390 auto rhsi = type_dyn_cast<IntType>(rhs);
4391 if (!lhsi || !rhsi || lhsi.isSigned() != rhsi.isSigned()) {
4394 mlir::emitError(*loc,
"second operand must be an integer type, not ")
4396 else if (!lhsi && rhsi)
4397 mlir::emitError(*loc,
"first operand must be an integer type, not ")
4399 else if (!lhsi && !rhsi)
4400 mlir::emitError(*loc,
"operands must be integer types, not ")
4401 << lhs <<
" and " << rhs;
4403 mlir::emitError(*loc,
"operand signedness must match");
4408 lhsWidth = lhsi.getWidthOrSentinel();
4409 rhsWidth = rhsi.getWidthOrSentinel();
4410 isConstResult = lhsi.isConst() && rhsi.isConst();
4415 assert(op->getNumOperands() == 2 &&
4416 "SameOperandsIntTypeKind on non-binary op");
4417 int32_t lhsWidth, rhsWidth;
4420 op->getOperand(1).getType(), lhsWidth,
4421 rhsWidth, isConstResult, op->getLoc()));
4425 ArrayRef<NamedAttribute> attrs,
4427 if (operands.size() != 2 || !attrs.empty()) {
4428 mlir::emitError(loc,
"operation requires two operands and no constants");
4435 std::optional<Location> loc) {
4436 int32_t lhsWidth, rhsWidth, resultWidth = -1;
4437 bool isConstResult =
false;
4441 if (lhsWidth != -1 && rhsWidth != -1)
4442 resultWidth = std::max(lhsWidth, rhsWidth) + 1;
4443 return IntType::get(lhs.getContext(), type_isa<SIntType>(lhs), resultWidth,
4448 std::optional<Location> loc) {
4449 int32_t lhsWidth, rhsWidth, resultWidth = -1;
4450 bool isConstResult =
false;
4454 if (lhsWidth != -1 && rhsWidth != -1)
4455 resultWidth = lhsWidth + rhsWidth;
4457 return IntType::get(lhs.getContext(), type_isa<SIntType>(lhs), resultWidth,
4462 std::optional<Location> loc) {
4463 int32_t lhsWidth, rhsWidth;
4464 bool isConstResult =
false;
4469 if (type_isa<UIntType>(lhs))
4470 return UIntType::get(lhs.getContext(), lhsWidth, isConstResult);
4473 int32_t resultWidth = lhsWidth != -1 ? lhsWidth + 1 : -1;
4474 return SIntType::get(lhs.getContext(), resultWidth, isConstResult);
4478 std::optional<Location> loc) {
4479 int32_t lhsWidth, rhsWidth, resultWidth = -1;
4480 bool isConstResult =
false;
4484 if (lhsWidth != -1 && rhsWidth != -1)
4485 resultWidth = std::min(lhsWidth, rhsWidth);
4486 return IntType::get(lhs.getContext(), type_isa<SIntType>(lhs), resultWidth,
4491 std::optional<Location> loc) {
4492 int32_t lhsWidth, rhsWidth, resultWidth = -1;
4493 bool isConstResult =
false;
4497 if (lhsWidth != -1 && rhsWidth != -1)
4498 resultWidth = std::max(lhsWidth, rhsWidth);
4499 return UIntType::get(lhs.getContext(), resultWidth, isConstResult);
4503 std::optional<Location> loc) {
4504 if (!type_isa<FVectorType>(lhs) || !type_isa<FVectorType>(rhs))
4507 auto lhsVec = type_cast<FVectorType>(lhs);
4508 auto rhsVec = type_cast<FVectorType>(rhs);
4510 if (lhsVec.getNumElements() != rhsVec.getNumElements())
4515 rhsVec.getElementTypePreservingConst(), loc);
4518 auto elemBaseType = type_cast<FIRRTLBaseType>(elemType);
4520 lhsVec.isConst() && rhsVec.isConst() &&
4521 elemBaseType.isConst());
4525 std::optional<Location> loc) {
4530 std::optional<Location> loc) {
4531 int32_t lhsWidth, rhsWidth, resultWidth = -1;
4532 bool isConstResult =
false;
4536 if (lhsWidth != -1 && rhsWidth != -1)
4537 resultWidth = lhsWidth + rhsWidth;
4538 return UIntType::get(lhs.getContext(), resultWidth, isConstResult);
4542 std::optional<Location> loc) {
4543 auto lhsi = type_dyn_cast<IntType>(lhs);
4544 auto rhsui = type_dyn_cast<UIntType>(rhs);
4545 if (!rhsui || !lhsi)
4547 loc,
"first operand should be integer, second unsigned int");
4551 auto width = lhsi.getWidthOrSentinel();
4552 if (
width == -1 || !rhsui.getWidth().has_value()) {
4555 auto amount = *rhsui.getWidth();
4558 "shift amount too large: second operand of "
4559 "dshl is wider than 31 bits");
4560 int64_t newWidth = (int64_t)
width + ((int64_t)1 << amount) - 1;
4561 if (newWidth > INT32_MAX)
4563 loc,
"shift amount too large: first operand shifted by maximum "
4564 "amount exceeds maximum width");
4568 lhsi.isConst() && rhsui.isConst());
4572 std::optional<Location> loc) {
4573 auto lhsi = type_dyn_cast<IntType>(lhs);
4574 auto rhsu = type_dyn_cast<UIntType>(rhs);
4577 loc,
"first operand should be integer, second unsigned int");
4578 return lhsi.getConstType(lhsi.isConst() && rhsu.isConst());
4582 std::optional<Location> loc) {
4583 auto lhsi = type_dyn_cast<IntType>(lhs);
4584 auto rhsu = type_dyn_cast<UIntType>(rhs);
4587 loc,
"first operand should be integer, second unsigned int");
4588 return lhsi.getConstType(lhsi.isConst() && rhsu.isConst());
4596 ArrayRef<NamedAttribute> attrs,
4598 if (operands.size() != 1 || !attrs.empty()) {
4599 mlir::emitError(loc,
"operation requires one operand and no constants");
4606 SizeOfIntrinsicOp::inferUnaryReturnType(
FIRRTLType input,
4607 std::optional<Location> loc) {
4612 std::optional<Location> loc) {
4613 auto base = type_dyn_cast<FIRRTLBaseType>(input);
4616 int32_t
width = base.getBitWidthOrSentinel();
4623 std::optional<Location> loc) {
4624 auto base = type_dyn_cast<FIRRTLBaseType>(input);
4627 int32_t
width = base.getBitWidthOrSentinel();
4634 AsAsyncResetPrimOp::inferUnaryReturnType(
FIRRTLType input,
4635 std::optional<Location> loc) {
4636 auto base = type_dyn_cast<FIRRTLBaseType>(input);
4639 "operand must be single bit scalar base type");
4640 int32_t
width = base.getBitWidthOrSentinel();
4647 std::optional<Location> loc) {
4652 std::optional<Location> loc) {
4653 if (
auto uiType = type_dyn_cast<UIntType>(input)) {
4654 auto width = uiType.getWidthOrSentinel();
4660 if (type_isa<SIntType>(input))
4667 std::optional<Location> loc) {
4668 auto inputi = type_dyn_cast<IntType>(input);
4671 int32_t
width = inputi.getWidthOrSentinel();
4678 std::optional<Location> loc) {
4679 auto inputi = type_dyn_cast<IntType>(input);
4682 return UIntType::get(input.getContext(), inputi.getWidthOrSentinel(),
4687 std::optional<Location> loc) {
4695 LogicalResult BitsPrimOp::validateArguments(ValueRange operands,
4696 ArrayRef<NamedAttribute> attrs,
4698 if (operands.size() != 1 || attrs.size() != 2) {
4699 mlir::emitError(loc,
"operation requires one operand and two constants");
4705 FIRRTLType BitsPrimOp::inferReturnType(ValueRange operands,
4706 ArrayRef<NamedAttribute> attrs,
4707 std::optional<Location> loc) {
4708 auto input = operands[0].getType();
4709 auto high = getAttr<IntegerAttr>(attrs,
"hi").getValue().getSExtValue();
4710 auto low = getAttr<IntegerAttr>(attrs,
"lo").getValue().getSExtValue();
4712 auto inputi = type_dyn_cast<IntType>(input);
4715 loc,
"input type should be the int type but got ", input);
4720 loc,
"high must be equal or greater than low, but got high = ", high,
4728 int32_t
width = inputi.getWidthOrSentinel();
4732 "high must be smaller than the width of input, but got high = ", high,
4733 ", width = ",
width);
4739 ArrayRef<NamedAttribute> attrs,
4741 if (operands.size() != 1 || attrs.size() != 1) {
4742 mlir::emitError(loc,
"operation requires one operand and one constant");
4748 FIRRTLType HeadPrimOp::inferReturnType(ValueRange operands,
4749 ArrayRef<NamedAttribute> attrs,
4750 std::optional<Location> loc) {
4751 auto input = operands[0].getType();
4752 auto amount = getAttr<IntegerAttr>(attrs,
"amount").getValue().getSExtValue();
4754 auto inputi = type_dyn_cast<IntType>(input);
4755 if (amount < 0 || !inputi)
4757 loc,
"operand must have integer type and amount must be >= 0");
4759 int32_t
width = inputi.getWidthOrSentinel();
4766 LogicalResult MuxPrimOp::validateArguments(ValueRange operands,
4767 ArrayRef<NamedAttribute> attrs,
4769 if (operands.size() != 3 || attrs.size() != 0) {
4770 mlir::emitError(loc,
"operation requires three operands and no constants");
4788 bool isConstCondition,
4789 std::optional<Location> loc) {
4795 if (high.getTypeID() != low.getTypeID())
4796 return emitInferRetTypeError<FIRRTLBaseType>(
4797 loc,
"incompatible mux operand types, true value type: ", high,
4798 ", false value type: ", low);
4800 bool outerTypeIsConst = isConstCondition && low.
isConst() && high.
isConst();
4805 if (type_isa<IntType>(low)) {
4810 if (highWidth == -1)
4812 return (lowWidth > highWidth ? low : high).
getConstType(outerTypeIsConst);
4816 auto highVector = type_dyn_cast<FVectorType>(high);
4817 auto lowVector = type_dyn_cast<FVectorType>(low);
4818 if (highVector && lowVector &&
4819 highVector.getNumElements() == lowVector.getNumElements()) {
4821 lowVector.getElementTypePreservingConst(),
4822 isConstCondition, loc);
4830 auto highBundle = type_dyn_cast<BundleType>(high);
4831 auto lowBundle = type_dyn_cast<BundleType>(low);
4832 if (highBundle && lowBundle) {
4833 auto highElements = highBundle.getElements();
4834 auto lowElements = lowBundle.getElements();
4837 SmallVector<BundleType::BundleElement> newElements;
4839 bool failed =
false;
4841 if (highElements[i].name != lowElements[i].name ||
4842 highElements[i].isFlip != lowElements[i].isFlip) {
4846 auto element = highElements[i];
4848 highBundle.getElementTypePreservingConst(i),
4849 lowBundle.getElementTypePreservingConst(i), isConstCondition, loc);
4852 newElements.push_back(element);
4855 return BundleType::get(low.getContext(), newElements, outerTypeIsConst);
4857 return emitInferRetTypeError<FIRRTLBaseType>(
4858 loc,
"incompatible mux operand bundle fields, true value type: ", high,
4859 ", false value type: ", low);
4864 return emitInferRetTypeError<FIRRTLBaseType>(
4865 loc,
"invalid mux operand types, true value type: ", high,
4866 ", false value type: ", low);
4869 FIRRTLType MuxPrimOp::inferReturnType(ValueRange operands,
4870 ArrayRef<NamedAttribute> attrs,
4871 std::optional<Location> loc) {
4872 auto highType = type_dyn_cast<FIRRTLBaseType>(operands[1].getType());
4873 auto lowType = type_dyn_cast<FIRRTLBaseType>(operands[2].getType());
4874 if (!highType || !lowType)
4880 FIRRTLType Mux2CellIntrinsicOp::inferReturnType(ValueRange operands,
4881 ArrayRef<NamedAttribute> attrs,
4882 std::optional<Location> loc) {
4883 auto highType = type_dyn_cast<FIRRTLBaseType>(operands[1].getType());
4884 auto lowType = type_dyn_cast<FIRRTLBaseType>(operands[2].getType());
4885 if (!highType || !lowType)
4891 FIRRTLType Mux4CellIntrinsicOp::inferReturnType(ValueRange operands,
4892 ArrayRef<NamedAttribute> attrs,
4893 std::optional<Location> loc) {
4894 SmallVector<FIRRTLBaseType> types;
4896 for (
unsigned i = 1; i < 5; i++) {
4897 types.push_back(type_dyn_cast<FIRRTLBaseType>(operands[i].getType()));
4902 isConst(operands[0].getType()), loc);
4906 result = types.back();
4912 FIRRTLType PadPrimOp::inferReturnType(ValueRange operands,
4913 ArrayRef<NamedAttribute> attrs,
4914 std::optional<Location> loc) {
4915 auto input = operands[0].getType();
4916 auto amount = getAttr<IntegerAttr>(attrs,
"amount").getValue().getSExtValue();
4918 auto inputi = type_dyn_cast<IntType>(input);
4919 if (amount < 0 || !inputi)
4921 loc,
"pad input must be integer and amount must be >= 0");
4923 int32_t
width = inputi.getWidthOrSentinel();
4932 FIRRTLType ShlPrimOp::inferReturnType(ValueRange operands,
4933 ArrayRef<NamedAttribute> attrs,
4934 std::optional<Location> loc) {
4935 auto input = operands[0].getType();
4936 auto amount = getAttr<IntegerAttr>(attrs,
"amount").getValue().getSExtValue();
4938 auto inputi = type_dyn_cast<IntType>(input);
4939 if (amount < 0 || !inputi)
4941 loc,
"shl input must be integer and amount must be >= 0");
4943 int32_t
width = inputi.getWidthOrSentinel();
4951 FIRRTLType ShrPrimOp::inferReturnType(ValueRange operands,
4952 ArrayRef<NamedAttribute> attrs,
4953 std::optional<Location> loc) {
4954 auto input = operands[0].getType();
4955 auto amount = getAttr<IntegerAttr>(attrs,
"amount").getValue().getSExtValue();
4957 auto inputi = type_dyn_cast<IntType>(input);
4958 if (amount < 0 || !inputi)
4960 loc,
"shr input must be integer and amount must be >= 0");
4962 int32_t
width = inputi.getWidthOrSentinel();
4964 width = std::max<int32_t>(1,
width - amount);
4970 FIRRTLType TailPrimOp::inferReturnType(ValueRange operands,
4971 ArrayRef<NamedAttribute> attrs,
4972 std::optional<Location> loc) {
4973 auto input = operands[0].getType();
4974 auto amount = getAttr<IntegerAttr>(attrs,
"amount").getValue().getSExtValue();
4976 auto inputi = type_dyn_cast<IntType>(input);
4977 if (amount < 0 || !inputi)
4979 loc,
"tail input must be integer and amount must be >= 0");
4981 int32_t
width = inputi.getWidthOrSentinel();
4985 loc,
"amount must be less than or equal operand width");
4997 function_ref<
void(Value, StringRef)> setNameFn) {
5001 auto isOkCharacter = [](
char c) {
return llvm::isAlnum(c) || c ==
'_'; };
5002 auto name = getText();
5004 if (name.startswith(
"`"))
5005 name = name.drop_front();
5006 name = name.take_while(isOkCharacter);
5008 setNameFn(getResult(), name);
5016 function_ref<
void(Value, StringRef)> setNameFn) {
5020 auto isOkCharacter = [](
char c) {
return llvm::isAlnum(c) || c ==
'_'; };
5021 auto name = getText();
5023 if (name.startswith(
"`"))
5024 name = name.drop_front();
5025 name = name.take_while(isOkCharacter);
5027 setNameFn(getResult(), name);
5034 LogicalResult HWStructCastOp::verify() {
5036 BundleType bundleType;
5037 hw::StructType structType;
5038 if ((bundleType = type_dyn_cast<BundleType>(getOperand().getType()))) {
5039 structType = getType().dyn_cast<hw::StructType>();
5041 return emitError(
"result type must be a struct");
5042 }
else if ((bundleType = type_dyn_cast<BundleType>(getType()))) {
5043 structType = getOperand().getType().dyn_cast<hw::StructType>();
5045 return emitError(
"operand type must be a struct");
5047 return emitError(
"either source or result type must be a bundle type");
5050 auto firFields = bundleType.getElements();
5051 auto hwFields = structType.getElements();
5052 if (firFields.size() != hwFields.size())
5053 return emitError(
"bundle and struct have different number of fields");
5055 for (
size_t findex = 0, fend = firFields.size(); findex < fend; ++findex) {
5056 if (firFields[findex].name.getValue() != hwFields[findex].name)
5057 return emitError(
"field names don't match '")
5058 << firFields[findex].name.getValue() <<
"', '"
5059 << hwFields[findex].name.getValue() <<
"'";
5063 if (firWidth > 0 && hwWidth > 0 && firWidth != hwWidth)
5064 return emitError(
"size of field '")
5065 << hwFields[findex].name.getValue() <<
"' don't match " << firWidth
5072 LogicalResult BitCastOp::verify() {
5073 auto inTypeBits =
getBitWidth(getInput().getType(),
true);
5075 if (inTypeBits.has_value() && resTypeBits.has_value()) {
5077 if (*inTypeBits == *resTypeBits) {
5080 return emitError(
"cannot cast non-'const' input type ")
5081 << getOperand().getType() <<
" to 'const' result type "
5085 return emitError(
"the bitwidth of input (")
5086 << *inTypeBits <<
") and result (" << *resTypeBits
5089 if (!inTypeBits.has_value())
5090 return emitError(
"bitwidth cannot be determined for input operand type ")
5091 << getInput().getType();
5092 return emitError(
"bitwidth cannot be determined for result type ")
5103 NamedAttrList &resultAttrs) {
5104 auto result = parser.parseOptionalAttrDict(resultAttrs);
5105 if (!resultAttrs.get(
"annotations"))
5106 resultAttrs.append(
"annotations", parser.getBuilder().getArrayAttr({}));
5112 DictionaryAttr attr,
5113 ArrayRef<StringRef> extraElides = {}) {
5114 SmallVector<StringRef> elidedAttrs(extraElides.begin(), extraElides.end());
5116 if (op->getAttrOfType<ArrayAttr>(
"annotations").empty())
5117 elidedAttrs.push_back(
"annotations");
5119 elidedAttrs.push_back(
"nameKind");
5121 p.printOptionalAttrDict(op->getAttrs(), elidedAttrs);
5127 NamedAttrList &resultAttrs) {
5130 if (!resultAttrs.get(
"portAnnotations")) {
5131 SmallVector<Attribute, 16> portAnnotations(
5132 parser.getNumResults(), parser.getBuilder().getArrayAttr({}));
5133 resultAttrs.append(
"portAnnotations",
5134 parser.getBuilder().getArrayAttr(portAnnotations));
5141 DictionaryAttr attr,
5142 ArrayRef<StringRef> extraElides = {}) {
5143 SmallVector<StringRef, 2> elidedAttrs(extraElides.begin(), extraElides.end());
5145 if (llvm::all_of(op->getAttrOfType<ArrayAttr>(
"portAnnotations"),
5146 [&](Attribute a) { return cast<ArrayAttr>(a).empty(); }))
5147 elidedAttrs.push_back(
"portAnnotations");
5156 firrtl::NameKindEnumAttr &result) {
5159 if (!parser.parseOptionalKeyword(&keyword,
5160 {
"interesting_name",
"droppable_name"})) {
5161 auto kind = symbolizeNameKindEnum(keyword);
5173 firrtl::NameKindEnumAttr attr,
5174 ArrayRef<StringRef> extraElides = {}) {
5175 if (attr.getValue() != NameKindEnum::DroppableName)
5176 p <<
" " << stringifyNameKindEnum(attr.getValue());
5184 NamedAttrList &resultAttrs) {
5192 DictionaryAttr attrs) {
5193 SmallVector<StringRef, 4> elides;
5195 elides.push_back(Forceable::getForceableAttrName());
5204 static ParseResult
parseMemOp(OpAsmParser &parser, NamedAttrList &resultAttrs) {
5209 static void printMemOp(OpAsmPrinter &p, Operation *op, DictionaryAttr attr) {
5220 if (ClassType::parseInterface(parser, type))
5227 type.printInterface(p);
5235 NamedAttrList &resultAttrs) {
5236 auto result = p.parseOptionalAttrDict(resultAttrs);
5237 if (!resultAttrs.get(
"name"))
5238 resultAttrs.append(
"name", p.getBuilder().getStringAttr(
""));
5244 DictionaryAttr attr,
5245 ArrayRef<StringRef> extraElides = {}) {
5246 SmallVector<StringRef> elides(extraElides.begin(), extraElides.end());
5247 if (op->getAttrOfType<StringAttr>(
"name").getValue().empty())
5248 elides.push_back(
"name");
5250 p.printOptionalAttrDict(op->getAttrs(), elides);
5254 NamedAttrList &resultAttrs) {
5259 DictionaryAttr attr) {
5268 DictionaryAttr attr) {
5277 DictionaryAttr attr) {
5289 if (op->getNumResults() == 1)
5290 if (
auto nameAttr = op->getAttrOfType<StringAttr>(
"name"))
5291 setNameFn(op->getResult(0), nameAttr.getValue());
5503 FIRRTLType RefResolveOp::inferReturnType(ValueRange operands,
5504 ArrayRef<NamedAttribute> attrs,
5505 std::optional<Location> loc) {
5506 Type inType = operands[0].getType();
5507 auto inRefType = type_dyn_cast<RefType>(inType);
5510 loc,
"ref.resolve operand must be ref type, not ", inType);
5511 return inRefType.getType();
5514 FIRRTLType RefSendOp::inferReturnType(ValueRange operands,
5515 ArrayRef<NamedAttribute> attrs,
5516 std::optional<Location> loc) {
5517 Type inType = operands[0].getType();
5518 auto inBaseType = type_dyn_cast<FIRRTLBaseType>(inType);
5521 loc,
"ref.send operand must be base type, not ", inType);
5525 FIRRTLType RefSubOp::inferReturnType(ValueRange operands,
5526 ArrayRef<NamedAttribute> attrs,
5527 std::optional<Location> loc) {
5528 auto refType = type_dyn_cast<RefType>(operands[0].getType());
5531 auto inType = refType.getType();
5533 getAttr<IntegerAttr>(attrs,
"index").getValue().getZExtValue();
5539 if (
auto vectorType = type_dyn_cast<FVectorType>(inType)) {
5540 if (fieldIdx < vectorType.getNumElements())
5542 vectorType.getElementType().getConstType(
5543 vectorType.isConst() || vectorType.getElementType().isConst()),
5544 refType.getForceable());
5546 "' in RefType of vector type ", refType);
5548 if (
auto bundleType = type_dyn_cast<BundleType>(inType)) {
5549 if (fieldIdx >= bundleType.getNumElements()) {
5551 "subfield element index is greater than "
5552 "the number of fields in the bundle type");
5554 return RefType::get(bundleType.getElement(fieldIdx).type.getConstType(
5555 bundleType.isConst() ||
5556 bundleType.getElement(fieldIdx).type.isConst()),
5557 refType.getForceable());
5561 loc,
"ref.sub op requires a RefType of vector or bundle base type");
5564 LogicalResult RWProbeOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
5565 auto targetRef = getTarget();
5566 if (targetRef.getModule() !=
5567 (*this)->getParentOfType<FModuleLike>().getModuleNameAttr())
5568 return emitOpError() <<
"has non-local target";
5570 auto target = ns.lookup(targetRef);
5572 return emitOpError() <<
"has target that cannot be resolved: " << targetRef;
5574 auto checkFinalType = [&](
auto type, Location loc) -> LogicalResult {
5579 auto baseType = type_dyn_cast<FIRRTLBaseType>(fType);
5580 if (!baseType || baseType.getPassiveType() != getType().getType()) {
5581 auto diag = emitOpError(
"has type mismatch: target resolves to ")
5582 << fType <<
" instead of expected " << getType().getType();
5583 diag.attachNote(loc) <<
"target resolves here";
5588 if (target.isPort()) {
5589 auto mod = cast<FModuleLike>(target.getOp());
5590 return checkFinalType(mod.getPortType(target.getPort()),
5591 mod.getPortLocation(target.getPort()));
5593 hw::InnerSymbolOpInterface symOp =
5594 cast<hw::InnerSymbolOpInterface>(target.getOp());
5595 if (!symOp.getTargetResult())
5596 return emitOpError(
"has target that cannot be probed")
5597 .attachNote(symOp.getLoc())
5598 .append(
"target resolves here");
5600 symOp.getTargetResult().getParentBlock()->findAncestorOpInBlock(**
this);
5601 if (!ancestor || !symOp->isBeforeInBlock(ancestor))
5602 return emitOpError(
"is not dominated by target")
5603 .attachNote(symOp.getLoc())
5604 .append(
"target here");
5605 return checkFinalType(symOp.getTargetResult().getType(), symOp.getLoc());
5612 LogicalResult GroupOp::verify() {
5613 auto groupName = getGroupName();
5614 auto *parentOp = (*this)->getParentOp();
5618 auto nestedReferences = groupName.getNestedReferences();
5619 if (nestedReferences.empty()) {
5620 if (!isa<FModuleOp>(parentOp)) {
5621 auto diag = emitOpError() <<
"has an un-nested group symbol, but does "
5622 "not have a 'firrtl.module' op as a parent";
5623 return diag.attachNote(parentOp->getLoc())
5624 <<
"illegal parent op defined here";
5627 auto parentGroup = dyn_cast<GroupOp>(parentOp);
5629 auto diag = emitOpError()
5630 <<
"has a nested group symbol, but does not have a '"
5631 << getOperationName() <<
"' op as a parent'";
5632 return diag.attachNote(parentOp->getLoc())
5633 <<
"illegal parent op defined here";
5635 auto parentGroupName = parentGroup.getGroupName();
5636 if (parentGroupName.getRootReference() != groupName.getRootReference() ||
5637 parentGroupName.getNestedReferences() !=
5638 groupName.getNestedReferences().drop_back()) {
5639 auto diag = emitOpError() <<
"is nested under an illegal group";
5640 return diag.attachNote(parentGroup->getLoc())
5641 <<
"illegal parent group defined here";
5646 Block *body = getBody(0);
5647 bool failed =
false;
5648 body->walk<mlir::WalkOrder::PreOrder>([&](Operation *op) {
5650 if (isa<GroupOp>(op))
5651 return WalkResult::skip();
5654 for (
auto operand : op->getOperands()) {
5656 if (operand.getParentBlock() == body)
5659 FIRRTLBaseType baseType = dyn_cast<FIRRTLBaseType>(operand.getType());
5661 auto diag = emitOpError()
5662 <<
"captures an operand which is not a FIRRTL base type";
5663 diag.attachNote(operand.getLoc()) <<
"operand is defined here";
5664 diag.attachNote(op->getLoc()) <<
"operand is used here";
5666 return WalkResult::advance();
5670 auto diag = emitOpError()
5671 <<
"captures an operand which is not a passive type";
5672 diag.attachNote(operand.getLoc()) <<
"operand is defined here";
5673 diag.attachNote(op->getLoc()) <<
"operand is used here";
5675 return WalkResult::advance();
5679 if (
auto connect = dyn_cast<FConnectLike>(op)) {
5681 if (dest.getParentBlock() == body)
5682 return WalkResult::advance();
5683 auto diag =
connect.emitOpError()
5684 <<
"connects to a destination which is defined outside its "
5686 diag.attachNote(getLoc()) <<
"enclosing group is defined here";
5687 diag.attachNote(dest.getLoc()) <<
"destination is defined here";
5690 return WalkResult::advance();
5698 LogicalResult GroupOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
5699 auto groupDeclOp = symbolTable.lookupNearestSymbolFrom<GroupDeclOp>(
5700 *
this, getGroupNameAttr());
5702 return emitOpError(
"invalid symbol reference");
5713 #define GET_OP_CLASSES
5714 #include "circt/Dialect/FIRRTL/FIRRTL.cpp.inc"
assert(baseType &&"element must be base type")
MlirType uint64_t numElements
static SmallVector< T > removeElementsAtIndices(ArrayRef< T > input, const llvm::BitVector &indicesToDrop)
Remove elements from the input array corresponding to set bits in indicesToDrop, returning the elemen...
static void printStopAttrs(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
ParseResult parseSubfieldLikeOp(OpAsmParser &parser, OperationState &result)
static bool isSameIntTypeKind(Type lhs, Type rhs, int32_t &lhsWidth, int32_t &rhsWidth, bool &isConstResult, std::optional< Location > loc)
If LHS and RHS are both UInt or SInt types, the return true and fill in the width of them if known.
static LogicalResult verifySubfieldLike(OpTy op)
static void eraseInternalPaths(T op, const llvm::BitVector &portIndices)
static bool isConstFieldDriven(FIRRTLBaseType type, bool isFlip=false, bool outerTypeIsConst=false)
Checks if the type has any 'const' leaf elements .
static hw::ModulePortInfo getPortListImpl(FModuleLike module)
static ParseResult parsePrintfAttrs(OpAsmParser &p, NamedAttrList &resultAttrs)
static RetTy emitInferRetTypeError(std::optional< Location > loc, const Twine &message, Args &&...args)
Emit an error if optional location is non-null, return null of return type.
static void buildModule(OpBuilder &builder, OperationState &result, StringAttr name, ArrayRef< PortInfo > ports, ArrayAttr annotations, bool withAnnotations=true)
static ParseResult parseFModuleLikeOp(OpAsmParser &parser, OperationState &result, bool hasSSAIdentifiers)
static bool printModulePorts(OpAsmPrinter &p, Block *block, ArrayRef< Direction > portDirections, ArrayRef< Attribute > portNames, ArrayRef< Attribute > portTypes, ArrayRef< Attribute > portAnnotations, ArrayRef< Attribute > portSyms, ArrayRef< Attribute > portLocs)
Print a list of module ports in the following form: in x: !firrtl.uint<1> [{class = "DontTouch}],...
static LogicalResult checkConnectConditionality(FConnectLike connect)
Checks that connections to 'const' destinations are not dependent on non-'const' conditions in when b...
static void erasePorts(FModuleLike op, const llvm::BitVector &portIndices)
Erases the ports that have their corresponding bit set in portIndices.
static ParseResult parseModulePorts(OpAsmParser &parser, bool hasSSAIdentifiers, bool supportsSymbols, SmallVectorImpl< OpAsmParser::Argument > &entryArgs, SmallVectorImpl< Direction > &portDirections, SmallVectorImpl< Attribute > &portNames, SmallVectorImpl< Attribute > &portTypes, SmallVectorImpl< Attribute > &portAnnotations, SmallVectorImpl< Attribute > &portSyms, SmallVectorImpl< Attribute > &portLocs)
Parse a list of module ports.
static ParseResult parseClassInterface(OpAsmParser &parser, Type &result)
static void printElidePortAnnotations(OpAsmPrinter &p, Operation *op, DictionaryAttr attr, ArrayRef< StringRef > extraElides={})
static void printNameKind(OpAsmPrinter &p, Operation *op, firrtl::NameKindEnumAttr attr, ArrayRef< StringRef > extraElides={})
static ParseResult parseStopAttrs(OpAsmParser &p, NamedAttrList &resultAttrs)
static ParseResult parseNameKind(OpAsmParser &parser, firrtl::NameKindEnumAttr &result)
A forward declaration for NameKind attribute parser.
static void printParameterList(ArrayAttr parameters, OpAsmPrinter &p)
Print a paramter list for a module or instance.
static size_t getAddressWidth(size_t depth)
static void forceableAsmResultNames(Forceable op, StringRef name, OpAsmSetValueNameFn setNameFn)
Helper for naming forceable declarations (and their optional ref result).
static void printSubfieldLikeOp(OpTy op, ::mlir::OpAsmPrinter &printer)
static bool checkAggConstant(Operation *op, Attribute attr, FIRRTLBaseType type)
static void printClassLike(OpAsmPrinter &p, ClassLike op)
static hw::ModulePort::Direction dirFtoH(Direction dir)
static MemOp::PortKind getMemPortKindFromType(FIRRTLType type)
Return the kind of port this is given the port type from a 'mem' decl.
static LogicalResult verifyInternalPaths(FModuleLike op, std::optional<::mlir::ArrayAttr > internalPaths)
static void genericAsmResultNames(Operation *op, OpAsmSetValueNameFn setNameFn)
static void printClassInterface(OpAsmPrinter &p, Operation *, ClassType type)
static void printPrintfAttrs(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
static void insertPorts(FModuleLike op, ArrayRef< std::pair< unsigned, PortInfo >> ports, bool supportsInternalPaths=false)
Inserts the given ports.
static void printFIRRTLImplicitSSAName(OpAsmPrinter &p, Operation *op, DictionaryAttr attrs)
static ParseResult parseFIRRTLImplicitSSAName(OpAsmParser &parser, NamedAttrList &resultAttrs)
static FIRRTLBaseType inferMuxReturnType(FIRRTLBaseType high, FIRRTLBaseType low, bool isConstCondition, std::optional< Location > loc)
Infer the result type for a multiplexer given its two operand types, which may be aggregates.
static ParseResult parseCircuitOpAttrs(OpAsmParser &parser, NamedAttrList &resultAttrs)
static SmallVector< PortInfo > getPortImpl(FModuleLike module)
constexpr const char * toString(Flow flow)
void getAsmBlockArgumentNamesImpl(Operation *op, mlir::Region ®ion, OpAsmSetValueNameFn setNameFn)
Get a special name to use when printing the entry block arguments of the region contained by an opera...
static void printElideAnnotations(OpAsmPrinter &p, Operation *op, DictionaryAttr attr, ArrayRef< StringRef > extraElides={})
static ParseResult parseElidePortAnnotations(OpAsmParser &parser, NamedAttrList &resultAttrs)
Parse an optional attribute dictionary, adding empty 'annotations' and 'portAnnotations' attributes i...
static ParseResult parseMemOp(OpAsmParser &parser, NamedAttrList &resultAttrs)
static LogicalResult checkConnectFlow(Operation *connect)
Check if the source and sink are of appropriate flow.
static ParseResult parseVerifAttrs(OpAsmParser &p, NamedAttrList &resultAttrs)
static ParseResult parseElideAnnotations(OpAsmParser &parser, NamedAttrList &resultAttrs)
Parse an optional attribute dictionary, adding an empty 'annotations' attribute if not specified.
static void printCircuitOpAttrs(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
static LogicalResult verifyPortSymbolUses(FModuleLike module, SymbolTableCollection &symbolTable)
static void printVerifAttrs(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
static Attribute getAttr(ArrayRef< NamedAttribute > attrs, StringRef name)
Get an attribute by name from a list of named attributes.
static void printMemOp(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
Always elide "ruw" and elide "annotations" if it exists or if it is empty.
static ParseResult parseElideEmptyName(OpAsmParser &p, NamedAttrList &resultAttrs)
static void printElideEmptyName(OpAsmPrinter &p, Operation *op, DictionaryAttr attr, ArrayRef< StringRef > extraElides={})
static ParseResult parseClassLike(OpAsmParser &parser, OperationState &result, bool hasSSAIdentifiers)
static void buildModuleWithoutAnnos(OpBuilder &builder, OperationState &result, StringAttr name, ArrayRef< PortInfo > ports)
static ModulePortInfo getPortList(ModuleTy &mod)
static InstancePath empty
FirMemory getSummary(MemOp op)
llvm::SmallVector< StringAttr > inputs
static std::optional< APInt > getInt(Value value)
Helper to convert a value to a constant integer if it is one.
static int64_t size(hw::ArrayType mType, capnp::schema::Field::Reader cField)
Returns the expected size of an array (capnp list) in 64-bit words.
Value getValue() const
Get the Value which created this location.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool hasDontTouch() const
firrtl.transforms.DontTouchAnnotation
static AnnotationSet forPort(FModuleLike op, size_t portNo)
Get an annotation set for the specified port.
ExprVisitor is a visitor for FIRRTL expression nodes.
ResultType dispatchExprVisitor(Operation *op, ExtraArgs... args)
FIRRTLBaseType getMaskType()
Return this type with all ground types replaced with UInt<1>.
int32_t getBitWidthOrSentinel()
If this is an IntType, AnalogType, or sugar type for a single bit (Clock, Reset, etc) then return the...
bool isConst()
Returns true if this is a 'const' type that can only hold compile-time constant values.
FIRRTLBaseType getAllConstDroppedType()
Return this type with a 'const' modifiers dropped.
bool isPassive() const
Return true if this is a "passive" type - one that contains no "flip" types recursively within itself...
FIRRTLBaseType getConstType(bool isConst)
Return a 'const' or non-'const' version of this type.
bool isConst()
Returns true if this is a 'const' type that can only hold compile-time constant values.
This is the common base class between SIntType and UIntType.
int32_t getWidthOrSentinel() const
Return the width of this type, or -1 if it has none specified.
static IntType get(MLIRContext *context, bool isSigned, int32_t widthOrSentinel=-1, bool isConst=false)
Return an SIntType or UIntType with the specified signedness, width, and constness.
bool hasWidth() const
Return true if this integer type has a known width.
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
int main(int argc, const char *argv[])
def connect(destination, source)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
std::string getInstanceName(mlir::func::CallOp callOp)
A helper function to get the instance name.
ClassType getInstanceTypeForClassLike(ClassLike classOp)
LogicalResult verifyTypeAgainstClassLike(ClassLike classOp, ClassType type, function_ref< InFlightDiagnostic()> emitError)
Assuming that the classOp is the source of truth, verify that the type accurately matches the signatu...
RefType getForceableResultType(bool forceable, Type type)
Return null or forceable reference result type.
IntegerAttr packAttribute(MLIRContext *context, ArrayRef< Direction > directions)
Return a IntegerAttr containing the packed representation of an array of directions.
StringRef toString(Direction direction)
SmallVector< Direction > unpackAttribute(IntegerAttr directions)
Turn a packed representation of port attributes into a vector that can be worked with.
LogicalResult validateBinaryOpArguments(ValueRange operands, ArrayRef< NamedAttribute > attrs, Location loc)
FIRRTLType inferElementwiseResult(FIRRTLType lhs, FIRRTLType rhs, std::optional< Location > loc)
LogicalResult inferReturnTypes(MLIRContext *context, std::optional< Location > loc, ValueRange operands, DictionaryAttr attrs, mlir::OpaqueProperties properties, mlir::RegionRange regions, SmallVectorImpl< Type > &results, llvm::function_ref< FIRRTLType(ValueRange, ArrayRef< NamedAttribute >, std::optional< Location >)> callback)
LogicalResult validateUnaryOpArguments(ValueRange operands, ArrayRef< NamedAttribute > attrs, Location loc)
FIRRTLType inferBitwiseResult(FIRRTLType lhs, FIRRTLType rhs, std::optional< Location > loc)
FIRRTLType inferAddSubResult(FIRRTLType lhs, FIRRTLType rhs, std::optional< Location > loc)
FIRRTLType inferComparisonResult(FIRRTLType lhs, FIRRTLType rhs, std::optional< Location > loc)
FIRRTLType inferReductionResult(FIRRTLType arg, std::optional< Location > loc)
LogicalResult validateOneOperandOneConst(ValueRange operands, ArrayRef< NamedAttribute > attrs, Location loc)
LogicalResult verifySameOperandsIntTypeKind(Operation *op)
Flow swapFlow(Flow flow)
Get a flow's reverse.
Direction
This represents the direction of a single port.
FieldRef getFieldRefFromValue(Value value, bool lookThroughCasts=false)
Get the FieldRef from a value.
bool isConstant(Operation *op)
Return true if the specified operation has a constant value.
bool areAnonymousTypesEquivalent(FIRRTLBaseType lhs, FIRRTLBaseType rhs)
Return true if anonymous types of given arguments are equivalent by pointer comparison.
constexpr bool isValidDst(Flow flow)
Flow foldFlow(Value val, Flow accumulatedFlow=Flow::Source)
Compute the flow for a Value, val, as determined by the FIRRTL specification.
bool areTypesEquivalent(FIRRTLType destType, FIRRTLType srcType, bool destOuterTypeIsConst=false, bool srcOuterTypeIsConst=false, bool requireSameWidths=false)
Returns whether the two types are equivalent.
bool hasDontTouch(Value value)
Check whether a block argument ("port") or the operation defining a value has a DontTouch annotation,...
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
bool isTypeLarger(FIRRTLBaseType dstType, FIRRTLBaseType srcType)
Returns true if the destination is at least as wide as a source.
bool containsConst(Type type)
Returns true if the type is or contains a 'const' type whose value is guaranteed to be unchanging at ...
bool isDuplexValue(Value val)
Returns true if the value results from an expression with duplex flow.
constexpr bool isValidSrc(Flow flow)
Value getModuleScopedDriver(Value val, bool lookThroughWires, bool lookThroughNodes, bool lookThroughCasts)
Return the value that drives another FIRRTL value within module scope.
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
bool isConst(Type type)
Returns true if this is a 'const' type whose value is guaranteed to be unchanging at circuit executio...
bool areTypesConstCastable(FIRRTLType destType, FIRRTLType srcType, bool srcOuterTypeIsConst=false)
Returns whether the srcType can be const-casted to the destType.
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
DeclKind getDeclarationKind(Value val)
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
::mlir::Type getFinalTypeByFieldID(Type type, uint64_t fieldID)
Operation * getReferencedModule(const HWSymbolCache *cache, Operation *instanceOp, mlir::FlatSymbolRefAttr moduleName)
Return a pointer to the referenced module operation.
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.
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
void elideImplicitSSAName(OpAsmPrinter &printer, Operation *op, DictionaryAttr attrs, SmallVectorImpl< StringRef > &elides)
Check if the name attribute in attrs matches the SSA name of the operation's first result.
bool inferImplicitSSAName(OpAsmParser &parser, NamedAttrList &attrs)
Ensure that attrs contains a name attribute by inferring its value from the SSA name of the operation...
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
StringAttr getFirMemoryName() const