about summary refs log tree commit diff
diff options
context:
space:
mode:
authorStuart Cook <Zalathar@users.noreply.github.com>2025-07-29 23:50:35 +1000
committerGitHub <noreply@github.com>2025-07-29 23:50:35 +1000
commit826c462c32642c8cc17d708a1bb9a7022fd5bdd5 (patch)
tree11a4ba4c50e698b28abd6fb0e1a7df77d121d925
parentf8370285cf81e8af24c7f95b77cbb83c3d6db1cf (diff)
parent8ef9233339c09d80e12b31c523082b94d9e09b44 (diff)
downloadrust-826c462c32642c8cc17d708a1bb9a7022fd5bdd5.tar.gz
rust-826c462c32642c8cc17d708a1bb9a7022fd5bdd5.zip
Rollup merge of #144566 - scottmcm:align-of-slice, r=oli-obk
Simplify `align_of_val::<[T]>(…)` → `align_of::<T>()`

I spotted this while working on the inliner (rust-lang/rust#144561).  In particular, if [`Layout::for_value`](https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.for_value) inlines, then it can be pretty easy to end up with an `align_of_val::<[T]>` today (demo: <https://rust.godbolt.org/z/Tesnscj4a>) where we can save at least a block, if not more, by using the version that's an rvalue and not a call.
-rw-r--r--compiler/rustc_mir_transform/src/instsimplify.rs31
-rw-r--r--tests/mir-opt/instsimplify/align_of_slice.of_val_slice.InstSimplify-after-simplifycfg.diff22
-rw-r--r--tests/mir-opt/instsimplify/align_of_slice.rs12
-rw-r--r--tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir108
-rw-r--r--tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir108
-rw-r--r--tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir108
-rw-r--r--tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir108
-rw-r--r--tests/mir-opt/pre-codegen/drop_boxed_slice.rs19
8 files changed, 516 insertions, 0 deletions
diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs
index dbcaed20953..c83bd25c663 100644
--- a/compiler/rustc_mir_transform/src/instsimplify.rs
+++ b/compiler/rustc_mir_transform/src/instsimplify.rs
@@ -55,6 +55,7 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify {
 
             let terminator = block.terminator.as_mut().unwrap();
             ctx.simplify_primitive_clone(terminator, &mut block.statements);
+            ctx.simplify_align_of_slice_val(terminator, &mut block.statements);
             ctx.simplify_intrinsic_assert(terminator);
             ctx.simplify_nounwind_call(terminator);
             simplify_duplicate_switch_targets(terminator);
@@ -252,6 +253,36 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
         terminator.kind = TerminatorKind::Goto { target: *destination_block };
     }
 
+    // Convert `align_of_val::<[T]>(ptr)` to `align_of::<T>()`, since the
+    // alignment of a slice doesn't actually depend on metadata at all
+    // and the element type is always `Sized`.
+    //
+    // This is here so it can run after inlining, where it's more useful.
+    // (LowerIntrinsics is done in cleanup, before the optimization passes.)
+    fn simplify_align_of_slice_val(
+        &self,
+        terminator: &mut Terminator<'tcx>,
+        statements: &mut Vec<Statement<'tcx>>,
+    ) {
+        if let TerminatorKind::Call {
+            func, args, destination, target: Some(destination_block), ..
+        } = &terminator.kind
+            && args.len() == 1
+            && let Some((fn_def_id, generics)) = func.const_fn_def()
+            && self.tcx.is_intrinsic(fn_def_id, sym::align_of_val)
+            && let ty::Slice(elem_ty) = *generics.type_at(0).kind()
+        {
+            statements.push(Statement::new(
+                terminator.source_info,
+                StatementKind::Assign(Box::new((
+                    *destination,
+                    Rvalue::NullaryOp(NullOp::AlignOf, elem_ty),
+                ))),
+            ));
+            terminator.kind = TerminatorKind::Goto { target: *destination_block };
+        }
+    }
+
     fn simplify_nounwind_call(&self, terminator: &mut Terminator<'tcx>) {
         let TerminatorKind::Call { ref func, ref mut unwind, .. } = terminator.kind else {
             return;
diff --git a/tests/mir-opt/instsimplify/align_of_slice.of_val_slice.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/align_of_slice.of_val_slice.InstSimplify-after-simplifycfg.diff
new file mode 100644
index 00000000000..042e19bf2aa
--- /dev/null
+++ b/tests/mir-opt/instsimplify/align_of_slice.of_val_slice.InstSimplify-after-simplifycfg.diff
@@ -0,0 +1,22 @@
+- // MIR for `of_val_slice` before InstSimplify-after-simplifycfg
++ // MIR for `of_val_slice` after InstSimplify-after-simplifycfg
+  
+  fn of_val_slice(_1: &[T]) -> usize {
+      debug slice => _1;
+      let mut _0: usize;
+      let mut _2: *const [T];
+  
+      bb0: {
+          StorageLive(_2);
+          _2 = &raw const (*_1);
+-         _0 = std::intrinsics::align_of_val::<[T]>(move _2) -> [return: bb1, unwind unreachable];
++         _0 = AlignOf(T);
++         goto -> bb1;
+      }
+  
+      bb1: {
+          StorageDead(_2);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/instsimplify/align_of_slice.rs b/tests/mir-opt/instsimplify/align_of_slice.rs
new file mode 100644
index 00000000000..0af05cb6b0b
--- /dev/null
+++ b/tests/mir-opt/instsimplify/align_of_slice.rs
@@ -0,0 +1,12 @@
+//@ test-mir-pass: InstSimplify-after-simplifycfg
+//@ needs-unwind
+
+#![crate_type = "lib"]
+#![feature(core_intrinsics)]
+
+// EMIT_MIR align_of_slice.of_val_slice.InstSimplify-after-simplifycfg.diff
+pub fn of_val_slice<T>(slice: &[T]) -> usize {
+    // CHECK-LABEL: fn of_val_slice(_1: &[T])
+    // CHECK: _0 = AlignOf(T);
+    unsafe { core::intrinsics::align_of_val(slice) }
+}
diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir
new file mode 100644
index 00000000000..2777bba893b
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir
@@ -0,0 +1,108 @@
+// MIR for `generic_in_place` after PreCodegen
+
+fn generic_in_place(_1: *mut Box<[T]>) -> () {
+    debug ptr => _1;
+    let mut _0: ();
+    scope 1 (inlined drop_in_place::<Box<[T]>> - shim(Some(Box<[T]>))) {
+        scope 2 (inlined <Box<[T]> as Drop>::drop) {
+            let _2: std::ptr::NonNull<[T]>;
+            let mut _3: *mut [T];
+            let mut _4: *const [T];
+            let _12: ();
+            scope 3 {
+                let _8: std::ptr::alignment::AlignmentEnum;
+                scope 4 {
+                    scope 12 (inlined Layout::size) {
+                    }
+                    scope 13 (inlined Unique::<[T]>::cast::<u8>) {
+                        scope 14 (inlined NonNull::<[T]>::cast::<u8>) {
+                            scope 15 (inlined NonNull::<[T]>::as_ptr) {
+                            }
+                        }
+                    }
+                    scope 16 (inlined <NonNull<u8> as From<Unique<u8>>>::from) {
+                        scope 17 (inlined Unique::<u8>::as_non_null_ptr) {
+                        }
+                    }
+                    scope 18 (inlined <std::alloc::Global as Allocator>::deallocate) {
+                        let mut _9: *mut u8;
+                        scope 19 (inlined Layout::size) {
+                        }
+                        scope 20 (inlined NonNull::<u8>::as_ptr) {
+                        }
+                        scope 21 (inlined std::alloc::dealloc) {
+                            let mut _11: usize;
+                            scope 22 (inlined Layout::size) {
+                            }
+                            scope 23 (inlined Layout::align) {
+                                scope 24 (inlined std::ptr::Alignment::as_usize) {
+                                    let mut _10: u32;
+                                }
+                            }
+                        }
+                    }
+                }
+                scope 5 (inlined Unique::<[T]>::as_ptr) {
+                    scope 6 (inlined NonNull::<[T]>::as_ptr) {
+                    }
+                }
+                scope 7 (inlined Layout::for_value_raw::<[T]>) {
+                    let mut _5: usize;
+                    let mut _6: usize;
+                    scope 8 {
+                        scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) {
+                            let mut _7: std::ptr::Alignment;
+                        }
+                    }
+                    scope 9 (inlined size_of_val_raw::<[T]>) {
+                    }
+                    scope 10 (inlined align_of_val_raw::<[T]>) {
+                    }
+                }
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_2);
+        _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>);
+        StorageLive(_4);
+        _3 = copy _2 as *mut [T] (Transmute);
+        _4 = copy _2 as *const [T] (Transmute);
+        StorageLive(_6);
+        _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable];
+    }
+
+    bb1: {
+        _6 = AlignOf(T);
+        StorageLive(_7);
+        _7 = copy _6 as std::ptr::Alignment (Transmute);
+        _8 = move (_7.0: std::ptr::alignment::AlignmentEnum);
+        StorageDead(_7);
+        StorageDead(_6);
+        StorageDead(_4);
+        switchInt(copy _5) -> [0: bb4, otherwise: bb2];
+    }
+
+    bb2: {
+        StorageLive(_9);
+        _9 = copy _3 as *mut u8 (PtrToPtr);
+        StorageLive(_11);
+        StorageLive(_10);
+        _10 = discriminant(_8);
+        _11 = move _10 as usize (IntToInt);
+        StorageDead(_10);
+        _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable];
+    }
+
+    bb3: {
+        StorageDead(_11);
+        StorageDead(_9);
+        goto -> bb4;
+    }
+
+    bb4: {
+        StorageDead(_2);
+        return;
+    }
+}
diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir
new file mode 100644
index 00000000000..2777bba893b
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir
@@ -0,0 +1,108 @@
+// MIR for `generic_in_place` after PreCodegen
+
+fn generic_in_place(_1: *mut Box<[T]>) -> () {
+    debug ptr => _1;
+    let mut _0: ();
+    scope 1 (inlined drop_in_place::<Box<[T]>> - shim(Some(Box<[T]>))) {
+        scope 2 (inlined <Box<[T]> as Drop>::drop) {
+            let _2: std::ptr::NonNull<[T]>;
+            let mut _3: *mut [T];
+            let mut _4: *const [T];
+            let _12: ();
+            scope 3 {
+                let _8: std::ptr::alignment::AlignmentEnum;
+                scope 4 {
+                    scope 12 (inlined Layout::size) {
+                    }
+                    scope 13 (inlined Unique::<[T]>::cast::<u8>) {
+                        scope 14 (inlined NonNull::<[T]>::cast::<u8>) {
+                            scope 15 (inlined NonNull::<[T]>::as_ptr) {
+                            }
+                        }
+                    }
+                    scope 16 (inlined <NonNull<u8> as From<Unique<u8>>>::from) {
+                        scope 17 (inlined Unique::<u8>::as_non_null_ptr) {
+                        }
+                    }
+                    scope 18 (inlined <std::alloc::Global as Allocator>::deallocate) {
+                        let mut _9: *mut u8;
+                        scope 19 (inlined Layout::size) {
+                        }
+                        scope 20 (inlined NonNull::<u8>::as_ptr) {
+                        }
+                        scope 21 (inlined std::alloc::dealloc) {
+                            let mut _11: usize;
+                            scope 22 (inlined Layout::size) {
+                            }
+                            scope 23 (inlined Layout::align) {
+                                scope 24 (inlined std::ptr::Alignment::as_usize) {
+                                    let mut _10: u32;
+                                }
+                            }
+                        }
+                    }
+                }
+                scope 5 (inlined Unique::<[T]>::as_ptr) {
+                    scope 6 (inlined NonNull::<[T]>::as_ptr) {
+                    }
+                }
+                scope 7 (inlined Layout::for_value_raw::<[T]>) {
+                    let mut _5: usize;
+                    let mut _6: usize;
+                    scope 8 {
+                        scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) {
+                            let mut _7: std::ptr::Alignment;
+                        }
+                    }
+                    scope 9 (inlined size_of_val_raw::<[T]>) {
+                    }
+                    scope 10 (inlined align_of_val_raw::<[T]>) {
+                    }
+                }
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_2);
+        _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>);
+        StorageLive(_4);
+        _3 = copy _2 as *mut [T] (Transmute);
+        _4 = copy _2 as *const [T] (Transmute);
+        StorageLive(_6);
+        _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable];
+    }
+
+    bb1: {
+        _6 = AlignOf(T);
+        StorageLive(_7);
+        _7 = copy _6 as std::ptr::Alignment (Transmute);
+        _8 = move (_7.0: std::ptr::alignment::AlignmentEnum);
+        StorageDead(_7);
+        StorageDead(_6);
+        StorageDead(_4);
+        switchInt(copy _5) -> [0: bb4, otherwise: bb2];
+    }
+
+    bb2: {
+        StorageLive(_9);
+        _9 = copy _3 as *mut u8 (PtrToPtr);
+        StorageLive(_11);
+        StorageLive(_10);
+        _10 = discriminant(_8);
+        _11 = move _10 as usize (IntToInt);
+        StorageDead(_10);
+        _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable];
+    }
+
+    bb3: {
+        StorageDead(_11);
+        StorageDead(_9);
+        goto -> bb4;
+    }
+
+    bb4: {
+        StorageDead(_2);
+        return;
+    }
+}
diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir
new file mode 100644
index 00000000000..2be0a478c85
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir
@@ -0,0 +1,108 @@
+// MIR for `generic_in_place` after PreCodegen
+
+fn generic_in_place(_1: *mut Box<[T]>) -> () {
+    debug ptr => _1;
+    let mut _0: ();
+    scope 1 (inlined drop_in_place::<Box<[T]>> - shim(Some(Box<[T]>))) {
+        scope 2 (inlined <Box<[T]> as Drop>::drop) {
+            let _2: std::ptr::NonNull<[T]>;
+            let mut _3: *mut [T];
+            let mut _4: *const [T];
+            let _12: ();
+            scope 3 {
+                let _8: std::ptr::alignment::AlignmentEnum;
+                scope 4 {
+                    scope 12 (inlined Layout::size) {
+                    }
+                    scope 13 (inlined Unique::<[T]>::cast::<u8>) {
+                        scope 14 (inlined NonNull::<[T]>::cast::<u8>) {
+                            scope 15 (inlined NonNull::<[T]>::as_ptr) {
+                            }
+                        }
+                    }
+                    scope 16 (inlined <NonNull<u8> as From<Unique<u8>>>::from) {
+                        scope 17 (inlined Unique::<u8>::as_non_null_ptr) {
+                        }
+                    }
+                    scope 18 (inlined <std::alloc::Global as Allocator>::deallocate) {
+                        let mut _9: *mut u8;
+                        scope 19 (inlined Layout::size) {
+                        }
+                        scope 20 (inlined NonNull::<u8>::as_ptr) {
+                        }
+                        scope 21 (inlined std::alloc::dealloc) {
+                            let mut _11: usize;
+                            scope 22 (inlined Layout::size) {
+                            }
+                            scope 23 (inlined Layout::align) {
+                                scope 24 (inlined std::ptr::Alignment::as_usize) {
+                                    let mut _10: u64;
+                                }
+                            }
+                        }
+                    }
+                }
+                scope 5 (inlined Unique::<[T]>::as_ptr) {
+                    scope 6 (inlined NonNull::<[T]>::as_ptr) {
+                    }
+                }
+                scope 7 (inlined Layout::for_value_raw::<[T]>) {
+                    let mut _5: usize;
+                    let mut _6: usize;
+                    scope 8 {
+                        scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) {
+                            let mut _7: std::ptr::Alignment;
+                        }
+                    }
+                    scope 9 (inlined size_of_val_raw::<[T]>) {
+                    }
+                    scope 10 (inlined align_of_val_raw::<[T]>) {
+                    }
+                }
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_2);
+        _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>);
+        StorageLive(_4);
+        _3 = copy _2 as *mut [T] (Transmute);
+        _4 = copy _2 as *const [T] (Transmute);
+        StorageLive(_6);
+        _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable];
+    }
+
+    bb1: {
+        _6 = AlignOf(T);
+        StorageLive(_7);
+        _7 = copy _6 as std::ptr::Alignment (Transmute);
+        _8 = move (_7.0: std::ptr::alignment::AlignmentEnum);
+        StorageDead(_7);
+        StorageDead(_6);
+        StorageDead(_4);
+        switchInt(copy _5) -> [0: bb4, otherwise: bb2];
+    }
+
+    bb2: {
+        StorageLive(_9);
+        _9 = copy _3 as *mut u8 (PtrToPtr);
+        StorageLive(_11);
+        StorageLive(_10);
+        _10 = discriminant(_8);
+        _11 = move _10 as usize (IntToInt);
+        StorageDead(_10);
+        _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable];
+    }
+
+    bb3: {
+        StorageDead(_11);
+        StorageDead(_9);
+        goto -> bb4;
+    }
+
+    bb4: {
+        StorageDead(_2);
+        return;
+    }
+}
diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir
new file mode 100644
index 00000000000..2be0a478c85
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir
@@ -0,0 +1,108 @@
+// MIR for `generic_in_place` after PreCodegen
+
+fn generic_in_place(_1: *mut Box<[T]>) -> () {
+    debug ptr => _1;
+    let mut _0: ();
+    scope 1 (inlined drop_in_place::<Box<[T]>> - shim(Some(Box<[T]>))) {
+        scope 2 (inlined <Box<[T]> as Drop>::drop) {
+            let _2: std::ptr::NonNull<[T]>;
+            let mut _3: *mut [T];
+            let mut _4: *const [T];
+            let _12: ();
+            scope 3 {
+                let _8: std::ptr::alignment::AlignmentEnum;
+                scope 4 {
+                    scope 12 (inlined Layout::size) {
+                    }
+                    scope 13 (inlined Unique::<[T]>::cast::<u8>) {
+                        scope 14 (inlined NonNull::<[T]>::cast::<u8>) {
+                            scope 15 (inlined NonNull::<[T]>::as_ptr) {
+                            }
+                        }
+                    }
+                    scope 16 (inlined <NonNull<u8> as From<Unique<u8>>>::from) {
+                        scope 17 (inlined Unique::<u8>::as_non_null_ptr) {
+                        }
+                    }
+                    scope 18 (inlined <std::alloc::Global as Allocator>::deallocate) {
+                        let mut _9: *mut u8;
+                        scope 19 (inlined Layout::size) {
+                        }
+                        scope 20 (inlined NonNull::<u8>::as_ptr) {
+                        }
+                        scope 21 (inlined std::alloc::dealloc) {
+                            let mut _11: usize;
+                            scope 22 (inlined Layout::size) {
+                            }
+                            scope 23 (inlined Layout::align) {
+                                scope 24 (inlined std::ptr::Alignment::as_usize) {
+                                    let mut _10: u64;
+                                }
+                            }
+                        }
+                    }
+                }
+                scope 5 (inlined Unique::<[T]>::as_ptr) {
+                    scope 6 (inlined NonNull::<[T]>::as_ptr) {
+                    }
+                }
+                scope 7 (inlined Layout::for_value_raw::<[T]>) {
+                    let mut _5: usize;
+                    let mut _6: usize;
+                    scope 8 {
+                        scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) {
+                            let mut _7: std::ptr::Alignment;
+                        }
+                    }
+                    scope 9 (inlined size_of_val_raw::<[T]>) {
+                    }
+                    scope 10 (inlined align_of_val_raw::<[T]>) {
+                    }
+                }
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_2);
+        _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>);
+        StorageLive(_4);
+        _3 = copy _2 as *mut [T] (Transmute);
+        _4 = copy _2 as *const [T] (Transmute);
+        StorageLive(_6);
+        _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable];
+    }
+
+    bb1: {
+        _6 = AlignOf(T);
+        StorageLive(_7);
+        _7 = copy _6 as std::ptr::Alignment (Transmute);
+        _8 = move (_7.0: std::ptr::alignment::AlignmentEnum);
+        StorageDead(_7);
+        StorageDead(_6);
+        StorageDead(_4);
+        switchInt(copy _5) -> [0: bb4, otherwise: bb2];
+    }
+
+    bb2: {
+        StorageLive(_9);
+        _9 = copy _3 as *mut u8 (PtrToPtr);
+        StorageLive(_11);
+        StorageLive(_10);
+        _10 = discriminant(_8);
+        _11 = move _10 as usize (IntToInt);
+        StorageDead(_10);
+        _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable];
+    }
+
+    bb3: {
+        StorageDead(_11);
+        StorageDead(_9);
+        goto -> bb4;
+    }
+
+    bb4: {
+        StorageDead(_2);
+        return;
+    }
+}
diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs
new file mode 100644
index 00000000000..11fb7afef0f
--- /dev/null
+++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs
@@ -0,0 +1,19 @@
+//@ compile-flags: -O -Zmir-opt-level=2
+// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+#![crate_type = "lib"]
+
+// EMIT_MIR drop_boxed_slice.generic_in_place.PreCodegen.after.mir
+pub unsafe fn generic_in_place<T: Copy>(ptr: *mut Box<[T]>) {
+    // CHECK-LABEL: fn generic_in_place(_1: *mut Box<[T]>)
+    // CHECK: (inlined <Box<[T]> as Drop>::drop)
+    // CHECK: [[SIZE:_.+]] = std::intrinsics::size_of_val::<[T]>
+    // CHECK: [[ALIGN:_.+]] = AlignOf(T);
+    // CHECK: [[B:_.+]] = copy [[ALIGN]] as std::ptr::Alignment (Transmute);
+    // CHECK: [[C:_.+]] = move ([[B]].0: std::ptr::alignment::AlignmentEnum);
+    // CHECK: [[D:_.+]] = discriminant([[C]]);
+    // CHECK: [[E:_.+]] = move [[D]] as usize (IntToInt);
+    // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[E]]) ->
+    std::ptr::drop_in_place(ptr)
+}