Skip to content

fix(verilog): link a module instantiation to the local definition, not a phantom - #3675

Open
rajatnagda45 wants to merge 1 commit into
Graphify-Labs:v8from
rajatnagda45:fix/verilog-instantiation-phantom-node
Open

rajatnagda45 wants to merge 1 commit into
Graphify-Labs:v8from
rajatnagda45:fix/verilog-instantiation-phantom-node

Conversation

@rajatnagda45

Copy link
Copy Markdown
Contributor

Problem

A Verilog/SystemVerilog module instantiation minted its target id with a bare, unscoped _make_id(name), while a module definition uses _make_id(stem, name). The two ids never matched, so instantiating a module defined in the same file:

module counter(input clk, output reg [7:0] count);
  // ...
endmodule

module top;
  counter u1(.clk(clk), .count(c));
endmodule

split counter into two nodes:

  • t_counter — the real definition (with a body, ports, everything), and
  • counter — a bare-id phantom that carried the instantiates edge.

So top's dependency on the module you actually defined pointed at an empty duplicate, and the real module looked like nothing ever instantiated it — exactly the wrong picture for a hardware design, where the module hierarchy is the architecture.

Worse, the phantom was sourced (it got this file's path + a line number), which bakes the file path into the id and blocks the corpus-level rewire that normally collapses cross-file reference stubs onto their real definitions — the same #1402 phantom-duplicate shape the other extractors deliberately avoid.

Fix

  • Pre-scan module names up front so a same-file instantiation resolves to the definition id (_make_id(stem, name)). Verilog allows instantiating a module declared later in the file / in any order across the compilation unit, so a forward scan is required rather than relying on declaration order.
  • A module defined in another file now becomes a SOURCELESS stub (empty source_file/source_location), so the corpus-level rewire collapses it onto the real definition instead of leaving one duplicate per instantiating file. This mirrors the Go extractor's ensure_named_node.

Test

Two new cases in tests/test_languages.py, both failing before and passing after:

  • test_systemverilog_instantiation_resolves_to_local_moduletopleaf in sample.sv links to the single, sourced leaf definition, no duplicate;
  • test_systemverilog_cross_file_instantiation_is_sourceless_stub — an instantiation of a module defined elsewhere emits a sourceless stub target.

All 407 language-extractor tests pass.

…t a phantom

A module instantiation minted its target id with a bare, unscoped
_make_id(name), while a module definition uses _make_id(stem, name). The
two never matched, so instantiating a module defined in the same file:

    module counter(...); endmodule
    module top;
      counter u1(...);
    endmodule

split `counter` into a real definition node and a second, sourced phantom
that carried the `instantiates` edge. `top`'s dependency on the module you
actually defined was invisible, and because the phantom was sourced it also
baked this file's path into the id and blocked the corpus-level rewire that
would otherwise collapse it (the Graphify-Labs#1402 duplicate shape the other
extractors guard against).

Pre-scan module names so a same-file instantiation (Verilog allows
forward/any-order instantiation) resolves to the definition id. A module
defined in another file becomes a SOURCELESS stub instead, so the
corpus-level rewire collapses it onto the real definition rather than a
per-file duplicate — mirroring the Go extractor's ensure_named_node.

Adds regression coverage for both the local-definition link and the
cross-file sourceless stub.
@rajatnagda45

Copy link
Copy Markdown
Contributor Author

Spotted a phantom-duplicate module node here: instantiations minted a bare-id target while definitions are stem-scoped, so a module got split into a real def + an empty instantiation target (and the sourced stub blocked the rewire, the #1402 shape). Handled it the way the Go extractor does cross-file stubs. @safishamsi curious whether the sourceless-stub approach is what you'd want for the cross-file case.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Fixes Verilog module instantiations that never linked to their target definition: extract_verilog now pre-scans all module_declaration names so an instantiation of a same-file module resolves to that module's scoped definition id (_make_id(stem, name)) instead of minting a bare-id phantom duplicate. Cross-file targets emit a sourceless stub (empty source_file) so the corpus-level rewire can later collapse it onto the real definition, rather than baking this file's path into the id and blocking the rewire. Adds two tests covering the local-resolution and sourceless-stub paths.

No blocking issues surfaced. 2 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 585 functions depend on the 585 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract_verilog() — 12 callers, 7 callees
  • new: _augment_systemverilog_semantics() — 1 callers, 7 callees

Verification — 585 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 585 function(s) in the blast radius were not formally verified this run

Test selection

Test selection

1 of 286 test file(s) selected (0%) via static blast radius.

  • tests/test_languages.py — impact, changed-test

Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.

Formal verification

Could not verify: Could not verify extract\_verilog.

The verifier did not have enough to check extract\_verilog, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: no capturable inputs from the test suite; property tier: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

· 2 more finding(s) on lines outside this diff (see the check run).

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.

1 participant