-
Notifications
You must be signed in to change notification settings - Fork 111
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
GVN transformation regarding lifetime.start #433
Comments
ping @RalfJung |
Via email you wrote
My personal operational interpretation is that during execution, we keep track of which locals are alive and which are not. When marking a local as alive, if the local is already live, then this is UB (or at least the value of the local gets reset to undef). Would that explain the behavior you are seeing? |
Okay, to revisit this issue:
A relevant thread: rust-lang/rust#42371 (comment) I think we can encode this precise semantics by resetting the corresponding local_block_val element. @nunoplopes What do you think? |
FWIW: Rust's MIR interpreter assumes that redundant StorageLive is UB, so refinement holds. |
Duplicated lifetime.start are not important. They never happen.
Setting it to poison seems the easiest. so I agree with you @aqjune. Just don't touch memory.cpp this month 😀 |
They certainly used to happen in the LLVM IR that rustc generated. Is that a bug, i.e., is it UB to mark an already live local as live?
This would mean that the memory stays at the same location, so pointers created to this local earlier will remain valid. |
I don't know if it's UB, but it's a bug for sure. Doesn't make sense to have 2 starts given that these start/end intrinsics are used for the stack allocator. They are used to compute the lifetime of an alloca, so I can't find a meaning for starting the lifetime twice (without ending it beforehand).
That's ok I guess. llc doesn't seem to do anything fancy with duplicated starts. |
If that is the consensus, it would be good to have that documented in the LangRef, and maybe have debug assertions catching this. For the people writing the Rust side of this, it was far from clear that this is considered buggy by LLVM. |
It seems a transformation may introduce unpaired lifetime.start and such one should be fixed as well.
|
The link you posted is about Polly. That isn't included in LLVM mainline. The replies in that thread also go in the direction of requiring paired lifetime start/end intrinsics. |
Btw, rustc still emits redundant |
That should be fine. The first one wins. As long as there's no use after any |
Okay.. I wrote a specification of lifetime.start/end. What do you think Nuno and Ralf? for each lifetime.start S of alloca A, there should exist lifetime.end E of A s.t.
Then, alloca A is live between the lifetime.start/end pairs. Accessing the alloca out of the lifetime is UB. lifetime.start initializes the bytes as undef. One additional concern is that it might be a good idea to syntactically restrict the pointer argument given to lifetime.start/end. It won't be meaningful if it was loaded from the memory, such as |
Two comments:
Then the question is about multiple syntactic start/end pairs for the same alloca. I assume pointers may become invalid between these as in theory the allocator can change the alloca'ed object to a different address. Should we use lifetime.start as an allocation site and lifetime.end as free? The syntactic check makes sense. I hope LLVM's verifier is enforcing that. 😅 |
Oh, I've never thought about this. I'll experiment with a few cases.
I think this is tricky. We'd like to assume that ptrtoint can be freely hoisted/sinked across lifetime intrinsic (or change LLVM to forbid that?)
I'd like to do so, but would people like this change to be accompanied with lifetime? It is about the value of uninitialized storage. |
That what I did in Miri.
Do we want to guarantee that they all go to the same allocation? I'd expect not, so we cold still model each |
Q1: Can lifetime end at a function terminator? Q2: When we have multiple start/end, can llc place them in different addresses? |
I think I had this idea before but we didn't propose it: Regarding ptr2int, we have to be careful with that one. If we can trigget llc to allocate different start/end pairs in different addresses, then we cannot allow free movement for ptr2int, otherwise it can catch the address of another start/end pair. |
Yeah, I remember that you mentioned it with a notion of SSI. BTW, I found that gep can also be hoisted across lifetime.start. https://gcc.godbolt.org/z/Wo9dbo |
Since duplicated start/end pairs are not produced by LLVM at least, let's ignore them and make the semantics stronger to enable free movement of geps, ptr2int, etc. |
Sure, I'll submit a patch for this! :) |
thank you! 😀 |
Okay, if https://reviews.llvm.org/D94002 is accepted, the GVN transformation can now be explained. |
I'll make a PR that implements the new lifetime semantics tomorrow. |
https://godbolt.org/z/8D-TvV
LangRef says
A load from the pointer that precedes this intrinsic can be replaced with 'undef'.
The text was updated successfully, but these errors were encountered: