[llvm] [InstCombine] Enable more fabs fold when the user ignores sign bit of zero/NaN (PR #139861)
via llvm-commits
llvm-commits at lists.llvm.org
Wed May 14 01:41:29 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Yingwei Zheng (dtcxzyw)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/139861.diff
3 Files Affected:
- (modified) llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp (+42-2)
- (modified) llvm/test/Transforms/InstCombine/fabs.ll (+101)
- (modified) llvm/test/Transforms/InstCombine/fneg.ll (+3-3)
``````````diff
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 f449d4b8e6b37..680c8fdee76d5 100644
--- a/llvm/test/Transforms/InstCombine/fabs.ll
+++ b/llvm/test/Transforms/InstCombine/fabs.ll
@@ -1276,3 +1276,104 @@ 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: [[SEL:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
+; 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: [[SEL:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
+; 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: [[SEL:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
+; 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: [[SEL:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
+; 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
+}
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]]
;
``````````
</details>
https://github.com/llvm/llvm-project/pull/139861
More information about the llvm-commits
mailing list