Skip to content

Commit 0ae25e2

Browse files
committed
[FIRRTL] Dedup: properly merge inner symbols
When we deduplicate modules, we allow operations with different inner symbol definitions to deduplicate with each other. Unfortunately the operation merging procedure could only handle inner symbols with field id 0, i.e. inner symbols which targeted the entire operation. If an operation with a field-specific inner symbol was merged into another operation, we would end up dropping the inner symbol. This change properly handles inner symbols with many symbol definitions when merging operations by properly merging the inner symbol property list.
1 parent 2f49837 commit 0ae25e2

File tree

2 files changed

+93
-38
lines changed

2 files changed

+93
-38
lines changed

lib/Dialect/FIRRTL/Transforms/Dedup.cpp

+59-29
Original file line numberDiff line numberDiff line change
@@ -1316,6 +1316,53 @@ struct Deduper {
13161316
}
13171317
}
13181318

1319+
hw::InnerSymAttr mergeInnerSymbols(RenameMap &renameMap, FModuleLike toModule,
1320+
hw::InnerSymAttr toSym,
1321+
hw::InnerSymAttr fromSym) {
1322+
if (fromSym && !fromSym.getProps().empty()) {
1323+
auto &isn = getNamespace(toModule);
1324+
// The properties for the new inner symbol..
1325+
SmallVector<hw::InnerSymPropertiesAttr> newProps;
1326+
// If the "to" op already has an inner symbol, copy all its properties.
1327+
if (toSym)
1328+
llvm::append_range(newProps, toSym);
1329+
// Add each property from the fromSym to the toSym.
1330+
for (auto fromProp : fromSym) {
1331+
hw::InnerSymPropertiesAttr newProp;
1332+
auto *it = llvm::find_if(newProps, [&](auto p) {
1333+
return p.getFieldID() == fromProp.getFieldID();
1334+
});
1335+
if (it != newProps.end()) {
1336+
// If we already have an inner sym with the same field id, use
1337+
// that.
1338+
newProp = *it;
1339+
// If the old symbol is public, we need to make the new one public.
1340+
if (fromProp.getSymVisibility().getValue() == "public" &&
1341+
newProp.getSymVisibility().getValue() != "public") {
1342+
*it = hw::InnerSymPropertiesAttr::get(context, newProp.getName(),
1343+
newProp.getFieldID(),
1344+
fromProp.getSymVisibility());
1345+
}
1346+
} else {
1347+
// We need to add a new property to the inner symbol for this field.
1348+
auto newName = isn.newName(fromProp.getName().getValue());
1349+
newProp = hw::InnerSymPropertiesAttr::get(
1350+
context, StringAttr::get(context, newName), fromProp.getFieldID(),
1351+
fromProp.getSymVisibility());
1352+
newProps.push_back(newProp);
1353+
}
1354+
renameMap[fromProp.getName()] = newProp.getName();
1355+
}
1356+
// Sort the fields by field id.
1357+
llvm::sort(newProps, [](auto &p, auto &q) {
1358+
return p.getFieldID() < q.getFieldID();
1359+
});
1360+
// Return the merged inner symbol.
1361+
return hw::InnerSymAttr::get(context, newProps);
1362+
}
1363+
return hw::InnerSymAttr();
1364+
}
1365+
13191366
// Record the symbol name change of the operation or any of its ports when
13201367
// merging two operations. The renamed symbols are used to update the
13211368
// target of any NLAs. This will add symbols to the "to" operation if needed.
@@ -1324,22 +1371,20 @@ struct Deduper {
13241371
Operation *from) {
13251372
// If the "from" operation has an inner_sym, we need to make sure the
13261373
// "to" operation also has an `inner_sym` and then record the renaming.
1327-
if (auto fromSym = getInnerSymName(from)) {
1328-
auto toSym =
1329-
getOrAddInnerSym(to, [&](auto _) -> hw::InnerSymbolNamespace & {
1330-
return getNamespace(toModule);
1331-
});
1332-
renameMap[fromSym] = toSym;
1374+
if (auto fromInnerSym = dyn_cast<hw::InnerSymbolOpInterface>(from)) {
1375+
auto toInnerSym = cast<hw::InnerSymbolOpInterface>(to);
1376+
if (auto newSymAttr = mergeInnerSymbols(renameMap, toModule,
1377+
toInnerSym.getInnerSymAttr(),
1378+
fromInnerSym.getInnerSymAttr()))
1379+
toInnerSym.setInnerSymbolAttr(newSymAttr);
13331380
}
13341381

13351382
// If there are no port symbols on the "from" operation, we are done here.
13361383
auto fromPortSyms = from->getAttrOfType<ArrayAttr>("portSymbols");
13371384
if (!fromPortSyms || fromPortSyms.empty())
13381385
return;
13391386
// We have to map each "fromPort" to each "toPort".
1340-
auto &moduleNamespace = getNamespace(toModule);
13411387
auto portCount = fromPortSyms.size();
1342-
auto portNames = to->getAttrOfType<ArrayAttr>("portNames");
13431388
auto toPortSyms = to->getAttrOfType<ArrayAttr>("portSymbols");
13441389

13451390
// Create an array of new port symbols for the "to" operation, copy in the
@@ -1351,27 +1396,12 @@ struct Deduper {
13511396
newPortSyms.assign(toPortSyms.begin(), toPortSyms.end());
13521397

13531398
for (unsigned portNo = 0; portNo < portCount; ++portNo) {
1354-
// If this fromPort doesn't have a symbol, move on to the next one.
1355-
if (!fromPortSyms[portNo])
1356-
continue;
1357-
auto fromSym = cast<hw::InnerSymAttr>(fromPortSyms[portNo]);
1358-
1359-
// If this toPort doesn't have a symbol, assign one.
1360-
hw::InnerSymAttr toSym;
1361-
if (!newPortSyms[portNo]) {
1362-
// Get a reasonable base name for the port.
1363-
StringRef symName = "inner_sym";
1364-
if (portNames)
1365-
symName = cast<StringAttr>(portNames[portNo]).getValue();
1366-
// Create the symbol and store it into the array.
1367-
toSym = hw::InnerSymAttr::get(
1368-
StringAttr::get(context, moduleNamespace.newName(symName)));
1369-
newPortSyms[portNo] = toSym;
1370-
} else
1371-
toSym = cast<hw::InnerSymAttr>(newPortSyms[portNo]);
1372-
1373-
// Record the renaming.
1374-
renameMap[fromSym.getSymName()] = toSym.getSymName();
1399+
if (auto newPortSym = mergeInnerSymbols(
1400+
renameMap, toModule,
1401+
llvm::cast_if_present<hw::InnerSymAttr>(newPortSyms[portNo]),
1402+
cast<hw::InnerSymAttr>(fromPortSyms[portNo]))) {
1403+
newPortSyms[portNo] = newPortSym;
1404+
}
13751405
}
13761406

13771407
// Commit the new symbol attribute.

test/Dialect/FIRRTL/dedup.mlir

+34-9
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,12 @@ firrtl.circuit "Annotations" {
161161

162162
// Should deduplicate wires where only one has a symbol
163163
// CHECK: %h = firrtl.wire sym @h : !firrtl.uint
164-
// CHECK: %i = firrtl.wire sym @sym : !firrtl.uint
164+
// CHECK: %i = firrtl.wire sym @p : !firrtl.uint
165165
%h = firrtl.wire sym @h : !firrtl.uint
166166
%i = firrtl.wire : !firrtl.uint
167167

168168
// Should merge multiple different symbols.
169-
// CHECK: %j = firrtl.wire sym [<@sym_0,0,public>, <@j,1,private>]
169+
// CHECK: %j = firrtl.wire sym [<@q,0,private>, <@j,1,private>] : !firrtl.bundle<a: uint<1>>
170170
%j = firrtl.wire sym [<@j, 1, private>] : !firrtl.bundle<a: uint<1>>
171171
}
172172
// CHECK-NOT: firrtl.module private @Annotations1
@@ -243,6 +243,31 @@ firrtl.circuit "PortAnnotations" {
243243
}
244244
}
245245

246+
// Check that we are merging inner symbols correctly when deduplicating.
247+
// CHECK-LABEL: firrtl.circuit "InnerSymOpTarget"
248+
firrtl.circuit "InnerSymOpTarget" {
249+
// CHECK: hw.hierpath private @path0 [@InnerSymOpTarget::@foo0, @Foo0::@A]
250+
hw.hierpath private @path0 [@InnerSymOpTarget::@foo0, @Foo0::@A]
251+
// CHECK: hw.hierpath private @path1 [@InnerSymOpTarget::@foo0, @Foo0::@C]
252+
hw.hierpath private @path1 [@InnerSymOpTarget::@foo0, @Foo0::@C]
253+
// CHECK: hw.hierpath private @path2 [@InnerSymOpTarget::@foo1, @Foo0::@A_0]
254+
hw.hierpath private @path2 [@InnerSymOpTarget::@foo1, @Foo1::@A]
255+
// CHECK: hw.hierpath private @path3 [@InnerSymOpTarget::@foo1, @Foo0::@C]
256+
hw.hierpath private @path3 [@InnerSymOpTarget::@foo1, @Foo1::@B]
257+
firrtl.module private @Foo0() {
258+
// CHECK: %w0 = firrtl.wire sym [<@A,0,private>, <@C,1,public>, <@A_0,2,private>]
259+
%w0 = firrtl.wire sym [<@A, 0, private>, <@C, 1, private>]: !firrtl.vector<uint<1>, 4>
260+
}
261+
firrtl.module private @Foo1() {
262+
%w1 = firrtl.wire sym [<@A, 2, private>, <@B, 1, public>]: !firrtl.vector<uint<1>, 4>
263+
}
264+
firrtl.module @InnerSymOpTarget() {
265+
firrtl.instance foo0 sym @foo0 @Foo0()
266+
// CHECK: firrtl.instance foo1 sym @foo1 @Foo0()
267+
firrtl.instance foo1 sym @foo1 @Foo1()
268+
}
269+
}
270+
246271
// Non-local annotations should have their path updated and bread crumbs should
247272
// not be turned into non-local annotations. Note that this should not create
248273
// totally new NLAs for the annotations, it should just update the existing
@@ -452,15 +477,15 @@ firrtl.circuit "ExtModuleTest" {
452477
// https://github.com/llvm/circt/issues/2713
453478
// CHECK-LABEL: firrtl.circuit "Foo"
454479
firrtl.circuit "Foo" {
455-
// CHECK: hw.hierpath private @nla_1 [@Foo::@b, @A::@a]
480+
// CHECK: hw.hierpath private @nla_1 [@Foo::@b, @A::@b_0]
456481
hw.hierpath private @nla_1 [@Foo::@b, @B::@b]
457-
// CHECK: firrtl.extmodule private @A(out a: !firrtl.clock sym @a [{circt.nonlocal = @nla_1}])
458-
firrtl.extmodule private @A(out a: !firrtl.clock) attributes {defname = "a"}
459-
firrtl.extmodule private @B(out a: !firrtl.clock sym @b [{circt.nonlocal = @nla_1}]) attributes {defname = "a"}
482+
// CHECK: firrtl.extmodule private @A(out a: !firrtl.clock sym @b_0 [{circt.nonlocal = @nla_1}], out b: !firrtl.clock sym @b)
483+
firrtl.extmodule private @A(out a: !firrtl.clock, out b: !firrtl.clock sym @b) attributes {defname = "a"}
484+
firrtl.extmodule private @B(out a: !firrtl.clock sym @b [{circt.nonlocal = @nla_1}], out b: !firrtl.clock) attributes {defname = "a"}
460485
firrtl.module @Foo() {
461-
%b0_out = firrtl.instance a @A(out a: !firrtl.clock)
462-
// CHECK: firrtl.instance b sym @b @A(out a: !firrtl.clock)
463-
%b1_out = firrtl.instance b sym @b @B(out a: !firrtl.clock)
486+
%a0, %b0 = firrtl.instance a @A(out a: !firrtl.clock, out b: !firrtl.clock)
487+
// CHECK: firrtl.instance b sym @b @A(out a: !firrtl.clock, out b: !firrtl.clock)
488+
%a1, %b1 = firrtl.instance b sym @b @B(out a: !firrtl.clock, out b: !firrtl.clock)
464489
}
465490
}
466491

0 commit comments

Comments
 (0)