1
- use rustc_abi:: { Align , BackendRepr , Endian , HasDataLayout , Primitive , Size } ;
1
+ use rustc_abi:: { Align , BackendRepr , Endian , HasDataLayout , Primitive , Size , TyAndLayout } ;
2
+ use rustc_codegen_ssa:: MemFlags ;
2
3
use rustc_codegen_ssa:: common:: IntPredicate ;
3
4
use rustc_codegen_ssa:: mir:: operand:: OperandRef ;
4
- use rustc_codegen_ssa:: traits:: { BaseTypeCodegenMethods , BuilderMethods , ConstCodegenMethods } ;
5
+ use rustc_codegen_ssa:: traits:: {
6
+ BaseTypeCodegenMethods , BuilderMethods , ConstCodegenMethods , LayoutTypeCodegenMethods ,
7
+ } ;
5
8
use rustc_middle:: ty:: Ty ;
6
9
use rustc_middle:: ty:: layout:: { HasTyCtxt , LayoutOf } ;
7
10
@@ -300,12 +303,16 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
300
303
// } va_list[1];
301
304
let va_list_addr = list. immediate ( ) ;
302
305
303
- let unsigned_int_offset = 4 ;
304
- let ptr_offset = 8 ;
305
- let gp_offset_ptr = va_list_addr;
306
- let fp_offset_ptr = bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( unsigned_int_offset) ) ;
306
+ // Peel off any newtype wrappers.
307
+ let layout = {
308
+ let mut layout = bx. cx . layout_of ( target_ty) ;
307
309
308
- let layout = bx. cx . layout_of ( target_ty) ;
310
+ while let Some ( ( _, inner) ) = layout. non_1zst_field ( bx. cx ) {
311
+ layout = inner;
312
+ }
313
+
314
+ layout
315
+ } ;
309
316
310
317
// AMD64-ABI 3.5.7p5: Step 1. Determine whether type may be passed
311
318
// in the registers. If not go to step 7.
@@ -317,37 +324,48 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
317
324
let mut num_gp_registers = 0 ;
318
325
let mut num_fp_registers = 0 ;
319
326
327
+ let mut registers_for_primitive = |p| match p {
328
+ Primitive :: Int ( integer, _is_signed) => {
329
+ num_gp_registers += integer. size ( ) . bytes ( ) . div_ceil ( 8 ) as u32 ;
330
+ }
331
+ Primitive :: Float ( float) => {
332
+ num_fp_registers += float. size ( ) . bytes ( ) . div_ceil ( 16 ) as u32 ;
333
+ }
334
+ Primitive :: Pointer ( _) => {
335
+ num_gp_registers += 1 ;
336
+ }
337
+ } ;
338
+
320
339
match layout. layout . backend_repr ( ) {
321
- BackendRepr :: Scalar ( scalar) => match scalar. primitive ( ) {
322
- Primitive :: Int ( integer, _is_signed) => {
323
- num_gp_registers += integer. size ( ) . bytes ( ) . div_ceil ( 8 ) as u32 ;
324
- }
325
- Primitive :: Float ( float) => {
326
- num_fp_registers += float. size ( ) . bytes ( ) . div_ceil ( 16 ) as u32 ;
327
- }
328
- Primitive :: Pointer ( _) => {
329
- num_gp_registers += 1 ;
330
- }
331
- } ,
332
- BackendRepr :: ScalarPair ( ..)
333
- | BackendRepr :: SimdVector { .. }
334
- | BackendRepr :: Memory { .. } => {
340
+ BackendRepr :: Scalar ( scalar) => {
341
+ registers_for_primitive ( scalar. primitive ( ) ) ;
342
+ }
343
+ BackendRepr :: ScalarPair ( scalar1, scalar2) => {
344
+ registers_for_primitive ( scalar1. primitive ( ) ) ;
345
+ registers_for_primitive ( scalar2. primitive ( ) ) ;
346
+ }
347
+ BackendRepr :: SimdVector { .. } => {
335
348
// Because no instance of VaArgSafe uses a non-scalar `BackendRepr`.
336
349
unreachable ! (
337
350
"No x86-64 SysV va_arg implementation for {:?}" ,
338
351
layout. layout. backend_repr( )
339
352
)
340
353
}
354
+ BackendRepr :: Memory { .. } => {
355
+ let mem_addr = x86_64_sysv64_va_arg_from_memory ( bx, va_list_addr, layout) ;
356
+ return bx. load ( layout. llvm_type ( bx) , mem_addr, layout. align . abi ) ;
357
+ }
341
358
} ;
342
359
343
- if num_gp_registers == 0 && num_fp_registers == 0 {
344
- unreachable ! ( "VaArgSafe is not implemented for ZSTs" )
345
- }
346
-
347
360
// AMD64-ABI 3.5.7p5: Step 3. Verify whether arguments fit into
348
361
// registers. In the case: l->gp_offset > 48 - num_gp * 8 or
349
362
// l->fp_offset > 176 - num_fp * 16 go to step 7.
350
363
364
+ let unsigned_int_offset = 4 ;
365
+ let ptr_offset = 8 ;
366
+ let gp_offset_ptr = va_list_addr;
367
+ let fp_offset_ptr = bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( unsigned_int_offset) ) ;
368
+
351
369
let gp_offset_v = bx. load ( bx. type_i32 ( ) , gp_offset_ptr, Align :: from_bytes ( 8 ) . unwrap ( ) ) ;
352
370
let fp_offset_v = bx. load ( bx. type_i32 ( ) , fp_offset_ptr, Align :: from_bytes ( 4 ) . unwrap ( ) ) ;
353
371
@@ -388,14 +406,87 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
388
406
bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( 2 * unsigned_int_offset + ptr_offset) ) ;
389
407
let reg_save_area_v = bx. load ( bx. type_ptr ( ) , reg_save_area_ptr, dl. pointer_align . abi ) ;
390
408
391
- let reg_addr = if num_gp_registers > 0 && num_fp_registers > 0 {
392
- unreachable ! ( "instances of VaArgSafe cannot use both int and sse registers" ) ;
393
- } else if num_gp_registers > 0 || num_fp_registers == 1 {
394
- let gp_or_fp_offset = if num_gp_registers > 0 { gp_offset_v } else { fp_offset_v } ;
395
- bx. gep ( bx. type_i8 ( ) , reg_save_area_v, & [ gp_or_fp_offset] )
396
- } else {
397
- // assert_eq!(num_sse_registers, 2);
398
- unreachable ! ( "all instances of VaArgSafe have an alignment <= 8" ) ;
409
+ let reg_addr = match layout. layout . backend_repr ( ) {
410
+ BackendRepr :: Scalar ( scalar) => match scalar. primitive ( ) {
411
+ Primitive :: Int ( _, _) | Primitive :: Pointer ( _) => {
412
+ let reg_addr = bx. gep ( bx. type_i8 ( ) , reg_save_area_v, & [ gp_offset_v] ) ;
413
+
414
+ // Copy into a temporary if the type is more aligned than the register save area.
415
+ copy_to_temporary_if_more_aligned ( bx, reg_addr, layout)
416
+ }
417
+ Primitive :: Float ( _) => bx. gep ( bx. type_i8 ( ) , reg_save_area_v, & [ fp_offset_v] ) ,
418
+ } ,
419
+ BackendRepr :: ScalarPair ( scalar1, scalar2) => {
420
+ let ty_lo = bx. cx ( ) . scalar_pair_element_backend_type ( layout, 0 , false ) ;
421
+ let ty_hi = bx. cx ( ) . scalar_pair_element_backend_type ( layout, 1 , false ) ;
422
+
423
+ let align_lo = layout. field ( bx. cx , 0 ) . layout . align ( ) . abi ;
424
+ let align_hi = layout. field ( bx. cx , 1 ) . layout . align ( ) . abi ;
425
+
426
+ match ( scalar1. primitive ( ) , scalar2. primitive ( ) ) {
427
+ ( Primitive :: Float ( _) , Primitive :: Float ( _) ) => {
428
+ // SSE registers are spaced 16 bytes apart in the register save
429
+ // area, we need to collect the two eightbytes together.
430
+ // The ABI isn't explicit about this, but it seems reasonable
431
+ // to assume that the slots are 16-byte aligned, since the stack is
432
+ // naturally 16-byte aligned and the prologue is expected to store
433
+ // all the SSE registers to the RSA.
434
+ let reg_lo_addr = bx. gep ( bx. type_i8 ( ) , reg_save_area_v, & [ fp_offset_v] ) ;
435
+ let reg_hi_addr = bx. gep ( bx. type_i8 ( ) , reg_lo_addr, & [ bx. const_i32 ( 16 ) ] ) ;
436
+
437
+ let align = layout. layout . align ( ) . abi ;
438
+ let tmp = bx. alloca ( layout. layout . size ( ) , align) ;
439
+
440
+ let reg_lo = bx. load ( ty_lo, reg_lo_addr, align_lo) ;
441
+ let reg_hi = bx. load ( ty_hi, reg_hi_addr, align_hi) ;
442
+
443
+ let offset = scalar1. size ( bx. cx ) . align_to ( align_hi) . bytes ( ) ;
444
+ let field0 = tmp;
445
+ let field1 = bx. gep ( bx. type_i8 ( ) , tmp, & [ bx. const_u32 ( offset as u32 ) ] ) ;
446
+
447
+ bx. store ( reg_lo, field0, align) ;
448
+ bx. store ( reg_hi, field1, align) ;
449
+
450
+ tmp
451
+ }
452
+ ( Primitive :: Float ( _) , _) | ( _, Primitive :: Float ( _) ) => {
453
+ let gp_addr = bx. gep ( bx. type_i8 ( ) , reg_save_area_v, & [ gp_offset_v] ) ;
454
+ let fp_addr = bx. gep ( bx. type_i8 ( ) , reg_save_area_v, & [ fp_offset_v] ) ;
455
+
456
+ let ( reg_lo_addr, reg_hi_addr) = match scalar1. primitive ( ) {
457
+ Primitive :: Float ( _) => ( fp_addr, gp_addr) ,
458
+ Primitive :: Int ( _, _) | Primitive :: Pointer ( _) => ( gp_addr, fp_addr) ,
459
+ } ;
460
+
461
+ let tmp = bx. alloca ( layout. layout . size ( ) , layout. layout . align ( ) . abi ) ;
462
+
463
+ let reg_lo = bx. load ( ty_lo, reg_lo_addr, align_lo) ;
464
+ let reg_hi = bx. load ( ty_hi, reg_hi_addr, align_hi) ;
465
+
466
+ let offset = scalar1. size ( bx. cx ) . align_to ( align_hi) . bytes ( ) ;
467
+ let field0 = tmp;
468
+ let field1 = bx. gep ( bx. type_i8 ( ) , tmp, & [ bx. const_u32 ( offset as u32 ) ] ) ;
469
+
470
+ bx. store ( reg_lo, field0, align_lo) ;
471
+ bx. store ( reg_hi, field1, align_hi) ;
472
+
473
+ tmp
474
+ }
475
+ ( _, _) => {
476
+ // Two integer/pointer values are just contiguous in memory.
477
+ let reg_addr = bx. gep ( bx. type_i8 ( ) , reg_save_area_v, & [ gp_offset_v] ) ;
478
+
479
+ // Copy into a temporary if the type is more aligned than the register save area.
480
+ copy_to_temporary_if_more_aligned ( bx, reg_addr, layout)
481
+ }
482
+ }
483
+ }
484
+ BackendRepr :: SimdVector { .. } => {
485
+ unreachable ! ( "panics in the previous match on `backend_repr`" )
486
+ }
487
+ BackendRepr :: Memory { .. } => {
488
+ unreachable ! ( "early returns in the previous match on `backend_repr`" )
489
+ }
399
490
} ;
400
491
401
492
// AMD64-ABI 3.5.7p5: Step 5. Set:
@@ -416,9 +507,47 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
416
507
bx. br ( end) ;
417
508
418
509
bx. switch_to_block ( in_mem) ;
510
+ let mem_addr = x86_64_sysv64_va_arg_from_memory ( bx, va_list_addr, layout) ;
511
+ bx. br ( end) ;
419
512
420
- let overflow_arg_area_ptr =
421
- bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( 2 * unsigned_int_offset) ) ;
513
+ bx. switch_to_block ( end) ;
514
+
515
+ let val_type = layout. llvm_type ( bx) ;
516
+ let val_addr = bx. phi ( bx. type_ptr ( ) , & [ reg_addr, mem_addr] , & [ in_reg, in_mem] ) ;
517
+
518
+ bx. load ( val_type, val_addr, layout. align . abi )
519
+ }
520
+
521
+ /// Copy into a temporary if the type is more aligned than the register save area.
522
+ fn copy_to_temporary_if_more_aligned < ' ll , ' tcx > (
523
+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
524
+ reg_addr : & ' ll Value ,
525
+ layout : TyAndLayout < ' tcx , Ty < ' tcx > > ,
526
+ ) -> & ' ll Value {
527
+ if layout. layout . align . abi . bytes ( ) > 8 {
528
+ let tmp = bx. alloca ( layout. layout . size ( ) , layout. layout . align ( ) . abi ) ;
529
+ bx. memcpy (
530
+ tmp,
531
+ layout. layout . align . abi ,
532
+ reg_addr,
533
+ Align :: from_bytes ( 8 ) . unwrap ( ) ,
534
+ bx. const_u32 ( layout. layout . size ( ) . bytes ( ) as u32 ) ,
535
+ MemFlags :: empty ( ) ,
536
+ ) ;
537
+ tmp
538
+ } else {
539
+ reg_addr
540
+ }
541
+ }
542
+
543
+ fn x86_64_sysv64_va_arg_from_memory < ' ll , ' tcx > (
544
+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
545
+ va_list_addr : & ' ll Value ,
546
+ layout : TyAndLayout < ' tcx , Ty < ' tcx > > ,
547
+ ) -> & ' ll Value {
548
+ let dl = bx. cx . data_layout ( ) ;
549
+
550
+ let overflow_arg_area_ptr = bx. inbounds_ptradd ( va_list_addr, bx. const_usize ( 8 ) ) ;
422
551
423
552
let overflow_arg_area_v = bx. load ( bx. type_ptr ( ) , overflow_arg_area_ptr, dl. pointer_align . abi ) ;
424
553
// AMD64-ABI 3.5.7p5: Step 7. Align l->overflow_arg_area upwards to a 16
@@ -441,14 +570,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
441
570
let overflow_arg_area = bx. gep ( bx. type_i8 ( ) , overflow_arg_area_v, & [ offset] ) ;
442
571
bx. store ( overflow_arg_area, overflow_arg_area_ptr, dl. pointer_align . abi ) ;
443
572
444
- bx. br ( end) ;
445
-
446
- bx. switch_to_block ( end) ;
447
-
448
- let val_type = layout. llvm_type ( bx) ;
449
- let val_addr = bx. phi ( bx. type_ptr ( ) , & [ reg_addr, mem_addr] , & [ in_reg, in_mem] ) ;
450
-
451
- bx. load ( val_type, val_addr, layout. align . abi )
573
+ mem_addr
452
574
}
453
575
454
576
fn emit_xtensa_va_arg < ' ll , ' tcx > (
0 commit comments