-
-
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 | ||
|
|
@@ -34,6 +34,10 @@ use crate::marker::PhantomCovariantLifetime; | |
| // | ||
| // The Clang `BuiltinVaListKind` enumerates the `va_list` variations that Clang supports, | ||
| // and we mirror these here. | ||
| // | ||
| // For all current LLVM targets, `va_copy` lowers to `memcpy`. Hence the inner structs below all | ||
| // derive `Copy`. However, in the future we might want to support a target where `va_copy` | ||
| // allocates, or otherwise violates the requirements of `Copy`. Therefore `VaList` is only `Clone`. | ||
| crate::cfg_select! { | ||
| all( | ||
| target_arch = "aarch64", | ||
|
|
@@ -45,10 +49,12 @@ crate::cfg_select! { | |
| /// | ||
| /// See the [AArch64 Procedure Call Standard] for more details. | ||
| /// | ||
| /// `va_copy` is `memcpy`: <https://github.com/llvm/llvm-project/blob/5aee01a3df011e660f26660bc30a8c94a1651d8e/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp#L12682-L12700> | ||
| /// | ||
| /// [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, | ||
|
|
@@ -62,11 +68,13 @@ crate::cfg_select! { | |
| /// | ||
| /// See the [LLVM source] and [GCC header] for more details. | ||
| /// | ||
| /// `va_copy` is `memcpy`: <https://github.com/llvm/llvm-project/blob/5aee01a3df011e660f26660bc30a8c94a1651d8e/llvm/lib/Target/PowerPC/PPCISelLowering.cpp#L3755-L3764> | ||
| /// | ||
| /// [LLVM source]: | ||
| /// 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, | ||
|
|
@@ -81,10 +89,12 @@ crate::cfg_select! { | |
| /// | ||
| /// See the [S/390x ELF Application Binary Interface Supplement] for more details. | ||
| /// | ||
| /// `va_copy` is `memcpy`: <https://github.com/llvm/llvm-project/blob/5aee01a3df011e660f26660bc30a8c94a1651d8e/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp#L4457-L4472> | ||
| /// | ||
| /// [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, | ||
|
|
@@ -98,10 +108,13 @@ crate::cfg_select! { | |
| /// | ||
| /// See the [System V AMD64 ABI] for more details. | ||
| /// | ||
| /// `va_copy` is `memcpy`: <https://github.com/llvm/llvm-project/blob/5aee01a3df011e660f26660bc30a8c94a1651d8e/llvm/lib/Target/X86/X86ISelLowering.cpp#26319> | ||
| /// (github won't render that file, look for `SDValue LowerVACOPY`) | ||
| /// | ||
| /// [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, | ||
|
|
@@ -115,10 +128,12 @@ crate::cfg_select! { | |
| /// | ||
| /// See the [LLVM source] for more details. | ||
| /// | ||
| /// `va_copy` is `memcpy`: <https://github.com/llvm/llvm-project/blob/5aee01a3df011e660f26660bc30a8c94a1651d8e/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp#L1260> | ||
| /// | ||
| /// [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, | ||
|
|
@@ -132,10 +147,12 @@ crate::cfg_select! { | |
| /// | ||
| /// See the [LLVM source] for more details. On bare metal Hexagon uses an opaque pointer. | ||
| /// | ||
| /// `va_copy` is `memcpy`: <https://github.com/llvm/llvm-project/blob/5aee01a3df011e660f26660bc30a8c94a1651d8e/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp#L1087-L1102> | ||
| /// | ||
| /// [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, | ||
|
|
@@ -156,8 +173,10 @@ crate::cfg_select! { | |
| // That pointer is probably just the next variadic argument on the caller's stack. | ||
| _ => { | ||
| /// Basic implementation of a `va_list`. | ||
| /// | ||
| /// `va_copy` is `memcpy`: <https://github.com/llvm/llvm-project/blob/87e8e7d8f0db53060ef2f6ef4ab612fc0f2b4490/llvm/lib/Transforms/IPO/ExpandVariadics.cpp#L127-L129> | ||
| #[repr(transparent)] | ||
| #[derive(Debug)] | ||
| #[derive(Debug, Clone, Copy)] | ||
| struct VaListInner { | ||
| ptr: *const c_void, | ||
| } | ||
|
|
@@ -179,6 +198,31 @@ 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). For the same reason we use an intrinsic | ||
| // to do the copying: the fact that on all current targets, this is just `memcpy`, is an implementation | ||
| // detail. The intrinsic lets Miri catch UB from code incorrectly relying on that implementation detail. | ||
| 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) } | ||
| } | ||
folkertdev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| mod sealed { | ||
| pub trait Sealed {} | ||
|
|
||
|
|
@@ -253,26 +297,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 _: () = { | ||
|
|
||
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
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.
That's just a declaration, how does the link confirm that this can be copied?
Is it worth putting these links in the code for future reference?
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.
ah sorry that should link to https://github.com/llvm/llvm-project/blob/5aee01a3df011e660f26660bc30a8c94a1651d8e/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp#L12682-L12700, where the implementation is just
memcpy.The links are kind of unwieldy, but I can put them in.