Skip to content

Commit 8e7c3b1

Browse files
committed
Support u128/i128 c-variadic arguments
1 parent e8e4541 commit 8e7c3b1

4 files changed

Lines changed: 68 additions & 15 deletions

File tree

compiler/rustc_codegen_llvm/src/va_arg.rs

Lines changed: 49 additions & 14 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;
@@ -8,7 +8,7 @@ use rustc_codegen_ssa::traits::{
88
use rustc_middle::bug;
99
use rustc_middle::ty::Ty;
1010
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
11-
use rustc_target::spec::{Arch, Env, LlvmAbi, RustcAbi};
11+
use rustc_target::spec::{Arch, Env, HasTargetSpec, LlvmAbi, RustcAbi};
1212

1313
use crate::builder::Builder;
1414
use crate::llvm::{Type, Value};
@@ -30,11 +30,9 @@ fn round_pointer_up_to_alignment<'ll>(
3030
ptr_ty: &'ll Type,
3131
) -> &'ll Value {
3232
let ptr = bx.inbounds_ptradd(addr, bx.const_i32(align.bytes() as i32 - 1));
33-
bx.call_intrinsic(
34-
"llvm.ptrmask",
35-
&[ptr_ty, bx.type_i32()],
36-
&[ptr, bx.const_int(bx.isize_ty, -(align.bytes() as isize) as i64)],
37-
)
33+
let pointer_width = bx.target_spec().pointer_width;
34+
let mask = align.bytes().wrapping_neg() & (u64::MAX >> (64 - pointer_width));
35+
bx.call_intrinsic("llvm.ptrmask", &[ptr_ty, bx.type_isize()], &[ptr, bx.const_usize(mask)])
3836
}
3937

4038
fn emit_direct_ptr_va_arg<'ll, 'tcx>(
@@ -75,6 +73,35 @@ fn emit_direct_ptr_va_arg<'ll, 'tcx>(
7573
}
7674
}
7775

76+
/// Some backends apply special alignment rules to c-variadic arguments.
77+
fn get_param_type_alignment<'ll, 'tcx>(
78+
bx: &mut Builder<'_, 'll, 'tcx>,
79+
layout: TyAndLayout<'tcx>,
80+
) -> Align {
81+
let BackendRepr::Scalar(scalar) = layout.backend_repr else {
82+
bug!("unexpected backend repr {:?}", layout.backend_repr);
83+
};
84+
85+
match bx.cx.tcx.sess.target.arch {
86+
Arch::PowerPC64 => match scalar.primitive() {
87+
Primitive::Int(integer, _) => match integer {
88+
Integer::I8 | Integer::I16 => unreachable!(),
89+
Integer::I32 | Integer::I64 => { /* fall through */ }
90+
Integer::I128 => return Align::EIGHT,
91+
},
92+
Primitive::Float(float) => match float {
93+
Float::F16 | Float::F32 => unreachable!(),
94+
Float::F64 => { /* fall through */ }
95+
Float::F128 => return Align::from_bytes(16).unwrap(),
96+
},
97+
Primitive::Pointer(_) => { /* fall through */ }
98+
},
99+
_ => { /* fall through */ }
100+
}
101+
102+
layout.align.abi
103+
}
104+
78105
enum PassMode {
79106
Direct,
80107
Indirect,
@@ -115,23 +142,23 @@ fn emit_ptr_va_arg<'ll, 'tcx>(
115142
(
116143
bx.cx.layout_of(Ty::new_imm_ptr(bx.cx.tcx, target_ty)).llvm_type(bx.cx),
117144
bx.cx.data_layout().pointer_size(),
118-
bx.cx.data_layout().pointer_align(),
145+
bx.cx.data_layout().pointer_align().abi,
119146
)
120147
} else {
121-
(layout.llvm_type(bx.cx), layout.size, layout.align)
148+
(layout.llvm_type(bx.cx), layout.size, get_param_type_alignment(bx, layout))
122149
};
123150
let (addr, addr_align) = emit_direct_ptr_va_arg(
124151
bx,
125152
list,
126153
size,
127-
align.abi,
154+
align,
128155
slot_size,
129156
allow_higher_align,
130157
force_right_adjust,
131158
);
132159
if indirect {
133160
let tmp_ret = bx.load(llty, addr, addr_align);
134-
bx.load(layout.llvm_type(bx.cx), tmp_ret, align.abi)
161+
bx.load(layout.llvm_type(bx.cx), tmp_ret, align)
135162
} else {
136163
bx.load(llty, addr, addr_align)
137164
}
@@ -765,9 +792,16 @@ fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>(
765792
// byte boundary if alignment needed by type exceeds 8 byte boundary.
766793
// It isn't stated explicitly in the standard, but in practice we use
767794
// alignment greater than 16 where necessary.
768-
if layout.layout.align.bytes() > 8 {
769-
unreachable!("all instances of VaArgSafe have an alignment <= 8");
770-
}
795+
let overflow_arg_area_v = if layout.layout.align.bytes() > 8 {
796+
round_pointer_up_to_alignment(
797+
bx,
798+
overflow_arg_area_v,
799+
layout.layout.align.abi,
800+
bx.type_ptr(),
801+
)
802+
} else {
803+
overflow_arg_area_v
804+
};
771805

772806
// AMD64-ABI 3.5.7p5: Step 8. Fetch type from l->overflow_arg_area.
773807
let mem_addr = overflow_arg_area_v;
@@ -1075,6 +1109,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
10751109
PassMode::Direct,
10761110
SlotSize::Bytes8,
10771111
AllowHigherAlign::Yes,
1112+
// ForceRightAdjust only takes effect on big-endian architectures.
10781113
ForceRightAdjust::Yes,
10791114
),
10801115
Arch::RiscV32 if target.llvm_abiname == LlvmAbi::Ilp32e => {

library/core/src/ffi/va_list.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,10 +268,12 @@ mod sealed {
268268

269269
impl Sealed for i32 {}
270270
impl Sealed for i64 {}
271+
impl Sealed for i128 {}
271272
impl Sealed for isize {}
272273

273274
impl Sealed for u32 {}
274275
impl Sealed for u64 {}
276+
impl Sealed for u128 {}
275277
impl Sealed for usize {}
276278

277279
impl Sealed for f64 {}
@@ -302,11 +304,13 @@ pub unsafe trait VaArgSafe: sealed::Sealed {}
302304
// i8 and i16 are implicitly promoted to c_int in C, and cannot implement `VaArgSafe`.
303305
unsafe impl VaArgSafe for i32 {}
304306
unsafe impl VaArgSafe for i64 {}
307+
unsafe impl VaArgSafe for i128 {}
305308
unsafe impl VaArgSafe for isize {}
306309

307310
// u8 and u16 are implicitly promoted to c_int in C, and cannot implement `VaArgSafe`.
308311
unsafe impl VaArgSafe for u32 {}
309312
unsafe impl VaArgSafe for u64 {}
313+
unsafe impl VaArgSafe for u128 {}
310314
unsafe impl VaArgSafe for usize {}
311315

312316
// f32 is implicitly promoted to c_double in C, and cannot implement `VaArgSafe`.

tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ pub unsafe extern "C" fn check_list_copy_0(mut ap: VaList) -> usize {
5757
if compare_c_str(ap.arg::<*const c_char>(), c"Correct") { 0 } else { 0xff }
5858
}
5959

60+
#[unsafe(no_mangle)]
61+
pub unsafe extern "C" fn check_list_i128(mut ap: VaList) -> usize {
62+
continue_if!(ap.arg::<i128>() == -42);
63+
// use a 32-bit value here to test the alignment logic.
64+
continue_if!(ap.arg::<c_int>() == 0xAAAA_AAAAu32.cast_signed());
65+
continue_if!(ap.arg::<u128>() == u128::MAX);
66+
0
67+
}
68+
6069
#[unsafe(no_mangle)]
6170
pub unsafe extern "C" fn check_varargs_0(_: c_int, mut ap: ...) -> usize {
6271
continue_if!(ap.arg::<c_int>() == 42);
@@ -156,7 +165,7 @@ extern "C" fn run_test_variadic() -> usize {
156165

157166
#[unsafe(no_mangle)]
158167
extern "C" fn run_test_va_list_by_value() -> usize {
159-
unsafe extern "C" fn helper(mut ap: ...) -> usize {
168+
unsafe extern "C" fn helper(ap: ...) -> usize {
160169
unsafe { test_va_list_by_value(ap) }
161170
}
162171

tests/run-make/c-link-to-rust-va-list-fn/test.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ extern size_t check_list_0(va_list ap);
88
extern size_t check_list_1(va_list ap);
99
extern size_t check_list_2(va_list ap);
1010
extern size_t check_list_copy_0(va_list ap);
11+
extern size_t check_list_i128(va_list ap);
1112
extern size_t check_varargs_0(int fixed, ...);
1213
extern size_t check_varargs_1(int fixed, ...);
1314
extern size_t check_varargs_2(int fixed, ...);
@@ -38,6 +39,10 @@ int main(int argc, char* argv[]) {
3839

3940
assert(test_rust(check_list_copy_0, 6.28, 16, 'A', "Skip Me!", "Correct") == 0);
4041

42+
#if defined(__SIZEOF_INT128__)
43+
assert(test_rust(check_list_i128, (__int128)-42, 0xAAAAAAAA, (unsigned __int128)-1) == 0);
44+
#endif
45+
4146
assert(check_varargs_0(0, 42, "Hello, World!") == 0);
4247

4348
assert(check_varargs_1(0, 3.14, 12l, 'A', 0x1LL) == 0);

0 commit comments

Comments
 (0)