Skip to content

Commit b895e50

Browse files
committed
[cc] aarch64 varargs fixes
1 parent 2dd0823 commit b895e50

File tree

1 file changed

+76
-42
lines changed

1 file changed

+76
-42
lines changed

cc/arch/aarch64/codegen.rs

Lines changed: 76 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)