Support borrowed locals in DestinationPropagation.#146743
Conversation
|
Some changes occurred to MIR optimizations cc @rust-lang/wg-mir-opt |
cc3a0a4 to
5e1c9fd
Compare
|
This PR was rebased onto a different master 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. |
|
Those conditions are insufficient since they make it possible to observe that storage of unified locals overlaps: $ cat a.rs
fn main() {
let x;
let y;
{
let a = 0;
x = &a as *const _;
let b = a;
y = &b as *const _;
}
assert!(x != y);
}
$ rustc +stage1 -O a.rs
$ ./a
thread 'main' (34419) panicked at a.rs:10:5:
assertion failed: x != y
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace |
|
Right, the correct way to calculate the live range of a borrowed local is to start from the assignment that initializes it (at the late position) and end at |
|
☔ The latest upstream changes (presumably #142771) made this pull request unmergeable. Please resolve the merge conflicts. |
Follow-up to 6b87f07. Closes the gap between the Range B+ implementation landed there and the 4 hand-blessed tests whose outputs change when borrowed- local dest_prop merges more candidates. mir-opt (blessed re-output): * `tests/mir-opt/pre-codegen/range_iter.inclusive_loop.runtime-optimized.after.{panic-abort,panic-unwind}.mir` — the iterator-state copy `_5 = copy _4` is `nop`'d and the subsequent `&mut _5` becomes `&mut _4` (the local indices and a `StorageLive`/ `StorageDead` pair shift accordingly). This is the optimization doing its job; no behavioural change. codegen-llvm (`CHECK` directives tightened, not blessed): * `tests/codegen-llvm/function-arguments.rs` — `%x.0` / `%x.1` → `%{{[^,]+}}` / `%{{[^)]+}}`. Parameter NAMES change when a name-bearing local is merged out; every LLVM attribute the test probes (`noundef nonnull align 4`, `noalias`, `captures(address, read_provenance)`) is byte-identical to before. Pure name-cosmetic. * `tests/codegen-llvm/noalias-box-off.rs` — `%foo` → `%{{[^)]+}}` for the same reason. The actual assertion (`CHECK-NOT: noalias` under `-Zbox-noalias=no`) is unchanged and still holds. * `tests/codegen-llvm/zip.rs` — the previous `CHECK: memcpy` was passing in the historical compiler by incidentally matching the 48-byte `Zip<Iter, IterMut>` iterator-state struct copy (`_5 = copy _3` lowered to `llvm.memcpy.p0.p0.i64(%iter, %_3, 48)`), NOT the loop body. At `-Cno-prepopulate-passes -Copt-level=3` the body lowers to per-byte `load i8` / `store i8` in both the historical compiler and the current one — LLVM never autovectorised it. With dest_prop merging borrowed-local candidates soundly (rust-lang#146743), the iterator-state copy is eliminated and no memcpy appears anywhere in either function. Inverting the assertion to `CHECK-NOT: memcpy` locks in the win: the test now regresses if dest_prop ever stops eliminating the redundant Zip copy. A test comment records why the original positive memcpy assertion was brittle and what changed. Re-validated: * `./x.py test --stage 1 tests/codegen-llvm`: 0 failed (was 935/3/128 in the bare Range B+ build at 6b87f07; now clean). * `./x.py test --stage 1 tests/mir-opt tests/codegen-units tests/assembly-llvm tests/crashes`: 0 failed. * `./x.py test --stage 1 tests/ui` (the load-bearing run-pass corpus): 21013/0/223, unchanged. * tmiasko's counterexample (rust-lang#146743 comment 2025-09-19): `x != y` holds at `-O` and `-Zmir-opt-level=3`, unchanged. * rustlantis differential, 1500 random UB-free seeds, base4 vs treatment: 1500 clean, 0 ICEs, 0 real divergences, unchanged. This commit also tightens the characterisation in 6b87f07's message: zip.rs is not a name-cosmetic golden diff — it's a real codegen improvement (one redundant 48-byte memcpy eliminated per `zip_copy`/`zip_copy_mapped` invocation) that the original `CHECK: memcpy` was incidentally matching. The other two codegen-llvm files genuinely are name-cosmetic and the regex tolerance preserves what the tests actually intend to assert. Refs: rust-lang#146743 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow-up to #142915
The current implementation of
DestinationPropagationrefuses to consider locals that are borrowed at any point in the MIR.This PR attempts to relax this requirement without trying to perform alias analysis.
For each local, we consider they are borrowed from the first
Rvalue::ReforRvalue::RawPtrstatement until they are markedStorageDead.The liveness analysis is modified to consider that:
Future extensions may consider a
moveto discard borrows, but this requires work on MIR semantics beforehand.r? @Amanieu