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 ;
23use rustc_codegen_ssa:: common:: IntPredicate ;
34use 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+ } ;
58use rustc_middle:: ty:: Ty ;
69use rustc_middle:: ty:: layout:: { HasTyCtxt , LayoutOf } ;
710
@@ -300,12 +303,16 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
300303 // } va_list[1];
301304 let va_list_addr = list. immediate ( ) ;
302305
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) ;
307309
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+ } ;
309316
310317 // AMD64-ABI 3.5.7p5: Step 1. Determine whether type may be passed
311318 // in the registers. If not go to step 7.
@@ -317,37 +324,48 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
317324 let mut num_gp_registers = 0 ;
318325 let mut num_fp_registers = 0 ;
319326
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+
320339 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 { .. } => {
335348 // Because no instance of VaArgSafe uses a non-scalar `BackendRepr`.
336349 unreachable ! (
337350 "No x86-64 SysV va_arg implementation for {:?}" ,
338351 layout. layout. backend_repr( )
339352 )
340353 }
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+ }
341358 } ;
342359
343- if num_gp_registers == 0 && num_fp_registers == 0 {
344- unreachable ! ( "VaArgSafe is not implemented for ZSTs" )
345- }
346-
347360 // AMD64-ABI 3.5.7p5: Step 3. Verify whether arguments fit into
348361 // registers. In the case: l->gp_offset > 48 - num_gp * 8 or
349362 // l->fp_offset > 176 - num_fp * 16 go to step 7.
350363
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+
351369 let gp_offset_v = bx. load ( bx. type_i32 ( ) , gp_offset_ptr, Align :: from_bytes ( 8 ) . unwrap ( ) ) ;
352370 let fp_offset_v = bx. load ( bx. type_i32 ( ) , fp_offset_ptr, Align :: from_bytes ( 4 ) . unwrap ( ) ) ;
353371
@@ -388,14 +406,87 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
388406 bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( 2 * unsigned_int_offset + ptr_offset) ) ;
389407 let reg_save_area_v = bx. load ( bx. type_ptr ( ) , reg_save_area_ptr, dl. pointer_align . abi ) ;
390408
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+ }
399490 } ;
400491
401492 // AMD64-ABI 3.5.7p5: Step 5. Set:
@@ -416,9 +507,47 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
416507 bx. br ( end) ;
417508
418509 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) ;
419512
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 ) ) ;
422551
423552 let overflow_arg_area_v = bx. load ( bx. type_ptr ( ) , overflow_arg_area_ptr, dl. pointer_align . abi ) ;
424553 // 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>(
441570 let overflow_arg_area = bx. gep ( bx. type_i8 ( ) , overflow_arg_area_v, & [ offset] ) ;
442571 bx. store ( overflow_arg_area, overflow_arg_area_ptr, dl. pointer_align . abi ) ;
443572
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
452574}
453575
454576fn emit_xtensa_va_arg < ' ll , ' tcx > (
0 commit comments