Skip to content

[ImportVerilog] Support use-before-declare across module bodies#10350

Open
AmurG wants to merge 4 commits into
llvm:mainfrom
AmurG:amurg/uvm-pr3-importverilog-testbench-compat
Open

[ImportVerilog] Support use-before-declare across module bodies#10350
AmurG wants to merge 4 commits into
llvm:mainfrom
AmurG:amurg/uvm-pr3-importverilog-testbench-compat

Conversation

@AmurG
Copy link
Copy Markdown
Contributor

@AmurG AmurG commented Apr 28, 2026

This is a frontend-only follow-up to the recent ImportVerilog virtual-interface work. It makes --allow-use-before-declare usable across more module-body contexts without changing the default diagnostics when the option is absent.

The importer now predeclares module-scope variables and nets through generated scopes, expands interface instances after storage predeclaration, and pre-instantiates module instances so earlier procedural code can resolve later hierarchical references. Declaration initializers and net declaration assignments are lowered after the module body has populated the value map, then attached back to the original Moore declaration ops.

The implementation stays behind --allow-use-before-declare and does not add Arcilator/runtime behavior. The tests cover direct module declarations, generated scopes, interface instances, module instances, and broader expression/procedural compatibility matrices for common testbench-style patterns.

Testing:

  • cmake --build build --target circt-verilog -- -j2
  • cmake --build build --target circt-translate -- -j2
  • focused ImportVerilog lit coverage over the use-before-declare, testbench-compat, interface expansion, and virtual-interface tests
  • build/bin/llvm-lit -sv test/Conversion/ImportVerilog

@AmurG AmurG force-pushed the amurg/uvm-pr3-importverilog-testbench-compat branch from 74a9c58 to 8cfb5b6 Compare April 29, 2026 09:39
@AmurG AmurG changed the title [ImportVerilog] Support use-before-declare in module bodies [ImportVerilog] Support use-before-declare across module bodies Apr 29, 2026
@AmurG AmurG force-pushed the amurg/uvm-pr3-importverilog-testbench-compat branch 2 times, most recently from ae8a0a6 to 5eeb624 Compare May 6, 2026 04:23
@AmurG
Copy link
Copy Markdown
Contributor Author

AmurG commented May 9, 2026

@fabianschuiki Sorry about the prev PR state. Can this be reviewed ?

Copy link
Copy Markdown
Contributor

@fabianschuiki fabianschuiki left a comment

Choose a reason for hiding this comment

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

Doing a pre-pass over the AST to predeclare things is a great idea!

The tests look like they cover a lot of redundant areas, though. Could you trim down those tests to actually focus on the thing this PR enables? For example, we don't need to check that forward-declared variables work in every single possible expression or statement nesting: there are independent tests that check whether all expression and statement nestings work as expected. For the forward declaration, you only need a single check where a forward-declared variable is used as a named expression. Basically all variants that could trigger different code paths in your changes to Structure.cpp.

On the code organization: since the ModulePredeclaration struct is pretty substantial, could we move that out into its on root-level struct in the file (in an anonymous namespace), basically like the other module visitor?

Since you've made the nice effort of supporting use-before-def, I don't think we need the use-after-def code paths in ImportVerilog anymore -- emitting errors on use-before-def with the option disabled is purely a Slang frontend issue. In ImportVerilog, we can just always predeclare members, since that supports both modes.

One thing I noted: the pending assignments/initializers might not work if the initializer expression uses variables from the local context. Once you hit the initializer, the scope variables have gone out of scope, and you can no longer resolve them if they are referred to in the deferred declaration. Maybe this currently works because everything supported by ModulePredeclaration lands in one global value symbol context?

How about this: instead of supporting both pre-declaration true/false in the regular module body visitor, let's always do pre-declaration (ignoring the initializers), and then in the module body visitor when we hit a variable/net, we lookup the pre-declared variable (and assert/error if it's missing, that'd be a pre-declaration bug), and then code-gen the initializer there.

@AmurG AmurG force-pushed the amurg/uvm-pr3-importverilog-testbench-compat branch from 70ab57b to 90ab58d Compare May 15, 2026 06:59
@circt-bot
Copy link
Copy Markdown

circt-bot Bot commented May 15, 2026

Results of circt-tests run for 90ab58d compared to results for d50d967:

sv-tests

Changes in emitted diagnostics:

  • -3 total change
  • -2 error: unsupported module member: InstanceArray
  • +2 error: interface instance was not expanded ``
  • -1 error: unknown hierarchical name `arg`
  • -1 error: unknown hierarchical name `e2`
  • -1 error: unknown hierarchical name `mailbox_write`
  • -1 error: unknown hierarchical name `tmp`
  • -1 error: unknown hierarchical name `x`
  • -1 error: unsupported module member: CovergroupType
  • +1 error: unsupported statement: ProceduralAssign
  • +1 error: unsupported system call `$fwrite`
  • +1 error: unsupported type: CovergroupType

@circt-bot
Copy link
Copy Markdown

circt-bot Bot commented May 15, 2026

Results of circt-tests run for c07c1db compared to results for 90ab58d: no change to test results.

AmurG added 4 commits May 15, 2026 18:27
Predeclare module-scope variables, nets, interface instances, and module instances when slang is allowed to resolve names before their declarations. The predeclaration walk recurses through generated scopes, creates storage before expanding interfaces, and instantiates modules before earlier procedural code lowers, so forward references through local names, interface members, and hierarchical instance paths can resolve.

Declaration initializers and net declaration assignments are lowered after the module body has populated the value map, then attached back to the original Moore declaration ops. This keeps the compatibility behavior behind --allow-use-before-declare and preserves existing diagnostics when the flag is absent.

Add focused regression coverage for direct module declarations, generated scopes, interface instances, and module instances, plus broader expression/procedural compatibility matrices for common testbench patterns.
@AmurG AmurG force-pushed the amurg/uvm-pr3-importverilog-testbench-compat branch from c07c1db to 8055d01 Compare May 15, 2026 18:32
@circt-bot
Copy link
Copy Markdown

circt-bot Bot commented May 15, 2026

Results of circt-tests run for 8055d01 compared to results for be870d5:

sv-tests

Changes in emitted diagnostics:

  • -3 total change
  • -2 error: unsupported module member: InstanceArray
  • +2 error: interface instance was not expanded ``
  • -1 error: unknown hierarchical name `arg`
  • -1 error: unknown hierarchical name `e2`
  • -1 error: unknown hierarchical name `mailbox_write`
  • -1 error: unknown hierarchical name `tmp`
  • -1 error: unknown hierarchical name `x`
  • -1 error: unsupported module member: CovergroupType
  • +1 error: unsupported statement: ProceduralAssign
  • +1 error: unsupported system call `$fwrite`
  • +1 error: unsupported type: CovergroupType

@AmurG
Copy link
Copy Markdown
Contributor Author

AmurG commented May 16, 2026

@fabianschuiki leaner and better now hopefully

Comment on lines -4607 to +4609
// CHECK: [[C0:%.+]] = moore.constant 0 : i32
// CHECK: [[V0:%.+]] = moore.variable [[C0]] : <i32>
// CHECK: [[C1:%.+]] = moore.constant 0 : i32
// CHECK: [[V1:%.+]] = moore.variable [[C1]] : <i32>
// CHECK-DAG: [[C0:%.+]] = moore.constant 0 : i32
// CHECK-DAG: [[V0:%.+]] = moore.variable {{.*}} : <i32>
// CHECK-DAG: [[C1:%.+]] = moore.constant 0 : i32
// CHECK-DAG: [[V1:%.+]] = moore.variable {{.*}} : <i32>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Any chance you can maintain the checks for explicit SSA values like [[C0]] instead of a wildcard {{.*}}?

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.

2 participants