-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Add ptr::{read,write}_unaligned #1725
Conversation
# Alternatives | ||
[alternatives]: #alternatives | ||
|
||
We could simply not add these, however figuring out how to do unaligned access properly is extremely unintuitive: you need to cast the pointer to `*mut u8` and then call `ptr::copy_nonoverlapping`. |
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.
What about passing the alignment explicitly, with the functions proposed here being only for an alignment of 1
?
That way, instead of always doing stack copies, they can generate optimal LLVM IR for that given alignment.
They could also be used with larger alignments to potentially hint that, e.g. SIMD can be used, although that works better for variants of the ptr::copy{,_nonoverlapping}
functions.
Now is that better than having the alignment as part of the pointer?
I don't know, #[align(1) *mut u32
crossed my mind and it's good enough for some cases, but doesn't work with generics (would have to be something that works with polymorphic constants, i.e. part of the typesystem).
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 think these use cases can be covered by using a newtype with an explicit alignment attribute:
#[align(1)]
struct UnalignedU32(u32);
This makes more sense since the alignment is attached to the type that is pointed to rather than the pointer itself.
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 was thinking about taking a reference to a packed field, to be clear. Otherwise we could do:
#[align(1)]
struct Unaligned<T>(T);
struct AlignAs<T, U>([U; 0], Unaligned<T>);
On architectures that allow unaligned accesses without faulting, these should become aliases for direct pointer reads and writes. These should only become a full call to |
@joshtriplett llvm already compiles copy_nonoverlapping that way, what I've seen. |
It looks like the only concern here is the ability to tell Rust how to optimize for alignments other than 1, and there's no way to do it for references to pre-existing packed struct fields. @eddyb Do you feel strongly about the importance of specifying alignment to these functions? This otherwise looks like a simple RFC to push through. |
Might it be better to explicitly specify an Putting unaligned addresses in a |
I think I agree with @comex here, alignment should be handled by the type system, not manually by users. Although, I don't think unaligned types like |
@comex You can't run a destructor on an unaligned object because that would create a misaligned These function are merely convenience wrappers around |
Oh, good point. And in fact, after some searching, this RFC purports to ban generic packed structs altogether due to the issue with Drop on their contents, though that rule doesn't seem to be implemented... |
Based on @Amanieu's statement that "these function are merely convenience wrappers around memcpy and are only useful in unsafe code", I tend to think we don't need a lot of ceremony here to protect users from themselves. This is surfacing an unsafe feature for a specialized purpose. Those that need to do a lot of unaligned work with some guarantees can develop those abstractions and test them in practice, rather than us speculate about what would be useful. Still hoping to hear from @eddyb about the alignment. |
Sorry, that ping got lost among the other messages. If the type-based approach can be used with the existing functions with some pointer casting, then we don't even need these new functions AFAICT, and if they can't be used, they're a bit limited. The problem with non-orthogonal functions is that you might also want unaligned (or differently aligned?) volatile variants of this, for example. There's potential candidates you'd keep adding. If none of those concerns actually apply, then I have nothing against this RFC in its current form. |
I don't think there is much of a use case for unaligned volatile accesses. These will likely be broken up into multiple memory accesses by the hardware, which somewhat defeats the point of volatile. |
I'm inclined to move this to FCP to see if we can stir up more opinions. It looks to me like these are the major points:
My inclination is that this is useful as-is for a pretty common case, and low risk. But I also think the case both for and against are not that strong. We don't need this in tree but it would be good to have some signaling that one can do unaligned load and store, and this is how. @rfcbot fcp merge |
Team member @brson has proposed to merge this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
Having this would have saved me time, so +1 for having obvious ways to dereference unaligned pointers to SIMD types in particular. One bikeshed question though: Is there a good reason for these being functions as opposed to methods on pointer types like the |
read_volatile() and write_volatile() are free functions in core::ptr, not methods. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
The final comment period is now complete. |
Looks like no new issues came up, so merging! Thanks again for the RFC @Amanieu! Tracking issue: rust-lang/rust#37955 |
I realize Rust is not C, but I'll just remind us of the difference here. I'm late now, but something like this could have been part of the RFC. With a background in C, it's strange to even pass around raw pointers that are not well aligned for their pointee type. (Casting to create such a pointer is UB in C!). In Rust it's a useful feeling anyway, because almost all consumers of raw pointers have the same requirement (well aligned for pointee type). But we make note that this is not an invariant of the raw pointer type itself in Rust. Yet, many implementations may feel it's more precise to store unaligned pointers as |
ptr::copy_nonoverlapping(&v as *const _ as *const u8, | ||
p as *mut u8, | ||
mem::size_of::<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.
forget(v)
is missing in the reference implementation here; this should move v
into the new location, just like write
does.
cc @rust-lang/lang re: @bluss's comment, which has some overlap with the unsafe guidelines work. Just want to make sure everyone is aware/on board. |
Please update the Rendered link to point to the final text if you get a chance. Thanks! |
Updated |
Rendered