@@ -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