From 6f49d40b2804392a029c0e8e2abfd246592015f7 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 16 Oct 2023 22:34:03 +0900 Subject: [PATCH] aarch64: Support run-time detection of FEAT_LSE2 --- src/imp/atomic128/README.md | 4 +- src/imp/atomic128/aarch64.rs | 329 ++++++++++++++++++-- src/imp/atomic128/detect/aarch64_aa64reg.rs | 34 +- src/imp/atomic128/detect/aarch64_macos.rs | 12 +- src/imp/atomic128/detect/auxv.rs | 9 +- src/imp/atomic128/detect/common.rs | 6 +- 6 files changed, 326 insertions(+), 68 deletions(-) diff --git a/src/imp/atomic128/README.md b/src/imp/atomic128/README.md index 3657add5..00a467e8 100644 --- a/src/imp/atomic128/README.md +++ b/src/imp/atomic128/README.md @@ -6,8 +6,8 @@ Here is the table of targets that support 128-bit atomics and the instructions u | target_arch | load | store | CAS | RMW | note | | ----------- | ---- | ----- | --- | --- | ---- | -| x86_64 | cmpxchg16b or vmovdqa | cmpxchg16b or vmovdqa | cmpxchg16b | cmpxchg16b | cmpxchg16b target feature required. vmovdqa requires Intel or AMD CPU with AVX.
Both compile-time and run-time detection are supported for cmpxchg16b. vmovdqa is currently run-time detection only.
Requires rustc 1.59+ when cmpxchg16b target feature is enabled at compile-time, otherwise requires rustc 1.69+ | -| aarch64 | ldxp/stxp or casp or ldp/ldiapp | ldxp/stxp or casp or stp/stilp/swpp | ldxp/stxp or casp | ldxp/stxp or casp/swpp/ldclrp/ldsetp | casp requires lse target feature, ldp/stp requires lse2 target feature, ldiapp/stilp requires lse2 and rcpc3 target features, swpp/ldclrp/ldsetp requires lse128 target feature.
Both compile-time and run-time detection are supported for lse. Others are currently compile-time detection only.
Requires rustc 1.59+ | +| x86_64 | cmpxchg16b or vmovdqa | cmpxchg16b or vmovdqa | cmpxchg16b | cmpxchg16b | cmpxchg16b target feature required. vmovdqa requires Intel or AMD CPU with AVX.
Both compile-time and run-time detection are supported for cmpxchg16b. vmovdqa is currently run-time detection only.
Requires rustc 1.59+ when cmpxchg16b target feature is enabled at compile-time, otherwise requires rustc 1.69+ | +| aarch64 | ldxp/stxp or casp or ldp/ldiapp | ldxp/stxp or casp or stp/stilp/swpp | ldxp/stxp or casp | ldxp/stxp or casp/swpp/ldclrp/ldsetp | casp requires lse target feature, ldp/stp requires lse2 target feature, ldiapp/stilp requires lse2 and rcpc3 target features, swpp/ldclrp/ldsetp requires lse128 target feature.
Both compile-time and run-time detection are supported for lse and lse2. Others are currently compile-time detection only.
Requires rustc 1.59+ | | powerpc64 | lq | stq | lqarx/stqcx. | lqarx/stqcx. | Requires target-cpu pwr8+ (powerpc64le is pwr8 by default). Both compile-time and run-time detection are supported (run-time detection is currently disabled by default).
Requires nightly | | s390x | lpq | stpq | cdsg | cdsg | Requires nightly | diff --git a/src/imp/atomic128/aarch64.rs b/src/imp/atomic128/aarch64.rs index 5d409bd4..554f4bc1 100644 --- a/src/imp/atomic128/aarch64.rs +++ b/src/imp/atomic128/aarch64.rs @@ -68,7 +68,13 @@ include!("macros.rs"); // On musl with static linking, it seems that getauxval is not always available. // See detect/auxv.rs for more. #[cfg(not(portable_atomic_no_outline_atomics))] -#[cfg(any(test, not(any(target_feature = "lse", portable_atomic_target_feature = "lse"))))] +#[cfg(any( + test, + not(all( + any(target_feature = "lse2", portable_atomic_target_feature = "lse2"), + any(target_feature = "lse", portable_atomic_target_feature = "lse"), + )), +))] #[cfg(any( all( target_os = "linux", @@ -84,7 +90,13 @@ include!("macros.rs"); #[path = "detect/auxv.rs"] mod detect; #[cfg(not(portable_atomic_no_outline_atomics))] -#[cfg(any(test, not(any(target_feature = "lse", portable_atomic_target_feature = "lse"))))] +#[cfg(any( + test, + not(all( + any(target_feature = "lse2", portable_atomic_target_feature = "lse2"), + any(target_feature = "lse", portable_atomic_target_feature = "lse"), + )), +))] #[cfg(any(target_os = "netbsd", target_os = "openbsd"))] #[path = "detect/aarch64_aa64reg.rs"] mod detect; @@ -154,6 +166,43 @@ macro_rules! debug_assert_lse { } }; } +#[rustfmt::skip] +#[cfg(any( + target_feature = "lse2", + portable_atomic_target_feature = "lse2", + not(portable_atomic_no_outline_atomics), +))] +macro_rules! debug_assert_lse2 { + () => { + #[cfg(all( + not(portable_atomic_no_outline_atomics), + any( + all( + target_os = "linux", + any( + target_env = "gnu", + all( + any(target_env = "musl", target_env = "ohos"), + not(target_feature = "crt-static"), + ), + portable_atomic_outline_atomics, + ), + ), + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + // These don't support detection of FEAT_LSE2. + // target_os = "fuchsia", + // target_os = "windows", + ), + ))] + #[cfg(not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")))] + { + debug_assert!(detect::detect().has_lse2()); + } + }; +} // Refs: https://developer.arm.com/documentation/100067/0612/armclang-Integrated-Assembler/AArch32-Target-selection-directives?lang=en // @@ -230,29 +279,141 @@ macro_rules! atomic_rmw { // cfg guarantee that the CPU supports FEAT_LSE2. #[cfg(any(target_feature = "lse2", portable_atomic_target_feature = "lse2"))] -use atomic_load_ldp as atomic_load; +use _atomic_load_ldp as atomic_load; #[cfg(not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")))] #[inline] unsafe fn atomic_load(src: *mut u128, order: Ordering) -> u128 { - #[cfg(any(target_feature = "lse", portable_atomic_target_feature = "lse"))] - // SAFETY: the caller must uphold the safety contract. - // cfg guarantee that the CPU supports FEAT_LSE. - unsafe { - _atomic_load_casp(src, order) + #[inline] + unsafe fn atomic_load_no_lse2(src: *mut u128, order: Ordering) -> u128 { + #[cfg(any(target_feature = "lse", portable_atomic_target_feature = "lse"))] + // SAFETY: the caller must uphold the safety contract. + // cfg guarantee that the CPU supports FEAT_LSE. + unsafe { + _atomic_load_casp(src, order) + } + #[cfg(not(any(target_feature = "lse", portable_atomic_target_feature = "lse")))] + // SAFETY: the caller must uphold the safety contract. + unsafe { + _atomic_load_ldxp_stxp(src, order) + } } - #[cfg(not(any(target_feature = "lse", portable_atomic_target_feature = "lse")))] + #[cfg(not(all( + not(portable_atomic_no_outline_atomics), + any( + all( + target_os = "linux", + any( + target_env = "gnu", + all( + any(target_env = "musl", target_env = "ohos"), + not(target_feature = "crt-static"), + ), + portable_atomic_outline_atomics, + ), + ), + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + // These don't support detection of FEAT_LSE2. + // target_os = "fuchsia", + // target_os = "windows", + ), + )))] // SAFETY: the caller must uphold the safety contract. unsafe { - _atomic_load_ldxp_stxp(src, order) + atomic_load_no_lse2(src, order) + } + #[cfg(all( + not(portable_atomic_no_outline_atomics), + any( + all( + target_os = "linux", + any( + target_env = "gnu", + all( + any(target_env = "musl", target_env = "ohos"), + not(target_feature = "crt-static"), + ), + portable_atomic_outline_atomics, + ), + ), + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + // These don't support detection of FEAT_LSE2. + // target_os = "fuchsia", + // target_os = "windows", + ), + ))] + { + fn_alias! { + // inline(never) is just a hint and also not strictly necessary + // because we use ifunc helper macro, but used for clarity. + #[inline(never)] + unsafe fn(src: *mut u128) -> u128; + atomic_load_lse2_relaxed = _atomic_load_ldp(Ordering::Relaxed); + atomic_load_lse2_acquire = _atomic_load_ldp(Ordering::Acquire); + atomic_load_lse2_seqcst = _atomic_load_ldp(Ordering::SeqCst); + } + fn_alias! { + unsafe fn(src: *mut u128) -> u128; + atomic_load_no_lse2_relaxed = atomic_load_no_lse2(Ordering::Relaxed); + atomic_load_no_lse2_acquire = atomic_load_no_lse2(Ordering::Acquire); + atomic_load_no_lse2_seqcst = atomic_load_no_lse2(Ordering::SeqCst); + } + // SAFETY: the caller must uphold the safety contract. + // and we've checked if FEAT_LSE2 is available. + unsafe { + match order { + Ordering::Relaxed => { + ifunc!(unsafe fn(src: *mut u128) -> u128 { + let cpuinfo = detect::detect(); + if cpuinfo.has_lse2() { + atomic_load_lse2_relaxed + } else { + atomic_load_no_lse2_relaxed + } + }) + } + Ordering::Acquire => { + ifunc!(unsafe fn(src: *mut u128) -> u128 { + let cpuinfo = detect::detect(); + if cpuinfo.has_lse2() { + atomic_load_lse2_acquire + } else { + atomic_load_no_lse2_acquire + } + }) + } + Ordering::SeqCst => { + ifunc!(unsafe fn(src: *mut u128) -> u128 { + let cpuinfo = detect::detect(); + if cpuinfo.has_lse2() { + atomic_load_lse2_seqcst + } else { + atomic_load_no_lse2_seqcst + } + }) + } + _ => unreachable!("{:?}", order), + } + } } } // If CPU supports FEAT_LSE2, LDP/LDIAPP is single-copy atomic reads, // otherwise it is two single-copy atomic reads. // Refs: B2.2.1 of the Arm Architecture Reference Manual Armv8, for Armv8-A architecture profile -#[cfg(any(target_feature = "lse2", portable_atomic_target_feature = "lse2"))] +#[cfg(any( + target_feature = "lse2", + portable_atomic_target_feature = "lse2", + not(portable_atomic_no_outline_atomics), +))] #[inline] -unsafe fn atomic_load_ldp(src: *mut u128, order: Ordering) -> u128 { +unsafe fn _atomic_load_ldp(src: *mut u128, order: Ordering) -> u128 { debug_assert!(src as usize % 16 == 0); + debug_assert_lse2!(); // SAFETY: the caller must guarantee that `dst` is valid for reads, // 16-byte aligned, that there are no concurrent non-atomic operations. @@ -384,37 +545,149 @@ unsafe fn _atomic_load_ldxp_stxp(src: *mut u128, order: Ordering) -> u128 { // cfg guarantee that the CPU supports FEAT_LSE2. #[cfg(any(target_feature = "lse2", portable_atomic_target_feature = "lse2"))] -use atomic_store_stp as atomic_store; +use _atomic_store_stp as atomic_store; #[cfg(not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")))] #[inline] unsafe fn atomic_store(dst: *mut u128, val: u128, order: Ordering) { - // If FEAT_LSE is available at compile-time and portable_atomic_ll_sc_rmw cfg is not set, - // we use CAS-based atomic RMW. - #[cfg(all( - any(target_feature = "lse", portable_atomic_target_feature = "lse"), - not(portable_atomic_ll_sc_rmw), - ))] - // SAFETY: the caller must uphold the safety contract. - // cfg guarantee that the CPU supports FEAT_LSE. - unsafe { - _atomic_swap_casp(dst, val, order); + #[inline] + unsafe fn atomic_store_no_lse2(dst: *mut u128, val: u128, order: Ordering) { + // If FEAT_LSE is available at compile-time and portable_atomic_ll_sc_rmw cfg is not set, + // we use CAS-based atomic RMW. + #[cfg(all( + any(target_feature = "lse", portable_atomic_target_feature = "lse"), + not(portable_atomic_ll_sc_rmw), + ))] + // SAFETY: the caller must uphold the safety contract. + // cfg guarantee that the CPU supports FEAT_LSE. + unsafe { + _atomic_swap_casp(dst, val, order); + } + #[cfg(not(all( + any(target_feature = "lse", portable_atomic_target_feature = "lse"), + not(portable_atomic_ll_sc_rmw), + )))] + // SAFETY: the caller must uphold the safety contract. + unsafe { + _atomic_store_ldxp_stxp(dst, val, order); + } } #[cfg(not(all( - any(target_feature = "lse", portable_atomic_target_feature = "lse"), - not(portable_atomic_ll_sc_rmw), + not(portable_atomic_no_outline_atomics), + any( + all( + target_os = "linux", + any( + target_env = "gnu", + all( + any(target_env = "musl", target_env = "ohos"), + not(target_feature = "crt-static"), + ), + portable_atomic_outline_atomics, + ), + ), + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + // These don't support detection of FEAT_LSE2. + // target_os = "fuchsia", + // target_os = "windows", + ), )))] // SAFETY: the caller must uphold the safety contract. unsafe { - _atomic_store_ldxp_stxp(dst, val, order); + atomic_store_no_lse2(dst, val, order); + } + #[cfg(all( + not(portable_atomic_no_outline_atomics), + any( + all( + target_os = "linux", + any( + target_env = "gnu", + all( + any(target_env = "musl", target_env = "ohos"), + not(target_feature = "crt-static"), + ), + portable_atomic_outline_atomics, + ), + ), + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + // These don't support detection of FEAT_LSE2. + // target_os = "fuchsia", + // target_os = "windows", + ), + ))] + { + fn_alias! { + // inline(never) is just a hint and also not strictly necessary + // because we use ifunc helper macro, but used for clarity. + #[inline(never)] + unsafe fn(dst: *mut u128, val: u128); + atomic_store_lse2_relaxed = _atomic_store_stp(Ordering::Relaxed); + atomic_store_lse2_release = _atomic_store_stp(Ordering::Release); + atomic_store_lse2_seqcst = _atomic_store_stp(Ordering::SeqCst); + } + fn_alias! { + unsafe fn(dst: *mut u128, val: u128); + atomic_store_no_lse2_relaxed = atomic_store_no_lse2(Ordering::Relaxed); + atomic_store_no_lse2_release = atomic_store_no_lse2(Ordering::Release); + atomic_store_no_lse2_seqcst = atomic_store_no_lse2(Ordering::SeqCst); + } + // SAFETY: the caller must uphold the safety contract. + // and we've checked if FEAT_LSE2 is available. + unsafe { + match order { + Ordering::Relaxed => { + ifunc!(unsafe fn(dst: *mut u128, val: u128) { + let cpuinfo = detect::detect(); + if cpuinfo.has_lse2() { + atomic_store_lse2_relaxed + } else { + atomic_store_no_lse2_relaxed + } + }); + } + Ordering::Release => { + ifunc!(unsafe fn(dst: *mut u128, val: u128) { + let cpuinfo = detect::detect(); + if cpuinfo.has_lse2() { + atomic_store_lse2_release + } else { + atomic_store_no_lse2_release + } + }); + } + Ordering::SeqCst => { + ifunc!(unsafe fn(dst: *mut u128, val: u128) { + let cpuinfo = detect::detect(); + if cpuinfo.has_lse2() { + atomic_store_lse2_seqcst + } else { + atomic_store_no_lse2_seqcst + } + }); + } + _ => unreachable!("{:?}", order), + } + } } } // If CPU supports FEAT_LSE2, STP/STILP is single-copy atomic writes, // otherwise it is two single-copy atomic writes. // Refs: B2.2.1 of the Arm Architecture Reference Manual Armv8, for Armv8-A architecture profile -#[cfg(any(target_feature = "lse2", portable_atomic_target_feature = "lse2"))] +#[cfg(any( + target_feature = "lse2", + portable_atomic_target_feature = "lse2", + not(portable_atomic_no_outline_atomics), +))] #[inline] -unsafe fn atomic_store_stp(dst: *mut u128, val: u128, order: Ordering) { +unsafe fn _atomic_store_stp(dst: *mut u128, val: u128, order: Ordering) { debug_assert!(dst as usize % 16 == 0); + debug_assert_lse2!(); // SAFETY: the caller must guarantee that `dst` is valid for writes, // 16-byte aligned, that there are no concurrent non-atomic operations. diff --git a/src/imp/atomic128/detect/aarch64_aa64reg.rs b/src/imp/atomic128/detect/aarch64_aa64reg.rs index f800f5af..1e9a9cee 100644 --- a/src/imp/atomic128/detect/aarch64_aa64reg.rs +++ b/src/imp/atomic128/detect/aarch64_aa64reg.rs @@ -37,7 +37,6 @@ struct AA64Reg { aa64isar0: u64, #[cfg(test)] aa64isar1: u64, - #[cfg(test)] aa64mmfr2: u64, } @@ -47,7 +46,6 @@ fn _detect(info: &mut CpuInfo) { aa64isar0, #[cfg(test)] aa64isar1, - #[cfg(test)] aa64mmfr2, } = imp::aa64reg(); @@ -56,7 +54,7 @@ fn _detect(info: &mut CpuInfo) { let atomic = extract(aa64isar0, 23, 20); if atomic >= 2 { info.set(CpuInfo::HAS_LSE); - // we currently only use FEAT_LSE in outline-atomics. + // we currently only use FEAT_LSE and FEAT_LSE2 in outline-atomics. #[cfg(test)] { if atomic >= 3 { @@ -64,7 +62,7 @@ fn _detect(info: &mut CpuInfo) { } } } - // we currently only use FEAT_LSE in outline-atomics. + // we currently only use FEAT_LSE and FEAT_LSE2 in outline-atomics. #[cfg(test)] { // ID_AA64ISAR1_EL1, Instruction Set Attribute Register 1 @@ -72,11 +70,11 @@ fn _detect(info: &mut CpuInfo) { if extract(aa64isar1, 23, 20) >= 3 { info.set(CpuInfo::HAS_RCPC3); } - // ID_AA64MMFR2_EL1, AArch64 Memory Model Feature Register 2 - // https://developer.arm.com/documentation/ddi0601/2023-06/AArch64-Registers/ID-AA64MMFR2-EL1--AArch64-Memory-Model-Feature-Register-2?lang=en - if extract(aa64mmfr2, 35, 32) >= 1 { - info.set(CpuInfo::HAS_LSE2); - } + } + // ID_AA64MMFR2_EL1, AArch64 Memory Model Feature Register 2 + // https://developer.arm.com/documentation/ddi0601/2023-06/AArch64-Registers/ID-AA64MMFR2-EL1--AArch64-Memory-Model-Feature-Register-2?lang=en + if extract(aa64mmfr2, 35, 32) >= 1 { + info.set(CpuInfo::HAS_LSE2); } } @@ -114,21 +112,16 @@ mod imp { options(pure, nomem, nostack, preserves_flags) ); } - #[cfg(test)] let aa64mmfr2: u64; - #[cfg(test)] - { - asm!( - "mrs {0}, ID_AA64MMFR2_EL1", - out(reg) aa64mmfr2, - options(pure, nomem, nostack, preserves_flags) - ); - } + asm!( + "mrs {0}, ID_AA64MMFR2_EL1", + out(reg) aa64mmfr2, + options(pure, nomem, nostack, preserves_flags) + ); AA64Reg { aa64isar0, #[cfg(test)] aa64isar1, - #[cfg(test)] aa64mmfr2, } } @@ -273,7 +266,6 @@ mod imp { pub(crate) const CPU_ID_AA64ISAR0: c_int = 2; #[cfg(test)] pub(crate) const CPU_ID_AA64ISAR1: c_int = 3; - #[cfg(test)] pub(crate) const CPU_ID_AA64MMFR2: c_int = 7; extern "C" { @@ -302,13 +294,11 @@ mod imp { let aa64isar0 = sysctl64(&[ffi::CTL_MACHDEP, ffi::CPU_ID_AA64ISAR0]).unwrap_or(0); #[cfg(test)] let aa64isar1 = sysctl64(&[ffi::CTL_MACHDEP, ffi::CPU_ID_AA64ISAR1]).unwrap_or(0); - #[cfg(test)] let aa64mmfr2 = sysctl64(&[ffi::CTL_MACHDEP, ffi::CPU_ID_AA64MMFR2]).unwrap_or(0); AA64Reg { aa64isar0, #[cfg(test)] aa64isar1, - #[cfg(test)] aa64mmfr2, } } diff --git a/src/imp/atomic128/detect/aarch64_macos.rs b/src/imp/atomic128/detect/aarch64_macos.rs index 2ed63664..d6bf9d00 100644 --- a/src/imp/atomic128/detect/aarch64_macos.rs +++ b/src/imp/atomic128/detect/aarch64_macos.rs @@ -6,7 +6,7 @@ // https://github.com/llvm/llvm-project/blob/llvmorg-17.0.0-rc2/llvm/include/llvm/TargetParser/AArch64TargetParser.h#L494 // // If macOS supporting Armv9.4-a becomes popular in the future, this module will -// be used to support outline atomics for FEAT_LSE128/FEAT_LRCPC3. +// be used to support outline-atomics for FEAT_LSE128/FEAT_LRCPC3. // // Refs: https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics // @@ -79,13 +79,13 @@ fn _detect(info: &mut CpuInfo) { } { info.set(CpuInfo::HAS_LSE); } - // we currently only use FEAT_LSE in outline-atomics. + // SAFETY: we passed a valid C string. + if unsafe { sysctlbyname32(b"hw.optional.arm.FEAT_LSE2\0").unwrap_or(0) != 0 } { + info.set(CpuInfo::HAS_LSE2); + } + // we currently only use FEAT_LSE and FEAT_LSE2 in outline-atomics. #[cfg(test)] { - // SAFETY: we passed a valid C string. - if unsafe { sysctlbyname32(b"hw.optional.arm.FEAT_LSE2\0").unwrap_or(0) != 0 } { - info.set(CpuInfo::HAS_LSE2); - } // SAFETY: we passed a valid C string. if unsafe { sysctlbyname32(b"hw.optional.arm.FEAT_LSE128\0").unwrap_or(0) != 0 } { info.set(CpuInfo::HAS_LSE128); diff --git a/src/imp/atomic128/detect/auxv.rs b/src/imp/atomic128/detect/auxv.rs index c13e5f8c..94954b7a 100644 --- a/src/imp/atomic128/detect/auxv.rs +++ b/src/imp/atomic128/detect/auxv.rs @@ -187,7 +187,6 @@ mod arch { // https://github.com/freebsd/freebsd-src/blob/release/13.0.0/sys/arm64/include/elf.h // https://github.com/freebsd/freebsd-src/blob/release/12.2.0/sys/arm64/include/elf.h pub(super) const HWCAP_ATOMICS: ffi::c_ulong = 1 << 8; - #[cfg(test)] pub(super) const HWCAP_USCAT: ffi::c_ulong = 1 << 25; #[cold] @@ -197,12 +196,8 @@ mod arch { if hwcap & HWCAP_ATOMICS != 0 { info.set(CpuInfo::HAS_LSE); } - // we currently only use FEAT_LSE in outline-atomics. - #[cfg(test)] - { - if hwcap & HWCAP_USCAT != 0 { - info.set(CpuInfo::HAS_LSE2); - } + if hwcap & HWCAP_USCAT != 0 { + info.set(CpuInfo::HAS_LSE2); } } } diff --git a/src/imp/atomic128/detect/common.rs b/src/imp/atomic128/detect/common.rs index dbe0a567..b87caa35 100644 --- a/src/imp/atomic128/detect/common.rs +++ b/src/imp/atomic128/detect/common.rs @@ -50,8 +50,7 @@ impl CpuInfo { /// Whether FEAT_LSE is available const HAS_LSE: u32 = 1; /// Whether FEAT_LSE2 is available - // This is currently only used in tests. - #[cfg(test)] + #[cfg_attr(not(test), allow(dead_code))] const HAS_LSE2: u32 = 2; /// Whether FEAT_LSE128 is available // This is currently only used in tests. @@ -67,7 +66,8 @@ impl CpuInfo { pub(crate) fn has_lse(self) -> bool { self.test(CpuInfo::HAS_LSE) } - #[cfg(test)] + #[cfg_attr(not(test), allow(dead_code))] + #[cfg(any(test, not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2"))))] #[inline] pub(crate) fn has_lse2(self) -> bool { self.test(CpuInfo::HAS_LSE2)