1313#include " circt/Dialect/FIRRTL/FIRRTLInstanceGraph.h"
1414#include " circt/Dialect/FIRRTL/FIRRTLOps.h"
1515#include " circt/Dialect/FIRRTL/Passes.h"
16+ #include " circt/Dialect/HW/HWOps.h"
1617#include " circt/Support/Debug.h"
1718#include " mlir/IR/Dominance.h"
1819#include " mlir/IR/Threading.h"
@@ -34,6 +35,9 @@ using namespace circt;
3435using namespace firrtl ;
3536using namespace mlir ;
3637
38+ static constexpr llvm::StringLiteral
39+ kExtMemoryEffectNLAAttrName (" circt.layerSink.externalMemoryEffect" );
40+
3741// ===----------------------------------------------------------------------===//
3842// Helpers
3943// ===----------------------------------------------------------------------===//
@@ -87,7 +91,10 @@ static bool cloneable(Operation *op) {
8791namespace {
8892class EffectInfo {
8993public:
90- EffectInfo (CircuitOp circuit, InstanceGraph &instanceGraph) {
94+ EffectInfo (CircuitOp circuit, InstanceGraph &instanceGraph,
95+ DenseSet<StringAttr> explicitEffectfulModules)
96+ : explicitEffectfulModules(std::move(explicitEffectfulModules)) {
97+ DenseSet<InstanceGraphNode *> visited;
9198 instanceGraph.walkPostOrder (
9299 [&](auto &node) { update (node.getModule ().getOperation ()); });
93100 }
@@ -111,6 +118,7 @@ class EffectInfo {
111118 }
112119
113120private:
121+ DenseSet<StringAttr> explicitEffectfulModules;
114122 // / Record whether the module contains any effectful ops.
115123 void update (FModuleOp moduleOp) {
116124 moduleOp.getBodyBlock ()->walk ([&](Operation *op) {
@@ -130,6 +138,14 @@ class EffectInfo {
130138 if (!annos.empty ())
131139 return markEffectful (moduleOp);
132140
141+ if (explicitEffectfulModules.contains (moduleOp.getModuleNameAttr ()))
142+ return markEffectful (moduleOp);
143+
144+ // Treat generated memory modules as effectful since they model stateful
145+ // hardware even though they have no body.
146+ if (isa<FMemModuleOp>(moduleOp.getOperation ()))
147+ return markEffectful (moduleOp);
148+
133149 for (auto annos : moduleOp.getPortAnnotations ())
134150 if (!cast<ArrayAttr>(annos).empty ())
135151 return markEffectful (moduleOp);
@@ -486,7 +502,19 @@ void LayerSinkPass::runOnOperation() {
486502 << " \n "
487503 << " Circuit: '" << circuit.getName () << " '\n " ;);
488504 auto &instanceGraph = getAnalysis<InstanceGraph>();
489- EffectInfo effectInfo (circuit, instanceGraph);
505+
506+ DenseSet<StringAttr> explicitEffectModules;
507+ SmallVector<hw::HierPathOp, 4 > effectNLAs;
508+ for (auto nla : circuit.getOps <hw::HierPathOp>()) {
509+ if (!nla->hasAttr (kExtMemoryEffectNLAAttrName ))
510+ continue ;
511+ if (auto leaf = nla.leafMod ())
512+ explicitEffectModules.insert (leaf);
513+ effectNLAs.push_back (nla);
514+ }
515+
516+ EffectInfo effectInfo (circuit, instanceGraph,
517+ std::move (explicitEffectModules));
490518
491519 std::atomic<bool > changed (false );
492520 parallelForEach (&getContext (), circuit.getOps <FModuleOp>(),
@@ -495,6 +523,9 @@ void LayerSinkPass::runOnOperation() {
495523 changed = true ;
496524 });
497525
526+ for (auto nla : effectNLAs)
527+ nla.erase ();
528+
498529 if (!changed)
499530 markAllAnalysesPreserved ();
500531 else
0 commit comments