Switch from 64-bit instruction enum to out-of-line arg values

This commit is contained in:
Noa committed 2023-01-12 23:05:17 -06:00
1 parent 91a897dd74
commit 2861c874ed
10 files changed
+1287 -796

No files matched your search

Generated
+1 -1
View File
@@ -2002,8 +2002,8 @@ dependencies = [
"lz4_flex",
"num-bigint",
"num-complex",
"num_enum",
"serde",
"static_assertions",
"thiserror",
]
File diff suppressed because it is too large. Load diff
+143 -53
View File
@@ -1,12 +1,47 @@
use std::ops;
use crate::IndexSet;
use rustpython_compiler_core::{CodeFlags, CodeObject, ConstantData, Instruction, Label, Location};
use rustpython_compiler_core::{
CodeFlags, CodeObject, CodeUnit, ConstantData, InstrDisplayContext, Instruction, Label,
Location, OpArg,
};
pub type BlockIdx = Label;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct BlockIdx(pub u32);
impl BlockIdx {
pub const NULL: BlockIdx = BlockIdx(u32::MAX);
const fn idx(self) -> usize {
self.0 as usize
}
}
impl ops::Index<BlockIdx> for [Block] {
type Output = Block;
fn index(&self, idx: BlockIdx) -> &Block {
&self[idx.idx()]
}
}
impl ops::IndexMut<BlockIdx> for [Block] {
fn index_mut(&mut self, idx: BlockIdx) -> &mut Block {
&mut self[idx.idx()]
}
}
impl ops::Index<BlockIdx> for Vec<Block> {
type Output = Block;
fn index(&self, idx: BlockIdx) -> &Block {
&self[idx.idx()]
}
}
impl ops::IndexMut<BlockIdx> for Vec<Block> {
fn index_mut(&mut self, idx: BlockIdx) -> &mut Block {
&mut self[idx.idx()]
}
}
#[derive(Debug)]
#[derive(Debug, Copy, Clone)]
pub struct InstructionInfo {
/// If the instruction has a Label argument, it's actually a BlockIdx, not a code offset
pub instr: Instruction,
pub arg: OpArg,
pub target: BlockIdx,
pub location: Location,
}
@@ -21,7 +56,7 @@ impl Default for Block {
fn default() -> Self {
Block {
instructions: Vec::new(),
next: Label(u32::MAX),
next: BlockIdx::NULL,
}
}
}
@@ -45,13 +80,13 @@ pub struct CodeInfo {
}
impl CodeInfo {
pub fn finalize_code(mut self, optimize: u8) -> CodeObject {
let max_stackdepth = self.max_stackdepth();
let cell2arg = self.cell2arg();
if optimize > 0 {
self.dce();
}
let max_stackdepth = self.max_stackdepth();
let cell2arg = self.cell2arg();
let CodeInfo {
flags,
posonlyarg_count,
@@ -61,7 +96,7 @@ impl CodeInfo {
first_line_number,
obj_name,
blocks,
mut blocks,
current_block: _,
constants,
name_cache,
@@ -70,26 +105,50 @@ impl CodeInfo {
freevar_cache,
} = self;
let mut num_instructions = 0;
let mut instructions = Vec::new();
let mut locations = Vec::new();
let mut block_to_offset = vec![Label(0); blocks.len()];
for (idx, block) in iter_blocks(&blocks) {
block_to_offset[idx.0 as usize] = Label(num_instructions as u32);
num_instructions += block.instructions.len();
}
let mut instructions = Vec::with_capacity(num_instructions);
let mut locations = Vec::with_capacity(num_instructions);
for (_, block) in iter_blocks(&blocks) {
for info in &block.instructions {
let mut instr = info.instr.clone();
if let Some(l) = instr.label_arg_mut() {
*l = block_to_offset[l.0 as usize];
loop {
let mut num_instructions = 0;
for (idx, block) in iter_blocks(&blocks) {
block_to_offset[idx.idx()] = Label(num_instructions as u32);
for instr in &block.instructions {
num_instructions += instr.arg.instr_size()
}
instructions.push(instr);
locations.push(info.location);
}
instructions.reserve_exact(num_instructions);
locations.reserve_exact(num_instructions);
let mut recompile_extended_arg = false;
let mut next_block = BlockIdx(0);
while next_block != BlockIdx::NULL {
let block = &mut blocks[next_block];
for info in &mut block.instructions {
let (op, arg, target) = (info.instr, &mut info.arg, info.target);
if target != BlockIdx::NULL {
let new_arg = OpArg(block_to_offset[target.idx()].0);
recompile_extended_arg |= new_arg.instr_size() != arg.instr_size();
*arg = new_arg;
}
let (extras, lo_arg) = arg.split();
locations.extend(std::iter::repeat(info.location).take(arg.instr_size()));
instructions.extend(
extras
.map(|byte| CodeUnit::new(Instruction::ExtendedArg, byte))
.chain([CodeUnit { op, arg: lo_arg }]),
);
}
next_block = block.next;
}
if !recompile_extended_arg {
break;
}
instructions.clear();
locations.clear()
}
CodeObject {
@@ -166,40 +225,48 @@ impl CodeInfo {
let mut stack = Vec::with_capacity(self.blocks.len());
let mut startdepths = vec![u32::MAX; self.blocks.len()];
startdepths[0] = 0;
stack.push(Label(0));
stack.push(BlockIdx(0));
const DEBUG: bool = false;
'process_blocks: while let Some(block) = stack.pop() {
let mut depth = startdepths[block.0 as usize];
let mut depth = startdepths[block.idx()];
if DEBUG {
eprintln!("===BLOCK {}===", block.0);
}
let block = &self.blocks[block.0 as usize];
let block = &self.blocks[block];
for i in &block.instructions {
let instr = &i.instr;
let effect = instr.stack_effect(false);
let effect = instr.stack_effect(i.arg, false);
if DEBUG {
eprint!("{instr:?}: {depth} {effect:+} => ");
let display_arg = if i.target == BlockIdx::NULL {
i.arg
} else {
OpArg(i.target.0)
};
let instr_display = instr.display(display_arg, self);
eprint!("{instr_display}: {depth} {effect:+} => ");
}
let new_depth = add_ui(depth, effect);
let new_depth = depth.checked_add_signed(effect).unwrap();
if DEBUG {
eprintln!("{new_depth}");
}
if new_depth > maxdepth {
maxdepth = new_depth
}
// we don't want to worry about Continue, it uses unwinding to jump to
// its targets and as such the stack size is taken care of in frame.rs by setting
// we don't want to worry about Break/Continue, they use unwinding to jump to
// their targets and as such the stack size is taken care of in frame.rs by setting
// it back to the level it was at when SetupLoop was run
let jump_label = instr
.label_arg()
.filter(|_| !matches!(instr, Instruction::Continue { .. }));
if let Some(&target_block) = jump_label {
let effect = instr.stack_effect(true);
let target_depth = add_ui(depth, effect);
if i.target != BlockIdx::NULL
&& !matches!(
instr,
Instruction::Continue { .. } | Instruction::Break { .. }
)
{
let effect = instr.stack_effect(i.arg, true);
let target_depth = depth.checked_add_signed(effect).unwrap();
if target_depth > maxdepth {
maxdepth = target_depth
}
stackdepth_push(&mut stack, &mut startdepths, target_block, target_depth);
stackdepth_push(&mut stack, &mut startdepths, i.target, target_depth);
}
depth = new_depth;
if instr.unconditional_branch() {
@@ -215,23 +282,46 @@ impl CodeInfo {
}
}
fn stackdepth_push(stack: &mut Vec<Label>, startdepths: &mut [u32], target: Label, depth: u32) {
let block_depth = &mut startdepths[target.0 as usize];
impl InstrDisplayContext for CodeInfo {
type Constant = ConstantData;
fn get_constant(&self, i: usize) -> &ConstantData {
&self.constants[i]
}
fn get_name(&self, i: usize) -> &str {
self.name_cache[i].as_ref()
}
fn get_varname(&self, i: usize) -> &str {
self.varname_cache[i].as_ref()
}
fn get_cellname(&self, i: usize) -> &str {
self.cellvar_cache
.get_index(i)
.unwrap_or_else(|| &self.freevar_cache[i - self.cellvar_cache.len()])
.as_ref()
}
}
fn stackdepth_push(
stack: &mut Vec<BlockIdx>,
startdepths: &mut [u32],
target: BlockIdx,
depth: u32,
) {
let block_depth = &mut startdepths[target.idx()];
if *block_depth == u32::MAX || depth > *block_depth {
*block_depth = depth;
stack.push(target);
}
}
fn add_ui(a: u32, b: i32) -> u32 {
if b < 0 {
a - b.wrapping_abs() as u32
} else {
a + b as u32
}
}
fn iter_blocks(blocks: &[Block]) -> impl Iterator<Item = (BlockIdx, &Block)> + '_ {
let get_idx = move |i: BlockIdx| blocks.get(i.0 as usize).map(|b| (i, b));
std::iter::successors(get_idx(Label(0)), move |(_, b)| get_idx(b.next)) // if b.next is u32::MAX that's the end
let mut next = BlockIdx(0);
std::iter::from_fn(move || {
if next == BlockIdx::NULL {
return None;
}
let (idx, b) = (next, &blocks[next]);
next = b.next;
Some((idx, b))
})
}
@@ -2,23 +2,23 @@
source: compiler/codegen/src/compile.rs
expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')):\n with self.subTest(type=type(stop_exc)):\n try:\n async with woohoo():\n raise stop_exc\n except Exception as ex:\n self.assertIs(ex, stop_exc)\n else:\n self.fail(f'{stop_exc} was suppressed')\n\")"
---
1 0 SetupLoop (69)
1 0 SetupLoop
1 LoadNameAny (0, StopIteration)
2 LoadConst ("spam")
3 CallFunctionPositional (1)
3 CallFunctionPositional(1)
4 LoadNameAny (1, StopAsyncIteration)
5 LoadConst ("ham")
6 CallFunctionPositional (1)
7 BuildTuple (2, false)
6 CallFunctionPositional(1)
7 BuildTuple (2)
8 GetIter
>> 9 ForIter (68)
10 StoreLocal (2, stop_exc)
2 11 LoadNameAny (3, self)
12 LoadMethod (subTest)
12 LoadMethod (4, subTest)
13 LoadNameAny (5, type)
14 LoadNameAny (2, stop_exc)
15 CallFunctionPositional (1)
15 CallFunctionPositional(1)
16 LoadConst (("type"))
17 CallMethodKeyword (1)
18 SetupWith (65)
@@ -27,7 +27,7 @@ expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyn
3 20 SetupExcept (40)
4 21 LoadNameAny (6, woohoo)
22 CallFunctionPositional (0)
22 CallFunctionPositional(0)
23 BeforeAsyncWith
24 GetAwaitable
25 LoadConst (None)
@@ -55,7 +55,7 @@ expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyn
44 StoreLocal (8, ex)
7 45 LoadNameAny (3, self)
46 LoadMethod (assertIs)
46 LoadMethod (9, assertIs)
47 LoadNameAny (8, ex)
48 LoadNameAny (2, stop_exc)
49 CallMethodPositional (2)
@@ -65,7 +65,7 @@ expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyn
>> 53 Raise (Reraise)
9 >> 54 LoadNameAny (3, self)
55 LoadMethod (fail)
55 LoadMethod (10, fail)
56 LoadConst ("")
57 LoadNameAny (2, stop_exc)
58 FormatValue (None)
@@ -80,6 +80,6 @@ expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyn
66 WithCleanupFinish
67 Jump (9)
>> 68 PopBlock
>> 69 LoadConst (None)
69 LoadConst (None)
70 ReturnValue
+1 -1
View File
@@ -15,6 +15,6 @@ itertools = "0.10.3"
lz4_flex = "0.9.2"
num-bigint = { version = "0.4.3", features = ["serde"] }
num-complex = { version = "0.4.0", features = ["serde"] }
num_enum = "0.5.7"
serde = { version = "1.0.136", features = ["derive"] }
static_assertions = "1.1.0"
thiserror = "1.0"
File diff suppressed because it is too large. Load diff
+2 -3
View File
@@ -6,8 +6,7 @@ use rustpython_parser::{
};
pub use rustpython_codegen::compile::CompileOpts;
pub use rustpython_compiler_core::CodeObject;
pub use rustpython_compiler_core::{BaseError as CompileErrorBody, Mode};
pub use rustpython_compiler_core::{BaseError as CompileErrorBody, CodeObject, Mode};
#[derive(Debug, thiserror::Error)]
pub enum CompileErrorType {
@@ -29,7 +28,7 @@ pub fn compile(
source: &str,
mode: compile::Mode,
source_path: String,
opts: compile::CompileOpts,
opts: CompileOpts,
) -> Result<CodeObject, CompileError> {
let mut ast = match parser::parse(source, mode.into(), &source_path) {
Ok(x) => x,
+20 -13
View File
@@ -2,7 +2,7 @@ use cranelift::prelude::*;
use num_traits::cast::ToPrimitive;
use rustpython_compiler_core::{
self as bytecode, BinaryOperator, BorrowedConstant, CodeObject, ComparisonOperator,
Instruction, Label, UnaryOperator,
Instruction, Label, OpArg, OpArgState, UnaryOperator,
};
use std::collections::HashMap;
@@ -155,7 +155,9 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
// label_targets alone
let label_targets = bytecode.label_targets();
let mut arg_state = OpArgState::default();
for (offset, instruction) in bytecode.instructions.iter().enumerate() {
let (instruction, arg) = arg_state.get(*instruction);
let label = Label(offset as u32);
if label_targets.contains(&label) {
let block = self.get_or_create_block(label);
@@ -175,7 +177,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
continue;
}
self.add_instruction(instruction, &bytecode.constants)?;
self.add_instruction(instruction, arg, &bytecode.constants)?;
}
Ok(())
@@ -214,15 +216,17 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
pub fn add_instruction<C: bytecode::Constant>(
&mut self,
instruction: &Instruction,
instruction: Instruction,
arg: OpArg,
constants: &[C],
) -> Result<(), JitCompileError> {
match instruction {
Instruction::ExtendedArg => Ok(()),
Instruction::JumpIfFalse { target } => {
let cond = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
let val = self.boolean_val(cond)?;
let then_block = self.get_or_create_block(*target);
let then_block = self.get_or_create_block(target.get(arg));
self.builder.ins().brz(val, then_block, &[]);
let block = self.builder.create_block();
@@ -235,7 +239,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
let cond = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
let val = self.boolean_val(cond)?;
let then_block = self.get_or_create_block(*target);
let then_block = self.get_or_create_block(target.get(arg));
self.builder.ins().brnz(val, then_block, &[]);
let block = self.builder.create_block();
@@ -245,13 +249,13 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
Ok(())
}
Instruction::Jump { target } => {
let target_block = self.get_or_create_block(*target);
let target_block = self.get_or_create_block(target.get(arg));
self.builder.ins().jump(target_block, &[]);
Ok(())
}
Instruction::LoadFast(idx) => {
let local = self.variables[*idx as usize]
let local = self.variables[idx.get(arg) as usize]
.as_ref()
.ok_or(JitCompileError::BadBytecode)?;
self.stack.push(JitValue::from_type_and_value(
@@ -262,13 +266,13 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
}
Instruction::StoreFast(idx) => {
let val = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
self.store_variable(*idx, val)
self.store_variable(idx.get(arg), val)
}
Instruction::LoadConst { idx } => {
self.load_const(constants[*idx as usize].borrow_constant())
self.load_const(constants[idx.get(arg) as usize].borrow_constant())
}
Instruction::BuildTuple { unpack, size } if !unpack => {
let elements = self.pop_multiple(*size as usize);
Instruction::BuildTuple { size } => {
let elements = self.pop_multiple(size.get(arg) as usize);
self.stack.push(JitValue::Tuple(elements));
Ok(())
}
@@ -280,7 +284,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
_ => return Err(JitCompileError::NotSupported),
};
if elements.len() != *size as usize {
if elements.len() != size.get(arg) as usize {
return Err(JitCompileError::NotSupported);
}
@@ -306,6 +310,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
Ok(())
}
Instruction::CompareOperation { op, .. } => {
let op = op.get(arg);
// the rhs is popped off first
let b = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
let a = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
@@ -347,6 +352,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
}
}
Instruction::UnaryOperation { op, .. } => {
let op = op.get(arg);
let a = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
match (op, a) {
(UnaryOperator::Minus, JitValue::Int(val)) => {
@@ -371,6 +377,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
}
}
Instruction::BinaryOperation { op } | Instruction::BinaryOperationInplace { op } => {
let op = op.get(arg);
// the rhs is popped off first
let b = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
let a = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
@@ -406,7 +413,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
TrapCode::User(CustomTrapCode::NegativeShiftCount as u16),
);
let out = if *op == BinaryOperator::Lshift {
let out = if op == BinaryOperator::Lshift {
self.builder.ins().ishl(a, b)
} else {
self.builder.ins().sshr(a, b)
+16 -11
View File
@@ -1,6 +1,7 @@
use rustpython_compiler_core::{CodeObject, ConstantData, Instruction};
use rustpython_compiler_core::{CodeObject, ConstantData, Instruction, OpArg, OpArgState};
use rustpython_jit::{CompiledCode, JitType};
use std::collections::HashMap;
use std::ops::ControlFlow;
#[derive(Debug, Clone)]
pub struct Function {
@@ -62,27 +63,30 @@ impl StackMachine {
}
pub fn run(&mut self, code: CodeObject) {
for instruction in code.instructions.into_vec() {
if self.process_instruction(instruction, &code.constants, &code.names) {
break;
}
}
let mut oparg_state = OpArgState::default();
code.instructions.iter().try_for_each(|&word| {
let (instruction, arg) = oparg_state.get(word);
self.process_instruction(instruction, arg, &code.constants, &code.names)
});
}
fn process_instruction(
&mut self,
instruction: Instruction,
arg: OpArg,
constants: &[ConstantData],
names: &[String],
) -> bool {
) -> ControlFlow<()> {
match instruction {
Instruction::LoadConst { idx } => {
let idx = idx.get(arg);
self.stack.push(constants[idx as usize].clone().into())
}
Instruction::LoadNameAny(idx) => self
.stack
.push(StackValue::String(names[idx as usize].clone())),
.push(StackValue::String(names[idx.get(arg) as usize].clone())),
Instruction::StoreLocal(idx) => {
let idx = idx.get(arg);
self.locals
.insert(names[idx as usize].clone(), self.stack.pop().unwrap());
}
@@ -93,7 +97,7 @@ impl StackMachine {
}
Instruction::BuildMap { size, .. } => {
let mut map = HashMap::new();
for _ in 0..size {
for _ in 0..size.get(arg) {
let value = self.stack.pop().unwrap();
let name = if let Some(StackValue::String(name)) = self.stack.pop() {
name
@@ -135,13 +139,14 @@ impl StackMachine {
let i = self.stack.len() - 3;
self.stack[i..].rotate_right(1);
}
Instruction::ReturnValue => return true,
Instruction::ReturnValue => return ControlFlow::Break(()),
Instruction::ExtendedArg => {}
_ => unimplemented!(
"instruction {:?} isn't yet supported in py_function!",
instruction
),
}
false
ControlFlow::Continue(())
}
pub fn get_function(&self, name: &str) -> Function {
+186 -155
View File
@@ -36,9 +36,7 @@ struct Block {
#[derive(Clone, Debug)]
enum BlockType {
Loop {
break_target: bytecode::Label,
},
Loop,
TryExcept {
handler: bytecode::Label,
},
@@ -72,7 +70,7 @@ enum UnwindReason {
// NoWorries,
/// We are unwinding blocks, since we hit break
Break,
Break { target: bytecode::Label },
/// We are unwinding blocks since we hit a continue statements.
Continue { target: bytecode::Label },
@@ -131,7 +129,7 @@ pub enum ExecutionResult {
}
/// A valid execution result, or an exception
pub type FrameResult = PyResult<Option<ExecutionResult>>;
type FrameResult = PyResult<Option<ExecutionResult>>;
impl Frame {
pub(crate) fn new(
@@ -351,13 +349,16 @@ impl ExecutingFrame<'_> {
flame_guard!(format!("Frame::run({})", self.code.obj_name));
// Execute until return or exception:
let instrs = &self.code.instructions;
let mut arg_state = bytecode::OpArgState::default();
loop {
let idx = self.lasti() as usize;
self.update_lasti(|i| *i += 1);
let instr = &instrs[idx];
let result = self.execute_instruction(instr, vm);
let bytecode::CodeUnit { op, arg } = instrs[idx];
let arg = arg_state.extend(arg);
let mut do_extend_arg = false;
let result = self.execute_instruction(op, arg, &mut do_extend_arg, vm);
match result {
Ok(None) => continue,
Ok(None) => {}
Ok(Some(value)) => {
break Ok(value);
}
@@ -387,24 +388,25 @@ impl ExecutingFrame<'_> {
}
match handle_exception(self, exception, idx, vm) {
Ok(None) => continue,
Ok(Some(result)) => {
break Ok(result);
}
Err(exception) => {
// TODO: append line number to traceback?
// traceback.append();
break Err(exception);
}
Ok(None) => {}
Ok(Some(result)) => break Ok(result),
// TODO: append line number to traceback?
// traceback.append();
Err(exception) => break Err(exception),
}
}
}
if !do_extend_arg {
arg_state.reset()
}
}
}
fn yield_from_target(&self) -> Option<&PyObject> {
if let Some(bytecode::Instruction::YieldFrom) =
self.code.instructions.get(self.lasti() as usize)
if let Some(bytecode::CodeUnit {
op: bytecode::Instruction::YieldFrom,
..
}) = self.code.instructions.get(self.lasti() as usize)
{
Some(self.last_value_ref())
} else {
@@ -478,7 +480,9 @@ impl ExecutingFrame<'_> {
#[inline(always)]
fn execute_instruction(
&mut self,
instruction: &bytecode::Instruction,
instruction: bytecode::Instruction,
arg: bytecode::OpArg,
extend_arg: &mut bool,
vm: &VirtualMachine,
) -> FrameResult {
vm.check_signals()?;
@@ -505,16 +509,16 @@ impl ExecutingFrame<'_> {
match instruction {
bytecode::Instruction::LoadConst { idx } => {
self.push_value(self.code.constants[*idx as usize].clone().into());
self.push_value(self.code.constants[idx.get(arg) as usize].clone().into());
Ok(None)
}
bytecode::Instruction::ImportName { idx } => {
self.import(vm, Some(self.code.names[*idx as usize].to_owned()))
self.import(vm, Some(self.code.names[idx.get(arg) as usize].to_owned()))
}
bytecode::Instruction::ImportNameless => self.import(vm, None),
bytecode::Instruction::ImportStar => self.import_star(vm),
bytecode::Instruction::ImportFrom { idx } => {
let obj = self.import_from(vm, *idx)?;
let obj = self.import_from(vm, idx.get(arg))?;
self.push_value(obj);
Ok(None)
}
@@ -529,7 +533,7 @@ impl ExecutingFrame<'_> {
format!("local variable '{varname}' referenced before assignment",),
)
}
let idx = *idx as usize;
let idx = idx.get(arg) as usize;
let x = self.fastlocals.lock()[idx]
.clone()
.ok_or_else(|| reference_error(self.code.varnames[idx], vm))?;
@@ -537,7 +541,7 @@ impl ExecutingFrame<'_> {
Ok(None)
}
bytecode::Instruction::LoadNameAny(idx) => {
let name = self.code.names[*idx as usize];
let name = self.code.names[idx.get(arg) as usize];
let result = self.locals.mapping().subscript(name, vm);
match result {
Ok(x) => self.push_value(x),
@@ -549,13 +553,13 @@ impl ExecutingFrame<'_> {
Ok(None)
}
bytecode::Instruction::LoadGlobal(idx) => {
let name = &self.code.names[*idx as usize];
let name = &self.code.names[idx.get(arg) as usize];
let x = self.load_global_or_builtin(name, vm)?;
self.push_value(x);
Ok(None)
}
bytecode::Instruction::LoadDeref(i) => {
let i = *i as usize;
let i = i.get(arg) as usize;
let x = self.cells_frees[i]
.get()
.ok_or_else(|| self.unbound_cell_exception(i, vm))?;
@@ -563,7 +567,7 @@ impl ExecutingFrame<'_> {
Ok(None)
}
bytecode::Instruction::LoadClassDeref(i) => {
let i = *i as usize;
let i = i.get(arg) as usize;
let name = self.code.freevars[i - self.code.cellvars.len()];
let value = self.locals.mapping().subscript(name, vm).ok();
self.push_value(match value {
@@ -576,11 +580,11 @@ impl ExecutingFrame<'_> {
}
bytecode::Instruction::StoreFast(idx) => {
let value = self.pop_value();
self.fastlocals.lock()[*idx as usize] = Some(value);
self.fastlocals.lock()[idx.get(arg) as usize] = Some(value);
Ok(None)
}
bytecode::Instruction::StoreLocal(idx) => {
let name = self.code.names[*idx as usize];
let name = self.code.names[idx.get(arg) as usize];
let value = self.pop_value();
self.locals.mapping().ass_subscript(name, Some(value), vm)?;
Ok(None)
@@ -588,20 +592,20 @@ impl ExecutingFrame<'_> {
bytecode::Instruction::StoreGlobal(idx) => {
let value = self.pop_value();
self.globals
.set_item(self.code.names[*idx as usize], value, vm)?;
.set_item(self.code.names[idx.get(arg) as usize], value, vm)?;
Ok(None)
}
bytecode::Instruction::StoreDeref(i) => {
let value = self.pop_value();
self.cells_frees[*i as usize].set(Some(value));
self.cells_frees[i.get(arg) as usize].set(Some(value));
Ok(None)
}
bytecode::Instruction::DeleteFast(idx) => {
self.fastlocals.lock()[*idx as usize] = None;
self.fastlocals.lock()[idx.get(arg) as usize] = None;
Ok(None)
}
bytecode::Instruction::DeleteLocal(idx) => {
let name = self.code.names[*idx as usize];
let name = self.code.names[idx.get(arg) as usize];
let res = self.locals.mapping().ass_subscript(name, None, vm);
match res {
@@ -614,7 +618,7 @@ impl ExecutingFrame<'_> {
Ok(None)
}
bytecode::Instruction::DeleteGlobal(idx) => {
let name = self.code.names[*idx as usize];
let name = self.code.names[idx.get(arg) as usize];
match self.globals.del_item(name, vm) {
Ok(()) => {}
Err(e) if e.fast_isinstance(vm.ctx.exceptions.key_error) => {
@@ -625,11 +629,11 @@ impl ExecutingFrame<'_> {
Ok(None)
}
bytecode::Instruction::DeleteDeref(i) => {
self.cells_frees[*i as usize].set(None);
self.cells_frees[i.get(arg) as usize].set(None);
Ok(None)
}
bytecode::Instruction::LoadClosure(i) => {
let value = self.cells_frees[*i as usize].clone();
let value = self.cells_frees[i.get(arg) as usize].clone();
self.push_value(value.into());
Ok(None)
}
@@ -662,7 +666,7 @@ impl ExecutingFrame<'_> {
bytecode::Instruction::Rotate3 => self.execute_rotate(3),
bytecode::Instruction::BuildString { size } => {
let s = self
.pop_multiple(*size as usize)
.pop_multiple(size.get(arg) as usize)
.as_slice()
.iter()
.map(|pyobj| pyobj.payload::<PyStr>().unwrap().as_ref())
@@ -671,40 +675,54 @@ impl ExecutingFrame<'_> {
self.push_value(str_obj.into());
Ok(None)
}
bytecode::Instruction::BuildList { size, unpack } => {
let elements = self.get_elements(vm, *size as usize, *unpack)?;
bytecode::Instruction::BuildList { size } => {
let elements = self.pop_multiple(size.get(arg) as usize).collect();
let list_obj = vm.ctx.new_list(elements);
self.push_value(list_obj.into());
Ok(None)
}
bytecode::Instruction::BuildSet { size, unpack } => {
bytecode::Instruction::BuildListUnpack { size } => {
let elements = self.unpack_elements(vm, size.get(arg) as usize)?;
let list_obj = vm.ctx.new_list(elements);
self.push_value(list_obj.into());
Ok(None)
}
bytecode::Instruction::BuildSet { size } => {
let set = PySet::new_ref(&vm.ctx);
{
let elements = self.pop_multiple(*size as usize);
if *unpack {
for element in elements {
vm.map_iterable_object(&element, |x| set.add(x, vm))??;
}
} else {
for element in elements {
set.add(element, vm)?;
}
for element in self.pop_multiple(size.get(arg) as usize) {
set.add(element, vm)?;
}
}
self.push_value(set.into());
Ok(None)
}
bytecode::Instruction::BuildTuple { size, unpack } => {
let elements = self.get_elements(vm, *size as usize, *unpack)?;
bytecode::Instruction::BuildSetUnpack { size } => {
let set = PySet::new_ref(&vm.ctx);
{
for element in self.pop_multiple(size.get(arg) as usize) {
vm.map_iterable_object(&element, |x| set.add(x, vm))??;
}
}
self.push_value(set.into());
Ok(None)
}
bytecode::Instruction::BuildTuple { size } => {
let elements = self.pop_multiple(size.get(arg) as usize).collect();
let list_obj = vm.ctx.new_tuple(elements);
self.push_value(list_obj.into());
Ok(None)
}
bytecode::Instruction::BuildMap {
size,
unpack,
for_call,
} => self.execute_build_map(vm, *size, *unpack, *for_call),
bytecode::Instruction::BuildTupleUnpack { size } => {
let elements = self.unpack_elements(vm, size.get(arg) as usize)?;
let list_obj = vm.ctx.new_tuple(elements);
self.push_value(list_obj.into());
Ok(None)
}
bytecode::Instruction::BuildMap { size } => self.execute_build_map(vm, size.get(arg)),
bytecode::Instruction::BuildMapForCall { size } => {
self.execute_build_map_for_call(vm, size.get(arg))
}
bytecode::Instruction::DictUpdate => {
let other = self.pop_value();
let dict = self
@@ -714,10 +732,12 @@ impl ExecutingFrame<'_> {
dict.merge_object(other, vm)?;
Ok(None)
}
bytecode::Instruction::BuildSlice { step } => self.execute_build_slice(vm, *step),
bytecode::Instruction::BuildSlice { step } => {
self.execute_build_slice(vm, step.get(arg))
}
bytecode::Instruction::ListAppend { i } => {
let item = self.pop_value();
let obj = self.nth_value(*i);
let obj = self.nth_value(i.get(arg));
let list: &Py<PyList> = unsafe {
// SAFETY: trust compiler
obj.downcast_unchecked_ref()
@@ -727,7 +747,7 @@ impl ExecutingFrame<'_> {
}
bytecode::Instruction::SetAdd { i } => {
let item = self.pop_value();
let obj = self.nth_value(*i);
let obj = self.nth_value(i.get(arg));
let set: &Py<PySet> = unsafe {
// SAFETY: trust compiler
obj.downcast_unchecked_ref()
@@ -738,7 +758,7 @@ impl ExecutingFrame<'_> {
bytecode::Instruction::MapAdd { i } => {
let value = self.pop_value();
let key = self.pop_value();
let obj = self.nth_value(*i);
let obj = self.nth_value(i.get(arg));
let dict: &Py<PyDict> = unsafe {
// SAFETY: trust compiler
obj.downcast_unchecked_ref()
@@ -746,16 +766,16 @@ impl ExecutingFrame<'_> {
dict.set_item(&*key, value, vm)?;
Ok(None)
}
bytecode::Instruction::BinaryOperation { op } => self.execute_binop(vm, *op),
bytecode::Instruction::BinaryOperation { op } => self.execute_binop(vm, op.get(arg)),
bytecode::Instruction::BinaryOperationInplace { op } => {
self.execute_binop_inplace(vm, *op)
self.execute_binop_inplace(vm, op.get(arg))
}
bytecode::Instruction::LoadAttr { idx } => self.load_attr(vm, *idx),
bytecode::Instruction::StoreAttr { idx } => self.store_attr(vm, *idx),
bytecode::Instruction::DeleteAttr { idx } => self.delete_attr(vm, *idx),
bytecode::Instruction::UnaryOperation { ref op } => self.execute_unop(vm, op),
bytecode::Instruction::TestOperation { ref op } => self.execute_test(vm, op),
bytecode::Instruction::CompareOperation { op } => self.execute_compare(vm, *op),
bytecode::Instruction::LoadAttr { idx } => self.load_attr(vm, idx.get(arg)),
bytecode::Instruction::StoreAttr { idx } => self.store_attr(vm, idx.get(arg)),
bytecode::Instruction::DeleteAttr { idx } => self.delete_attr(vm, idx.get(arg)),
bytecode::Instruction::UnaryOperation { op } => self.execute_unop(vm, op.get(arg)),
bytecode::Instruction::TestOperation { op } => self.execute_test(vm, op.get(arg)),
bytecode::Instruction::CompareOperation { op } => self.execute_compare(vm, op.get(arg)),
bytecode::Instruction::ReturnValue => {
let value = self.pop_value();
self.unwind_blocks(vm, UnwindReason::Returning { value })
@@ -771,18 +791,20 @@ impl ExecutingFrame<'_> {
}
bytecode::Instruction::YieldFrom => self.execute_yield_from(vm),
bytecode::Instruction::SetupAnnotation => self.setup_annotations(vm),
bytecode::Instruction::SetupLoop { break_target } => {
self.push_block(BlockType::Loop {
break_target: *break_target,
});
bytecode::Instruction::SetupLoop => {
self.push_block(BlockType::Loop);
Ok(None)
}
bytecode::Instruction::SetupExcept { handler } => {
self.push_block(BlockType::TryExcept { handler: *handler });
self.push_block(BlockType::TryExcept {
handler: handler.get(arg),
});
Ok(None)
}
bytecode::Instruction::SetupFinally { handler } => {
self.push_block(BlockType::Finally { handler: *handler });
self.push_block(BlockType::Finally {
handler: handler.get(arg),
});
Ok(None)
}
bytecode::Instruction::EnterFinally => {
@@ -819,7 +841,9 @@ impl ExecutingFrame<'_> {
)?;
let exit = context_manager.get_attr(identifier!(vm, __exit__), vm)?;
self.push_value(exit);
self.push_block(BlockType::Finally { handler: *end });
self.push_block(BlockType::Finally {
handler: end.get(arg),
});
self.push_value(enter_res);
Ok(None)
}
@@ -835,7 +859,9 @@ impl ExecutingFrame<'_> {
}
bytecode::Instruction::SetupAsyncWith { end } => {
let enter_res = self.pop_value();
self.push_block(BlockType::Finally { handler: *end });
self.push_block(BlockType::Finally {
handler: end.get(arg),
});
self.push_value(enter_res);
Ok(None)
}
@@ -938,23 +964,25 @@ impl ExecutingFrame<'_> {
Err(exc.downcast().unwrap())
}
}
bytecode::Instruction::ForIter { target } => self.execute_for_iter(vm, *target),
bytecode::Instruction::MakeFunction(flags) => self.execute_make_function(vm, *flags),
bytecode::Instruction::ForIter { target } => self.execute_for_iter(vm, target.get(arg)),
bytecode::Instruction::MakeFunction(flags) => {
self.execute_make_function(vm, flags.get(arg))
}
bytecode::Instruction::CallFunctionPositional { nargs } => {
let args = self.collect_positional_args(*nargs);
let args = self.collect_positional_args(nargs.get(arg));
self.execute_call(args, vm)
}
bytecode::Instruction::CallFunctionKeyword { nargs } => {
let args = self.collect_keyword_args(*nargs);
let args = self.collect_keyword_args(nargs.get(arg));
self.execute_call(args, vm)
}
bytecode::Instruction::CallFunctionEx { has_kwargs } => {
let args = self.collect_ex_args(vm, *has_kwargs)?;
let args = self.collect_ex_args(vm, has_kwargs.get(arg))?;
self.execute_call(args, vm)
}
bytecode::Instruction::LoadMethod { idx } => {
let obj = self.pop_value();
let method_name = self.code.names[*idx as usize];
let method_name = self.code.names[idx.get(arg) as usize];
let method = PyMethod::get(obj, method_name.to_owned(), vm)?;
let (target, is_method, func) = match method {
PyMethod::Function { target, func } => (target, true, func),
@@ -968,48 +996,61 @@ impl ExecutingFrame<'_> {
Ok(None)
}
bytecode::Instruction::CallMethodPositional { nargs } => {
let args = self.collect_positional_args(*nargs);
let args = self.collect_positional_args(nargs.get(arg));
self.execute_method_call(args, vm)
}
bytecode::Instruction::CallMethodKeyword { nargs } => {
let args = self.collect_keyword_args(*nargs);
let args = self.collect_keyword_args(nargs.get(arg));
self.execute_method_call(args, vm)
}
bytecode::Instruction::CallMethodEx { has_kwargs } => {
let args = self.collect_ex_args(vm, *has_kwargs)?;
let args = self.collect_ex_args(vm, has_kwargs.get(arg))?;
self.execute_method_call(args, vm)
}
bytecode::Instruction::Jump { target } => {
self.jump(*target);
self.jump(target.get(arg));
Ok(None)
}
bytecode::Instruction::JumpIfTrue { target } => self.jump_if(vm, *target, true),
bytecode::Instruction::JumpIfFalse { target } => self.jump_if(vm, *target, false),
bytecode::Instruction::JumpIfTrue { target } => self.jump_if(vm, target.get(arg), true),
bytecode::Instruction::JumpIfFalse { target } => {
self.jump_if(vm, target.get(arg), false)
}
bytecode::Instruction::JumpIfTrueOrPop { target } => {
self.jump_if_or_pop(vm, *target, true)
self.jump_if_or_pop(vm, target.get(arg), true)
}
bytecode::Instruction::JumpIfFalseOrPop { target } => {
self.jump_if_or_pop(vm, *target, false)
self.jump_if_or_pop(vm, target.get(arg), false)
}
bytecode::Instruction::Raise { kind } => self.execute_raise(vm, *kind),
bytecode::Instruction::Raise { kind } => self.execute_raise(vm, kind.get(arg)),
bytecode::Instruction::Break { target: _ } => {
self.unwind_blocks(vm, UnwindReason::Break)
}
bytecode::Instruction::Continue { target } => {
self.unwind_blocks(vm, UnwindReason::Continue { target: *target })
}
bytecode::Instruction::Break { target } => self.unwind_blocks(
vm,
UnwindReason::Break {
target: target.get(arg),
},
),
bytecode::Instruction::Continue { target } => self.unwind_blocks(
vm,
UnwindReason::Continue {
target: target.get(arg),
},
),
bytecode::Instruction::PrintExpr => self.print_expr(vm),
bytecode::Instruction::LoadBuildClass => {
self.push_value(vm.builtins.get_attr(identifier!(vm, __build_class__), vm)?);
Ok(None)
}
bytecode::Instruction::UnpackSequence { size } => self.unpack_sequence(*size, vm),
bytecode::Instruction::UnpackEx { before, after } => {
self.execute_unpack_ex(vm, *before, *after)
bytecode::Instruction::UnpackSequence { size } => {
self.unpack_sequence(size.get(arg), vm)
}
bytecode::Instruction::UnpackEx { args } => {
let args = args.get(arg);
self.execute_unpack_ex(vm, args.before, args.after)
}
bytecode::Instruction::FormatValue { conversion } => {
self.format_value(conversion.get(arg), vm)
}
bytecode::Instruction::FormatValue { conversion } => self.format_value(*conversion, vm),
bytecode::Instruction::PopException {} => {
let block = self.pop_block();
if let BlockType::ExceptHandler { prev_exc } = block.typ {
@@ -1021,7 +1062,11 @@ impl ExecutingFrame<'_> {
}
bytecode::Instruction::Reverse { amount } => {
let stack_len = self.state.stack.len();
self.state.stack[stack_len - *amount as usize..stack_len].reverse();
self.state.stack[stack_len - amount.get(arg) as usize..stack_len].reverse();
Ok(None)
}
bytecode::Instruction::ExtendedArg => {
*extend_arg = true;
Ok(None)
}
}
@@ -1037,23 +1082,13 @@ impl ExecutingFrame<'_> {
}
#[cfg_attr(feature = "flame-it", flame("Frame"))]
fn get_elements(
&mut self,
vm: &VirtualMachine,
size: usize,
unpack: bool,
) -> PyResult<Vec<PyObjectRef>> {
let elements = self.pop_multiple(size);
if unpack {
let mut result = Vec::<PyObjectRef>::new();
for element in elements {
let items: Vec<_> = element.try_to_value(vm)?;
result.extend(items);
}
Ok(result)
} else {
Ok(elements.collect())
fn unpack_elements(&mut self, vm: &VirtualMachine, size: usize) -> PyResult<Vec<PyObjectRef>> {
let mut result = Vec::<PyObjectRef>::new();
for element in self.pop_multiple(size) {
let items: Vec<_> = element.try_to_value(vm)?;
result.extend(items);
}
Ok(result)
}
#[cfg_attr(feature = "flame-it", flame("Frame"))]
@@ -1127,10 +1162,10 @@ impl ExecutingFrame<'_> {
// First unwind all existing blocks on the block stack:
while let Some(block) = self.current_block() {
match block.typ {
BlockType::Loop { break_target } => match reason {
UnwindReason::Break => {
BlockType::Loop => match reason {
UnwindReason::Break { target } => {
self.pop_block();
self.jump(break_target);
self.jump(target);
return Ok(None);
}
UnwindReason::Continue { target } => {
@@ -1215,38 +1250,34 @@ impl ExecutingFrame<'_> {
Ok(None)
}
fn execute_build_map(
&mut self,
vm: &VirtualMachine,
size: u32,
unpack: bool,
for_call: bool,
) -> FrameResult {
fn execute_build_map(&mut self, vm: &VirtualMachine, size: u32) -> FrameResult {
let size = size as usize;
let map_obj = vm.ctx.new_dict();
if unpack {
for obj in self.pop_multiple(size) {
// Take all key-value pairs from the dict:
let dict: PyDictRef = obj.downcast().map_err(|obj| {
vm.new_type_error(format!("'{}' object is not a mapping", obj.class().name()))
})?;
for (key, value) in dict {
#[allow(clippy::collapsible_if)]
if for_call {
if map_obj.contains_key(&*key, vm) {
let key_repr = &key.repr(vm)?;
let msg = format!(
"got multiple values for keyword argument {}",
key_repr.as_str()
);
return Err(vm.new_type_error(msg));
}
}
map_obj.set_item(&*key, value, vm)?;
for (key, value) in self.pop_multiple(2 * size).tuples() {
map_obj.set_item(&*key, value, vm)?;
}
self.push_value(map_obj.into());
Ok(None)
}
fn execute_build_map_for_call(&mut self, vm: &VirtualMachine, size: u32) -> FrameResult {
let size = size as usize;
let map_obj = vm.ctx.new_dict();
for obj in self.pop_multiple(size) {
// Take all key-value pairs from the dict:
let dict: PyDictRef = obj.downcast().map_err(|obj| {
vm.new_type_error(format!("'{}' object is not a mapping", obj.class().name()))
})?;
for (key, value) in dict {
if map_obj.contains_key(&*key, vm) {
let key_repr = &key.repr(vm)?;
let msg = format!(
"got multiple values for keyword argument {}",
key_repr.as_str()
);
return Err(vm.new_type_error(msg));
}
}
} else {
for (key, value) in self.pop_multiple(2 * size).tuples() {
map_obj.set_item(&*key, value, vm)?;
}
}
@@ -1632,9 +1663,9 @@ impl ExecutingFrame<'_> {
}
#[cfg_attr(feature = "flame-it", flame("Frame"))]
fn execute_unop(&mut self, vm: &VirtualMachine, op: &bytecode::UnaryOperator) -> FrameResult {
fn execute_unop(&mut self, vm: &VirtualMachine, op: bytecode::UnaryOperator) -> FrameResult {
let a = self.pop_value();
let value = match *op {
let value = match op {
bytecode::UnaryOperator::Minus => vm._neg(&a)?,
bytecode::UnaryOperator::Plus => vm._pos(&a)?,
bytecode::UnaryOperator::Invert => vm._invert(&a)?,
@@ -1759,10 +1790,10 @@ impl ExecutingFrame<'_> {
}
#[cfg_attr(feature = "flame-it", flame("Frame"))]
fn execute_test(&mut self, vm: &VirtualMachine, op: &bytecode::TestOperator) -> FrameResult {
fn execute_test(&mut self, vm: &VirtualMachine, op: bytecode::TestOperator) -> FrameResult {
let b = self.pop_value();
let a = self.pop_value();
let value = match *op {
let value = match op {
bytecode::TestOperator::Is => a.is(&b),
bytecode::TestOperator::IsNot => !a.is(&b),
bytecode::TestOperator::In => self._in(vm, a, b)?,