405 DenseMap<Value, Operation *> nodeCache;
408 auto getOrCreateNodeOp = [&](Value operand,
409 ImplicitLocOpBuilder &builder) -> Operation * {
411 auto *nodeOp = nodeCache.lookup(operand);
416 OpBuilder::InsertionGuard guard(builder);
417 builder.setInsertionPointAfterValue(operand);
418 SmallString<16> nameHint;
420 if (
auto *definingOp = operand.getDefiningOp()) {
421 if (
auto instanceOp = dyn_cast<InstanceOp>(definingOp)) {
422 nameHint.append(instanceOp.getName());
423 nameHint.push_back(
'_');
425 instanceOp.getPortName(cast<OpResult>(operand).getResultNumber()));
426 }
else if (
auto opName = definingOp->getAttrOfType<StringAttr>(
"name")) {
427 nameHint.append(opName);
430 return nodeOp = NodeOp::create(builder, operand.getLoc(), operand,
431 nameHint.empty() ?
"_layer_probe"
432 : StringRef(nameHint));
438 DenseMap<Value, Value> replacements;
439 auto getReplacement = [&](Operation *user, Value value) -> Value {
440 auto it = replacements.find(value);
441 if (it != replacements.end())
442 return it->getSecond();
444 ImplicitLocOpBuilder localBuilder(value.getLoc(), &getContext());
447 auto layerBlockOp = user->getParentOfType<LayerBlockOp>();
448 localBuilder.setInsertionPointToStart(layerBlockOp.getBody());
455 if (type_isa<FStringType>(value.getType())) {
456 localBuilder.setInsertionPoint(user);
457 replacement = localBuilder.clone(*value.getDefiningOp())->getResult(0);
458 replacements.insert({value, replacement});
463 auto *definingOp = value.getDefiningOp();
464 if (isa_and_present<XMRRefOp>(definingOp)) {
465 replacement = localBuilder.clone(*definingOp)->getResult(0);
466 replacements.insert({value, replacement});
479 auto baseType = type_cast<FIRRTLBaseType>(value.getType());
480 if (baseType && baseType.getBitWidthOrSentinel() == 0) {
481 OpBuilder::InsertionGuard guard(localBuilder);
482 auto zeroUIntType = UIntType::get(localBuilder.getContext(), 0);
483 replacement = localBuilder.createOrFold<BitCastOp>(
484 value.getType(), ConstantOp::create(localBuilder, zeroUIntType,
487 auto *definingOp = value.getDefiningOp();
488 hw::InnerRefAttr innerRef;
496 definingOp = getOrCreateNodeOp(value, localBuilder);
500 auto portIdx = cast<BlockArgument>(value).getArgNumber();
502 cast<FModuleLike>(*moduleOp), portIdx,
506 hw::HierPathOp hierPathOp;
509 auto insertPoint = OpBuilder::InsertPoint(moduleOp->getBlock(),
510 Block::iterator(moduleOp));
511 llvm::sys::SmartScopedLock<true> circuitLock(*
circuitMutex);
513 localBuilder.getArrayAttr({innerRef}), localBuilder.getLoc(),
515 hierPathOp.setVisibility(SymbolTable::Visibility::Private);
518 replacement = XMRDerefOp::create(localBuilder, value.getType(),
519 hierPathOp.getSymNameAttr());
522 replacements.insert({value, replacement});
530 DenseMap<Operation *, FModuleOp> createdInstances;
534 auto opPreconditionCheck = [](Operation *op) -> LogicalResult {
536 if (isa<RefCastOp, RefDefineOp, RefResolveOp, RefSendOp, RefSubOp,
538 return op->emitOpError()
539 <<
"cannot be handled by the lower-layers pass. This should have "
540 "already been removed by the lower-xmr pass.";
559 DenseMap<Operation *, Attribute> domainMap;
560 auto getDomain = [&domainMap](Value value,
561 Attribute &domain) -> LogicalResult {
562 SmallVector<Operation *> wires;
567 if (
auto arg = dyn_cast<BlockArgument>(value)) {
568 domain = cast<FModuleLike>(arg.getOwner()->getParentOp())
569 .getDomainInfoAttrForPort(arg.getArgNumber());
574 TypeSwitch<Operation *, LogicalResult>(value.getDefiningOp())
575 .Case<WireOp>([&](WireOp op) {
576 auto it = domainMap.find(op);
577 if (it != domainMap.end()) {
578 domain = it->getSecond();
581 for (
auto *user : op->getUsers()) {
582 auto connect = dyn_cast<FConnectLike>(user);
583 if (!connect || connect.getDest() != value)
585 value = connect.getSrc();
589 emitError(value.getLoc())
590 <<
"unable to determine domain kind for source likely "
592 "violation of static-single-connect";
595 .Case<InstanceOp>([&](
auto op) {
597 op.getPortDomain(cast<OpResult>(value).getResultNumber());
600 .Case<DomainCreateAnonOp>([&](
auto op) {
601 domain = op.getDomainAttr();
604 .Default([&](
auto op) {
605 op->emitOpError() <<
"unhandled domain source in 'LowerLayers";
613 for (
auto *wire : wires)
614 domainMap[wire] = domain;
641 auto result = moduleOp.walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
642 if (failed(opPreconditionCheck(op)))
643 return WalkResult::interrupt();
646 for (
auto result : op->getResults())
650 if (
auto instance = dyn_cast<InstanceOp>(op))
651 instance.setLayers({});
653 auto layerBlock = dyn_cast<LayerBlockOp>(op);
655 return WalkResult::advance();
658 auto layer =
symbolToLayer.lookup(layerBlock.getLayerName());
660 if (layer.getConvention() == LayerConvention::Inline) {
662 return WalkResult::advance();
666 assert(layer.getConvention() == LayerConvention::Bind);
671 SmallVector<PortInfo> ports;
672 SmallVector<Value> connectValues;
677 auto createInputPort = [&](Value src, Location loc) -> LogicalResult {
679 if (failed(getDomain(src, domain)))
685 name = StringAttr::get(src.getContext(), portNs.
newName(nameHint));
687 name = StringAttr::get(src.getContext(), portNs.
newName(
"anonDomain"));
696 ports.push_back(port);
697 connectValues.push_back(src);
698 BlockArgument replacement =
699 layerBlock.getBody()->addArgument(port.
type, port.
loc);
700 src.replaceUsesWithIf(replacement, [&](OpOperand &use) {
701 auto *user = use.getOwner();
702 if (!layerBlock->isAncestor(user))
708 if (
auto connectLike = dyn_cast<FConnectLike>(user)) {
709 auto *destDefiningOp = connectLike.getDest().getDefiningOp();
710 return connectLike.getSrc() == src &&
711 !createdInstances.contains(destDefiningOp);
721 auto getPortLoc = [&](Value port) -> Location {
722 Location loc = UnknownLoc::get(port.getContext());
723 if (
auto *destOp = port.getDefiningOp())
724 if (
auto instOp = dyn_cast<InstanceOp>(destOp)) {
725 auto modOpIt = createdInstances.find(instOp);
726 if (modOpIt != createdInstances.end()) {
727 auto portNum = cast<OpResult>(port).getResultNumber();
728 loc = modOpIt->getSecond().getPortLocation(portNum);
736 auto createOutputPort = [&](Value src, Value dest) -> LogicalResult {
738 if (failed(getDomain(src, domain)))
744 name = StringAttr::get(src.getContext(), portNs.
newName(nameHint));
746 name = StringAttr::get(src.getContext(), portNs.
newName(
"anonDomain"));
755 ports.push_back(port);
756 connectValues.push_back(dest);
757 BlockArgument replacement =
758 layerBlock.getBody()->addArgument(port.type, port.loc);
759 dest.replaceUsesWithIf(replacement, [&](OpOperand &use) {
760 auto *user = use.getOwner();
761 if (!layerBlock->isAncestor(user))
764 if (
auto connectLike = dyn_cast<FConnectLike>(user))
765 return connectLike.getDest() == dest;
772 replacements.clear();
773 OpBuilder builder(moduleOp);
774 SmallVector<hw::InnerSymAttr> innerSyms;
775 SmallVector<sv::VerbatimOp> verbatims;
776 DenseSet<Operation *> spilledSubOps;
777 auto layerBlockWalkResult = layerBlock.walk([&](Operation *op) {
779 if (failed(opPreconditionCheck(op)))
780 return WalkResult::interrupt();
793 auto fixSubOp = [&](
auto subOp) {
794 auto input = subOp.getInput();
798 return WalkResult::advance();
801 if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive()) {
802 subOp.getInputMutable().assign(getReplacement(subOp, input));
803 return WalkResult::advance();
807 op->moveBefore(layerBlock);
808 spilledSubOps.insert(op);
809 return WalkResult::advance();
812 if (
auto subOp = dyn_cast<SubfieldOp>(op))
813 return fixSubOp(subOp);
815 if (
auto subOp = dyn_cast<SubindexOp>(op))
816 return fixSubOp(subOp);
818 if (
auto subOp = dyn_cast<SubaccessOp>(op)) {
819 auto input = subOp.getInput();
820 auto index = subOp.getIndex();
826 subOp.getIndexMutable().assign(getReplacement(subOp, index));
828 return WalkResult::advance();
832 if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive()) {
833 subOp.getInputMutable().assign(getReplacement(subOp, input));
835 subOp.getIndexMutable().assign(getReplacement(subOp, index));
836 return WalkResult::advance();
841 subOp->moveBefore(layerBlock);
842 spilledSubOps.insert(op);
843 return WalkResult::advance();
848 auto diag = op->emitOpError()
849 <<
"has a non-passive operand and captures a value defined "
850 "outside its enclosing bind-convention layerblock. The "
851 "'LowerLayers' pass cannot lower this as it would "
852 "create an output port on the resulting module.";
853 diag.attachNote(layerBlock.getLoc())
854 <<
"the layerblock is defined here";
855 return WalkResult::interrupt();
858 if (
auto nodeOp = dyn_cast<NodeOp>(op)) {
859 auto *definingOp = nodeOp.getInput().getDefiningOp();
861 spilledSubOps.contains(nodeOp.getInput().getDefiningOp())) {
862 op->moveBefore(layerBlock);
863 return WalkResult::advance();
871 if (
auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op))
872 if (
auto innerSym = symOp.getInnerSymAttr())
873 innerSyms.push_back(innerSym);
885 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
887 if (!createdInstances.contains(instOp))
888 return WalkResult::advance();
892 <<
" Found instance created from nested layer block:\n"
893 <<
" module: " << instOp.getModuleName() <<
"\n"
894 <<
" instance: " << instOp.getName() <<
"\n";
896 instOp->moveBefore(layerBlock);
897 return WalkResult::advance();
910 if (
auto domainDefineOp = dyn_cast<DomainDefineOp>(op)) {
911 auto src = domainDefineOp.getSrc();
912 auto dest = domainDefineOp.getDest();
916 if (srcInLayerBlock) {
918 if (destInLayerBlock)
919 return WalkResult::advance();
925 return WalkResult(createOutputPort(src, dest));
931 if (destInLayerBlock)
932 return WalkResult(createInputPort(src, domainDefineOp.getLoc()));
938 domainDefineOp->moveBefore(layerBlock);
939 return WalkResult::advance();
945 for (
size_t i = 0, e = op->getNumOperands(); i != e; ++i) {
946 auto operand = op->getOperand(i);
954 op->setOperand(i, getReplacement(op, operand));
957 if (
auto verbatim = dyn_cast<sv::VerbatimOp>(op))
958 verbatims.push_back(verbatim);
960 return WalkResult::advance();
963 if (layerBlockWalkResult.wasInterrupted())
964 return WalkResult::interrupt();
971 if (llvm::all_of(layerBlock.getRegion().getBlocks(),
972 [](
auto &a) { return a.empty(); })) {
973 assert(verbatims.empty());
975 return WalkResult::advance();
980 newModule.getBody().takeBody(layerBlock.getRegion());
981 SymbolTable::setSymbolVisibility(newModule,
982 SymbolTable::Visibility::Private);
985 llvm::dbgs() <<
" New Module: "
987 llvm::dbgs() <<
" ports:\n";
988 for (
size_t i = 0, e = ports.size(); i != e; ++i) {
989 auto port = ports[i];
990 auto value = connectValues[i];
991 llvm::dbgs() <<
" - name: " << port.getName() <<
"\n"
992 <<
" type: " << port.type <<
"\n"
993 <<
" direction: " << port.direction <<
"\n"
994 <<
" value: " << value <<
"\n";
1004 builder.setInsertionPointAfter(layerBlock);
1007 hw::InnerSymAttr::get(builder.getStringAttr(ns.
newName(instanceName)));
1009 auto instanceOp = InstanceOp::create(
1010 builder, layerBlock.getLoc(), newModule,
1012 instanceName, NameKindEnum::DroppableName,
1013 ArrayRef<Attribute>{},
1014 ArrayRef<Attribute>{},
false,
1016 for (
auto [lhs, rhs] : llvm::zip(instanceOp.getResults(), connectValues))
1017 if (instanceOp.getPortDirection(lhs.getResultNumber()) == Direction::In)
1018 DomainDefineOp::create(builder, builder.getUnknownLoc(), lhs, rhs);
1020 DomainDefineOp::create(builder, builder.getUnknownLoc(), rhs, lhs);
1024 layerBlock.getLayerName());
1025 instanceOp->setAttr(
"output_file", outputFile);
1027 createdInstances.try_emplace(instanceOp, newModule);
1031 auto builder = OpBuilder::atBlockEnd(
bindFiles[moduleOp][layer].body);
1032 BindOp::create(builder, layerBlock.getLoc(), moduleOp.getModuleNameAttr(),
1033 instanceOp.getInnerSymAttr().getSymName());
1036 LLVM_DEBUG(llvm::dbgs() <<
" moved inner refs:\n");
1037 for (hw::InnerSymAttr innerSym : innerSyms) {
1038 auto oldInnerRef = hw::InnerRefAttr::get(moduleOp.getModuleNameAttr(),
1039 innerSym.getSymName());
1040 auto splice = std::make_pair(instanceOp.getInnerSymAttr(),
1041 newModule.getModuleNameAttr());
1042 innerRefMap.insert({oldInnerRef, splice});
1043 LLVM_DEBUG(llvm::dbgs() <<
" - ref: " << oldInnerRef <<
"\n"
1044 <<
" splice: " << splice.first <<
", "
1045 << splice.second <<
"\n";);
1049 if (!verbatims.empty()) {
1050 mlir::AttrTypeReplacer replacer;
1051 replacer.addReplacement(
1052 [&innerRefMap](hw::InnerRefAttr ref) -> std::optional<Attribute> {
1053 auto it = innerRefMap.find(ref);
1054 if (it != innerRefMap.end())
1055 return hw::InnerRefAttr::get(it->second.second, ref.getName());
1056 return std::nullopt;
1058 for (
auto verbatim : verbatims)
1059 replacer.replaceElementsIn(verbatim);
1064 return WalkResult::advance();
1066 return success(!result.wasInterrupted());
1108 SymbolRefAttr layerName, LayerOp layer) {
1109 assert(layer.getConvention() == LayerConvention::Bind);
1110 auto module = node->getModule<FModuleOp>();
1111 auto loc =
module.getLoc();
1115 auto macroSymbol = ns.
newName(macroName);
1116 auto macroNameAttr = StringAttr::get(&getContext(), macroName);
1117 auto macroSymbolAttr = StringAttr::get(&getContext(), macroSymbol);
1118 auto macroSymbolRefAttr = FlatSymbolRefAttr::get(macroSymbolAttr);
1125 auto dir = layer->getAttrOfType<hw::OutputFileAttr>(
"output_file");
1126 StringAttr filename = StringAttr::get(&getContext(), bindFileName);
1129 path = StringAttr::get(&getContext(),
1130 Twine(dir.getDirectory()) + bindFileName);
1135 sv::MacroDeclOp::create(b, loc, macroSymbolAttr, ArrayAttr{}, macroNameAttr);
1138 auto bindFile = emit::FileOp::create(b, loc, path);
1139 OpBuilder::InsertionGuard _(b);
1140 b.setInsertionPointToEnd(bindFile.getBody());
1143 auto includeGuard = sv::IfDefOp::create(b, loc, macroSymbolRefAttr);
1144 b.createBlock(&includeGuard.getElseRegion());
1147 sv::MacroDefOp::create(b, loc, macroSymbolRefAttr);
1150 auto parent = layer->getParentOfType<LayerOp>();
1154 if (parent.getConvention() == LayerConvention::Bind) {
1155 auto target =
bindFiles[module][parent].filename;
1156 sv::IncludeOp::create(b, loc, IncludeStyle::Local, target);
1162 if (parent.getConvention() == LayerConvention::Inline) {
1163 auto parentMacroSymbolRefAttr =
macroNames[parent];
1164 auto parentGuard = sv::IfDefOp::create(b, loc, parentMacroSymbolRefAttr);
1165 OpBuilder::InsertionGuard guard(b);
1166 b.createBlock(&parentGuard.getElseRegion());
1167 auto message = StringAttr::get(&getContext(),
1168 Twine(parent.getName()) +
" not enabled");
1169 sv::MacroErrorOp::create(b, loc, message);
1170 parent = parent->getParentOfType<LayerOp>();
1175 llvm_unreachable(
"unknown layer convention");
1180 SmallPtrSet<Operation *, 8> seen;
1181 for (
auto *record : *node) {
1182 auto *child = record->getTarget()->getModule().getOperation();
1183 if (!std::get<bool>(seen.insert(child)))
1186 auto lookup = files.find(layer);
1187 if (lookup != files.end())
1188 sv::IncludeOp::create(b, loc, IncludeStyle::Local,
1189 lookup->second.filename);
1194 info.filename = filename;
1195 info.body = includeGuard.getElseBlock();