Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.