diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index 7e2b25ccbef..3e6fbce2146 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -30,8 +30,8 @@ use rustpython_compiler_core::{ bytecode::{ 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, + Invert, LoadSuperAttr, OpArg, OpArgType, PseudoInstruction, SpecialMethod, UnpackExArgs, + encode_load_attr_arg, }, }; use rustpython_wtf8::Wtf8Buf; @@ -7818,28 +7818,44 @@ impl Compiler { /// 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); + let encoded = LoadSuperAttr::builder() + .name_idx(name_idx) + .is_load_method(false) + .has_class(true) + .build(); 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); + let encoded = LoadSuperAttr::builder() + .name_idx(name_idx) + .is_load_method(true) + .has_class(true) + .build(); 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); + let encoded = LoadSuperAttr::builder() + .name_idx(name_idx) + .is_load_method(false) + .has_class(false) + .build(); 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); + let encoded = LoadSuperAttr::builder() + .name_idx(name_idx) + .is_load_method(true) + .has_class(false) + .build(); self.emit_arg(encoded, |arg| Instruction::LoadSuperAttr { arg }) } diff --git a/crates/compiler-core/src/bytecode.rs b/crates/compiler-core/src/bytecode.rs index 13884dc8a73..3080b4e623e 100644 --- a/crates/compiler-core/src/bytecode.rs +++ b/crates/compiler-core/src/bytecode.rs @@ -17,13 +17,13 @@ use rustpython_wtf8::{Wtf8, Wtf8Buf}; pub use crate::bytecode::{ instruction::{ AnyInstruction, Arg, Instruction, InstructionMetadata, PseudoInstruction, StackEffect, - decode_load_attr_arg, decode_load_super_attr_arg, encode_load_attr_arg, - encode_load_super_attr_arg, + decode_load_attr_arg, encode_load_attr_arg, }, oparg::{ BinaryOperator, BuildSliceArgCount, CommonConstant, ComparisonOperator, ConvertValueOparg, - IntrinsicFunction1, IntrinsicFunction2, Invert, Label, MakeFunctionFlags, NameIdx, OpArg, - OpArgByte, OpArgState, OpArgType, RaiseKind, ResumeType, SpecialMethod, UnpackExArgs, + IntrinsicFunction1, IntrinsicFunction2, Invert, Label, LoadSuperAttr, MakeFunctionFlags, + NameIdx, OpArg, OpArgByte, OpArgState, OpArgType, RaiseKind, ResumeType, SpecialMethod, + UnpackExArgs, }, }; diff --git a/crates/compiler-core/src/bytecode/instruction.rs b/crates/compiler-core/src/bytecode/instruction.rs index ee36220b538..1f168749e63 100644 --- a/crates/compiler-core/src/bytecode/instruction.rs +++ b/crates/compiler-core/src/bytecode/instruction.rs @@ -6,8 +6,8 @@ use crate::{ oparg::{ BinaryOperator, BuildSliceArgCount, CommonConstant, ComparisonOperator, ConvertValueOparg, IntrinsicFunction1, IntrinsicFunction2, Invert, Label, - MakeFunctionFlags, NameIdx, OpArg, OpArgByte, OpArgType, RaiseKind, SpecialMethod, - UnpackExArgs, + LoadSuperAttr, MakeFunctionFlags, NameIdx, OpArg, OpArgByte, OpArgType, RaiseKind, + SpecialMethod, UnpackExArgs, }, }, marshal::MarshalError, @@ -198,7 +198,7 @@ pub enum Instruction { method: Arg, } = 95, LoadSuperAttr { - arg: Arg, + arg: Arg, } = 96, MakeCell(Arg) = 97, MapAdd { @@ -862,13 +862,15 @@ impl InstructionMetadata for Instruction { Self::LoadName(idx) => w!(LOAD_NAME, name = idx), Self::LoadSpecial { method } => w!(LOAD_SPECIAL, method), Self::LoadSuperAttr { arg: idx } => { - let encoded = idx.get(arg); - let (name_idx, load_method, has_class) = decode_load_super_attr_arg(encoded); - let attr_name = name(name_idx); + let oparg = idx.get(arg); write!( f, "{:pad$}({}, {}, method={}, class={})", - "LOAD_SUPER_ATTR", encoded, attr_name, load_method, has_class + "LOAD_SUPER_ATTR", + u32::from(oparg), + name(oparg.name_idx()), + oparg.is_load_method(), + oparg.has_class() ) } Self::MakeFunction => w!(MAKE_FUNCTION), @@ -1304,18 +1306,3 @@ pub const fn decode_load_attr_arg(oparg: u32) -> (u32, bool) { let name_idx = oparg >> 1; (name_idx, is_method) } - -/// Encode LOAD_SUPER_ATTR oparg: bit 0 = load_method, bit 1 = has_class, bits 2+ = name index. -#[inline] -pub const fn encode_load_super_attr_arg(name_idx: u32, load_method: bool, has_class: bool) -> u32 { - (name_idx << 2) | ((has_class as u32) << 1) | (load_method as u32) -} - -/// Decode LOAD_SUPER_ATTR oparg: returns (name_idx, load_method, has_class). -#[inline] -pub const fn decode_load_super_attr_arg(oparg: u32) -> (u32, bool, bool) { - let load_method = (oparg & 1) == 1; - let has_class = (oparg & 2) == 2; - let name_idx = oparg >> 2; - (name_idx, load_method, has_class) -} diff --git a/crates/compiler-core/src/bytecode/oparg.rs b/crates/compiler-core/src/bytecode/oparg.rs index 724fd6fcd10..6378f04bbf9 100644 --- a/crates/compiler-core/src/bytecode/oparg.rs +++ b/crates/compiler-core/src/bytecode/oparg.rs @@ -655,3 +655,97 @@ impl fmt::Display for UnpackExArgs { write!(f, "before: {}, after: {}", self.before, self.after) } } + +#[derive(Clone, Copy)] +pub struct LoadSuperAttr(u32); + +impl LoadSuperAttr { + #[must_use] + pub const fn new(value: u32) -> Self { + Self(value) + } + + #[must_use] + pub fn builder() -> LoadSuperAttrBuilder { + LoadSuperAttrBuilder::default() + } + + #[must_use] + pub const fn name_idx(self) -> u32 { + self.0 >> 2 + } + + #[must_use] + pub const fn is_load_method(self) -> bool { + (self.0 & 1) == 1 + } + + #[must_use] + pub const fn has_class(self) -> bool { + (self.0 & 2) == 2 + } +} + +impl OpArgType for LoadSuperAttr { + #[inline(always)] + fn from_op_arg(x: u32) -> Result { + Ok(x.into()) + } + + #[inline(always)] + fn to_op_arg(self) -> u32 { + self.into() + } +} + +impl From for LoadSuperAttr { + fn from(value: u32) -> Self { + Self::new(value) + } +} + +impl From for u32 { + fn from(value: LoadSuperAttr) -> Self { + value.0 + } +} + +#[derive(Clone, Copy, Default)] +pub struct LoadSuperAttrBuilder { + name_idx: u32, + is_load_method: bool, + has_class: bool, +} + +impl LoadSuperAttrBuilder { + #[must_use] + pub const fn build(self) -> LoadSuperAttr { + let value = + (self.name_idx << 2) | ((self.has_class as u32) << 1) | (self.is_load_method as u32); + LoadSuperAttr::new(value) + } + + #[must_use] + pub const fn name_idx(mut self, value: u32) -> Self { + self.name_idx = value; + self + } + + #[must_use] + pub const fn is_load_method(mut self, value: bool) -> Self { + self.is_load_method = value; + self + } + + #[must_use] + pub const fn has_class(mut self, value: bool) -> Self { + self.has_class = value; + self + } +} + +impl From for LoadSuperAttr { + fn from(builder: LoadSuperAttrBuilder) -> Self { + builder.build() + } +} diff --git a/crates/vm/src/frame.rs b/crates/vm/src/frame.rs index 807d751e723..da918af18c8 100644 --- a/crates/vm/src/frame.rs +++ b/crates/vm/src/frame.rs @@ -10,7 +10,7 @@ use crate::{ function::{PyCell, PyCellRef, PyFunction}, tuple::{PyTuple, PyTupleRef}, }, - bytecode::{self, Instruction}, + bytecode::{self, Instruction, LoadSuperAttr}, convert::{IntoObject, ToPyResult}, coroutine::Coro, exceptions::ExceptionCtor, @@ -2859,9 +2859,8 @@ impl ExecutingFrame<'_> { Ok(None) } - fn load_super_attr(&mut self, vm: &VirtualMachine, oparg: u32) -> FrameResult { - let (name_idx, load_method, has_class) = bytecode::decode_load_super_attr_arg(oparg); - let attr_name = self.code.names[name_idx as usize]; + fn load_super_attr(&mut self, vm: &VirtualMachine, oparg: LoadSuperAttr) -> FrameResult { + let attr_name = self.code.names[oparg.name_idx() as usize]; // Stack layout (bottom to top): [super, class, self] // Pop in LIFO order: self, class, super @@ -2871,13 +2870,13 @@ impl ExecutingFrame<'_> { // Create super object - pass args based on has_class flag // When super is shadowed, has_class=false means call with 0 args - let super_obj = if has_class { + let super_obj = if oparg.has_class() { global_super.call((class.clone(), self_obj.clone()), vm)? } else { global_super.call((), vm)? }; - if load_method { + if oparg.is_load_method() { // Method load: push [method, self_or_null] let method = PyMethod::get(super_obj, attr_name, vm)?; match method {