From 22b471255754c90921bd8f6b9e91c40d0c7315e9 Mon Sep 17 00:00:00 2001 From: sayantn Date: Tue, 2 Sep 2025 07:10:09 +0530 Subject: [PATCH] Add the `cpuid` target feature - Make `cpuid` and friends safe with `#[target_feature(enable = "cpuid")]` - Update documentation --- compiler/rustc_codegen_gcc/src/gcc_util.rs | 1 + compiler/rustc_codegen_llvm/src/llvm_util.rs | 2 + compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/target_features.rs | 43 ++++++++++--------- library/core/src/lib.rs | 1 + library/std_detect/src/detect/arch/x86.rs | 2 + library/std_detect/src/detect/os/x86.rs | 2 + library/stdarch/crates/core_arch/src/lib.rs | 3 +- .../stdarch/crates/core_arch/src/x86/cpuid.rs | 43 ++++++++++++------- tests/ui/check-cfg/target_feature.stderr | 1 + .../feature-gate-cpuid_target_feature.rs | 19 ++++++++ .../feature-gate-cpuid_target_feature.stderr | 34 +++++++++++++++ 13 files changed, 116 insertions(+), 38 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-cpuid_target_feature.rs create mode 100644 tests/ui/feature-gates/feature-gate-cpuid_target_feature.stderr diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 42ba40692b75c..c4b7fbbe1c957 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -82,6 +82,7 @@ pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> ("x86", "avx512vbmi2") => smallvec!["avx512vbmi2", "avx512bw"], // NOTE: seems like GCC requires 'avx512bw' for 'avx512bitalg'. ("x86", "avx512bitalg") => smallvec!["avx512bitalg", "avx512bw"], + ("x86", "cpuid") => smallvec![], ("aarch64", "rcpc2") => smallvec!["rcpc-immo"], ("aarch64", "dpb") => smallvec!["ccpp"], ("aarch64", "dpb2") => smallvec!["ccdp"], diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 45c5c9aa5514d..851bb0ebb68de 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -280,6 +280,8 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option None, (_, s) => Some(LLVMFeature::new(s)), } } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 93e5588146e14..a3669a3936c06 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -455,6 +455,8 @@ declare_features! ( /// Allows function attribute `#[coverage(on/off)]`, to control coverage /// instrumentation of that function. (unstable, coverage_attribute, "1.74.0", Some(84605)), + /// Allows using the `cpuid` target feature + (unstable, cpuid_target_feature, "CURRENT_RUSTC_VERSION", Some(146558)), /// Allows non-builtin attributes in inner attribute position. (unstable, custom_inner_attributes, "1.30.0", Some(54726)), /// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4fef65f46b1fd..6691221ff4883 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -780,6 +780,7 @@ symbols! { count, coverage, coverage_attribute, + cpuid_target_feature, cr, crate_in_paths, crate_local, diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index dc70089c385fe..f69aeabe18462 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -375,7 +375,7 @@ const AARCH64_TIED_FEATURES: &[&[&str]] = &[ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("adx", Stable, &[]), + ("adx", Stable, &["cpuid"]), ("aes", Stable, &["sse2"]), ("amx-avx512", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("amx-bf16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), @@ -385,9 +385,9 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("amx-int8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("amx-movrs", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("amx-tf32", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]), + ("amx-tile", Unstable(sym::x86_amx_intrinsics), &["cpuid"]), ("amx-transpose", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("apxf", Unstable(sym::apx_target_feature), &[]), + ("apxf", Unstable(sym::apx_target_feature), &["cpuid"]), ("avx", Stable, &["sse4.2"]), ("avx2", Stable, &["avx"]), ( @@ -429,24 +429,25 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("avxvnni", Stable, &["avx2"]), ("avxvnniint8", Stable, &["avx2"]), ("avxvnniint16", Stable, &["avx2"]), - ("bmi1", Stable, &[]), - ("bmi2", Stable, &[]), - ("cmpxchg16b", Stable, &[]), - ("ermsb", Unstable(sym::ermsb_target_feature), &[]), + ("bmi1", Stable, &["cpuid"]), + ("bmi2", Stable, &["cpuid"]), + ("cmpxchg16b", Stable, &["cpuid"]), + ("cpuid", Unstable(sym::cpuid_target_feature), &[]), + ("ermsb", Unstable(sym::ermsb_target_feature), &["cpuid"]), ("f16c", Stable, &["avx"]), ("fma", Stable, &["avx"]), - ("fxsr", Stable, &[]), + ("fxsr", Stable, &["cpuid"]), ("gfni", Stable, &["sse2"]), ("kl", Stable, &["sse2"]), - ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), - ("lzcnt", Stable, &[]), - ("movbe", Stable, &[]), - ("movrs", Unstable(sym::movrs_target_feature), &[]), + ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &["cpuid"]), + ("lzcnt", Stable, &["cpuid"]), + ("movbe", Stable, &["cpuid"]), + ("movrs", Unstable(sym::movrs_target_feature), &["cpuid"]), ("pclmulqdq", Stable, &["sse2"]), - ("popcnt", Stable, &[]), - ("prfchw", Unstable(sym::prfchw_target_feature), &[]), - ("rdrand", Stable, &[]), - ("rdseed", Stable, &[]), + ("popcnt", Stable, &["cpuid"]), + ("prfchw", Unstable(sym::prfchw_target_feature), &["cpuid"]), + ("rdrand", Stable, &["cpuid"]), + ("rdseed", Stable, &["cpuid"]), ( "retpoline-external-thunk", Stability::Forbidden { reason: "use `-Zretpoline-external-thunk` compiler flag instead" }, @@ -462,7 +463,7 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ Stability::Forbidden { reason: "use `-Zretpoline` compiler flag instead" }, &[], ), - ("rtm", Unstable(sym::rtm_target_feature), &[]), + ("rtm", Unstable(sym::rtm_target_feature), &["cpuid"]), ("sha", Stable, &["sse2"]), ("sha512", Stable, &["avx2"]), ("sm3", Stable, &["avx"]), @@ -470,20 +471,20 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // This cannot actually be toggled, the ABI always fixes it, so it'd make little sense to // stabilize. It must be in this list for the ABI check to be able to use it. ("soft-float", Stability::Unstable(sym::x87_target_feature), &[]), - ("sse", Stable, &[]), + ("sse", Stable, &["cpuid"]), ("sse2", Stable, &["sse"]), ("sse3", Stable, &["sse2"]), ("sse4.1", Stable, &["ssse3"]), ("sse4.2", Stable, &["sse4.1"]), ("sse4a", Stable, &["sse3"]), ("ssse3", Stable, &["sse3"]), - ("tbm", Stable, &[]), + ("tbm", Stable, &["cpuid"]), ("vaes", Stable, &["avx2", "aes"]), ("vpclmulqdq", Stable, &["avx", "pclmulqdq"]), ("widekl", Stable, &["kl"]), - ("x87", Unstable(sym::x87_target_feature), &[]), + ("x87", Unstable(sym::x87_target_feature), &["cpuid"]), ("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), - ("xsave", Stable, &[]), + ("xsave", Stable, &["cpuid"]), ("xsavec", Stable, &["xsave"]), ("xsaveopt", Stable, &["xsave"]), ("xsaves", Stable, &["xsave"]), diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 5d52bfb1b125f..83a5f66daaaf9 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -194,6 +194,7 @@ // tidy-alphabetical-start #![feature(aarch64_unstable_target_feature)] #![feature(arm_target_feature)] +#![feature(cpuid_target_feature)] #![feature(hexagon_target_feature)] #![feature(loongarch_target_feature)] #![feature(mips_target_feature)] diff --git a/library/std_detect/src/detect/arch/x86.rs b/library/std_detect/src/detect/arch/x86.rs index bd749b88f566d..d8df8fbb2aab9 100644 --- a/library/std_detect/src/detect/arch/x86.rs +++ b/library/std_detect/src/detect/arch/x86.rs @@ -281,4 +281,6 @@ features! { /// ERMSB, Enhanced REP MOVSB and STOSB @FEATURE: #[unstable(feature = "xop_target_feature", issue = "127208")] xop: "xop"; /// XOP: eXtended Operations (AMD) + @FEATURE: #[unstable(feature = "cpuid_target_feature", issue = "146558")] cpuid: "cpuid"; + /// CPUID } diff --git a/library/std_detect/src/detect/os/x86.rs b/library/std_detect/src/detect/os/x86.rs index cf11d8333127f..b0f1c241ed875 100644 --- a/library/std_detect/src/detect/os/x86.rs +++ b/library/std_detect/src/detect/os/x86.rs @@ -32,6 +32,8 @@ pub(crate) fn detect_features() -> cache::Initializer { return value; } + value.set(Feature::cpuid as u32); + // Calling `__cpuid`/`__cpuid_count` from here on is safe because the CPU // has `cpuid` support. diff --git a/library/stdarch/crates/core_arch/src/lib.rs b/library/stdarch/crates/core_arch/src/lib.rs index 26a9cb5899183..b444e3bf67af6 100644 --- a/library/stdarch/crates/core_arch/src/lib.rs +++ b/library/stdarch/crates/core_arch/src/lib.rs @@ -34,7 +34,8 @@ f16, aarch64_unstable_target_feature, bigint_helper_methods, - funnel_shifts + funnel_shifts, + cpuid_target_feature )] #![cfg_attr(test, feature(test, abi_vectorcall, stdarch_internal))] #![deny(clippy::missing_inline_in_public_items)] diff --git a/library/stdarch/crates/core_arch/src/x86/cpuid.rs b/library/stdarch/crates/core_arch/src/x86/cpuid.rs index 0634f10a99fdc..8866333113da0 100644 --- a/library/stdarch/crates/core_arch/src/x86/cpuid.rs +++ b/library/stdarch/crates/core_arch/src/x86/cpuid.rs @@ -27,11 +27,20 @@ pub struct CpuidResult { /// Returns the result of the `cpuid` instruction for a given `leaf` (`EAX`) /// and `sub_leaf` (`ECX`). -/// -/// The highest-supported leaf value is returned by the first tuple argument of -/// [`__get_cpuid_max(0)`](fn.__get_cpuid_max.html). For leaves containing -/// sub-leaves, the second tuple argument returns the highest-supported -/// sub-leaf value. +/// +/// There are 2 types of information leafs - basic leafs (with `leaf < 0x8000000`) +/// and extended leafs (with `leaf >= 0x80000000`). The highest supported basic and +/// extended leafs can be obtained by calling CPUID with `0` and `0x80000000`, +/// respectively, and reading the value in the `EAX` register. If the leaf supports +/// more than one sub-leafs, then the procedure of obtaining the highest supported +/// sub-leaf, as well as the behavior if a invalid sub-leaf value is passed, depends +/// on the specific leaf. +/// +/// If the `leaf` value is higher than the maximum supported basic or extended leaf +/// for the processor, this returns the information for the highest supported basic +/// information leaf (with the passed `sub_leaf` value). If the `leaf` value is less +/// than or equal to the highest basic or extended leaf value, but the leaf is not +/// supported on the processor, all zeros are returned. /// /// The [CPUID Wikipedia page][wiki_cpuid] contains how to query which /// information using the `EAX` and `ECX` registers, and the interpretation of @@ -48,8 +57,9 @@ pub struct CpuidResult { /// [amd64_ref]: http://support.amd.com/TechDocs/24594.pdf #[inline] #[cfg_attr(test, assert_instr(cpuid))] +#[target_feature(enable = "cpuid")] #[stable(feature = "simd_x86", since = "1.27.0")] -pub unsafe fn __cpuid_count(leaf: u32, sub_leaf: u32) -> CpuidResult { +pub fn __cpuid_count(leaf: u32, sub_leaf: u32) -> CpuidResult { let eax; let ebx; let ecx; @@ -58,7 +68,7 @@ pub unsafe fn __cpuid_count(leaf: u32, sub_leaf: u32) -> CpuidResult { // LLVM sometimes reserves `ebx` for its internal use, we so we need to use // a scratch register for it instead. #[cfg(target_arch = "x86")] - { + unsafe { asm!( "mov {0}, ebx", "cpuid", @@ -71,7 +81,7 @@ pub unsafe fn __cpuid_count(leaf: u32, sub_leaf: u32) -> CpuidResult { ); } #[cfg(target_arch = "x86_64")] - { + unsafe { asm!( "mov {0:r}, rbx", "cpuid", @@ -86,27 +96,28 @@ pub unsafe fn __cpuid_count(leaf: u32, sub_leaf: u32) -> CpuidResult { CpuidResult { eax, ebx, ecx, edx } } +/// Calls CPUID with the provided `leaf` value, with `sub_leaf` set to 0. /// See [`__cpuid_count`](fn.__cpuid_count.html). #[inline] #[cfg_attr(test, assert_instr(cpuid))] +#[target_feature(enable = "cpuid")] #[stable(feature = "simd_x86", since = "1.27.0")] -pub unsafe fn __cpuid(leaf: u32) -> CpuidResult { +pub fn __cpuid(leaf: u32) -> CpuidResult { __cpuid_count(leaf, 0) } -/// Returns the highest-supported `leaf` (`EAX`) and sub-leaf (`ECX`) `cpuid` -/// values. +/// Returns the EAX and EBX register after calling CPUID with the provided `leaf`, +/// with `sub_leaf` set to 0. /// -/// If `cpuid` is supported, and `leaf` is zero, then the first tuple argument -/// contains the highest `leaf` value that `cpuid` supports. For `leaf`s -/// containing sub-leafs, the second tuple argument contains the -/// highest-supported sub-leaf value. +/// If `leaf` if 0 or `0x80000000`, the first tuple argument contains the maximum +/// supported basic or extended leaf, respectively. /// /// See also [`__cpuid`](fn.__cpuid.html) and /// [`__cpuid_count`](fn.__cpuid_count.html). #[inline] +#[target_feature(enable = "cpuid")] #[stable(feature = "simd_x86", since = "1.27.0")] -pub unsafe fn __get_cpuid_max(leaf: u32) -> (u32, u32) { +pub fn __get_cpuid_max(leaf: u32) -> (u32, u32) { let CpuidResult { eax, ebx, .. } = __cpuid(leaf); (eax, ebx) } diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index 258f21324661b..2a837c1ae0400 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -64,6 +64,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `cache` `cmpxchg16b` `concurrent-functions` +`cpuid` `crc` `crt-static` `cssc` diff --git a/tests/ui/feature-gates/feature-gate-cpuid_target_feature.rs b/tests/ui/feature-gates/feature-gate-cpuid_target_feature.rs new file mode 100644 index 0000000000000..75b3499e016e1 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cpuid_target_feature.rs @@ -0,0 +1,19 @@ +//@ only-x86_64 + +use std::arch::x86_64::*; + +#[target_feature(enable = "cpuid")] +//~^ ERROR: currently unstable +fn annotation() {} + +fn detection() -> bool { + is_x86_feature_detected!("cpuid") + //~^ ERROR: use of unstable library feature +} + +fn intrinsic() -> CpuidResult { + __cpuid(0) + //~^ ERROR: requires unsafe function or block +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-cpuid_target_feature.stderr b/tests/ui/feature-gates/feature-gate-cpuid_target_feature.stderr new file mode 100644 index 0000000000000..97643bdc461e2 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cpuid_target_feature.stderr @@ -0,0 +1,34 @@ +error[E0658]: use of unstable library feature `cpuid_target_feature` + --> $DIR/feature-gate-cpuid_target_feature.rs:10:5 + | +LL | is_x86_feature_detected!("cpuid") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #146558 for more information + = help: add `#![feature(cpuid_target_feature)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: this error originates in the macro `$crate::detect_feature` which comes from the expansion of the macro `is_x86_feature_detected` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0658]: the target feature `cpuid` is currently unstable + --> $DIR/feature-gate-cpuid_target_feature.rs:5:18 + | +LL | #[target_feature(enable = "cpuid")] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #146558 for more information + = help: add `#![feature(cpuid_target_feature)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0133]: call to function `std::arch::x86_64::__cpuid` with `#[target_feature]` is unsafe and requires unsafe function or block + --> $DIR/feature-gate-cpuid_target_feature.rs:15:5 + | +LL | __cpuid(0) + | ^^^^^^^^^^ call to function with `#[target_feature]` + | + = help: in order for the call to be safe, the context requires the following additional target feature: cpuid + = note: the cpuid target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0133, E0658. +For more information about an error, try `rustc --explain E0133`.