Skip to content

mpl: improved notches#9282

Merged
maliberty merged 32 commits intoThe-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:mpl-improve-notches
Feb 4, 2026
Merged

mpl: improved notches#9282
maliberty merged 32 commits intoThe-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:mpl-improve-notches

Conversation

@openroad-ci
Copy link
Collaborator

This PR improves the notch detection for the notch penalty calculation. It achieves it by considering closely aligned macros as aligned (in order to generate a more meaningful grid) and merging cells in order to find the notches actual size and amount.

It also enhances the debug visualization, showing the notches when using the mpl::mpl_debug command.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

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 introduces an improved notch detection algorithm for penalty calculation, along with debug visualization for the detected notches. The core logic changes are in SACoreSoftMacro::calNotchPenalty. My review focuses on enhancing the performance and maintainability of this new algorithm. I've identified a significant performance bottleneck due to redundant computations and suggest a fix. Additionally, I've provided recommendations for refactoring to reduce code duplication, replacing magic numbers with constants, and removing a non-standard header for better portability.


#include "SACoreSoftMacro.h"

#include <bits/ranges_algo.h>
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The header <bits/ranges_algo.h> is a non-standard, GCC-specific implementation detail. For better portability, it's best to rely on standard C++ headers. Since <algorithm> and <ranges> are already included, this header is likely unnecessary and should be removed.

Comment on lines 703 to 718
std::vector<int> x_coords;
int epsilon = outline_.dx() / 100;
for (size_t i = 0; i < x_point.size(); i++) {
if (i + 1 < x_point.size()
&& std::abs(x_point[i + 1] - x_point[i]) <= epsilon) {
continue;
}
x_point.insert(macro.getX());
x_point.insert(macro.getX() + macro.getWidth());
y_point.insert(macro.getY());
y_point.insert(macro.getY() + macro.getHeight());
x_coords.push_back(x_point[i]);
}

std::vector<int> y_coords;
epsilon = outline_.dy() / 100;
for (size_t i = 0; i < y_point.size(); i++) {
if (i + 1 < y_point.size()
&& std::abs(y_point[i + 1] - y_point[i]) <= epsilon) {
continue;
}
y_coords.push_back(y_point[i]);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The value 100 is used as a magic number in two places to calculate epsilon. It would be better to define it as a single named constant (e.g., constexpr int kCoordEpsilonDivisor = 100;) at a suitable scope to improve readability, maintainability, and ensure consistency.

References
  1. Define tunable parameters as named constants instead of using hardcoded magic numbers.

Comment on lines 844 to 864
if (n.total() == 4) {
notch_penalty_ += calSingleNotchPenalty(block_->dbuToMicrons(width),
block_->dbuToMicrons(height));
if (graphics_) {
graphics_->addNotch(odb::Rect(x_coords[start_col],
y_coords[start_row],
x_coords[end_col + 1],
y_coords[end_row + 1]));
}
} else if (n.top && n.bottom) {
if (height < notch_h_th_) {
notch_penalty_ += calSingleNotchPenalty(block_->dbuToMicrons(width),
block_->dbuToMicrons(height));
if (graphics_) {
graphics_->addNotch(odb::Rect(x_coords[start_col],
y_coords[start_row],
x_coords[end_col + 1],
y_coords[end_row + 1]));
}
}
} else if (n.left && n.right) {
if (width < notch_v_th_) {
notch_penalty_ += calSingleNotchPenalty(block_->dbuToMicrons(width),
block_->dbuToMicrons(height));
if (graphics_) {
graphics_->addNotch(odb::Rect(x_coords[start_col],
y_coords[start_row],
x_coords[end_col + 1],
y_coords[end_row + 1]));
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The code block for calculating the notch penalty and adding the notch for visualization is duplicated in three places within the if-else if structure. This can be refactored to improve maintainability and reduce redundancy.

You could determine if a notch should be penalized using a boolean flag, and then have a single block of code that performs the penalty calculation and graphics update.

      bool is_notch = false;
      if (n.total() == 4) {
        is_notch = true;
      } else if (n.top && n.bottom) {
        if (height < notch_h_th_) {
          is_notch = true;
        }
      } else if (n.left && n.right) {
        if (width < notch_v_th_) {
          is_notch = true;
        }
      }

      if (is_notch) {
        notch_penalty_ += calSingleNotchPenalty(block_->dbuToMicrons(width),
                                                block_->dbuToMicrons(height));
        if (graphics_) {
          graphics_->addNotch(odb::Rect(x_coords[start_col],
                                        y_coords[start_row],
                                        x_coords[end_col + 1],
                                        y_coords[end_row + 1]));
        }
      }

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

#include <cmath>
#include <iterator>
#include <limits>
#include <ranges>
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: included header limits is not used directly [misc-include-cleaner]

Suggested change
#include <ranges>
#include <ranges>

#include <iterator>
#include <limits>
#include <ranges>
#include <set>
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: included header ranges is not used directly [misc-include-cleaner]

Suggested change
#include <set>
#include <set>


if (graphics_) {
graphics_->setNotchPenalty(
{"Notch", notch_weight_, notch_penalty_, norm_notch_penalty_});
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: use designated initializer list to initialize 'PenaltyData' [modernize-use-designated-initializers]

Suggested change
{"Notch", notch_weight_, notch_penalty_, norm_notch_penalty_});
{.name="Notch", .weight=notch_weight_, .value=notch_penalty_, .normalization_factor=norm_notch_penalty_});
Additional context

src/mpl/src/mpl-util.h:89: aggregate type is defined here

struct PenaltyData
^


std::vector<int> x_coords;
int epsilon = outline_.dx() / 100;
for (size_t i = 0; i < x_point.size(); i++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: no header providing "size_t" is directly included [misc-include-cleaner]

src/mpl/src/SACoreSoftMacro.cpp:9:

- #include <iterator>
+ #include <cstddef>
+ #include <iterator>

@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@joaomai joaomai requested a review from AcKoucher January 21, 2026 13:54
Copy link
Contributor

@AcKoucher AcKoucher left a comment

Choose a reason for hiding this comment

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

I think that the notch function is too big and should be broken into smaller pieces, that's why most of the comments. We've been making a large effort to a more granular code and it has been proven to be a very helpful thing.

I tend to prefer functions over lambdas if it starts to get too big. I think it does depend on the context (it looks that the idea here was to keep the notch detection code well encapsulated), but for concision and readability, let's keep things smaller. We can have a "section" of methods only for notch penalty.

A question: are the notches being erased when rendering after MPL is done? (see eraseDrawing).

bool left = true;
bool right = true;

int total() { return top + bottom + left + right; }
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not a fan of implicit casting. However the logic here is sound. @maliberty would you have an opinion on it?

for (int col = 0; col < num_x; col++) {
if (grid[row][col]) {
continue;
auto neighbors = [&](int row1, int col1, int row2, int col2) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should be a function `findNeighbors".

int end_row = start_row;
int end_col = start_col;

Neighbors n = neighbors(start_row, start_col, end_row, end_col);
Copy link
Contributor

Choose a reason for hiding this comment

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

Please, avoid this type of abbreviation.

return neighbors;
};

auto valid = [&](int row1, int col1, int row2, int col2) {
Copy link
Contributor

@AcKoucher AcKoucher Jan 21, 2026

Choose a reason for hiding this comment

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

This should also be a function. Validity doesn't feel like the best quality here. Perhaps expansionOverlapped or something in this sense? I'm not attached to my suggestion.

return;
}

struct Neighbors
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be a member of the annealer itself. I also think we should have a comment explaining what are Neighbors (seeing a boolean in the code doesn't tell us much).

@joaomai joaomai requested a review from AcKoucher January 22, 2026 12:22
@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

painter.drawRect(rect);
}

// notches_.clear();
Copy link
Contributor

Choose a reason for hiding this comment

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

Leftover?

@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@joaomai joaomai requested a review from AcKoucher January 28, 2026 16:36

bool enhancements_on_ = false;
bool centralization_was_reverted_ = false;
bool is_single_array_single_std_cell_cluster = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would make more readable for the perspective of HierRTLMP class if this bool has something like force_centralization_.

Then, when setting it we'd have:

if (single_array_single_std_cell_cluster) {
  sa->forceCentralization();
}

@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@joaomai
Copy link
Contributor

joaomai commented Jan 29, 2026

Currently blocked by #3846 on ORFS.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 3, 2026

clang-tidy review says "All clean, LGTM! 👍"

Signed-off-by: João Mai <[email protected]>
@github-actions
Copy link
Contributor

github-actions bot commented Feb 4, 2026

clang-tidy review says "All clean, LGTM! 👍"

Signed-off-by: João Mai <[email protected]>
@github-actions
Copy link
Contributor

github-actions bot commented Feb 4, 2026

clang-tidy review says "All clean, LGTM! 👍"

@joaomai
Copy link
Contributor

joaomai commented Feb 4, 2026

Paired with #3863 on ORFS.

@joaomai joaomai requested a review from maliberty February 4, 2026 13:03
Comment on lines +652 to +653
if (i + 1 < x_point.size()
&& std::abs(x_point[i + 1] - x_point[i]) <= epsilon) {
Copy link
Member

Choose a reason for hiding this comment

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

This has the potential to skip too many points. Imagine a series of points all 0.9 epsilon apart. You would collapse them all. It would be better to set a threshold from an accepted point and skip all points within that threshold.

Changing this will require another secure CI so I suggest you do it in a follow up PR.

Comment on lines +716 to +720
bool SACoreSoftMacro::isSegmentEmpty(std::vector<std::vector<bool>>& grid,
int start_row,
int start_col,
int end_row,
int end_col)
Copy link
Member

Choose a reason for hiding this comment

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

All arguments should be const and the method itself can be static.

Comment on lines +669 to +674
SACoreSoftMacro::Neighbors SACoreSoftMacro::findNeighbors(
std::vector<std::vector<bool>>& grid,
int start_row,
int start_col,
int end_row,
int end_col)
Copy link
Member

Choose a reason for hiding this comment

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

same: const + static

Comment on lines +854 to +855
if (current_neighbors.total() == 4) {
is_notch = true;
Copy link
Member

Choose a reason for hiding this comment

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

Pathological but you could have neighbors on all sides of a region larger than notch_h/v_th_ in both directions (e.g. a ring of macros). Probably that shouldn't be a notch.

Comment on lines +77 to +78
// Used to calculate notches
struct Neighbors
Copy link
Member

Choose a reason for hiding this comment

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

I had to read the code to understand the semantics of these bools. A better descriptive comment would be helpful.

@maliberty maliberty merged commit 33cde43 into The-OpenROAD-Project:master Feb 4, 2026
13 checks passed
@maliberty maliberty deleted the mpl-improve-notches branch February 4, 2026 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants