From 39dff7a7fa79ca94b1ee4062a1daded25b0b4f53 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 31 Jan 2026 10:33:36 +0200 Subject: [PATCH 1/2] Use `stack_effect_info` for getting pop&push count --- crates/codegen/src/ir.rs | 60 ++-------------------------------------- 1 file changed, 3 insertions(+), 57 deletions(-) diff --git a/crates/codegen/src/ir.rs b/crates/codegen/src/ir.rs index 28b3468c258..a791bf63f58 100644 --- a/crates/codegen/src/ir.rs +++ b/crates/codegen/src/ir.rs @@ -713,7 +713,7 @@ impl CodeInfo { // (or NOT_LOCAL if not from LOAD_FAST/LOAD_FAST_LOAD_FAST). // // CPython (flowgraph.c optimize_load_fast) pre-fills the stack with - // dummy refs for values inherited from predecessor blocks. We take + // dummy refs for values inherited from predecessor blocks. We take // the simpler approach of aborting the optimisation for the whole // block on stack underflow. let mut stack: Vec = Vec::new(); @@ -724,62 +724,8 @@ impl CodeInfo { continue; }; - // Decompose into (pops, pushes). - // - // stack_effect() returns pushes − pops, which is ambiguous for - // instructions that both pop and push (e.g. BinaryOp: effect=-1 - // is pop 2 push 1, not pop 1 push 0). We list those explicitly; - // the fallback under-pops and under-pushes, which is conservative - // (may miss optimisation opportunities but never miscompiles). - let effect = instr.stack_effect(info.arg.into()); - let (pops, pushes) = match instr { - // --- pop 2, push 1 --- - Instruction::BinaryOp { .. } - | Instruction::BinaryOpInplaceAddUnicode - | Instruction::CompareOp { .. } - | Instruction::ContainsOp(_) - | Instruction::IsOp(_) - | Instruction::ImportName { .. } - | Instruction::FormatWithSpec => (2, 1), - - // --- pop 1, push 1 --- - Instruction::UnaryInvert - | Instruction::UnaryNegative - | Instruction::UnaryNot - | Instruction::ToBool - | Instruction::GetIter - | Instruction::GetAIter - | Instruction::FormatSimple - | Instruction::LoadFromDictOrDeref(_) - | Instruction::LoadFromDictOrGlobals(_) => (1, 1), - - // LoadAttr: pop receiver, push attr. - // method=true: push (method, self_or_null) → (1, 2) - Instruction::LoadAttr { idx } => { - let (_, is_method) = - rustpython_compiler_core::bytecode::decode_load_attr_arg( - idx.get(info.arg), - ); - if is_method { (1, 2) } else { (1, 1) } - } - - // --- pop 3, push 1 --- - Instruction::BinarySlice => (3, 1), - - // --- variable pops, push 1 --- - Instruction::Call { nargs } => (nargs.get(info.arg) as usize + 2, 1), - Instruction::CallKw { nargs } => (nargs.get(info.arg) as usize + 3, 1), - - // --- conservative fallback --- - // under-pops (≤ actual pops) and under-pushes (≤ actual pushes), - // which keeps extra refs on the stack → marks them unconsumed → - // prevents optimisation. Safe but may miss opportunities. - _ => { - let p = if effect < 0 { (-effect) as usize } else { 0 }; - let q = if effect > 0 { effect as usize } else { 0 }; - (p, q) - } - }; + let stack_effect_info = instr.stack_effect_info(info.arg.into()); + let (pushes, pops) = (stack_effect_info.pushed(), stack_effect_info.popped()); // Pop values from stack for _ in 0..pops { From f3a527a1a2a4b7aaf7c8195eedb023db21b629b2 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 31 Jan 2026 12:55:48 +0200 Subject: [PATCH 2/2] Update snapshot --- ...n_codegen__compile__tests__nested_double_async_with.snap | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap index 28a84bc1e86..2a482f3ecb9 100644 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap +++ b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap @@ -25,7 +25,7 @@ expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIter 16 LOAD_ATTR (7, subTest, method=true) 17 LOAD_GLOBAL (4, type) 18 PUSH_NULL - 19 LOAD_FAST_BORROW (0, stop_exc) + 19 LOAD_FAST (0, stop_exc) 20 CALL (1) 21 LOAD_CONST (("type")) 22 CALL_KW (1) @@ -105,8 +105,8 @@ expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIter 8 92 LOAD_GLOBAL (2, self) 93 LOAD_ATTR (15, assertIs, method=true) - 94 LOAD_FAST_BORROW (1, ex) - 95 LOAD_FAST_BORROW (0, stop_exc) + 94 LOAD_FAST (1, ex) + 95 LOAD_FAST (0, stop_exc) 96 CALL (2) 97 POP_TOP 98 JUMP_FORWARD (103)