-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Add the missing inttoptr when we ptrtoint in ptr atomics #122917
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
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// LLVM does not support some atomic RMW operations on pointers, so inside codegen we lower those | ||
// to integer atomics, surrounded by casts to and from integer type. | ||
// This test ensures that we do the round-trip correctly for AtomicPtr::fetch_byte_add, and also | ||
// ensures that we do not have such a round-trip for AtomicPtr::swap, because LLVM supports pointer | ||
// arguments to `atomicrmw xchg`. | ||
|
||
//@ compile-flags: -O -Cno-prepopulate-passes | ||
#![crate_type = "lib"] | ||
|
||
#![feature(strict_provenance)] | ||
#![feature(strict_provenance_atomic_ptr)] | ||
|
||
use std::sync::atomic::AtomicPtr; | ||
use std::sync::atomic::Ordering::Relaxed; | ||
use std::ptr::without_provenance_mut; | ||
|
||
// Portability hack so that we can say [[USIZE]] instead of i64/i32/i16 for usize. | ||
// CHECK: @helper([[USIZE:i[0-9]+]] noundef %_1) | ||
#[no_mangle] | ||
pub fn helper(_: usize) {} | ||
|
||
// CHECK-LABEL: @atomicptr_fetch_byte_add | ||
#[no_mangle] | ||
pub fn atomicptr_fetch_byte_add(a: &AtomicPtr<u8>, v: usize) -> *mut u8 { | ||
// CHECK: %[[INTPTR:.*]] = ptrtoint ptr %{{.*}} to [[USIZE]] | ||
// CHECK-NEXT: %[[RET:.*]] = atomicrmw add ptr %{{.*}}, [[USIZE]] %[[INTPTR]] | ||
// CHECK-NEXT: inttoptr [[USIZE]] %[[RET]] to ptr | ||
a.fetch_byte_add(v, Relaxed) | ||
} | ||
|
||
// CHECK-LABEL: @atomicptr_swap | ||
#[no_mangle] | ||
pub fn atomicptr_swap(a: &AtomicPtr<u8>, ptr: *mut u8) -> *mut u8 { | ||
// CHECK-NOT: ptrtoint | ||
// CHECK: atomicrmw xchg ptr %{{.*}}, ptr %{{.*}} monotonic | ||
// CHECK-NOT: inttoptr | ||
a.swap(ptr, Relaxed) | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 this would be clearer (and not too fragile) if you match the whole thing end-to-end, i.e.
(Could also match the argument names from the signature instead of hardcoding %a and %v, but I think it's fine, it's pretty unlikely that LLVM will start changing/dropping them in the future.)
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 then leads to the question--why do we roundtrip %v through inttoptr/ptrtoint?
I suppose this is because in MIR the atomic op takes two pointer arguments, but does it really make sense to add two pointers?
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.
Correct, adding two pointers doesn't make sense. The problem is that we're implementing an operation which takes a pointer operand and an integer operand. There's no atomic ptradd.
In terms of provenance, this is cursed if there are two pointer operands because which provenance is written back through the pointer? It's also cursed if we do this with integers, because then haven't we written an integer over a pointer, wiping the provenance from the memory? (I'm sort of hand-waving the kind of provenance semantics in Miri over LLVM IR which is itself a bit dubious)
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.
Right, that makes sense. I should've been more explicit--what I mean is, the relevant part of the MIR for your example currently looks like:
i.e. the intrinsic we're implementing here does take two pointer operands. Isn't that wrong? (Well, maybe not wrong but suboptimal.)
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.
In what way would an intrinsic based on a pointer to an integer and and an integer operand be better? Or are you suggesting that we add an intrinsic
fn atomic_ptradd_relaxed(*mut *mut T, usize)
?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. (And the same for every atomic operation on pointers (xor, or, etc.) that only makes sense with an integer second operand.)
Given that the only sensible operation here is offsetting a pointer by an integer, wouldn't it be better for the intrinsic to match that?
(I guess this isn't so much a problem because we can just define
atomic_xadd_relaxed::<*mut u8>
to always use the provenance of the first argument, and I assume this is what Miri implements. Having to convert the offset to/from a pointer "just" makes the codegen more complex.)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 indeed.
But ideally we'd convince LLVM to provide an intrinsic that takes
(*mut *mut T, usize)
; that is the only way to avoid pointer-integer transmutation.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.
Right, we need the LLVM change regardless, I don't mean to imply that this would solve the provenance issue. It would just remove some other unnecessary weirdness.
It doesn't need to be done as part of this PR though, I can do it separately.
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.
Agree with @erikdesjardins that the Rust intrinsic should accept usize rather than pointer for the second arg.