Skip to content

Commit 0f6fe2e

Browse files
Rollup merge of #155486 - folkertdev:c-variadic-roundtrip, r=RalfJung
c-variadic: add roundtrip test tracking issue: #44930 Test that our `va_arg` implementation matches (as in, can decode) how LLVM passes c-variadic arguments. And some comment followup to #152980 (cc @RalfJung, feel free to review this PR too btw). r? tgross35
2 parents e7c135e + fa740c7 commit 0f6fe2e

2 files changed

Lines changed: 93 additions & 8 deletions

File tree

compiler/rustc_codegen_llvm/src/intrinsic.rs

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

55
use rustc_abi::{
6-
Align, BackendRepr, Float, HasDataLayout, NumScalableVectors, Primitive, Size, WrappingRange,
6+
Align, BackendRepr, Float, HasDataLayout, Integer, NumScalableVectors, Primitive, Size,
7+
WrappingRange,
78
};
89
use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh};
910
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
@@ -288,10 +289,17 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
288289
bug!("the va_arg intrinsic does not support non-scalar types")
289290
};
290291

292+
// We reject types that would never be passed as varargs in C because
293+
// they get promoted to a larger type, specifically integers smaller than
294+
// c_int and float type smaller than c_double.
291295
match scalar.primitive() {
292296
Primitive::Pointer(_) => {
293297
// Pointers are always OK.
294-
emit_va_arg(self, args[0], result.layout.ty)
298+
}
299+
Primitive::Int(Integer::I128, _) => {
300+
// FIXME: maybe we should support these? At least on 32-bit powerpc
301+
// the logic in LLVM does not handle i128 correctly though.
302+
bug!("the va_arg intrinsic does not support `i128`/`u128`")
295303
}
296304
Primitive::Int(..) => {
297305
let int_width = self.cx().size_of(result.layout.ty).bits();
@@ -305,27 +313,26 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
305313
target_c_int_width
306314
);
307315
}
308-
emit_va_arg(self, args[0], result.layout.ty)
309316
}
310317
Primitive::Float(Float::F16) => {
311318
bug!("the va_arg intrinsic does not support `f16`")
312319
}
313320
Primitive::Float(Float::F32) => {
314-
if self.cx().sess().target.arch == Arch::Avr {
315-
// c_double is actually f32 on avr.
316-
emit_va_arg(self, args[0], result.layout.ty)
317-
} else {
321+
// c_double is actually f32 on avr.
322+
if self.cx().sess().target.arch != Arch::Avr {
318323
bug!("the va_arg intrinsic does not support `f32` on this target")
319324
}
320325
}
321326
Primitive::Float(Float::F64) => {
322327
// 64-bit floats are always OK.
323-
emit_va_arg(self, args[0], result.layout.ty)
324328
}
325329
Primitive::Float(Float::F128) => {
330+
// FIXME(f128) figure out whether we should support this.
326331
bug!("the va_arg intrinsic does not support `f128`")
327332
}
328333
}
334+
335+
emit_va_arg(self, args[0], result.layout.ty)
329336
}
330337

331338
sym::volatile_load | sym::unaligned_volatile_load => {

tests/ui/c-variadic/roundtrip.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//@ run-pass
2+
//@ ignore-backends: gcc
3+
#![feature(c_variadic, const_c_variadic, const_destruct, const_raw_ptr_comparison)]
4+
5+
use std::ffi::*;
6+
7+
// In rustc we implement `va_arg` for the callee reading from a VaList, but still rely on LLVM
8+
// for exactly how to pass c-variadic arguments and for constructing the VaList. Here we test
9+
// that the rustc implementation works with what LLVM gives us.
10+
11+
#[allow(improper_ctypes_definitions)]
12+
const unsafe extern "C" fn variadic<T: VaArgSafe>(mut ap: ...) -> (T, T) {
13+
let x = ap.arg::<T>();
14+
// Intersperse a small type to test alignment logic. A `u32` (i.e. `c_uint`) is the smallest
15+
// type that implements `VaArgSafe`: smaller types would automatically be promoted.
16+
assert!(ap.arg::<u32>() == 0xAAAA_AAAA);
17+
let y = ap.arg::<T>();
18+
19+
(x, y)
20+
}
21+
22+
macro_rules! roundtrip {
23+
($ty:ty, $a:expr, $b:expr) => {
24+
const {
25+
let a: $ty = $a;
26+
let b: $ty = $b;
27+
let (x, y) = variadic::<$ty>(a, 0xAAAA_AAAAu32, b);
28+
assert!(a == x);
29+
assert!(b == y);
30+
}
31+
32+
let a: $ty = $a;
33+
let b: $ty = $b;
34+
assert_eq!(variadic::<$ty>(a, 0xAAAA_AAAAu32, b), (a, b))
35+
};
36+
}
37+
38+
macro_rules! roundtrip_ptr {
39+
($ty:ty, $a:expr, $b:expr) => {
40+
const {
41+
let a: $ty = $a;
42+
let b: $ty = $b;
43+
let (x, y) = variadic::<$ty>(a, 0xAAAA_AAAAu32, b);
44+
assert!(a.guaranteed_eq(x).unwrap());
45+
assert!(b.guaranteed_eq(y).unwrap());
46+
}
47+
48+
let a: $ty = $a;
49+
let b: $ty = $b;
50+
assert_eq!(variadic::<$ty>(a, 0xAAAA_AAAAu32, b), (a, b))
51+
};
52+
}
53+
54+
fn main() {
55+
unsafe {
56+
roundtrip!(i32, -1, -2);
57+
roundtrip!(i64, -1, -2);
58+
roundtrip!(isize, -1, -2);
59+
roundtrip!(c_int, -1, -2);
60+
roundtrip!(c_long, -1, -2);
61+
roundtrip!(c_longlong, -1, -2);
62+
63+
roundtrip!(u32, 1, 2);
64+
roundtrip!(u64, 1, 2);
65+
roundtrip!(usize, 1, 2);
66+
roundtrip!(c_uint, 1, 2);
67+
roundtrip!(c_ulong, 1, 2);
68+
roundtrip!(c_ulonglong, 1, 2);
69+
70+
roundtrip!(f64, 3.14, 6.28);
71+
roundtrip!(c_double, 3.14, 6.28);
72+
73+
static mut A: u32 = 1u32;
74+
static mut B: u32 = 2u32;
75+
roundtrip_ptr!(*const u32, &raw const A, &raw const B);
76+
roundtrip_ptr!(*mut u32, &raw mut A, &raw mut B);
77+
}
78+
}

0 commit comments

Comments
 (0)