Skip to content

Commit 67de59a

Browse files
committed
Use relative jump offsets and fix bytecode layout
- Convert jump arguments from absolute to relative offsets in frame.rs, monitoring.rs, and stack_analysis - Add jump_relative_forward/backward helpers to ExecutingFrame - Resolve pseudo jump instructions before offset fixpoint loop - Emit NOP for break, continue, pass to match line-tracing - Fix async for: emit EndAsyncFor with correct target, add NotTaken - Fix comprehension if-cleanup to use separate block - Fix super() source range for multi-line calls - Fix NOP removal to preserve line-marker NOPs - Fix InstrumentedLine cache skipping after re-dispatch - Match InstrumentedResume/YieldValue in yield_from_target - Remove CALL_FUNCTION_EX cache entry from opcode.py - Remove resolved expectedFailure markers
1 parent e1218b1 commit 67de59a

18 files changed

+722
-372
lines changed

Lib/opcode.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,6 @@
9090
"counter": 1,
9191
"func_version": 2,
9292
},
93-
"CALL_FUNCTION_EX": {
94-
"counter": 1,
95-
},
9693
"STORE_SUBSCR": {
9794
"counter": 1,
9895
},

Lib/test/test_dis.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2288,7 +2288,6 @@ def last_item(iterable):
22882288
self.assertEqual(14, instructions[6].offset)
22892289
self.assertEqual(8, instructions[6].start_offset)
22902290

2291-
@unittest.expectedFailure # TODO: RUSTPYTHON; no inline caches
22922291
def test_cache_offset_and_end_offset(self):
22932292
code = bytes([
22942293
opcode.opmap["LOAD_GLOBAL"], 0x01,
@@ -2587,7 +2586,6 @@ def f():
25872586
with contextlib.redirect_stderr(io.StringIO()):
25882587
_ = self.invoke_dis('--unknown')
25892588

2590-
@unittest.expectedFailure # TODO: RUSTPYTHON
25912589
def test_show_cache(self):
25922590
# test 'python -m dis -C/--show-caches'
25932591
source = 'print()'

Lib/test/test_monitoring.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1671,7 +1671,6 @@ def func():
16711671
('return', 'func', None),
16721672
('line', 'get_events', 11)])
16731673

1674-
@unittest.expectedFailure # TODO: RUSTPYTHON; - bytecode layout differs from CPython
16751674
def test_while_offset_consistency(self):
16761675

16771676
def foo(n=0):
@@ -1689,7 +1688,6 @@ def foo(n=0):
16891688
in_loop,
16901689
exit_loop])
16911690

1692-
@unittest.expectedFailure # TODO: RUSTPYTHON; - bytecode layout differs from CPython
16931691
def test_async_for(self):
16941692

16951693
def func():
@@ -2125,7 +2123,6 @@ def test_get_local_events_uninitialized(self):
21252123

21262124
class TestRegressions(MonitoringTestBase, unittest.TestCase):
21272125

2128-
@unittest.expectedFailure # TODO: RUSTPYTHON; + inner
21292126
def test_105162(self):
21302127
caught = None
21312128

Lib/test/test_peepholer.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,6 @@ def f():
571571
self.check_jump_targets(f)
572572
self.check_lnotab(f)
573573

574-
@unittest.expectedFailure # TODO: RUSTPYTHON; KeyError: 44
575574
def test_elim_jump_to_uncond_jump3(self):
576575
# Intentionally use two-line expressions to test issue37213.
577576
# POP_JUMP_IF_FALSE to POP_JUMP_IF_FALSE --> POP_JUMP_IF_FALSE to non-jump

crates/codegen/src/compile.rs

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,7 +1433,7 @@ impl Compiler {
14331433
if matches!(info.fb_type, FBlockType::AsyncWith) {
14341434
emit!(self, Instruction::GetAwaitable { arg: 2 });
14351435
self.emit_load_const(ConstantData::None);
1436-
self.compile_yield_from_sequence(true)?;
1436+
let _ = self.compile_yield_from_sequence(true)?;
14371437
}
14381438

14391439
// Pop the __exit__ result
@@ -2371,12 +2371,16 @@ impl Compiler {
23712371
}
23722372
}
23732373
ast::Stmt::Break(_) => {
2374+
// Match CPython line-tracing behavior (codegen_break emits NOP).
2375+
emit!(self, Instruction::Nop);
23742376
// Unwind fblock stack until we find a loop, emitting cleanup for each fblock
23752377
self.compile_break_continue(statement.range(), true)?;
23762378
let dead = self.new_block();
23772379
self.switch_to_block(dead);
23782380
}
23792381
ast::Stmt::Continue(_) => {
2382+
// Match CPython line-tracing behavior (codegen_continue emits NOP).
2383+
emit!(self, Instruction::Nop);
23802384
// Unwind fblock stack until we find a loop, emitting cleanup for each fblock
23812385
self.compile_break_continue(statement.range(), false)?;
23822386
let dead = self.new_block();
@@ -2449,7 +2453,8 @@ impl Compiler {
24492453
}
24502454
}
24512455
ast::Stmt::Pass(_) => {
2452-
// No need to emit any code here :)
2456+
// Match CPython line-tracing behavior (Pass_kind emits NOP).
2457+
emit!(self, Instruction::Nop);
24532458
}
24542459
ast::Stmt::TypeAlias(ast::StmtTypeAlias {
24552460
name,
@@ -4942,7 +4947,7 @@ impl Compiler {
49424947
emit!(self, Instruction::Call { nargs: 0 }); // [bound_aexit, awaitable]
49434948
emit!(self, Instruction::GetAwaitable { arg: 1 });
49444949
self.emit_load_const(ConstantData::None);
4945-
self.compile_yield_from_sequence(true)?;
4950+
let _ = self.compile_yield_from_sequence(true)?;
49464951
} else {
49474952
// Load __exit__ and __enter__, then call __enter__
49484953
emit!(
@@ -5025,7 +5030,7 @@ impl Compiler {
50255030
if is_async {
50265031
emit!(self, Instruction::GetAwaitable { arg: 2 });
50275032
self.emit_load_const(ConstantData::None);
5028-
self.compile_yield_from_sequence(true)?;
5033+
let _ = self.compile_yield_from_sequence(true)?;
50295034
}
50305035
emit!(self, Instruction::PopTop); // Pop __exit__ result
50315036
emit!(
@@ -5063,7 +5068,7 @@ impl Compiler {
50635068
if is_async {
50645069
emit!(self, Instruction::GetAwaitable { arg: 2 });
50655070
self.emit_load_const(ConstantData::None);
5066-
self.compile_yield_from_sequence(true)?;
5071+
let _ = self.compile_yield_from_sequence(true)?;
50675072
}
50685073

50695074
// TO_BOOL + POP_JUMP_IF_TRUE: check if exception is suppressed
@@ -5137,6 +5142,7 @@ impl Compiler {
51375142
let for_block = self.new_block();
51385143
let else_block = self.new_block();
51395144
let after_block = self.new_block();
5145+
let mut end_async_for_target = BlockIdx::NULL;
51405146

51415147
// The thing iterated:
51425148
self.compile_expression(iter)?;
@@ -5156,9 +5162,10 @@ impl Compiler {
51565162
emit!(self, PseudoInstruction::SetupFinally { target: else_block });
51575163
emit!(self, Instruction::GetANext);
51585164
self.emit_load_const(ConstantData::None);
5159-
self.compile_yield_from_sequence(true)?;
5165+
end_async_for_target = self.compile_yield_from_sequence(true)?;
51605166
// POP_BLOCK for SETUP_FINALLY - only GetANext/yield_from are protected
51615167
emit!(self, PseudoInstruction::PopBlock);
5168+
emit!(self, Instruction::NotTaken);
51625169

51635170
// Success block for __anext__
51645171
self.compile_store(target)?;
@@ -5192,7 +5199,7 @@ impl Compiler {
51925199
let saved_range = self.current_source_range;
51935200
self.set_source_range(iter.range());
51945201
if is_async {
5195-
emit!(self, Instruction::EndAsyncFor);
5202+
self.emit_end_async_for(end_async_for_target);
51965203
} else {
51975204
emit!(self, Instruction::EndFor);
51985205
emit!(self, Instruction::PopIter);
@@ -6786,7 +6793,7 @@ impl Compiler {
67866793
/// CLEANUP_THROW
67876794
/// exit:
67886795
/// END_SEND
6789-
fn compile_yield_from_sequence(&mut self, is_await: bool) -> CompileResult<()> {
6796+
fn compile_yield_from_sequence(&mut self, is_await: bool) -> CompileResult<BlockIdx> {
67906797
let send_block = self.new_block();
67916798
let fail_block = self.new_block();
67926799
let exit_block = self.new_block();
@@ -6842,7 +6849,7 @@ impl Compiler {
68426849
self.switch_to_block(exit_block);
68436850
emit!(self, Instruction::EndSend);
68446851

6845-
Ok(())
6852+
Ok(send_block)
68466853
}
68476854

68486855
fn compile_expression(&mut self, expression: &ast::Expr) -> CompileResult<()> {
@@ -6897,7 +6904,11 @@ impl Compiler {
68976904
if let Some(super_type) = self.can_optimize_super_call(value, attr.as_str()) {
68986905
// super().attr or super(cls, self).attr optimization
68996906
// Stack: [global_super, class, self] → LOAD_SUPER_ATTR → [attr]
6907+
// Set source range to super() call for arg-loading instructions
6908+
let super_range = value.range();
6909+
self.set_source_range(super_range);
69006910
self.load_args_for_super(&super_type)?;
6911+
self.set_source_range(super_range);
69016912
let idx = self.name(attr.as_str());
69026913
match super_type {
69036914
SuperCallType::TwoArg { .. } => {
@@ -6983,7 +6994,7 @@ impl Compiler {
69836994
self.compile_expression(value)?;
69846995
emit!(self, Instruction::GetAwaitable { arg: 0 });
69856996
self.emit_load_const(ConstantData::None);
6986-
self.compile_yield_from_sequence(true)?;
6997+
let _ = self.compile_yield_from_sequence(true)?;
69876998
}
69886999
ast::Expr::YieldFrom(ast::ExprYieldFrom { value, .. }) => {
69897000
match self.ctx.func {
@@ -6999,7 +7010,7 @@ impl Compiler {
69997010
self.compile_expression(value)?;
70007011
emit!(self, Instruction::GetYieldFromIter);
70017012
self.emit_load_const(ConstantData::None);
7002-
self.compile_yield_from_sequence(false)?;
7013+
let _ = self.compile_yield_from_sequence(false)?;
70037014
}
70047015
ast::Expr::Name(ast::ExprName { id, .. }) => self.load_name(id.as_str())?,
70057016
ast::Expr::Lambda(ast::ExprLambda {
@@ -7354,7 +7365,11 @@ impl Compiler {
73547365
if let Some(super_type) = self.can_optimize_super_call(value, attr.as_str()) {
73557366
// super().method() or super(cls, self).method() optimization
73567367
// Stack: [global_super, class, self] → LOAD_SUPER_METHOD → [method, self]
7368+
// Set source range to the super() call for LOAD_GLOBAL/LOAD_DEREF/etc.
7369+
let super_range = value.range();
7370+
self.set_source_range(super_range);
73577371
self.load_args_for_super(&super_type)?;
7372+
self.set_source_range(super_range);
73587373
let idx = self.name(attr.as_str());
73597374
match super_type {
73607375
SuperCallType::TwoArg { .. } => {
@@ -7364,7 +7379,11 @@ impl Compiler {
73647379
self.emit_load_zero_super_method(idx);
73657380
}
73667381
}
7367-
self.codegen_call_helper(0, args, call_range)?;
7382+
// NOP for line tracking at .method( line
7383+
self.set_source_range(attr.range());
7384+
emit!(self, Instruction::Nop);
7385+
// CALL at .method( line (not the full expression line)
7386+
self.codegen_call_helper(0, args, attr.range())?;
73687387
} else {
73697388
// Normal method call: compile object, then LOAD_ATTR with method flag
73707389
// LOAD_ATTR(method=1) pushes [method, self_or_null] on stack
@@ -7654,6 +7673,7 @@ impl Compiler {
76547673
let mut loop_labels = vec![];
76557674
for generator in generators {
76567675
let loop_block = self.new_block();
7676+
let if_cleanup_block = self.new_block();
76577677
let after_block = self.new_block();
76587678

76597679
if loop_labels.is_empty() {
@@ -7671,8 +7691,8 @@ impl Compiler {
76717691
}
76727692
}
76737693

7674-
loop_labels.push((loop_block, after_block, generator.is_async));
76757694
self.switch_to_block(loop_block);
7695+
let mut end_async_for_target = BlockIdx::NULL;
76767696
if generator.is_async {
76777697
emit!(
76787698
self,
@@ -7687,7 +7707,7 @@ impl Compiler {
76877707
after_block,
76887708
)?;
76897709
self.emit_load_const(ConstantData::None);
7690-
self.compile_yield_from_sequence(true)?;
7710+
end_async_for_target = self.compile_yield_from_sequence(true)?;
76917711
// POP_BLOCK before store: only __anext__/yield_from are
76927712
// protected by SetupFinally targeting END_ASYNC_FOR.
76937713
emit!(self, PseudoInstruction::PopBlock);
@@ -7702,23 +7722,35 @@ impl Compiler {
77027722
);
77037723
self.compile_store(&generator.target)?;
77047724
}
7725+
loop_labels.push((
7726+
loop_block,
7727+
if_cleanup_block,
7728+
after_block,
7729+
generator.is_async,
7730+
end_async_for_target,
7731+
));
77057732

77067733
// Now evaluate the ifs:
77077734
for if_condition in &generator.ifs {
7708-
self.compile_jump_if(if_condition, false, loop_block)?
7735+
self.compile_jump_if(if_condition, false, if_cleanup_block)?
77097736
}
77107737
}
77117738

77127739
compile_element(self)?;
77137740

7714-
for (loop_block, after_block, is_async) in loop_labels.iter().rev().copied() {
7741+
for (loop_block, if_cleanup_block, after_block, is_async, end_async_for_target) in
7742+
loop_labels.iter().rev().copied()
7743+
{
7744+
emit!(self, PseudoInstruction::Jump { target: loop_block });
7745+
7746+
self.switch_to_block(if_cleanup_block);
77157747
emit!(self, PseudoInstruction::Jump { target: loop_block });
77167748

77177749
self.switch_to_block(after_block);
77187750
if is_async {
77197751
// EndAsyncFor pops both the exception and the aiter
77207752
// (handler depth is before GetANext, so aiter is at handler depth)
7721-
emit!(self, Instruction::EndAsyncFor);
7753+
self.emit_end_async_for(end_async_for_target);
77227754
} else {
77237755
// END_FOR + POP_ITER pattern (CPython 3.14)
77247756
emit!(self, Instruction::EndFor);
@@ -7756,7 +7788,7 @@ impl Compiler {
77567788
if is_async_list_set_dict_comprehension {
77577789
emit!(self, Instruction::GetAwaitable { arg: 0 });
77587790
self.emit_load_const(ConstantData::None);
7759-
self.compile_yield_from_sequence(true)?;
7791+
let _ = self.compile_yield_from_sequence(true)?;
77607792
}
77617793

77627794
Ok(())
@@ -7867,6 +7899,7 @@ impl Compiler {
78677899
let mut loop_labels = vec![];
78687900
for (i, generator) in generators.iter().enumerate() {
78697901
let loop_block = self.new_block();
7902+
let if_cleanup_block = self.new_block();
78707903
let after_block = self.new_block();
78717904

78727905
if i > 0 {
@@ -7879,13 +7912,13 @@ impl Compiler {
78797912
}
78807913
}
78817914

7882-
loop_labels.push((loop_block, after_block, generator.is_async));
78837915
self.switch_to_block(loop_block);
7916+
let mut end_async_for_target = BlockIdx::NULL;
78847917

78857918
if generator.is_async {
78867919
emit!(self, Instruction::GetANext);
78877920
self.emit_load_const(ConstantData::None);
7888-
self.compile_yield_from_sequence(true)?;
7921+
end_async_for_target = self.compile_yield_from_sequence(true)?;
78897922
self.compile_store(&generator.target)?;
78907923
} else {
78917924
emit!(
@@ -7896,22 +7929,35 @@ impl Compiler {
78967929
);
78977930
self.compile_store(&generator.target)?;
78987931
}
7932+
loop_labels.push((
7933+
loop_block,
7934+
if_cleanup_block,
7935+
after_block,
7936+
generator.is_async,
7937+
end_async_for_target,
7938+
));
78997939

79007940
// Evaluate the if conditions
79017941
for if_condition in &generator.ifs {
7902-
self.compile_jump_if(if_condition, false, loop_block)?;
7942+
self.compile_jump_if(if_condition, false, if_cleanup_block)?;
79037943
}
79047944
}
79057945

79067946
// Step 6: Compile the element expression and append to collection
79077947
compile_element(self)?;
79087948

79097949
// Step 7: Close all loops
7910-
for (loop_block, after_block, is_async) in loop_labels.iter().rev().copied() {
7950+
for (loop_block, if_cleanup_block, after_block, is_async, end_async_for_target) in
7951+
loop_labels.iter().rev().copied()
7952+
{
7953+
emit!(self, PseudoInstruction::Jump { target: loop_block });
7954+
7955+
self.switch_to_block(if_cleanup_block);
79117956
emit!(self, PseudoInstruction::Jump { target: loop_block });
7957+
79127958
self.switch_to_block(after_block);
79137959
if is_async {
7914-
emit!(self, Instruction::EndAsyncFor);
7960+
self.emit_end_async_for(end_async_for_target);
79157961
// Pop the iterator
79167962
emit!(self, Instruction::PopTop);
79177963
} else {
@@ -8048,6 +8094,10 @@ impl Compiler {
80488094
emit!(self, Instruction::ReturnValue)
80498095
}
80508096

8097+
fn emit_end_async_for(&mut self, send_target: BlockIdx) {
8098+
self._emit(Instruction::EndAsyncFor, OpArg::NULL, send_target);
8099+
}
8100+
80518101
/// Emit LOAD_ATTR for attribute access (method=false).
80528102
/// Encodes: (name_idx << 1) | 0
80538103
fn emit_load_attr(&mut self, name_idx: u32) {
@@ -8260,7 +8310,7 @@ impl Compiler {
82608310
if is_async {
82618311
emit!(self, Instruction::GetAwaitable { arg: 2 });
82628312
self.emit_load_const(ConstantData::None);
8263-
self.compile_yield_from_sequence(true)?;
8313+
let _ = self.compile_yield_from_sequence(true)?;
82648314
}
82658315

82668316
emit!(self, Instruction::PopTop);

0 commit comments

Comments
 (0)