@@ -1229,9 +1229,12 @@ impl Aarch64CodeGen {
12291229 let stack_size = alloc. stack_size ( ) ;
12301230 let callee_saved = alloc. callee_saved_used ( ) . to_vec ( ) ;
12311231
1232- // For variadic functions, we need extra space for the register save area
1232+ // For variadic functions on Linux/FreeBSD , we need extra space for the register save area
12331233 // AAPCS64: 8 GP regs (x0-x7) * 8 bytes = 64 bytes
1234- let reg_save_area_size: i32 = if is_variadic { 64 } else { 0 } ;
1234+ // On Darwin (macOS/iOS), variadic args are passed on the stack by the caller,
1235+ // so we don't need a register save area.
1236+ let is_darwin = self . target . os == crate :: target:: Os :: MacOS ;
1237+ let reg_save_area_size: i32 = if is_variadic && !is_darwin { 64 } else { 0 } ;
12351238
12361239 // Calculate total frame size
12371240 // Need space for: fp/lr (16 bytes) + callee-saved regs + local vars + reg save area
@@ -1435,10 +1438,11 @@ impl Aarch64CodeGen {
14351438 }
14361439 }
14371440
1438- // For variadic functions, save ALL argument registers to the register save area
1441+ // For variadic functions on Linux/FreeBSD , save ALL argument registers to the register save area
14391442 // This must be done BEFORE any code uses these registers, so va_arg can access them
14401443 // AAPCS64: x0-x7 are saved at reg_save_area_offset from FP
1441- if is_variadic {
1444+ // On Darwin, variadic args are passed on the stack by the caller, so we skip this.
1445+ if is_variadic && !is_darwin {
14421446 let arg_regs = Reg :: arg_regs ( ) ;
14431447 for ( i, reg) in arg_regs. iter ( ) . enumerate ( ) {
14441448 // Each register at offset: reg_save_area_offset + (i * 8)
@@ -3029,9 +3033,10 @@ impl Aarch64CodeGen {
30293033
30303034 // For Darwin variadic calls, we need to:
30313035 // 1. First pass fixed args in registers as normal
3032- // 2. Then push ALL variadic args onto the stack (in reverse order for correct layout)
3036+ // 2. Allocate stack space for variadic args (with 16-byte alignment)
3037+ // 3. Store variadic args at 8-byte offsets from sp (like clang does)
30333038 if is_darwin_variadic {
3034- // Collect variadic args to push in reverse order
3039+ // Collect variadic args and fixed args separately
30353040 let mut variadic_args: Vec < ( PseudoId , bool , u32 ) > = Vec :: new ( ) ;
30363041
30373042 // Process all arguments
@@ -3051,7 +3056,7 @@ impl Aarch64CodeGen {
30513056 } ;
30523057
30533058 if i >= variadic_start {
3054- // Variadic arg - collect for stack pushing
3059+ // Variadic arg - collect for stack storing
30553060 variadic_args. push ( ( arg, is_fp, arg_size) ) ;
30563061 } else {
30573062 // Fixed arg - use registers as normal
@@ -3065,41 +3070,56 @@ impl Aarch64CodeGen {
30653070 self . emit_fp_move ( arg, fp_arg_regs[ fp_arg_idx] , fp_size, frame_size) ;
30663071 fp_arg_idx += 1 ;
30673072 }
3068- // Fixed args shouldn't overflow to stack in well-formed calls
30693073 } else if int_arg_idx < int_arg_regs. len ( ) {
30703074 self . emit_move ( arg, int_arg_regs[ int_arg_idx] , arg_size, frame_size) ;
30713075 int_arg_idx += 1 ;
30723076 }
30733077 }
30743078 }
30753079
3076- // Push variadic args in reverse order so first variadic arg ends up at [sp]
3077- for ( arg, is_fp, arg_size) in variadic_args. into_iter ( ) . rev ( ) {
3078- if is_fp {
3079- // Load FP value to temp register, then store to stack
3080- // For variadic calls on Darwin, floats are passed as 8-byte values on stack
3081- self . emit_fp_move ( arg, VReg :: V16 , arg_size, frame_size) ;
3082- self . push_lir ( Aarch64Inst :: StrFp {
3083- size : FpSize :: Double , // Always 8 bytes on stack for Darwin variadic
3084- src : VReg :: V16 ,
3085- addr : MemAddr :: PreIndex {
3086- base : Reg :: SP ,
3087- offset : -16 ,
3088- } ,
3089- } ) ;
3090- } else {
3091- // Integer/pointer arg
3092- self . emit_move ( arg, Reg :: X9 , arg_size, frame_size) ;
3093- self . push_lir ( Aarch64Inst :: Str {
3094- size : OperandSize :: B64 ,
3095- src : Reg :: X9 ,
3096- addr : MemAddr :: PreIndex {
3097- base : Reg :: SP ,
3098- offset : -16 ,
3099- } ,
3100- } ) ;
3080+ // Calculate stack space for variadic args: 8 bytes each, rounded up to 16
3081+ let num_variadic = variadic_args. len ( ) ;
3082+ if num_variadic > 0 {
3083+ let variadic_bytes = ( num_variadic * 8 ) as i32 ;
3084+ let aligned_bytes = ( variadic_bytes + 15 ) & !15 ; // Round up to 16
3085+
3086+ // Allocate stack space for variadic args
3087+ self . push_lir ( Aarch64Inst :: Sub {
3088+ size : OperandSize :: B64 ,
3089+ src1 : Reg :: sp ( ) ,
3090+ src2 : GpOperand :: Imm ( aligned_bytes as i64 ) ,
3091+ dst : Reg :: sp ( ) ,
3092+ } ) ;
3093+
3094+ // Store variadic args at 8-byte offsets from sp
3095+ // First variadic arg at [sp+0], second at [sp+8], etc.
3096+ for ( idx, ( arg, is_fp, arg_size) ) in variadic_args. into_iter ( ) . enumerate ( ) {
3097+ let offset = ( idx * 8 ) as i32 ;
3098+ if is_fp {
3099+ self . emit_fp_move ( arg, VReg :: V16 , arg_size, frame_size) ;
3100+ self . push_lir ( Aarch64Inst :: StrFp {
3101+ size : FpSize :: Double ,
3102+ src : VReg :: V16 ,
3103+ addr : MemAddr :: BaseOffset {
3104+ base : Reg :: SP ,
3105+ offset,
3106+ } ,
3107+ } ) ;
3108+ } else {
3109+ self . emit_move ( arg, Reg :: X9 , arg_size, frame_size) ;
3110+ self . push_lir ( Aarch64Inst :: Str {
3111+ size : OperandSize :: B64 ,
3112+ src : Reg :: X9 ,
3113+ addr : MemAddr :: BaseOffset {
3114+ base : Reg :: SP ,
3115+ offset,
3116+ } ,
3117+ } ) ;
3118+ }
31013119 }
3102- stack_args += 1 ;
3120+
3121+ // Track stack space for cleanup
3122+ stack_args = ( aligned_bytes + 15 ) / 16 ; // Number of 16-byte units
31033123 }
31043124 } else {
31053125 // Standard AAPCS64 (Linux, FreeBSD) or non-variadic calls
@@ -3858,12 +3878,17 @@ impl Aarch64CodeGen {
38583878 // Variadic function support (va_* builtins)
38593879 // ========================================================================
38603880 //
3861- // On macOS ARM64 (Apple Silicon) and AAPCS64, va_list is a char* pointer
3862- // pointing to the first variadic argument in the register save area.
3881+ // Platform-specific va_list handling:
3882+ //
3883+ // Linux/FreeBSD (AAPCS64):
3884+ // - va_list is a char* pointing to register save area
3885+ // - In prologue, we save x0-x7 to the register save area
3886+ // - va_start computes: reg_save_area + (num_fixed_gp_params * 8)
38633887 //
3864- // In the prologue, we save x0-x7 to the register save area. va_start
3865- // computes the address of the first variadic argument by skipping
3866- // the fixed parameters: reg_save_area + (num_fixed_gp_params * 8)
3888+ // Darwin (macOS/iOS):
3889+ // - Variadic args are passed on the stack by the caller
3890+ // - va_list is a char* pointing to the caller's stack
3891+ // - va_start computes: FP + frame_size (where caller placed variadic args)
38673892
38683893 /// Emit va_start: Initialize va_list to point to first variadic arg
38693894 /// Note: ap_addr is the ADDRESS of the va_list variable (from symaddr), not the va_list itself
@@ -3876,9 +3901,18 @@ impl Aarch64CodeGen {
38763901 let ap_loc = self . get_location ( ap_addr) ;
38773902 let ( scratch0, scratch1) = Reg :: scratch_regs ( ) ;
38783903
3879- // Compute address of first variadic argument in the register save area
3880- // First variadic arg is at: FP + reg_save_area_offset + (num_fixed_gp_params * 8)
3881- let vararg_offset = self . reg_save_area_offset + ( self . num_fixed_gp_params as i32 * 8 ) ;
3904+ // Compute address of first variadic argument
3905+ let is_darwin = self . target . os == crate :: target:: Os :: MacOS ;
3906+ let vararg_offset = if is_darwin {
3907+ // Darwin: Variadic args are on the stack, placed by the caller
3908+ // They start at FP + frame_size (the original SP before prologue)
3909+ frame_size
3910+ } else {
3911+ // Linux/FreeBSD: Variadic args are in the register save area
3912+ // First variadic arg is at: reg_save_area_offset + (num_fixed_gp_params * 8)
3913+ self . reg_save_area_offset + ( self . num_fixed_gp_params as i32 * 8 )
3914+ } ;
3915+
38823916 self . push_lir ( Aarch64Inst :: Add {
38833917 size : OperandSize :: B64 ,
38843918 src1 : Reg :: X29 ,
0 commit comments