@@ -44,12 +44,12 @@ fn emit_direct_ptr_va_arg<'ll, 'tcx>(
4444
4545 let aligned_size = size. align_to ( slot_size) . bytes ( ) as i32 ;
4646 let full_direct_size = bx. cx ( ) . const_i32 ( aligned_size) ;
47- let next = bx. inbounds_gep ( bx . type_i8 ( ) , addr, & [ full_direct_size] ) ;
47+ let next = bx. inbounds_ptradd ( addr, full_direct_size) ;
4848 bx. store ( next, va_list_addr, bx. tcx ( ) . data_layout . pointer_align . abi ) ;
4949
5050 if size. bytes ( ) < slot_size. bytes ( ) && bx. tcx ( ) . sess . target . endian == Endian :: Big {
5151 let adjusted_size = bx. cx ( ) . const_i32 ( ( slot_size. bytes ( ) - size. bytes ( ) ) as i32 ) ;
52- let adjusted = bx. inbounds_gep ( bx . type_i8 ( ) , addr, & [ adjusted_size] ) ;
52+ let adjusted = bx. inbounds_ptradd ( addr, adjusted_size) ;
5353 ( adjusted, addr_align)
5454 } else {
5555 ( addr, addr_align)
@@ -89,11 +89,31 @@ fn emit_aapcs_va_arg<'ll, 'tcx>(
8989 list : OperandRef < ' tcx , & ' ll Value > ,
9090 target_ty : Ty < ' tcx > ,
9191) -> & ' ll Value {
92+ let dl = bx. cx . data_layout ( ) ;
93+
9294 // Implementation of the AAPCS64 calling convention for va_args see
9395 // https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst
96+ //
97+ // typedef struct va_list {
98+ // void * stack; // next stack param
99+ // void * gr_top; // end of GP arg reg save area
100+ // void * vr_top; // end of FP/SIMD arg reg save area
101+ // int gr_offs; // offset from gr_top to next GP register arg
102+ // int vr_offs; // offset from vr_top to next FP/SIMD register arg
103+ // } va_list;
94104 let va_list_addr = list. immediate ( ) ;
95- let va_list_layout = list. deref ( bx. cx ) . layout ;
96- let va_list_ty = va_list_layout. llvm_type ( bx) ;
105+
106+ // There is no padding between fields since `void*` is size=8 align=8, `int` is size=4 align=4.
107+ // See https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst
108+ // Table 1, Byte size and byte alignment of fundamental data types
109+ // Table 3, Mapping of C & C++ built-in data types
110+ let ptr_offset = 8 ;
111+ let i32_offset = 4 ;
112+ let gr_top = bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( ptr_offset) ) ;
113+ let vr_top = bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( 2 * ptr_offset) ) ;
114+ let gr_offs = bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( 3 * ptr_offset) ) ;
115+ let vr_offs = bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( 3 * ptr_offset + i32_offset) ) ;
116+
97117 let layout = bx. cx . layout_of ( target_ty) ;
98118
99119 let maybe_reg = bx. append_sibling_block ( "va_arg.maybe_reg" ) ;
@@ -104,16 +124,12 @@ fn emit_aapcs_va_arg<'ll, 'tcx>(
104124 let offset_align = Align :: from_bytes ( 4 ) . unwrap ( ) ;
105125
106126 let gr_type = target_ty. is_any_ptr ( ) || target_ty. is_integral ( ) ;
107- let ( reg_off, reg_top_index, slot_size) = if gr_type {
108- let gr_offs =
109- bx. struct_gep ( va_list_ty, va_list_addr, va_list_layout. llvm_field_index ( bx. cx , 3 ) ) ;
127+ let ( reg_off, reg_top, slot_size) = if gr_type {
110128 let nreg = ( layout. size . bytes ( ) + 7 ) / 8 ;
111- ( gr_offs, va_list_layout . llvm_field_index ( bx . cx , 1 ) , nreg * 8 )
129+ ( gr_offs, gr_top , nreg * 8 )
112130 } else {
113- let vr_off =
114- bx. struct_gep ( va_list_ty, va_list_addr, va_list_layout. llvm_field_index ( bx. cx , 4 ) ) ;
115131 let nreg = ( layout. size . bytes ( ) + 15 ) / 16 ;
116- ( vr_off , va_list_layout . llvm_field_index ( bx . cx , 2 ) , nreg * 16 )
132+ ( vr_offs , vr_top , nreg * 16 )
117133 } ;
118134
119135 // if the offset >= 0 then the value will be on the stack
@@ -141,15 +157,14 @@ fn emit_aapcs_va_arg<'ll, 'tcx>(
141157
142158 bx. switch_to_block ( in_reg) ;
143159 let top_type = bx. type_ptr ( ) ;
144- let top = bx. struct_gep ( va_list_ty, va_list_addr, reg_top_index) ;
145- let top = bx. load ( top_type, top, bx. tcx ( ) . data_layout . pointer_align . abi ) ;
160+ let top = bx. load ( top_type, reg_top, dl. pointer_align . abi ) ;
146161
147162 // reg_value = *(@top + reg_off_v);
148- let mut reg_addr = bx. gep ( bx . type_i8 ( ) , top, & [ reg_off_v] ) ;
163+ let mut reg_addr = bx. ptradd ( top, reg_off_v) ;
149164 if bx. tcx ( ) . sess . target . endian == Endian :: Big && layout. size . bytes ( ) != slot_size {
150165 // On big-endian systems the value is right-aligned in its slot.
151166 let offset = bx. const_i32 ( ( slot_size - layout. size . bytes ( ) ) as i32 ) ;
152- reg_addr = bx. gep ( bx . type_i8 ( ) , reg_addr, & [ offset] ) ;
167+ reg_addr = bx. ptradd ( reg_addr, offset) ;
153168 }
154169 let reg_type = layout. llvm_type ( bx) ;
155170 let reg_value = bx. load ( reg_type, reg_addr, layout. align . abi ) ;
@@ -173,11 +188,29 @@ fn emit_s390x_va_arg<'ll, 'tcx>(
173188 list : OperandRef < ' tcx , & ' ll Value > ,
174189 target_ty : Ty < ' tcx > ,
175190) -> & ' ll Value {
191+ let dl = bx. cx . data_layout ( ) ;
192+
176193 // Implementation of the s390x ELF ABI calling convention for va_args see
177194 // https://github.com/IBM/s390x-abi (chapter 1.2.4)
195+ //
196+ // typedef struct __va_list_tag {
197+ // long __gpr;
198+ // long __fpr;
199+ // void *__overflow_arg_area;
200+ // void *__reg_save_area;
201+ // } va_list[1];
178202 let va_list_addr = list. immediate ( ) ;
179- let va_list_layout = list. deref ( bx. cx ) . layout ;
180- let va_list_ty = va_list_layout. llvm_type ( bx) ;
203+
204+ // There is no padding between fields since `long` and `void*` both have size=8 align=8.
205+ // https://github.com/IBM/s390x-abi (Table 1.1.: Scalar types)
206+ let i64_offset = 8 ;
207+ let ptr_offset = 8 ;
208+ let gpr = va_list_addr;
209+ let fpr = bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( i64_offset) ) ;
210+ let overflow_arg_area = bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( 2 * i64_offset) ) ;
211+ let reg_save_area =
212+ bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( 2 * i64_offset + ptr_offset) ) ;
213+
181214 let layout = bx. cx . layout_of ( target_ty) ;
182215
183216 let in_reg = bx. append_sibling_block ( "va_arg.in_reg" ) ;
@@ -192,15 +225,10 @@ fn emit_s390x_va_arg<'ll, 'tcx>(
192225 let padding = padded_size - unpadded_size;
193226
194227 let gpr_type = indirect || !layout. is_single_fp_element ( bx. cx ) ;
195- let ( max_regs, reg_count_field , reg_save_index, reg_padding) =
196- if gpr_type { ( 5 , 0 , 2 , padding) } else { ( 4 , 1 , 16 , 0 ) } ;
228+ let ( max_regs, reg_count , reg_save_index, reg_padding) =
229+ if gpr_type { ( 5 , gpr , 2 , padding) } else { ( 4 , fpr , 16 , 0 ) } ;
197230
198231 // Check whether the value was passed in a register or in memory.
199- let reg_count = bx. struct_gep (
200- va_list_ty,
201- va_list_addr,
202- va_list_layout. llvm_field_index ( bx. cx , reg_count_field) ,
203- ) ;
204232 let reg_count_v = bx. load ( bx. type_i64 ( ) , reg_count, Align :: from_bytes ( 8 ) . unwrap ( ) ) ;
205233 let use_regs = bx. icmp ( IntPredicate :: IntULT , reg_count_v, bx. const_u64 ( max_regs) ) ;
206234 bx. cond_br ( use_regs, in_reg, in_mem) ;
@@ -209,12 +237,10 @@ fn emit_s390x_va_arg<'ll, 'tcx>(
209237 bx. switch_to_block ( in_reg) ;
210238
211239 // Work out the address of the value in the register save area.
212- let reg_ptr =
213- bx. struct_gep ( va_list_ty, va_list_addr, va_list_layout. llvm_field_index ( bx. cx , 3 ) ) ;
214- let reg_ptr_v = bx. load ( bx. type_ptr ( ) , reg_ptr, bx. tcx ( ) . data_layout . pointer_align . abi ) ;
240+ let reg_ptr_v = bx. load ( bx. type_ptr ( ) , reg_save_area, dl. pointer_align . abi ) ;
215241 let scaled_reg_count = bx. mul ( reg_count_v, bx. const_u64 ( 8 ) ) ;
216242 let reg_off = bx. add ( scaled_reg_count, bx. const_u64 ( reg_save_index * 8 + reg_padding) ) ;
217- let reg_addr = bx. gep ( bx . type_i8 ( ) , reg_ptr_v, & [ reg_off] ) ;
243+ let reg_addr = bx. ptradd ( reg_ptr_v, reg_off) ;
218244
219245 // Update the register count.
220246 let new_reg_count_v = bx. add ( reg_count_v, bx. const_u64 ( 1 ) ) ;
@@ -225,27 +251,23 @@ fn emit_s390x_va_arg<'ll, 'tcx>(
225251 bx. switch_to_block ( in_mem) ;
226252
227253 // Work out the address of the value in the argument overflow area.
228- let arg_ptr =
229- bx. struct_gep ( va_list_ty, va_list_addr, va_list_layout. llvm_field_index ( bx. cx , 2 ) ) ;
230- let arg_ptr_v = bx. load ( bx. type_ptr ( ) , arg_ptr, bx. tcx ( ) . data_layout . pointer_align . abi ) ;
254+ let arg_ptr_v =
255+ bx. load ( bx. type_ptr ( ) , overflow_arg_area, bx. tcx ( ) . data_layout . pointer_align . abi ) ;
231256 let arg_off = bx. const_u64 ( padding) ;
232- let mem_addr = bx. gep ( bx . type_i8 ( ) , arg_ptr_v, & [ arg_off] ) ;
257+ let mem_addr = bx. ptradd ( arg_ptr_v, arg_off) ;
233258
234259 // Update the argument overflow area pointer.
235260 let arg_size = bx. cx ( ) . const_u64 ( padded_size) ;
236- let new_arg_ptr_v = bx. inbounds_gep ( bx . type_i8 ( ) , arg_ptr_v, & [ arg_size] ) ;
237- bx. store ( new_arg_ptr_v, arg_ptr , bx . tcx ( ) . data_layout . pointer_align . abi ) ;
261+ let new_arg_ptr_v = bx. inbounds_ptradd ( arg_ptr_v, arg_size) ;
262+ bx. store ( new_arg_ptr_v, overflow_arg_area , dl . pointer_align . abi ) ;
238263 bx. br ( end) ;
239264
240265 // Return the appropriate result.
241266 bx. switch_to_block ( end) ;
242267 let val_addr = bx. phi ( bx. type_ptr ( ) , & [ reg_addr, mem_addr] , & [ in_reg, in_mem] ) ;
243268 let val_type = layout. llvm_type ( bx) ;
244- let val_addr = if indirect {
245- bx. load ( bx. cx . type_ptr ( ) , val_addr, bx. tcx ( ) . data_layout . pointer_align . abi )
246- } else {
247- val_addr
248- } ;
269+ let val_addr =
270+ if indirect { bx. load ( bx. cx . type_ptr ( ) , val_addr, dl. pointer_align . abi ) } else { val_addr } ;
249271 bx. load ( val_type, val_addr, layout. align . abi )
250272}
251273
0 commit comments