Skip to content

Conversation

@prithayan
Copy link
Contributor

@prithayan prithayan commented Nov 11, 2025

This PR enhances the existing --hw-flatten-modules pass with configurable heuristics, hierarchical path support, and module NLA protection for fine-grained control over module inlining.

The FlattenModules pass now supports:

  • Configurable inlining heuristics (empty, no-outputs, single-use, small modules)
  • HierPath updating when inlining instances with inner symbols
  • Inner symbol renaming using InnerSymbolNamespace to avoid conflicts

Inlining Heuristics (7 new options)

--inline-all             (default: true)  - Inline all private modules (original behavior)
--inline-empty           (default: true)  - Inline empty modules
--inline-no-outputs      (default: true)  - Inline modules with no outputs
--inline-single-use      (default: true)  - Inline single-use modules
--inline-small           (default: true)  - Inline small modules
--small-threshold        (default: 8)     - Size threshold for small modules
--inline-with-state      (default: false) - Allow inlining stateful modules

This PR also adds new lit tests for testing these enhancements.

@prithayan prithayan requested a review from darthscsi as a code owner November 11, 2025 04:34
@prithayan prithayan marked this pull request as draft November 11, 2025 04:46
@prithayan prithayan changed the title [HW] Add HWInliner pass for inlining private HW modules [HW] Enhance FlattenModules pass with configurable inlining heuristics Nov 11, 2025
@prithayan prithayan force-pushed the dev/pbarua/hwinliner branch 2 times, most recently from f3e4ccf to c921ca7 Compare November 11, 2025 17:12
@prithayan prithayan marked this pull request as ready for review November 12, 2025 18:19
Copy link
Member

@seldridge seldridge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

I didn't review the inner sym / hierarchical path update logic in much detail, though. I realize this was probably the most important thing to review. However, the tests are doing the right thing!

Comment on lines +71 to +79
InnerSymbolNamespace *ns, HWModuleOp parentModule,
HWModuleOp sourceModule,
DenseMap<StringAttr, StringAttr> *symMapping,
mlir::AttrTypeReplacer *replacer, HierPathTable *pathsTable,
hw::InnerRefAttr instanceRef)
: InlinerInterface(context), prefix(prefix), ns(ns),
parentModule(parentModule), sourceModule(sourceModule),
symMapping(symMapping), replacer(replacer), pathsTable(pathsTable),
instanceRef(instanceRef) {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This constructor may be unnecessary if you use struct initialization. E.g., PrefixingInliner{a, b, c}. I don't know if there's any utility in adding a one-to-one constructor here as opposed to the {} one.

Comment on lines +31 to +42
// Test that public modules are not inlined
// CHECK-LABEL: hw.module @DontInlinePublic
hw.module @DontInlinePublic(in %x: i4, out y: i4) {
// CHECK-NEXT: hw.instance "pub" @PublicModule
%0 = hw.instance "pub" @PublicModule(a: %x: i4) -> (b: i4)
hw.output %0 : i4
}
// CHECK: hw.module @PublicModule
hw.module @PublicModule(in %a: i4, out b: i4) {
%0 = comb.add %a, %a : i4
hw.output %0 : i4
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aside to think about: we could inline these still, just we can't delete them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, based on the options, if we inline it still keep the public module.

// CHECK-NOT: hw.instance
// CHECK-NEXT: %[[V0:.+]] = comb.add %x, %x
// CHECK-NEXT: %[[W0:.+]] = hw.wire %[[V0]] sym @[[SYM:wire_op]]
// CHECK-NEXT: sv.verbatim "ref to {{[{][{]}}0{{[}][}]}}" {symbols = [#hw.innerNameRef<@InlineWithInnerRef::@[[SYM]]>]}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mega-nit: There is only this one verbatim and the test could avoid the {{{{{}}}}}} shenanigans with just:

CHECK-NEXT: sv.verbatim {{.*}} {symbols = ...}

Or:

CHECK-NEXT: sv.verbatim
CHECK-SAME:   {symbols = [#hw.innerNameRef<@InlineWithInnerRef::@[[SYM]]>]}

Ditto throughout.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, done.

Only evaluate the smallThreshold when inlineSmall is enabled. This avoids
unnecessary computation when the small module heuristic is disabled and
makes the code more efficient and clearer in intent.
Rename all pass options to follow the convention of prefixing with the
dialect name:
- inline-all -> hw-inline-all
- inline-empty -> hw-inline-empty
- inline-no-outputs -> hw-inline-no-outputs
- inline-single-use -> hw-inline-single-use
- inline-small -> hw-inline-small
- small-threshold -> hw-small-threshold
- inline-with-state -> hw-inline-with-state

This makes the options consistent with other HW dialect passes and
improves clarity when using multiple passes together.
@prithayan prithayan force-pushed the dev/pbarua/hwinliner branch from c921ca7 to ae0b612 Compare December 2, 2025 00:25
@prithayan
Copy link
Contributor Author

@seldridge thanks a lot for the detailed review, and sorry for the long delay in addressing them. I'll merge this once the CI passes.

@prithayan prithayan merged commit fabc335 into main Dec 2, 2025
7 checks passed
@prithayan prithayan deleted the dev/pbarua/hwinliner branch December 2, 2025 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants