Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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