Skip to content

Commit aee82fa

Browse files
committed
Auto merge of #155429 - folkertdev:c-variadic-i128, r=<try>
Support `u128`/`i128` c-variadic arguments try-job: dist-various-1 try-job: dist-various-2 try-job: aarch64-apple try-job: x86_64-mingw-1
2 parents 9838411 + a34dc03 commit aee82fa

16 files changed

Lines changed: 1919 additions & 23 deletions

File tree

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use std::ffi::c_uint;
33
use std::{assert_matches, iter, ptr};
44

55
use rustc_abi::{
6-
Align, BackendRepr, Float, HasDataLayout, Integer, NumScalableVectors, Primitive, Size,
7-
WrappingRange,
6+
Align, BackendRepr, Float, HasDataLayout, NumScalableVectors, Primitive, Size, WrappingRange,
87
};
98
use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh};
109
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
@@ -298,11 +297,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
298297
Primitive::Pointer(_) => {
299298
// Pointers are always OK.
300299
}
301-
Primitive::Int(Integer::I128, _) => {
302-
// FIXME: maybe we should support these? At least on 32-bit powerpc
303-
// the logic in LLVM does not handle i128 correctly though.
304-
bug!("the va_arg intrinsic does not support `i128`/`u128`")
305-
}
306300
Primitive::Int(..) => {
307301
let int_width = self.cx().size_of(result.layout.ty).bits();
308302
let target_c_int_width = self.cx().sess().target.options.c_int_width;

compiler/rustc_codegen_llvm/src/va_arg.rs

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_abi::{Align, BackendRepr, Endian, HasDataLayout, Primitive, Size};
1+
use rustc_abi::{Align, BackendRepr, Endian, Float, HasDataLayout, Integer, Primitive, Size};
22
use rustc_codegen_ssa::MemFlags;
33
use rustc_codegen_ssa::common::IntPredicate;
44
use rustc_codegen_ssa::mir::operand::OperandRef;
@@ -77,6 +77,35 @@ fn emit_direct_ptr_va_arg<'ll, 'tcx>(
7777
}
7878
}
7979

80+
/// Some backends apply special alignment rules to c-variadic arguments.
81+
fn get_param_type_alignment<'ll, 'tcx>(
82+
bx: &mut Builder<'_, 'll, 'tcx>,
83+
layout: TyAndLayout<'tcx>,
84+
) -> Align {
85+
let BackendRepr::Scalar(scalar) = layout.backend_repr else {
86+
bug!("unexpected backend repr {:?}", layout.backend_repr);
87+
};
88+
89+
match bx.cx.tcx.sess.target.arch {
90+
Arch::PowerPC64 => match scalar.primitive() {
91+
Primitive::Int(integer, _) => match integer {
92+
Integer::I8 | Integer::I16 => unreachable!(),
93+
Integer::I32 | Integer::I64 => { /* fall through */ }
94+
Integer::I128 => return Align::EIGHT,
95+
},
96+
Primitive::Float(float) => match float {
97+
Float::F16 | Float::F32 => unreachable!(),
98+
Float::F64 => { /* fall through */ }
99+
Float::F128 => return Align::from_bytes(16).unwrap(),
100+
},
101+
Primitive::Pointer(_) => { /* fall through */ }
102+
},
103+
_ => { /* fall through */ }
104+
}
105+
106+
layout.align.abi
107+
}
108+
80109
enum PassMode {
81110
Direct,
82111
Indirect,
@@ -136,23 +165,23 @@ fn emit_ptr_va_arg<'ll, 'tcx>(
136165
(
137166
bx.cx.layout_of(Ty::new_imm_ptr(bx.cx.tcx, target_ty)).llvm_type(bx.cx),
138167
bx.cx.data_layout().pointer_size(),
139-
bx.cx.data_layout().pointer_align(),
168+
bx.cx.data_layout().pointer_align().abi,
140169
)
141170
} else {
142-
(layout.llvm_type(bx.cx), layout.size, layout.align)
171+
(layout.llvm_type(bx.cx), layout.size, get_param_type_alignment(bx, layout))
143172
};
144173
let (addr, addr_align) = emit_direct_ptr_va_arg(
145174
bx,
146175
list,
147176
size,
148-
align.abi,
177+
align,
149178
slot_size,
150179
allow_higher_align,
151180
force_right_adjust,
152181
);
153182
if indirect {
154183
let tmp_ret = bx.load(llty, addr, addr_align);
155-
bx.load(layout.llvm_type(bx.cx), tmp_ret, align.abi)
184+
bx.load(layout.llvm_type(bx.cx), tmp_ret, align)
156185
} else {
157186
bx.load(llty, addr, addr_align)
158187
}
@@ -585,8 +614,10 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
585614
// registers. In the case: l->gp_offset > 48 - num_gp * 8 or
586615
// l->fp_offset > 176 - num_fp * 16 go to step 7.
587616

617+
// We support x86_64-unknown-linux-gnux32 which uses 4-byte pointers.
588618
let unsigned_int_offset = 4;
589-
let ptr_offset = 8;
619+
let ptr_offset = bx.tcx().data_layout.pointer_size().bytes();
620+
590621
let gp_offset_ptr = va_list_addr;
591622
let fp_offset_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(unsigned_int_offset));
592623

@@ -660,7 +691,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
660691
let reg_hi_addr = bx.inbounds_ptradd(reg_lo_addr, bx.const_i32(16));
661692

662693
let align = layout.layout.align().abi;
663-
let tmp = bx.alloca(layout.layout.size(), align);
694+
let tmp = bx.alloca_with_ty(layout);
664695

665696
let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo);
666697
let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi);
@@ -683,7 +714,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
683714
Primitive::Int(_, _) | Primitive::Pointer(_) => (gp_addr, fp_addr),
684715
};
685716

686-
let tmp = bx.alloca(layout.layout.size(), layout.layout.align().abi);
717+
let tmp = bx.alloca_with_ty(layout);
687718

688719
let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo);
689720
let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi);
@@ -751,7 +782,7 @@ fn copy_to_temporary_if_more_aligned<'ll, 'tcx>(
751782
src_align: Align,
752783
) -> &'ll Value {
753784
if layout.layout.align.abi > src_align {
754-
let tmp = bx.alloca(layout.layout.size(), layout.layout.align().abi);
785+
let tmp = bx.alloca_with_ty(layout);
755786
bx.memcpy(
756787
tmp,
757788
layout.layout.align.abi,
@@ -782,9 +813,11 @@ fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>(
782813
// byte boundary if alignment needed by type exceeds 8 byte boundary.
783814
// It isn't stated explicitly in the standard, but in practice we use
784815
// alignment greater than 16 where necessary.
785-
if layout.layout.align.bytes() > 8 {
786-
unreachable!("all instances of VaArgSafe have an alignment <= 8");
787-
}
816+
let overflow_arg_area_v = if layout.layout.align.bytes() > 8 {
817+
round_pointer_up_to_alignment(bx, overflow_arg_area_v, layout.layout.align.abi)
818+
} else {
819+
overflow_arg_area_v
820+
};
788821

789822
// AMD64-ABI 3.5.7p5: Step 8. Fetch type from l->overflow_arg_area.
790823
let mem_addr = overflow_arg_area_v;
@@ -1069,7 +1102,8 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
10691102
Arch::AArch64 => emit_aapcs_va_arg(bx, addr, target_ty),
10701103
Arch::Arm => {
10711104
// 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.
1105+
// such types, but `VaArgSafe` is not implemented for any type that is this large on
1106+
// arm (i.e. 32-bit) targets.
10731107
assert!(bx.cx.size_of(target_ty).bytes() <= 16);
10741108

10751109
emit_ptr_va_arg(
@@ -1091,6 +1125,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
10911125
PassMode::Direct,
10921126
SlotSize::Bytes8,
10931127
AllowHigherAlign::Yes,
1128+
// ForceRightAdjust only takes effect on big-endian architectures.
10941129
ForceRightAdjust::Yes,
10951130
),
10961131
Arch::RiscV32 if target.llvm_abiname == LlvmAbi::Ilp32e => {

library/core/src/ffi/va_list.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,15 @@ mod sealed {
286286
impl Sealed for f32 {}
287287
impl Sealed for f64 {}
288288

289+
// The unstable annotation here is not needed, but makes sure that the feature gate is defined
290+
// on all targets, even though the actual instances of VaArgSafe for i128/u128 are not.
291+
#[unstable_feature_bound(c_variadic_int128)]
292+
#[unstable(feature = "c_variadic_int128", issue = "155752")]
293+
impl Sealed for i128 {}
294+
#[unstable_feature_bound(c_variadic_int128)]
295+
#[unstable(feature = "c_variadic_int128", issue = "155752")]
296+
impl Sealed for u128 {}
297+
289298
impl<T> Sealed for *mut T {}
290299
impl<T> Sealed for *const T {}
291300
}
@@ -310,6 +319,9 @@ mod sealed {
310319
/// and [`c_float`] is promoted to [`c_double`]. Implementing this trait for types that are
311320
/// subject to this promotion rule is invalid.
312321
///
322+
/// This trait is only implemented for 128-bit integers when the platform defines the `__int128`
323+
/// type.
324+
///
313325
/// [`c_int`]: core::ffi::c_int
314326
/// [`c_long`]: core::ffi::c_long
315327
/// [`c_longlong`]: core::ffi::c_longlong
@@ -363,6 +375,51 @@ unsafe impl VaArgSafe for u32 {}
363375
unsafe impl VaArgSafe for u64 {}
364376
unsafe impl VaArgSafe for usize {}
365377

378+
// Implement `VaArgSafe` for 128-bit integers on targets where clang provides `__int128`.
379+
//
380+
// GCC does not implement `__int128` for any 16-bit/32-bit target:
381+
//
382+
// https://gcc.gnu.org/onlinedocs/gcc-15.2.0/gcc/_005f_005fint128.html
383+
//
384+
// > There is no support in GCC for expressing an integer constant of type __int128 for targets
385+
// > with long long integer less than 128 bits wide.
386+
//
387+
// Per https://learn.microsoft.com/en-us/cpp/cpp/data-type-ranges?view=msvc-170, MSVC does not
388+
// define `__int128`.
389+
//
390+
// Clang is slightly more permissive: it defines `__int128` on wasm32 (a 32-bit target) and also
391+
// does provide `__int128` on 64-bit `*-pc-windows-msvc`, and we follow suit.
392+
cfg_select! {
393+
any(
394+
target_arch = "aarch64",
395+
target_arch = "amdgpu",
396+
target_arch = "arm64ec",
397+
target_arch = "bpf",
398+
target_arch = "loongarch64",
399+
target_arch = "mips64",
400+
target_arch = "mips64r6",
401+
target_arch = "nvptx64",
402+
target_arch = "powerpc64",
403+
target_arch = "riscv64",
404+
target_arch = "s390x",
405+
target_arch = "sparc64",
406+
target_arch = "wasm32",
407+
target_arch = "wasm64",
408+
target_arch = "x86_64",
409+
) => {
410+
#[cfg(not(any(target_arch = "wasm32", target_abi = "x32", target_pointer_width = "64")))]
411+
compile_error!("unexpected target architecture for 128-bit c-variadic");
412+
413+
#[unstable_feature_bound(c_variadic_int128)]
414+
#[unstable(feature = "c_variadic_int128", issue = "155752")]
415+
unsafe impl VaArgSafe for i128 {}
416+
#[unstable_feature_bound(c_variadic_int128)]
417+
#[unstable(feature = "c_variadic_int128", issue = "155752")]
418+
unsafe impl VaArgSafe for u128 {}
419+
}
420+
_ => {}
421+
}
422+
366423
unsafe impl VaArgSafe for f64 {}
367424

368425
unsafe impl<T> VaArgSafe for *mut T {}

0 commit comments

Comments
 (0)