c-variadic: handle c_int being i16 and c_double being f32 on avr

This commit is contained in:
Folkert de Vries
2026-02-22 17:32:24 +01:00
parent 7db0ab43a7
commit b8ba4002f5
5 changed files with 99 additions and 41 deletions
+39 -29
View File
@@ -285,37 +285,47 @@ fn codegen_intrinsic_call(
}
sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[], &[]),
sym::va_arg => {
match result.layout.backend_repr {
BackendRepr::Scalar(scalar) => {
match scalar.primitive() {
Primitive::Int(..) => {
if self.cx().size_of(result.layout.ty).bytes() < 4 {
// `va_arg` should not be called on an integer type
// less than 4 bytes in length. If it is, promote
// the integer to an `i32` and truncate the result
// back to the smaller type.
let promoted_result = emit_va_arg(self, args[0], tcx.types.i32);
self.trunc(promoted_result, result.layout.llvm_type(self))
} else {
emit_va_arg(self, args[0], result.layout.ty)
}
}
Primitive::Float(Float::F16) => {
bug!("the va_arg intrinsic does not work with `f16`")
}
Primitive::Float(Float::F64) | Primitive::Pointer(_) => {
emit_va_arg(self, args[0], result.layout.ty)
}
// `va_arg` should never be used with the return type f32.
Primitive::Float(Float::F32) => {
bug!("the va_arg intrinsic does not work with `f32`")
}
Primitive::Float(Float::F128) => {
bug!("the va_arg intrinsic does not work with `f128`")
}
let BackendRepr::Scalar(scalar) = result.layout.backend_repr else {
bug!("the va_arg intrinsic does not support non-scalar types")
};
match scalar.primitive() {
Primitive::Pointer(_) => {
// Pointers are always OK.
emit_va_arg(self, args[0], result.layout.ty)
}
Primitive::Int(..) => {
let int_width = self.cx().size_of(result.layout.ty).bits();
let target_c_int_width = self.cx().sess().target.options.c_int_width;
if int_width < u64::from(target_c_int_width) {
// Smaller integer types are automatically promototed and `va_arg`
// should not be called on them.
bug!(
"va_arg got i{} but needs at least c_int (an i{})",
int_width,
target_c_int_width
);
}
emit_va_arg(self, args[0], result.layout.ty)
}
Primitive::Float(Float::F16) => {
bug!("the va_arg intrinsic does not support `f16`")
}
Primitive::Float(Float::F32) => {
if self.cx().sess().target.arch == Arch::Avr {
// c_double is actually f32 on avr.
emit_va_arg(self, args[0], result.layout.ty)
} else {
bug!("the va_arg intrinsic does not support `f32` on this target")
}
}
_ => bug!("the va_arg intrinsic does not work with non-scalar types"),
Primitive::Float(Float::F64) => {
// 64-bit floats are always OK.
emit_va_arg(self, args[0], result.layout.ty)
}
Primitive::Float(Float::F128) => {
bug!("the va_arg intrinsic does not support `f128`")
}
}
}
@@ -499,10 +499,16 @@ fn variadic_error<'tcx>(
ty::Float(ty::FloatTy::F32) => {
variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
}
ty::Int(ty::IntTy::I8 | ty::IntTy::I16) | ty::Bool => {
ty::Int(ty::IntTy::I8) | ty::Bool => {
variadic_error(tcx.sess, arg.span, arg_ty, "c_int");
}
ty::Uint(ty::UintTy::U8 | ty::UintTy::U16) => {
ty::Uint(ty::UintTy::U8) => {
variadic_error(tcx.sess, arg.span, arg_ty, "c_uint");
}
ty::Int(ty::IntTy::I16) if tcx.sess.target.options.c_int_width > 16 => {
variadic_error(tcx.sess, arg.span, arg_ty, "c_int");
}
ty::Uint(ty::UintTy::U16) if tcx.sess.target.options.c_int_width > 16 => {
variadic_error(tcx.sess, arg.span, arg_ty, "c_uint");
}
ty::FnDef(..) => {
+45 -3
View File
@@ -266,14 +266,17 @@ fn drop(&mut self) {
mod sealed {
pub trait Sealed {}
impl Sealed for i16 {}
impl Sealed for i32 {}
impl Sealed for i64 {}
impl Sealed for isize {}
impl Sealed for u16 {}
impl Sealed for u32 {}
impl Sealed for u64 {}
impl Sealed for usize {}
impl Sealed for f32 {}
impl Sealed for f64 {}
impl<T> Sealed for *mut T {}
@@ -299,22 +302,61 @@ impl<T> Sealed for *const T {}
// to accept unsupported types in the meantime.
pub unsafe trait VaArgSafe: sealed::Sealed {}
// i8 and i16 are implicitly promoted to c_int in C, and cannot implement `VaArgSafe`.
crate::cfg_select! {
any(target_arch = "avr", target_arch = "msp430") => {
// c_int/c_uint are i16/u16 on these targets.
//
// - i8 is implicitly promoted to c_int in C, and cannot implement `VaArgSafe`.
// - u8 is implicitly promoted to c_uint in C, and cannot implement `VaArgSafe`.
unsafe impl VaArgSafe for i16 {}
unsafe impl VaArgSafe for u16 {}
}
_ => {
// c_int/c_uint are i32/u32 on this target.
//
// - i8 and i16 are implicitly promoted to c_int in C, and cannot implement `VaArgSafe`.
// - u8 and u16 are implicitly promoted to c_uint in C, and cannot implement `VaArgSafe`.
}
}
crate::cfg_select! {
target_arch = "avr" => {
// c_double is f32 on this target.
unsafe impl VaArgSafe for f32 {}
}
_ => {
// c_double is f64 on this target.
//
// - f32 is implicitly promoted to c_double in C, and cannot implement `VaArgSafe`.
}
}
unsafe impl VaArgSafe for i32 {}
unsafe impl VaArgSafe for i64 {}
unsafe impl VaArgSafe for isize {}
// u8 and u16 are implicitly promoted to c_int in C, and cannot implement `VaArgSafe`.
unsafe impl VaArgSafe for u32 {}
unsafe impl VaArgSafe for u64 {}
unsafe impl VaArgSafe for usize {}
// f32 is implicitly promoted to c_double in C, and cannot implement `VaArgSafe`.
unsafe impl VaArgSafe for f64 {}
unsafe impl<T> VaArgSafe for *mut T {}
unsafe impl<T> VaArgSafe for *const T {}
// Check that relevant `core::ffi` types implement `VaArgSafe`.
const _: () = {
const fn va_arg_safe_check<T: VaArgSafe>() {}
va_arg_safe_check::<crate::ffi::c_int>();
va_arg_safe_check::<crate::ffi::c_uint>();
va_arg_safe_check::<crate::ffi::c_long>();
va_arg_safe_check::<crate::ffi::c_ulong>();
va_arg_safe_check::<crate::ffi::c_longlong>();
va_arg_safe_check::<crate::ffi::c_ulonglong>();
va_arg_safe_check::<crate::ffi::c_double>();
};
impl<'f> VaList<'f> {
/// Read an argument from the variable argument list, and advance to the next argument.
///
@@ -30,17 +30,17 @@ unsafe fn compare_c_str(ptr: *const c_char, val: &CStr) -> bool {
continue_if!(ap.arg::<c_int>() == '4' as c_int);
continue_if!(ap.arg::<c_int>() == ';' as c_int);
continue_if!(ap.arg::<c_int>() == 0x32);
continue_if!(ap.arg::<c_int>() == 0x10000001);
continue_if!(ap.arg::<i32>() == 0x10000001);
continue_if!(compare_c_str(ap.arg::<*const c_char>(), c"Valid!"));
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn check_list_2(mut ap: VaList) -> usize {
continue_if!(ap.arg::<c_double>() == 3.14f64);
continue_if!(ap.arg::<c_double>() == 3.14);
continue_if!(ap.arg::<c_long>() == 12);
continue_if!(ap.arg::<c_int>() == 'a' as c_int);
continue_if!(ap.arg::<c_double>() == 6.28f64);
continue_if!(ap.arg::<c_double>() == 6.28);
continue_if!(compare_c_str(ap.arg::<*const c_char>(), c"Hello"));
continue_if!(ap.arg::<c_int>() == 42);
continue_if!(compare_c_str(ap.arg::<*const c_char>(), c"World"));
@@ -49,7 +49,7 @@ unsafe fn compare_c_str(ptr: *const c_char, val: &CStr) -> bool {
#[unsafe(no_mangle)]
pub unsafe extern "C" fn check_list_copy_0(mut ap: VaList) -> usize {
continue_if!(ap.arg::<c_double>() == 6.28f64);
continue_if!(ap.arg::<c_double>() == 6.28);
continue_if!(ap.arg::<c_int>() == 16);
continue_if!(ap.arg::<c_int>() == 'A' as c_int);
continue_if!(compare_c_str(ap.arg::<*const c_char>(), c"Skip Me!"));
@@ -66,7 +66,7 @@ unsafe fn compare_c_str(ptr: *const c_char, val: &CStr) -> bool {
#[unsafe(no_mangle)]
pub unsafe extern "C" fn check_varargs_1(_: c_int, mut ap: ...) -> usize {
continue_if!(ap.arg::<c_double>() == 3.14f64);
continue_if!(ap.arg::<c_double>() == 3.14);
continue_if!(ap.arg::<c_long>() == 12);
continue_if!(ap.arg::<c_int>() == 'A' as c_int);
continue_if!(ap.arg::<c_longlong>() == 1);
@@ -156,7 +156,7 @@ extern "C" fn run_test_variadic() -> usize {
#[unsafe(no_mangle)]
extern "C" fn run_test_va_list_by_value() -> usize {
unsafe extern "C" fn helper(mut ap: ...) -> usize {
unsafe extern "C" fn helper(ap: ...) -> usize {
unsafe { test_va_list_by_value(ap) }
}
@@ -32,7 +32,7 @@ int test_rust(size_t (*fn)(va_list), ...) {
int main(int argc, char* argv[]) {
assert(test_rust(check_list_0, 0x01LL, 0x02, 0x03LL) == 0);
assert(test_rust(check_list_1, -1, 'A', '4', ';', 0x32, 0x10000001, "Valid!") == 0);
assert(test_rust(check_list_1, -1, 'A', '4', ';', 0x32, (int32_t)0x10000001, "Valid!") == 0);
assert(test_rust(check_list_2, 3.14, 12l, 'a', 6.28, "Hello", 42, "World") == 0);