-
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
Correct alignment of atomic types and (re)add Atomic{I,U}128 #53514
Conversation
r? @kennytm (rust_highfive has picked a reviewer for you, use r? to override) |
I've nominated for beta since a cursory look suggests that's necessary. |
r? @cramertj |
src/libcore/sync/atomic.rs
Outdated
@@ -124,7 +124,7 @@ pub fn spin_loop_hint() { | |||
/// [`bool`]: ../../../std/primitive.bool.html | |||
#[cfg(target_has_atomic = "8")] | |||
#[stable(feature = "rust1", since = "1.0.0")] | |||
#[repr(transparent)] | |||
#[repr(align(1))] |
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.
align(1)
doesn't do anything here, but repr(transparent)
does, so I'd opt to use that here.
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, this was only added for consistency. I don't know if #[repr(transparent)]
is really appropriate for AtomicBool
though because the internal type is actually u8
and if we wanted to support platforms that don't have byte level atomics it would have to be something bigger.
src/libcore/sync/atomic.rs
Outdated
@@ -148,7 +148,9 @@ unsafe impl Sync for AtomicBool {} | |||
/// This type has the same in-memory representation as a `*mut T`. | |||
#[cfg(target_has_atomic = "ptr")] | |||
#[stable(feature = "rust1", since = "1.0.0")] | |||
#[repr(transparent)] | |||
#[cfg_attr(target_pointer_width = "16", repr(align(2)))] |
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.
Do we support any platforms where size isn't already equal to alignment for *mut
? This seems redundant to me, and it sacrifices the #[repr(transparent)]
.
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 aware of any but I don't know what platforms Rust might want to support in the future.
src/libcore/sync/atomic.rs
Outdated
@@ -1101,7 +1104,7 @@ macro_rules! atomic_int { | |||
/// | |||
/// [module-level documentation]: index.html | |||
#[$stable] | |||
#[repr(transparent)] | |||
#[repr(align($align))] |
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.
As above, I think that this is already always true with the exception of u/i128
. I'd prefer a solution which kept repr(transparent)
intact for other primitives and special-cased the 128-bit types to receive higher-alignment attributes rather than #[repr(transparent)]
.
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.
u64
is only aligned to 4 on 32-bit linux systems.
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.
@retep998 Thanks! I think I'd still prefer to special-case and allow repr(transparent)
on more platforms, but that makes me unsure.
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, that's the issue, it's not clear which types can be repr(transparent)
. That's why I just added repr(align)
to all types to avoid the issue. We presumably definitely don't want the same type to have repr(transparent)
on some platforms but not others so I don't really know what else to do here.
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 either. Ping @rust-lang/libs for your thoughts? This is a breaking (but necessary) change to the representation guarantees of several atomics-- it'd be nice to keep the repr(transparent)
where possible, but I agree that having it be platform-dependent is pretty dodgy.
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.
@sfackler I'd think you can still construct a *const AtomicU64
at the right address without repr(transparent)
?
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.
As long as the layout is the same, then *const AtomicU64
to arbitrary memory is still fine. Where repr(transparent)
is needed is specifically for compatibility in function calling convention ABIs because two types having the same layout may not be passed the same.
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.
My understanding is that repr(transparent)
is the only thing that guarantees that the layout is the same - do we have a separate guarantee that single field structs have the same layout as their element?
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.
When not talking about passing things around in functions by value, wouldn't that be repr(C)
?
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.
Yes, repr(C)
would be one way to achieve layout compatibility. However, since this is in libstd, we can also just state that it's layout compatible and rely on the fact that rustc de facto makes the layout identical. Furthermore, I am convinced we should guarantee layout compatibility of repr(Rust)
newtypes anyway.
I am not clear on whether this is @rust-lang/compiler, @rust-lang/libs, or even @rust-lang/lang, but I do agree we ought to backport if we decide to accept this PR. =) I'm going to cc @rust-lang/wg-unsafe-code-guidelines as well well I'm going crazy because it seems like the sort of thing that is related to unsafe code. It seems like the real question at play here is:
We sort of can't do both because they may be in conflict? (Is that correct? That was the gist I was getting from the comments, but I don't know if there are concrete examples of this) Is there a conservative decision we can make in the meantime which we can backport? |
My feeling btw is that the ultimate decision here probably requires at least an FCP and might benefit from an RFC (since there may be subtle constraints at play we are not fully considering) — but if we can take some immediate steps (e.g., removing |
For at least u16/u32/u64, it's not clear to me why we can't have both |
Removing
Given that LLVM mandates alignment requirements, our hands are bound. |
The latter seems more important to me, but are there any platforms where u64 is not naturally aligned in the ABI, yet 64 bit atomics are supported and require natural alignment? Normally, 64 bit CPUs make u64 naturally aligned while 32 bit CPUs emulate in a library 64 bit atomics in software without corresponding alignment requirements. I suppose a 32 bit platform with double-word CAS might be relevant, but is that even a thing and do we care about it? |
Oh, I missed that LLVM mandates this. Then I guess it's settled. Note that this has more repercussions than just removing |
The semantics of this on x86 are... unclear. See e.g. http://joeduffyblog.com/2006/12/06/clr-data-alignment-and-cmpxchg8b/ https://reviews.llvm.org/D27653 |
Ping! This PR is beta-nominated, and if we want it to reach 1.29 stable we need to merge this in less than a week. What's the progress? |
Added in rust-lang#52149 the discussion in rust-lang#53514 is showing how we may not want to actually add this attribute to the atomic types. While we continue to debate rust-lang#53514 this commit reverts the addition of the `transparent` attribute before it hits stable.
Thanks for the ping @pietroalbini, I've submitted to #53978 to temporarily revert #52149 on beta-only as it should give us some more time to discuss this PR |
Added in rust-lang#52149 the discussion in rust-lang#53514 is showing how we may not want to actually add this attribute to the atomic types. While we continue to debate rust-lang#53514 this commit reverts the addition of the `transparent` attribute. This should be a more conservative route which leaves us the ability to tweak this in the future but in the meantime allows us to continue discussion as well.
I've also sent #53979 for the master branch |
…r=nikomatsakis [beta]: Remove `#[repr(transparent)]` from atomics Added in #52149 the discussion in #53514 is showing how we may not want to actually add this attribute to the atomic types. While we continue to debate #53514 this commit reverts the addition of the `transparent` attribute before it hits stable.
@Aaronepower #53979 already landed removing the |
discussed briefly at T-compiler meeting. We cannot make a spot decision on this without more careful review. Re-assigning to @nagisa for now to make a summary comment (potentially linking to other relevant bugs) for the rest of the compiler team to read. |
I'm a bit confused about what the points of decision are here. Can someone maybe crystallize into the key questions at hand? My impression from @rkruppe's comment:
Is that (a) we really just have to do whatever LLVM mandates and (b) this may mean that Also note that |
I think you can s/ |
So, I was tasked with summarizing this. Here it goes (tis gonna be a long one). First, there is one critical requirement that we cannot avoid to keep this correct: For atomic operations to be valid on There’s no way around it, even if alignment of the type is less than its size (e.g. In C/C++ land this is solved by having types
The safe API must by all means ensure that these requirements (and all other machine-specific requirements) are met by itself. With that out of the way… What follows are some of the options: We could simply "just" specify This is the approach taken by this PR.
Only if we include alignment into list of type “equivalency” requirements. On one hand, that would mean that Making this (dual repr attributes) possible would solve all the problems with atomics, AFAIK.
In order to use atomics in an FFI context it is critical to make it possible to execute atomic operations on pointers obtained over the FFI boundary. After all the whole purpose of the atomic operations is to work on areas of memory, not on values specifically. The question then is, how do we expose atomic operations for pointers obtained via FFI? One option is to allow (the fairly unsafe) equivalence between (i.e. the proposal above):
Another option is to have simply:
Either way, some sort of representational equivalency between It is important to remember that the alignment for the atomic instruction itself (in generated LLVM code) must be specified and must satisfy the requirements mentioned above. This means that the way we call the atomic intrinsics (such as this) must change as well (i.e. instead of being called with e.g. This made me think of another option of which I haven’t thought of before, which is changing the implementation of the /* #[ ... ] */
struct AtomicU8 {
v: UnsafeCell<u8>
} to #[repr(align(correct))]
newtype atomic_u8_t = /*???*/;
#[repr(transparent)]
struct AtomicU8 {
v: UnsafeCell<atomic_u8_t>
} EDIT: removed "natural" from "natural alignment" as per comment below. |
Terminology nit: You mean ABI alignment here. "Natural alignment" means alignment equal to the data type size.
For the record, for the definition in libcore we could also merge this PR as-is. We don't currently guarantee that a
We argued about this before (in this thread and on IRC too, I believe) but I still believe the second half of this, particularly the choice of "must", is wrong. We can simply amend the definition of these intrinsics to require suitable alignment with no change to the signature. |
Is that true? It's still a newtype, just without Basically, I agree with @rkruppe. |
I do not trust I’m not sure of what newtype guarantees you’re speaking about. |
Basically, newtypes having the same layout (size and field offsets) as the type they wrap. Like, your So just using |
This is the link to the discussion. |
Why not? What are the steps required to make that true:
Is there some other subtlety involved? |
@nikomatsakis my distrust with that comes specifically from While I agree that “newtypes” having equivalent memory layout as the types they contain seem like a sensible idea, it is not something that we ever explicitly specified and making unsafe code to rely on such unwritten rule is scary. |
OK. I feel like it is within our power, however, to reach this decision. =) (Not on this PR per se.) |
Ping from triage! It looks like this PR has been superseded by #55410, so I'm closing it. If my understanding is incorrect, please re-open. |
Correct alignment of atomic types and (re)add Atomic{I,U}128 This is a updated #53514 to also make atomic types `repr(C)` as per comment in #53514 (comment). Fixes #39590 Closes #53514 r? @pnkfelix
LLVM requires that atomic loads and stores be aligned to at least the size of the type. https://llvm.org/docs/LangRef.html#id193
Increasing the alignment is incompatible with
#[repr(transparent)]
but fortunately that was only recently added but either this PR or a revert of #52149 would have to be backported to 1.29 beta to avoid breakage from that.Using
#[repr(align)]
is a breaking change though because for some reason you can't put a#[repr(align)]
struct inside a#[repr(packed)]
struct. I don't know if there's a way to increase the alignment without that issue.cc. #32976
Fixes #39590