Add the missing inttoptr when we ptrtoint in ptr atomics#122917
Add the missing inttoptr when we ptrtoint in ptr atomics#122917bors merged 1 commit intorust-lang:masterfrom
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Are the extra arguments necessary?
There was a problem hiding this comment.
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
AtomicPtrmethods didn't have provenance. Yikes.Here's the IR for
AtomicPtr::fetch_byte_addon 1.76: https://godbolt.org/z/8qTEjeraYr? @RalfJung
cc @nikic