Skip to content

Commit cec5173

Browse files
committed
Auto merge of #158678 - jhpratt:rollup-UiZ1E5v, r=jhpratt
Rollup of 3 pull requests Successful merges: - #155429 (Support `u128`/`i128` c-variadic arguments) - #158669 (Remove `src/tools/test-float-parse/Cargo.lock`) - #158674 (library: Polish transmute's `split_at_stdlib` example)
2 parents c397dae + df1d5c6 commit cec5173

17 files changed

Lines changed: 1731 additions & 120 deletions

File tree

compiler/rustc_codegen_llvm/src/intrinsic.rs

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

55
use rustc_abi::{
6-
AddressSpace, Align, BackendRepr, CVariadicStatus, Float, HasDataLayout, Integer,
7-
NumScalableVectors, Primitive, Size, WrappingRange,
6+
AddressSpace, Align, BackendRepr, CVariadicStatus, Float, HasDataLayout, NumScalableVectors,
7+
Primitive, Size, WrappingRange,
88
};
99
use rustc_codegen_ssa::RetagInfo;
1010
use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh};
@@ -315,11 +315,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
315315
Primitive::Pointer(_) => {
316316
// Pointers are always OK.
317317
}
318-
Primitive::Int(Integer::I128, _) => {
319-
// FIXME: maybe we should support these? At least on 32-bit powerpc
320-
// the logic in LLVM does not handle i128 correctly though.
321-
bug!("the va_arg intrinsic does not support `i128`/`u128`")
322-
}
323318
Primitive::Int(..) => {
324319
let int_width = self.cx().size_of(result_layout.ty).bits();
325320
let target_c_int_width = self.cx().sess().target.options.c_int_width;

compiler/rustc_codegen_llvm/src/va_arg.rs

Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
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+
};
34
use rustc_codegen_ssa::common::IntPredicate;
45
use rustc_codegen_ssa::mir::operand::OperandRef;
56
use 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+
80110
enum 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);
@@ -683,7 +715,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
683715
Primitive::Int(_, _) | Primitive::Pointer(_) => (gp_addr, fp_addr),
684716
};
685717

686-
let tmp = bx.alloca(layout.layout.size(), layout.layout.align().abi);
718+
let tmp = bx.alloca(layout.size, layout.align.abi);
687719

688720
let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo);
689721
let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi);
@@ -751,16 +783,12 @@ fn copy_to_temporary_if_more_aligned<'ll, 'tcx>(
751783
src_align: Align,
752784
) -> &'ll Value {
753785
if layout.layout.align.abi > src_align {
754-
let tmp = bx.alloca(layout.layout.size(), layout.layout.align().abi);
755-
bx.memcpy(
756-
tmp,
757-
layout.layout.align.abi,
758-
reg_addr,
759-
src_align,
760-
bx.const_u32(layout.layout.size().bytes() as u32),
761-
MemFlags::empty(),
762-
None,
763-
);
786+
assert!(layout.ty.is_integral());
787+
788+
// A memcpy below optimizes poorly for 128-bit integers.
789+
let tmp = bx.alloca(layout.size, layout.align.abi);
790+
let val = bx.load(layout.llvm_type(bx), reg_addr, src_align);
791+
bx.store(val, tmp, layout.align.abi);
764792
tmp
765793
} else {
766794
reg_addr
@@ -782,9 +810,14 @@ fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>(
782810
// byte boundary if alignment needed by type exceeds 8 byte boundary.
783811
// It isn't stated explicitly in the standard, but in practice we use
784812
// alignment greater than 16 where necessary.
785-
if layout.layout.align.bytes() > 8 {
786-
unreachable!("all instances of VaArgSafe have an alignment <= 8");
787-
}
813+
// The AMD64 psABI leaves unspecified what to do for alignments above 16, but
814+
// this behavior for 32+ alignment matches clang.
815+
// It currently (2026 July) can only occur for 16-byte-aligned types.
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;
@@ -1071,7 +1104,8 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
10711104
Arch::AArch64 => emit_aapcs_va_arg(bx, addr, target_ty),
10721105
Arch::Arm => {
10731106
// Types wider than 16 bytes are not currently supported. Clang has special logic for
1074-
// such types, but `VaArgSafe` is not implemented for any type that is this large.
1107+
// such types, but `VaArgSafe` is not implemented for any type that is this large on
1108+
// arm (i.e. 32-bit) targets.
10751109
assert!(bx.cx.size_of(target_ty).bytes() <= 16);
10761110

10771111
emit_ptr_va_arg(
@@ -1093,6 +1127,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
10931127
PassMode::Direct,
10941128
SlotSize::Bytes8,
10951129
AllowHigherAlign::Yes,
1130+
// ForceRightAdjust only takes effect on big-endian architectures.
10961131
ForceRightAdjust::Yes,
10971132
),
10981133
Arch::RiscV32 if target.llvm_abiname == LlvmAbi::Ilp32e => {

library/core/src/ffi/va_list.rs

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@ const impl<'f> Drop for VaList<'f> {
299299
/// and [`c_float`] is promoted to [`c_double`]. Implementing this trait for types that are
300300
/// subject to this promotion rule is invalid.
301301
///
302+
/// This trait is only implemented for 128-bit integers when the platform defines the `__int128`
303+
/// type.
304+
///
302305
/// [`c_int`]: core::ffi::c_int
303306
/// [`c_long`]: core::ffi::c_long
304307
/// [`c_longlong`]: core::ffi::c_longlong
@@ -310,8 +313,8 @@ const impl<'f> Drop for VaList<'f> {
310313
/// [`c_float`]: core::ffi::c_float
311314
/// [`c_double`]: core::ffi::c_double
312315
// We may unseal this trait in the future, but currently our `va_arg` implementations don't support
313-
// types with an alignment larger than 8, or with a non-scalar layout. Inline assembly can be used
314-
// to accept unsupported types in the meantime.
316+
// types with a non-scalar layout. Inline assembly can be used to accept unsupported types in the
317+
// meantime.
315318
#[lang = "va_arg_safe"]
316319
pub impl(self) unsafe trait VaArgSafe: Copy {}
317320

@@ -352,6 +355,61 @@ unsafe impl VaArgSafe for u32 {}
352355
unsafe impl VaArgSafe for u64 {}
353356
unsafe impl VaArgSafe for usize {}
354357

358+
// Implement `VaArgSafe` for 128-bit integers on targets where clang provides `__int128`.
359+
//
360+
// GCC does not implement `__int128` for any 16-bit/32-bit target:
361+
//
362+
// https://gcc.gnu.org/onlinedocs/gcc-15.2.0/gcc/_005f_005fint128.html
363+
//
364+
// > There is no support in GCC for expressing an integer constant of type __int128 for targets
365+
// > with long long integer less than 128 bits wide.
366+
//
367+
// Per https://learn.microsoft.com/en-us/cpp/cpp/data-type-ranges?view=msvc-170, MSVC does not
368+
// define `__int128`.
369+
//
370+
// Clang is slightly more permissive: it defines `__int128` on wasm32 (a 32-bit target) and also
371+
// does provide `__int128` on 64-bit `*-pc-windows-msvc`, and we follow suit.
372+
cfg_select! {
373+
any(
374+
target_arch = "aarch64",
375+
target_arch = "amdgpu",
376+
target_arch = "arm64ec",
377+
target_arch = "bpf",
378+
target_arch = "loongarch64",
379+
target_arch = "mips64",
380+
target_arch = "mips64r6",
381+
target_arch = "nvptx64",
382+
target_arch = "powerpc64",
383+
target_arch = "riscv64",
384+
target_arch = "s390x",
385+
target_arch = "sparc64",
386+
target_arch = "wasm32",
387+
target_arch = "wasm64",
388+
target_arch = "x86_64",
389+
) => {
390+
#[cfg(not(any(target_arch = "wasm32", target_abi = "x32", target_pointer_width = "64")))]
391+
compile_error!("unexpected target architecture for 128-bit c-variadic");
392+
393+
#[unstable_feature_bound(c_variadic_int128)]
394+
#[unstable(feature = "c_variadic_int128", issue = "155752")]
395+
unsafe impl VaArgSafe for i128 {}
396+
#[unstable_feature_bound(c_variadic_int128)]
397+
#[unstable(feature = "c_variadic_int128", issue = "155752")]
398+
unsafe impl VaArgSafe for u128 {}
399+
}
400+
_ => {
401+
#[repr(transparent)]
402+
#[derive(Clone, Copy)]
403+
struct S(i32);
404+
405+
// When there are no actual implementations on i128, declare the c_variadic_int128 feature
406+
// on a private type so that the feature is defined on all targets.
407+
#[unstable(feature = "c_variadic_int128", issue = "155752")]
408+
unsafe impl VaArgSafe for S {}
409+
410+
}
411+
}
412+
355413
unsafe impl VaArgSafe for f64 {}
356414

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

library/core/src/intrinsics/mod.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -818,19 +818,19 @@ pub const fn forget<T: ?Sized>(_: T);
818818
///
819819
/// // This is how the standard library does it. This is the best method, if
820820
/// // you need to do something like this
821-
/// fn split_at_stdlib<T>(slice: &mut [T], mid: usize)
821+
/// fn split_at_stdlib<T>(to_split: &mut [T], mid: usize)
822822
/// -> (&mut [T], &mut [T]) {
823-
/// let len = slice.len();
823+
/// let len = to_split.len();
824824
/// assert!(mid <= len);
825825
/// unsafe {
826-
/// let ptr = slice.as_mut_ptr();
827-
/// // This now has three mutable references pointing at the same
828-
/// // memory. `slice`, the rvalue ret.0, and the rvalue ret.1.
829-
/// // `slice` is never used after `let ptr = ...`, and so one can
830-
/// // treat it as "dead", and therefore, you only have two real
831-
/// // mutable slices.
832-
/// (slice::from_raw_parts_mut(ptr, mid),
833-
/// slice::from_raw_parts_mut(ptr.add(mid), len - mid))
826+
/// let ptr = to_split.as_mut_ptr();
827+
/// let fst = slice::from_raw_parts_mut(ptr, mid);
828+
/// let snd = slice::from_raw_parts_mut(ptr.add(mid), len - mid);
829+
/// // The function now has three mutable references to overlapping memory:
830+
/// // `to_split`, `fst`, and `snd`.
831+
/// // `to_split` is never used after `let ptr = ...` so it can be treated as "dead".
832+
/// // This leaves two "live" mutable slice references, `fst` and `snd`, with no overlap.
833+
/// (fst, snd)
834834
/// }
835835
/// }
836836
/// ```

src/tools/test-float-parse/Cargo.lock

Lines changed: 0 additions & 75 deletions
This file was deleted.

0 commit comments

Comments
 (0)