diff --git a/lib/Dialect/FIRRTL/Transforms/LayerSink.cpp b/lib/Dialect/FIRRTL/Transforms/LayerSink.cpp index d0dbe25400b5..4adad9f30ca8 100644 --- a/lib/Dialect/FIRRTL/Transforms/LayerSink.cpp +++ b/lib/Dialect/FIRRTL/Transforms/LayerSink.cpp @@ -13,6 +13,7 @@ #include "circt/Dialect/FIRRTL/FIRRTLInstanceGraph.h" #include "circt/Dialect/FIRRTL/FIRRTLOps.h" #include "circt/Dialect/FIRRTL/Passes.h" +#include "circt/Dialect/HW/HWOps.h" #include "circt/Support/Debug.h" #include "mlir/IR/Dominance.h" #include "mlir/IR/Threading.h" @@ -34,6 +35,9 @@ using namespace circt; using namespace firrtl; using namespace mlir; +static constexpr llvm::StringLiteral + kExtMemoryEffectNLAAttrName("circt.layerSink.externalMemoryEffect"); + //===----------------------------------------------------------------------===// // Helpers //===----------------------------------------------------------------------===// @@ -87,7 +91,10 @@ static bool cloneable(Operation *op) { namespace { class EffectInfo { public: - EffectInfo(CircuitOp circuit, InstanceGraph &instanceGraph) { + EffectInfo(CircuitOp circuit, InstanceGraph &instanceGraph, + DenseSet explicitEffectfulModules) + : explicitEffectfulModules(std::move(explicitEffectfulModules)) { + DenseSet visited; instanceGraph.walkPostOrder( [&](auto &node) { update(node.getModule().getOperation()); }); } @@ -111,6 +118,7 @@ class EffectInfo { } private: + DenseSet explicitEffectfulModules; /// Record whether the module contains any effectful ops. void update(FModuleOp moduleOp) { moduleOp.getBodyBlock()->walk([&](Operation *op) { @@ -130,6 +138,14 @@ class EffectInfo { if (!annos.empty()) return markEffectful(moduleOp); + if (explicitEffectfulModules.contains(moduleOp.getModuleNameAttr())) + return markEffectful(moduleOp); + + // Treat generated memory modules as effectful since they model stateful + // hardware even though they have no body. + if (isa(moduleOp.getOperation())) + return markEffectful(moduleOp); + for (auto annos : moduleOp.getPortAnnotations()) if (!cast(annos).empty()) return markEffectful(moduleOp); @@ -486,7 +502,19 @@ void LayerSinkPass::runOnOperation() { << "\n" << "Circuit: '" << circuit.getName() << "'\n";); auto &instanceGraph = getAnalysis(); - EffectInfo effectInfo(circuit, instanceGraph); + + DenseSet explicitEffectModules; + SmallVector effectNLAs; + for (auto nla : circuit.getOps()) { + if (!nla->hasAttr(kExtMemoryEffectNLAAttrName)) + continue; + if (auto leaf = nla.leafMod()) + explicitEffectModules.insert(leaf); + effectNLAs.push_back(nla); + } + + EffectInfo effectInfo(circuit, instanceGraph, + std::move(explicitEffectModules)); std::atomic changed(false); parallelForEach(&getContext(), circuit.getOps(), @@ -495,6 +523,9 @@ void LayerSinkPass::runOnOperation() { changed = true; }); + for (auto nla : effectNLAs) + nla.erase(); + if (!changed) markAllAnalysesPreserved(); else diff --git a/lib/Dialect/FIRRTL/Transforms/LowerMemory.cpp b/lib/Dialect/FIRRTL/Transforms/LowerMemory.cpp index 02780cb44a7a..8d08afc31bd3 100644 --- a/lib/Dialect/FIRRTL/Transforms/LowerMemory.cpp +++ b/lib/Dialect/FIRRTL/Transforms/LowerMemory.cpp @@ -24,6 +24,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLFunctionalExtras.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/Parallel.h" #include #include @@ -189,6 +190,9 @@ LowerMemoryPass::getMemoryModulePorts(const FirMemory &mem) { return ports; } +static constexpr llvm::StringLiteral + kExtMemoryEffectNLAAttrName("circt.layerSink.externalMemoryEffect"); + FMemModuleOp LowerMemoryPass::emitMemoryModule(MemOp op, const FirMemory &mem, const SmallVectorImpl &ports) { @@ -208,6 +212,21 @@ LowerMemoryPass::emitMemoryModule(MemOp op, const FirMemory &mem, mem.numReadWritePorts, mem.dataWidth, mem.maskBits, mem.readLatency, mem.writeLatency, mem.depth, *symbolizeRUWBehavior(static_cast(mem.readUnderWrite))); + // Mark the generated external memory as effectful so downstream passes do not + // treat it as pure and drop the wrapper wiring around it. We create a + // temporary HierPathOp, which LayerSink will consume and erase. + auto circuit = op->getParentOfType(); + OpBuilder nlaBuilder(circuit); + nlaBuilder.setInsertionPointToEnd(circuit.getBodyBlock()); + auto pathAttr = nlaBuilder.getArrayAttr(SmallVector{ + FlatSymbolRefAttr::get(moduleOp.getModuleNameAttr())}); + auto nlaName = circuitNamespace.newName( + (moduleOp.getModuleNameAttr().getValue() + "_effect_nla").str()); + auto nla = nlaBuilder.create( + mem.loc, StringAttr::get(&getContext(), nlaName), pathAttr); + nla->setAttr(kExtMemoryEffectNLAAttrName, + FlatSymbolRefAttr::get(moduleOp.getModuleNameAttr())); + SymbolTable::setSymbolVisibility(nla, SymbolTable::Visibility::Private); SymbolTable::setSymbolVisibility(moduleOp, SymbolTable::Visibility::Private); return moduleOp; }