- for (i, info) in block.instructions.iter().enumerate() {
- let Some(instr) = info.instr.real() else {
- continue;
- };
-
- // Get stack effect
- let effect = instr.stack_effect(info.arg);
+ let mut valid_block = true;
+ for (i, info) in block.instructions.iter().enumerate() {
+ let instr_real = info.instr.real();
+
+ // Get stack effect (includes pseudo ops)
+ let effect = info.instr.stack_effect(info.arg);
let num_popped = if effect < 0 { (-effect) as usize } else { 0 };
let num_pushed = if effect > 0 { effect as usize } else { 0 };
// More precise: calculate actual pops and pushes
// For most instructions: pops = max(0, -effect), pushes = max(0, effect)
// But some instructions have both pops and pushes
- let (pops, pushes) = match instr {
+ let (pops, pushes) = match instr_real {
+ Some(Instruction::LoadAttr { idx }) => {
+ let (_, is_method) = bytecode::decode_load_attr_arg(idx.get(info.arg));
+ if is_method { (1, 2) } else { (1, 1) }
+ }
// Instructions that both pop and push
- Instruction::BinaryOp { .. } => (2, 1),
- Instruction::CompareOp { .. } => (2, 1),
- Instruction::ContainsOp(_) => (2, 1),
- Instruction::IsOp(_) => (2, 1),
- Instruction::UnaryInvert
- | Instruction::UnaryNegative
- | Instruction::UnaryNot
- | Instruction::ToBool => (1, 1),
- Instruction::GetIter | Instruction::GetAIter => (1, 1),
- Instruction::LoadAttr { .. } => (1, 1), // simplified
- Instruction::Call { nargs } => (nargs.get(info.arg) as usize + 2, 1),
- Instruction::CallKw { nargs } => (nargs.get(info.arg) as usize + 3, 1),
+ Some(Instruction::BinaryOp { .. }) => (2, 1),
+ Some(Instruction::CompareOp { .. }) => (2, 1),
+ Some(Instruction::ContainsOp(_)) => (2, 1),
+ Some(Instruction::IsOp(_)) => (2, 1),
+ Some(Instruction::UnaryInvert)
+ | Some(Instruction::UnaryNegative)
+ | Some(Instruction::UnaryNot)
+ | Some(Instruction::ToBool) => (1, 1),
+ Some(Instruction::GetIter) | Some(Instruction::GetAIter) => (1, 1),
+ Some(Instruction::Call { nargs }) => (nargs.get(info.arg) as usize + 2, 1),
+ Some(Instruction::CallKw { nargs }) => (nargs.get(info.arg) as usize + 3, 1),
// Use stack effect for others
_ => (num_popped, num_pushed),
};
// Pop values from stack
for _ in 0..pops {
if stack.pop().is_none() {
// Stack underflow in simulation - block receives values from elsewhere
// Conservative: don't optimize this block
- break;
+ valid_block = false;
+ break;
}
}
+ if !valid_block {
+ break;
+ }
// Push values to stack with source instruction index
- let source = match instr {
- Instruction::LoadFast(_) | Instruction::LoadFastLoadFast { .. } => i,
+ let source = match instr_real {
+ Some(Instruction::LoadFast(_))
+ | Some(Instruction::LoadFastLoadFast { .. }) => i,
_ => NOT_LOCAL,
};
for _ in 0..pushes {
stack.push(source);
}
}
+ if !valid_block {
+ continue;
+ }
// Mark instructions whose values remain on stack at block end
for &src in &stack {
if src != NOT_LOCAL {
unconsumed[src] = true;
}
}