Skip to content

Commit 431dc7c

Browse files
authored
Merge pull request #438 from rustcoreutils/cc
Cc updates
2 parents d3254ea + 9f1800f commit 431dc7c

File tree

25 files changed

+3232
-112
lines changed

25 files changed

+3232
-112
lines changed

cc/README.md

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -78,42 +78,22 @@ EOF
7878
## Current Limitations
7979

8080
Supported:
81-
- Basic types: void, char, short, int, long, long long (signed/unsigned), float, double, \_Bool
82-
- Pointers (including void\*), arrays, structs, unions
83-
- Control flow: if/else, while, do-while, for, switch, goto
84-
- Functions with parameters and return values
85-
- Function pointer declarations
86-
- Enums
87-
- Variadic functions (va\_list, va\_start, va\_arg, va\_copy, va\_end)
88-
- Storage class specifiers: static, extern
89-
- Static local variables (block scope with static duration)
90-
- Static functions (internal linkage)
91-
- Static global variables (internal linkage)
92-
- Extern declarations
93-
- C preprocessor basics (#include, #define, #ifdef, #ifndef, #if, #elif, #else, #endif)
94-
- Bitfields (named, unnamed, zero-width for alignment)
95-
96-
Not yet implemented:
97-
- longjmp, setjmp
98-
- `inline` and inlining support
81+
- C99 standard
82+
83+
Not yet implemented (exceptions to C99, or features we want to add):
84+
- Actual inlining optimization (the `inline` keyword is supported but functions are not inlined)
9985
- multi-register returns (for structs larger than 8 bytes)
10086
- -fverbose-asm
101-
- Complex initializers
102-
- constant expression evaluation
10387
- VLAs (variable-length arrays)
10488
- top builtins to implement:
10589
__builtin_expect
106-
__builtin_clz / clzl / clzll
107-
__builtin_ctz / ctzl / ctzll
10890
__sync_synchronize
10991
__sync_fetch_and_add (and maybe a couple of its siblings)
110-
__builtin_unreachable (helps optimizations + silences some warnings)
111-
- DCE and other opt passes
11292
- assembly peephole optimizations
11393
- _Complex
11494
- C11 Alignment Specifiers (_Alignas, _Alignof)
11595
- C11 Thread-Local Storage (_Thread_local) and atomics (_Atomic)
116-
- Other C11 features: _Static_assert, _Generic, _Noreturn, anonymous structs
96+
- Other C11 features: _Static_assert, _Generic, anonymous structs
11797

11898
## Known Issues
11999

cc/arch/aarch64/codegen.rs

Lines changed: 265 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,15 @@ impl Aarch64CodeGen {
188188
self.push_lir(Aarch64Inst::Directive(Directive::global_label(name)));
189189

190190
// Emit initializer
191+
self.emit_initializer_data(init, size as usize);
192+
}
193+
194+
/// Emit data for an initializer, recursively handling complex types
195+
fn emit_initializer_data(&mut self, init: &Initializer, size: usize) {
191196
match init {
192197
Initializer::None => {
193-
// For static uninitialized, use .zero (not .comm)
194-
self.push_lir(Aarch64Inst::Directive(Directive::Zero(size)));
198+
// Zero-fill
199+
self.push_lir(Aarch64Inst::Directive(Directive::Zero(size as u32)));
195200
}
196201
Initializer::Int(val) => match size {
197202
1 => self.push_lir(Aarch64Inst::Directive(Directive::Byte(*val))),
@@ -210,6 +215,82 @@ impl Aarch64CodeGen {
210215
self.push_lir(Aarch64Inst::Directive(Directive::Quad(bits as i64)));
211216
}
212217
}
218+
Initializer::String(s) => {
219+
// Emit string as .ascii (without null terminator)
220+
self.push_lir(Aarch64Inst::Directive(Directive::Ascii(escape_string(s))));
221+
// Zero-fill remaining bytes if array is larger than string
222+
let string_len = s.len() + 1; // +1 for null terminator
223+
if size > string_len {
224+
self.push_lir(Aarch64Inst::Directive(Directive::Zero(
225+
(size - string_len) as u32,
226+
)));
227+
} else if size > s.len() {
228+
// Need null terminator
229+
self.push_lir(Aarch64Inst::Directive(Directive::Byte(0)));
230+
}
231+
}
232+
Initializer::Array {
233+
elem_size,
234+
total_size,
235+
elements,
236+
} => {
237+
// Emit array elements with gaps filled by zeros
238+
let mut current_offset = 0usize;
239+
240+
for (offset, elem_init) in elements {
241+
// Zero-fill gap before this element
242+
if *offset > current_offset {
243+
self.push_lir(Aarch64Inst::Directive(Directive::Zero(
244+
(*offset - current_offset) as u32,
245+
)));
246+
}
247+
248+
// Emit element
249+
self.emit_initializer_data(elem_init, *elem_size);
250+
current_offset = offset + elem_size;
251+
}
252+
253+
// Zero-fill remaining space
254+
if *total_size > current_offset {
255+
self.push_lir(Aarch64Inst::Directive(Directive::Zero(
256+
(*total_size - current_offset) as u32,
257+
)));
258+
}
259+
}
260+
Initializer::Struct { total_size, fields } => {
261+
// Emit struct fields with gaps (padding) filled by zeros
262+
let mut current_offset = 0usize;
263+
264+
for (offset, field_size, field_init) in fields {
265+
// Zero-fill gap before this field (padding)
266+
if *offset > current_offset {
267+
self.push_lir(Aarch64Inst::Directive(Directive::Zero(
268+
(*offset - current_offset) as u32,
269+
)));
270+
}
271+
272+
// Emit field with its proper size
273+
self.emit_initializer_data(field_init, *field_size);
274+
current_offset = offset + field_size;
275+
}
276+
277+
// Zero-fill remaining space (trailing padding)
278+
if *total_size > current_offset {
279+
self.push_lir(Aarch64Inst::Directive(Directive::Zero(
280+
(*total_size - current_offset) as u32,
281+
)));
282+
}
283+
}
284+
Initializer::SymAddr(name) => {
285+
// Emit symbol address as 64-bit relocatable reference
286+
use crate::arch::lir::Symbol;
287+
let sym = if name.starts_with('.') {
288+
Symbol::local(name.clone())
289+
} else {
290+
Symbol::global(name.clone())
291+
};
292+
self.push_lir(Aarch64Inst::Directive(Directive::QuadSym(sym)));
293+
}
213294
}
214295
}
215296

@@ -1126,10 +1207,40 @@ impl Aarch64CodeGen {
11261207
self.emit_bswap64(insn, *total_frame);
11271208
}
11281209

1210+
// ================================================================
1211+
// Count trailing zeros builtins
1212+
// ================================================================
1213+
Opcode::Ctz32 => {
1214+
self.emit_ctz32(insn, *total_frame);
1215+
}
1216+
1217+
Opcode::Ctz64 => {
1218+
self.emit_ctz64(insn, *total_frame);
1219+
}
1220+
11291221
Opcode::Alloca => {
11301222
self.emit_alloca(insn, *total_frame);
11311223
}
11321224

1225+
Opcode::Unreachable => {
1226+
// Emit brk #1 instruction - software breakpoint that traps
1227+
// This is used for __builtin_unreachable() to indicate code
1228+
// that should never be reached. If it is reached, the CPU
1229+
// will generate a SIGTRAP.
1230+
self.push_lir(Aarch64Inst::Brk { imm: 1 });
1231+
}
1232+
1233+
// ================================================================
1234+
// setjmp/longjmp support
1235+
// ================================================================
1236+
Opcode::Setjmp => {
1237+
self.emit_setjmp(insn, *total_frame);
1238+
}
1239+
1240+
Opcode::Longjmp => {
1241+
self.emit_longjmp(insn, *total_frame);
1242+
}
1243+
11331244
// Skip no-ops and unimplemented
11341245
_ => {}
11351246
}
@@ -3517,6 +3628,158 @@ impl Aarch64CodeGen {
35173628
}
35183629
}
35193630

3631+
/// Emit __builtin_ctz - count trailing zeros for 32-bit value
3632+
fn emit_ctz32(&mut self, insn: &Instruction, frame_size: i32) {
3633+
self.emit_ctz(insn, frame_size, OperandSize::B32);
3634+
}
3635+
3636+
/// Emit __builtin_ctzl/__builtin_ctzll - count trailing zeros for 64-bit value
3637+
fn emit_ctz64(&mut self, insn: &Instruction, frame_size: i32) {
3638+
self.emit_ctz(insn, frame_size, OperandSize::B64);
3639+
}
3640+
3641+
/// Emit count trailing zeros - shared implementation
3642+
/// On AArch64, CTZ is computed as CLZ(RBIT(x))
3643+
fn emit_ctz(&mut self, insn: &Instruction, frame_size: i32, src_size: OperandSize) {
3644+
let src = match insn.src.first() {
3645+
Some(&s) => s,
3646+
None => return,
3647+
};
3648+
let dst = match insn.target {
3649+
Some(t) => t,
3650+
None => return,
3651+
};
3652+
3653+
let src_loc = self.get_location(src);
3654+
let dst_loc = self.get_location(dst);
3655+
let scratch = Reg::X9;
3656+
3657+
// Load source into scratch register
3658+
match src_loc {
3659+
Loc::Reg(r) => {
3660+
self.push_lir(Aarch64Inst::Mov {
3661+
size: src_size,
3662+
src: GpOperand::Reg(r),
3663+
dst: scratch,
3664+
});
3665+
}
3666+
Loc::Stack(off) => {
3667+
// FP-relative for alloca safety
3668+
let adjusted = frame_size + off;
3669+
self.push_lir(Aarch64Inst::Ldr {
3670+
size: src_size,
3671+
addr: MemAddr::BaseOffset {
3672+
base: Reg::X29,
3673+
offset: adjusted,
3674+
},
3675+
dst: scratch,
3676+
});
3677+
}
3678+
Loc::Imm(v) => {
3679+
self.push_lir(Aarch64Inst::Mov {
3680+
size: src_size,
3681+
src: GpOperand::Imm(v),
3682+
dst: scratch,
3683+
});
3684+
}
3685+
_ => return,
3686+
}
3687+
3688+
// Reverse bits: RBIT
3689+
self.push_lir(Aarch64Inst::Rbit {
3690+
size: src_size,
3691+
src: scratch,
3692+
dst: scratch,
3693+
});
3694+
3695+
// Count leading zeros: CLZ - this gives us the count of trailing zeros
3696+
self.push_lir(Aarch64Inst::Clz {
3697+
size: src_size,
3698+
src: scratch,
3699+
dst: scratch,
3700+
});
3701+
3702+
// Store result (return type is int, always 32-bit)
3703+
match dst_loc {
3704+
Loc::Reg(r) => {
3705+
self.push_lir(Aarch64Inst::Mov {
3706+
size: OperandSize::B32,
3707+
src: GpOperand::Reg(scratch),
3708+
dst: r,
3709+
});
3710+
}
3711+
Loc::Stack(off) => {
3712+
// FP-relative for alloca safety
3713+
let adjusted = frame_size + off;
3714+
self.push_lir(Aarch64Inst::Str {
3715+
size: OperandSize::B32,
3716+
src: scratch,
3717+
addr: MemAddr::BaseOffset {
3718+
base: Reg::X29,
3719+
offset: adjusted,
3720+
},
3721+
});
3722+
}
3723+
_ => {}
3724+
}
3725+
}
3726+
3727+
/// Emit setjmp(env) - saves execution context
3728+
/// AAPCS64: env in X0, returns int in W0
3729+
fn emit_setjmp(&mut self, insn: &Instruction, frame_size: i32) {
3730+
let env = match insn.src.first() {
3731+
Some(&e) => e,
3732+
None => return,
3733+
};
3734+
let target = match insn.target {
3735+
Some(t) => t,
3736+
None => return,
3737+
};
3738+
3739+
// Put env argument in X0 (first argument register)
3740+
self.emit_move(env, Reg::X0, 64, frame_size);
3741+
3742+
// Call setjmp
3743+
self.push_lir(Aarch64Inst::Bl {
3744+
target: CallTarget::Direct(Symbol::global("setjmp")),
3745+
});
3746+
3747+
// Store result from W0 to target
3748+
let dst_loc = self.get_location(target);
3749+
self.emit_move_to_loc(Reg::X0, &dst_loc, 32, frame_size);
3750+
}
3751+
3752+
/// Emit longjmp(env, val) - restores execution context (noreturn)
3753+
/// AAPCS64: env in X0, val in X1
3754+
fn emit_longjmp(&mut self, insn: &Instruction, frame_size: i32) {
3755+
let env = match insn.src.first() {
3756+
Some(&e) => e,
3757+
None => return,
3758+
};
3759+
let val = match insn.src.get(1) {
3760+
Some(&v) => v,
3761+
None => return,
3762+
};
3763+
3764+
// IMPORTANT: Load val first into X1, THEN env into X0.
3765+
// If we loaded env into X0 first and val was passed as the first
3766+
// function argument (in X0), it would get overwritten.
3767+
// Put val argument in X1 (second argument register) FIRST
3768+
self.emit_move(val, Reg::X1, 32, frame_size);
3769+
3770+
// Put env argument in X0 (first argument register)
3771+
self.emit_move(env, Reg::X0, 64, frame_size);
3772+
3773+
// Call longjmp (noreturn - control never comes back)
3774+
self.push_lir(Aarch64Inst::Bl {
3775+
target: CallTarget::Direct(Symbol::global("longjmp")),
3776+
});
3777+
3778+
// Emit brk after longjmp since it never returns
3779+
// This helps catch any bugs where longjmp somehow returns
3780+
self.push_lir(Aarch64Inst::Brk { imm: 1 });
3781+
}
3782+
35203783
/// Emit __builtin_alloca - dynamic stack allocation
35213784
fn emit_alloca(&mut self, insn: &Instruction, frame_size: i32) {
35223785
let size = match insn.src.first() {

0 commit comments

Comments
 (0)