Skip to content

Conversation

Rajveer100
Copy link
Member

Follow up of #158097

Similar to simplifyAndOrOfICmpsWithConstants, we can do so for floating point comparisons.

@Rajveer100 Rajveer100 requested a review from nikic as a code owner September 17, 2025 10:28
@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms labels Sep 17, 2025
@Rajveer100 Rajveer100 requested a review from dtcxzyw September 17, 2025 10:29
@llvmbot
Copy link
Member

llvmbot commented Sep 17, 2025

@llvm/pr-subscribers-llvm-analysis

@llvm/pr-subscribers-llvm-transforms

Author: Rajveer Singh Bharadwaj (Rajveer100)

Changes

Follow up of #158097

Similar to simplifyAndOrOfICmpsWithConstants, we can do so for floating point comparisons.


Full diff: https://github.com/llvm/llvm-project/pull/159315.diff

2 Files Affected:

  • (modified) llvm/lib/Analysis/InstructionSimplify.cpp (+46-28)
  • (modified) llvm/test/Transforms/InstCombine/redundant-fcmp.ll (+9-12)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 100fa428cb842..ffe0b2dd6f6ff 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -35,6 +35,7 @@
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Analysis/ValueTracking.h"
 #include "llvm/Analysis/VectorUtils.h"
+#include "llvm/IR/ConstantFPRange.h"
 #include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/Dominators.h"
@@ -1812,6 +1813,49 @@ static Value *simplifyOrOfICmps(ICmpInst *Op0, ICmpInst *Op1,
   return nullptr;
 }
 
+/// Test if a pair of compares with a shared operand and 2 constants has an
+/// empty set intersection, full set union, or if one compare is a superset of
+/// the other.
+static Value *simplifyAndOrOfFCmpsWithConstants(FCmpInst *Cmp0, FCmpInst *Cmp1,
+                                                bool IsAnd) {
+  // Look for this pattern: {and/or} (fcmp X, C0), (fcmp X, C1)).
+  if (Cmp0->getOperand(0) != Cmp1->getOperand(0))
+    return nullptr;
+
+  const APFloat *C0, *C1;
+  if (!match(Cmp0->getOperand(1), m_APFloat(C0)) ||
+      !match(Cmp1->getOperand(1), m_APFloat(C1)))
+    return nullptr;
+
+  auto Range0 = ConstantFPRange::makeExactFCmpRegion(Cmp0->getPredicate(), *C0);
+  auto Range1 = ConstantFPRange::makeExactFCmpRegion(Cmp1->getPredicate(), *C1);
+
+  if (!Range0 || !Range1)
+    return nullptr;
+
+  // For and-of-compares, check if the intersection is empty:
+  // (fcmp X, C0) && (fcmp X, C1) --> empty set --> false
+  if (IsAnd && (*Range0).intersectWith(*Range1).isEmptySet())
+    return getFalse(Cmp0->getType());
+
+  // For or-of-compares, check if the union is full:
+  // (fcmp X, C0) || (fcmp X, C1) --> full set --> true
+  if (!IsAnd && (*Range0).unionWith(*Range1).isFullSet())
+    return getTrue(Cmp0->getType());
+
+  // Is one range a superset of the other?
+  // If this is and-of-compares, take the smaller set:
+  // (fcmp sgt X, 4) && (fcmp sgt X, 42) --> fcmp sgt X, 42
+  // If this is or-of-compares, take the larger set:
+  // (fcmp sgt X, 4) || (fcmp sgt X, 42) --> fcmp sgt X, 4
+  if ((*Range0).contains(*Range1))
+    return IsAnd ? Cmp1 : Cmp0;
+  if ((*Range1).contains(*Range0))
+    return IsAnd ? Cmp0 : Cmp1;
+
+  return nullptr;
+}
+
 static Value *simplifyAndOrOfFCmps(const SimplifyQuery &Q, FCmpInst *LHS,
                                    FCmpInst *RHS, bool IsAnd) {
   Value *LHS0 = LHS->getOperand(0), *LHS1 = LHS->getOperand(1);
@@ -1850,34 +1894,8 @@ static Value *simplifyAndOrOfFCmps(const SimplifyQuery &Q, FCmpInst *LHS,
                  : ConstantInt::getBool(LHS->getType(), !IsAnd);
   }
 
-  Value *V0;
-  const APFloat *V0Op1, *V1Op1;
-  // (fcmp olt V0, V0Op1) || (fcmp olt V0, V1Op1)
-  //                      --> fcmp olt V0, max(V0Op1, V1Op1)
-  // (fcmp ogt V0, V0Op1) || (fcmp ogt V0, V1Op1)
-  //                      --> fcmp ogt V0, max(V0Op1, V1Op1)
-  //
-  // (fcmp olt V0, V0Op1) && (fcmp olt V0, V1Op1)
-  //                      --> fcmp olt V0, min(V0Op1, V1Op1)
-  // (fcmp ogt V0, V0Op1) && (fcmp ogt V0, V1Op1)
-  //                      --> fcmp ogt V0, min(V0Op1, V1Op1)
-  if (match(LHS, m_SpecificFCmp(FCmpInst::FCMP_OLT, m_Value(V0),
-                                m_APFloat(V0Op1))) &&
-      match(RHS, m_SpecificFCmp(FCmpInst::FCMP_OLT, m_Specific(V0),
-                                m_APFloat(V1Op1)))) {
-    if (*V0Op1 > *V1Op1)
-      return IsAnd ? RHS : LHS;
-    if (*V1Op1 > *V0Op1)
-      return IsAnd ? LHS : RHS;
-  } else if (match(LHS, m_SpecificFCmp(FCmpInst::FCMP_OGT, m_Value(V0),
-                                       m_APFloat(V0Op1))) &&
-             match(RHS, m_SpecificFCmp(FCmpInst::FCMP_OGT, m_Specific(V0),
-                                       m_APFloat(V1Op1)))) {
-    if (*V0Op1 < *V1Op1)
-      return IsAnd ? RHS : LHS;
-    if (*V1Op1 < *V0Op1)
-      return IsAnd ? LHS : RHS;
-  }
+  if (auto *V = simplifyAndOrOfFCmpsWithConstants(LHS, RHS, IsAnd))
+    return V;
 
   return nullptr;
 }
diff --git a/llvm/test/Transforms/InstCombine/redundant-fcmp.ll b/llvm/test/Transforms/InstCombine/redundant-fcmp.ll
index 0f5fe9fb9a1b2..5d1529ef0b214 100644
--- a/llvm/test/Transforms/InstCombine/redundant-fcmp.ll
+++ b/llvm/test/Transforms/InstCombine/redundant-fcmp.ll
@@ -45,8 +45,8 @@ define i1 @or_fcmp_redundant_or4(double %v0) {
   ret i1 %v3
 }
 
-define i1 @or_fcmp_redundant_or_neg1(double %v0) {
-; CHECK-LABEL: @or_fcmp_redundant_or_neg1(
+define i1 @or_fcmp_redundant_or_5(double %v0) {
+; CHECK-LABEL: @or_fcmp_redundant_or_5(
 ; CHECK-NEXT:    [[V1:%.*]] = fcmp olt double [[V0:%.*]], 1.000000e-02
 ; CHECK-NEXT:    [[V2:%.*]] = fcmp ogt double [[V0]], 1.990000e+00
 ; CHECK-NEXT:    [[V3:%.*]] = or i1 [[V1]], [[V2]]
@@ -58,8 +58,8 @@ define i1 @or_fcmp_redundant_or_neg1(double %v0) {
   ret i1 %v3
 }
 
-define i1 @or_fcmp_redundant_or_neg2(double %v0) {
-; CHECK-LABEL: @or_fcmp_redundant_or_neg2(
+define i1 @or_fcmp_redundant_or_6(double %v0) {
+; CHECK-LABEL: @or_fcmp_redundant_or_6(
 ; CHECK-NEXT:    [[V1:%.*]] = fcmp ogt double [[V0:%.*]], 1.000000e-02
 ; CHECK-NEXT:    [[V2:%.*]] = fcmp olt double [[V0]], 1.990000e+00
 ; CHECK-NEXT:    [[V3:%.*]] = or i1 [[V1]], [[V2]]
@@ -115,12 +115,9 @@ define i1 @or_fcmp_redundant_and4(double %v0) {
   ret i1 %v3
 }
 
-define i1 @or_fcmp_redundant_and_neg1(double %v0) {
-; CHECK-LABEL: @or_fcmp_redundant_and_neg1(
-; CHECK-NEXT:    [[V1:%.*]] = fcmp olt double [[V0:%.*]], 1.000000e-02
-; CHECK-NEXT:    [[V2:%.*]] = fcmp ogt double [[V0]], 1.990000e+00
-; CHECK-NEXT:    [[V3:%.*]] = and i1 [[V1]], [[V2]]
-; CHECK-NEXT:    ret i1 [[V3]]
+define i1 @or_fcmp_redundant_and_5(double %v0) {
+; CHECK-LABEL: @or_fcmp_redundant_and_5(
+; CHECK-NEXT:    ret i1 false
 ;
   %v1 = fcmp olt double %v0, 1.000000e-02
   %v2 = fcmp ogt double %v0, 1.990000e+00
@@ -128,8 +125,8 @@ define i1 @or_fcmp_redundant_and_neg1(double %v0) {
   ret i1 %v3
 }
 
-define i1 @or_fcmp_redundant_and_neg2(double %v0) {
-; CHECK-LABEL: @or_fcmp_redundant_and_neg2(
+define i1 @or_fcmp_redundant_and_6(double %v0) {
+; CHECK-LABEL: @or_fcmp_redundant_and_6(
 ; CHECK-NEXT:    [[V1:%.*]] = fcmp ogt double [[V0:%.*]], 1.000000e-02
 ; CHECK-NEXT:    [[V2:%.*]] = fcmp olt double [[V0]], 1.990000e+00
 ; CHECK-NEXT:    [[V3:%.*]] = and i1 [[V1]], [[V2]]

@Rajveer100 Rajveer100 force-pushed the opt-generalise-redundant-fcmp branch from 66ced86 to 5b032ab Compare September 17, 2025 10:30
@nikic nikic added the floating-point Floating-point math label Sep 17, 2025
@Rajveer100 Rajveer100 force-pushed the opt-generalise-redundant-fcmp branch from 5b032ab to a9d6669 Compare September 17, 2025 11:34
…parisons with `ConstantFPRange`

Follow up of llvm#158097

Similar to `simplifyAndOrOfICmpsWithConstants`, we can do so for floating point
comparisons.
@Rajveer100 Rajveer100 force-pushed the opt-generalise-redundant-fcmp branch from a9d6669 to c06a312 Compare September 19, 2025 12:40
@Rajveer100 Rajveer100 requested a review from dtcxzyw September 19, 2025 12:42
@dtcxzyw dtcxzyw requested a review from arsenm September 19, 2025 15:31
@Rajveer100 Rajveer100 force-pushed the opt-generalise-redundant-fcmp branch 2 times, most recently from 4a99eb5 to d58da88 Compare September 20, 2025 10:36
@Rajveer100 Rajveer100 requested a review from dtcxzyw September 20, 2025 10:37
@dtcxzyw
Copy link
Member

dtcxzyw commented Sep 20, 2025

@zyw-bot mfuzz

@Rajveer100 Rajveer100 force-pushed the opt-generalise-redundant-fcmp branch from d58da88 to 34646df Compare September 20, 2025 10:52
@Rajveer100 Rajveer100 requested a review from dtcxzyw September 20, 2025 10:53
@Rajveer100 Rajveer100 force-pushed the opt-generalise-redundant-fcmp branch from 34646df to dd232fc Compare September 20, 2025 10:54
@dtcxzyw
Copy link
Member

dtcxzyw commented Sep 20, 2025

Failed Tests (1):
LLVM :: Transforms/InstCombine/create-class-from-logic-fcmp.ll

Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG w/ tests fixed. Thank you!

@Rajveer100 Rajveer100 force-pushed the opt-generalise-redundant-fcmp branch from dd232fc to f95bdfc Compare September 20, 2025 12:04
@Rajveer100 Rajveer100 enabled auto-merge (squash) September 20, 2025 12:06
@Rajveer100 Rajveer100 merged commit 5d39cae into llvm:main Sep 20, 2025
9 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Sep 20, 2025

LLVM Buildbot has detected a new failure on builder clang-aarch64-quick running on linaro-clang-aarch64-quick while building llvm at step 5 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/65/builds/22918

Here is the relevant piece of the build log for the reference
Step 5 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'lit :: shtest-readfile-external.py' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 4
env LIT_USE_INTERNAL_SHELL=0 not env -u FILECHECK_OPTS "/usr/bin/python3.10" /home/tcwg-buildbot/worker/clang-aarch64-quick/llvm/llvm/utils/lit/lit.py -j1 --order=lexical -a -v Inputs/shtest-readfile | FileCheck -match-full-lines -DTEMP_PATH=/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/utils/lit/tests/Inputs/shtest-readfile/Output /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/utils/lit/tests/shtest-readfile-external.py
# executed command: env LIT_USE_INTERNAL_SHELL=0 not env -u FILECHECK_OPTS /usr/bin/python3.10 /home/tcwg-buildbot/worker/clang-aarch64-quick/llvm/llvm/utils/lit/lit.py -j1 --order=lexical -a -v Inputs/shtest-readfile
# executed command: FileCheck -match-full-lines -DTEMP_PATH=/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/utils/lit/tests/Inputs/shtest-readfile/Output /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/utils/lit/tests/shtest-readfile-external.py
# .---command stderr------------
# | /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/utils/lit/tests/shtest-readfile-external.py:10:10: error: CHECK: expected string not found in input
# | # CHECK: + echo hello
# |          ^
# | <stdin>:16:286: note: scanning from here
# | echo $(cat /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp) && test -e /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp # RUN: at line 3
# |                                                                                                                                                                                                                                                                                              ^
# | <stdin>:21:1: note: possible intended match here
# | + not echo return
# | ^
# | 
# | Input file: <stdin>
# | Check file: /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/utils/lit/tests/shtest-readfile-external.py
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             .
# |             .
# |             .
# |            11: -- 
# |            12: Command Output (stderr): 
# |            13: -- 
# |            14: echo -n "hello" > /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp # RUN: at line 2 
# |            15: + echo -n hello 
# |            16: echo $(cat /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp) && test -e /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp # RUN: at line 3 
# | check:10'0                                                                                                                                                                                                                                                                                                  X error: no match found
# |            17: ++ cat /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp 
# | check:10'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |            18: + echo 
# | check:10'0     ~~~~~~~
# |            19: + test -e /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp 
# | check:10'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |            20: not echo return # RUN: at line 6 
# | check:10'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |            21: + not echo return 
# | check:10'0     ~~~~~~~~~~~~~~~~~~
# | check:10'1     ?                  possible intended match
# |            22:  
# | check:10'0     ~
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
floating-point Floating-point math llvm:analysis Includes value tracking, cost tables and constant folding llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants