-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Split stack_effect into pushed & popped #6893
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughRefactors stack-effect handling by introducing a new Changes
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()
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this 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 negativeopargvalues.CPython's
dis.stack_effecttreatsopargas an unsigned bytecode argument and raisesValueError("invalid opcode or oparg")ifopargis negative. Your change fromu32toi32allows 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.
| /// 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; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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:)
There was a problem hiding this comment.
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)
There was a problem hiding this 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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Auto generated both
num_poppedandnum_pushed, and then compared the result with the currentstack_effectimpl. 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
✏️ Tip: You can customize this high-level summary in your review settings.