Skip to content

rsz: Add UnbufferMt to the PARALLEL_EVAL WNS tail - #11802

Merged
maliberty merged 6 commits into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:secure-rsz-unbuffer-mt
Oct 18, 2026
Merged

maliberty merged 6 commits into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:secure-rsz-unbuffer-mt

Conversation

@jhkim-pii

@jhkim-pii jhkim-pii commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Part 9 of 12 of the PARALLEL_EVAL setup repair stack. It builds on #11801 and also shows the commits of parts 1 to 8; its own change is the last commit.

Summary

  • Add UnbufferMt, the Mt form of buffer removal, to the PARALLEL_EVAL WNS tail.

Changes

  • UnbufferMt checks on the main thread that a buffer on the target path may go and estimates the delay its removal saves; the removal is offered only when the estimate is a gain, and the batch timing update judges the real effect.
  • The WNS tail removes buffers only in single-move batches, because a removal cannot be reverted in place inside the batch journal.
  • -sequence accepts unbuffer; without -sequence the WNS tail adds UnbufferMt, and -skip_buffer_removal keeps it out.
flowchart LR
  D["driver"] --> B["buffer on the target path"] --> L["loads"]
  D -. "after removal" .-> L
Loading

Stack

  1. test: Add LEGACY repair setup path regressions #11794 test: Add LEGACY repair setup path regressions
  2. rsz: Remove MT1, MEASURED_VT_SWAP, LEGACY_MT and the RSZ_* knobs #11795 rsz: Remove MT1, MEASURED_VT_SWAP, LEGACY_MT and the RSZ_* knobs
  3. rsz: Add the setup repair profiler #11796 rsz: Add the setup repair profiler
  4. rsz: Model side paths in the Mt delay estimator #11797 rsz: Model side paths in the Mt delay estimator
  5. rsz: Add the RebufferMt buffer-tree move #11798 rsz: Add the RebufferMt buffer-tree move
  6. rsz: Add the experimental PARALLEL_EVAL setup repair policy #11799 rsz: Add the experimental PARALLEL_EVAL setup repair policy
  7. rsz: Add SplitLoadMt to PARALLEL_EVAL #11800 rsz: Add SplitLoadMt to PARALLEL_EVAL
  8. rsz: Add SwapPinsMt to the PARALLEL_EVAL WNS tail #11801 rsz: Add SwapPinsMt to the PARALLEL_EVAL WNS tail
  9. rsz: Add UnbufferMt to the PARALLEL_EVAL WNS tail #11802 rsz: Add UnbufferMt to the PARALLEL_EVAL WNS tail (this PR)
  10. rsz: Add SizeDownFanoutMt to the PARALLEL_EVAL WNS tail #11803 rsz: Add SizeDownFanoutMt to the PARALLEL_EVAL WNS tail
  11. rsz: Add the per-move analysis and the rebuffer accuracy report #11804 rsz: Add the per-move analysis and the rebuffer accuracy report
  12. doc: Update the rsz setup repair architecture #11805 doc: Update the rsz setup repair architecture

@jhkim-pii jhkim-pii self-assigned this Oct 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR exceeds the recommended size of 1000 lines. Please make sure you are NOT addressing multiple issues with one PR. Note this PR might be rejected due to its size.

@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 introduces the experimental PARALLEL_EVAL policy for batch setup timing repair, replacing several older experimental policies. It adds multi-threaded implementations for key optimization moves (such as RebufferMt, SizeUpMt, SplitLoadMt, SwapPinsMt, and UnbufferMt), integrates a setup repair profiler, and refactors the delay estimator and target collector to support parallel execution. The review feedback highlights critical null pointer dereference risks in DelayEstimator.cc, UnbufferMtGenerator.cc, and SwapPinsMtGenerator.cc when resolving driver pins or top-level ports, and recommends using safer helper methods or adding defensive null checks.

Comment thread src/rsz/src/DelayEstimator.cc
Comment thread src/rsz/src/move/UnbufferMtGenerator.cc
Comment thread src/rsz/src/move/SwapPinsMtGenerator.cc
@jhkim-pii

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-17T05:51:50.869111Z c9a3413 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Something went wrong. Try again later by commenting “@codex review”.

Failed to sample tokens
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

What: the Mt delay estimator models the fanin side loads, the opposite
output transition, the latch D-side driver and the flip-flop setup and
hold change of a cell swap, and SizeUpMt and VtSwapMt charge them.

Why: scoring a swap on the selected path alone let a scored gain turn
into a loss after the timing update.

How: side models in DelayEstimator and SequentialTimingEstimator, input
cap budgets that workers can check without STA, and ArcDelayState
renamed to EstimatorContext. The side models are off by default.

Setup repair profiler review follow-ups: the profiler constructor takes
a RepairSetupProfiler::Mode instead of an int, CRIT_VT_SWAP samples WNS
and TNS before its sweep, and the Bazel TestResizerMt target drops the
--gtest_filter that skipped the profiler and fixture tests.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: RebufferMt, an Mt buffering move that plans a beam of Rebuffer
trees per driver and scores each with the buffer-tree estimator.

Why: buffering needs the same scored, comparable candidates as the cell
swaps before a batch policy can choose among them.

How: makeRebufferTreePlans() and applyRebufferTreePlan() in Rebuffer,
estimateBufferTreeWithSidePaths() in DelayEstimator, and current
required times after the endpoint scan. No policy runs RebufferMt yet;
PARALLEL_EVAL will. LEGACY is unchanged.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: PARALLEL_EVAL, an experimental setup repair policy that commits a
batch of VtSwapMt, SizeUpMt and RebufferMt moves per timing update.

Why: one timing update per batch instead of per move cuts the repair
time.

How: worker threads scan endpoints and generate and score candidates
from prepared data; BatchSelector, BottleneckAnalysis and
ParallelEvalState pick, rank and stop; each batch is kept, salvaged or
rolled back by its WNS and TNS (the group's under -path_group).
OptimizationPolicy::finish() ends each phase, and the Optimizer prints
the final report after the last phase. LEGACY results do not change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: SplitLoadMt, the Mt form of SplitLoad, joins the PARALLEL_EVAL
default sequence.

Why: splitting off the less critical loads is a strong setup move that
PARALLEL_EVAL lacked.

How: estimateLoadChange() scores the lighter target load and the moved
loads are charged as side loads; the split itself is chosen as in the
serial move. LEGACY is unchanged.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: SwapPinsMt, the Mt form of the pin swap, joins the PARALLEL_EVAL WNS
tail.

Why: a pin swap is a cheap gain on the worst endpoints once the batches
stall.

How: Resizer::swapPinDelays() is shared with the serial move; the Mt
generator measures the arc delays on the main thread and picks the
fastest input on a worker. LEGACY is unchanged.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: UnbufferMt, the Mt form of buffer removal, joins the PARALLEL_EVAL
WNS tail.

Why: removing a buffer on the worst path is a gain the other Mt moves
cannot make.

How: the main thread checks the buffer and estimates the saved delay;
the tail removes buffers only in single-move batches, since a removal
cannot be reverted in place. LEGACY is unchanged.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
@openroad-ci
openroad-ci force-pushed the secure-rsz-unbuffer-mt branch from c9a3413 to 62b365d Compare October 17, 2026 07:20
@maliberty

Copy link
Copy Markdown
Member

The WNS tail removes buffers only in single-move batches, because a removal cannot be reverted in place inside the batch journal.

Why not?

@maliberty

Copy link
Copy Markdown
Member

Claude

== Priority 1 (correctness) ==

  • SetupParallelEvalPolicy.cc undoMissedBatch / ParallelEvalState.cc WnsTail::keepSpeculative
    bug: a removed buffer leaves a dangling sta::Instance* behind.
    commitBatch puts target.inst() (the buffer) into batch_instances_. After the
    rollback, that pointer goes into MissTabu (until_, undo_counts_, excluded_) and
    into speculative_instances_.
    Two consequences:
    (a) The restored buffer is a new instance, so it is not on the tabu list. The
    same removal can win again in the next batch. undo_counts_ never reaches
    max_undos_per_instance, because every retry has a new pointer.
    (b) When the allocator reuses the freed address, an unrelated new instance
    (such as a buffer that was just inserted) can be tabu'd, or permanently
    put in excluded_.
    Suggest recording a removal by something that survives the undo (for example
    the input or output net, or the dbInst id if the journal restores it), or
    leaving removals out of the tabu bookkeeping on purpose.

  • ParallelEvalState.hh unbufferAllowed()
    question: removals are limited to single-move batches so that they never need
    a partial salvage. But unbufferAllowed() requires greedy_stalled_, and that is
    the same condition that makes maySpeculate() true. So most removals run in
    speculative batches that are kept and rolled back later across several
    batches. Is that multi-batch journal restore known to be safe with a removal
    in the middle? Later batches may edit the merged net. The comment says
    "cannot be salvaged in place", which only covers the salvage path.

  • SetupParallelEvalPolicy.cc generatorAllowed
    question: with -sequence ... unbuffer, a removal is allowed in every
    multi-move batch. A missed batch then skips the salvage
    (batch_salvage_skipped_removal), so one removal discards all the other moves
    in that batch. Is that intended? It weakens the sequence path compared with
    the tail path.

  • UnbufferMtGenerator.cc passesMoveConflictGuard
    question: this guard drops kSizeUp, which the serial
    hasBlockingBufferRemovalMove still blocks. So a buffer that an earlier batch
    sized up can now be removed. The comment justifies it, but has the sizeup ->
    remove -> rebuffer churn been checked? In the path_group test, 23 buffers are
    removed while 17 are inserted.

== Priority 4-6 (architecture / style / process) ==

  • UnbufferMtGenerator.cc copies roughly 300 lines of UnbufferGenerator.cc almost
    verbatim (the context struct, validTarget, resolveDriverContext, the path
    context, and the fanout/cap/slack guards). Suggest moving the shared code into
    a helper both use, so the two cannot drift apart. They already differ in the
    conflict guard and in how slack_scene is set.
  • estimateRemovalGain repeats computeNewDelaysSlews and bufferDelay, which
    estimatedSlackOK has just computed for the same buffer, so every eligible
    target runs those delay calcs twice on the main thread. Consider returning the
    gain from the slack check.

@maliberty

Copy link
Copy Markdown
Member

Codex

  • Correctness: UnbufferMtCandidate.cc:63 marks only output-net pins as reconnected. removeBuffer() can instead preserve the output net and reconnect the input-net pins. In a multi-move batch, the selector can then miss a conflict. The impact should follow the same surviving-net choice as removal.
  • QoR: UnbufferMtGenerator.cc:371 uses the max-cap check’s scene for the slack guard. When there is no max-cap limit, that scene is null and estimatedSlackOK() rejects every removal. The active path scene appears to be the appropriate fallback.


using utl::RSZ;

constexpr int kBufferRemovalMaxFanout = 10;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why 10?

@maliberty
maliberty merged commit d41e09a into The-OpenROAD-Project:master Oct 18, 2026
20 checks passed
@maliberty
maliberty deleted the secure-rsz-unbuffer-mt branch October 18, 2026 02:24
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.

2 participants