Skip to content

Bytecode parity#7541

Merged
youknowone merged 1 commit intoRustPython:mainfrom
youknowone:bytecode-parity
Mar 31, 2026
Merged

Bytecode parity#7541
youknowone merged 1 commit intoRustPython:mainfrom
youknowone:bytecode-parity

Conversation

@youknowone
Copy link
Copy Markdown
Member

@youknowone youknowone commented Mar 30, 2026

Summary by CodeRabbit

  • New Features

    • Optimized handling for built‑ins called with a single generator (tuple/list/set/all/any) with safe fallback.
  • Performance

    • Smarter constant folding: tuples and f‑strings treated as constant only when all parts are constant; returns may fold to constants and skip work; improved integer and sequence folding; better iterable/list folding.
    • Faster loads for list/set common constants.
  • Bug Fixes

    • Corrected name resolution for nested scopes declaring globals.
    • Improved try/except (no finally) and comparison jump emission.
  • Tests

    • Added coverage for generator-call optimizations, folding, f‑strings and global-store behavior.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Compiler adds builtin-generator-call optimizations for tuple/list/set/all/any with a single generator, tightens constant-folding (tuples, ints, f-strings), changes module-level resolution for nested global, refactors try/except-no-finally and compare-jump emission; bytecode/VM updated to support new builtin list/set common-constants and tests added.

Changes

Cohort / File(s) Summary
Bytecode constants & VM support
crates/compiler-core/src/bytecode/oparg.rs, crates/vm/src/frame.rs
Added BuiltinList/BuiltinSet to CommonConstant (display mappings) and extended VM LoadCommonConstant to push list_type/set_type.
Compiler call-site & control-flow logic
crates/codegen/src/compile.rs
Added builtin-generator-call matcher/optimizer for tuple/list/set/all/any (single positional generator, no keywords) with specialized emission + fallback; adjusted module-scope name resolution for nested global; changed return <expr> to try constant-fold first; refactored try/except-no-finally and compare-conditional jump emission.
IR constant folding & integer guards
crates/codegen/src/ir.rs
Introduced MAX_INT_SIZE_BITS and MIN_CONST_SEQUENCE_SIZE; tightened multiply/power/lshift integer-folding guards and adjusted sequence folding thresholds and BUILD_LIST→LOAD_CONST/BUILD_TUPLE rewrite logic.
F-strings & constant detection
crates/codegen/src/compile.rs (f-string parts)
Require all f-string parts to be constant to treat f-string as constant; added emit_pending_fstring_literal() to elide empty literal fragments while preserving fully-empty f-strings.
Fold/sequence optimization
crates/codegen/src/ir.rs (fold_const_iterable_for_iter)
Narrowed recognized GET_ITER patterns, made operand lookups bounds-safe, and added optimization to replace BUILD_LIST+GET_ITER with a single LOAD_CONST(Tuple) when producers are compile-time constants.
Tests & helpers
crates/codegen/...tests, crates/codegen/src/...helpers
Added tests for builtin-generator-call optimizations and fallback/shadowing, module global resolution, tuple/return constant-folds, f-string literal/empty handling, and other compilation invariants.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(200,230,201,0.5)
    Compiler->>Compiler: detect builtin-generator-call pattern or constant-foldable expr
    Compiler->>Bytecode: emit specialized bytecode (aggregate loops / short-circuit) or fallback CALL sequence
    end
    rect rgba(187,222,251,0.5)
    Bytecode->>VM: execute LOAD_COMMON_CONSTANT(BuiltinList/BuiltinSet/...), CALL/ITER ops
    VM->>Stack: push types/tuple/constants and iterate/accumulate results
    end
    rect rgba(255,224,178,0.5)
    VM->>Runtime: perform call/iteration/return control flow as emitted
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

Possibly related PRs

Suggested reviewers

  • ShaharNaveh

Poem

🐰 I hopped through constants, tucked tuples neat,
Turned generators into faster feet,
Lists and sets now wear bright bytecode bows,
Jumps and tries aligned in tidy rows,
A carrot-cheer for compiler shows! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Bytecode parity' is overly vague and generic. While it relates to bytecode changes, it does not clearly convey what specific improvements or fixes are being implemented. Consider a more descriptive title that captures the primary objective, such as 'Optimize builtin generator calls and improve constant folding' or 'Add generator call optimizations and refactor constant expression handling'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@youknowone youknowone marked this pull request as ready for review March 30, 2026 13:59
Copy link
Copy Markdown
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 (3)
crates/codegen/src/compile.rs (3)

2014-2022: Consider caching the nested-global lookup.

compile_name() will re-walk the nested symbol-table tree for every module-scope name op here. On large modules with lots of nested scopes, that turns this parity fix into repeated O(subtables) work. Precomputing the affected names once on SymbolTable would keep this path cheap.

Also applies to: 2110-2114

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

In `@crates/codegen/src/compile.rs` around lines 2014 - 2022, The nested-global
lookup in module_name_declared_global_in_nested_scope is expensive because
compile_name re-walks sub_tables for every module-scope name; add a cached
collection on SymbolTable (e.g., nested_globals: HashSet<String> or a
lazily-computed cached_nested_globals) that is populated once by walking
sub_tables and noting names with SymbolScope::GlobalExplicit, then change
module_name_declared_global_in_nested_scope (or its callers such as
compile_name) to consult that cache instead of recursively iterating sub_tables;
ensure the cache is updated/filled when the SymbolTable is built or mutated so
subsequent calls are O(1).

3445-3599: Factor the shared handler emitter back out of the no-finally fast path.

compile_try_except_no_finally() now carries a second copy of the exception-match / alias-cleanup flow that already exists in compile_try_statement(). That makes future parity or exception-table fixes easy to miss in one branch. Please consider extracting the common handler emission and only specializing the exit targets.

As per coding guidelines, "When branches differ only in a value but share common logic, extract the differing value first, then call the common logic once to avoid duplicate code."

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

In `@crates/codegen/src/compile.rs` around lines 3445 - 3599, The duplicate
exception-match / alias-cleanup logic in compile_try_except_no_finally should be
factored into a shared helper used by both compile_try_except_no_finally and
compile_try_statement: extract the per-handler emission (the code that handles
CheckExcMatch, PopTop/PopExcept, alias store/cleanup, JumpNoInterrupt to
end_block and the cleanup-end path that Reraise) into a new method (e.g.,
emit_exception_handler or emit_handler_body) that accepts parameters for the
handler AST node, the next_handler block, the end_block/cleanup_end targets, and
any FBlockDatum needed; replace the duplicated blocks in
compile_try_except_no_finally with calls to that helper and update
compile_try_statement to call it too so only the differing targets are passed in
and the shared logic lives in one place.

2574-2584: Use the fold result as the branch condition.

This path classifies constants twice (is_const_expression() and try_fold_constant_expr()), and the expect() turns any future drift between them into a compiler panic. Branching on try_fold_constant_expr() directly keeps the optimization and removes the invariant.

Refactor sketch
-                        let preserve_tos = !Self::is_const_expression(v);
-                        if preserve_tos {
-                            self.compile_expression(v)?;
-                        }
-                        self.unwind_fblock_stack(preserve_tos, false)?;
-                        if !preserve_tos {
-                            let constant = self
-                                .try_fold_constant_expr(v)?
-                                .expect("literal return should fold to a constant");
-                            self.emit_load_const(constant);
-                        }
+                        if let Some(constant) = self.try_fold_constant_expr(v)? {
+                            self.unwind_fblock_stack(false, false)?;
+                            self.emit_load_const(constant);
+                        } else {
+                            self.compile_expression(v)?;
+                            self.unwind_fblock_stack(true, false)?;
+                        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/codegen/src/compile.rs` around lines 2574 - 2584, Call
try_fold_constant_expr(v) once and branch based on its Option result instead of
using is_const_expression() and expect(): let folded =
self.try_fold_constant_expr(v)?; set preserve_tos = folded.is_none(); if
preserve_tos then call self.compile_expression(v)?; then call
self.unwind_fblock_stack(preserve_tos, false)?; finally, if let Some(constant) =
folded { self.emit_load_const(constant); } This removes the double
classification (is_const_expression vs try_fold_constant_expr), avoids the
expect() panic, and preserves the original behavior of compile_expression,
unwind_fblock_stack, and emit_load_const.
🤖 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/compiler-core/src/bytecode/oparg.rs`:
- Around line 648-651: The bytecode format changed by adding new CommonConstant
discriminants (BuiltinList and BuiltinSet), so update the PYC_MAGIC_NUMBER
constant to the next value (increment from 2997 to 2998) wherever
PYC_MAGIC_NUMBER is defined/used so .pyc headers reflect the new format; ensure
the change references the CommonConstant enum (BuiltinList = 5, BuiltinSet = 6)
so any tooling that reads/writes .pyc files uses the new magic number.

---

Nitpick comments:
In `@crates/codegen/src/compile.rs`:
- Around line 2014-2022: The nested-global lookup in
module_name_declared_global_in_nested_scope is expensive because compile_name
re-walks sub_tables for every module-scope name; add a cached collection on
SymbolTable (e.g., nested_globals: HashSet<String> or a lazily-computed
cached_nested_globals) that is populated once by walking sub_tables and noting
names with SymbolScope::GlobalExplicit, then change
module_name_declared_global_in_nested_scope (or its callers such as
compile_name) to consult that cache instead of recursively iterating sub_tables;
ensure the cache is updated/filled when the SymbolTable is built or mutated so
subsequent calls are O(1).
- Around line 3445-3599: The duplicate exception-match / alias-cleanup logic in
compile_try_except_no_finally should be factored into a shared helper used by
both compile_try_except_no_finally and compile_try_statement: extract the
per-handler emission (the code that handles CheckExcMatch, PopTop/PopExcept,
alias store/cleanup, JumpNoInterrupt to end_block and the cleanup-end path that
Reraise) into a new method (e.g., emit_exception_handler or emit_handler_body)
that accepts parameters for the handler AST node, the next_handler block, the
end_block/cleanup_end targets, and any FBlockDatum needed; replace the
duplicated blocks in compile_try_except_no_finally with calls to that helper and
update compile_try_statement to call it too so only the differing targets are
passed in and the shared logic lives in one place.
- Around line 2574-2584: Call try_fold_constant_expr(v) once and branch based on
its Option result instead of using is_const_expression() and expect(): let
folded = self.try_fold_constant_expr(v)?; set preserve_tos = folded.is_none();
if preserve_tos then call self.compile_expression(v)?; then call
self.unwind_fblock_stack(preserve_tos, false)?; finally, if let Some(constant) =
folded { self.emit_load_const(constant); } This removes the double
classification (is_const_expression vs try_fold_constant_expr), avoids the
expect() panic, and preserves the original behavior of compile_expression,
unwind_fblock_stack, and emit_load_const.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 530d1c2c-b85c-4605-bce9-ac3e930ac148

📥 Commits

Reviewing files that changed from the base of the PR and between 9900c76 and 3f69ae4.

⛔ Files ignored due to path filters (1)
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap is excluded by !**/*.snap
📒 Files selected for processing (3)
  • crates/codegen/src/compile.rs
  • crates/compiler-core/src/bytecode/oparg.rs
  • crates/vm/src/frame.rs

Comment on lines +648 to +651
/// Built-in `list` type
BuiltinList = 5,
/// Built-in `set` type
BuiltinSet = 6,
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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if PYC_MAGIC_NUMBER was modified in this PR
echo "=== Current PYC_MAGIC_NUMBER value ==="
rg -n "PYC_MAGIC_NUMBER:\s*u16\s*=" crates/vm/src/version.rs

echo ""
echo "=== Check git diff for version.rs changes ==="
git diff origin/main -- crates/vm/src/version.rs 2>/dev/null || echo "Unable to diff against main"

echo ""
echo "=== Full context around magic number ==="
rg -B2 -A5 "pub const PYC_MAGIC_NUMBER" crates/vm/src/version.rs

Repository: RustPython/RustPython

Length of output: 780


🏁 Script executed:

#!/bin/bash
# Search for CommonConstant serialization/deserialization
echo "=== CommonConstant TryFrom/From implementations ==="
rg -B2 -A10 "impl.*TryFrom.*CommonConstant|impl.*From.*CommonConstant" crates/

echo ""
echo "=== Check if CommonConstant is stored in bytecode ==="
rg -B3 -A3 "CommonConstant" crates/compiler-core/src/bytecode/ | head -100

echo ""
echo "=== Check version.rs for any notes on format versioning ==="
cat crates/vm/src/version.rs | head -50

Repository: RustPython/RustPython

Length of output: 5546


🏁 Script executed:

#!/bin/bash
# Check if BuiltinList and BuiltinSet are actually used in the compiler
echo "=== Usage of BuiltinList and BuiltinSet in compiler ==="
rg "BuiltinList|BuiltinSet" crates/compiler-core/src/ --with-filename

echo ""
echo "=== Check if CommonConstant is used in code generation ==="
rg "CommonConstant::" crates/compiler-core/src/ -B2 -A2 | head -80

echo ""
echo "=== Check the complete CommonConstant enum definition ==="
rg -B5 "pub enum CommonConstant" crates/compiler-core/src/bytecode/oparg.rs -A30

Repository: RustPython/RustPython

Length of output: 1688


🏁 Script executed:

#!/bin/bash
# Check if there are any compiler passes or functions that generate LoadCommonConstant instructions
echo "=== Search for LoadCommonConstant instruction generation ==="
rg "LoadCommonConstant" crates/compiler-core/src/ -B3 -A3

echo ""
echo "=== Check compilation context for common constant usage ==="
rg "CommonConstant" crates/compiler-core/src/codegen/ -B2 -A2

echo ""
echo "=== Verify if these enum variants are actually referenced anywhere ==="
rg "BuiltinList|BuiltinSet" crates/ --type rust | grep -v "oparg.rs" | grep -v "\.pyc"

Repository: RustPython/RustPython

Length of output: 2905


🏁 Script executed:

#!/bin/bash
# Check how LoadCommonConstant is encoded/decoded in bytecode
echo "=== Check bytecode marshaling/unmarshaling of CommonConstant ==="
rg "CommonConstant" crates/vm/src/ -B3 -A3

echo ""
echo "=== Check if there's a TryFrom u8 for CommonConstant ==="
rg -A20 "impl.*TryFrom.*u8.*CommonConstant|impl.*From.*u8.*CommonConstant" crates/

echo ""
echo "=== Check recent git history for magic number bumps ==="
git log --oneline crates/vm/src/version.rs | head -10

echo ""
echo "=== Look for any serialization logic ==="
rg "serialize|deserialize" crates/compiler-core/src/bytecode/oparg.rs | head -20

Repository: RustPython/RustPython

Length of output: 1892


🏁 Script executed:

#!/bin/bash
# Check the current git status to understand what's being changed
echo "=== Check git status and recent changes ==="
git status

echo ""
echo "=== Check if BuiltinList/BuiltinSet references exist in current HEAD ==="
git show HEAD:crates/compiler-core/src/bytecode/oparg.rs | grep -c "BuiltinList\|BuiltinSet" || echo "Not found in HEAD"

echo ""
echo "=== Full diff for oparg.rs to see what was changed ==="
git diff HEAD -- crates/compiler-core/src/bytecode/oparg.rs | head -100

echo ""
echo "=== Check if frame.rs already had these references before ==="
git show HEAD:crates/vm/src/frame.rs | grep -A2 "BuiltinList\|BuiltinSet" | head -20

Repository: RustPython/RustPython

Length of output: 612


🏁 Script executed:

#!/bin/bash
# Verify these variants are actively being generated in bytecode
echo "=== Check when BuiltinList/BuiltinSet were added to the enum ==="
git log -p --all -S "BuiltinList" -- crates/compiler-core/src/bytecode/oparg.rs | head -80

echo ""
echo "=== Check git log for PYC_MAGIC_NUMBER changes ==="
git log --all --oneline -- crates/vm/src/version.rs | head -20

echo ""
echo "=== Check if there are comments or TODOs about versioning ==="
rg "TODO|FIXME|bump.*magic|version.*bytecode" crates/vm/src/version.rs crates/compiler-core/src/bytecode/oparg.rs

Repository: RustPython/RustPython

Length of output: 652


🏁 Script executed:

#!/bin/bash
# Check the complete diff context for the PR
echo "=== Check all diffs for oparg.rs ==="
git diff HEAD -- crates/compiler-core/src/bytecode/oparg.rs

echo ""
echo "=== Verify current state: do BuiltinList and BuiltinSet exist in oparg.rs? ==="
rg -n "BuiltinList|BuiltinSet" crates/compiler-core/src/bytecode/oparg.rs

echo ""
echo "=== Check compile.rs for these references ==="
git show HEAD:crates/codegen/src/compile.rs | grep -B3 -A3 "BuiltinList\|BuiltinSet" | head -30

Repository: RustPython/RustPython

Length of output: 1345


🏁 Script executed:

#!/bin/bash
# Check if these variants are actually new or already existed
echo "=== Check commit history for when these were added ==="
git log --all --oneline -- crates/compiler-core/src/bytecode/oparg.rs | head -5

echo ""
echo "=== Check the specific commit that added BuiltinList/Set ==="
git log --all -p -- crates/compiler-core/src/bytecode/oparg.rs | grep -B20 "BuiltinList" | head -50

echo ""
echo "=== Determine current PR: check if these were just added or already existed ==="
# Try to find the most recent change to these specific lines
git blame crates/compiler-core/src/bytecode/oparg.rs | grep -A1 -B1 "BuiltinList\|BuiltinSet"

Repository: RustPython/RustPython

Length of output: 2316


🏁 Script executed:

#!/bin/bash
# Check what commit 3f69ae47 actually changed
echo "=== Examine commit 3f69ae47 ==="
git show 3f69ae47 --stat | head -30

echo ""
echo "=== Check if magic number was changed in that commit ==="
git show 3f69ae47 -- crates/vm/src/version.rs | head -50

echo ""
echo "=== Full diff of version.rs in that commit ==="
git show 3f69ae47:crates/vm/src/version.rs | grep -B2 -A2 "PYC_MAGIC_NUMBER"

Repository: RustPython/RustPython

Length of output: 3910


🏁 Script executed:

#!/bin/bash
# Check if BuiltinList/BuiltinSet existed before commit 3f69ae47
echo "=== Check parent commit of 3f69ae47 ==="
git show 3f69ae47^:crates/compiler-core/src/bytecode/oparg.rs 2>/dev/null | grep -A20 "pub enum CommonConstant" || echo "File did not exist in parent"

echo ""
echo "=== Verify: were these variants newly added or already present? ==="
git show 959b088d:crates/compiler-core/src/bytecode/oparg.rs 2>/dev/null | grep "BuiltinList\|BuiltinSet" || echo "Not found in 959b088d"

echo ""
echo "=== Check if CommonConstant enum structure changed ==="
git diff 959b088d 3f69ae47 -- crates/compiler-core/src/bytecode/oparg.rs | grep -B5 -A5 "BuiltinList\|BuiltinSet" || echo "No direct diff found"

echo ""
echo "=== Summary: does HEAD have BuiltinList/BuiltinSet implementation in frame.rs? ==="
git show HEAD:crates/vm/src/frame.rs | grep -B2 -A2 "BuiltinList\|BuiltinSet" | wc -l

Repository: RustPython/RustPython

Length of output: 968


Bump PYC_MAGIC_NUMBER for bytecode format change.

Adding new CommonConstant discriminants (BuiltinList = 5, BuiltinSet = 6) extends the bytecode format. Although the compiler and VM implementations are coordinated in this commit, the PYC_MAGIC_NUMBER should be incremented from 2997 to prevent bytecode format confusion when .pyc files are distributed or cached. Old .pyc files with magic 2997 won't contain these new constant values, but new files will, creating a compatibility risk.

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

In `@crates/compiler-core/src/bytecode/oparg.rs` around lines 648 - 651, The
bytecode format changed by adding new CommonConstant discriminants (BuiltinList
and BuiltinSet), so update the PYC_MAGIC_NUMBER constant to the next value
(increment from 2997 to 2998) wherever PYC_MAGIC_NUMBER is defined/used so .pyc
headers reflect the new format; ensure the change references the CommonConstant
enum (BuiltinList = 5, BuiltinSet = 6) so any tooling that reads/writes .pyc
files uses the new magic number.

Copy link
Copy Markdown
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: 2

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

1178-1186: Minor: Redundant bounds check.

The condition i + 1 < block.instructions.len() on line 1178 is redundant since the outer while loop (line 1137) already guarantees this invariant. The code is correct, just slightly verbose.

♻️ Suggested simplification
-                } else if i + 1 < block.instructions.len()
-                    && matches!(
+                } else if matches!(
                         block.instructions[i].instr.real(),
                         Some(Instruction::BuildList { .. } | Instruction::BuildSet { .. })
                     )
🤖 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 1178 - 1186, Remove the redundant
bounds check `i + 1 < block.instructions.len()` from the compound `if` condition
since the surrounding `while` loop already ensures `i + 1` is in range; keep the
remaining `matches!` checks against `block.instructions[i].instr.real()` and
`block.instructions[i + 1].instr.real()` (the `Instruction::BuildList |
BuildSet` and `Instruction::GetIter` checks) unchanged so the logic is
preserved.
🤖 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/compile.rs`:
- Around line 2574-2584: Compute the constant fold result first (call
try_fold_constant_expr(v) and store the Option) and use its presence to decide
the fast-path instead of relying on is_const_expression(); set preserve_tos =
folded.is_none(), call compile_expression(v) only when preserve_tos is true,
call unwind_fblock_stack(preserve_tos, false) as before, and when folded is
Some(constant) call emit_load_const(constant) (avoiding the expect on
try_fold_constant_expr and using the fold result directly). Use the symbols
try_fold_constant_expr, is_const_expression, compile_expression,
unwind_fblock_stack, and emit_load_const to locate and update the logic.
- Around line 3525-3554: The current normal-exit alias cleanup keeps the outer
FBlockType::ExceptionHandler active across emit!(Instruction::PopExcept) and the
subsequent store_name/compile_name calls, which can cause PopExcept/Copy to run
against a stack missing prev_exc; to fix, pop the outer ExceptionHandler (call
self.pop_fblock(FBlockType::ExceptionHandler)) before emitting PopExcept and
performing the alias cleanup (the sequence around emit!(Instruction::PopExcept),
self.emit_load_const(ConstantData::None), self.store_name(alias.as_str()), and
self.compile_name(alias.as_str(), NameUsage::Delete)), and mirror the
non-finalbody teardown ordering where cleanup_end is switched-to/compiled first
and the handler is popped/re-pushed around normal-exit cleanup; adjust usage of
switch_to_block(cleanup_end), emit!(PseudoInstruction::JumpNoInterrupt { delta:
end_block }), and emit!(Instruction::Reraise { depth: 1 }) accordingly so the
outer handler is removed prior to name cleanup.

---

Nitpick comments:
In `@crates/codegen/src/ir.rs`:
- Around line 1178-1186: Remove the redundant bounds check `i + 1 <
block.instructions.len()` from the compound `if` condition since the surrounding
`while` loop already ensures `i + 1` is in range; keep the remaining `matches!`
checks against `block.instructions[i].instr.real()` and `block.instructions[i +
1].instr.real()` (the `Instruction::BuildList | BuildSet` and
`Instruction::GetIter` checks) unchanged so the logic is preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: dbd017b1-5bf6-4100-a5f7-e635015ed7a9

📥 Commits

Reviewing files that changed from the base of the PR and between 3f69ae4 and bdc7365.

⛔ Files ignored due to path filters (1)
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap is excluded by !**/*.snap
📒 Files selected for processing (4)
  • crates/codegen/src/compile.rs
  • crates/codegen/src/ir.rs
  • crates/compiler-core/src/bytecode/oparg.rs
  • crates/vm/src/frame.rs
✅ Files skipped from review due to trivial changes (1)
  • crates/compiler-core/src/bytecode/oparg.rs

Copy link
Copy Markdown
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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/codegen/src/ir.rs (1)

1170-1177: ⚠️ Potential issue | 🟠 Major

The frozenset fast path still misses the common 3+ constant-set case.

Because fold_set_constants() still triggers for set_size >= 3 at Line 1260 and inserts a tuple constant at Lines 1302-1304, the new ConstantData::Frozenset branch here is only reachable for 1-2 element sets. Lines 1170-1177 then strip BUILD_SET/SET_UPDATE from the 3+ element path, leaving LOAD_CONST (tuple) + GET_ITER. That makes the iterator source depend on literal size and misses the parity goal; the earlier set fold needs to emit Frozenset too before this fast path can elide the build.

Also applies to: 1210-1216

🤖 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 1170 - 1177, The fast-path that
replaces BUILD_SET/SET_UPDATE with NOPs only handles ConstantData::Frozenset for
1–2 element cases but fold_set_constants still emits a tuple constant for sets
with size >= 3, so update the code path so the parity invariant holds: modify
fold_set_constants to emit ConstantData::Frozenset for all folded literal sets
(including size >= 3) instead of a tuple, or alternatively extend the
BUILD_SET/SET_UPDATE fast-path in the block handling (the code that touches
block.instructions[i], block.instructions[i+2], and sets Instruction::Nop) to
also detect the LOAD_CONST(tuple) pattern and treat it equivalently to
ConstantData::Frozenset; apply the same change to the sibling fast-path handling
(the other block analogous to the one around i+40) so both locations handle the
3+ element case.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@crates/codegen/src/ir.rs`:
- Around line 1170-1177: The fast-path that replaces BUILD_SET/SET_UPDATE with
NOPs only handles ConstantData::Frozenset for 1–2 element cases but
fold_set_constants still emits a tuple constant for sets with size >= 3, so
update the code path so the parity invariant holds: modify fold_set_constants to
emit ConstantData::Frozenset for all folded literal sets (including size >= 3)
instead of a tuple, or alternatively extend the BUILD_SET/SET_UPDATE fast-path
in the block handling (the code that touches block.instructions[i],
block.instructions[i+2], and sets Instruction::Nop) to also detect the
LOAD_CONST(tuple) pattern and treat it equivalently to ConstantData::Frozenset;
apply the same change to the sibling fast-path handling (the other block
analogous to the one around i+40) so both locations handle the 3+ element case.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 2f246cfa-9428-4106-9a96-a28ca59eea46

📥 Commits

Reviewing files that changed from the base of the PR and between bdc7365 and 9334687.

⛔ Files ignored due to path filters (1)
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap is excluded by !**/*.snap
📒 Files selected for processing (4)
  • crates/codegen/src/compile.rs
  • crates/codegen/src/ir.rs
  • crates/compiler-core/src/bytecode/oparg.rs
  • crates/vm/src/frame.rs
✅ Files skipped from review due to trivial changes (2)
  • crates/compiler-core/src/bytecode/oparg.rs
  • crates/vm/src/frame.rs
👮 Files not reviewed due to content moderation or server errors (1)
  • crates/codegen/src/compile.rs

Copy link
Copy Markdown
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 (1)
crates/codegen/src/ir.rs (1)

28-29: Use the new sequence threshold in the set-folding path too.

MIN_CONST_SEQUENCE_SIZE now drives list folding, but fold_set_constants still hardcodes 3. The thresholds will drift the next time this gets tuned.

♻️ Suggested cleanup
-                if set_size < 3 || i < set_size {
+                if set_size < MIN_CONST_SEQUENCE_SIZE || i < set_size {

As per coding guidelines, "When branches differ only in a value but share common logic, extract the differing value first, then call the common logic once to avoid duplicate code".

Also applies to: 1083-1085

🤖 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 28 - 29, Replace the hardcoded "3"
used in the set-folding path with the MIN_CONST_SEQUENCE_SIZE constant and
refactor the branching so the threshold is computed once and shared by both list
and set folding logic; specifically update the fold_set_constants logic to
reference MIN_CONST_SEQUENCE_SIZE (and/or assign it to a local variable like
threshold) instead of literal 3 and call the common folding code/path so both
branches use the same configurable threshold (also apply the same change to the
nearby branches around fold_set_constants that currently duplicate the value).
🤖 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 832-836: The code casts the exponent with "as usize" before
calling num_traits::pow::pow which truncates on 32-bit targets; replace the
unchecked cast with a checked conversion (e.g. use exp.try_into() or
usize::try_from(exp)) and return None if the exponent does not fit in usize so
we don't call pow with a truncated exponent; update the block around the
variables r/exp and the call to num_traits::pow::pow and keep the existing
overflow/size check against MAX_INT_SIZE_BITS.

---

Nitpick comments:
In `@crates/codegen/src/ir.rs`:
- Around line 28-29: Replace the hardcoded "3" used in the set-folding path with
the MIN_CONST_SEQUENCE_SIZE constant and refactor the branching so the threshold
is computed once and shared by both list and set folding logic; specifically
update the fold_set_constants logic to reference MIN_CONST_SEQUENCE_SIZE (and/or
assign it to a local variable like threshold) instead of literal 3 and call the
common folding code/path so both branches use the same configurable threshold
(also apply the same change to the nearby branches around fold_set_constants
that currently duplicate the value).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: fdbc29b4-ca92-4e33-a4ff-3a1bee3f01c1

📥 Commits

Reviewing files that changed from the base of the PR and between 9334687 and 7f7b29e.

⛔ Files ignored due to path filters (1)
  • crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap is excluded by !**/*.snap
📒 Files selected for processing (4)
  • crates/codegen/src/compile.rs
  • crates/codegen/src/ir.rs
  • crates/compiler-core/src/bytecode/oparg.rs
  • crates/vm/src/frame.rs
✅ Files skipped from review due to trivial changes (1)
  • crates/vm/src/frame.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/compiler-core/src/bytecode/oparg.rs
👮 Files not reviewed due to content moderation or server errors (1)
  • crates/codegen/src/compile.rs

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 31, 2026

📦 Library Dependencies

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

(module 'monitoring test_scope' not found)

Legend:

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

@youknowone youknowone force-pushed the bytecode-parity branch 2 times, most recently from e150d64 to 8651fbb Compare March 31, 2026 05:11
- Add CFG block splitting, jump threading, backward jump normalization
- Add genexpr StopIteration wrapper
- Add ConstantData::Slice and constant slice folding
- Add duplicate_exits_without_lineno and Block: Clone
- Add builtin(genexpr) optimization for tuple/list/set/all/any
- Add compile_try_except_no_finally for try-except without finally
- Add module_name_declared_global_in_nested_scope
- Add constant tuple folding in try_fold_constant_expr
- Add fstring literal-only optimization and empty literal elision
- Fix duplicate_exits_without_lineno: splice new blocks into linked list
@youknowone youknowone merged commit 403c2be into RustPython:main Mar 31, 2026
21 checks passed
@youknowone youknowone deleted the bytecode-parity branch March 31, 2026 06:45
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