@@ -3,8 +3,9 @@ use crate::type_::Type;
3
3
use crate :: type_of:: LayoutLlvmExt ;
4
4
use crate :: value:: Value ;
5
5
use 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 } ,
8
9
} ;
9
10
use rustc_middle:: ty:: layout:: HasTyCtxt ;
10
11
use rustc_middle:: ty:: Ty ;
@@ -89,6 +90,81 @@ fn emit_ptr_va_arg(
89
90
}
90
91
}
91
92
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
+
92
168
pub ( super ) fn emit_va_arg (
93
169
bx : & mut Builder < ' a , ' ll , ' tcx > ,
94
170
addr : OperandRef < ' tcx , & ' ll Value > ,
@@ -115,6 +191,7 @@ pub(super) fn emit_va_arg(
115
191
( "aarch64" , _) if target. target_os == "ios" => {
116
192
emit_ptr_va_arg ( bx, addr, target_ty, false , Align :: from_bytes ( 8 ) . unwrap ( ) , true )
117
193
}
194
+ ( "aarch64" , _) => emit_aapcs_va_arg ( bx, addr, target_ty) ,
118
195
// Windows x86_64
119
196
( "x86_64" , true ) => {
120
197
let target_ty_size = bx. cx . size_of ( target_ty) . bytes ( ) ;
0 commit comments