Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions cc/arch/aarch64/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,11 @@ impl Aarch64CodeGen {
// Data section
self.push_lir(Aarch64Inst::Directive(Directive::Data));

// Global visibility (if not static)
if !is_static {
// Check if this is a local symbol (starts with '.')
let is_local = name.starts_with('.');

// Global visibility (if not static and not local)
if !is_static && !is_local {
self.push_lir(Aarch64Inst::Directive(Directive::global(name)));
}

Expand All @@ -184,8 +187,12 @@ impl Aarch64CodeGen {
)));
}

// Label
self.push_lir(Aarch64Inst::Directive(Directive::global_label(name)));
// Label - use local_label for names starting with '.'
if is_local {
self.push_lir(Aarch64Inst::Directive(Directive::local_label(name)));
} else {
self.push_lir(Aarch64Inst::Directive(Directive::global_label(name)));
}

// Emit initializer
self.emit_initializer_data(init, size as usize);
Expand Down
15 changes: 11 additions & 4 deletions cc/arch/x86_64/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,11 @@ impl X86_64CodeGen {
// Data section
self.push_lir(X86Inst::Directive(Directive::Data));

// Global visibility (if not static)
if !is_static {
// Check if this is a local symbol (starts with '.')
let is_local = name.starts_with('.');

// Global visibility (if not static and not local)
if !is_static && !is_local {
self.push_lir(X86Inst::Directive(Directive::global(name)));
}

Expand All @@ -182,8 +185,12 @@ impl X86_64CodeGen {
self.push_lir(X86Inst::Directive(Directive::Align(align.trailing_zeros())));
}

// Label
self.push_lir(X86Inst::Directive(Directive::global_label(name)));
// Label - use local_label for names starting with '.'
if is_local {
self.push_lir(X86Inst::Directive(Directive::local_label(name)));
} else {
self.push_lir(X86Inst::Directive(Directive::global_label(name)));
}

// Emit initializer
self.emit_initializer_data(init, size as usize);
Expand Down
19 changes: 0 additions & 19 deletions cc/doc/BUGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,6 @@ This file documents known bugs where valid C99 code fails to compile.

## Parse Errors on Valid C99 Code

### Compound Literals

**Status**: Parse error

**Valid C99 Code**:
```c
struct Point { int x, y; };
struct Point p = (struct Point){1, 2}; // Compound literal
```

**Error**:
```
parse error: unexpected token in expression
```

**C99 Reference**: 6.5.2.5

---

### Array Parameter with `static`

**Status**: Parse error
Expand Down
95 changes: 93 additions & 2 deletions cc/ir/linearize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ pub struct Linearizer<'a> {
current_func_name: String,
/// Counter for generating unique static local names
static_local_counter: u32,
/// Counter for generating unique compound literal names (for file-scope compound literals)
compound_literal_counter: u32,
/// Static local variables (local name -> static local info)
/// This is persistent across function calls (not cleared per function)
static_locals: HashMap<String, StaticLocalInfo>,
Expand Down Expand Up @@ -135,6 +137,7 @@ impl<'a> Linearizer<'a> {
struct_return_size: 0,
current_func_name: String::new(),
static_local_counter: 0,
compound_literal_counter: 0,
static_locals: HashMap::new(),
current_pos: None,
target,
Expand Down Expand Up @@ -518,7 +521,8 @@ impl<'a> Linearizer<'a> {
/// - Struct initializers with designated and positional fields
/// - Address-of expressions (&symbol)
/// - Nested initializers
fn ast_init_to_ir(&self, expr: &Expr, typ: TypeId) -> Initializer {
/// - Compound literals (C99 6.5.2.5)
fn ast_init_to_ir(&mut self, expr: &Expr, typ: TypeId) -> Initializer {
match &expr.kind {
ExprKind::IntLit(v) => Initializer::Int(*v),
ExprKind::FloatLit(v) => Initializer::Float(*v),
Expand Down Expand Up @@ -573,6 +577,33 @@ impl<'a> Linearizer<'a> {
// Initializer list for arrays/structs
ExprKind::InitList { elements } => self.ast_init_list_to_ir(elements, typ),

// Compound literal in initializer context (C99 6.5.2.5)
ExprKind::CompoundLiteral {
typ: cl_type,
elements,
} => {
// Check if compound literal type matches target type
if *cl_type == typ {
// Direct value - treat like InitList
self.ast_init_list_to_ir(elements, typ)
} else if self.types.kind(typ) == TypeKind::Pointer {
// Pointer initialization - create anonymous static global
// and return its address
let anon_name = format!(".CL{}", self.compound_literal_counter);
self.compound_literal_counter += 1;

// Create the anonymous global
let init = self.ast_init_list_to_ir(elements, *cl_type);
self.module.add_global(&anon_name, *cl_type, init);

// Return address of the anonymous global
Initializer::SymAddr(anon_name)
} else {
// Type mismatch - try to use the initializer list directly
self.ast_init_list_to_ir(elements, typ)
}
}

// Identifier - for constant addresses (function pointers, array decay, etc.)
// or enum constants
ExprKind::Ident { name } => {
Expand Down Expand Up @@ -603,7 +634,7 @@ impl<'a> Linearizer<'a> {
}

/// Convert an AST initializer list to an IR Initializer
fn ast_init_list_to_ir(&self, elements: &[InitElement], typ: TypeId) -> Initializer {
fn ast_init_list_to_ir(&mut self, elements: &[InitElement], typ: TypeId) -> Initializer {
let type_kind = self.types.kind(typ);
let total_size = (self.types.size_bits(typ) / 8) as usize;

Expand Down Expand Up @@ -2147,6 +2178,28 @@ impl<'a> Linearizer<'a> {
));
addr
}
ExprKind::CompoundLiteral { typ, elements } => {
// Compound literal as lvalue: create it and return its address
// This is used for &(struct S){...}
let sym_id = self.alloc_pseudo();
let unique_name = format!(".compound_literal#{}", sym_id.0);
let sym = Pseudo::sym(sym_id, unique_name.clone());
if let Some(func) = &mut self.current_func {
func.add_pseudo(sym);
func.add_local(&unique_name, sym_id, *typ, false, self.current_bb);
}
self.linearize_init_list(sym_id, *typ, elements);

// Return address of the compound literal
let result = self.alloc_pseudo();
let pseudo = Pseudo::reg(result, result.0);
if let Some(func) = &mut self.current_func {
func.add_pseudo(pseudo);
}
let ptr_type = self.types.pointer_to(*typ);
self.emit(Instruction::sym_addr(result, sym_id, ptr_type));
result
}
_ => {
// Fallback: just evaluate the expression (shouldn't happen for valid lvalues)
self.linearize_expr(expr)
Expand Down Expand Up @@ -3249,6 +3302,44 @@ impl<'a> Linearizer<'a> {
panic!("InitList should be handled in declaration context, not as standalone expression")
}

ExprKind::CompoundLiteral { typ, elements } => {
// Compound literals have automatic storage at block scope
// Create an anonymous local variable, similar to how local variables work

// Create a symbol pseudo for the compound literal (its address)
let sym_id = self.alloc_pseudo();
let unique_name = format!(".compound_literal#{}", sym_id.0);
let sym = Pseudo::sym(sym_id, unique_name.clone());
if let Some(func) = &mut self.current_func {
func.add_pseudo(sym);
// Register as local for proper stack allocation
func.add_local(&unique_name, sym_id, *typ, false, self.current_bb);
}

// Initialize using existing init list machinery
self.linearize_init_list(sym_id, *typ, elements);

// For arrays: return pointer (array-to-pointer decay)
// For structs/scalars: load and return the value
let result = self.alloc_pseudo();
let pseudo = Pseudo::reg(result, result.0);
if let Some(func) = &mut self.current_func {
func.add_pseudo(pseudo);
}

if self.types.kind(*typ) == TypeKind::Array {
// Array compound literal - decay to pointer to first element
let elem_type = self.types.base_type(*typ).unwrap_or(self.types.int_id);
let ptr_type = self.types.pointer_to(elem_type);
self.emit(Instruction::sym_addr(result, sym_id, ptr_type));
} else {
// Struct/scalar compound literal - load the value
let size = self.types.size_bits(*typ);
self.emit(Instruction::load(result, sym_id, 0, *typ, size));
}
result
}

// ================================================================
// Variadic function support (va_* builtins)
// ================================================================
Expand Down
7 changes: 7 additions & 0 deletions cc/parse/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,13 @@ pub enum ExprKind {
expr: Box<Expr>,
},

/// Compound literal: (type){ init-list }
/// C99 6.5.2.5: Creates unnamed object with automatic/static storage
CompoundLiteral {
typ: TypeId,
elements: Vec<InitElement>,
},

/// sizeof type: sizeof(int)
SizeofType(TypeId),

Expand Down
46 changes: 42 additions & 4 deletions cc/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1511,12 +1511,19 @@ impl<'a> Parser<'a> {
result_id = self.types.intern(Type::pointer(result_id));
}

// Handle array declarators: int[10], char[20], etc.
// Handle array declarators: int[10], char[20], int[], etc.
while self.is_special(b'[') {
self.advance();
if let Ok(size_expr) = self.parse_conditional_expr() {
// Check for empty brackets [] (incomplete array type for compound literals)
if self.is_special(b']') {
// Empty brackets - create array with size 0 (size to be determined from initializer)
result_id = self.types.intern(Type::array(result_id, 0));
} else if let Ok(size_expr) = self.parse_conditional_expr() {
if let Some(size) = self.eval_const_expr(&size_expr) {
result_id = self.types.intern(Type::array(result_id, size as usize));
} else {
// Non-constant size (VLA in type name) - create array with size 0
result_id = self.types.intern(Type::array(result_id, 0));
}
}
if !self.is_special(b']') {
Expand Down Expand Up @@ -2214,11 +2221,42 @@ impl<'a> Parser<'a> {
let paren_pos = self.current_pos();
self.advance();

// Try to detect cast (type) - simplified check
// Try to detect cast (type) or compound literal (type){...}
if let Some(typ) = self.try_parse_type_name() {
self.expect_special(b')')?;

// Check for compound literal: (type){ ... }
if self.is_special(b'{') {
let init_list = self.parse_initializer_list()?;
let elements = match init_list.kind {
ExprKind::InitList { elements } => elements,
_ => unreachable!(),
};

// For incomplete array types (size 0), determine size from initializer
let final_typ = if self.types.kind(typ) == TypeKind::Array
&& self.types.get(typ).array_size == Some(0)
{
// Array size should be determined from initializer element count
let elem_type =
self.types.base_type(typ).unwrap_or(self.types.int_id);
self.types.intern(Type::array(elem_type, elements.len()))
} else {
typ
};

return Ok(Self::typed_expr(
ExprKind::CompoundLiteral {
typ: final_typ,
elements,
},
final_typ,
paren_pos,
));
}

// Regular cast expression
let expr = self.parse_unary_expr()?;
// Cast expression has the cast type
return Ok(Self::typed_expr(
ExprKind::Cast {
cast_type: typ,
Expand Down
Loading