407 DenseMap<Value, Operation *> nodeCache;
410 auto getOrCreateNodeOp = [&](Value operand,
411 ImplicitLocOpBuilder &builder) -> Operation * {
413 auto *nodeOp = nodeCache.lookup(operand);
418 OpBuilder::InsertionGuard guard(builder);
419 builder.setInsertionPointAfterValue(operand);
420 SmallString<16> nameHint;
422 if (
auto *definingOp = operand.getDefiningOp()) {
423 if (
auto instanceOp = dyn_cast<InstanceOp>(definingOp)) {
424 nameHint.append(instanceOp.getName());
425 nameHint.push_back(
'_');
427 instanceOp.getPortName(cast<OpResult>(operand).getResultNumber()));
428 }
else if (
auto opName = definingOp->getAttrOfType<StringAttr>(
"name")) {
429 nameHint.append(opName);
432 return nodeOp = NodeOp::create(builder, operand.getLoc(), operand,
433 nameHint.empty() ?
"_layer_probe"
434 : StringRef(nameHint));
440 DenseMap<Value, Value> replacements;
441 auto getReplacement = [&](Operation *user, Value value) -> Value {
442 auto it = replacements.find(value);
443 if (it != replacements.end())
444 return it->getSecond();
446 ImplicitLocOpBuilder localBuilder(value.getLoc(), &getContext());
449 auto layerBlockOp = user->getParentOfType<LayerBlockOp>();
450 localBuilder.setInsertionPointToStart(layerBlockOp.getBody());
457 if (type_isa<FStringType>(value.getType())) {
458 localBuilder.setInsertionPoint(user);
459 replacement = localBuilder.clone(*value.getDefiningOp())->getResult(0);
460 replacements.insert({value, replacement});
465 auto *definingOp = value.getDefiningOp();
466 if (isa_and_present<XMRRefOp>(definingOp)) {
467 replacement = localBuilder.clone(*definingOp)->getResult(0);
468 replacements.insert({value, replacement});
481 auto baseType = type_cast<FIRRTLBaseType>(value.getType());
482 if (baseType && baseType.getBitWidthOrSentinel() == 0) {
483 OpBuilder::InsertionGuard guard(localBuilder);
484 auto zeroUIntType = UIntType::get(localBuilder.getContext(), 0);
485 replacement = localBuilder.createOrFold<BitCastOp>(
486 value.getType(), ConstantOp::create(localBuilder, zeroUIntType,
489 auto *definingOp = value.getDefiningOp();
490 hw::InnerRefAttr innerRef;
498 definingOp = getOrCreateNodeOp(value, localBuilder);
502 auto portIdx = cast<BlockArgument>(value).getArgNumber();
504 cast<FModuleLike>(*moduleOp), portIdx,
508 hw::HierPathOp hierPathOp;
511 auto insertPoint = OpBuilder::InsertPoint(moduleOp->getBlock(),
512 Block::iterator(moduleOp));
513 llvm::sys::SmartScopedLock<true> circuitLock(*
circuitMutex);
515 localBuilder.getArrayAttr({innerRef}), localBuilder.getLoc(),
517 hierPathOp.setVisibility(SymbolTable::Visibility::Private);
520 replacement = XMRDerefOp::create(localBuilder, value.getType(),
521 hierPathOp.getSymNameAttr());
524 replacements.insert({value, replacement});
532 DenseMap<InstanceOp, FModuleOp> createdInstances;
536 auto opPreconditionCheck = [](Operation *op) -> LogicalResult {
538 if (isa<RefCastOp, RefDefineOp, RefResolveOp, RefSendOp, RefSubOp,
540 return op->emitOpError()
541 <<
"cannot be handled by the lower-layers pass. This should have "
542 "already been removed by the lower-xmr pass.";
566 auto result = moduleOp.walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
567 if (failed(opPreconditionCheck(op)))
568 return WalkResult::interrupt();
571 for (
auto result : op->getResults())
575 if (
auto instance = dyn_cast<InstanceOp>(op))
576 instance.setLayers({});
578 auto layerBlock = dyn_cast<LayerBlockOp>(op);
580 return WalkResult::advance();
583 auto layer =
symbolToLayer.lookup(layerBlock.getLayerName());
585 if (layer.getConvention() == LayerConvention::Inline) {
587 return WalkResult::advance();
591 assert(layer.getConvention() == LayerConvention::Bind);
594 replacements.clear();
595 OpBuilder builder(moduleOp);
596 SmallVector<hw::InnerSymAttr> innerSyms;
597 SmallVector<sv::VerbatimOp> verbatims;
598 DenseSet<Operation *> spilledSubOps;
599 auto layerBlockWalkResult = layerBlock.walk([&](Operation *op) {
601 if (failed(opPreconditionCheck(op)))
602 return WalkResult::interrupt();
615 auto fixSubOp = [&](
auto subOp) {
616 auto input = subOp.getInput();
620 return WalkResult::advance();
623 if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive()) {
624 subOp.getInputMutable().assign(getReplacement(subOp, input));
625 return WalkResult::advance();
629 op->moveBefore(layerBlock);
630 spilledSubOps.insert(op);
631 return WalkResult::advance();
634 if (
auto subOp = dyn_cast<SubfieldOp>(op))
635 return fixSubOp(subOp);
637 if (
auto subOp = dyn_cast<SubindexOp>(op))
638 return fixSubOp(subOp);
640 if (
auto subOp = dyn_cast<SubaccessOp>(op)) {
641 auto input = subOp.getInput();
642 auto index = subOp.getIndex();
648 subOp.getIndexMutable().assign(getReplacement(subOp, index));
650 return WalkResult::advance();
654 if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive()) {
655 subOp.getInputMutable().assign(getReplacement(subOp, input));
657 subOp.getIndexMutable().assign(getReplacement(subOp, index));
658 return WalkResult::advance();
663 subOp->moveBefore(layerBlock);
664 spilledSubOps.insert(op);
665 return WalkResult::advance();
670 auto diag = op->emitOpError()
671 <<
"has a non-passive operand and captures a value defined "
672 "outside its enclosing bind-convention layerblock. The "
673 "'LowerLayers' pass cannot lower this as it would "
674 "create an output port on the resulting module.";
675 diag.attachNote(layerBlock.getLoc())
676 <<
"the layerblock is defined here";
677 return WalkResult::interrupt();
680 if (
auto nodeOp = dyn_cast<NodeOp>(op)) {
681 auto *definingOp = nodeOp.getInput().getDefiningOp();
683 spilledSubOps.contains(nodeOp.getInput().getDefiningOp())) {
684 op->moveBefore(layerBlock);
685 return WalkResult::advance();
693 if (
auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op))
694 if (
auto innerSym = symOp.getInnerSymAttr())
695 innerSyms.push_back(innerSym);
707 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
709 if (!createdInstances.contains(instOp))
710 return WalkResult::advance();
714 <<
" Found instance created from nested layer block:\n"
715 <<
" module: " << instOp.getModuleName() <<
"\n"
716 <<
" instance: " << instOp.getName() <<
"\n";
718 instOp->moveBefore(layerBlock);
719 return WalkResult::advance();
725 for (
size_t i = 0, e = op->getNumOperands(); i != e; ++i) {
726 auto operand = op->getOperand(i);
734 op->setOperand(i, getReplacement(op, operand));
737 if (
auto verbatim = dyn_cast<sv::VerbatimOp>(op))
738 verbatims.push_back(verbatim);
740 return WalkResult::advance();
743 if (layerBlockWalkResult.wasInterrupted())
744 return WalkResult::interrupt();
748 SymbolTable::setSymbolVisibility(newModule,
749 SymbolTable::Visibility::Private);
750 newModule.getBody().takeBody(layerBlock.getRegion());
753 llvm::dbgs() <<
" New Module: "
763 builder.setInsertionPointAfter(layerBlock);
766 hw::InnerSymAttr::get(builder.getStringAttr(ns.
newName(instanceName)));
768 auto instanceOp = InstanceOp::create(
769 builder, layerBlock.getLoc(), newModule,
771 instanceName, NameKindEnum::DroppableName,
772 ArrayRef<Attribute>{},
773 ArrayRef<Attribute>{},
false,
777 layerBlock.getLayerName());
778 instanceOp->setAttr(
"output_file", outputFile);
780 createdInstances.try_emplace(instanceOp, newModule);
784 auto builder = OpBuilder::atBlockEnd(
bindFiles[moduleOp][layer].body);
785 BindOp::create(builder, layerBlock.getLoc(), moduleOp.getModuleNameAttr(),
786 instanceOp.getInnerSymAttr().getSymName());
789 LLVM_DEBUG(llvm::dbgs() <<
" moved inner refs:\n");
790 for (hw::InnerSymAttr innerSym : innerSyms) {
791 auto oldInnerRef = hw::InnerRefAttr::get(moduleOp.getModuleNameAttr(),
792 innerSym.getSymName());
793 auto splice = std::make_pair(instanceOp.getInnerSymAttr(),
794 newModule.getModuleNameAttr());
795 innerRefMap.insert({oldInnerRef, splice});
796 LLVM_DEBUG(llvm::dbgs() <<
" - ref: " << oldInnerRef <<
"\n"
797 <<
" splice: " << splice.first <<
", "
798 << splice.second <<
"\n";);
802 if (!verbatims.empty()) {
803 mlir::AttrTypeReplacer replacer;
804 replacer.addReplacement(
805 [&innerRefMap](hw::InnerRefAttr ref) -> std::optional<Attribute> {
806 auto it = innerRefMap.find(ref);
807 if (it != innerRefMap.end())
808 return hw::InnerRefAttr::get(it->second.second, ref.getName());
811 for (
auto verbatim : verbatims)
812 replacer.replaceElementsIn(verbatim);
817 return WalkResult::advance();
819 return success(!result.wasInterrupted());
861 SymbolRefAttr layerName, LayerOp layer) {
862 assert(layer.getConvention() == LayerConvention::Bind);
863 auto module = node->getModule<FModuleOp>();
864 auto loc =
module.getLoc();
868 auto macroSymbol = ns.
newName(macroName);
869 auto macroNameAttr = StringAttr::get(&getContext(), macroName);
870 auto macroSymbolAttr = StringAttr::get(&getContext(), macroSymbol);
871 auto macroSymbolRefAttr = FlatSymbolRefAttr::get(macroSymbolAttr);
878 auto dir = layer->getAttrOfType<hw::OutputFileAttr>(
"output_file");
879 StringAttr filename = StringAttr::get(&getContext(), bindFileName);
882 path = StringAttr::get(&getContext(),
883 Twine(dir.getDirectory()) + bindFileName);
888 sv::MacroDeclOp::create(b, loc, macroSymbolAttr, ArrayAttr{}, macroNameAttr);
891 auto bindFile = emit::FileOp::create(b, loc, path);
892 OpBuilder::InsertionGuard _(b);
893 b.setInsertionPointToEnd(bindFile.getBody());
896 auto includeGuard = sv::IfDefOp::create(b, loc, macroSymbolRefAttr);
897 b.createBlock(&includeGuard.getElseRegion());
900 sv::MacroDefOp::create(b, loc, macroSymbolRefAttr);
903 auto parent = layer->getParentOfType<LayerOp>();
907 if (parent.getConvention() == LayerConvention::Bind) {
908 auto target =
bindFiles[module][parent].filename;
909 sv::IncludeOp::create(b, loc, IncludeStyle::Local, target);
915 if (parent.getConvention() == LayerConvention::Inline) {
916 auto parentMacroSymbolRefAttr =
macroNames[parent];
917 auto parentGuard = sv::IfDefOp::create(b, loc, parentMacroSymbolRefAttr);
918 OpBuilder::InsertionGuard guard(b);
919 b.createBlock(&parentGuard.getElseRegion());
920 auto message = StringAttr::get(&getContext(),
921 Twine(parent.getName()) +
" not enabled");
922 sv::MacroErrorOp::create(b, loc, message);
923 parent = parent->getParentOfType<LayerOp>();
928 llvm_unreachable(
"unknown layer convention");
933 SmallPtrSet<Operation *, 8> seen;
934 for (
auto *record : *node) {
935 auto *child = record->getTarget()->getModule().getOperation();
936 if (!std::get<bool>(seen.insert(child)))
939 auto lookup = files.find(layer);
940 if (lookup != files.end())
941 sv::IncludeOp::create(b, loc, IncludeStyle::Local,
942 lookup->second.filename);
947 info.filename = filename;
948 info.body = includeGuard.getElseBlock();
1075 llvm::dbgs() <<
"==----- Running LowerLayers "
1076 "-------------------------------------------------===\n");
1077 CircuitOp circuitOp = getOperation();
1080 llvm::sys::SmartMutex<true> mutex;
1083 auto *ig = &getAnalysis<InstanceGraph>();
1086 &ns, OpBuilder::InsertPoint(getOperation().
getBodyBlock(),
1093 auto mergeMaps = [](
auto &&a,
auto &&b) {
1095 return std::forward<
decltype(a)>(a);
1097 return std::forward<
decltype(b)>(b);
1101 return std::forward<decltype(a)>(a);
1105 SmallVector<FModuleLike> modules(
1106 circuitOp.getBodyBlock()->getOps<FModuleLike>());
1108 circuitOp.getContext(), modules, FailureOr<InnerRefMap>(
InnerRefMap{}),
1109 mergeMaps, [
this](FModuleLike mod) -> FailureOr<InnerRefMap> {
1110 return runOnModuleLike(mod);
1112 if (failed(failureOrInnerRefMap))
1113 return signalPassFailure();
1114 auto &innerRefMap = *failureOrInnerRefMap;
1123 for (hw::HierPathOp hierPathOp : circuitOp.getOps<hw::HierPathOp>()) {
1124 SmallVector<Attribute> newNamepath;
1125 bool modified =
false;
1126 for (
auto attr : hierPathOp.getNamepath()) {
1127 hw::InnerRefAttr innerRef = dyn_cast<hw::InnerRefAttr>(attr);
1129 newNamepath.push_back(attr);
1132 auto it = innerRefMap.find(innerRef);
1133 if (it == innerRefMap.end()) {
1134 newNamepath.push_back(attr);
1138 auto &[inst, mod] = it->getSecond();
1139 newNamepath.push_back(
1140 hw::InnerRefAttr::get(innerRef.getModule(), inst.getSymName()));
1141 newNamepath.push_back(hw::InnerRefAttr::get(mod, innerRef.getName()));
1145 hierPathOp.setNamepathAttr(
1146 ArrayAttr::get(circuitOp.getContext(), newNamepath));
1151 llvm::make_early_inc_range(circuitOp.getBodyBlock()->getOps<LayerOp>()))