From b15544d52fc005ca0142fb34a40d591147596c8c Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Thu, 23 Apr 2026 11:17:22 +0100 Subject: [PATCH] Handle index projections in call destinations in DSE Since call destinations are evaluated after call arguments, we can't turn copy arguments into moves if the same local is later used as an index projection in the call destination. --- .../src/dead_store_elimination.rs | 10 +++++++- ...eadStoreElimination-final.panic-abort.diff | 15 ++++++++++++ ...adStoreElimination-final.panic-unwind.diff | 15 ++++++++++++ .../dead-store-elimination/call_arg_copy.rs | 24 +++++++++++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-abort.diff create mode 100644 tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-unwind.diff diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 63ee69322eef..e968ed640ecf 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -47,13 +47,21 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { let mut patch = Vec::new(); for (bb, bb_data) in traversal::preorder(body) { - if let TerminatorKind::Call { ref args, .. } = bb_data.terminator().kind { + if let TerminatorKind::Call { ref args, ref destination, .. } = bb_data.terminator().kind { let loc = Location { block: bb, statement_index: bb_data.statements.len() }; // Position ourselves between the evaluation of `args` and the write to `destination`. live.seek_to_block_end(bb); let mut state = live.get().clone(); + // Don't turn into a move if the local is used as an index + // projection for the destination place. + LivenessTransferFunction(&mut state).visit_place( + destination, + visit::PlaceContext::MutatingUse(visit::MutatingUseContext::Call), + loc, + ); + for (index, arg) in args.iter().map(|a| &a.node).enumerate().rev() { if let Operand::Copy(place) = *arg && !place.is_indirect() diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-abort.diff b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-abort.diff new file mode 100644 index 000000000000..012dd7d88a5f --- /dev/null +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-abort.diff @@ -0,0 +1,15 @@ +- // MIR for `move_index` before DeadStoreElimination-final ++ // MIR for `move_index` after DeadStoreElimination-final + + fn move_index(_1: [usize; 10], _2: usize) -> () { + let mut _0: (); + + bb0: { + _1[_2] = passthrough_usize(copy _2) -> [return: bb1, unwind unreachable]; + } + + bb1: { + return; + } + } + diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-unwind.diff b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-unwind.diff new file mode 100644 index 000000000000..fcd0ae43e289 --- /dev/null +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-unwind.diff @@ -0,0 +1,15 @@ +- // MIR for `move_index` before DeadStoreElimination-final ++ // MIR for `move_index` after DeadStoreElimination-final + + fn move_index(_1: [usize; 10], _2: usize) -> () { + let mut _0: (); + + bb0: { + _1[_2] = passthrough_usize(copy _2) -> [return: bb1, unwind continue]; + } + + bb1: { + return; + } + } + diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs index 27b5ccdb936d..00a9a49c2abb 100644 --- a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs @@ -40,7 +40,31 @@ fn move_packed(packed: Packed) { } } +#[inline(never)] +fn passthrough_usize(a: usize) -> usize { + a +} + +// EMIT_MIR call_arg_copy.move_index.DeadStoreElimination-final.diff +#[custom_mir(dialect = "analysis")] +fn move_index(a: [usize; 10], b: usize) { + // CHECK-LABEL: fn move_index( + // CHECK: = passthrough_usize(copy _2) + mir! { + { + // The index is used again after the operand is evaluated to + // evaluate the destionation place, so the argument cannot be turned + // into a move. + Call(a[b] = passthrough_usize(b), ReturnTo(ret), UnwindContinue()) + } + ret = { + Return() + } + } +} + fn main() { move_simple(1); move_packed(Packed { x: 0, y: 1 }); + move_index([0; _], 1); }