Skip to content

rcx: fix bench_wires segfault on duplicate pattern names - #11154

Open
gadfort wants to merge 2 commits into
The-OpenROAD-Project:masterfrom
gadfort:rcx-bench-wires-dup-pattern-names
Open

rcx: fix bench_wires segfault on duplicate pattern names#11154
gadfort wants to merge 2 commits into
The-OpenROAD-Project:masterfrom
gadfort:rcx-bench-wires-dup-pattern-names

Conversation

@gadfort

@gadfort gadfort commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Reopens #7897 — the merged fix 788fca19f1 is narrower than the bug.

Symptom

bench_wires -all on a technology with thick top metal and a coarse database
unit emits a duplicate-net warning and then dies:

[INFO RCX-0055] Finished 72 bench measurements for pattern MET_OVER_MET
[WARNING RCX-0409] Cannot create net R6_M3oM0_W2200W2200_S00000S01800_3, duplicate net
Signal 11 received

Symbolized:

odb::dbNet::get1stBTerm()
rcx::extMeasure::createNetSingleWire()
rcx::extRCModel::writeBenchWires_DB_res()
rcx::extRCModel::benchDB_WS()
rcx::extRCModel::linesOverBench()

Root cause

The pattern name is doubly quantized before it reaches the net name:

  • setTargetParams floors the coupling spacing to a 10nm grid
    (n2 = (n2 / 10) * 10)
  • mkNet_prefix then rescales by (1000 * s_nm) / dbunit with integer
    division

So two entries of the spacing table built in benchDB_WS collide whenever they
land in the same bucket, even though they are distinct as doubles.

Concretely, on a layer with WIDTH 2.200 / SPACING 1.800 / PITCH 4.000 and
DATABASE MICRONS 2000, the table holds both SPACING and PITCH - WIDTH.
Those are equal in exact arithmetic, differ by exactly one ULP as doubles, and
both round to 1800 in the name.

788fca19f1 guarded this with std::fabs(spacing - s1) > DBL_EPSILON. That is
insufficient in two ways:

  1. It guards only that one hardcoded pair. The s1 and s2 entries added by
    the later loop iterations are never compared against anything.
  2. DBL_EPSILON is an absolute tolerance, meaningful only for values near
    1.0. One ULP of 3.6 is 4.44e-16, which is > DBL_EPSILON, so the guard
    does not fire and both values enter the table.

More fundamentally, because the name is quantized to 10nm, two spacings that
are genuinely several nm apart can also collide. No floating point tolerance
can catch that.

Fix

  • Dedupe on the generated name. benchDB_WS now skips any
    (width, spacing, spacing2) combination whose pattern name was already
    emitted, keyed on _wireDirName itself. That is the value that actually has
    to be unique, so it covers both collision mechanisms and any future one. The
    set is local to one benchDB_WS call, which is correct — the name embeds
    the pattern kind and the metal/under-metal pair, so those cannot alias
    across calls.
  • Make a null net a hard error. All three extMeasure::create*NetSingleWire*
    wrappers dereferenced the null that dbCreateNetUtil::createNetSingleWire
    returns. They now report RCX-529 / RCX-530 / RCX-531. Reaching this is
    a bug, so it should fail loudly rather than crash or silently drop wires.
  • Give the solver flow a logger. The four extRCModel::lines* pattern
    generators built their extMeasure with a null logger, and one of them
    reaches createDiagNetSingleWire. Passing the model logger keeps the new
    error reportable instead of turning into another null dereference.

The DBL_EPSILON guard is left in place. It is now redundant, but removing it
would add entries to the spacing table and change pattern output for every
technology where it currently fires — out of scope for a crash fix.

Testing

New regression src/rcx/test/dup_pattern_names, registered in both CMake and
Bazel. It ships a minimal hand-written routing-only tech LEF (no vendor file)
carrying the geometry that triggers the collision, and asserts the colliding
pattern is generated exactly once with all of its wires. Before this change the
test reproduces the reported crash with the identical net name.

bazel test //src/rcx/test/... — 18/18 pass. generate_pattern is unchanged,
confirming the dedupe does not over-fire on technologies that were already
fine.

Type of Change

  • Bug fix

Impact

No segfault on bench creation

Verification

  • I have verified that the local build succeeds (./etc/Build.sh).
  • I have run the relevant tests and they pass.
  • My code follows the repository's formatting guidelines.
  • I have included tests to prevent regressions.
  • I have signed my commits (DCO).

Related Issues

#7897

bench_wires names each pattern after its widths and spacings rounded onto
a coarse integer nm grid: setTargetParams floors the coupling spacing to
10nm, and mkNet_prefix then rescales it by 1000/dbunit with integer
division. Two entries of the spacing table built in benchDB_WS can
therefore land on the same pattern name while being distinct as doubles.

When that happened the second pattern asked odb for a net name that
already existed, got a null back with warning RCX-0409, and rcx
dereferenced it:

  odb::dbNet::get1stBTerm()
  rcx::extMeasure::createNetSingleWire()
  rcx::extRCModel::writeBenchWires_DB_res()
  rcx::extRCModel::benchDB_WS()
  rcx::extRCModel::linesOverBench()

788fca1 addressed one instance of this by comparing SPACING against
PITCH-WIDTH at DBL_EPSILON, but that only guards a single hardcoded pair,
and an absolute DBL_EPSILON tolerance is only meaningful for values near
1.0 - a technology with thick top metal and a coarse database unit
collides at one ULP of a much larger value and slips through. Spacings
that are several nm apart can also collide, since the name is quantized
to 10nm, which no floating point tolerance can catch.

Dedupe on the generated name instead, which is the value that actually
has to be unique, and make a null net a hard error rather than a
segfault. The solver pattern flow built its extMeasure with a null
logger, so pass the model logger there to keep that error reportable.

Fixes The-OpenROAD-Project#7897

Signed-off-by: Peter Gadfort <gadfort@zeroasic.com>
@gadfort
gadfort requested a review from a team as a code owner August 16, 2026 01:26
@gadfort
gadfort requested a review from AcKoucher August 16, 2026 01:26

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request addresses a crash caused by duplicate pattern names during bench wire generation by tracking generated pattern names in a set and skipping duplicates. It also updates extMeasure to use the proper logger and adds error logging when net creation fails. The review feedback highlights critical safety issues where the code still dereferences potentially null net pointers immediately after logging the error, which would lead to crashes. Additionally, the feedback recommends adding null checks for _wireDirName and logger_ to prevent undefined behavior and crashes in legacy execution paths.

Comment thread src/rcx/src/extBenchDB.cpp
Comment thread src/rcx/src/extmeasure.cpp
Comment thread src/rcx/src/extmeasure.cpp
Comment thread src/rcx/src/extmeasure.cpp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant