408 auto getOrCreateNodeOp = [&](Value operand,
409 ImplicitLocOpBuilder &builder) -> NodeOp {
411 OpBuilder::InsertionGuard guard(builder);
412 builder.setInsertionPointAfterValue(operand);
413 SmallString<16> nameHint;
415 if (
auto *definingOp = operand.getDefiningOp()) {
416 if (
auto instanceOp = dyn_cast<InstanceOp>(definingOp)) {
417 nameHint.append(instanceOp.getName());
418 nameHint.push_back(
'_');
420 instanceOp.getPortName(cast<OpResult>(operand).getResultNumber()));
421 }
else if (
auto opName = definingOp->getAttrOfType<StringAttr>(
"name")) {
422 nameHint.append(opName);
424 nameHint.append(
"_layerCapture");
427 return NodeOp::create(builder, operand.getLoc(), operand,
428 StringRef(nameHint));
434 DenseMap<Value, Value> replacements;
435 std::function<Value(Operation *, Value)> getReplacement =
436 [&](Operation *user, Value value) -> Value {
437 auto it = replacements.find(value);
438 if (it != replacements.end())
439 return it->getSecond();
441 ImplicitLocOpBuilder localBuilder(value.getLoc(), &getContext());
444 auto layerBlockOp = user->getParentOfType<LayerBlockOp>();
445 localBuilder.setInsertionPointToStart(layerBlockOp.getBody());
452 if (type_isa<FStringType>(value.getType())) {
453 localBuilder.setInsertionPoint(user);
454 replacement = localBuilder.clone(*value.getDefiningOp())->getResult(0);
455 replacements.insert({value, replacement});
460 auto *definingOp = value.getDefiningOp();
461 if (isa_and_present<XMRRefOp>(definingOp)) {
462 replacement = localBuilder.clone(*definingOp)->getResult(0);
463 replacements.insert({value, replacement});
474 if (isa_and_present<InstanceOp, InstanceChoiceOp>(definingOp)) {
475 bool isInstanceInputPort =
476 TypeSwitch<Operation *, bool>(definingOp)
477 .Case<InstanceOp, InstanceChoiceOp>([&](
auto instOp) {
478 for (
auto [idx, result] : llvm::enumerate(instOp.getResults()))
480 return instOp.getPortDirection(idx) == Direction::In;
485 if (isInstanceInputPort) {
489 replacement = getReplacement(user, driver);
490 replacements.insert({value, replacement});
507 auto baseType = type_cast<FIRRTLBaseType>(value.getType());
508 if (baseType && baseType.getBitWidthOrSentinel() == 0) {
509 OpBuilder::InsertionGuard guard(localBuilder);
510 auto zeroUIntType = UIntType::get(localBuilder.getContext(), 0);
511 replacement = localBuilder.createOrFold<BitCastOp>(
512 value.getType(), ConstantOp::create(localBuilder, zeroUIntType,
515 hw::InnerRefAttr innerRef;
516 if (
auto *definingOp = value.getDefiningOp()) {
519 auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(definingOp);
520 if (innerSymOp && innerSymOp.getTargetResultIndex()) {
529 auto node = getOrCreateNodeOp(value, localBuilder);
532 auto newValue = node.getResult();
533 value.replaceAllUsesExcept(newValue, node);
537 auto portIdx = cast<BlockArgument>(value).getArgNumber();
539 cast<FModuleLike>(*moduleOp), portIdx,
543 hw::HierPathOp hierPathOp;
546 auto insertPoint = OpBuilder::InsertPoint(moduleOp->getBlock(),
547 Block::iterator(moduleOp));
548 llvm::sys::SmartScopedLock<true> circuitLock(*
circuitMutex);
550 localBuilder.getArrayAttr({innerRef}), localBuilder.getLoc(),
552 hierPathOp.setVisibility(SymbolTable::Visibility::Private);
555 replacement = XMRDerefOp::create(localBuilder, value.getType(),
556 hierPathOp.getSymNameAttr());
559 replacements.insert({value, replacement});
567 DenseMap<Operation *, FModuleOp> createdInstances;
571 auto opPreconditionCheck = [](Operation *op) -> LogicalResult {
573 if (isa<RefCastOp, RefDefineOp, RefResolveOp, RefSendOp, RefSubOp,
575 return op->emitOpError()
576 <<
"cannot be handled by the lower-layers pass. This should have "
577 "already been removed by the lower-xmr pass.";
596 DenseMap<Operation *, Attribute> domainMap;
597 auto getDomain = [&domainMap](Value value,
598 Attribute &domain) -> LogicalResult {
599 SmallVector<Operation *> wires;
604 if (
auto arg = dyn_cast<BlockArgument>(value)) {
605 domain = cast<FModuleLike>(arg.getOwner()->getParentOp())
606 .getDomainInfoAttrForPort(arg.getArgNumber());
611 TypeSwitch<Operation *, LogicalResult>(value.getDefiningOp())
612 .Case<WireOp>([&](WireOp op) {
613 auto it = domainMap.find(op);
614 if (it != domainMap.end()) {
615 domain = it->getSecond();
618 for (
auto *user : op->getUsers()) {
619 auto connect = dyn_cast<FConnectLike>(user);
620 if (!connect || connect.getDest() != value)
622 value = connect.getSrc();
626 emitError(value.getLoc())
627 <<
"unable to determine domain kind for source likely "
629 "violation of static-single-connect";
632 .Case<InstanceOp>([&](
auto op) {
634 op.getPortDomain(cast<OpResult>(value).getResultNumber());
637 .Case<DomainCreateAnonOp, DomainCreateOp>([&](
auto op) {
638 domain = op.getDomainAttr();
641 .Default([&](
auto op) {
642 op->emitOpError() <<
"unhandled domain source in 'LowerLayers";
650 for (
auto *wire : wires)
651 domainMap[wire] = domain;
678 auto result = moduleOp.walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
679 if (failed(opPreconditionCheck(op)))
680 return WalkResult::interrupt();
683 for (
auto result : op->getResults())
687 if (
auto instance = dyn_cast<InstanceOp>(op))
688 instance.setLayers({});
690 auto layerBlock = dyn_cast<LayerBlockOp>(op);
692 return WalkResult::advance();
695 auto layer =
symbolToLayer.lookup(layerBlock.getLayerName());
697 if (layer.getConvention() == LayerConvention::Inline) {
699 return WalkResult::advance();
703 assert(layer.getConvention() == LayerConvention::Bind);
708 SmallVector<PortInfo> ports;
709 SmallVector<Value> connectValues;
716 DenseMap<Value, BlockArgument> domainInputPorts;
717 auto getOrCreateDomainInputPort =
718 [&](Value src, Location loc) -> FailureOr<BlockArgument> {
719 if (
auto it = domainInputPorts.find(src); it != domainInputPorts.end())
720 return it->getSecond();
723 if (failed(getDomain(src, domain)))
729 name = StringAttr::get(src.getContext(), portNs.
newName(nameHint));
731 name = StringAttr::get(src.getContext(), portNs.
newName(
"anonDomain"));
733 auto domainInfo = ArrayAttr::get(src.getContext(), {});
742 ports.push_back(port);
743 connectValues.push_back(src);
744 BlockArgument replacement =
745 layerBlock.getBody()->addArgument(port.
type, port.
loc);
746 domainInputPorts[src] = replacement;
753 auto createInputPort = [&](Value src, Location loc) -> LogicalResult {
754 auto replacement = getOrCreateDomainInputPort(src, loc);
755 if (failed(replacement))
757 src.replaceUsesWithIf(*replacement, [&](OpOperand &use) {
758 auto *user = use.getOwner();
759 if (!layerBlock->isAncestor(user))
765 if (
auto connectLike = dyn_cast<FConnectLike>(user)) {
766 auto *destDefiningOp = connectLike.getDest().getDefiningOp();
767 return connectLike.getSrc() == src &&
768 !createdInstances.contains(destDefiningOp);
778 auto getPortLoc = [&](Value port) -> Location {
779 Location loc = UnknownLoc::get(port.getContext());
780 if (
auto *destOp = port.getDefiningOp())
781 if (
auto instOp = dyn_cast<InstanceOp>(destOp)) {
782 auto modOpIt = createdInstances.find(instOp);
783 if (modOpIt != createdInstances.end()) {
784 auto portNum = cast<OpResult>(port).getResultNumber();
785 loc = modOpIt->getSecond().getPortLocation(portNum);
793 auto createOutputPort = [&](Value src, Value dest) -> LogicalResult {
795 if (failed(getDomain(src, domain)))
801 name = StringAttr::get(src.getContext(), portNs.
newName(nameHint));
803 name = StringAttr::get(src.getContext(), portNs.
newName(
"anonDomain"));
805 auto domainInfo = ArrayAttr::get(src.getContext(), {});
814 ports.push_back(port);
815 connectValues.push_back(dest);
816 BlockArgument replacement =
817 layerBlock.getBody()->addArgument(port.type, port.loc);
818 dest.replaceUsesWithIf(replacement, [&](OpOperand &use) {
819 auto *user = use.getOwner();
820 if (!layerBlock->isAncestor(user))
823 if (
auto connectLike = dyn_cast<FConnectLike>(user))
824 return connectLike.getDest() == dest;
831 replacements.clear();
832 OpBuilder builder(moduleOp);
833 SmallVector<hw::InnerSymAttr> innerSyms;
834 SmallVector<sv::VerbatimOp> verbatims;
835 DenseSet<Operation *> spilledSubOps;
836 auto layerBlockWalkResult = layerBlock.walk([&](Operation *op) {
838 if (failed(opPreconditionCheck(op)))
839 return WalkResult::interrupt();
852 auto fixSubOp = [&](
auto subOp) {
853 auto input = subOp.getInput();
857 return WalkResult::advance();
860 if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive()) {
861 subOp.getInputMutable().assign(getReplacement(subOp, input));
862 return WalkResult::advance();
866 op->moveBefore(layerBlock);
867 spilledSubOps.insert(op);
868 return WalkResult::advance();
871 if (
auto subOp = dyn_cast<SubfieldOp>(op))
872 return fixSubOp(subOp);
874 if (
auto subOp = dyn_cast<SubindexOp>(op))
875 return fixSubOp(subOp);
877 if (
auto subOp = dyn_cast<SubaccessOp>(op)) {
878 auto input = subOp.getInput();
879 auto index = subOp.getIndex();
885 subOp.getIndexMutable().assign(getReplacement(subOp, index));
887 return WalkResult::advance();
891 if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive()) {
892 subOp.getInputMutable().assign(getReplacement(subOp, input));
894 subOp.getIndexMutable().assign(getReplacement(subOp, index));
895 return WalkResult::advance();
900 subOp->moveBefore(layerBlock);
901 spilledSubOps.insert(op);
902 return WalkResult::advance();
907 auto diag = op->emitOpError()
908 <<
"has a non-passive operand and captures a value defined "
909 "outside its enclosing bind-convention layerblock. The "
910 "'LowerLayers' pass cannot lower this as it would "
911 "create an output port on the resulting module.";
912 diag.attachNote(layerBlock.getLoc())
913 <<
"the layerblock is defined here";
914 return WalkResult::interrupt();
917 if (
auto nodeOp = dyn_cast<NodeOp>(op)) {
918 auto *definingOp = nodeOp.getInput().getDefiningOp();
920 spilledSubOps.contains(nodeOp.getInput().getDefiningOp())) {
921 op->moveBefore(layerBlock);
922 return WalkResult::advance();
930 if (
auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op))
931 if (
auto innerSym = symOp.getInnerSymAttr())
932 innerSyms.push_back(innerSym);
944 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
946 if (!createdInstances.contains(instOp))
947 return WalkResult::advance();
951 <<
" Found instance created from nested layer block:\n"
952 <<
" module: " << instOp.getModuleName() <<
"\n"
953 <<
" instance: " << instOp.getName() <<
"\n";
955 instOp->moveBefore(layerBlock);
956 return WalkResult::advance();
969 if (
auto domainDefineOp = dyn_cast<DomainDefineOp>(op)) {
970 auto src = domainDefineOp.getSrc();
971 auto dest = domainDefineOp.getDest();
975 if (srcInLayerBlock) {
977 if (destInLayerBlock)
978 return WalkResult::advance();
984 return WalkResult(createOutputPort(src, dest));
990 if (destInLayerBlock)
991 return WalkResult(createInputPort(src, domainDefineOp.getLoc()));
997 domainDefineOp->moveBefore(layerBlock);
998 return WalkResult::advance();
1004 for (
size_t i = 0, e = op->getNumOperands(); i != e; ++i) {
1005 auto operand = op->getOperand(i);
1015 if (type_isa<DomainType>(operand.getType())) {
1016 auto replacement = getOrCreateDomainInputPort(operand, op->getLoc());
1017 if (failed(replacement))
1018 return WalkResult::interrupt();
1019 op->setOperand(i, *replacement);
1023 op->setOperand(i, getReplacement(op, operand));
1026 if (
auto verbatim = dyn_cast<sv::VerbatimOp>(op))
1027 verbatims.push_back(verbatim);
1029 return WalkResult::advance();
1032 if (layerBlockWalkResult.wasInterrupted())
1033 return WalkResult::interrupt();
1040 if (llvm::all_of(layerBlock.getRegion().getBlocks(),
1041 [](
auto &a) { return a.empty(); })) {
1042 assert(verbatims.empty());
1044 return WalkResult::advance();
1048 FModuleOp newModule =
buildNewModule(builder, layerBlock, ports);
1049 newModule.getBody().takeBody(layerBlock.getRegion());
1050 SymbolTable::setSymbolVisibility(newModule,
1051 SymbolTable::Visibility::Private);
1054 llvm::dbgs() <<
" New Module: "
1056 llvm::dbgs() <<
" ports:\n";
1057 for (
size_t i = 0, e = ports.size(); i != e; ++i) {
1058 auto port = ports[i];
1059 auto value = connectValues[i];
1060 llvm::dbgs() <<
" - name: " << port.getName() <<
"\n"
1061 <<
" type: " << port.type <<
"\n"
1062 <<
" direction: " << port.direction <<
"\n"
1063 <<
" value: " << value <<
"\n";
1073 builder.setInsertionPointAfter(layerBlock);
1076 hw::InnerSymAttr::get(builder.getStringAttr(ns.
newName(instanceName)));
1078 auto instanceOp = InstanceOp::create(
1079 builder, layerBlock.getLoc(), newModule,
1081 instanceName, NameKindEnum::DroppableName,
1082 ArrayRef<Attribute>{},
1083 ArrayRef<Attribute>{},
false,
1085 for (
auto [lhs, rhs] : llvm::zip(instanceOp.getResults(), connectValues))
1086 if (instanceOp.getPortDirection(lhs.getResultNumber()) == Direction::In)
1087 DomainDefineOp::create(builder, builder.getUnknownLoc(), lhs, rhs);
1089 DomainDefineOp::create(builder, builder.getUnknownLoc(), rhs, lhs);
1093 layerBlock.getLayerName());
1094 instanceOp->setAttr(
"output_file", outputFile);
1096 createdInstances.try_emplace(instanceOp, newModule);
1100 auto builder = OpBuilder::atBlockEnd(
bindFiles[moduleOp][layer].body);
1101 BindOp::create(builder, layerBlock.getLoc(), moduleOp.getModuleNameAttr(),
1102 instanceOp.getInnerSymAttr().getSymName());
1105 LLVM_DEBUG(llvm::dbgs() <<
" moved inner refs:\n");
1106 for (hw::InnerSymAttr innerSym : innerSyms) {
1107 auto oldInnerRef = hw::InnerRefAttr::get(moduleOp.getModuleNameAttr(),
1108 innerSym.getSymName());
1109 auto splice = std::make_pair(instanceOp.getInnerSymAttr(),
1110 newModule.getModuleNameAttr());
1111 innerRefMap.insert({oldInnerRef, splice});
1112 LLVM_DEBUG(llvm::dbgs() <<
" - ref: " << oldInnerRef <<
"\n"
1113 <<
" splice: " << splice.first <<
", "
1114 << splice.second <<
"\n";);
1118 if (!verbatims.empty()) {
1119 mlir::AttrTypeReplacer replacer;
1120 replacer.addReplacement(
1121 [&innerRefMap](hw::InnerRefAttr ref) -> std::optional<Attribute> {
1122 auto it = innerRefMap.find(ref);
1123 if (it != innerRefMap.end())
1124 return hw::InnerRefAttr::get(it->second.second, ref.getName());
1125 return std::nullopt;
1127 for (
auto verbatim : verbatims)
1128 replacer.replaceElementsIn(verbatim);
1133 return WalkResult::advance();
1135 return success(!result.wasInterrupted());
1177 SymbolRefAttr layerName, LayerOp layer,
1179 assert(layer.getConvention() == LayerConvention::Bind);
1180 auto module = node->getModule<FModuleOp>();
1181 auto loc =
module.getLoc();
1185 auto macroSymbol = ns.
newName(macroName);
1186 auto macroNameAttr = StringAttr::get(&getContext(), macroName);
1187 auto macroSymbolAttr = StringAttr::get(&getContext(), macroSymbol);
1188 auto macroSymbolRefAttr = FlatSymbolRefAttr::get(macroSymbolAttr);
1195 auto dir = layer->getAttrOfType<hw::OutputFileAttr>(
"output_file");
1196 StringAttr filename = StringAttr::get(&getContext(), bindFileName);
1199 path = StringAttr::get(&getContext(),
1200 Twine(dir.getDirectory()) + bindFileName);
1205 sv::MacroDeclOp::create(b, loc, macroSymbolAttr, ArrayAttr{}, macroNameAttr);
1208 auto bindFile = emit::FileOp::create(b, loc, path);
1209 OpBuilder::InsertionGuard _(b);
1210 b.setInsertionPointToEnd(bindFile.getBody());
1213 auto includeGuard = sv::IfDefOp::create(b, loc, macroSymbolRefAttr);
1214 b.createBlock(&includeGuard.getElseRegion());
1217 sv::MacroDefOp::create(b, loc, macroSymbolRefAttr);
1220 auto parent = layer->getParentOfType<LayerOp>();
1224 if (parent.getConvention() == LayerConvention::Bind) {
1225 auto target =
bindFiles[module][parent].filename;
1226 sv::IncludeOp::create(b, loc, IncludeStyle::Local, target);
1232 if (parent.getConvention() == LayerConvention::Inline) {
1233 auto parentMacroSymbolRefAttr =
macroNames[parent];
1234 auto parentGuard = sv::IfDefOp::create(b, loc, parentMacroSymbolRefAttr);
1235 OpBuilder::InsertionGuard guard(b);
1236 b.createBlock(&parentGuard.getElseRegion());
1237 auto message = StringAttr::get(&getContext(),
1238 Twine(parent.getName()) +
" not enabled");
1239 sv::MacroErrorOp::create(b, loc, message);
1240 parent = parent->getParentOfType<LayerOp>();
1245 llvm_unreachable(
"unknown layer convention");
1250 SmallPtrSet<Operation *, 8> seen;
1251 for (
auto *record : *node) {
1252 auto *child = record->getTarget()->getModule().getOperation();
1253 if (!std::get<bool>(seen.insert(child)))
1256 auto lookup = files.find(layer);
1257 if (lookup == files.end() || !lookup->second.effectful)
1259 sv::IncludeOp::create(b, loc, IncludeStyle::Local, lookup->second.filename);
1264 info.filename = filename;
1265 info.body = includeGuard.getElseBlock();
1266 info.effectful = effectful;