◐ Shell
clean mode source ↗

Opcode metadata auto generation by ShaharNaveh · Pull Request #6174 · RustPython/RustPython

impl Instruction {
/// Gets the label stored inside this instruction, if it exists
#[inline]
pub const fn label_arg(&self) -> Option<Arg<Label>> {
match self {
Jump { target: l }
| PopJumpIfTrue { target: l }
| PopJumpIfFalse { target: l }
| JumpIfTrueOrPop { target: l }
| JumpIfFalseOrPop { target: l }
| ForIter { target: l }
| SetupFinally { handler: l }
| SetupExcept { handler: l }
| SetupWith { end: l }
| SetupAsyncWith { end: l }
| Break { target: l }
| Continue { target: l } => Some(*l),
_ => None,
}
}
/// Whether this is an unconditional branching
///
/// # Examples
///
/// ```
/// use rustpython_compiler_core::bytecode::{Arg, Instruction};
/// let jump_inst = Instruction::Jump { target: Arg::marker() };
/// assert!(jump_inst.unconditional_branch())
/// ```
pub const fn unconditional_branch(&self) -> bool {
matches!(
self,
Jump { .. }
| Continue { .. }
| Break { .. }
| ReturnValue
| ReturnConst { .. }
| Raise { .. }
)
}
/// What effect this instruction has on the stack
///
/// # Examples
///
/// ```
/// use rustpython_compiler_core::bytecode::{Arg, Instruction, Label, UnaryOperator};
/// let (target, jump_arg) = Arg::new(Label(0xF));
/// let jump_instruction = Instruction::Jump { target };
/// let (op, invert_arg) = Arg::new(UnaryOperator::Invert);
/// let invert_instruction = Instruction::UnaryOperation { op };
/// assert_eq!(jump_instruction.stack_effect(jump_arg, true), 0);
/// assert_eq!(invert_instruction.stack_effect(invert_arg, false), 0);
/// ```
///
pub fn stack_effect(&self, arg: OpArg, jump: bool) -> i32 {
match self {
Nop => 0,
ImportName { .. } | ImportNameless => -1,
ImportFrom { .. } => 1,
LoadFast(_) | LoadNameAny(_) | LoadGlobal(_) | LoadDeref(_) | LoadClassDeref(_) => 1,
StoreFast(_) | StoreLocal(_) | StoreGlobal(_) | StoreDeref(_) => -1,
DeleteFast(_) | DeleteLocal(_) | DeleteGlobal(_) | DeleteDeref(_) => 0,
LoadClosure(_) => 1,
Subscript => -1,
StoreSubscript => -3,
DeleteSubscript => -2,
LoadAttr { .. } => 0,
StoreAttr { .. } => -2,
DeleteAttr { .. } => -1,
LoadConst { .. } => 1,
UnaryOperation { .. } => 0,
BinaryOperation { .. }
| BinaryOperationInplace { .. }
| TestOperation { .. }
| CompareOperation { .. } => -1,
BinarySubscript => -1,
CopyItem { .. } => 1,
Pop => -1,
Swap { .. } => 0,
ToBool => 0,
Rotate2 | Rotate3 => 0,
Duplicate => 1,
Duplicate2 => 2,
GetIter => 0,
GetLen => 1,
CallIntrinsic1 { .. } => 0, // Takes 1, pushes 1
CallIntrinsic2 { .. } => -1, // Takes 2, pushes 1
Continue { .. } => 0,
Break { .. } => 0,
Jump { .. } => 0,
PopJumpIfTrue { .. } | PopJumpIfFalse { .. } => -1,
JumpIfTrueOrPop { .. } | JumpIfFalseOrPop { .. } => {
if jump {
0
} else {
-1
}
}
MakeFunction => {
// CPython 3.13 style: MakeFunction only pops code object
-1 + 1 // pop code, push function
}
SetFunctionAttribute { .. } => {
// pops attribute value and function, pushes function back
-2 + 1
}
CallFunctionPositional { nargs } => -(nargs.get(arg) as i32) - 1 + 1,
CallMethodPositional { nargs } => -(nargs.get(arg) as i32) - 3 + 1,
CallFunctionKeyword { nargs } => -1 - (nargs.get(arg) as i32) - 1 + 1,
CallMethodKeyword { nargs } => -1 - (nargs.get(arg) as i32) - 3 + 1,
CallFunctionEx { has_kwargs } => -1 - (has_kwargs.get(arg) as i32) - 1 + 1,
CallMethodEx { has_kwargs } => -1 - (has_kwargs.get(arg) as i32) - 3 + 1,
LoadMethod { .. } => -1 + 3,
ForIter { .. } => {
if jump {
-1
} else {
1
}
}
ReturnValue => -1,
ReturnConst { .. } => 0,
Resume { .. } => 0,
YieldValue => 0,
YieldFrom => -1,
SetupAnnotation | SetupLoop | SetupFinally { .. } | EnterFinally | EndFinally => 0,
SetupExcept { .. } => jump as i32,
SetupWith { .. } => (!jump) as i32,
WithCleanupStart => 0,
WithCleanupFinish => -1,
PopBlock => 0,
Raise { kind } => -(kind.get(arg) as u8 as i32),
BuildString { size }
| BuildTuple { size, .. }
| BuildTupleFromTuples { size, .. }
| BuildList { size, .. }
| BuildListFromTuples { size, .. }
| BuildSet { size, .. }
| BuildSetFromTuples { size, .. } => -(size.get(arg) as i32) + 1,
BuildTupleFromIter => 0,
BuildMap { size } => {
let nargs = size.get(arg) * 2;
-(nargs as i32) + 1
}
BuildMapForCall { size } => {
let nargs = size.get(arg);
-(nargs as i32) + 1
}
DictUpdate { .. } => -1,
BuildSlice { step } => -2 - (step.get(arg) as i32) + 1,
ListAppend { .. } | SetAdd { .. } => -1,
MapAdd { .. } => -2,
PrintExpr => -1,
LoadBuildClass => 1,
UnpackSequence { size } => -1 + size.get(arg) as i32,
UnpackEx { args } => {
let UnpackExArgs { before, after } = args.get(arg);
-1 + before as i32 + 1 + after as i32
}
FormatValue { .. } => -1,
PopException => 0,
Reverse { .. } => 0,
GetAwaitable => 0,
BeforeAsyncWith => 1,
SetupAsyncWith { .. } => {
if jump {
-1
} else {
0
}
}
GetAIter => 0,
GetANext => 1,
EndAsyncFor => -2,
MatchMapping | MatchSequence => 1, // Push bool result
MatchKeys => 1, // Pop 2 (subject, keys), push 3 (subject, keys_or_none, values_or_none)
MatchClass(_) => -2,
ExtendedArg => 0,
}
}
pub fn display<'a>(
&'a self,
arg: OpArg,
ctx: &'a impl InstrDisplayContext,
) -> impl fmt::Display + 'a {
struct FmtFn<F>(F);
impl<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result> fmt::Display for FmtFn<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(self.0)(f)
}
}
FmtFn(move |f: &mut fmt::Formatter<'_>| self.fmt_dis(arg, f, ctx, false, 0, 0))
}
#[allow(clippy::too_many_arguments)]
fn fmt_dis(
&self,
arg: OpArg,
f: &mut fmt::Formatter<'_>,
ctx: &impl InstrDisplayContext,
expand_code_objects: bool,
pad: usize,
level: usize,
) -> fmt::Result {
macro_rules! w {
($variant:ident) => {
write!(f, stringify!($variant))
};
($variant:ident, $map:ident = $arg_marker:expr) => {{
let arg = $arg_marker.get(arg);
write!(f, "{:pad$}({}, {})", stringify!($variant), arg, $map(arg))
}};
($variant:ident, $arg_marker:expr) => {
write!(f, "{:pad$}({})", stringify!($variant), $arg_marker.get(arg))
};
($variant:ident, ?$arg_marker:expr) => {
write!(
f,
"{:pad$}({:?})",
stringify!($variant),
$arg_marker.get(arg)
)
};
}
let varname = |i: u32| ctx.get_varname(i as usize);
let name = |i: u32| ctx.get_name(i as usize);
let cell_name = |i: u32| ctx.get_cell_name(i as usize);
let fmt_const =
|op: &str, arg: OpArg, f: &mut fmt::Formatter<'_>, idx: &Arg<u32>| -> fmt::Result {
let value = ctx.get_constant(idx.get(arg) as usize);
match value.borrow_constant() {
BorrowedConstant::Code { code } if expand_code_objects => {
write!(f, "{op:pad$}({code:?}):")?;
code.display_inner(f, true, level + 1)?;
Ok(())
}
c => {
write!(f, "{op:pad$}(")?;
c.fmt_display(f)?;
write!(f, ")")
}
}
};
match self {
Nop => w!(Nop),
ImportName { idx } => w!(ImportName, name = idx),
ImportNameless => w!(ImportNameless),
ImportFrom { idx } => w!(ImportFrom, name = idx),
LoadFast(idx) => w!(LoadFast, varname = idx),
LoadNameAny(idx) => w!(LoadNameAny, name = idx),
LoadGlobal(idx) => w!(LoadGlobal, name = idx),
LoadDeref(idx) => w!(LoadDeref, cell_name = idx),
LoadClassDeref(idx) => w!(LoadClassDeref, cell_name = idx),
StoreFast(idx) => w!(StoreFast, varname = idx),
StoreLocal(idx) => w!(StoreLocal, name = idx),
StoreGlobal(idx) => w!(StoreGlobal, name = idx),
StoreDeref(idx) => w!(StoreDeref, cell_name = idx),
DeleteFast(idx) => w!(DeleteFast, varname = idx),
DeleteLocal(idx) => w!(DeleteLocal, name = idx),
DeleteGlobal(idx) => w!(DeleteGlobal, name = idx),
DeleteDeref(idx) => w!(DeleteDeref, cell_name = idx),
LoadClosure(i) => w!(LoadClosure, cell_name = i),
Subscript => w!(Subscript),
StoreSubscript => w!(StoreSubscript),
DeleteSubscript => w!(DeleteSubscript),
StoreAttr { idx } => w!(StoreAttr, name = idx),
DeleteAttr { idx } => w!(DeleteAttr, name = idx),
LoadConst { idx } => fmt_const("LoadConst", arg, f, idx),
UnaryOperation { op } => w!(UnaryOperation, ?op),
BinaryOperation { op } => w!(BinaryOperation, ?op),
BinaryOperationInplace { op } => w!(BinaryOperationInplace, ?op),
BinarySubscript => w!(BinarySubscript),
LoadAttr { idx } => w!(LoadAttr, name = idx),
TestOperation { op } => w!(TestOperation, ?op),
CompareOperation { op } => w!(CompareOperation, ?op),
CopyItem { index } => w!(CopyItem, index),
Pop => w!(Pop),
Swap { index } => w!(Swap, index),
ToBool => w!(ToBool),
Rotate2 => w!(Rotate2),
Rotate3 => w!(Rotate3),
Duplicate => w!(Duplicate),
Duplicate2 => w!(Duplicate2),
GetIter => w!(GetIter),
// GET_LEN
GetLen => w!(GetLen),
CallIntrinsic1 { func } => w!(CallIntrinsic1, ?func),
CallIntrinsic2 { func } => w!(CallIntrinsic2, ?func),
Continue { target } => w!(Continue, target),
Break { target } => w!(Break, target),
Jump { target } => w!(Jump, target),
PopJumpIfTrue { target } => w!(PopJumpIfTrue, target),
PopJumpIfFalse { target } => w!(PopJumpIfFalse, target),
JumpIfTrueOrPop { target } => w!(JumpIfTrueOrPop, target),
JumpIfFalseOrPop { target } => w!(JumpIfFalseOrPop, target),
MakeFunction => w!(MakeFunction),
SetFunctionAttribute { attr } => w!(SetFunctionAttribute, ?attr),
CallFunctionPositional { nargs } => w!(CallFunctionPositional, nargs),
CallFunctionKeyword { nargs } => w!(CallFunctionKeyword, nargs),
CallFunctionEx { has_kwargs } => w!(CallFunctionEx, has_kwargs),
LoadMethod { idx } => w!(LoadMethod, name = idx),
CallMethodPositional { nargs } => w!(CallMethodPositional, nargs),
CallMethodKeyword { nargs } => w!(CallMethodKeyword, nargs),
CallMethodEx { has_kwargs } => w!(CallMethodEx, has_kwargs),
ForIter { target } => w!(ForIter, target),
ReturnValue => w!(ReturnValue),
ReturnConst { idx } => fmt_const("ReturnConst", arg, f, idx),
Resume { arg } => w!(Resume, arg),
YieldValue => w!(YieldValue),
YieldFrom => w!(YieldFrom),
SetupAnnotation => w!(SetupAnnotation),
SetupLoop => w!(SetupLoop),
SetupExcept { handler } => w!(SetupExcept, handler),
SetupFinally { handler } => w!(SetupFinally, handler),
EnterFinally => w!(EnterFinally),
EndFinally => w!(EndFinally),
SetupWith { end } => w!(SetupWith, end),
WithCleanupStart => w!(WithCleanupStart),
WithCleanupFinish => w!(WithCleanupFinish),
BeforeAsyncWith => w!(BeforeAsyncWith),
SetupAsyncWith { end } => w!(SetupAsyncWith, end),
PopBlock => w!(PopBlock),
Raise { kind } => w!(Raise, ?kind),
BuildString { size } => w!(BuildString, size),
BuildTuple { size } => w!(BuildTuple, size),
BuildTupleFromTuples { size } => w!(BuildTupleFromTuples, size),
BuildTupleFromIter => w!(BuildTupleFromIter),
BuildList { size } => w!(BuildList, size),
BuildListFromTuples { size } => w!(BuildListFromTuples, size),
BuildSet { size } => w!(BuildSet, size),
BuildSetFromTuples { size } => w!(BuildSetFromTuples, size),
BuildMap { size } => w!(BuildMap, size),
BuildMapForCall { size } => w!(BuildMapForCall, size),
DictUpdate { index } => w!(DictUpdate, index),
BuildSlice { step } => w!(BuildSlice, step),
ListAppend { i } => w!(ListAppend, i),
SetAdd { i } => w!(SetAdd, i),
MapAdd { i } => w!(MapAdd, i),
PrintExpr => w!(PrintExpr),
LoadBuildClass => w!(LoadBuildClass),
UnpackSequence { size } => w!(UnpackSequence, size),
UnpackEx { args } => w!(UnpackEx, args),
FormatValue { conversion } => w!(FormatValue, ?conversion),
PopException => w!(PopException),
Reverse { amount } => w!(Reverse, amount),
GetAwaitable => w!(GetAwaitable),
GetAIter => w!(GetAIter),
GetANext => w!(GetANext),
EndAsyncFor => w!(EndAsyncFor),
MatchMapping => w!(MatchMapping),
MatchSequence => w!(MatchSequence),
MatchKeys => w!(MatchKeys),
MatchClass(arg) => w!(MatchClass, arg),
ExtendedArg => w!(ExtendedArg, Arg::<u32>::marker()),
}
}
}