diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py index 790104fd6..bb55ee423 100644 --- a/Lib/_opcode_metadata.py +++ b/Lib/_opcode_metadata.py @@ -245,10 +245,6 @@ opmap = { 'SETUP_FINALLY': 264, 'SETUP_WITH': 265, 'STORE_FAST_MAYBE_NULL': 266, - 'LOAD_ATTR_METHOD': 267, - 'LOAD_SUPER_METHOD': 268, - 'LOAD_ZERO_SUPER_ATTR': 269, - 'LOAD_ZERO_SUPER_METHOD': 270, } # CPython 3.13 compatible: opcodes < 44 have no argument diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index 061f356fe..5ace63c53 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -31,6 +31,7 @@ use rustpython_compiler_core::{ self, AnyInstruction, Arg as OpArgMarker, BinaryOperator, BuildSliceArgCount, CodeObject, ComparisonOperator, ConstantData, ConvertValueOparg, Instruction, IntrinsicFunction1, Invert, OpArg, OpArgType, PseudoInstruction, SpecialMethod, UnpackExArgs, + encode_load_attr_arg, encode_load_super_attr_arg, }, }; use rustpython_wtf8::Wtf8Buf; @@ -2108,7 +2109,7 @@ impl Compiler { if let Some(alias) = &name.asname { for part in name.name.split('.').skip(1) { let idx = self.name(part); - emit!(self, Instruction::LoadAttr { idx }); + self.emit_load_attr(idx); } self.store_name(alias.as_str())? } else { @@ -6280,7 +6281,7 @@ impl Compiler { self.compile_expression(value)?; emit!(self, Instruction::Copy { index: 1_u32 }); let idx = self.name(attr); - emit!(self, Instruction::LoadAttr { idx }); + self.emit_load_attr(idx); AugAssignKind::Attr { idx } } _ => { @@ -6615,19 +6616,17 @@ impl Compiler { let idx = self.name(attr.as_str()); match super_type { SuperCallType::TwoArg { .. } => { - // LoadSuperAttr (pseudo) - will be converted to real LoadSuperAttr - // with flags=0b10 (has_class=true, load_method=false) in ir.rs - emit!(self, Instruction::LoadSuperAttr { arg: idx }); + self.emit_load_super_attr(idx); } SuperCallType::ZeroArg => { - emit!(self, PseudoInstruction::LoadZeroSuperAttr { idx }); + self.emit_load_zero_super_attr(idx); } } } else { // Normal attribute access self.compile_expression(value)?; let idx = self.name(attr.as_str()); - emit!(self, Instruction::LoadAttr { idx }); + self.emit_load_attr(idx); } } ast::Expr::Compare(ast::ExprCompare { @@ -7070,19 +7069,19 @@ impl Compiler { let idx = self.name(attr.as_str()); match super_type { SuperCallType::TwoArg { .. } => { - emit!(self, PseudoInstruction::LoadSuperMethod { idx }); + self.emit_load_super_method(idx); } SuperCallType::ZeroArg => { - emit!(self, PseudoInstruction::LoadZeroSuperMethod { idx }); + self.emit_load_zero_super_method(idx); } } self.codegen_call_helper(0, args)?; } else { - // Normal method call: compile object, then LOAD_ATTR_METHOD - // LOAD_ATTR_METHOD pushes [method, self_or_null] on stack + // Normal method call: compile object, then LOAD_ATTR with method flag + // LOAD_ATTR(method=1) pushes [method, self_or_null] on stack self.compile_expression(value)?; let idx = self.name(attr.as_str()); - emit!(self, PseudoInstruction::LoadAttrMethod { idx }); + self.emit_load_attr_method(idx); self.codegen_call_helper(0, args)?; } } else { @@ -7782,6 +7781,48 @@ impl Compiler { emit!(self, Instruction::ReturnValue) } + /// Emit LOAD_ATTR for attribute access (method=false). + /// Encodes: (name_idx << 1) | 0 + fn emit_load_attr(&mut self, name_idx: u32) { + let encoded = encode_load_attr_arg(name_idx, false); + self.emit_arg(encoded, |arg| Instruction::LoadAttr { idx: arg }) + } + + /// Emit LOAD_ATTR with method flag set (for method calls). + /// Encodes: (name_idx << 1) | 1 + fn emit_load_attr_method(&mut self, name_idx: u32) { + let encoded = encode_load_attr_arg(name_idx, true); + self.emit_arg(encoded, |arg| Instruction::LoadAttr { idx: arg }) + } + + /// Emit LOAD_SUPER_ATTR for 2-arg super().attr access. + /// Encodes: (name_idx << 2) | 0b10 (method=0, class=1) + fn emit_load_super_attr(&mut self, name_idx: u32) { + let encoded = encode_load_super_attr_arg(name_idx, false, true); + self.emit_arg(encoded, |arg| Instruction::LoadSuperAttr { arg }) + } + + /// Emit LOAD_SUPER_ATTR for 2-arg super().method() call. + /// Encodes: (name_idx << 2) | 0b11 (method=1, class=1) + fn emit_load_super_method(&mut self, name_idx: u32) { + let encoded = encode_load_super_attr_arg(name_idx, true, true); + self.emit_arg(encoded, |arg| Instruction::LoadSuperAttr { arg }) + } + + /// Emit LOAD_SUPER_ATTR for 0-arg super().attr access. + /// Encodes: (name_idx << 2) | 0b00 (method=0, class=0) + fn emit_load_zero_super_attr(&mut self, name_idx: u32) { + let encoded = encode_load_super_attr_arg(name_idx, false, false); + self.emit_arg(encoded, |arg| Instruction::LoadSuperAttr { arg }) + } + + /// Emit LOAD_SUPER_ATTR for 0-arg super().method() call. + /// Encodes: (name_idx << 2) | 0b01 (method=1, class=0) + fn emit_load_zero_super_method(&mut self, name_idx: u32) { + let encoded = encode_load_super_attr_arg(name_idx, true, false); + self.emit_arg(encoded, |arg| Instruction::LoadSuperAttr { arg }) + } + fn emit_return_value(&mut self) { emit!(self, Instruction::ReturnValue) } diff --git a/crates/codegen/src/ir.rs b/crates/codegen/src/ir.rs index 3174f3444..59848fb23 100644 --- a/crates/codegen/src/ir.rs +++ b/crates/codegen/src/ir.rs @@ -7,8 +7,7 @@ use rustpython_compiler_core::{ bytecode::{ AnyInstruction, Arg, CodeFlags, CodeObject, CodeUnit, CodeUnits, ConstantData, ExceptionTableEntry, InstrDisplayContext, Instruction, InstructionMetadata, Label, OpArg, - PseudoInstruction, PyCodeLocationInfoKind, encode_exception_table, encode_load_attr_arg, - encode_load_super_attr_arg, + PseudoInstruction, PyCodeLocationInfoKind, encode_exception_table, }, varint::{write_signed_varint, write_varint}, }; @@ -207,62 +206,16 @@ impl CodeInfo { .filter(|b| b.next != BlockIdx::NULL || !b.instructions.is_empty()) { for info in &mut block.instructions { - // Special case for: - // - `Instruction::LoadAttr` - // - `Instruction::LoadSuperAttr` - - if let Some(instr) = info.instr.real() { - match instr { - // LOAD_ATTR → encode with method flag=0 - Instruction::LoadAttr { idx } => { - let encoded = encode_load_attr_arg(idx.get(info.arg), false); - info.arg = OpArg(encoded); - info.instr = Instruction::LoadAttr { idx: Arg::marker() }.into(); - } - // LOAD_SUPER_ATTR → encode with flags=0b10 (method=0, class=1) - Instruction::LoadSuperAttr { arg: idx } => { - let encoded = - encode_load_super_attr_arg(idx.get(info.arg), false, true); - info.arg = OpArg(encoded); - info.instr = Instruction::LoadSuperAttr { arg: Arg::marker() }.into(); - } - _ => {} - } - + // Real instructions are already encoded by compile.rs + let Some(instr) = info.instr.pseudo() else { continue; - } - - let instr = info.instr.expect_pseudo(); + }; match instr { - // LOAD_ATTR_METHOD pseudo → LOAD_ATTR (with method flag=1) - PseudoInstruction::LoadAttrMethod { idx } => { - let encoded = encode_load_attr_arg(idx.get(info.arg), true); - info.arg = OpArg(encoded); - info.instr = Instruction::LoadAttr { idx: Arg::marker() }.into(); - } // POP_BLOCK pseudo → NOP PseudoInstruction::PopBlock => { info.instr = Instruction::Nop.into(); } - // LOAD_SUPER_METHOD pseudo → LOAD_SUPER_ATTR (flags=0b11: method=1, class=1) - PseudoInstruction::LoadSuperMethod { idx } => { - let encoded = encode_load_super_attr_arg(idx.get(info.arg), true, true); - info.arg = OpArg(encoded); - info.instr = Instruction::LoadSuperAttr { arg: Arg::marker() }.into(); - } - // LOAD_ZERO_SUPER_ATTR pseudo → LOAD_SUPER_ATTR (flags=0b00: method=0, class=0) - PseudoInstruction::LoadZeroSuperAttr { idx } => { - let encoded = encode_load_super_attr_arg(idx.get(info.arg), false, false); - info.arg = OpArg(encoded); - info.instr = Instruction::LoadSuperAttr { arg: Arg::marker() }.into(); - } - // LOAD_ZERO_SUPER_METHOD pseudo → LOAD_SUPER_ATTR (flags=0b01: method=1, class=0) - PseudoInstruction::LoadZeroSuperMethod { idx } => { - let encoded = encode_load_super_attr_arg(idx.get(info.arg), true, false); - info.arg = OpArg(encoded); - info.instr = Instruction::LoadSuperAttr { arg: Arg::marker() }.into(); - } // LOAD_CLOSURE pseudo → LOAD_FAST (with varnames offset) PseudoInstruction::LoadClosure(idx) => { let varnames_len = varname_cache.len() as u32; diff --git a/crates/compiler-core/src/bytecode/instruction.rs b/crates/compiler-core/src/bytecode/instruction.rs index a4b6489ca..1962e1955 100644 --- a/crates/compiler-core/src/bytecode/instruction.rs +++ b/crates/compiler-core/src/bytecode/instruction.rs @@ -474,7 +474,13 @@ impl InstructionMetadata for Instruction { Self::LoadFromDictOrDeref(_) => 1, Self::StoreSubscr => -3, Self::DeleteSubscr => -2, - Self::LoadAttr { .. } => 0, + Self::LoadAttr { idx } => { + // Stack effect depends on method flag in encoded oparg + // method=false: pop obj, push attr → effect = 0 + // method=true: pop obj, push (method, self_or_null) → effect = +1 + let (_, is_method) = decode_load_attr_arg(idx.get(arg)); + if is_method { 1 } else { 0 } + } Self::StoreAttr { .. } => -2, Self::DeleteAttr { .. } => -1, Self::LoadCommonConstant { .. } => 1, @@ -923,47 +929,22 @@ impl InstructionMetadata for Instruction { /// Instructions used by the compiler. They are not executed by the VM. /// -/// CPython 3.14.2 aligned (256-266), RustPython-specific variants start at 267. +/// CPython 3.14.2 aligned (256-266). #[derive(Clone, Copy, Debug)] #[repr(u16)] pub enum PseudoInstruction { // CPython 3.14.2 pseudo instructions (256-266) AnnotationsPlaceholder = 256, - Jump { - target: Arg