[llvm-branch-commits] [llvm] SelectionDAG: Build FP immediates from integer bits in the legalizer (PR #233767)
Matt Arsenault via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Oct 22 07:48:05 PDT 2026
https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/233767
>From 00f53ea6428b0dfac967c89b3e044c7907d77cac Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Fri, 16 Oct 2026 05:19:37 -0700
Subject: [PATCH] SelectionDAG: Build FP immediates from integer bits in the
legalizer
Teach the generic legalizer to legalize FP immediates as a bitcast of
an opaque integer constant. Migrate LoongArch to use the new helper
instead of doing it in custom lowering.
Co-authored-by: Claude Opus 5.5 <noreply at anthropic.com>
---
llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp | 40 ++++++-----
.../LoongArch/LoongArchFloat32InstrInfo.td | 4 +-
.../LoongArch/LoongArchISelLowering.cpp | 67 +++++++++----------
.../Target/LoongArch/LoongArchISelLowering.h | 5 ++
4 files changed, 65 insertions(+), 51 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
index 84bfc6aa43dc48..6c073d1290a7e3 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
@@ -202,7 +202,7 @@ class SelectionDAGLegalize {
SDValue ExpandVectorBuildThroughStack(SDNode* Node);
SDValue ExpandConcatVectors(SDNode *Node);
- SDValue ExpandConstantFP(ConstantFPSDNode *CFP, bool UseCP);
+ SDValue ExpandConstantFP(ConstantFPSDNode *CFP);
SDValue ExpandConstant(ConstantSDNode *CP);
// if ExpandNode returns false, LegalizeOp falls back to ConvertNodeToLibcall
@@ -314,10 +314,8 @@ SDValue SelectionDAGLegalize::ShuffleWithNarrowerEltType(
return DAG.getVectorShuffle(NVT, dl, N1, N2, NewMask);
}
-/// Expands the ConstantFP node to an integer constant or
-/// a load from the constant pool.
-SDValue
-SelectionDAGLegalize::ExpandConstantFP(ConstantFPSDNode *CFP, bool UseCP) {
+/// Expands the ConstantFP node to a load from the constant pool.
+SDValue SelectionDAGLegalize::ExpandConstantFP(ConstantFPSDNode *CFP) {
bool Extend = false;
SDLoc dl(CFP);
@@ -329,11 +327,6 @@ SelectionDAGLegalize::ExpandConstantFP(ConstantFPSDNode *CFP, bool UseCP) {
// fp stack or PPC FP unit).
EVT VT = CFP->getValueType(0);
ConstantFP *LLVMC = const_cast<ConstantFP*>(CFP->getConstantFPValue());
- if (!UseCP) {
- assert((VT == MVT::f64 || VT == MVT::f32) && "Invalid type expansion");
- return DAG.getConstant(LLVMC->getValueAPF().bitcastToAPInt(), dl,
- (VT == MVT::f64) ? MVT::i64 : MVT::i32);
- }
APFloat APF = CFP->getValueAPF();
EVT OrigVT = VT;
@@ -3996,11 +3989,28 @@ bool SelectionDAGLegalize::ExpandNode(SDNode *Node) {
break;
case ISD::ConstantFP: {
ConstantFPSDNode *CFP = cast<ConstantFPSDNode>(Node);
- // Check to see if this FP immediate is already legal.
- // If this is a legal constant, turn it into a TargetConstantFP node.
- if (!TLI.isFPImmLegal(CFP->getValueAPF(), Node->getValueType(0),
- DAG.shouldOptForSize()))
- Results.push_back(ExpandConstantFP(CFP, true));
+ EVT VT = Node->getValueType(0);
+ TargetLowering::FPImmAction Action =
+ TLI.getFPImmAction(CFP->getValueAPF(), VT, DAG.shouldOptForSize());
+ switch (Action) {
+ case TargetLowering::FPImmAction::Legal:
+ break;
+ case TargetLowering::FPImmAction::Bitcast: {
+ EVT IntVT = VT.changeTypeToInteger();
+ assert(TLI.isTypeLegal(IntVT) &&
+ "integer type for FP immediate bits must be legal");
+ // The constant is opaque so the bitcast is not folded back.
+ SDValue Bits =
+ DAG.getConstant(CFP->getValueAPF().bitcastToAPInt(), dl, IntVT,
+ /*isTarget=*/false, /*isOpaque=*/true);
+ Results.push_back(DAG.getBitcast(VT, Bits));
+ break;
+ }
+ case TargetLowering::FPImmAction::ConstantPool:
+ Results.push_back(ExpandConstantFP(CFP));
+ break;
+ }
+
break;
}
case ISD::Constant: {
diff --git a/llvm/lib/Target/LoongArch/LoongArchFloat32InstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchFloat32InstrInfo.td
index 3c5635942ca8c9..a69a250f27d489 100644
--- a/llvm/lib/Target/LoongArch/LoongArchFloat32InstrInfo.td
+++ b/llvm/lib/Target/LoongArch/LoongArchFloat32InstrInfo.td
@@ -278,7 +278,7 @@ def : Pat<(fdiv (loongarch_movgr2fr_w_la64 (i64 1065353216)), (fsqrt FPR32:$fj))
(FRSQRT_S FPR32:$fj)>;
} // Predicates = [HasBasicF, IsLA64]
let Predicates = [HasBasicF, IsLA32] in {
-def : Pat<(fdiv (loongarch_movgr2fr_w (i32 1065353216)), (fsqrt FPR32:$fj)),
+def : Pat<(fdiv (bitconvert (i32 1065353216)), (fsqrt FPR32:$fj)),
(FRSQRT_S FPR32:$fj)>;
} // Predicates = [HasBasicF, IsLA32]
def : Pat<(fcanonicalize FPR32:$fj), (FMAX_S $fj, $fj)>;
@@ -399,7 +399,7 @@ def : Pat<(fdiv (loongarch_movgr2fr_w_la64 (i64 1065353216)), FPR32:$src),
(FRECIP_S $src)>;
} // Predicates = [HasBasicF, IsLA64]
let Predicates = [HasBasicF, IsLA32] in {
-def : Pat<(fdiv (loongarch_movgr2fr_w (i32 1065353216)), FPR32:$src),
+def : Pat<(fdiv (bitconvert (i32 1065353216)), FPR32:$src),
(FRECIP_S $src)>;
} // Predicates = [HasBasicF, IsLA32]
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index 8d6e7a65c5449a..c3f9e2372230b6 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -952,41 +952,25 @@ SDValue LoongArchTargetLowering::lowerConstantFP(SDValue Op,
if (isFPImmVLDILegal(FPVal, VT))
return SDValue();
- // Construct as integer, and move to float register.
- APInt INTVal = FPVal.bitcastToAPInt();
-
- // If more than MaterializeFPImmInsNum instructions will be used to
- // generate the INTVal and move it to float register, fallback to
- // use floating point load from the constant pool.
- auto Seq = LoongArchMatInt::generateInstSeq(INTVal.getSExtValue());
- int InsNum = Seq.size() + ((VT == MVT::f64 && !Subtarget.is64Bit()) ? 2 : 1);
- if (InsNum > MaterializeFPImmInsNum && !FPVal.isOne())
+ // The legalizer builds immediates the size of a GPR from their bits. Fall
+ // back to a load from the constant pool for expensive immediates.
+ if (VT.getSizeInBits() == Subtarget.getGRLen() ||
+ !isFPImmCheapInGPR(FPVal, VT))
return SDValue();
- switch (VT.getSimpleVT().SimpleTy) {
- default:
- llvm_unreachable("Unexpected floating point type!");
- break;
- case MVT::f32: {
- SDValue NewVal = DAG.getConstant(INTVal, DL, MVT::i32);
- if (Subtarget.is64Bit())
- NewVal = DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, NewVal);
- return DAG.getNode(Subtarget.is64Bit() ? LoongArchISD::MOVGR2FR_W_LA64
- : LoongArchISD::MOVGR2FR_W,
- DL, VT, NewVal);
- }
- case MVT::f64: {
- if (Subtarget.is64Bit()) {
- SDValue NewVal = DAG.getConstant(INTVal, DL, MVT::i64);
- return DAG.getNode(LoongArchISD::MOVGR2FR_D, DL, VT, NewVal);
- }
- SDValue Lo = DAG.getConstant(INTVal.trunc(32), DL, MVT::i32);
- SDValue Hi = DAG.getConstant(INTVal.lshr(32).trunc(32), DL, MVT::i32);
- return DAG.getNode(LoongArchISD::MOVGR2FR_D_LO_HI, DL, VT, Lo, Hi);
- }
+ // Construct as integer, and move to float register.
+ APInt INTVal = FPVal.bitcastToAPInt();
+ if (VT == MVT::f32) {
+ assert(Subtarget.is64Bit());
+ SDValue NewVal = DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64,
+ DAG.getConstant(INTVal, DL, MVT::i32));
+ return DAG.getNode(LoongArchISD::MOVGR2FR_W_LA64, DL, VT, NewVal);
}
- return SDValue();
+ assert(VT == MVT::f64 && !Subtarget.is64Bit());
+ SDValue Lo = DAG.getConstant(INTVal.trunc(32), DL, MVT::i32);
+ SDValue Hi = DAG.getConstant(INTVal.lshr(32).trunc(32), DL, MVT::i32);
+ return DAG.getNode(LoongArchISD::MOVGR2FR_D_LO_HI, DL, VT, Lo, Hi);
}
// Ensure SETCC result and operand have the same bit width; isel does not
@@ -11088,9 +11072,24 @@ LoongArchTargetLowering::getFPImmAction(const APFloat &Imm, EVT VT,
return FPImmAction::ConstantPool;
if (VT == MVT::f64 && !Subtarget.hasBasicD())
return FPImmAction::ConstantPool;
- return (Imm.isZero() || Imm.isOne() || isFPImmVLDILegal(Imm, VT))
- ? FPImmAction::Legal
- : FPImmAction::ConstantPool;
+ if (Imm.isZero() || isFPImmVLDILegal(Imm, VT))
+ return FPImmAction::Legal;
+
+ // Immediates the size of a GPR are built from their bits. Others are built in
+ // lowerConstantFP.
+ if (VT.getSizeInBits() == Subtarget.getGRLen() && isFPImmCheapInGPR(Imm, VT))
+ return FPImmAction::Bitcast;
+ return Imm.isOne() ? FPImmAction::Legal : FPImmAction::ConstantPool;
+}
+
+bool LoongArchTargetLowering::isFPImmCheapInGPR(const APFloat &Imm,
+ EVT VT) const {
+ // Count the instructions to generate the bits and move them to a float
+ // register.
+ auto Seq =
+ LoongArchMatInt::generateInstSeq(Imm.bitcastToAPInt().getSExtValue());
+ int InsNum = Seq.size() + ((VT == MVT::f64 && !Subtarget.is64Bit()) ? 2 : 1);
+ return InsNum <= MaterializeFPImmInsNum || Imm.isOne();
}
bool LoongArchTargetLowering::isCheapToSpeculateCttz(Type *) const {
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
index d71bf3185eb9f2..29a25e07d336bf 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
@@ -158,6 +158,11 @@ class LoongArchTargetLowering : public TargetLowering {
Align &PrefAlign) const override;
bool isFPImmVLDILegal(const APFloat &Imm, EVT VT) const;
+
+ /// Returns true if building \p Imm in a GPR and moving it to a
+ /// float register is cheaper than loading it from the constant
+ /// pool.
+ bool isFPImmCheapInGPR(const APFloat &Imm, EVT VT) const;
LegalizeTypeAction getPreferredVectorAction(MVT VT) const override;
void computeKnownBitsForTargetNode(const SDValue Op, KnownBits &Known,
More information about the llvm-branch-commits
mailing list