From edf729ea34cf3217c91dfb4c33e68e57aad63498 Mon Sep 17 00:00:00 2001 From: Vastargazing Date: Sun, 2 Aug 2026 09:03:11 +0300 Subject: [PATCH] Emit noundef on small aggregate returns in the x86 Win32 ABI The x86 Win32 (MSVC) ABI returns small aggregates in an integer register via `cast_to`, which drops the argument attributes -- so these returns never carried `noundef`, unlike scalars, which get it from `arg_attrs_for_rust_scalar`. Switch those casts to `cast_to_maybe_noundef` so a return whose layout is provably free of uninit bytes gets `noundef`, while padded layouts correctly omit it. --- .../rustc_target/src/callconv/x86_win32.rs | 8 +- tests/codegen-llvm/abi-noundef-cast-win32.rs | 95 +++++++++++++++++++ 2 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 tests/codegen-llvm/abi-noundef-cast-win32.rs diff --git a/compiler/rustc_target/src/callconv/x86_win32.rs b/compiler/rustc_target/src/callconv/x86_win32.rs index 824e7cc098a46..4cee6579a0b09 100644 --- a/compiler/rustc_target/src/callconv/x86_win32.rs +++ b/compiler/rustc_target/src/callconv/x86_win32.rs @@ -27,10 +27,10 @@ pub(crate) fn compute_abi_info<'a, Ty, C>( // - fixed in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85667 if t.abi_return_struct_as_int || opts.reg_struct_return { match fn_abi.ret.layout.size.bytes() { - 1 => fn_abi.ret.cast_to(Reg::i8()), - 2 => fn_abi.ret.cast_to(Reg::i16()), - 4 => fn_abi.ret.cast_to(Reg::i32()), - 8 => fn_abi.ret.cast_to(Reg::i64()), + 1 => fn_abi.ret.cast_to_maybe_noundef(Reg::i8(), cx), + 2 => fn_abi.ret.cast_to_maybe_noundef(Reg::i16(), cx), + 4 => fn_abi.ret.cast_to_maybe_noundef(Reg::i32(), cx), + 8 => fn_abi.ret.cast_to_maybe_noundef(Reg::i64(), cx), _ => fn_abi.ret.make_indirect(), } } else { diff --git a/tests/codegen-llvm/abi-noundef-cast-win32.rs b/tests/codegen-llvm/abi-noundef-cast-win32.rs new file mode 100644 index 0000000000000..2f928a41d8410 --- /dev/null +++ b/tests/codegen-llvm/abi-noundef-cast-win32.rs @@ -0,0 +1,95 @@ +// Checks that small aggregates returned as an integer under the Win32 (MSVC) ABI +// (`abi_return_struct_as_int`) carry `noundef` when the layout is provably free of +// uninit bytes, and omit it when the layout may contain uninit bytes (padding or a +// union). +// +// See . + +//@ add-minicore +//@ compile-flags: --target i686-pc-windows-msvc -Cno-prepopulate-passes -Copt-level=3 +//@ needs-llvm-components: x86 + +#![feature(no_core, lang_items)] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +// Fully-defined layouts, one per integer register size (i8/i16/i32/i64). + +#[repr(C)] +pub struct OneU8 { + x: u8, +} + +#[repr(C)] +pub struct TwoU8 { + x: u8, + y: u8, +} + +#[repr(C)] +pub struct TwoU16 { + x: u16, + y: u16, +} + +#[repr(C)] +pub struct TwoU32 { + x: u32, + y: u32, +} + +// Layouts that may contain uninit bytes. + +// Tail padding: `u8` at offset 4 leaves bytes 5..8 uninit. +#[repr(C)] +pub struct Padded { + x: u32, + y: u8, +} + +#[repr(C)] +pub union U { + x: u32, + y: u32, +} + +// CHECK: define noundef i8 @ret_one_u8() +#[no_mangle] +pub extern "C" fn ret_one_u8() -> OneU8 { + OneU8 { x: 1 } +} + +// CHECK: define noundef i16 @ret_two_u8() +#[no_mangle] +pub extern "C" fn ret_two_u8() -> TwoU8 { + TwoU8 { x: 1, y: 2 } +} + +// CHECK: define noundef i32 @ret_two_u16() +#[no_mangle] +pub extern "C" fn ret_two_u16() -> TwoU16 { + TwoU16 { x: 1, y: 2 } +} + +// CHECK: define noundef i64 @ret_two_u32() +#[no_mangle] +pub extern "C" fn ret_two_u32() -> TwoU32 { + TwoU32 { x: 1, y: 2 } +} + +// Tail padding -> no `noundef`. +// CHECK: define i64 @ret_padded() +#[no_mangle] +pub extern "C" fn ret_padded() -> Padded { + Padded { x: 1, y: 2 } +} + +// Union -> no `noundef`. +// CHECK: define i32 @ret_union() +#[no_mangle] +pub extern "C" fn ret_union() -> U { + U { x: 1 } +}