Skip to content

Commit 9792d68

Browse files
committed
Align CPython 3.14 LOAD_GLOBAL null-bit and RERAISE semantics
1 parent 2912aab commit 9792d68

File tree

9 files changed

+367
-308
lines changed

9 files changed

+367
-308
lines changed

crates/codegen/src/compile.rs

Lines changed: 23 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2010,7 +2010,10 @@ impl Compiler {
20102010
NameOp::Global => {
20112011
let idx = self.get_global_name_index(&name);
20122012
let op = match usage {
2013-
NameUsage::Load => Instruction::LoadGlobal,
2013+
NameUsage::Load => {
2014+
self.emit_load_global(idx, false);
2015+
return Ok(());
2016+
}
20142017
NameUsage::Store => Instruction::StoreGlobal,
20152018
NameUsage::Delete => Instruction::DeleteGlobal,
20162019
};
@@ -2973,24 +2976,14 @@ impl Compiler {
29732976
emit!(self, Instruction::PopExcept);
29742977

29752978
// RERAISE 0: re-raise the original exception to outer handler
2976-
emit!(
2977-
self,
2978-
Instruction::RaiseVarargs {
2979-
kind: bytecode::RaiseKind::ReraiseFromStack
2980-
}
2981-
);
2979+
emit!(self, Instruction::Reraise { depth: 0 });
29822980
}
29832981

29842982
if let Some(cleanup) = finally_cleanup_block {
29852983
self.switch_to_block(cleanup);
29862984
emit!(self, Instruction::Copy { index: 3_u32 });
29872985
emit!(self, Instruction::PopExcept);
2988-
emit!(
2989-
self,
2990-
Instruction::RaiseVarargs {
2991-
kind: bytecode::RaiseKind::ReraiseFromStack
2992-
}
2993-
);
2986+
emit!(self, Instruction::Reraise { depth: 1 });
29942987
}
29952988

29962989
self.switch_to_block(end_block);
@@ -3128,12 +3121,7 @@ impl Compiler {
31283121
// Stack at entry: [prev_exc (at handler_depth), lasti, exc]
31293122
// This RERAISE is within ExceptionHandler scope, so it routes to cleanup_block
31303123
// which does COPY 3; POP_EXCEPT; RERAISE
3131-
emit!(
3132-
self,
3133-
Instruction::RaiseVarargs {
3134-
kind: bytecode::RaiseKind::ReraiseFromStack,
3135-
}
3136-
);
3124+
emit!(self, Instruction::Reraise { depth: 1 });
31373125
}
31383126

31393127
// Switch to normal exit block - this is where handler body success continues
@@ -3181,12 +3169,7 @@ impl Compiler {
31813169
// RERAISE 0
31823170
// Stack: [prev_exc, exc] - exception is on stack from PUSH_EXC_INFO
31833171
// NOTE: We emit RERAISE 0 BEFORE popping fblock so it is within cleanup handler scope
3184-
emit!(
3185-
self,
3186-
Instruction::RaiseVarargs {
3187-
kind: bytecode::RaiseKind::ReraiseFromStack,
3188-
}
3189-
);
3172+
emit!(self, Instruction::Reraise { depth: 0 });
31903173

31913174
// Pop EXCEPTION_HANDLER fblock
31923175
// Pop after RERAISE so the instruction has the correct exception handler
@@ -3200,12 +3183,7 @@ impl Compiler {
32003183
self.switch_to_block(cleanup_block);
32013184
emit!(self, Instruction::Copy { index: 3_u32 });
32023185
emit!(self, Instruction::PopExcept);
3203-
emit!(
3204-
self,
3205-
Instruction::RaiseVarargs {
3206-
kind: bytecode::RaiseKind::ReraiseFromStack,
3207-
}
3208-
);
3186+
emit!(self, Instruction::Reraise { depth: 1 });
32093187

32103188
// We successfully ran the try block:
32113189
// else:
@@ -3277,12 +3255,7 @@ impl Compiler {
32773255

32783256
// RERAISE 0: re-raise the original exception to outer handler
32793257
// Stack: [lasti, prev_exc, exc] - exception is on top
3280-
emit!(
3281-
self,
3282-
Instruction::RaiseVarargs {
3283-
kind: bytecode::RaiseKind::ReraiseFromStack,
3284-
}
3285-
);
3258+
emit!(self, Instruction::Reraise { depth: 0 });
32863259
}
32873260

32883261
// finally cleanup block
@@ -3295,12 +3268,7 @@ impl Compiler {
32953268
// POP_EXCEPT: restore prev_exc as current exception
32963269
emit!(self, Instruction::PopExcept);
32973270
// RERAISE 1: reraise with lasti from stack
3298-
emit!(
3299-
self,
3300-
Instruction::RaiseVarargs {
3301-
kind: bytecode::RaiseKind::ReraiseFromStack,
3302-
}
3303-
);
3271+
emit!(self, Instruction::Reraise { depth: 1 });
33043272
}
33053273

33063274
// End block - continuation point after try-finally
@@ -3673,23 +3641,13 @@ impl Compiler {
36733641

36743642
emit!(self, Instruction::Copy { index: 2_u32 });
36753643
emit!(self, Instruction::PopExcept);
3676-
emit!(
3677-
self,
3678-
Instruction::RaiseVarargs {
3679-
kind: bytecode::RaiseKind::ReraiseFromStack
3680-
}
3681-
);
3644+
emit!(self, Instruction::Reraise { depth: 0 });
36823645

36833646
if let Some(cleanup) = finally_cleanup_block {
36843647
self.switch_to_block(cleanup);
36853648
emit!(self, Instruction::Copy { index: 3_u32 });
36863649
emit!(self, Instruction::PopExcept);
3687-
emit!(
3688-
self,
3689-
Instruction::RaiseVarargs {
3690-
kind: bytecode::RaiseKind::ReraiseFromStack
3691-
}
3692-
);
3650+
emit!(self, Instruction::Reraise { depth: 1 });
36933651
}
36943652
}
36953653

@@ -4036,7 +3994,7 @@ impl Compiler {
40363994
emit!(self, Instruction::LoadDeref(idx));
40373995
} else {
40383996
let cond_annotations_name = self.name("__conditional_annotations__");
4039-
emit!(self, Instruction::LoadGlobal(cond_annotations_name));
3997+
self.emit_load_global(cond_annotations_name, false);
40403998
}
40413999
// CONTAINS_OP (in)
40424000
emit!(self, Instruction::ContainsOp(bytecode::Invert::No));
@@ -4528,7 +4486,7 @@ impl Compiler {
45284486

45294487
// Load (global) __name__ and store as __module__
45304488
let dunder_name = self.name("__name__");
4531-
emit!(self, Instruction::LoadGlobal(dunder_name));
4489+
self.emit_load_global(dunder_name, false);
45324490
let dunder_module = self.name("__module__");
45334491
emit!(self, Instruction::StoreName(dunder_module));
45344492

@@ -7994,12 +7952,7 @@ impl Compiler {
79947952
emit!(self, Instruction::StoreFast(idx));
79957953
}
79967954
// Re-raise the exception
7997-
emit!(
7998-
self,
7999-
Instruction::RaiseVarargs {
8000-
kind: bytecode::RaiseKind::ReraiseFromStack
8001-
}
8002-
);
7955+
emit!(self, Instruction::Reraise { depth: 0 });
80037956

80047957
// Normal end path
80057958
self.switch_to_block(end_block);
@@ -8118,6 +8071,13 @@ impl Compiler {
81188071
self.emit_arg(encoded, |arg| Instruction::LoadAttr { idx: arg })
81198072
}
81208073

8074+
/// Emit LOAD_GLOBAL.
8075+
/// Encodes: (name_idx << 1) | push_null_bit
8076+
fn emit_load_global(&mut self, name_idx: u32, push_null: bool) {
8077+
let encoded = (name_idx << 1) | u32::from(push_null);
8078+
self.emit_arg(encoded, Instruction::LoadGlobal);
8079+
}
8080+
81218081
/// Emit LOAD_SUPER_ATTR for 2-arg super().attr access.
81228082
/// Encodes: (name_idx << 2) | 0b10 (method=0, class=1)
81238083
fn emit_load_super_attr(&mut self, name_idx: u32) {

crates/codegen/src/ir.rs

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ impl CodeInfo {
213213
label_exception_targets(&mut self.blocks);
214214
push_cold_blocks_to_end(&mut self.blocks);
215215
normalize_jumps(&mut self.blocks);
216+
self.optimize_load_global_push_null();
216217

217218
let max_stackdepth = self.max_stackdepth()?;
218219
let cell2arg = self.cell2arg();
@@ -264,12 +265,12 @@ impl CodeInfo {
264265
}
265266
for block_idx in block_order {
266267
let bi = block_idx.idx();
267-
let mut src_instrs = core::mem::take(&mut blocks[bi].instructions);
268-
let mut kept = Vec::with_capacity(src_instrs.len());
268+
let mut src_instructions = core::mem::take(&mut blocks[bi].instructions);
269+
let mut kept = Vec::with_capacity(src_instructions.len());
269270
let mut prev_lineno = -1i32;
270271

271-
for src in 0..src_instrs.len() {
272-
let instr = src_instrs[src];
272+
for src in 0..src_instructions.len() {
273+
let instr = src_instructions[src];
273274
let lineno = instr
274275
.lineno_override
275276
.unwrap_or_else(|| instr.location.line.get() as i32);
@@ -281,14 +282,14 @@ impl CodeInfo {
281282
remove = true;
282283
}
283284
// Remove if the next instruction has same line or no line.
284-
else if src < src_instrs.len() - 1 {
285-
let next_lineno = src_instrs[src + 1]
285+
else if src < src_instructions.len() - 1 {
286+
let next_lineno = src_instructions[src + 1]
286287
.lineno_override
287-
.unwrap_or_else(|| src_instrs[src + 1].location.line.get() as i32);
288+
.unwrap_or_else(|| src_instructions[src + 1].location.line.get() as i32);
288289
if next_lineno == lineno {
289290
remove = true;
290291
} else if next_lineno < 0 {
291-
src_instrs[src + 1].lineno_override = Some(lineno);
292+
src_instructions[src + 1].lineno_override = Some(lineno);
292293
remove = true;
293294
}
294295
}
@@ -701,6 +702,34 @@ impl CodeInfo {
701702
}
702703
}
703704

705+
/// CPython flowgraph.c:
706+
/// LOAD_GLOBAL <even> + PUSH_NULL -> LOAD_GLOBAL <odd>, NOP
707+
fn optimize_load_global_push_null(&mut self) {
708+
for block in &mut self.blocks {
709+
let mut i = 0;
710+
while i + 1 < block.instructions.len() {
711+
let curr = &block.instructions[i];
712+
let next = &block.instructions[i + 1];
713+
714+
let (Some(Instruction::LoadGlobal(_)), Some(Instruction::PushNull)) =
715+
(curr.instr.real(), next.instr.real())
716+
else {
717+
i += 1;
718+
continue;
719+
};
720+
721+
let oparg = u32::from(block.instructions[i].arg);
722+
if (oparg & 1) != 0 {
723+
i += 1;
724+
continue;
725+
}
726+
727+
block.instructions[i].arg = OpArg::new(oparg | 1);
728+
block.instructions.remove(i + 1);
729+
}
730+
}
731+
}
732+
704733
/// Convert LOAD_CONST for small integers to LOAD_SMALL_INT
705734
/// maybe_instr_make_load_smallint
706735
fn convert_to_load_small_int(&mut self) {

0 commit comments

Comments
 (0)