[llvm-branch-commits] [llvm] [InstSimplify] Fold nested FP min/max with a shared operand (PR #233365)

Harrison Hao via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Oct 19 01:16:39 PDT 2026


https://github.com/harrisonGPU created https://github.com/llvm/llvm-project/pull/233365

foldMinimumMaximumSharedOp only matched m(m(X, Y), X). Look through
nested calls of the same intrinsic so the shared operand can be found
deeper in the tree. The FP min/max intrinsics are commutative,
associative and idempotent (ignoring sNaN quieting, as the existing
folds already do), so if m(X, Z) == X then m(m(X, Y), Z) == m(X, Y).

```llvm
%ab = call float @llvm.maxnum.f32(float %a, float %b)
%abc = call float @llvm.maxnum.f32(float %ab, float %c)
%r = call float @llvm.maxnum.f32(float %abc, float %a)
  -->
%r = %abc
```

---

<sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>

>From 3599fb86e8cedd3a1087386b644b4183e9052c6d Mon Sep 17 00:00:00 2001
From: Harrison Hao <tsworld1314 at gmail.com>
Date: Mon, 19 Oct 2026 15:59:18 +0800
Subject: [PATCH] [InstSimplify] Fold nested FP min/max with a shared operand

foldMinimumMaximumSharedOp only matched m(m(X, Y), X). Look through
nested calls of the same intrinsic so the shared operand can be found
deeper in the tree. The FP min/max intrinsics are commutative,
associative and idempotent (ignoring sNaN quieting, as the existing
folds already do), so if m(X, Z) == X then m(m(X, Y), Z) == m(X, Y).

```llvm
%ab = call float @llvm.maxnum.f32(float %a, float %b)
%abc = call float @llvm.maxnum.f32(float %ab, float %c)
%r = call float @llvm.maxnum.f32(float %abc, float %a)
  -->
%r = %abc
```
---
 llvm/lib/Analysis/InstructionSimplify.cpp     | 16 ++++++---
 .../Transforms/InstSimplify/fminmax-folds.ll  | 36 +++++++------------
 2 files changed, 25 insertions(+), 27 deletions(-)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 74a266660b234..93674180b1016 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6763,7 +6763,7 @@ static Value *foldMinMaxSharedOp(Intrinsic::ID IID, Value *Op0, Value *Op1) {
 /// operand that is another min/max intrinsic with shared operand(s). The caller
 /// is expected to swap the operand arguments to handle commutation.
 static Value *foldMinimumMaximumSharedOp(Intrinsic::ID IID, Value *Op0,
-                                         Value *Op1) {
+                                         Value *Op1, unsigned MaxRecurse) {
   auto IsMinimumMaximumIntrinsic = [](Intrinsic::ID ID) {
     switch (ID) {
     case Intrinsic::maxnum:
@@ -6797,6 +6797,13 @@ static Value *foldMinimumMaximumSharedOp(Intrinsic::ID IID, Value *Op0,
   if (X0 == Op1 || Y0 == Op1)
     return M0;
 
+  // m(m(X, Y), Z) --> m(X, Y) if m(X, Z) == X or m(Y, Z) == Y.
+  if (MaxRecurse--) {
+    if (foldMinimumMaximumSharedOp(IID, X0, Op1, MaxRecurse) ||
+        foldMinimumMaximumSharedOp(IID, Y0, Op1, MaxRecurse))
+      return M0;
+  }
+
   auto *M1 = dyn_cast<IntrinsicInst>(Op1);
   if (!M1 || !IsMinimumMaximumIntrinsic(M1->getIntrinsicID()))
     return nullptr;
@@ -7324,10 +7331,11 @@ static Value *simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
     }
 
     // Min/max of the same operation with common operand:
-    // m(m(X, Y)), X --> m(X, Y) (4 commuted variants)
-    if (Value *V = foldMinimumMaximumSharedOp(IID, Op0, Op1))
+    // m(m(X, Y), X) --> m(X, Y) (4 commuted variants), also through nested m,
+    // e.g. m(m(m(X, Y), Z), X) --> m(m(X, Y), Z).
+    if (Value *V = foldMinimumMaximumSharedOp(IID, Op0, Op1, RecursionLimit))
       return V;
-    if (Value *V = foldMinimumMaximumSharedOp(IID, Op1, Op0))
+    if (Value *V = foldMinimumMaximumSharedOp(IID, Op1, Op0, RecursionLimit))
       return V;
 
     break;
diff --git a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
index 9b264ca3c03dd..e66cd1cf93e1a 100644
--- a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
+++ b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
@@ -891,28 +891,22 @@ define void @minmax_minmax_xy_maxmin_yx(double %x, double %y, ptr %minnum_res, p
 ;###############################################################
 define void @minmax_minmax_minmax_xy_z_x(float %x, float %y, float %z, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_minmax_minmax_xy_z_x(
-; CHECK-NEXT:    [[MINNUM_XY:%.*]] = call float @llvm.minnum.f32(float [[X:%.*]], float [[Y:%.*]])
-; CHECK-NEXT:    [[MINNUM_XYZ:%.*]] = call float @llvm.minnum.f32(float [[MINNUM_XY]], float [[Z:%.*]])
-; CHECK-NEXT:    [[MINNUM_NESTED:%.*]] = call float @llvm.minnum.f32(float [[MINNUM_XYZ]], float [[X]])
+; CHECK-NEXT:    [[MINNUM_XYZ:%.*]] = call float @llvm.minnum.f32(float [[MINNUM_XY:%.*]], float [[Z:%.*]])
+; CHECK-NEXT:    [[MINNUM_NESTED:%.*]] = call float @llvm.minnum.f32(float [[MINNUM_XYZ]], float [[X:%.*]])
 ; CHECK-NEXT:    store float [[MINNUM_NESTED]], ptr [[MINNUM_RES:%.*]], align 4
-; CHECK-NEXT:    [[MAXNUM_XY:%.*]] = call float @llvm.maxnum.f32(float [[X]], float [[Y]])
-; CHECK-NEXT:    [[MAXNUM_XYZ:%.*]] = call float @llvm.maxnum.f32(float [[MAXNUM_XY]], float [[Z]])
+; CHECK-NEXT:    [[MAXNUM_XYZ:%.*]] = call float @llvm.maxnum.f32(float [[MINNUM_XY]], float [[Z]])
 ; CHECK-NEXT:    [[MAXNUM_NESTED:%.*]] = call float @llvm.maxnum.f32(float [[MAXNUM_XYZ]], float [[X]])
 ; CHECK-NEXT:    store float [[MAXNUM_NESTED]], ptr [[MAXNUM_RES:%.*]], align 4
-; CHECK-NEXT:    [[MINIMUM_XY:%.*]] = call float @llvm.minimum.f32(float [[X]], float [[Y]])
-; CHECK-NEXT:    [[MINIMUM_XYZ:%.*]] = call float @llvm.minimum.f32(float [[MINIMUM_XY]], float [[Z]])
+; CHECK-NEXT:    [[MINIMUM_XYZ:%.*]] = call float @llvm.minimum.f32(float [[MINNUM_XY]], float [[Z]])
 ; CHECK-NEXT:    [[MINIMUM_NESTED:%.*]] = call float @llvm.minimum.f32(float [[MINIMUM_XYZ]], float [[X]])
 ; CHECK-NEXT:    store float [[MINIMUM_NESTED]], ptr [[MINIMUM_RES:%.*]], align 4
-; CHECK-NEXT:    [[MAXIMUM_XY:%.*]] = call float @llvm.maximum.f32(float [[X]], float [[Y]])
-; CHECK-NEXT:    [[MAXIMUM_XYZ:%.*]] = call float @llvm.maximum.f32(float [[MAXIMUM_XY]], float [[Z]])
+; CHECK-NEXT:    [[MAXIMUM_XYZ:%.*]] = call float @llvm.maximum.f32(float [[MINNUM_XY]], float [[Z]])
 ; CHECK-NEXT:    [[MAXIMUM_NESTED:%.*]] = call float @llvm.maximum.f32(float [[MAXIMUM_XYZ]], float [[X]])
 ; CHECK-NEXT:    store float [[MAXIMUM_NESTED]], ptr [[MAXIMUM_RES:%.*]], align 4
-; CHECK-NEXT:    [[MINIMUMNUM_XY:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float [[Y]])
-; CHECK-NEXT:    [[MINIMUMNUM_XYZ:%.*]] = call float @llvm.minimumnum.f32(float [[MINIMUMNUM_XY]], float [[Z]])
+; CHECK-NEXT:    [[MINIMUMNUM_XYZ:%.*]] = call float @llvm.minimumnum.f32(float [[MINNUM_XY]], float [[Z]])
 ; CHECK-NEXT:    [[MINIMUMNUM_NESTED:%.*]] = call float @llvm.minimumnum.f32(float [[MINIMUMNUM_XYZ]], float [[X]])
 ; CHECK-NEXT:    store float [[MINIMUMNUM_NESTED]], ptr [[MINIMUMNUM_RES:%.*]], align 4
-; CHECK-NEXT:    [[MAXIMUMNUM_XY:%.*]] = call float @llvm.maximumnum.f32(float [[X]], float [[Y]])
-; CHECK-NEXT:    [[MAXIMUMNUM_XYZ:%.*]] = call float @llvm.maximumnum.f32(float [[MAXIMUMNUM_XY]], float [[Z]])
+; CHECK-NEXT:    [[MAXIMUMNUM_XYZ:%.*]] = call float @llvm.maximumnum.f32(float [[MINNUM_XY]], float [[Z]])
 ; CHECK-NEXT:    [[MAXIMUMNUM_NESTED:%.*]] = call float @llvm.maximumnum.f32(float [[MAXIMUMNUM_XYZ]], float [[X]])
 ; CHECK-NEXT:    store float [[MAXIMUMNUM_NESTED]], ptr [[MAXIMUMNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    ret void
@@ -951,9 +945,8 @@ define void @minmax_minmax_minmax_xy_z_x(float %x, float %y, float %z, ptr %minn
 
 define <2 x float> @minimum_x_minimum_z_minimum_yx_v2f32(<2 x float> %x, <2 x float> %y, <2 x float> %z) {
 ; CHECK-LABEL: @minimum_x_minimum_z_minimum_yx_v2f32(
-; CHECK-NEXT:    [[YX:%.*]] = call <2 x float> @llvm.minimum.v2f32(<2 x float> [[Y:%.*]], <2 x float> [[X:%.*]])
-; CHECK-NEXT:    [[ZYX:%.*]] = call <2 x float> @llvm.minimum.v2f32(<2 x float> [[Z:%.*]], <2 x float> [[YX]])
-; CHECK-NEXT:    [[R:%.*]] = call <2 x float> @llvm.minimum.v2f32(<2 x float> [[X]], <2 x float> [[ZYX]])
+; CHECK-NEXT:    [[ZYX:%.*]] = call <2 x float> @llvm.minimum.v2f32(<2 x float> [[Z:%.*]], <2 x float> [[YX:%.*]])
+; CHECK-NEXT:    [[R:%.*]] = call <2 x float> @llvm.minimum.v2f32(<2 x float> [[X:%.*]], <2 x float> [[ZYX]])
 ; CHECK-NEXT:    ret <2 x float> [[R]]
 ;
   %yx = call <2 x float> @llvm.minimum.v2f32(<2 x float> %y, <2 x float> %x)
@@ -964,10 +957,8 @@ define <2 x float> @minimum_x_minimum_z_minimum_yx_v2f32(<2 x float> %x, <2 x fl
 
 define double @minimumnum_minimumnum_xy_z_maximumnum_yx(double %x, double %y, double %z) {
 ; CHECK-LABEL: @minimumnum_minimumnum_xy_z_maximumnum_yx(
-; CHECK-NEXT:    [[XY:%.*]] = call double @llvm.minimumnum.f64(double [[X:%.*]], double [[Y:%.*]])
-; CHECK-NEXT:    [[XYZ:%.*]] = call double @llvm.minimumnum.f64(double [[XY]], double [[Z:%.*]])
-; CHECK-NEXT:    [[YX:%.*]] = call double @llvm.maximumnum.f64(double [[Y]], double [[X]])
-; CHECK-NEXT:    [[R:%.*]] = call double @llvm.minimumnum.f64(double [[XYZ]], double [[YX]])
+; CHECK-NEXT:    [[XYZ:%.*]] = call double @llvm.minimumnum.f64(double [[XY:%.*]], double [[Z:%.*]])
+; CHECK-NEXT:    [[R:%.*]] = call double @llvm.minimumnum.f64(double [[XYZ]], double [[YX:%.*]])
 ; CHECK-NEXT:    ret double [[R]]
 ;
   %xy = call double @llvm.minimumnum.f64(double %x, double %y)
@@ -979,11 +970,10 @@ define double @minimumnum_minimumnum_xy_z_maximumnum_yx(double %x, double %y, do
 
 define float @maximumnum_shared_op_depth4(float %x, float %a, float %b, float %c, float %d) {
 ; CHECK-LABEL: @maximumnum_shared_op_depth4(
-; CHECK-NEXT:    [[M1:%.*]] = call float @llvm.maximumnum.f32(float [[X:%.*]], float [[A:%.*]])
-; CHECK-NEXT:    [[M2:%.*]] = call float @llvm.maximumnum.f32(float [[M1]], float [[B:%.*]])
+; CHECK-NEXT:    [[M2:%.*]] = call float @llvm.maximumnum.f32(float [[M1:%.*]], float [[B:%.*]])
 ; CHECK-NEXT:    [[M3:%.*]] = call float @llvm.maximumnum.f32(float [[M2]], float [[C:%.*]])
 ; CHECK-NEXT:    [[M4:%.*]] = call float @llvm.maximumnum.f32(float [[M3]], float [[D:%.*]])
-; CHECK-NEXT:    [[R:%.*]] = call float @llvm.maximumnum.f32(float [[M4]], float [[X]])
+; CHECK-NEXT:    [[R:%.*]] = call float @llvm.maximumnum.f32(float [[M4]], float [[X:%.*]])
 ; CHECK-NEXT:    ret float [[R]]
 ;
   %m1 = call float @llvm.maximumnum.f32(float %x, float %a)



More information about the llvm-branch-commits mailing list