Skip to content

cold block reordering and jump normalization#7210

Merged
youknowone merged 2 commits intoRustPython:mainfrom
youknowone:cold-and-jump
Feb 23, 2026
Merged

cold block reordering and jump normalization#7210
youknowone merged 2 commits intoRustPython:mainfrom
youknowone:cold-and-jump

Conversation

@youknowone
Copy link
Member

@youknowone youknowone commented Feb 23, 2026

Add mark_cold, push_cold_blocks_to_end, and normalize_jumps passes to the codegen CFG pipeline. Use JumpNoInterrupt for exception handler exit paths in try-except-finally compilation.

Summary by CodeRabbit

  • Bug Fixes

    • Improved exception/unwinding behavior to better manage interrupts during cleanup paths.
  • Refactor

    • Reordered cold/infrequently-used code blocks to the end for more efficient layout.
    • Normalized jump/control-flow sequences to reduce redundant transitions and improve stability.
  • Tests

    • Updated runtime and debug handling so no-op markers are ignored during execution and annotation output.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 23, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b9eb37 and 38e342a.

⛔ Files ignored due to path filters (6)
  • Lib/test/test_peepholer.py is excluded by !Lib/**
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_bool_op.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap is excluded by !**/*.snap
📒 Files selected for processing (5)
  • crates/codegen/src/compile.rs
  • crates/codegen/src/ir.rs
  • crates/compiler-core/src/bytecode/instruction.rs
  • crates/jit/src/instructions.rs
  • crates/jit/tests/common.rs

📝 Walkthrough

Walkthrough

This change adds IR cold-block analysis and reordering, inserts jump-normalization, changes exception-cleanup jumps from Jump to JumpNoInterrupt, and treats a new NotTaken pseudo-instruction as a no-op across formatter, JIT, and tests.

Changes

Cohort / File(s) Summary
IR cold-block analysis & passes
crates/codegen/src/ir.rs
Adds cold: bool to Block; implements mark_cold, push_cold_blocks_to_end, normalize_jumps, is_conditional_jump; runs new passes in CodeInfo::finalize_code; imports VecDeque.
Compile exception-jump semantics
crates/codegen/src/compile.rs
Replaces Jump { target } with JumpNoInterrupt { target } in exception-cleanup/unwind paths.
NotTaken instruction handling
crates/compiler-core/src/bytecode/instruction.rs, crates/jit/src/instructions.rs, crates/jit/tests/common.rs
Adds NotTaken display branch; treats NotTaken as a no-op in JIT instruction insertion and test annotation/stack-machine handling.

Sequence Diagram(s)

sequenceDiagram
    participant Frontend as Frontend/Bytecode
    participant Codegen as Codegen(IR)
    participant Finalizer as CodeInfo::finalize_code
    participant Compiler as compile.rs
    participant JIT as JIT/StackMachine

    Frontend->>Codegen: produce basic blocks & jumps
    Codegen->>Finalizer: finalize_code(blocks)
    Note over Finalizer: runs label_exception_targets\nthen mark_cold -> push_cold_blocks_to_end -> normalize_jumps
    Finalizer-->>Codegen: reordered/normalized blocks
    Codegen->>Compiler: emit control-flow (Jump / JumpNoInterrupt)
    Note over Compiler: exception cleanup paths now use JumpNoInterrupt
    Compiler->>JIT: lower/emit instructions (includes NotTaken)
    JIT->>JIT: treat NotTaken as NOP during codegen/execution
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • ShaharNaveh

Poem

"I hop through blocks both cold and neat,
I nudge the jumps so flows can meet,
No interrupt while cleaning up,
I nibble NotTaken—it's just a sup,
A rabbit's tweak to make code sweet 🐇✨"

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: introducing cold block reordering and jump normalization passes to the CFG pipeline.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

Code has been automatically formatted

The code in this PR has been formatted using:

  • cargo fmt --all
    Please pull the latest changes before pushing again:
git pull origin cold-and-jump

@youknowone youknowone force-pushed the cold-and-jump branch 2 times, most recently from 37691ba to 1da0e65 Compare February 23, 2026 15:16
@github-actions
Copy link
Contributor

github-actions bot commented Feb 23, 2026

📦 Library Dependencies

The following Lib/ modules were modified. Here are their dependencies:

[ ] test: cpython/Lib/test/test_compile.py (TODO: 24)
[x] test: cpython/Lib/test/test_compiler_assemble.py
[x] test: cpython/Lib/test/test_compiler_codegen.py
[x] test: cpython/Lib/test/test_peepholer.py (TODO: 31)

dependencies:

dependent tests: (no tests depend on compile)

Legend:

  • [+] path exists in CPython
  • [x] up-to-date, [ ] outdated

@youknowone youknowone marked this pull request as ready for review February 23, 2026 23:30
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
crates/codegen/src/ir.rs (2)

1164-1210: Consider extracting the repeated has_fallthrough predicate.

The same three-line pattern:

.last().map(|ins| !ins.instr.is_scope_exit() && !ins.instr.is_unconditional_jump()).unwrap_or(true)

appears in mark_cold (line 1183–1185), push_cold_blocks_to_end (line 1231–1234), and label_exception_targets. A small free function would eliminate the duplication.

♻️ Proposed helper
+/// Returns true if `block` falls through to its `next` block
+/// (i.e. its last instruction is neither a scope exit nor an unconditional jump).
+fn block_has_fallthrough(block: &Block) -> bool {
+    block
+        .instructions
+        .last()
+        .map(|ins| !ins.instr.is_scope_exit() && !ins.instr.is_unconditional_jump())
+        .unwrap_or(true)
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/codegen/src/ir.rs` around lines 1164 - 1210, The three-line
fallthrough predicate (.instructions.last().map(|ins| !ins.instr.is_scope_exit()
&& !ins.instr.is_unconditional_jump()).unwrap_or(true)) is duplicated across
mark_cold, push_cold_blocks_to_end, and label_exception_targets; extract it as a
small free function (e.g., fn last_allows_fallthrough(block: &Block) -> bool or
fn instr_allows_fallthrough(ins: Option<&InstrWrapper>) -> bool) and replace the
inline expression in those functions with a call to the new helper, keeping
semantics identical (true when no last instruction, otherwise !is_scope_exit &&
!is_unconditional_jump).

1252-1271: Document the cold→warm jump invariant at the insertion site.

PseudoInstruction::JumpNoInterrupt is correctly included in is_unconditional_jump() for both the fixup filter and normalize_jumps, so no functional issue exists there.

However, the ordering invariant that enables the unconditional conversion to JumpBackwardNoInterrupt in finalize_code (line 318) is implicit: JumpNoInterrupt is inserted here before cold blocks are reordered to the end, and only after reordering does every cold→warm jump become backward. This dependency should be documented at the insertion site (around line 1257) to clarify why the later conversion is safe, and optionally reinforced with a debug_assert! at conversion time.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/codegen/src/ir.rs` around lines 1252 - 1271, Document that the
JumpNoInterrupt insertion relies on the invariant that cold blocks are moved to
the end later so that any cold→warm JumpNoInterrupt becomes a backward jump
during finalize_code; add a brief comment at the jump insertion site (around
jump_block creation / PseudoInstruction::JumpNoInterrupt) explaining this
dependency and referencing normalize_jumps and finalize_code, and add a
debug_assert! in finalize_code near the conversion to JumpBackwardNoInterrupt
that checks the jump target is backward for JumpNoInterrupt (so the invariant is
validated in debug builds).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/codegen/src/ir.rs`:
- Around line 1328-1339: The is_conditional_jump function currently only matches
PopJumpIf* variants; update its match in is_conditional_jump to also include
Instruction::ForIter { .. } and Instruction::Send { .. } so those
conditional-jump instructions (which have target labels and require NOT_TAKEN
hints) are treated as conditional jumps alongside
Instruction::PopJumpIfFalse/True/None/NotNone.

---

Nitpick comments:
In `@crates/codegen/src/ir.rs`:
- Around line 1164-1210: The three-line fallthrough predicate
(.instructions.last().map(|ins| !ins.instr.is_scope_exit() &&
!ins.instr.is_unconditional_jump()).unwrap_or(true)) is duplicated across
mark_cold, push_cold_blocks_to_end, and label_exception_targets; extract it as a
small free function (e.g., fn last_allows_fallthrough(block: &Block) -> bool or
fn instr_allows_fallthrough(ins: Option<&InstrWrapper>) -> bool) and replace the
inline expression in those functions with a call to the new helper, keeping
semantics identical (true when no last instruction, otherwise !is_scope_exit &&
!is_unconditional_jump).
- Around line 1252-1271: Document that the JumpNoInterrupt insertion relies on
the invariant that cold blocks are moved to the end later so that any cold→warm
JumpNoInterrupt becomes a backward jump during finalize_code; add a brief
comment at the jump insertion site (around jump_block creation /
PseudoInstruction::JumpNoInterrupt) explaining this dependency and referencing
normalize_jumps and finalize_code, and add a debug_assert! in finalize_code near
the conversion to JumpBackwardNoInterrupt that checks the jump target is
backward for JumpNoInterrupt (so the invariant is validated in debug builds).

ℹ️ Review info

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0244657 and 1b9eb37.

⛔ Files ignored due to path filters (6)
  • Lib/test/test_peepholer.py is excluded by !Lib/**
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_bool_op.snap is excluded by !**/*.snap
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap is excluded by !**/*.snap
📒 Files selected for processing (5)
  • crates/codegen/src/compile.rs
  • crates/codegen/src/ir.rs
  • crates/compiler-core/src/bytecode/instruction.rs
  • crates/jit/src/instructions.rs
  • crates/jit/tests/common.rs

Add mark_cold, push_cold_blocks_to_end, and normalize_jumps
passes to the codegen CFG pipeline. Use JumpNoInterrupt for
exception handler exit paths in try-except-finally compilation.
@youknowone youknowone merged commit dc7cd26 into RustPython:main Feb 23, 2026
12 of 14 checks passed
@youknowone youknowone deleted the cold-and-jump branch February 23, 2026 23:53
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.

1 participant