[llvm] [Hexagon] Fix ISel error: LLVM: cannot select -fmaximum (PR #211283)
Fateme Hosseini via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 22 08:05:25 PDT 2026
https://github.com/fhossein-quic created https://github.com/llvm/llvm-project/pull/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.
>From 8dd548d99b265ab2b407222618e0ffa196f12879 Mon Sep 17 00:00:00 2001
From: Fateme Hosseini <fhossein at qti.qualcomm.com>
Date: Tue, 21 Jul 2026 13:31:08 -0700
Subject: [PATCH] [Hexagon] Fix ISel error: LLVM: cannot select -fmaximum
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.
---
.../Target/Hexagon/HexagonISelLowering.cpp | 45 ++++++++++++++
llvm/lib/Target/Hexagon/HexagonISelLowering.h | 1 +
llvm/test/CodeGen/Hexagon/fmaximum.ll | 59 +++++++++++++++++++
llvm/test/CodeGen/Hexagon/fminimum.ll | 59 +++++++++++++++++++
4 files changed, 164 insertions(+)
create mode 100644 llvm/test/CodeGen/Hexagon/fmaximum.ll
create mode 100644 llvm/test/CodeGen/Hexagon/fminimum.ll
diff --git a/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp b/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp
index 0ac657f51724b..1e23e5a4fa08a 100644
--- a/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp
@@ -803,6 +803,44 @@ 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);
+ }
+ }
+ 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 +1848,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 +3367,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 53aff87091775..a4a0e18495294 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..0de24a71289d7
--- /dev/null
+++ b/llvm/test/CodeGen/Hexagon/fmaximum.ll
@@ -0,0 +1,59 @@
+; 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: = sfmax({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+
+
+; 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: = sfmax({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+
+
+; 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: = sfmax({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+
+
+; 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: = sfmax({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+
+
+; 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: = sfmax({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+
+
+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..8392444bc6b36
--- /dev/null
+++ b/llvm/test/CodeGen/Hexagon/fminimum.ll
@@ -0,0 +1,59 @@
+; 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: = sfmin({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+
+
+; 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: = sfmin({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+
+
+; 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: = sfmin({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+
+
+; 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: = sfmin({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+
+
+; 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: = sfmin({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+; CHECK: = sfcmp.uo({{.*}},{{.*}})
+
+
+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