Sitelet https://github.com/starkware-libs/cairo/pull/10033
Skip to content

fix(lowering): early_unsafe_panic must keep side-effecting statements - #10033

Merged
orizi merged 1 commit into
mainfrom
orizi/06-04-fix_lowering_early_unsafe_panic_must_keep_side-effecting_statements
Jun 4, 2026
Merged

orizi merged 1 commit into
mainfrom
orizi/06-04-fix_lowering_early_unsafe_panic_must_keep_side-effecting_statements

Conversation

@orizi

@orizi orizi commented Jun 4, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Fixes the early_unsafe_panic optimization so that the unsafe_panic insertion point is placed after the last side-effecting statement in a block, rather than at the statement itself. Previously, the loop iterated forward and pushed the fix at index i (overwriting the side-effecting statement), and the test harness ran against PreOptimizations lowering where inlined externs like debug::print were not yet visible. Now the loop iterates in reverse, finds the last side-effecting statement, and inserts the early panic at i + 1 — preserving the side effect. The test stage is also updated to PostBaseline so that inlined side-effecting externs are present when the optimization runs.


Type of change

Please check one:

  • Bug fix (fixes incorrect behavior)
  • New feature
  • Performance improvement
  • Documentation change with concrete technical impact
  • Style, wording, formatting, or typo-only change

Why is this change needed?

The previous forward-iterating loop pushed a fix at index i for the first side-effecting statement it found, which would replace that statement with an unsafe_panic call instead of inserting the panic after it. This caused side-effecting statements (e.g., debug::print) to be dropped from the lowered output. Additionally, the test harness used PreOptimizations lowering, which does not include inlined externs, so the bug was not caught by the existing test for the side-effect-preservation case.


What was the behavior or documentation before?

When a block contained a side-effecting statement followed by dead code (no reachable return), the optimization replaced the side-effecting statement itself with unsafe_panic, discarding the side effect.


What is the behavior or documentation after?

The optimization now inserts unsafe_panic immediately after the last side-effecting statement, preserving all side effects. A new test case (Test that a side-effecting statement (debug::print) before the dead tail is preserved) verifies this behavior using PostBaseline lowering.


Related issue or discussion (if any)

N/A


Additional context

The fix also simplifies the conditional logic: instead of checking ReachableSideEffects::Reachable to early-return and then re-checking Unreachable inside the loop, the new code destructures Unreachable upfront and returns early if the state is Reachable.

  The pass replaces the dead tail of a function (from which return is
  unreachable) with unsafe_panic, and is meant to preserve side-effecting
  libfunc calls (debug::print / internal::trace) that must still run first.
  But it scanned statements forward and truncated AT the first side effect
  (`truncate(i)`), deleting the very statement it meant to keep. It also only
  considered the first side effect, dropping later ones in the same block.

  Scan in reverse and truncate after the last side effect (`i + 1`). Move the
  test from PreOptimizations to PostBaseline (where this pass actually runs, so
  print is the inlined extern it detects) and add a regression case.

  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@reviewable-StarkWare

Copy link
Copy Markdown

This change is Reviewable

orizi commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@orizi
orizi marked this pull request as ready for review June 4, 2026 08:05
@cursor

cursor Bot commented Jun 4, 2026 •

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes compiler lowering for unreachable paths and which statements are truncated before unsafe_panic; incorrect placement could still drop observable side effects or alter panic behavior.

Overview
Fixes early_unsafe_panic so dead tails are cut after the last side-effecting lowered call (e.g. debug::print), not at that statement—by scanning block statements in reverse and recording the fix at i + 1.

The early_unsafe_panic test harness now snapshots PostBaseline lowering (where inlining exposes those externs), with updated expectations and a debug::print case proving the print call survives before unsafe_panic.

Reviewed by Cursor Bugbot for commit 782884c. Bugbot is set up for automated code reviews on this repo. Configure here.

@eytan-starkware eytan-starkware left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:lgtm:

@eytan-starkware reviewed 3 files and all commit messages, and made 1 comment.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on TomerStarkware).

@orizi
orizi added this pull request to the merge queue Jun 4, 2026
Merged via the queue into main with commit 17ef2ee Jun 4, 2026
54 checks passed
@orizi
orizi deleted the orizi/06-04-fix_lowering_early_unsafe_panic_must_keep_side-effecting_statements branch June 6, 2026 16:23
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.

3 participants