Split blocks at branch points so each block has at most one branch (conditional/unconditional jump) as its last instruction. This matches CPython's CFG structure where each basic block has one exit.
(blocks: &mut Vec<Block>)
| 2245 | /// (conditional/unconditional jump) as its last instruction. |
| 2246 | /// This matches CPython's CFG structure where each basic block has one exit. |
| 2247 | fn split_blocks_at_jumps(blocks: &mut Vec<Block>) { |
| 2248 | let mut bi = 0; |
| 2249 | while bi < blocks.len() { |
| 2250 | // Find the first jump/branch instruction in the block |
| 2251 | let split_at = { |
| 2252 | let block = &blocks[bi]; |
| 2253 | let mut found = None; |
| 2254 | for (i, ins) in block.instructions.iter().enumerate() { |
| 2255 | if is_conditional_jump(&ins.instr) |
| 2256 | || ins.instr.is_unconditional_jump() |
| 2257 | || ins.instr.is_scope_exit() |
| 2258 | { |
| 2259 | if i + 1 < block.instructions.len() { |
| 2260 | found = Some(i + 1); |
| 2261 | } |
| 2262 | break; |
| 2263 | } |
| 2264 | } |
| 2265 | found |
| 2266 | }; |
| 2267 | if let Some(pos) = split_at { |
| 2268 | let new_block_idx = BlockIdx(blocks.len() as u32); |
| 2269 | let tail: Vec<InstructionInfo> = blocks[bi].instructions.drain(pos..).collect(); |
| 2270 | let old_next = blocks[bi].next; |
| 2271 | let cold = blocks[bi].cold; |
| 2272 | blocks[bi].next = new_block_idx; |
| 2273 | blocks.push(Block { |
| 2274 | instructions: tail, |
| 2275 | next: old_next, |
| 2276 | cold, |
| 2277 | ..Block::default() |
| 2278 | }); |
| 2279 | // Don't increment bi - re-check current block (it might still have issues) |
| 2280 | } else { |
| 2281 | bi += 1; |
| 2282 | } |
| 2283 | } |
| 2284 | } |
| 2285 | |
| 2286 | /// Jump threading: when a block's last jump targets a block whose first |
| 2287 | /// instruction is an unconditional jump, redirect to the final target. |
no test coverage detected