Skip to content

Commit a53f9a0

Browse files
committed
feat: use non-local annotations to mark memories with extmodule
This fixes #8994. We use NLA here to pass information from LowerMemory to LayerSink to avoid marking memories with an extmodule as not effectful. Signed-off-by: Tianrui Wei <[email protected]>
1 parent 40962a3 commit a53f9a0

File tree

2 files changed

+55
-5
lines changed

2 files changed

+55
-5
lines changed

lib/Dialect/FIRRTL/Transforms/LayerSink.cpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
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;
3435
using namespace firrtl;
3536
using namespace mlir;
3637

38+
static constexpr llvm::StringLiteral kExtMemoryEffectNLAAttrName(
39+
"circt.layerSink.externalMemoryEffect");
40+
3741
//===----------------------------------------------------------------------===//
3842
// Helpers
3943
//===----------------------------------------------------------------------===//
@@ -87,7 +91,10 @@ static bool cloneable(Operation *op) {
8791
namespace {
8892
class EffectInfo {
8993
public:
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

113120
private:
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

lib/Dialect/FIRRTL/Transforms/LowerMemory.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "llvm/ADT/STLExtras.h"
2525
#include "llvm/ADT/STLFunctionalExtras.h"
2626
#include "llvm/ADT/SmallPtrSet.h"
27+
#include "llvm/ADT/StringRef.h"
2728
#include "llvm/Support/Parallel.h"
2829
#include <optional>
2930
#include <set>
@@ -189,9 +190,11 @@ LowerMemoryPass::getMemoryModulePorts(const FirMemory &mem) {
189190
return ports;
190191
}
191192

192-
FMemModuleOp
193-
LowerMemoryPass::emitMemoryModule(MemOp op, const FirMemory &mem,
194-
const SmallVectorImpl<PortInfo> &ports) {
193+
static constexpr llvm::StringLiteral kExtMemoryEffectNLAAttrName(
194+
"circt.layerSink.externalMemoryEffect");
195+
196+
FMemModuleOp LowerMemoryPass::emitMemoryModule(
197+
MemOp op, const FirMemory &mem, const SmallVectorImpl<PortInfo> &ports) {
195198
// Get a non-colliding name for the memory module, and update the summary.
196199
StringRef prefix = "";
197200
if (mem.prefix)
@@ -208,6 +211,22 @@ LowerMemoryPass::emitMemoryModule(MemOp op, const FirMemory &mem,
208211
mem.numReadWritePorts, mem.dataWidth, mem.maskBits, mem.readLatency,
209212
mem.writeLatency, mem.depth,
210213
*symbolizeRUWBehavior(static_cast<uint32_t>(mem.readUnderWrite)));
214+
// Mark the generated external memory as effectful so downstream passes do not
215+
// treat it as pure and drop the wrapper wiring around it. We create a
216+
// temporary HierPathOp, which LayerSink will consume and erase.
217+
auto circuit = op->getParentOfType<CircuitOp>();
218+
OpBuilder nlaBuilder(circuit);
219+
nlaBuilder.setInsertionPointToEnd(circuit.getBodyBlock());
220+
auto pathAttr = nlaBuilder.getArrayAttr(
221+
SmallVector<Attribute, 1>{
222+
FlatSymbolRefAttr::get(moduleOp.getModuleNameAttr())});
223+
auto nlaName = circuitNamespace.newName(
224+
(moduleOp.getModuleNameAttr().getValue() + "_effect_nla").str());
225+
auto nla = nlaBuilder.create<hw::HierPathOp>(
226+
mem.loc, StringAttr::get(&getContext(), nlaName), pathAttr);
227+
nla->setAttr(kExtMemoryEffectNLAAttrName,
228+
FlatSymbolRefAttr::get(moduleOp.getModuleNameAttr()));
229+
SymbolTable::setSymbolVisibility(nla, SymbolTable::Visibility::Private);
211230
SymbolTable::setSymbolVisibility(moduleOp, SymbolTable::Visibility::Private);
212231
return moduleOp;
213232
}

0 commit comments

Comments
 (0)