mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-26 13:01:27 +03:00
97bd985467
`adjust_for_rust_abi` was casting small aggregates to an integer register without propagating `noundef`, causing a performance regression (#123183) — LLVM could no longer assume the bits were fully defined. Add `layout_is_noundef` (conservative) + `fields_are_noundef` helper, then use `cast_to_with_attrs` to forward `NoUndef` when proven: Scalar → `!is_uninit_valid()` ScalarPair → both scalars valid + `s1.size + s2.size == layout.size` (size equality rejects layouts with inter-scalar padding) Array → recurse into element; empty arrays unconditionally noundef Arbitrary → `Variants::Single` required; walk fields in offset order — any gap, non-noundef field, or trailing pad returns false Union / Primitive / Simd → false (conservative) Bless `pass-indirectly-attr.stderr` and `debuginfo-dse.rs` for the new attribute on Cast args. Add `tests/codegen-llvm/abi-noundef-cast.rs` covering positive Cast cases (arrays, plain structs, single-variant enum) and negative Cast cases (MaybeUninit, multi-variant enum, field/pair gap, trailing padding). Fixes #123183. Co-authored-by: Ralf Jung <post@ralfj.de>
207 lines
3.9 KiB
Rust
207 lines
3.9 KiB
Rust
// Verify that `PassMode::Cast` arguments/returns in the Rust ABI carry `noundef`
|
|
// when the original layout provably contains no uninit bytes, and correctly omit
|
|
// it when uninit bytes or padding may be present.
|
|
//
|
|
// See <https://github.com/rust-lang/rust/issues/123183>.
|
|
|
|
//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes
|
|
//@ only-64bit
|
|
|
|
#![crate_type = "lib"]
|
|
|
|
use std::mem::MaybeUninit;
|
|
|
|
// CHECK-LABEL: @arg_array_u32x2(
|
|
// CHECK-SAME: i64 noundef
|
|
#[no_mangle]
|
|
pub fn arg_array_u32x2(v: [u32; 2]) -> u32 {
|
|
v[0]
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_array_u8x4(
|
|
// CHECK-SAME: i32 noundef
|
|
#[no_mangle]
|
|
pub fn arg_array_u8x4(v: [u8; 4]) -> u8 {
|
|
v[0]
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_nested_array(
|
|
// CHECK-SAME: i64 noundef
|
|
#[no_mangle]
|
|
pub fn arg_nested_array(v: [[u8; 2]; 4]) -> u8 {
|
|
v[0][0]
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_array_bool(
|
|
// CHECK-SAME: i64 noundef
|
|
#[no_mangle]
|
|
pub fn arg_array_bool(v: [bool; 8]) -> bool {
|
|
v[0]
|
|
}
|
|
|
|
struct FourU8 {
|
|
a: u8,
|
|
b: u8,
|
|
c: u8,
|
|
d: u8,
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_four_u8(
|
|
// CHECK-SAME: i32 noundef
|
|
#[no_mangle]
|
|
pub fn arg_four_u8(v: FourU8) -> u8 {
|
|
v.a
|
|
}
|
|
|
|
struct Wrapper([u32; 2]);
|
|
|
|
// CHECK-LABEL: @arg_newtype_wrapper(
|
|
// CHECK-SAME: i64 noundef
|
|
#[no_mangle]
|
|
pub fn arg_newtype_wrapper(v: Wrapper) -> u32 {
|
|
(v.0)[0]
|
|
}
|
|
|
|
enum SingleVariant {
|
|
Only([u32; 2]),
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_single_variant_enum(
|
|
// CHECK-SAME: i64 noundef
|
|
#[no_mangle]
|
|
pub fn arg_single_variant_enum(v: SingleVariant) -> u32 {
|
|
match v {
|
|
SingleVariant::Only(a) => a[0],
|
|
}
|
|
}
|
|
|
|
struct ContainsScalarPair {
|
|
a: (u16, u16),
|
|
b: u32,
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_contains_scalar_pair(
|
|
// CHECK-SAME: i64 noundef
|
|
#[no_mangle]
|
|
pub fn arg_contains_scalar_pair(v: ContainsScalarPair) -> u32 {
|
|
v.b
|
|
}
|
|
|
|
// CHECK: define noundef i64 @ret_array_u32x2(
|
|
#[no_mangle]
|
|
pub fn ret_array_u32x2(x: u32, y: u32) -> [u32; 2] {
|
|
[x, y]
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_maybeuninit_u8x8(
|
|
// CHECK-SAME: i64 %
|
|
#[no_mangle]
|
|
pub fn arg_maybeuninit_u8x8(v: [MaybeUninit<u8>; 8]) -> MaybeUninit<u8> {
|
|
v[0]
|
|
}
|
|
|
|
enum MultiVariant {
|
|
A(u8),
|
|
B(u16),
|
|
C,
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_multi_variant_enum(
|
|
// CHECK-SAME: i32 %
|
|
#[no_mangle]
|
|
pub fn arg_multi_variant_enum(v: MultiVariant) -> u8 {
|
|
match v {
|
|
MultiVariant::A(x) => x,
|
|
MultiVariant::B(_) | MultiVariant::C => 0,
|
|
}
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct HasFieldGap {
|
|
a: u8,
|
|
b: u16,
|
|
c: u8,
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_struct_field_gap(
|
|
// CHECK-SAME: i48 %
|
|
#[no_mangle]
|
|
pub fn arg_struct_field_gap(v: HasFieldGap) -> u8 {
|
|
v.a
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct HasPaddedPairField {
|
|
a: (u8, u16),
|
|
b: u8,
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_struct_padded_pair_field(
|
|
// CHECK-SAME: i48 %
|
|
#[no_mangle]
|
|
pub fn arg_struct_padded_pair_field(v: HasPaddedPairField) -> u8 {
|
|
v.b
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct HasUndefPairField {
|
|
a: (MaybeUninit<u16>, u16),
|
|
b: u32,
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_struct_undef_pair_field(
|
|
// CHECK-SAME: i64 %
|
|
#[no_mangle]
|
|
pub fn arg_struct_undef_pair_field(v: HasUndefPairField) -> u32 {
|
|
v.b
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_triple_maybeuninit_u8(
|
|
// CHECK-SAME: i24 %
|
|
#[no_mangle]
|
|
pub fn arg_triple_maybeuninit_u8(
|
|
v: (MaybeUninit<u8>, MaybeUninit<u8>, MaybeUninit<u8>),
|
|
) -> MaybeUninit<u8> {
|
|
v.0
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct HasTrailingPadding {
|
|
x: u32,
|
|
y: u16,
|
|
z: u8,
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_struct_trailing_pad(
|
|
// CHECK-SAME: i64 %
|
|
#[no_mangle]
|
|
pub fn arg_struct_trailing_pad(v: HasTrailingPadding) -> u32 {
|
|
v.x
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_tuple_i8_i16(
|
|
// CHECK-SAME: i8 noundef
|
|
// CHECK-SAME: i16 noundef
|
|
#[no_mangle]
|
|
pub fn arg_tuple_i8_i16(v: (i8, i16)) -> i8 {
|
|
v.0
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_tuple_i16_maybeuninit(
|
|
// CHECK-SAME: i16 noundef
|
|
// CHECK-SAME: i16 %
|
|
#[no_mangle]
|
|
pub fn arg_tuple_i16_maybeuninit(v: (i16, MaybeUninit<i16>)) -> i16 {
|
|
v.0
|
|
}
|
|
|
|
// CHECK-LABEL: @arg_result_i32(
|
|
// CHECK-SAME: i32 noundef
|
|
// CHECK-SAME: i32 noundef
|
|
#[no_mangle]
|
|
pub fn arg_result_i32(v: Result<i32, i32>) -> i32 {
|
|
match v {
|
|
Ok(x) | Err(x) => x,
|
|
}
|
|
}
|