[llvm-branch-commits] [llvm] InstCombine: Handle fpext in SimplifyDemandedFPClass (PR #174849)

Matt Arsenault via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Jan 7 11:29:36 PST 2026


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

None

>From e03d65130631cf14e1e6c55bb6f003b96e5945bd Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Tue, 30 Dec 2025 12:45:12 +0100
Subject: [PATCH] InstCombine: Handle fpext in SimplifyDemandedFPClass

---
 llvm/include/llvm/Support/KnownFPClass.h      |  6 +++++
 llvm/lib/Analysis/ValueTracking.cpp           | 17 +++---------
 llvm/lib/Support/KnownFPClass.cpp             | 22 +++++++++++++++
 .../InstCombineSimplifyDemanded.cpp           | 22 +++++++++++++++
 .../simplify-demanded-fpclass-fpext.ll        | 27 +++++++------------
 5 files changed, 62 insertions(+), 32 deletions(-)

diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index a3a485a961877..4d2f42202301a 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -20,6 +20,7 @@
 
 namespace llvm {
 class APFloat;
+struct fltSemantics;
 
 struct KnownFPClass {
   /// Floating-point classes the value could be one of.
@@ -283,6 +284,11 @@ struct KnownFPClass {
   static LLVM_ABI KnownFPClass
   log(const KnownFPClass &Src, DenormalMode Mode = DenormalMode::getDynamic());
 
+  /// Propagate known class for fpext.
+  static LLVM_ABI KnownFPClass fpext(const KnownFPClass &KnownSrc,
+                                     const fltSemantics &DstTy,
+                                     const fltSemantics &SrcTy);
+
   void resetAll() { *this = KnownFPClass(); }
 };
 
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index dbe74f463b57c..44b8713ede471 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5750,27 +5750,16 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
     break;
   }
   case Instruction::FPExt: {
-    // Infinity, nan and zero propagate from source.
+    KnownFPClass KnownSrc;
     computeKnownFPClass(Op->getOperand(0), DemandedElts, InterestedClasses,
-                        Known, Q, Depth + 1);
+                        KnownSrc, Q, Depth + 1);
 
     const fltSemantics &DstTy =
         Op->getType()->getScalarType()->getFltSemantics();
     const fltSemantics &SrcTy =
         Op->getOperand(0)->getType()->getScalarType()->getFltSemantics();
 
-    // All subnormal inputs should be in the normal range in the result type.
-    if (APFloat::isRepresentableAsNormalIn(SrcTy, DstTy)) {
-      if (Known.KnownFPClasses & fcPosSubnormal)
-        Known.KnownFPClasses |= fcPosNormal;
-      if (Known.KnownFPClasses & fcNegSubnormal)
-        Known.KnownFPClasses |= fcNegNormal;
-      Known.knownNot(fcSubnormal);
-    }
-
-    // Sign bit of a nan isn't guaranteed.
-    if (!Known.isKnownNeverNaN())
-      Known.SignBit = std::nullopt;
+    Known = KnownFPClass::fpext(KnownSrc, DstTy, SrcTy);
     break;
   }
   case Instruction::FPTrunc: {
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index a352f1267be27..7a60221f7e8f8 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -329,3 +329,25 @@ KnownFPClass KnownFPClass::log(const KnownFPClass &KnownSrc,
 
   return Known;
 }
+
+KnownFPClass KnownFPClass::fpext(const KnownFPClass &KnownSrc,
+                                 const fltSemantics &DstTy,
+                                 const fltSemantics &SrcTy) {
+  // Infinity, nan and zero propagate from source.
+  KnownFPClass Known = KnownSrc;
+
+  // All subnormal inputs should be in the normal range in the result type.
+  if (APFloat::isRepresentableAsNormalIn(SrcTy, DstTy)) {
+    if (Known.KnownFPClasses & fcPosSubnormal)
+      Known.KnownFPClasses |= fcPosNormal;
+    if (Known.KnownFPClasses & fcNegSubnormal)
+      Known.KnownFPClasses |= fcNegNormal;
+    Known.knownNot(fcSubnormal);
+  }
+
+  // Sign bit of a nan isn't guaranteed.
+  if (!Known.isKnownNeverNaN())
+    Known.SignBit = std::nullopt;
+
+  return Known;
+}
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index c628f53f28cbb..1a575ae7b4bfd 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -2241,6 +2241,28 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Value *V,
     FPClassTest ValidResults = DemandedMask & Known.KnownFPClasses;
     return getFPClassConstant(VTy, ValidResults, /*IsCanonicalizing=*/true);
   }
+  case Instruction::FPExt: {
+    FPClassTest SrcDemandedMask = DemandedMask;
+
+    // No subnormal result does not imply not-subnormal in the source type.
+    if ((DemandedMask & fcNegSubnormal) == fcNone)
+      SrcDemandedMask |= fcNegSubnormal;
+    if ((DemandedMask & fcPosSubnormal) == fcNone)
+      SrcDemandedMask |= fcPosSubnormal;
+
+    KnownFPClass KnownSrc;
+    if (SimplifyDemandedFPClass(I, 0, SrcDemandedMask, KnownSrc, Depth + 1))
+      return I;
+
+    const fltSemantics &DstTy = VTy->getScalarType()->getFltSemantics();
+    const fltSemantics &SrcTy =
+        I->getOperand(0)->getType()->getScalarType()->getFltSemantics();
+
+    Known = KnownFPClass::fpext(KnownSrc, DstTy, SrcTy);
+    FPClassTest ValidResults = DemandedMask & Known.KnownFPClasses;
+
+    return getFPClassConstant(VTy, ValidResults, /*IsCanonicalizing=*/true);
+  }
   case Instruction::Call: {
     CallInst *CI = cast<CallInst>(I);
     const Intrinsic::ID IID = CI->getIntrinsicID();
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fpext.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fpext.ll
index 4996a206a7e8f..a1f4b1e9428ac 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fpext.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fpext.ll
@@ -46,8 +46,7 @@ define nofpclass(inf norm sub zero snan) float @ret_only_qnan__fpext(half %x) {
 define nofpclass(inf norm sub zero) float @ret_only_nan__fpext(half %x) {
 ; CHECK-LABEL: define nofpclass(inf zero sub norm) float @ret_only_nan__fpext(
 ; CHECK-SAME: half [[X:%.*]]) {
-; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[X]] to float
-; CHECK-NEXT:    ret float [[RESULT]]
+; CHECK-NEXT:    ret float 0x7FF8000000000000
 ;
   %result = fpext half %x to float
   ret float %result
@@ -114,8 +113,7 @@ define nofpclass(nan) float @ret_no_nan__fpext__select_nan_or_unknown(i1 %cond,
 ; CHECK-LABEL: define nofpclass(nan) float @ret_no_nan__fpext__select_nan_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[NAN:%.*]] = call half @returns_nan_f16()
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], half [[NAN]], half [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[SELECT]] to float
+; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[UNKNOWN]] to float
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %nan = call half @returns_nan_f16()
@@ -129,8 +127,7 @@ define nofpclass(pinf) float @ret_no_pinf__fpext__select_pinf_or_unknown(i1 %con
 ; CHECK-LABEL: define nofpclass(pinf) float @ret_no_pinf__fpext__select_pinf_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[PINF:%.*]] = call half @returns_pinf_f16()
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], half [[PINF]], half [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[SELECT]] to float
+; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[UNKNOWN]] to float
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %pinf = call half @returns_pinf_f16()
@@ -144,8 +141,7 @@ define nofpclass(ninf) float @ret_no_ninf__fpext__select_ninf_or_unknown(i1 %con
 ; CHECK-LABEL: define nofpclass(ninf) float @ret_no_ninf__fpext__select_ninf_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[NINF:%.*]] = call half @returns_ninf_f16()
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], half [[NINF]], half [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[SELECT]] to float
+; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[UNKNOWN]] to float
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %ninf = call half @returns_ninf_f16()
@@ -159,8 +155,7 @@ define nofpclass(inf) float @ret_no_inf__fpext__select_inf_or_unknown(i1 %cond,
 ; CHECK-LABEL: define nofpclass(inf) float @ret_no_inf__fpext__select_inf_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[INF:%.*]] = call half @returns_inf_f16()
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], half [[INF]], half [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[SELECT]] to float
+; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[UNKNOWN]] to float
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %inf = call half @returns_inf_f16()
@@ -174,8 +169,7 @@ define nofpclass(nan inf) float @ret_no_inf_no_nan__fpext__select_inf_or_nan_or_
 ; CHECK-LABEL: define nofpclass(nan inf) float @ret_no_inf_no_nan__fpext__select_inf_or_nan_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[INF_OR_NAN:%.*]] = call half @returns_inf_or_nan_f16()
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], half [[INF_OR_NAN]], half [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[SELECT]] to float
+; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[UNKNOWN]] to float
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %inf.or.nan = call half @returns_inf_or_nan_f16()
@@ -447,8 +441,7 @@ define nofpclass(nzero) float @ret_no_nzero__fpext__select_nzero_or_unknown(i1 %
 ; CHECK-LABEL: define nofpclass(nzero) float @ret_no_nzero__fpext__select_nzero_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[NZERO:%.*]] = call half @returns_nzero_f16()
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], half [[NZERO]], half [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[SELECT]] to float
+; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[UNKNOWN]] to float
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %nzero = call half @returns_nzero_f16()
@@ -461,8 +454,7 @@ define nofpclass(pzero) float @ret_no_pzero__fpext__select_pzero_or_unknown(i1 %
 ; CHECK-LABEL: define nofpclass(pzero) float @ret_no_pzero__fpext__select_pzero_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero_f16()
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], half [[PZERO]], half [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[SELECT]] to float
+; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[UNKNOWN]] to float
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %pzero = call half @returns_pzero_f16()
@@ -475,8 +467,7 @@ define nofpclass(zero) float @ret_no_zero__fpext__select_zero_or_unknown(i1 %con
 ; CHECK-LABEL: define nofpclass(zero) float @ret_no_zero__fpext__select_zero_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[ZERO:%.*]] = call half @returns_zero_f16()
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], half [[ZERO]], half [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[SELECT]] to float
+; CHECK-NEXT:    [[RESULT:%.*]] = fpext half [[UNKNOWN]] to float
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %zero = call half @returns_zero_f16()



More information about the llvm-branch-commits mailing list