[ImportVerilog] Support use-before-declare across module bodies#10350
[ImportVerilog] Support use-before-declare across module bodies#10350AmurG wants to merge 4 commits into
Conversation
74a9c58 to
8cfb5b6
Compare
ae8a0a6 to
5eeb624
Compare
|
@fabianschuiki Sorry about the prev PR state. Can this be reviewed ? |
fabianschuiki
left a comment
There was a problem hiding this comment.
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.
70ab57b to
90ab58d
Compare
|
Results of circt-tests run for 90ab58d compared to results for d50d967: sv-testsChanges in emitted diagnostics:
|
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.
c07c1db to
8055d01
Compare
|
Results of circt-tests run for 8055d01 compared to results for be870d5: sv-testsChanges in emitted diagnostics:
|
|
@fabianschuiki leaner and better now hopefully |
| // 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> |
There was a problem hiding this comment.
Any chance you can maintain the checks for explicit SSA values like [[C0]] instead of a wildcard {{.*}}?
This is a frontend-only follow-up to the recent ImportVerilog virtual-interface work. It makes
--allow-use-before-declareusable 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-declareand 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 -- -j2cmake --build build --target circt-translate -- -j2build/bin/llvm-lit -sv test/Conversion/ImportVerilog