mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
cold block reordering and jump normalization (#7210)
* cold block reordering and jump normalization Add mark_cold, push_cold_blocks_to_end, and normalize_jumps passes to the codegen CFG pipeline. Use JumpNoInterrupt for exception handler exit paths in try-except-finally compilation. * mark test_peepholer
This commit is contained in:
45
Lib/test/test_peepholer.py
vendored
45
Lib/test/test_peepholer.py
vendored
@@ -82,7 +82,7 @@ class TestTranforms(BytecodeTestCase):
|
||||
# aren't very many tests of lnotab), if peepholer wasn't scheduled
|
||||
# to be replaced anyway.
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; RETURN_VALUE
|
||||
def test_unot(self):
|
||||
# UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE'
|
||||
def unot(x):
|
||||
@@ -133,7 +133,7 @@ class TestTranforms(BytecodeTestCase):
|
||||
self.assertInBytecode(f, 'LOAD_CONST', None)
|
||||
self.check_lnotab(f)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; RETURN_VALUE
|
||||
def test_while_one(self):
|
||||
# Skip over: LOAD_CONST trueconst POP_JUMP_IF_FALSE xx
|
||||
def f():
|
||||
@@ -160,7 +160,7 @@ class TestTranforms(BytecodeTestCase):
|
||||
self.assertNotInBytecode(code, 'UNPACK_SEQUENCE')
|
||||
self.check_lnotab(code)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 1 != 2
|
||||
def test_constant_folding_tuples_of_constants(self):
|
||||
for line, elem in (
|
||||
('a = 1,2,3', (1, 2, 3)),
|
||||
@@ -329,7 +329,7 @@ class TestTranforms(BytecodeTestCase):
|
||||
self.assertNotStartsWith(instr.opname, 'UNARY_')
|
||||
self.check_lnotab(negzero)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; BINARY_OP 26 ([])
|
||||
def test_constant_folding_binop(self):
|
||||
tests = [
|
||||
('1 + 2', 'NB_ADD', True, 'LOAD_SMALL_INT', 3),
|
||||
@@ -531,7 +531,7 @@ class TestTranforms(BytecodeTestCase):
|
||||
self.assertEqual(len(returns), 1)
|
||||
self.check_lnotab(f)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; KeyError: 20
|
||||
def test_elim_jump_to_return(self):
|
||||
# JUMP_FORWARD to RETURN --> RETURN
|
||||
def f(cond, true_value, false_value):
|
||||
@@ -546,7 +546,6 @@ class TestTranforms(BytecodeTestCase):
|
||||
self.assertEqual(len(returns), 2)
|
||||
self.check_lnotab(f)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_elim_jump_to_uncond_jump(self):
|
||||
# POP_JUMP_IF_FALSE to JUMP_FORWARD --> POP_JUMP_IF_FALSE to non-jump
|
||||
def f():
|
||||
@@ -560,7 +559,7 @@ class TestTranforms(BytecodeTestCase):
|
||||
self.check_jump_targets(f)
|
||||
self.check_lnotab(f)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; KeyError: 38
|
||||
def test_elim_jump_to_uncond_jump2(self):
|
||||
# POP_JUMP_IF_FALSE to JUMP_BACKWARD --> POP_JUMP_IF_FALSE to non-jump
|
||||
def f():
|
||||
@@ -572,7 +571,7 @@ class TestTranforms(BytecodeTestCase):
|
||||
self.check_jump_targets(f)
|
||||
self.check_lnotab(f)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; KeyError: 44
|
||||
def test_elim_jump_to_uncond_jump3(self):
|
||||
# Intentionally use two-line expressions to test issue37213.
|
||||
# POP_JUMP_IF_FALSE to POP_JUMP_IF_FALSE --> POP_JUMP_IF_FALSE to non-jump
|
||||
@@ -606,7 +605,7 @@ class TestTranforms(BytecodeTestCase):
|
||||
self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 1)
|
||||
self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 1)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; KeyError: 6
|
||||
def test_elim_jump_to_uncond_jump4(self):
|
||||
def f():
|
||||
for i in range(5):
|
||||
@@ -614,7 +613,7 @@ class TestTranforms(BytecodeTestCase):
|
||||
print(i)
|
||||
self.check_jump_targets(f)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; 611 JUMP_BACKWARD 16
|
||||
def test_elim_jump_after_return1(self):
|
||||
# Eliminate dead code: jumps immediately after returns can't be reached
|
||||
def f(cond1, cond2):
|
||||
@@ -683,7 +682,7 @@ class TestTranforms(BytecodeTestCase):
|
||||
return 6
|
||||
self.check_lnotab(f)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 2 != 1
|
||||
def test_assignment_idiom_in_comprehensions(self):
|
||||
def listcomp():
|
||||
return [y for x in a for y in [f(x)]]
|
||||
@@ -743,7 +742,7 @@ class TestTranforms(BytecodeTestCase):
|
||||
self.assertEqual(format('x = %s!', '%% %s'), 'x = %% %s!')
|
||||
self.assertEqual(format('x = %s, y = %d', 12, 34), 'x = 12, y = 34')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError: unsupported format character 'z' (0x7a) at index 3
|
||||
def test_format_errors(self):
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
'not enough arguments for format string'):
|
||||
@@ -863,14 +862,14 @@ class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
|
||||
self.addCleanup(sys.settrace, sys.gettrace())
|
||||
sys.settrace(None)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; BINARY_OP 0 (+)
|
||||
def test_load_fast_known_simple(self):
|
||||
def f():
|
||||
x = 1
|
||||
y = x + x
|
||||
self.assertInBytecode(f, 'LOAD_FAST_BORROW_LOAD_FAST_BORROW')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; RETURN_VALUE
|
||||
def test_load_fast_unknown_simple(self):
|
||||
def f():
|
||||
if condition():
|
||||
@@ -879,7 +878,7 @@ class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
|
||||
self.assertInBytecode(f, 'LOAD_FAST_CHECK')
|
||||
self.assertNotInBytecode(f, 'LOAD_FAST')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; RETURN_VALUE
|
||||
def test_load_fast_unknown_because_del(self):
|
||||
def f():
|
||||
x = 1
|
||||
@@ -888,7 +887,7 @@ class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
|
||||
self.assertInBytecode(f, 'LOAD_FAST_CHECK')
|
||||
self.assertNotInBytecode(f, 'LOAD_FAST')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; RETURN_VALUE
|
||||
def test_load_fast_known_because_parameter(self):
|
||||
def f1(x):
|
||||
print(x)
|
||||
@@ -915,7 +914,7 @@ class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
|
||||
self.assertInBytecode(f5, 'LOAD_FAST_BORROW')
|
||||
self.assertNotInBytecode(f5, 'LOAD_FAST_CHECK')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; RETURN_VALUE
|
||||
def test_load_fast_known_because_already_loaded(self):
|
||||
def f():
|
||||
if condition():
|
||||
@@ -925,7 +924,7 @@ class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
|
||||
self.assertInBytecode(f, 'LOAD_FAST_CHECK')
|
||||
self.assertInBytecode(f, 'LOAD_FAST_BORROW')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; RETURN_VALUE
|
||||
def test_load_fast_known_multiple_branches(self):
|
||||
def f():
|
||||
if condition():
|
||||
@@ -936,7 +935,7 @@ class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
|
||||
self.assertInBytecode(f, 'LOAD_FAST_BORROW')
|
||||
self.assertNotInBytecode(f, 'LOAD_FAST_CHECK')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; L5 to L6 -> L6 [1] lasti
|
||||
def test_load_fast_unknown_after_error(self):
|
||||
def f():
|
||||
try:
|
||||
@@ -948,7 +947,7 @@ class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
|
||||
# Assert that it doesn't occur in the LOAD_FAST_CHECK branch.
|
||||
self.assertInBytecode(f, 'LOAD_FAST_CHECK')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; L5 to L6 -> L6 [1] lasti
|
||||
def test_load_fast_unknown_after_error_2(self):
|
||||
def f():
|
||||
try:
|
||||
@@ -959,7 +958,7 @@ class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
|
||||
self.assertInBytecode(f, 'LOAD_FAST_CHECK')
|
||||
self.assertNotInBytecode(f, 'LOAD_FAST')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; RETURN_VALUE
|
||||
def test_load_fast_too_many_locals(self):
|
||||
# When there get to be too many locals to analyze completely,
|
||||
# later locals are all converted to LOAD_FAST_CHECK, except
|
||||
@@ -1034,7 +1033,7 @@ class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
|
||||
self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
|
||||
self.assertEqual(f.__code__.co_code, co_code)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered
|
||||
def test_setting_lineno_one_undefined(self):
|
||||
code = textwrap.dedent("""\
|
||||
def f():
|
||||
@@ -1069,7 +1068,7 @@ class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
|
||||
self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
|
||||
self.assertEqual(f.__code__.co_code, co_code)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeWarning not triggered
|
||||
def test_setting_lineno_two_undefined(self):
|
||||
code = textwrap.dedent("""\
|
||||
def f():
|
||||
|
||||
@@ -3088,7 +3088,7 @@ impl Compiler {
|
||||
let handler_normal_exit = self.new_block();
|
||||
emit!(
|
||||
self,
|
||||
PseudoInstruction::Jump {
|
||||
PseudoInstruction::JumpNoInterrupt {
|
||||
target: handler_normal_exit,
|
||||
}
|
||||
);
|
||||
@@ -3144,7 +3144,7 @@ impl Compiler {
|
||||
// Jump to finally block
|
||||
emit!(
|
||||
self,
|
||||
PseudoInstruction::Jump {
|
||||
PseudoInstruction::JumpNoInterrupt {
|
||||
target: finally_block,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use alloc::collections::VecDeque;
|
||||
use core::ops;
|
||||
|
||||
use crate::{IndexMap, IndexSet, error::InternalError};
|
||||
@@ -132,6 +133,8 @@ pub struct Block {
|
||||
pub preserve_lasti: bool,
|
||||
/// Stack depth at block entry, set by stack depth analysis
|
||||
pub start_depth: Option<u32>,
|
||||
/// Whether this block is only reachable via exception table (b_cold)
|
||||
pub cold: bool,
|
||||
}
|
||||
|
||||
impl Default for Block {
|
||||
@@ -142,6 +145,7 @@ impl Default for Block {
|
||||
except_handler: false,
|
||||
preserve_lasti: false,
|
||||
start_depth: None,
|
||||
cold: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,6 +209,8 @@ impl CodeInfo {
|
||||
// Post-codegen CFG analysis passes (flowgraph.c pipeline)
|
||||
mark_except_handlers(&mut self.blocks);
|
||||
label_exception_targets(&mut self.blocks);
|
||||
push_cold_blocks_to_end(&mut self.blocks);
|
||||
normalize_jumps(&mut self.blocks);
|
||||
|
||||
let max_stackdepth = self.max_stackdepth()?;
|
||||
let cell2arg = self.cell2arg();
|
||||
@@ -1154,6 +1160,220 @@ pub(crate) fn mark_except_handlers(blocks: &mut [Block]) {
|
||||
}
|
||||
}
|
||||
|
||||
/// flowgraph.c mark_cold
|
||||
fn mark_cold(blocks: &mut [Block]) {
|
||||
let n = blocks.len();
|
||||
let mut warm = vec![false; n];
|
||||
let mut queue = VecDeque::new();
|
||||
|
||||
warm[0] = true;
|
||||
queue.push_back(BlockIdx(0));
|
||||
|
||||
while let Some(block_idx) = queue.pop_front() {
|
||||
let block = &blocks[block_idx.idx()];
|
||||
|
||||
let has_fallthrough = block
|
||||
.instructions
|
||||
.last()
|
||||
.map(|ins| !ins.instr.is_scope_exit() && !ins.instr.is_unconditional_jump())
|
||||
.unwrap_or(true);
|
||||
if has_fallthrough && block.next != BlockIdx::NULL {
|
||||
let next_idx = block.next.idx();
|
||||
if !blocks[next_idx].except_handler && !warm[next_idx] {
|
||||
warm[next_idx] = true;
|
||||
queue.push_back(block.next);
|
||||
}
|
||||
}
|
||||
|
||||
for instr in &block.instructions {
|
||||
if instr.target != BlockIdx::NULL {
|
||||
let target_idx = instr.target.idx();
|
||||
if !blocks[target_idx].except_handler && !warm[target_idx] {
|
||||
warm[target_idx] = true;
|
||||
queue.push_back(instr.target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i, block) in blocks.iter_mut().enumerate() {
|
||||
block.cold = !warm[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// flowgraph.c push_cold_blocks_to_end
|
||||
fn push_cold_blocks_to_end(blocks: &mut Vec<Block>) {
|
||||
if blocks.len() <= 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
mark_cold(blocks);
|
||||
|
||||
// If a cold block falls through to a warm block, add an explicit jump
|
||||
let fixups: Vec<(BlockIdx, BlockIdx)> = iter_blocks(blocks)
|
||||
.filter(|(_, block)| {
|
||||
block.cold
|
||||
&& block.next != BlockIdx::NULL
|
||||
&& !blocks[block.next.idx()].cold
|
||||
&& block
|
||||
.instructions
|
||||
.last()
|
||||
.map(|ins| !ins.instr.is_scope_exit() && !ins.instr.is_unconditional_jump())
|
||||
.unwrap_or(true)
|
||||
})
|
||||
.map(|(idx, block)| (idx, block.next))
|
||||
.collect();
|
||||
|
||||
for (cold_idx, warm_next) in fixups {
|
||||
let jump_block_idx = BlockIdx(blocks.len() as u32);
|
||||
let loc = blocks[cold_idx.idx()]
|
||||
.instructions
|
||||
.last()
|
||||
.map(|i| i.location)
|
||||
.unwrap_or_default();
|
||||
let end_loc = blocks[cold_idx.idx()]
|
||||
.instructions
|
||||
.last()
|
||||
.map(|i| i.end_location)
|
||||
.unwrap_or_default();
|
||||
let mut jump_block = Block {
|
||||
cold: true,
|
||||
..Block::default()
|
||||
};
|
||||
jump_block.instructions.push(InstructionInfo {
|
||||
instr: PseudoInstruction::JumpNoInterrupt {
|
||||
target: Arg::marker(),
|
||||
}
|
||||
.into(),
|
||||
arg: OpArg::new(0),
|
||||
target: warm_next,
|
||||
location: loc,
|
||||
end_location: end_loc,
|
||||
except_handler: None,
|
||||
lineno_override: None,
|
||||
});
|
||||
jump_block.next = blocks[cold_idx.idx()].next;
|
||||
blocks[cold_idx.idx()].next = jump_block_idx;
|
||||
blocks.push(jump_block);
|
||||
}
|
||||
|
||||
// Extract cold block streaks and append at the end
|
||||
let mut cold_head: BlockIdx = BlockIdx::NULL;
|
||||
let mut cold_tail: BlockIdx = BlockIdx::NULL;
|
||||
let mut current = BlockIdx(0);
|
||||
assert!(!blocks[0].cold);
|
||||
|
||||
while current != BlockIdx::NULL {
|
||||
let next = blocks[current.idx()].next;
|
||||
if next == BlockIdx::NULL {
|
||||
break;
|
||||
}
|
||||
|
||||
if blocks[next.idx()].cold {
|
||||
let cold_start = next;
|
||||
let mut cold_end = next;
|
||||
while blocks[cold_end.idx()].next != BlockIdx::NULL
|
||||
&& blocks[blocks[cold_end.idx()].next.idx()].cold
|
||||
{
|
||||
cold_end = blocks[cold_end.idx()].next;
|
||||
}
|
||||
|
||||
let after_cold = blocks[cold_end.idx()].next;
|
||||
blocks[current.idx()].next = after_cold;
|
||||
blocks[cold_end.idx()].next = BlockIdx::NULL;
|
||||
|
||||
if cold_head == BlockIdx::NULL {
|
||||
cold_head = cold_start;
|
||||
} else {
|
||||
blocks[cold_tail.idx()].next = cold_start;
|
||||
}
|
||||
cold_tail = cold_end;
|
||||
} else {
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
if cold_head != BlockIdx::NULL {
|
||||
let mut last = current;
|
||||
while blocks[last.idx()].next != BlockIdx::NULL {
|
||||
last = blocks[last.idx()].next;
|
||||
}
|
||||
blocks[last.idx()].next = cold_head;
|
||||
}
|
||||
}
|
||||
|
||||
fn is_conditional_jump(instr: &AnyInstruction) -> bool {
|
||||
matches!(
|
||||
instr.real(),
|
||||
Some(
|
||||
Instruction::PopJumpIfFalse { .. }
|
||||
| Instruction::PopJumpIfTrue { .. }
|
||||
| Instruction::PopJumpIfNone { .. }
|
||||
| Instruction::PopJumpIfNotNone { .. }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/// flowgraph.c normalize_jumps + remove_redundant_jumps
|
||||
fn normalize_jumps(blocks: &mut [Block]) {
|
||||
let mut visit_order = Vec::new();
|
||||
let mut visited = vec![false; blocks.len()];
|
||||
let mut current = BlockIdx(0);
|
||||
while current != BlockIdx::NULL {
|
||||
visit_order.push(current);
|
||||
visited[current.idx()] = true;
|
||||
current = blocks[current.idx()].next;
|
||||
}
|
||||
|
||||
visited.fill(false);
|
||||
|
||||
for block_idx in visit_order {
|
||||
let idx = block_idx.idx();
|
||||
visited[idx] = true;
|
||||
|
||||
// Remove redundant unconditional jump to next block
|
||||
let next = blocks[idx].next;
|
||||
if next != BlockIdx::NULL {
|
||||
let last = blocks[idx].instructions.last();
|
||||
let is_jump_to_next = last.is_some_and(|ins| {
|
||||
ins.instr.is_unconditional_jump()
|
||||
&& ins.target != BlockIdx::NULL
|
||||
&& ins.target == next
|
||||
});
|
||||
if is_jump_to_next && let Some(last_instr) = blocks[idx].instructions.last_mut() {
|
||||
last_instr.instr = Instruction::Nop.into();
|
||||
last_instr.target = BlockIdx::NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert NOT_TAKEN after forward conditional jumps
|
||||
let mut insert_positions: Vec<(usize, InstructionInfo)> = Vec::new();
|
||||
for (i, ins) in blocks[idx].instructions.iter().enumerate() {
|
||||
if is_conditional_jump(&ins.instr)
|
||||
&& ins.target != BlockIdx::NULL
|
||||
&& !visited[ins.target.idx()]
|
||||
{
|
||||
insert_positions.push((
|
||||
i + 1,
|
||||
InstructionInfo {
|
||||
instr: Instruction::NotTaken.into(),
|
||||
arg: OpArg::new(0),
|
||||
target: BlockIdx::NULL,
|
||||
location: ins.location,
|
||||
end_location: ins.end_location,
|
||||
except_handler: ins.except_handler,
|
||||
lineno_override: None,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
for (pos, info) in insert_positions.into_iter().rev() {
|
||||
blocks[idx].instructions.insert(pos, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Label exception targets: walk CFG with except stack, set per-instruction
|
||||
/// handler info and block preserve_lasti flag. Converts POP_BLOCK to NOP.
|
||||
/// flowgraph.c label_exception_targets + push_except_block
|
||||
|
||||
@@ -4,11 +4,14 @@ expression: "compile_exec(\"\\\nif True and False and False:\n pass\n\")"
|
||||
---
|
||||
1 0 RESUME (0)
|
||||
1 LOAD_CONST (True)
|
||||
2 POP_JUMP_IF_FALSE (7)
|
||||
3 LOAD_CONST (False)
|
||||
4 POP_JUMP_IF_FALSE (7)
|
||||
5 LOAD_CONST (False)
|
||||
6 POP_JUMP_IF_FALSE (7)
|
||||
2 POP_JUMP_IF_FALSE (10)
|
||||
3 NOT_TAKEN
|
||||
4 LOAD_CONST (False)
|
||||
5 POP_JUMP_IF_FALSE (10)
|
||||
6 NOT_TAKEN
|
||||
7 LOAD_CONST (False)
|
||||
8 POP_JUMP_IF_FALSE (10)
|
||||
9 NOT_TAKEN
|
||||
|
||||
2 >> 7 LOAD_CONST (None)
|
||||
8 RETURN_VALUE
|
||||
2 >> 10 LOAD_CONST (None)
|
||||
11 RETURN_VALUE
|
||||
|
||||
@@ -4,13 +4,17 @@ expression: "compile_exec(\"\\\nif (True and False) or (False and True):\n pa
|
||||
---
|
||||
1 0 RESUME (0)
|
||||
1 LOAD_CONST (True)
|
||||
2 POP_JUMP_IF_FALSE (5)
|
||||
3 LOAD_CONST (False)
|
||||
4 POP_JUMP_IF_TRUE (9)
|
||||
>> 5 LOAD_CONST (False)
|
||||
6 POP_JUMP_IF_FALSE (9)
|
||||
7 LOAD_CONST (True)
|
||||
8 POP_JUMP_IF_FALSE (9)
|
||||
2 POP_JUMP_IF_FALSE (7)
|
||||
3 NOT_TAKEN
|
||||
4 LOAD_CONST (False)
|
||||
5 POP_JUMP_IF_TRUE (13)
|
||||
6 NOT_TAKEN
|
||||
>> 7 LOAD_CONST (False)
|
||||
8 POP_JUMP_IF_FALSE (13)
|
||||
9 NOT_TAKEN
|
||||
10 LOAD_CONST (True)
|
||||
11 POP_JUMP_IF_FALSE (13)
|
||||
12 NOT_TAKEN
|
||||
|
||||
2 >> 9 LOAD_CONST (None)
|
||||
10 RETURN_VALUE
|
||||
2 >> 13 LOAD_CONST (None)
|
||||
14 RETURN_VALUE
|
||||
|
||||
@@ -4,11 +4,14 @@ expression: "compile_exec(\"\\\nif True or False or False:\n pass\n\")"
|
||||
---
|
||||
1 0 RESUME (0)
|
||||
1 LOAD_CONST (True)
|
||||
2 POP_JUMP_IF_TRUE (7)
|
||||
3 LOAD_CONST (False)
|
||||
4 POP_JUMP_IF_TRUE (7)
|
||||
5 LOAD_CONST (False)
|
||||
6 POP_JUMP_IF_FALSE (7)
|
||||
2 POP_JUMP_IF_TRUE (10)
|
||||
3 NOT_TAKEN
|
||||
4 LOAD_CONST (False)
|
||||
5 POP_JUMP_IF_TRUE (10)
|
||||
6 NOT_TAKEN
|
||||
7 LOAD_CONST (False)
|
||||
8 POP_JUMP_IF_FALSE (10)
|
||||
9 NOT_TAKEN
|
||||
|
||||
2 >> 7 LOAD_CONST (None)
|
||||
8 RETURN_VALUE
|
||||
2 >> 10 LOAD_CONST (None)
|
||||
11 RETURN_VALUE
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: crates/codegen/src/compile.rs
|
||||
assertion_line: 9071
|
||||
expression: "compile_exec(\"\\\nx = Test() and False or False\n\")"
|
||||
---
|
||||
1 0 RESUME (0)
|
||||
@@ -8,13 +7,15 @@ expression: "compile_exec(\"\\\nx = Test() and False or False\n\")"
|
||||
2 PUSH_NULL
|
||||
3 CALL (0)
|
||||
4 COPY (1)
|
||||
5 POP_JUMP_IF_FALSE (10)
|
||||
6 POP_TOP
|
||||
7 LOAD_CONST (False)
|
||||
8 COPY (1)
|
||||
9 POP_JUMP_IF_TRUE (12)
|
||||
>> 10 POP_TOP
|
||||
11 LOAD_CONST (False)
|
||||
>> 12 STORE_NAME (1, x)
|
||||
13 LOAD_CONST (None)
|
||||
14 RETURN_VALUE
|
||||
5 POP_JUMP_IF_FALSE (12)
|
||||
6 NOT_TAKEN
|
||||
7 POP_TOP
|
||||
8 LOAD_CONST (False)
|
||||
9 COPY (1)
|
||||
10 POP_JUMP_IF_TRUE (14)
|
||||
11 NOT_TAKEN
|
||||
>> 12 POP_TOP
|
||||
13 LOAD_CONST (False)
|
||||
>> 14 STORE_NAME (1, x)
|
||||
15 LOAD_CONST (None)
|
||||
16 RETURN_VALUE
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: crates/codegen/src/compile.rs
|
||||
assertion_line: 9046
|
||||
expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')):\n with self.subTest(type=type(stop_exc)):\n try:\n async with egg():\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 RESUME (0)
|
||||
@@ -19,7 +18,7 @@ expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIter
|
||||
10 CALL (1)
|
||||
11 BUILD_TUPLE (2)
|
||||
12 GET_ITER
|
||||
>> 13 FOR_ITER (141)
|
||||
>> 13 FOR_ITER (49)
|
||||
14 STORE_FAST (0, stop_exc)
|
||||
|
||||
3 15 LOAD_GLOBAL (2, self)
|
||||
@@ -49,116 +48,121 @@ expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIter
|
||||
38 CALL (0)
|
||||
39 GET_AWAITABLE (1)
|
||||
40 LOAD_CONST (None)
|
||||
>> 41 SEND (46)
|
||||
>> 41 SEND (45)
|
||||
42 YIELD_VALUE (1)
|
||||
43 RESUME (3)
|
||||
44 JUMP_BACKWARD_NO_INTERRUPT(41)
|
||||
45 CLEANUP_THROW
|
||||
>> 46 END_SEND
|
||||
47 POP_TOP
|
||||
>> 45 END_SEND
|
||||
46 POP_TOP
|
||||
|
||||
6 48 LOAD_FAST (0, stop_exc)
|
||||
49 RAISE_VARARGS (Raise)
|
||||
6 47 LOAD_FAST (0, stop_exc)
|
||||
48 RAISE_VARARGS (Raise)
|
||||
|
||||
5 50 PUSH_NULL
|
||||
3 >> 49 END_FOR
|
||||
50 POP_ITER
|
||||
51 LOAD_CONST (None)
|
||||
52 LOAD_CONST (None)
|
||||
53 LOAD_CONST (None)
|
||||
54 CALL (3)
|
||||
55 GET_AWAITABLE (2)
|
||||
52 RETURN_VALUE
|
||||
|
||||
5 53 CLEANUP_THROW
|
||||
54 JUMP_BACKWARD_NO_INTERRUPT(45)
|
||||
55 PUSH_NULL
|
||||
56 LOAD_CONST (None)
|
||||
>> 57 SEND (62)
|
||||
58 YIELD_VALUE (1)
|
||||
59 RESUME (3)
|
||||
60 JUMP_BACKWARD_NO_INTERRUPT(57)
|
||||
61 CLEANUP_THROW
|
||||
>> 62 END_SEND
|
||||
63 POP_TOP
|
||||
64 JUMP_FORWARD (86)
|
||||
65 PUSH_EXC_INFO
|
||||
66 WITH_EXCEPT_START
|
||||
67 GET_AWAITABLE (2)
|
||||
68 LOAD_CONST (None)
|
||||
>> 69 SEND (74)
|
||||
70 YIELD_VALUE (1)
|
||||
71 RESUME (3)
|
||||
72 JUMP_BACKWARD_NO_INTERRUPT(69)
|
||||
73 CLEANUP_THROW
|
||||
>> 74 END_SEND
|
||||
75 TO_BOOL
|
||||
76 POP_JUMP_IF_TRUE (78)
|
||||
77 RERAISE (2)
|
||||
>> 78 POP_TOP
|
||||
79 POP_EXCEPT
|
||||
80 POP_TOP
|
||||
81 POP_TOP
|
||||
82 JUMP_FORWARD (86)
|
||||
83 COPY (3)
|
||||
84 POP_EXCEPT
|
||||
85 RERAISE (1)
|
||||
>> 86 JUMP_FORWARD (112)
|
||||
87 PUSH_EXC_INFO
|
||||
57 LOAD_CONST (None)
|
||||
58 LOAD_CONST (None)
|
||||
59 CALL (3)
|
||||
60 GET_AWAITABLE (2)
|
||||
61 LOAD_CONST (None)
|
||||
>> 62 SEND (67)
|
||||
63 YIELD_VALUE (1)
|
||||
64 RESUME (3)
|
||||
65 JUMP_BACKWARD_NO_INTERRUPT(62)
|
||||
66 CLEANUP_THROW
|
||||
>> 67 END_SEND
|
||||
68 POP_TOP
|
||||
69 JUMP_FORWARD (92)
|
||||
70 PUSH_EXC_INFO
|
||||
71 WITH_EXCEPT_START
|
||||
72 GET_AWAITABLE (2)
|
||||
73 LOAD_CONST (None)
|
||||
>> 74 SEND (79)
|
||||
75 YIELD_VALUE (1)
|
||||
76 RESUME (3)
|
||||
77 JUMP_BACKWARD_NO_INTERRUPT(74)
|
||||
78 CLEANUP_THROW
|
||||
>> 79 END_SEND
|
||||
80 TO_BOOL
|
||||
81 POP_JUMP_IF_TRUE (84)
|
||||
82 NOT_TAKEN
|
||||
83 RERAISE (2)
|
||||
>> 84 POP_TOP
|
||||
85 POP_EXCEPT
|
||||
86 POP_TOP
|
||||
87 POP_TOP
|
||||
88 JUMP_FORWARD (92)
|
||||
89 COPY (3)
|
||||
90 POP_EXCEPT
|
||||
91 RERAISE (1)
|
||||
>> 92 JUMP_FORWARD (119)
|
||||
93 PUSH_EXC_INFO
|
||||
|
||||
7 88 LOAD_GLOBAL (6, Exception)
|
||||
89 CHECK_EXC_MATCH
|
||||
90 POP_JUMP_IF_FALSE (108)
|
||||
91 STORE_FAST (1, ex)
|
||||
7 94 LOAD_GLOBAL (6, Exception)
|
||||
95 CHECK_EXC_MATCH
|
||||
96 POP_JUMP_IF_FALSE (115)
|
||||
97 NOT_TAKEN
|
||||
98 STORE_FAST (1, ex)
|
||||
|
||||
8 92 LOAD_GLOBAL (2, self)
|
||||
93 LOAD_ATTR (15, assertIs, method=true)
|
||||
94 LOAD_FAST (1, ex)
|
||||
95 LOAD_FAST (0, stop_exc)
|
||||
96 CALL (2)
|
||||
97 POP_TOP
|
||||
98 JUMP_FORWARD (103)
|
||||
99 LOAD_CONST (None)
|
||||
100 STORE_FAST (1, ex)
|
||||
101 DELETE_FAST (1, ex)
|
||||
102 RAISE_VARARGS (ReraiseFromStack)
|
||||
>> 103 POP_EXCEPT
|
||||
104 LOAD_CONST (None)
|
||||
105 STORE_FAST (1, ex)
|
||||
106 DELETE_FAST (1, ex)
|
||||
107 JUMP_FORWARD (120)
|
||||
>> 108 RAISE_VARARGS (ReraiseFromStack)
|
||||
109 COPY (3)
|
||||
110 POP_EXCEPT
|
||||
111 RAISE_VARARGS (ReraiseFromStack)
|
||||
8 99 LOAD_GLOBAL (2, self)
|
||||
100 LOAD_ATTR (15, assertIs, method=true)
|
||||
101 LOAD_FAST (1, ex)
|
||||
102 LOAD_FAST (0, stop_exc)
|
||||
103 CALL (2)
|
||||
104 POP_TOP
|
||||
105 JUMP_BACKWARD_NO_INTERRUPT(110)
|
||||
106 LOAD_CONST (None)
|
||||
107 STORE_FAST (1, ex)
|
||||
108 DELETE_FAST (1, ex)
|
||||
109 RAISE_VARARGS (ReraiseFromStack)
|
||||
>> 110 POP_EXCEPT
|
||||
111 LOAD_CONST (None)
|
||||
112 STORE_FAST (1, ex)
|
||||
113 DELETE_FAST (1, ex)
|
||||
114 JUMP_BACKWARD_NO_INTERRUPT(127)
|
||||
>> 115 RAISE_VARARGS (ReraiseFromStack)
|
||||
116 COPY (3)
|
||||
117 POP_EXCEPT
|
||||
118 RAISE_VARARGS (ReraiseFromStack)
|
||||
|
||||
10 >> 112 LOAD_GLOBAL (2, self)
|
||||
113 LOAD_ATTR (17, fail, method=true)
|
||||
114 LOAD_FAST_BORROW (0, stop_exc)
|
||||
115 FORMAT_SIMPLE
|
||||
116 LOAD_CONST (" was suppressed")
|
||||
117 BUILD_STRING (2)
|
||||
118 CALL (1)
|
||||
119 POP_TOP
|
||||
10 >> 119 LOAD_GLOBAL (2, self)
|
||||
120 LOAD_ATTR (17, fail, method=true)
|
||||
121 LOAD_FAST_BORROW (0, stop_exc)
|
||||
122 FORMAT_SIMPLE
|
||||
123 LOAD_CONST (" was suppressed")
|
||||
124 BUILD_STRING (2)
|
||||
125 CALL (1)
|
||||
126 POP_TOP
|
||||
|
||||
3 >> 120 PUSH_NULL
|
||||
121 LOAD_CONST (None)
|
||||
122 LOAD_CONST (None)
|
||||
123 LOAD_CONST (None)
|
||||
124 CALL (3)
|
||||
125 POP_TOP
|
||||
126 JUMP_FORWARD (140)
|
||||
127 PUSH_EXC_INFO
|
||||
128 WITH_EXCEPT_START
|
||||
129 TO_BOOL
|
||||
130 POP_JUMP_IF_TRUE (132)
|
||||
131 RERAISE (2)
|
||||
>> 132 POP_TOP
|
||||
133 POP_EXCEPT
|
||||
134 POP_TOP
|
||||
135 POP_TOP
|
||||
136 JUMP_FORWARD (140)
|
||||
137 COPY (3)
|
||||
138 POP_EXCEPT
|
||||
139 RERAISE (1)
|
||||
>> 140 JUMP_BACKWARD (13)
|
||||
>> 141 END_FOR
|
||||
142 POP_ITER
|
||||
143 LOAD_CONST (None)
|
||||
144 RETURN_VALUE
|
||||
3 >> 127 PUSH_NULL
|
||||
128 LOAD_CONST (None)
|
||||
129 LOAD_CONST (None)
|
||||
130 LOAD_CONST (None)
|
||||
131 CALL (3)
|
||||
132 POP_TOP
|
||||
133 JUMP_FORWARD (148)
|
||||
134 PUSH_EXC_INFO
|
||||
135 WITH_EXCEPT_START
|
||||
136 TO_BOOL
|
||||
137 POP_JUMP_IF_TRUE (140)
|
||||
138 NOT_TAKEN
|
||||
139 RERAISE (2)
|
||||
>> 140 POP_TOP
|
||||
141 POP_EXCEPT
|
||||
142 POP_TOP
|
||||
143 POP_TOP
|
||||
144 JUMP_FORWARD (148)
|
||||
145 COPY (3)
|
||||
146 POP_EXCEPT
|
||||
147 RERAISE (1)
|
||||
>> 148 JUMP_BACKWARD (13)
|
||||
|
||||
2 MAKE_FUNCTION
|
||||
3 STORE_NAME (0, test)
|
||||
|
||||
@@ -879,6 +879,7 @@ impl InstructionMetadata for Instruction {
|
||||
Self::MatchMapping => w!(MATCH_MAPPING),
|
||||
Self::MatchSequence => w!(MATCH_SEQUENCE),
|
||||
Self::Nop => w!(NOP),
|
||||
Self::NotTaken => w!(NOT_TAKEN),
|
||||
Self::PopExcept => w!(POP_EXCEPT),
|
||||
Self::PopJumpIfFalse { target } => w!(POP_JUMP_IF_FALSE, target),
|
||||
Self::PopJumpIfTrue { target } => w!(POP_JUMP_IF_TRUE, target),
|
||||
|
||||
@@ -614,7 +614,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Instruction::Nop => Ok(()),
|
||||
Instruction::Nop | Instruction::NotTaken => Ok(()),
|
||||
Instruction::PopJumpIfFalse { target } => {
|
||||
let cond = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
|
||||
let val = self.boolean_val(cond)?;
|
||||
|
||||
@@ -138,7 +138,8 @@ fn extract_annotations_from_annotate_code(code: &CodeObject) -> HashMap<Wtf8Buf,
|
||||
Instruction::Resume { .. }
|
||||
| Instruction::LoadFast(_)
|
||||
| Instruction::CompareOp { .. }
|
||||
| Instruction::ExtendedArg => {
|
||||
| Instruction::ExtendedArg
|
||||
| Instruction::NotTaken => {
|
||||
// Ignore these instructions for annotation extraction
|
||||
}
|
||||
Instruction::ReturnValue => {
|
||||
@@ -184,8 +185,8 @@ impl StackMachine {
|
||||
names: &[String],
|
||||
) -> ControlFlow<()> {
|
||||
match instruction {
|
||||
Instruction::Resume { .. } => {
|
||||
// No-op for JIT tests - just marks function entry point
|
||||
Instruction::Resume { .. } | Instruction::NotTaken => {
|
||||
// No-op for JIT tests
|
||||
}
|
||||
Instruction::LoadConst { idx } => {
|
||||
let idx = idx.get(arg);
|
||||
|
||||
Reference in New Issue
Block a user