Skip to content

Conversation

@ShaharNaveh
Copy link
Collaborator

@ShaharNaveh ShaharNaveh commented Dec 25, 2025

Implements python/cpython#102870

Summary by CodeRabbit

  • Refactor

    • Optimized internal compilation logic for comparison operations and boolean expressions, improving code maintainability.
  • Documentation

    • Enhanced compiler documentation with reference materials and detailed explanatory comments.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 25, 2025

📝 Walkthrough

Walkthrough

This PR refactors comparison operation compilation in the bytecode generator, introducing a new compile_addcompare method to handle chained comparisons and replacing JumpIfFalseOrPop patterns with explicit Jump/PopJump sequences paired with precise stack manipulations.

Changes

Cohort / File(s) Summary
Comparison and Boolean Expression Compilation
crates/codegen/src/compile.rs
Introduced compile_addcompare method to map CmpOp variants to bytecode operations; refactored compile_compare to process chained comparisons via a new comparators structure with conditional jumps and cleanup blocks; updated boolean expression control flow from JumpIfFalseOrPop/JumpIfTrueOrPop to explicit Jump/PopJump patterns with adjusted CopyItem/PopTop indices; added CPython-inspired documentation and semantics references; modified multiple call sites to align stack operations with the new comparison workflow.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • youknowone

Poem

🐰 A rabbit hops through bytecode so clear,
Where comparisons chain without fear,
Stack operations aligned just right,
Jump and PopJump in boolean's light,
CPython's wisdom now channels through here!

Pre-merge checks and finishing touches

✅ 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 reflects the main changes: reducing usage of JumpIfTrueOrPop and JumpIfFalseOrPop opcodes and refactoring the compile_compare method, which are the primary objectives described in the PR summary.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 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.

@ShaharNaveh ShaharNaveh changed the title Remove JumpIfTrueOrPop & JumpIfFalseOrPop opcdes Reduce usage JumpIfTrueOrPop & JumpIfFalseOrPop opcdes. Refactor compile_compare Dec 26, 2025
@ShaharNaveh ShaharNaveh marked this pull request as ready for review December 26, 2025 12:29
@ShaharNaveh
Copy link
Collaborator Author

This started as an attempt to remove JumpIfTrueOrPop & JumpIfFalseOrPop opcodes.
While it did work, some tests where failing mainly because of dis implementation difference, so I decided to pivot this PR to something else

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: 0

🧹 Nitpick comments (1)
crates/codegen/src/compile.rs (1)

4118-4126: Commented-out code and inconsistent refactoring with PR goal.

The PR objective is to reduce usage of JumpIfFalseOrPop opcode, but:

  1. Lines 4118-4123 contain commented-out code showing the intended replacement pattern
  2. Line 4125 still uses JumpIfFalseOrPop

This appears to be an intentional reversion based on commit history ("Reverted PopJumpIfFalse changes for comparisons"), but the commented code creates noise and the inconsistency should be documented. Consider either:

  • Removing the commented code if the reversion is permanent
  • Adding a TODO/FIXME comment explaining why JumpIfFalseOrPop is still used here
🔎 Clean up commented code
             self.compile_addcompare(op);

             // if comparison result is false, we break with this value; if true, try the next one.
-            /*
-            emit!(self, Instruction::CopyItem { index: 1 });
-            // emit!(self, Instruction::ToBool); // TODO: Uncomment this
-            emit!(self, Instruction::PopJumpIfFalse { target: cleanup });
-            emit!(self, Instruction::PopTop);
-            */
-
+            // NOTE: Using JumpIfFalseOrPop here instead of CopyItem+PopJumpIfFalse+PopTop
+            // because chained comparisons require the comparison result to remain on stack
+            // for cleanup when short-circuiting.
             emit!(self, Instruction::JumpIfFalseOrPop { target: cleanup });
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c9bf8df and 582d1e0.

📒 Files selected for processing (1)
  • crates/codegen/src/compile.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rs: Follow the default rustfmt code style by running cargo fmt to format Rust code
Always run clippy to lint Rust code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
Follow Rust best practices for error handling and memory management
Use the macro system (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

Files:

  • crates/codegen/src/compile.rs
🔇 Additional comments (4)
crates/codegen/src/compile.rs (4)

4049-4066: Clean helper extraction for comparison operations.

The compile_addcompare method is a well-structured helper that maps CmpOp variants to their corresponding bytecode instructions. This follows the CPython reference and improves code organization.


4467-4491: Correct implementation of the opcode replacement pattern.

The refactoring correctly replaces JumpIfFalseOrPop/JumpIfTrueOrPop with the explicit sequence:

  1. CopyItem { index: 1 } - duplicates TOS
  2. PopJumpIfFalse/PopJumpIfTrue - conditional jump, pops TOS
  3. PopTop - cleans up if no jump occurred

This properly implements short-circuit evaluation:

  • For and: if any value is false, jump with that value on stack
  • For or: if any value is true, jump with that value on stack

The semantics are preserved while using more explicit opcodes.


4086-4141: The refactored compile_compare correctly aligns with CPython's chained comparison behavior.

The stack manipulation pattern with Swap { index: 2 } and CopyItem { index: 2 } achieves the same effect as CPython's DUP_TOP and ROT_THREE instructions. Both approaches preserve the middle operand for the next comparison in the chain. The cleanup block correctly handles early exit by restoring stack invariants when a comparison result is false. The implementation using JumpIfFalseOrPop matches CPython's semantics for conditional chaining.


4033-4035: Verify stack cleanup when guard evaluates to false in default case.

When the default case guard evaluates to false and jumps to end (line 4039), the guard result is left on the stack:

  • After CopyItem: [guard_result, guard_result]
  • After PopJumpIfFalse (false branch): pops the copy, leaving [guard_result]
  • PopTop only executes on true path

The end block performs no stack cleanup, so verify whether the guard result should be popped in the false path or if this is intentional handling.

}
);
}
/*
Copy link
Member

Choose a reason for hiding this comment

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

what was the blocker of this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's the only reason why both opcodes stayed:/

This change caused regression for

def test_no_wraparound_jump(self):
# See https://bugs.python.org/issue46724
def while_not_chained(a, b, c):
while not (a < b < c):
pass
for instr in dis.Bytecode(while_not_chained):
self.assertNotEqual(instr.opname, "EXTENDED_ARG")

Not because it found an EXTENDED_ARG, but because iterating over dis.Bytecode(...) gave an error

Copy link
Member

Choose a reason for hiding this comment

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

if that's because our test_compile is an old one, replacing it will be also good.

}
);
}
/*
Copy link
Member

Choose a reason for hiding this comment

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

if that's because our test_compile is an old one, replacing it will be also good.

@youknowone youknowone merged commit b704f42 into RustPython:main Dec 26, 2025
13 checks passed
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.

2 participants