Skip to content

RuntimeLibcalls: Stop opting out of exp10 #148604

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

arsenm
Copy link
Contributor

@arsenm arsenm commented Jul 14, 2025

This changes the behavior on old darwin triples for x86_fp80;
it now turns into an error instead of emitting exp10l. The comments
in TargetLibraryInfo suggest it never existed.

@arsenm arsenm marked this pull request as ready for review July 14, 2025 10:16
@llvmbot
Copy link
Member

llvmbot commented Jul 14, 2025

@llvm/pr-subscribers-backend-x86

Author: Matt Arsenault (arsenm)

Changes

This changes the behavior on old darwin triples for x86_fp80;
it now turns into an error instead of emitting exp10l. The comments
in TargetLibraryInfo suggest it never existed.


Full diff: https://github.com/llvm/llvm-project/pull/148604.diff

4 Files Affected:

  • (modified) llvm/include/llvm/IR/RuntimeLibcalls.td (+27-8)
  • (modified) llvm/lib/IR/RuntimeLibcalls.cpp (+6-8)
  • (modified) llvm/test/CodeGen/X86/exp10-libcall-names.ll (+1-44)
  • (added) llvm/test/CodeGen/X86/exp10l-libcall-names.ll (+46)
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.td b/llvm/include/llvm/IR/RuntimeLibcalls.td
index 7aa30f5491d73..6579ba43df918 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.td
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.td
@@ -29,6 +29,9 @@ def isNotOSWindowsOrIsCygwinMinGW
 def isGNUEnvironment : RuntimeLibcallPredicate<"TT.isGNUEnvironment()">;
 def darwinHasSinCosStret : RuntimeLibcallPredicate<"darwinHasSinCosStret(TT)">;
 def darwinHasExp10 : RuntimeLibcallPredicate<"darwinHasExp10(TT)">;
+
+def hasExp10 : RuntimeLibcallPredicate<[{!TT.isOSDarwin()}]>;
+
 def hasSinCos : RuntimeLibcallPredicate<"hasSinCos(TT)">;
 
 // FIXME: Way to combine predicates
@@ -776,12 +779,6 @@ def __exp2l_finite_f80 : RuntimeLibcallImpl<EXP2_FINITE_F80, "__exp2l_finite">;
 def __exp2l_finite_f128 : RuntimeLibcallImpl<EXP2_FINITE_F128, "__exp2l_finite">;
 def __exp2l_finite_ppcf128 : RuntimeLibcallImpl<EXP2_FINITE_PPCF128, "__exp2l_finite">;
 
-def exp10f : RuntimeLibcallImpl<EXP10_F32>;
-def exp10 : RuntimeLibcallImpl<EXP10_F64>;
-def exp10l_f80 : RuntimeLibcallImpl<EXP10_F80, "exp10l">;
-def exp10l_f128 : RuntimeLibcallImpl<EXP10_F128, "exp10l">;
-def exp10l_ppcf128 : RuntimeLibcallImpl<EXP10_PPCF128, "exp10l">;
-
 def sinf : RuntimeLibcallImpl<SIN_F32>;
 def sin : RuntimeLibcallImpl<SIN_F64>;
 defm sin : LibmLongDoubleLibCall;
@@ -942,6 +939,12 @@ def calloc : RuntimeLibcallImpl<CALLOC>;
 
 } // End let IsDefault = true
 
+def exp10f : RuntimeLibcallImpl<EXP10_F32>;
+def exp10 : RuntimeLibcallImpl<EXP10_F64>;
+def exp10l_f80 : RuntimeLibcallImpl<EXP10_F80, "exp10l">;
+def exp10l_f128 : RuntimeLibcallImpl<EXP10_F128, "exp10l">;
+def exp10l_ppcf128 : RuntimeLibcallImpl<EXP10_PPCF128, "exp10l">;
+
 //--------------------------------------------------------------------
 // compiler-rt/libgcc but 64-bit only, not available by default
 //--------------------------------------------------------------------
@@ -1101,6 +1104,12 @@ defvar LibmHasSinCosF80 = LibcallImpls<(add sincos_f80), hasSinCos>;
 defvar LibmHasSinCosF128 = LibcallImpls<(add sincos_f128), hasSinCos>;
 defvar LibmHasSinCosPPCF128 = LibcallImpls<(add sincos_ppcf128), hasSinCos>;
 
+defvar LibmHasExp10F32 = LibcallImpls<(add exp10f), hasExp10>;
+defvar LibmHasExp10F64 = LibcallImpls<(add exp10), hasExp10>;
+defvar LibmHasExp10F80 = LibcallImpls<(add exp10l_f80), hasExp10>;
+defvar LibmHasExp10F128 = LibcallImpls<(add exp10l_f128), hasExp10>;
+defvar LibmHasExp10PPCF128 = LibcallImpls<(add exp10l_ppcf128), hasExp10>;
+
 defvar WindowsMathRemovals = [
   ldexpf, ldexp_f80, ldexp_f128, ldexp_ppcf128,
   frexpf, frexp_f80, frexp_f128, frexp_ppcf128
@@ -1199,7 +1208,8 @@ def AArch64SystemLibrary : SystemRuntimeLibrary<
        LibcallImpls<(add Int128RTLibcalls), isAArch64_ILP64>,
        LibcallImpls<(add bzero), isOSDarwin>,
        DarwinExp10, DarwinSinCosStret,
-       LibmHasSinCosF32, LibmHasSinCosF64, LibmHasSinCosF128)
+       LibmHasSinCosF32, LibmHasSinCosF64, LibmHasSinCosF128,
+       LibmHasExp10F32, LibmHasExp10F64, LibmHasExp10F128)
 >;
 
 // Prepend a # to every name
@@ -1470,6 +1480,7 @@ def ARMSystemLibrary
            AEABIDivRemCalls,
            DarwinSinCosStret, DarwinExp10,
            LibmHasSinCosF32, LibmHasSinCosF64, LibmHasSinCosF128,
+           LibmHasExp10F32, LibmHasExp10F64,  LibmHasExp10F128,
 
            // Use divmod compiler-rt calls for iOS 5.0 and later.
            LibcallImpls<(add __divmodsi4, __udivmodsi4),
@@ -1962,6 +1973,7 @@ def PPCSystemLibrary
                 DefaultRuntimeLibcallImpls_f128),
            __extendkftf2, __trunctfkf2,
            DefaultRuntimeLibcallImpls_ppcf128,
+           exp10f, exp10, exp10l_ppcf128,
            LibmF128Libcalls, AIX32Calls, AIX64Calls,
            LibmHasSinCosF32, LibmHasSinCosF64, LibmHasSinCosF128,
            LibmHasSinCosPPCF128,
@@ -1977,7 +1989,9 @@ def isRISCV64 : RuntimeLibcallPredicate<"TT.isRISCV64()">;
 
 def RISCVSystemLibrary
     : SystemRuntimeLibrary<isRISCV,
-      (add DefaultRuntimeLibcallImpls, __riscv_flush_icache,
+      (add DefaultRuntimeLibcallImpls,
+           exp10f, exp10, exp10l_f128,
+           __riscv_flush_icache,
            LibcallImpls<(add Int128RTLibcalls), isRISCV64>)>;
 
 //===----------------------------------------------------------------------===//
@@ -2086,6 +2100,10 @@ defvar X86CommonLibcalls =
        LibmHasFrexpF32, LibmHasLdexpF32,
        LibmHasFrexpF80, LibmHasLdexpF80,
        DefaultRuntimeLibcallImpls_f80,
+
+       LibmHasExp10F32, LibmHasExp10F64, LibmHasExp10F80,
+       LibmHasExp10F128,
+
        // MSVCRT doesn't have powi
        // FIXME: It's almost certainly correct that MSVCRT has
        // __powitf2_f128, but at least one test is relying on it.
@@ -2230,4 +2248,5 @@ def WasmSystemLibrary
     : SystemRuntimeLibrary<isWasm,
       (add DefaultRuntimeLibcallImpls, Int128RTLibcalls,
            CompilerRTOnlyInt64Libcalls, CompilerRTOnlyInt128Libcalls,
+           exp10f, exp10,
            emscripten_return_address)>;
diff --git a/llvm/lib/IR/RuntimeLibcalls.cpp b/llvm/lib/IR/RuntimeLibcalls.cpp
index 60da0047291a9..038a511ed2e68 100644
--- a/llvm/lib/IR/RuntimeLibcalls.cpp
+++ b/llvm/lib/IR/RuntimeLibcalls.cpp
@@ -99,21 +99,15 @@ void RuntimeLibcallsInfo::initLibcalls(const Triple &TT,
       setLibcallImplCallingConv(RTLIB::__sincos_stret,
                                 CallingConv::ARM_AAPCS_VFP);
     }
-
-    if (!darwinHasExp10(TT)) {
-      // FIXME: Remove exp10 from default set
-      setLibcallImpl(RTLIB::EXP10_F32, RTLIB::Unsupported);
-      setLibcallImpl(RTLIB::EXP10_F64, RTLIB::Unsupported);
-    }
   }
 
   if (TT.isOSOpenBSD()) {
     setLibcallImpl(RTLIB::STACKPROTECTOR_CHECK_FAIL, RTLIB::Unsupported);
   }
 
-  // Skip default manual processing for targets that have been fully ported to
+  // Skip default manual processing for targets that have been mostly ported to
   // tablegen for now. Eventually the rest of this should be deleted.
-  if (TT.isX86() || TT.isAArch64() || TT.isWasm())
+  if (TT.isX86() || TT.isAArch64() || TT.isWasm() || TT.isPPC())
     return;
 
   if (TT.isARM() || TT.isThumb()) {
@@ -127,6 +121,10 @@ void RuntimeLibcallsInfo::initLibcalls(const Triple &TT,
     setLibcallImpl(RTLIB::SINCOS_F128, RTLIB::sincos_f128);
   }
 
+  setLibcallImpl(RTLIB::EXP10_F32, RTLIB::exp10f);
+  setLibcallImpl(RTLIB::EXP10_F64, RTLIB::exp10);
+  setLibcallImpl(RTLIB::EXP10_F128, RTLIB::exp10l_f128);
+
   // These libcalls are only available in compiler-rt, not libgcc.
   if (TT.isArch64Bit()) {
     setLibcallImpl(RTLIB::SHL_I128, RTLIB::__ashlti3);
diff --git a/llvm/test/CodeGen/X86/exp10-libcall-names.ll b/llvm/test/CodeGen/X86/exp10-libcall-names.ll
index 96e3aae408e94..2688474b2ce5c 100644
--- a/llvm/test/CodeGen/X86/exp10-libcall-names.ll
+++ b/llvm/test/CodeGen/X86/exp10-libcall-names.ll
@@ -13,10 +13,7 @@
 ; RUN: llc < %s -mtriple=i686-linux-gnu -global-isel -global-isel-abort=1 | FileCheck %s --check-prefix=GISEL-X86
 ; RUN: llc < %s -mtriple=x86_64-linux-gnu -global-isel -global-isel-abort=1 | FileCheck %s --check-prefix=GISEL-X64
 
-; RUN: not llc -mtriple=x86_64-apple-macos10.8 -filetype=null %s 2>&1 | FileCheck -check-prefix=ERR %s
-; Check exp10/exp10f is emitted as __exp10/__exp10f on assorted systems.
-
-; ERR: no libcall available for fexp10
+; Check exp10/exp10f is emitted as __exp10/__exp10f on assorted darwin systems.
 
 define float @test_exp10_f32(float %x) nounwind {
 ; LINUX-LABEL: test_exp10_f32:
@@ -78,43 +75,3 @@ define double @test_exp10_f64(double %x) nounwind {
   %ret = call double @llvm.exp10.f64(double %x)
   ret double %ret
 }
-
-define x86_fp80 @test_exp10_f80(x86_fp80 %x) nounwind {
-; LINUX-LABEL: test_exp10_f80:
-; LINUX:       # %bb.0:
-; LINUX-NEXT:    subq $24, %rsp
-; LINUX-NEXT:    fldt {{[0-9]+}}(%rsp)
-; LINUX-NEXT:    fstpt (%rsp)
-; LINUX-NEXT:    callq exp10l@PLT
-; LINUX-NEXT:    addq $24, %rsp
-; LINUX-NEXT:    retq
-;
-; APPLE-LABEL: test_exp10_f80:
-; APPLE:       ## %bb.0:
-; APPLE-NEXT:    subq $24, %rsp
-; APPLE-NEXT:    fldt {{[0-9]+}}(%rsp)
-; APPLE-NEXT:    fstpt (%rsp)
-; APPLE-NEXT:    callq _exp10l
-; APPLE-NEXT:    addq $24, %rsp
-; APPLE-NEXT:    retq
-;
-; GISEL-X86-LABEL: test_exp10_f80:
-; GISEL-X86:       # %bb.0:
-; GISEL-X86-NEXT:    subl $12, %esp
-; GISEL-X86-NEXT:    fldt {{[0-9]+}}(%esp)
-; GISEL-X86-NEXT:    fstpt (%esp)
-; GISEL-X86-NEXT:    calll exp10l
-; GISEL-X86-NEXT:    addl $12, %esp
-; GISEL-X86-NEXT:    retl
-;
-; GISEL-X64-LABEL: test_exp10_f80:
-; GISEL-X64:       # %bb.0:
-; GISEL-X64-NEXT:    subq $24, %rsp
-; GISEL-X64-NEXT:    fldt {{[0-9]+}}(%rsp)
-; GISEL-X64-NEXT:    fstpt (%rsp)
-; GISEL-X64-NEXT:    callq exp10l
-; GISEL-X64-NEXT:    addq $24, %rsp
-; GISEL-X64-NEXT:    retq
-  %ret = call x86_fp80 @llvm.exp10.f80(x86_fp80 %x)
-  ret x86_fp80 %ret
-}
diff --git a/llvm/test/CodeGen/X86/exp10l-libcall-names.ll b/llvm/test/CodeGen/X86/exp10l-libcall-names.ll
new file mode 100644
index 0000000000000..2e7f9e34f662a
--- /dev/null
+++ b/llvm/test/CodeGen/X86/exp10l-libcall-names.ll
@@ -0,0 +1,46 @@
+; RUN: llc -mtriple=x86_64-linux-gnu < %s | FileCheck -check-prefix=LINUX %s
+; RUN: not llc -mtriple=x86_64-apple-macos10.9 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-ios9.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-tvos9.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-watchos9.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-xros9.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-ios8.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-tvos8.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-xros8.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-driverkit < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-driverkit24.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: llc < %s -mtriple=i686-linux-gnu -global-isel -global-isel-abort=1 | FileCheck %s --check-prefix=GISEL-X86
+; RUN: llc < %s -mtriple=x86_64-linux-gnu -global-isel -global-isel-abort=1 | FileCheck %s --check-prefix=GISEL-X64
+
+; ERR: no libcall available for fexp10
+
+define x86_fp80 @test_exp10_f80(x86_fp80 %x) nounwind {
+; LINUX-LABEL: test_exp10_f80:
+; LINUX:       # %bb.0:
+; LINUX-NEXT:    subq $24, %rsp
+; LINUX-NEXT:    fldt {{[0-9]+}}(%rsp)
+; LINUX-NEXT:    fstpt (%rsp)
+; LINUX-NEXT:    callq exp10l@PLT
+; LINUX-NEXT:    addq $24, %rsp
+; LINUX-NEXT:    retq
+;
+; GISEL-X86-LABEL: test_exp10_f80:
+; GISEL-X86:       # %bb.0:
+; GISEL-X86-NEXT:    subl $12, %esp
+; GISEL-X86-NEXT:    fldt {{[0-9]+}}(%esp)
+; GISEL-X86-NEXT:    fstpt (%esp)
+; GISEL-X86-NEXT:    calll exp10l
+; GISEL-X86-NEXT:    addl $12, %esp
+; GISEL-X86-NEXT:    retl
+;
+; GISEL-X64-LABEL: test_exp10_f80:
+; GISEL-X64:       # %bb.0:
+; GISEL-X64-NEXT:    subq $24, %rsp
+; GISEL-X64-NEXT:    fldt {{[0-9]+}}(%rsp)
+; GISEL-X64-NEXT:    fstpt (%rsp)
+; GISEL-X64-NEXT:    callq exp10l
+; GISEL-X64-NEXT:    addq $24, %rsp
+; GISEL-X64-NEXT:    retq
+  %ret = call x86_fp80 @llvm.exp10.f80(x86_fp80 %x)
+  ret x86_fp80 %ret
+}

@llvmbot
Copy link
Member

llvmbot commented Jul 14, 2025

@llvm/pr-subscribers-llvm-ir

Author: Matt Arsenault (arsenm)

Changes

This changes the behavior on old darwin triples for x86_fp80;
it now turns into an error instead of emitting exp10l. The comments
in TargetLibraryInfo suggest it never existed.


Full diff: https://github.com/llvm/llvm-project/pull/148604.diff

4 Files Affected:

  • (modified) llvm/include/llvm/IR/RuntimeLibcalls.td (+27-8)
  • (modified) llvm/lib/IR/RuntimeLibcalls.cpp (+6-8)
  • (modified) llvm/test/CodeGen/X86/exp10-libcall-names.ll (+1-44)
  • (added) llvm/test/CodeGen/X86/exp10l-libcall-names.ll (+46)
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.td b/llvm/include/llvm/IR/RuntimeLibcalls.td
index 7aa30f5491d73..6579ba43df918 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.td
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.td
@@ -29,6 +29,9 @@ def isNotOSWindowsOrIsCygwinMinGW
 def isGNUEnvironment : RuntimeLibcallPredicate<"TT.isGNUEnvironment()">;
 def darwinHasSinCosStret : RuntimeLibcallPredicate<"darwinHasSinCosStret(TT)">;
 def darwinHasExp10 : RuntimeLibcallPredicate<"darwinHasExp10(TT)">;
+
+def hasExp10 : RuntimeLibcallPredicate<[{!TT.isOSDarwin()}]>;
+
 def hasSinCos : RuntimeLibcallPredicate<"hasSinCos(TT)">;
 
 // FIXME: Way to combine predicates
@@ -776,12 +779,6 @@ def __exp2l_finite_f80 : RuntimeLibcallImpl<EXP2_FINITE_F80, "__exp2l_finite">;
 def __exp2l_finite_f128 : RuntimeLibcallImpl<EXP2_FINITE_F128, "__exp2l_finite">;
 def __exp2l_finite_ppcf128 : RuntimeLibcallImpl<EXP2_FINITE_PPCF128, "__exp2l_finite">;
 
-def exp10f : RuntimeLibcallImpl<EXP10_F32>;
-def exp10 : RuntimeLibcallImpl<EXP10_F64>;
-def exp10l_f80 : RuntimeLibcallImpl<EXP10_F80, "exp10l">;
-def exp10l_f128 : RuntimeLibcallImpl<EXP10_F128, "exp10l">;
-def exp10l_ppcf128 : RuntimeLibcallImpl<EXP10_PPCF128, "exp10l">;
-
 def sinf : RuntimeLibcallImpl<SIN_F32>;
 def sin : RuntimeLibcallImpl<SIN_F64>;
 defm sin : LibmLongDoubleLibCall;
@@ -942,6 +939,12 @@ def calloc : RuntimeLibcallImpl<CALLOC>;
 
 } // End let IsDefault = true
 
+def exp10f : RuntimeLibcallImpl<EXP10_F32>;
+def exp10 : RuntimeLibcallImpl<EXP10_F64>;
+def exp10l_f80 : RuntimeLibcallImpl<EXP10_F80, "exp10l">;
+def exp10l_f128 : RuntimeLibcallImpl<EXP10_F128, "exp10l">;
+def exp10l_ppcf128 : RuntimeLibcallImpl<EXP10_PPCF128, "exp10l">;
+
 //--------------------------------------------------------------------
 // compiler-rt/libgcc but 64-bit only, not available by default
 //--------------------------------------------------------------------
@@ -1101,6 +1104,12 @@ defvar LibmHasSinCosF80 = LibcallImpls<(add sincos_f80), hasSinCos>;
 defvar LibmHasSinCosF128 = LibcallImpls<(add sincos_f128), hasSinCos>;
 defvar LibmHasSinCosPPCF128 = LibcallImpls<(add sincos_ppcf128), hasSinCos>;
 
+defvar LibmHasExp10F32 = LibcallImpls<(add exp10f), hasExp10>;
+defvar LibmHasExp10F64 = LibcallImpls<(add exp10), hasExp10>;
+defvar LibmHasExp10F80 = LibcallImpls<(add exp10l_f80), hasExp10>;
+defvar LibmHasExp10F128 = LibcallImpls<(add exp10l_f128), hasExp10>;
+defvar LibmHasExp10PPCF128 = LibcallImpls<(add exp10l_ppcf128), hasExp10>;
+
 defvar WindowsMathRemovals = [
   ldexpf, ldexp_f80, ldexp_f128, ldexp_ppcf128,
   frexpf, frexp_f80, frexp_f128, frexp_ppcf128
@@ -1199,7 +1208,8 @@ def AArch64SystemLibrary : SystemRuntimeLibrary<
        LibcallImpls<(add Int128RTLibcalls), isAArch64_ILP64>,
        LibcallImpls<(add bzero), isOSDarwin>,
        DarwinExp10, DarwinSinCosStret,
-       LibmHasSinCosF32, LibmHasSinCosF64, LibmHasSinCosF128)
+       LibmHasSinCosF32, LibmHasSinCosF64, LibmHasSinCosF128,
+       LibmHasExp10F32, LibmHasExp10F64, LibmHasExp10F128)
 >;
 
 // Prepend a # to every name
@@ -1470,6 +1480,7 @@ def ARMSystemLibrary
            AEABIDivRemCalls,
            DarwinSinCosStret, DarwinExp10,
            LibmHasSinCosF32, LibmHasSinCosF64, LibmHasSinCosF128,
+           LibmHasExp10F32, LibmHasExp10F64,  LibmHasExp10F128,
 
            // Use divmod compiler-rt calls for iOS 5.0 and later.
            LibcallImpls<(add __divmodsi4, __udivmodsi4),
@@ -1962,6 +1973,7 @@ def PPCSystemLibrary
                 DefaultRuntimeLibcallImpls_f128),
            __extendkftf2, __trunctfkf2,
            DefaultRuntimeLibcallImpls_ppcf128,
+           exp10f, exp10, exp10l_ppcf128,
            LibmF128Libcalls, AIX32Calls, AIX64Calls,
            LibmHasSinCosF32, LibmHasSinCosF64, LibmHasSinCosF128,
            LibmHasSinCosPPCF128,
@@ -1977,7 +1989,9 @@ def isRISCV64 : RuntimeLibcallPredicate<"TT.isRISCV64()">;
 
 def RISCVSystemLibrary
     : SystemRuntimeLibrary<isRISCV,
-      (add DefaultRuntimeLibcallImpls, __riscv_flush_icache,
+      (add DefaultRuntimeLibcallImpls,
+           exp10f, exp10, exp10l_f128,
+           __riscv_flush_icache,
            LibcallImpls<(add Int128RTLibcalls), isRISCV64>)>;
 
 //===----------------------------------------------------------------------===//
@@ -2086,6 +2100,10 @@ defvar X86CommonLibcalls =
        LibmHasFrexpF32, LibmHasLdexpF32,
        LibmHasFrexpF80, LibmHasLdexpF80,
        DefaultRuntimeLibcallImpls_f80,
+
+       LibmHasExp10F32, LibmHasExp10F64, LibmHasExp10F80,
+       LibmHasExp10F128,
+
        // MSVCRT doesn't have powi
        // FIXME: It's almost certainly correct that MSVCRT has
        // __powitf2_f128, but at least one test is relying on it.
@@ -2230,4 +2248,5 @@ def WasmSystemLibrary
     : SystemRuntimeLibrary<isWasm,
       (add DefaultRuntimeLibcallImpls, Int128RTLibcalls,
            CompilerRTOnlyInt64Libcalls, CompilerRTOnlyInt128Libcalls,
+           exp10f, exp10,
            emscripten_return_address)>;
diff --git a/llvm/lib/IR/RuntimeLibcalls.cpp b/llvm/lib/IR/RuntimeLibcalls.cpp
index 60da0047291a9..038a511ed2e68 100644
--- a/llvm/lib/IR/RuntimeLibcalls.cpp
+++ b/llvm/lib/IR/RuntimeLibcalls.cpp
@@ -99,21 +99,15 @@ void RuntimeLibcallsInfo::initLibcalls(const Triple &TT,
       setLibcallImplCallingConv(RTLIB::__sincos_stret,
                                 CallingConv::ARM_AAPCS_VFP);
     }
-
-    if (!darwinHasExp10(TT)) {
-      // FIXME: Remove exp10 from default set
-      setLibcallImpl(RTLIB::EXP10_F32, RTLIB::Unsupported);
-      setLibcallImpl(RTLIB::EXP10_F64, RTLIB::Unsupported);
-    }
   }
 
   if (TT.isOSOpenBSD()) {
     setLibcallImpl(RTLIB::STACKPROTECTOR_CHECK_FAIL, RTLIB::Unsupported);
   }
 
-  // Skip default manual processing for targets that have been fully ported to
+  // Skip default manual processing for targets that have been mostly ported to
   // tablegen for now. Eventually the rest of this should be deleted.
-  if (TT.isX86() || TT.isAArch64() || TT.isWasm())
+  if (TT.isX86() || TT.isAArch64() || TT.isWasm() || TT.isPPC())
     return;
 
   if (TT.isARM() || TT.isThumb()) {
@@ -127,6 +121,10 @@ void RuntimeLibcallsInfo::initLibcalls(const Triple &TT,
     setLibcallImpl(RTLIB::SINCOS_F128, RTLIB::sincos_f128);
   }
 
+  setLibcallImpl(RTLIB::EXP10_F32, RTLIB::exp10f);
+  setLibcallImpl(RTLIB::EXP10_F64, RTLIB::exp10);
+  setLibcallImpl(RTLIB::EXP10_F128, RTLIB::exp10l_f128);
+
   // These libcalls are only available in compiler-rt, not libgcc.
   if (TT.isArch64Bit()) {
     setLibcallImpl(RTLIB::SHL_I128, RTLIB::__ashlti3);
diff --git a/llvm/test/CodeGen/X86/exp10-libcall-names.ll b/llvm/test/CodeGen/X86/exp10-libcall-names.ll
index 96e3aae408e94..2688474b2ce5c 100644
--- a/llvm/test/CodeGen/X86/exp10-libcall-names.ll
+++ b/llvm/test/CodeGen/X86/exp10-libcall-names.ll
@@ -13,10 +13,7 @@
 ; RUN: llc < %s -mtriple=i686-linux-gnu -global-isel -global-isel-abort=1 | FileCheck %s --check-prefix=GISEL-X86
 ; RUN: llc < %s -mtriple=x86_64-linux-gnu -global-isel -global-isel-abort=1 | FileCheck %s --check-prefix=GISEL-X64
 
-; RUN: not llc -mtriple=x86_64-apple-macos10.8 -filetype=null %s 2>&1 | FileCheck -check-prefix=ERR %s
-; Check exp10/exp10f is emitted as __exp10/__exp10f on assorted systems.
-
-; ERR: no libcall available for fexp10
+; Check exp10/exp10f is emitted as __exp10/__exp10f on assorted darwin systems.
 
 define float @test_exp10_f32(float %x) nounwind {
 ; LINUX-LABEL: test_exp10_f32:
@@ -78,43 +75,3 @@ define double @test_exp10_f64(double %x) nounwind {
   %ret = call double @llvm.exp10.f64(double %x)
   ret double %ret
 }
-
-define x86_fp80 @test_exp10_f80(x86_fp80 %x) nounwind {
-; LINUX-LABEL: test_exp10_f80:
-; LINUX:       # %bb.0:
-; LINUX-NEXT:    subq $24, %rsp
-; LINUX-NEXT:    fldt {{[0-9]+}}(%rsp)
-; LINUX-NEXT:    fstpt (%rsp)
-; LINUX-NEXT:    callq exp10l@PLT
-; LINUX-NEXT:    addq $24, %rsp
-; LINUX-NEXT:    retq
-;
-; APPLE-LABEL: test_exp10_f80:
-; APPLE:       ## %bb.0:
-; APPLE-NEXT:    subq $24, %rsp
-; APPLE-NEXT:    fldt {{[0-9]+}}(%rsp)
-; APPLE-NEXT:    fstpt (%rsp)
-; APPLE-NEXT:    callq _exp10l
-; APPLE-NEXT:    addq $24, %rsp
-; APPLE-NEXT:    retq
-;
-; GISEL-X86-LABEL: test_exp10_f80:
-; GISEL-X86:       # %bb.0:
-; GISEL-X86-NEXT:    subl $12, %esp
-; GISEL-X86-NEXT:    fldt {{[0-9]+}}(%esp)
-; GISEL-X86-NEXT:    fstpt (%esp)
-; GISEL-X86-NEXT:    calll exp10l
-; GISEL-X86-NEXT:    addl $12, %esp
-; GISEL-X86-NEXT:    retl
-;
-; GISEL-X64-LABEL: test_exp10_f80:
-; GISEL-X64:       # %bb.0:
-; GISEL-X64-NEXT:    subq $24, %rsp
-; GISEL-X64-NEXT:    fldt {{[0-9]+}}(%rsp)
-; GISEL-X64-NEXT:    fstpt (%rsp)
-; GISEL-X64-NEXT:    callq exp10l
-; GISEL-X64-NEXT:    addq $24, %rsp
-; GISEL-X64-NEXT:    retq
-  %ret = call x86_fp80 @llvm.exp10.f80(x86_fp80 %x)
-  ret x86_fp80 %ret
-}
diff --git a/llvm/test/CodeGen/X86/exp10l-libcall-names.ll b/llvm/test/CodeGen/X86/exp10l-libcall-names.ll
new file mode 100644
index 0000000000000..2e7f9e34f662a
--- /dev/null
+++ b/llvm/test/CodeGen/X86/exp10l-libcall-names.ll
@@ -0,0 +1,46 @@
+; RUN: llc -mtriple=x86_64-linux-gnu < %s | FileCheck -check-prefix=LINUX %s
+; RUN: not llc -mtriple=x86_64-apple-macos10.9 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-ios9.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-tvos9.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-watchos9.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-xros9.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-ios8.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-tvos8.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-xros8.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-driverkit < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: not llc -mtriple=x86_64-apple-driverkit24.0 < %s 2>&1 | FileCheck -check-prefix=ERR %s
+; RUN: llc < %s -mtriple=i686-linux-gnu -global-isel -global-isel-abort=1 | FileCheck %s --check-prefix=GISEL-X86
+; RUN: llc < %s -mtriple=x86_64-linux-gnu -global-isel -global-isel-abort=1 | FileCheck %s --check-prefix=GISEL-X64
+
+; ERR: no libcall available for fexp10
+
+define x86_fp80 @test_exp10_f80(x86_fp80 %x) nounwind {
+; LINUX-LABEL: test_exp10_f80:
+; LINUX:       # %bb.0:
+; LINUX-NEXT:    subq $24, %rsp
+; LINUX-NEXT:    fldt {{[0-9]+}}(%rsp)
+; LINUX-NEXT:    fstpt (%rsp)
+; LINUX-NEXT:    callq exp10l@PLT
+; LINUX-NEXT:    addq $24, %rsp
+; LINUX-NEXT:    retq
+;
+; GISEL-X86-LABEL: test_exp10_f80:
+; GISEL-X86:       # %bb.0:
+; GISEL-X86-NEXT:    subl $12, %esp
+; GISEL-X86-NEXT:    fldt {{[0-9]+}}(%esp)
+; GISEL-X86-NEXT:    fstpt (%esp)
+; GISEL-X86-NEXT:    calll exp10l
+; GISEL-X86-NEXT:    addl $12, %esp
+; GISEL-X86-NEXT:    retl
+;
+; GISEL-X64-LABEL: test_exp10_f80:
+; GISEL-X64:       # %bb.0:
+; GISEL-X64-NEXT:    subq $24, %rsp
+; GISEL-X64-NEXT:    fldt {{[0-9]+}}(%rsp)
+; GISEL-X64-NEXT:    fstpt (%rsp)
+; GISEL-X64-NEXT:    callq exp10l
+; GISEL-X64-NEXT:    addq $24, %rsp
+; GISEL-X64-NEXT:    retq
+  %ret = call x86_fp80 @llvm.exp10.f80(x86_fp80 %x)
+  ret x86_fp80 %ret
+}

@arsenm arsenm force-pushed the users/arsenm/runtime-libcalls/stop-opt-out-exp10 branch from d2d9452 to b2fc78b Compare July 15, 2025 05:05
@arsenm arsenm force-pushed the users/arsenm/runtime-libcalls/x86-mostly-move-libcalls-tablegen branch from 1a5ee21 to 5915e12 Compare July 15, 2025 05:05
@arsenm arsenm force-pushed the users/arsenm/runtime-libcalls/stop-opt-out-exp10 branch from a64799d to c35ac3c Compare July 15, 2025 06:19
@arsenm arsenm force-pushed the users/arsenm/runtime-libcalls/x86-mostly-move-libcalls-tablegen branch from 5915e12 to 27fd017 Compare July 15, 2025 06:19
Base automatically changed from users/arsenm/runtime-libcalls/x86-mostly-move-libcalls-tablegen to main July 15, 2025 07:18
arsenm and others added 2 commits July 15, 2025 07:20
This changes the behavior on old darwin triples for x86_fp80;
it now turns into an error instead of emitting exp10l. The comments
in TargetLibraryInfo suggest it never existed.
@arsenm arsenm force-pushed the users/arsenm/runtime-libcalls/stop-opt-out-exp10 branch from c35ac3c to 465db65 Compare July 15, 2025 07:20
@tgross35
Copy link
Contributor

For my understanding: this is expected to ICE because there is no possible libcall here (since exp10l is f64 on Apple) and not because ICEs are expected when the default target lib isn't known to provide a symbol, right? Otherwise I would be a bit concerned about our ability to make fp128 work cross-platform by vendoring needed symbols (unrelated to this change of course).

@arsenm
Copy link
Contributor Author

arsenm commented Jul 15, 2025

For my understanding: this is expected to ICE because there is no possible libcall here (since exp10l is f64 on Apple) and not because ICEs are expected when the default target lib isn't known to provide a symbol, right?

These are equivalent. There needs to be a known symbol that provides the behavior needed to implement exp10.fp128. The "default target lib" concept doesn't really exist, it's a historical implementation artifact. The compiler needs accurate knowledge about a concrete target

Otherwise I would be a bit concerned about our ability to make fp128 work cross-platform by vendoring needed symbols (unrelated to this change of course).

In theory the backend needs to do something to provide the intrinsic behavior. This is currently built on top of emitting libm calls to shoddy host math libraries. In a better world we would have complete coverage of all functions x types in compiler-rt and ignore the host libraries, or rip out most math intrinsics and disallow types we don't have a direct compiler implementation of

@nikic
Copy link
Contributor

nikic commented Jul 15, 2025

Except for target specific types (x86_fp80 and ppc_fp128) I think it is preferable if missing FP libcalls that cannot be legalized result in a linker error instead of a compiler error, which allows the user to provide them. At least when considering the world we live in now, rather than possible future designs.

@tgross35
Copy link
Contributor

tgross35 commented Jul 15, 2025

Actually, is there anything unique about exp10 here? All x86_fp80 intrinsics on Apple (as well as Windows) that get lowered to libm calls have the same problem.

Except for target specific types (x86_fp80 and ppc_fp128) I think it is preferable if missing FP libcalls that cannot be legalized result in a linker error instead of a compiler error, which allows the user to provide them. At least when considering the world we live in now, rather than possible future designs.

In theory this could lower to the C23 exp10f64x symbol rather than having an error (supported by glibc but not much else, yet). The long-term correctness of this depends on an assumption that Windows and Apple will either never specify _Float64x, or if they do specify it, make it x86_fp80 (cc #97335).

Edit: sorry, I see you aren't talking about x86_fp80 here.

@tgross35
Copy link
Contributor

In theory the backend needs to do something to provide the intrinsic behavior. This is currently built on top of emitting libm calls to shoddy host math libraries. In a better world we would have complete coverage of all functions x types in compiler-rt and ignore the host libraries, or rip out most math intrinsics and disallow types we don't have a direct compiler implementation of

It would be nice if there was a triple flag to indicate presence of a backend-agnostic C23-compliant library, so e.g. sqrtf16, sqrtf32, sqrtf64, sqrtf64x (for x87 f80), and sqrtf128 can be lowered to and long double never needs to be considered. Glibc has already had these for a while. ppc_fp128 would still be a bit weird since I don't believe it can classify as _Float64x.

I know we talked about something like this on Discord a while back.

@nikic
Copy link
Contributor

nikic commented Jul 15, 2025

Actually, is there anything unique about exp10 here? All x86_fp80 intrinsics on Apple (as well as Windows) that get lowered to libm calls have the same problem.

Yeah, that's my understanding as well.

Except for target specific types (x86_fp80 and ppc_fp128) I think it is preferable if missing FP libcalls that cannot be legalized result in a linker error instead of a compiler error, which allows the user to provide them. At least when considering the world we live in now, rather than possible future designs.

In theory this could lower to the C23 exp10f64x symbol rather than having an error (supported by glibc but not much else, yet). The long-term correctness of this depends on an assumption that Windows and Apple will either never specify _Float64x, or if they do specify it, make it x86_fp80 (cc #97335).

Edit: sorry, I see you aren't talking about x86_fp80 here.

Yes, what specifically concerns me is that change to fp128 behavior in this PR. I don't think that should ever result in a hard compiler error.

I wasn't aware that the f64x suffix is a thing. That does seem like a reasonable fallback for targets where long double is not x86_fp80. But I'm also not sure this is really worth bothering with. Is there any real interest in having fp80 work across all x86 targets?

@arsenm
Copy link
Contributor Author

arsenm commented Jul 15, 2025

I think it is preferable if missing FP libcalls that cannot be legalized result in a linker error instead of a compiler error, which allows the user to provide them

Yes, what specifically concerns me is that change to fp128 behavior in this PR. I don't think that should ever result in a hard compiler error.

I don't think it's reasonable to support this kind of use case for a known triple. Maybe for something defined as freestanding, but not a definitive target. In theory the compiler should be using this knowledge of this-call-exists-or-not to use a fallback path to avoid using that call. If we have to pretend phantom calls exist in the off chance someone is hacking around library defects, I don't see how you can ever move beyond broken host libraries

@arsenm
Copy link
Contributor Author

arsenm commented Jul 15, 2025

Plus this is one of the odd cases that happened to emit some kind of code. Most of these untested, accidental libcall cases hit asserts and crashes in codegen so it's not like a designed system

@tgross35
Copy link
Contributor

For rust I suppose we could switch to calling the *f128 functions directly on all platforms rather than going via intrinsics. That would allow us to avoid #44744 anyway. I expect other languages may get a bit of a surprise though (cc @alexrp for Zig).

Regardless whether the fp128 changes are kept or if this gets reduced to only x86_f80, is there any reason not to treat all libcalls the same rather than only exp10?

Is there any real interest in having fp80 work across all x86 targets?

I'd like to add the type to core::arch eventually (have a branch floating around somewhere). But this would most likely only be arithmetic, probably not all math symbols. If that changes we could always call sqrtf64x without going through intrinsics, though something like #148604 (comment) would make that nicer.

@alexrp
Copy link
Member

alexrp commented Jul 15, 2025

Except for target specific types (x86_fp80 and ppc_fp128) I think it is preferable if missing FP libcalls that cannot be legalized result in a linker error instead of a compiler error, which allows the user to provide them. At least when considering the world we live in now, rather than possible future designs.

I agree with this. A linker error we can straightforwardly address by implementing the missing function in Zig's compiler-rt. An ICE from LLVM is significantly more annoying to work around.

Is there any real interest in having fp80 work across all x86 targets?

In Zig, we currently lower f80 to i80 and manual soft float calls if the target is not x86 with long double = x86_fp80. It would be nice if we didn't have to do that, I suppose, but it's not terribly important.

I expect other languages may get a bit of a surprise though (cc @alexrp for Zig).

I don't believe Zig emits exp10 intrinsic calls at the moment, FWIW.

@nikic
Copy link
Contributor

nikic commented Jul 15, 2025

I think it is preferable if missing FP libcalls that cannot be legalized result in a linker error instead of a compiler error, which allows the user to provide them

Yes, what specifically concerns me is that change to fp128 behavior in this PR. I don't think that should ever result in a hard compiler error.

I don't think it's reasonable to support this kind of use case for a known triple. Maybe for something defined as freestanding, but not a definitive target. In theory the compiler should be using this knowledge of this-call-exists-or-not to use a fallback path to avoid using that call. If we have to pretend phantom calls exist in the off chance someone is hacking around library defects, I don't see how you can ever move beyond broken host libraries

I think that would be a reasonable position to take if the fallback path to avoid the call actually existed. But it does not, so we should not unnecessarily force an error.

The longer term solution here would be along the lines of what @tgross35 suggested, that is the ability to override LLVM's default assumptions about libcalls for the targets, to be able to say things like "I'm actually linking against compiler-rt rather than libgcc" or "I will link against a full featured (glibc-style) libm".

But I think that until that is supported, we should not change the status quo of how the FP libcall fallbacks work.

For rust I suppose we could switch to calling the *f128 functions directly on all platforms rather than going via intrinsics. That would allow us to avoid #44744 anyway. I expect other languages may get a bit of a surprise though (cc @alexrp for Zig).

I don't think that's a great option, as LLVM will no longer understand these libcalls and won't be able to e.g. constant fold them.

I don't believe Zig emits exp10 intrinsic calls at the moment, FWIW.

I think neither does Rust, so the behavior for this particular builtin changing wouldn't be a big deal. But if this were to extend to other FP libcalls, there'd be a problem.

@dpaoliello
Copy link
Contributor

Can darwinHasExp10 be removed (or unified) with this change?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants