diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 651082cee5d43..6e8f005be9569 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -2,7 +2,7 @@ use std::borrow::Borrow; -use libc::{c_char, c_uint}; +use libc::c_uint; use rustc_abi::Primitive::Pointer; use rustc_abi::{self as abi, ExternAbi, HasDataLayout as _}; use rustc_ast::Mutability; @@ -511,21 +511,3 @@ pub(crate) fn get_dllimport<'tcx>( tcx.native_library(id) .and_then(|lib| lib.dll_imports.iter().find(|di| di.name.as_str() == name)) } - -/// Extension trait for explicit casts to `*const c_char`. -pub(crate) trait AsCCharPtr { - /// Equivalent to `self.as_ptr().cast()`, but only casts to `*const c_char`. - fn as_c_char_ptr(&self) -> *const c_char; -} - -impl AsCCharPtr for str { - fn as_c_char_ptr(&self) -> *const c_char { - self.as_ptr().cast() - } -} - -impl AsCCharPtr for [u8] { - fn as_c_char_ptr(&self) -> *const c_char { - self.as_ptr().cast() - } -} diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 2e76223c68c12..8f6cc30e1166d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -1,6 +1,6 @@ #![expect(dead_code)] -use libc::{c_char, c_uint}; +use libc::{c_char, c_uchar, c_uint}; use super::MetadataKindId; use super::ffi::{AttributeKind, BasicBlock, Context, Metadata, Module, Type, Value}; @@ -47,10 +47,14 @@ unsafe extern "C" { pub(crate) fn LLVMRustGetArrayNumElements(Ty: &Type) -> u64; pub(crate) fn LLVMRustHasFnAttribute( F: &Value, - Name: *const c_char, + Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: libc::size_t, ) -> bool; - pub(crate) fn LLVMRustRemoveFnAttribute(F: &Value, Name: *const c_char, NameLen: libc::size_t); + pub(crate) fn LLVMRustRemoveFnAttribute( + F: &Value, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: libc::size_t, + ); pub(crate) fn LLVMGetFirstFunction(M: &Module) -> Option<&Value>; pub(crate) fn LLVMGetNextFunction(Fn: &Value) -> Option<&Value>; pub(crate) fn LLVMRustRemoveEnumAttributeAtIndex( diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 749ef8120da6f..30ecd09adf8fb 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -982,7 +982,11 @@ unsafe extern "C" { // Operations on all values pub(crate) fn LLVMTypeOf(Val: &Value) -> &Type; pub(crate) fn LLVMGetValueName2(Val: &Value, Length: *mut size_t) -> *const c_char; - pub(crate) fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t); + pub(crate) fn LLVMSetValueName2( + Val: &Value, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + ); pub(crate) fn LLVMReplaceAllUsesWith<'a>(OldVal: &'a Value, NewVal: &'a Value); pub(crate) safe fn LLVMGetMetadata<'a>( Val: &'a Value, @@ -1117,9 +1121,9 @@ unsafe extern "C" { // Operations on attributes pub(crate) fn LLVMCreateStringAttribute( C: &Context, - Name: *const c_char, + Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: c_uint, - Value: *const c_char, + Value: *const c_uchar, // See "PTR_LEN_STR". ValueLen: c_uint, ) -> &Attribute; @@ -1139,7 +1143,10 @@ unsafe extern "C" { ) -> &'a Value; // Operations about llvm intrinsics - pub(crate) fn LLVMLookupIntrinsicID(Name: *const c_char, NameLen: size_t) -> c_uint; + pub(crate) fn LLVMLookupIntrinsicID( + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + ) -> c_uint; pub(crate) fn LLVMIntrinsicIsOverloaded(ID: NonZero) -> Bool; pub(crate) fn LLVMGetIntrinsicDeclaration<'a>( Mod: &'a Module, @@ -1675,7 +1682,7 @@ unsafe extern "C" { pub(crate) fn LLVMSetComdat(V: &Value, C: &Comdat); pub(crate) fn LLVMCreateOperandBundle( - Tag: *const c_char, + Tag: *const c_uchar, // See "PTR_LEN_STR". TagLen: size_t, Args: *const &'_ Value, NumArgs: c_uint, @@ -2186,7 +2193,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustAddModuleFlagU32( M: &Module, MergeBehavior: ModuleFlagMergeBehavior, - Name: *const c_char, + Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: size_t, Value: u32, ); @@ -2194,9 +2201,9 @@ unsafe extern "C" { pub(crate) fn LLVMRustAddModuleFlagString( M: &Module, MergeBehavior: ModuleFlagMergeBehavior, - Name: *const c_char, + Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: size_t, - Value: *const c_char, + Value: *const c_uchar, // See "PTR_LEN_STR". ValueLen: size_t, ); diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 89e4d60656d34..e189dc758d8e7 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -14,7 +14,6 @@ pub(crate) use self::CodeGenOptSize::*; pub(crate) use self::conversions::*; pub(crate) use self::ffi::*; pub(crate) use self::metadata_kind::*; -use crate::common::AsCCharPtr; mod conversions; pub(crate) mod diagnostic; @@ -46,11 +45,11 @@ pub(crate) fn AddFunctionAttributes<'ll>( } pub(crate) fn HasStringAttribute<'ll>(llfn: &'ll Value, name: &str) -> bool { - unsafe { LLVMRustHasFnAttribute(llfn, name.as_c_char_ptr(), name.len()) } + unsafe { LLVMRustHasFnAttribute(llfn, name.as_ptr(), name.len()) } } pub(crate) fn RemoveStringAttrFromFn<'ll>(llfn: &'ll Value, name: &str) { - unsafe { LLVMRustRemoveFnAttribute(llfn, name.as_c_char_ptr(), name.len()) } + unsafe { LLVMRustRemoveFnAttribute(llfn, name.as_ptr(), name.len()) } } pub(crate) fn AddCallSiteAttributes<'ll>( @@ -71,9 +70,9 @@ pub(crate) fn CreateAttrStringValue<'ll>( unsafe { LLVMCreateStringAttribute( llcx, - attr.as_c_char_ptr(), + attr.as_ptr(), attr.len().try_into().unwrap(), - value.as_c_char_ptr(), + value.as_ptr(), value.len().try_into().unwrap(), ) } @@ -83,13 +82,15 @@ pub(crate) fn CreateAttrStringValueFromCStr<'ll>( attr: &std::ffi::CStr, value: &std::ffi::CStr, ) -> &'ll Attribute { + let attr = attr.to_bytes(); + let value = value.to_bytes(); unsafe { LLVMCreateStringAttribute( llcx, - (*attr).as_ptr(), - (*attr).to_bytes().len() as c_uint, - (*value).as_ptr(), - (*value).to_bytes().len() as c_uint, + attr.as_ptr(), + attr.len() as c_uint, + value.as_ptr(), + value.len() as c_uint, ) } } @@ -98,7 +99,7 @@ pub(crate) fn CreateAttrString<'ll>(llcx: &'ll Context, attr: &str) -> &'ll Attr unsafe { LLVMCreateStringAttribute( llcx, - attr.as_c_char_ptr(), + attr.as_ptr(), attr.len().try_into().unwrap(), std::ptr::null(), 0, @@ -340,7 +341,7 @@ pub(crate) struct Intrinsic { impl Intrinsic { pub(crate) fn lookup(name: &[u8]) -> Option { - let id = unsafe { LLVMLookupIntrinsicID(name.as_c_char_ptr(), name.len()) }; + let id = unsafe { LLVMLookupIntrinsicID(name.as_ptr(), name.len()) }; NonZero::new(id).map(|id| Self { id }) } @@ -365,10 +366,7 @@ impl Intrinsic { /// Safe wrapper for `LLVMSetValueName2` from a byte slice pub(crate) fn set_value_name(value: &Value, name: &[u8]) { - unsafe { - let data = name.as_c_char_ptr(); - LLVMSetValueName2(value, data, name.len()); - } + unsafe { LLVMSetValueName2(value, name.as_ptr(), name.len()) }; } pub(crate) fn build_string(f: impl FnOnce(&RustString)) -> Result { @@ -408,12 +406,7 @@ pub(crate) struct OperandBundleBox<'a> { impl<'a> OperandBundleBox<'a> { pub(crate) fn new(name: &str, vals: &[&'a Value]) -> Self { let raw = unsafe { - LLVMCreateOperandBundle( - name.as_c_char_ptr(), - name.len(), - vals.as_ptr(), - vals.len() as c_uint, - ) + LLVMCreateOperandBundle(name.as_ptr(), name.len(), vals.as_ptr(), vals.len() as c_uint) }; Self { raw: ptr::NonNull::new(raw).unwrap() } } @@ -444,7 +437,7 @@ pub(crate) fn add_module_flag_u32( value: u32, ) { unsafe { - LLVMRustAddModuleFlagU32(module, merge_behavior, key.as_c_char_ptr(), key.len(), value); + LLVMRustAddModuleFlagU32(module, merge_behavior, key.as_ptr(), key.len(), value); } } @@ -458,9 +451,9 @@ pub(crate) fn add_module_flag_str( LLVMRustAddModuleFlagString( module, merge_behavior, - key.as_c_char_ptr(), + key.as_ptr(), key.len(), - value.as_c_char_ptr(), + value.as_ptr(), value.len(), ); } diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 25eb10230541a..70d48c3782b7d 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -100,19 +100,21 @@ pub enum PassMode { }, } -/// Attributes of a function argument that affect its ABI. -/// -/// Not all internal compiler attributes are exposed here, as some are -/// LLVM-specific optimization hints. The internal representation is kept -/// private so it can be expanded in the future. +/// Attributes of a function argument or return value. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub struct ArgAttributes { + pub(crate) regular: ArgAttributeFlags, pub(crate) arg_ext: ArgExtension, pub(crate) pointee_size: Size, pub(crate) pointee_align: Option, } impl ArgAttributes { + /// Return the calling convention and optimization flags for this argument. + pub fn regular(&self) -> ArgAttributeFlags { + self.regular + } + /// Return how this argument should be extended when passed in a register. /// /// Relevant for integer arguments smaller than the register width. @@ -128,12 +130,34 @@ impl ArgAttributes { self.pointee_align } - /// Return the minimum dereferenceable size of the pointee, if known. + /// Return the minimum dereferenceable size of the pointee, if it is non-null, + /// at function entry (for arguments) or return (for return values). + /// + /// This alone does not guarantee dereferenceability for the duration of the call. pub fn pointee_size(&self) -> Size { self.pointee_size } } +/// Calling convention and optimization flags for a function argument or return value. +/// +/// The capture flags overlap: `captures_none` implies `captures_address`, which +/// implies `captures_read_only`. +#[non_exhaustive] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct ArgAttributeFlags { + pub captures_none: bool, + pub captures_address: bool, + pub captures_read_only: bool, + pub no_alias: bool, + pub non_null: bool, + pub read_only: bool, + pub in_reg: bool, + pub no_undef: bool, + pub writable: bool, + pub no_free: bool, +} + /// How a small integer argument should be extended to fill a register. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum ArgExtension { diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index 39cffc194bc30..a5e7302e9a4c7 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -10,10 +10,11 @@ use rustc_target::callconv; use crate::IndexedVal; use crate::abi::{ - AddressSpace, ArgAbi, ArgAttributes, ArgExtension, CallConvention, CastTarget, FieldsShape, - FloatLength, FnAbi, IndirectMode, IntegerLength, IntegerType, Layout, LayoutShape, - NumScalableVectors, PassMode, Primitive, Reg, RegKind, ReprFlags, ReprOptions, Scalar, - TagEncoding, TyAndLayout, Uniform, ValueRepr, VariantFields, VariantsShape, WrappingRange, + AddressSpace, ArgAbi, ArgAttributeFlags, ArgAttributes, ArgExtension, CallConvention, + CastTarget, FieldsShape, FloatLength, FnAbi, IndirectMode, IntegerLength, IntegerType, Layout, + LayoutShape, NumScalableVectors, PassMode, Primitive, Reg, RegKind, ReprFlags, ReprOptions, + Scalar, TagEncoding, TyAndLayout, Uniform, ValueRepr, VariantFields, VariantsShape, + WrappingRange, }; use crate::compiler_interface::BridgeTys; use crate::target::MachineSize as Size; @@ -264,6 +265,18 @@ impl<'tcx> Stable<'tcx> for callconv::ArgAttributes { _: &CompilerCtxt<'cx, BridgeTys>, ) -> Self::T { ArgAttributes { + regular: ArgAttributeFlags { + captures_none: self.regular.contains(callconv::ArgAttribute::CapturesNone), + captures_address: self.regular.contains(callconv::ArgAttribute::CapturesAddress), + captures_read_only: self.regular.contains(callconv::ArgAttribute::CapturesReadOnly), + no_alias: self.regular.contains(callconv::ArgAttribute::NoAlias), + non_null: self.regular.contains(callconv::ArgAttribute::NonNull), + read_only: self.regular.contains(callconv::ArgAttribute::ReadOnly), + in_reg: self.regular.contains(callconv::ArgAttribute::InReg), + no_undef: self.regular.contains(callconv::ArgAttribute::NoUndef), + writable: self.regular.contains(callconv::ArgAttribute::Writable), + no_free: self.regular.contains(callconv::ArgAttribute::NoFree), + }, arg_ext: match self.arg_ext { callconv::ArgExtension::None => ArgExtension::None, callconv::ArgExtension::Zext => ArgExtension::Zext, diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index e14e285fc1185..1985e5f51a100 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -545,7 +545,9 @@ impl<'a, Ty> ArgAbi<'a, Ty> { // Only integers have signedness if let BackendRepr::Scalar(scalar) = self.layout.backend_repr && let Primitive::Int(i, signed) = scalar.primitive() - && i.size().bits() < bits + // bools are i1 but their primitive is i8. Unlike other types, + // bools can be extended to their primitive size. + && (i.size().bits() < bits || self.layout.backend_repr.is_bool()) && let PassMode::Direct(ref mut attrs) = self.mode { if signed { diff --git a/compiler/rustc_target/src/callconv/x86_64.rs b/compiler/rustc_target/src/callconv/x86_64.rs index 57e997f45d4c5..98f3a626155a8 100644 --- a/compiler/rustc_target/src/callconv/x86_64.rs +++ b/compiler/rustc_target/src/callconv/x86_64.rs @@ -247,6 +247,11 @@ where } else if is_arg || cx.target_spec().is_like_darwin { arg.extend_integer_width_to(32); } + // The x86_64 psABI requires bools to be zero-extended to 8-bits, with high bits unspecified. + // zeroext i1 has this behavior in the LLVM X86 backend. + if !is_arg && arg.layout.backend_repr.is_bool() { + arg.extend_integer_width_to(8); + } } } }; diff --git a/library/core/src/attribute_docs.rs b/library/core/src/attribute_docs.rs index a397cd436b13a..b84d6c5db977c 100644 --- a/library/core/src/attribute_docs.rs +++ b/library/core/src/attribute_docs.rs @@ -194,6 +194,42 @@ const _: () = (); /// [`if`]: ./keyword.if.html const _: () = (); +#[doc(attribute = "crate_name")] +// +/// Specifies the name of the crate. +/// +/// Written as an inner attribute at the top of the crate root, `crate_name` is used +/// to specify the name of a crate. +/// +/// ``` +/// #![crate_name = "my_crate"] +/// ``` +/// +/// The crate name must not be empty, and must only contain [Unicode alphanumeric] or +/// `_` (U+005F) characters. +/// +/// Note that the specified name must match the name given by the `--crate-name` flag. +/// +/// ```text +/// error: `--crate-name` and `#[crate_name]` are required to match, but `cli_name` != `attr_name` +/// --> main.rs:1:17 +/// | +/// 1 | #![crate_name = "attr_name"] +/// | ^^^^^^^^^^^ +/// ``` +/// +/// In a Cargo project the crate name is always passed as `--crate-name`, derived from the target +/// name (`[lib] name` or `[[bin]] name` if set, otherwise `[package] name` for libraries and the +/// primary binary, or the source file name for other binaries). The `crate_name` attribute is +/// therefore redundant here: it adds nothing, and if it is present it must match Cargo's name, +/// otherwise `rustc` reports an error. +/// +/// For more information, see the Reference on [the `crate_name` attribute]. +/// +/// [Unicode alphanumeric]: ../std/primitive.char.html#method.is_alphanumeric +/// [the `crate_name` attribute]: ../reference/crates-and-source-files.html#the-crate_name-attribute +const _: () = (); + #[doc(attribute = "deny")] // /// Emits an error, preventing the compilation from finishing, when a lint check has failed. 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 fdaf0dc58f106..79be3bd5f57b4 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 @@ -1,10 +1,12 @@ //@ add-minicore //@ compile-flags: -Cno-prepopulate-passes -Copt-level=0 -//@ revisions:x86_64 i686 aarch64-apple aarch64-windows aarch64-linux arm riscv +//@ revisions:x86_64-linux x86_64-uefi i686 aarch64-apple aarch64-windows aarch64-linux arm riscv -//@[x86_64] compile-flags: --target x86_64-unknown-uefi -//@[x86_64] needs-llvm-components: x86 +//@[x86_64-uefi] compile-flags: --target x86_64-unknown-uefi +//@[x86_64-uefi] needs-llvm-components: x86 +//@[x86_64-linux] compile-flags: --target x86_64-unknown-linux-gnu +//@[x86_64-linux] needs-llvm-components: x86 //@[i686] compile-flags: --target i686-unknown-linux-musl //@[i686] needs-llvm-components: x86 //@[aarch64-windows] compile-flags: --target aarch64-pc-windows-msvc @@ -33,7 +35,8 @@ use minicore::*; // // ZERO/SIGN-EXTENDING TO 32 BITS NON-EXTENDING // ============================== ======================= -// x86_64: void @c_arg_bool(i1 zeroext %_a) +// x86_64-linux: void @c_arg_bool(i1 zeroext %_a) +// x86_64-uefi: 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) @@ -43,7 +46,8 @@ use minicore::*; #[no_mangle] pub extern "C" fn c_arg_bool(_a: bool) {} -// x86_64: void @c_arg_u8(i8 zeroext %_a) +// x86_64-linux: void @c_arg_u8(i8 zeroext %_a) +// x86_64-uefi: void @c_arg_u8(i8 zeroext %_a) // i686: void @c_arg_u8(i8 zeroext %_a) // aarch64-apple: void @c_arg_u8(i8 zeroext %_a) // aarch64-windows: void @c_arg_u8(i8 %_a) @@ -53,7 +57,8 @@ pub extern "C" fn c_arg_bool(_a: bool) {} #[no_mangle] pub extern "C" fn c_arg_u8(_a: u8) {} -// x86_64: void @c_arg_u16(i16 zeroext %_a) +// x86_64-linux: void @c_arg_u16(i16 zeroext %_a) +// x86_64-uefi: void @c_arg_u16(i16 zeroext %_a) // i686: void @c_arg_u16(i16 zeroext %_a) // aarch64-apple: void @c_arg_u16(i16 zeroext %_a) // aarch64-windows: void @c_arg_u16(i16 %_a) @@ -63,7 +68,8 @@ pub extern "C" fn c_arg_u8(_a: u8) {} #[no_mangle] pub extern "C" fn c_arg_u16(_a: u16) {} -// x86_64: void @c_arg_u32(i32 %_a) +// x86_64-linux: void @c_arg_u32(i32 %_a) +// x86_64-uefi: void @c_arg_u32(i32 %_a) // i686: void @c_arg_u32(i32 %_a) // aarch64-apple: void @c_arg_u32(i32 %_a) // aarch64-windows: void @c_arg_u32(i32 %_a) @@ -73,7 +79,8 @@ pub extern "C" fn c_arg_u16(_a: u16) {} #[no_mangle] pub extern "C" fn c_arg_u32(_a: u32) {} -// x86_64: void @c_arg_u64(i64 %_a) +// x86_64-linux: void @c_arg_u64(i64 %_a) +// x86_64-uefi: void @c_arg_u64(i64 %_a) // i686: void @c_arg_u64(i64 %_a) // aarch64-apple: void @c_arg_u64(i64 %_a) // aarch64-windows: void @c_arg_u64(i64 %_a) @@ -83,7 +90,8 @@ pub extern "C" fn c_arg_u32(_a: u32) {} #[no_mangle] pub extern "C" fn c_arg_u64(_a: u64) {} -// x86_64: void @c_arg_i8(i8 signext %_a) +// x86_64-linux: void @c_arg_i8(i8 signext %_a) +// x86_64-uefi: void @c_arg_i8(i8 signext %_a) // i686: void @c_arg_i8(i8 signext %_a) // aarch64-apple: void @c_arg_i8(i8 signext %_a) // aarch64-windows: void @c_arg_i8(i8 %_a) @@ -93,7 +101,8 @@ pub extern "C" fn c_arg_u64(_a: u64) {} #[no_mangle] pub extern "C" fn c_arg_i8(_a: i8) {} -// x86_64: void @c_arg_i16(i16 signext %_a) +// x86_64-linux: void @c_arg_i16(i16 signext %_a) +// x86_64-uefi: void @c_arg_i16(i16 signext %_a) // i686: void @c_arg_i16(i16 signext %_a) // aarch64-apple: void @c_arg_i16(i16 signext %_a) // aarch64-windows: void @c_arg_i16(i16 %_a) @@ -103,7 +112,8 @@ pub extern "C" fn c_arg_i8(_a: i8) {} #[no_mangle] pub extern "C" fn c_arg_i16(_a: i16) {} -// x86_64: void @c_arg_i32(i32 %_a) +// x86_64-linux: void @c_arg_i32(i32 %_a) +// x86_64-uefi: void @c_arg_i32(i32 %_a) // i686: void @c_arg_i32(i32 %_a) // aarch64-apple: void @c_arg_i32(i32 %_a) // aarch64-windows: void @c_arg_i32(i32 %_a) @@ -113,7 +123,8 @@ pub extern "C" fn c_arg_i16(_a: i16) {} #[no_mangle] pub extern "C" fn c_arg_i32(_a: i32) {} -// x86_64: void @c_arg_i64(i64 %_a) +// x86_64-linux: void @c_arg_i64(i64 %_a) +// x86_64-uefi: void @c_arg_i64(i64 %_a) // i686: void @c_arg_i64(i64 %_a) // aarch64-apple: void @c_arg_i64(i64 %_a) // aarch64-windows: void @c_arg_i64(i64 %_a) @@ -123,7 +134,8 @@ 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() +// x86_64-linux: zeroext i1 @c_ret_bool() +// x86_64-uefi: zeroext i1 @c_ret_bool() // i686: zeroext i1 @c_ret_bool() // aarch64-apple: zeroext i1 @c_ret_bool() // aarch64-windows: i1 @c_ret_bool() @@ -135,7 +147,8 @@ pub extern "C" fn c_ret_bool() -> bool { false } -// x86_64: zeroext i8 @c_ret_u8() +// x86_64-linux: i8 @c_ret_u8() +// x86_64-uefi: zeroext i8 @c_ret_u8() // i686: zeroext i8 @c_ret_u8() // aarch64-apple: zeroext i8 @c_ret_u8() // aarch64-windows: i8 @c_ret_u8() @@ -147,7 +160,8 @@ pub extern "C" fn c_ret_u8() -> u8 { 0 } -// x86_64: zeroext i16 @c_ret_u16() +// x86_64-linux: i16 @c_ret_u16() +// x86_64-uefi: zeroext i16 @c_ret_u16() // i686: zeroext i16 @c_ret_u16() // aarch64-apple: zeroext i16 @c_ret_u16() // aarch64-windows: i16 @c_ret_u16() @@ -159,7 +173,8 @@ pub extern "C" fn c_ret_u16() -> u16 { 0 } -// x86_64: i32 @c_ret_u32() +// x86_64-linux: i32 @c_ret_u32() +// x86_64-uefi: i32 @c_ret_u32() // i686: i32 @c_ret_u32() // aarch64-apple: i32 @c_ret_u32() // aarch64-windows: i32 @c_ret_u32() @@ -171,7 +186,8 @@ pub extern "C" fn c_ret_u32() -> u32 { 0 } -// x86_64: i64 @c_ret_u64() +// x86_64-linux: i64 @c_ret_u64() +// x86_64-uefi: i64 @c_ret_u64() // i686: i64 @c_ret_u64() // aarch64-apple: i64 @c_ret_u64() // aarch64-windows: i64 @c_ret_u64() @@ -183,7 +199,8 @@ pub extern "C" fn c_ret_u64() -> u64 { 0 } -// x86_64: signext i8 @c_ret_i8() +// x86_64-linux: i8 @c_ret_i8() +// x86_64-uefi: signext i8 @c_ret_i8() // i686: signext i8 @c_ret_i8() // aarch64-apple: signext i8 @c_ret_i8() // aarch64-windows: i8 @c_ret_i8() @@ -195,7 +212,8 @@ pub extern "C" fn c_ret_i8() -> i8 { 0 } -// x86_64: signext i16 @c_ret_i16() +// x86_64-linux: i16 @c_ret_i16() +// x86_64-uefi: signext i16 @c_ret_i16() // i686: signext i16 @c_ret_i16() // aarch64-apple: signext i16 @c_ret_i16() // aarch64-windows: i16 @c_ret_i16() @@ -207,7 +225,8 @@ pub extern "C" fn c_ret_i16() -> i16 { 0 } -// x86_64: i32 @c_ret_i32() +// x86_64-linux: i32 @c_ret_i32() +// x86_64-uefi: i32 @c_ret_i32() // i686: i32 @c_ret_i32() // aarch64-apple: i32 @c_ret_i32() // aarch64-windows: i32 @c_ret_i32() @@ -219,7 +238,8 @@ pub extern "C" fn c_ret_i32() -> i32 { 0 } -// x86_64: i64 @c_ret_i64() +// x86_64-linux: i64 @c_ret_i64() +// x86_64-uefi: i64 @c_ret_i64() // i686: i64 @c_ret_i64() // aarch64-apple: i64 @c_ret_i64() // aarch64-windows: i64 @c_ret_i64() diff --git a/tests/ui-fulldeps/rustc_public/check_abi.rs b/tests/ui-fulldeps/rustc_public/check_abi.rs index 6c99265c468ac..c394e05e76fbb 100644 --- a/tests/ui-fulldeps/rustc_public/check_abi.rs +++ b/tests/ui-fulldeps/rustc_public/check_abi.rs @@ -13,6 +13,7 @@ extern crate rustc_interface; extern crate rustc_middle; #[macro_use] extern crate rustc_public; +extern crate rustc_target; use std::assert_matches; use std::collections::HashSet; @@ -34,15 +35,18 @@ const CRATE_NAME: &str = "input"; /// This function uses the Stable MIR APIs to get information about the test crate. fn test_stable_mir() -> ControlFlow<()> { + check_regular_attributes(); + // Find items in the local crate. let items = rustc_public::all_local_items(); // Test fn_abi - let target_fn = *get_item(&items, (ItemKind::Fn, "input::fn_abi")).unwrap(); - let instance = Instance::try_from(target_fn).unwrap(); - let fn_abi = instance.fn_abi().unwrap(); - assert_eq!(fn_abi.conv, CallConvention::Rust); - assert_eq!(fn_abi.args.len(), 3); + let target_fn = + *get_item(&items, (ItemKind::Fn, "input::fn_abi")).expect("Expected fn_abi function"); + let instance = Instance::try_from(target_fn).expect("Expected fn_abi instance"); + let fn_abi = instance.fn_abi().expect("Expected function ABI"); + assert_eq!(fn_abi.conv, CallConvention::Rust, "Expected Rust calling convention for fn_abi"); + assert_eq!(fn_abi.args.len(), 3, "Expected three arguments for fn_abi"); check_ignore(&fn_abi.args[0]); check_primitive(&fn_abi.args[1]); @@ -50,35 +54,225 @@ fn test_stable_mir() -> ControlFlow<()> { check_result(&fn_abi.ret); // Test variadic function. - let variadic_fn = *get_item(&items, (ItemKind::Fn, "input::variadic_fn")).unwrap(); + let variadic_fn = *get_item(&items, (ItemKind::Fn, "input::variadic_fn")) + .expect("Expected variadic_fn function"); check_variadic(variadic_fn); // Extract function pointers. - let fn_ptr_holder = *get_item(&items, (ItemKind::Fn, "input::fn_ptr_holder")).unwrap(); - let fn_ptr_holder_instance = Instance::try_from(fn_ptr_holder).unwrap(); - let body = fn_ptr_holder_instance.body().unwrap(); + let fn_ptr_holder = *get_item(&items, (ItemKind::Fn, "input::fn_ptr_holder")) + .expect("Expected fn_ptr_holder function"); + let fn_ptr_holder_instance = + Instance::try_from(fn_ptr_holder).expect("Expected fn_ptr_holder instance"); + let body = fn_ptr_holder_instance.body().expect("Expected fn_ptr_holder body"); let args = body.arg_locals(); // Test fn_abi of function pointer version. - let ptr_fn_abi = args[0].ty.kind().fn_sig().unwrap().fn_ptr_abi().unwrap(); - assert_eq!(ptr_fn_abi, fn_abi); + let ptr_fn_abi = args[0] + .ty + .kind() + .fn_sig() + .expect("Expected ComplexFn signature") + .fn_ptr_abi() + .expect("Expected ComplexFn ABI"); + assert_eq!(ptr_fn_abi, fn_abi, "Expected matching function and function pointer ABIs"); // Test variadic_fn of function pointer version. - let ptr_variadic_fn_abi = args[1].ty.kind().fn_sig().unwrap().fn_ptr_abi().unwrap(); - assert!(ptr_variadic_fn_abi.c_variadic); - assert_eq!(ptr_variadic_fn_abi.args.len(), 1); - - let entry = rustc_public::entry_fn().unwrap(); - let main_fn = Instance::try_from(entry).unwrap(); + let ptr_variadic_fn_abi = args[1] + .ty + .kind() + .fn_sig() + .expect("Expected VariadicFn signature") + .fn_ptr_abi() + .expect("Expected VariadicFn ABI"); + assert!(ptr_variadic_fn_abi.c_variadic, "Expected C variadic function pointer"); + assert_eq!(ptr_variadic_fn_abi.args.len(), 1, "Expected one fixed variadic pointer argument"); + + let entry = rustc_public::entry_fn().expect("Expected main function"); + let main_fn = Instance::try_from(entry).expect("Expected main instance"); let mut visitor = AdtDefVisitor::default(); - visitor.visit_body(&main_fn.body().unwrap()); + visitor.visit_body(&main_fn.body().expect("Expected main body")); let AdtDefVisitor { adt_defs } = visitor; - assert_eq!(adt_defs.len(), 1); + assert_eq!(adt_defs.len(), 1, "Expected one ADT in main"); // Test ADT representation options - let repr_c_struct = adt_defs.iter().find(|def| def.trimmed_name() == "ReprCStruct").unwrap(); - assert!(repr_c_struct.repr().flags.is_c); + let repr_c_struct = adt_defs + .iter() + .find(|def| def.trimmed_name() == "ReprCStruct") + .expect("Expected ReprCStruct definition"); + assert!(repr_c_struct.repr().flags.is_c, "Expected repr(C) for ReprCStruct"); + + ControlFlow::Continue(()) +} + +fn check_regular_attributes() { + use rustc_target::callconv::{ArgAttribute, ArgAttributes}; + + let flags = [ + ArgAttribute::CapturesNone, + ArgAttribute::CapturesAddress, + ArgAttribute::CapturesReadOnly, + ArgAttribute::NoAlias, + ArgAttribute::NonNull, + ArgAttribute::ReadOnly, + ArgAttribute::InReg, + ArgAttribute::NoUndef, + ArgAttribute::Writable, + ArgAttribute::NoFree, + ]; + for regular in [ArgAttribute::empty(), ArgAttribute::all()].iter().copied().chain(flags) { + let attrs = rustc_public::rustc_internal::stable(ArgAttributes::from(regular)).regular(); + for (flag, actual) in flags.iter().copied().zip([ + attrs.captures_none, + attrs.captures_address, + attrs.captures_read_only, + attrs.no_alias, + attrs.non_null, + attrs.read_only, + attrs.in_reg, + attrs.no_undef, + attrs.writable, + attrs.no_free, + ]) { + assert_eq!(actual, regular.contains(flag), "Expected {flag:?} for {regular:?}"); + } + } +} + +struct ExpectedArgAttributes { + no_alias: bool, + non_null: bool, + read_only: bool, + no_free: bool, + pointee_size: usize, +} +fn check_args(args: &[ArgAbi], expected: &[(&str, ExpectedArgAttributes)]) { + assert_eq!(args.len(), expected.len(), "Expected one set of attributes per argument"); + for (arg, (name, expected)) in args.iter().zip(expected) { + let PassMode::Direct(attrs) = &arg.mode else { + panic!("Expected PassMode::Direct for {name}, got: {:?}", arg.mode); + }; + let regular = attrs.regular(); + assert_eq!(regular.no_alias, expected.no_alias, "Expected no_alias for {name}"); + assert_eq!(regular.non_null, expected.non_null, "Expected non_null for {name}"); + assert_eq!(regular.read_only, expected.read_only, "Expected read_only for {name}"); + assert_eq!(regular.no_free, expected.no_free, "Expected no_free for {name}"); + assert_eq!( + attrs.pointee_size().bytes(), + expected.pointee_size, + "Expected pointee_size for {name}", + ); + assert!(regular.no_undef, "Expected no_undef for {}", name); + assert_eq!( + regular.captures_read_only, + expected.read_only, + "Expected captures_read_only for {name}", + ); + assert!(!regular.captures_none, "Expected captures_none to be unset for {}", name); + assert!(!regular.captures_address, "Expected captures_address to be unset for {}", name); + } +} + +fn test_pointer_attributes() -> ControlFlow<()> { + let items = rustc_public::all_local_items(); + let target_fn = get_item(&items, (ItemKind::Fn, "input::pointer_attributes")) + .expect("Expected pointer_attributes function"); + // Use the signature to exclude attributes deduced from the body. + let abi = target_fn + .ty() + .kind() + .fn_sig() + .expect("Expected pointer_attributes signature") + .fn_ptr_abi() + .expect("Expected pointer_attributes ABI"); + assert_eq!(abi.args.len(), 6, "Expected six arguments for pointer_attributes"); + + check_args( + &abi.args[..4], + &[ + ( + "shared", + ExpectedArgAttributes { + no_alias: true, + non_null: true, + read_only: true, + no_free: true, + pointee_size: 4, + }, + ), + ( + "cell", + ExpectedArgAttributes { + no_alias: false, + non_null: true, + read_only: false, + no_free: false, + pointee_size: 0, + }, + ), + ( + "raw", + ExpectedArgAttributes { + no_alias: false, + non_null: false, + read_only: false, + no_free: false, + pointee_size: 0, + }, + ), + ( + "nullable", + ExpectedArgAttributes { + no_alias: true, + non_null: false, + read_only: true, + no_free: true, + pointee_size: 4, + }, + ), + ], + ); + + let PassMode::Pair(data, len) = &abi.args[4].mode else { + panic!("Expected PassMode::Pair for slice, got: {:?}", abi.args[4].mode); + }; + assert!(data.regular().non_null, "Expected non_null for slice data"); + assert!(data.regular().no_free, "Expected no_free for slice data"); + assert!(data.regular().no_alias, "Expected no_alias for slice data"); + assert!(data.regular().read_only, "Expected read_only for slice data"); + assert!(data.regular().captures_read_only, "Expected captures_read_only for slice data"); + assert!(len.regular().no_undef, "Expected no_undef for slice length"); + assert!(!len.regular().non_null, "Expected non_null to be unset for slice length"); + + let PassMode::Indirect { attrs, .. } = &abi.args[5].mode else { + panic!("Expected PassMode::Indirect for indirect, got: {:?}", abi.args[5].mode); + }; + let regular = attrs.regular(); + assert!(regular.no_alias, "Expected no_alias for indirect"); + assert!(regular.non_null, "Expected non_null for indirect"); + assert!(regular.no_undef, "Expected no_undef for indirect"); + assert!(regular.no_free, "Expected no_free for indirect"); + assert!(!regular.captures_none, "Expected captures_none to be unset for indirect"); + assert!(regular.captures_address, "Expected captures_address for indirect"); + assert!(regular.captures_read_only, "Expected captures_read_only for indirect"); + assert_eq!(attrs.pointee_size().bytes(), 256, "Expected pointee_size for indirect"); + + let PassMode::Direct(attrs) = &abi.ret.mode else { + panic!("Expected PassMode::Direct for return value, got: {:?}", abi.ret.mode); + }; + let regular = attrs.regular(); + assert!(regular.non_null, "Expected non_null for return value"); + assert!(regular.no_undef, "Expected no_undef for return value"); + assert!(!regular.no_alias, "Expected no_alias to be unset for return value"); + assert!(!regular.read_only, "Expected read_only to be unset for return value"); + assert!(!regular.no_free, "Expected no_free to be unset for return value"); + assert!(!regular.captures_none, "Expected captures_none to be unset for return value"); + assert!(!regular.captures_address, "Expected captures_address to be unset for return value"); + assert!( + !regular.captures_read_only, + "Expected captures_read_only to be unset for return value" + ); + assert_eq!(attrs.pointee_size().bytes(), 4, "Expected pointee_size for return value"); ControlFlow::Continue(()) } @@ -89,74 +283,98 @@ fn test_stable_mir() -> ControlFlow<()> { /// } /// ``` fn check_variadic(variadic_fn: CrateItem) { - let instance = Instance::try_from(variadic_fn).unwrap(); - let abi = instance.fn_abi().unwrap(); - assert!(abi.c_variadic); - assert_eq!(abi.args.len(), 1); + let instance = Instance::try_from(variadic_fn).expect("Expected variadic_fn instance"); + let abi = instance.fn_abi().expect("Expected function ABI"); + assert!(abi.c_variadic, "Expected C variadic function"); + assert_eq!(abi.args.len(), 1, "Expected one fixed variadic argument"); } /// Check the argument to be ignored: `ignore: [u8; 0]`. fn check_ignore(abi: &ArgAbi) { - assert!(abi.ty.kind().is_array()); - assert_eq!(abi.mode, PassMode::Ignore); + assert!(abi.ty.kind().is_array(), "Expected array argument"); + assert_eq!(abi.mode, PassMode::Ignore, "Expected PassMode::Ignore for empty array"); let layout = abi.layout.shape(); - assert!(layout.is_sized()); - assert!(layout.is_1zst()); + assert!(layout.is_sized(), "Expected sized layout for empty array"); + assert!(layout.is_1zst(), "Expected empty array to be a 1-ZST"); } /// Check the primitive argument: `primitive: char`. fn check_primitive(abi: &ArgAbi) { - assert!(abi.ty.kind().is_char()); + assert!(abi.ty.kind().is_char(), "Expected char argument"); let PassMode::Direct(ref attrs) = abi.mode else { panic!("Expected PassMode::Direct for char, got: {:?}", abi.mode); }; // A char (32-bit) doesn't need sign/zero extension on most platforms. #[cfg(not(any(target_arch = "loongarch64", target_arch = "riscv64")))] - assert_eq!(attrs.arg_extension(), ArgExtension::None); + assert_eq!(attrs.arg_extension(), ArgExtension::None, "Expected no extension for char"); // However, LoongArch64 and RiscV64 ABIs require that 32-bit integers // (signed or unsigned) are sign-extended when passed in registers. #[cfg(any(target_arch = "loongarch64", target_arch = "riscv64"))] - assert_eq!(attrs.arg_extension(), ArgExtension::Sext); + assert_eq!(attrs.arg_extension(), ArgExtension::Sext, "Expected sign extension for char"); // Direct arguments are not pointers, so no pointee alignment. - assert_eq!(attrs.pointee_align(), None); + assert_eq!(attrs.pointee_align(), None, "Expected no pointee alignment for char"); let layout = abi.layout.shape(); - assert!(layout.is_sized()); - assert!(!layout.is_1zst()); - assert_matches!(layout.fields, FieldsShape::Primitive); + assert!(layout.is_sized(), "Expected sized layout for char"); + assert!(!layout.is_1zst(), "Expected char not to be a 1-ZST"); + assert_matches!(layout.fields, FieldsShape::Primitive, "Expected primitive fields for char"); } /// Check the return value: `Result`. fn check_result(abi: &ArgAbi) { - assert!(abi.ty.kind().is_enum()); + assert!(abi.ty.kind().is_enum(), "Expected Result enum"); let PassMode::Indirect { ref attrs, address_space: _, mode } = abi.mode else { panic!("Expected PassMode::Indirect for Result, got: {:?}", abi.mode); }; // Indirect arguments have a pointee alignment (the pointer must be aligned). - assert!(attrs.pointee_align().is_some()); - assert!(mode == IndirectMode::Pointer); + assert!(attrs.pointee_align().is_some(), "Expected pointee alignment for Result"); + assert_eq!(mode, IndirectMode::Pointer, "Expected indirect pointer for Result"); let layout = abi.layout.shape(); - assert!(layout.is_sized()); - assert_matches!(layout.fields, FieldsShape::Arbitrary { .. }); - assert_matches!(layout.variants, VariantsShape::Multiple { .. }) + assert!(layout.is_sized(), "Expected sized layout for Result"); + assert_matches!( + layout.fields, + FieldsShape::Arbitrary { .. }, + "Expected arbitrary fields for Result", + ); + assert_matches!( + layout.variants, + VariantsShape::Multiple { .. }, + "Expected multiple variants for Result", + ); } /// Checks the niche information about `NonZero`. fn check_niche(abi: &ArgAbi) { - assert!(abi.ty.kind().is_struct()); - assert_matches!(abi.mode, PassMode::Direct { .. }); + assert!(abi.ty.kind().is_struct(), "Expected NonZero struct"); + assert_matches!(abi.mode, PassMode::Direct { .. }, "Expected PassMode::Direct for NonZero"); let layout = abi.layout.shape(); - assert!(layout.is_sized()); - assert_eq!(layout.size.bytes(), 1); - - let ValueRepr::Scalar(scalar) = layout.value_repr else { unreachable!() }; - assert!(scalar.has_niche(&MachineInfo::target()), "Opps: {:?}", scalar); - - let Scalar::Initialized { value, valid_range } = scalar else { unreachable!() }; - assert_matches!(value, Primitive::Int { length: IntegerLength::I8, signed: false }); - assert_eq!(valid_range.start, 1); - assert_eq!(valid_range.end, u8::MAX.into()); - assert!(!valid_range.contains(0)); - assert!(!valid_range.wraps_around()); + assert!(layout.is_sized(), "Expected sized layout for NonZero"); + assert_eq!(layout.size.bytes(), 1, "Expected one-byte size for NonZero"); + + let ValueRepr::Scalar(scalar) = layout.value_repr else { + panic!("Expected scalar representation for NonZero, got: {:?}", layout.value_repr); + }; + assert!( + scalar.has_niche(&MachineInfo::target()), + "Expected niche for NonZero, got: {:?}", + scalar, + ); + + let Scalar::Initialized { value, valid_range } = scalar else { + panic!("Expected initialized scalar for NonZero, got: {:?}", scalar); + }; + assert_matches!( + value, + Primitive::Int { length: IntegerLength::I8, signed: false }, + "Expected u8 scalar for NonZero", + ); + assert_eq!(valid_range.start, 1, "Expected valid range to start at one for NonZero"); + assert_eq!( + valid_range.end, + u8::MAX.into(), + "Expected valid range to end at u8::MAX for NonZero" + ); + assert!(!valid_range.contains(0), "Expected zero outside valid range for NonZero"); + assert!(!valid_range.wraps_around(), "Expected non-wrapping valid range for NonZero"); } fn get_item<'a>( @@ -186,15 +404,17 @@ impl MirVisitor for AdtDefVisitor { /// it will run the compiler. fn main() { let path = "alloc_input.rs"; - generate_input(&path).unwrap(); - let args = &[ + generate_input(&path).expect("Expected input file to be generated"); + let mut args = vec![ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-name".to_string(), CRATE_NAME.to_string(), path.to_string(), ]; - run!(args, test_stable_mir).unwrap(); + run!(&args, test_stable_mir).expect("Expected ABI checks to succeed"); + args.push("-Copt-level=1".to_string()); + run!(&args, test_pointer_attributes).expect("Expected pointer attribute checks to succeed"); } fn generate_input(path: &str) -> std::io::Result<()> { @@ -215,6 +435,17 @@ fn generate_input(path: &str) -> std::io::Result<()> { todo!() }} + pub fn pointer_attributes<'a>( + shared: &'a u32, + cell: &std::cell::Cell, + raw: *const u32, + nullable: Option<&u32>, + slice: &[u32], + indirect: [u64; 32], + ) -> &'a u32 {{ + shared + }} + pub unsafe extern "C" fn variadic_fn(n: usize, mut args: ...) -> usize {{ 0 }}