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
4 changes: 4 additions & 0 deletions cc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ Not yet implemented (features we want to add):
- assembly peephole optimizations
- C11 `_Generic` type-generic selection

Will not implement:
- C99 `_Imaginary` type (removed in C11; no mainstream compiler implements it)
- C89 trigraphs (deprecated in C99, removed in C11; GCC/Clang disable by default)

## Code Quality

Please run `cargo fmt` before committing code, and `cargo clippy` regularly while working. Code should build without warnings.
Expand Down
97 changes: 94 additions & 3 deletions cc/arch/aarch64/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ pub struct Aarch64CodeGen {
pub(super) num_fixed_gp_params: usize,
/// External symbols (need GOT access on macOS)
pub(super) extern_symbols: HashSet<String>,
/// Thread-local storage symbols (need TLS access)
pub(super) tls_symbols: HashSet<String>,
/// Shared library mode (affects TLS model selection)
shared_mode: bool,
/// Position-independent code mode (for shared libraries)
pic_mode: bool,
/// Counter for generating unique labels (atomic loops, etc.)
Expand Down Expand Up @@ -88,6 +92,8 @@ impl Aarch64CodeGen {
reg_save_area_size: 0,
num_fixed_gp_params: 0,
extern_symbols: HashSet::new(),
tls_symbols: HashSet::new(),
shared_mode: false,
pic_mode: false,
unique_label_counter: 0,
stack_alloc_size: 0,
Expand Down Expand Up @@ -1766,6 +1772,12 @@ impl Aarch64CodeGen {

/// Load address of a global symbol into a register
pub(super) fn emit_load_addr(&mut self, name: &str, dst: Reg) {
// Thread-local storage: compute TLS address directly (Linux ELF only)
if self.tls_symbols.contains(name) && self.base.target.os == Os::Linux {
self.emit_tls_addr(name, dst);
return;
}

// Local labels (starting with '.') don't get the _ prefix on macOS
let sym = if name.starts_with('.') {
Symbol::local(name)
Expand Down Expand Up @@ -1800,8 +1812,79 @@ impl Aarch64CodeGen {
}
}

/// Check if a TLS symbol should use the Initial Exec model.
/// IE is used for extern TLS symbols or when building shared libraries.
/// Local Exec is used for locally-defined TLS in non-shared executables.
fn use_tls_ie(&self, name: &str) -> bool {
self.shared_mode || self.extern_symbols.contains(name)
}

/// Emit TLS address computation into dst register.
/// After this call, dst holds the address of the TLS variable.
fn emit_tls_addr(&mut self, name: &str, dst: Reg) {
let sym = Symbol::global(name);
if self.use_tls_ie(name) {
// Initial Exec model (extern TLS or shared library):
// adrp dst, :gottpoff:sym
// ldr dst, [dst, :gottpoff_lo12:sym]
// mrs tmp, tpidr_el0
// add dst, tmp, dst
let tmp = Reg::X16; // scratch register
self.push_lir(Aarch64Inst::AdrpGottpoff {
sym: sym.clone(),
dst,
});
self.push_lir(Aarch64Inst::LdrGottpoffLo12 {
sym,
base: dst,
dst,
});
self.push_lir(Aarch64Inst::Mrs {
sysreg: "tpidr_el0",
dst: tmp,
});
self.push_lir(Aarch64Inst::Add {
size: OperandSize::B64,
src1: tmp,
src2: GpOperand::Reg(dst),
dst,
});
} else {
// Local Exec model (locally-defined TLS in executable):
// mrs dst, tpidr_el0
// add dst, dst, :tprel_hi12:sym
// add dst, dst, :tprel_lo12_nc:sym
self.push_lir(Aarch64Inst::Mrs {
sysreg: "tpidr_el0",
dst,
});
self.push_lir(Aarch64Inst::AddTprelHi12 {
sym: sym.clone(),
base: dst,
dst,
});
self.push_lir(Aarch64Inst::AddTprelLo12Nc {
sym,
base: dst,
dst,
});
}
}

/// Load value of a global symbol into a register with specified size
pub(super) fn emit_load_global(&mut self, name: &str, dst: Reg, size: OperandSize) {
// Thread-local storage: compute TLS address, then load value (Linux ELF only)
if self.tls_symbols.contains(name) && self.base.target.os == Os::Linux {
self.emit_tls_addr(name, dst);
// dst now holds the address of the TLS variable; load from it
self.push_lir(Aarch64Inst::Ldr {
size,
addr: MemAddr::Base(dst),
dst,
});
return;
}

// Local labels (starting with '.') don't get the _ prefix on macOS
let sym = if name.starts_with('.') {
Symbol::local(name)
Expand Down Expand Up @@ -3602,6 +3685,15 @@ impl CodeGenerator for Aarch64CodeGen {
self.base.emit_debug = module.debug;
self.extern_symbols = module.extern_symbols.clone();

// Collect thread-local storage symbols (both defined and extern)
self.tls_symbols = module
.globals
.iter()
.filter(|g| g.is_thread_local)
.map(|g| g.name.clone())
.chain(module.extern_tls_symbols.iter().cloned())
.collect();

// Emit file header
self.emit_header();

Expand Down Expand Up @@ -3700,9 +3792,8 @@ impl CodeGenerator for Aarch64CodeGen {
self.pic_mode = pic;
}

fn set_shared_mode(&mut self, _shared: bool) {
// AArch64 TLS model selection not yet implemented
// For now, this is a no-op
fn set_shared_mode(&mut self, shared: bool) {
self.shared_mode = shared;
}
}

Expand Down
59 changes: 59 additions & 0 deletions cc/arch/aarch64/lir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,22 @@ pub enum Aarch64Inst {
/// Emits: ldr dst, [base, sym@GOTPAGEOFF]
LdrSymGotPageOff { sym: Symbol, base: Reg, dst: Reg },

/// MRS - Move system register to general-purpose register
/// Used for TLS: mrs dst, tpidr_el0
Mrs { sysreg: &'static str, dst: Reg },

/// TLS Local Exec: add dst, base, :tprel_hi12:sym
AddTprelHi12 { sym: Symbol, base: Reg, dst: Reg },

/// TLS Local Exec: add dst, base, :tprel_lo12_nc:sym
AddTprelLo12Nc { sym: Symbol, base: Reg, dst: Reg },

/// TLS Initial Exec: adrp dst, :gottpoff:sym
AdrpGottpoff { sym: Symbol, dst: Reg },

/// TLS Initial Exec: ldr dst, [base, :gottpoff_lo12:sym]
LdrGottpoffLo12 { sym: Symbol, base: Reg, dst: Reg },

// ========================================================================
// Integer Arithmetic
// ========================================================================
Expand Down Expand Up @@ -972,6 +988,49 @@ impl EmitAsm for Aarch64Inst {
);
}

// TLS Instructions
Aarch64Inst::Mrs { sysreg, dst } => {
let _ = writeln!(out, " mrs {}, {}", dst.name64(), sysreg);
}

Aarch64Inst::AddTprelHi12 { sym, base, dst } => {
let sym_name = sym.format_for_target(target);
let _ = writeln!(
out,
" add {}, {}, :tprel_hi12:{}",
dst.name64(),
base.name64(),
sym_name
);
}

Aarch64Inst::AddTprelLo12Nc { sym, base, dst } => {
let sym_name = sym.format_for_target(target);
let _ = writeln!(
out,
" add {}, {}, :tprel_lo12_nc:{}",
dst.name64(),
base.name64(),
sym_name
);
}

Aarch64Inst::AdrpGottpoff { sym, dst } => {
let sym_name = sym.format_for_target(target);
let _ = writeln!(out, " adrp {}, :gottpoff:{}", dst.name64(), sym_name);
}

Aarch64Inst::LdrGottpoffLo12 { sym, base, dst } => {
let sym_name = sym.format_for_target(target);
let _ = writeln!(
out,
" ldr {}, [{}, :gottpoff_lo12:{}]",
dst.name64(),
base.name64(),
sym_name
);
}

// Integer Arithmetic
Aarch64Inst::Add {
size,
Expand Down
12 changes: 12 additions & 0 deletions cc/builtin_headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ pub const STDALIGN_H: &str = include_str!("include/stdalign.h");
/// Builtin stdatomic.h - C11 atomic operations
pub const STDATOMIC_H: &str = include_str!("include/stdatomic.h");

/// Builtin stdnoreturn.h - C11 _Noreturn convenience macro
pub const STDNORETURN_H: &str = include_str!("include/stdnoreturn.h");

/// Builtin complex.h - C99 complex arithmetic
pub const COMPLEX_H: &str = include_str!("include/complex.h");

/// Builtin iso646.h - C95/C99 alternative operator spellings
pub const ISO646_H: &str = include_str!("include/iso646.h");

/// Builtin float.h - floating-point characteristics
pub const FLOAT_H: &str = include_str!("include/float.h");

Expand All @@ -50,11 +59,14 @@ pub const EMMINTRIN_H: &str = include_str!("include/emmintrin.h");
pub fn get_builtin_header(name: &str) -> Option<&'static str> {
match name {
"stdarg.h" => Some(STDARG_H),
"complex.h" => Some(COMPLEX_H),
"iso646.h" => Some(ISO646_H),
"stdbool.h" => Some(STDBOOL_H),
"stddef.h" => Some(STDDEF_H),
"limits.h" => Some(LIMITS_H),
"stdalign.h" => Some(STDALIGN_H),
"stdatomic.h" => Some(STDATOMIC_H),
"stdnoreturn.h" => Some(STDNORETURN_H),
"float.h" => Some(FLOAT_H),
"cpuid.h" => Some(CPUID_H),
"xmmintrin.h" => Some(XMMINTRIN_H),
Expand Down
Loading
Loading