[llvm] f30df09 - [Hexagon] Fix ISel error: LLVM: cannot select -fmaximum (#211283)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 30 07:04:43 PDT 2026


Author: Fateme Hosseini
Date: 2026-07-30T09:04:38-05:00
New Revision: f30df0953917256142cfbe1172e22ebbca8204b3

URL: https://github.com/llvm/llvm-project/commit/f30df0953917256142cfbe1172e22ebbca8204b3
DIFF: https://github.com/llvm/llvm-project/commit/f30df0953917256142cfbe1172e22ebbca8204b3.diff

LOG: [Hexagon] Fix ISel error: LLVM: cannot select -fmaximum (#211283)

This patch adds a custom lowering for FMAXIMUM.
FMAXIMUM is NaN-propagating, and currently, we
don't have any hexagon instruction to support
that behaviour. The patch is also extended to
cover FMINIMUM as well.

Check if any of the inputs are NaN. If so,
propagate the NaN to the output, otherwise
return the maximum/minimum of the inputs,
using ISD::FMINNUM/ISD::FMAXNUM to run
Hexagon's F2_sfmin/F2_sfmax when no operand
is NaN. NaN is propagated (rather than
replaced with a new ConstantFP NaN node) to
avoid triggering a wrong C2_MUX selection in
ISD::SELECT.

Added: 
    llvm/test/CodeGen/Hexagon/fmaximum.ll
    llvm/test/CodeGen/Hexagon/fminimum.ll

Modified: 
    llvm/lib/Target/Hexagon/HexagonISelLowering.cpp
    llvm/lib/Target/Hexagon/HexagonISelLowering.h

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp b/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp
index 0ac657f51724b..0e1b4d72eb03f 100644
--- a/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp
@@ -803,6 +803,45 @@ HexagonTargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op,
   return AA;
 }
 
+SDValue HexagonTargetLowering::LowerFMINFMAX(SDValue Op,
+                                             SelectionDAG &DAG) const {
+  EVT OpVT = Op.getValueType();
+  MVT SimpleVT = OpVT.getSimpleVT();
+  SDLoc DL(Op);
+
+  // Check if any of the inputs are NaN. If so, propagate the NaN
+  // to the output, otherwise return the maximum/minimum of the inputs.
+  // We can safely use ISD::FMINNUM/ISD::FMAXNUM to run
+  // Hexagon's F2_sfmin/F2_sfmax, when no operand is NaN.
+  // Note: We cannot directly compare nodes against NaN node to find NaNs,
+  // because comparing NaN with anything always returns False (except for !=0
+  // which always return True). To work around that, we compare input operands
+  // with themselves under ISD::SETUO, which only returns true if the operand is
+  // NaN.
+
+  SDValue Op1 = Op.getOperand(0);
+  SDValue Op2 = Op.getOperand(1);
+  SDValue isOp1NaN = DAG.getSetCC(DL, MVT::i1, Op1, Op1, ISD::SETUO);
+  SDValue isOp2NaN = DAG.getSetCC(DL, MVT::i1, Op2, Op2, ISD::SETUO);
+
+  switch (Op.getOpcode()) {
+  case ISD::FMAXIMUM: {
+    SDValue FmaxNode = DAG.getNode(ISD::FMAXNUM, DL, SimpleVT, Op1, Op2);
+    SDValue result =
+        DAG.getNode(ISD::SELECT, DL, SimpleVT, isOp2NaN, Op2, FmaxNode);
+    return DAG.getNode(ISD::SELECT, DL, SimpleVT, isOp1NaN, Op1, result);
+  }
+  case ISD::FMINIMUM: {
+    SDValue FminNode = DAG.getNode(ISD::FMINNUM, DL, SimpleVT, Op1, Op2);
+    SDValue result =
+        DAG.getNode(ISD::SELECT, DL, SimpleVT, isOp2NaN, Op2, FminNode);
+    return DAG.getNode(ISD::SELECT, DL, SimpleVT, isOp1NaN, Op1, result);
+  }
+  default:
+    llvm_unreachable("Invalid opcode for LowerFMINFMAX");
+  }
+}
+
 SDValue HexagonTargetLowering::LowerFormalArguments(
     SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
     const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &dl,
@@ -1810,6 +1849,10 @@ HexagonTargetLowering::HexagonTargetLowering(const TargetMachine &TM,
   setOperationAction(ISD::FMAXIMUMNUM, MVT::f32, Legal);
   setOperationAction(ISD::FMINNUM, MVT::f32, Legal);
   setOperationAction(ISD::FMAXNUM, MVT::f32, Legal);
+  setOperationAction(ISD::FMAXIMUM, MVT::f32, Custom);
+  setOperationAction(ISD::FMAXIMUM, MVT::f16, Custom);
+  setOperationAction(ISD::FMINIMUM, MVT::f32, Custom);
+  setOperationAction(ISD::FMINIMUM, MVT::f16, Custom);
 
   setOperationAction(ISD::FP_TO_UINT, MVT::i1,  Promote);
   setOperationAction(ISD::FP_TO_UINT, MVT::i8,  Promote);
@@ -3325,6 +3368,9 @@ HexagonTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
     case ISD::INTRINSIC_VOID:       return LowerINTRINSIC_VOID(Op, DAG);
     case ISD::PREFETCH:
       return LowerPREFETCH(Op, DAG);
+    case ISD::FMAXIMUM:
+    case ISD::FMINIMUM:
+      return LowerFMINFMAX(Op, DAG);
       break;
   }
 

diff  --git a/llvm/lib/Target/Hexagon/HexagonISelLowering.h b/llvm/lib/Target/Hexagon/HexagonISelLowering.h
index 9ed6e95e3c44f..cf2263fdc2ad8 100644
--- a/llvm/lib/Target/Hexagon/HexagonISelLowering.h
+++ b/llvm/lib/Target/Hexagon/HexagonISelLowering.h
@@ -117,6 +117,7 @@ class HexagonTargetLowering : public TargetLowering {
   SDValue LowerUAddSubOCarry(SDValue Op, SelectionDAG &DAG) const;
 
   SDValue LowerDYNAMIC_STACKALLOC(SDValue Op, SelectionDAG &DAG) const;
+  SDValue LowerFMINFMAX(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerINLINEASM(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerFDIV(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerPREFETCH(SDValue Op, SelectionDAG &DAG) const;

diff  --git a/llvm/test/CodeGen/Hexagon/fmaximum.ll b/llvm/test/CodeGen/Hexagon/fmaximum.ll
new file mode 100644
index 0000000000000..6a7c6a91946c5
--- /dev/null
+++ b/llvm/test/CodeGen/Hexagon/fmaximum.ll
@@ -0,0 +1,72 @@
+; RUN: llc -O0 -march=hexagon < %s | FileCheck %s
+
+; CHECK-LABEL: fmaximum_vec32f32
+define float @fmaximum_vec32f32(<32 x float> %vec) {
+  %res = call float @llvm.vector.reduce.fmaximum.v32f32(<32 x float> %vec)
+  ret float %res
+}
+; CHECK: r{{[0-9]+}} = sfmax(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+
+
+; CHECK-LABEL: fmaximum_vec2f16
+define half @fmaximum_vec2f16(<2 x half> %vec) {
+  %res = call half @llvm.vector.reduce.fmaximum.v2f16(<2 x half> %vec)
+  ret half %res
+}
+; CHECK: call __extendhfsf2
+; CHECK: r{{[0-9]+}} = sfmax(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+
+
+; CHECK-LABEL: fmaximum_vec64f16
+define half @fmaximum_vec64f16(<64 x half> %vec) {
+  %res = call half @llvm.vector.reduce.fmaximum.v64f16(<64 x half> %vec)
+  ret half %res
+}
+; CHECK: call __extendhfsf2
+; CHECK: r{{[0-9]+}} = sfmax(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+
+
+; CHECK-LABEL: fmaximum_float
+define float @fmaximum_float(float %a, float %b) {
+  %res = call float @llvm.maximum.f32(float %a, float %b)
+  ret float %res
+}
+; CHECK: r{{[0-9]+}} = sfmax(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+
+
+; CHECK-LABEL: fmaximum_half
+define half @fmaximum_half(half %a, half %b) {
+  %res = call half @llvm.maximum.f16(half %a, half %b)
+  ret half %res
+}
+; CHECK: call __extendhfsf2
+; CHECK: r{{[0-9]+}} = sfmax(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+
+
+declare float @llvm.vector.reduce.fmaximum.v32f32(<32 x float> %vec) #0
+declare half @llvm.vector.reduce.fmaximum.v2f16(<2 x half> %vec) #0
+declare half @llvm.vector.reduce.fmaximum.v64f16(<64 x half> %vec) #0
+declare float @llvm.maximum.f32(float %a, float %b) #0
+declare half @llvm.maximum.f16(half %a, half %b) #0
+
+attributes #0 = { nounwind "target-cpu"="hexagonv75" }

diff  --git a/llvm/test/CodeGen/Hexagon/fminimum.ll b/llvm/test/CodeGen/Hexagon/fminimum.ll
new file mode 100644
index 0000000000000..5b5ad5ffb9552
--- /dev/null
+++ b/llvm/test/CodeGen/Hexagon/fminimum.ll
@@ -0,0 +1,72 @@
+; RUN: llc -O0 -march=hexagon < %s | FileCheck %s
+
+; CHECK-LABEL: fminimum_vec32f32
+define float @fminimum_vec32f32(<32 x float> %vec) {
+  %res = call float @llvm.vector.reduce.fminimum.v32f32(<32 x float> %vec)
+  ret float %res
+}
+; CHECK: r{{[0-9]+}} = sfmin(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+
+
+; CHECK-LABEL: fminimum_vec2f16
+define half @fminimum_vec2f16(<2 x half> %vec) {
+  %res = call half @llvm.vector.reduce.fminimum.v2f16(<2 x half> %vec)
+  ret half %res
+}
+; CHECK: call __extendhfsf2
+; CHECK: r{{[0-9]+}} = sfmin(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+
+
+; CHECK-LABEL: fminimum_vec64f16
+define half @fminimum_vec64f16(<64 x half> %vec) {
+  %res = call half @llvm.vector.reduce.fminimum.v64f16(<64 x half> %vec)
+  ret half %res
+}
+; CHECK: call __extendhfsf2
+; CHECK: r{{[0-9]+}} = sfmin(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+
+
+; CHECK-LABEL: fminimum_float
+define float @fminimum_float(float %a, float %b) {
+  %res = call float @llvm.minimum.f32(float %a, float %b)
+  ret float %res
+}
+; CHECK: r{{[0-9]+}} = sfmin(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+
+
+; CHECK-LABEL: fminimum_half
+define half @fminimum_half(half %a, half %b) {
+  %res = call half @llvm.minimum.f16(half %a, half %b)
+  ret half %res
+}
+; CHECK: call __extendhfsf2
+; CHECK: r{{[0-9]+}} = sfmin(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: p{{[0-9]+}} = sfcmp.uo(r{{[0-9]+}},r{{[0-9]+}})
+; CHECK: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
+
+
+declare float @llvm.vector.reduce.fminimum.v32f32(<32 x float> %vec) #0
+declare half @llvm.vector.reduce.fminimum.v2f16(<2 x half> %vec) #0
+declare half @llvm.vector.reduce.fminimum.v64f16(<64 x half> %vec) #0
+declare float @llvm.minimum.f32(float %a, float %b) #0
+declare half @llvm.minimum.f16(half %a, half %b) #0
+
+attributes #0 = { nounwind "target-cpu"="hexagonv75" }


        


More information about the llvm-commits mailing list