-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Implement va_arg for AArch64 Linux #62207
Conversation
r? @kennytm (rust_highfive has picked a reviewer for you, use r? to override) |
It is something I wanted to experiment with. It certainly is much easier to read and less error prone than src/librustc_codegen_llvm/va_arg.rs CC #56489 |
src/libcore/ffi.rs
Outdated
pub trait VaArgSafe { | ||
#[doc(hidden)] | ||
#[cfg(not(bootstrap))] | ||
unsafe fn __va_arg(ap: &mut super::VaListImpl<'_>) -> Self; |
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.
I'm not sure about the double-underscore prefix, wouldn't va_arg_internal
be a better name?
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.
It's sealed so va_arg
or va_arg_from_list
would be fine.
($($t:ty),+) => { | ||
$( | ||
#[unstable(feature = "c_variadic", | ||
reason = "the `c_variadic` feature has not been properly tested on \ | ||
all supported platforms", | ||
issue = "44930")] | ||
impl sealed_trait::VaArgSafe for $t {} | ||
impl sealed_trait::VaArgSafe for $t { |
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.
This patch adds a lot of this boilerplate, I'm wondering if there's a way to cut it down (maybe using the type system).
src/libcore/ffi.rs
Outdated
ptr::read((self.gr_top as usize).wrapping_add(offs as usize) as *const T) | ||
} | ||
|
||
// Rust doesn't support these yet |
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.
Would moving this implementation into the compiler (inside src/librustc_codegen_llvm/va_arg.rs) let you implement this?
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.
Actually, are these SIMD types? There are some of those in nightly, even for AArch64.
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.
Yeah it would and allow supporting aggregates in general. What I meant by this comment was rust doesn't support aggregates in variadic functions (yet) so it doesn't need to be implemented.
@@ -268,35 +268,86 @@ mod sealed_trait { | |||
reason = "the `c_variadic` feature has not been properly tested on \ | |||
all supported platforms", | |||
issue = "44930")] | |||
pub trait VaArgSafe {} | |||
pub trait VaArgSafe { |
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.
Maybe VaArgSafe
should be renamed to something more appropriate, like VaArgType
.
I added some comments (mostly just nitpicks). |
src/libcore/ffi.rs
Outdated
unsafe fn __va_arg(ap: &mut VaListImpl<'_>) -> Self { | ||
va_arg(ap) | ||
} | ||
#[cfg(all(target_arch = "aarch64", not(target_os = "ios"), not(windows)))] |
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.
You can put the #[cfg]
on the expression inside the body, I think?
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 yes, I think this is just left over from my first iteration where they had different signatures.
How much complexity is left after all the decision-making based on the type is done? |
src/libcore/ffi.rs
Outdated
let nreg = (mem::size_of::<T>() as i32 + 7) / 8; | ||
self.gr_offs = offs + (nreg * 8); | ||
if self.gr_offs > 0 { | ||
return self.va_arg_on_stack(); // overflowed reg save area |
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.
This reminds me I wanted to change how rustc_target::abi::call
works for a long time, and make it compute "designations" such as "on-stack" or "in register, if available, otherwise on-stack", with per-target "resources" (such as general-purpose registers for args/return and floating-point registers etc.), and put all of that together to compute final "assignments", afterwards (and we could even cache the per-type computation).
If we also tracked which registers were being used, that might even help Cranelift (cc @sunfishcode).
The relevant point here is making the per-type information context-free, so that it can be used for both signatures and va_arg
.
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 sounds ideal but outside my current knowledge/ability. Perhaps I could have a look at that after fixing it this way.
☔ The latest upstream changes (presumably #62376) made this pull request unmergeable. Please resolve the merge conflicts. |
Basically just checking whether you've overflowed the number of registers and need to go to the stack save area. |
I've tidied it up a bit and extended the tests a bit which now pass (x.py test log if anyone's interested) |
fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaListImpl<'_>) -> T; | ||
} | ||
|
||
#[cfg(all(target_arch = "aarch64", not(target_os = "ios"), not(windows)))] |
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.
You could move this to the top of the file.
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.
After a review of this, this seems like a reasonable way to prototype support for new architectures. It is similar to how va_list-rs
implements va_arg
. The calculation also seems simple enough to implement in IR if we want to go down that road.
@joshtriplett @eddyb thoughts on implementing va_arg
in pure rust? I'm not a fan of the sprawl of va_arg
implementations we'd get, but that is the only drawback I can see so far. After this, we'd have some architectures rely on LLVMs va_arg
(x86_64), others rely on custom LLVM IR we generate (i686), and then we'd have Aarch64 in libstd. Ultimately I'd like to pick one and try to support them all in one place.
{ | ||
if | ||
/* classof(type) != "aggregate" && */ | ||
mem::size_of::<T>() < reg_size { |
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.
nit: spacing. Could you change this comment to something like
FIXME(parched): When aggregate types are supported by `VaList::arg` we
should ensure the return type is not an aggregate type before this calculation
unsafe fn get_begin_and_end<T>(current_pos: usize, reg_size: usize) -> (usize, usize) { | ||
let mut begin = current_pos; | ||
if mem::align_of::<T>() > 8 && reg_size == 8 { | ||
begin = begin.wrapping_add(15) & !15; // round up |
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.
The spec uses -16. Is there a reason to use !15 instead.
} | ||
|
||
unsafe fn va_arg_on_stack<T: sealed_trait::VaArgSafe>(stack: &mut *mut c_void) -> T { | ||
let (begin, end) = Self::get_begin_and_end::<T>(*stack as usize, 8); |
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.
AFAIK this can give us different values than the spec if the stack isn't 8 byte aligned, but I suppose much worse things would be happening if that were the case.
In that case, you should be able to make this pretty general by having an int/float register decision based on the type and then emitting the appropriate IR. |
ptr::read(begin as *const T) | ||
} | ||
|
||
unsafe fn va_arg_gr_or_vr<T: sealed_trait::VaArgSafe>( |
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.
I would rename this to va_arg_reg
.
*r_offs = end as i32; | ||
if *r_offs > 0 { | ||
return Self::va_arg_on_stack(stack); // overflowed reg save area | ||
} |
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.
Wouldn't it be possible to do this check only once?
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.
r=me with @dlrobertson's, @ahomescu's and my nits
Ping from triage @parched, could you implement the reviewers' comments? |
r? @eddyb |
Sorry, just on holiday at the moment, but will get on to it this coming week. |
Closing for now, until I have time to come back to this, hopefully that will be next week. |
Fixes #56475
WIP. I would like some feedback before I tidy it up and properly verify it.
Is this approach of implementing it in rust instead of an intrinsic fine? I did it this way because it was simpler for me to write and should be simpler to understand. Does it matter that I added a hidden method to
VaArgSafe
?cc @dlrobertson, #59625 (@joshtriplett @eddyb @ahomescu)