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>([&](
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;
714 auto createInputPort = [&](Value src, Location loc) -> LogicalResult {
716 if (failed(getDomain(src, domain)))
722 name = StringAttr::get(src.getContext(), portNs.
newName(nameHint));
724 name = StringAttr::get(src.getContext(), portNs.
newName(
"anonDomain"));
726 auto domainInfo = ArrayAttr::get(src.getContext(), {});
735 ports.push_back(port);
736 connectValues.push_back(src);
737 BlockArgument replacement =
738 layerBlock.getBody()->addArgument(port.
type, port.
loc);
739 src.replaceUsesWithIf(replacement, [&](OpOperand &use) {
740 auto *user = use.getOwner();
741 if (!layerBlock->isAncestor(user))
747 if (
auto connectLike = dyn_cast<FConnectLike>(user)) {
748 auto *destDefiningOp = connectLike.getDest().getDefiningOp();
749 return connectLike.getSrc() == src &&
750 !createdInstances.contains(destDefiningOp);
760 auto getPortLoc = [&](Value port) -> Location {
761 Location loc = UnknownLoc::get(port.getContext());
762 if (
auto *destOp = port.getDefiningOp())
763 if (
auto instOp = dyn_cast<InstanceOp>(destOp)) {
764 auto modOpIt = createdInstances.find(instOp);
765 if (modOpIt != createdInstances.end()) {
766 auto portNum = cast<OpResult>(port).getResultNumber();
767 loc = modOpIt->getSecond().getPortLocation(portNum);
775 auto createOutputPort = [&](Value src, Value dest) -> LogicalResult {
777 if (failed(getDomain(src, domain)))
783 name = StringAttr::get(src.getContext(), portNs.
newName(nameHint));
785 name = StringAttr::get(src.getContext(), portNs.
newName(
"anonDomain"));
787 auto domainInfo = ArrayAttr::get(src.getContext(), {});
796 ports.push_back(port);
797 connectValues.push_back(dest);
798 BlockArgument replacement =
799 layerBlock.getBody()->addArgument(port.type, port.loc);
800 dest.replaceUsesWithIf(replacement, [&](OpOperand &use) {
801 auto *user = use.getOwner();
802 if (!layerBlock->isAncestor(user))
805 if (
auto connectLike = dyn_cast<FConnectLike>(user))
806 return connectLike.getDest() == dest;
813 replacements.clear();
814 OpBuilder builder(moduleOp);
815 SmallVector<hw::InnerSymAttr> innerSyms;
816 SmallVector<sv::VerbatimOp> verbatims;
817 DenseSet<Operation *> spilledSubOps;
818 auto layerBlockWalkResult = layerBlock.walk([&](Operation *op) {
820 if (failed(opPreconditionCheck(op)))
821 return WalkResult::interrupt();
834 auto fixSubOp = [&](
auto subOp) {
835 auto input = subOp.getInput();
839 return WalkResult::advance();
842 if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive()) {
843 subOp.getInputMutable().assign(getReplacement(subOp, input));
844 return WalkResult::advance();
848 op->moveBefore(layerBlock);
849 spilledSubOps.insert(op);
850 return WalkResult::advance();
853 if (
auto subOp = dyn_cast<SubfieldOp>(op))
854 return fixSubOp(subOp);
856 if (
auto subOp = dyn_cast<SubindexOp>(op))
857 return fixSubOp(subOp);
859 if (
auto subOp = dyn_cast<SubaccessOp>(op)) {
860 auto input = subOp.getInput();
861 auto index = subOp.getIndex();
867 subOp.getIndexMutable().assign(getReplacement(subOp, index));
869 return WalkResult::advance();
873 if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive()) {
874 subOp.getInputMutable().assign(getReplacement(subOp, input));
876 subOp.getIndexMutable().assign(getReplacement(subOp, index));
877 return WalkResult::advance();
882 subOp->moveBefore(layerBlock);
883 spilledSubOps.insert(op);
884 return WalkResult::advance();
889 auto diag = op->emitOpError()
890 <<
"has a non-passive operand and captures a value defined "
891 "outside its enclosing bind-convention layerblock. The "
892 "'LowerLayers' pass cannot lower this as it would "
893 "create an output port on the resulting module.";
894 diag.attachNote(layerBlock.getLoc())
895 <<
"the layerblock is defined here";
896 return WalkResult::interrupt();
899 if (
auto nodeOp = dyn_cast<NodeOp>(op)) {
900 auto *definingOp = nodeOp.getInput().getDefiningOp();
902 spilledSubOps.contains(nodeOp.getInput().getDefiningOp())) {
903 op->moveBefore(layerBlock);
904 return WalkResult::advance();
912 if (
auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op))
913 if (
auto innerSym = symOp.getInnerSymAttr())
914 innerSyms.push_back(innerSym);
926 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
928 if (!createdInstances.contains(instOp))
929 return WalkResult::advance();
933 <<
" Found instance created from nested layer block:\n"
934 <<
" module: " << instOp.getModuleName() <<
"\n"
935 <<
" instance: " << instOp.getName() <<
"\n";
937 instOp->moveBefore(layerBlock);
938 return WalkResult::advance();
951 if (
auto domainDefineOp = dyn_cast<DomainDefineOp>(op)) {
952 auto src = domainDefineOp.getSrc();
953 auto dest = domainDefineOp.getDest();
957 if (srcInLayerBlock) {
959 if (destInLayerBlock)
960 return WalkResult::advance();
966 return WalkResult(createOutputPort(src, dest));
972 if (destInLayerBlock)
973 return WalkResult(createInputPort(src, domainDefineOp.getLoc()));
979 domainDefineOp->moveBefore(layerBlock);
980 return WalkResult::advance();
986 for (
size_t i = 0, e = op->getNumOperands(); i != e; ++i) {
987 auto operand = op->getOperand(i);
995 op->setOperand(i, getReplacement(op, operand));
998 if (
auto verbatim = dyn_cast<sv::VerbatimOp>(op))
999 verbatims.push_back(verbatim);
1001 return WalkResult::advance();
1004 if (layerBlockWalkResult.wasInterrupted())
1005 return WalkResult::interrupt();
1012 if (llvm::all_of(layerBlock.getRegion().getBlocks(),
1013 [](
auto &a) { return a.empty(); })) {
1014 assert(verbatims.empty());
1016 return WalkResult::advance();
1020 FModuleOp newModule =
buildNewModule(builder, layerBlock, ports);
1021 newModule.getBody().takeBody(layerBlock.getRegion());
1022 SymbolTable::setSymbolVisibility(newModule,
1023 SymbolTable::Visibility::Private);
1026 llvm::dbgs() <<
" New Module: "
1028 llvm::dbgs() <<
" ports:\n";
1029 for (
size_t i = 0, e = ports.size(); i != e; ++i) {
1030 auto port = ports[i];
1031 auto value = connectValues[i];
1032 llvm::dbgs() <<
" - name: " << port.getName() <<
"\n"
1033 <<
" type: " << port.type <<
"\n"
1034 <<
" direction: " << port.direction <<
"\n"
1035 <<
" value: " << value <<
"\n";
1045 builder.setInsertionPointAfter(layerBlock);
1048 hw::InnerSymAttr::get(builder.getStringAttr(ns.
newName(instanceName)));
1050 auto instanceOp = InstanceOp::create(
1051 builder, layerBlock.getLoc(), newModule,
1053 instanceName, NameKindEnum::DroppableName,
1054 ArrayRef<Attribute>{},
1055 ArrayRef<Attribute>{},
false,
1057 for (
auto [lhs, rhs] : llvm::zip(instanceOp.getResults(), connectValues))
1058 if (instanceOp.getPortDirection(lhs.getResultNumber()) == Direction::In)
1059 DomainDefineOp::create(builder, builder.getUnknownLoc(), lhs, rhs);
1061 DomainDefineOp::create(builder, builder.getUnknownLoc(), rhs, lhs);
1065 layerBlock.getLayerName());
1066 instanceOp->setAttr(
"output_file", outputFile);
1068 createdInstances.try_emplace(instanceOp, newModule);
1072 auto builder = OpBuilder::atBlockEnd(
bindFiles[moduleOp][layer].body);
1073 BindOp::create(builder, layerBlock.getLoc(), moduleOp.getModuleNameAttr(),
1074 instanceOp.getInnerSymAttr().getSymName());
1077 LLVM_DEBUG(llvm::dbgs() <<
" moved inner refs:\n");
1078 for (hw::InnerSymAttr innerSym : innerSyms) {
1079 auto oldInnerRef = hw::InnerRefAttr::get(moduleOp.getModuleNameAttr(),
1080 innerSym.getSymName());
1081 auto splice = std::make_pair(instanceOp.getInnerSymAttr(),
1082 newModule.getModuleNameAttr());
1083 innerRefMap.insert({oldInnerRef, splice});
1084 LLVM_DEBUG(llvm::dbgs() <<
" - ref: " << oldInnerRef <<
"\n"
1085 <<
" splice: " << splice.first <<
", "
1086 << splice.second <<
"\n";);
1090 if (!verbatims.empty()) {
1091 mlir::AttrTypeReplacer replacer;
1092 replacer.addReplacement(
1093 [&innerRefMap](hw::InnerRefAttr ref) -> std::optional<Attribute> {
1094 auto it = innerRefMap.find(ref);
1095 if (it != innerRefMap.end())
1096 return hw::InnerRefAttr::get(it->second.second, ref.getName());
1097 return std::nullopt;
1099 for (
auto verbatim : verbatims)
1100 replacer.replaceElementsIn(verbatim);
1105 return WalkResult::advance();
1107 return success(!result.wasInterrupted());
1149 SymbolRefAttr layerName, LayerOp layer,
1151 assert(layer.getConvention() == LayerConvention::Bind);
1152 auto module = node->getModule<FModuleOp>();
1153 auto loc =
module.getLoc();
1157 auto macroSymbol = ns.
newName(macroName);
1158 auto macroNameAttr = StringAttr::get(&getContext(), macroName);
1159 auto macroSymbolAttr = StringAttr::get(&getContext(), macroSymbol);
1160 auto macroSymbolRefAttr = FlatSymbolRefAttr::get(macroSymbolAttr);
1167 auto dir = layer->getAttrOfType<hw::OutputFileAttr>(
"output_file");
1168 StringAttr filename = StringAttr::get(&getContext(), bindFileName);
1171 path = StringAttr::get(&getContext(),
1172 Twine(dir.getDirectory()) + bindFileName);
1177 sv::MacroDeclOp::create(b, loc, macroSymbolAttr, ArrayAttr{}, macroNameAttr);
1180 auto bindFile = emit::FileOp::create(b, loc, path);
1181 OpBuilder::InsertionGuard _(b);
1182 b.setInsertionPointToEnd(bindFile.getBody());
1185 auto includeGuard = sv::IfDefOp::create(b, loc, macroSymbolRefAttr);
1186 b.createBlock(&includeGuard.getElseRegion());
1189 sv::MacroDefOp::create(b, loc, macroSymbolRefAttr);
1192 auto parent = layer->getParentOfType<LayerOp>();
1196 if (parent.getConvention() == LayerConvention::Bind) {
1197 auto target =
bindFiles[module][parent].filename;
1198 sv::IncludeOp::create(b, loc, IncludeStyle::Local, target);
1204 if (parent.getConvention() == LayerConvention::Inline) {
1205 auto parentMacroSymbolRefAttr =
macroNames[parent];
1206 auto parentGuard = sv::IfDefOp::create(b, loc, parentMacroSymbolRefAttr);
1207 OpBuilder::InsertionGuard guard(b);
1208 b.createBlock(&parentGuard.getElseRegion());
1209 auto message = StringAttr::get(&getContext(),
1210 Twine(parent.getName()) +
" not enabled");
1211 sv::MacroErrorOp::create(b, loc, message);
1212 parent = parent->getParentOfType<LayerOp>();
1217 llvm_unreachable(
"unknown layer convention");
1222 SmallPtrSet<Operation *, 8> seen;
1223 for (
auto *record : *node) {
1224 auto *child = record->getTarget()->getModule().getOperation();
1225 if (!std::get<bool>(seen.insert(child)))
1228 auto lookup = files.find(layer);
1229 if (lookup == files.end() || !lookup->second.effectful)
1231 sv::IncludeOp::create(b, loc, IncludeStyle::Local, lookup->second.filename);
1236 info.filename = filename;
1237 info.body = includeGuard.getElseBlock();
1238 info.effectful = effectful;