Thanks to visit codestin.com
Credit goes to GitHub.com

Skip to content

Conversation

@ShaharNaveh
Copy link
Contributor

@ShaharNaveh ShaharNaveh commented Jan 29, 2026

Auto generated both num_popped and num_pushed, and then compared the result with the current stack_effect impl. This will make it easier to auto-generate this in the future, and gets us 1 step closer to #6746 as imo it's better to construct the instruction once instead of validate that the oparg is valid at every operation. Also, it's easier to see if we differences in our opcode impls from CPython (if the stack_effect does not match)

Summary by CodeRabbit

  • Refactor
    • Consolidated stack-effect handling into a structured push/pop abstraction and exposed it across instruction representations so callers can query pushed/popped counts or a combined effect.
  • Chores
    • Added bidirectional conversion for opcode argument values to simplify argument handling and adjusted internal call sites accordingly.
  • Impact
    • Improves consistency and reliability when analyzing and optimizing bytecode.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 29, 2026

📝 Walkthrough

Walkthrough

Refactors stack-effect handling by introducing a new StackEffect type (pushed/popped + effect), extends InstructionMetadata with stack_effect_info (returning StackEffect) while keeping a default stack_effect delegating to it, adds From<OpArg> for u32, re-exports StackEffect, and updates call sites in codegen and stdlib to use the new API (plus a minor local import removal).

Changes

Cohort / File(s) Summary
Core trait & implementations
crates/compiler-core/src/bytecode/instruction.rs, crates/compiler-core/src/bytecode.rs
Introduce pub struct StackEffect { pushed, popped }; add stack_effect_info(&self, oparg: u32) -> StackEffect to InstructionMetadata with a default stack_effect that returns .effect(); implement for Instruction/PseudoInstruction/AnyInstruction; re-export StackEffect.
OpArg conversions
crates/compiler-core/src/bytecode/oparg.rs
Add impl From<OpArg> for u32 to enable converting OpArg -> u32.
Call-site updates
crates/codegen/src/ir.rs, crates/stdlib/src/_opcode.rs
Update call sites to pass numeric oparg (use .into() or pass u32) and to consume the new stack-effect API; remove a local import of InstructionMetadata in codegen/src/ir.rs; minor change in _opcode.rs to pass oparg directly.

Sequence Diagram(s)

sequenceDiagram
  participant Codegen as Codegen (crates/codegen)
  participant Stdlib as Stdlib (_opcode)
  participant Compiler as Compiler-core (AnyInstruction / Instruction)
  participant OpArg as OpArg (converter)
  Note over Codegen,Stdlib: Obtain numeric oparg and request stack effect
  Codegen->>OpArg: convert/forward oparg -> u32 (use `.into()` where needed)
  Stdlib->>OpArg: use oparg as u32 / forward as-is
  Codegen->>Compiler: call stack_effect_info(u32) or stack_effect(u32)
  Stdlib->>Compiler: call stack_effect(u32)
  Compiler-->>Codegen: return StackEffect (pushed,popped) / .effect()
  Compiler-->>Stdlib: return StackEffect (pushed,popped) / .effect()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • youknowone

Poem

🐇 I hop through bytes and stacky streams,
Counting pops and pushed-up dreams.
From OpArg bits to effect’s song,
I tally tidy, neat, and strong—
A rabbit’s cheer for code that gleams! 🥕

🚥 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 'Split stack_effect into pushed & popped' directly and accurately describes the main architectural change: refactoring stack_effect from a single i32 return into a structured StackEffect type with separate pushed and popped fields.
Docstring Coverage ✅ Passed Docstring coverage is 94.44% 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
🧪 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.

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

Caution

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

⚠️ Outside diff range comments (1)
crates/stdlib/src/_opcode.rs (1)

169-201: Add validation to reject negative oparg values.

CPython's dis.stack_effect treats oparg as an unsigned bytecode argument and raises ValueError("invalid opcode or oparg") if oparg is negative. Your change from u32 to i32 allows negatives to pass through unchecked, creating a compatibility divergence. Add a guard:

if oparg < 0 {
    return Err(vm.new_value_error("oparg must be non-negative"));
}
🤖 Fix all issues with AI agents
In `@crates/codegen/src/ir.rs`:
- Line 734: Replace the unchecked cast u32::from(info.arg) as i32 with a checked
conversion using i32::try_from(u32::from(info.arg)) and handle the Err case
instead of allowing silent wraparound; specifically, call
instr.stack_effect(i32::try_from(u32::from(info.arg)).expect("OpArg out of i32
range")) or propagate an error if appropriate for your context, and make the
same replacement for the other occurrence noted (the second use of
instr.stack_effect with info.arg).

In `@crates/compiler-core/src/bytecode/instruction.rs`:
- Around line 900-906: The match arm for the opcode BinaryOpInplaceAddUnicode
currently returns 0 pushes which contradicts the IR treatment (pop 2 / push 1)
and causes stack_effect = -2; update the implementation that computes pushes
(e.g., num_pushed or the match in instruction.rs where BinaryOpInplaceAddUnicode
is handled) to return 1 like the other binary ops
(BinaryOpAddFloat/Int/Unicode/Extend/Multiply) so the stack depth calculation
matches the IR semantics.

@youknowone youknowone enabled auto-merge (squash) January 29, 2026 13:36
Comment on lines 855 to 859
/// How many items this instruction is popping from the stack.
fn num_popped(&self, oparg: i32) -> i32;

/// How many items this instruction is pushing on the stack.
fn num_pushed(&self, oparg: i32) -> i32;
Copy link
Member

Choose a reason for hiding this comment

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

I like this idea. How do you think about stack_popped_and_pushed(&self, oparg: i32) -> (i32, i32) to avoid a whole instruction match twice

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I can do it as a followup improvement if this is fine by you

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've made StackEffect into a struct, this will be clearer than returning an unnamed tuple

Copy link
Member

Choose a reason for hiding this comment

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

oh, i meant not about changing stack_effect return type but merging num_popped and num_pushed into single function returning a tuple (or StackEffect, that's looking better!) to avoid full instruciton match for each function.
if we are going to use stack_effect as a single number for most of places, keeping current -> i32 will be easier for user side.

}

pub trait InstructionMetadata {
pub trait StackEffect {
Copy link
Member

Choose a reason for hiding this comment

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

What's good if we split this trait? It seems required to implement for the all types implementing InstructionMetadata

Copy link
Contributor Author

@ShaharNaveh ShaharNaveh Jan 29, 2026

Choose a reason for hiding this comment

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

No need really, it made more sense for me to split it. but it can be part of InstructionMetadata. I'm fine with whatever you want:)

Copy link
Member

Choose a reason for hiding this comment

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

Splitting traits as grouping is looking attractive while defining and implementing traits, but it usually leads to import hell for user side. I'd like to keep a single trait if all InstructionMetadata and StackEffect has manual implementation requirements. It can be splitted when one has manual implementation but the other one has blanket implementation (or any kind of different implementation strategy)

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

🤖 Fix all issues with AI agents
In `@crates/compiler-core/src/bytecode/instruction.rs`:
- Around line 1396-1435: The From<StackEffect> for i32 impl uses unsafe
unwrap_unchecked on conversions of StackEffect.pushed() and .popped(), which can
UB if values exceed i32::MAX; replace the unsafe block by performing safe
conversions (use i32::try_from(...) and propagate or panic with expect including
the offending value), e.g. convert pushed and popped to i32 via try_from and
subtract normally, and update StackEffect::effect() to rely on this safe
conversion; also fix the "SAFTEY" typo to "SAFETY" in the comment to avoid
confusion.

fn stack_effect_info(&self, oparg: u32) -> StackEffect;

/// Stack effect of [`Self::stack_effect_info`].
fn stack_effect(&self, oparg: u32) -> i32 {
Copy link
Member

Choose a reason for hiding this comment

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

👍

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