[llvm-branch-commits] [llvm] DAG: Gracefully diagnose missing soft-float libcalls (PR #215065)

Matt Arsenault via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Aug 12 04:18:42 PDT 2026


https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/215065

>From 6d53fba5c226706a59b88c9b34babf320d48363c Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Sun, 9 Aug 2026 07:46:49 +0200
Subject: [PATCH] DAG: Gracefully diagnose missing soft-float and strict-FP
 libcalls

Several soft-float legalization paths legalizer, called into a libcall
without checking whether the target provides one, fatally erroring for
fp128 operations on targets with no soft-float support.

Co-authored-by: Claude (Claude-Opus-4.8) <noreply at anthropic.com>
---
 llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp | 14 +++--
 .../SelectionDAG/LegalizeFloatTypes.cpp       | 56 +++++++++++++++----
 llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h |  1 +
 llvm/test/CodeGen/AMDGPU/fneg.ll              |  4 +-
 .../CodeGen/NVPTX/f128-no-libcall-error.ll    | 35 ++++++++++++
 5 files changed, 93 insertions(+), 17 deletions(-)
 create mode 100644 llvm/test/CodeGen/NVPTX/f128-no-libcall-error.ll

diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
index 9f58ee4024c3a..3b3d96f5b9b8c 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
@@ -2224,14 +2224,20 @@ void SelectionDAGLegalize::ExpandFPLibCall(SDNode* Node,
 
   if (Node->isStrictFPOpcode()) {
     EVT RetVT = Node->getValueType(0);
+    RTLIB::LibcallImpl LCImpl = DAG.getLibcalls().getLibcallImpl(LC);
+    if (LCImpl == RTLIB::Unsupported) {
+      DAG.getContext()->emitError(Twine("no libcall available for ") +
+                                  Node->getOperationName(&DAG));
+      Results.push_back(DAG.getPOISON(RetVT));
+      Results.push_back(Node->getOperand(0));
+      return;
+    }
     SmallVector<SDValue, 4> Ops(drop_begin(Node->ops()));
     TargetLowering::MakeLibCallOptions CallOptions;
     CallOptions.IsPostTypeLegalization = true;
     // FIXME: This doesn't support tail calls.
-    std::pair<SDValue, SDValue> Tmp = TLI.makeLibCall(DAG, LC, RetVT,
-                                                      Ops, CallOptions,
-                                                      SDLoc(Node),
-                                                      Node->getOperand(0));
+    std::pair<SDValue, SDValue> Tmp = TLI.makeLibCall(
+        DAG, LCImpl, RetVT, Ops, CallOptions, SDLoc(Node), Node->getOperand(0));
     Results.push_back(Tmp.first);
     Results.push_back(Tmp.second);
   } else {
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
index 9d28b67ea2869..946e045633ad1 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
@@ -189,6 +189,16 @@ void DAGTypeLegalizer::SoftenFloatResult(SDNode *N, unsigned ResNo) {
   }
 }
 
+// No libcall is available to soften this operation. Emit a diagnostic and
+// produce a poison result of the softened type \p NVT.
+SDValue DAGTypeLegalizer::SoftenFloatRes_NoLibcall(SDNode *N, EVT NVT) {
+  DAG.getContext()->emitError(Twine("no libcall available for ") +
+                              N->getOperationName(&DAG));
+  if (N->isStrictFPOpcode())
+    ReplaceValueWith(SDValue(N, 1), N->getOperand(0));
+  return DAG.getPOISON(NVT);
+}
+
 SDValue DAGTypeLegalizer::SoftenFloatRes_Unary(SDNode *N, RTLIB::Libcall LC) {
   bool IsStrict = N->isStrictFPOpcode();
   EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), N->getValueType(0));
@@ -197,12 +207,14 @@ SDValue DAGTypeLegalizer::SoftenFloatRes_Unary(SDNode *N, RTLIB::Libcall LC) {
          "Unexpected number of operands!");
   SDValue Op = GetSoftenedFloat(N->getOperand(0 + Offset));
   SDValue Chain = IsStrict ? N->getOperand(0) : SDValue();
+  RTLIB::LibcallImpl LCImpl = DAG.getLibcalls().getLibcallImpl(LC);
+  if (LCImpl == RTLIB::Unsupported)
+    return SoftenFloatRes_NoLibcall(N, NVT);
   TargetLowering::MakeLibCallOptions CallOptions;
   EVT OpVT = N->getOperand(0 + Offset).getValueType();
   CallOptions.setTypeListBeforeSoften(OpVT, N->getValueType(0));
-  std::pair<SDValue, SDValue> Tmp = TLI.makeLibCall(DAG, LC, NVT, Op,
-                                                    CallOptions, SDLoc(N),
-                                                    Chain);
+  std::pair<SDValue, SDValue> Tmp =
+      TLI.makeLibCall(DAG, LCImpl, NVT, Op, CallOptions, SDLoc(N), Chain);
   if (IsStrict)
     ReplaceValueWith(SDValue(N, 1), Tmp.second);
   return Tmp.first;
@@ -217,13 +229,15 @@ SDValue DAGTypeLegalizer::SoftenFloatRes_Binary(SDNode *N, RTLIB::Libcall LC) {
   SDValue Ops[2] = { GetSoftenedFloat(N->getOperand(0 + Offset)),
                      GetSoftenedFloat(N->getOperand(1 + Offset)) };
   SDValue Chain = IsStrict ? N->getOperand(0) : SDValue();
+  RTLIB::LibcallImpl LCImpl = DAG.getLibcalls().getLibcallImpl(LC);
+  if (LCImpl == RTLIB::Unsupported)
+    return SoftenFloatRes_NoLibcall(N, NVT);
   TargetLowering::MakeLibCallOptions CallOptions;
   EVT OpsVT[2] = { N->getOperand(0 + Offset).getValueType(),
                    N->getOperand(1 + Offset).getValueType() };
   CallOptions.setTypeListBeforeSoften(OpsVT, N->getValueType(0));
-  std::pair<SDValue, SDValue> Tmp = TLI.makeLibCall(DAG, LC, NVT, Ops,
-                                                    CallOptions, SDLoc(N),
-                                                    Chain);
+  std::pair<SDValue, SDValue> Tmp =
+      TLI.makeLibCall(DAG, LCImpl, NVT, Ops, CallOptions, SDLoc(N), Chain);
   if (IsStrict)
     ReplaceValueWith(SDValue(N, 1), Tmp.second);
   return Tmp.first;
@@ -777,9 +791,18 @@ SDValue DAGTypeLegalizer::SoftenFloatRes_FFREXP(SDNode *N) {
   EVT VT0 = N->getValueType(0);
   EVT VT1 = N->getValueType(1);
   RTLIB::Libcall LC = RTLIB::getFREXP(VT0);
+  RTLIB::LibcallImpl LCImpl = DAG.getLibcalls().getLibcallImpl(LC);
   EVT NVT0 = TLI.getTypeToTransformTo(*DAG.getContext(), VT0);
   SDLoc DL(N);
 
+  if (LCImpl == RTLIB::Unsupported) {
+    DAG.getContext()->emitError(Twine("no libcall available for ") +
+                                N->getOperationName(&DAG));
+    SDValue PoisonExp = DAG.getPOISON(VT1);
+    ReplaceValueWith(SDValue(N, 1), PoisonExp);
+    return DAG.getMergeValues({DAG.getPOISON(NVT0), PoisonExp}, DL);
+  }
+
   if (DAG.getLibInfo().getIntSize() != VT1.getSizeInBits()) {
     // If the exponent does not match with sizeof(int) a libcall would use the
     // wrong type for the argument.
@@ -803,8 +826,8 @@ SDValue DAGTypeLegalizer::SoftenFloatRes_FFREXP(SDNode *N) {
   CallOptions.setTypeListBeforeSoften({OpsVT}, VT0)
       .setOpsTypeOverrides(CallOpsTypeOverrides);
 
-  auto [ReturnVal, Chain] = TLI.makeLibCall(DAG, LC, NVT0, Ops, CallOptions, DL,
-                                            /*Chain=*/SDValue());
+  auto [ReturnVal, Chain] = TLI.makeLibCall(DAG, LCImpl, NVT0, Ops, CallOptions,
+                                            DL, /*Chain=*/SDValue());
   int FrameIdx = cast<FrameIndexSDNode>(StackSlot)->getIndex();
   auto PtrInfo =
       MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FrameIdx);
@@ -1481,12 +1504,23 @@ SDValue DAGTypeLegalizer::SoftenFloatOp_Unary(SDNode *N, RTLIB::Libcall LC) {
   unsigned Offset = IsStrict ? 1 : 0;
   SDValue Op = GetSoftenedFloat(N->getOperand(0 + Offset));
   SDValue Chain = IsStrict ? N->getOperand(0) : SDValue();
+  RTLIB::LibcallImpl LCImpl = DAG.getLibcalls().getLibcallImpl(LC);
+  if (LCImpl == RTLIB::Unsupported) {
+    DAG.getContext()->emitError(Twine("no libcall available for ") +
+                                N->getOperationName(&DAG));
+    SDValue Poison = DAG.getPOISON(N->getValueType(0));
+    if (IsStrict) {
+      ReplaceValueWith(SDValue(N, 1), Chain);
+      ReplaceValueWith(SDValue(N, 0), Poison);
+      return SDValue();
+    }
+    return Poison;
+  }
   TargetLowering::MakeLibCallOptions CallOptions;
   EVT OpVT = N->getOperand(0 + Offset).getValueType();
   CallOptions.setTypeListBeforeSoften(OpVT, N->getValueType(0));
-  std::pair<SDValue, SDValue> Tmp = TLI.makeLibCall(DAG, LC, NVT, Op,
-                                                    CallOptions, SDLoc(N),
-                                                    Chain);
+  std::pair<SDValue, SDValue> Tmp =
+      TLI.makeLibCall(DAG, LCImpl, NVT, Op, CallOptions, SDLoc(N), Chain);
   if (IsStrict) {
     ReplaceValueWith(SDValue(N, 1), Tmp.second);
     ReplaceValueWith(SDValue(N, 0), Tmp.first);
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
index 1b86e83a037a3..08e51ecc86c8b 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
@@ -566,6 +566,7 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer {
 
   // Convert Float Results to Integer.
   void SoftenFloatResult(SDNode *N, unsigned ResNo);
+  SDValue SoftenFloatRes_NoLibcall(SDNode *N, EVT NVT);
   SDValue SoftenFloatRes_Unary(SDNode *N, RTLIB::Libcall LC);
   bool SoftenFloatRes_UnaryWithTwoFPResults(
       SDNode *N, RTLIB::Libcall LC, std::optional<unsigned> CallRetResNo = {});
diff --git a/llvm/test/CodeGen/AMDGPU/fneg.ll b/llvm/test/CodeGen/AMDGPU/fneg.ll
index c15ac28288a08..2366fce16bbd5 100644
--- a/llvm/test/CodeGen/AMDGPU/fneg.ll
+++ b/llvm/test/CodeGen/AMDGPU/fneg.ll
@@ -3,9 +3,9 @@
 ; RUN: llc -mtriple=amdgpu8.02 < %s | FileCheck -enable-var-scope -check-prefixes=GCN,VI %s
 ; RUN: llc -mtriple=amdgpu11.00 -mattr=+real-true16 < %s | FileCheck -enable-var-scope -check-prefixes=GCN,GFX11,GFX11-TRUE16 %s
 ; RUN: llc -mtriple=amdgpu11.00 -mattr=-real-true16 < %s | FileCheck -enable-var-scope -check-prefixes=GCN,GFX11,GFX11-FAKE16 %s
-; RUN: not --crash llc -mtriple=r600 -mcpu=redwood < %s 2>&1 | FileCheck -check-prefix=R600-ERR %s
+; RUN: not llc -mtriple=r600 -mcpu=redwood -filetype=null %s 2>&1 | FileCheck -check-prefix=R600-ERR %s
 
-; R600-ERR: LLVM ERROR: unsupported library call operation
+; R600-ERR: error: no libcall available for fsub
 
 define amdgpu_kernel void @s_fneg_f32(ptr addrspace(1) %out, float %in) {
 ; SI-LABEL: s_fneg_f32:
diff --git a/llvm/test/CodeGen/NVPTX/f128-no-libcall-error.ll b/llvm/test/CodeGen/NVPTX/f128-no-libcall-error.ll
new file mode 100644
index 0000000000000..d3286a0bf0391
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/f128-no-libcall-error.ll
@@ -0,0 +1,35 @@
+; RUN: not llc -mtriple=nvptx64 -filetype=null %s 2>&1 | FileCheck %s
+
+; NVPTX has no fp128 soft-float library, so these operations have no libcall
+; available. Make sure they emit a proper diagnostic rather than fatal erroring
+; in makeLibCall.
+
+; CHECK: error: no libcall available for fexp10
+define fp128 @test_exp10(fp128 %x) nounwind {
+  %r = call fp128 @llvm.exp10.f128(fp128 %x)
+  ret fp128 %r
+}
+
+; CHECK: error: no libcall available for fmaximum
+define fp128 @test_maximum(fp128 %x, fp128 %y) nounwind {
+  %r = call fp128 @llvm.maximum.f128(fp128 %x, fp128 %y)
+  ret fp128 %r
+}
+
+; CHECK: error: no libcall available for ffrexp
+define { fp128, i32 } @test_frexp(fp128 %x) nounwind {
+  %r = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %x)
+  ret { fp128, i32 } %r
+}
+
+; CHECK: error: no libcall available for lrint
+define i32 @test_lrint(fp128 %x) nounwind {
+  %r = call i32 @llvm.lrint.i32.f128(fp128 %x)
+  ret i32 %r
+}
+
+; CHECK: error: no libcall available for strict_fadd
+define fp128 @test_strict_fadd(fp128 %x, fp128 %y) nounwind strictfp {
+  %r = call fp128 @llvm.experimental.constrained.fadd.f128(fp128 %x, fp128 %y, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret fp128 %r
+}



More information about the llvm-branch-commits mailing list