diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index a80088e41cd31..8c02d6a56cf20 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -205,6 +205,12 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { + // Perform some sign/arg extension on integers to mirror our C ABI + for arg in fn_abi.args.iter_mut() { + arg.extend_integer_width_to(8); + } + fn_abi.ret.extend_integer_width_to(8); + // Avoid returning floats in x87 registers on x86 as loading and storing from x87 // registers will quiet signalling NaNs. Also avoid using SSE registers since they // are not always available (depending on target features). diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 8873fb13efe03..8dd5a6ae91c8e 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -13,9 +13,7 @@ use rustc_middle::ty::layout::{ use rustc_middle::ty::{self, InstanceKind, ShimKind, Ty, TyCtxt, Unnormalized}; use rustc_span::DUMMY_SP; use rustc_span::def_id::DefId; -use rustc_target::callconv::{ - AbiMap, ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, FnAbi, PassMode, -}; +use rustc_target::callconv::{AbiMap, ArgAbi, ArgAttribute, ArgAttributes, FnAbi, PassMode}; use tracing::debug; pub(crate) fn provide(providers: &mut Providers) { @@ -331,13 +329,6 @@ fn arg_attrs_for_rust_scalar<'tcx>( ) -> ArgAttributes { let mut attrs = ArgAttributes::new(); - // Booleans are always a noundef i1 that needs to be zero-extended. - if scalar.is_bool() { - attrs.ext(ArgExtension::Zext); - attrs.set(ArgAttribute::NoUndef); - return attrs; - } - if !scalar.is_uninit_valid() { attrs.set(ArgAttribute::NoUndef); } diff --git a/tests/assembly-llvm/aarch64/mask-bool-ffi-return.rs b/tests/assembly-llvm/aarch64/mask-bool-ffi-return.rs new file mode 100644 index 0000000000000..c3d81deef3056 --- /dev/null +++ b/tests/assembly-llvm/aarch64/mask-bool-ffi-return.rs @@ -0,0 +1,41 @@ +//@ add-minicore +//@ assembly-output: emit-asm +//@ needs-llvm-components: aarch64 +//@ compile-flags: -Copt-level=3 --target=aarch64-unknown-linux-gnu + +// Previously, Rust used to assume omnipresent zero-extension on bool in FFI returns +// which resulted in LLVM assuming that a register did not require explicit masking (e.g. `and`) +// and could be correctly handled via `cmp w0, #0`. +// `cmp` looks at the full register, but only the 8 bits that contain the bool are specified, +// and this is particularly glaring in the event of a branch or csel based on this. +// Thus we are looking for explicit handling like `tst w0, #0x1` or `and w0, #0xFF` + +// NOTE: simplifying this further is risky as `tbnz x0, #0, ...` only examines 1 bit, +// so LLVM will optimize it all away if there's an immediate jump to another function + +#![crate_type = "lib"] +#![feature(no_core)] +#![no_core] + +extern crate minicore; + +#[repr(C)] +struct Bools { + a: bool, + b: bool, +} + +#[link(name = "rust_test_helpers")] +unsafe extern "C" { + safe fn bools_get_first_bool(bools: Bools) -> bool; +} + +// CHECK-LABEL: broken +pub fn broken() -> i32 { + let bools = Bools { a: false, b: true }; + // CHECK: bl bools_get_first_bool + // CHECK-NOT: cmp + // CHECK: tst w0, #0x1 + // CHECK-NOT: cmp + if bools_get_first_bool(bools) { 123 } else { 321 } +} diff --git a/tests/codegen-llvm/enum/enum-aggregate.rs b/tests/codegen-llvm/enum/enum-aggregate.rs index 46a1a03e96371..dd707a2ec29cb 100644 --- a/tests/codegen-llvm/enum/enum-aggregate.rs +++ b/tests/codegen-llvm/enum/enum-aggregate.rs @@ -10,7 +10,7 @@ use std::ptr::NonNull; #[no_mangle] fn make_some_bool(x: bool) -> Option { - // CHECK-LABEL: i8 @make_some_bool(i1 zeroext %x) + // CHECK-LABEL: i8 @make_some_bool(i1{{( zeroext)?}} %x) // CHECK-NEXT: start: // CHECK-NEXT: %[[WIDER:.+]] = zext i1 %x to i8 // CHECK-NEXT: ret i8 %[[WIDER]] diff --git a/tests/codegen-llvm/function-arguments-noopt.rs b/tests/codegen-llvm/function-arguments-noopt.rs index c80f119696df1..5a3e3e0825e5b 100644 --- a/tests/codegen-llvm/function-arguments-noopt.rs +++ b/tests/codegen-llvm/function-arguments-noopt.rs @@ -10,7 +10,7 @@ pub struct S { _field: [i32; 8], } -// CHECK: zeroext i1 @boolean(i1 zeroext %x) +// CHECK: {{(zeroext)?}} i1 @boolean(i1{{( zeroext)?}} %x) #[no_mangle] pub fn boolean(x: bool) -> bool { x @@ -19,7 +19,7 @@ pub fn boolean(x: bool) -> bool { // CHECK-LABEL: @boolean_call #[no_mangle] pub fn boolean_call(x: bool, f: fn(bool) -> bool) -> bool { - // CHECK: call zeroext i1 %f(i1 zeroext %x) + // CHECK: call{{( zeroext)?}} i1 %f(i1{{( zeroext)?}} %x) f(x) } @@ -55,7 +55,7 @@ pub fn struct_call(x: S, f: fn(S) -> S) -> S { f(x) } -// CHECK: { i1, i8 } @enum_(i1 zeroext %x.0, i8 %x.1) +// CHECK: { i1, i8 } @enum_(i1{{( zeroext)?}} %x.0, i8 %x.1) #[no_mangle] pub fn enum_(x: Option) -> Option { x @@ -64,6 +64,6 @@ pub fn enum_(x: Option) -> Option { // CHECK-LABEL: @enum_call #[no_mangle] pub fn enum_call(x: Option, f: fn(Option) -> Option) -> Option { - // CHECK: call { i1, i8 } %f(i1 zeroext %x.0, i8 %x.1) + // CHECK: call { i1, i8 } %f(i1{{( zeroext)?}} %x.0, i8 %x.1) f(x) } diff --git a/tests/codegen-llvm/function-arguments.rs b/tests/codegen-llvm/function-arguments.rs index ef056769b147d..841df06a2b0c8 100644 --- a/tests/codegen-llvm/function-arguments.rs +++ b/tests/codegen-llvm/function-arguments.rs @@ -26,7 +26,7 @@ pub enum MyBool { False, } -// CHECK: noundef zeroext i1 @boolean(i1 noundef zeroext %x) +// CHECK: noundef{{( zeroext)?}} i1 @boolean(i1 noundef{{( zeroext)?}} %x) #[no_mangle] pub fn boolean(x: bool) -> bool { x @@ -38,7 +38,7 @@ pub fn maybeuninit_boolean(x: MaybeUninit) -> MaybeUninit { x } -// CHECK: noundef zeroext i1 @enum_bool(i1 noundef zeroext %x) +// CHECK: noundef{{( zeroext)?}} i1 @enum_bool(i1 noundef{{( zeroext)?}} %x) #[no_mangle] pub fn enum_bool(x: MyBool) -> MyBool { x @@ -271,7 +271,7 @@ pub fn enum_id_1(x: Option>) -> Option> { x } -// CHECK: { i1, i8 } @enum_id_2(i1 noundef zeroext %x.0, i8 %x.1) +// CHECK: { i1, i8 } @enum_id_2(i1 noundef{{( zeroext)?}} %x.0, i8 %x.1) #[no_mangle] pub fn enum_id_2(x: Option) -> Option { x diff --git a/tests/codegen-llvm/mir-aggregate-no-alloca.rs b/tests/codegen-llvm/mir-aggregate-no-alloca.rs index 77d367ed5da9e..b0816f4b6f9ae 100644 --- a/tests/codegen-llvm/mir-aggregate-no-alloca.rs +++ b/tests/codegen-llvm/mir-aggregate-no-alloca.rs @@ -51,7 +51,7 @@ pub fn make_2_tuple(x: u32) -> (u32, u32) { pair } -// CHECK-LABEL: i8 @make_cell_of_bool(i1 noundef zeroext %b) +// CHECK-LABEL: i8 @make_cell_of_bool(i1 noundef{{( zeroext)?}} %b) #[no_mangle] pub fn make_cell_of_bool(b: bool) -> std::cell::Cell { // CHECK: %[[BYTE:.+]] = zext i1 %b to i8 @@ -59,7 +59,7 @@ pub fn make_cell_of_bool(b: bool) -> std::cell::Cell { std::cell::Cell::new(b) } -// CHECK-LABEL: { i8, i16 } @make_cell_of_bool_and_short(i1 noundef zeroext %b, i16{{.*}} %s) +// CHECK-LABEL: { i8, i16 } @make_cell_of_bool_and_short(i1 noundef{{( zeroext)?}} %b, i16{{.*}} %s) #[no_mangle] pub fn make_cell_of_bool_and_short(b: bool, s: u16) -> std::cell::Cell<(bool, u16)> { // CHECK-NOT: alloca @@ -70,7 +70,7 @@ pub fn make_cell_of_bool_and_short(b: bool, s: u16) -> std::cell::Cell<(bool, u1 std::cell::Cell::new((b, s)) } -// CHECK-LABEL: { i1, i1 } @make_tuple_of_bools(i1 noundef zeroext %a, i1 noundef zeroext %b) +// CHECK-LABEL: { i1, i1 } @make_tuple_of_bools(i1 noundef{{( zeroext)?}} %a, i1 noundef{{( zeroext)?}} %b) #[no_mangle] pub fn make_tuple_of_bools(a: bool, b: bool) -> (bool, bool) { // CHECK-NOT: alloca diff --git a/tests/codegen-llvm/rust-abi-arch-specific-adjustment.rs b/tests/codegen-llvm/rust-abi-arch-specific-adjustment.rs index 119722c4ef34d..f03ed5ce6a094 100644 --- a/tests/codegen-llvm/rust-abi-arch-specific-adjustment.rs +++ b/tests/codegen-llvm/rust-abi-arch-specific-adjustment.rs @@ -86,8 +86,8 @@ pub fn arg_attr_i128(x: i128) -> i128 { } #[no_mangle] -// riscv64: define noundef zeroext i1 @arg_attr_bool(i1 noundef zeroext %x) -// loongarch64: define noundef zeroext i1 @arg_attr_bool(i1 noundef zeroext %x) +// riscv64: define noundef i1 @arg_attr_bool(i1 noundef zeroext %x) +// loongarch64: define noundef i1 @arg_attr_bool(i1 noundef zeroext %x) pub fn arg_attr_bool(x: bool) -> bool { x } diff --git a/tests/codegen-llvm/scalar-pair-bool.rs b/tests/codegen-llvm/scalar-pair-bool.rs index def3b32f71aa4..a094e3076aea0 100644 --- a/tests/codegen-llvm/scalar-pair-bool.rs +++ b/tests/codegen-llvm/scalar-pair-bool.rs @@ -2,25 +2,25 @@ #![crate_type = "lib"] -// CHECK: define{{.*}}{ i1, i1 } @pair_bool_bool(i1 noundef zeroext %pair.0, i1 noundef zeroext %pair.1) +// CHECK: define{{.*}}{ i1, i1 } @pair_bool_bool(i1 noundef{{( zeroext)?}} %pair.0, i1 noundef{{( zeroext)?}} %pair.1) #[no_mangle] pub fn pair_bool_bool(pair: (bool, bool)) -> (bool, bool) { pair } -// CHECK: define{{.*}}{ i1, i32 } @pair_bool_i32(i1 noundef zeroext %pair.0, i32 noundef %pair.1) +// CHECK: define{{.*}}{ i1, i32 } @pair_bool_i32(i1 noundef{{( zeroext)?}} %pair.0, i32 noundef %pair.1) #[no_mangle] pub fn pair_bool_i32(pair: (bool, i32)) -> (bool, i32) { pair } -// CHECK: define{{.*}}{ i32, i1 } @pair_i32_bool(i32 noundef %pair.0, i1 noundef zeroext %pair.1) +// CHECK: define{{.*}}{ i32, i1 } @pair_i32_bool(i32 noundef %pair.0, i1 noundef{{( zeroext)?}} %pair.1) #[no_mangle] pub fn pair_i32_bool(pair: (i32, bool)) -> (i32, bool) { pair } -// CHECK: define{{.*}}{ i1, i1 } @pair_and_or(i1 noundef zeroext %_1.0, i1 noundef zeroext %_1.1) +// CHECK: define{{.*}}{ i1, i1 } @pair_and_or(i1 noundef{{( zeroext)?}} %_1.0, i1 noundef{{( zeroext)?}} %_1.1) #[no_mangle] pub fn pair_and_or((a, b): (bool, bool)) -> (bool, bool) { // Make sure it can operate directly on the unpacked args @@ -30,7 +30,7 @@ pub fn pair_and_or((a, b): (bool, bool)) -> (bool, bool) { (a && b, a || b) } -// CHECK: define{{.*}}void @pair_branches(i1 noundef zeroext %_1.0, i1 noundef zeroext %_1.1) +// CHECK: define{{.*}}void @pair_branches(i1 noundef{{( zeroext)?}} %_1.0, i1 noundef{{( zeroext)?}} %_1.1) #[no_mangle] pub fn pair_branches((a, b): (bool, bool)) { // Make sure it can branch directly on the unpacked bool args diff --git a/tests/codegen-llvm/some-abis-do-extend-params-to-32-bits.rs b/tests/codegen-llvm/some-abis-do-extend-params-to-32-bits.rs index 8032ff445ae88..fdaf0dc58f106 100644 --- a/tests/codegen-llvm/some-abis-do-extend-params-to-32-bits.rs +++ b/tests/codegen-llvm/some-abis-do-extend-params-to-32-bits.rs @@ -33,6 +33,16 @@ use minicore::*; // // ZERO/SIGN-EXTENDING TO 32 BITS NON-EXTENDING // ============================== ======================= +// x86_64: void @c_arg_bool(i1 zeroext %_a) +// i686: void @c_arg_bool(i1 zeroext %_a) +// aarch64-apple: void @c_arg_bool(i1 zeroext %_a) +// aarch64-windows: void @c_arg_bool(i1 %_a) +// aarch64-linux: void @c_arg_bool(i1 %_a) +// arm: void @c_arg_bool(i1 zeroext %_a) +// riscv: void @c_arg_bool(i1 zeroext %_a) +#[no_mangle] +pub extern "C" fn c_arg_bool(_a: bool) {} + // x86_64: void @c_arg_u8(i8 zeroext %_a) // i686: void @c_arg_u8(i8 zeroext %_a) // aarch64-apple: void @c_arg_u8(i8 zeroext %_a) @@ -113,6 +123,18 @@ pub extern "C" fn c_arg_i32(_a: i32) {} #[no_mangle] pub extern "C" fn c_arg_i64(_a: i64) {} +// x86_64: zeroext i1 @c_ret_bool() +// i686: zeroext i1 @c_ret_bool() +// aarch64-apple: zeroext i1 @c_ret_bool() +// aarch64-windows: i1 @c_ret_bool() +// aarch64-linux: i1 @c_ret_bool() +// arm: zeroext i1 @c_ret_bool() +// riscv: zeroext i1 @c_ret_bool() +#[no_mangle] +pub extern "C" fn c_ret_bool() -> bool { + false +} + // x86_64: zeroext i8 @c_ret_u8() // i686: zeroext i8 @c_ret_u8() // aarch64-apple: zeroext i8 @c_ret_u8() @@ -210,10 +232,13 @@ pub extern "C" fn c_ret_i64() -> i64 { } const C_SOURCE_FILE: &'static str = r##" +#include #include #include #include +void c_arg_bool(bool _a) { } + void c_arg_u8(uint8_t _a) { } void c_arg_u16(uint16_t _a) { } void c_arg_u32(uint32_t _a) { } @@ -224,6 +249,8 @@ void c_arg_i16(int16_t _a) { } void c_arg_i32(int32_t _a) { } void c_arg_i64(int64_t _a) { } +bool c_ret_bool() { return false; } + uint8_t c_ret_u8() { return 0; } uint16_t c_ret_u16() { return 0; } uint32_t c_ret_u32() { return 0; } diff --git a/tests/codegen-llvm/transmute-scalar.rs b/tests/codegen-llvm/transmute-scalar.rs index 16adfb663fdbb..0c0fbc1b083f4 100644 --- a/tests/codegen-llvm/transmute-scalar.rs +++ b/tests/codegen-llvm/transmute-scalar.rs @@ -24,7 +24,7 @@ pub fn f32_to_bits(x: f32) -> u32 { unsafe { mem::transmute(x) } } -// CHECK-LABEL: define{{.*}}i8 @bool_to_byte(i1 zeroext %b) +// CHECK-LABEL: define{{.*}}i8 @bool_to_byte(i1{{( zeroext)?}} %b) // CHECK: %_0 = zext i1 %b to i8 // CHECK-NEXT: ret i8 %_0 #[no_mangle] @@ -32,7 +32,7 @@ pub fn bool_to_byte(b: bool) -> u8 { unsafe { mem::transmute(b) } } -// CHECK-LABEL: define{{.*}}zeroext i1 @byte_to_bool(i8{{.*}} %byte) +// CHECK-LABEL: define{{.*}} i1 @byte_to_bool(i8{{.*}} %byte) // CHECK: %_0 = trunc{{( nuw)?}} i8 %byte to i1 // CHECK-NEXT: ret i1 %_0 #[no_mangle] @@ -72,7 +72,7 @@ pub enum FakeBoolSigned { True = 1, } -// CHECK-LABEL: define{{.*}}i8 @bool_to_fake_bool_signed(i1 zeroext %b) +// CHECK-LABEL: define{{.*}}i8 @bool_to_fake_bool_signed(i1{{( zeroext)?}} %b) // CHECK: %_0 = zext i1 %b to i8 // CHECK-NEXT: ret i8 %_0 #[no_mangle] @@ -94,14 +94,14 @@ pub enum FakeBoolUnsigned { True = 1, } -// CHECK-LABEL: define{{.*}}i1 @bool_to_fake_bool_unsigned(i1 zeroext %b) +// CHECK-LABEL: define{{.*}}i1 @bool_to_fake_bool_unsigned(i1{{( zeroext)?}} %b) // CHECK: ret i1 %b #[no_mangle] pub fn bool_to_fake_bool_unsigned(b: bool) -> FakeBoolUnsigned { unsafe { mem::transmute(b) } } -// CHECK-LABEL: define{{.*}}i1 @fake_bool_unsigned_to_bool(i1 zeroext %b) +// CHECK-LABEL: define{{.*}}i1 @fake_bool_unsigned_to_bool(i1{{( zeroext)?}} %b) // CHECK: ret i1 %b #[no_mangle] pub fn fake_bool_unsigned_to_bool(b: FakeBoolUnsigned) -> bool { diff --git a/tests/codegen-llvm/union-abi.rs b/tests/codegen-llvm/union-abi.rs index 28acc4de2f327..b937c9c371266 100644 --- a/tests/codegen-llvm/union-abi.rs +++ b/tests/codegen-llvm/union-abi.rs @@ -137,7 +137,7 @@ pub fn test_CUnionU128(_: CUnionU128) { pub union UnionBool { b: bool, } -// CHECK: define {{(dso_local )?}}noundef zeroext i1 @test_UnionBool(i8{{.*}} %b) +// CHECK: define {{(dso_local )?}}noundef{{( zeroext)?}} i1 @test_UnionBool(i8{{.*}} %b) #[no_mangle] pub fn test_UnionBool(b: UnionBool) -> bool { unsafe { b.b } diff --git a/tests/codegen-llvm/union-aggregate.rs b/tests/codegen-llvm/union-aggregate.rs index 984e2f5715e50..50fb71e5e0279 100644 --- a/tests/codegen-llvm/union-aggregate.rs +++ b/tests/codegen-llvm/union-aggregate.rs @@ -17,7 +17,7 @@ use std::ptr::NonNull; #[no_mangle] fn make_mu_bool(x: bool) -> MU { - // CHECK-LABEL: i8 @make_mu_bool(i1 zeroext %x) + // CHECK-LABEL: i8 @make_mu_bool(i1{{( zeroext)?}} %x) // CHECK-NEXT: start: // CHECK-NEXT: %[[WIDER:.+]] = zext i1 %x to i8 // CHECK-NEXT: ret i8 %[[WIDER]] diff --git a/tests/ui/abi/debug.generic.stderr b/tests/ui/abi/debug.generic.stderr index 125150f2c2e3f..950d0ec389c3c 100644 --- a/tests/ui/abi/debug.generic.stderr +++ b/tests/ui/abi/debug.generic.stderr @@ -111,7 +111,7 @@ error: fn_abi_of(test) = FnAbi { mode: Direct( ArgAttributes { regular: NoUndef, - arg_ext: Zext, + arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None, }, @@ -169,7 +169,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { mode: Direct( ArgAttributes { regular: NoUndef, - arg_ext: Zext, + arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None, }, diff --git a/tests/ui/abi/debug.loongarch64.stderr b/tests/ui/abi/debug.loongarch64.stderr index 5f05174c12760..e95cbe049dbe6 100644 --- a/tests/ui/abi/debug.loongarch64.stderr +++ b/tests/ui/abi/debug.loongarch64.stderr @@ -111,7 +111,7 @@ error: fn_abi_of(test) = FnAbi { mode: Direct( ArgAttributes { regular: NoUndef, - arg_ext: Zext, + arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None, }, diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr index 5f05174c12760..e95cbe049dbe6 100644 --- a/tests/ui/abi/debug.riscv64.stderr +++ b/tests/ui/abi/debug.riscv64.stderr @@ -111,7 +111,7 @@ error: fn_abi_of(test) = FnAbi { mode: Direct( ArgAttributes { regular: NoUndef, - arg_ext: Zext, + arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None, },