◐ Shell
clean mode source ↗

More Load instructions by youknowone · Pull Request #6886 · RustPython/RustPython

-            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;
                 }
             }