diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 65c3cf1a60748..c3f148d7f7ec4 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -8,8 +8,8 @@ use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt}; use rustc_session::config::OptLevel; use rustc_span::def_id::DefId; use rustc_target::abi::call::{ - ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, Conv, FnAbi, PassMode, Reg, RegKind, - RiscvInterruptKind, + ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, Conv, FnAbi, PassMode, Reg, + RegKind, RiscvInterruptKind, }; use rustc_target::abi::*; use rustc_target::spec::abi::Abi as SpecAbi; @@ -784,6 +784,21 @@ fn fn_abi_adjust_for_abi<'tcx>( // an LLVM aggregate type for this leads to bad optimizations, // so we pick an appropriately sized integer type instead. arg.cast_to(Reg { kind: RegKind::Integer, size }); + + // Let's see if we can add a `noundef`. This is only legal for arrays, definitely + // not for unions. This is also legal for `#[repr(transparent)] struct` or + // `#[repr(transparent)] enum` containing array. Note that `#[repr(transparent)]` + // can contain other `#[repr(transparent)]` structs or enums, which can eventually + // contain an array! + if arg.layout.ty.is_array() || is_transparent_array(cx, arg.layout) { + // Fixup arg attribute with `noundef`. + let PassMode::Cast { ref mut cast, .. } = &mut arg.mode else { + bug!("this cannot fail because of the previous cast_to `Reg`"); + }; + + let box CastTarget { ref mut attrs, .. } = cast; + attrs.set(ArgAttribute::NoUndef); + } } // If we deduced that this parameter was read-only, add that to the attribute list now. @@ -819,6 +834,20 @@ fn fn_abi_adjust_for_abi<'tcx>( Ok(()) } +fn is_transparent_array<'tcx>( + cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, + outermost_layout: TyAndLayout<'tcx>, +) -> bool { + let mut adt_layout = outermost_layout; + // Recursively walk a layout, seeing through all `#[repr(transparent)]` layers. + while adt_layout.is_transparent::>>() + && let Some((_, layout)) = adt_layout.non_1zst_field(cx) + { + adt_layout = layout; + } + adt_layout.ty.is_array() +} + #[tracing::instrument(level = "debug", skip(cx))] fn make_thin_self_ptr<'tcx>( cx: &(impl HasTyCtxt<'tcx> + HasParamEnv<'tcx>), diff --git a/tests/codegen/array-immediate-param-noundef.rs b/tests/codegen/array-immediate-param-noundef.rs new file mode 100644 index 0000000000000..f2457de537592 --- /dev/null +++ b/tests/codegen/array-immediate-param-noundef.rs @@ -0,0 +1,125 @@ +// Check that small array immediates that fits in target pointer size in argument position have +// `noundef` parameter metadata. Note that the `noundef` parameter metadata is only applied if: +// - `!arg.layout.is_unsized() && size <= Pointer(AddressSpace::DATA).size(cx)` +// - optimizations are turned on. +// +//@ only-64bit (presence of noundef depends on pointer width) +//@ compile-flags: -C no-prepopulate-passes -O +#![crate_type = "lib"] + +// CHECK: define noundef i64 @short_array_u64x1(i64 noundef %{{.*}}) +#[no_mangle] +pub fn short_array_u64x1(v: [u64; 1]) -> [u64; 1] { + v +} + +// CHECK: define noundef i32 @short_array_u32x1(i32 noundef %{{.*}}) +#[no_mangle] +pub fn short_array_u32x1(v: [u32; 1]) -> [u32; 1] { + v +} + +// CHECK: define noundef i64 @short_array_u32x2(i64 noundef %{{.*}}) +#[no_mangle] +pub fn short_array_u32x2(v: [u32; 2]) -> [u32; 2] { + v +} + +// CHECK: define noundef i16 @short_array_u16x1(i16 noundef %{{.*}}) +#[no_mangle] +pub fn short_array_u16x1(v: [u16; 1]) -> [u16; 1] { + v +} + +// CHECK: define noundef i32 @short_array_u16x2(i32 noundef %{{.*}}) +#[no_mangle] +pub fn short_array_u16x2(v: [u16; 2]) -> [u16; 2] { + v +} + +// CHECK: define noundef i48 @short_array_u16x3(i48 noundef %{{.*}}) +#[no_mangle] +pub fn short_array_u16x3(v: [u16; 3]) -> [u16; 3] { + v +} + +// CHECK: define noundef i64 @short_array_u16x4(i64 noundef %{{.*}}) +#[no_mangle] +pub fn short_array_u16x4(v: [u16; 4]) -> [u16; 4] { + v +} + +// CHECK: define noundef i8 @short_array_u8x1(i8 noundef %{{.*}}) +#[no_mangle] +pub fn short_array_u8x1(v: [u8; 1]) -> [u8; 1] { + v +} + +// CHECK: define noundef i16 @short_array_u8x2(i16 noundef %{{.*}}) +#[no_mangle] +pub fn short_array_u8x2(v: [u8; 2]) -> [u8; 2] { + v +} + +// CHECK: define noundef i24 @short_array_u8x3(i24 noundef %{{.*}}) +#[no_mangle] +pub fn short_array_u8x3(v: [u8; 3]) -> [u8; 3] { + v +} + +// CHECK: define noundef i64 @short_array_u8x8(i64 noundef %{{.*}}) +#[no_mangle] +pub fn short_array_u8x8(v: [u8; 8]) -> [u8; 8] { + v +} + +#[repr(transparent)] +pub struct Foo([u8; 4]); + +// CHECK: define noundef i32 @repr_transparent_struct_short_array(i32 noundef %{{.*}}) +#[no_mangle] +pub fn repr_transparent_struct_short_array(v: Foo) -> Foo { + v +} + +#[repr(transparent)] +pub enum Bar { + Default([u8; 4]), +} + +// CHECK: define noundef i32 @repr_transparent_enum_short_array(i32 noundef %{{.*}}) +#[no_mangle] +pub fn repr_transparent_enum_short_array(v: Bar) -> Bar { + v +} + +#[repr(transparent)] +pub struct Owo([u8; 4]); + +#[repr(transparent)] +pub struct Uwu(Owo); + +#[repr(transparent)] +pub struct Oowoo(Uwu); + +// CHECK: define noundef i32 @repr_transparent_nested_struct_short_array(i32 noundef %{{.*}}) +#[no_mangle] +pub fn repr_transparent_nested_struct_short_array(v: Oowoo) -> Oowoo { + v +} + +// # Negative examples + +// This inner struct is *not* `#[repr(transparent)]`, so we must not emit `noundef` for the outer +// struct. +pub struct NotTransparent([u8; 4]); + +#[repr(transparent)] +pub struct Transparent(NotTransparent); + +// CHECK-LABEL: not_all_transparent_nested_struct_short_array +// CHECK-NOT: noundef +#[no_mangle] +pub fn not_all_transparent_nested_struct_short_array(v: Transparent) -> Transparent { + v +}