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,11 +303,6 @@ 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) ) ;
307
-
308
306
let layout = bx. cx . layout_of ( target_ty) ;
309
307
310
308
// AMD64-ABI 3.5.7p5: Step 1. Determine whether type may be passed
@@ -317,37 +315,48 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
317
315
let mut num_gp_registers = 0 ;
318
316
let mut num_fp_registers = 0 ;
319
317
318
+ let mut registers_for_primitive = |p| match p {
319
+ Primitive :: Int ( integer, _is_signed) => {
320
+ num_gp_registers += integer. size ( ) . bytes ( ) . div_ceil ( 8 ) as u32 ;
321
+ }
322
+ Primitive :: Float ( float) => {
323
+ num_fp_registers += float. size ( ) . bytes ( ) . div_ceil ( 16 ) as u32 ;
324
+ }
325
+ Primitive :: Pointer ( _) => {
326
+ num_gp_registers += 1 ;
327
+ }
328
+ } ;
329
+
320
330
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 { .. } => {
331
+ BackendRepr :: Scalar ( scalar) => {
332
+ registers_for_primitive ( scalar. primitive ( ) ) ;
333
+ }
334
+ BackendRepr :: ScalarPair ( scalar1, scalar2) => {
335
+ registers_for_primitive ( scalar1. primitive ( ) ) ;
336
+ registers_for_primitive ( scalar2. primitive ( ) ) ;
337
+ }
338
+ BackendRepr :: SimdVector { .. } => {
335
339
// Because no instance of VaArgSafe uses a non-scalar `BackendRepr`.
336
340
unreachable ! (
337
341
"No x86-64 SysV va_arg implementation for {:?}" ,
338
342
layout. layout. backend_repr( )
339
343
)
340
344
}
345
+ BackendRepr :: Memory { .. } => {
346
+ let mem_addr = x86_64_sysv64_va_arg_from_memory ( bx, va_list_addr, layout) ;
347
+ return bx. load ( layout. llvm_type ( bx) , mem_addr, layout. align . abi ) ;
348
+ }
341
349
} ;
342
350
343
- if num_gp_registers == 0 && num_fp_registers == 0 {
344
- unreachable ! ( "VaArgSafe is not implemented for ZSTs" )
345
- }
346
-
347
351
// AMD64-ABI 3.5.7p5: Step 3. Verify whether arguments fit into
348
352
// registers. In the case: l->gp_offset > 48 - num_gp * 8 or
349
353
// l->fp_offset > 176 - num_fp * 16 go to step 7.
350
354
355
+ let unsigned_int_offset = 4 ;
356
+ let ptr_offset = 8 ;
357
+ let gp_offset_ptr = va_list_addr;
358
+ let fp_offset_ptr = bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( unsigned_int_offset) ) ;
359
+
351
360
let gp_offset_v = bx. load ( bx. type_i32 ( ) , gp_offset_ptr, Align :: from_bytes ( 8 ) . unwrap ( ) ) ;
352
361
let fp_offset_v = bx. load ( bx. type_i32 ( ) , fp_offset_ptr, Align :: from_bytes ( 4 ) . unwrap ( ) ) ;
353
362
@@ -388,14 +397,85 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
388
397
bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( 2 * unsigned_int_offset + ptr_offset) ) ;
389
398
let reg_save_area_v = bx. load ( bx. type_ptr ( ) , reg_save_area_ptr, dl. pointer_align . abi ) ;
390
399
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" ) ;
400
+ let reg_addr = match layout. layout . backend_repr ( ) {
401
+ BackendRepr :: Scalar ( scalar) => match scalar. primitive ( ) {
402
+ Primitive :: Int ( _, _) | Primitive :: Pointer ( _) => {
403
+ let reg_addr = bx. gep ( bx. type_i8 ( ) , reg_save_area_v, & [ gp_offset_v] ) ;
404
+
405
+ // Copy into a temporary if the type is more aligned than the register save area.
406
+ copy_to_temporary_if_more_aligned ( bx, reg_addr, layout)
407
+ }
408
+ Primitive :: Float ( _) => bx. gep ( bx. type_i8 ( ) , reg_save_area_v, & [ fp_offset_v] ) ,
409
+ } ,
410
+ BackendRepr :: ScalarPair ( scalar1, scalar2) => {
411
+ let ty_lo = bx. cx ( ) . scalar_pair_element_backend_type ( layout, 0 , false ) ;
412
+ let ty_hi = bx. cx ( ) . scalar_pair_element_backend_type ( layout, 1 , false ) ;
413
+
414
+ let align_lo = layout. field ( bx. cx , 0 ) . layout . align ( ) . abi ;
415
+ let align_hi = layout. field ( bx. cx , 1 ) . layout . align ( ) . abi ;
416
+
417
+ match ( scalar1. primitive ( ) , scalar2. primitive ( ) ) {
418
+ ( Primitive :: Float ( _) , Primitive :: Float ( _) ) => {
419
+ // SSE registers are spaced 16 bytes apart in the register save
420
+ // area, we need to collect the two eightbytes together.
421
+ // The ABI isn't explicit about this, but it seems reasonable
422
+ // to assume that the slots are 16-byte aligned, since the stack is
423
+ // naturally 16-byte aligned and the prologue is expected to store
424
+ // all the SSE registers to the RSA.
425
+ let reg_lo_addr = bx. gep ( bx. type_i8 ( ) , reg_save_area_v, & [ fp_offset_v] ) ;
426
+ let reg_hi_addr = bx. gep ( bx. type_i8 ( ) , reg_lo_addr, & [ bx. const_i32 ( 16 ) ] ) ;
427
+
428
+ let tmp = bx. alloca ( layout. layout . size ( ) , layout. layout . align ( ) . abi ) ;
429
+ let align = layout. layout . align ( ) . abi ;
430
+
431
+ let reg_lo = bx. load ( ty_lo, reg_lo_addr, align_lo) ;
432
+ let reg_hi = bx. load ( ty_hi, reg_hi_addr, align_hi) ;
433
+
434
+ let field0 = tmp;
435
+ let field1 = bx. gep ( bx. type_i8 ( ) , tmp, & [ bx. const_i32 ( 8 ) ] ) ;
436
+
437
+ bx. store ( reg_lo, field0, align) ;
438
+ bx. store ( reg_hi, field1, align) ;
439
+
440
+ tmp
441
+ }
442
+ ( Primitive :: Float ( _) , _) | ( _, Primitive :: Float ( _) ) => {
443
+ let gp_addr = bx. gep ( bx. type_i8 ( ) , reg_save_area_v, & [ gp_offset_v] ) ;
444
+ let fp_addr = bx. gep ( bx. type_i8 ( ) , reg_save_area_v, & [ fp_offset_v] ) ;
445
+
446
+ let ( reg_lo_addr, reg_hi_addr) = match scalar1. primitive ( ) {
447
+ Primitive :: Float ( _) => ( fp_addr, gp_addr) ,
448
+ Primitive :: Int ( _, _) | Primitive :: Pointer ( _) => ( gp_addr, fp_addr) ,
449
+ } ;
450
+
451
+ let tmp = bx. alloca ( layout. layout . size ( ) , layout. layout . align ( ) . abi ) ;
452
+
453
+ let reg_lo = bx. load ( ty_lo, reg_lo_addr, align_lo) ;
454
+ let reg_hi = bx. load ( ty_hi, reg_hi_addr, align_hi) ;
455
+
456
+ let field0 = tmp;
457
+ let field1 = bx. gep ( bx. type_i8 ( ) , tmp, & [ bx. const_i32 ( 8 ) ] ) ;
458
+
459
+ bx. store ( reg_lo, field0, layout. layout . align ( ) . abi ) ;
460
+ bx. store ( reg_hi, field1, layout. layout . align ( ) . abi ) ;
461
+
462
+ tmp
463
+ }
464
+ ( _, _) => {
465
+ // Two integer/pointer values are just contiguous in memory.
466
+ let reg_addr = bx. gep ( bx. type_i8 ( ) , reg_save_area_v, & [ gp_offset_v] ) ;
467
+
468
+ // Copy into a temporary if the type is more aligned than the register save area.
469
+ copy_to_temporary_if_more_aligned ( bx, reg_addr, layout)
470
+ }
471
+ }
472
+ }
473
+ BackendRepr :: SimdVector { .. } => {
474
+ unreachable ! ( "panics in the previous match on `backend_repr`" )
475
+ }
476
+ BackendRepr :: Memory { .. } => {
477
+ unreachable ! ( "early returns in the previous match on `backend_repr`" )
478
+ }
399
479
} ;
400
480
401
481
// AMD64-ABI 3.5.7p5: Step 5. Set:
@@ -416,9 +496,47 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
416
496
bx. br ( end) ;
417
497
418
498
bx. switch_to_block ( in_mem) ;
499
+ let mem_addr = x86_64_sysv64_va_arg_from_memory ( bx, va_list_addr, layout) ;
500
+ bx. br ( end) ;
501
+
502
+ bx. switch_to_block ( end) ;
503
+
504
+ let val_type = layout. llvm_type ( bx) ;
505
+ let val_addr = bx. phi ( bx. type_ptr ( ) , & [ reg_addr, mem_addr] , & [ in_reg, in_mem] ) ;
419
506
420
- let overflow_arg_area_ptr =
421
- bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( 2 * unsigned_int_offset) ) ;
507
+ bx. load ( val_type, val_addr, layout. align . abi )
508
+ }
509
+
510
+ /// Copy into a temporary if the type is more aligned than the register save area.
511
+ fn copy_to_temporary_if_more_aligned < ' ll , ' tcx > (
512
+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
513
+ reg_addr : & ' ll Value ,
514
+ layout : TyAndLayout < ' tcx , Ty < ' tcx > > ,
515
+ ) -> & ' ll Value {
516
+ if layout. layout . align . abi . bytes ( ) > 8 {
517
+ let tmp = bx. alloca ( layout. layout . size ( ) , layout. layout . align ( ) . abi ) ;
518
+ bx. memcpy (
519
+ tmp,
520
+ layout. layout . align . abi ,
521
+ reg_addr,
522
+ Align :: from_bytes ( 8 ) . unwrap ( ) ,
523
+ bx. const_u32 ( layout. layout . size ( ) . bytes ( ) as u32 ) ,
524
+ MemFlags :: empty ( ) ,
525
+ ) ;
526
+ tmp
527
+ } else {
528
+ reg_addr
529
+ }
530
+ }
531
+
532
+ fn x86_64_sysv64_va_arg_from_memory < ' ll , ' tcx > (
533
+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
534
+ va_list_addr : & ' ll Value ,
535
+ layout : TyAndLayout < ' tcx , Ty < ' tcx > > ,
536
+ ) -> & ' ll Value {
537
+ let dl = bx. cx . data_layout ( ) ;
538
+
539
+ let overflow_arg_area_ptr = bx. inbounds_ptradd ( va_list_addr, bx. const_usize ( 8 ) ) ;
422
540
423
541
let overflow_arg_area_v = bx. load ( bx. type_ptr ( ) , overflow_arg_area_ptr, dl. pointer_align . abi ) ;
424
542
// AMD64-ABI 3.5.7p5: Step 7. Align l->overflow_arg_area upwards to a 16
@@ -441,14 +559,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
441
559
let overflow_arg_area = bx. gep ( bx. type_i8 ( ) , overflow_arg_area_v, & [ offset] ) ;
442
560
bx. store ( overflow_arg_area, overflow_arg_area_ptr, dl. pointer_align . abi ) ;
443
561
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 )
562
+ mem_addr
452
563
}
453
564
454
565
fn emit_xtensa_va_arg < ' ll , ' tcx > (
0 commit comments