Skip to content

Commit fb6bde3

Browse files
authored
Merge pull request #10431 from erendn/resizer_nested_journal_fix
rsz: Support nested journals' move result tracking
2 parents e009659 + 4ce7b85 commit fb6bde3

8 files changed

Lines changed: 634 additions & 72 deletions

File tree

src/rsz/src/MoveCommitter.cc

Lines changed: 113 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <cassert>
99
#include <cstddef>
1010
#include <memory>
11+
#include <stack>
1112
#include <string>
1213
#include <unordered_set>
1314
#include <utility>
@@ -59,15 +60,15 @@ MoveCommitter::MoveCommitter(Resizer& resizer) : resizer_(resizer)
5960

6061
void MoveCommitter::init()
6162
{
62-
journal_open_ = false;
63+
pending_move_results_ = std::stack<std::vector<MoveResult>>{};
6364
std::ranges::fill(summary_carried_by_type_, 0);
6465
summary_carried_by_type_[typeIndex(MoveType::kSizeUpMatch)]
6566
= persisted_summary_by_type_[typeIndex(MoveType::kSizeUpMatch)];
6667
std::ranges::fill(pending_by_type_, 0);
6768
std::ranges::fill(committed_by_type_, 0);
6869
for (size_t index = 0; index < pending_instances_by_type_.size(); ++index) {
6970
pending_instances_by_type_[index].clear();
70-
all_instances_by_type_[index].clear();
71+
committed_instances_by_type_[index].clear();
7172
}
7273

7374
const bool report_enabled
@@ -90,6 +91,12 @@ MoveResult MoveCommitter::commit(MoveCandidate& candidate)
9091
MoveResult result = candidate.apply();
9192
if (result.accepted) {
9293
recordAcceptedResult(result);
94+
// Only the journaled path needs the per-level result trail; non-journaled
95+
// commits (e.g. MeasuredVtSwapPolicy) flush via acceptPendingMoves and
96+
// never restore.
97+
if (!pending_move_results_.empty()) {
98+
pending_move_results_.top().push_back(result);
99+
}
93100
}
94101
return result;
95102
}
@@ -366,16 +373,28 @@ bool MoveCommitter::canManageJournal() const
366373

367374
void MoveCommitter::recordAcceptedResult(const MoveResult& result)
368375
{
369-
// Track committed instances by move type so later passes can avoid
370-
// conflicts.
376+
// Track committed instances by move type so later passes can avoid conflicts
371377
const size_t index = typeIndex(result.type);
372378
pending_by_type_[index] += result.move_count;
373379
for (sta::Instance* inst : result.touched_instances) {
374380
pending_instances_by_type_[index].insert(inst);
375381
// Preserve legacy BaseMove semantics: once a move type touched an
376382
// instance in this repair session, later guards still see it even if the
377383
// journal branch is restored.
378-
all_instances_by_type_[index].insert(inst);
384+
// FIXME: This should be removed after policies start using the nested
385+
// journaling capability.
386+
committed_instances_by_type_[index].insert(inst);
387+
}
388+
}
389+
390+
void MoveCommitter::unrecordAcceptedResult(const MoveResult& result)
391+
{
392+
// Remove the reverted move results
393+
const size_t index = typeIndex(result.type);
394+
pending_by_type_[index] -= result.move_count;
395+
for (sta::Instance* inst : result.touched_instances) {
396+
pending_instances_by_type_[index].erase(
397+
pending_instances_by_type_[index].find(inst));
379398
}
380399
}
381400

@@ -392,16 +411,13 @@ void MoveCommitter::beginEcoJournal() const
392411

393412
void MoveCommitter::commitEcoJournal() const
394413
{
395-
debugPrint(resizer_.logger(), RSZ, "journal", 1, "journal end");
396-
// Refresh timing only once after a batch of ECO edits becomes permanent.
397-
if (ecoHasPendingChanges()) {
398-
resizer_.updateParasiticsAndTiming();
399-
}
414+
debugPrint(resizer_.logger(), RSZ, "journal", 1, "journal commit");
400415
odb::dbDatabase::commitEco(resizer_.block());
401416
}
402417

403418
void MoveCommitter::restoreEcoJournal() const
404419
{
420+
debugPrint(resizer_.logger(), RSZ, "journal", 1, "journal restore");
405421
odb::dbDatabase::undoEco(resizer_.block());
406422
}
407423

@@ -466,51 +482,115 @@ void MoveCommitter::beginJournal()
466482
if (!canManageJournal()) {
467483
return;
468484
}
469-
470-
// Start one reversible ECO batch for the current search branch.
485+
// Start one reversible ECO batch for the current search branch. Stacks
486+
// naturally: odb's beginEco pushes the current journal aside, and we mirror
487+
// that with a fresh per-level result vector here.
471488
beginEcoJournal();
472-
journal_open_ = true;
489+
pending_move_results_.emplace();
490+
// Mirror the journal level in the tracker so a nested restore rejects only
491+
// its own moves.
492+
if (move_tracker_) {
493+
move_tracker_->beginJournal();
494+
}
473495
}
474496

475497
void MoveCommitter::commitJournal()
476498
{
477-
if (!canManageJournal() || !journal_open_) {
499+
if (!canManageJournal() || pending_move_results_.empty()) {
478500
return;
479501
}
480-
481-
// Close the reversible batch and fold the accepted moves into the totals.
502+
// Refresh timing only once after a batch of ECO edits becomes permanent
503+
if (ecoHasPendingChanges()) {
504+
resizer_.updateParasiticsAndTiming();
505+
}
506+
// Close the reversible batch. odb::commitEco appends the inner journal to its
507+
// parent when one exists, otherwise the changes become permanent.
482508
commitEcoJournal();
483-
acceptPendingMoves();
484-
journal_open_ = false;
509+
// Mirror the journal commit in the tracker: an outermost commit finalizes
510+
// the tracked moves, a nested commit merges them into the parent level.
511+
if (move_tracker_) {
512+
move_tracker_->commitJournal();
513+
}
514+
515+
std::vector<MoveResult> top = std::move(pending_move_results_.top());
516+
pending_move_results_.pop();
517+
518+
if (pending_move_results_.empty()) {
519+
// Outermost commit: fold the accumulated pending counters into the
520+
// committed totals and flush the tracker.
521+
acceptPendingMoves();
522+
} else {
523+
// Nested commit: the moves remain pending against the parent journal,
524+
// so the parent restore can still undo them. Merge our results into
525+
// the parent's vector; pending_by_type_ / pending_instances_by_type_
526+
// already reflect the cumulative state and stay untouched.
527+
debugPrint(resizer_.logger(),
528+
RSZ,
529+
"journal",
530+
2,
531+
"nested journal commit: merged {} results into parent",
532+
top.size());
533+
auto& parent = pending_move_results_.top();
534+
for (MoveResult& result : top) {
535+
parent.push_back(std::move(result));
536+
}
537+
}
485538
}
486539

487540
void MoveCommitter::restoreJournal()
488541
{
489-
if (!canManageJournal() || !journal_open_) {
542+
if (!canManageJournal() || pending_move_results_.empty()) {
490543
return;
491544
}
492-
493-
// Undo both database edits and in-memory accounting for the rejected branch.
545+
// Undo both database edits and in-memory accounting for the rejected branch
494546
debugPrint(
495547
resizer_.logger(), RSZ, "journal", 1, "journal restore starts >>>");
496548
resizer_.initForJournalRestore();
497549

498550
const bool had_eco_changes = ecoHasPendingChanges();
499551
restoreEcoJournal();
500-
if (!had_eco_changes) {
501-
rejectPendingMoves();
502-
journal_open_ = false;
552+
// Mirror the journal restore in the tracker so its moves are rejected,
553+
// whether this is an outermost or a nested restore.
554+
if (move_tracker_) {
555+
move_tracker_->restoreJournal();
556+
}
557+
558+
// Unrecord just this level's accepted results; the parent's accumulated
559+
// pending state (if any) must survive.
560+
std::vector<MoveResult> top = std::move(pending_move_results_.top());
561+
pending_move_results_.pop();
562+
int reverted_moves = 0;
563+
for (const MoveResult& result : top) {
564+
reverted_moves += result.move_count;
565+
}
566+
resizer_.addRejectedLegacyMoveCount(reverted_moves);
567+
if (pending_move_results_.empty()) {
568+
// pending_by_type_ still holds the about-to-be-reverted counts at this
569+
// point; log the per-type breakdown before draining them.
570+
logPendingMoves("UNDO", reverted_moves);
571+
} else {
503572
debugPrint(resizer_.logger(),
504573
RSZ,
505574
"journal",
506-
1,
507-
"journal restore ends due to empty ECO >>>");
508-
return;
575+
2,
576+
"nested journal restore: reverted {} moves",
577+
reverted_moves);
578+
}
579+
for (const MoveResult& result : top) {
580+
unrecordAcceptedResult(result);
509581
}
510582

511-
resizer_.updateParasiticsAndTiming();
512-
rejectPendingMoves();
513-
journal_open_ = false;
583+
if (had_eco_changes) {
584+
resizer_.updateParasiticsAndTiming();
585+
resizer_.invalidateVertexOrdering();
586+
}
587+
588+
if (pending_move_results_.empty()) {
589+
// Outermost restore: the tracker's pending entries are no longer
590+
// load-bearing. Flush them and emit the totals snapshot.
591+
rejectTrackedMoves();
592+
logCommittedTotals();
593+
}
514594

515595
debugPrint(resizer_.logger(), RSZ, "journal", 1, "journal restore ends <<<");
516596
}
@@ -527,7 +607,7 @@ void MoveCommitter::acceptPendingMoves()
527607
}
528608
committed_by_type_[index] += pending_by_type_[index];
529609
pending_by_type_[index] = 0;
530-
all_instances_by_type_[index].insert(
610+
committed_instances_by_type_[index].insert(
531611
pending_instances_by_type_[index].begin(),
532612
pending_instances_by_type_[index].end());
533613
pending_instances_by_type_[index].clear();
@@ -544,7 +624,7 @@ void MoveCommitter::rejectPendingMoves()
544624
resizer_.addRejectedLegacyMoveCount(move_count);
545625

546626
std::ranges::fill(pending_by_type_, 0);
547-
for (std::unordered_set<sta::Instance*>& pending_instances :
627+
for (std::unordered_multiset<sta::Instance*>& pending_instances :
548628
pending_instances_by_type_) {
549629
pending_instances.clear();
550630
}
@@ -585,7 +665,7 @@ bool MoveCommitter::hasPendingMoves(const MoveType type,
585665
bool MoveCommitter::hasMoves(const MoveType type, sta::Instance* inst) const
586666
{
587667
const size_t index = typeIndex(type);
588-
return all_instances_by_type_[index].contains(inst)
668+
return committed_instances_by_type_[index].contains(inst)
589669
|| pending_instances_by_type_[index].contains(inst);
590670
}
591671

src/rsz/src/MoveCommitter.hh

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <array>
77
#include <cstddef>
88
#include <memory>
9+
#include <stack>
910
#include <string>
1011
#include <unordered_set>
1112
#include <utility>
@@ -137,6 +138,7 @@ class MoveCommitter
137138
// === Commit result handling ==============================================
138139
bool canManageJournal() const;
139140
void recordAcceptedResult(const MoveResult& result);
141+
void unrecordAcceptedResult(const MoveResult& result);
140142
int pendingMoveCount() const;
141143

142144
// === ECO journal internals ===============================================
@@ -156,14 +158,18 @@ class MoveCommitter
156158
Resizer& resizer_;
157159

158160
// === Journal and move accounting state ===================================
159-
bool journal_open_{false};
161+
// Stack depth equals the number of currently open ECO journals. Each vector
162+
// holds the results accepted at that nesting level so a nested restore can
163+
// unrecord just its own moves and a nested commit can merge its results into
164+
// the parent's vector (mirroring odb's commitEco append-to-parent semantics).
165+
std::stack<std::vector<MoveResult>> pending_move_results_;
160166
std::array<int, kTypeCount> pending_by_type_{};
161167
std::array<int, kTypeCount> committed_by_type_{};
162168
std::array<int, kTypeCount> summary_carried_by_type_{};
163-
std::array<std::unordered_set<sta::Instance*>, kTypeCount>
169+
std::array<std::unordered_multiset<sta::Instance*>, kTypeCount>
164170
pending_instances_by_type_{};
165171
std::array<std::unordered_set<sta::Instance*>, kTypeCount>
166-
all_instances_by_type_{};
172+
committed_instances_by_type_{};
167173

168174
// === MoveTracker state ====================================================
169175
std::unique_ptr<MoveTracker> move_tracker_;

0 commit comments

Comments
 (0)