Skip to content

improve_placement crashes (std::bad_optional_access) and corrupts an already-legal placement #10677

@sfmth

Description

@sfmth

Describe the bug

improve_placement crashes (std::bad_optional_access) and corrupts an already-legal placement

Summary

On the attached designs, detailed_placement legalizes the placement 100%
(check_placement reports zero violations). The very next improve_placement
call then, on that already-legal placement:

  • with default settings, crashes with std::bad_optional_access
    ("bad optional access"); and
  • with a small -max_displacement (e.g. {5 1}), instead displaces the legal
    placement
    [WARNING DPL-0200] Unexpected displacement during legalization
    and leaves it illegal (DPL-0314 site-alignment + DPL-0311 overlaps →
    check_placement fails with DPL-0033).

improve_placement is a detailed-placement optimizer; given a legal input it
must return a legal placement (it should never make a legal placement illegal,
and never crash). Here it does both.

Environment

  • OpenROAD: 26Q2-1164-g08f67ee5ec (please also try your current master).
  • No display/X server required — detailed_placement, improve_placement, and
    check_placement are not GUI commands.
  • Each attached .odb embeds its tech LEF + liberty, so no extra files are needed.

Files

issue/
├── README.md         this report
├── reproduce.sh      portable runner (uses $OPENROAD or `openroad` on PATH)
├── reproduce.tcl     the OpenROAD script
├── expected_output.txt   sample output
└── data/
    ├── case1.odb     nangate45 design, ~85k std cells + hard macros (post global placement)
    ├── case2.odb     "  (variant)
    ├── case3.odb     "  (variant)
    └── case4.odb     "  (variant, most severe)

The four .odbs are variants of the same nangate45 design with increasing
severity of the symptom.

Manual reproduction (no scripts needed)

The shortest path — open any openroad session and paste these commands, with
<case>.odb being any of the four attached .odb files. The .odb embeds the
tech LEF + liberty, so nothing else needs to be set up; no display required.

A) Default improve_placement → crash

read_db <case>.odb
set_placement_padding -global -left 0 -right 0   ;# design has zero cell padding
detailed_placement                               ;# legalizes to 0 violations
check_placement                                  ;# passes
improve_placement                                ;# CRASHES on case1 / case3 / case4
                                                 ;# -> Error ... bad optional access

B) Small -max_displacement → corruption (DPL-0200 + DPL-0033)

read_db <case>.odb
set_placement_padding -global -left 0 -right 0
detailed_placement
check_placement                                  ;# passes
improve_placement -max_displacement {5 1}        ;# WARN DPL-0200 on a legal input
check_placement                                  ;# fails: DPL-0033 (all 4 cases)

One-liner (no interactive session; catch keeps openroad from sitting at the
prompt after the C++ exception is reported as a Tcl error):

echo 'read_db case1.odb
      set_placement_padding -global -left 0 -right 0
      detailed_placement
      check_placement
      if {[catch {improve_placement} m]} { puts "BUG: $m" }
      exit' | openroad -no_init

Last lines you should see (variant A — default):

[INFO  DPL-0310] Assigned ... cells into segments.  Movement in X-direction is 7520200.000000, ...
[WARNING DPL-0200] Unexpected displacement during legalization.
[INFO  DPL-0314] Found 447 site alignment problems.
BUG: bad optional access

Steps to reproduce (scripted)

If you'd rather not paste, the attached reproduce.{sh,tcl} automate the loop
across all four .odbs — they're a convenience, not a requirement; they call
exactly the same OpenROAD commands shown above.

# openroad must be on PATH, or:  export OPENROAD=/path/to/openroad
cd issue

# A) default improve_placement -> crash (std::bad_optional_access)
./reproduce.sh

# B) small displacement budget -> DPL-0200 + DPL-0033 (no crash)
DPL_MAXDISP="5 1" ./reproduce.sh

Or one case directly:

DPL_ODB=data/case1.odb openroad -no_init -exit reproduce.tcl                    # crash
DPL_ODB=data/case1.odb DPL_MAXDISP="5 1" openroad -no_init -exit reproduce.tcl  # DPL-0033

Expected

improve_placement returns a legal placement (check_placement passes), or at
worst leaves the input untouched. It should never crash, and never turn a legal
placement into an illegal one.

Actual (OpenROAD 26Q2-1164-g08f67ee5ec)

detailed_placement legalizes every case to 0 violations. Then:

case detailed_placement improve_placement (default) improve_placement -max_displacement {5 1}
case1 LEGAL (0 viol) CRASH bad optional access DPL-0033 (379 site-align)
case2 LEGAL (0 viol) DPL-0033 (58 site-align) DPL-0033 (445 site-align)
case3 LEGAL (0 viol) CRASH bad optional access DPL-0033 (1034 site-align)
case4 LEGAL (0 viol) CRASH bad optional access DPL-0033 (1732 site-align)

In the non-crash runs, improve_placement first logs, on the legal input:

[INFO  DPL-0310] Assigned N cells into segments.  Movement in X-direction is 7520200, movement in Y-direction is 1545600.
[WARNING DPL-0200] Unexpected displacement during legalization.
[INFO  DPL-0314] Found 447 site alignment problems.
[INFO  DPL-0311] Found 194 overlaps between adjacent cells.
...
[ERROR DPL-0033] detailed placement checks failed during check placement.

i.e. it moves millions of DBU of cells while setting up on a placement that was
already legal, then cannot put them back. (Full sample in expected_output.txt.)

Likely cause (code pointers; line numbers for the tested version)

  • src/dpl/src/Optdp.cppOpendp::improvePlacement() runs a ShiftLegalizer
    to populate its data structures; the comment there explicitly states that a
    warning on a legal input means a bug:

    // If it errors or prints a warning when given a legal placement, that likely means there is a bug in my code somewhere.

    We hit exactly that warning (DPL-0200, emitted in
    src/dpl/src/legalize_shift.cxx).

  • The crash is an unchecked std::optional dereference. improve_placement
    evaluates a candidate site via
    grid_->getSiteOrientation(x, y, site).value()
    (src/dpl/src/Place.cpp, two call sites), and Grid::getSiteOrientation(...)
    (src/dpl/src/infrastructure/Grid.cpp) returns an empty optional for a
    position with no matching site — so .value() throws std::bad_optional_access.
    The default displacement reaches such a position; a small -max_displacement
    stays local and instead exposes the displacement/legality bug above.

Note

detailed_placement alone (i.e. improve_placement disabled) produces a fully
legal placement on all four cases — so these designs are legalizable; the failure
is specific to improve_placement.

Download files here: https://drive.google.com/file/d/1Rxcz75mDLKc7uCLHDvmXcCR276qoNRcY/view?usp=sharing

Expected Behavior

in the first prompt
Download files here: https://drive.google.com/file/d/1Rxcz75mDLKc7uCLHDvmXcCR276qoNRcY/view?usp=sharing

Environment

in the first prompt
Download files here: [https://drive.google.com/file/d/1Rxcz75mDLKc7uCLHDvmXcCR276qoNRcY/view?usp=sharing](https://drive.google.com/file/d/1Rxcz75mDLKc7uCLHDvmXcCR276qoNRcY/view?usp=sharing)

To Reproduce

Download files here: https://drive.google.com/file/d/1Rxcz75mDLKc7uCLHDvmXcCR276qoNRcY/view?usp=sharing

Relevant log output

Screenshots

No response

Additional Context

No response

Metadata

Metadata

Assignees

Labels

dpoDetailed Placement Optimization

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions