Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
fad76c6
save
ShaharNaveh Jan 11, 2026
af70361
Merge remote-tracking branch 'upstream/main' into bytecode-pseudo-opc…
ShaharNaveh Jan 11, 2026
ea53cec
save
ShaharNaveh Jan 11, 2026
1ca20a9
Merge remote-tracking branch 'upstream/main' into bytecode-pseudo-opc…
ShaharNaveh Jan 12, 2026
6f09ebe
Base compiler-core
ShaharNaveh Jan 12, 2026
6805175
save
ShaharNaveh Jan 12, 2026
046e17a
Codegen compile
ShaharNaveh Jan 12, 2026
d627623
Move LoadCloure back to RealInstruction
ShaharNaveh Jan 12, 2026
697fe41
Fix opcode.rs
ShaharNaveh Jan 12, 2026
11fc63f
Merge remote-tracking branch 'upstream/main' into bytecode-pseudo-opc…
ShaharNaveh Jan 13, 2026
33b4554
Fix `TryFrom<u8>` for RealInstruction
ShaharNaveh Jan 13, 2026
6140186
Fix script
ShaharNaveh Jan 13, 2026
7acdc1a
Fix jit
ShaharNaveh Jan 13, 2026
0fad43f
Fix typo
ShaharNaveh Jan 13, 2026
24f5161
Fix typo
ShaharNaveh Jan 13, 2026
6e91410
Remove popblock
ShaharNaveh Jan 13, 2026
8751953
Fix docs
ShaharNaveh Jan 13, 2026
8b73441
Fix more docs
ShaharNaveh Jan 13, 2026
d6e46f8
ok word `argty`
ShaharNaveh Jan 13, 2026
0872cb6
Revert "ok word `argty`"
ShaharNaveh Jan 13, 2026
0c7880d
Rename argty -> arg_ty
ShaharNaveh Jan 13, 2026
1a5c72b
Merge remote-tracking branch 'upstream/main' into bytecode-pseudo-opc…
ShaharNaveh Jan 13, 2026
761fa4a
Simplify `emit` macro
ShaharNaveh Jan 13, 2026
4eed137
Trigger CI
ShaharNaveh Jan 13, 2026
8f777d2
Trigger CI
ShaharNaveh Jan 13, 2026
bff25eb
Trigger CI
ShaharNaveh Jan 13, 2026
91c01e6
Merge remote-tracking branch 'upstream/main' into bytecode-pseudo-opc…
ShaharNaveh Jan 14, 2026
f6a0b26
Merge remote-tracking branch 'upstream/main' into bytecode-pseudo-opc…
ShaharNaveh Jan 14, 2026
118ecf3
Move docs
ShaharNaveh Jan 14, 2026
c517585
Fix oparg docs
ShaharNaveh Jan 14, 2026
78958aa
Revert "Move docs"
ShaharNaveh Jan 14, 2026
a405cca
Merge remote-tracking branch 'upstream/main' into bytecode-pseudo-opc…
ShaharNaveh Jan 14, 2026
8ae98b2
Remove `Eq` & `ParitalEq` for RealInstruction
ShaharNaveh Jan 14, 2026
1453726
Simplify `match` arms
ShaharNaveh Jan 14, 2026
f207284
Trigger CI
ShaharNaveh Jan 14, 2026
a1555dc
Trigger CI
ShaharNaveh Jan 14, 2026
c443f8e
Remove `repr(u16)` for `Instruction`
ShaharNaveh Jan 14, 2026
453212e
Fix doc
ShaharNaveh Jan 14, 2026
1f8722a
Rename the enums
ShaharNaveh Jan 14, 2026
0096a65
Restore fmt_dis for LoadClousure
ShaharNaveh Jan 14, 2026
045d576
Fix script
ShaharNaveh Jan 14, 2026
b00f81f
Fix commet
ShaharNaveh Jan 14, 2026
4823d17
Merge remote-tracking branch 'upstream/main' into bytecode-pseudo-opc…
ShaharNaveh Jan 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cspell.dict/rust-more.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
ahash
argty
arrayvec
bidi
biguint
Expand Down
18 changes: 12 additions & 6 deletions Lib/_opcode_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,20 @@
'JUMP_IF_NOT_EXC_MATCH': 131,
'SET_EXC_INFO': 134,
'SUBSCRIPT': 135,
'LOAD_SUPER_METHOD': 136,
'LOAD_ZERO_SUPER_ATTR': 137,
'LOAD_ZERO_SUPER_METHOD': 138,
'RESUME': 149,
'JUMP': 252,
'LOAD_CLOSURE': 253,
'LOAD_ATTR_METHOD': 254,
'POP_BLOCK': 255,
'JUMP': 256,
'JUMP_NO_INTERRUPT': 257,
'RESERVED_258': 258,
'LOAD_ATTR_METHOD': 259,
'LOAD_SUPER_METHOD': 260,
'LOAD_ZERO_SUPER_ATTR': 261,
'LOAD_ZERO_SUPER_METHOD': 262,
'POP_BLOCK': 263,
'SETUP_CLEANUP': 264,
'SETUP_FINALLY': 265,
'SETUP_WITH': 266,
'STORE_FAST_MAYBE_NULL': 267,
}

# CPython 3.13 compatible: opcodes < 44 have no argument
Expand Down
1 change: 0 additions & 1 deletion Lib/test/test__opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ def check_bool_function_result(self, func, ops, expected):
self.assertIsInstance(func(op), bool)
self.assertEqual(func(op), expected)

@unittest.expectedFailure # TODO: RUSTPYTHON; Only supporting u8 ATM
def test_invalid_opcodes(self):
invalid = [-100, -1, 255, 512, 513, 1000]
self.check_bool_function_result(_opcode.is_valid, invalid, False)
Expand Down
970 changes: 503 additions & 467 deletions crates/codegen/src/compile.rs

Large diffs are not rendered by default.

46 changes: 25 additions & 21 deletions crates/codegen/src/ir.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use core::ops;

use crate::{IndexMap, IndexSet, error::InternalError};

use rustpython_compiler_core::{
OneIndexed, SourceLocation,
bytecode::{
Arg, CodeFlags, CodeObject, CodeUnit, CodeUnits, ConstantData, ExceptionTableEntry,
InstrDisplayContext, Instruction, Label, OpArg, PyCodeLocationInfoKind,
encode_exception_table, encode_load_attr_arg, encode_load_super_attr_arg,
InstrDisplayContext, Instruction, InstructionMetadata, Label, OpArg, PseudoInstruction,
PyCodeLocationInfoKind, RealInstruction, encode_exception_table, encode_load_attr_arg,
encode_load_super_attr_arg,
},
varint::{write_signed_varint, write_varint},
};
Expand Down Expand Up @@ -197,44 +199,44 @@ impl CodeInfo {
for info in &mut block.instructions {
match info.instr {
// LOAD_ATTR_METHOD pseudo → LOAD_ATTR (with method flag=1)
Instruction::LoadAttrMethod { idx } => {
Instruction::Pseudo(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() };
info.instr = RealInstruction::LoadAttr { idx: Arg::marker() }.into();
}
// LOAD_ATTR → encode with method flag=0
Instruction::LoadAttr { idx } => {
Instruction::Real(RealInstruction::LoadAttr { idx }) => {
let encoded = encode_load_attr_arg(idx.get(info.arg), false);
info.arg = OpArg(encoded);
info.instr = Instruction::LoadAttr { idx: Arg::marker() };
info.instr = RealInstruction::LoadAttr { idx: Arg::marker() }.into();
}
// POP_BLOCK pseudo → NOP
Instruction::PopBlock => {
info.instr = Instruction::Nop;
Instruction::Pseudo(PseudoInstruction::PopBlock) => {
info.instr = RealInstruction::Nop.into();
}
// LOAD_SUPER_METHOD pseudo → LOAD_SUPER_ATTR (flags=0b11: method=1, class=1)
Instruction::LoadSuperMethod { idx } => {
Instruction::Pseudo(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() };
info.instr = RealInstruction::LoadSuperAttr { arg: Arg::marker() }.into();
}
// LOAD_ZERO_SUPER_ATTR pseudo → LOAD_SUPER_ATTR (flags=0b00: method=0, class=0)
Instruction::LoadZeroSuperAttr { idx } => {
Instruction::Pseudo(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() };
info.instr = RealInstruction::LoadSuperAttr { arg: Arg::marker() }.into();
}
// LOAD_ZERO_SUPER_METHOD pseudo → LOAD_SUPER_ATTR (flags=0b01: method=1, class=0)
Instruction::LoadZeroSuperMethod { idx } => {
Instruction::Pseudo(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() };
info.instr = RealInstruction::LoadSuperAttr { arg: Arg::marker() }.into();
}
// LOAD_SUPER_ATTR → encode with flags=0b10 (method=0, class=1)
Instruction::LoadSuperAttr { arg: idx } => {
Instruction::Real(RealInstruction::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() };
info.instr = RealInstruction::LoadSuperAttr { arg: Arg::marker() }.into();
}
Comment on lines +259 to 266
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

JumpNoInterrupt and other placeholders will panic at runtime.

If any of these placeholder pseudo instructions are emitted during compilation, the code will panic with unimplemented!(). Notably, JumpNoInterrupt is defined with a target field but is not handled for conversion.

Consider either:

  1. Implementing conversion for JumpNoInterrupt (similar to Jump at lines 307-323)
  2. Ensuring these instructions are never emitted by the compiler
🐛 Proposed fix for JumpNoInterrupt

Add handling in the match at lines 257-267 and in the JUMP conversion block:

                     PseudoInstruction::Jump { .. } => {
                         // PseudoInstruction::Jump instructions are handled later
                     }
-                    PseudoInstruction::JumpNoInterrupt { .. }
+                    PseudoInstruction::JumpNoInterrupt { .. } => {
+                        // PseudoInstruction::JumpNoInterrupt instructions are handled later
+                    }
                     | PseudoInstruction::Reserved258

And update the conversion block (lines 307-323) to handle JumpNoInterrupt:

                     let op = match info.instr {
                         Instruction::Pseudo(PseudoInstruction::Jump { .. })
+                            | Instruction::Pseudo(PseudoInstruction::JumpNoInterrupt { .. })
                             if target != BlockIdx::NULL =>
                         {
                             let target_offset = block_to_offset[target.idx()].0;
                             if target_offset > current_offset {
-                                RealInstruction::JumpForward {
+                                // For JumpNoInterrupt, use JumpBackwardNoInterrupt
+                                if matches!(info.instr, Instruction::Pseudo(PseudoInstruction::JumpNoInterrupt { .. })) {
+                                    // JumpNoInterrupt should only jump backward
+                                    RealInstruction::JumpBackwardNoInterrupt { target: Arg::marker() }
+                                } else {
+                                    RealInstruction::JumpForward { target: Arg::marker() }
+                                }
🤖 Prompt for AI Agents
In `@crates/codegen/src/ir.rs` around lines 260 - 267, The match arm that
currently groups PseudoInstruction::JumpNoInterrupt with placeholders and calls
unimplemented! must be fixed: add an explicit PseudoInstruction::JumpNoInterrupt
{ target } arm and implement its conversion similar to PseudoInstruction::Jump
(the conversion block around the existing Jump handling at lines ~307-323),
using the target field to produce the equivalent bytecode/IR for a
non-interrupting jump; update the Jump conversion logic to handle both
PseudoInstruction::Jump and PseudoInstruction::JumpNoInterrupt (e.g., by
branching on which enum variant and mapping to the same jump emission code path
but preserving the non-interrupt semantics), or alternatively ensure elsewhere
in the emitter that JumpNoInterrupt is never produced — but prefer adding the
explicit conversion for JumpNoInterrupt in the match and conversion block so it
no longer panics.

_ => {}
}
Expand Down Expand Up @@ -277,19 +279,21 @@ impl CodeInfo {

// Convert JUMP pseudo to real instructions (direction depends on offset)
let op = match info.instr {
Instruction::Jump { .. } if target != BlockIdx::NULL => {
Instruction::Pseudo(PseudoInstruction::Jump { .. })
if target != BlockIdx::NULL =>
{
let target_offset = block_to_offset[target.idx()].0;
if target_offset > current_offset {
Instruction::JumpForward {
RealInstruction::JumpForward {
target: Arg::marker(),
}
} else {
Instruction::JumpBackward {
RealInstruction::JumpBackward {
target: Arg::marker(),
}
}
}
other => other,
other => other.expect_real(),
};

let (extras, lo_arg) = info.arg.split();
Expand All @@ -299,7 +303,7 @@ impl CodeInfo {
));
instructions.extend(
extras
.map(|byte| CodeUnit::new(Instruction::ExtendedArg, byte))
.map(|byte| CodeUnit::new(RealInstruction::ExtendedArg, byte))
.chain([CodeUnit { op, arg: lo_arg }]),
);
current_offset += info.arg.instr_size() as u32;
Expand Down
22 changes: 12 additions & 10 deletions crates/compiler-core/src/bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ use malachite_bigint::BigInt;
use num_complex::Complex64;
use rustpython_wtf8::{Wtf8, Wtf8Buf};

pub use crate::bytecode::instruction::Instruction;
pub use crate::bytecode::instruction::{
Instruction, InstructionMetadata, PseudoInstruction, RealInstruction,
};

mod instruction;

Expand Down Expand Up @@ -117,7 +119,7 @@ pub const fn decode_load_super_attr_arg(oparg: u32) -> (u32, bool, bool) {
(name_idx, load_method, has_class)
}

/// Oparg values for [`Instruction::ConvertValue`].
/// Oparg values for [`RealInstruction::ConvertValue`].
///
/// ## See also
///
Expand Down Expand Up @@ -161,7 +163,7 @@ impl fmt::Display for ConvertValueOparg {
Self::Str => "1 (str)",
Self::Repr => "2 (repr)",
Self::Ascii => "3 (ascii)",
// We should never reach this. `FVC_NONE` are being handled by `Instruction::FormatSimple`
// We should never reach this. `FVC_NONE` are being handled by `RealInstruction::FormatSimple`
Self::None => "",
};

Expand Down Expand Up @@ -467,9 +469,9 @@ pub struct OpArgState {

impl OpArgState {
#[inline(always)]
pub fn get(&mut self, ins: CodeUnit) -> (Instruction, OpArg) {
pub fn get(&mut self, ins: CodeUnit) -> (RealInstruction, OpArg) {
let arg = self.extend(ins.arg);
if ins.op != Instruction::ExtendedArg {
if ins.op != RealInstruction::ExtendedArg {
self.reset();
}
(ins.op, arg)
Expand Down Expand Up @@ -605,7 +607,7 @@ impl<T: OpArgType> fmt::Debug for Arg<T> {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
// XXX: if you add a new instruction that stores a Label, make sure to add it in
// Instruction::label_arg
// RealInstruction::label_arg
pub struct Label(pub u32);

impl OpArgType for Label {
Expand Down Expand Up @@ -692,14 +694,14 @@ pub type NameIdx = u32;
#[derive(Copy, Clone)]
#[repr(C)]
pub struct CodeUnit {
pub op: Instruction,
pub op: RealInstruction,
pub arg: OpArgByte,
}

const _: () = assert!(mem::size_of::<CodeUnit>() == 2);

impl CodeUnit {
pub const fn new(op: Instruction, arg: OpArgByte) -> Self {
pub const fn new(op: RealInstruction, arg: OpArgByte) -> Self {
Self { op, arg }
}
}
Expand Down Expand Up @@ -956,9 +958,9 @@ op_arg_enum!(
/// # Examples
///
/// ```rust
/// use rustpython_compiler_core::bytecode::{Arg, BinaryOperator, Instruction};
/// use rustpython_compiler_core::bytecode::{Arg, BinaryOperator, RealInstruction};
/// let (op, _) = Arg::new(BinaryOperator::Add);
/// let instruction = Instruction::BinaryOp { op };
/// let instruction = RealInstruction::BinaryOp { op };
/// ```
///
/// See also:
Expand Down
Loading
Loading