[llvm] aa22fca - [DAG] Add initial version of SelectionDAG::computeKnownFPClass (#188790)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Mar 30 07:08:57 PDT 2026
Author: Xinlong Chen
Date: 2026-03-30T14:08:44Z
New Revision: aa22fca59a4d541def13f18a496d4ca62f239adb
URL: https://github.com/llvm/llvm-project/commit/aa22fca59a4d541def13f18a496d4ca62f239adb
DIFF: https://github.com/llvm/llvm-project/commit/aa22fca59a4d541def13f18a496d4ca62f239adb.diff
LOG: [DAG] Add initial version of SelectionDAG::computeKnownFPClass (#188790)
This patch adds an initial skeleton for `SelectionDAG::computeKnownFPClass`.
The initial version includes:
- DemandedElts wrapper and max depth early-out
- `ConstantFPSDNode` and `BUILD_VECTOR` handling
- `TargetLowering::computeKnownFPClassForTargetNode` virtual hook for backend extensions
Initial test coverage for constant scalars, BUILD_VECTOR, and max depth
early-out is added in `AArch64SelectionDAGTest.cpp`.
closes #175571
Added:
Modified:
llvm/include/llvm/CodeGen/SelectionDAG.h
llvm/include/llvm/CodeGen/TargetLowering.h
llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h
index 20846168e9a8e..c1012c940ed5b 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAG.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAG.h
@@ -40,6 +40,7 @@
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/KnownFPClass.h"
#include "llvm/Support/RecyclingAllocator.h"
#include <cassert>
#include <cstdint>
@@ -2398,6 +2399,24 @@ class SelectionDAG {
/// X|Cst == X+Cst iff X&Cst = 0.
LLVM_ABI bool isBaseWithConstantOffset(SDValue Op) const;
+ /// Determine floating-point class information about \p Op. For vectors, the
+ /// known FP classes are those shared by every demanded vector element.
+ /// \p InterestedClasses is a hint for which FP classes we care about;
+ /// the implementation may bail out early if it can determine that
+ /// none of the interested classes are possible.
+ LLVM_ABI KnownFPClass computeKnownFPClass(SDValue Op,
+ FPClassTest InterestedClasses,
+ unsigned Depth = 0) const;
+
+ /// Determine floating-point class information about \p Op. The
+ /// DemandedElts argument allows us to only collect the known FP classes
+ /// that are shared by the requested vector elements.
+ /// \p InterestedClasses is a hint for which FP classes we care about.
+ LLVM_ABI KnownFPClass computeKnownFPClass(SDValue Op,
+ const APInt &DemandedElts,
+ FPClassTest InterestedClasses,
+ unsigned Depth = 0) const;
+
/// Test whether the given SDValue (or all elements of it, if it is a
/// vector) is known to never be NaN in \p DemandedElts. If \p SNaN is true,
/// returns if \p Op is known to never be a signaling NaN (it may still be a
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 2e66f574981ab..ec76318c4af5a 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -4448,6 +4448,15 @@ class LLVM_ABI TargetLowering : public TargetLoweringBase {
/// NOTE: You must check for implicit extensions of the constant by LD.
virtual const Constant *getTargetConstantFromLoad(LoadSDNode *LD) const;
+ /// Determine floating-point class information for a target node. The
+ /// DemandedElts argument allows us to only collect the known FP classes
+ /// that are shared by the requested vector elements.
+ virtual void computeKnownFPClassForTargetNode(const SDValue Op,
+ KnownFPClass &Known,
+ const APInt &DemandedElts,
+ const SelectionDAG &DAG,
+ unsigned Depth = 0) const;
+
/// If \p SNaN is false, \returns true if \p Op is known to never be any
/// NaN. If \p sNaN is true, returns if \p Op is known to never be a signaling
/// NaN.
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 58b9106c08e06..9094e8341627a 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -64,6 +64,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/KnownBits.h"
+#include "llvm/Support/KnownFPClass.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
@@ -6050,6 +6051,79 @@ bool SelectionDAG::isBaseWithConstantOffset(SDValue Op) const {
(Op.isAnyAdd() || isADDLike(Op));
}
+KnownFPClass SelectionDAG::computeKnownFPClass(SDValue Op,
+ FPClassTest InterestedClasses,
+ unsigned Depth) const {
+ EVT VT = Op.getValueType();
+ APInt DemandedElts = VT.isFixedLengthVector()
+ ? APInt::getAllOnes(VT.getVectorNumElements())
+ : APInt(1, 1);
+ return computeKnownFPClass(Op, DemandedElts, InterestedClasses, Depth);
+}
+
+KnownFPClass SelectionDAG::computeKnownFPClass(SDValue Op,
+ const APInt &DemandedElts,
+ FPClassTest InterestedClasses,
+ unsigned Depth) const {
+ KnownFPClass Known;
+
+ if (const auto *CFP = dyn_cast<ConstantFPSDNode>(Op))
+ return KnownFPClass(CFP->getValueAPF());
+
+ if (Depth >= MaxRecursionDepth)
+ return Known;
+
+ if (Op.getOpcode() == ISD::UNDEF)
+ return Known;
+
+ EVT VT = Op.getValueType();
+ assert((!VT.isFixedLengthVector() ||
+ DemandedElts.getBitWidth() == VT.getVectorNumElements()) &&
+ "Unexpected vector size");
+
+ if (!DemandedElts)
+ return Known;
+
+ unsigned Opcode = Op.getOpcode();
+ switch (Opcode) {
+ case ISD::POISON: {
+ Known.KnownFPClasses = fcNone;
+ Known.SignBit = false;
+ break;
+ }
+ case ISD::BUILD_VECTOR: {
+ assert(!VT.isScalableVector());
+ bool First = true;
+ for (unsigned I = 0, E = Op.getNumOperands(); I != E; ++I) {
+ if (!DemandedElts[I])
+ continue;
+
+ if (First) {
+ Known =
+ computeKnownFPClass(Op.getOperand(I), InterestedClasses, Depth + 1);
+ First = false;
+ } else {
+ Known |=
+ computeKnownFPClass(Op.getOperand(I), InterestedClasses, Depth + 1);
+ }
+
+ if (Known.isUnknown())
+ break;
+ }
+ break;
+ }
+ default:
+ if (Opcode >= ISD::BUILTIN_OP_END || Opcode == ISD::INTRINSIC_WO_CHAIN ||
+ Opcode == ISD::INTRINSIC_W_CHAIN || Opcode == ISD::INTRINSIC_VOID) {
+ TLI->computeKnownFPClassForTargetNode(Op, Known, DemandedElts, *this,
+ Depth);
+ }
+ break;
+ }
+
+ return Known;
+}
+
bool SelectionDAG::isKnownNeverNaN(SDValue Op, bool SNaN,
unsigned Depth) const {
EVT VT = Op.getValueType();
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index 2c8926167b6da..30ad097f85596 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -4065,6 +4065,19 @@ bool TargetLowering::canCreateUndefOrPoisonForTargetNode(
return true;
}
+void TargetLowering::computeKnownFPClassForTargetNode(const SDValue Op,
+ KnownFPClass &Known,
+ const APInt &DemandedElts,
+ const SelectionDAG &DAG,
+ unsigned Depth) const {
+ assert((Op.getOpcode() >= ISD::BUILTIN_OP_END ||
+ Op.getOpcode() == ISD::INTRINSIC_WO_CHAIN ||
+ Op.getOpcode() == ISD::INTRINSIC_W_CHAIN ||
+ Op.getOpcode() == ISD::INTRINSIC_VOID) &&
+ "Should use computeKnownFPClass if you don't know whether Op"
+ " is a target node!");
+}
+
bool TargetLowering::isKnownNeverNaNForTargetNode(SDValue Op,
const APInt &DemandedElts,
const SelectionDAG &DAG,
diff --git a/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp b/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp
index b0c48e8c97995..12b7763274f6c 100644
--- a/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp
+++ b/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp
@@ -16,6 +16,7 @@
#include "llvm/IR/Module.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/KnownBits.h"
+#include "llvm/Support/KnownFPClass.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
@@ -1574,4 +1575,130 @@ TEST_F(AArch64SelectionDAGTest, KnownNeverZero_Select) {
EXPECT_FALSE(DAG->isKnownNeverZero(VSelect444Big, DemandAll));
EXPECT_TRUE(DAG->isKnownNeverZero(VSelect4444, DemandAll));
}
+
+// tests for SelectionDAG::computeKnownFPClass
+TEST_F(AArch64SelectionDAGTest, ComputeKnownFPClass_ConstantScalar) {
+ SDLoc Loc;
+
+ SDValue PosZero = DAG->getConstantFP(APFloat::getZero(APFloat::IEEEsingle()),
+ Loc, MVT::f32);
+ KnownFPClass Known = DAG->computeKnownFPClass(PosZero, fcAllFlags);
+ EXPECT_EQ(Known.KnownFPClasses, fcPosZero);
+ EXPECT_TRUE(Known.SignBit.has_value());
+ EXPECT_FALSE(*Known.SignBit);
+
+ SDValue NegZero = DAG->getConstantFP(
+ APFloat::getZero(APFloat::IEEEsingle(), true), Loc, MVT::f32);
+ Known = DAG->computeKnownFPClass(NegZero, fcAllFlags);
+ EXPECT_EQ(Known.KnownFPClasses, fcNegZero);
+ EXPECT_TRUE(Known.SignBit.has_value());
+ EXPECT_TRUE(*Known.SignBit);
+
+ SDValue PosInf =
+ DAG->getConstantFP(APFloat::getInf(APFloat::IEEEsingle()), Loc, MVT::f32);
+ Known = DAG->computeKnownFPClass(PosInf, fcAllFlags);
+ EXPECT_EQ(Known.KnownFPClasses, fcPosInf);
+ EXPECT_TRUE(Known.SignBit.has_value());
+ EXPECT_FALSE(*Known.SignBit);
+
+ SDValue QNaN = DAG->getConstantFP(APFloat::getQNaN(APFloat::IEEEsingle()),
+ Loc, MVT::f32);
+ Known = DAG->computeKnownFPClass(QNaN, fcAllFlags);
+ EXPECT_EQ(Known.KnownFPClasses, fcQNan);
+
+ SDValue One = DAG->getConstantFP(1.0, Loc, MVT::f32);
+ Known = DAG->computeKnownFPClass(One, fcAllFlags);
+ EXPECT_EQ(Known.KnownFPClasses, fcPosNormal);
+ EXPECT_TRUE(Known.SignBit.has_value());
+ EXPECT_FALSE(*Known.SignBit);
+}
+
+TEST_F(AArch64SelectionDAGTest, ComputeKnownFPClass_BuildVector) {
+ SDLoc Loc;
+
+ SDValue PosOne = DAG->getConstantFP(1.0, Loc, MVT::f32);
+ SDValue PosTwo = DAG->getConstantFP(2.0, Loc, MVT::f32);
+ SDValue NegOne = DAG->getConstantFP(-1.0, Loc, MVT::f32);
+
+ EVT VecVT = MVT::v2f32;
+
+ SDValue VecPosPos = DAG->getBuildVector(VecVT, Loc, {PosOne, PosTwo});
+ KnownFPClass Known = DAG->computeKnownFPClass(VecPosPos, fcAllFlags);
+ EXPECT_EQ(Known.KnownFPClasses, fcPosNormal);
+ EXPECT_TRUE(Known.SignBit.has_value());
+ EXPECT_FALSE(*Known.SignBit);
+
+ SDValue VecPosNeg = DAG->getBuildVector(VecVT, Loc, {PosOne, NegOne});
+ Known = DAG->computeKnownFPClass(VecPosNeg, fcAllFlags);
+ EXPECT_EQ(Known.KnownFPClasses, fcPosNormal | fcNegNormal);
+ EXPECT_FALSE(Known.SignBit.has_value());
+}
+
+TEST_F(AArch64SelectionDAGTest, ComputeKnownFPClass_DemandedElts) {
+ SDLoc Loc;
+
+ SDValue PosOne = DAG->getConstantFP(1.0, Loc, MVT::f32);
+ SDValue NegOne = DAG->getConstantFP(-1.0, Loc, MVT::f32);
+ EVT VecVT = MVT::v2f32;
+ SDValue Vec = DAG->getBuildVector(VecVT, Loc, {PosOne, NegOne});
+
+ APInt DemandLo(2, 1);
+ KnownFPClass Known = DAG->computeKnownFPClass(Vec, DemandLo, fcAllFlags);
+ EXPECT_EQ(Known.KnownFPClasses, fcPosNormal);
+ EXPECT_TRUE(Known.SignBit.has_value());
+ EXPECT_FALSE(*Known.SignBit);
+
+ APInt DemandHi(2, 2);
+ Known = DAG->computeKnownFPClass(Vec, DemandHi, fcAllFlags);
+ EXPECT_EQ(Known.KnownFPClasses, fcNegNormal);
+ EXPECT_TRUE(Known.SignBit.has_value());
+ EXPECT_TRUE(*Known.SignBit);
+
+ APInt DemandAll(2, 3);
+ Known = DAG->computeKnownFPClass(Vec, DemandAll, fcAllFlags);
+ EXPECT_EQ(Known.KnownFPClasses, fcPosNormal | fcNegNormal);
+ EXPECT_FALSE(Known.SignBit.has_value());
+
+ APInt DemandNone(2, 0);
+ Known = DAG->computeKnownFPClass(Vec, DemandNone, fcAllFlags);
+ EXPECT_EQ(Known.KnownFPClasses, fcAllFlags);
+ EXPECT_FALSE(Known.SignBit.has_value());
+}
+
+TEST_F(AArch64SelectionDAGTest, ComputeKnownFPClass_MaxDepth) {
+ SDLoc Loc;
+
+ SDValue PosOne = DAG->getConstantFP(1.0, Loc, MVT::f32);
+ SDValue PosTwo = DAG->getConstantFP(2.0, Loc, MVT::f32);
+ EVT VecVT = MVT::v2f32;
+ SDValue Vec = DAG->getBuildVector(VecVT, Loc, {PosOne, PosTwo});
+
+ // At depth 0, BUILD_VECTOR of constants is fully analyzed.
+ KnownFPClass Known = DAG->computeKnownFPClass(Vec, fcAllFlags, /*Depth=*/0);
+ EXPECT_EQ(Known.KnownFPClasses, fcPosNormal);
+
+ // At MaxRecursionDepth, the non-constant node bails out as unknown.
+ Known = DAG->computeKnownFPClass(Vec, fcAllFlags,
+ SelectionDAG::MaxRecursionDepth);
+ EXPECT_EQ(Known.KnownFPClasses, fcAllFlags);
+ EXPECT_FALSE(Known.SignBit.has_value());
+}
+
+TEST_F(AArch64SelectionDAGTest, ComputeKnownFPClass_UndefAndPoison) {
+ SDLoc Loc;
+
+ // UNDEF is unknown — could be any FP class.
+ SDValue Undef = DAG->getUNDEF(MVT::f32);
+ KnownFPClass Known = DAG->computeKnownFPClass(Undef, fcAllFlags);
+ EXPECT_EQ(Known.KnownFPClasses, fcAllFlags);
+ EXPECT_FALSE(Known.SignBit.has_value());
+
+ // POISON is fcNone — can be assumed to never be observed.
+ SDValue Poison = DAG->getPOISON(MVT::f32);
+ Known = DAG->computeKnownFPClass(Poison, fcAllFlags);
+ EXPECT_EQ(Known.KnownFPClasses, fcNone);
+ EXPECT_TRUE(Known.SignBit.has_value());
+ EXPECT_FALSE(*Known.SignBit);
+}
+
} // end namespace llvm
More information about the llvm-commits
mailing list