c-variadic: gate va_arg on c_variadic_experimental_arch

Just gating `...` is insufficient because we make the types available
everywhere, and you could still define and export functions that used
va_arg for targets where we don't want to stably support it.
This commit is contained in:
Folkert de Vries
2026-05-01 12:39:27 +02:00
parent 0469a92a76
commit 8e0ebb9044
3 changed files with 234 additions and 2 deletions
+13 -2
View File
@@ -3,8 +3,8 @@ use std::ffi::c_uint;
use std::{assert_matches, iter, ptr};
use rustc_abi::{
AddressSpace, Align, BackendRepr, Float, HasDataLayout, Integer, NumScalableVectors, Primitive,
Size, WrappingRange,
AddressSpace, Align, BackendRepr, CVariadicStatus, Float, HasDataLayout, Integer,
NumScalableVectors, Primitive, Size, WrappingRange,
};
use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh};
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
@@ -23,6 +23,7 @@ use rustc_middle::ty::{
};
use rustc_middle::{bug, span_bug};
use rustc_session::config::CrateType;
use rustc_session::errors::feature_err;
use rustc_session::lint::builtin::DEPRECATED_LLVM_INTRINSIC;
use rustc_span::{Span, Symbol, sym};
use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate};
@@ -288,6 +289,16 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
}
sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[], &[]),
sym::va_arg => {
let target = &self.cx.tcx.sess.target;
let stability = target.supports_c_variadic_definitions();
if let CVariadicStatus::Unstable { feature } = stability
&& !self.tcx.features().enabled(feature)
{
let msg =
format!("C-variadic function definitions on this target are unstable");
feature_err(&*self.sess(), feature, span, msg).emit();
}
let BackendRepr::Scalar(scalar) = result.layout.backend_repr else {
bug!("the va_arg intrinsic does not support non-scalar types")
};
+220
View File
@@ -0,0 +1,220 @@
//@ add-minicore
//@ assembly-output: emit-asm
//
//@ revisions: AVR
//@ [AVR] compile-flags: -Copt-level=3 --target=avr-none -Ctarget-cpu=atmega328p
//@ [AVR] needs-llvm-components: avr
#![feature(c_variadic, c_variadic_experimental_arch, no_core, lang_items, intrinsics, rustc_attrs)]
#![no_core]
#![crate_type = "lib"]
// Check that rustc and clang output match, see https://godbolt.org/z/1MvxoceeT.
extern crate minicore;
use minicore::*;
#[lang = "va_arg_safe"]
pub unsafe trait VaArgSafe {}
unsafe impl VaArgSafe for i16 {}
unsafe impl VaArgSafe for i32 {}
unsafe impl VaArgSafe for i64 {}
unsafe impl VaArgSafe for f32 {}
unsafe impl VaArgSafe for f64 {}
unsafe impl<T> VaArgSafe for *const T {}
#[repr(transparent)]
struct VaListInner {
ptr: *const c_void,
}
#[repr(transparent)]
#[lang = "va_list"]
pub struct VaList<'a> {
inner: VaListInner,
_marker: PhantomData<&'a mut ()>,
}
#[rustc_intrinsic]
#[rustc_nounwind]
pub const unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaList<'_>) -> T;
#[unsafe(no_mangle)]
unsafe extern "C" fn read_f32(ap: &mut VaList<'_>) -> f32 {
// CHECK-LABEL: read_f32
//
// AVR: movw r30, r24
// AVR-NEXT: ld r24, Z
// AVR-NEXT: ldd r25, Z+1
// AVR-NEXT: movw r26, r24
// AVR-NEXT: adiw r26, 2
// AVR-NEXT: std Z+1, r27
// AVR-NEXT: st Z, r26
// AVR-NEXT: movw r20, r30
// AVR-NEXT: movw r18, r24
// AVR-NEXT: movw r30, r18
// AVR-NEXT: ld r22, Z
// AVR-NEXT: ldd r23, Z+1
// AVR-NEXT: adiw r24, 4
// AVR-NEXT: movw r30, r20
// AVR-NEXT: std Z+1, r25
// AVR-NEXT: st Z, r24
// AVR-NEXT: movw r30, r18
// AVR-NEXT: ldd r24, Z+2
// AVR-NEXT: ldd r25, Z+3
// AVR-NEXT: ret
va_arg(ap)
}
#[unsafe(no_mangle)]
unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 {
// CHECK-LABEL: read_f64
//
// AVR: push r14
// AVR-NEXT: push r15
// AVR-NEXT: push r16
// AVR-NEXT: push r17
// AVR-NEXT: movw r30, r24
// AVR-NEXT: ld r24, Z
// AVR-NEXT: ldd r25, Z+1
// AVR-NEXT: movw r26, r24
// AVR-NEXT: adiw r26, 2
// AVR-NEXT: std Z+1, r27
// AVR-NEXT: st Z, r26
// AVR-NEXT: movw r14, r30
// AVR-NEXT: movw r16, r24
// AVR-NEXT: movw r30, r16
// AVR-NEXT: ld r18, Z
// AVR-NEXT: ldd r19, Z+1
// AVR-NEXT: movw r26, r24
// AVR-NEXT: adiw r26, 4
// AVR-NEXT: movw r30, r14
// AVR-NEXT: std Z+1, r27
// AVR-NEXT: st Z, r26
// AVR-NEXT: movw r30, r16
// AVR-NEXT: ldd r20, Z+2
// AVR-NEXT: ldd r21, Z+3
// AVR-NEXT: movw r26, r24
// AVR-NEXT: adiw r26, 6
// AVR-NEXT: movw r30, r14
// AVR-NEXT: std Z+1, r27
// AVR-NEXT: st Z, r26
// AVR-NEXT: movw r30, r16
// AVR-NEXT: ldd r22, Z+4
// AVR-NEXT: ldd r23, Z+5
// AVR-NEXT: adiw r24, 8
// AVR-NEXT: movw r30, r14
// AVR-NEXT: std Z+1, r25
// AVR-NEXT: st Z, r24
// AVR-NEXT: movw r30, r16
// AVR-NEXT: ldd r24, Z+6
// AVR-NEXT: ldd r25, Z+7
// AVR-NEXT: pop r17
// AVR-NEXT: pop r16
// AVR-NEXT: pop r15
// AVR-NEXT: pop r14
// AVR-NEXT: ret
va_arg(ap)
}
#[unsafe(no_mangle)]
unsafe extern "C" fn read_i16(ap: &mut VaList<'_>) -> i16 {
// CHECK-LABEL: read_i16
//
// AVR: movw r30, r24
// AVR-NEXT: ld r24, Z
// AVR-NEXT: ldd r25, Z+1
// AVR-NEXT: movw r26, r24
// AVR-NEXT: adiw r26, 2
// AVR-NEXT: std Z+1, r27
// AVR-NEXT: st Z, r26
// AVR-NEXT: movw r30, r24
// AVR-NEXT: ld r24, Z
// AVR-NEXT: ldd r25, Z+1
// AVR-NEXT: ret
va_arg(ap)
}
#[unsafe(no_mangle)]
unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 {
// CHECK-LABEL: read_i32
//
// AVR: movw r30, r24
// AVR-NEXT: ld r24, Z
// AVR-NEXT: ldd r25, Z+1
// AVR-NEXT: movw r26, r24
// AVR-NEXT: adiw r26, 2
// AVR-NEXT: std Z+1, r27
// AVR-NEXT: st Z, r26
// AVR-NEXT: movw r20, r30
// AVR-NEXT: movw r18, r24
// AVR-NEXT: movw r30, r18
// AVR-NEXT: ld r22, Z
// AVR-NEXT: ldd r23, Z+1
// AVR-NEXT: adiw r24, 4
// AVR-NEXT: movw r30, r20
// AVR-NEXT: std Z+1, r25
// AVR-NEXT: st Z, r24
// AVR-NEXT: movw r30, r18
// AVR-NEXT: ldd r24, Z+2
// AVR-NEXT: ldd r25, Z+3
// AVR-NEXT: ret
va_arg(ap)
}
#[unsafe(no_mangle)]
unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 {
// CHECK-LABEL: read_i64
//
// AVR: push r14
// AVR-NEXT: push r15
// AVR-NEXT: push r16
// AVR-NEXT: push r17
// AVR-NEXT: movw r30, r24
// AVR-NEXT: ld r24, Z
// AVR-NEXT: ldd r25, Z+1
// AVR-NEXT: movw r26, r24
// AVR-NEXT: adiw r26, 2
// AVR-NEXT: std Z+1, r27
// AVR-NEXT: st Z, r26
// AVR-NEXT: movw r14, r30
// AVR-NEXT: movw r16, r24
// AVR-NEXT: movw r30, r16
// AVR-NEXT: ld r18, Z
// AVR-NEXT: ldd r19, Z+1
// AVR-NEXT: movw r26, r24
// AVR-NEXT: adiw r26, 4
// AVR-NEXT: movw r30, r14
// AVR-NEXT: std Z+1, r27
// AVR-NEXT: st Z, r26
// AVR-NEXT: movw r30, r16
// AVR-NEXT: ldd r20, Z+2
// AVR-NEXT: ldd r21, Z+3
// AVR-NEXT: movw r26, r24
// AVR-NEXT: adiw r26, 6
// AVR-NEXT: movw r30, r14
// AVR-NEXT: std Z+1, r27
// AVR-NEXT: st Z, r26
// AVR-NEXT: movw r30, r16
// AVR-NEXT: ldd r22, Z+4
// AVR-NEXT: ldd r23, Z+5
// AVR-NEXT: adiw r24, 8
// AVR-NEXT: movw r30, r14
// AVR-NEXT: std Z+1, r25
// AVR-NEXT: st Z, r24
// AVR-NEXT: movw r30, r16
// AVR-NEXT: ldd r24, Z+6
// AVR-NEXT: ldd r25, Z+7
// AVR-NEXT: pop r17
// AVR-NEXT: pop r16
// AVR-NEXT: pop r15
// AVR-NEXT: pop r14
// AVR-NEXT: ret
va_arg(ap)
}
#[unsafe(no_mangle)]
unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 {
// AVR: read_ptr = pm(read_i16)
va_arg(ap)
}
+1
View File
@@ -7,6 +7,7 @@
//@ [SPARC64] compile-flags: -Copt-level=3 --target sparc64-unknown-linux-gnu
//@ [SPARC64] needs-llvm-components: sparc
#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs, asm_experimental_arch)]
#![cfg_attr(target_arch = "sparc", feature(c_variadic_experimental_arch))]
#![no_core]
#![crate_type = "lib"]