align psuedo ops to 3.14.2

This commit is contained in:
Jeong YunWon
2026-01-24 00:03:15 +09:00
parent 771650a012
commit c0bdb9a3e5
5 changed files with 88 additions and 106 deletions

View File

@@ -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

View File

@@ -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)
}

View File

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

View File

@@ -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<Label>,
} = 257,
JumpIfFalse {
target: Arg<Label>,
} = 258,
JumpIfTrue {
target: Arg<Label>,
} = 259,
JumpNoInterrupt {
target: Arg<Label>,
} = 260,
Jump { target: Arg<Label> } = 257,
JumpIfFalse { target: Arg<Label> } = 258,
JumpIfTrue { target: Arg<Label> } = 259,
JumpNoInterrupt { target: Arg<Label> } = 260,
LoadClosure(Arg<NameIdx>) = 261,
PopBlock = 262,
SetupCleanup = 263,
SetupFinally = 264,
SetupWith = 265,
StoreFastMaybeNull(Arg<NameIdx>) = 266,
// RustPython-specific pseudo instructions (267+)
LoadAttrMethod {
idx: Arg<NameIdx>,
} = 267,
// "Zero" variants are for 0-arg super() calls (has_class=false).
// Non-"Zero" variants are for 2-arg super(cls, self) calls (has_class=true).
/// 2-arg super(cls, self).method() - has_class=true, load_method=true
LoadSuperMethod {
idx: Arg<NameIdx>,
} = 268,
LoadZeroSuperAttr {
idx: Arg<NameIdx>,
} = 269,
LoadZeroSuperMethod {
idx: Arg<NameIdx>,
} = 270,
}
const _: () = assert!(mem::size_of::<PseudoInstruction>() == 2);
@@ -982,7 +963,7 @@ impl TryFrom<u16> for PseudoInstruction {
#[inline]
fn try_from(value: u16) -> Result<Self, MarshalError> {
let start = u16::from(Self::AnnotationsPlaceholder);
let end = u16::from(Self::LoadZeroSuperMethod { idx: Arg::marker() });
let end = u16::from(Self::StoreFastMaybeNull(Arg::marker()));
if (start..=end).contains(&value) {
Ok(unsafe { mem::transmute::<u16, Self>(value) })
@@ -1024,10 +1005,6 @@ impl InstructionMetadata for PseudoInstruction {
Self::SetupFinally => 0,
Self::SetupWith => 0,
Self::StoreFastMaybeNull(_) => -1,
Self::LoadAttrMethod { .. } => 1, // pop obj, push method + self_or_null
Self::LoadSuperMethod { .. } => -3 + 2, // pop 3, push [method, self_or_null]
Self::LoadZeroSuperAttr { .. } => -3 + 1, // pop 3, push [attr]
Self::LoadZeroSuperMethod { .. } => -3 + 2, // pop 3, push [method, self_or_null]
}
}

View File

@@ -92,7 +92,7 @@ mod _opcode {
| Instruction::StoreAttr { .. }
| Instruction::StoreGlobal(_)
| Instruction::StoreName(_)
) | AnyInstruction::Pseudo(PseudoInstruction::LoadAttrMethod { .. }))
))
)
}
@@ -148,8 +148,11 @@ mod _opcode {
}
}
// prepare specialization
#[pyattr]
const ENABLE_SPECIALIZATION: i8 = 1;
#[allow(dead_code)]
const ENABLE_SPECIALIZATION_FT: i8 = 1;
#[derive(FromArgs)]
struct StackEffectArgs {
@@ -303,6 +306,7 @@ mod _opcode {
("NB_INPLACE_SUBTRACT", "-="),
("NB_INPLACE_TRUE_DIVIDE", "/="),
("NB_INPLACE_XOR", "^="),
("NB_SUBSCR", "[]"),
]
.into_iter()
.map(|(a, b)| {
@@ -314,8 +318,19 @@ mod _opcode {
}
#[pyfunction]
fn get_executor(_code: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
// TODO
fn get_special_method_names(vm: &VirtualMachine) -> Vec<PyObjectRef> {
["__enter__", "__exit__", "__aenter__", "__aexit__"]
.into_iter()
.map(|x| vm.ctx.new_str(x).into())
.collect()
}
#[pyfunction]
fn get_executor(
_code: PyObjectRef,
_offset: i32,
vm: &VirtualMachine,
) -> PyResult<PyObjectRef> {
Ok(vm.ctx.none())
}