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
1 change: 0 additions & 1 deletion cc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ Supported:

Not yet implemented (exceptions to C99, or features we want to add):
- Actual inlining optimization (the `inline` keyword is supported but functions are not inlined)
- multi-register returns (for structs larger than 8 bytes)
- -fverbose-asm
- top builtins to implement:
__builtin_expect
Expand Down
51 changes: 49 additions & 2 deletions cc/arch/aarch64/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -789,12 +789,19 @@ impl Aarch64CodeGen {

Opcode::Ret => {
// Move return value to x0 (integer), v0 (float), or v0+v1 (complex) if present
// Two-register struct returns (9-16 bytes) go in X0+X1
if let Some(&src) = insn.src.first() {
let src_loc = self.get_location(src);
let is_complex = insn.typ.is_some_and(|t| types.is_complex(t));
let is_fp = matches!(src_loc, Loc::VReg(_) | Loc::FImm(..));

if is_complex {
if insn.is_two_reg_return {
// Two-register struct return: src[0] -> X0, src[1] -> X1
self.emit_move(src, Reg::X0, 64, *total_frame);
if let Some(&src2) = insn.src.get(1) {
self.emit_move(src2, Reg::X1, 64, *total_frame);
}
} else if is_complex {
// Complex return value: load real into V0, imag into V1
// The source is a pointer to the complex value
let (fp_size, imag_offset) = complex_fp_info(types, insn.typ.unwrap());
Expand Down Expand Up @@ -2633,7 +2640,47 @@ impl Aarch64CodeGen {
// Get return value size from type
let ret_size = insn.size.max(32);

if is_complex_result {
if insn.is_two_reg_return {
// Two-register struct return: X0 has low 8 bytes, X1 has high 8 bytes
// Store both to the target location (which must be a stack slot)
match dst_loc {
Loc::Stack(offset) => {
let actual_offset = self.stack_offset(frame_size, offset);
// Store X0 (low 8 bytes)
self.push_lir(Aarch64Inst::Str {
size: OperandSize::B64,
src: Reg::X0,
addr: MemAddr::BaseOffset {
base: Reg::X29,
offset: actual_offset,
},
});
// Store X1 (high 8 bytes)
self.push_lir(Aarch64Inst::Str {
size: OperandSize::B64,
src: Reg::X1,
addr: MemAddr::BaseOffset {
base: Reg::X29,
offset: actual_offset + 8,
},
});
}
Loc::Reg(r) => {
// Address in register - store through it
self.push_lir(Aarch64Inst::Str {
size: OperandSize::B64,
src: Reg::X0,
addr: MemAddr::BaseOffset { base: r, offset: 0 },
});
self.push_lir(Aarch64Inst::Str {
size: OperandSize::B64,
src: Reg::X1,
addr: MemAddr::BaseOffset { base: r, offset: 8 },
});
}
_ => {}
}
} else if is_complex_result {
// Complex return value is in V0 (real) + V1 (imag)
// Store both parts to the target location
let (fp_size, imag_offset) = complex_fp_info(types, insn.typ.unwrap());
Expand Down
51 changes: 49 additions & 2 deletions cc/arch/x86_64/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -594,12 +594,19 @@ impl X86_64CodeGen {
Opcode::Ret => {
// Move return value to appropriate register if present
// System V AMD64 ABI: integers in RAX, floats in XMM0, complex in XMM0+XMM1
// Two-register struct returns (9-16 bytes) go in RAX+RDX
if let Some(src) = insn.src.first() {
let src_loc = self.get_location(*src);
let is_complex = insn.typ.is_some_and(|t| types.is_complex(t));
let is_fp = matches!(src_loc, Loc::Xmm(_) | Loc::FImm(..))
|| insn.typ.is_some_and(|t| types.is_float(t));
if is_complex {
if insn.is_two_reg_return {
// Two-register struct return: src[0] -> RAX, src[1] -> RDX
self.emit_move(*src, Reg::Rax, 64);
if let Some(&src2) = insn.src.get(1) {
self.emit_move(src2, Reg::Rdx, 64);
}
} else if is_complex {
// Complex return value: load real into XMM0, imag into XMM1
// The source is a pointer to the complex value
let (fp_size, imag_offset) = complex_fp_info(types, insn.typ.unwrap());
Expand Down Expand Up @@ -3026,7 +3033,47 @@ impl X86_64CodeGen {
// Get return value size from type
let ret_size = insn.size.max(32);

if is_complex_result {
if insn.is_two_reg_return {
// Two-register struct return: RAX has low 8 bytes, RDX has high 8 bytes
// Store both to the target location (which must be a stack slot)
match dst_loc {
Loc::Stack(offset) => {
let adjusted = offset + self.callee_saved_offset;
// Store RAX (low 8 bytes)
self.push_lir(X86Inst::Mov {
size: OperandSize::B64,
src: GpOperand::Reg(Reg::Rax),
dst: GpOperand::Mem(MemAddr::BaseOffset {
base: Reg::Rbp,
offset: -adjusted,
}),
});
// Store RDX (high 8 bytes)
self.push_lir(X86Inst::Mov {
size: OperandSize::B64,
src: GpOperand::Reg(Reg::Rdx),
dst: GpOperand::Mem(MemAddr::BaseOffset {
base: Reg::Rbp,
offset: -adjusted + 8,
}),
});
}
Loc::Reg(r) => {
// Address in register - store through it
self.push_lir(X86Inst::Mov {
size: OperandSize::B64,
src: GpOperand::Reg(Reg::Rax),
dst: GpOperand::Mem(MemAddr::BaseOffset { base: r, offset: 0 }),
});
self.push_lir(X86Inst::Mov {
size: OperandSize::B64,
src: GpOperand::Reg(Reg::Rdx),
dst: GpOperand::Mem(MemAddr::BaseOffset { base: r, offset: 8 }),
});
}
_ => {}
}
} else if is_complex_result {
// Complex return value is in XMM0 (real) + XMM1 (imag)
// Store both parts to the target location
let (fp_size, imag_offset) = complex_fp_info(types, insn.typ.unwrap());
Expand Down
59 changes: 0 additions & 59 deletions cc/doc/C99.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ organized by priority and complexity.
## Table of Contents

- [Medium Priority](#medium-priority)
- [Array Parameter Qualifiers](#array-parameter-qualifiers)
- [`[*]` in Function Prototypes](#-in-function-prototypes)
- [Multi-Register Struct Returns](#multi-register-struct-returns)
- [Low Priority](#low-priority)
- [Inline Function Optimization](#inline-function-optimization)
- [FP Register Save for Variadic Functions](#fp-register-save-for-variadic-functions)
Expand All @@ -17,34 +15,6 @@ organized by priority and complexity.

## Medium Priority

### Array Parameter Qualifiers

**Status**: NOT IMPLEMENTED

**C99 Reference**: 6.7.5.3

**Description**: Qualifiers and `static` in array parameter declarations:
```c
void f(int a[const 10]); // const-qualified array parameter
void f(int a[static 10]); // at least 10 elements guaranteed
void f(int a[restrict]); // restrict-qualified
void f(int a[const static 10]); // combined
```

**Current Behavior**: Parse error: "expected ']'"

**Location**: `cc/parse/parser.rs` array declarator parsing

**Implementation Notes**:
- Extend array declarator parsing to allow qualifiers after `[`
- `static` indicates minimum size (optimization hint)
- Qualifiers apply to the pointer the array decays to
- These are hints for optimization, not semantic requirements

**Complexity**: Medium

---

### `[*]` in Function Prototypes

**Status**: NOT IMPLEMENTED
Expand All @@ -67,32 +37,6 @@ void f(int n, int arr[*]); // VLA parameter with unspecified size

---

### Multi-Register Struct Returns

**Status**: PARTIAL

**C99 Reference**: ABI-dependent (not in C99 itself)

**Description**: Structs that fit in two registers should be returned in two registers per ABI.

**Current Behavior**:
- Structs > 8 bytes use sret (hidden pointer parameter)
- Structs 9-16 bytes should use two registers but don't

**Location**:
- `cc/README.md` line 85
- `cc/target.rs` lines 99-100

**Implementation Notes**:
- x86-64 SysV: structs up to 16 bytes can be returned in RAX+RDX
- AArch64: structs up to 16 bytes can be returned in X0+X1
- Need to classify struct fields according to ABI rules
- Affects both caller and callee code generation

**Complexity**: Medium-High

---

## Low Priority

These items are implemented but incomplete or have known limitations that rarely affect real code.
Expand Down Expand Up @@ -145,10 +89,7 @@ These items are implemented but incomplete or have known limitations that rarely

| Feature | Status | Complexity | Priority |
|---------|--------|------------|----------|
| `_Complex` | **Implemented** | High | High |
| Array parameter qualifiers | Not implemented | Medium | Medium |
| `[*]` in prototypes | Not implemented | Low | Medium |
| Multi-register struct returns | Partial | Medium-High | Medium |
| Inline optimization | Partial | High | Low |
| FP variadic save | Partial | Medium | Low |

Expand Down
Loading