Skip to content

Commit 19e7342

Browse files
committed
c_variadic: use Clone instead of LLVM va_copy
1 parent 0d162b2 commit 19e7342

9 files changed

Lines changed: 82 additions & 85 deletions

File tree

compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1506,7 +1506,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
15061506
}
15071507

15081508
// FIXME implement variadics in cranelift
1509-
sym::va_copy | sym::va_arg | sym::va_end => {
1509+
sym::va_arg | sym::va_end => {
15101510
fx.tcx.dcx().span_fatal(
15111511
source_info.span,
15121512
"Defining variadic functions is not yet supported by Cranelift",

compiler/rustc_codegen_gcc/src/intrinsic/mod.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -391,9 +391,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
391391
sym::breakpoint => {
392392
unimplemented!();
393393
}
394-
sym::va_copy => {
395-
unimplemented!();
396-
}
397394
sym::va_arg => {
398395
unimplemented!();
399396
}

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -269,14 +269,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
269269
return Ok(());
270270
}
271271
sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[], &[]),
272-
sym::va_copy => {
273-
let dest = args[0].immediate();
274-
self.call_intrinsic(
275-
"llvm.va_copy",
276-
&[self.val_ty(dest)],
277-
&[dest, args[1].immediate()],
278-
)
279-
}
280272
sym::va_arg => {
281273
match result.layout.backend_repr {
282274
BackendRepr::Scalar(scalar) => {

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
214214
| sym::type_id_eq
215215
| sym::type_name
216216
| sym::ub_checks
217+
| sym::va_copy
217218
| sym::variant_count
218219
| sym::vtable_for
219220
| sym::wrapping_add
@@ -617,14 +618,13 @@ pub(crate) fn check_intrinsic_type(
617618
)
618619
}
619620

620-
sym::va_start | sym::va_end => {
621-
(0, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], tcx.types.unit)
622-
}
623-
624621
sym::va_copy => {
625622
let (va_list_ref_ty, va_list_ty) = mk_va_list_ty(hir::Mutability::Not);
626-
let va_list_ptr_ty = Ty::new_mut_ptr(tcx, va_list_ty);
627-
(0, 0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.types.unit)
623+
(0, 0, vec![va_list_ref_ty], va_list_ty)
624+
}
625+
626+
sym::va_start | sym::va_end => {
627+
(0, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], tcx.types.unit)
628628
}
629629

630630
sym::va_arg => (1, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], param(0)),

library/core/src/ffi/va_list.rs

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ crate::cfg_select! {
4848
/// [AArch64 Procedure Call Standard]:
4949
/// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
5050
#[repr(C)]
51-
#[derive(Debug)]
51+
#[derive(Debug, Clone, Copy)]
5252
struct VaListInner {
5353
stack: *const c_void,
5454
gr_top: *const c_void,
@@ -66,7 +66,7 @@ crate::cfg_select! {
6666
/// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/PowerPC/PPCISelLowering.cpp#L4089-L4111
6767
/// [GCC header]: https://web.mit.edu/darwin/src/modules/gcc/gcc/ginclude/va-ppc.h
6868
#[repr(C)]
69-
#[derive(Debug)]
69+
#[derive(Debug, Clone, Copy)]
7070
#[rustc_pass_indirectly_in_non_rustic_abis]
7171
struct VaListInner {
7272
gpr: u8,
@@ -84,7 +84,7 @@ crate::cfg_select! {
8484
/// [S/390x ELF Application Binary Interface Supplement]:
8585
/// https://docs.google.com/gview?embedded=true&url=https://github.com/IBM/s390x-abi/releases/download/v1.7/lzsabi_s390x.pdf
8686
#[repr(C)]
87-
#[derive(Debug)]
87+
#[derive(Debug, Clone, Copy)]
8888
#[rustc_pass_indirectly_in_non_rustic_abis]
8989
struct VaListInner {
9090
gpr: i64,
@@ -101,7 +101,7 @@ crate::cfg_select! {
101101
/// [System V AMD64 ABI]:
102102
/// https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf
103103
#[repr(C)]
104-
#[derive(Debug)]
104+
#[derive(Debug, Clone, Copy)]
105105
#[rustc_pass_indirectly_in_non_rustic_abis]
106106
struct VaListInner {
107107
gp_offset: i32,
@@ -118,7 +118,7 @@ crate::cfg_select! {
118118
/// [LLVM source]:
119119
/// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp#L1211-L1215
120120
#[repr(C)]
121-
#[derive(Debug)]
121+
#[derive(Debug, Clone, Copy)]
122122
#[rustc_pass_indirectly_in_non_rustic_abis]
123123
struct VaListInner {
124124
stk: *const i32,
@@ -135,7 +135,7 @@ crate::cfg_select! {
135135
/// [LLVM source]:
136136
/// https://github.com/llvm/llvm-project/blob/0cdc1b6dd4a870fc41d4b15ad97e0001882aba58/clang/lib/CodeGen/Targets/Hexagon.cpp#L407-L417
137137
#[repr(C)]
138-
#[derive(Debug)]
138+
#[derive(Debug, Clone, Copy)]
139139
#[rustc_pass_indirectly_in_non_rustic_abis]
140140
struct VaListInner {
141141
__current_saved_reg_area_pointer: *const c_void,
@@ -157,7 +157,7 @@ crate::cfg_select! {
157157
_ => {
158158
/// Basic implementation of a `va_list`.
159159
#[repr(transparent)]
160-
#[derive(Debug)]
160+
#[derive(Debug, Clone, Copy)]
161161
struct VaListInner {
162162
ptr: *const c_void,
163163
}
@@ -179,6 +179,39 @@ impl fmt::Debug for VaList<'_> {
179179
}
180180
}
181181

182+
impl VaList<'_> {
183+
// Helper used in the implementation of the `va_copy` intrinsic.
184+
pub(crate) fn duplicate(&self) -> Self {
185+
Self { inner: self.inner.clone(), _marker: self.__marker }
186+
}
187+
}
188+
189+
impl Clone for VaList<'_> {
190+
#[inline]
191+
fn clone(&self) -> Self {
192+
// We only implement Clone and not Copy because some future target might not be able to
193+
// implement Copy (e.g. because it allocates).
194+
195+
// We still use a `va_copy` intrinsic to provide a hook for const evaluation. The hook is
196+
// used to report UB when a variable argument list is duplicated with a manual `memcpy`.
197+
// While that works in practice for all current targets, we want to be able to support
198+
// targets in the future where that is not the case.
199+
va_copy(self)
200+
}
201+
}
202+
203+
impl Drop for VaList<'_> {
204+
fn drop(&mut self) {
205+
// For all current LLVM targets `va_end` is a no-op.
206+
//
207+
// We implement `Drop` here because some future target might need to actually run
208+
// destructors (e.g. to deallocate).
209+
//
210+
// Rust requires that not calling `va_end` on a `va_list` does not cause undefined
211+
// behaviour: it is safe to leak values.
212+
}
213+
}
214+
182215
mod sealed {
183216
pub trait Sealed {}
184217

@@ -253,26 +286,6 @@ impl<'f> VaList<'f> {
253286
}
254287
}
255288

256-
impl<'f> Clone for VaList<'f> {
257-
#[inline]
258-
fn clone(&self) -> Self {
259-
let mut dest = crate::mem::MaybeUninit::uninit();
260-
// SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal.
261-
unsafe {
262-
va_copy(dest.as_mut_ptr(), self);
263-
dest.assume_init()
264-
}
265-
}
266-
}
267-
268-
impl<'f> Drop for VaList<'f> {
269-
fn drop(&mut self) {
270-
// Rust requires that not calling `va_end` on a `va_list` does not cause undefined behaviour
271-
// (as it is safe to leak values). As `va_end` is a no-op on all current LLVM targets, this
272-
// destructor is empty.
273-
}
274-
}
275-
276289
// Checks (via an assert in `compiler/rustc_ty_utils/src/abi.rs`) that the C ABI for the current
277290
// target correctly implements `rustc_pass_indirectly_in_non_rustic_abis`.
278291
const _: () = {

library/core/src/intrinsics/mod.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3445,19 +3445,6 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
34453445
)
34463446
}
34473447

3448-
/// Copies the current location of arglist `src` to the arglist `dst`.
3449-
///
3450-
/// # Safety
3451-
///
3452-
/// You must check the following invariants before you call this function:
3453-
///
3454-
/// - `dest` must be non-null and point to valid, writable memory.
3455-
/// - `dest` must not alias `src`.
3456-
///
3457-
#[rustc_intrinsic]
3458-
#[rustc_nounwind]
3459-
pub unsafe fn va_copy<'f>(dest: *mut VaList<'f>, src: &VaList<'f>);
3460-
34613448
/// Loads an argument of type `T` from the `va_list` `ap` and increment the
34623449
/// argument `ap` points to.
34633450
///
@@ -3476,6 +3463,17 @@ pub unsafe fn va_copy<'f>(dest: *mut VaList<'f>, src: &VaList<'f>);
34763463
#[rustc_nounwind]
34773464
pub unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaList<'_>) -> T;
34783465

3466+
/// Duplicates a variable argument list. The returned list is initially at the same position as
3467+
/// the one in `src`, but can be advanced independently.
3468+
#[rustc_intrinsic]
3469+
#[rustc_nounwind]
3470+
pub fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> {
3471+
// NOTE: this intrinsic exists only as a hook for constant evaluation, and is used to detect UB
3472+
// when `VaList` is used incorrectly. Codegen backends should not have custom behavior for this
3473+
// intrinsic, they should always use this fallback implementation.
3474+
src.duplicate()
3475+
}
3476+
34793477
/// Destroy the arglist `ap` after initialization with `va_start` or `va_copy`.
34803478
///
34813479
/// # Safety

tests/codegen-llvm/cffi/c-variadic-copy.rs

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

tests/codegen-llvm/cffi/c-variadic-opt.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,3 @@ pub unsafe extern "C" fn c_variadic_no_use(fmt: *const i8, mut ap: ...) -> i32 {
1717
vprintf(fmt, ap)
1818
// CHECK: call void @llvm.va_end
1919
}
20-
21-
// Check that `VaList::clone` gets inlined into a direct call to `llvm.va_copy`
22-
#[no_mangle]
23-
pub unsafe extern "C" fn c_variadic_clone(fmt: *const i8, mut ap: ...) -> i32 {
24-
// CHECK: call void @llvm.va_start
25-
let mut ap2 = ap.clone();
26-
// CHECK: call void @llvm.va_copy
27-
let res = vprintf(fmt, ap2);
28-
res
29-
// CHECK: call void @llvm.va_end
30-
}

tests/ui/c-variadic/copy.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ run-pass
2+
//@ ignore-backends: gcc
3+
#![feature(c_variadic)]
4+
5+
// Test the behavior of `VaList::clone`. In C a `va_list` is duplicated using `va_copy`, but the
6+
// rust api just uses `Clone`. This should create a completely independent cursor into the
7+
// variable argument list: advancing the original has no effect on the copy and vice versa.
8+
9+
fn main() {
10+
unsafe { variadic(1, 2, 3) }
11+
}
12+
13+
unsafe extern "C" fn variadic(mut ap1: ...) {
14+
let mut ap2 = ap1.clone();
15+
16+
assert_eq!(ap1.arg::<i32>(), 1);
17+
assert_eq!(ap2.arg::<i32>(), 1);
18+
19+
assert_eq!(ap2.arg::<i32>(), 2);
20+
assert_eq!(ap1.arg::<i32>(), 2);
21+
22+
drop(ap1);
23+
assert_eq!(ap2.arg::<i32>(), 3);
24+
}

0 commit comments

Comments
 (0)