Skip to content

GVN: Use the borrows only if they are always live - #147886

Closed
dianqk wants to merge 5 commits into
rust-lang:mainfrom
dianqk:gvn-new-deref-arg
Closed

GVN: Use the borrows only if they are always live#147886
dianqk wants to merge 5 commits into
rust-lang:mainfrom
dianqk:gvn-new-deref-arg

Conversation

@dianqk

@dianqkdianqk commented Oct 19, 2025

Copy link
Copy Markdown
Member

Fixes#141313 and #141313 (comment). This PR forbids introducing borrows outside their lifetime.

c8fbb62fixes#130853.

I agree with what #147844 (comment) mentioned:

We can reason with the value behind a reference because it is UB to directly assign to the underlying local while the reference is live. We allow creating new derefs, this means extending the liveness of references, so we are creating UB.

r? cjgillot

@rustbot

Copy link
Copy Markdown
Collaborator

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

@rustbotrustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Oct 19, 2025
/// Check that we allow dereferences in the RHS if the LHS is a stable projection.
// This introduces copy overlapping if dereferencing `_2` or `_4`.
#[custom_mir(dialect = "runtime")]
fn stable_projection(_1: (Adt,)) {

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

@cjgillot I just found this still introduces copy overlapping.

@dianqk

Copy link
Copy Markdown
MemberAuthor

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

rust-borsBot added a commit that referenced this pull request Oct 19, 2025
GVN: Only introduce new derefs if the immutable borrow is always valid.
@rust-bors

This comment has been minimized.

@rustbotrustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Oct 19, 2025
@cjgillot

Copy link
Copy Markdown
Contributor

I'd have kept introducing Value::Projection(Deref) as exists, but forbidden introducing Deref projections in try_as_place. What do you think?

@rust-bors

rust-borsBot commented Oct 19, 2025

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: efb5f98 (efb5f98020c7392070aae01c719ed60b78144ff5, parent: c6efb9019b3169fc672248339dbbf13e6a134de3)

@rust-timer

This comment has been minimized.

@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (efb5f98): comparison URL.

Overall result: ❌✅ regressions and improvements - please read the text below

Benchmarking this pull request means it may be perf-sensitive – we'll automatically label it not fit for rolling up. You can override this, but we strongly advise not to, due to possible changes in compiler perf.

Next Steps: If you can justify the regressions found in this try perf run, please do so in sufficient writing along with @rustbot label: +perf-regression-triaged. If not, please fix the regressions and do another perf run. If its results are neutral or positive, the label will be automatically removed.

@bors rollup=never
@rustbot label: -S-waiting-on-perf +perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

meanrangecount
Regressions ❌
(primary)
0.8%[0.8%, 0.8%]1
Regressions ❌
(secondary)
--0
Improvements ✅
(primary)
-0.3%[-0.4%, -0.3%]3
Improvements ✅
(secondary)
-0.3%[-0.3%, -0.3%]1
All ❌✅ (primary)-0.1%[-0.4%, 0.8%]4

Max RSS (memory usage)

Results (primary 4.1%, secondary 0.8%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

meanrangecount
Regressions ❌
(primary)
4.1%[2.8%, 5.3%]2
Regressions ❌
(secondary)
0.8%[0.8%, 0.8%]1
Improvements ✅
(primary)
--0
Improvements ✅
(secondary)
--0
All ❌✅ (primary)4.1%[2.8%, 5.3%]2

Cycles

Results (secondary 3.2%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

meanrangecount
Regressions ❌
(primary)
--0
Regressions ❌
(secondary)
3.2%[3.2%, 3.2%]1
Improvements ✅
(primary)
--0
Improvements ✅
(secondary)
--0
All ❌✅ (primary)--0

Binary size

Results (primary 0.0%, secondary -0.0%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

meanrangecount
Regressions ❌
(primary)
0.2%[0.0%, 1.0%]11
Regressions ❌
(secondary)
0.0%[0.0%, 0.2%]15
Improvements ✅
(primary)
-0.1%[-0.5%, -0.0%]17
Improvements ✅
(secondary)
-0.1%[-0.1%, -0.0%]9
All ❌✅ (primary)0.0%[-0.5%, 1.0%]28

Bootstrap: 472.659s -> 474.116s (0.31%)
Artifact size: 390.57 MiB -> 391.13 MiB (0.14%)

@rustbotrustbot added perf-regression Performance regression. and removed S-waiting-on-perf Status: Waiting on a perf run to be completed. labels Oct 19, 2025
@dianqk

dianqk commented Oct 19, 2025

Copy link
Copy Markdown
MemberAuthor

I'd have kept introducing Value::Projection(Deref) as exists, but forbidden introducing Deref projections in try_as_place. What do you think?

Oh, I think this is a great insight. IIUC, I can remove invalid_derefs and introduce a borrow-reaching definition that is used in try_as_place. This should allow us to preserve derefs in the RPO A-B(terminator is call)-C.

@dianqk

Copy link
Copy Markdown
MemberAuthor

I'd have kept introducing Value::Projection(Deref) as exists, but forbidden introducing Deref projections in try_as_place. What do you think?

Oh, I think this is a great insight. IIUC, I can remove invalid_derefs and introduce a borrow-reaching definition that is used in try_as_place. This should allow us to preserve derefs in the RPO A-B(terminator is call)-C.

Hmm, I still need to handle this in simplify_binary_inner​ to check whether *_1​ is still valid at the location of *_2​. There might be other codes I’ve missed. I can also check the deref in get​.

Actually, that's not enough. insert​ reuses VnIndex​ when values are equal. For that, I could add a provenance, but I don't see what difference it makes not to introduce new derefs. Maybe do this when we can know if the immutable borrow is in its lifetime scope at the location.

@rust-log-analyzer

This comment has been minimized.

@dianqkdianqk added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Oct 27, 2025
@dianqkdianqk changed the title GVN: Only introduce new derefs if the immutable borrow is always valid.GVN: Use the borrows only if they are always liveOct 29, 2025
@rustbot

This comment has been minimized.

@dianqk

dianqk commented Oct 29, 2025

Copy link
Copy Markdown
MemberAuthor

I'd have kept introducing Value::Projection(Deref) as exists, but forbidden introducing Deref projections in try_as_place. What do you think?

I changed it to forbid introducing borrows outside their lifetime. And c8fbb62 should fix #130853.

I have another experimental patch, 6e2ecde, using live variables, but this may introduce miscompile, and I won't add it.

@rustbot review

@rustbotrustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Oct 29, 2025
rust-borsBot added a commit that referenced this pull request Dec 10, 2025
GVN: Use the borrows only if they are always live
@rustbotrustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Dec 10, 2025
@rust-bors

rust-borsBot commented Dec 10, 2025

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 2b9aa76 (2b9aa76acf43f7c466566e82eb9c30d4cd75d243, parent: 377656d3dd3f9c23a9c8713e163f4365a5261a84)

@rust-timer

This comment has been minimized.

@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (2b9aa76): comparison URL.

Overall result: ❌✅ regressions and improvements - please read the text below

Benchmarking this pull request means it may be perf-sensitive – we'll automatically label it not fit for rolling up. You can override this, but we strongly advise not to, due to possible changes in compiler perf.

Next Steps: If you can justify the regressions found in this try perf run, please do so in sufficient writing along with @rustbot label: +perf-regression-triaged. If not, please fix the regressions and do another perf run. If its results are neutral or positive, the label will be automatically removed.

@bors rollup=never
@rustbot label: -S-waiting-on-perf +perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

meanrangecount
Regressions ❌
(primary)
0.3%[0.1%, 0.6%]8
Regressions ❌
(secondary)
0.5%[0.2%, 1.4%]24
Improvements ✅
(primary)
-1.0%[-2.5%, -0.2%]3
Improvements ✅
(secondary)
-0.4%[-2.4%, -0.1%]8
All ❌✅ (primary)-0.0%[-2.5%, 0.6%]11

Max RSS (memory usage)

Results (primary 2.2%, secondary 1.1%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

meanrangecount
Regressions ❌
(primary)
3.8%[2.4%, 5.3%]2
Regressions ❌
(secondary)
1.1%[1.1%, 1.1%]1
Improvements ✅
(primary)
-1.2%[-1.2%, -1.2%]1
Improvements ✅
(secondary)
--0
All ❌✅ (primary)2.2%[-1.2%, 5.3%]3

Cycles

Results (primary -2.2%, secondary 0.1%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

meanrangecount
Regressions ❌
(primary)
--0
Regressions ❌
(secondary)
2.4%[2.4%, 2.4%]1
Improvements ✅
(primary)
-2.2%[-2.2%, -2.2%]1
Improvements ✅
(secondary)
-2.2%[-2.2%, -2.2%]1
All ❌✅ (primary)-2.2%[-2.2%, -2.2%]1

Binary size

Results (primary -0.1%, secondary 0.6%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

meanrangecount
Regressions ❌
(primary)
0.0%[0.0%, 0.1%]18
Regressions ❌
(secondary)
1.6%[0.0%, 15.0%]10
Improvements ✅
(primary)
-0.3%[-1.6%, -0.0%]19
Improvements ✅
(secondary)
-0.2%[-1.6%, -0.0%]11
All ❌✅ (primary)-0.1%[-1.6%, 0.1%]37

Bootstrap: 471.42s -> 472.371s (0.20%)
Artifact size: 389.04 MiB -> 389.08 MiB (0.01%)

@rustbotrustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Dec 10, 2025
@dianqkdianqk mentioned this pull request Dec 21, 2025
rust-borsBot added a commit that referenced this pull request Dec 21, 2025
@rustbot

Copy link
Copy Markdown
Collaborator

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.

@dianqk

Copy link
Copy Markdown
MemberAuthor

@rustbot author

@rustbotrustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Dec 28, 2025
@dianqk

Copy link
Copy Markdown
MemberAuthor

I realized that we are propagating the borrows of non-SSA locals. This is why we use borrows outside their lifetime.
I think #150485 is a better resolution.

@rust-bors

rust-borsBot commented Jan 16, 2026

Copy link
Copy Markdown
Contributor

☔ The latest upstream changes (presumably #150925) made this pull request unmergeable. Please resolve the merge conflicts.

@dianqk

Copy link
Copy Markdown
MemberAuthor

Superseded by #150485.

@dianqkdianqk closed this Jan 21, 2026
@rustbotrustbot removed the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Jan 21, 2026
@dianqk
dianqk deleted the gvn-new-deref-arg branch January 21, 2026 13:59
rust-borsBot pushed a commit that referenced this pull request Feb 8, 2026
GVN: Only propagate borrows from SSA locals
Fixes#141313. This is a more principled fix than #147886.
Using a reference that is not a borrowing of an SSA local at a new location may be UB.
The PR has two major changes.
The first one, when introducing a new dereference at a new location, is that the reference must point to an SSA local or be an immutable argument. `dereference_address` has handled SSA locals.
The second one, if we cannot guard to the reference point to an SSA local in `visit_assign`, we have to rewrite the value to opaque. This avoids unifying the following dereferences that also are references:
```rust
let b: &T = *a;
// ... `a` is allowed to be modified. `c` and `b` have different borrowing lifetime.
// Unifying them will extend the lifetime of `b`.
let c: &T = *a; ```
See also #130853.
This still allows unifying non-reference dereferences:
```rust
let a: &T = ...;
let b: T = *a;
// ... a is NOT allowed to be modified.
let c: T = *a; ```
r? @cjgillot
github-actionsBot pushed a commit to rust-lang/miri that referenced this pull request Feb 13, 2026
GVN: Only propagate borrows from SSA locals
Fixesrust-lang/rust#141313. This is a more principled fix than rust-lang/rust#147886.
Using a reference that is not a borrowing of an SSA local at a new location may be UB.
The PR has two major changes.
The first one, when introducing a new dereference at a new location, is that the reference must point to an SSA local or be an immutable argument. `dereference_address` has handled SSA locals.
The second one, if we cannot guard to the reference point to an SSA local in `visit_assign`, we have to rewrite the value to opaque. This avoids unifying the following dereferences that also are references:
```rust
let b: &T = *a;
// ... `a` is allowed to be modified. `c` and `b` have different borrowing lifetime.
// Unifying them will extend the lifetime of `b`.
let c: &T = *a; ```
See also rust-lang/rust#130853.
This still allows unifying non-reference dereferences:
```rust
let a: &T = ...;
let b: T = *a;
// ... a is NOT allowed to be modified.
let c: T = *a; ```
r? @cjgillot
github-actionsBot pushed a commit to rust-lang/stdarch that referenced this pull request Feb 16, 2026
GVN: Only propagate borrows from SSA locals
Fixesrust-lang/rust#141313. This is a more principled fix than rust-lang/rust#147886.
Using a reference that is not a borrowing of an SSA local at a new location may be UB.
The PR has two major changes.
The first one, when introducing a new dereference at a new location, is that the reference must point to an SSA local or be an immutable argument. `dereference_address` has handled SSA locals.
The second one, if we cannot guard to the reference point to an SSA local in `visit_assign`, we have to rewrite the value to opaque. This avoids unifying the following dereferences that also are references:
```rust
let b: &T = *a;
// ... `a` is allowed to be modified. `c` and `b` have different borrowing lifetime.
// Unifying them will extend the lifetime of `b`.
let c: &T = *a; ```
See also rust-lang/rust#130853.
This still allows unifying non-reference dereferences:
```rust
let a: &T = ...;
let b: T = *a;
// ... a is NOT allowed to be modified.
let c: T = *a; ```
r? @cjgillot
github-actionsBot pushed a commit to rust-lang/rustc-dev-guide that referenced this pull request Feb 16, 2026
GVN: Only propagate borrows from SSA locals
Fixesrust-lang/rust#141313. This is a more principled fix than rust-lang/rust#147886.
Using a reference that is not a borrowing of an SSA local at a new location may be UB.
The PR has two major changes.
The first one, when introducing a new dereference at a new location, is that the reference must point to an SSA local or be an immutable argument. `dereference_address` has handled SSA locals.
The second one, if we cannot guard to the reference point to an SSA local in `visit_assign`, we have to rewrite the value to opaque. This avoids unifying the following dereferences that also are references:
```rust
let b: &T = *a;
// ... `a` is allowed to be modified. `c` and `b` have different borrowing lifetime.
// Unifying them will extend the lifetime of `b`.
let c: &T = *a; ```
See also rust-lang/rust#130853.
This still allows unifying non-reference dereferences:
```rust
let a: &T = ...;
let b: T = *a;
// ... a is NOT allowed to be modified.
let c: T = *a; ```
r? @cjgillot
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

perf-regressionPerformance regression.T-compilerRelevant 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.

GVN misunderstands aliasing, can create overlapping assignments (again) Miscompile in the GVN transform

5 participants

@dianqk@rustbot@rust-timer@cjgillot@rust-log-analyzer