@@ -2452,6 +2452,11 @@ impl Assembler
24522452 for ( insn, insn_id) in old_insns. into_iter ( ) . zip ( old_ids. into_iter ( ) ) {
24532453 if let Insn :: CCall { opnds, out, start_marker, end_marker, fptr } = insn {
24542454 let insn_number = insn_id. map ( |id| id. 0 ) . unwrap_or ( 0 ) ;
2455+ let call_result_live = out. is_vreg ( )
2456+ && intervals[ out. vreg_idx ( ) . 0 ]
2457+ . range
2458+ . end
2459+ . is_some_and ( |end| end > insn_number) ;
24552460
24562461 // Find survivors: intervals that survive this Call instruction
24572462 // We need to preserve the "surviving" registers past the ccall,
@@ -2467,19 +2472,26 @@ impl Assembler
24672472 . map ( |interval| interval. id )
24682473 . collect ( ) ;
24692474
2470- // Push all survivors on the stack
2475+ let survivor_regs: Vec < Opnd > = survivors. iter ( )
2476+ . map ( |& s| match assignments[ s] . unwrap ( ) {
2477+ Allocation :: Reg ( n) => Opnd :: Reg ( ALLOC_REGS [ n] ) ,
2478+ Allocation :: Fixed ( reg) => Opnd :: Reg ( reg) ,
2479+ _ => unreachable ! ( ) ,
2480+ } )
2481+ . collect ( ) ;
2482+ let survivor_push_groups: Vec < Vec < Opnd > > = survivor_regs
2483+ . chunks ( 2 )
2484+ . map ( |group| group. to_vec ( ) )
2485+ . collect ( ) ;
2486+
2487+ // Push all survivors on the stack, pairing adjacent pushes when possible.
24712488 let needs_alignment = cfg ! ( target_arch = "x86_64" ) && survivors. len ( ) % 2 == 1 ;
2472- for & s in & survivors {
2473- let reg_n = match assignments[ s] . unwrap ( ) {
2474- Allocation :: Reg ( n) => n,
2475- Allocation :: Fixed ( reg) => {
2476- new_insns. push ( Insn :: CPush ( Opnd :: Reg ( reg) ) ) ;
2477- new_ids. push ( None ) ;
2478- continue ;
2479- }
2489+ for group in & survivor_push_groups {
2490+ match group. as_slice ( ) {
2491+ [ left, right] => new_insns. push ( Insn :: CPushPair ( * left, * right) ) ,
2492+ [ reg] => new_insns. push ( Insn :: CPush ( * reg) ) ,
24802493 _ => unreachable ! ( ) ,
2481- } ;
2482- new_insns. push ( Insn :: CPush ( Opnd :: Reg ( ALLOC_REGS [ reg_n] ) ) ) ;
2494+ }
24832495 new_ids. push ( None ) ;
24842496 }
24852497 // Maintain 16-byte stack alignment for x86_64
@@ -2542,41 +2554,42 @@ impl Assembler
25422554 }
25432555
25442556 if survivors. is_empty ( ) {
2545- // No survivors to restore — move result directly to output
2546- let out = Self :: rewritten_opnd ( out, assignments) ;
2547- new_insns. push ( Insn :: Mov { dest : out, src : C_RET_OPND } ) ;
2548- new_ids. push ( None ) ;
2557+ if call_result_live {
2558+ // No survivors to restore — move result directly to output.
2559+ let out = Self :: rewritten_opnd ( out, assignments) ;
2560+ new_insns. push ( Insn :: Mov { dest : out, src : C_RET_OPND } ) ;
2561+ new_ids. push ( None ) ;
2562+ }
25492563 } else {
2550- // Save CCall result to scratch immediately, before pops
2551- // can clobber either C_RET or the output register.
2552- new_insns. push ( Insn :: Mov { dest : Opnd :: Reg ( SCRATCH_REG ) , src : C_RET_OPND } ) ;
2553- new_ids. push ( None ) ;
2564+ if call_result_live {
2565+ // Save CCall result to scratch immediately, before pops
2566+ // can clobber either C_RET or the output register.
2567+ new_insns. push ( Insn :: Mov { dest : Opnd :: Reg ( SCRATCH_REG ) , src : C_RET_OPND } ) ;
2568+ new_ids. push ( None ) ;
2569+ }
25542570
25552571 // Pop alignment padding (if needed)
25562572 if needs_alignment {
25572573 new_insns. push ( Insn :: CPopInto ( Opnd :: Reg ( ALLOC_REGS [ 0 ] ) ) ) ;
25582574 new_ids. push ( None ) ;
25592575 }
25602576
2561- // Restore all survivors
2562- for & s in survivors. iter ( ) . rev ( ) {
2563- let reg_n = match assignments[ s] . unwrap ( ) {
2564- Allocation :: Reg ( n) => n,
2565- Allocation :: Fixed ( reg) => {
2566- new_insns. push ( Insn :: CPopInto ( Opnd :: Reg ( reg) ) ) ;
2567- new_ids. push ( None ) ;
2568- continue ;
2569- }
2577+ // Restore all survivors in reverse stack order, pairing adjacent pops when possible.
2578+ for group in survivor_push_groups. iter ( ) . rev ( ) {
2579+ match group. as_slice ( ) {
2580+ [ left, right] => new_insns. push ( Insn :: CPopPairInto ( * right, * left) ) ,
2581+ [ reg] => new_insns. push ( Insn :: CPopInto ( * reg) ) ,
25702582 _ => unreachable ! ( ) ,
2571- } ;
2572- new_insns. push ( Insn :: CPopInto ( Opnd :: Reg ( ALLOC_REGS [ reg_n] ) ) ) ;
2583+ }
25732584 new_ids. push ( None ) ;
25742585 }
25752586
2576- // Move result from scratch to output AFTER all pops
2577- let out = Self :: rewritten_opnd ( out, assignments) ;
2578- new_insns. push ( Insn :: Mov { dest : out, src : Opnd :: Reg ( SCRATCH_REG ) } ) ;
2579- new_ids. push ( None ) ;
2587+ if call_result_live {
2588+ // Move result from scratch to output AFTER all pops.
2589+ let out = Self :: rewritten_opnd ( out, assignments) ;
2590+ new_insns. push ( Insn :: Mov { dest : out, src : Opnd :: Reg ( SCRATCH_REG ) } ) ;
2591+ new_ids. push ( None ) ;
2592+ }
25802593 }
25812594 } else {
25822595 new_insns. push ( insn) ;
0 commit comments