mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Macro for defining opcode & instruction enums (#7573)
* Macro for defining opcode & instruction enums * Convert `Instruction` as well * revert `#[repr(...)]` changes
This commit is contained in:
1 parent
a2c3e65b81
commit
330aa08488
7 files changed
+845
-908
No files matched your search
+101
-223
@@ -8,10 +8,10 @@ use num_traits::{ToPrimitive, Zero};
|
||||
use rustpython_compiler_core::{
|
||||
OneIndexed, SourceLocation,
|
||||
bytecode::{
|
||||
AnyInstruction, Arg, CO_FAST_CELL, CO_FAST_FREE, CO_FAST_HIDDEN, CO_FAST_LOCAL, CodeFlags,
|
||||
CodeObject, CodeUnit, CodeUnits, ConstantData, ExceptionTableEntry, InstrDisplayContext,
|
||||
Instruction, InstructionMetadata, Label, OpArg, PseudoInstruction, PyCodeLocationInfoKind,
|
||||
encode_exception_table, oparg,
|
||||
AnyInstruction, AnyOpcode, CO_FAST_CELL, CO_FAST_FREE, CO_FAST_HIDDEN, CO_FAST_LOCAL,
|
||||
CodeFlags, CodeObject, CodeUnit, CodeUnits, ConstantData, ExceptionTableEntry,
|
||||
InstrDisplayContext, Instruction, InstructionMetadata, Label, OpArg, Opcode,
|
||||
PseudoInstruction, PseudoOpcode, PyCodeLocationInfoKind, encode_exception_table, oparg,
|
||||
},
|
||||
varint::{write_signed_varint, write_varint},
|
||||
};
|
||||
@@ -449,23 +449,15 @@ impl CodeInfo {
|
||||
// Direction must be based on concrete instruction offsets.
|
||||
// Empty blocks can share offsets, so block-order-based resolution
|
||||
// may classify some jumps incorrectly.
|
||||
op = match op {
|
||||
Instruction::JumpForward { .. } if target_offset <= current_offset => {
|
||||
Instruction::JumpBackward {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
op = match op.into() {
|
||||
Opcode::JumpForward if target_offset <= current_offset => {
|
||||
Opcode::JumpBackward.into()
|
||||
}
|
||||
Instruction::JumpBackward { .. } if target_offset > current_offset => {
|
||||
Instruction::JumpForward {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
Opcode::JumpBackward if target_offset > current_offset => {
|
||||
Opcode::JumpForward.into()
|
||||
}
|
||||
Instruction::JumpBackwardNoInterrupt { .. }
|
||||
if target_offset > current_offset =>
|
||||
{
|
||||
Instruction::JumpForward {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
Opcode::JumpBackwardNoInterrupt if target_offset > current_offset => {
|
||||
Opcode::JumpForward.into()
|
||||
}
|
||||
_ => op,
|
||||
};
|
||||
@@ -479,9 +471,8 @@ impl CodeInfo {
|
||||
.expect("END_ASYNC_FOR target must be before instruction");
|
||||
OpArg::new(arg)
|
||||
} else if matches!(
|
||||
op,
|
||||
Instruction::JumpBackward { .. }
|
||||
| Instruction::JumpBackwardNoInterrupt { .. }
|
||||
op.into(),
|
||||
Opcode::JumpBackward | Opcode::JumpBackwardNoInterrupt
|
||||
) {
|
||||
let arg = offset_after
|
||||
.checked_sub(target_offset)
|
||||
@@ -713,10 +704,7 @@ impl CodeInfo {
|
||||
let (const_idx, _) = self.metadata.consts.insert_full(neg_const);
|
||||
// Replace LOAD_CONST/LOAD_SMALL_INT with new LOAD_CONST
|
||||
let load_location = block.instructions[i].location;
|
||||
block.instructions[i].instr = Instruction::LoadConst {
|
||||
consti: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[i].instr = Opcode::LoadConst.into();
|
||||
block.instructions[i].arg = OpArg::new(const_idx as u32);
|
||||
// Replace UNARY_NEGATIVE with NOP, inheriting the LOAD_CONST
|
||||
// location so that remove_nops can clean it up
|
||||
@@ -772,10 +760,7 @@ impl CodeInfo {
|
||||
}
|
||||
let (const_idx, _) = self.metadata.consts.insert_full(result_const);
|
||||
// Replace first instruction with LOAD_CONST result
|
||||
block.instructions[i].instr = Instruction::LoadConst {
|
||||
consti: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[i].instr = Opcode::LoadConst.into();
|
||||
block.instructions[i].arg = OpArg::new(const_idx as u32);
|
||||
// NOP out the second and third instructions
|
||||
let loc = block.instructions[i].location;
|
||||
@@ -982,10 +967,7 @@ impl CodeInfo {
|
||||
let (const_idx, _) = self.metadata.consts.insert_full(ConstantData::Tuple {
|
||||
elements: Vec::new(),
|
||||
});
|
||||
block.instructions[i].instr = Instruction::LoadConst {
|
||||
consti: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[i].instr = Opcode::LoadConst.into();
|
||||
block.instructions[i].arg = OpArg::new(const_idx as u32);
|
||||
i += 1;
|
||||
continue;
|
||||
@@ -1050,10 +1032,7 @@ impl CodeInfo {
|
||||
}
|
||||
|
||||
// Replace BUILD_TUPLE with LOAD_CONST
|
||||
block.instructions[i].instr = Instruction::LoadConst {
|
||||
consti: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[i].instr = Opcode::LoadConst.into();
|
||||
block.instructions[i].arg = OpArg::new(const_idx as u32);
|
||||
|
||||
i += 1;
|
||||
@@ -1123,20 +1102,14 @@ impl CodeInfo {
|
||||
let eh = block.instructions[i].except_handler;
|
||||
|
||||
// slot[start_idx] → BUILD_LIST 0
|
||||
block.instructions[start_idx].instr = Instruction::BuildList {
|
||||
count: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[start_idx].instr = Opcode::BuildList.into();
|
||||
block.instructions[start_idx].arg = OpArg::new(0);
|
||||
block.instructions[start_idx].location = folded_loc;
|
||||
block.instructions[start_idx].end_location = end_loc;
|
||||
block.instructions[start_idx].except_handler = eh;
|
||||
|
||||
// slot[start_idx+1] → LOAD_CONST (tuple)
|
||||
block.instructions[start_idx + 1].instr = Instruction::LoadConst {
|
||||
consti: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[start_idx + 1].instr = Opcode::LoadConst.into();
|
||||
block.instructions[start_idx + 1].arg = OpArg::new(const_idx as u32);
|
||||
block.instructions[start_idx + 1].location = folded_loc;
|
||||
block.instructions[start_idx + 1].end_location = end_loc;
|
||||
@@ -1149,7 +1122,7 @@ impl CodeInfo {
|
||||
}
|
||||
|
||||
// slot[i] (was BUILD_LIST) → LIST_EXTEND 1
|
||||
block.instructions[i].instr = Instruction::ListExtend { i: Arg::marker() }.into();
|
||||
block.instructions[i].instr = Opcode::ListExtend.into();
|
||||
block.instructions[i].arg = OpArg::new(1);
|
||||
|
||||
i += 1;
|
||||
@@ -1239,20 +1212,14 @@ impl CodeInfo {
|
||||
block.instructions[j].location = folded_loc;
|
||||
}
|
||||
|
||||
block.instructions[i].instr = Instruction::LoadConst {
|
||||
consti: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[i].instr = Opcode::LoadConst.into();
|
||||
block.instructions[i].arg = OpArg::new(const_idx as u32);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
block.instructions[i].instr = Instruction::BuildTuple {
|
||||
count: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[i].instr = Opcode::BuildTuple.into();
|
||||
i += 2;
|
||||
} else {
|
||||
i += 1;
|
||||
@@ -1323,19 +1290,13 @@ impl CodeInfo {
|
||||
let end_loc = block.instructions[i].end_location;
|
||||
let eh = block.instructions[i].except_handler;
|
||||
|
||||
block.instructions[start_idx].instr = Instruction::BuildSet {
|
||||
count: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[start_idx].instr = Opcode::BuildSet.into();
|
||||
block.instructions[start_idx].arg = OpArg::new(0);
|
||||
block.instructions[start_idx].location = folded_loc;
|
||||
block.instructions[start_idx].end_location = end_loc;
|
||||
block.instructions[start_idx].except_handler = eh;
|
||||
|
||||
block.instructions[start_idx + 1].instr = Instruction::LoadConst {
|
||||
consti: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[start_idx + 1].instr = Opcode::LoadConst.into();
|
||||
block.instructions[start_idx + 1].arg = OpArg::new(const_idx as u32);
|
||||
block.instructions[start_idx + 1].location = folded_loc;
|
||||
block.instructions[start_idx + 1].end_location = end_loc;
|
||||
@@ -1346,7 +1307,7 @@ impl CodeInfo {
|
||||
block.instructions[j].location = folded_loc;
|
||||
}
|
||||
|
||||
block.instructions[i].instr = Instruction::SetUpdate { i: Arg::marker() }.into();
|
||||
block.instructions[i].instr = Opcode::SetUpdate.into();
|
||||
block.instructions[i].arg = OpArg::new(1);
|
||||
|
||||
i += 1;
|
||||
@@ -1377,16 +1338,15 @@ impl CodeInfo {
|
||||
}
|
||||
match n {
|
||||
1 => {
|
||||
instructions[i].instr = AnyInstruction::Real(Instruction::Nop);
|
||||
instructions[i].instr = Opcode::Nop.into();
|
||||
instructions[i].arg = OpArg::new(0);
|
||||
instructions[i + 1].instr = AnyInstruction::Real(Instruction::Nop);
|
||||
instructions[i + 1].instr = Opcode::Nop.into();
|
||||
instructions[i + 1].arg = OpArg::new(0);
|
||||
}
|
||||
2 | 3 => {
|
||||
instructions[i].instr = AnyInstruction::Real(Instruction::Nop);
|
||||
instructions[i].instr = Opcode::Nop.into();
|
||||
instructions[i].arg = OpArg::new(0);
|
||||
instructions[i + 1].instr =
|
||||
AnyInstruction::Real(Instruction::Swap { i: Arg::marker() });
|
||||
instructions[i + 1].instr = Opcode::Swap.into();
|
||||
instructions[i + 1].arg = OpArg::new(n);
|
||||
}
|
||||
_ => {}
|
||||
@@ -1409,15 +1369,15 @@ impl CodeInfo {
|
||||
/// Instruction classes that are safe to reorder around SWAP.
|
||||
fn is_swappable(instr: &AnyInstruction) -> bool {
|
||||
matches!(
|
||||
instr,
|
||||
AnyInstruction::Real(Instruction::StoreFast { .. } | Instruction::PopTop)
|
||||
(*instr).into(),
|
||||
AnyOpcode::Real(Opcode::StoreFast | Opcode::PopTop)
|
||||
)
|
||||
}
|
||||
|
||||
/// Variable index that a STORE_FAST writes to, or None.
|
||||
fn stores_to(info: &InstructionInfo) -> Option<u32> {
|
||||
match info.instr {
|
||||
AnyInstruction::Real(Instruction::StoreFast { .. }) => Some(u32::from(info.arg)),
|
||||
match info.instr.into() {
|
||||
AnyOpcode::Real(Opcode::StoreFast) => Some(u32::from(info.arg)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -1517,7 +1477,7 @@ impl CodeInfo {
|
||||
}
|
||||
}
|
||||
// Safe to reorder. SWAP -> NOP, swap j and k.
|
||||
instructions[i].instr = AnyInstruction::Real(Instruction::Nop);
|
||||
instructions[i].instr = Opcode::Nop.into();
|
||||
instructions[i].arg = OpArg::new(0);
|
||||
instructions.swap(j, k);
|
||||
i += 1;
|
||||
@@ -1544,10 +1504,8 @@ impl CodeInfo {
|
||||
while i < len {
|
||||
// Look for UNPACK_SEQUENCE or UNPACK_EX
|
||||
let is_unpack = matches!(
|
||||
instructions[i].instr,
|
||||
AnyInstruction::Real(
|
||||
Instruction::UnpackSequence { .. } | Instruction::UnpackEx { .. }
|
||||
)
|
||||
instructions[i].instr.into(),
|
||||
AnyOpcode::Real(Opcode::UnpackSequence | Opcode::UnpackEx)
|
||||
);
|
||||
if !is_unpack {
|
||||
i += 1;
|
||||
@@ -1558,8 +1516,8 @@ impl CodeInfo {
|
||||
let mut run_end = run_start;
|
||||
while run_end < len
|
||||
&& matches!(
|
||||
instructions[run_end].instr,
|
||||
AnyInstruction::Real(Instruction::StoreFast { .. })
|
||||
instructions[run_end].instr.into(),
|
||||
AnyOpcode::Real(Opcode::StoreFast)
|
||||
)
|
||||
{
|
||||
run_end += 1;
|
||||
@@ -1574,7 +1532,7 @@ impl CodeInfo {
|
||||
for (j, instr) in instructions[run_start..run_end].iter_mut().enumerate() {
|
||||
let idx = u32::from(instr.arg);
|
||||
if last_occurrence[&idx] != j {
|
||||
instr.instr = AnyInstruction::Real(Instruction::PopTop);
|
||||
instr.instr = Opcode::PopTop.into();
|
||||
instr.arg = OpArg::new(0);
|
||||
}
|
||||
}
|
||||
@@ -1600,9 +1558,9 @@ impl CodeInfo {
|
||||
};
|
||||
|
||||
if matches!(
|
||||
next_instr,
|
||||
Instruction::PopJumpIfFalse { .. } | Instruction::PopJumpIfTrue { .. }
|
||||
) && matches!(curr_instr, Instruction::CompareOp { .. })
|
||||
next_instr.into(),
|
||||
Opcode::PopJumpIfFalse | Opcode::PopJumpIfTrue
|
||||
) && matches!(curr_instr.into(), Opcode::CompareOp)
|
||||
{
|
||||
block.instructions[i].arg = OpArg::new(
|
||||
u32::from(block.instructions[i].arg) | oparg::COMPARE_OP_BOOL_MASK,
|
||||
@@ -1624,12 +1582,7 @@ impl CodeInfo {
|
||||
let idx2 = u32::from(next.arg);
|
||||
if idx1 < 16 && idx2 < 16 {
|
||||
let packed = (idx1 << 4) | idx2;
|
||||
Some((
|
||||
Instruction::LoadFastLoadFast {
|
||||
var_nums: Arg::marker(),
|
||||
},
|
||||
OpArg::new(packed),
|
||||
))
|
||||
Some((Opcode::LoadFastLoadFast.into(), OpArg::new(packed)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -1649,12 +1602,7 @@ impl CodeInfo {
|
||||
let idx2 = u32::from(next.arg);
|
||||
if idx1 < 16 && idx2 < 16 {
|
||||
let packed = (idx1 << 4) | idx2;
|
||||
Some((
|
||||
Instruction::StoreFastStoreFast {
|
||||
var_nums: Arg::marker(),
|
||||
},
|
||||
OpArg::new(packed),
|
||||
))
|
||||
Some((Opcode::StoreFastStoreFast.into(), OpArg::new(packed)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -1688,12 +1636,7 @@ impl CodeInfo {
|
||||
.metadata
|
||||
.consts
|
||||
.insert_full(ConstantData::Boolean { value: !value });
|
||||
Some((
|
||||
(Instruction::LoadConst {
|
||||
consti: Arg::marker(),
|
||||
}),
|
||||
OpArg::new(const_idx as u32),
|
||||
))
|
||||
Some(((Opcode::LoadConst.into()), OpArg::new(const_idx as u32)))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
@@ -1767,7 +1710,7 @@ impl CodeInfo {
|
||||
// LOAD_SMALL_INT oparg is unsigned, so only 0..=255 can be encoded
|
||||
if let Some(small) = value.to_i32().filter(|v| (0..=255).contains(v)) {
|
||||
// Convert LOAD_CONST to LOAD_SMALL_INT
|
||||
instr.instr = Instruction::LoadSmallInt { i: Arg::marker() }.into();
|
||||
instr.instr = Opcode::LoadSmallInt.into();
|
||||
// The arg is the i32 value stored as u32 (two's complement)
|
||||
instr.arg = OpArg::new(small as u32);
|
||||
}
|
||||
@@ -1883,10 +1826,7 @@ impl CodeInfo {
|
||||
let idx2 = u32::from(next.arg);
|
||||
if idx1 < 16 && idx2 < 16 {
|
||||
let packed = (idx1 << 4) | idx2;
|
||||
block.instructions[i].instr = Instruction::StoreFastLoadFast {
|
||||
var_nums: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[i].instr = Opcode::StoreFastLoadFast.into();
|
||||
block.instructions[i].arg = OpArg::new(packed);
|
||||
// Replace second instruction with NOP (CPython: INSTR_SET_OP0(inst2, NOP))
|
||||
set_to_nop(&mut block.instructions[i + 1]);
|
||||
@@ -1943,8 +1883,8 @@ impl CodeInfo {
|
||||
}
|
||||
|
||||
// Push values to stack with source instruction index
|
||||
let source = match instr {
|
||||
Instruction::LoadFast { .. } | Instruction::LoadFastLoadFast { .. } => i,
|
||||
let source = match instr.into() {
|
||||
Opcode::LoadFast | Opcode::LoadFastLoadFast => i,
|
||||
_ => NOT_LOCAL,
|
||||
};
|
||||
for _ in 0..pushes {
|
||||
@@ -1971,18 +1911,12 @@ impl CodeInfo {
|
||||
let Some(instr) = info.instr.real() else {
|
||||
continue;
|
||||
};
|
||||
match instr {
|
||||
Instruction::LoadFast { .. } => {
|
||||
info.instr = Instruction::LoadFastBorrow {
|
||||
var_num: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
match instr.into() {
|
||||
Opcode::LoadFast => {
|
||||
info.instr = Opcode::LoadFastBorrow.into();
|
||||
}
|
||||
Instruction::LoadFastLoadFast { .. } => {
|
||||
info.instr = Instruction::LoadFastBorrowLoadFastBorrow {
|
||||
var_nums: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
Opcode::LoadFastLoadFast => {
|
||||
info.instr = Opcode::LoadFastBorrowLoadFastBorrow.into();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -2078,10 +2012,7 @@ impl CodeInfo {
|
||||
Some(Instruction::LoadFast { var_num }) => {
|
||||
let var_idx = usize::from(var_num.get(info.arg));
|
||||
if var_idx < nlocals && unsafe_mask[var_idx] {
|
||||
info.instr = Instruction::LoadFastCheck {
|
||||
var_num: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
info.instr = Opcode::LoadFastCheck.into();
|
||||
changed = true;
|
||||
}
|
||||
if var_idx < nlocals {
|
||||
@@ -2099,28 +2030,19 @@ impl CodeInfo {
|
||||
if needs_check_1 || needs_check_2 {
|
||||
let mut first = info;
|
||||
first.instr = if needs_check_1 {
|
||||
Instruction::LoadFastCheck {
|
||||
var_num: Arg::marker(),
|
||||
}
|
||||
Opcode::LoadFastCheck
|
||||
} else {
|
||||
Instruction::LoadFast {
|
||||
var_num: Arg::marker(),
|
||||
}
|
||||
Opcode::LoadFast
|
||||
}
|
||||
.into();
|
||||
first.arg = OpArg::new(idx1 as u32);
|
||||
|
||||
let mut second = info;
|
||||
second.instr = if needs_check_2 {
|
||||
Instruction::LoadFastCheck {
|
||||
var_num: Arg::marker(),
|
||||
}
|
||||
Opcode::LoadFastCheck.into()
|
||||
} else {
|
||||
Instruction::LoadFast {
|
||||
var_num: Arg::marker(),
|
||||
}
|
||||
}
|
||||
.into();
|
||||
Opcode::LoadFast.into()
|
||||
};
|
||||
second.arg = OpArg::new(idx2 as u32);
|
||||
|
||||
new_instructions.push(first);
|
||||
@@ -2615,10 +2537,7 @@ fn push_cold_blocks_to_end(blocks: &mut Vec<Block>) {
|
||||
..Block::default()
|
||||
};
|
||||
jump_block.instructions.push(InstructionInfo {
|
||||
instr: PseudoInstruction::JumpNoInterrupt {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
.into(),
|
||||
instr: PseudoOpcode::JumpNoInterrupt.into(),
|
||||
arg: OpArg::new(0),
|
||||
target: warm_next,
|
||||
location: SourceLocation::default(),
|
||||
@@ -2737,16 +2656,13 @@ enum JumpThreadKind {
|
||||
}
|
||||
|
||||
fn jump_thread_kind(instr: AnyInstruction) -> Option<JumpThreadKind> {
|
||||
match instr {
|
||||
AnyInstruction::Pseudo(PseudoInstruction::Jump { .. })
|
||||
| AnyInstruction::Real(Instruction::JumpForward { .. })
|
||||
| AnyInstruction::Real(Instruction::JumpBackward { .. }) => Some(JumpThreadKind::Plain),
|
||||
AnyInstruction::Pseudo(PseudoInstruction::JumpNoInterrupt { .. })
|
||||
| AnyInstruction::Real(Instruction::JumpBackwardNoInterrupt { .. }) => {
|
||||
Some(JumpThreadKind::NoInterrupt)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
Some(match instr.into() {
|
||||
AnyOpcode::Pseudo(PseudoOpcode::Jump)
|
||||
| AnyOpcode::Real(Opcode::JumpForward | Opcode::JumpBackward) => JumpThreadKind::Plain,
|
||||
AnyOpcode::Pseudo(PseudoOpcode::JumpNoInterrupt)
|
||||
| AnyOpcode::Real(Opcode::JumpBackwardNoInterrupt) => JumpThreadKind::NoInterrupt,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
fn threaded_jump_instr(
|
||||
@@ -2763,19 +2679,11 @@ fn threaded_jump_instr(
|
||||
if source_kind == JumpThreadKind::NoInterrupt {
|
||||
return Some(source);
|
||||
}
|
||||
Some(match source {
|
||||
AnyInstruction::Pseudo(_) => PseudoInstruction::Jump {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
.into(),
|
||||
AnyInstruction::Real(Instruction::JumpBackwardNoInterrupt { .. }) => {
|
||||
Instruction::JumpBackward {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
AnyInstruction::Real(Instruction::JumpForward { .. })
|
||||
| AnyInstruction::Real(Instruction::JumpBackward { .. }) => source,
|
||||
|
||||
Some(match source.into() {
|
||||
AnyOpcode::Pseudo(_) => PseudoOpcode::Jump.into(),
|
||||
AnyOpcode::Real(Opcode::JumpBackwardNoInterrupt) => Opcode::JumpBackward.into(),
|
||||
AnyOpcode::Real(Opcode::JumpForward | Opcode::JumpBackward) => source,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@@ -2852,35 +2760,23 @@ fn jump_threading_impl(blocks: &mut [Block], include_conditional: bool) {
|
||||
|
||||
fn is_conditional_jump(instr: &AnyInstruction) -> bool {
|
||||
matches!(
|
||||
instr.real(),
|
||||
instr.real().map(Into::into),
|
||||
Some(
|
||||
Instruction::PopJumpIfFalse { .. }
|
||||
| Instruction::PopJumpIfTrue { .. }
|
||||
| Instruction::PopJumpIfNone { .. }
|
||||
| Instruction::PopJumpIfNotNone { .. }
|
||||
Opcode::PopJumpIfFalse
|
||||
| Opcode::PopJumpIfTrue
|
||||
| Opcode::PopJumpIfNone
|
||||
| Opcode::PopJumpIfNotNone
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/// Invert a conditional jump opcode.
|
||||
fn reversed_conditional(instr: &AnyInstruction) -> Option<AnyInstruction> {
|
||||
Some(match instr.real()? {
|
||||
Instruction::PopJumpIfFalse { .. } => Instruction::PopJumpIfTrue {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
.into(),
|
||||
Instruction::PopJumpIfTrue { .. } => Instruction::PopJumpIfFalse {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
.into(),
|
||||
Instruction::PopJumpIfNone { .. } => Instruction::PopJumpIfNotNone {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
.into(),
|
||||
Instruction::PopJumpIfNotNone { .. } => Instruction::PopJumpIfNone {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
.into(),
|
||||
Some(match AnyOpcode::from(*instr).real()? {
|
||||
Opcode::PopJumpIfFalse => Opcode::PopJumpIfTrue.into(),
|
||||
Opcode::PopJumpIfTrue => Opcode::PopJumpIfFalse.into(),
|
||||
Opcode::PopJumpIfNone => Opcode::PopJumpIfNotNone.into(),
|
||||
Opcode::PopJumpIfNotNone => Opcode::PopJumpIfNone.into(),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@@ -2928,7 +2824,7 @@ fn normalize_jumps(blocks: &mut Vec<Block>) {
|
||||
if is_forward {
|
||||
// Insert NOT_TAKEN after forward conditional jump
|
||||
let not_taken = InstructionInfo {
|
||||
instr: Instruction::NotTaken.into(),
|
||||
instr: Opcode::NotTaken.into(),
|
||||
arg: OpArg::new(0),
|
||||
target: BlockIdx::NULL,
|
||||
location: last_ins.location,
|
||||
@@ -2957,7 +2853,7 @@ fn normalize_jumps(blocks: &mut Vec<Block>) {
|
||||
..Block::default()
|
||||
};
|
||||
new_block.instructions.push(InstructionInfo {
|
||||
instr: Instruction::NotTaken.into(),
|
||||
instr: Opcode::NotTaken.into(),
|
||||
arg: OpArg::new(0),
|
||||
target: BlockIdx::NULL,
|
||||
location: loc,
|
||||
@@ -2967,10 +2863,7 @@ fn normalize_jumps(blocks: &mut Vec<Block>) {
|
||||
cache_entries: 0,
|
||||
});
|
||||
new_block.instructions.push(InstructionInfo {
|
||||
instr: PseudoInstruction::Jump {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
.into(),
|
||||
instr: PseudoOpcode::Jump.into(),
|
||||
arg: OpArg::new(0),
|
||||
target,
|
||||
location: loc,
|
||||
@@ -3019,34 +2912,22 @@ fn normalize_jumps(blocks: &mut Vec<Block>) {
|
||||
continue;
|
||||
}
|
||||
let target_pos = block_order[target.idx()];
|
||||
info.instr = match info.instr {
|
||||
AnyInstruction::Pseudo(PseudoInstruction::Jump { .. }) => {
|
||||
info.instr = match info.instr.into() {
|
||||
AnyOpcode::Pseudo(PseudoOpcode::Jump) => {
|
||||
if target_pos > source_pos {
|
||||
Instruction::JumpForward {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
.into()
|
||||
Opcode::JumpForward.into()
|
||||
} else {
|
||||
Instruction::JumpBackward {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
.into()
|
||||
Opcode::JumpBackward.into()
|
||||
}
|
||||
}
|
||||
AnyInstruction::Pseudo(PseudoInstruction::JumpNoInterrupt { .. }) => {
|
||||
AnyOpcode::Pseudo(PseudoOpcode::JumpNoInterrupt) => {
|
||||
if target_pos > source_pos {
|
||||
Instruction::JumpForward {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
.into()
|
||||
Opcode::JumpForward.into()
|
||||
} else {
|
||||
Instruction::JumpBackwardNoInterrupt {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
.into()
|
||||
Opcode::JumpBackwardNoInterrupt.into()
|
||||
}
|
||||
}
|
||||
other => other,
|
||||
_ => info.instr,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -4105,10 +3986,7 @@ pub(crate) fn convert_pseudo_ops(blocks: &mut [Block], cellfixedoffsets: &[u32])
|
||||
let cell_relative = i.get(info.arg) as usize;
|
||||
let new_idx = cellfixedoffsets[cell_relative];
|
||||
info.arg = OpArg::new(new_idx);
|
||||
info.instr = Instruction::LoadFast {
|
||||
var_num: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
info.instr = Opcode::LoadFast.into();
|
||||
}
|
||||
// Jump pseudo ops are resolved during block linearization
|
||||
PseudoInstruction::Jump { .. } | PseudoInstruction::JumpNoInterrupt { .. } => {}
|
||||
@@ -4160,12 +4038,12 @@ pub(crate) fn fixup_deref_opargs(blocks: &mut [Block], cellfixedoffsets: &[u32])
|
||||
continue;
|
||||
};
|
||||
let needs_fixup = matches!(
|
||||
instr,
|
||||
Instruction::LoadDeref { .. }
|
||||
| Instruction::StoreDeref { .. }
|
||||
| Instruction::DeleteDeref { .. }
|
||||
| Instruction::LoadFromDictOrDeref { .. }
|
||||
| Instruction::MakeCell { .. }
|
||||
instr.into(),
|
||||
Opcode::LoadDeref
|
||||
| Opcode::StoreDeref
|
||||
| Opcode::DeleteDeref
|
||||
| Opcode::LoadFromDictOrDeref
|
||||
| Opcode::MakeCell
|
||||
);
|
||||
if needs_fixup {
|
||||
let cell_relative = u32::from(info.arg) as usize;
|
||||
|
||||
@@ -21,7 +21,8 @@ use rustpython_wtf8::{Wtf8, Wtf8Buf};
|
||||
|
||||
pub use crate::bytecode::{
|
||||
instruction::{
|
||||
AnyInstruction, Arg, Instruction, InstructionMetadata, PseudoInstruction, StackEffect,
|
||||
AnyInstruction, AnyOpcode, Arg, Instruction, InstructionMetadata, Opcode,
|
||||
PseudoInstruction, PseudoOpcode, StackEffect,
|
||||
},
|
||||
oparg::{
|
||||
BinaryOperator, BuildSliceArgCount, CommonConstant, ComparisonOperator, ConvertValueOparg,
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+70
-152
@@ -5,152 +5,23 @@ mod _opcode {
|
||||
use crate::vm::{
|
||||
AsObject, PyObjectRef, PyResult, VirtualMachine,
|
||||
builtins::{PyInt, PyIntRef},
|
||||
bytecode::{AnyInstruction, Instruction, InstructionMetadata, PseudoInstruction},
|
||||
bytecode::{AnyInstruction, AnyOpcode, InstructionMetadata, Opcode, PseudoOpcode},
|
||||
};
|
||||
use core::ops::Deref;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Opcode(AnyInstruction);
|
||||
|
||||
impl Deref for Opcode {
|
||||
type Target = AnyInstruction;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
fn try_from_i32(raw: i32) -> Result<AnyOpcode, ()> {
|
||||
u16::try_from(raw)
|
||||
.map_err(|_| ())?
|
||||
.try_into()
|
||||
.map_err(|_| ())
|
||||
}
|
||||
|
||||
impl TryFrom<i32> for Opcode {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
Ok(Self(
|
||||
u16::try_from(value)
|
||||
.map_err(|_| ())?
|
||||
.try_into()
|
||||
.map_err(|_| ())?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Opcode {
|
||||
// https://github.com/python/cpython/blob/v3.14.2/Include/opcode_ids.h#L252
|
||||
const HAVE_ARGUMENT: i32 = 43;
|
||||
|
||||
pub fn try_from_pyint(raw: PyIntRef, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
let instruction = raw
|
||||
.try_to_primitive::<u16>(vm)
|
||||
.and_then(|v| {
|
||||
AnyInstruction::try_from(v).map_err(|_| {
|
||||
vm.new_exception_empty(vm.ctx.exceptions.value_error.to_owned())
|
||||
})
|
||||
})
|
||||
.map_err(|_| vm.new_value_error("invalid opcode or oparg"))?;
|
||||
|
||||
Ok(Self(instruction))
|
||||
}
|
||||
|
||||
const fn inner(self) -> AnyInstruction {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Check if opcode is valid (can be converted to an AnyInstruction)
|
||||
#[must_use]
|
||||
pub fn is_valid(opcode: i32) -> bool {
|
||||
Self::try_from(opcode).is_ok()
|
||||
}
|
||||
|
||||
/// Check if instruction has an argument
|
||||
#[must_use]
|
||||
pub fn has_arg(opcode: i32) -> bool {
|
||||
Self::is_valid(opcode) && opcode > Self::HAVE_ARGUMENT
|
||||
}
|
||||
|
||||
/// Check if instruction uses co_consts
|
||||
#[must_use]
|
||||
pub fn has_const(opcode: i32) -> bool {
|
||||
matches!(
|
||||
Self::try_from(opcode).map(|op| op.inner()),
|
||||
Ok(AnyInstruction::Real(Instruction::LoadConst { .. }))
|
||||
)
|
||||
}
|
||||
|
||||
/// Check if instruction uses co_names
|
||||
#[must_use]
|
||||
pub fn has_name(opcode: i32) -> bool {
|
||||
matches!(
|
||||
Self::try_from(opcode).map(|op| op.inner()),
|
||||
Ok(AnyInstruction::Real(
|
||||
Instruction::DeleteAttr { .. }
|
||||
| Instruction::DeleteGlobal { .. }
|
||||
| Instruction::DeleteName { .. }
|
||||
| Instruction::ImportFrom { .. }
|
||||
| Instruction::ImportName { .. }
|
||||
| Instruction::LoadAttr { .. }
|
||||
| Instruction::LoadGlobal { .. }
|
||||
| Instruction::LoadName { .. }
|
||||
| Instruction::StoreAttr { .. }
|
||||
| Instruction::StoreGlobal { .. }
|
||||
| Instruction::StoreName { .. }
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
/// Check if instruction is a jump
|
||||
#[must_use]
|
||||
pub fn has_jump(opcode: i32) -> bool {
|
||||
matches!(
|
||||
Self::try_from(opcode).map(|op| op.inner()),
|
||||
Ok(AnyInstruction::Real(
|
||||
Instruction::ForIter { .. }
|
||||
| Instruction::PopJumpIfFalse { .. }
|
||||
| Instruction::PopJumpIfTrue { .. }
|
||||
| Instruction::Send { .. }
|
||||
) | AnyInstruction::Pseudo(PseudoInstruction::Jump { .. }))
|
||||
)
|
||||
}
|
||||
|
||||
/// Check if instruction uses co_freevars/co_cellvars
|
||||
#[must_use]
|
||||
pub fn has_free(opcode: i32) -> bool {
|
||||
matches!(
|
||||
Self::try_from(opcode).map(|op| op.inner()),
|
||||
Ok(AnyInstruction::Real(
|
||||
Instruction::DeleteDeref { .. }
|
||||
| Instruction::LoadFromDictOrDeref { .. }
|
||||
| Instruction::LoadDeref { .. }
|
||||
| Instruction::StoreDeref { .. }
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
/// Check if instruction uses co_varnames (local variables)
|
||||
#[must_use]
|
||||
pub fn has_local(opcode: i32) -> bool {
|
||||
matches!(
|
||||
Self::try_from(opcode).map(|op| op.inner()),
|
||||
Ok(AnyInstruction::Real(
|
||||
Instruction::DeleteFast { .. }
|
||||
| Instruction::LoadFast { .. }
|
||||
| Instruction::LoadFastAndClear { .. }
|
||||
| Instruction::StoreFast { .. }
|
||||
| Instruction::StoreFastLoadFast { .. }
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
/// Check if instruction has exception info
|
||||
#[must_use]
|
||||
pub fn has_exc(_opcode: i32) -> bool {
|
||||
// No instructions have exception info in RustPython
|
||||
// (exception handling is done via exception table)
|
||||
false
|
||||
}
|
||||
}
|
||||
// https://github.com/python/cpython/blob/v3.14.2/Include/opcode_ids.h#L252
|
||||
const HAVE_ARGUMENT: i32 = 43;
|
||||
|
||||
// prepare specialization
|
||||
#[pyattr]
|
||||
const ENABLE_SPECIALIZATION: i8 = 1;
|
||||
|
||||
#[pyattr]
|
||||
const ENABLE_SPECIALIZATION_FT: i8 = 1;
|
||||
|
||||
@@ -199,62 +70,109 @@ mod _opcode {
|
||||
None => None,
|
||||
};
|
||||
|
||||
let opcode = Opcode::try_from_pyint(args.opcode, vm)?;
|
||||
let instr = args
|
||||
.opcode
|
||||
.try_to_primitive::<u16>(vm)
|
||||
.and_then(|v| {
|
||||
AnyInstruction::try_from(v)
|
||||
.map_err(|_| vm.new_exception_empty(vm.ctx.exceptions.value_error.to_owned()))
|
||||
})
|
||||
.map_err(|_| vm.new_value_error("invalid opcode or oparg"))?;
|
||||
|
||||
// Raise ValueError if specialized.
|
||||
if opcode.inner().real().is_some_and(|op| op.deopt().is_some()) {
|
||||
if instr.real().is_some_and(|op| op.deopt().is_some()) {
|
||||
return Err(vm.new_value_error("invalid opcode or oparg"));
|
||||
}
|
||||
|
||||
let effect = match jump {
|
||||
Some(true) => opcode.stack_effect_jump(oparg),
|
||||
Some(false) => opcode.stack_effect(oparg),
|
||||
Some(true) => instr.stack_effect_jump(oparg),
|
||||
Some(false) => instr.stack_effect(oparg),
|
||||
// jump=None: max of both paths (CPython convention)
|
||||
None => opcode
|
||||
None => instr
|
||||
.stack_effect(oparg)
|
||||
.max(opcode.stack_effect_jump(oparg)),
|
||||
.max(instr.stack_effect_jump(oparg)),
|
||||
};
|
||||
Ok(effect)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn is_valid(opcode: i32) -> bool {
|
||||
Opcode::is_valid(opcode)
|
||||
try_from_i32(opcode).is_ok()
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn has_arg(opcode: i32) -> bool {
|
||||
Opcode::has_arg(opcode)
|
||||
try_from_i32(opcode).is_ok_and(|_| opcode > HAVE_ARGUMENT)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn has_const(opcode: i32) -> bool {
|
||||
Opcode::has_const(opcode)
|
||||
matches!(try_from_i32(opcode), Ok(AnyOpcode::Real(Opcode::LoadConst)))
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn has_name(opcode: i32) -> bool {
|
||||
Opcode::has_name(opcode)
|
||||
matches!(
|
||||
try_from_i32(opcode),
|
||||
Ok(AnyOpcode::Real(
|
||||
Opcode::DeleteAttr
|
||||
| Opcode::DeleteGlobal
|
||||
| Opcode::DeleteName
|
||||
| Opcode::ImportFrom
|
||||
| Opcode::ImportName
|
||||
| Opcode::LoadAttr
|
||||
| Opcode::LoadGlobal
|
||||
| Opcode::LoadName
|
||||
| Opcode::StoreAttr
|
||||
| Opcode::StoreGlobal
|
||||
| Opcode::StoreName
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn has_jump(opcode: i32) -> bool {
|
||||
Opcode::has_jump(opcode)
|
||||
matches!(
|
||||
try_from_i32(opcode),
|
||||
Ok(AnyOpcode::Real(
|
||||
Opcode::ForIter | Opcode::PopJumpIfFalse | Opcode::PopJumpIfTrue | Opcode::Send
|
||||
) | AnyOpcode::Pseudo(PseudoOpcode::Jump))
|
||||
)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn has_free(opcode: i32) -> bool {
|
||||
Opcode::has_free(opcode)
|
||||
matches!(
|
||||
try_from_i32(opcode),
|
||||
Ok(AnyOpcode::Real(
|
||||
Opcode::DeleteDeref
|
||||
| Opcode::LoadFromDictOrDeref
|
||||
| Opcode::LoadDeref
|
||||
| Opcode::StoreDeref
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn has_local(opcode: i32) -> bool {
|
||||
Opcode::has_local(opcode)
|
||||
matches!(
|
||||
try_from_i32(opcode),
|
||||
Ok(AnyOpcode::Real(
|
||||
Opcode::DeleteFast
|
||||
| Opcode::LoadFast
|
||||
| Opcode::LoadFastAndClear
|
||||
| Opcode::StoreFast
|
||||
| Opcode::StoreFastLoadFast
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn has_exc(opcode: i32) -> bool {
|
||||
Opcode::has_exc(opcode)
|
||||
// No instructions have exception info in RustPython
|
||||
// (exception handling is done via exception table)
|
||||
let _ = opcode;
|
||||
false
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
|
||||
+9
-16
@@ -22,7 +22,7 @@ use crate::{
|
||||
tuple::{PyTuple, PyTupleIterator, PyTupleRef},
|
||||
},
|
||||
bytecode::{
|
||||
self, ADAPTIVE_COOLDOWN_VALUE, Arg, Instruction, LoadAttr, LoadSuperAttr, SpecialMethod,
|
||||
self, ADAPTIVE_COOLDOWN_VALUE, Instruction, LoadAttr, LoadSuperAttr, Opcode, SpecialMethod,
|
||||
},
|
||||
convert::{ToPyObject, ToPyResult},
|
||||
coroutine::Coro,
|
||||
@@ -1487,10 +1487,8 @@ impl ExecutingFrame<'_> {
|
||||
// When neither is enabled, prev_line is stale but unused.
|
||||
if vm.use_tracing.get() {
|
||||
if !matches!(
|
||||
op,
|
||||
Instruction::Resume { .. }
|
||||
| Instruction::ExtendedArg
|
||||
| Instruction::InstrumentedLine
|
||||
op.into(),
|
||||
Opcode::Resume | Opcode::ExtendedArg | Opcode::InstrumentedLine
|
||||
) && let Some((loc, _)) = self.code.locations.get(idx)
|
||||
{
|
||||
*self.prev_line = loc.line.get() as u32;
|
||||
@@ -1502,10 +1500,8 @@ impl ExecutingFrame<'_> {
|
||||
if !vm.is_none(&self.object.trace.lock())
|
||||
&& *self.object.trace_opcodes.lock()
|
||||
&& !matches!(
|
||||
op,
|
||||
Instruction::Resume { .. }
|
||||
| Instruction::InstrumentedResume
|
||||
| Instruction::ExtendedArg
|
||||
op.into(),
|
||||
Opcode::Resume | Opcode::InstrumentedResume | Opcode::ExtendedArg
|
||||
)
|
||||
{
|
||||
vm.trace_event(crate::protocol::TraceEvent::Opcode, None)?;
|
||||
@@ -1739,8 +1735,8 @@ impl ExecutingFrame<'_> {
|
||||
if lasti > 0
|
||||
&& let Some(prev_unit) = self.code.instructions.get(lasti - 1)
|
||||
&& matches!(
|
||||
&prev_unit.op,
|
||||
Instruction::YieldValue { .. } | Instruction::InstrumentedYieldValue
|
||||
&prev_unit.op.into(),
|
||||
Opcode::YieldValue | Opcode::InstrumentedYieldValue
|
||||
)
|
||||
{
|
||||
// YIELD_VALUE arg: 0 = direct yield, >= 1 = yield-from/await
|
||||
@@ -8056,7 +8052,7 @@ impl ExecutingFrame<'_> {
|
||||
cache_base: usize,
|
||||
left: &PyObject,
|
||||
) -> Option<usize> {
|
||||
let next_idx = cache_base + Instruction::BinaryOp { op: Arg::marker() }.cache_entries();
|
||||
let next_idx = cache_base + Instruction::from(Opcode::BinaryOp).cache_entries();
|
||||
let unit = self.code.instructions.get(next_idx)?;
|
||||
let next_op = unit.op.to_base().unwrap_or(unit.op);
|
||||
if !matches!(next_op, Instruction::StoreFast { .. }) {
|
||||
@@ -8933,10 +8929,7 @@ impl ExecutingFrame<'_> {
|
||||
fn for_iter_has_end_for_shape(&self, instr_idx: usize, jump_delta: u32) -> bool {
|
||||
let target_idx = instr_idx
|
||||
+ 1
|
||||
+ Instruction::ForIter {
|
||||
delta: Arg::marker(),
|
||||
}
|
||||
.cache_entries()
|
||||
+ Instruction::from(Opcode::ForIter).cache_entries()
|
||||
+ jump_delta as usize;
|
||||
self.code.instructions.get(target_idx).is_some_and(|unit| {
|
||||
matches!(
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::{
|
||||
object, pystr,
|
||||
type_::PyAttributes,
|
||||
},
|
||||
bytecode::{self, CodeFlags, CodeUnit, Instruction},
|
||||
bytecode::{self, CodeFlags, CodeUnit, Instruction, Opcode},
|
||||
class::StaticType,
|
||||
common::rc::PyRc,
|
||||
exceptions,
|
||||
@@ -410,9 +410,7 @@ impl Context {
|
||||
arg: 0.into(),
|
||||
},
|
||||
CodeUnit {
|
||||
op: Instruction::Resume {
|
||||
context: bytecode::Arg::marker(),
|
||||
},
|
||||
op: Opcode::Resume.into(),
|
||||
arg: 0.into(),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -26,32 +26,38 @@ PRIORITY_OPMAP = {
|
||||
}
|
||||
|
||||
|
||||
def to_pascal_case(s: str) -> str:
|
||||
def to_snake_case(s: str) -> str:
|
||||
res = re.sub(r"(?<=[a-z0-9])([A-Z])", r"_\1", s)
|
||||
return re.sub(r"(\D)(\d+)$", r"\1_\2", res).upper()
|
||||
|
||||
|
||||
class Opcode(typing.NamedTuple):
|
||||
rust_name: str
|
||||
cpython_name: str
|
||||
id: int
|
||||
have_oparg: bool
|
||||
|
||||
@property
|
||||
def cpython_name(self) -> str:
|
||||
return to_pascal_case(self.rust_name)
|
||||
|
||||
@property
|
||||
def is_instrumented(self):
|
||||
return self.cpython_name.startswith("INSTRUMENTED_")
|
||||
|
||||
@classmethod
|
||||
def from_str(cls, body: str):
|
||||
raw_variants = re.split(r"(\d+),", body.strip())
|
||||
raw_variants.remove("")
|
||||
for raw_name, raw_id in itertools.batched(raw_variants, 2, strict=True):
|
||||
have_oparg = "Arg<" in raw_name # Hacky but works
|
||||
name = re.findall(r"\b[A-Z][A-Za-z]*\d*\b(?=\s*[\({=])", raw_name)[0]
|
||||
yield cls(rust_name=name.strip(), id=int(raw_id), have_oparg=have_oparg)
|
||||
def from_str(cls, text: str):
|
||||
# Split on commas that are followed by a newline + an uppercase letter (new entry)
|
||||
entries = re.split(r",\s*\n\s*(?=[A-Z])", text)
|
||||
for entry in entries:
|
||||
entry = entry.strip()
|
||||
if not entry:
|
||||
continue
|
||||
have_oparg = "Arg<" in entry # Hacky but works
|
||||
rust_name = re.match(r"(\w+)", entry).group(1)
|
||||
id_num, cpython_name = re.search(r'\((\d+),\s*"([^"]+)"\)', entry).groups()
|
||||
yield cls(
|
||||
rust_name=rust_name,
|
||||
cpython_name=cpython_name,
|
||||
id=int(id_num),
|
||||
have_oparg=have_oparg,
|
||||
)
|
||||
|
||||
def __lt__(self, other: typing.Self) -> bool:
|
||||
sprio, oprio = (
|
||||
@@ -60,16 +66,23 @@ class Opcode(typing.NamedTuple):
|
||||
return (sprio, self.id) < (oprio, other.id)
|
||||
|
||||
|
||||
def extract_enum_body(contents: str, enum_name: str) -> str:
|
||||
res = re.search(f"pub enum {enum_name} " + r"\{(.+?)\n\}", contents, re.DOTALL)
|
||||
if not res:
|
||||
raise ValueError(f"Could not find {enum_name} enum")
|
||||
def extract_enum_body(text: str, name: str) -> str:
|
||||
# Find the start of the enum block
|
||||
start_match = re.search(rf"enum\s+{name}\s*\{{", text)
|
||||
if not start_match:
|
||||
return None
|
||||
|
||||
return "\n".join(
|
||||
line.split("//")[0].strip() # Remove any comment. i.e. "foo // some comment"
|
||||
for line in res.group(1).splitlines()
|
||||
if not line.strip().startswith("//") # Ignore comment lines
|
||||
)
|
||||
# Manually track brace depth from that point
|
||||
depth = 0
|
||||
start = start_match.end() - 1 # position of opening '{'
|
||||
for i, ch in enumerate(text[start:], start):
|
||||
if ch == "{":
|
||||
depth += 1
|
||||
elif ch == "}":
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
# Return only the inner content (excluding outer braces)
|
||||
return text[start + 1 : i]
|
||||
|
||||
|
||||
def build_deopts(contents: str) -> dict[str, list[str]]:
|
||||
@@ -112,7 +125,7 @@ def build_deopts(contents: str) -> dict[str, list[str]]:
|
||||
|
||||
deopts = {}
|
||||
for arm in arms:
|
||||
*specialized, deopt = map(to_pascal_case, re.findall(r"Self::(\w*)\b", arm))
|
||||
*specialized, deopt = map(to_snake_case, re.findall(r"Self::(\w*)\b", arm))
|
||||
deopts[deopt] = specialized
|
||||
|
||||
return deopts
|
||||
|
||||
Reference in new issue
Block a user