@@ -431,6 +431,7 @@ impl ABIMachineSpec for Riscv64MachineDeps {
431431 fn gen_clobber_save (
432432 _call_conv : isa:: CallConv ,
433433 flags : & settings:: Flags ,
434+ isa_flags : & RiscvFlags ,
434435 frame_layout : & FrameLayout ,
435436 ) -> SmallVec < [ Inst ; 16 ] > {
436437 let mut insts = SmallVec :: new ( ) ;
@@ -479,61 +480,91 @@ impl ABIMachineSpec for Riscv64MachineDeps {
479480 } ) ;
480481 }
481482
482- // Adjust the stack pointer downward for clobbers, the function fixed
483- // frame (spillslots and storage slots), and outgoing arguments.
484- let stack_size = frame_layout. clobber_size
485- + frame_layout. fixed_frame_storage_size
486- + frame_layout. outgoing_args_size ;
487-
488- // Store each clobbered register in order at offsets from SP,
489- // placing them above the fixed frame slots.
490- if stack_size > 0 {
491- insts. extend ( Self :: gen_sp_reg_adjust ( -( stack_size as i32 ) ) ) ;
492-
493- let mut cur_offset = 0 ;
494- for reg in & frame_layout. clobbered_callee_saves {
495- let r_reg = reg. to_reg ( ) ;
496- let ty = match r_reg. class ( ) {
497- RegClass :: Int => I64 ,
498- RegClass :: Float => F64 ,
499- RegClass :: Vector => I8X16 ,
500- } ;
501- cur_offset = align_to ( cur_offset, ty. bytes ( ) ) ;
502- insts. push ( Inst :: gen_store (
503- AMode :: SPOffset ( i64:: from ( stack_size - cur_offset - ty. bytes ( ) ) ) ,
504- Reg :: from ( reg. to_reg ( ) ) ,
505- ty,
506- MemFlagsData :: trusted ( ) ,
507- ) ) ;
483+ let clobber_size = frame_layout. clobber_size ;
484+ let remaining_frame_size =
485+ frame_layout. fixed_frame_storage_size + frame_layout. outgoing_args_size ;
508486
509- if flags. unwind_info ( ) {
510- insts. push ( Inst :: Unwind {
511- inst : UnwindInst :: SaveReg {
512- clobber_offset : frame_layout. clobber_size - cur_offset - ty. bytes ( ) ,
513- reg : r_reg,
514- } ,
515- } ) ;
516- }
487+ // When the Zca extension is available, split the SP adjustment so
488+ // callee-save stores use small offsets that fit in c.sdsp (≤504 bytes).
489+ // Without Zca, use a single combined decrement to avoid an extra instruction.
490+ let split_adjustment = isa_flags. has_zca ( ) && clobber_size > 0 && remaining_frame_size > 0 ;
491+
492+ if split_adjustment {
493+ insts. extend ( Self :: gen_sp_reg_adjust ( -( clobber_size as i32 ) ) ) ;
494+ } else {
495+ let stack_size = clobber_size + remaining_frame_size;
496+ if stack_size > 0 {
497+ insts. extend ( Self :: gen_sp_reg_adjust ( -( stack_size as i32 ) ) ) ;
498+ }
499+ }
500+
501+ let store_base = if split_adjustment {
502+ clobber_size
503+ } else {
504+ clobber_size + remaining_frame_size
505+ } ;
506+
507+ let mut cur_offset = 0 ;
508+ for reg in & frame_layout. clobbered_callee_saves {
509+ let r_reg = reg. to_reg ( ) ;
510+ let ty = match r_reg. class ( ) {
511+ RegClass :: Int => I64 ,
512+ RegClass :: Float => F64 ,
513+ RegClass :: Vector => I8X16 ,
514+ } ;
515+ cur_offset = align_to ( cur_offset, ty. bytes ( ) ) ;
516+ insts. push ( Inst :: gen_store (
517+ AMode :: SPOffset ( i64:: from ( store_base - cur_offset - ty. bytes ( ) ) ) ,
518+ Reg :: from ( reg. to_reg ( ) ) ,
519+ ty,
520+ MemFlagsData :: trusted ( ) ,
521+ ) ) ;
517522
518- cur_offset += ty. bytes ( ) ;
519- assert ! ( cur_offset <= stack_size) ;
523+ if flags. unwind_info ( ) {
524+ insts. push ( Inst :: Unwind {
525+ inst : UnwindInst :: SaveReg {
526+ clobber_offset : clobber_size - cur_offset - ty. bytes ( ) ,
527+ reg : r_reg,
528+ } ,
529+ } ) ;
520530 }
531+
532+ cur_offset += ty. bytes ( ) ;
533+ assert ! ( cur_offset <= clobber_size) ;
521534 }
535+
536+ if split_adjustment {
537+ insts. extend ( Self :: gen_sp_reg_adjust ( -( remaining_frame_size as i32 ) ) ) ;
538+ }
539+
522540 insts
523541 }
524542
525543 fn gen_clobber_restore (
526544 _call_conv : isa:: CallConv ,
527545 _flags : & settings:: Flags ,
546+ isa_flags : & RiscvFlags ,
528547 frame_layout : & FrameLayout ,
529548 ) -> SmallVec < [ Inst ; 16 ] > {
530549 let mut insts = SmallVec :: new ( ) ;
531550
532- let stack_size = frame_layout. clobber_size
533- + frame_layout. fixed_frame_storage_size
534- + frame_layout. outgoing_args_size ;
535- let mut cur_offset = 0 ;
551+ let clobber_size = frame_layout. clobber_size ;
552+ let remaining_frame_size =
553+ frame_layout. fixed_frame_storage_size + frame_layout. outgoing_args_size ;
554+
555+ let split_adjustment = isa_flags. has_zca ( ) && clobber_size > 0 && remaining_frame_size > 0 ;
536556
557+ if split_adjustment {
558+ insts. extend ( Self :: gen_sp_reg_adjust ( remaining_frame_size as i32 ) ) ;
559+ }
560+
561+ let load_base = if split_adjustment {
562+ clobber_size
563+ } else {
564+ clobber_size + remaining_frame_size
565+ } ;
566+
567+ let mut cur_offset = 0 ;
537568 for reg in & frame_layout. clobbered_callee_saves {
538569 let rreg = reg. to_reg ( ) ;
539570 let ty = match rreg. class ( ) {
@@ -544,15 +575,20 @@ impl ABIMachineSpec for Riscv64MachineDeps {
544575 cur_offset = align_to ( cur_offset, ty. bytes ( ) ) ;
545576 insts. push ( Inst :: gen_load (
546577 reg. map ( Reg :: from) ,
547- AMode :: SPOffset ( i64:: from ( stack_size - cur_offset - ty. bytes ( ) ) ) ,
578+ AMode :: SPOffset ( i64:: from ( load_base - cur_offset - ty. bytes ( ) ) ) ,
548579 ty,
549580 MemFlagsData :: trusted ( ) ,
550581 ) ) ;
551582 cur_offset += ty. bytes ( ) ;
552583 }
553584
554- if stack_size > 0 {
555- insts. extend ( Self :: gen_sp_reg_adjust ( stack_size as i32 ) ) ;
585+ let restore_size = if split_adjustment {
586+ clobber_size
587+ } else {
588+ clobber_size + remaining_frame_size
589+ } ;
590+ if restore_size > 0 {
591+ insts. extend ( Self :: gen_sp_reg_adjust ( restore_size as i32 ) ) ;
556592 }
557593
558594 insts
0 commit comments