-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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 the missing inttoptr when we ptrtoint in ptr atomics #122917
Conversation
// CHECK: %[[INTPTR:.*]] = ptrtoint ptr %{{.*}} to [[USIZE]] | ||
// CHECK-NEXT: %[[RET:.*]] = atomicrmw add ptr %{{.*}}, [[USIZE]] %[[INTPTR]] | ||
// CHECK-NEXT: inttoptr [[USIZE]] %[[RET]] to ptr |
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.
// CHECK: %[[INTPTR:.*]] = ptrtoint ptr %{{.*}} to [[USIZE]] | |
// CHECK-NEXT: %[[RET:.*]] = atomicrmw add ptr %{{.*}}, [[USIZE]] %[[INTPTR]] | |
// CHECK-NEXT: inttoptr [[USIZE]] %[[RET]] to ptr | |
// CHECK: %[[V_PTR:.*]] = inttoptr [[USIZE]] %v to ptr | |
// CHECK: %[[V_INT:.*]] = ptrtoint ptr %[[V_PTR]] to [[USIZE]] | |
// CHECK: %[[RET_INT:.*]] = atomicrmw add ptr %a, [[USIZE]] %[[V_INT]] | |
// CHECK: %[[RET_PTR:.*]] = inttoptr [[USIZE]] %[[RET]] to ptr | |
// CHECK: ret ptr %[[RET_PTR]] |
(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:
fn atomicptr_fetch_byte_add(_1: &AtomicPtr<u8>, _2: usize) -> *mut u8 {
...
_4 = move _7 as *mut *mut u8 (PtrToPtr);
_6 = _2 as *mut u8 (Transmute);
_3 = atomic_xadd_relaxed::<*mut u8>(move _4, move _6) -> [return: bb1, unwind unreachable];
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.
Or are you suggesting that we add an intrinsic fn atomic_ptradd_relaxed(*mut *mut T, usize)?
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.
always use the provenance of the first argument, and I assume this is what Miri implements.
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.
tests/codegen/atomicptr.rs
Outdated
|
||
// CHECK-LABEL: @atomicptr_swap | ||
#[no_mangle] | ||
pub fn atomicptr_swap(a: &AtomicPtr<u8>, ptr1: *mut u8, ptr2: *mut u8, ptr3: *mut u8) -> *mut u8 { |
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.
Are the extra arguments necessary?
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.
Whoops. Leftover from a previous idea.
76fa2d4
to
6b794f6
Compare
This assumes LLVM uses our semantics for int2ptr transmutes, which is far from given. But it'd be a conservative choice to not rely on these transmutes to work. OTOH, the transmutes still occur with this PR if load/store/CAS and fetch_add are mixed on the same Meanwhile, the question here is what is the way to generate code that is least likely to make LLVM do anything wrong. That's outside my league. |
@bors r+ |
I'd say basically "yes". But at the same time I don't think this particularly matters right now, given how atomicrmw operates in-memory, so the actual type of the value is hidden. I guess the only case where this would become visible is if LLVM expands the atomicrmw into a cmpxchg loop. As such I'd probably delay such an addition until we have ptradd, in which case |
☀️ Test successful - checks-actions |
Finished benchmarking commit (5dcb678): comparison URL. Overall result: no relevant changes - no action needed@rustbot label: -perf-regression Instruction countThis benchmark run did not return any relevant results for this metric. Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 677.003s -> 679.002s (0.30%) |
Ralf noticed this here: #122220 (comment)
Our previous codegen forgot to add the cast back to integer type. The code compiles anyway, because of course all locals are in-memory to start with, so previous codegen would do the integer atomic, store the integer to a local, then load a pointer from that local. Which is definitely not what we wanted: That's an integer-to-pointer transmute, so all pointers returned by these
AtomicPtr
methods didn't have provenance. Yikes.Here's the IR for
AtomicPtr::fetch_byte_add
on 1.76: https://godbolt.org/z/8qTEjeraYr? @RalfJung
cc @nikic