@@ -175,6 +175,89 @@ fn emit_aapcs_va_arg<'ll, 'tcx>(
175
175
val
176
176
}
177
177
178
+ fn emit_s390x_va_arg < ' ll , ' tcx > (
179
+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
180
+ list : OperandRef < ' tcx , & ' ll Value > ,
181
+ target_ty : Ty < ' tcx > ,
182
+ ) -> & ' ll Value {
183
+ // Implementation of the s390x ELF ABI calling convention for va_args see
184
+ // https://github.com/IBM/s390x-abi (chapter 1.2.4)
185
+ let va_list_addr = list. immediate ( ) ;
186
+ let va_list_layout = list. deref ( bx. cx ) . layout ;
187
+ let va_list_ty = va_list_layout. llvm_type ( bx) ;
188
+ let layout = bx. cx . layout_of ( target_ty) ;
189
+
190
+ let in_reg = bx. append_sibling_block ( "va_arg.in_reg" ) ;
191
+ let in_mem = bx. append_sibling_block ( "va_arg.in_mem" ) ;
192
+ let end = bx. append_sibling_block ( "va_arg.end" ) ;
193
+
194
+ // FIXME: vector ABI not yet supported.
195
+ let target_ty_size = bx. cx . size_of ( target_ty) . bytes ( ) ;
196
+ let indirect: bool = target_ty_size > 8 || !target_ty_size. is_power_of_two ( ) ;
197
+ let unpadded_size = if indirect { 8 } else { target_ty_size } ;
198
+ let padded_size = 8 ;
199
+ let padding = padded_size - unpadded_size;
200
+
201
+ let gpr_type = indirect || !layout. is_single_fp_element ( bx. cx ) ;
202
+ let ( max_regs, reg_count_field, reg_save_index, reg_padding) =
203
+ if gpr_type { ( 5 , 0 , 2 , padding) } else { ( 4 , 1 , 16 , 0 ) } ;
204
+
205
+ // Check whether the value was passed in a register or in memory.
206
+ let reg_count = bx. struct_gep (
207
+ va_list_ty,
208
+ va_list_addr,
209
+ va_list_layout. llvm_field_index ( bx. cx , reg_count_field) ,
210
+ ) ;
211
+ let reg_count_v = bx. load ( bx. type_i64 ( ) , reg_count, Align :: from_bytes ( 8 ) . unwrap ( ) ) ;
212
+ let use_regs = bx. icmp ( IntPredicate :: IntULT , reg_count_v, bx. const_u64 ( max_regs) ) ;
213
+ bx. cond_br ( use_regs, in_reg, in_mem) ;
214
+
215
+ // Emit code to load the value if it was passed in a register.
216
+ bx. switch_to_block ( in_reg) ;
217
+
218
+ // Work out the address of the value in the register save area.
219
+ let reg_ptr =
220
+ bx. struct_gep ( va_list_ty, va_list_addr, va_list_layout. llvm_field_index ( bx. cx , 3 ) ) ;
221
+ let reg_ptr_v = bx. load ( bx. type_i8p ( ) , reg_ptr, bx. tcx ( ) . data_layout . pointer_align . abi ) ;
222
+ let scaled_reg_count = bx. mul ( reg_count_v, bx. const_u64 ( 8 ) ) ;
223
+ let reg_off = bx. add ( scaled_reg_count, bx. const_u64 ( reg_save_index * 8 + reg_padding) ) ;
224
+ let reg_addr = bx. gep ( bx. type_i8 ( ) , reg_ptr_v, & [ reg_off] ) ;
225
+
226
+ // Update the register count.
227
+ let new_reg_count_v = bx. add ( reg_count_v, bx. const_u64 ( 1 ) ) ;
228
+ bx. store ( new_reg_count_v, reg_count, Align :: from_bytes ( 8 ) . unwrap ( ) ) ;
229
+ bx. br ( end) ;
230
+
231
+ // Emit code to load the value if it was passed in memory.
232
+ bx. switch_to_block ( in_mem) ;
233
+
234
+ // Work out the address of the value in the argument overflow area.
235
+ let arg_ptr =
236
+ bx. struct_gep ( va_list_ty, va_list_addr, va_list_layout. llvm_field_index ( bx. cx , 2 ) ) ;
237
+ let arg_ptr_v = bx. load ( bx. type_i8p ( ) , arg_ptr, bx. tcx ( ) . data_layout . pointer_align . abi ) ;
238
+ let arg_off = bx. const_u64 ( padding) ;
239
+ let mem_addr = bx. gep ( bx. type_i8 ( ) , arg_ptr_v, & [ arg_off] ) ;
240
+
241
+ // Update the argument overflow area pointer.
242
+ let arg_size = bx. cx ( ) . const_u64 ( padded_size) ;
243
+ let new_arg_ptr_v = bx. inbounds_gep ( bx. type_i8 ( ) , arg_ptr_v, & [ arg_size] ) ;
244
+ bx. store ( new_arg_ptr_v, arg_ptr, bx. tcx ( ) . data_layout . pointer_align . abi ) ;
245
+ bx. br ( end) ;
246
+
247
+ // Return the appropriate result.
248
+ bx. switch_to_block ( end) ;
249
+ let val_addr = bx. phi ( bx. type_i8p ( ) , & [ reg_addr, mem_addr] , & [ in_reg, in_mem] ) ;
250
+ let val_type = layout. llvm_type ( bx) ;
251
+ let val_addr = if indirect {
252
+ let ptr_type = bx. cx . type_ptr_to ( val_type) ;
253
+ let ptr_addr = bx. bitcast ( val_addr, bx. cx . type_ptr_to ( ptr_type) ) ;
254
+ bx. load ( ptr_type, ptr_addr, bx. tcx ( ) . data_layout . pointer_align . abi )
255
+ } else {
256
+ bx. bitcast ( val_addr, bx. cx . type_ptr_to ( val_type) )
257
+ } ;
258
+ bx. load ( val_type, val_addr, layout. align . abi )
259
+ }
260
+
178
261
pub ( super ) fn emit_va_arg < ' ll , ' tcx > (
179
262
bx : & mut Builder < ' _ , ' ll , ' tcx > ,
180
263
addr : OperandRef < ' tcx , & ' ll Value > ,
@@ -200,6 +283,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
200
283
emit_ptr_va_arg ( bx, addr, target_ty, false , Align :: from_bytes ( 8 ) . unwrap ( ) , true )
201
284
}
202
285
"aarch64" => emit_aapcs_va_arg ( bx, addr, target_ty) ,
286
+ "s390x" => emit_s390x_va_arg ( bx, addr, target_ty) ,
203
287
// Windows x86_64
204
288
"x86_64" if target. is_like_windows => {
205
289
let target_ty_size = bx. cx . size_of ( target_ty) . bytes ( ) ;
0 commit comments