Skip to content

rsz: build equivalence classes without the liberty dont_use filter - #11042

Open
dsengupta0628 wants to merge 2 commits into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:or_dontuse_rsz
Open

rsz: build equivalence classes without the liberty dont_use filter#11042
dsengupta0628 wants to merge 2 commits into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:or_dontuse_rsz

Conversation

@dsengupta0628

Copy link
Copy Markdown
Contributor

Problem

unset_dont_use had no effect on sizing. OpenROAD keeps "don't use" in two
places: the liberty attribute sta::LibertyCell::dont_use_, written only by the
liberty reader, and the resizer's effective policy rsz::Resizer::dont_use_,
written by set_dont_use / unset_dont_use and seeded from liberty by
copyDontUseFromLiberty(). sta::EquivCells filters on the liberty attribute,
so a cell re-enabled with unset_dont_use never entered the equivalence table:
getSwappableCells got a null class, returned only the source cell, and the
instance stayed at its original drive.

This bites vendor flows that mark a multibit-flop family dont_use in liberty
and re-enable it before clustering. MBFF::IsValidTray consults only
Resizer::dont_use_, so clustering produced the trays; sizing then could not
find a single candidate for them and the flops stayed at minimum drive with bad
slew.

Change

The resizer builds its own equivalence classes over the link cells instead of
calling sta::Sta::makeEquivCells, with no dont_use filtering of any kind.
Policy is applied at query time, where swappable_cells_cache_ invalidation
already lives. Bucketing uses a necessary condition (sta::hashCell's port
term) and classes are refined with the public sta::equivCells(), so the
equivalence maths is unchanged.

src/sta is untouched. Consolidating the two dont_use fields is the real fix
and is deferred for now; this index can be deleted then.

Two latent problems surfaced and are fixed here because they blocked the above:

  • getSwappableCells now returns {source_cell} for a dont_use source. Once
    such a cell had a real class, the filter dropped the source itself and left
    only the other members, so instances of dont_use cells were sized away
    against a target_load_map_ baseline of 0 (maximum distance, any candidate
    wins).
  • target_load_map_ is reset in setDontUse / resetDontUse /
    postReadLiberty. It was built once excluding dont_use cells and never
    rebuilt, so unset_dont_use issued after a preamble left the re-enabled cells
    with target load 0 and ranked them worst.

Timing::equivCells moves to the resizer index, applies dontUse, always
reports the queried cell, and null-checks staToDb. The GUI keeps its own STA
table; now that rsz no longer touches it, it can no longer clobber the resizer
mid-session.

Test

src/rsz/test/unset_dont_use_equiv_cells.tcl, registered in CMake and Bazel.
Nangate45 FILLCELL_X* is a liberty-dont_use family with drive variants, so
no synthetic .lib is needed. report_equiv_cells FILLCELL_X1 gives 1
candidate (itself, matching stock upstream) and 3 after unset_dont_use;
set_dont_use BUF_X4 then drops it from BUF_X2's list, showing policy is
applied at query time and not baked into the index.

Type of Change

  • Bug fix

Impact

improves slew after MBFF clustering and resizing

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).

@dsengupta0628 dsengupta0628 self-assigned this Jul 31, 2026
@dsengupta0628
dsengupta0628 marked this pull request as ready for review July 31, 2026 17:57
@dsengupta0628
dsengupta0628 requested review from a team as code owners July 31, 2026 17:57

@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 refactors how cell equivalence classes are managed, moving the logic from OpenSTA's equivalence table directly into the Resizer. This allows cells that are marked as dont_use in liberty but subsequently re-enabled with unset_dont_use to remain viable sizing candidates. The review feedback points out two critical robustness issues: a potential null pointer dereference in Timing::equivCells if the resizer is unavailable, and another potential null pointer dereference in equivCellKey if a port lacks a defined direction.

Comment thread src/Timing.cc
Comment on lines +404 to +411
sta::LibertyCellSeq* equiv_cells = resizer->equivCells(libcell);
if (equiv_cells) {
for (sta::LibertyCell* equiv_cell : *equiv_cells) {
// The classes are built without dont_use filtering. The cell asked
// about is always reported, dont_use or not.
if (equiv_cell != libcell && resizer->dontUse(equiv_cell)) {
continue;
}

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.

high

If design_->getResizer() returns nullptr (e.g., if the resizer is not initialized or not available in the current session), calling resizer->equivCells(libcell) and resizer->dontUse(equiv_cell) will result in a segmentation fault. Adding a null check for resizer and falling back to sta->equivCells(libcell) ensures robustness.

Suggested change
sta::LibertyCellSeq* equiv_cells = resizer->equivCells(libcell);
if (equiv_cells) {
for (sta::LibertyCell* equiv_cell : *equiv_cells) {
// The classes are built without dont_use filtering. The cell asked
// about is always reported, dont_use or not.
if (equiv_cell != libcell && resizer->dontUse(equiv_cell)) {
continue;
}
sta::LibertyCellSeq* equiv_cells = resizer ? resizer->equivCells(libcell) : sta->equivCells(libcell);
if (equiv_cells) {
for (sta::LibertyCell* equiv_cell : *equiv_cells) {
// The classes are built without dont_use filtering. The cell asked
// about is always reported, dont_use or not.
if (equiv_cell != libcell && resizer && resizer->dontUse(equiv_cell)) {
continue;
}

Comment thread src/rsz/src/Resizer.cc
Comment on lines +2456 to +2465
static unsigned equivCellKey(const sta::LibertyCell* cell)
{
unsigned key = 0;
sta::LibertyCellPortIterator port_iter(cell);
while (port_iter.hasNext()) {
const sta::LibertyPort* port = port_iter.next();
key += sta::hashString(port->name()) * 3 + port->direction()->index() * 5;
}
return key;
}

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.

high

In Liberty files, some ports (such as special or malformed ports) might not have a defined direction, causing port->direction() to return nullptr. Dereferencing it directly with port->direction()->index() will lead to a segmentation fault. Adding a null check for port->direction() prevents potential crashes.

static unsigned equivCellKey(const sta::LibertyCell* cell)
{
  unsigned key = 0;
  sta::LibertyCellPortIterator port_iter(cell);
  while (port_iter.hasNext()) {
    const sta::LibertyPort* port = port_iter.next();
    const sta::PortDirection* dir = port->direction();
    unsigned dir_index = dir ? dir->index() : 0;
    key += sta::hashString(port->name()) * 3 + dir_index * 5;
  }
  return key;
}

@dsengupta0628

dsengupta0628 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Gemini hallucination in review:

  1. getResizer(): OpenRoad::resizer_ is allocated unconditionally in init() and only freed in the destructor; getResizer() is a plain accessor. Timing::makeEquivCells() immediately above already calls through it unchecked. So this cannot be null.

  2. Regarding port->direction(): ConcretePort initialises direction_ to PortDirection::unknown(), and is never null. equivCellKey intentionally mirrors OpenSTA's hashPort in EquivCells.cc, which has this line port->direction()->index() * 5U unguarded too and has no null dereferencing ever.

@dsengupta0628
dsengupta0628 requested review from maliberty and povik July 31, 2026 19:52
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