@@ -40,6 +40,7 @@ fn emit_direct_ptr_va_arg<'ll, 'tcx>(
40
40
align : Align ,
41
41
slot_size : Align ,
42
42
allow_higher_align : bool ,
43
+ force_right_adjust : bool ,
43
44
) -> ( & ' ll Value , Align ) {
44
45
let va_list_ty = bx. type_ptr ( ) ;
45
46
let va_list_addr = list. immediate ( ) ;
@@ -57,7 +58,10 @@ fn emit_direct_ptr_va_arg<'ll, 'tcx>(
57
58
let next = bx. inbounds_ptradd ( addr, full_direct_size) ;
58
59
bx. store ( next, va_list_addr, bx. tcx ( ) . data_layout . pointer_align . abi ) ;
59
60
60
- if size. bytes ( ) < slot_size. bytes ( ) && bx. tcx ( ) . sess . target . endian == Endian :: Big {
61
+ if size. bytes ( ) < slot_size. bytes ( )
62
+ && bx. tcx ( ) . sess . target . endian == Endian :: Big
63
+ && force_right_adjust
64
+ {
61
65
let adjusted_size = bx. cx ( ) . const_i32 ( ( slot_size. bytes ( ) - size. bytes ( ) ) as i32 ) ;
62
66
let adjusted = bx. inbounds_ptradd ( addr, adjusted_size) ;
63
67
( adjusted, addr_align)
@@ -81,16 +85,23 @@ enum AllowHigherAlign {
81
85
Yes ,
82
86
}
83
87
88
+ enum ForceRightAdjust {
89
+ No ,
90
+ Yes ,
91
+ }
92
+
84
93
fn emit_ptr_va_arg < ' ll , ' tcx > (
85
94
bx : & mut Builder < ' _ , ' ll , ' tcx > ,
86
95
list : OperandRef < ' tcx , & ' ll Value > ,
87
96
target_ty : Ty < ' tcx > ,
88
97
pass_mode : PassMode ,
89
98
slot_size : SlotSize ,
90
99
allow_higher_align : AllowHigherAlign ,
100
+ force_right_adjust : ForceRightAdjust ,
91
101
) -> & ' ll Value {
92
102
let indirect = matches ! ( pass_mode, PassMode :: Indirect ) ;
93
103
let allow_higher_align = matches ! ( allow_higher_align, AllowHigherAlign :: Yes ) ;
104
+ let force_right_adjust = matches ! ( force_right_adjust, ForceRightAdjust :: Yes ) ;
94
105
let slot_size = Align :: from_bytes ( slot_size as u64 ) . unwrap ( ) ;
95
106
96
107
let layout = bx. cx . layout_of ( target_ty) ;
@@ -103,8 +114,15 @@ fn emit_ptr_va_arg<'ll, 'tcx>(
103
114
} else {
104
115
( layout. llvm_type ( bx. cx ) , layout. size , layout. align )
105
116
} ;
106
- let ( addr, addr_align) =
107
- emit_direct_ptr_va_arg ( bx, list, size, align. abi , slot_size, allow_higher_align) ;
117
+ let ( addr, addr_align) = emit_direct_ptr_va_arg (
118
+ bx,
119
+ list,
120
+ size,
121
+ align. abi ,
122
+ slot_size,
123
+ allow_higher_align,
124
+ force_right_adjust,
125
+ ) ;
108
126
if indirect {
109
127
let tmp_ret = bx. load ( llty, addr, addr_align) ;
110
128
bx. load ( bx. cx . layout_of ( target_ty) . llvm_type ( bx. cx ) , tmp_ret, align. abi )
@@ -208,6 +226,7 @@ fn emit_aapcs_va_arg<'ll, 'tcx>(
208
226
PassMode :: Direct ,
209
227
SlotSize :: Bytes8 ,
210
228
AllowHigherAlign :: Yes ,
229
+ ForceRightAdjust :: No ,
211
230
) ;
212
231
bx. br ( end) ;
213
232
@@ -218,6 +237,150 @@ fn emit_aapcs_va_arg<'ll, 'tcx>(
218
237
val
219
238
}
220
239
240
+ fn emit_powerpc_va_arg < ' ll , ' tcx > (
241
+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
242
+ list : OperandRef < ' tcx , & ' ll Value > ,
243
+ target_ty : Ty < ' tcx > ,
244
+ ) -> & ' ll Value {
245
+ let dl = bx. cx . data_layout ( ) ;
246
+
247
+ // struct __va_list_tag {
248
+ // unsigned char gpr;
249
+ // unsigned char fpr;
250
+ // unsigned short reserved;
251
+ // void *overflow_arg_area;
252
+ // void *reg_save_area;
253
+ // };
254
+ let va_list_addr = list. immediate ( ) ;
255
+
256
+ // Peel off any newtype wrappers.
257
+ let layout = {
258
+ let mut layout = bx. cx . layout_of ( target_ty) ;
259
+
260
+ while let Some ( ( _, inner) ) = layout. non_1zst_field ( bx. cx ) {
261
+ layout = inner;
262
+ }
263
+
264
+ layout
265
+ } ;
266
+
267
+ // Rust does not currently support any powerpc softfloat targets.
268
+ let target = & bx. cx . tcx . sess . target ;
269
+ let is_soft_float_abi = target. abi == "softfloat" ;
270
+ assert ! ( !is_soft_float_abi) ;
271
+
272
+ // All instances of VaArgSafe are passed directly.
273
+ let is_indirect = false ;
274
+
275
+ let ( is_i64, is_int, is_f64) = match layout. layout . backend_repr ( ) {
276
+ BackendRepr :: Scalar ( scalar) => match scalar. primitive ( ) {
277
+ rustc_abi:: Primitive :: Int ( integer, _) => ( integer. size ( ) . bits ( ) == 64 , true , false ) ,
278
+ rustc_abi:: Primitive :: Float ( float) => ( false , false , float. size ( ) . bits ( ) == 64 ) ,
279
+ rustc_abi:: Primitive :: Pointer ( _) => ( false , true , false ) ,
280
+ } ,
281
+ _ => unreachable ! ( "all instances of VaArgSafe are represented as scalars" ) ,
282
+ } ;
283
+
284
+ let num_regs_addr = if is_int || is_soft_float_abi {
285
+ va_list_addr // gpr
286
+ } else {
287
+ bx. inbounds_ptradd ( va_list_addr, bx. const_usize ( 1 ) ) // fpr
288
+ } ;
289
+
290
+ let mut num_regs = bx. load ( bx. type_i8 ( ) , num_regs_addr, dl. i8_align . abi ) ;
291
+
292
+ // "Align" the register count when the type is passed as `i64`.
293
+ if is_i64 || ( is_f64 && is_soft_float_abi) {
294
+ num_regs = bx. add ( num_regs, bx. const_u8 ( 1 ) ) ;
295
+ num_regs = bx. and ( num_regs, bx. const_u8 ( 0b1111_1110 ) ) ;
296
+ }
297
+
298
+ let max_regs = 8u8 ;
299
+ let use_regs = bx. icmp ( IntPredicate :: IntULT , num_regs, bx. const_u8 ( max_regs) ) ;
300
+
301
+ let in_reg = bx. append_sibling_block ( "va_arg.in_reg" ) ;
302
+ let in_mem = bx. append_sibling_block ( "va_arg.in_mem" ) ;
303
+ let end = bx. append_sibling_block ( "va_arg.end" ) ;
304
+
305
+ bx. cond_br ( use_regs, in_reg, in_mem) ;
306
+
307
+ let reg_addr = {
308
+ bx. switch_to_block ( in_reg) ;
309
+
310
+ let reg_safe_area_ptr = bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( 1 + 1 + 2 + 4 ) ) ;
311
+ let mut reg_addr = bx. load ( bx. type_ptr ( ) , reg_safe_area_ptr, dl. pointer_align . abi ) ;
312
+
313
+ // Floating-point registers start after the general-purpose registers.
314
+ if !is_int && !is_soft_float_abi {
315
+ reg_addr = bx. inbounds_ptradd ( reg_addr, bx. cx . const_usize ( 32 ) )
316
+ }
317
+
318
+ // Get the address of the saved value by scaling the number of
319
+ // registers we've used by the number of.
320
+ let reg_size = if is_int || is_soft_float_abi { 4 } else { 8 } ;
321
+ let reg_offset = bx. mul ( num_regs, bx. cx ( ) . const_u8 ( reg_size) ) ;
322
+ let reg_addr = bx. inbounds_ptradd ( reg_addr, reg_offset) ;
323
+
324
+ // Increase the used-register count.
325
+ let reg_incr = if is_i64 || ( is_f64 && is_soft_float_abi) { 2 } else { 1 } ;
326
+ let new_num_regs = bx. add ( num_regs, bx. cx . const_u8 ( reg_incr) ) ;
327
+ bx. store ( new_num_regs, num_regs_addr, dl. i8_align . abi ) ;
328
+
329
+ bx. br ( end) ;
330
+
331
+ reg_addr
332
+ } ;
333
+
334
+ let mem_addr = {
335
+ bx. switch_to_block ( in_mem) ;
336
+
337
+ bx. store ( bx. const_u8 ( max_regs) , num_regs_addr, dl. i8_align . abi ) ;
338
+
339
+ // Everything in the overflow area is rounded up to a size of at least 4.
340
+ let overflow_area_align = Align :: from_bytes ( 4 ) . unwrap ( ) ;
341
+
342
+ let size = if !is_indirect {
343
+ layout. layout . size . align_to ( overflow_area_align)
344
+ } else {
345
+ dl. pointer_size
346
+ } ;
347
+
348
+ let overflow_area_ptr = bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( 1 + 1 + 2 ) ) ;
349
+ let mut overflow_area = bx. load ( bx. type_ptr ( ) , overflow_area_ptr, dl. pointer_align . abi ) ;
350
+
351
+ // Round up address of argument to alignment
352
+ if layout. layout . align . abi > overflow_area_align {
353
+ overflow_area = round_pointer_up_to_alignment (
354
+ bx,
355
+ overflow_area,
356
+ layout. layout . align . abi ,
357
+ bx. type_ptr ( ) ,
358
+ ) ;
359
+ }
360
+
361
+ let mem_addr = overflow_area;
362
+
363
+ // Increase the overflow area.
364
+ overflow_area = bx. inbounds_ptradd ( overflow_area, bx. const_usize ( size. bytes ( ) ) ) ;
365
+ bx. store ( overflow_area, overflow_area_ptr, dl. pointer_align . abi ) ;
366
+
367
+ bx. br ( end) ;
368
+
369
+ mem_addr
370
+ } ;
371
+
372
+ // Return the appropriate result.
373
+ bx. switch_to_block ( end) ;
374
+ let val_addr = bx. phi ( bx. type_ptr ( ) , & [ reg_addr, mem_addr] , & [ in_reg, in_mem] ) ;
375
+ let val_type = layout. llvm_type ( bx) ;
376
+ let val_addr = if is_indirect {
377
+ bx. load ( bx. cx . type_ptr ( ) , val_addr, dl. pointer_align . abi )
378
+ } else {
379
+ val_addr
380
+ } ;
381
+ bx. load ( val_type, val_addr, layout. align . abi )
382
+ }
383
+
221
384
fn emit_s390x_va_arg < ' ll , ' tcx > (
222
385
bx : & mut Builder < ' _ , ' ll , ' tcx > ,
223
386
list : OperandRef < ' tcx , & ' ll Value > ,
@@ -728,6 +891,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
728
891
PassMode :: Direct ,
729
892
SlotSize :: Bytes4 ,
730
893
if target. is_like_windows { AllowHigherAlign :: No } else { AllowHigherAlign :: Yes } ,
894
+ ForceRightAdjust :: No ,
731
895
) ,
732
896
"aarch64" | "arm64ec" if target. is_like_windows || target. is_like_darwin => {
733
897
emit_ptr_va_arg (
@@ -737,10 +901,24 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
737
901
PassMode :: Direct ,
738
902
SlotSize :: Bytes8 ,
739
903
if target. is_like_windows { AllowHigherAlign :: No } else { AllowHigherAlign :: Yes } ,
904
+ ForceRightAdjust :: No ,
740
905
)
741
906
}
742
907
"aarch64" => emit_aapcs_va_arg ( bx, addr, target_ty) ,
743
908
"s390x" => emit_s390x_va_arg ( bx, addr, target_ty) ,
909
+ "powerpc" => emit_powerpc_va_arg ( bx, addr, target_ty) ,
910
+ "powerpc64" | "powerpc64le" => emit_ptr_va_arg (
911
+ bx,
912
+ addr,
913
+ target_ty,
914
+ PassMode :: Direct ,
915
+ SlotSize :: Bytes8 ,
916
+ AllowHigherAlign :: Yes ,
917
+ match & * target. arch {
918
+ "powerpc64" => ForceRightAdjust :: Yes ,
919
+ _ => ForceRightAdjust :: No ,
920
+ } ,
921
+ ) ,
744
922
// Windows x86_64
745
923
"x86_64" if target. is_like_windows => {
746
924
let target_ty_size = bx. cx . size_of ( target_ty) . bytes ( ) ;
@@ -755,6 +933,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
755
933
} ,
756
934
SlotSize :: Bytes8 ,
757
935
AllowHigherAlign :: No ,
936
+ ForceRightAdjust :: No ,
758
937
)
759
938
}
760
939
// This includes `target.is_like_darwin`, which on x86_64 targets is like sysv64.
0 commit comments