Skip to content

Commit 4d6c9b9

Browse files
committed
[cc] continue bugfixing regalloc and calls
1 parent 418abb9 commit 4d6c9b9

File tree

3 files changed

+145
-35
lines changed

3 files changed

+145
-35
lines changed

cc/arch/aarch64/call.rs

Lines changed: 82 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ impl Aarch64CodeGen {
127127
}
128128

129129
/// Set up register arguments for standard AAPCS64 calls
130+
///
131+
/// AAPCS64 requires stack arguments to be placed in parameter order at
132+
/// consecutive 8-byte slots starting from SP. Unlike x86-64, we don't use
133+
/// push instructions - instead we pre-allocate space and store directly.
130134
pub(super) fn setup_register_args(
131135
&mut self,
132136
insn: &Instruction,
@@ -136,9 +140,17 @@ impl Aarch64CodeGen {
136140
) -> i32 {
137141
let int_arg_regs = Reg::arg_regs();
138142
let fp_arg_regs = VReg::arg_regs();
143+
144+
// First pass: identify which args go to registers vs stack
145+
// Collect stack args with their info for the second pass
146+
struct StackArg {
147+
pseudo: PseudoId,
148+
is_fp: bool,
149+
size: u32,
150+
}
151+
let mut stack_args_info: Vec<StackArg> = Vec::new();
139152
let mut int_arg_idx = 0;
140153
let mut fp_arg_idx = 0;
141-
let mut stack_args = 0;
142154

143155
for (i, &arg) in insn.src.iter().enumerate().skip(args_start) {
144156
let arg_type = insn.arg_types.get(i).copied();
@@ -168,52 +180,96 @@ impl Aarch64CodeGen {
168180
);
169181
fp_arg_idx += 2;
170182
} else {
171-
stack_args += 2;
183+
// Complex on stack needs 2 slots
184+
stack_args_info.push(StackArg {
185+
pseudo: arg,
186+
is_fp: true,
187+
size: arg_size,
188+
});
189+
stack_args_info.push(StackArg {
190+
pseudo: arg,
191+
is_fp: true,
192+
size: arg_size,
193+
});
172194
}
173195
} else if is_fp {
174-
let fp_size = if let Some(typ) = arg_type {
175-
types.size_bits(typ)
176-
} else {
177-
64
178-
};
179196
if fp_arg_idx < fp_arg_regs.len() {
197+
let fp_size = if let Some(typ) = arg_type {
198+
types.size_bits(typ)
199+
} else {
200+
64
201+
};
180202
self.emit_fp_move(arg, fp_arg_regs[fp_arg_idx], fp_size, frame_size);
181203
fp_arg_idx += 1;
182204
} else {
183-
self.emit_fp_move(arg, VReg::V16, fp_size, frame_size);
184-
let fp_sz = if fp_size == 32 {
185-
FpSize::Single
186-
} else {
187-
FpSize::Double
188-
};
189-
self.push_lir(Aarch64Inst::StrFp {
190-
size: fp_sz,
191-
src: VReg::V16,
192-
addr: MemAddr::PreIndex {
193-
base: Reg::SP,
194-
offset: -16,
195-
},
205+
stack_args_info.push(StackArg {
206+
pseudo: arg,
207+
is_fp: true,
208+
size: arg_size,
196209
});
197-
stack_args += 1;
198210
}
199211
} else if int_arg_idx < int_arg_regs.len() {
200212
self.emit_move(arg, int_arg_regs[int_arg_idx], arg_size, frame_size);
201213
int_arg_idx += 1;
202214
} else {
203-
self.emit_move(arg, Reg::X9, arg_size, frame_size);
215+
stack_args_info.push(StackArg {
216+
pseudo: arg,
217+
is_fp: false,
218+
size: arg_size,
219+
});
220+
}
221+
}
222+
223+
// If no stack args, we're done
224+
if stack_args_info.is_empty() {
225+
return 0;
226+
}
227+
228+
// Pre-allocate stack space for all stack args (8 bytes each, 16-byte aligned)
229+
let num_stack_args = stack_args_info.len();
230+
let stack_bytes = (num_stack_args * 8) as i32;
231+
let aligned_bytes = (stack_bytes + 15) & !15;
232+
233+
self.push_lir(Aarch64Inst::Sub {
234+
size: OperandSize::B64,
235+
src1: Reg::sp(),
236+
src2: GpOperand::Imm(aligned_bytes as i64),
237+
dst: Reg::sp(),
238+
});
239+
240+
// Store each stack arg at its proper offset from SP (in parameter order)
241+
for (idx, stack_arg) in stack_args_info.into_iter().enumerate() {
242+
let offset = (idx * 8) as i32;
243+
if stack_arg.is_fp {
244+
self.emit_fp_move(stack_arg.pseudo, VReg::V16, stack_arg.size, frame_size);
245+
let fp_sz = if stack_arg.size == 32 {
246+
FpSize::Single
247+
} else {
248+
FpSize::Double
249+
};
250+
self.push_lir(Aarch64Inst::StrFp {
251+
size: fp_sz,
252+
src: VReg::V16,
253+
addr: MemAddr::BaseOffset {
254+
base: Reg::SP,
255+
offset,
256+
},
257+
});
258+
} else {
259+
self.emit_move(stack_arg.pseudo, Reg::X9, stack_arg.size, frame_size);
204260
self.push_lir(Aarch64Inst::Str {
205261
size: OperandSize::B64,
206262
src: Reg::X9,
207-
addr: MemAddr::PreIndex {
263+
addr: MemAddr::BaseOffset {
208264
base: Reg::SP,
209-
offset: -16,
265+
offset,
210266
},
211267
});
212-
stack_args += 1;
213268
}
214269
}
215270

216-
stack_args
271+
// Return number of 16-byte units allocated (for cleanup)
272+
(aligned_bytes + 15) / 16
217273
}
218274

219275
/// Set up a complex number argument (real + imaginary in two V registers)

cc/arch/x86_64/regalloc.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -498,11 +498,20 @@ impl RegAlloc {
498498
}
499499

500500
/// Pre-allocate argument registers per System V AMD64 ABI
501+
///
502+
/// System V AMD64 ABI passes arguments as follows:
503+
/// - First 6 integer/pointer args in RDI, RSI, RDX, RCX, R8, R9
504+
/// - First 8 FP args in XMM0-XMM7
505+
/// - Remaining args go on the stack in parameter order (not separated by type)
501506
fn allocate_arguments(&mut self, func: &Function, types: &TypeTable) {
502507
let int_arg_regs = Reg::arg_regs();
503508
let fp_arg_regs = XmmReg::arg_regs();
504509
let mut int_arg_idx = 0;
505510
let mut fp_arg_idx = 0;
511+
// Stack offset for overflow args - must be shared across all types
512+
// because System V AMD64 ABI places stack args in parameter order
513+
// 16 = saved rbp (8) + return address (8)
514+
let mut stack_arg_offset = 16i32;
506515

507516
// Detect hidden return pointer for large struct returns
508517
let sret_pseudo = func
@@ -530,11 +539,10 @@ impl RegAlloc {
530539
self.free_xmm_regs.retain(|&r| r != fp_arg_regs[fp_arg_idx]);
531540
self.fp_pseudos.insert(pseudo.id);
532541
} else {
533-
// Stack-passed FP argument: at [rbp + offset]
534-
// 16 = saved rbp (8) + return address (8)
535-
let offset =
536-
16 + (i - int_arg_regs.len() - fp_arg_regs.len()) as i32 * 8;
537-
self.locations.insert(pseudo.id, Loc::IncomingArg(offset));
542+
// Stack args are placed in parameter order per System V AMD64 ABI
543+
self.locations
544+
.insert(pseudo.id, Loc::IncomingArg(stack_arg_offset));
545+
stack_arg_offset += 8;
538546
}
539547
fp_arg_idx += 1;
540548
} else {
@@ -543,10 +551,10 @@ impl RegAlloc {
543551
.insert(pseudo.id, Loc::Reg(int_arg_regs[int_arg_idx]));
544552
self.free_regs.retain(|&r| r != int_arg_regs[int_arg_idx]);
545553
} else {
546-
// Stack-passed integer argument: at [rbp + offset]
547-
// 16 = saved rbp (8) + return address (8)
548-
let offset = 16 + (int_arg_idx - int_arg_regs.len()) as i32 * 8;
549-
self.locations.insert(pseudo.id, Loc::IncomingArg(offset));
554+
// Stack args are placed in parameter order per System V AMD64 ABI
555+
self.locations
556+
.insert(pseudo.id, Loc::IncomingArg(stack_arg_offset));
557+
stack_arg_offset += 8;
550558
}
551559
int_arg_idx += 1;
552560
}

cc/tests/features/many_args.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,49 @@ int main(void) {
4141
"#;
4242
assert_eq!(compile_and_run("many_mixed_args", code), 0);
4343
}
44+
45+
#[test]
46+
fn test_many_int_args() {
47+
let code = r#"
48+
// Function with 12 int args: 6 in registers, 6 on stack
49+
// x86_64: RDI, RSI, RDX, RCX, R8, R9 (6 regs)
50+
// aarch64: X0-X7 (8 regs)
51+
// Tests integer-only stack overflow
52+
long many_ints(int a, int b, int c, int d, int e, int f,
53+
int g, int h, int i, int j, int k, int l) {
54+
return (long)a + b + c + d + e + f + g + h + i + j + k + l;
55+
}
56+
57+
int main(void) {
58+
// 1+2+3+4+5+6+7+8+9+10+11+12 = 78
59+
long result = many_ints(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
60+
if (result != 78) return 1;
61+
return 0;
62+
}
63+
"#;
64+
assert_eq!(compile_and_run("many_int_args", code), 0);
65+
}
66+
67+
#[test]
68+
fn test_many_fp_args() {
69+
let code = r#"
70+
// Function with 10 double args: 8 in registers, 2 on stack
71+
// x86_64: XMM0-XMM7 (8 regs)
72+
// aarch64: V0-V7 (8 regs)
73+
// Tests FP-only stack overflow - triggers negative offset bug if broken
74+
double many_doubles(double a, double b, double c, double d,
75+
double e, double f, double g, double h,
76+
double i, double j) {
77+
return a + b + c + d + e + f + g + h + i + j;
78+
}
79+
80+
int main(void) {
81+
// 1.0+2.0+3.0+4.0+5.0+6.0+7.0+8.0+9.0+10.0 = 55.0
82+
double result = many_doubles(1.0, 2.0, 3.0, 4.0, 5.0,
83+
6.0, 7.0, 8.0, 9.0, 10.0);
84+
if (result < 54.9 || result > 55.1) return 1;
85+
return 0;
86+
}
87+
"#;
88+
assert_eq!(compile_and_run("many_fp_args", code), 0);
89+
}

0 commit comments

Comments
 (0)