[llvm] [VectorCombine] foldShuffleChainsToReduce - add fmaximum/fminimum handling (PR #200818)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 1 06:42:09 PDT 2026
https://github.com/lijinpei-amd created https://github.com/llvm/llvm-project/pull/200818
Extend foldShuffleChainsToReduce to fold shuffle-reduction chains of the floating-point maximum/minimum intrinsics into the corresponding vector reductions:
llvm.maximum -> llvm.vector.reduce.fmaximum
llvm.minimum -> llvm.vector.reduce.fminimum
Addresses #199038
>From edac07cce988c537fa6b9dc69e1b34f3ef665545 Mon Sep 17 00:00:00 2001
From: Li Jinpei <jinpli at amd.com>
Date: Mon, 1 Jun 2026 20:15:36 +0800
Subject: [PATCH] [VectorCombine] foldShuffleChainsToReduce - add
fmaximum/fminimum handling
Extend foldShuffleChainsToReduce to fold shuffle-reduction chains of the
floating-point maximum/minimum intrinsics into the corresponding vector
reductions:
llvm.maximum -> llvm.vector.reduce.fmaximum
llvm.minimum -> llvm.vector.reduce.fminimum
Addresses #199038
---
llvm/lib/Transforms/Utils/LoopUtils.cpp | 4 +
.../Transforms/Vectorize/VectorCombine.cpp | 4 +-
.../fold-shuffle-chains-to-reduce.ll | 123 ++++++++++++++++++
3 files changed, 130 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/Utils/LoopUtils.cpp b/llvm/lib/Transforms/Utils/LoopUtils.cpp
index f0586e4f0f464..138005f6d4ee3 100644
--- a/llvm/lib/Transforms/Utils/LoopUtils.cpp
+++ b/llvm/lib/Transforms/Utils/LoopUtils.cpp
@@ -1153,6 +1153,10 @@ Intrinsic::ID llvm::getMinMaxReductionIntrinsicID(Intrinsic::ID IID) {
return Intrinsic::vector_reduce_smin;
case Intrinsic::smax:
return Intrinsic::vector_reduce_smax;
+ case Intrinsic::minimum:
+ return Intrinsic::vector_reduce_fminimum;
+ case Intrinsic::maximum:
+ return Intrinsic::vector_reduce_fmaximum;
}
}
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index da8a52a6b3dca..d1d7ecddcb396 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -4029,7 +4029,9 @@ bool VectorCombine::foldShuffleChainsToReduce(Instruction &I) {
case Intrinsic::umin:
case Intrinsic::umax:
case Intrinsic::smin:
- case Intrinsic::smax: {
+ case Intrinsic::smax:
+ case Intrinsic::maximum:
+ case Intrinsic::minimum: {
auto *Op0 = II->getOperand(0);
auto *Op1 = II->getOperand(1);
PrevVecV[0] = Op0;
diff --git a/llvm/test/Transforms/VectorCombine/fold-shuffle-chains-to-reduce.ll b/llvm/test/Transforms/VectorCombine/fold-shuffle-chains-to-reduce.ll
index e4cb41b238cf1..0f6bdbf8f1db9 100644
--- a/llvm/test/Transforms/VectorCombine/fold-shuffle-chains-to-reduce.ll
+++ b/llvm/test/Transforms/VectorCombine/fold-shuffle-chains-to-reduce.ll
@@ -194,6 +194,129 @@ define i16 @test_reduce_v6i16_xor_neg(<6 x i16> %a0) {
ret i16 %7
}
+define float @test_reduce_v8f32_fmaximum(<8 x float> %a0) {
+; CHECK-LABEL: define float @test_reduce_v8f32_fmaximum(
+; CHECK-SAME: <8 x float> [[A0:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.vector.reduce.fmaximum.v8f32(<8 x float> [[A0]])
+; CHECK-NEXT: ret float [[TMP1]]
+;
+ %1 = shufflevector <8 x float> %a0, <8 x float> poison, <8 x i32> <i32 4, i32 5, i32 6, i32 7, i32 poison, i32 poison, i32 poison, i32 poison>
+ %2 = tail call <8 x float> @llvm.maximum.v8f32(<8 x float> %a0, <8 x float> %1)
+ %3 = shufflevector <8 x float> %2, <8 x float> poison, <8 x i32> <i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %4 = tail call <8 x float> @llvm.maximum.v8f32(<8 x float> %2, <8 x float> %3)
+ %5 = shufflevector <8 x float> %4, <8 x float> poison, <8 x i32> <i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %6 = tail call <8 x float> @llvm.maximum.v8f32(<8 x float> %4, <8 x float> %5)
+ %7 = extractelement <8 x float> %6, i64 0
+ ret float %7
+}
+
+define float @test_reduce_v8f32_fminimum(<8 x float> %a0) {
+; CHECK-LABEL: define float @test_reduce_v8f32_fminimum(
+; CHECK-SAME: <8 x float> [[A0:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.vector.reduce.fminimum.v8f32(<8 x float> [[A0]])
+; CHECK-NEXT: ret float [[TMP1]]
+;
+ %1 = shufflevector <8 x float> %a0, <8 x float> poison, <8 x i32> <i32 4, i32 5, i32 6, i32 7, i32 poison, i32 poison, i32 poison, i32 poison>
+ %2 = tail call <8 x float> @llvm.minimum.v8f32(<8 x float> %a0, <8 x float> %1)
+ %3 = shufflevector <8 x float> %2, <8 x float> poison, <8 x i32> <i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %4 = tail call <8 x float> @llvm.minimum.v8f32(<8 x float> %2, <8 x float> %3)
+ %5 = shufflevector <8 x float> %4, <8 x float> poison, <8 x i32> <i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %6 = tail call <8 x float> @llvm.minimum.v8f32(<8 x float> %4, <8 x float> %5)
+ %7 = extractelement <8 x float> %6, i64 0
+ ret float %7
+}
+
+; Negative tests: maxnum/minnum/maximumnum/minimumnum reductions map to
+; llvm.vector.reduce.fmax/fmin, whose comparison order is non-deterministic for
+; signaling-NaN inputs, so these chains are intentionally not folded.
+define float @test_reduce_v8f32_maxnum_neg(<8 x float> %a0) {
+; CHECK-LABEL: define float @test_reduce_v8f32_maxnum_neg(
+; CHECK-SAME: <8 x float> [[A0:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x float> [[A0]], <8 x float> poison, <8 x i32> <i32 4, i32 5, i32 6, i32 7, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP2:%.*]] = tail call <8 x float> @llvm.maxnum.v8f32(<8 x float> [[A0]], <8 x float> [[TMP1]])
+; CHECK-NEXT: [[TMP3:%.*]] = shufflevector <8 x float> [[TMP2]], <8 x float> poison, <8 x i32> <i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP4:%.*]] = tail call <8 x float> @llvm.maxnum.v8f32(<8 x float> [[TMP2]], <8 x float> [[TMP3]])
+; CHECK-NEXT: [[TMP5:%.*]] = shufflevector <8 x float> [[TMP4]], <8 x float> poison, <8 x i32> <i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP6:%.*]] = tail call <8 x float> @llvm.maxnum.v8f32(<8 x float> [[TMP4]], <8 x float> [[TMP5]])
+; CHECK-NEXT: [[TMP7:%.*]] = extractelement <8 x float> [[TMP6]], i64 0
+; CHECK-NEXT: ret float [[TMP7]]
+;
+ %1 = shufflevector <8 x float> %a0, <8 x float> poison, <8 x i32> <i32 4, i32 5, i32 6, i32 7, i32 poison, i32 poison, i32 poison, i32 poison>
+ %2 = tail call <8 x float> @llvm.maxnum.v8f32(<8 x float> %a0, <8 x float> %1)
+ %3 = shufflevector <8 x float> %2, <8 x float> poison, <8 x i32> <i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %4 = tail call <8 x float> @llvm.maxnum.v8f32(<8 x float> %2, <8 x float> %3)
+ %5 = shufflevector <8 x float> %4, <8 x float> poison, <8 x i32> <i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %6 = tail call <8 x float> @llvm.maxnum.v8f32(<8 x float> %4, <8 x float> %5)
+ %7 = extractelement <8 x float> %6, i64 0
+ ret float %7
+}
+
+define float @test_reduce_v8f32_minnum_neg(<8 x float> %a0) {
+; CHECK-LABEL: define float @test_reduce_v8f32_minnum_neg(
+; CHECK-SAME: <8 x float> [[A0:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x float> [[A0]], <8 x float> poison, <8 x i32> <i32 4, i32 5, i32 6, i32 7, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP2:%.*]] = tail call <8 x float> @llvm.minnum.v8f32(<8 x float> [[A0]], <8 x float> [[TMP1]])
+; CHECK-NEXT: [[TMP3:%.*]] = shufflevector <8 x float> [[TMP2]], <8 x float> poison, <8 x i32> <i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP4:%.*]] = tail call <8 x float> @llvm.minnum.v8f32(<8 x float> [[TMP2]], <8 x float> [[TMP3]])
+; CHECK-NEXT: [[TMP5:%.*]] = shufflevector <8 x float> [[TMP4]], <8 x float> poison, <8 x i32> <i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP6:%.*]] = tail call <8 x float> @llvm.minnum.v8f32(<8 x float> [[TMP4]], <8 x float> [[TMP5]])
+; CHECK-NEXT: [[TMP7:%.*]] = extractelement <8 x float> [[TMP6]], i64 0
+; CHECK-NEXT: ret float [[TMP7]]
+;
+ %1 = shufflevector <8 x float> %a0, <8 x float> poison, <8 x i32> <i32 4, i32 5, i32 6, i32 7, i32 poison, i32 poison, i32 poison, i32 poison>
+ %2 = tail call <8 x float> @llvm.minnum.v8f32(<8 x float> %a0, <8 x float> %1)
+ %3 = shufflevector <8 x float> %2, <8 x float> poison, <8 x i32> <i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %4 = tail call <8 x float> @llvm.minnum.v8f32(<8 x float> %2, <8 x float> %3)
+ %5 = shufflevector <8 x float> %4, <8 x float> poison, <8 x i32> <i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %6 = tail call <8 x float> @llvm.minnum.v8f32(<8 x float> %4, <8 x float> %5)
+ %7 = extractelement <8 x float> %6, i64 0
+ ret float %7
+}
+
+define float @test_reduce_v8f32_maximumnum_neg(<8 x float> %a0) {
+; CHECK-LABEL: define float @test_reduce_v8f32_maximumnum_neg(
+; CHECK-SAME: <8 x float> [[A0:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x float> [[A0]], <8 x float> poison, <8 x i32> <i32 4, i32 5, i32 6, i32 7, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP2:%.*]] = tail call <8 x float> @llvm.maximumnum.v8f32(<8 x float> [[A0]], <8 x float> [[TMP1]])
+; CHECK-NEXT: [[TMP3:%.*]] = shufflevector <8 x float> [[TMP2]], <8 x float> poison, <8 x i32> <i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP4:%.*]] = tail call <8 x float> @llvm.maximumnum.v8f32(<8 x float> [[TMP2]], <8 x float> [[TMP3]])
+; CHECK-NEXT: [[TMP5:%.*]] = shufflevector <8 x float> [[TMP4]], <8 x float> poison, <8 x i32> <i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP6:%.*]] = tail call <8 x float> @llvm.maximumnum.v8f32(<8 x float> [[TMP4]], <8 x float> [[TMP5]])
+; CHECK-NEXT: [[TMP7:%.*]] = extractelement <8 x float> [[TMP6]], i64 0
+; CHECK-NEXT: ret float [[TMP7]]
+;
+ %1 = shufflevector <8 x float> %a0, <8 x float> poison, <8 x i32> <i32 4, i32 5, i32 6, i32 7, i32 poison, i32 poison, i32 poison, i32 poison>
+ %2 = tail call <8 x float> @llvm.maximumnum.v8f32(<8 x float> %a0, <8 x float> %1)
+ %3 = shufflevector <8 x float> %2, <8 x float> poison, <8 x i32> <i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %4 = tail call <8 x float> @llvm.maximumnum.v8f32(<8 x float> %2, <8 x float> %3)
+ %5 = shufflevector <8 x float> %4, <8 x float> poison, <8 x i32> <i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %6 = tail call <8 x float> @llvm.maximumnum.v8f32(<8 x float> %4, <8 x float> %5)
+ %7 = extractelement <8 x float> %6, i64 0
+ ret float %7
+}
+
+define float @test_reduce_v8f32_minimumnum_neg(<8 x float> %a0) {
+; CHECK-LABEL: define float @test_reduce_v8f32_minimumnum_neg(
+; CHECK-SAME: <8 x float> [[A0:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x float> [[A0]], <8 x float> poison, <8 x i32> <i32 4, i32 5, i32 6, i32 7, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP2:%.*]] = tail call <8 x float> @llvm.minimumnum.v8f32(<8 x float> [[A0]], <8 x float> [[TMP1]])
+; CHECK-NEXT: [[TMP3:%.*]] = shufflevector <8 x float> [[TMP2]], <8 x float> poison, <8 x i32> <i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP4:%.*]] = tail call <8 x float> @llvm.minimumnum.v8f32(<8 x float> [[TMP2]], <8 x float> [[TMP3]])
+; CHECK-NEXT: [[TMP5:%.*]] = shufflevector <8 x float> [[TMP4]], <8 x float> poison, <8 x i32> <i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP6:%.*]] = tail call <8 x float> @llvm.minimumnum.v8f32(<8 x float> [[TMP4]], <8 x float> [[TMP5]])
+; CHECK-NEXT: [[TMP7:%.*]] = extractelement <8 x float> [[TMP6]], i64 0
+; CHECK-NEXT: ret float [[TMP7]]
+;
+ %1 = shufflevector <8 x float> %a0, <8 x float> poison, <8 x i32> <i32 4, i32 5, i32 6, i32 7, i32 poison, i32 poison, i32 poison, i32 poison>
+ %2 = tail call <8 x float> @llvm.minimumnum.v8f32(<8 x float> %a0, <8 x float> %1)
+ %3 = shufflevector <8 x float> %2, <8 x float> poison, <8 x i32> <i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %4 = tail call <8 x float> @llvm.minimumnum.v8f32(<8 x float> %2, <8 x float> %3)
+ %5 = shufflevector <8 x float> %4, <8 x float> poison, <8 x i32> <i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %6 = tail call <8 x float> @llvm.minimumnum.v8f32(<8 x float> %4, <8 x float> %5)
+ %7 = extractelement <8 x float> %6, i64 0
+ ret float %7
+}
+
; Partial reduction: reduce lower 8 elements of a 16-element vector using smax.
define i16 @test_partial_reduce_v16i16_v8i16_smax(<16 x i16> %a0) {
; CHECK-LABEL: define i16 @test_partial_reduce_v16i16_v8i16_smax(
More information about the llvm-commits
mailing list