405 auto getOrCreateNodeOp = [&](Value operand,
406 ImplicitLocOpBuilder &builder) -> NodeOp {
408 OpBuilder::InsertionGuard guard(builder);
409 builder.setInsertionPointAfterValue(operand);
410 SmallString<16> nameHint;
412 if (
auto *definingOp = operand.getDefiningOp()) {
413 if (
auto instanceOp = dyn_cast<InstanceOp>(definingOp)) {
414 nameHint.append(instanceOp.getName());
415 nameHint.push_back(
'_');
417 instanceOp.getPortName(cast<OpResult>(operand).getResultNumber()));
418 }
else if (
auto opName = definingOp->getAttrOfType<StringAttr>(
"name")) {
419 nameHint.append(opName);
421 nameHint.append(
"_layerCapture");
424 return NodeOp::create(builder, operand.getLoc(), operand,
425 StringRef(nameHint));
431 DenseMap<Value, Value> replacements;
432 std::function<Value(Operation *, Value)> getReplacement =
433 [&](Operation *user, Value value) -> Value {
434 auto it = replacements.find(value);
435 if (it != replacements.end())
436 return it->getSecond();
438 ImplicitLocOpBuilder localBuilder(value.getLoc(), &getContext());
441 auto layerBlockOp = user->getParentOfType<LayerBlockOp>();
442 localBuilder.setInsertionPointToStart(layerBlockOp.getBody());
449 if (type_isa<FStringType>(value.getType())) {
450 localBuilder.setInsertionPoint(user);
451 replacement = localBuilder.clone(*value.getDefiningOp())->getResult(0);
452 replacements.insert({value, replacement});
457 auto *definingOp = value.getDefiningOp();
458 if (isa_and_present<XMRRefOp>(definingOp)) {
459 replacement = localBuilder.clone(*definingOp)->getResult(0);
460 replacements.insert({value, replacement});
471 if (isa_and_present<InstanceOp, InstanceChoiceOp>(definingOp)) {
472 bool isInstanceInputPort =
473 TypeSwitch<Operation *, bool>(definingOp)
474 .Case<InstanceOp, InstanceChoiceOp>([&](
auto instOp) {
475 for (
auto [idx, result] : llvm::enumerate(instOp.getResults()))
477 return instOp.getPortDirection(idx) == Direction::In;
482 if (isInstanceInputPort) {
486 replacement = getReplacement(user, driver);
487 replacements.insert({value, replacement});
504 auto baseType = type_cast<FIRRTLBaseType>(value.getType());
505 if (baseType && baseType.getBitWidthOrSentinel() == 0) {
506 OpBuilder::InsertionGuard guard(localBuilder);
507 auto zeroUIntType = UIntType::get(localBuilder.getContext(), 0);
508 replacement = localBuilder.createOrFold<BitCastOp>(
509 value.getType(), ConstantOp::create(localBuilder, zeroUIntType,
512 hw::InnerRefAttr innerRef;
513 if (
auto *definingOp = value.getDefiningOp()) {
516 auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(definingOp);
517 if (innerSymOp && innerSymOp.getTargetResultIndex()) {
526 auto node = getOrCreateNodeOp(value, localBuilder);
529 auto newValue = node.getResult();
530 value.replaceAllUsesExcept(newValue, node);
534 auto portIdx = cast<BlockArgument>(value).getArgNumber();
536 cast<FModuleLike>(*moduleOp), portIdx,
540 hw::HierPathOp hierPathOp;
543 auto insertPoint = OpBuilder::InsertPoint(moduleOp->getBlock(),
544 Block::iterator(moduleOp));
545 llvm::sys::SmartScopedLock<true> circuitLock(*
circuitMutex);
547 localBuilder.getArrayAttr({innerRef}), localBuilder.getLoc(),
549 hierPathOp.setVisibility(SymbolTable::Visibility::Private);
552 replacement = XMRDerefOp::create(localBuilder, value.getType(),
553 hierPathOp.getSymNameAttr());
556 replacements.insert({value, replacement});
564 DenseMap<Operation *, FModuleOp> createdInstances;
568 auto opPreconditionCheck = [](Operation *op) -> LogicalResult {
570 if (isa<RefCastOp, RefDefineOp, RefResolveOp, RefSendOp, RefSubOp,
572 return op->emitOpError()
573 <<
"cannot be handled by the lower-layers pass. This should have "
574 "already been removed by the lower-xmr pass.";
593 DenseMap<Operation *, Attribute> domainMap;
594 auto getDomain = [&domainMap](Value value,
595 Attribute &domain) -> LogicalResult {
596 SmallVector<Operation *> wires;
601 if (
auto arg = dyn_cast<BlockArgument>(value)) {
602 domain = cast<FModuleLike>(arg.getOwner()->getParentOp())
603 .getDomainInfoAttrForPort(arg.getArgNumber());
608 TypeSwitch<Operation *, LogicalResult>(value.getDefiningOp())
609 .Case<WireOp>([&](WireOp op) {
610 auto it = domainMap.find(op);
611 if (it != domainMap.end()) {
612 domain = it->getSecond();
615 for (
auto *user : op->getUsers()) {
616 auto connect = dyn_cast<FConnectLike>(user);
617 if (!connect || connect.getDest() != value)
619 value = connect.getSrc();
623 emitError(value.getLoc())
624 <<
"unable to determine domain kind for source likely "
626 "violation of static-single-connect";
629 .Case<InstanceOp>([&](
auto op) {
631 op.getPortDomain(cast<OpResult>(value).getResultNumber());
634 .Case<DomainCreateAnonOp>([&](
auto op) {
635 domain = op.getDomainAttr();
638 .Default([&](
auto op) {
639 op->emitOpError() <<
"unhandled domain source in 'LowerLayers";
647 for (
auto *wire : wires)
648 domainMap[wire] = domain;
675 auto result = moduleOp.walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
676 if (failed(opPreconditionCheck(op)))
677 return WalkResult::interrupt();
680 for (
auto result : op->getResults())
684 if (
auto instance = dyn_cast<InstanceOp>(op))
685 instance.setLayers({});
687 auto layerBlock = dyn_cast<LayerBlockOp>(op);
689 return WalkResult::advance();
692 auto layer =
symbolToLayer.lookup(layerBlock.getLayerName());
694 if (layer.getConvention() == LayerConvention::Inline) {
696 return WalkResult::advance();
700 assert(layer.getConvention() == LayerConvention::Bind);
705 SmallVector<PortInfo> ports;
706 SmallVector<Value> connectValues;
711 auto createInputPort = [&](Value src, Location loc) -> LogicalResult {
713 if (failed(getDomain(src, domain)))
719 name = StringAttr::get(src.getContext(), portNs.
newName(nameHint));
721 name = StringAttr::get(src.getContext(), portNs.
newName(
"anonDomain"));
723 auto domainInfo = ArrayAttr::get(src.getContext(), {});
732 ports.push_back(port);
733 connectValues.push_back(src);
734 BlockArgument replacement =
735 layerBlock.getBody()->addArgument(port.
type, port.
loc);
736 src.replaceUsesWithIf(replacement, [&](OpOperand &use) {
737 auto *user = use.getOwner();
738 if (!layerBlock->isAncestor(user))
744 if (
auto connectLike = dyn_cast<FConnectLike>(user)) {
745 auto *destDefiningOp = connectLike.getDest().getDefiningOp();
746 return connectLike.getSrc() == src &&
747 !createdInstances.contains(destDefiningOp);
757 auto getPortLoc = [&](Value port) -> Location {
758 Location loc = UnknownLoc::get(port.getContext());
759 if (
auto *destOp = port.getDefiningOp())
760 if (
auto instOp = dyn_cast<InstanceOp>(destOp)) {
761 auto modOpIt = createdInstances.find(instOp);
762 if (modOpIt != createdInstances.end()) {
763 auto portNum = cast<OpResult>(port).getResultNumber();
764 loc = modOpIt->getSecond().getPortLocation(portNum);
772 auto createOutputPort = [&](Value src, Value dest) -> LogicalResult {
774 if (failed(getDomain(src, domain)))
780 name = StringAttr::get(src.getContext(), portNs.
newName(nameHint));
782 name = StringAttr::get(src.getContext(), portNs.
newName(
"anonDomain"));
784 auto domainInfo = ArrayAttr::get(src.getContext(), {});
793 ports.push_back(port);
794 connectValues.push_back(dest);
795 BlockArgument replacement =
796 layerBlock.getBody()->addArgument(port.type, port.loc);
797 dest.replaceUsesWithIf(replacement, [&](OpOperand &use) {
798 auto *user = use.getOwner();
799 if (!layerBlock->isAncestor(user))
802 if (
auto connectLike = dyn_cast<FConnectLike>(user))
803 return connectLike.getDest() == dest;
810 replacements.clear();
811 OpBuilder builder(moduleOp);
812 SmallVector<hw::InnerSymAttr> innerSyms;
813 SmallVector<sv::VerbatimOp> verbatims;
814 DenseSet<Operation *> spilledSubOps;
815 auto layerBlockWalkResult = layerBlock.walk([&](Operation *op) {
817 if (failed(opPreconditionCheck(op)))
818 return WalkResult::interrupt();
831 auto fixSubOp = [&](
auto subOp) {
832 auto input = subOp.getInput();
836 return WalkResult::advance();
839 if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive()) {
840 subOp.getInputMutable().assign(getReplacement(subOp, input));
841 return WalkResult::advance();
845 op->moveBefore(layerBlock);
846 spilledSubOps.insert(op);
847 return WalkResult::advance();
850 if (
auto subOp = dyn_cast<SubfieldOp>(op))
851 return fixSubOp(subOp);
853 if (
auto subOp = dyn_cast<SubindexOp>(op))
854 return fixSubOp(subOp);
856 if (
auto subOp = dyn_cast<SubaccessOp>(op)) {
857 auto input = subOp.getInput();
858 auto index = subOp.getIndex();
864 subOp.getIndexMutable().assign(getReplacement(subOp, index));
866 return WalkResult::advance();
870 if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive()) {
871 subOp.getInputMutable().assign(getReplacement(subOp, input));
873 subOp.getIndexMutable().assign(getReplacement(subOp, index));
874 return WalkResult::advance();
879 subOp->moveBefore(layerBlock);
880 spilledSubOps.insert(op);
881 return WalkResult::advance();
886 auto diag = op->emitOpError()
887 <<
"has a non-passive operand and captures a value defined "
888 "outside its enclosing bind-convention layerblock. The "
889 "'LowerLayers' pass cannot lower this as it would "
890 "create an output port on the resulting module.";
891 diag.attachNote(layerBlock.getLoc())
892 <<
"the layerblock is defined here";
893 return WalkResult::interrupt();
896 if (
auto nodeOp = dyn_cast<NodeOp>(op)) {
897 auto *definingOp = nodeOp.getInput().getDefiningOp();
899 spilledSubOps.contains(nodeOp.getInput().getDefiningOp())) {
900 op->moveBefore(layerBlock);
901 return WalkResult::advance();
909 if (
auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op))
910 if (
auto innerSym = symOp.getInnerSymAttr())
911 innerSyms.push_back(innerSym);
923 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
925 if (!createdInstances.contains(instOp))
926 return WalkResult::advance();
930 <<
" Found instance created from nested layer block:\n"
931 <<
" module: " << instOp.getModuleName() <<
"\n"
932 <<
" instance: " << instOp.getName() <<
"\n";
934 instOp->moveBefore(layerBlock);
935 return WalkResult::advance();
948 if (
auto domainDefineOp = dyn_cast<DomainDefineOp>(op)) {
949 auto src = domainDefineOp.getSrc();
950 auto dest = domainDefineOp.getDest();
954 if (srcInLayerBlock) {
956 if (destInLayerBlock)
957 return WalkResult::advance();
963 return WalkResult(createOutputPort(src, dest));
969 if (destInLayerBlock)
970 return WalkResult(createInputPort(src, domainDefineOp.getLoc()));
976 domainDefineOp->moveBefore(layerBlock);
977 return WalkResult::advance();
983 for (
size_t i = 0, e = op->getNumOperands(); i != e; ++i) {
984 auto operand = op->getOperand(i);
992 op->setOperand(i, getReplacement(op, operand));
995 if (
auto verbatim = dyn_cast<sv::VerbatimOp>(op))
996 verbatims.push_back(verbatim);
998 return WalkResult::advance();
1001 if (layerBlockWalkResult.wasInterrupted())
1002 return WalkResult::interrupt();
1009 if (llvm::all_of(layerBlock.getRegion().getBlocks(),
1010 [](
auto &a) { return a.empty(); })) {
1011 assert(verbatims.empty());
1013 return WalkResult::advance();
1017 FModuleOp newModule =
buildNewModule(builder, layerBlock, ports);
1018 newModule.getBody().takeBody(layerBlock.getRegion());
1019 SymbolTable::setSymbolVisibility(newModule,
1020 SymbolTable::Visibility::Private);
1023 llvm::dbgs() <<
" New Module: "
1025 llvm::dbgs() <<
" ports:\n";
1026 for (
size_t i = 0, e = ports.size(); i != e; ++i) {
1027 auto port = ports[i];
1028 auto value = connectValues[i];
1029 llvm::dbgs() <<
" - name: " << port.getName() <<
"\n"
1030 <<
" type: " << port.type <<
"\n"
1031 <<
" direction: " << port.direction <<
"\n"
1032 <<
" value: " << value <<
"\n";
1042 builder.setInsertionPointAfter(layerBlock);
1045 hw::InnerSymAttr::get(builder.getStringAttr(ns.
newName(instanceName)));
1047 auto instanceOp = InstanceOp::create(
1048 builder, layerBlock.getLoc(), newModule,
1050 instanceName, NameKindEnum::DroppableName,
1051 ArrayRef<Attribute>{},
1052 ArrayRef<Attribute>{},
false,
1054 for (
auto [lhs, rhs] : llvm::zip(instanceOp.getResults(), connectValues))
1055 if (instanceOp.getPortDirection(lhs.getResultNumber()) == Direction::In)
1056 DomainDefineOp::create(builder, builder.getUnknownLoc(), lhs, rhs);
1058 DomainDefineOp::create(builder, builder.getUnknownLoc(), rhs, lhs);
1062 layerBlock.getLayerName());
1063 instanceOp->setAttr(
"output_file", outputFile);
1065 createdInstances.try_emplace(instanceOp, newModule);
1069 auto builder = OpBuilder::atBlockEnd(
bindFiles[moduleOp][layer].body);
1070 BindOp::create(builder, layerBlock.getLoc(), moduleOp.getModuleNameAttr(),
1071 instanceOp.getInnerSymAttr().getSymName());
1074 LLVM_DEBUG(llvm::dbgs() <<
" moved inner refs:\n");
1075 for (hw::InnerSymAttr innerSym : innerSyms) {
1076 auto oldInnerRef = hw::InnerRefAttr::get(moduleOp.getModuleNameAttr(),
1077 innerSym.getSymName());
1078 auto splice = std::make_pair(instanceOp.getInnerSymAttr(),
1079 newModule.getModuleNameAttr());
1080 innerRefMap.insert({oldInnerRef, splice});
1081 LLVM_DEBUG(llvm::dbgs() <<
" - ref: " << oldInnerRef <<
"\n"
1082 <<
" splice: " << splice.first <<
", "
1083 << splice.second <<
"\n";);
1087 if (!verbatims.empty()) {
1088 mlir::AttrTypeReplacer replacer;
1089 replacer.addReplacement(
1090 [&innerRefMap](hw::InnerRefAttr ref) -> std::optional<Attribute> {
1091 auto it = innerRefMap.find(ref);
1092 if (it != innerRefMap.end())
1093 return hw::InnerRefAttr::get(it->second.second, ref.getName());
1094 return std::nullopt;
1096 for (
auto verbatim : verbatims)
1097 replacer.replaceElementsIn(verbatim);
1102 return WalkResult::advance();
1104 return success(!result.wasInterrupted());
1146 SymbolRefAttr layerName, LayerOp layer) {
1147 assert(layer.getConvention() == LayerConvention::Bind);
1148 auto module = node->getModule<FModuleOp>();
1149 auto loc =
module.getLoc();
1153 auto macroSymbol = ns.
newName(macroName);
1154 auto macroNameAttr = StringAttr::get(&getContext(), macroName);
1155 auto macroSymbolAttr = StringAttr::get(&getContext(), macroSymbol);
1156 auto macroSymbolRefAttr = FlatSymbolRefAttr::get(macroSymbolAttr);
1163 auto dir = layer->getAttrOfType<hw::OutputFileAttr>(
"output_file");
1164 StringAttr filename = StringAttr::get(&getContext(), bindFileName);
1167 path = StringAttr::get(&getContext(),
1168 Twine(dir.getDirectory()) + bindFileName);
1173 sv::MacroDeclOp::create(b, loc, macroSymbolAttr, ArrayAttr{}, macroNameAttr);
1176 auto bindFile = emit::FileOp::create(b, loc, path);
1177 OpBuilder::InsertionGuard _(b);
1178 b.setInsertionPointToEnd(bindFile.getBody());
1181 auto includeGuard = sv::IfDefOp::create(b, loc, macroSymbolRefAttr);
1182 b.createBlock(&includeGuard.getElseRegion());
1185 sv::MacroDefOp::create(b, loc, macroSymbolRefAttr);
1188 auto parent = layer->getParentOfType<LayerOp>();
1192 if (parent.getConvention() == LayerConvention::Bind) {
1193 auto target =
bindFiles[module][parent].filename;
1194 sv::IncludeOp::create(b, loc, IncludeStyle::Local, target);
1200 if (parent.getConvention() == LayerConvention::Inline) {
1201 auto parentMacroSymbolRefAttr =
macroNames[parent];
1202 auto parentGuard = sv::IfDefOp::create(b, loc, parentMacroSymbolRefAttr);
1203 OpBuilder::InsertionGuard guard(b);
1204 b.createBlock(&parentGuard.getElseRegion());
1205 auto message = StringAttr::get(&getContext(),
1206 Twine(parent.getName()) +
" not enabled");
1207 sv::MacroErrorOp::create(b, loc, message);
1208 parent = parent->getParentOfType<LayerOp>();
1213 llvm_unreachable(
"unknown layer convention");
1218 SmallPtrSet<Operation *, 8> seen;
1219 for (
auto *record : *node) {
1220 auto *child = record->getTarget()->getModule().getOperation();
1221 if (!std::get<bool>(seen.insert(child)))
1224 auto lookup = files.find(layer);
1225 if (lookup != files.end())
1226 sv::IncludeOp::create(b, loc, IncludeStyle::Local,
1227 lookup->second.filename);
1232 info.filename = filename;
1233 info.body = includeGuard.getElseBlock();