From ab783ad752014e8f7b99df1cab16e5c472af9489 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 24 Jun 2026 00:56:23 +0200 Subject: [PATCH 1/6] accept extern calls to compiler-builtin functions in `is_call_from_compiler_builtins_to_upstream_monomorphization`, suggested by Saethlin --- compiler/rustc_codegen_ssa/src/base.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 4e979df471318..5b235bcba7b43 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -883,6 +883,10 @@ pub fn codegen_crate< /// unlinkable calls. /// /// Note that calls to LLVM intrinsics are uniquely okay because they won't make it to the linker. +/// Note also that calls to foreign items that are actually exported by the local crate are also +/// okay. This situation arises because compiler-builtins calls functions in core that are +/// `#[inline]` wrappers for `extern "C"` declarations in core, which resolve to a symbol exported +/// by compiler-builtins. pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, @@ -895,11 +899,19 @@ pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( } } + fn is_extern_call_to_local_crate<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> bool { + tcx.is_foreign_item(instance.def_id()) + && tcx.exported_non_generic_symbols(LOCAL_CRATE).iter().any(|(sym, _info)| { + sym.symbol_name_for_local_instance(tcx) == tcx.symbol_name(instance) + }) + } + let def_id = instance.def_id(); !def_id.is_local() && tcx.is_compiler_builtins(LOCAL_CRATE) && !is_llvm_intrinsic(tcx, def_id) && !tcx.should_codegen_locally(instance) + && !is_extern_call_to_local_crate(tcx, instance) } impl CrateInfo { From 47acba7c146471017f651e865def06af1b87968e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 4 Jul 2026 17:31:26 +0200 Subject: [PATCH 2/6] remove `-Zmiri-force-intrinsic-fallback` test run We'll do this using `-Zforce-intrinsic-fallback` in the main repo going forward --- src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh b/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh index c2a5b308b3284..0ccd364be21bb 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh @@ -15,14 +15,6 @@ if [ -z "${PR_CI_JOB:-}" ]; then else python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri fi -# We re-run the test suite for a chance to find bugs in the intrinsic fallback bodies and in MIR -# optimizations. This can miss UB, so we only run the "pass" tests. We need to enable debug -# assertions as `-O` disables them but some tests rely on them. We also set a cfg flag so tests can -# adjust their expectations if needed. This can change the output of the tests so we ignore that, -# we only ensure that all assertions still pass. -MIRIFLAGS="-Zmiri-force-intrinsic-fallback --cfg force_intrinsic_fallback -O -Zmir-opt-level=4 -Cdebug-assertions=yes" \ - MIRI_SKIP_UI_CHECKS=1 \ - python3 "$X_PY" test --stage 2 src/tools/miri -- tests/pass tests/panic # We natively run this script on x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc. # Also cover some other targets via cross-testing, in particular all tier 1 targets. case $HOST_TARGET in From 63bec90ff294fcbeca39f3fcb1b18a3b17c85d6f Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 10 Jan 2026 17:32:06 -0600 Subject: [PATCH 3/6] intrinsics: Add a fallback for non-const libm float functions A number of float operations from libm have intrinsics for optimization, but it is also okay to just call the libm functions directly. Add a fallback for these cases, including converting to/from another float size where needed, so the backends don't need to override these. We do not add a fallback body for intrinsics that have a softfloat implementation (e.g. sqrt, fma), because it would make them harder to constify later. --- compiler/rustc_codegen_ssa/src/base.rs | 4 +- library/core/src/intrinsics/mod.rs | 161 ++++++++++++++++++++----- library/core/src/num/imp/libm.rs | 148 ++++++++++++++++++++++- 3 files changed, 278 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 5b235bcba7b43..b9684eb52f3f4 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -877,9 +877,9 @@ pub fn codegen_crate< /// Returns whether a call from the current crate to the [`Instance`] would produce a call /// from `compiler_builtins` to a symbol the linker must resolve. /// -/// Such calls from `compiler_bultins` are effectively impossible for the linker to handle. Some +/// Such calls from `compiler_builtins` are effectively impossible for the linker to handle. Some /// linkers will optimize such that dead calls to unresolved symbols are not an error, but this is -/// not guaranteed. So we used this function in codegen backends to ensure we do not generate any +/// not guaranteed. So we use this function in codegen backends to ensure we do not generate any /// unlinkable calls. /// /// Note that calls to LLVM intrinsics are uniquely okay because they won't make it to the linker. diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index bc96c768c0c94..84e8aa9569fec 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -55,6 +55,7 @@ use crate::ffi::{VaArgSafe, VaList}; use crate::marker::{ConstParamTy, DiscriminantKind, PointeeSized, Tuple}; +use crate::num::imp::libm; use crate::{mem, ptr}; mod bounds; @@ -1083,233 +1084,329 @@ pub fn powif128(a: f128, x: i32) -> f128; /// /// The stabilized version of this intrinsic is /// [`f16::sin`](../../std/primitive.f16.html#method.sin) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn sinf16(x: f16) -> f16; +pub fn sinf16(x: f16) -> f16 { + libm::likely_available::sinf(x as f32) as f16 +} /// Returns the sine of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::sin`](../../std/primitive.f32.html#method.sin) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn sinf32(x: f32) -> f32; +pub fn sinf32(x: f32) -> f32 { + libm::likely_available::sinf(x) +} /// Returns the sine of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::sin`](../../std/primitive.f64.html#method.sin) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn sinf64(x: f64) -> f64; +pub fn sinf64(x: f64) -> f64 { + libm::likely_available::sin(x) +} /// Returns the sine of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::sin`](../../std/primitive.f128.html#method.sin) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn sinf128(x: f128) -> f128; +pub fn sinf128(x: f128) -> f128 { + libm::maybe_available::sinf128(x) +} /// Returns the cosine of an `f16`. /// /// The stabilized version of this intrinsic is /// [`f16::cos`](../../std/primitive.f16.html#method.cos) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn cosf16(x: f16) -> f16; +pub fn cosf16(x: f16) -> f16 { + libm::likely_available::cosf(x as f32) as f16 +} /// Returns the cosine of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::cos`](../../std/primitive.f32.html#method.cos) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn cosf32(x: f32) -> f32; +pub fn cosf32(x: f32) -> f32 { + libm::likely_available::cosf(x) +} /// Returns the cosine of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::cos`](../../std/primitive.f64.html#method.cos) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn cosf64(x: f64) -> f64; +pub fn cosf64(x: f64) -> f64 { + libm::likely_available::cos(x) +} /// Returns the cosine of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::cos`](../../std/primitive.f128.html#method.cos) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn cosf128(x: f128) -> f128; +pub fn cosf128(x: f128) -> f128 { + libm::maybe_available::cosf128(x) +} /// Raises an `f16` to an `f16` power. /// /// The stabilized version of this intrinsic is /// [`f16::powf`](../../std/primitive.f16.html#method.powf) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn powf16(a: f16, x: f16) -> f16; +pub fn powf16(a: f16, x: f16) -> f16 { + libm::likely_available::powf(a as f32, x as f32) as f16 +} /// Raises an `f32` to an `f32` power. /// /// The stabilized version of this intrinsic is /// [`f32::powf`](../../std/primitive.f32.html#method.powf) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn powf32(a: f32, x: f32) -> f32; +pub fn powf32(a: f32, x: f32) -> f32 { + libm::likely_available::powf(a, x) +} /// Raises an `f64` to an `f64` power. /// /// The stabilized version of this intrinsic is /// [`f64::powf`](../../std/primitive.f64.html#method.powf) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn powf64(a: f64, x: f64) -> f64; +pub fn powf64(a: f64, x: f64) -> f64 { + libm::likely_available::pow(a, x) +} /// Raises an `f128` to an `f128` power. /// /// The stabilized version of this intrinsic is /// [`f128::powf`](../../std/primitive.f128.html#method.powf) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn powf128(a: f128, x: f128) -> f128; +pub fn powf128(a: f128, x: f128) -> f128 { + libm::maybe_available::powf128(a, x) +} /// Returns the exponential of an `f16`. /// /// The stabilized version of this intrinsic is /// [`f16::exp`](../../std/primitive.f16.html#method.exp) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn expf16(x: f16) -> f16; +pub fn expf16(x: f16) -> f16 { + libm::likely_available::expf(x as f32) as f16 +} /// Returns the exponential of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::exp`](../../std/primitive.f32.html#method.exp) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn expf32(x: f32) -> f32; +pub fn expf32(x: f32) -> f32 { + libm::likely_available::expf(x) +} /// Returns the exponential of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::exp`](../../std/primitive.f64.html#method.exp) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn expf64(x: f64) -> f64; +pub fn expf64(x: f64) -> f64 { + libm::likely_available::exp(x) +} /// Returns the exponential of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::exp`](../../std/primitive.f128.html#method.exp) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn expf128(x: f128) -> f128; +pub fn expf128(x: f128) -> f128 { + libm::maybe_available::expf128(x) +} /// Returns 2 raised to the power of an `f16`. /// /// The stabilized version of this intrinsic is /// [`f16::exp2`](../../std/primitive.f16.html#method.exp2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn exp2f16(x: f16) -> f16; +pub fn exp2f16(x: f16) -> f16 { + libm::likely_available::exp2f(x as f32) as f16 +} /// Returns 2 raised to the power of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::exp2`](../../std/primitive.f32.html#method.exp2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn exp2f32(x: f32) -> f32; +pub fn exp2f32(x: f32) -> f32 { + libm::likely_available::exp2f(x) +} /// Returns 2 raised to the power of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::exp2`](../../std/primitive.f64.html#method.exp2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn exp2f64(x: f64) -> f64; +pub fn exp2f64(x: f64) -> f64 { + libm::likely_available::exp2(x) +} /// Returns 2 raised to the power of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::exp2`](../../std/primitive.f128.html#method.exp2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn exp2f128(x: f128) -> f128; +pub fn exp2f128(x: f128) -> f128 { + libm::maybe_available::exp2f128(x) +} /// Returns the natural logarithm of an `f16`. /// /// The stabilized version of this intrinsic is /// [`f16::ln`](../../std/primitive.f16.html#method.ln) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn logf16(x: f16) -> f16; +pub fn logf16(x: f16) -> f16 { + libm::likely_available::logf(x as f32) as f16 +} /// Returns the natural logarithm of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::ln`](../../std/primitive.f32.html#method.ln) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn logf32(x: f32) -> f32; +pub fn logf32(x: f32) -> f32 { + libm::likely_available::logf(x) +} /// Returns the natural logarithm of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::ln`](../../std/primitive.f64.html#method.ln) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn logf64(x: f64) -> f64; +pub fn logf64(x: f64) -> f64 { + libm::likely_available::log(x) +} /// Returns the natural logarithm of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::ln`](../../std/primitive.f128.html#method.ln) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn logf128(x: f128) -> f128; +pub fn logf128(x: f128) -> f128 { + libm::maybe_available::logf128(x) +} /// Returns the base 10 logarithm of an `f16`. /// /// The stabilized version of this intrinsic is /// [`f16::log10`](../../std/primitive.f16.html#method.log10) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log10f16(x: f16) -> f16; +pub fn log10f16(x: f16) -> f16 { + libm::likely_available::log10f(x as f32) as f16 +} /// Returns the base 10 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::log10`](../../std/primitive.f32.html#method.log10) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log10f32(x: f32) -> f32; +pub fn log10f32(x: f32) -> f32 { + libm::likely_available::log10f(x) +} /// Returns the base 10 logarithm of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::log10`](../../std/primitive.f64.html#method.log10) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log10f64(x: f64) -> f64; +pub fn log10f64(x: f64) -> f64 { + libm::likely_available::log10(x) +} /// Returns the base 10 logarithm of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::log10`](../../std/primitive.f128.html#method.log10) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log10f128(x: f128) -> f128; +pub fn log10f128(x: f128) -> f128 { + libm::maybe_available::log10f128(x) +} /// Returns the base 2 logarithm of an `f16`. /// /// The stabilized version of this intrinsic is /// [`f16::log2`](../../std/primitive.f16.html#method.log2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log2f16(x: f16) -> f16; +pub fn log2f16(x: f16) -> f16 { + libm::likely_available::log2f(x as f32) as f16 +} /// Returns the base 2 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::log2`](../../std/primitive.f32.html#method.log2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log2f32(x: f32) -> f32; +pub fn log2f32(x: f32) -> f32 { + libm::likely_available::log2f(x) +} /// Returns the base 2 logarithm of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::log2`](../../std/primitive.f64.html#method.log2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log2f64(x: f64) -> f64; +pub fn log2f64(x: f64) -> f64 { + libm::likely_available::log2(x) +} /// Returns the base 2 logarithm of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::log2`](../../std/primitive.f128.html#method.log2) +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub fn log2f128(x: f128) -> f128; +pub fn log2f128(x: f128) -> f128 { + libm::maybe_available::log2f128(x) +} /// Returns `a * b + c` for `f16` values. /// diff --git a/library/core/src/num/imp/libm.rs b/library/core/src/num/imp/libm.rs index aeabb08723095..c67adc337c1cb 100644 --- a/library/core/src/num/imp/libm.rs +++ b/library/core/src/num/imp/libm.rs @@ -1,11 +1,157 @@ //! Bindings to math functions provided by the system `libm` or by the `libm` crate, exposed //! via `compiler-builtins`. +//! +//! The functions in the root of this module are "guaranteed" to be available; see the +//! `full_availability` module in compiler-builtins for details. // SAFETY: These symbols have standard interfaces in C and are defined by `libm`, or are // provided by `compiler-builtins` on unsupported platforms. +#[allow(dead_code)] // This list reflects what is available rather than what is consumed. unsafe extern "C" { - pub(crate) safe fn cbrt(n: f64) -> f64; + pub(crate) safe fn cbrt(x: f64) -> f64; pub(crate) safe fn cbrtf(n: f32) -> f32; + pub(crate) safe fn ceil(x: f64) -> f64; + pub(crate) safe fn ceilf(x: f32) -> f32; + pub(crate) safe fn ceilf128(x: f128) -> f128; + pub(crate) safe fn ceilf16(x: f16) -> f16; + pub(crate) safe fn copysign(x: f64, y: f64) -> f64; + pub(crate) safe fn copysignf(x: f32, y: f32) -> f32; + pub(crate) safe fn copysignf128(x: f128, y: f128) -> f128; + pub(crate) safe fn copysignf16(x: f16, y: f16) -> f16; + pub(crate) safe fn fabs(x: f64) -> f64; + pub(crate) safe fn fabsf(x: f32) -> f32; + pub(crate) safe fn fabsf128(x: f128) -> f128; + pub(crate) safe fn fabsf16(x: f16) -> f16; pub(crate) safe fn fdim(a: f64, b: f64) -> f64; pub(crate) safe fn fdimf(a: f32, b: f32) -> f32; + pub(crate) safe fn fdimf128(x: f128, y: f128) -> f128; + pub(crate) safe fn fdimf16(x: f16, y: f16) -> f16; + pub(crate) safe fn floor(x: f64) -> f64; + pub(crate) safe fn floorf(x: f32) -> f32; + pub(crate) safe fn floorf128(x: f128) -> f128; + pub(crate) safe fn floorf16(x: f16) -> f16; + pub(crate) safe fn fma(x: f64, y: f64, z: f64) -> f64; + pub(crate) safe fn fmaf(x: f32, y: f32, z: f32) -> f32; + pub(crate) safe fn fmaf128(x: f128, y: f128, z: f128) -> f128; + pub(crate) safe fn fmax(x: f64, y: f64) -> f64; + pub(crate) safe fn fmaxf(x: f32, y: f32) -> f32; + pub(crate) safe fn fmaxf128(x: f128, y: f128) -> f128; + pub(crate) safe fn fmaxf16(x: f16, y: f16) -> f16; + pub(crate) safe fn fmaximum(x: f64, y: f64) -> f64; + pub(crate) safe fn fmaximumf(x: f32, y: f32) -> f32; + pub(crate) safe fn fmaximumf128(x: f128, y: f128) -> f128; + pub(crate) safe fn fmaximumf16(x: f16, y: f16) -> f16; + pub(crate) safe fn fmin(x: f64, y: f64) -> f64; + pub(crate) safe fn fminf(x: f32, y: f32) -> f32; + pub(crate) safe fn fminf128(x: f128, y: f128) -> f128; + pub(crate) safe fn fminf16(x: f16, y: f16) -> f16; + pub(crate) safe fn fminimum(x: f64, y: f64) -> f64; + pub(crate) safe fn fminimumf(x: f32, y: f32) -> f32; + pub(crate) safe fn fminimumf128(x: f128, y: f128) -> f128; + pub(crate) safe fn fminimumf16(x: f16, y: f16) -> f16; + pub(crate) safe fn fmod(x: f64, y: f64) -> f64; + pub(crate) safe fn fmodf(x: f32, y: f32) -> f32; + pub(crate) safe fn fmodf128(x: f128, y: f128) -> f128; + pub(crate) safe fn fmodf16(x: f16, y: f16) -> f16; + pub(crate) safe fn rint(x: f64) -> f64; + pub(crate) safe fn rintf(x: f32) -> f32; + pub(crate) safe fn rintf128(x: f128) -> f128; + pub(crate) safe fn rintf16(x: f16) -> f16; + pub(crate) safe fn round(x: f64) -> f64; + pub(crate) safe fn roundeven(x: f64) -> f64; + pub(crate) safe fn roundevenf(x: f32) -> f32; + pub(crate) safe fn roundevenf128(x: f128) -> f128; + pub(crate) safe fn roundevenf16(x: f16) -> f16; + pub(crate) safe fn roundf(x: f32) -> f32; + pub(crate) safe fn roundf128(x: f128) -> f128; + pub(crate) safe fn roundf16(x: f16) -> f16; + pub(crate) safe fn sqrt(x: f64) -> f64; + pub(crate) safe fn sqrtf(x: f32) -> f32; + pub(crate) safe fn sqrtf128(x: f128) -> f128; + pub(crate) safe fn sqrtf16(x: f16) -> f16; + pub(crate) safe fn trunc(x: f64) -> f64; + pub(crate) safe fn truncf(x: f32) -> f32; + pub(crate) safe fn truncf128(x: f128) -> f128; + pub(crate) safe fn truncf16(x: f16) -> f16; +} + +/// These symbols will be available when `std` is available, and on many no-std platforms. However, +/// since this isn't a guarantee, we cannot rely on them for stable implementations. +pub(crate) mod likely_available { + #[allow(dead_code)] + unsafe extern "C" { + pub(crate) safe fn acos(x: f64) -> f64; + pub(crate) safe fn acosf(n: f32) -> f32; + pub(crate) safe fn asin(x: f64) -> f64; + pub(crate) safe fn asinf(n: f32) -> f32; + pub(crate) safe fn atan(x: f64) -> f64; + pub(crate) safe fn atan2(x: f64, y: f64) -> f64; + pub(crate) safe fn atan2f(a: f32, b: f32) -> f32; + pub(crate) safe fn atanf(n: f32) -> f32; + pub(crate) safe fn cos(x: f64) -> f64; + pub(crate) safe fn cosf(x: f32) -> f32; + pub(crate) safe fn cosh(x: f64) -> f64; + pub(crate) safe fn coshf(n: f32) -> f32; + pub(crate) safe fn erf(x: f64) -> f64; + pub(crate) safe fn erfc(x: f64) -> f64; + pub(crate) safe fn erfcf(x: f32) -> f32; + pub(crate) safe fn erff(x: f32) -> f32; + pub(crate) safe fn exp(x: f64) -> f64; + pub(crate) safe fn exp2(x: f64) -> f64; + pub(crate) safe fn exp2f(x: f32) -> f32; + pub(crate) safe fn expf(x: f32) -> f32; + pub(crate) safe fn expm1(x: f64) -> f64; + pub(crate) safe fn expm1f(n: f32) -> f32; + pub(crate) safe fn hypot(x: f64, y: f64) -> f64; + pub(crate) safe fn hypotf(x: f32, y: f32) -> f32; + pub(crate) safe fn ldexp(f: f64, n: i32) -> f64; + pub(crate) safe fn ldexpf(f: f32, n: i32) -> f32; + pub(crate) safe fn log(x: f64) -> f64; + pub(crate) safe fn log10(x: f64) -> f64; + pub(crate) safe fn log10f(x: f32) -> f32; + pub(crate) safe fn log1p(x: f64) -> f64; + pub(crate) safe fn log1pf(n: f32) -> f32; + pub(crate) safe fn log2(x: f64) -> f64; + pub(crate) safe fn log2f(x: f32) -> f32; + pub(crate) safe fn logf(x: f32) -> f32; + pub(crate) safe fn pow(x: f64, y: f64) -> f64; + pub(crate) safe fn powf(x: f32, y: f32) -> f32; + pub(crate) safe fn sin(x: f64) -> f64; + pub(crate) safe fn sinf(x: f32) -> f32; + pub(crate) safe fn sinh(x: f64) -> f64; + pub(crate) safe fn sinhf(n: f32) -> f32; + pub(crate) safe fn tan(x: f64) -> f64; + pub(crate) safe fn tanf(n: f32) -> f32; + pub(crate) safe fn tanh(x: f64) -> f64; + pub(crate) safe fn tanhf(n: f32) -> f32; + pub(crate) safe fn tgamma(x: f64) -> f64; + pub(crate) safe fn tgammaf(x: f32) -> f32; + } +} + +/// These symbols exist on some platforms but do not have a compiler-builtins fallback. +pub(crate) mod maybe_available { + #[allow(dead_code)] + unsafe extern "C" { + pub(crate) safe fn acosf128(x: f128) -> f128; + pub(crate) safe fn asinf128(x: f128) -> f128; + pub(crate) safe fn atanf128(x: f128) -> f128; + pub(crate) safe fn cbrtf128(x: f128) -> f128; + pub(crate) safe fn cosf128(x: f128) -> f128; + pub(crate) safe fn erff128(x: f128) -> f128; + pub(crate) safe fn expf128(x: f128) -> f128; + pub(crate) safe fn exp2f128(x: f128) -> f128; + pub(crate) safe fn expm1f128(x: f128) -> f128; + pub(crate) safe fn hypotf128(x: f128, y: f128) -> f128; + pub(crate) safe fn ldexpf128(f: f128, n: i32) -> f128; + pub(crate) safe fn log10f128(x: f128) -> f128; + pub(crate) safe fn log1pf128(x: f128) -> f128; + pub(crate) safe fn log2f128(x: f128) -> f128; + pub(crate) safe fn logf128(x: f128) -> f128; + pub(crate) safe fn powf128(x: f128, y: f128) -> f128; + pub(crate) safe fn sinf128(x: f128) -> f128; + pub(crate) safe fn tanf128(x: f128) -> f128; + pub(crate) safe fn tanhf128(x: f128) -> f128; + pub(crate) safe fn tgammaf128(x: f128) -> f128; + } } From a5ee71ca93f4175998e62b8a36d65bb219311bae Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 8 Jul 2026 21:19:49 +0200 Subject: [PATCH 4/6] use f32 intrinsic directly in fallback for f16 intrinsic --- library/core/src/intrinsics/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 84e8aa9569fec..a520fe5811e53 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -1088,7 +1088,7 @@ pub fn powif128(a: f128, x: i32) -> f128; #[rustc_intrinsic] #[rustc_nounwind] pub fn sinf16(x: f16) -> f16 { - libm::likely_available::sinf(x as f32) as f16 + sinf32(x as f32) as f16 } /// Returns the sine of an `f32`. /// @@ -1129,7 +1129,7 @@ pub fn sinf128(x: f128) -> f128 { #[rustc_intrinsic] #[rustc_nounwind] pub fn cosf16(x: f16) -> f16 { - libm::likely_available::cosf(x as f32) as f16 + cosf32(x as f32) as f16 } /// Returns the cosine of an `f32`. /// @@ -1170,7 +1170,7 @@ pub fn cosf128(x: f128) -> f128 { #[rustc_intrinsic] #[rustc_nounwind] pub fn powf16(a: f16, x: f16) -> f16 { - libm::likely_available::powf(a as f32, x as f32) as f16 + powf32(a as f32, x as f32) as f16 } /// Raises an `f32` to an `f32` power. /// @@ -1211,7 +1211,7 @@ pub fn powf128(a: f128, x: f128) -> f128 { #[rustc_intrinsic] #[rustc_nounwind] pub fn expf16(x: f16) -> f16 { - libm::likely_available::expf(x as f32) as f16 + expf32(x as f32) as f16 } /// Returns the exponential of an `f32`. /// @@ -1252,7 +1252,7 @@ pub fn expf128(x: f128) -> f128 { #[rustc_intrinsic] #[rustc_nounwind] pub fn exp2f16(x: f16) -> f16 { - libm::likely_available::exp2f(x as f32) as f16 + exp2f32(x as f32) as f16 } /// Returns 2 raised to the power of an `f32`. /// @@ -1293,7 +1293,7 @@ pub fn exp2f128(x: f128) -> f128 { #[rustc_intrinsic] #[rustc_nounwind] pub fn logf16(x: f16) -> f16 { - libm::likely_available::logf(x as f32) as f16 + logf32(x as f32) as f16 } /// Returns the natural logarithm of an `f32`. /// @@ -1334,7 +1334,7 @@ pub fn logf128(x: f128) -> f128 { #[rustc_intrinsic] #[rustc_nounwind] pub fn log10f16(x: f16) -> f16 { - libm::likely_available::log10f(x as f32) as f16 + log10f32(x as f32) as f16 } /// Returns the base 10 logarithm of an `f32`. /// @@ -1375,7 +1375,7 @@ pub fn log10f128(x: f128) -> f128 { #[rustc_intrinsic] #[rustc_nounwind] pub fn log2f16(x: f16) -> f16 { - libm::likely_available::log2f(x as f32) as f16 + log2f32(x as f32) as f16 } /// Returns the base 2 logarithm of an `f32`. /// From 58c2c076ca9cd8ffbdbe031ad829f96ddc32b048 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 8 Jul 2026 23:33:23 +0200 Subject: [PATCH 5/6] on msvc, make f32 fallback to f64 the 32-bit symbols are not available on msvc --- library/core/src/intrinsics/mod.rs | 40 ++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index a520fe5811e53..b812b9eb2afc1 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -1098,7 +1098,10 @@ pub fn sinf16(x: f16) -> f16 { #[rustc_intrinsic] #[rustc_nounwind] pub fn sinf32(x: f32) -> f32 { - libm::likely_available::sinf(x) + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => sinf64(x as f64) as f32, + _ => libm::likely_available::sinf(x), + } } /// Returns the sine of an `f64`. /// @@ -1139,7 +1142,10 @@ pub fn cosf16(x: f16) -> f16 { #[rustc_intrinsic] #[rustc_nounwind] pub fn cosf32(x: f32) -> f32 { - libm::likely_available::cosf(x) + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => cosf64(x as f64) as f32, + _ => libm::likely_available::cosf(x), + } } /// Returns the cosine of an `f64`. /// @@ -1180,7 +1186,10 @@ pub fn powf16(a: f16, x: f16) -> f16 { #[rustc_intrinsic] #[rustc_nounwind] pub fn powf32(a: f32, x: f32) -> f32 { - libm::likely_available::powf(a, x) + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => powf64(a as f64, x as f64) as f32, + _ => libm::likely_available::powf(a, x), + } } /// Raises an `f64` to an `f64` power. /// @@ -1221,7 +1230,10 @@ pub fn expf16(x: f16) -> f16 { #[rustc_intrinsic] #[rustc_nounwind] pub fn expf32(x: f32) -> f32 { - libm::likely_available::expf(x) + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => expf64(x as f64) as f32, + _ => libm::likely_available::expf(x), + } } /// Returns the exponential of an `f64`. /// @@ -1262,7 +1274,10 @@ pub fn exp2f16(x: f16) -> f16 { #[rustc_intrinsic] #[rustc_nounwind] pub fn exp2f32(x: f32) -> f32 { - libm::likely_available::exp2f(x) + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => exp2f64(x as f64) as f32, + _ => libm::likely_available::exp2f(x), + } } /// Returns 2 raised to the power of an `f64`. /// @@ -1303,7 +1318,10 @@ pub fn logf16(x: f16) -> f16 { #[rustc_intrinsic] #[rustc_nounwind] pub fn logf32(x: f32) -> f32 { - libm::likely_available::logf(x) + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => logf64(x as f64) as f32, + _ => libm::likely_available::logf(x), + } } /// Returns the natural logarithm of an `f64`. /// @@ -1344,7 +1362,10 @@ pub fn log10f16(x: f16) -> f16 { #[rustc_intrinsic] #[rustc_nounwind] pub fn log10f32(x: f32) -> f32 { - libm::likely_available::log10f(x) + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => log10f64(x as f64) as f32, + _ => libm::likely_available::log10f(x), + } } /// Returns the base 10 logarithm of an `f64`. /// @@ -1385,7 +1406,10 @@ pub fn log2f16(x: f16) -> f16 { #[rustc_intrinsic] #[rustc_nounwind] pub fn log2f32(x: f32) -> f32 { - libm::likely_available::log2f(x) + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => log2f64(x as f64) as f32, + _ => libm::likely_available::log2f(x), + } } /// Returns the base 2 logarithm of an `f64`. /// From e732dd16fe82ec7400ad835bccc1ebfcfc43c0a4 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 9 Jul 2026 11:36:51 +0200 Subject: [PATCH 6/6] configure out 32-bit transcendental functions on x86 msvc we know that the symbols are not available there --- library/core/src/num/imp/libm.rs | 57 +++++++++++++++++++------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/library/core/src/num/imp/libm.rs b/library/core/src/num/imp/libm.rs index c67adc337c1cb..388f1479b2461 100644 --- a/library/core/src/num/imp/libm.rs +++ b/library/core/src/num/imp/libm.rs @@ -78,54 +78,65 @@ unsafe extern "C" { /// These symbols will be available when `std` is available, and on many no-std platforms. However, /// since this isn't a guarantee, we cannot rely on them for stable implementations. pub(crate) mod likely_available { + cfg_select! { + all(target_env = "msvc", target_arch = "x86") => { + /* these symbols are not available on x86 msvc */ + } + _ => { + #[allow(dead_code)] + unsafe extern "C" { + pub(crate) safe fn acosf(n: f32) -> f32; + pub(crate) safe fn asinf(n: f32) -> f32; + pub(crate) safe fn atan2f(a: f32, b: f32) -> f32; + pub(crate) safe fn atanf(n: f32) -> f32; + pub(crate) safe fn cosf(x: f32) -> f32; + pub(crate) safe fn coshf(n: f32) -> f32; + pub(crate) safe fn erfcf(x: f32) -> f32; + pub(crate) safe fn erff(x: f32) -> f32; + pub(crate) safe fn exp2f(x: f32) -> f32; + pub(crate) safe fn expf(x: f32) -> f32; + pub(crate) safe fn expm1f(n: f32) -> f32; + pub(crate) safe fn hypotf(x: f32, y: f32) -> f32; + pub(crate) safe fn ldexpf(f: f32, n: i32) -> f32; + pub(crate) safe fn log10f(x: f32) -> f32; + pub(crate) safe fn log1pf(n: f32) -> f32; + pub(crate) safe fn log2f(x: f32) -> f32; + pub(crate) safe fn logf(x: f32) -> f32; + pub(crate) safe fn powf(x: f32, y: f32) -> f32; + pub(crate) safe fn sinf(x: f32) -> f32; + pub(crate) safe fn sinhf(n: f32) -> f32; + pub(crate) safe fn tanf(n: f32) -> f32; + pub(crate) safe fn tanhf(n: f32) -> f32; + pub(crate) safe fn tgammaf(x: f32) -> f32; + } + } + } + #[allow(dead_code)] unsafe extern "C" { pub(crate) safe fn acos(x: f64) -> f64; - pub(crate) safe fn acosf(n: f32) -> f32; pub(crate) safe fn asin(x: f64) -> f64; - pub(crate) safe fn asinf(n: f32) -> f32; pub(crate) safe fn atan(x: f64) -> f64; pub(crate) safe fn atan2(x: f64, y: f64) -> f64; - pub(crate) safe fn atan2f(a: f32, b: f32) -> f32; - pub(crate) safe fn atanf(n: f32) -> f32; pub(crate) safe fn cos(x: f64) -> f64; - pub(crate) safe fn cosf(x: f32) -> f32; pub(crate) safe fn cosh(x: f64) -> f64; - pub(crate) safe fn coshf(n: f32) -> f32; pub(crate) safe fn erf(x: f64) -> f64; pub(crate) safe fn erfc(x: f64) -> f64; - pub(crate) safe fn erfcf(x: f32) -> f32; - pub(crate) safe fn erff(x: f32) -> f32; pub(crate) safe fn exp(x: f64) -> f64; pub(crate) safe fn exp2(x: f64) -> f64; - pub(crate) safe fn exp2f(x: f32) -> f32; - pub(crate) safe fn expf(x: f32) -> f32; pub(crate) safe fn expm1(x: f64) -> f64; - pub(crate) safe fn expm1f(n: f32) -> f32; pub(crate) safe fn hypot(x: f64, y: f64) -> f64; - pub(crate) safe fn hypotf(x: f32, y: f32) -> f32; pub(crate) safe fn ldexp(f: f64, n: i32) -> f64; - pub(crate) safe fn ldexpf(f: f32, n: i32) -> f32; pub(crate) safe fn log(x: f64) -> f64; pub(crate) safe fn log10(x: f64) -> f64; - pub(crate) safe fn log10f(x: f32) -> f32; pub(crate) safe fn log1p(x: f64) -> f64; - pub(crate) safe fn log1pf(n: f32) -> f32; pub(crate) safe fn log2(x: f64) -> f64; - pub(crate) safe fn log2f(x: f32) -> f32; - pub(crate) safe fn logf(x: f32) -> f32; pub(crate) safe fn pow(x: f64, y: f64) -> f64; - pub(crate) safe fn powf(x: f32, y: f32) -> f32; pub(crate) safe fn sin(x: f64) -> f64; - pub(crate) safe fn sinf(x: f32) -> f32; pub(crate) safe fn sinh(x: f64) -> f64; - pub(crate) safe fn sinhf(n: f32) -> f32; pub(crate) safe fn tan(x: f64) -> f64; - pub(crate) safe fn tanf(n: f32) -> f32; pub(crate) safe fn tanh(x: f64) -> f64; - pub(crate) safe fn tanhf(n: f32) -> f32; pub(crate) safe fn tgamma(x: f64) -> f64; - pub(crate) safe fn tgammaf(x: f32) -> f32; } }