@@ -175,6 +175,89 @@ fn emit_aapcs_va_arg<'ll, 'tcx>(
175175 val
176176}
177177
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+
178261pub ( super ) fn emit_va_arg < ' ll , ' tcx > (
179262 bx : & mut Builder < ' _ , ' ll , ' tcx > ,
180263 addr : OperandRef < ' tcx , & ' ll Value > ,
@@ -200,6 +283,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
200283 emit_ptr_va_arg ( bx, addr, target_ty, false , Align :: from_bytes ( 8 ) . unwrap ( ) , true )
201284 }
202285 "aarch64" => emit_aapcs_va_arg ( bx, addr, target_ty) ,
286+ "s390x" => emit_s390x_va_arg ( bx, addr, target_ty) ,
203287 // Windows x86_64
204288 "x86_64" if target. is_like_windows => {
205289 let target_ty_size = bx. cx . size_of ( target_ty) . bytes ( ) ;
0 commit comments