Skip to content

Commit 9823bd9

Browse files
committed
[cc] fixes
(not included: comment and other minor fix descriptions) Fixed a bug in the C compiler's declarator parser (cc/parse/parser.rs) where outer pointer modifiers in grouped declarators were applied in the wrong order. The Bug For declarations like struct node *(*fp)(int) (function pointer returning a pointer) and int *(*p)[3] (pointer to array of pointers), the type chain was being built incorrectly: Before (wrong): - struct node *(*fp)(int) → Pointer → Pointer → Function → struct node - int *(*p)[3] → Pointer → Pointer → Array → int After (correct): - struct node *(*fp)(int) → Pointer → Function(returns Pointer → struct node) - int *(*p)[3] → Pointer → Array(of Pointer → int) The Fix In parse_declarator(), moved the application of outer pointer modifiers before applying function parameters and array dimensions (lines 4040-4055). This ensures the outer * becomes part of the function's return type or array element type, not an extra wrapper around the whole construct. Files Modified 1. cc/parse/parser.rs - Fixed type chain building order in grouped declarators 2. Added two regression tests: - test_function_pointer_returning_struct_pointer - test_pointer_to_array_of_pointers
1 parent ff3b1ff commit 9823bd9

File tree

7 files changed

+706
-28
lines changed

7 files changed

+706
-28
lines changed

cc/arch/x86_64/expression.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ impl X86_64CodeGen {
2929
None => return,
3030
};
3131
let dst_loc = self.get_location(target);
32-
// Use R10 as scratch register when dst is on stack, to avoid clobbering
33-
// live values in caller-saved registers (Rax may hold phi values)
32+
// Use R10 (reserved scratch register, never allocated) when dst is on stack
3433
let work_reg = match &dst_loc {
3534
Loc::Reg(r) => *r,
3635
_ => Reg::R10,

cc/ir/linearize.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1287,7 +1287,7 @@ impl<'a> Linearizer<'a> {
12871287
}
12881288

12891289
// Create a symbol pseudo for this local variable (its address)
1290-
// Use unique name (name#id) to distinguish shadowed variables for SSA
1290+
// Use unique name (name.id) to distinguish shadowed variables
12911291
let sym_id = self.alloc_pseudo();
12921292
let unique_name = format!("{}.{}", declarator.name, sym_id.0);
12931293
let sym = Pseudo::sym(sym_id, unique_name.clone());

cc/parse/parser.rs

Lines changed: 119 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4011,19 +4011,38 @@ impl Parser<'_> {
40114011
let mut result_type_id = base_type_id;
40124012

40134013
if let Some(inner_tid) = inner_type_id {
4014-
// Grouped declarator: int (*p)[3] or void (*fp)(int)
4015-
// Arrays/functions in suffix apply to the base type first
4016-
// Then we substitute into the inner declarator
4014+
// Grouped declarator: int (*p)[3] or void (*fp)(int) or int *(*q)[3]
4015+
// Outer pointers (before parens) apply to the base type first
4016+
// Then arrays/functions in suffix are applied
4017+
// Finally we substitute into the inner declarator
4018+
4019+
// Apply any outer pointers (before the parens) to base type FIRST
4020+
// For struct node *(*fp)(int): base is struct node, outer * -> Pointer(struct node)
4021+
// For int *(*q)[3]: base is int, outer * -> Pointer(int)
4022+
for modifiers in pointer_modifiers.into_iter().rev() {
4023+
let ptr_type = Type {
4024+
kind: TypeKind::Pointer,
4025+
modifiers,
4026+
base: Some(result_type_id),
4027+
array_size: None,
4028+
params: None,
4029+
variadic: false,
4030+
noreturn: false,
4031+
composite: None,
4032+
};
4033+
result_type_id = self.types.intern(ptr_type);
4034+
}
40174035

4018-
// Apply function parameters to base type first (if present)
4019-
// For void (*fp)(int): base is void, suffix (int) -> Function(void, [int])
4036+
// Apply function parameters to (possibly pointer-modified) base type
4037+
// For struct node *(*fp)(int): result is Pointer(struct node)
4038+
// -> Function(Pointer(struct node), [int])
40204039
if let Some((param_type_ids, variadic)) = func_params {
40214040
let func_type = Type::function(result_type_id, param_type_ids, variadic);
40224041
result_type_id = self.types.intern(func_type);
40234042
}
40244043

4025-
// Apply array dimensions to base type
4026-
// For int (*p)[3]: base is int, suffix [3] -> Array(3, int)
4044+
// Apply array dimensions to result type
4045+
// For int *(*q)[3]: result is Pointer(int) -> Array(3, Pointer(int))
40274046
for size in dimensions.into_iter().rev() {
40284047
let arr_type = Type {
40294048
kind: TypeKind::Array,
@@ -4038,26 +4057,12 @@ impl Parser<'_> {
40384057
result_type_id = self.types.intern(arr_type);
40394058
}
40404059

4041-
// Apply any outer pointers (before the parens)
4042-
for modifiers in pointer_modifiers.into_iter().rev() {
4043-
let ptr_type = Type {
4044-
kind: TypeKind::Pointer,
4045-
modifiers,
4046-
base: Some(result_type_id),
4047-
array_size: None,
4048-
params: None,
4049-
variadic: false,
4050-
noreturn: false,
4051-
composite: None,
4052-
};
4053-
result_type_id = self.types.intern(ptr_type);
4054-
}
4055-
40564060
// Substitute into inner declarator
40574061
// For int (*p)[3]: inner_decl is Pointer(Void), result_type is Array(3, int)
40584062
// -> Pointer(Array(3, int))
4059-
// For void (*fp)(int): inner_decl is Pointer(Void), result_type is Function(void, [int])
4060-
// -> Pointer(Function(void, [int]))
4063+
// For struct node *(*fp)(int): inner_decl is Pointer(Void),
4064+
// result_type is Function(Pointer(struct node), [int])
4065+
// -> Pointer(Function(Pointer(struct node), [int]))
40614066
result_type_id = self.substitute_base_type(inner_tid, result_type_id);
40624067
} else {
40634068
// Simple declarator: char *arr[3]
@@ -7753,6 +7758,96 @@ mod tests {
77537758
}
77547759
}
77557760

7761+
#[test]
7762+
fn test_function_pointer_returning_struct_pointer() {
7763+
// Function pointer returning a struct pointer: struct node *(*fp)(int)
7764+
// This declares fp as a pointer to a function (int) -> struct node*
7765+
// Regression test for issue where outer pointers in grouped declarators
7766+
// were applied after the function type instead of before (to the return type)
7767+
let (tu, types, _strings) =
7768+
parse_tu("struct node { int value; }; struct node *(*fp)(int);").unwrap();
7769+
assert_eq!(tu.items.len(), 2);
7770+
match &tu.items[1] {
7771+
ExternalDecl::Declaration(decl) => {
7772+
assert_eq!(decl.declarators.len(), 1);
7773+
// fp should be Pointer -> Function -> Pointer -> struct node
7774+
assert_eq!(types.kind(decl.declarators[0].typ), TypeKind::Pointer);
7775+
7776+
// Base type of fp should be Function (not Pointer!)
7777+
let func_id = types.base_type(decl.declarators[0].typ).unwrap();
7778+
assert_eq!(
7779+
types.kind(func_id),
7780+
TypeKind::Function,
7781+
"Function pointer base type should be Function, not Pointer"
7782+
);
7783+
7784+
// Function return type should be Pointer
7785+
let ret_id = types.base_type(func_id).unwrap();
7786+
assert_eq!(
7787+
types.kind(ret_id),
7788+
TypeKind::Pointer,
7789+
"Function return type should be Pointer"
7790+
);
7791+
7792+
// Pointer base type should be Struct
7793+
let struct_id = types.base_type(ret_id).unwrap();
7794+
assert_eq!(
7795+
types.kind(struct_id),
7796+
TypeKind::Struct,
7797+
"Pointer base type should be Struct"
7798+
);
7799+
7800+
// Function should take one int parameter
7801+
if let Some(params) = types.params(func_id) {
7802+
assert_eq!(params.len(), 1);
7803+
assert_eq!(types.kind(params[0]), TypeKind::Int);
7804+
}
7805+
}
7806+
_ => panic!("Expected Declaration"),
7807+
}
7808+
}
7809+
7810+
#[test]
7811+
fn test_pointer_to_array_of_pointers() {
7812+
// Pointer to array of pointers: int *(*p)[3]
7813+
// This declares p as a pointer to an array of 3 int pointers
7814+
// Regression test for issue where type chain was incorrectly built
7815+
let (tu, types, _strings) = parse_tu("int *(*p)[3];").unwrap();
7816+
assert_eq!(tu.items.len(), 1);
7817+
match &tu.items[0] {
7818+
ExternalDecl::Declaration(decl) => {
7819+
assert_eq!(decl.declarators.len(), 1);
7820+
// p should be Pointer -> Array -> Pointer -> int
7821+
assert_eq!(types.kind(decl.declarators[0].typ), TypeKind::Pointer);
7822+
7823+
// Base type of p should be Array
7824+
let array_id = types.base_type(decl.declarators[0].typ).unwrap();
7825+
assert_eq!(
7826+
types.kind(array_id),
7827+
TypeKind::Array,
7828+
"Pointer base type should be Array"
7829+
);
7830+
7831+
// Array element type should be Pointer
7832+
let elem_id = types.base_type(array_id).unwrap();
7833+
assert_eq!(
7834+
types.kind(elem_id),
7835+
TypeKind::Pointer,
7836+
"Array element type should be Pointer"
7837+
);
7838+
7839+
// Pointer base type should be Int
7840+
let int_id = types.base_type(elem_id).unwrap();
7841+
assert_eq!(
7842+
types.kind(int_id),
7843+
TypeKind::Int,
7844+
"Pointer base type should be Int"
7845+
);
7846+
}
7847+
_ => panic!("Expected Declaration"),
7848+
}
7849+
}
7850+
77567851
// ========================================================================
77577852
// Bitfield tests
77587853
// ========================================================================

cc/scripts/pcc_debug.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# -L <dir> : Library directory
1515
# Extra args after file are passed to compiler (e.g., libz.a)
1616

17-
PCC=/Users/jgarzik/repo/posixutils-rs/target/release/pcc
17+
PCC="${PCC:-pcc}"
1818
WORKDIR=/tmp/pcc_debug_$$
1919
mkdir -p $WORKDIR
2020

cc/tests/features/many_args.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// Copyright (c) 2025-2026 Jeff Garzik
3+
//
4+
// This file is part of the posixutils-rs project covered under
5+
// the MIT License. For the full license text, please see the LICENSE
6+
// file in the root directory of this project.
7+
// SPDX-License-Identifier: MIT
8+
//
9+
// Tests for functions with many mixed int/FP arguments exceeding register counts
10+
// Verifies correct stack offset calculations in regalloc.rs
11+
//
12+
13+
use crate::common::compile_and_run;
14+
15+
#[test]
16+
fn test_many_mixed_args() {
17+
let code = r#"
18+
// Function with 18 interleaved args: 9 ints + 9 doubles
19+
// x86_64 SysV ABI: 6 int regs (RDI-R9) + 8 FP regs (XMM0-7)
20+
// Stack args: m,o,q (ints at idx 6,7,8) + r (double at idx 8)
21+
double many_args(int a, double b, int c, double d, int e, double f,
22+
int g, double h, int i, double j, int k, double l,
23+
int m, double n, int o, double p, int q, double r) {
24+
double sum = (double)a + b + (double)c + d + (double)e + f +
25+
(double)g + h + (double)i + j + (double)k + l +
26+
(double)m + n + (double)o + p + (double)q + r;
27+
return sum;
28+
}
29+
30+
int main(void) {
31+
// ints: 1+2+3+4+5+6+7+8+9 = 45
32+
// doubles: 0.5+1.5+2.5+3.5+4.5+5.5+6.5+7.5+8.5 = 40.5
33+
// total: 85.5
34+
double result = many_args(1, 0.5, 2, 1.5, 3, 2.5,
35+
4, 3.5, 5, 4.5, 6, 5.5,
36+
7, 6.5, 8, 7.5, 9, 8.5);
37+
38+
if (result < 85.4 || result > 85.6) return 1;
39+
return 0;
40+
}
41+
"#;
42+
assert_eq!(compile_and_run("many_mixed_args", code), 0);
43+
}

cc/tests/features/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ mod has_feature;
2121
mod inline;
2222
mod inline_asm;
2323
mod macros;
24+
mod many_args;
2425
mod noreturn;
26+
mod ops_struct;
2527
mod optimization;
2628
mod popcount;
2729
mod setjmp;

0 commit comments

Comments
 (0)