@@ -1106,9 +1106,11 @@ impl Aarch64CodeGen {
11061106 Opcode :: SymAddr => {
11071107 if let ( Some ( target) , Some ( & src) ) = ( insn. target , insn. src . first ( ) ) {
11081108 let dst_loc = self . get_location ( target) ;
1109+ // Use X16 as scratch to avoid clobbering live values
1110+ // X16 is the intra-procedure-call scratch register (IP0)
11091111 let dst_reg = match & dst_loc {
11101112 Loc :: Reg ( r) => * r,
1111- _ => Reg :: X9 ,
1113+ _ => Reg :: X16 ,
11121114 } ;
11131115 let src_loc = self . get_location ( src) ;
11141116 match src_loc {
@@ -1693,6 +1695,13 @@ impl Aarch64CodeGen {
16931695
16941696 // Get source address (where the struct data is)
16951697 let value_loc = self . get_location ( value) ;
1698+
1699+ // Special case: if value is immediate 0, zero the struct instead of copying
1700+ if let Loc :: Imm ( 0 ) = value_loc {
1701+ self . emit_struct_zero ( insn, addr, num_qwords, frame_size) ;
1702+ return ;
1703+ }
1704+
16961705 // Get destination address
16971706 let addr_loc = self . get_location ( addr) ;
16981707
@@ -1777,6 +1786,71 @@ impl Aarch64CodeGen {
17771786 }
17781787 }
17791788
1789+ /// Emit code to zero a struct (for struct = {0} initialization)
1790+ fn emit_struct_zero (
1791+ & mut self ,
1792+ insn : & Instruction ,
1793+ addr : PseudoId ,
1794+ num_qwords : u32 ,
1795+ frame_size : i32 ,
1796+ ) {
1797+ let addr_loc = self . get_location ( addr) ;
1798+
1799+ // Load destination address into X17
1800+ match addr_loc {
1801+ Loc :: Stack ( offset) => {
1802+ let total_offset = self . stack_offset ( frame_size, offset) + insn. offset as i32 ;
1803+ self . push_lir ( Aarch64Inst :: Add {
1804+ size : OperandSize :: B64 ,
1805+ src1 : Reg :: X29 ,
1806+ src2 : GpOperand :: Imm ( total_offset as i64 ) ,
1807+ dst : Reg :: X17 ,
1808+ } ) ;
1809+ }
1810+ Loc :: Reg ( r) => {
1811+ if insn. offset != 0 {
1812+ self . push_lir ( Aarch64Inst :: Add {
1813+ size : OperandSize :: B64 ,
1814+ src1 : r,
1815+ src2 : GpOperand :: Imm ( insn. offset ) ,
1816+ dst : Reg :: X17 ,
1817+ } ) ;
1818+ } else if r != Reg :: X17 {
1819+ self . push_lir ( Aarch64Inst :: Mov {
1820+ size : OperandSize :: B64 ,
1821+ src : GpOperand :: Reg ( r) ,
1822+ dst : Reg :: X17 ,
1823+ } ) ;
1824+ }
1825+ }
1826+ Loc :: Global ( ref name) => {
1827+ self . emit_load_addr ( name, Reg :: X17 ) ;
1828+ if insn. offset != 0 {
1829+ self . push_lir ( Aarch64Inst :: Add {
1830+ size : OperandSize :: B64 ,
1831+ src1 : Reg :: X17 ,
1832+ src2 : GpOperand :: Imm ( insn. offset ) ,
1833+ dst : Reg :: X17 ,
1834+ } ) ;
1835+ }
1836+ }
1837+ _ => return ,
1838+ }
1839+
1840+ // Store zeros using XZR (zero register) - aarch64 has hardware zero reg!
1841+ for i in 0 ..num_qwords {
1842+ let byte_offset = ( i * 8 ) as i32 ;
1843+ self . push_lir ( Aarch64Inst :: Str {
1844+ size : OperandSize :: B64 ,
1845+ src : Reg :: Xzr ,
1846+ addr : MemAddr :: BaseOffset {
1847+ base : Reg :: X17 ,
1848+ offset : byte_offset,
1849+ } ,
1850+ } ) ;
1851+ }
1852+ }
1853+
17801854 fn emit_call ( & mut self , insn : & Instruction , frame_size : i32 , types : & TypeTable ) {
17811855 // Get function name (or placeholder for indirect calls)
17821856 let func_name = if insn. indirect_target . is_some ( ) {
@@ -1830,29 +1904,42 @@ impl Aarch64CodeGen {
18301904 let size = insn. size . max ( 32 ) ;
18311905 let op_size = OperandSize :: from_bits ( size) ;
18321906 let dst_loc = self . get_location ( target) ;
1907+ // Use X16 as default scratch to avoid clobbering live values
18331908 let dst_reg = match & dst_loc {
18341909 Loc :: Reg ( r) => * r,
1835- _ => Reg :: X9 ,
1910+ _ => Reg :: X16 ,
1911+ } ;
1912+
1913+ // Pick non-conflicting temp registers for cond/then/else values
1914+ // If dst_reg is one of our default temps, shift allocation to avoid conflicts
1915+ let ( cond_reg, then_reg, else_reg) = if dst_reg == Reg :: X10 {
1916+ ( Reg :: X11 , Reg :: X12 , Reg :: X13 )
1917+ } else if dst_reg == Reg :: X11 {
1918+ ( Reg :: X10 , Reg :: X12 , Reg :: X13 )
1919+ } else if dst_reg == Reg :: X12 {
1920+ ( Reg :: X10 , Reg :: X11 , Reg :: X13 )
1921+ } else {
1922+ ( Reg :: X10 , Reg :: X11 , Reg :: X12 ) // Original allocation
18361923 } ;
18371924
18381925 // Load condition, then and else values
1839- self . emit_move ( cond, Reg :: X10 , 64 , frame_size) ;
1840- self . emit_move ( then_val, Reg :: X11 , size, frame_size) ;
1841- self . emit_move ( else_val, Reg :: X12 , size, frame_size) ;
1926+ self . emit_move ( cond, cond_reg , 64 , frame_size) ;
1927+ self . emit_move ( then_val, then_reg , size, frame_size) ;
1928+ self . emit_move ( else_val, else_reg , size, frame_size) ;
18421929
18431930 // LIR: compare condition with zero
18441931 self . push_lir ( Aarch64Inst :: Cmp {
18451932 size : OperandSize :: B64 ,
1846- src1 : Reg :: X10 ,
1933+ src1 : cond_reg ,
18471934 src2 : GpOperand :: Imm ( 0 ) ,
18481935 } ) ;
18491936
18501937 // Use csel: if cond != 0, select then_val, else select else_val
18511938 self . push_lir ( Aarch64Inst :: Csel {
18521939 size : op_size,
18531940 cond : CondCode :: Ne ,
1854- src_true : Reg :: X11 ,
1855- src_false : Reg :: X12 ,
1941+ src_true : then_reg ,
1942+ src_false : else_reg ,
18561943 dst : dst_reg,
18571944 } ) ;
18581945
@@ -2146,6 +2233,7 @@ fn asm_reg_name_64(reg: Reg) -> &'static str {
21462233 Reg :: X29 => "x29" ,
21472234 Reg :: X30 => "x30" ,
21482235 Reg :: SP => "sp" ,
2236+ Reg :: Xzr => "xzr" ,
21492237 }
21502238}
21512239
@@ -2183,6 +2271,7 @@ fn asm_reg_name_32(reg: Reg) -> &'static str {
21832271 Reg :: X29 => "w29" ,
21842272 Reg :: X30 => "w30" ,
21852273 Reg :: SP => "wsp" ,
2274+ Reg :: Xzr => "wzr" ,
21862275 }
21872276}
21882277
0 commit comments