Emit llvm dereferenceable in more cases on newer LLVM versions - #158863
WaffleLapkin wants to merge 3 commits into
Conversation
|
With LLVM 23 we can also put |
|
@RalfJung yes. What currently stops rust/compiler/rustc_ty_utils/src/abi.rs Lines 406 to 424 in f10db29 With I suppose another way to put this is that rustc already uses dereferenceable-at-a-point semantics since #156281, and applies dereferenceable-at-a-point everywhere it's valid. The only check that prevents llvm |
|
Nice, that's what I was hoping for. :) This will be much easier to review once we actually have LLVM 23 and can look at the codegen test diff. |
| // This one is *not* `noalias` because it might be self-referential. | ||
| // It is also not `dereferenceable` due to | ||
| // It is also not `dereferenceable` prior to LLVM23 due to | ||
| // <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>. |
There was a problem hiding this comment.
@RalfJung I'm confused by this. You said applying dereferenceable on &!Freeze / &mut !Unpin should be fine now, but rust-lang/unsafe-code-guidelines#381 seems to disagree with that. Was there some kind of update since that issue was written or am I missing something else?
There was a problem hiding this comment.
Ah... good point. I had forgotten about this. This also relates to an old LLVM semantics question that never got resolved.
In the end the question is what dereferenceable really means to LLVM. Does it mean "this memory is in-bounds of some allocation" or does it mean "pretend we did a byte-type read here". The former is fine, Miri checks that. The latter is not fine as that read can affect the aliasing model (and the data race model, but LLVM ~defines that problem away and this is not our worst crime on the memory model side so we hope it's fine).
So what is unambiguously correct is to add dereferenceable to &i32 and &mut i32 return types, and to Box<i32> arguments and return types. But for !Freeze or !Unpin things are indeed less clear.
There was a problem hiding this comment.
Changed to PR such that it only emits dereferenceable in cases when miri performs implicit reads. #158863 (comment).
|
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Emit llvm dereferenceable in more cases on newer LLVM versions
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Finished benchmarking commit (69d43e7): comparison URL. Overall result: ❌ regressions - please read:Benchmarking means the PR may be perf-sensitive. It's automatically marked not fit for rolling up. Overriding is possible but disadvised: it risks changing compiler perf. Next, please: If you can, justify the regressions found in this try perf run in writing along with @bors rollup=never rustc-perf Instruction countOur most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.
Max RSS (memory usage)Results (primary 4.3%, secondary -1.8%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (primary -2.4%, secondary 4.1%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeThis perf run didn't have relevant results for this metric. Bootstrap: 459.982s -> 458.725s (-0.27%) |
|
Regression feels like noise to me. |
5851f2c to
cad30cc
Compare
This comment has been minimized.
This comment has been minimized.
cad30cc to
e4b3389
Compare
|
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
e4b3389 to
5284ae8
Compare
This comment has been minimized.
This comment has been minimized.
5284ae8 to
b43f02b
Compare
|
After a discussion with @RalfJung, it seems clear that After that the semantics changes in this PR (given
@rustbot ready |
| // - https://github.com/rust-lang/unsafe-code-guidelines/issues/381 | ||
| // - https://github.com/llvm/llvm-project/pull/218413 | ||
| // - https://discourse.llvm.org/t/interaction-of-noalias-and-dereferenceable/66979 | ||
| size = layout.size * frozen as _; |
There was a problem hiding this comment.
Hm, the entire point of having frozen in the PointerKind was that dealing with this is handled later, when this is translated into LLVM attributes. Seems a bit messy to have some of the frozen logic here and some of it very far away in a different part of the compiler -- can't we avoid that?
There was a problem hiding this comment.
I think that place might be wherever PointeeInfo gets turned into ArgAttributes.
There was a problem hiding this comment.
So, I'm kind of on the fence about this...
In #150447 (and specifically 312055f) I made it so size is always correct, independently of safe pointer kind. I like this because it means that there isn't a weird invariant where interpretation of a value of the field (of PointeeInfo) depends on the value of another field.
I could move this into arg_attrs_for_rust_scalar, but that would mean that meaning of size is entangled with safe again...
I do see your point though, not sure what is the best separation to make here.
There was a problem hiding this comment.
In #150447 (and specifically 312055f) I made it so size is always correct, independently of safe pointer kind. I like this because it means that there isn't a weird invariant where interpretation of a value of the field (of PointeeInfo) depends on the value of another field.
Yeah, that's exactly why this PR here feels like a step back now.
There was a problem hiding this comment.
The underlying problem is that there are (at least) two notions of dereferenceable(N):
- There exist N bytes of in-bounds memory starting at that pointer.
- We can insert a load of N bytes without introducing UB.
(1) is true for all references, but we only want to emit LLVM dereferenceable for (2). We need to decide what PointeeInfo::size means. Given that (1) is not really relevant for codegen at all, it's just a thing Miri does internally, IMO it doesn't make much sense to represent (1) explicitly in the compiler at this time, so we should change the meaning of PointeeInfo::size to (2).
... well I think I just argued for what the PR does, and against what I wrote above. I hate it when that happens. 😂
| // is also set. | ||
| if deref != 0 && regular.contains(ArgAttribute::NoFree) { | ||
| let llvm_version = crate::llvm_util::get_version(); | ||
| if deref != 0 && (llvm_version >= (23, 0, 0) || regular.contains(ArgAttribute::NoFree)) { |
There was a problem hiding this comment.
FWIW the size field docs for PointeeInfo will also need changing.
On a function argument, “dereferenceable” here means “dereferenceable for the entire duration of this function call”, i.e. it is UB for the memory that this pointer points to be freed while this function is still running.
There was a problem hiding this comment.
Does this sound good to you?
rust/compiler/rustc_abi/src/lib.rs
Lines 2354 to 2368 in ccaaed3
There was a problem hiding this comment.
I'd put the new speculative read part at the beginning.
And then I think we can just have a brief comment saying that this can be used in argument and return position and in both cases it means "dereferenceable right now (but could be freed any time later)".
Specifically on llvm >= 23.0.0 emit llvm `dereferenceable` attribute even without `nofree`, since new semantics allow that.
... in order to comply with semantics where `dereferenceable` implies a spurious read.
b43f02b to
8d1588f
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
The job Click to see the possible cause of the failure (guessed by this bot) |
| // Set the `size` to zero for non-frozen shared references. | ||
| // | ||
| // `dereferenceable` annotation is used in LLVM to intoduce spurious | ||
| // reads, as such it is only valid when introducing spurious reads is | ||
| // sound. (at the time of writing these LLVM semantics are not fully | ||
| // decided yet, but that's our best/most conservative idea). |
There was a problem hiding this comment.
This should refer to the semantics as documented on PointeeInfo. What LLVM does is only indirectly relevant here.
View all comments
On llvm >= 23 emit
dereferenceableeven withoutnofree, since llvm/llvm-project#204795 makesdereferenceableact "at a point" / not implynofree.Additionally restrict
dereferenceableto&Freeze/&mut Unpin/Box<Unpin, _>only (this is only relevant forllvm >= 23as otherwise!Freeze/!Unpinsuppressnofreeand as suchdereferenceable).r? nikic
I thought this will be a bit harder change, but since your refactoring in #156281 it's actually trivial ^^'
Draft because I don't think this can be tested prior to LLVM update. I'm also not sure if the version check I did is the appropriate one...
cc @RalfJung