1- use rustc_abi:: { Align , BackendRepr , CVariadicStatus , Endian , HasDataLayout , Primitive , Size } ;
2- use rustc_codegen_ssa:: MemFlags ;
1+ use rustc_abi:: {
2+ Align , BackendRepr , CVariadicStatus , Endian , Float , HasDataLayout , Integer , Primitive , Size ,
3+ } ;
34use rustc_codegen_ssa:: common:: IntPredicate ;
45use rustc_codegen_ssa:: mir:: operand:: OperandRef ;
56use rustc_codegen_ssa:: traits:: {
@@ -77,6 +78,35 @@ fn emit_direct_ptr_va_arg<'ll, 'tcx>(
7778 }
7879}
7980
81+ /// Some backends apply special alignment rules to c-variadic arguments.
82+ fn get_param_type_alignment < ' ll , ' tcx > (
83+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
84+ layout : TyAndLayout < ' tcx > ,
85+ ) -> Align {
86+ let BackendRepr :: Scalar ( scalar) = layout. backend_repr else {
87+ bug ! ( "unexpected backend repr {:?}" , layout. backend_repr) ;
88+ } ;
89+
90+ match bx. cx . tcx . sess . target . arch {
91+ Arch :: PowerPC64 => match scalar. primitive ( ) {
92+ Primitive :: Int ( integer, _) => match integer {
93+ Integer :: I8 | Integer :: I16 => unreachable ! ( ) ,
94+ Integer :: I32 | Integer :: I64 => { /* fall through */ }
95+ Integer :: I128 => return Align :: EIGHT ,
96+ } ,
97+ Primitive :: Float ( float) => match float {
98+ Float :: F16 | Float :: F32 => unreachable ! ( ) ,
99+ Float :: F64 => { /* fall through */ }
100+ Float :: F128 => return Align :: from_bytes ( 16 ) . unwrap ( ) ,
101+ } ,
102+ Primitive :: Pointer ( _) => { /* fall through */ }
103+ } ,
104+ _ => { /* fall through */ }
105+ }
106+
107+ layout. align . abi
108+ }
109+
80110enum PassMode {
81111 Direct ,
82112 Indirect ,
@@ -136,23 +166,23 @@ fn emit_ptr_va_arg<'ll, 'tcx>(
136166 (
137167 bx. cx . layout_of ( Ty :: new_imm_ptr ( bx. cx . tcx , target_ty) ) . llvm_type ( bx. cx ) ,
138168 bx. cx . data_layout ( ) . pointer_size ( ) ,
139- bx. cx . data_layout ( ) . pointer_align ( ) ,
169+ bx. cx . data_layout ( ) . pointer_align ( ) . abi ,
140170 )
141171 } else {
142- ( layout. llvm_type ( bx. cx ) , layout. size , layout. align )
172+ ( layout. llvm_type ( bx. cx ) , layout. size , get_param_type_alignment ( bx , layout) )
143173 } ;
144174 let ( addr, addr_align) = emit_direct_ptr_va_arg (
145175 bx,
146176 list,
147177 size,
148- align. abi ,
178+ align,
149179 slot_size,
150180 allow_higher_align,
151181 force_right_adjust,
152182 ) ;
153183 if indirect {
154184 let tmp_ret = bx. load ( llty, addr, addr_align) ;
155- bx. load ( layout. llvm_type ( bx. cx ) , tmp_ret, align. abi )
185+ bx. load ( layout. llvm_type ( bx. cx ) , tmp_ret, align)
156186 } else {
157187 bx. load ( llty, addr, addr_align)
158188 }
@@ -585,8 +615,10 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
585615 // registers. In the case: l->gp_offset > 48 - num_gp * 8 or
586616 // l->fp_offset > 176 - num_fp * 16 go to step 7.
587617
618+ // We support x86_64-unknown-linux-gnux32 which uses 4-byte pointers.
588619 let unsigned_int_offset = 4 ;
589- let ptr_offset = 8 ;
620+ let ptr_offset = bx. tcx ( ) . data_layout . pointer_size ( ) . bytes ( ) ;
621+
590622 let gp_offset_ptr = va_list_addr;
591623 let fp_offset_ptr = bx. inbounds_ptradd ( va_list_addr, bx. cx . const_usize ( unsigned_int_offset) ) ;
592624
@@ -660,7 +692,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
660692 let reg_hi_addr = bx. inbounds_ptradd ( reg_lo_addr, bx. const_i32 ( 16 ) ) ;
661693
662694 let align = layout. layout . align ( ) . abi ;
663- let tmp = bx. alloca ( layout. layout . size ( ) , align) ;
695+ let tmp = bx. alloca ( layout. size , layout . align . abi ) ;
664696
665697 let reg_lo = bx. load ( ty_lo, reg_lo_addr, align_lo) ;
666698 let reg_hi = bx. load ( ty_hi, reg_hi_addr, align_hi) ;
@@ -682,7 +714,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
682714 Primitive :: Int ( _, _) | Primitive :: Pointer ( _) => ( gp_addr, fp_addr) ,
683715 } ;
684716
685- let tmp = bx. alloca ( layout. layout . size ( ) , layout. layout . align ( ) . abi ) ;
717+ let tmp = bx. alloca ( layout. size , layout. align . abi ) ;
686718
687719 let reg_lo = bx. load ( ty_lo, reg_lo_addr, align_lo) ;
688720 let reg_hi = bx. load ( ty_hi, reg_hi_addr, align_hi) ;
@@ -749,16 +781,12 @@ fn copy_to_temporary_if_more_aligned<'ll, 'tcx>(
749781 src_align : Align ,
750782) -> & ' ll Value {
751783 if layout. layout . align . abi > src_align {
752- let tmp = bx. alloca ( layout. layout . size ( ) , layout. layout . align ( ) . abi ) ;
753- bx. memcpy (
754- tmp,
755- layout. layout . align . abi ,
756- reg_addr,
757- src_align,
758- bx. const_u32 ( layout. layout . size ( ) . bytes ( ) as u32 ) ,
759- MemFlags :: empty ( ) ,
760- None ,
761- ) ;
784+ assert ! ( layout. ty. is_integral( ) ) ;
785+
786+ // A memcpy below optimizes poorly for 128-bit integers.
787+ let tmp = bx. alloca ( layout. size , layout. align . abi ) ;
788+ let val = bx. load ( layout. llvm_type ( bx) , reg_addr, src_align) ;
789+ bx. store ( val, tmp, layout. align . abi ) ;
762790 tmp
763791 } else {
764792 reg_addr
@@ -780,9 +808,14 @@ fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>(
780808 // byte boundary if alignment needed by type exceeds 8 byte boundary.
781809 // It isn't stated explicitly in the standard, but in practice we use
782810 // alignment greater than 16 where necessary.
783- if layout. layout . align . bytes ( ) > 8 {
784- unreachable ! ( "all instances of VaArgSafe have an alignment <= 8" ) ;
785- }
811+ // The AMD64 psABI leaves unspecified what to do for alignments above 16, but
812+ // this behavior for 32+ alignment matches clang.
813+ // It currently (2026 July) can only occur for 16-byte-aligned types.
814+ let overflow_arg_area_v = if layout. layout . align . bytes ( ) > 8 {
815+ round_pointer_up_to_alignment ( bx, overflow_arg_area_v, layout. layout . align . abi )
816+ } else {
817+ overflow_arg_area_v
818+ } ;
786819
787820 // AMD64-ABI 3.5.7p5: Step 8. Fetch type from l->overflow_arg_area.
788821 let mem_addr = overflow_arg_area_v;
@@ -1052,9 +1085,15 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
10521085 bx,
10531086 addr,
10541087 target_ty,
1055- PassMode :: Direct ,
1088+ // MS x64 ABI requirement: "Any argument that doesn't fit in 8 bytes, or is
1089+ // not 1, 2, 4, or 8 bytes, must be passed by reference."
1090+ if target_ty_size > 8 || !target_ty_size. is_power_of_two ( ) {
1091+ PassMode :: Indirect
1092+ } else {
1093+ PassMode :: Direct
1094+ } ,
10561095 SlotSize :: Bytes8 ,
1057- if target . is_like_windows { AllowHigherAlign :: No } else { AllowHigherAlign :: Yes } ,
1096+ AllowHigherAlign :: No ,
10581097 ForceRightAdjust :: No ,
10591098 ) ,
10601099 Arch :: AArch64 if target. is_like_windows || target. is_like_darwin => emit_ptr_va_arg (
@@ -1063,13 +1102,14 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
10631102 target_ty,
10641103 PassMode :: Direct ,
10651104 SlotSize :: Bytes8 ,
1066- if target . is_like_windows { AllowHigherAlign :: No } else { AllowHigherAlign :: Yes } ,
1105+ AllowHigherAlign :: Yes ,
10671106 ForceRightAdjust :: No ,
10681107 ) ,
10691108 Arch :: AArch64 => emit_aapcs_va_arg ( bx, addr, target_ty) ,
10701109 Arch :: Arm => {
10711110 // Types wider than 16 bytes are not currently supported. Clang has special logic for
1072- // such types, but `VaArgSafe` is not implemented for any type that is this large.
1111+ // such types, but `VaArgSafe` is not implemented for any type that is this large on
1112+ // arm (i.e. 32-bit) targets.
10731113 assert ! ( bx. cx. size_of( target_ty) . bytes( ) <= 16 ) ;
10741114
10751115 emit_ptr_va_arg (
@@ -1091,6 +1131,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
10911131 PassMode :: Direct ,
10921132 SlotSize :: Bytes8 ,
10931133 AllowHigherAlign :: Yes ,
1134+ // ForceRightAdjust only takes effect on big-endian architectures.
10941135 ForceRightAdjust :: Yes ,
10951136 ) ,
10961137 Arch :: RiscV32 if target. llvm_abiname == LlvmAbi :: Ilp32e => {
0 commit comments