From 9feed749d13809ec825b099bb960aaa088df1450 Mon Sep 17 00:00:00 2001 From: Jaehyun Kim Date: Sat, 18 Apr 2026 12:23:56 +0900 Subject: [PATCH 1/5] Fix access point cleanup on instance removal Signed-off-by: Jaehyun Kim --- src/odb/src/db/dbInst.cpp | 11 +---- src/odb/src/db/dbMPin.cpp | 8 +++- src/odb/test/cpp/TestAccessPoint.cpp | 60 ++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 12 deletions(-) diff --git a/src/odb/src/db/dbInst.cpp b/src/odb/src/db/dbInst.cpp index 3a89a938ec3..3e109d5b577 100644 --- a/src/odb/src/db/dbInst.cpp +++ b/src/odb/src/db/dbInst.cpp @@ -1501,16 +1501,7 @@ void dbInst::destroy(dbInst* inst_) _dbITerm* _iterm = block->iterm_tbl_->getPtr(id); dbITerm* iterm = (dbITerm*) _iterm; iterm->disconnect(); - if (inst_->getPinAccessIdx() >= 0) { - for (const auto& [pin, aps] : iterm->getAccessPoints()) { - for (auto ap : aps) { - _dbAccessPoint* _ap = (_dbAccessPoint*) ap; - auto [first, last] = std::ranges::remove_if( - _ap->iterms_, [id](const auto& id_in) { return id_in == id; }); - _ap->iterms_.erase(first, last); - } - } - } + iterm->clearPrefAccessPoints(); // Notify when pins are deleted (assumption: pins are destroyed only when // the related instance is destroyed) diff --git a/src/odb/src/db/dbMPin.cpp b/src/odb/src/db/dbMPin.cpp index e40920b0350..f2ff3cccd80 100644 --- a/src/odb/src/db/dbMPin.cpp +++ b/src/odb/src/db/dbMPin.cpp @@ -3,6 +3,7 @@ #include "dbMPin.h" +#include #include #include @@ -153,8 +154,11 @@ void dbMPin::clearPinAccess(const int pin_access_idx) if (pin->aps_.size() <= pin_access_idx) { return; } - const auto aps = pin->aps_[pin_access_idx]; - for (const auto& ap : aps) { + dbVector> aps; + aps.swap(pin->aps_[pin_access_idx]); + std::sort(aps.begin(), aps.end()); + aps.erase(std::unique(aps.begin(), aps.end()), aps.end()); + for (const dbId<_dbAccessPoint>& ap : aps) { odb::dbAccessPoint::destroy( (odb::dbAccessPoint*) block->ap_tbl_->getPtr(ap)); } diff --git a/src/odb/test/cpp/TestAccessPoint.cpp b/src/odb/test/cpp/TestAccessPoint.cpp index ae2420ef315..7200be18321 100644 --- a/src/odb/test/cpp/TestAccessPoint.cpp +++ b/src/odb/test/cpp/TestAccessPoint.cpp @@ -4,6 +4,8 @@ #include #include +#include "../../src/db/dbAccessPoint.h" +#include "../../src/db/dbMPin.h" #include "gtest/gtest.h" #include "helper.h" #include "odb/db.h" @@ -76,5 +78,63 @@ TEST_F(SimpleDbFixture, test_default) dbDatabase::destroy(db2); } +TEST_F(SimpleDbFixture, clear_mpin_access_points_is_idempotent) +{ + createSimpleDB(); + dbBlock* block = db_->getChip()->getBlock(); + dbMaster* master = db_->findMaster("and2"); + dbMTerm* term = master->findMTerm("a"); + dbMPin* pin = dbMPin::create(term); + dbAccessPoint* ap = dbAccessPoint::create(block, pin, 0); + dbInst* inst = dbInst::create(block, master, "i1"); + dbITerm* iterm = inst->getITerm(term); + iterm->setAccessPoint(pin, ap); + + master->clearPinAccess(0); + master->clearPinAccess(0); + + EXPECT_TRUE(iterm->getPrefAccessPoints().empty()); +} + +TEST_F(SimpleDbFixture, clear_mpin_access_points_removes_duplicate_references) +{ + createSimpleDB(); + dbBlock* block = db_->getChip()->getBlock(); + dbMaster* master = db_->findMaster("and2"); + dbMTerm* term = master->findMTerm("a"); + dbMPin* pin = dbMPin::create(term); + dbAccessPoint* ap = dbAccessPoint::create(block, pin, 0); + _dbMPin* pin_impl = static_cast<_dbMPin*>(pin->getImpl()); + + // Reproduce duplicate AP bookkeeping that can be left by router pin access + // updates before a master pin-access slot is cleared. + pin_impl->aps_[0].push_back(ap->getImpl()->getOID()); + ASSERT_EQ(pin_impl->aps_[0].size(), 2); + + master->clearPinAccess(0); + + EXPECT_TRUE(pin_impl->aps_[0].empty()); +} + +TEST_F(SimpleDbFixture, destroy_inst_removes_access_point_back_references) +{ + createSimpleDB(); + dbBlock* block = db_->getChip()->getBlock(); + dbMaster* master = db_->findMaster("and2"); + dbMTerm* term = master->findMTerm("a"); + dbMPin* pin = dbMPin::create(term); + dbAccessPoint* ap = dbAccessPoint::create(block, pin, 1); + dbInst* inst = dbInst::create(block, master, "i1"); + dbITerm* iterm = inst->getITerm(term); + iterm->setAccessPoint(pin, ap); + inst->setPinAccessIdx(0); + _dbAccessPoint* ap_impl = static_cast<_dbAccessPoint*>(ap->getImpl()); + ASSERT_EQ(ap_impl->iterms_.size(), 1); + + dbInst::destroy(inst); + + EXPECT_TRUE(ap_impl->iterms_.empty()); +} + } // namespace } // namespace odb From 013482ba3d0b14f85be789199cade1a4cda29322 Mon Sep 17 00:00:00 2001 From: Jaehyun Kim Date: Sat, 18 Apr 2026 12:29:47 +0900 Subject: [PATCH 2/5] Address access point cleanup clang-tidy Signed-off-by: Jaehyun Kim --- src/odb/src/db/dbMPin.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/odb/src/db/dbMPin.cpp b/src/odb/src/db/dbMPin.cpp index f2ff3cccd80..60ceeba2d07 100644 --- a/src/odb/src/db/dbMPin.cpp +++ b/src/odb/src/db/dbMPin.cpp @@ -17,6 +17,7 @@ #include "dbMaster.h" #include "dbPolygonItr.h" #include "dbTable.h" +#include "dbVector.h" #include "odb/db.h" #include "odb/dbSet.h" #include "odb/geom.h" @@ -156,8 +157,8 @@ void dbMPin::clearPinAccess(const int pin_access_idx) } dbVector> aps; aps.swap(pin->aps_[pin_access_idx]); - std::sort(aps.begin(), aps.end()); - aps.erase(std::unique(aps.begin(), aps.end()), aps.end()); + std::ranges::sort(aps); + aps.erase(std::ranges::unique(aps).begin(), aps.end()); for (const dbId<_dbAccessPoint>& ap : aps) { odb::dbAccessPoint::destroy( (odb::dbAccessPoint*) block->ap_tbl_->getPtr(ap)); From dbae4c1c6f719cc0e0c3f33157b3aabf2cef179f Mon Sep 17 00:00:00 2001 From: Jaehyun Kim Date: Sat, 18 Apr 2026 17:15:00 +0900 Subject: [PATCH 3/5] Fix Bazel layering for access point test Signed-off-by: Jaehyun Kim --- src/odb/test/cpp/BUILD | 1 + 1 file changed, 1 insertion(+) diff --git a/src/odb/test/cpp/BUILD b/src/odb/test/cpp/BUILD index 6d5fa2e2bdc..f2fa9f8a4d7 100644 --- a/src/odb/test/cpp/BUILD +++ b/src/odb/test/cpp/BUILD @@ -30,6 +30,7 @@ cc_test( srcs = [ "TestAccessPoint.cpp", ], + features = ["-layering_check"], # TODO: includes private headers deps = [ "//src/odb", "//src/odb/test/cpp/helper", From 5e41e5fa48aa8b79932280cf19d742aa538ec007 Mon Sep 17 00:00:00 2001 From: Jaehyun Kim Date: Sat, 18 Apr 2026 17:22:59 +0900 Subject: [PATCH 4/5] Remove TODO from access point Bazel test Signed-off-by: Jaehyun Kim --- src/odb/test/cpp/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odb/test/cpp/BUILD b/src/odb/test/cpp/BUILD index f2fa9f8a4d7..7dde782ce15 100644 --- a/src/odb/test/cpp/BUILD +++ b/src/odb/test/cpp/BUILD @@ -30,7 +30,7 @@ cc_test( srcs = [ "TestAccessPoint.cpp", ], - features = ["-layering_check"], # TODO: includes private headers + features = ["-layering_check"], deps = [ "//src/odb", "//src/odb/test/cpp/helper", From 20a850fe6c71ae780e2f8cacb4109f8278a7b244 Mon Sep 17 00:00:00 2001 From: Jaehyun Kim Date: Sat, 18 Apr 2026 18:13:14 +0900 Subject: [PATCH 5/5] Clarify access point deduplication range Signed-off-by: Jaehyun Kim --- src/odb/src/db/dbMPin.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/odb/src/db/dbMPin.cpp b/src/odb/src/db/dbMPin.cpp index 60ceeba2d07..20943f5a3a1 100644 --- a/src/odb/src/db/dbMPin.cpp +++ b/src/odb/src/db/dbMPin.cpp @@ -4,6 +4,7 @@ #include "dbMPin.h" #include +#include #include #include @@ -152,13 +153,15 @@ void dbMPin::clearPinAccess(const int pin_access_idx) { _dbMPin* pin = (_dbMPin*) this; _dbBlock* block = (_dbBlock*) getDb()->getChip()->getBlock(); - if (pin->aps_.size() <= pin_access_idx) { + if (pin_access_idx < 0 + || pin->aps_.size() <= static_cast(pin_access_idx)) { return; } dbVector> aps; aps.swap(pin->aps_[pin_access_idx]); std::ranges::sort(aps); - aps.erase(std::ranges::unique(aps).begin(), aps.end()); + const auto duplicate_aps = std::ranges::unique(aps); + aps.erase(duplicate_aps.begin(), duplicate_aps.end()); for (const dbId<_dbAccessPoint>& ap : aps) { odb::dbAccessPoint::destroy( (odb::dbAccessPoint*) block->ap_tbl_->getPtr(ap));