Skip to content

rsz: Address PARALLEL_EVAL review feedback - #11905

Merged
maliberty merged 47 commits into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:secure-apply-parallel-eval-reviews-master
Oct 22, 2026
Merged

maliberty merged 47 commits into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:secure-apply-parallel-eval-reviews-master

Conversation

@jhkim-pii

Copy link
Copy Markdown
Contributor

Feedback and responses

Original PR #11794

ID Original reviewer feedback Response and commit Author reply already posted (verbatim)
R01 “Drives LEGACY repair_setup. is pretty vague and used for every test.” Give each LEGACY regression a design/path-specific header. d296d1ff74 -

Original PR #11796

ID Original reviewer feedback Response and commit Author reply already posted (verbatim)
R02 “Trace hierarchical connectivity edits” Reply only; no new commit in this PR. reply: Don't need to support it.
R03 “Sample QoR before the critical VT sweep” Already fixed in merged #11797; phase_before is sampled before the sweep. ef64398ca1. -
R04 “Run the profiler tests in the Bazel target” Already fixed in merged #11797 by removing the test filter. ef64398ca1. -
R05 “Why an int and not Mode?” Already fixed in merged #11797: constructor takes Mode. ef64398ca1. -
R06 “Codex looks minor but maybe worth a follow up later” R03-R05 were handled in #11797; R02 requires an OpenDB callback. No new commit in this PR. -

Original PR #11797

ID Original reviewer feedback Response and commit Author reply already posted (verbatim)
R07 “Tests are skipped” Already fixed in merged #11797; reviewer saw an earlier head. ef64398ca1. -
R08 “Mode 1 creates an undocumented file” Write events.csv only in profiler mode 2. 39c7e01470 -
R09 “I tested removing the filter arg and all tests pass so I'm unclear on the purpose.” The filter was already removed; no new commit in this PR. reply: It was for local testing, but mistakenly merged. I removed it.
R10 “keep the minimum allowed increase across all drivers of an input” Store one cap headroom per input and align the live limit check. 6c1ea36e45 -
R11 “out of line - too big for a header” Move the slew-bias cache hash implementation out of the header. 3ea849bf6a -
R12 “static_assert that float size matches uint32_t size.” Reply only; no new commit in this PR. reply: std::bit_cast checks the bit size.
R13 “Why hash the floats?” Use numeric float keys instead of bitwise float copies. 9526ecadd8 -
R14 “Why 3?” Document the empirically tuned side-loss weight. 67a4fa8cb5, 91098381b2 reply: It is tuned empirically.
It is to reduce the overall timing QoR degradation by side-path impact. Smaller fanin_side_loss_weight can allow more aggressive moves that degrade side paths, which results in worse QoR.
R15 “why .75 ?” Document the size-up score scaling rationale. 2ccb956354, 91098381b2 reply: Empirically tuned.
VtSwap weight is 1. Upsizing has more cost than VtSwap (need more area, increase input pin cap, can cause displacement by legalizer, can hurt side-path timings, ...), so it has lower weight than Vt swap.
R16 “Don’t do it.” Replace private/public macro rewriting with a test peer. 14c6cb6d0a -

Original PR #11798

ID Original reviewer feedback Response and commit Author reply already posted (verbatim)
R17 “Why 20?” Reply only; no new commit in this PR. reply: The same behavior as the legacy Buffer move.
Rebuffer for large fanout net has runtime overhead, thus SplitLoad move covers the fanout > 20 cases.
In the 8 < fanout <= 19 cases, both SplitLoadMt and RebufferMt move candidates are generated and compete.
R18 “I'm assuming this is just moving the existing > 1 problem.” Reply only; no new commit in this PR. reply: Right. Let me study more about it and open a separate PR.
R19 “Apply can accept a tree different from the one scored” Drop RebufferMt trees that put a dont-touch load behind a buffer when the plans are made, so no candidate can build a tree other than the scored one. 383ddb1e95 -
R20 “Are these factors stable across PDKs? Its is unclear what data set this report was generated with.” Name the Nangate45-only calibration data and its scope. 3fdb7069a4 reply: Not sure. I will test it further.
R21 “This feels like a workaround for a bad slew estimation. Why is it needed?” Explain the downstream delay-floor safeguard and its limit. 9e4c35c255, 91098381b2 reply 1: Yes. It was a safe guard to avoid too optimistic stage delay a few months ago.
I will check its usefulness again, and remove it if not necessary anymore.

reply 2: Finished the experiment.
This logic is still useful. It can be removed later if the estimation accuracy improves further.
R22 “Also seems like a correction term whose originating cause is upstream. Explain further why this is required.” Document the RC-shielding rationale, then remove the correction after repeated 34-design QoR checks. Two Nangate45 goldens show larger slew error and were updated. 8e3efcddae, 8dae86dcf7 reply 1: I will check its usefulness again, and remove it if not necessary anymore.

reply 2: Removed this logic since it is not that useful.
R23 “Don't use default, enumerate all cases. In a future value is added then the compiler will notify you the code needs updating.” Enumerate switch values and retain logger-backed impossible-state errors. aabda19b09, cebbe37da1 -
R24 “Should there be an area vs slack trade off here?” Reply only; no new commit in this PR. reply: - There is a min gain threshold. No buffering for less than 5ps speedup.
- It is better to have the area vs timing, or power vs timing trade-offs. I will enhance that part for power-aware timing repair.

Original PR #11799

ID Original reviewer feedback Response and commit Author reply already posted (verbatim)
R25a “the batch keep rule” * is a phase marker, not a saved checkpoint; a stricter rule worsened 2nm QoR, so no change. -
R25b “logic_error throws” Message IDs only: the salvage invariant failures now raise RSZ logger errors. The design state after such an error is not guaranteed. The max-area journal closure was already merged in #11803. 606c992205 -
R25c “recordUndo()/addTabu() store Instance*” Keep rollback-deleted instances off the tabu list. 5981e78dd7 -
R25d “dependsOn()” Explain the one-way dependency rule; batch STA still judges the result. d1a252c868, 91098381b2 -
R25e “charged to the WorkMeter” Charge salvage rollback timing updates to the work meter. 21eb2db6fd -
R25f “findRequireds() no longer runs” Refresh required times before path-group endpoint collection. d61d99071d -
R25g “pops from idle_” Return a failed planner acquisition when the pool is empty. f93a70c1bc -
R25h “iterate() returns” Clear candidate/swap pointers at the end of iterate(). 2949a45dc8 -
R27 “This should be a sizeof for the relevant type (uint64_t I think).” Derive reach-set word width from uint64_t. d9f4e92406 -
R28 “the cone’s filter does not fully match OpenSTA’s traversal rules” Skip disabled preset/clear and bidirectional edges during cone traversal. 3fc5c25fc9 -
R29 “Avoid cryptic names (fep_).” Spell out failing-endpoint identifiers. fb1ef6ebaa -
R30 “More meaningful names than alpha/gamma” Name the bottleneck priority controls and deficit/severity concepts. 85706e9c0a, efc34fc541 -
R31 “Where do these values come from?” Document how the work-meter weights were fitted. a7f4e4a30f reply: They are fitted to estimate the elapsed time.
- Used a non-negative least-squares fit of the wall time (seconds) of 3k profiled batch samples on a reference machine.
- We cannot use the actual elapsed time. The work meter is to provide a deterministic estimated time.

Original PR #11800

ID Original reviewer feedback Response and commit Author reply already posted (verbatim)
R32 “why 8?” Document the SplitLoadMt fanout threshold source. fe44c0cea1 reply: The same threshold as LEGACY SplitLoad move.
R33a “different cells” Both paths use findTargetCell(); the load model differs. No change. -
R33b “wire delay to the moved loads” The buffer sits at the driver; previous wire delay is already reflected in load slack. No change. -
R33c “two disagree” Ranking uses path RF, while the budget uses worst RF because both transitions are affected. No change. -
R33d “bufferOutputPin() is evaluated again” Look up the SplitLoadMt buffer output pin once. eceb1d3f62 -
R33e “TNS 3.4x worse” WNS improved vs checkpoint; off-path penalty was low, but full-weight QoR was mixed. No change. -
R33f “result changes” Six splits were attempted but rolled back, changing later batch selection. No change. -
R33g “ORFS results across platforms” Reply only; no new commit in this PR. reply: Having SplitLoadMt move showed better Fmax on average.

Original PR #11802

ID Original reviewer feedback Response and commit Author reply already posted (verbatim)
R34 “why 10?” Document the UnbufferMt fanout-limit source. d782f3fe56 reply: Same limit as LEGACY.
It looks like a conservative guard not to create a new DRC (or ERC) violation.
R35 “Why not?” Explain why removal needs whole-journal undo and runs alone in the tail. 3c2ff547e6 reply: A batch consists of multiple moves.
We cannot undo a specific move in the middle of a batch.
But VtSwap or SizeUp moves can be easily reverted by performing a reverse VtSwap or Sizing moves.
We cannot do that for unbuffer move. It requires a journal undo. So, the limitation (removes buffers only in single-move batches) has been added.
R36a “a removed buffer leaves a dangling sta::Instance* behind” Validate name plus address before honoring tabu entries; avoid copying the live name on map-hit checks. 43c5d18dd9, 8c3f6bbc07 -
R36b “speculative batches” Journal undo reverses later merged-net edits first; speculative rollback is whole-journal. No change. -
R36c “multi-move batch” Explicit unbuffer batches skip salvage and roll back wholesale by design. No change. -
R36d “churn been checked?” Sized-up buffers remain eligible for removal; batch STA judges the result. No change. -
R36e “copies roughly 300 lines” Mt/serial duplication is intentional until serial moves retire; shared helper deferred. -
R36f “delay calcs twice” Limited to eligible tail targets; changing the shared LEGACY check API is deferred. -
R37a “marks only output-net pins as reconnected” Model the actual surviving net in UnbufferMt impact. 65729aba5b -
R37b “that scene is null” Use the active path scene when no max-cap scene exists. 7429ebf711 -

Original PR #11803

ID Original reviewer feedback Response and commit Author reply already posted (verbatim)
R38a “on the target path” On-path loads have no growth budget; skipping them worsened QoR. No change. -
R38b “target().inst()” Tabu is target/driver-based by design; batch STA judges alternate loads. No change. -
R38c “missed cheap revert” Rollback and re-commit are correct; in-place size-down revert remains follow-up. -
R38d “removeBuffer() doesn't update the area that overMaxArea() tracks” Fix tracked area in Resizer::removeBuffer(). c2fa3f0019 -
R38e “copies ~450 lines” Intentional Mt/serial separation until serial moves retire. No change. -
R39a “At exactly zero slack, any growth passes” Disallow delay growth when the budget is zero. 4f7418fcc3 -
R39b “sequential loads lack an input-side check” Batch STA judges D setup; hold is outside -setup. Skipping flops hurt QoR. No change. -

Original PR #11804

ID Original reviewer feedback Response and commit Author reply already posted (verbatim)
R42a “windowDelay() looks up the vertex” Measure the sampled window at its captured path end. 76ebfc453e -
R42b “-driver_pin is not checked” Reject mismatched/non-output driver pins and add a command test. 33e6bc292f -
R42c “when source.endpoint_pin is” Null endpoint pin means no captured path; the analysis cannot dereference stale paths. No change. -
R42d “everything and returns quietly” Warn when report-trial undo or timing refresh fails. e4697df737 -
R42e “mode 3” Mode 3 refreshes timing but does not steer decisions; focused modes 0/2/3 ended identically. No change. -
R42f “no candidate is legal” The report picks the best legal per-target score; illegal cases are reported explicitly. No change. -
R43 “never checks that the pin belongs to -inst” The same driver-pin validation and command test address this report. 33e6bc292f -
R44a “This name is confusing as it isn't really a full analysis but more of a capture of state. Perhaps RepairSetupMoveSampler ?” Rename the capture helper to RepairSetupMoveSampler. 2786589e44 -
R44b “Similarly RepairSetupMoveSnapshot.hh” Rename the snapshot header and implementation. fee34ef25d -
R44c “Do such validation in c++ so it covers all APIs and not just TCL” Validate effort/profile_mode in C++ for Tcl and Python callers. b4fcc179e4 -

Original PR #11805

ID Original reviewer feedback Response and commit Author reply already posted (verbatim)
R26 “WNS precent change gives strange numbers” Agreed. WNS-relative percentages are misleading near zero. We will review a clock-period-normalized reg-to-reg stopping criterion and implement the improvement in a separate PR. No change in this PR. -
R41 “Here is it called 'keep' but later it is called 'keep on speculation' or just 'Speculate'. One term would be better and speculate is clearer.” Use speculate consistently in architecture and policy comments. b4d4923f19 -
R45 “@oharboe XiangShan does fairly well in the table” Observation to another reviewer; no action required. -

What: the switches this stack added on BufferedNetType (Rebuffer.cc)
and on MoveType (SetupParallelEvalPolicy::generatorAllowed()) list every
enumerator and have no default label.

Why: review of The-OpenROAD-Project#11798. With a default label the compiler does not warn
when a new enumerator is added, so the code that needs a decision for it
is not found.

How: replace each default label with the missing cases. A function that
returns a value keeps a return after the switch, as BufferedNet.cc does.
The three "unhandled BufferedNet type" errors RSZ-1009, RSZ-1011 and
RSZ-1012 go with their labels. No behavior change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: profile mode 1 writes metadata.csv, stats.csv and timing.csv;
events.csv starts at mode 2, as the README and architecture.md say.

Why: review of The-OpenROAD-Project#11797. Mode 1 opened events.csv and wrote only its
header, because emit() writes rows only from mode 2, so the mode 1
output had an empty file the documentation does not list.

How: open events.csv and write its header only when the mode is at
least Mode::kEvents. The two profiler unit tests check that mode 1
leaves no events.csv and read the trace in mode 2 only.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: SlewBiasModelKey hashes with a nested Hash functor whose
operator() is defined in DelayEstimator.cc, instead of an inline friend
hash_value() in DelayEstimator.hh.

Why: review of The-OpenROAD-Project#11797. The hash was too big for a header.

How: declare SlewBiasModelKey::Hash in the header and use it as the
hasher of SlewBiasModelMap, move the body unchanged to the .cc, and move
the boost/functional/hash.hpp include with it. No behavior change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: the slew-bias cache key of DelayEstimatorCache and the buffer
delay cache key of Rebuffer hold their slews, caps and Pi model values
as floats, not as the bits of those floats.

Why: review of The-OpenROAD-Project#11797. The caches only need equal values to reuse a
sample, so the bit copies added code without need. Equal floats hash
alike with boost::hash, which also treats +0.0 and -0.0 as equal; the
keys never hold a NaN.

How: replace the uint32_t fields with float fields, drop floatBits()
and the float_bits lambda, and hash the floats with
boost::hash_combine. No behavior change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: InputCapBudget stores the smallest load increase any driver of
the input net still accepts, instead of a list of DriverCapLimit with
three fields per driver. Resizer::maxCapIncrease() returns that
headroom for one driver, and checkMaxCapOK() compares the cap delta
with it.

Why: review of The-OpenROAD-Project#11797. negative_slack only served to pick the limit at
the comparison, so the budget can store the result. Doing the live
check the same way keeps the two answers equal at the float boundary,
where cap + delta > limit and delta > limit - cap can differ.

How: maxCapIncrease() is 0 for a violating driver, max_cap - cap with
headroom and infinity without a limit; inputCapBudgets() keeps the
minimum over the drivers. A unit test checks that the budgets and
replacementPreservesMaxCap() agree with a tight limit and with a
violated one.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: batch_candidates_ and batch_swaps_ are cleared at the end of
SetupParallelEvalPolicy::iterate(), and resetRunState() clears
batch_swaps_ too.

Why: review of The-OpenROAD-Project#11799. Both vectors point into the BatchPool local to
iterate(), so they dangled until the next batch cleared them. Nothing
reads them after iterate() today; clearing them keeps it that way.

How: clear both vectors after the stop rules and the batch summary, and
say in the member comment that they belong to the pool. No behavior
change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: collectViolatingEndpoints() calls findRequireds() before it
branches on -path_group, as RebufferMt had it, not only in the
design-wide branch.

Why: review of The-OpenROAD-Project#11799. RebufferMt planners on worker threads read
required times without a refresh (annotateLoadSlacks()). Under
-path_group they were current only because main-thread slack queries
for the side loads happened to update them first.

How: move the findRequireds() block above the branch and say in its
comment which workers depend on it. It is a no-op when timing is up to
date, so results do not change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: RebufferPlannerPool::acquire() raises RSZ-2036 when no planner is
idle, instead of calling back() and pop_back() on an empty vector.

Why: review of The-OpenROAD-Project#11799. The pool holds one planner per worker thread, so
it cannot run dry today, but nested pool work inside planning would
take a second lease on a thread and hit undefined behavior.

How: check idle_ under the lock and call logger()->error(), which
throws; parallelFor() rethrows on the main thread and no lease is
taken. No behavior change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: an undone PARALLEL_EVAL batch puts on the tabu only the instances
that its journal rollback kept, for the batch targets and for the
instances of a failed speculative run.

Why: review of The-OpenROAD-Project#11799. In the WNS tail a speculative run keeps the
journal open, so a later batch can target a buffer the run created.
The rollback deletes that buffer, and OpenDB gives its address to the
next instance it creates; that instance started out on the tabu, or
excluded after enough undos.

How: commitBatch() records each target instance with its OpenDB name,
read before the move, and the speculative run keeps those records.
After the rollback, keptInstances() keeps an instance only when an
instance of the recorded name is still at the recorded address; the
recorded pointers are compared, never dereferenced. The golden logs do
not change; a unit test deletes an instance and reuses its slot.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: the comment of size_up_mt_score_scale says that 0.75 comes from
an Fmax sweep and what it does.

Why: review of The-OpenROAD-Project#11797 ("why .75?"). The value had no explanation in
the code.

How: comment only. The scale was set by an Fmax sweep when SizeUpMt and
VtSwapMt started to share one score pool; below 1 it lets a VT swap win
over a size-up of similar gain.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: the comment above the buffer-tree calibration factors names the
data set they were fitted on and what an off factor costs.

Why: review of The-OpenROAD-Project#11798. It was not clear which data the accuracy report
was run on, or whether the factors hold on other PDKs.

How: comment only. The factors come from the Nangate45 cases of
report_delay_estimator_accuracy_for_rebuffering (the
report_delay_estimator_rebuffer_* tests), and no other platform was
checked; the estimate only ranks trees, and STA times each batch before
it is kept. The comment also gets the report command name right.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: boundBufferTreeDownstreamStageDelay() says what it bounds and
why.

Why: review of The-OpenROAD-Project#11798, which asked whether the floor works around a
bad slew estimate. It does: the tree model's wire slew is approximate,
and an optimistic slew at the inputs after the tree made the table
delta credit those stages with too much delay gain.

How: comment only. A stage after the tree keeps 80% of its current
delay when its input slew does not improve, falling to 50% as the slew
gets sharper.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: the comment of kBufferTreeHighCapSlewRatio and
kBufferTreeHighCapSlewFactor says why a heavily loaded final buffer
takes its output slew at a lower effective cap.

Why: review of The-OpenROAD-Project#11798, which asked why this correction is needed. It
has the same cause as the effective-cap factors above it: the Liberty
lookup uses a lumped cap, while STA's delay calculator drives an RC
load with an effective cap below it, and the wire resistance hides the
most cap at a final buffer that drives a long load.

How: comment only.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: the header of each repair_setup_path test says which platform
and design the path slice comes from, how many endpoints violate, which
LEGACY moves the golden log covers and where its worst path runs.

Why: review of The-OpenROAD-Project#11794. Every header ended with "Drives LEGACY
repair_setup.", which said nothing about what the case adds.

How: comments only; the facts come from each test's golden log.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: the TestResizerMt fixture no longer redefines "private" or
"protected" around any header. The tests reach the internals of
SetupParallelEvalPolicy, StallMonitor, WnsTail, SizeUpMtGenerator and
RepairTargetCollector through ResizerTestPeer, which those classes
befriend, and the three pure batch rules of SetupParallelEvalPolicy
are public. Two flip-flop tests attach OpenSTA scaled models through
ScaledTimingArc and ScaledLibertyPort.

Why: review of The-OpenROAD-Project#11797. Redefining an access keyword before including a
header is undefined behavior: it can change the class layout and break
the one-definition rule.

How: one friend declaration per class; ResizerTestPeer in the fixture
forwards each internal the tests read or set, and the tests call it.
adaptiveBatchSize(), evaluationEndpointLimit() and
batchImprovesCheckpoint() move to the public part of the policy. The
redefinition around tst/IntegratedFixture.h was not needed.
TimingArc::addScaledModel() and LibertyPort::addScaledPort() stay
protected: a class derived from each takes a pointer to the member,
which C++ allows, so OpenSTA is unchanged.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: salvageMissedBatch() counts a timing update before it rolls the
batch back, as undoBatch() and restoreBatchCheckpoint() already do.

Why: review of The-OpenROAD-Project#11799. MoveCommitter::restoreJournal() updates
parasitics and timing after it restores ECO edits, so the salvage
rollback is real STA work. The work meter left it out, so the stall
window and the tail budget saw less work than the run did.

How: call noteTimingUpdate() before rollBackJournal() and fix the
comment there and the work-meter doc in OptimizationPolicyConfig.hh.
WNS, TNS, instance count and area stay the same on 34 designs.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: comments in BatchSelector::admit(), the batch selection section
of architecture.md and the BatchSelector dependency test say why a
candidate is admitted when it changes timing that a member's estimate
read.

Why: review of The-OpenROAD-Project#11799 asked why one direction is enough. Every
estimate reads the same pre-batch timing, so either direction leaves a
stale estimate, and the measured batch verdict and the salvage catch a
batch that misses. Checking both ways was measured on 34 designs:
average Fmax +0.1%, but 10 designs better and 14 worse; TNS better on
11 and worse on 6. The test comment called impact3 "later", which read
as if the order made the one-way check safe.

How: comments and docs only. No behavior change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: kBitsPerWord in BottleneckAnalysis.cc is sizeof(uint64_t) *
CHAR_BIT instead of the literal 64.

Why: review of The-OpenROAD-Project#11799. The reach sets are vectors of uint64_t, and the
literal did not say that it is the bit width of that type.

How: compute the constant from the word type. No behavior change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: the bottleneck cone of PARALLEL_EVAL no longer follows the
preset/clear arcs OpenSTA disables by default, or disabled bidirect
instance paths.

Why: review of The-OpenROAD-Project#11799. The cone sums run in level order and assume
that every fanin has the lower level. Levelize::searchThru() skips
these edges, so a vertex reached only through one can sit above its
fanout: a reset pin RN can have a higher level than Q. The fanout sums
then hand Q's endpoints to RN after RN passed its own set on, and the
cone weights are wrong.

How: forEachDataFanin() also skips a regSetClr arc while preset/clear
arcs are disabled and an edge isDisabledBidirectInstPath() reports. A
new unit test connects RN of a DFFR_X1 to a deep net: without the fix
RN collects the three endpoints behind Q. The rsz regression goldens
do not change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: fep_, SavedCheckpoint::fep and the fep_count parameters of the
PARALLEL_EVAL stall monitor and batch cap are named after the failing
endpoint count they hold.

Why: review of The-OpenROAD-Project#11799 asked to avoid cryptic names such as fep_.

How: rename the members, parameters, locals and test variables; the
profiler event batch_fep_at_collect keeps its name because reports read
it. No behavior change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: BatchPriorityConfig::alpha and gamma are now cone_bonus_scale and
cone_bonus_exponent, in the code, the tests and architecture.md.

Why: review of The-OpenROAD-Project#11799 asked for more meaningful names than alpha and
gamma. The rank is score * (1 + cone_bonus_scale *
w^cone_bonus_exponent), so the names say which part of the bonus each
one sets.

How: rename the two fields and update the formula comments and the
field table. No behavior change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: the work-meter comment in OptimizationPolicyConfig.hh says the
work_* weights are a non-negative least-squares fit of profiled batch
wall times on the reference machine.

Why: review of The-OpenROAD-Project#11799 asked where these values come from.

How: comment only. No behavior change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: a comment on kSplitLoadMinFanout in SplitLoadMtGenerator.cc says
it is the fanout threshold of the LEGACY split-load move.

Why: review of The-OpenROAD-Project#11800 asked why 8. SplitLoadGenerator, and
split_load_min_fanout_ before it, uses the same value, so both policies
consider the same drivers.

How: comment only. No behavior change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: SplitLoadMtCandidate::apply() finds the output pin of the new
buffer once, before the resize, and uses it for the resize and for the
parasitics of the new net.

Why: review of The-OpenROAD-Project#11800. The pin was looked up again after the resize;
that works because dbInst::swapMaster() keeps the instance terminals,
but one lookup is clearer.

How: hoist bufferOutputPin() into a local. No behavior change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: a comment on kBufferRemovalMaxFanout in UnbufferMtGenerator.cc
says it is the fanout limit of the LEGACY buffer removal, used when the
upstream driver has no max-fanout limit of its own.

Why: review of The-OpenROAD-Project#11802 asked why 10. UnbufferGenerator uses the same
value, which came with the buffer removal move in 2024.

How: comment only. No behavior change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: the comment on WnsTail::unbufferAllowed() explains why a salvage
cannot undo a buffer removal on its own.

Why: review of The-OpenROAD-Project#11802 asked why a removal cannot be reverted in place.
A cell swap goes back with one replaceCell() and every pin and net
stays the same object. A removed buffer and its net come back only
through the journal restore, which undoes the whole batch.

How: comment only. No behavior change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: MissTabu entries keep the name of the instance they were
recorded for, and a lookup counts an entry only when the live instance
at that address still has that name. A new instance at a reused
address starts with no tabu, no undo count and no exclusion.

Why: review of The-OpenROAD-Project#11802. A kept batch can delete an instance that has a
tabu entry: a buffer removal, or a buffer a rebuffer replaced. OpenDB
hands the freed slot to the next new instance, which then inherited
the old entry and could be skipped, or excluded for the rest of the
run. A rollback is not affected: the journal restores a deleted
instance at its old address with its old name, so the undone buffer
keeps its tabu and its undo count.

How: an Entry of name and value for each map, filled through the
dbNetwork MissTabu::reset() now takes; isTabu(), isExcluded() and
recordUndo() compare the name and read only the live instance. A new
unit test deletes a tabu'd instance, creates one in its slot and checks
that the new one is free.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: UnbufferMtCandidate::impact() marks as reconnected the pins of the
net removeBuffer() drops: the output net's loads, or the input net's
pins when only the output net has a port. Resizer gains
removeBufferKeepsOutputNet(), which reports that choice.

Why: review of The-OpenROAD-Project#11802. The impact always marked the output net's pins,
but removeBuffer() keeps a port net and moves the pins of the input net
onto it. A move on the upstream driver then showed no reconnect
conflict, so a multi-move batch could admit both. Only batches with
unbuffer in -sequence hold a removal next to other moves.

How: removeBufferKeepsOutputNet() applies the rule removeBuffer() and
canRemoveBuffer() use, bufferRemovalKeepsOutputNet(). A new unit test
checks both cases on the test netlist.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: when the upstream driver has no capacitance limit, UnbufferMt runs
its slack guard and gain estimate on the target path's scene.

Why: review of The-OpenROAD-Project#11802. The guard used the scene of the driver's worst
capacitance check, which is null without a limit, and
estimatedSlackOK() rejects every removal on a null scene. So
UnbufferMt never removed a buffer behind a driver without a limit.

How: fall back to the path scene that resolveDriverContext() already
holds. The LEGACY UnbufferGenerator has the same gap and is unchanged.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: Resizer::removeBuffer() takes the buffer's area off the tracked
design area that overMaxArea() reads, and PARALLEL_EVAL judges
-max_utilization with overMaxArea() instead of walking the area.

Why: review of The-OpenROAD-Project#11803. Every insertion and resize updates the tracked
area, but a removal left it unchanged, so overMaxArea() saw the area of
removed buffers. LEGACY's stop rules and the final checks read it.
PARALLEL_EVAL worked around this with an instance walk per edit, and
rollBackJournal() recounts the area after an undo already.

How: designAreaIncr() with the negative master area before both
removal paths delete the buffer. withinMaxUtilization() returns
!overMaxArea(); the walk stays only for the RSZ-2033 message. The unit
test of a batch with a removal and an upsize now checks that the
tracked area stays below the limit.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: RepairSetupAnalysis::windowDelay() searches the paths of the pin
where the captured path ends, instead of the original endpoint.

Why: review of The-OpenROAD-Project#11804. The window chain is captured from the target's
endpoint path, which ends at the target driver for a driver prefix and
at a latch D pin for a latch fanin segment. windowDelay() looked for
that chain among the paths of the original endpoint, never found it,
and left window_delay_s, actual_window_gain_s and estimate_error_s
empty without a warning.

How: look up the vertex of path_points_.back(). A new unit test builds
a driver-prefix target and checks that its window delay is sampled;
it fails before the fix.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: report_delay_estimator_accuracy_for_rebuffering errors out with
RSZ-3211 when -driver_pin is not an output pin of -inst.

Why: review of The-OpenROAD-Project#11804 (Claude and Codex). The pin was used as given:
with -inst r2 -driver_pin r1/Q the report analyzed r1/Q under the
header of r2, and an input pin of -inst was taken as its driver. The
instance also feeds the estimator context, so the comparison was
misleading.

How: reportAccuracyForRebufferTree() checks that the pin belongs to the
instance and is a driver. The new report_delay_estimator_rebuffer_
driver_pin test covers both cases and is registered in Bazel and CMake.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: the ECO scope of the delay estimator accuracy reports prints
warning RSZ-3212 when its undo or the timing update after it throws.

Why: review of The-OpenROAD-Project#11804. The destructor caught every exception and
returned quietly, so a failed undo left the report's trial buffers or
cell swap in the design with no message.

How: keep the catch, since a destructor must not throw, and warn in
it. No change when the undo succeeds.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: Resizer::repairEffortFromString() raises RSZ-3242 for an unknown
effort and Resizer::repairSetup() raises RSZ-3243 for a profile mode
outside 0-3. The Tcl range checks are gone; Tcl still checks that
-profile_mode is an integer and keeps its two warnings.

Why: review of The-OpenROAD-Project#11804 asked for such validation in C++ so it covers
every API. The Python binding reached repairSetup() without either
check: an unknown effort became high, and a mode past 3 reached the
profiler, which no longer clamps it.

How: repairEffortFromString() is a const member now, so it can log; the
Tcl and Python bindings call it on the Resizer. The effort unit test
also checks the error, and the new repair_timing_option_errors test,
registered in Bazel and CMake, checks both messages from Tcl.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: the class that captures timing and topology around one move,
and its RepairSetupAnalysis.hh/.cc files, are now
RepairSetupMoveSampler.

Why: review of The-OpenROAD-Project#11804. The class does not analyze anything; it samples
the state before and after a move, which the report then analyzes.

How: rename the class, the files, the includes, the Bazel and CMake
source lists and the architecture.md references. The report, the
analysis target and the profiler mode keep their names. No behavior
change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: RepairSetupAnalysisData.hh, which holds the snapshot types of the
move sampler, is now RepairSetupMoveSnapshot.hh, and
RepairSetupAnalysisSnapshot.cc, which captures those snapshots, is now
RepairSetupMoveSnapshot.cc.

Why: review of The-OpenROAD-Project#11804, following the RepairSetupMoveSampler rename: the
files hold the state captured around a move, not an analysis.

How: rename the files, the includes, the Bazel and CMake source lists
and the architecture.md reference. The types keep their names. No
behavior change.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: RebufferMtGenerator drops a tree plan that puts a dont-touch load
behind a buffer, so no candidate is made from it.

Why: review of The-OpenROAD-Project#11798 (Codex). The tree export skips a dont-touch load
and leaves it on the driver net, so the committed tree could differ
from the scored one while apply() accepted any positive buffer count.
Otherwise the export builds the scored tree: insertBufferBeforeLoads()
fails without an error only on a dont-touch net or load, and
okToBufferNet() already keeps a dont-touch driver net out. Dropping
the tree at generation keeps it out of scoring and batch selection.

How: makeTreePlans() adds a plan gate next to the one-buffer and
score-cap gates; a tree that keeps the dont-touch load on the driver
net stays. RebufferMtCandidate.hh joins the headers the tests see. A
unit test marks a load that a planned tree puts behind a buffer as
dont-touch and checks that no candidate puts it there.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: the comment of fanin_side_loss_weight says that the weight of 3
is calibrated and how.

Why: review of The-OpenROAD-Project#11797 ("Why 3?"). The value had no explanation in the
code.

How: comment only. Applied moves whose estimated fanin side loss
reached about a quarter of their gain did not improve the measured
TNS, and a weight of 3, which leaves no gain once the side loss reaches
a third, fit best.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
Keep exhaustive switches without default labels so new enum values receive compiler diagnostics. Restore the Rebuffer message IDs after the switches and add logger failures for the helper and generator gate when an unhandled value reaches runtime.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
Replace the remaining logic_error throws in parallel evaluation salvage with RSZ logger errors. This keeps error propagation while reporting failures with stable RSZ message IDs and removes the unused stdexcept include.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
Rename the slack-deficit, bottleneck-severity, and priority-scale identifiers to reflect their actual roles in stalled-batch ranking. Keep the scoring formula and defaults unchanged while updating tests and architecture documentation.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: Remove the special final-buffer slew lookup and update the two affected rebuffer accuracy goldens.

Why: Two repeated 34-design PARALLEL_EVAL comparisons preserved Fmax, WNS, TNS, and FEP, while one buffer was avoided in hercules_is_int. Clean runtime runs differed by less than one percent overall.

How: Keep the existing path effective-load lookup for buffer delay and slew; delete the second high-cap lookup and its constants.
Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
(cherry picked from commit 5dc99ce1ea15dae3a06e996698c458be1b53f3b5)
What: Compare the stored tabu name against the live OpenDB constant name on a map hit.

Why: dbInst::getName returns a std::string copy, which is unnecessary for lookup comparisons on long instance names.

How: Use getConstName for matching while retaining owned string snapshots in tabu entries.
Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
What: the comments on the downstream delay floor, the fanin side loss
weight, the SizeUpMt score scale and the one-way batch dependency check
give their rationale instead of tuning outcomes.

Why: review of The-OpenROAD-Project#11797, The-OpenROAD-Project#11798 and The-OpenROAD-Project#11799. The floor comment named a
cause that the calibration data does not show, and the other three
described tuning runs rather than the reason, unlike the replies posted
on those PRs.

How: comment only. The floor is a safeguard against crediting the
stages after the tree with more gain than the built tree gives; the
side loss weight keeps out moves that degrade side paths; a size-up
ranks below a VT swap of similar gain because it costs more; a two-way
dependency check would defer more candidates.

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
@jhkim-pii jhkim-pii self-assigned this Oct 21, 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.

@jhkim-pii
jhkim-pii marked this pull request as ready for review October 21, 2026 16:30
@jhkim-pii
jhkim-pii requested a review from a team as a code owner October 21, 2026 16:30

@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 the parallel evaluation setup and bottleneck analysis in the resizer tool. Key changes include renaming RepairSetupAnalysis to RepairSetupMoveSampler, updating the bottleneck priority configuration to use deficit-based bottleneck severity, and refactoring MissTabu to track instances by name to prevent issues with reused memory addresses. Additionally, float bitwise caching was replaced with standard floats and custom hashes, and option validation for -effort and -profile_mode was moved to C++. The review feedback identifies critical null pointer dereferences in SetupParallelEvalPolicy::commitBatch, MissTabu::name, and MissTabu::hasEntry when handling null instances (such as top-level ports), which should be guarded.

Comment thread src/rsz/src/policy/SetupParallelEvalPolicy.cc
Comment thread src/rsz/src/policy/ParallelEvalState.cc
Comment thread src/rsz/src/policy/ParallelEvalState.cc
…el-eval-reviews-master

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
@openroad-ci
openroad-ci force-pushed the secure-apply-parallel-eval-reviews-master branch from 5ff1b78 to 4f6afc9 Compare October 21, 2026 17:49
@jhkim-pii

Copy link
Copy Markdown
Contributor Author

@maliberty Applied your review feedbacks.

@jhkim-pii
jhkim-pii requested a review from maliberty October 21, 2026 19:34
// on the rebuffer accuracy report,
// report_delay_estimator_accuracy_for_rebuffering, which prints the estimate
// of every stage of the planned tree next to the delay STA measures after the
// tree is built. The data set is the Nangate45 cases of that report, the

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.

Is a single PDK sufficient?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Definitely, not sufficient. I'll check and enhance the accuracy further with other PDKs.

@maliberty

Copy link
Copy Markdown
Member

Thanks. My only concern is when I see constants like

constexpr int kSplitLoadMinFanout = 8;

I was asking more about why it is 8 than what it means. How are the various magic constant's values arrived at (ie how could someone else reproduce those numbers)?

@maliberty
maliberty merged commit c83a825 into The-OpenROAD-Project:master Oct 22, 2026
20 checks passed
@maliberty
maliberty deleted the secure-apply-parallel-eval-reviews-master branch October 22, 2026 05:16
@jhkim-pii

Copy link
Copy Markdown
Contributor Author

Thanks. My only concern is when I see constants like

constexpr int kSplitLoadMinFanout = 8;

I was asking more about why it is 8 than what it means. How are the various magic constant's values arrived at (ie how could someone else reproduce those numbers)?

The various constants related to PARALLEL_EVAL policy were empirically tuned with small set of test cases within a logically valid range.
Users can tune the constants with their own test cases if they are willing to do.
Determining the constant values with theories will be difficult in most cases.

The kSplitLoadMinFanout = 8 is the legacy behavior. But I guess it was tuned in a similar manner.

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