WIP: hardening DeepRegion against min_coherence toggles (IHP PDK optimization)

This commit is contained in:
Matthias Koefferlein 2026-07-22 00:00:33 +02:00
parent 5ae17c17a5
commit a82f428d4c
3 changed files with 151 additions and 6 deletions

View File

@ -179,8 +179,10 @@ DeepRegion::~DeepRegion ()
DeepRegion::DeepRegion (const DeepRegion &other)
: MutableRegion (other), DeepShapeCollectionDelegateBase (other),
m_merged_polygons_valid (other.m_merged_polygons_valid),
m_merged_polygons_min_coherence (other.m_merged_polygons_min_coherence),
m_merged_polygons_boc_hash (other.m_merged_polygons_boc_hash),
m_is_merged (other.m_is_merged)
m_is_merged (other.m_is_merged),
m_is_merged_min_coherence (other.m_is_merged_min_coherence)
{
if (m_merged_polygons_valid) {
m_merged_polygons = other.m_merged_polygons.copy ();
@ -196,8 +198,10 @@ DeepRegion::operator= (const DeepRegion &other)
DeepShapeCollectionDelegateBase::operator= (other);
m_merged_polygons_valid = other.m_merged_polygons_valid;
m_merged_polygons_min_coherence = other.m_merged_polygons_min_coherence;
m_merged_polygons_boc_hash = other.m_merged_polygons_boc_hash;
m_is_merged = other.m_is_merged;
m_is_merged_min_coherence = other.m_is_merged_min_coherence;
if (m_merged_polygons_valid) {
m_merged_polygons = other.m_merged_polygons.copy ();
}
@ -210,9 +214,11 @@ DeepRegion::operator= (const DeepRegion &other)
void DeepRegion::init ()
{
m_merged_polygons_valid = false;
m_merged_polygons_min_coherence = false;
m_merged_polygons_boc_hash = 0;
m_merged_polygons = db::DeepLayer ();
m_is_merged = false;
m_is_merged_min_coherence = false;
}
RegionDelegate *
@ -228,7 +234,7 @@ void DeepRegion::merged_semantics_changed ()
void DeepRegion::min_coherence_changed ()
{
set_is_merged (false);
// merged status is tracked in separate variables and is validated on inquiry
}
void DeepRegion::join_properties_on_merge_changed ()
@ -468,7 +474,7 @@ DeepRegion::empty () const
bool
DeepRegion::is_merged () const
{
return m_is_merged;
return m_is_merged && (m_is_merged_min_coherence == min_coherence ());
}
const db::Polygon *
@ -739,18 +745,21 @@ DeepRegion::merged_deep_layer () const
bool
DeepRegion::merged_polygons_available () const
{
return m_is_merged || (m_merged_polygons_valid && m_merged_polygons_boc_hash == deep_layer ().breakout_cells_hash ());
return (m_is_merged && m_is_merged_min_coherence == min_coherence ()) ||
(m_merged_polygons_valid && m_merged_polygons_min_coherence == min_coherence () && m_merged_polygons_boc_hash == deep_layer ().breakout_cells_hash ());
}
void
DeepRegion::ensure_merged_polygons_valid () const
{
if (! m_merged_polygons_valid || (! m_is_merged && m_merged_polygons_boc_hash != deep_layer ().breakout_cells_hash ())) {
if (! m_merged_polygons_valid || m_merged_polygons_min_coherence != min_coherence () ||
((! m_is_merged || m_is_merged_min_coherence != min_coherence ()) && m_merged_polygons_boc_hash != deep_layer ().breakout_cells_hash ())) {
if (m_is_merged) {
if (m_is_merged && m_is_merged_min_coherence == min_coherence ()) {
// NOTE: this will reuse the deep layer reference
m_merged_polygons = deep_layer ();
m_merged_polygons_min_coherence = min_coherence ();
} else {
@ -796,6 +805,7 @@ DeepRegion::ensure_merged_polygons_valid () const
}
m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = min_coherence ();
m_merged_polygons_boc_hash = deep_layer ().breakout_cells_hash ();
}
@ -813,7 +823,9 @@ DeepRegion::ensure_unmerged_polygons_valid () const
db::DeepLayer &polygons = const_cast<db::DeepLayer &> (deep_layer ());
m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = m_is_merged_min_coherence;
m_is_merged = false;
m_is_merged_min_coherence = min_coherence ();
m_merged_polygons_boc_hash = deep_layer ().breakout_cells_hash ();
db::Layout &layout = polygons.layout ();
@ -845,6 +857,7 @@ void
DeepRegion::set_is_merged (bool f)
{
m_is_merged = f;
m_is_merged_min_coherence = min_coherence ();
m_merged_polygons_valid = false;
m_merged_polygons_boc_hash = 0;
m_merged_polygons = db::DeepLayer ();
@ -2026,6 +2039,7 @@ DeepRegion::merged () const
res->deep_layer ().layer ();
res->set_min_coherence (min_coherence ());
res->set_is_merged (true);
return res.release ();
}

View File

@ -179,8 +179,10 @@ private:
mutable DeepLayer m_merged_polygons;
mutable bool m_merged_polygons_valid;
mutable bool m_merged_polygons_min_coherence;
mutable size_t m_merged_polygons_boc_hash;
mutable bool m_is_merged;
mutable bool m_is_merged_min_coherence;
void init ();
void ensure_merged_polygons_valid () const;

View File

@ -3341,3 +3341,132 @@ TEST(deep_region_peel)
db::compare_layouts (_this, ly, tl::testdata () + "/algo/deep_region_peel_au.gds");
}
TEST(min_coherence_and_merge)
{
db::Layout ly;
auto &top = ly.cell (ly.add_cell ("TOP"));
unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0));
top.shapes (l1).insert (db::Box (0, 0, 1000, 2000));
top.shapes (l1).insert (db::Box (1000, 2000, 10000, 3000));
top.shapes (l1).insert (db::Box (9000, 3000, 10000, 10000));
db::DeepShapeStore dss;
db::Region r1 (db::RecursiveShapeIterator (ly, top, l1), dss);
EXPECT_EQ (r1.min_coherence (), false);
EXPECT_EQ (r1.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::DeepRegion *> (r1.delegate ())->merged_polygons_available (), false);
EXPECT_EQ (r1.to_string (), "(0,0;0,2000;1000,2000;1000,0);(1000,2000;1000,3000;10000,3000;10000,2000);(9000,3000;9000,10000;10000,10000;10000,3000)");
// forces merged status
r1.sized (1);
EXPECT_EQ (r1.min_coherence (), false);
EXPECT_EQ (r1.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::DeepRegion *> (r1.delegate ())->merged_polygons_available (), true);
db::Region r2 = r1.merged ();
EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)");
EXPECT_EQ (r2.min_coherence (), false);
EXPECT_EQ (r1.min_coherence (), false);
EXPECT_EQ (r1.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::DeepRegion *> (r1.delegate ())->merged_polygons_available (), true);
r1.set_min_coherence (true);
// valid_merged_polygons is invalidated
EXPECT_EQ (r1.min_coherence (), true);
EXPECT_EQ (r1.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::DeepRegion *> (r1.delegate ())->merged_polygons_available (), false);
r1.set_min_coherence (false);
// setting min_coherence to false will bring the validation back
EXPECT_EQ (r1.min_coherence (), false);
EXPECT_EQ (r1.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::DeepRegion *> (r1.delegate ())->merged_polygons_available (), true);
r1.set_min_coherence (true);
// valid_merged_polygons is invalidated again by setting min_coherence to true
EXPECT_EQ (r1.min_coherence (), true);
EXPECT_EQ (r1.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::DeepRegion *> (r1.delegate ())->merged_polygons_available (), false);
// inserting a box into the region will reset the merged valid flag and not bring it back by resetting min_coherence
db::Region r3 = r1;
r3.insert (db::Box (0, 0, 100, 100));
r3.set_min_coherence (false);
EXPECT_EQ (r3.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::DeepRegion *> (r3.delegate ())->merged_polygons_available (), false);
// creates a intrinsically merged copy and tests if the min_coherence
// flag is inherited
r3 = r1.merged ();
EXPECT_EQ (r3.to_string (), "(0,0;0,2000;1000,2000;1000,0);(1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000)");
EXPECT_EQ (r3.is_merged (), true);
EXPECT_EQ (r3.min_coherence (), true);
// merged polygons are validated against min_coherence = true during r1.merged() before, so
// resetting min_coherence to false will invalidat them
r1.set_min_coherence (false);
EXPECT_EQ (r1.min_coherence (), false);
EXPECT_EQ (r1.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::DeepRegion *> (r1.delegate ())->merged_polygons_available (), false);
// creates a intrinsically merged copy and tests if the min_coherence
// flag is inherited
r3 = r1.merged ();
EXPECT_EQ (r3.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)");
EXPECT_EQ (r3.is_merged (), true);
EXPECT_EQ (r3.min_coherence (), false);
// r2 is an intrinsically merged region (with min coherence false)
EXPECT_EQ (r2.min_coherence (), false);
EXPECT_EQ (r2.is_merged (), true);
EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)");
// after changing min_coherence, the merged status is no longer true
r2.set_min_coherence (true);
EXPECT_EQ (r2.min_coherence (), true);
EXPECT_EQ (r2.is_merged (), false);
EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)");
// read-only experiments with r2
// inserting a box into the region will reset the merged state
// and not bring it back by resetting min_coherence
r3 = r2;
r3.insert (db::Box (0, 0, 100, 100));
r3.set_min_coherence (false);
EXPECT_EQ (r3.is_merged (), false);
EXPECT_EQ (r3.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0);(0,0;0,100;100,100;100,0)");
// unmerge after resetting min coherence
r3 = r2;
r3.set_min_coherence (false);
EXPECT_EQ (r3.is_merged (), true);
r3.begin_unmerged ();
EXPECT_EQ (r3.is_merged (), false);
EXPECT_EQ (r3.to_string (), "(1000,2000;1000,3000;10000,3000;10000,2000);(9000,3000;9000,10000;10000,10000;10000,3000);(0,0;0,2000;1000,2000;1000,0)");
// r2 still remembers it is merged, even though it does not say so and
// will unmerge
r3 = r2;
EXPECT_EQ (r3.is_merged (), false);
// unmerge
r3.begin_unmerged ();
EXPECT_EQ (r3.is_merged (), false);
EXPECT_EQ (r3.to_string (), "(1000,2000;1000,3000;10000,3000;10000,2000);(9000,3000;9000,10000;10000,10000;10000,3000);(0,0;0,2000;1000,2000;1000,0)");
// r2 merged state it can be restored by resetting min_coherence to false
r2.set_min_coherence (false);
EXPECT_EQ (r2.min_coherence (), false);
EXPECT_EQ (r2.is_merged (), true);
EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)");
}