@@ -3,8 +3,9 @@ use crate::type_::Type;
33use crate :: type_of:: LayoutLlvmExt ;
44use crate :: value:: Value ;
55use rustc_codegen_ssa:: mir:: operand:: OperandRef ;
6- use rustc_codegen_ssa:: traits:: {
7- BaseTypeMethods , BuilderMethods , ConstMethods , DerivedTypeMethods ,
6+ use rustc_codegen_ssa:: {
7+ common:: IntPredicate ,
8+ traits:: { BaseTypeMethods , BuilderMethods , ConstMethods , DerivedTypeMethods } ,
89} ;
910use rustc_middle:: ty:: layout:: HasTyCtxt ;
1011use rustc_middle:: ty:: Ty ;
@@ -89,6 +90,81 @@ fn emit_ptr_va_arg(
8990 }
9091}
9192
93+ fn emit_aapcs_va_arg (
94+ bx : & mut Builder < ' a , ' ll , ' tcx > ,
95+ list : OperandRef < ' tcx , & ' ll Value > ,
96+ target_ty : Ty < ' tcx > ,
97+ ) -> & ' ll Value {
98+ // Implementation of the AAPCS64 calling convention for va_args see
99+ // https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst
100+ let va_list_addr = list. immediate ( ) ;
101+ let layout = bx. cx . layout_of ( target_ty) ;
102+
103+ let mut maybe_reg = bx. build_sibling_block ( "va_arg.maybe_reg" ) ;
104+ let mut in_reg = bx. build_sibling_block ( "va_arg.in_reg" ) ;
105+ let mut on_stack = bx. build_sibling_block ( "va_arg.on_stack" ) ;
106+ let mut end = bx. build_sibling_block ( "va_arg.end" ) ;
107+ let zero = bx. const_i32 ( 0 ) ;
108+ let offset_align = Align :: from_bytes ( 4 ) . unwrap ( ) ;
109+ assert ! ( & * bx. tcx( ) . sess. target. target. target_endian == "little" ) ;
110+
111+ let gr_type = target_ty. is_any_ptr ( ) || target_ty. is_integral ( ) ;
112+ let ( reg_off, reg_top_index, slot_size) = if gr_type {
113+ let gr_offs = bx. struct_gep ( va_list_addr, 7 ) ;
114+ let nreg = ( layout. size . bytes ( ) + 7 ) / 8 ;
115+ ( gr_offs, 3 , nreg * 8 )
116+ } else {
117+ let vr_off = bx. struct_gep ( va_list_addr, 9 ) ;
118+ let nreg = ( layout. size . bytes ( ) + 15 ) / 16 ;
119+ ( vr_off, 5 , nreg * 16 )
120+ } ;
121+
122+ // if the offset >= 0 then the value will be on the stack
123+ let mut reg_off_v = bx. load ( reg_off, offset_align) ;
124+ let use_stack = bx. icmp ( IntPredicate :: IntSGE , reg_off_v, zero) ;
125+ bx. cond_br ( use_stack, & on_stack. llbb ( ) , & maybe_reg. llbb ( ) ) ;
126+
127+ // The value at this point might be in a register, but there is a chance that
128+ // it could be on the stack so we have to update the offset and then check
129+ // the offset again.
130+
131+ if gr_type && layout. align . abi . bytes ( ) > 8 {
132+ reg_off_v = maybe_reg. add ( reg_off_v, bx. const_i32 ( 15 ) ) ;
133+ reg_off_v = maybe_reg. and ( reg_off_v, bx. const_i32 ( -16 ) ) ;
134+ }
135+ let new_reg_off_v = maybe_reg. add ( reg_off_v, bx. const_i32 ( slot_size as i32 ) ) ;
136+
137+ maybe_reg. store ( new_reg_off_v, reg_off, offset_align) ;
138+
139+ // Check to see if we have overflowed the registers as a result of this.
140+ // If we have then we need to use the stack for this value
141+ let use_stack = maybe_reg. icmp ( IntPredicate :: IntSGT , new_reg_off_v, zero) ;
142+ maybe_reg. cond_br ( use_stack, & on_stack. llbb ( ) , & in_reg. llbb ( ) ) ;
143+
144+ let top = in_reg. struct_gep ( va_list_addr, reg_top_index) ;
145+ let top = in_reg. load ( top, bx. tcx ( ) . data_layout . pointer_align . abi ) ;
146+
147+ // reg_value = *(@top + reg_off_v);
148+ let top = in_reg. gep ( top, & [ reg_off_v] ) ;
149+ let top = in_reg. bitcast ( top, bx. cx . type_ptr_to ( layout. llvm_type ( bx) ) ) ;
150+ let reg_value = in_reg. load ( top, layout. align . abi ) ;
151+ in_reg. br ( & end. llbb ( ) ) ;
152+
153+ // On Stack block
154+ let stack_value =
155+ emit_ptr_va_arg ( & mut on_stack, list, target_ty, false , Align :: from_bytes ( 8 ) . unwrap ( ) , true ) ;
156+ on_stack. br ( & end. llbb ( ) ) ;
157+
158+ let val = end. phi (
159+ layout. immediate_llvm_type ( bx) ,
160+ & [ reg_value, stack_value] ,
161+ & [ & in_reg. llbb ( ) , & on_stack. llbb ( ) ] ,
162+ ) ;
163+
164+ * bx = end;
165+ val
166+ }
167+
92168pub ( super ) fn emit_va_arg (
93169 bx : & mut Builder < ' a , ' ll , ' tcx > ,
94170 addr : OperandRef < ' tcx , & ' ll Value > ,
@@ -115,6 +191,7 @@ pub(super) fn emit_va_arg(
115191 ( "aarch64" , _) if target. target_os == "ios" => {
116192 emit_ptr_va_arg ( bx, addr, target_ty, false , Align :: from_bytes ( 8 ) . unwrap ( ) , true )
117193 }
194+ ( "aarch64" , _) => emit_aapcs_va_arg ( bx, addr, target_ty) ,
118195 // Windows x86_64
119196 ( "x86_64" , true ) => {
120197 let target_ty_size = bx. cx . size_of ( target_ty) . bytes ( ) ;
0 commit comments