[llvm] 6c86b7d - [ValueTracking][InstCombine] Generalize ignoreSignBitOfZero/NaN to handle more cases (#141015)
via llvm-commits
llvm-commits at lists.llvm.org
Wed May 28 04:17:55 PDT 2025
Author: Yingwei Zheng
Date: 2025-05-28T19:17:51+08:00
New Revision: 6c86b7d7d8bc0f77242e938e68c0325acc7f04c3
URL: https://github.com/llvm/llvm-project/commit/6c86b7d7d8bc0f77242e938e68c0325acc7f04c3
DIFF: https://github.com/llvm/llvm-project/commit/6c86b7d7d8bc0f77242e938e68c0325acc7f04c3.diff
LOG: [ValueTracking][InstCombine] Generalize ignoreSignBitOfZero/NaN to handle more cases (#141015)
This patch was originally part of
https://github.com/llvm/llvm-project/pull/139861. It generalizes
`ignoreSignBitOfZero/NaN` to handle more instructions/intrinsics.
BTW, I find it mitigates performance regressions caused by
https://github.com/llvm/llvm-project/pull/141010 (IR diff
https://github.com/dtcxzyw/llvm-opt-benchmark/pull/2365/files). We don't
need to propagate FMF from fcmp into select, since we can infer demanded
properties from the user of select.
Added:
Modified:
llvm/include/llvm/Analysis/ValueTracking.h
llvm/lib/Analysis/ValueTracking.cpp
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
llvm/test/Transforms/InstCombine/fabs.ll
Removed:
################################################################################
diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index e67f8383bd312..d81c3f10bba90 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -300,6 +300,14 @@ LLVM_ABI bool isKnownNeverNaN(const Value *V, unsigned Depth,
LLVM_ABI std::optional<bool>
computeKnownFPSignBit(const Value *V, unsigned Depth, const SimplifyQuery &SQ);
+/// Return true if the sign bit of the FP value can be ignored by the user when
+/// the value is zero.
+bool canIgnoreSignBitOfZero(const Use &U);
+
+/// Return true if the sign bit of the FP value can be ignored by the user when
+/// the value is NaN.
+bool canIgnoreSignBitOfNaN(const Use &U);
+
/// If the specified value can be set by repeating the same byte in memory,
/// return the i8 value that it is represented with. This is true for all i8
/// values obviously, but is also true for i32 0, i32 -1, i16 0xF0F0, double
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index b7084cf3619f3..fc19b2ccf7964 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5937,6 +5937,114 @@ std::optional<bool> llvm::computeKnownFPSignBit(const Value *V, unsigned Depth,
return Known.SignBit;
}
+bool llvm::canIgnoreSignBitOfZero(const Use &U) {
+ auto *User = cast<Instruction>(U.getUser());
+ if (auto *FPOp = dyn_cast<FPMathOperator>(User)) {
+ if (FPOp->hasNoSignedZeros())
+ return true;
+ }
+
+ switch (User->getOpcode()) {
+ case Instruction::FPToSI:
+ case Instruction::FPToUI:
+ return true;
+ case Instruction::FCmp:
+ // fcmp treats both positive and negative zero as equal.
+ return true;
+ case Instruction::Call:
+ if (auto *II = dyn_cast<IntrinsicInst>(User)) {
+ switch (II->getIntrinsicID()) {
+ case Intrinsic::fabs:
+ return true;
+ case Intrinsic::copysign:
+ return U.getOperandNo() == 0;
+ case Intrinsic::is_fpclass:
+ case Intrinsic::vp_is_fpclass: {
+ auto Test =
+ static_cast<FPClassTest>(
+ cast<ConstantInt>(II->getArgOperand(1))->getZExtValue()) &
+ FPClassTest::fcZero;
+ return Test == FPClassTest::fcZero || Test == FPClassTest::fcNone;
+ }
+ default:
+ return false;
+ }
+ }
+ return false;
+ default:
+ return false;
+ }
+}
+
+bool llvm::canIgnoreSignBitOfNaN(const Use &U) {
+ auto *User = cast<Instruction>(U.getUser());
+ if (auto *FPOp = dyn_cast<FPMathOperator>(User)) {
+ if (FPOp->hasNoNaNs())
+ return true;
+ }
+
+ switch (User->getOpcode()) {
+ case Instruction::FPToSI:
+ case Instruction::FPToUI:
+ return true;
+ // Proper FP math operations ignore the sign bit of NaN.
+ case Instruction::FAdd:
+ case Instruction::FSub:
+ case Instruction::FMul:
+ case Instruction::FDiv:
+ case Instruction::FRem:
+ case Instruction::FPTrunc:
+ case Instruction::FPExt:
+ case Instruction::FCmp:
+ return true;
+ // Bitwise FP operations should preserve the sign bit of NaN.
+ case Instruction::FNeg:
+ case Instruction::Select:
+ case Instruction::PHI:
+ return false;
+ case Instruction::Ret:
+ return User->getFunction()->getAttributes().getRetNoFPClass() &
+ FPClassTest::fcNan;
+ case Instruction::Call:
+ case Instruction::Invoke: {
+ if (auto *II = dyn_cast<IntrinsicInst>(User)) {
+ switch (II->getIntrinsicID()) {
+ case Intrinsic::fabs:
+ return true;
+ case Intrinsic::copysign:
+ return U.getOperandNo() == 0;
+ // Other proper FP math intrinsics ignore the sign bit of NaN.
+ case Intrinsic::maxnum:
+ case Intrinsic::minnum:
+ case Intrinsic::maximum:
+ case Intrinsic::minimum:
+ case Intrinsic::maximumnum:
+ case Intrinsic::minimumnum:
+ case Intrinsic::canonicalize:
+ case Intrinsic::fma:
+ case Intrinsic::fmuladd:
+ case Intrinsic::sqrt:
+ case Intrinsic::pow:
+ case Intrinsic::powi:
+ case Intrinsic::fptoui_sat:
+ case Intrinsic::fptosi_sat:
+ case Intrinsic::is_fpclass:
+ case Intrinsic::vp_is_fpclass:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ FPClassTest NoFPClass =
+ cast<CallBase>(User)->getParamNoFPClass(U.getOperandNo());
+ return NoFPClass & FPClassTest::fcNan;
+ }
+ default:
+ return false;
+ }
+}
+
Value *llvm::isBytewiseValue(Value *V, const DataLayout &DL) {
// All byte-wide stores are splatable, even of arbitrary variables.
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 2ef233bc25d72..3882d4cb59e01 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -2773,47 +2773,6 @@ 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,
@@ -2838,7 +2797,8 @@ 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() || ignoreSignBitOfNaN(SI) ||
+ (cast<FPMathOperator>(CondVal)->hasNoNaNs() || SI.hasNoNaNs() ||
+ (SI.hasOneUse() && canIgnoreSignBitOfNaN(*SI.use_begin())) ||
isKnownNeverNaN(X, /*Depth=*/0,
IC.getSimplifyQuery().getWithInstruction(
cast<Instruction>(CondVal))))) {
@@ -2885,7 +2845,11 @@ 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 (!ignoreSignBitOfZero(SI) || !ignoreSignBitOfNaN(SI))
+ if (!SI.hasNoSignedZeros() &&
+ (!SI.hasOneUse() || !canIgnoreSignBitOfZero(*SI.use_begin())))
+ return nullptr;
+ if (!SI.hasNoNaNs() &&
+ (!SI.hasOneUse() || !canIgnoreSignBitOfNaN(*SI.use_begin())))
return nullptr;
if (Swap)
diff --git a/llvm/test/Transforms/InstCombine/fabs.ll b/llvm/test/Transforms/InstCombine/fabs.ll
index ab4376bf78a67..0a22d1431b5f1 100644
--- a/llvm/test/Transforms/InstCombine/fabs.ll
+++ b/llvm/test/Transforms/InstCombine/fabs.ll
@@ -1329,12 +1329,9 @@ define float @test_fabs_fsub_used_by_fpop_nnan(float %x, float %y) {
ret float %add
}
-; TODO: fadd ignores the sign bit of NaN.
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: [[SEL:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
; CHECK-NEXT: [[ADD:%.*]] = fadd nsz float [[SEL]], [[Y:%.*]]
; CHECK-NEXT: ret float [[ADD]]
;
@@ -1345,13 +1342,9 @@ define float @test_fabs_used_by_fpop_nsz(float %x, float %y) {
ret float %add
}
-; TODO: copysign ignores the sign bit of NaN magnitude.
define float @test_fabs_used_by_fcopysign_mag(float %x, float %y) {
; CHECK-LABEL: @test_fabs_used_by_fcopysign_mag(
-; CHECK-NEXT: [[CMP:%.*]] = fcmp oge float [[X1:%.*]], 0.000000e+00
-; CHECK-NEXT: [[NEG:%.*]] = fneg float [[X1]]
-; CHECK-NEXT: [[X:%.*]] = select i1 [[CMP]], float [[X1]], float [[NEG]]
-; CHECK-NEXT: [[COPYSIGN:%.*]] = call float @llvm.copysign.f32(float [[X]], float [[Y:%.*]])
+; CHECK-NEXT: [[COPYSIGN:%.*]] = call float @llvm.copysign.f32(float [[X:%.*]], float [[Y:%.*]])
; CHECK-NEXT: ret float [[COPYSIGN]]
;
%cmp = fcmp oge float %x, 0.000000e+00
@@ -1361,6 +1354,325 @@ define float @test_fabs_used_by_fcopysign_mag(float %x, float %y) {
ret float %copysign
}
+define float @test_fabs_nsz_used_by_canonicalize(float %x) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_canonicalize(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[SEL]])
+; CHECK-NEXT: ret float [[CANON]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ %canon = call float @llvm.canonicalize.f32(float %sel)
+ ret float %canon
+}
+
+define void @test_fabs_used_by_nofpclass_nan(float %x) {
+; CHECK-LABEL: @test_fabs_used_by_nofpclass_nan(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: call void @use(float nofpclass(nan) [[SEL]])
+; CHECK-NEXT: ret void
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ call void @use(float nofpclass(nan) %sel)
+ ret void
+}
+
+define nofpclass(nan) float @test_fabs_used_by_ret_nofpclass_nan(float %x) {
+; CHECK-LABEL: @test_fabs_used_by_ret_nofpclass_nan(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: ret float [[SEL]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ ret float %sel
+}
+
+define i32 @test_fabs_used_fptosi(float %x) {
+; CHECK-LABEL: @test_fabs_used_fptosi(
+; CHECK-NEXT: [[SEL:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[FPTOSI:%.*]] = fptosi float [[SEL]] to i32
+; CHECK-NEXT: ret i32 [[FPTOSI]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select i1 %cmp, float %x, float %neg
+ %fptosi = fptosi float %sel to i32
+ ret i32 %fptosi
+}
+
+define i32 @test_fabs_used_fptoui(float %x) {
+; CHECK-LABEL: @test_fabs_used_fptoui(
+; CHECK-NEXT: [[SEL:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[FPTOSI:%.*]] = fptoui float [[SEL]] to i32
+; CHECK-NEXT: ret i32 [[FPTOSI]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select i1 %cmp, float %x, float %neg
+ %fptosi = fptoui float %sel to i32
+ ret i32 %fptosi
+}
+
+define float @test_fabs_nsz_used_by_maxnum(float %x, float %y) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_maxnum(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[MAX:%.*]] = call float @llvm.maxnum.f32(float [[Y:%.*]], float [[SEL]])
+; CHECK-NEXT: ret float [[MAX]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ %max = call float @llvm.maxnum.f32(float %y, float %sel)
+ ret float %max
+}
+
+define i1 @test_fabs_used_is_fpclass_pnorm_or_nan(float %x) {
+; CHECK-LABEL: @test_fabs_used_is_fpclass_pnorm_or_nan(
+; CHECK-NEXT: [[IS_FPCLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 267)
+; CHECK-NEXT: ret i1 [[IS_FPCLASS]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select i1 %cmp, float %x, float %neg
+ %is_fpclass = call i1 @llvm.is.fpclass.f32(float %sel, i32 259)
+ ret i1 %is_fpclass
+}
+
+define i1 @test_fabs_used_is_fpclass_zero_or_pinf(float %x) {
+; CHECK-LABEL: @test_fabs_used_is_fpclass_zero_or_pinf(
+; CHECK-NEXT: [[IS_FPCLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 612)
+; CHECK-NEXT: ret i1 [[IS_FPCLASS]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select i1 %cmp, float %x, float %neg
+ %is_fpclass = call i1 @llvm.is.fpclass.f32(float %sel, i32 608)
+ ret i1 %is_fpclass
+}
+
+define float @test_fabs_nsz_used_by_fadd(float %x) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_fadd(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[OP:%.*]] = fadd float [[SEL]], 1.000000e+00
+; CHECK-NEXT: ret float [[OP]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ %op = fadd float %sel, 1.000000e+00
+ ret float %op
+}
+
+define float @test_fabs_nsz_used_by_fsub(float %x) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_fsub(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[OP:%.*]] = fsub float 1.000000e+00, [[SEL]]
+; CHECK-NEXT: ret float [[OP]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ %op = fsub float 1.000000e+00, %sel
+ ret float %op
+}
+
+define float @test_fabs_nsz_used_by_fmul(float %x) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_fmul(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[OP:%.*]] = fmul float [[SEL]], 3.000000e+00
+; CHECK-NEXT: ret float [[OP]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ %op = fmul float %sel, 3.000000e+00
+ ret float %op
+}
+
+define float @test_fabs_nsz_used_by_fdiv(float %x) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_fdiv(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[OP:%.*]] = fdiv float [[SEL]], 3.000000e+00
+; CHECK-NEXT: ret float [[OP]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ %op = fdiv float %sel, 3.000000e+00
+ ret float %op
+}
+
+define float @test_fabs_nsz_used_by_frem(float %x) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_frem(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[OP:%.*]] = fdiv float [[SEL]], 3.000000e+00
+; CHECK-NEXT: ret float [[OP]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ %op = fdiv float %sel, 3.000000e+00
+ ret float %op
+}
+
+define half @test_fabs_nsz_used_by_fptrunc(float %x) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_fptrunc(
+; CHECK-NEXT: [[TMP1:%.*]] = fptrunc float [[X:%.*]] to half
+; CHECK-NEXT: [[OP:%.*]] = call nsz half @llvm.fabs.f16(half [[TMP1]])
+; CHECK-NEXT: ret half [[OP]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ %op = fptrunc float %sel to half
+ ret half %op
+}
+
+define float @test_fabs_nsz_used_by_fpext(half %x) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_fpext(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz half @llvm.fabs.f16(half [[X:%.*]])
+; CHECK-NEXT: [[OP:%.*]] = fpext half [[SEL]] to float
+; CHECK-NEXT: ret float [[OP]]
+;
+ %cmp = fcmp oge half %x, 0.000000e+00
+ %neg = fneg half %x
+ %sel = select nsz i1 %cmp, half %x, half %neg
+ %op = fpext half %sel to float
+ ret float %op
+}
+
+define float @test_fabs_nsz_used_by_maximum(float %x, float %y) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_maximum(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[MAX:%.*]] = call float @llvm.maximum.f32(float [[Y:%.*]], float [[SEL]])
+; CHECK-NEXT: ret float [[MAX]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ %max = call float @llvm.maximum.f32(float %y, float %sel)
+ ret float %max
+}
+
+define float @test_fabs_nsz_used_by_maximumnum(float %x, float %y) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_maximumnum(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[MAX:%.*]] = call float @llvm.maximumnum.f32(float [[Y:%.*]], float [[SEL]])
+; CHECK-NEXT: ret float [[MAX]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ %max = call float @llvm.maximumnum.f32(float %y, float %sel)
+ ret float %max
+}
+
+define float @test_fabs_nsz_used_by_fma(float %x, float %y, float %z) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_fma(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[MAX:%.*]] = call float @llvm.fma.f32(float [[SEL]], float [[Z:%.*]], float [[Z]])
+; CHECK-NEXT: ret float [[MAX]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ %max = call float @llvm.fma.f32(float %sel, float %z, float %z)
+ ret float %max
+}
+
+define float @test_fabs_nsz_used_by_sqrt(float %x) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_sqrt(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[OP:%.*]] = call float @llvm.sqrt.f32(float [[SEL]])
+; CHECK-NEXT: ret float [[OP]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ %op = call float @llvm.sqrt.f32(float %sel)
+ ret float %op
+}
+
+define float @test_fabs_nsz_used_by_pow(float %x, float %y) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_pow(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[OP:%.*]] = call float @llvm.pow.f32(float [[SEL]], float [[Y:%.*]])
+; CHECK-NEXT: ret float [[OP]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ %op = call float @llvm.pow.f32(float %sel, float %y)
+ ret float %op
+}
+
+define float @test_fabs_nsz_used_by_powi(float %x, i32 %y) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_powi(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[OP:%.*]] = call float @llvm.powi.f32.i32(float [[SEL]], i32 [[Y:%.*]])
+; CHECK-NEXT: ret float [[OP]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ %op = call float @llvm.powi.f32(float %sel, i32 %y)
+ ret float %op
+}
+
+define i32 @test_fabs_nsz_used_by_fptoui_sat(float %x) {
+; CHECK-LABEL: @test_fabs_nsz_used_by_fptoui_sat(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: [[OP:%.*]] = call i32 @llvm.fptoui.sat.i32.f32(float [[SEL]])
+; CHECK-NEXT: ret i32 [[OP]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ %op = call i32 @llvm.fptoui.sat.i32.f32(float %sel)
+ ret i32 %op
+}
+
+define <2 x i1> @test_fabs_used_vp_is_fpclass_zero_or_pinf(<2 x float> %x, <2 x i1> %mask, i32 %evl) {
+; CHECK-LABEL: @test_fabs_used_vp_is_fpclass_zero_or_pinf(
+; CHECK-NEXT: [[SEL:%.*]] = call <2 x float> @llvm.fabs.v2f32(<2 x float> [[X:%.*]])
+; CHECK-NEXT: [[IS_FPCLASS:%.*]] = call <2 x i1> @llvm.vp.is.fpclass.v2f32(<2 x float> [[SEL]], i32 608, <2 x i1> [[MASK:%.*]], i32 [[EVL:%.*]])
+; CHECK-NEXT: ret <2 x i1> [[IS_FPCLASS]]
+;
+ %cmp = fcmp oge <2 x float> %x, zeroinitializer
+ %neg = fneg <2 x float> %x
+ %sel = select <2 x i1> %cmp, <2 x float> %x, <2 x float> %neg
+ %is_fpclass = call <2 x i1> @llvm.vp.is.fpclass.v2f32(<2 x float> %sel, i32 608, <2 x i1> %mask, i32 %evl)
+ ret <2 x i1> %is_fpclass
+}
+
+define void @test_fabs_nsz_used_by_invoke(float %x) personality ptr null {
+; CHECK-LABEL: @test_fabs_nsz_used_by_invoke(
+; CHECK-NEXT: [[SEL:%.*]] = call nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT: invoke void @use(float nofpclass(nan) [[SEL]])
+; CHECK-NEXT: to label [[NORMAL:%.*]] unwind label [[UNWIND:%.*]]
+; CHECK: normal:
+; CHECK-NEXT: ret void
+; CHECK: unwind:
+; CHECK-NEXT: [[TMP1:%.*]] = landingpad ptr
+; CHECK-NEXT: cleanup
+; CHECK-NEXT: resume ptr null
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select nsz i1 %cmp, float %x, float %neg
+ invoke void @use(float nofpclass(nan) %sel) to label %normal unwind label %unwind
+
+normal:
+ ret void
+
+unwind:
+ landingpad ptr cleanup
+ resume ptr null
+}
; Negative tests
@@ -1455,3 +1767,18 @@ define float @test_fabs_used_by_select(float %x, i1 %cond) {
%sel2 = select i1 %cond, float %sel, float 0.000000e+00
ret float %sel2
}
+
+define i1 @test_fabs_used_is_fpclass_pzero(float %x) {
+; CHECK-LABEL: @test_fabs_used_is_fpclass_pzero(
+; 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: [[IS_FPCLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[SEL]], i32 64)
+; CHECK-NEXT: ret i1 [[IS_FPCLASS]]
+;
+ %cmp = fcmp oge float %x, 0.000000e+00
+ %neg = fneg float %x
+ %sel = select i1 %cmp, float %x, float %neg
+ %is_fpclass = call i1 @llvm.is.fpclass.f32(float %sel, i32 64)
+ ret i1 %is_fpclass
+}
More information about the llvm-commits
mailing list