Skip to content
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

Clarify MIR semantics of storage statements #99050

Merged
merged 1 commit into from
Jul 9, 2022

Conversation

JakobDegen
Copy link
Contributor

Seems worthwhile to start closing out some of the less controversial open questions about MIR semantics. Hopefully this is fairly non-controversial - it's what we implement already, and I see no reason to do anything more restrictive. cc @tmiasko who commented on this when it was discussed in the original PR that added these docs.

@rustbot rustbot added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jul 8, 2022
@rust-highfive
Copy link
Collaborator

r? @cjgillot

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jul 8, 2022
@JakobDegen
Copy link
Contributor Author

Hmm, rustbot seems to have a better sleep schedule than I do. Manually tagging @RalfJung @oli-obk @davidtwco and @celinval for the MIR semantics change.

Also going to proactively rename the function in the docs so that it'll be correct once #99022 lands.

@tmiasko
Copy link
Contributor

tmiasko commented Jul 9, 2022

Thanks!

@bors r+ rollup

@bors
Copy link
Contributor

bors commented Jul 9, 2022

📌 Commit 4939f6c has been approved by tmiasko

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 9, 2022
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jul 9, 2022
Clarify MIR semantics of storage statements

Seems worthwhile to start closing out some of the less controversial open questions about MIR semantics. Hopefully this is fairly non-controversial - it's what we implement already, and I see no reason to do anything more restrictive. cc `@tmiasko` who commented on this when it was discussed in the original PR that added these docs.
/// computing these locals.
///
/// If the local is already allocated, calling `StorageLive` again is UB. However, for an
/// unallocated local an additional `StorageDead` all is simply a nop.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// unallocated local an additional `StorageDead` all is simply a nop.
/// unallocated local an additional `StorageDead` call is simply a nop.

(probably too late for this PR though, it's already rolled up)

/// computing these locals.
///
/// If the local is already allocated, calling `StorageLive` again is UB. However, for an
/// unallocated local an additional `StorageDead` all is simply a nop.
Copy link
Member

@RalfJung RalfJung Jul 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should consider double-free to also be UB, just for consistency? The only reason I allow this is that we do generate redundant StorageDead in MIR currently. (Or at least we did last time I checked this.)

MiniRust says double-dead is UB: I think I'd prefer that given that double-free of a heap allocation is also UB.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recently reported #98896 indicates this is still an issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was actually going to suggest that we make the StorageLive version of this not be UB either. I don't see what we gain from adding the UB

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would you suggest translating StorageLive to LLVM ir if double StorageLive isn't UB? I believe StorageLive(_1); _1 = 42; StorageLive(_1); _2 = _1; is UB in LLVM ir.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should be UB in MIR too - the semantics I am suggesting for StorageLive are that calling it when the local is already live is allowed but still fully reallocates the local

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allowed but still fully reallocates the local

Oh I see. Yes that would satisfy the "reset to uninit" condition.

FWIW a separate StorageReallocate has been suggested in the past for this purpose (#61849, #68622).

Copy link
Member

@RalfJung RalfJung Jul 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still we should get confirmation from LLVM that lifetime.start will not have any effects that flow "backwards" in the execution and make preceding accesses UB -- ideally, to explicitly state in their docs "doing this on an already live local is fine (but reset its contents to be uninitialized)".

Copy link
Contributor Author

@JakobDegen JakobDegen Jul 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If ptr is a non-stack-allocated object, it does not point to the first byte of the object or it is a stack object that is already alive, it simply fills all bytes of the object with poison.

https://llvm.org/docs/LangRef.html#llvm-lifetime-start-intrinsic

Seems like they already do.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I didn't see the "already alive" part. Good point. And for lifetime.end it says

Calling llvm.lifetime.end on an already dead alloca is no-op.

So yeah this removes my main motivation for making redundant annotations UB in the first place. That means I do not have a strong opinion either way. I don't know if there are any good motivations for doing one or the other, or any optimizations/transformations that would be helped by either choice.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's discuss this in a new issue: #99160

bors added a commit to rust-lang-ci/rust that referenced this pull request Jul 9, 2022
…askrgr

Rollup of 3 pull requests

Successful merges:

 - rust-lang#99022 (MIR dataflow: Rename function to `always_storage_live_locals`)
 - rust-lang#99050 (Clarify MIR semantics of storage statements)
 - rust-lang#99067 (Intra-doc-link-ify reference to Clone::clone_from)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 140250c into rust-lang:master Jul 9, 2022
@rustbot rustbot added this to the 1.64.0 milestone Jul 9, 2022
@JakobDegen JakobDegen deleted the storage-docs branch August 3, 2022 07:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants