From 4c220dd25e0210a0ebf894dc1c27f5e006a6882e Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Mon, 23 Feb 2026 22:45:50 +0900 Subject: [PATCH 1/2] 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. --- crates/codegen/src/compile.rs | 4 +- crates/codegen/src/ir.rs | 220 ++++++++++++++++++ ...thon_codegen__compile__tests__if_ands.snap | 17 +- ...hon_codegen__compile__tests__if_mixed.snap | 22 +- ...ython_codegen__compile__tests__if_ors.snap | 17 +- ...degen__compile__tests__nested_bool_op.snap | 23 +- ...pile__tests__nested_double_async_with.snap | 206 ++++++++-------- .../compiler-core/src/bytecode/instruction.rs | 1 + crates/jit/src/instructions.rs | 2 +- crates/jit/tests/common.rs | 7 +- 10 files changed, 378 insertions(+), 141 deletions(-) diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index 205facd65bd..2bed4cde780 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -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, } ); diff --git a/crates/codegen/src/ir.rs b/crates/codegen/src/ir.rs index e44c7223f2c..651eb59107c 100644 --- a/crates/codegen/src/ir.rs +++ b/crates/codegen/src/ir.rs @@ -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, + /// 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) { + 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 diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap index 26583f5da0f..920754e8ec0 100644 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap +++ b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap @@ -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 diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap index 21976b257ef..f8e18abf2cf 100644 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap +++ b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap @@ -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 diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap index e1d41377db7..d0b7028e3d8 100644 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap +++ b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap @@ -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 diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_bool_op.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_bool_op.snap index 5b9a2182bdf..dea5f5386ff 100644 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_bool_op.snap +++ b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_bool_op.snap @@ -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 diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap index c23ef890daf..f80ef5c97fe 100644 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap +++ b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap @@ -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) diff --git a/crates/compiler-core/src/bytecode/instruction.rs b/crates/compiler-core/src/bytecode/instruction.rs index 7ac117cb54c..6f43e79c168 100644 --- a/crates/compiler-core/src/bytecode/instruction.rs +++ b/crates/compiler-core/src/bytecode/instruction.rs @@ -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), diff --git a/crates/jit/src/instructions.rs b/crates/jit/src/instructions.rs index 19931038fe0..5e760d05449 100644 --- a/crates/jit/src/instructions.rs +++ b/crates/jit/src/instructions.rs @@ -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)?; diff --git a/crates/jit/tests/common.rs b/crates/jit/tests/common.rs index a862d9eef69..d47aac60aec 100644 --- a/crates/jit/tests/common.rs +++ b/crates/jit/tests/common.rs @@ -138,7 +138,8 @@ fn extract_annotations_from_annotate_code(code: &CodeObject) -> HashMap { + | 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); From 38e342aad62e2ba89bf6ad5a2dc4782048eaf47d Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Tue, 24 Feb 2026 00:50:35 +0900 Subject: [PATCH 2/2] mark test_peepholer --- Lib/test/test_peepholer.py | 45 +++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index c5c10cb5318..3f12ce135af 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -82,7 +82,7 @@ def check_lnotab(self, code): # 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 @@ def f(): 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 @@ def test_pack_unpack(self): 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 @@ def negzero(): 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 @@ def f(x): 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 @@ def f(cond, true_value, false_value): 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 @@ def f(): 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 @@ def f(): 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 @@ def f(a, b, c): 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 @@ def f(): 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 @@ def f(x): 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 @@ def format(fmt, *values): 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 @@ def setUp(self): 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 @@ def f(): 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 @@ def f(): 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 @@ def f5(x=0): 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 @@ def f(): 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 @@ def f(): 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 @@ def f(): # 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 @@ def f(): 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 @@ def trace(frame, event, arg): 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 @@ def trace(frame, event, arg): 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():