diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 584e4a895be9b..97f88e5fbfec3 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -409,6 +409,9 @@ fn codegen_float_intrinsic_call<'tcx>( sym::sinf64 => ("sin", 1, fx.tcx.types.f64, types::F64), sym::sinf128 => ("sinf128", 1, fx.tcx.types.f128, types::F128), + sym::tanhf32 => ("tanhf", 1, fx.tcx.types.f32, types::F32), + sym::tanhf64 => ("tanh", 1, fx.tcx.types.f64, types::F64), + sym::cosf16 => return false, // has a fallback via f32 sym::cosf32 => ("cosf", 1, fx.tcx.types.f32, types::F32), sym::cosf64 => ("cos", 1, fx.tcx.types.f64, types::F64), diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index b3b1bea68e0b4..7b67b1d03754b 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -64,6 +64,8 @@ fn get_simple_intrinsic<'gcc, 'tcx>( sym::powif64 => "__builtin_powi", sym::sinf32 => "sinf", sym::sinf64 => "sin", + sym::tanhf32 => "tanhf", + sym::tanhf64 => "tanh", sym::cosf32 => "cosf", sym::cosf64 => "cos", sym::powf32 => "powf", diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 36709e4ff954f..8a4b7bb30da8c 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -71,6 +71,9 @@ fn call_simple_intrinsic<'ll, 'tcx>( sym::sinf64 => ("llvm.sin", &[bx.type_f64()]), sym::sinf128 => ("llvm.sin", &[bx.type_f128()]), + sym::tanhf32 => ("llvm.tanh", &[bx.type_f32()]), + sym::tanhf64 => ("llvm.tanh", &[bx.type_f64()]), + sym::cosf16 => ("llvm.cos", &[bx.type_f16()]), sym::cosf32 => ("llvm.cos", &[bx.type_f32()]), sym::cosf64 => ("llvm.cos", &[bx.type_f64()]), diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index a8a8edf98bd86..e308e76f5a33d 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -337,6 +337,7 @@ impl CodegenBackend for LlvmCodegenBackend { sym::integer_min, // Fallback via libm, but the LLVM intrinsic is used instead. + sym::tanhf32, sym::tanhf64, sym::sinf16, sym::sinf32, sym::sinf64, sym::cosf16, sym::cosf32, sym::cosf64, sym::powf16, sym::powf32, sym::powf64, diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 3354b1e4bc3e4..987d8fe93ccd3 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -214,6 +214,8 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::sqrtf64 | sym::sqrtf128 | sym::sub_with_overflow + | sym::tanhf32 + | sym::tanhf64 | sym::three_way_compare | sym::truncf16 | sym::truncf32 @@ -446,6 +448,9 @@ pub(crate) fn check_intrinsic_type( sym::sinf64 => (0, 0, vec![tcx.types.f64], tcx.types.f64), sym::sinf128 => (0, 0, vec![tcx.types.f128], tcx.types.f128), + sym::tanhf32 => (0, 0, vec![tcx.types.f32], tcx.types.f32), + sym::tanhf64 => (0, 0, vec![tcx.types.f64], tcx.types.f64), + sym::cosf16 => (0, 0, vec![tcx.types.f16], tcx.types.f16), sym::cosf32 => (0, 0, vec![tcx.types.f32], tcx.types.f32), sym::cosf64 => (0, 0, vec![tcx.types.f64], tcx.types.f64), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 057ea6bd3d0a2..1d68da8a23cd7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2099,6 +2099,8 @@ symbols! { sync, synthetic, t32, + tanhf32, + tanhf64, target, target_abi, target_arch, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 4e9187f7933b2..69c4109ea50c2 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -1143,6 +1143,19 @@ pub fn sinf32(x: f32) -> f32 { pub fn sinf64(x: f64) -> f64 { libm::likely_available::sin(x) } +#[inline] +#[rustc_intrinsic] +#[rustc_nounwind] +pub fn tanhf64(x: f64) -> f64 { + libm::likely_available::tanh(x) +} +#[inline] +#[rustc_intrinsic] +#[rustc_nounwind] +pub fn tanhf32(x: f32) -> f32 { + libm::likely_available::tanhf(x) +} + /// Returns the sine of an `f128`. /// /// The stabilized version of this intrinsic is diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index 385533748cb5d..e8fb94d988910 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -1047,8 +1047,6 @@ impl f32 { /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `tanhf` from libc on Unix - /// and Windows. Note that this might change in the future. /// /// # Examples /// @@ -1068,7 +1066,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn tanh(self) -> f32 { - cmath::tanhf(self) + intrinsics::tanhf32(self) } /// Inverse hyperbolic sine function. diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index 66482a8b654a5..6132efea30bdf 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -1047,8 +1047,6 @@ impl f64 { /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `tanh` from libc on Unix - /// and Windows. Note that this might change in the future. /// /// # Examples /// @@ -1068,7 +1066,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn tanh(self) -> f64 { - cmath::tanh(self) + intrinsics::tanhf64(self) } /// Inverse hyperbolic sine function. diff --git a/library/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs index 2104087141d1a..5254990848d9e 100644 --- a/library/std/src/sys/cmath.rs +++ b/library/std/src/sys/cmath.rs @@ -20,7 +20,6 @@ unsafe extern "C" { pub safe fn log1pf(n: f32) -> f32; pub safe fn sinh(n: f64) -> f64; pub safe fn tan(n: f64) -> f64; - pub safe fn tanh(n: f64) -> f64; pub safe fn tgamma(n: f64) -> f64; pub safe fn tgammaf(n: f32) -> f32; pub safe fn lgamma_r(n: f64, s: &mut i32) -> f64; diff --git a/tests/codegen-llvm/intrinsics/tanh.rs b/tests/codegen-llvm/intrinsics/tanh.rs new file mode 100644 index 0000000000000..f9552d8567fee --- /dev/null +++ b/tests/codegen-llvm/intrinsics/tanh.rs @@ -0,0 +1,48 @@ +//@ revisions: DEV OPT +//@ [DEV] compile-flags: -C no-prepopulate-passes +//@ [OPT] compile-flags: -O + +// We previously lowered tanh to libm calls, which would set errno and thus prevent a couple of +// optimizations, since LLVM would mark the call as `memory(errnomem: write)`. +// LLVM since gained additional math intrinsics, including tanh, to which we're now lowering +// instead. They are marked as `readnone`, and thus allow the optimizations shown below. + +#![crate_type = "lib"] + +// CHECK-LABEL: @tanhf64 +#[no_mangle] +pub fn tanhf64(x: f64) -> f64 { + // CHECK: call double @llvm.tanh.f64 + // CHECK-NOT: call double @tanh( + x.tanh() +} + +// CHECK-LABEL: @tanhf32 +#[no_mangle] +pub fn tanhf32(x: f32) -> f32 { + // CHECK: call float @llvm.tanh.f32 + // CHECK-NOT: call float @tanhf( + x.tanh() +} + +// Since we're now marked as readnone, the call to tanh can be optimized away +// CHECK-LABEL: @dce_tanh +#[no_mangle] +pub fn dce_tanh(x: f64) -> f64 { + // DEV: call double @llvm.tanh.f64 + // OPT-NOT: llvm.tanh + let _ = x.tanh(); + 1.0 +} + +// Since we're now marked as speculate, we can fold the branch into a select +// CHECK-LABEL: @speculate +#[no_mangle] +pub fn speculate(x: f64, cond: bool) -> f64 { + // DEV: br i1 %cond + // OPT-NOT: br i1 + // OPT: %0 = tail call double @llvm.tanh.f64(double %x) + // OPT-NEXT: %_0.sroa.0.0 = select i1 %cond, double %0, double 0.000000e+00 + // OPT-NEXT: ret double %_0.sroa.0.0 + if cond { x.tanh() } else { 0.0 } +}