Unified Verilog hierarchical reference resolver#1508
Draft
gitmodimo wants to merge 3 commits into
Draft
Conversation
4423b90 to
8c377f7
Compare
added 2 commits
April 27, 2026 12:37
Bring Verilog hierarchical references to a single elab-time
resolver consuming a language-neutral scope tree shared with
VHDL. Replaces the previous stack of sem-time + elab-time
mechanisms with one pass run after the full instance tree is
built.
Resolver:
* Anchor-table dispatch on V_HIER_REF.I_SUBKIND
(RELATIVE / $root / $unit) — adding a new anchor (this/super)
is an enum value plus a dispatch case, not a rewrite.
* Synthetic-root sentinel always present at the head of the
walk-up chain; bare <top>.<sig> references reach a sibling
top under multi-top elab via library-prefix synthesis.
* The resolver writes the resolved target's dotted path on
I_IDENT2 and re-binds I_REF to the tail decl. I_VALUE is
not written — it would be a cross-tree edge that
object_visit follows, letting re-deferred resolution passes
fan out into V_HIER_REFs in unrelated bodies.
Named procedural blocks (IEEE 1800-2017 §23.6) become
hier-addressable scopes:
* V_BLOCK gains an I_IDENT2 slot for the canonical scope name.
* elab_verilog_block recurses through procedural control-flow
on the parent's tree before vlog_new_instance copies it,
pre-stamping every nested named V_BLOCK with its dotted.
* Named V_BLOCKs are added to copy_instance_pred and
copy_generate_pred so each clone / iteration owns its own
node and its own per-clone scope name.
* vlog_lower_stmts pushes a named-block frame on entry to a
stamped V_BLOCK; V_REFs to block-local decls reach the
wrapper unit's signal storage via link_package + link_var.
link_package is re-emitted at every use site so the SSA
value is live in the current basic block.
* elab_verilog_proc_blocks descends V_INITIAL/V_ALWAYS bodies
through the full procedural-control-flow set
(V_IF/V_COND, V_CASE/V_CASE_ITEM, V_FOR_LOOP, V_FOREVER,
V_REPEAT, V_WHILE, V_DO_WHILE, V_WAIT, V_TIMING) to reach
named V_BLOCKs at any depth.
* elab_verilog_sub_blocks descends procedural control-flow
at the top level of a V_BLOCK body too.
Cache-hit clone-sharing is gated: bodies containing per-clone
state (tracked through vlog_has_per_clone_state — single source
of truth shared by copy_instance_pred, copy_generate_pred, and
elab_module_needs_per_clone_state) get a fresh ei (and therefore
a fresh MIR) per clone.
DEBUG assertions:
* Forward — every Verilog body deferred for hier-ref
resolution must have a scope_tree entry.
* Reverse — every named V_BLOCK reachable inside any deferred
body must have I_IDENT2 set and that dotted must hash to a
scope_tree entry.
* Post-resolver — every V_HIER_REF must have I_IDENT2 set and
I_VALUE clear.
Other corrections that landed alongside the unified resolver
work and are needed for the regression set to pass: PSL next_a
support (see psl-fsm.c, psl-dump.c), terminal-link escape
handling on diag output, and removal of obsolete sem-time
hier-ref errors.
Comprehensive test coverage for the unified hierarchical-reference
resolver and named-procedural-block scopes:
* vlog46-99 — pre-existing Verilog regression tests that became
reachable through the unified resolver, covering downward,
upward, and cross-module references; generate-block scopes;
forward continuous-assign XMRs; force/release; \$root and
rooted-absolute paths; static task-local hier-refs.
* vlog100 — multi-top elaboration baseline.
* vlog136 — module-instance XMR through a VHDL top, with
nested named procedural blocks and continuous assigns
reading block-local values.
* vlog137 — multi-top elab with VHDL siblings.
* vlog138 — cross-top hier-ref under multi-top elab; both
\$root.<sibling>.<sig> and bare <sibling>.<sig> exercised
in both directions, with a 3-top phase.
* vlog139 — Verilog hier-ref reaching across a VHDL parent
boundary to a sibling Verilog instance.
* vlog140 — multi-instance module containing a named
procedural block; per-clone storage and force/release.
* vlog141 — VHDL component-binding two instances of a
Verilog module that contains a named V_BLOCK.
* vlog142 — named procedural block inside a for-generate
iteration; per-iteration deep-copy.
* vlog143 — named procedural blocks nested inside
procedural V_IF/V_CASE/V_FOR_LOOP.
* vlog144 — multi-instance module containing only a
V_HIER_REF (no named blocks); pure-V_HIER_REF clone gating.
Unit-test additions:
* test/vlog/href_bracket.v, href_disable.v, href_nbtrigger.v,
href_taskcall.v — parse-time + sem-time pinning of the
hier-path forms exercised through the resolver.
* test/test_vlog.c — updated test_href1 to expect elaboration-
time resolution rather than parse-time errors for upward
references.
PSL test additions inherited alongside the resolver work
(psl23.vhd next_a case, gold/psl23.txt, gold/issue1512.xml,
issue1512.vhd, issue1433.sh fixed-string grep fix) reflect
correctness fixes that landed in the same series and are
required for the regression set to pass.
0cba1f4 to
9f964cf
Compare
Two coupled additions: vlog_lower_systf_param: V_REF whose decl is a V_VAR_DECL inside an active named-procedural-block frame is now eagerly evaluated through the named-block-frame redirect (link_package + link_var into the wrapper unit's signal storage), instead of returning MIR_NULL_VALUE. Without this, $display "%h" on a block-local read empty because the standard VPI handle table doesn't address named-block locals — same visibility gap that motivated the named-block frame in vlog_lower_select. Mirrors the V_HIER_REF sys-task-param case. vlog145.sh: combined-corner regression — multi-top elab, named procedural block in one of the tops, run under reheat (-r). No existing test covered this combination: vlog136-144 each cover one or two corners but not all three. The named-block local must survive serialise/restore and the cross-top reference must work after rebuild. 4-flavour gate verified clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a single elab-time two-pass resolver that handles all hierarchical access patterns uniformly:
downward, upward, cross-instance, $root-anchored, and through generate blocks.
Why this is one large change
Two earlier local attempts tackled hier-refs incrementally: a sem-time downward resolver for
parent.child.signal within the lexical scope, and an elab-time upward resolver for cross-instance
access after the full tree was built. Neither generalised well — the sem-time pass set I_REF to the
prefix head which conflicted with later elab-time rebinding; cloned instances shared resolver state
across clones; and reheat had to independently recompute aliases that elab had already derived. Fixing
one edge case kept breaking another because two resolvers with different lifetime assumptions were
sharing the same node slots.
Those attempts didn't pan out, so this branch starts fresh with a unified design: one resolver, one
pass over the full instance tree, one alias computation shared by elab and reheat. The scope is large
because hierarchical references touch every pipeline stage — parser, sem, elab, lowering, JIT, reheat,
and VPI. Splitting it would leave intermediate states where some access patterns silently produce wrong
results.
Pipeline changes
(->, ->>), disable, and hier-ref task/function calls all route through it.
unconditionally — the elab resolver owns validation.
constant expressions in reg widths and generate conditions). A post-elab pass walks the full instance
tree and resolves everything else.
constants; runtime variables use link_package(alias) + link_var. Adds force/release lowering and
V_CONCAT non-blocking assignment lvalue support.
cross-instance access.
correctly.
Design disciplines (enforced by the implementation)
Also included
Blocked spec tests
19 spec tests are committed but commented out in testlist.txt, each annotated with its specific
orthogonal blocker (e.g. V_FORK sem, defparam, program/endprogram). When each blocker lands,
uncommenting the test is the only change needed.