Skip to content

Commit 66cb68b

Browse files
committed
pseudo instruction
1 parent e1ef05c commit 66cb68b

File tree

4 files changed

+134
-55
lines changed

4 files changed

+134
-55
lines changed

crates/codegen/src/ir.rs

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{IndexMap, IndexSet, error::InternalError};
44
use rustpython_compiler_core::{
55
OneIndexed, SourceLocation,
66
bytecode::{
7-
CodeFlags, CodeObject, CodeUnit, CodeUnits, ConstantData, ExceptionTableEntry,
7+
Arg, CodeFlags, CodeObject, CodeUnit, CodeUnits, ConstantData, ExceptionTableEntry,
88
InstrDisplayContext, Instruction, Label, OpArg, PyCodeLocationInfoKind,
99
encode_exception_table,
1010
},
@@ -189,6 +189,34 @@ impl CodeInfo {
189189
let mut instructions = Vec::new();
190190
let mut locations = Vec::new();
191191

192+
// convert_pseudo_ops: instructions before the main loop
193+
for block in blocks
194+
.iter_mut()
195+
.filter(|b| b.next != BlockIdx::NULL || !b.instructions.is_empty())
196+
{
197+
for info in &mut block.instructions {
198+
match info.instr {
199+
// LOAD_ATTR_METHOD pseudo → LOAD_ATTR (with method flag=1)
200+
Instruction::LoadAttrMethod { idx } => {
201+
let encoded = OpArg((idx.get(info.arg) << 1) | 1);
202+
info.arg = encoded;
203+
info.instr = Instruction::LoadAttr { idx: Arg::marker() };
204+
}
205+
// LOAD_ATTR → encode with method flag=0
206+
Instruction::LoadAttr { idx } => {
207+
let encoded = OpArg(idx.get(info.arg) << 1);
208+
info.arg = encoded;
209+
info.instr = Instruction::LoadAttr { idx: Arg::marker() };
210+
}
211+
// POP_BLOCK pseudo → NOP
212+
Instruction::PopBlock => {
213+
info.instr = Instruction::Nop;
214+
}
215+
_ => {}
216+
}
217+
}
218+
}
219+
192220
let mut block_to_offset = vec![Label(0); blocks.len()];
193221
// block_to_index: maps block idx to instruction index (for exception table)
194222
// This is the index into the final instructions array, including EXTENDED_ARG
@@ -213,23 +241,44 @@ impl CodeInfo {
213241
let mut next_block = BlockIdx(0);
214242
while next_block != BlockIdx::NULL {
215243
let block = &mut blocks[next_block];
244+
// Track current instruction offset for jump direction resolution
245+
let mut current_offset = block_to_offset[next_block.idx()].0;
216246
for info in &mut block.instructions {
217-
let (op, arg, target) = (info.instr, &mut info.arg, info.target);
247+
let target = info.target;
218248
if target != BlockIdx::NULL {
219249
let new_arg = OpArg(block_to_offset[target.idx()].0);
220-
recompile_extended_arg |= new_arg.instr_size() != arg.instr_size();
221-
*arg = new_arg;
250+
recompile_extended_arg |= new_arg.instr_size() != info.arg.instr_size();
251+
info.arg = new_arg;
222252
}
223-
let (extras, lo_arg) = arg.split();
253+
254+
// Convert JUMP pseudo to real instructions (direction depends on offset)
255+
let op = match info.instr {
256+
Instruction::Jump { .. } if target != BlockIdx::NULL => {
257+
let target_offset = block_to_offset[target.idx()].0;
258+
if target_offset > current_offset {
259+
Instruction::JumpForward {
260+
target: Arg::marker(),
261+
}
262+
} else {
263+
Instruction::JumpBackward {
264+
target: Arg::marker(),
265+
}
266+
}
267+
}
268+
other => other,
269+
};
270+
271+
let (extras, lo_arg) = info.arg.split();
224272
locations.extend(core::iter::repeat_n(
225273
(info.location, info.end_location),
226-
arg.instr_size(),
274+
info.arg.instr_size(),
227275
));
228276
instructions.extend(
229277
extras
230278
.map(|byte| CodeUnit::new(Instruction::ExtendedArg, byte))
231279
.chain([CodeUnit { op, arg: lo_arg }]),
232280
);
281+
current_offset += info.arg.instr_size() as u32;
233282
}
234283
next_block = block.next;
235284
}

crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap

Lines changed: 21 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/compiler-core/src/bytecode.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2060,11 +2060,29 @@ impl Instruction {
20602060
ImportName { idx } => w!(IMPORT_NAME, name = idx),
20612061
IsOp(inv) => w!(IS_OP, ?inv),
20622062
Jump { target } => w!(JUMP, target),
2063+
JumpBackward { target } => w!(JUMP_BACKWARD, target),
2064+
JumpBackwardNoInterrupt { target } => w!(JUMP_BACKWARD_NO_INTERRUPT, target),
2065+
JumpForward { target } => w!(JUMP_FORWARD, target),
20632066
JumpIfFalseOrPop { target } => w!(JUMP_IF_FALSE_OR_POP, target),
20642067
JumpIfNotExcMatch(target) => w!(JUMP_IF_NOT_EXC_MATCH, target),
20652068
JumpIfTrueOrPop { target } => w!(JUMP_IF_TRUE_OR_POP, target),
20662069
ListAppend { i } => w!(LIST_APPEND, i),
2067-
LoadAttr { idx } => w!(LOAD_ATTR, name = idx),
2070+
LoadAttr { idx } => {
2071+
// oparg encoding: bit 0 = method flag, bits 1+ = name index
2072+
let encoded = idx.get(arg);
2073+
let is_method = (encoded & 1) == 1;
2074+
let name_idx = encoded >> 1;
2075+
let attr_name = name(name_idx);
2076+
if is_method {
2077+
write!(
2078+
f,
2079+
"{:pad$}({}, {}, method=true)",
2080+
"LOAD_ATTR", encoded, attr_name
2081+
)
2082+
} else {
2083+
write!(f, "{:pad$}({}, {})", "LOAD_ATTR", encoded, attr_name)
2084+
}
2085+
}
20682086
LoadAttrMethod { idx } => w!(LOAD_ATTR_METHOD, name = idx),
20692087
LoadBuildClass => w!(LOAD_BUILD_CLASS),
20702088
LoadClassDeref(idx) => w!(LOAD_CLASSDEREF, cell_name = idx),

crates/vm/src/frame.rs

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,18 @@ impl ExecutingFrame<'_> {
10821082
self.jump(target.get(arg));
10831083
Ok(None)
10841084
}
1085+
bytecode::Instruction::JumpForward { target } => {
1086+
self.jump(target.get(arg));
1087+
Ok(None)
1088+
}
1089+
bytecode::Instruction::JumpBackward { target } => {
1090+
self.jump(target.get(arg));
1091+
Ok(None)
1092+
}
1093+
bytecode::Instruction::JumpBackwardNoInterrupt { target } => {
1094+
self.jump(target.get(arg));
1095+
Ok(None)
1096+
}
10851097
bytecode::Instruction::ListAppend { i } => {
10861098
let item = self.pop_value();
10871099
let obj = self.nth_value(i.get(arg));
@@ -1093,6 +1105,9 @@ impl ExecutingFrame<'_> {
10931105
Ok(None)
10941106
}
10951107
bytecode::Instruction::LoadAttr { idx } => self.load_attr(vm, idx.get(arg)),
1108+
bytecode::Instruction::LoadAttrMethod { .. } => {
1109+
unreachable!("LoadAttrMethod is converted to LoadAttr during compilation")
1110+
}
10961111
bytecode::Instruction::LoadBuildClass => {
10971112
self.push_value(vm.builtins.get_attr(identifier!(vm, __build_class__), vm)?);
10981113
Ok(None)
@@ -1164,9 +1179,6 @@ impl ExecutingFrame<'_> {
11641179
self.push_value(x);
11651180
Ok(None)
11661181
}
1167-
bytecode::Instruction::LoadAttrMethod { idx } => {
1168-
self.load_attr_method(vm, idx.get(arg))
1169-
}
11701182
bytecode::Instruction::LoadName(idx) => {
11711183
let name = self.code.names[idx.get(arg) as usize];
11721184
let result = self.locals.mapping().subscript(name, vm);
@@ -1401,8 +1413,9 @@ impl ExecutingFrame<'_> {
14011413
Ok(None)
14021414
}
14031415
bytecode::Instruction::Nop => Ok(None),
1404-
// PopBlock is now a pseudo-instruction - exception table handles this
1405-
bytecode::Instruction::PopBlock => Ok(None),
1416+
bytecode::Instruction::PopBlock => {
1417+
unreachable!("PopBlock is converted to Nop during compilation")
1418+
}
14061419
bytecode::Instruction::PopExcept => {
14071420
// Pop prev_exc from value stack and restore it
14081421
let prev_exc = self.pop_value();
@@ -2493,31 +2506,30 @@ impl ExecutingFrame<'_> {
24932506
Ok(None)
24942507
}
24952508

2496-
fn load_attr(&mut self, vm: &VirtualMachine, idx: u32) -> FrameResult {
2497-
// Regular attribute access: pop obj, push attr
2498-
let attr_name = self.code.names[idx as usize];
2499-
let parent = self.pop_value();
2500-
let obj = parent.get_attr(attr_name, vm)?;
2501-
self.push_value(obj);
2502-
Ok(None)
2503-
}
2504-
2505-
fn load_attr_method(&mut self, vm: &VirtualMachine, idx: u32) -> FrameResult {
2506-
// Method call: pop obj, push [method, self_or_null]
2507-
let attr_name = self.code.names[idx as usize];
2509+
fn load_attr(&mut self, vm: &VirtualMachine, oparg: u32) -> FrameResult {
2510+
// oparg encoding: bit 0 = method flag, bits 1+ = name index
2511+
let is_method = (oparg & 1) == 1;
2512+
let name_idx = (oparg >> 1) as usize;
2513+
let attr_name = self.code.names[name_idx];
25082514
let parent = self.pop_value();
25092515

2510-
match PyMethod::get(parent, attr_name, vm)? {
2511-
PyMethod::Function { target, func } => {
2512-
// Method descriptor found: push [method, self]
2513-
self.push_value(func);
2514-
self.push_value(target);
2515-
}
2516-
PyMethod::Attribute(val) => {
2517-
// Regular attribute: push [attr, NULL]
2518-
self.push_value(val);
2519-
self.push_null();
2516+
if is_method {
2517+
// Method call: push [method, self_or_null]
2518+
let method = PyMethod::get(parent.clone(), attr_name, vm)?;
2519+
match method {
2520+
PyMethod::Function { target: _, func } => {
2521+
self.push_value(func);
2522+
self.push_value(parent);
2523+
}
2524+
PyMethod::Attribute(val) => {
2525+
self.push_value(val);
2526+
self.push_null();
2527+
}
25202528
}
2529+
} else {
2530+
// Regular attribute access
2531+
let obj = parent.get_attr(attr_name, vm)?;
2532+
self.push_value(obj);
25212533
}
25222534
Ok(None)
25232535
}

0 commit comments

Comments
 (0)