@@ -1136,6 +1136,13 @@ impl Compiler {
11361136 // Set the source range for the RESUME instruction
11371137 // For now, just use an empty range at the beginning
11381138 self . current_source_range = TextRange :: default ( ) ;
1139+
1140+ // For async functions/coroutines, emit RETURN_GENERATOR + POP_TOP before RESUME
1141+ if scope_type == CompilerScope :: AsyncFunction {
1142+ emit ! ( self , Instruction :: ReturnGenerator ) ;
1143+ emit ! ( self , Instruction :: PopTop ) ;
1144+ }
1145+
11391146 emit ! (
11401147 self ,
11411148 Instruction :: Resume {
@@ -1371,7 +1378,7 @@ impl Compiler {
13711378 if preserve_tos {
13721379 emit ! ( self , Instruction :: Swap { index: 2 } ) ;
13731380 }
1374- emit ! ( self , Instruction :: PopTop ) ;
1381+ emit ! ( self , Instruction :: PopIter ) ;
13751382 }
13761383
13771384 FBlockType :: TryExcept => {
@@ -3623,6 +3630,7 @@ impl Compiler {
36233630 self . current_code_info ( ) . flags |= bytecode:: CodeFlags :: HAS_DOCSTRING ;
36243631 }
36253632 // If no docstring, don't add None to co_consts
3633+ // Note: RETURN_GENERATOR + POP_TOP for async functions is emitted in enter_scope()
36263634
36273635 // Compile body statements
36283636 self . compile_statements ( body) ?;
@@ -4348,10 +4356,7 @@ impl Compiler {
43484356
43494357 // PEP 649: Initialize __classdict__ cell for class annotation scope
43504358 if self . current_symbol_table ( ) . needs_classdict {
4351- let locals_name = self . name ( "locals" ) ;
4352- emit ! ( self , Instruction :: LoadName ( locals_name) ) ;
4353- emit ! ( self , Instruction :: PushNull ) ;
4354- emit ! ( self , Instruction :: Call { nargs: 0 } ) ;
4359+ emit ! ( self , Instruction :: LoadLocals ) ;
43554360 let classdict_idx = self . get_cell_var_index ( "__classdict__" ) ?;
43564361 emit ! ( self , Instruction :: StoreDeref ( classdict_idx) ) ;
43574362 }
@@ -4978,8 +4983,10 @@ impl Compiler {
49784983 if is_async {
49794984 emit ! ( self , Instruction :: EndAsyncFor ) ;
49804985 } else {
4981- // Pop the iterator after loop ends
4982- emit ! ( self , Instruction :: PopTop ) ;
4986+ // END_FOR + POP_ITER pattern (CPython 3.14)
4987+ // FOR_ITER jumps to END_FOR, but VM skips it (+1) to reach POP_ITER
4988+ emit ! ( self , Instruction :: EndFor ) ;
4989+ emit ! ( self , Instruction :: PopIter ) ;
49834990 }
49844991 self . compile_statements ( orelse) ?;
49854992
@@ -6527,8 +6534,11 @@ impl Compiler {
65276534 }
65286535 ) ;
65296536
6530- // JUMP_NO_INTERRUPT send (regular JUMP in RustPython)
6531- emit ! ( self , PseudoInstruction :: Jump { target: send_block } ) ;
6537+ // JUMP_BACKWARD_NO_INTERRUPT send
6538+ emit ! (
6539+ self ,
6540+ PseudoInstruction :: JumpNoInterrupt { target: send_block }
6541+ ) ;
65326542
65336543 // fail: CLEANUP_THROW
65346544 // Stack when exception: [receiver, yielded_value, exc]
@@ -7424,7 +7434,9 @@ impl Compiler {
74247434 emit ! ( self , Instruction :: EndAsyncFor ) ;
74257435 emit ! ( self , Instruction :: PopTop ) ;
74267436 } else {
7427- emit ! ( self , Instruction :: PopTop ) ;
7437+ // END_FOR + POP_ITER pattern (CPython 3.14)
7438+ emit ! ( self , Instruction :: EndFor ) ;
7439+ emit ! ( self , Instruction :: PopIter ) ;
74287440 }
74297441 }
74307442
@@ -7621,9 +7633,13 @@ impl Compiler {
76217633 self . switch_to_block ( after_block) ;
76227634 if is_async {
76237635 emit ! ( self , Instruction :: EndAsyncFor ) ;
7636+ // Pop the iterator
7637+ emit ! ( self , Instruction :: PopTop ) ;
7638+ } else {
7639+ // END_FOR + POP_ITER pattern (CPython 3.14)
7640+ emit ! ( self , Instruction :: EndFor ) ;
7641+ emit ! ( self , Instruction :: PopIter ) ;
76247642 }
7625- // Pop the iterator
7626- emit ! ( self , Instruction :: PopTop ) ;
76277643 }
76287644
76297645 // Step 8: Clean up - restore saved locals
@@ -7741,6 +7757,18 @@ impl Compiler {
77417757 }
77427758
77437759 fn emit_load_const ( & mut self , constant : ConstantData ) {
7760+ // Use LOAD_SMALL_INT for integers in small int cache range (-5..=256)
7761+ // Still add to co_consts for compatibility (CPython does this too)
7762+ if let ConstantData :: Integer { ref value } = constant
7763+ && let Some ( small_int) = value. to_i32 ( )
7764+ && ( -5 ..=256 ) . contains ( & small_int)
7765+ {
7766+ // Add to co_consts even though we use LOAD_SMALL_INT
7767+ let _idx = self . arg_constant ( constant) ;
7768+ // Store as u32 (two's complement for negative values)
7769+ self . emit_arg ( small_int as u32 , |idx| Instruction :: LoadSmallInt { idx } ) ;
7770+ return ;
7771+ }
77447772 let idx = self . arg_constant ( constant) ;
77457773 self . emit_arg ( idx, |idx| Instruction :: LoadConst { idx } )
77467774 }
@@ -7924,7 +7952,7 @@ impl Compiler {
79247952
79257953 // For break in a for loop, pop the iterator
79267954 if is_break && is_for_loop {
7927- emit ! ( self , Instruction :: PopTop ) ;
7955+ emit ! ( self , Instruction :: PopIter ) ;
79287956 }
79297957
79307958 // Jump to target
0 commit comments