Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Use stack_effect_info for getting pop&push count
  • Loading branch information
ShaharNaveh committed Jan 31, 2026
commit 39dff7a7fa79ca94b1ee4062a1daded25b0b4f53
60 changes: 3 additions & 57 deletions crates/codegen/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<usize> = Vec::new();
Expand All @@ -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 {
Expand Down
Loading