Skip to content

Commit 1d49f59

Browse files
authored
Merge pull request #8968 from calewis/resizer_determinism
RSZ: Remove non-determinism and platform-specific behavior
2 parents 398628e + a70b8ff commit 1d49f59

File tree

7 files changed

+107
-65
lines changed

7 files changed

+107
-65
lines changed

src/dbSta/src/dbNetwork.cc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ DbInstanceNetIterator::DbInstanceNetIterator(const Instance* instance,
415415

416416
// Keep track of flat nets that are already represented by a mod_net
417417
// to avoid returning both.
418-
std::set<dbNet*> handled_flat_nets;
418+
std::unordered_set<dbNet*> handled_flat_nets;
419419
for (dbModNet* mod_net : mod_nets) {
420420
dbNet* flat_net = network_->findRelatedDbNet(mod_net);
421421
if (flat_net) {
@@ -425,7 +425,7 @@ DbInstanceNetIterator::DbInstanceNetIterator(const Instance* instance,
425425

426426
// Collect dbNets from children dbInsts' pins that are not already
427427
// handled.
428-
std::set<dbNet*> flat_nets_set;
428+
std::unordered_set<dbNet*> flat_nets_set;
429429
for (dbInst* child_inst : module->getInsts()) {
430430
for (dbITerm* iterm : child_inst->getITerms()) {
431431
dbNet* flat_net = iterm->getNet();
@@ -447,6 +447,9 @@ DbInstanceNetIterator::DbInstanceNetIterator(const Instance* instance,
447447
}
448448
}
449449
flat_nets_vec_.assign(flat_nets_set.begin(), flat_nets_set.end());
450+
// Sort these nets so their order is determinisitc
451+
std::ranges::sort(
452+
flat_nets_vec_, {}, [](auto const* net1) { return net1->getId(); });
450453
}
451454
} else {
452455
//

src/rsz/include/rsz/Resizer.hh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <array>
77
#include <optional>
88
#include <string>
9+
#include <unordered_set>
910
#include <vector>
1011

1112
#include "db_sta/dbNetwork.hh"
@@ -738,7 +739,7 @@ class Resizer : public dbStaState, public dbNetworkObserver
738739
const MinMax* max_ = MinMax::max();
739740
LibertyCellSeq buffer_cells_;
740741
LibertyCell* buffer_lowest_drive_ = nullptr;
741-
std::set<LibertyCell*> buffer_fast_sizes_;
742+
std::unordered_set<LibertyCell*> buffer_fast_sizes_;
742743
// Buffer list created by CTS kept here so that we use the
743744
// exact same buffers when reparing clock nets.
744745
LibertyCellSeq clk_buffers_;

src/rsz/src/BaseMove.cc

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -982,14 +982,26 @@ float BaseMove::computeElmoreSlewFactor(const Pin* output_pin,
982982

983983
LibertyCellSeq BaseMove::getSwappableCells(LibertyCell* base)
984984
{
985-
LibertyCellSeq buffer_sizes;
986985
if (base->isBuffer()) {
987-
for (LibertyCell* buffer : resizer_->buffer_fast_sizes_) {
988-
buffer_sizes.push_back(buffer);
989-
}
990986
if (resizer_->buffer_fast_sizes_.count(base) == 0) {
991987
return LibertyCellSeq();
992988
}
989+
990+
LibertyCellSeq buffer_sizes;
991+
buffer_sizes.reserve(resizer_->buffer_fast_sizes_.size());
992+
for (LibertyCell* buffer : resizer_->buffer_fast_sizes_) {
993+
buffer_sizes.push_back(buffer);
994+
}
995+
// Sort output to ensure deterministic order
996+
std::ranges::sort(buffer_sizes,
997+
[](LibertyCell const* c1, LibertyCell const* c2) {
998+
auto area1 = c1->area();
999+
auto area2 = c2->area();
1000+
if (area1 != area2) {
1001+
return area1 < area2;
1002+
}
1003+
return c1->id() < c2->id();
1004+
});
9931005
return buffer_sizes;
9941006
}
9951007
return resizer_->getSwappableCells(base);

src/rsz/src/Resizer.cc

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2719,8 +2719,19 @@ NetSeq Resizer::resizeWorstSlackNets()
27192719
for (auto pair : net_slack_map_) {
27202720
nets.push_back(pair.first);
27212721
}
2722-
sort(nets, [this](const Net* net1, const Net* net2) {
2723-
return resizeNetSlack(net1) < resizeNetSlack(net2);
2722+
// We are manually breaking ties to enforce stability, so use std::sort since
2723+
// there is no need to pay the cost for stable sorting here
2724+
std::ranges::sort(nets, [this](const Net* net1, const Net* net2) {
2725+
auto slack1 = resizeNetSlack(net1);
2726+
auto slack2 = resizeNetSlack(net2);
2727+
if (slack1 != slack2) {
2728+
return slack1 < slack2;
2729+
}
2730+
2731+
// In the event of a tie use ID to keep the sort stable.
2732+
auto dbnet1 = db_network_->staToDb(net1);
2733+
auto dbnet2 = db_network_->staToDb(net2);
2734+
return dbnet1->getId() < dbnet2->getId();
27242735
});
27252736

27262737
int nworst_nets = std::ceil(nets.size() * worst_slack_nets_percent_ / 100.0);

src/rsz/src/SwapPinsMove.cc

Lines changed: 61 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
#include <optional>
1212
#include <sstream>
1313
#include <string>
14+
#include <unordered_set>
1415
#include <utility>
1516

1617
#include "BaseMove.hh"
1718
#include "odb/db.h"
1819
#include "sta/ArcDelayCalc.hh"
1920
#include "sta/Delay.hh"
21+
#include "sta/FuncExpr.hh"
2022
#include "sta/Graph.hh"
2123
#include "sta/Liberty.hh"
2224
#include "sta/MinMax.hh"
@@ -85,7 +87,7 @@ bool SwapPinsMove::doMove(const Path* drvr_path,
8587
// We get the driver port and the cell for that port.
8688
LibertyPort* input_port = network_->libertyPort(in_pin);
8789
LibertyPort* swap_port = input_port;
88-
sta::LibertyPortSet ports;
90+
LibertyPortVec ports;
8991

9092
// Skip output to output paths
9193
if (input_port->direction()->isOutput()) {
@@ -204,7 +206,7 @@ void SwapPinsMove::swapPins(Instance* inst,
204206
// (depending on agreement).
205207
void SwapPinsMove::equivCellPins(const LibertyCell* cell,
206208
LibertyPort* input_port,
207-
sta::LibertyPortSet& ports)
209+
LibertyPortVec& ports)
208210
{
209211
if (cell->hasSequentials() || cell->isIsolationCell()) {
210212
ports.clear();
@@ -230,51 +232,59 @@ void SwapPinsMove::equivCellPins(const LibertyCell* cell,
230232
}
231233
}
232234

233-
if (outputs >= 1 && inputs >= 2) {
234-
sta::LibertyCellPortIterator port_iter2(cell);
235-
while (port_iter2.hasNext()) {
236-
LibertyPort* candidate_port = port_iter2.next();
237-
if (!candidate_port->direction()->isInput()) {
238-
continue;
239-
}
235+
if (outputs < 1 || inputs < 2) {
236+
return;
237+
}
240238

241-
sta::LibertyCellPortIterator output_port_iter(cell);
242-
std::optional<bool> is_equivalent;
243-
// Loop through all the output ports and make sure they are equivalent
244-
// under swaps of candidate_port and input_port. For multi-ouput gates
245-
// like full adders.
246-
while (output_port_iter.hasNext()) {
247-
LibertyPort* output_candidate_port = output_port_iter.next();
248-
sta::FuncExpr* output_expr = output_candidate_port->function();
249-
if (!output_candidate_port->direction()->isOutput()) {
250-
continue;
251-
}
239+
sta::LibertyCellPortIterator port_iter2(cell);
240+
std::unordered_set<LibertyPort*> seen_ports;
241+
while (port_iter2.hasNext()) {
242+
LibertyPort* candidate_port = port_iter2.next();
243+
if (!candidate_port->direction()->isInput()) {
244+
continue;
245+
}
252246

253-
if (output_expr == nullptr) {
254-
continue;
255-
}
247+
sta::LibertyCellPortIterator output_port_iter(cell);
248+
std::optional<bool> is_equivalent;
249+
// Loop through all the output ports and make sure they are equivalent
250+
// under swaps of candidate_port and input_port. For multi-ouput gates
251+
// like full adders.
252+
while (output_port_iter.hasNext()) {
253+
LibertyPort* output_candidate_port = output_port_iter.next();
254+
sta::FuncExpr* output_expr = output_candidate_port->function();
255+
if (!output_candidate_port->direction()->isOutput()) {
256+
continue;
257+
}
256258

257-
if (input_port == candidate_port) {
258-
continue;
259-
}
259+
if (output_expr == nullptr) {
260+
continue;
261+
}
260262

261-
bool is_equivalent_result
262-
= isPortEqiv(output_expr, cell, input_port, candidate_port);
263+
if (input_port == candidate_port) {
264+
continue;
265+
}
263266

264-
if (!is_equivalent.has_value()) {
265-
is_equivalent = is_equivalent_result;
266-
continue;
267-
}
267+
bool is_equivalent_result
268+
= isPortEqiv(output_expr, cell, input_port, candidate_port);
268269

269-
is_equivalent = is_equivalent.value() && is_equivalent_result;
270+
if (!is_equivalent.has_value()) {
271+
is_equivalent = is_equivalent_result;
272+
continue;
270273
}
271274

272-
// candidate_port is equivalent to input_port under all output ports
273-
// of this cell.
274-
if (is_equivalent.has_value() && is_equivalent.value()) {
275-
ports.insert(candidate_port);
276-
}
275+
is_equivalent = is_equivalent.value() && is_equivalent_result;
277276
}
277+
278+
// candidate_port is equivalent to input_port under all output ports
279+
// of this cell.
280+
if (is_equivalent.has_value() && is_equivalent.value()
281+
&& !seen_ports.contains(candidate_port)) {
282+
seen_ports.insert(candidate_port);
283+
ports.push_back(candidate_port);
284+
}
285+
}
286+
if (!seen_ports.empty()) { // If we added any ports sort them.
287+
std::ranges::sort(ports, {}, [](auto* p1) { return p1->id(); });
278288
}
279289
}
280290

@@ -293,7 +303,7 @@ void SwapPinsMove::reportSwappablePins()
293303
if (!port->direction()->isInput()) {
294304
continue;
295305
}
296-
sta::LibertyPortSet ports;
306+
LibertyPortVec ports;
297307
equivCellPins(cell, port, ports);
298308
std::ostringstream ostr;
299309
for (auto port : ports) {
@@ -310,7 +320,7 @@ void SwapPinsMove::reportSwappablePins()
310320
// where 2 paths go through the same gate (we could end up swapping pins twice)
311321
void SwapPinsMove::findSwapPinCandidate(LibertyPort* input_port,
312322
LibertyPort* drvr_port,
313-
const sta::LibertyPortSet& equiv_ports,
323+
const LibertyPortVec& equiv_ports,
314324
float load_cap,
315325
const DcalcAnalysisPt* dcalc_ap,
316326
LibertyPort** swap_port)
@@ -359,18 +369,21 @@ void SwapPinsMove::findSwapPinCandidate(LibertyPort* input_port,
359369
}
360370

361371
for (LibertyPort* port : equiv_ports) {
362-
if (port_delays.find(port) == port_delays.end()) {
363-
// It's possible than an equivalent pin doesn't have
364-
// a path to the driver.
372+
// Guard Clause:
373+
// 1. Check if port delay exists
374+
// 2. Check if port is NOT an input
375+
// 3. Check if port is equivalent to input_port
376+
// 4. Check if port is equivalent to drvr_port
377+
if (!port_delays.contains(port) || !port->direction()->isInput()
378+
|| sta::LibertyPort::equiv(input_port, port)
379+
|| sta::LibertyPort::equiv(drvr_port, port)) {
365380
continue;
366381
}
367382

368-
if (port->direction()->isInput()
369-
&& !sta::LibertyPort::equiv(input_port, port)
370-
&& !sta::LibertyPort::equiv(drvr_port, port)
371-
&& port_delays[port] < base_delay) {
383+
auto port_delay = port_delays[port];
384+
if (port_delay < base_delay) {
372385
*swap_port = port;
373-
base_delay = port_delays[port];
386+
base_delay = port_delay;
374387
}
375388
}
376389
}

src/rsz/src/SwapPinsMove.hh

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
// SPDX-License-Identifier: BSD-3-Clause
22
// Copyright (c) 2025-2025, The OpenROAD Authors
33

4+
#include <vector>
5+
46
#include "BaseMove.hh"
7+
#include "sta/Liberty.hh"
8+
#include "sta/UnorderedMap.hh"
59

610
namespace rsz {
711

@@ -34,23 +38,21 @@ class SwapPinsMove : public BaseMove
3438
void reportSwappablePins();
3539

3640
private:
41+
using LibertyPortVec = std::vector<LibertyPort*>;
3742
void swapPins(Instance* inst, LibertyPort* port1, LibertyPort* port2);
3843
void equivCellPins(const LibertyCell* cell,
3944
LibertyPort* input_port,
40-
sta::LibertyPortSet& ports);
45+
LibertyPortVec& ports);
4146
void annotateInputSlews(Instance* inst, const DcalcAnalysisPt* dcalc_ap);
4247
void findSwapPinCandidate(LibertyPort* input_port,
4348
LibertyPort* drvr_port,
44-
const sta::LibertyPortSet& equiv_ports,
49+
const LibertyPortVec& equiv_ports,
4550
float load_cap,
4651
const DcalcAnalysisPt* dcalc_ap,
4752
LibertyPort** swap_port);
4853
void resetInputSlews();
4954

50-
Map<Instance*, LibertyPortTuple> swapped_pins_;
51-
InstanceSet all_swapped_pin_inst_set_;
52-
53-
sta::UnorderedMap<LibertyPort*, sta::LibertyPortSet> equiv_pin_map_;
55+
sta::UnorderedMap<LibertyPort*, LibertyPortVec> equiv_pin_map_;
5456
};
5557

5658
} // namespace rsz

src/rsz/test/BUILD

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ TESTS = [
3636
"liberty_dont_use",
3737
"pinswap_flat",
3838
"pinswap_hier",
39+
"pin_swap1",
3940
"rebuffer1",
4041
"rebuffer1_hier",
4142
"recover_power_verbose",
@@ -181,6 +182,7 @@ TESTS = [
181182
"resize5",
182183
"resize6",
183184
"resize_slack1",
185+
"resize_slack2",
184186
"resize_slack3",
185187
"set_dont_touch1",
186188
"set_dont_use1",
@@ -230,8 +232,6 @@ BIG_TESTS = [
230232
MANUAL_FOR_BAZEL_TESTS = [
231233
"repair_tie11_hier",
232234
"repair_tie12_hier",
233-
"resize_slack2",
234-
"pin_swap1",
235235
"liberty_dont_use",
236236
"pinswap_flat",
237237
"pinswap_hier",

0 commit comments

Comments
 (0)