[llvm] [InstCombine] Enable more fabs fold when the user ignores sign bit of zero/NaN (PR #139861)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Wed May 14 01:40:52 PDT 2025


https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/139861

When the only user of select is a fcmp or a fp operation with nnan/nsz, the sign bit of zero/NaN can be ignored.
Alive2: https://alive2.llvm.org/ce/z/ZcxeIv

Closes https://github.com/llvm/llvm-project/issues/133367.


>From 7add1bcd02b1f72d580bb2e64a1fe4a8bdc085d9 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 14 May 2025 15:51:50 +0800
Subject: [PATCH 1/2] [InstCombine] Add pre-commit tests. NFC.

---
 llvm/test/Transforms/InstCombine/fabs.ll | 109 +++++++++++++++++++++++
 1 file changed, 109 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/fabs.ll b/llvm/test/Transforms/InstCombine/fabs.ll
index f449d4b8e6b37..e73d99e2900bb 100644
--- a/llvm/test/Transforms/InstCombine/fabs.ll
+++ b/llvm/test/Transforms/InstCombine/fabs.ll
@@ -1276,3 +1276,112 @@ define <2 x float> @test_select_neg_negx_x_wrong_type(<2 x float> %value) {
   %value.addr.0.i = select i1 %a1, <2 x float> %fneg.i, <2 x float> %value
   ret <2 x float> %value.addr.0.i
 }
+
+define i1 @test_fabs_used_by_fcmp(float %x, float %y) {
+; CHECK-LABEL: @test_fabs_used_by_fcmp(
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp oge float [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[NEG:%.*]] = fneg float [[X]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[X]], float [[NEG]]
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp olt float [[SEL]], [[Y:%.*]]
+; CHECK-NEXT:    ret i1 [[CMP2]]
+;
+  %cmp = fcmp oge float %x, 0.000000e+00
+  %neg = fneg float %x
+  %sel = select i1 %cmp, float %x, float %neg
+  %cmp2 = fcmp olt float %sel, %y
+  ret i1 %cmp2
+}
+
+define float @test_fabs_used_by_fpop_nnan_nsz(float %x, float %y) {
+; CHECK-LABEL: @test_fabs_used_by_fpop_nnan_nsz(
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp oge float [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[NEG:%.*]] = fneg float [[X]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[X]], float [[NEG]]
+; CHECK-NEXT:    [[ADD:%.*]] = fadd nnan nsz float [[SEL]], [[Y:%.*]]
+; CHECK-NEXT:    ret float [[ADD]]
+;
+  %cmp = fcmp oge float %x, 0.000000e+00
+  %neg = fneg float %x
+  %sel = select i1 %cmp, float %x, float %neg
+  %add = fadd nnan nsz float %sel, %y
+  ret float %add
+}
+
+define i1 @test_fabs_fsub_used_by_fcmp(float %x, float %y) {
+; CHECK-LABEL: @test_fabs_fsub_used_by_fcmp(
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp ogt float [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[NEG:%.*]] = fsub float 0.000000e+00, [[X]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[X]], float [[NEG]]
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp olt float [[SEL]], [[Y:%.*]]
+; CHECK-NEXT:    ret i1 [[CMP2]]
+;
+  %cmp = fcmp ogt float %x, 0.000000e+00
+  %neg = fsub float 0.000000e+00, %x
+  %sel = select i1 %cmp, float %x, float %neg
+  %cmp2 = fcmp olt float %sel, %y
+  ret i1 %cmp2
+}
+
+define float @test_fabs_fsub_used_by_fpop_nnan(float %x, float %y) {
+; CHECK-LABEL: @test_fabs_fsub_used_by_fpop_nnan(
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp ogt float [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[NEG:%.*]] = fsub float 0.000000e+00, [[X]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[X]], float [[NEG]]
+; CHECK-NEXT:    [[ADD:%.*]] = fadd nnan float [[SEL]], [[Y:%.*]]
+; CHECK-NEXT:    ret float [[ADD]]
+;
+  %cmp = fcmp ogt float %x, 0.000000e+00
+  %neg = fsub float 0.000000e+00, %x
+  %sel = select i1 %cmp, float %x, float %neg
+  %add = fadd nnan float %sel, %y
+  ret float %add
+}
+
+; Negative tests
+
+define float @test_fabs_used_by_fpop_nnan(float %x, float %y) {
+; CHECK-LABEL: @test_fabs_used_by_fpop_nnan(
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp oge float [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[NEG:%.*]] = fneg float [[X]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[X]], float [[NEG]]
+; CHECK-NEXT:    [[ADD:%.*]] = fadd nnan float [[SEL]], [[Y:%.*]]
+; CHECK-NEXT:    ret float [[ADD]]
+;
+  %cmp = fcmp oge float %x, 0.000000e+00
+  %neg = fneg float %x
+  %sel = select i1 %cmp, float %x, float %neg
+  %add = fadd nnan float %sel, %y
+  ret float %add
+}
+
+define float @test_fabs_used_by_fpop_nsz(float %x, float %y) {
+; CHECK-LABEL: @test_fabs_used_by_fpop_nsz(
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp oge float [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[NEG:%.*]] = fneg float [[X]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[X]], float [[NEG]]
+; CHECK-NEXT:    [[ADD:%.*]] = fadd nsz float [[SEL]], [[Y:%.*]]
+; CHECK-NEXT:    ret float [[ADD]]
+;
+  %cmp = fcmp oge float %x, 0.000000e+00
+  %neg = fneg float %x
+  %sel = select i1 %cmp, float %x, float %neg
+  %add = fadd nsz float %sel, %y
+  ret float %add
+}
+
+define i1 @test_fabs_used_by_fcmp_multiuse(float %x, float %y) {
+; CHECK-LABEL: @test_fabs_used_by_fcmp_multiuse(
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp oge float [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[NEG:%.*]] = fneg float [[X]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[X]], float [[NEG]]
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp olt float [[SEL]], [[Y:%.*]]
+; CHECK-NEXT:    call void @use(float [[SEL]])
+; CHECK-NEXT:    ret i1 [[CMP2]]
+;
+  %cmp = fcmp oge float %x, 0.000000e+00
+  %neg = fneg float %x
+  %sel = select i1 %cmp, float %x, float %neg
+  %cmp2 = fcmp olt float %sel, %y
+  call void @use(float %sel)
+  ret i1 %cmp2
+}

>From 5a5e1063a7de8f40e1c6f300936d7ff0f0efa353 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 14 May 2025 16:33:28 +0800
Subject: [PATCH 2/2] [InstCombine] Enable more fabs fold when the user ignores
 sign bit of zero/NaN

---
 .../InstCombine/InstCombineSelect.cpp         | 44 ++++++++++++++++++-
 llvm/test/Transforms/InstCombine/fabs.ll      | 16 ++-----
 llvm/test/Transforms/InstCombine/fneg.ll      |  6 +--
 3 files changed, 49 insertions(+), 17 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index b5a40892694c1..7f94ae333ca79 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -2773,6 +2773,46 @@ Instruction *InstCombinerImpl::foldAndOrOfSelectUsingImpliedCond(Value *Op,
   return nullptr;
 }
 
+/// Return true if the sign bit of result can be ignored when the result is zero.
+static bool ignoreSignBitOfZero(Instruction &I) {
+  if (I.hasNoSignedZeros())
+    return true;
+
+  // Check if the sign bit is ignored by the only user.
+  if (!I.hasOneUse())
+    return false;
+  Instruction *User = I.user_back();
+
+  // fcmp treats both positive and negative zero as equal.
+  if (User->getOpcode() == Instruction::FCmp)
+    return true;
+
+  if (auto *FPOp = dyn_cast<FPMathOperator>(User))
+    return FPOp->hasNoSignedZeros();
+
+  return false;
+}
+
+/// Return true if the sign bit of result can be ignored when the result is NaN.
+static bool ignoreSignBitOfNaN(Instruction &I) {
+  if (I.hasNoNaNs())
+    return true;
+
+  // Check if the sign bit is ignored by the only user.
+  if (!I.hasOneUse())
+    return false;
+  Instruction *User = I.user_back();
+
+  // fcmp ignores the sign bit of NaN.
+  if (User->getOpcode() == Instruction::FCmp)
+    return true;
+
+  if (auto *FPOp = dyn_cast<FPMathOperator>(User))
+    return FPOp->hasNoNaNs();
+  
+  return false;
+}
+
 // Canonicalize select with fcmp to fabs(). -0.0 makes this tricky. We need
 // fast-math-flags (nsz) or fsub with +0.0 (not fneg) for this to work.
 static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
@@ -2797,7 +2837,7 @@ static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
     //       of NAN, but IEEE-754 specifies the signbit of NAN values with
     //       fneg/fabs operations.
     if (match(TrueVal, m_FSub(m_PosZeroFP(), m_Specific(X))) &&
-        (cast<FPMathOperator>(CondVal)->hasNoNaNs() || SI.hasNoNaNs() ||
+        (cast<FPMathOperator>(CondVal)->hasNoNaNs() || ignoreSignBitOfNaN(SI) ||
          isKnownNeverNaN(X, /*Depth=*/0,
                          IC.getSimplifyQuery().getWithInstruction(
                              cast<Instruction>(CondVal))))) {
@@ -2844,7 +2884,7 @@ static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
     // Note: We require "nnan" for this fold because fcmp ignores the signbit
     //       of NAN, but IEEE-754 specifies the signbit of NAN values with
     //       fneg/fabs operations.
-    if (!SI.hasNoSignedZeros() || !SI.hasNoNaNs())
+    if (!ignoreSignBitOfZero(SI) || !ignoreSignBitOfNaN(SI))
       return nullptr;
 
     if (Swap)
diff --git a/llvm/test/Transforms/InstCombine/fabs.ll b/llvm/test/Transforms/InstCombine/fabs.ll
index e73d99e2900bb..680c8fdee76d5 100644
--- a/llvm/test/Transforms/InstCombine/fabs.ll
+++ b/llvm/test/Transforms/InstCombine/fabs.ll
@@ -1279,9 +1279,7 @@ define <2 x float> @test_select_neg_negx_x_wrong_type(<2 x float> %value) {
 
 define i1 @test_fabs_used_by_fcmp(float %x, float %y) {
 ; CHECK-LABEL: @test_fabs_used_by_fcmp(
-; CHECK-NEXT:    [[CMP:%.*]] = fcmp oge float [[X:%.*]], 0.000000e+00
-; CHECK-NEXT:    [[NEG:%.*]] = fneg float [[X]]
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[X]], float [[NEG]]
+; CHECK-NEXT:    [[SEL:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
 ; CHECK-NEXT:    [[CMP2:%.*]] = fcmp olt float [[SEL]], [[Y:%.*]]
 ; CHECK-NEXT:    ret i1 [[CMP2]]
 ;
@@ -1294,9 +1292,7 @@ define i1 @test_fabs_used_by_fcmp(float %x, float %y) {
 
 define float @test_fabs_used_by_fpop_nnan_nsz(float %x, float %y) {
 ; CHECK-LABEL: @test_fabs_used_by_fpop_nnan_nsz(
-; CHECK-NEXT:    [[CMP:%.*]] = fcmp oge float [[X:%.*]], 0.000000e+00
-; CHECK-NEXT:    [[NEG:%.*]] = fneg float [[X]]
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[X]], float [[NEG]]
+; CHECK-NEXT:    [[SEL:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd nnan nsz float [[SEL]], [[Y:%.*]]
 ; CHECK-NEXT:    ret float [[ADD]]
 ;
@@ -1309,9 +1305,7 @@ define float @test_fabs_used_by_fpop_nnan_nsz(float %x, float %y) {
 
 define i1 @test_fabs_fsub_used_by_fcmp(float %x, float %y) {
 ; CHECK-LABEL: @test_fabs_fsub_used_by_fcmp(
-; CHECK-NEXT:    [[CMP:%.*]] = fcmp ogt float [[X:%.*]], 0.000000e+00
-; CHECK-NEXT:    [[NEG:%.*]] = fsub float 0.000000e+00, [[X]]
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[X]], float [[NEG]]
+; CHECK-NEXT:    [[SEL:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
 ; CHECK-NEXT:    [[CMP2:%.*]] = fcmp olt float [[SEL]], [[Y:%.*]]
 ; CHECK-NEXT:    ret i1 [[CMP2]]
 ;
@@ -1324,9 +1318,7 @@ define i1 @test_fabs_fsub_used_by_fcmp(float %x, float %y) {
 
 define float @test_fabs_fsub_used_by_fpop_nnan(float %x, float %y) {
 ; CHECK-LABEL: @test_fabs_fsub_used_by_fpop_nnan(
-; CHECK-NEXT:    [[CMP:%.*]] = fcmp ogt float [[X:%.*]], 0.000000e+00
-; CHECK-NEXT:    [[NEG:%.*]] = fsub float 0.000000e+00, [[X]]
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[X]], float [[NEG]]
+; CHECK-NEXT:    [[SEL:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd nnan float [[SEL]], [[Y:%.*]]
 ; CHECK-NEXT:    ret float [[ADD]]
 ;
diff --git a/llvm/test/Transforms/InstCombine/fneg.ll b/llvm/test/Transforms/InstCombine/fneg.ll
index 755beff9bf77a..a9d1b9a4ab837 100644
--- a/llvm/test/Transforms/InstCombine/fneg.ll
+++ b/llvm/test/Transforms/InstCombine/fneg.ll
@@ -709,7 +709,7 @@ define float @select_common_op_fneg_false(float %x, i1 %b) {
 
 define float @fabs(float %a) {
 ; CHECK-LABEL: @fabs(
-; CHECK-NEXT:    [[FNEG1:%.*]] = call nnan ninf nsz float @llvm.fabs.f32(float [[A:%.*]])
+; CHECK-NEXT:    [[FNEG1:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]])
 ; CHECK-NEXT:    ret float [[FNEG1]]
 ;
   %fneg = fneg float %a
@@ -721,7 +721,7 @@ define float @fabs(float %a) {
 
 define float @fnabs(float %a) {
 ; CHECK-LABEL: @fnabs(
-; CHECK-NEXT:    [[TMP1:%.*]] = call fast float @llvm.fabs.f32(float [[A:%.*]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]])
 ; CHECK-NEXT:    [[FNEG1:%.*]] = fneg fast float [[TMP1]]
 ; CHECK-NEXT:    ret float [[FNEG1]]
 ;
@@ -734,7 +734,7 @@ define float @fnabs(float %a) {
 
 define float @fnabs_1(float %a) {
 ; CHECK-LABEL: @fnabs_1(
-; CHECK-NEXT:    [[TMP1:%.*]] = call fast float @llvm.fabs.f32(float [[A:%.*]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]])
 ; CHECK-NEXT:    [[FNEG1:%.*]] = fneg fast float [[TMP1]]
 ; CHECK-NEXT:    ret float [[FNEG1]]
 ;



More information about the llvm-commits mailing list