-
-
Notifications
You must be signed in to change notification settings - Fork 14.4k
c_variadic: impl va_copy and va_end as Rust intrinsics
#150436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,7 +5,7 @@ | |||||||||||||||||||||||||||||||||
| #[cfg(not(target_arch = "xtensa"))] | ||||||||||||||||||||||||||||||||||
| use crate::ffi::c_void; | ||||||||||||||||||||||||||||||||||
| use crate::fmt; | ||||||||||||||||||||||||||||||||||
| use crate::intrinsics::{va_arg, va_copy}; | ||||||||||||||||||||||||||||||||||
| use crate::intrinsics::{va_arg, va_copy, va_end}; | ||||||||||||||||||||||||||||||||||
| use crate::marker::PhantomCovariantLifetime; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // There are currently three flavors of how a C `va_list` is implemented for | ||||||||||||||||||||||||||||||||||
|
|
@@ -48,7 +48,7 @@ crate::cfg_select! { | |||||||||||||||||||||||||||||||||
| /// [AArch64 Procedure Call Standard]: | ||||||||||||||||||||||||||||||||||
| /// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf | ||||||||||||||||||||||||||||||||||
| #[repr(C)] | ||||||||||||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||||||||||||
| #[derive(Debug, Clone, Copy)] | ||||||||||||||||||||||||||||||||||
| struct VaListInner { | ||||||||||||||||||||||||||||||||||
| stack: *const c_void, | ||||||||||||||||||||||||||||||||||
| gr_top: *const c_void, | ||||||||||||||||||||||||||||||||||
|
|
@@ -66,7 +66,7 @@ crate::cfg_select! { | |||||||||||||||||||||||||||||||||
| /// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/PowerPC/PPCISelLowering.cpp#L4089-L4111 | ||||||||||||||||||||||||||||||||||
| /// [GCC header]: https://web.mit.edu/darwin/src/modules/gcc/gcc/ginclude/va-ppc.h | ||||||||||||||||||||||||||||||||||
| #[repr(C)] | ||||||||||||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||||||||||||
| #[derive(Debug, Clone, Copy)] | ||||||||||||||||||||||||||||||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||||||||||||||
| #[rustc_pass_indirectly_in_non_rustic_abis] | ||||||||||||||||||||||||||||||||||
| struct VaListInner { | ||||||||||||||||||||||||||||||||||
| gpr: u8, | ||||||||||||||||||||||||||||||||||
|
|
@@ -84,7 +84,7 @@ crate::cfg_select! { | |||||||||||||||||||||||||||||||||
| /// [S/390x ELF Application Binary Interface Supplement]: | ||||||||||||||||||||||||||||||||||
| /// https://docs.google.com/gview?embedded=true&url=https://github.com/IBM/s390x-abi/releases/download/v1.7/lzsabi_s390x.pdf | ||||||||||||||||||||||||||||||||||
| #[repr(C)] | ||||||||||||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||||||||||||
| #[derive(Debug, Clone, Copy)] | ||||||||||||||||||||||||||||||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||||||||||||||
| #[rustc_pass_indirectly_in_non_rustic_abis] | ||||||||||||||||||||||||||||||||||
| struct VaListInner { | ||||||||||||||||||||||||||||||||||
| gpr: i64, | ||||||||||||||||||||||||||||||||||
|
|
@@ -101,7 +101,7 @@ crate::cfg_select! { | |||||||||||||||||||||||||||||||||
| /// [System V AMD64 ABI]: | ||||||||||||||||||||||||||||||||||
| /// https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf | ||||||||||||||||||||||||||||||||||
| #[repr(C)] | ||||||||||||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||||||||||||
| #[derive(Debug, Clone, Copy)] | ||||||||||||||||||||||||||||||||||
| #[rustc_pass_indirectly_in_non_rustic_abis] | ||||||||||||||||||||||||||||||||||
| struct VaListInner { | ||||||||||||||||||||||||||||||||||
| gp_offset: i32, | ||||||||||||||||||||||||||||||||||
|
|
@@ -118,7 +118,7 @@ crate::cfg_select! { | |||||||||||||||||||||||||||||||||
| /// [LLVM source]: | ||||||||||||||||||||||||||||||||||
| /// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp#L1211-L1215 | ||||||||||||||||||||||||||||||||||
| #[repr(C)] | ||||||||||||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||||||||||||
| #[derive(Debug, Clone, Copy)] | ||||||||||||||||||||||||||||||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||||||||||||||
| #[rustc_pass_indirectly_in_non_rustic_abis] | ||||||||||||||||||||||||||||||||||
| struct VaListInner { | ||||||||||||||||||||||||||||||||||
| stk: *const i32, | ||||||||||||||||||||||||||||||||||
|
|
@@ -135,7 +135,7 @@ crate::cfg_select! { | |||||||||||||||||||||||||||||||||
| /// [LLVM source]: | ||||||||||||||||||||||||||||||||||
| /// https://github.com/llvm/llvm-project/blob/0cdc1b6dd4a870fc41d4b15ad97e0001882aba58/clang/lib/CodeGen/Targets/Hexagon.cpp#L407-L417 | ||||||||||||||||||||||||||||||||||
| #[repr(C)] | ||||||||||||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||||||||||||
| #[derive(Debug, Clone, Copy)] | ||||||||||||||||||||||||||||||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||||||||||||||
| #[rustc_pass_indirectly_in_non_rustic_abis] | ||||||||||||||||||||||||||||||||||
| struct VaListInner { | ||||||||||||||||||||||||||||||||||
| __current_saved_reg_area_pointer: *const c_void, | ||||||||||||||||||||||||||||||||||
|
|
@@ -157,7 +157,7 @@ crate::cfg_select! { | |||||||||||||||||||||||||||||||||
| _ => { | ||||||||||||||||||||||||||||||||||
| /// Basic implementation of a `va_list`. | ||||||||||||||||||||||||||||||||||
| #[repr(transparent)] | ||||||||||||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||||||||||||
| #[derive(Debug, Clone, Copy)] | ||||||||||||||||||||||||||||||||||
| struct VaListInner { | ||||||||||||||||||||||||||||||||||
| ptr: *const c_void, | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
158
to
163
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||||||||||||||
|
|
@@ -179,6 +179,34 @@ impl fmt::Debug for VaList<'_> { | |||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| impl VaList<'_> { | ||||||||||||||||||||||||||||||||||
| // Helper used in the implementation of the `va_copy` intrinsic. | ||||||||||||||||||||||||||||||||||
| pub(crate) fn duplicate(&self) -> Self { | ||||||||||||||||||||||||||||||||||
| Self { inner: self.inner.clone(), _marker: self._marker } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| impl Clone for VaList<'_> { | ||||||||||||||||||||||||||||||||||
| #[inline] | ||||||||||||||||||||||||||||||||||
| fn clone(&self) -> Self { | ||||||||||||||||||||||||||||||||||
| // We only implement Clone and not Copy because some future target might not be able to | ||||||||||||||||||||||||||||||||||
| // implement Copy (e.g. because it allocates). | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // We still use a `va_copy` intrinsic to provide a hook for const evaluation. The hook is | ||||||||||||||||||||||||||||||||||
| // used to report UB when a variable argument list is duplicated with a manual `memcpy`. | ||||||||||||||||||||||||||||||||||
| // While that works in practice for all current targets, we want to be able to support | ||||||||||||||||||||||||||||||||||
| // targets in the future where that is not the case. | ||||||||||||||||||||||||||||||||||
| va_copy(self) | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| impl<'f> Drop for VaList<'f> { | ||||||||||||||||||||||||||||||||||
| fn drop(&mut self) { | ||||||||||||||||||||||||||||||||||
| // SAFETY: this variable argument list is being dropped, so won't be read from again. | ||||||||||||||||||||||||||||||||||
| unsafe { va_end(self) } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is fun, but I don't think it is relevant for this comment. The same-scope restriction is a C implementation detail (portable macro assembler and all that), rust (and modern C implementations) don't have that problem.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm satisfied with it being part of the PR history, then. I just wanted to make sure it was incredibly, completely, totally dead. :^) |
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| mod sealed { | ||||||||||||||||||||||||||||||||||
| pub trait Sealed {} | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
@@ -253,26 +281,6 @@ impl<'f> VaList<'f> { | |||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| impl<'f> Clone for VaList<'f> { | ||||||||||||||||||||||||||||||||||
| #[inline] | ||||||||||||||||||||||||||||||||||
| fn clone(&self) -> Self { | ||||||||||||||||||||||||||||||||||
| let mut dest = crate::mem::MaybeUninit::uninit(); | ||||||||||||||||||||||||||||||||||
| // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal. | ||||||||||||||||||||||||||||||||||
| unsafe { | ||||||||||||||||||||||||||||||||||
| va_copy(dest.as_mut_ptr(), self); | ||||||||||||||||||||||||||||||||||
| dest.assume_init() | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| impl<'f> Drop for VaList<'f> { | ||||||||||||||||||||||||||||||||||
| fn drop(&mut self) { | ||||||||||||||||||||||||||||||||||
| // Rust requires that not calling `va_end` on a `va_list` does not cause undefined behaviour | ||||||||||||||||||||||||||||||||||
| // (as it is safe to leak values). As `va_end` is a no-op on all current LLVM targets, this | ||||||||||||||||||||||||||||||||||
| // destructor is empty. | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Checks (via an assert in `compiler/rustc_ty_utils/src/abi.rs`) that the C ABI for the current | ||||||||||||||||||||||||||||||||||
| // target correctly implements `rustc_pass_indirectly_in_non_rustic_abis`. | ||||||||||||||||||||||||||||||||||
| const _: () = { | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3450,19 +3450,6 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize | |
| ) | ||
| } | ||
|
|
||
| /// Copies the current location of arglist `src` to the arglist `dst`. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// You must check the following invariants before you call this function: | ||
| /// | ||
| /// - `dest` must be non-null and point to valid, writable memory. | ||
| /// - `dest` must not alias `src`. | ||
| /// | ||
| #[rustc_intrinsic] | ||
| #[rustc_nounwind] | ||
| pub unsafe fn va_copy<'f>(dest: *mut VaList<'f>, src: &VaList<'f>); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The docs for Also, seems like it is now newly legal to call |
||
|
|
||
| /// Loads an argument of type `T` from the `va_list` `ap` and increment the | ||
| /// argument `ap` points to. | ||
| /// | ||
|
|
@@ -3481,12 +3468,32 @@ pub unsafe fn va_copy<'f>(dest: *mut VaList<'f>, src: &VaList<'f>); | |
| #[rustc_nounwind] | ||
| pub unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaList<'_>) -> T; | ||
|
|
||
| /// Destroy the arglist `ap` after initialization with `va_start` or `va_copy`. | ||
| /// Duplicates a variable argument list. The returned list is initially at the same position as | ||
| /// the one in `src`, but can be advanced independently. | ||
| #[rustc_intrinsic] | ||
| #[rustc_nounwind] | ||
| pub fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The more I think about it, maybe this function should now have different name? It no longer is
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It still is an implementation, in effect, of the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. essentially, yes. Perhaps I should push the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure! |
||
| // NOTE: this intrinsic exists only as a hook for constant evaluation, and is used to detect UB | ||
| // when `VaList` is used incorrectly. Codegen backends should not have custom behavior for this | ||
| // intrinsic, they should always use this fallback implementation. | ||
| src.duplicate() | ||
| } | ||
|
|
||
| /// Destroy the variable argument list `ap` after initialization with `va_start` (part of the | ||
| /// desugaring of `...`) or `va_copy`. | ||
| /// | ||
| /// Code generation backends should not provide a custom implementation for this intrinsic. This | ||
| /// intrinsic *does not* map to the LLVM `va_end` intrinsic. | ||
| /// | ||
| /// This function is a no-op on all current targets, but used as a hook for const evaluation to | ||
| /// detect UB when a variable argument list is used incorrectly. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// `ap` must not be used to access variable arguments after this call. | ||
| /// | ||
| #[rustc_intrinsic] | ||
| #[rustc_nounwind] | ||
| pub unsafe fn va_end(ap: &mut VaList<'_>); | ||
| pub unsafe fn va_end(ap: &mut VaList<'_>) { | ||
| /* deliberately does nothing */ | ||
| } | ||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| //@ run-pass | ||
| //@ ignore-backends: gcc | ||
| #![feature(c_variadic)] | ||
|
|
||
| // Test the behavior of `VaList::clone`. In C a `va_list` is duplicated using `va_copy`, but the | ||
| // rust api just uses `Clone`. This should create a completely independent cursor into the | ||
| // variable argument list: advancing the original has no effect on the copy and vice versa. | ||
|
|
||
| fn main() { | ||
| unsafe { variadic(1, 2, 3) } | ||
| } | ||
|
|
||
| unsafe extern "C" fn variadic(mut ap1: ...) { | ||
| let mut ap2 = ap1.clone(); | ||
|
|
||
| assert_eq!(ap1.arg::<i32>(), 1); | ||
| assert_eq!(ap2.arg::<i32>(), 1); | ||
|
|
||
| assert_eq!(ap2.arg::<i32>(), 2); | ||
| assert_eq!(ap1.arg::<i32>(), 2); | ||
|
|
||
| drop(ap1); | ||
| assert_eq!(ap2.arg::<i32>(), 3); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/llvm/llvm-project/blob/5aee01a3df011e660f26660bc30a8c94a1651d8e/llvm/lib/Target/AArch64/AArch64ISelLowering.h#L709