-
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
Add element-wise atomic memory operations #59155
Conversation
r? @pnkfelix (rust_highfive has picked a reviewer for you, use r? to override) |
99c60bf
to
63ed27a
Compare
@@ -549,6 +553,17 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { | |||
Err(()) => return | |||
} | |||
} | |||
name if name.starts_with("atomic_element_") => { | |||
let ty = substs.type_at(0); | |||
if int_type_width_signed(ty, self).is_some() { |
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 restricts the type to integer types. I'm not sure if this is the best action, or if we should accept any type with a size that is a power of two less than the target-specific atomic size limit. But this is what we do for other atomic operations, so it is my first inclination.
src/rustllvm/RustWrapper.cpp
Outdated
unwrap(Size), ElementSize)); | ||
#else | ||
// TODO | ||
abort(); |
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.
LLVM 6.0 doesn't have a method for the memmove or memcpy intrinsics.
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.
Could this and the version below return a nullptr
in this case and then it's handled in rustc with a bug/panic macro?
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 a possibility. Since LLVM 6.0 does have the intrinsics, just not the method, another possibility would be to define something similar to CreateElementUnorderedAtomicMemMove
(maybe even basing it off the implementation in LLVM 7+). This was just a placeholder until I got to making a better solution.
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.
Nah I think it's fine to return nullptr
here as these are unstable and we don't provide a hard guarantee about support for older LLVM (only a guarantee of support for some LLVM which we ship). The easiest thing to do would probably be to return null here and it can be expanded later if need be
src/libcore/intrinsics.rs
Outdated
@@ -962,6 +962,33 @@ extern "rust-intrinsic" { | |||
/// value is not necessarily valid to be used to actually access memory. | |||
pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T; | |||
|
|||
/// Equivalent to the appropriate `llvm.memcpy.p0i8.p0i8.*` intrinsic, with |
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.
Is this maybe a copy/paste typo? Should this mention that it's the atomic version?
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.
yep, I'll fix that.
Out of curiosity, could you expand a bit on the use cases for an atomic memcpy? |
To be honest, I don't know. This was just in "Call for Participation" in TWiR, and looked like something I could do. @joshlf ? |
Sure thing! In Fuchsia, we often have two processes that communicate by sharing memory. If the two processes are mutually-distrusting, then, when operating on that shared memory, they each need to assume that the other is maliciously modifying the memory concurrently. From the perspective of the memory model, this is identical to another thread in the same process modifying the memory. Thus, to avoid UB, we can only operate on the memory using atomic operations. Our approach is very simple - copy all of the data into a separate part of the address space which is not shared, and then operate on it. Originally, we did this using a loop of |
sure thing |
As long as we don't incur additional platform dependence or fundamental abilities when stably exposing this it seems fine to me from a T-Lang hat-perspective. |
In general this is useful for optimistic concurrency, where you read some memory, and then verify that the read was "OK" via some other mechanism (and typically retrying if it wasn't ok). I think this is currently the best tool for that under the C memory model. Seqlocks could use it here: https://amanieu.github.io/seqlock/src/seqlock/src/lib.rs.html#147 Some relevant discussion here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0690r1.html |
☔ The latest upstream changes (presumably #58846) made this pull request unmergeable. Please resolve the merge conflicts. |
ab89d83
to
035fdfc
Compare
Well, I'm a little stuck. Running the tests, I get this error:
I'm presuming that the references are generated by llvm, but I'm not sure what they are supposed to be linked to and why it isn't getting linked. Any help would be appreciated. |
LLVM will often lower intrinsic calls or other operations to function calls to various platform-specific intrinsics. Perhaps the best example are the memcpy/memmove intrinsics in LLVM, which may lower to literal calls to If it's not there in |
☔ The latest upstream changes (presumably #59810) made this pull request unmergeable. Please resolve the merge conflicts. |
there does not appear to be an implementation in compiler-builtins :( |
If they're not present there this may unfortunately be a case where it's a bit too early to bind these intrinsics. |
Ah that's a shame. Is there anything we can do to unblock ourselves? Who controls compiler-builtins? |
I think to unblock this we need someone who knows how to implement, and I at least know that person is not me! The source of compiler-builtins is here, though. |
Visiting this for triage; I'm going to close this pull request as it looks like it's blocked on outstanding work in (probably) compiler builtins and is unlikely to make progress in the short term. Feel free to re-open once there's some progress, though! |
So folks on this thread are notified: rust-lang/compiler-builtins#311 |
Now that rust-lang/compiler-builtins#311 has been merged, I think this can go forward? |
I started working on this again, but ran into an issue where it complains that the new intrinsics are "unrecognized atomic operation"'s the first time libcore is compiled. I think when I first implemented this I fixed that by adding |
Should I create a new pull request for this? |
@Ralith Might have thoughts on your issue. |
I was doing some GitHub archeology related to sequence locks and ended up here.
Better late than never I suppose: The problem was that
@tmccombs Did you end up opening a new PR? Or do you know if anything else happened towards adding these intrinsics after the last comments here? |
LLVM's element-wise atomic operations actually generate very poor code in practice since they are always lowered to a call to the intrinsic. You can get much better codegen by using volatile reads and writes instead, which have similar (but stricter) guarantees. |
No, as far as I remember, I didn't do anything after my last comment. |
WIP
This is an attempt to implement #58599