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:
Jeong, YunWon
2026-02-24 08:53:07 +09:00
committed by GitHub
parent 0244657770
commit dc7cd26c3c
11 changed files with 400 additions and 164 deletions

View File

@@ -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():

View File

@@ -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,
}
);

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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),

View File

@@ -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)?;

View File

@@ -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);