[llvm] DAG: Gracefully diagnose missing FP conversion libcalls when softening (PR #215765)

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 12 02:47:56 PDT 2026


https://github.com/arsenm created https://github.com/llvm/llvm-project/pull/215765

Diagnose the missing libcall and return poison instead.

Co-authored-by: Claude (Claude-Opus-4.8) <noreply at anthropic.com>

>From f9d1cdac69b528f59d50a7a83542214c7a7005d2 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Tue, 11 Aug 2026 20:43:02 +0200
Subject: [PATCH] DAG: Gracefully diagnose missing FP conversion libcalls when
 softening

Diagnose the missing libcall and return poison instead.

Co-authored-by: Claude (Claude-Opus-4.8) <noreply at anthropic.com>
---
 .../SelectionDAG/LegalizeFloatTypes.cpp       | 60 ++++++++++++----
 .../NVPTX/fp128-conv-no-libcall-error.ll      | 70 +++++++++++++++++++
 2 files changed, 116 insertions(+), 14 deletions(-)
 create mode 100644 llvm/test/CodeGen/NVPTX/fp128-conv-no-libcall-error.ll

diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
index d0a29b5365daa..80f557ff5beb3 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
@@ -661,12 +661,14 @@ SDValue DAGTypeLegalizer::SoftenFloatRes_FP_EXTEND(SDNode *N) {
       ReplaceValueWith(SDValue(N, 1), Chain);
     return DAG.getPOISON(NVT);
   }
+  RTLIB::LibcallImpl LCImpl = DAG.getLibcalls().getLibcallImpl(LC);
+  if (LCImpl == RTLIB::Unsupported)
+    return SoftenFloatRes_NoLibcall(N, NVT);
   TargetLowering::MakeLibCallOptions CallOptions;
   EVT OpVT = N->getOperand(IsStrict ? 1 : 0).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;
@@ -713,12 +715,14 @@ SDValue DAGTypeLegalizer::SoftenFloatRes_FP_ROUND(SDNode *N) {
   SDValue Chain = IsStrict ? N->getOperand(0) : SDValue();
   RTLIB::Libcall LC = RTLIB::getFPROUND(Op.getValueType(), N->getValueType(0));
   assert(LC != RTLIB::UNKNOWN_LIBCALL && "Unsupported FP_ROUND!");
+  RTLIB::LibcallImpl LCImpl = DAG.getLibcalls().getLibcallImpl(LC);
+  if (LCImpl == RTLIB::Unsupported)
+    return SoftenFloatRes_NoLibcall(N, NVT);
   TargetLowering::MakeLibCallOptions CallOptions;
   EVT OpVT = N->getOperand(IsStrict ? 1 : 0).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;
@@ -1118,6 +1122,11 @@ SDValue DAGTypeLegalizer::SoftenFloatRes_XINT_TO_FP(SDNode *N) {
   }
   assert(LC != RTLIB::UNKNOWN_LIBCALL && "Unsupported XINT_TO_FP!");
 
+  EVT NRVT = TLI.getTypeToTransformTo(*DAG.getContext(), RVT);
+  RTLIB::LibcallImpl LCImpl = DAG.getLibcalls().getLibcallImpl(LC);
+  if (LCImpl == RTLIB::Unsupported)
+    return SoftenFloatRes_NoLibcall(N, NRVT);
+
   SDValue Chain = IsStrict ? N->getOperand(0) : SDValue();
   // Sign/zero extend the argument if the libcall takes a larger type.
   SDValue Op = DAG.getNode(Signed ? ISD::SIGN_EXTEND : ISD::ZERO_EXTEND, dl,
@@ -1126,8 +1135,7 @@ SDValue DAGTypeLegalizer::SoftenFloatRes_XINT_TO_FP(SDNode *N) {
   CallOptions.setIsSigned(Signed);
   CallOptions.setTypeListBeforeSoften(SVT, RVT);
   std::pair<SDValue, SDValue> Tmp =
-      TLI.makeLibCall(DAG, LC, TLI.getTypeToTransformTo(*DAG.getContext(), RVT),
-                      Op, CallOptions, dl, Chain);
+      TLI.makeLibCall(DAG, LCImpl, NRVT, Op, CallOptions, dl, Chain);
 
   if (IsStrict)
     ReplaceValueWith(SDValue(N, 1), Tmp.second);
@@ -1250,12 +1258,23 @@ SDValue DAGTypeLegalizer::SoftenFloatOp_FP_ROUND(SDNode *N) {
   assert(LC != RTLIB::UNKNOWN_LIBCALL && "Unsupported FP_ROUND libcall");
 
   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(RVT);
+    if (IsStrict) {
+      ReplaceValueWith(SDValue(N, 1), Chain);
+      ReplaceValueWith(SDValue(N, 0), Poison);
+      return SDValue();
+    }
+    return Poison;
+  }
   Op = GetSoftenedFloat(Op);
   TargetLowering::MakeLibCallOptions CallOptions;
   CallOptions.setTypeListBeforeSoften(SVT, RVT);
-  std::pair<SDValue, SDValue> Tmp = TLI.makeLibCall(DAG, LC, RVT, Op,
-                                                    CallOptions, SDLoc(N),
-                                                    Chain);
+  std::pair<SDValue, SDValue> Tmp =
+      TLI.makeLibCall(DAG, LCImpl, RVT, Op, CallOptions, SDLoc(N), Chain);
   if (IsStrict) {
     ReplaceValueWith(SDValue(N, 1), Tmp.second);
     ReplaceValueWith(SDValue(N, 0), Tmp.first);
@@ -1325,12 +1344,25 @@ SDValue DAGTypeLegalizer::SoftenFloatOp_FP_TO_XINT(SDNode *N) {
   assert(LC != RTLIB::UNKNOWN_LIBCALL && NVT.isSimple() &&
          "Unsupported FP_TO_XINT!");
 
-  Op = GetSoftenedFloat(Op);
   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(RVT);
+    if (IsStrict) {
+      ReplaceValueWith(SDValue(N, 1), Chain);
+      ReplaceValueWith(SDValue(N, 0), Poison);
+      return SDValue();
+    }
+    return Poison;
+  }
+
+  Op = GetSoftenedFloat(Op);
   TargetLowering::MakeLibCallOptions CallOptions;
   CallOptions.setTypeListBeforeSoften(SVT, RVT);
-  std::pair<SDValue, SDValue> Tmp = TLI.makeLibCall(DAG, LC, NVT, Op,
-                                                    CallOptions, dl, Chain);
+  std::pair<SDValue, SDValue> Tmp =
+      TLI.makeLibCall(DAG, LCImpl, NVT, Op, CallOptions, dl, Chain);
 
   // Truncate the result if the libcall returns a larger type.
   SDValue Res = DAG.getNode(ISD::TRUNCATE, dl, RVT, Tmp.first);
diff --git a/llvm/test/CodeGen/NVPTX/fp128-conv-no-libcall-error.ll b/llvm/test/CodeGen/NVPTX/fp128-conv-no-libcall-error.ll
new file mode 100644
index 0000000000000..5d4b00ae4e81f
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/fp128-conv-no-libcall-error.ll
@@ -0,0 +1,70 @@
+; RUN: not llc -mtriple=nvptx64 -filetype=null %s 2>&1 | FileCheck %s
+
+; NVPTX has no fp128 soft-float library, so these conversions should diagnose a
+; missing libcall rather than crash.
+
+; CHECK: error: no libcall available for fp_extend
+define fp128 @test_fpext_f64(double %x) nounwind {
+  %r = fpext double %x to fp128
+  ret fp128 %r
+}
+
+; CHECK: error: no libcall available for fp_round
+define double @test_fptrunc_f128(fp128 %x) nounwind {
+  %r = fptrunc fp128 %x to double
+  ret double %r
+}
+
+; CHECK: error: no libcall available for fp_round
+define half @test_fptrunc_f128_half(fp128 %x) nounwind {
+  %r = fptrunc fp128 %x to half
+  ret half %r
+}
+
+; CHECK: error: no libcall available for sint_to_fp
+define fp128 @test_sitofp(i32 %x) nounwind {
+  %r = sitofp i32 %x to fp128
+  ret fp128 %r
+}
+
+; CHECK: error: no libcall available for uint_to_fp
+define fp128 @test_uitofp(i32 %x) nounwind {
+  %r = uitofp i32 %x to fp128
+  ret fp128 %r
+}
+
+; CHECK: error: no libcall available for fp_to_sint
+define i32 @test_fptosi(fp128 %x) nounwind {
+  %r = fptosi fp128 %x to i32
+  ret i32 %r
+}
+
+; CHECK: error: no libcall available for fp_to_uint
+define i32 @test_fptoui(fp128 %x) nounwind {
+  %r = fptoui fp128 %x to i32
+  ret i32 %r
+}
+
+; CHECK: error: no libcall available for strict_fp_extend
+define fp128 @test_strict_fpext(double %x) nounwind strictfp {
+  %r = call fp128 @llvm.experimental.constrained.fpext.f128.f64(double %x, metadata !"fpexcept.strict")
+  ret fp128 %r
+}
+
+; CHECK: error: no libcall available for strict_fp_round
+define double @test_strict_fptrunc(fp128 %x) nounwind strictfp {
+  %r = call double @llvm.experimental.constrained.fptrunc.f64.f128(fp128 %x, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret double %r
+}
+
+; CHECK: error: no libcall available for strict_sint_to_fp
+define fp128 @test_strict_sitofp(i32 %x) nounwind strictfp {
+  %r = call fp128 @llvm.experimental.constrained.sitofp.f128.i32(i32 %x, metadata !"round.tonearest", metadata !"fpexcept.strict")
+  ret fp128 %r
+}
+
+; CHECK: error: no libcall available for strict_fp_to_sint
+define i32 @test_strict_fptosi(fp128 %x) nounwind strictfp {
+  %r = call i32 @llvm.experimental.constrained.fptosi.i32.f128(fp128 %x, metadata !"fpexcept.strict")
+  ret i32 %r
+}



More information about the llvm-commits mailing list