44
55#[ cfg( not( target_arch = "xtensa" ) ) ]
66use crate :: ffi:: c_void;
7- #[ allow( unused_imports) ]
87use crate :: fmt;
9- use crate :: intrinsics:: { va_arg, va_copy, va_end} ;
10- use crate :: marker:: { PhantomData , PhantomInvariantLifetime } ;
11- use crate :: ops:: { Deref , DerefMut } ;
8+ use crate :: intrinsics:: { va_arg, va_copy} ;
9+ use crate :: marker:: PhantomCovariantLifetime ;
1210
13- // The name is WIP, using `VaListImpl` for now.
11+ // There are currently three flavors of how a C `va_list` is implemented for
12+ // targets that Rust supports:
1413//
15- // Most targets explicitly specify the layout of `va_list`, this layout is matched here.
14+ // - `va_list` is an opaque pointer
15+ // - `va_list` is a struct
16+ // - `va_list` is a single-element array, containing a struct
17+ //
18+ // The opaque pointer approach is the simplest to implement: the pointer just
19+ // points to an array of arguments on the caller's stack.
20+ //
21+ // The struct and single-element array variants are more complex, but
22+ // potentially more efficient because the additional state makes it
23+ // possible to pass variadic arguments via registers.
24+ //
25+ // The Rust `VaList` type is ABI-compatible with the C `va_list`.
26+ // The struct and pointer cases straightforwardly map to their Rust equivalents,
27+ // but the single-element array case is special: in C, this type is subject to
28+ // array-to-pointer decay.
29+ //
30+ // The `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute is used to match
31+ // the pointer decay behavior in Rust, while otherwise matching Rust semantics.
32+ // This attribute ensures that the compiler uses the correct ABI for functions
33+ // like `extern "C" fn takes_va_list(va: VaList<'_>)` by passing `va` indirectly.
1634crate :: cfg_select! {
1735 all(
1836 target_arch = "aarch64" ,
1937 not( target_vendor = "apple" ) ,
2038 not( target_os = "uefi" ) ,
2139 not( windows) ,
2240 ) => {
23- /// AArch64 ABI implementation of a `va_list`. See the
24- /// [AArch64 Procedure Call Standard] for more details.
41+ /// AArch64 ABI implementation of a `va_list`.
42+ ///
43+ /// See the [AArch64 Procedure Call Standard] for more details.
2544 ///
2645 /// [AArch64 Procedure Call Standard]:
2746 /// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
2847 #[ repr( C ) ]
2948 #[ derive( Debug ) ]
30- #[ lang = "va_list" ]
31- pub struct VaListImpl <' f> {
32- stack: * mut c_void,
33- gr_top: * mut c_void,
34- vr_top: * mut c_void,
49+ struct VaListInner {
50+ stack: * const c_void,
51+ gr_top: * const c_void,
52+ vr_top: * const c_void,
3553 gr_offs: i32 ,
3654 vr_offs: i32 ,
37- _marker: PhantomInvariantLifetime <' f>,
3855 }
3956 }
4057 all( target_arch = "powerpc" , not( target_os = "uefi" ) , not( windows) ) => {
4158 /// PowerPC ABI implementation of a `va_list`.
59+ ///
60+ /// See the [LLVM source] and [GCC header] for more details.
61+ ///
62+ /// [LLVM source]:
63+ /// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/PowerPC/PPCISelLowering.cpp#L4089-L4111
64+ /// [GCC header]: https://web.mit.edu/darwin/src/modules/gcc/gcc/ginclude/va-ppc.h
4265 #[ repr( C ) ]
4366 #[ derive( Debug ) ]
44- #[ lang = "va_list" ]
45- pub struct VaListImpl < ' f> {
67+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
68+ struct VaListInner {
4669 gpr: u8 ,
4770 fpr: u8 ,
4871 reserved: u16 ,
49- overflow_arg_area: * mut c_void,
50- reg_save_area: * mut c_void,
51- _marker: PhantomInvariantLifetime <' f>,
72+ overflow_arg_area: * const c_void,
73+ reg_save_area: * const c_void,
5274 }
5375 }
5476 target_arch = "s390x" => {
5577 /// s390x ABI implementation of a `va_list`.
78+ ///
79+ /// See the [S/390x ELF Application Binary Interface Supplement] for more details.
80+ ///
81+ /// [S/390x ELF Application Binary Interface Supplement]:
82+ /// https://docs.google.com/gview?embedded=true&url=https://github.com/IBM/s390x-abi/releases/download/v1.7/lzsabi_s390x.pdf
5683 #[ repr( C ) ]
5784 #[ derive( Debug ) ]
58- #[ lang = "va_list" ]
59- pub struct VaListImpl < ' f> {
85+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
86+ struct VaListInner {
6087 gpr: i64 ,
6188 fpr: i64 ,
62- overflow_arg_area: * mut c_void,
63- reg_save_area: * mut c_void,
64- _marker: PhantomInvariantLifetime <' f>,
89+ overflow_arg_area: * const c_void,
90+ reg_save_area: * const c_void,
6591 }
6692 }
6793 all( target_arch = "x86_64" , not( target_os = "uefi" ) , not( windows) ) => {
68- /// x86_64 ABI implementation of a `va_list`.
94+ /// x86_64 System V ABI implementation of a `va_list`.
95+ ///
96+ /// See the [System V AMD64 ABI] for more details.
97+ ///
98+ /// [System V AMD64 ABI]:
99+ /// https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf
69100 #[ repr( C ) ]
70101 #[ derive( Debug ) ]
71- #[ lang = "va_list" ]
72- pub struct VaListImpl < ' f> {
102+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
103+ struct VaListInner {
73104 gp_offset: i32 ,
74105 fp_offset: i32 ,
75- overflow_arg_area: * mut c_void,
76- reg_save_area: * mut c_void,
77- _marker: PhantomInvariantLifetime <' f>,
106+ overflow_arg_area: * const c_void,
107+ reg_save_area: * const c_void,
78108 }
79109 }
80110 target_arch = "xtensa" => {
81111 /// Xtensa ABI implementation of a `va_list`.
112+ ///
113+ /// See the [LLVM source] for more details.
114+ ///
115+ /// [LLVM source]:
116+ /// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp#L1211-L1215
82117 #[ repr( C ) ]
83118 #[ derive( Debug ) ]
84- #[ lang = "va_list" ]
85- pub struct VaListImpl < ' f> {
86- stk: * mut i32 ,
87- reg: * mut i32 ,
119+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
120+ struct VaListInner {
121+ stk: * const i32 ,
122+ reg: * const i32 ,
88123 ndx: i32 ,
89- _marker: PhantomInvariantLifetime <' f>,
90124 }
91125 }
92126
93127 // The fallback implementation, used for:
94128 //
95129 // - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599)
96130 // - windows
131+ // - powerpc64 & powerpc64le
97132 // - uefi
98- // - any other target for which we don't specify the `VaListImpl ` above
133+ // - any other target for which we don't specify the `VaListInner ` above
99134 //
100135 // In this implementation the `va_list` type is just an alias for an opaque pointer.
101136 // That pointer is probably just the next variadic argument on the caller's stack.
102137 _ => {
103138 /// Basic implementation of a `va_list`.
104139 #[ repr( transparent) ]
105- #[ lang = "va_list" ]
106- pub struct VaListImpl <' f> {
107- ptr: * mut c_void,
108-
109- // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to
110- // the region of the function it's defined in
111- _marker: PhantomInvariantLifetime <' f>,
112- }
113-
114- impl <' f> fmt:: Debug for VaListImpl <' f> {
115- fn fmt( & self , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
116- write!( f, "va_list* {:p}" , self . ptr)
117- }
118- }
119- }
120- }
121-
122- crate :: cfg_select! {
123- all(
124- any(
125- target_arch = "aarch64" ,
126- target_arch = "powerpc" ,
127- target_arch = "s390x" ,
128- target_arch = "x86_64"
129- ) ,
130- not( target_arch = "xtensa" ) ,
131- any( not( target_arch = "aarch64" ) , not( target_vendor = "apple" ) ) ,
132- not( target_family = "wasm" ) ,
133- not( target_os = "uefi" ) ,
134- not( windows) ,
135- ) => {
136- /// A wrapper for a `va_list`
137- #[ repr( transparent) ]
138- #[ derive( Debug ) ]
139- pub struct VaList <' a, ' f: ' a> {
140- inner: & ' a mut VaListImpl <' f>,
141- _marker: PhantomData <& ' a mut VaListImpl <' f>>,
142- }
143-
144-
145- impl <' f> VaListImpl <' f> {
146- /// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`.
147- #[ inline]
148- pub fn as_va_list<' a>( & ' a mut self ) -> VaList <' a, ' f> {
149- VaList { inner: self , _marker: PhantomData }
150- }
151- }
152- }
153-
154- _ => {
155- /// A wrapper for a `va_list`
156- #[ repr( transparent) ]
157140 #[ derive( Debug ) ]
158- pub struct VaList <' a, ' f: ' a> {
159- inner: VaListImpl <' f>,
160- _marker: PhantomData <& ' a mut VaListImpl <' f>>,
161- }
162-
163- impl <' f> VaListImpl <' f> {
164- /// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`.
165- #[ inline]
166- pub fn as_va_list<' a>( & ' a mut self ) -> VaList <' a, ' f> {
167- VaList { inner: VaListImpl { ..* self } , _marker: PhantomData }
168- }
141+ struct VaListInner {
142+ ptr: * const c_void,
169143 }
170144 }
171145}
172146
173- impl < ' a , ' f : ' a > Deref for VaList < ' a , ' f > {
174- type Target = VaListImpl < ' f > ;
175-
176- #[ inline]
177- fn deref ( & self ) -> & VaListImpl < ' f > {
178- & self . inner
179- }
147+ /// A variable argument list, equivalent to `va_list` in C.
148+ #[ repr( transparent) ]
149+ #[ lang = "va_list" ]
150+ pub struct VaList < ' a > {
151+ inner : VaListInner ,
152+ _marker : PhantomCovariantLifetime < ' a > ,
180153}
181154
182- impl < ' a , ' f : ' a > DerefMut for VaList < ' a , ' f > {
183- # [ inline ]
184- fn deref_mut ( & mut self ) -> & mut VaListImpl < ' f > {
185- & mut self . inner
155+ impl fmt :: Debug for VaList < ' _ > {
156+ fn fmt ( & self , f : & mut fmt :: Formatter < ' _ > ) -> fmt :: Result {
157+ // No need to include `_marker` in debug output.
158+ f . debug_tuple ( "VaList" ) . field ( & self . inner ) . finish ( )
186159 }
187160}
188161
@@ -203,7 +176,7 @@ mod sealed {
203176 impl < T > Sealed for * const T { }
204177}
205178
206- /// Types that are valid to read using [`VaListImpl ::arg`].
179+ /// Types that are valid to read using [`VaList ::arg`].
207180///
208181/// # Safety
209182///
@@ -238,7 +211,7 @@ unsafe impl VaArgSafe for f64 {}
238211unsafe impl < T > VaArgSafe for * mut T { }
239212unsafe impl < T > VaArgSafe for * const T { }
240213
241- impl < ' f > VaListImpl < ' f > {
214+ impl < ' f > VaList < ' f > {
242215 /// Advance to and read the next variable argument.
243216 ///
244217 /// # Safety
@@ -258,46 +231,25 @@ impl<'f> VaListImpl<'f> {
258231 // SAFETY: the caller must uphold the safety contract for `va_arg`.
259232 unsafe { va_arg ( self ) }
260233 }
261-
262- /// Copies the `va_list` at the current location.
263- pub unsafe fn with_copy < F , R > ( & self , f : F ) -> R
264- where
265- F : for < ' copy > FnOnce ( VaList < ' copy , ' f > ) -> R ,
266- {
267- let mut ap = self . clone ( ) ;
268- let ret = f ( ap. as_va_list ( ) ) ;
269- // SAFETY: the caller must uphold the safety contract for `va_end`.
270- unsafe {
271- va_end ( & mut ap) ;
272- }
273- ret
274- }
275234}
276235
277- impl < ' f > Clone for VaListImpl < ' f > {
236+ impl < ' f > Clone for VaList < ' f > {
278237 #[ inline]
279238 fn clone ( & self ) -> Self {
280239 let mut dest = crate :: mem:: MaybeUninit :: uninit ( ) ;
281- // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal
240+ // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal.
282241 unsafe {
283242 va_copy ( dest. as_mut_ptr ( ) , self ) ;
284243 dest. assume_init ( )
285244 }
286245 }
287246}
288247
289- impl < ' f > Drop for VaListImpl < ' f > {
248+ impl < ' f > Drop for VaList < ' f > {
290249 fn drop ( & mut self ) {
291- // FIXME: this should call `va_end`, but there's no clean way to
292- // guarantee that `drop` always gets inlined into its caller,
293- // so the `va_end` would get directly called from the same function as
294- // the corresponding `va_copy`. `man va_end` states that C requires this,
295- // and LLVM basically follows the C semantics, so we need to make sure
296- // that `va_end` is always called from the same function as `va_copy`.
297- // For more details, see https://github.com/rust-lang/rust/pull/59625
298- // and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic.
299- //
300- // This works for now, since `va_end` is a no-op on all current LLVM targets.
250+ // Rust requires that not calling `va_end` on a `va_list` does not cause undefined behaviour
251+ // (as it is safe to leak values). As `va_end` is a no-op on all current LLVM targets, this
252+ // destructor is empty.
301253 }
302254}
303255
0 commit comments