Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 36 additions & 15 deletions compiler/rustc_mir_transform/src/instsimplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,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_size_or_align_of_val(terminator, &mut block.statements);
ctx.simplify_intrinsic_assert(terminator);
ctx.simplify_nounwind_call(terminator);
simplify_duplicate_switch_targets(terminator);
Expand Down Expand Up @@ -246,13 +246,18 @@ 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(
/// Simplify `size_of_val` and `align_of_val` if we don't actually need
/// to look at the value in order to calculate the result:
/// - For `Sized` types we can always do this for both,
/// - For `align_of_val::<[T]>` we can return `align_of::<T>()`, since it
/// doesn't depend on the slice's length and the elements are sized.
///
/// This is here so it can run after inlining, where it's more useful.
/// (LowerIntrinsics is done in cleanup, before the optimization passes.)
///
/// Note that we intentionally just produce the lang item constants so this
/// works on generic types and avoids any risk of layout calculation cycles.
fn simplify_size_or_align_of_val(
&self,
terminator: &mut Terminator<'tcx>,
statements: &mut Vec<Statement<'tcx>>,
Expand All @@ -263,19 +268,35 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
} = &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()
{
let align_def_id = self.tcx.require_lang_item(LangItem::AlignOf, source_info.span);
let align_const = Operand::unevaluated_constant(
let lang_item = if self.tcx.is_intrinsic(fn_def_id, sym::size_of_val) {
LangItem::SizeOf
} else if self.tcx.is_intrinsic(fn_def_id, sym::align_of_val) {
LangItem::AlignOf
} else {
return;
};
let generic_ty = generics.type_at(0);
let ty = if generic_ty.is_sized(self.tcx, self.typing_env) {
generic_ty
} else if let LangItem::AlignOf = lang_item
&& let ty::Slice(elem_ty) = *generic_ty.kind()
{
elem_ty
} else {
return;
};

let const_def_id = self.tcx.require_lang_item(lang_item, source_info.span);
let const_op = Operand::unevaluated_constant(
self.tcx,
align_def_id,
&[elem_ty.into()],
const_def_id,
&[ty.into()],
source_info.span,
);
statements.push(Statement::new(
source_info,
StatementKind::Assign(Box::new((*destination, Rvalue::Use(align_const)))),
StatementKind::Assign(Box::new((*destination, Rvalue::Use(const_op)))),
));
terminator.kind = TerminatorKind::Goto { target: *destination_block };
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
- // MIR for `align_of_val_sized` before InstSimplify-after-simplifycfg
+ // MIR for `align_of_val_sized` after InstSimplify-after-simplifycfg

fn align_of_val_sized(_1: &T) -> usize {
debug val => _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 = const <T as std::mem::SizedTypeProperties>::ALIGN;
+ goto -> bb1;
}

bb1: {
StorageDead(_2);
return;
}
}

19 changes: 19 additions & 0 deletions tests/mir-opt/instsimplify/align_or_size_of_sized_val.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//@ test-mir-pass: InstSimplify-after-simplifycfg
//@ needs-unwind

#![crate_type = "lib"]
#![feature(core_intrinsics)]

// EMIT_MIR align_or_size_of_sized_val.align_of_val_sized.InstSimplify-after-simplifycfg.diff
pub fn align_of_val_sized<T>(val: &T) -> usize {
// CHECK-LABEL: fn align_of_val_sized
// CHECK: _0 = const <T as std::mem::SizedTypeProperties>::ALIGN;
unsafe { core::intrinsics::align_of_val(val) }
}

// EMIT_MIR align_or_size_of_sized_val.size_of_val_sized.InstSimplify-after-simplifycfg.diff
pub fn size_of_val_sized<T>(val: &T) -> usize {
// CHECK-LABEL: fn size_of_val_sized
// CHECK: _0 = const <T as std::mem::SizedTypeProperties>::SIZE;
unsafe { core::intrinsics::size_of_val(val) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
- // MIR for `size_of_val_sized` before InstSimplify-after-simplifycfg
+ // MIR for `size_of_val_sized` after InstSimplify-after-simplifycfg

fn size_of_val_sized(_1: &T) -> usize {
debug val => _1;
let mut _0: usize;
let mut _2: *const T;

bb0: {
StorageLive(_2);
_2 = &raw const (*_1);
- _0 = std::intrinsics::size_of_val::<T>(move _2) -> [return: bb1, unwind unreachable];
+ _0 = const <T as std::mem::SizedTypeProperties>::SIZE;
+ goto -> bb1;
}

bb1: {
StorageDead(_2);
return;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// MIR for `drop_bytes` after PreCodegen

fn drop_bytes(_1: *mut Box<[u8; 1024]>) -> () {
debug x => _1;
let mut _0: ();
scope 1 (inlined drop_in_place::<Box<[u8; 1024]>> - shim(Some(Box<[u8; 1024]>))) {
scope 2 (inlined <Box<[u8; 1024]> as Drop>::drop) {
let _2: std::ptr::NonNull<[u8; 1024]>;
let _4: ();
scope 3 {
scope 4 {
scope 17 (inlined Layout::size) {
}
scope 18 (inlined std::ptr::Unique::<[u8; 1024]>::cast::<u8>) {
scope 19 (inlined NonNull::<[u8; 1024]>::cast::<u8>) {
scope 20 (inlined NonNull::<[u8; 1024]>::as_ptr) {
}
}
}
scope 21 (inlined <NonNull<u8> as From<std::ptr::Unique<u8>>>::from) {
scope 22 (inlined std::ptr::Unique::<u8>::as_non_null_ptr) {
}
}
scope 23 (inlined <std::alloc::Global as Allocator>::deallocate) {
scope 24 (inlined std::alloc::Global::deallocate_impl) {
scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
let mut _3: *mut u8;
scope 26 (inlined Layout::size) {
}
scope 27 (inlined NonNull::<u8>::as_ptr) {
}
scope 28 (inlined std::alloc::dealloc) {
scope 29 (inlined Layout::size) {
}
scope 30 (inlined Layout::alignment) {
}
}
}
}
}
}
scope 5 (inlined std::ptr::Unique::<[u8; 1024]>::as_ptr) {
scope 6 (inlined NonNull::<[u8; 1024]>::as_ptr) {
}
}
scope 7 (inlined Layout::for_value_raw::<[u8; 1024]>) {
scope 8 {
scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) {
}
}
scope 9 (inlined size_of_val_raw::<[u8; 1024]>) {
}
scope 10 (inlined std::ptr::Alignment::of_val_raw::<[u8; 1024]>) {
scope 11 {
scope 13 (inlined #[track_caller] std::ptr::Alignment::new_unchecked) {
scope 14 (inlined core::ub_checks::check_language_ub) {
scope 15 (inlined core::ub_checks::check_language_ub::runtime) {
}
}
}
}
scope 12 (inlined align_of_val_raw::<[u8; 1024]>) {
}
}
}
}
}
}

bb0: {
_2 = copy (((*_1).0: std::ptr::Unique<[u8; 1024]>).0: std::ptr::NonNull<[u8; 1024]>);
StorageLive(_3);
_3 = copy _2 as *mut u8 (Transmute);
_4 = alloc::alloc::__rust_dealloc(move _3, const 1024_usize, const std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }}) -> [return: bb1, unwind unreachable];
}

bb1: {
StorageDead(_3);
return;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// MIR for `drop_bytes` after PreCodegen

fn drop_bytes(_1: *mut Box<[u8; 1024]>) -> () {
debug x => _1;
let mut _0: ();
scope 1 (inlined drop_in_place::<Box<[u8; 1024]>> - shim(Some(Box<[u8; 1024]>))) {
scope 2 (inlined <Box<[u8; 1024]> as Drop>::drop) {
let _2: std::ptr::NonNull<[u8; 1024]>;
let _4: ();
scope 3 {
scope 4 {
scope 17 (inlined Layout::size) {
}
scope 18 (inlined std::ptr::Unique::<[u8; 1024]>::cast::<u8>) {
scope 19 (inlined NonNull::<[u8; 1024]>::cast::<u8>) {
scope 20 (inlined NonNull::<[u8; 1024]>::as_ptr) {
}
}
}
scope 21 (inlined <NonNull<u8> as From<std::ptr::Unique<u8>>>::from) {
scope 22 (inlined std::ptr::Unique::<u8>::as_non_null_ptr) {
}
}
scope 23 (inlined <std::alloc::Global as Allocator>::deallocate) {
scope 24 (inlined std::alloc::Global::deallocate_impl) {
scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
let mut _3: *mut u8;
scope 26 (inlined Layout::size) {
}
scope 27 (inlined NonNull::<u8>::as_ptr) {
}
scope 28 (inlined std::alloc::dealloc) {
scope 29 (inlined Layout::size) {
}
scope 30 (inlined Layout::alignment) {
}
}
}
}
}
}
scope 5 (inlined std::ptr::Unique::<[u8; 1024]>::as_ptr) {
scope 6 (inlined NonNull::<[u8; 1024]>::as_ptr) {
}
}
scope 7 (inlined Layout::for_value_raw::<[u8; 1024]>) {
scope 8 {
scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) {
}
}
scope 9 (inlined size_of_val_raw::<[u8; 1024]>) {
}
scope 10 (inlined std::ptr::Alignment::of_val_raw::<[u8; 1024]>) {
scope 11 {
scope 13 (inlined #[track_caller] std::ptr::Alignment::new_unchecked) {
scope 14 (inlined core::ub_checks::check_language_ub) {
scope 15 (inlined core::ub_checks::check_language_ub::runtime) {
}
}
}
}
scope 12 (inlined align_of_val_raw::<[u8; 1024]>) {
}
}
}
}
}
}

bb0: {
_2 = copy (((*_1).0: std::ptr::Unique<[u8; 1024]>).0: std::ptr::NonNull<[u8; 1024]>);
StorageLive(_3);
_3 = copy _2 as *mut u8 (Transmute);
_4 = alloc::alloc::__rust_dealloc(move _3, const 1024_usize, const std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }}) -> [return: bb1, unwind unreachable];
}

bb1: {
StorageDead(_3);
return;
}
}
Loading
Loading