about summary refs log tree commit diff
diff options
context:
space:
mode:
authorStuart Cook <Zalathar@users.noreply.github.com>2025-08-09 13:58:44 +1000
committerGitHub <noreply@github.com>2025-08-09 13:58:44 +1000
commite63e769c9b6233e1f9c0f53d0ec8d276cb7d882f (patch)
tree846dd3da557546400307cffb8c5e16965656e100
parent4f80489767f367dc5dd6abb07df2a780ab04a6a1 (diff)
parent4e81ecaf3a45a4bb5a8a78ac407d1ef6a7367e72 (diff)
downloadrust-e63e769c9b6233e1f9c0f53d0ec8d276cb7d882f.tar.gz
rust-e63e769c9b6233e1f9c0f53d0ec8d276cb7d882f.zip
Rollup merge of #144883 - scottmcm:remove-unneeded-drop_in_place, r=nnethercote
Remove unneeded `drop_in_place` calls

Might as well pull this out from rust-lang/rust#144561 because this is still used in things like `Vec::truncate` where it'd be nice to allow it be removed if inlined enough to see that the type is `Copy`.

So long as perf says it's ok, at least 🤞
-rw-r--r--compiler/rustc_mir_transform/src/remove_unneeded_drops.rs31
-rw-r--r--tests/mir-opt/remove_unneeded_drop_in_place.rs17
-rw-r--r--tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff20
3 files changed, 61 insertions, 7 deletions
diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
index 43f80508e4a..6c2dfc59da2 100644
--- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
+++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
@@ -4,7 +4,13 @@
 //! useful because (unlike MIR building) it runs after type checking, so it can make use of
 //! `TypingMode::PostAnalysis` to provide more precise type information, especially about opaque
 //! types.
+//!
+//! When we're optimizing, we also remove calls to `drop_in_place<T>` when `T` isn't `needs_drop`,
+//! as those are essentially equivalent to `Drop` terminators. While the compiler doesn't insert
+//! them automatically, preferring the built-in instead, they're common in generic code (such as
+//! `Vec::truncate`) so removing them from things like inlined `Vec<u8>` is helpful.
 
+use rustc_hir::LangItem;
 use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
 use tracing::{debug, trace};
@@ -21,15 +27,26 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUnneededDrops {
         let mut should_simplify = false;
         for block in body.basic_blocks.as_mut() {
             let terminator = block.terminator_mut();
-            if let TerminatorKind::Drop { place, target, .. } = terminator.kind {
-                let ty = place.ty(&body.local_decls, tcx);
-                if ty.ty.needs_drop(tcx, typing_env) {
-                    continue;
+            let (ty, target) = match terminator.kind {
+                TerminatorKind::Drop { place, target, .. } => {
+                    (place.ty(&body.local_decls, tcx).ty, target)
+                }
+                TerminatorKind::Call { ref func, target: Some(target), .. }
+                    if tcx.sess.mir_opt_level() > 0
+                        && let Some((def_id, generics)) = func.const_fn_def()
+                        && tcx.is_lang_item(def_id, LangItem::DropInPlace) =>
+                {
+                    (generics.type_at(0), target)
                 }
-                debug!("SUCCESS: replacing `drop` with goto({:?})", target);
-                terminator.kind = TerminatorKind::Goto { target };
-                should_simplify = true;
+                _ => continue,
+            };
+
+            if ty.needs_drop(tcx, typing_env) {
+                continue;
             }
+            debug!("SUCCESS: replacing `drop` with goto({:?})", target);
+            terminator.kind = TerminatorKind::Goto { target };
+            should_simplify = true;
         }
 
         // if we applied optimizations, we potentially have some cfg to cleanup to
diff --git a/tests/mir-opt/remove_unneeded_drop_in_place.rs b/tests/mir-opt/remove_unneeded_drop_in_place.rs
new file mode 100644
index 00000000000..470c6499d16
--- /dev/null
+++ b/tests/mir-opt/remove_unneeded_drop_in_place.rs
@@ -0,0 +1,17 @@
+//@ test-mir-pass: RemoveUnneededDrops
+//@ needs-unwind
+//@ compile-flags: -Z mir-opt-level=1
+
+// EMIT_MIR remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff
+unsafe fn slice_in_place(ptr: *mut [char]) {
+    // CHECK-LABEL: fn slice_in_place(_1: *mut [char])
+    // CHECK: bb0: {
+    // CHECK-NEXT: return;
+    // CHECK-NEXT: }
+    std::ptr::drop_in_place(ptr)
+}
+
+fn main() {
+    let mut a = ['o', 'k'];
+    unsafe { slice_in_place(&raw mut a) };
+}
diff --git a/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff b/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff
new file mode 100644
index 00000000000..4d70e7151c3
--- /dev/null
+++ b/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff
@@ -0,0 +1,20 @@
+- // MIR for `slice_in_place` before RemoveUnneededDrops
++ // MIR for `slice_in_place` after RemoveUnneededDrops
+  
+  fn slice_in_place(_1: *mut [char]) -> () {
+      debug ptr => _1;
+      let mut _0: ();
+      let mut _2: *mut [char];
+  
+      bb0: {
+          StorageLive(_2);
+          _2 = copy _1;
+-         _0 = drop_in_place::<[char]>(move _2) -> [return: bb1, unwind continue];
+-     }
+- 
+-     bb1: {
+          StorageDead(_2);
+          return;
+      }
+  }
+