[llvm] [InstCombine] Optimize `sinh` and `cosh` divivsions (PR #81433)
Felix Kellenbenz via llvm-commits
llvm-commits at lists.llvm.org
Thu Feb 15 06:35:55 PST 2024
https://github.com/felixkellenbenz updated https://github.com/llvm/llvm-project/pull/81433
>From 105b58df7b19e38846767d41067b2e3c9f0b1bf5 Mon Sep 17 00:00:00 2001
From: Felix Kellenbenz <fe.kellenbenz.computer at outlook.de>
Date: Sat, 10 Feb 2024 17:41:04 +0100
Subject: [PATCH] [InstCombine] Optimize sinh and cosh divivsion
---
.../InstCombine/InstCombineMulDivRem.cpp | 82 +++++++++++--
.../Transforms/InstCombine/fdiv-cosh-sinh.ll | 114 ++++++++++++++++++
.../Transforms/InstCombine/fdiv-sinh-cosh.ll | 111 +++++++++++++++++
.../NVPTX/lower-gep-reorder.ll | 15 ++-
4 files changed, 307 insertions(+), 15 deletions(-)
create mode 100644 llvm/test/Transforms/InstCombine/fdiv-cosh-sinh.ll
create mode 100644 llvm/test/Transforms/InstCombine/fdiv-sinh-cosh.ll
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index 0bd4b6d1a835af..937ef4c8a6aa2c 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -1767,26 +1767,90 @@ Instruction *InstCombinerImpl::visitFDiv(BinaryOperator &I) {
if (I.hasAllowReassoc() && Op0->hasOneUse() && Op1->hasOneUse()) {
// sin(X) / cos(X) -> tan(X)
// cos(X) / sin(X) -> 1/tan(X) (cotangent)
- Value *X;
+ // sinh(X) / cosh(X) -> tanh(X)
+ // cosh(X) / sinh(X) -> 1/tanh(X)
+ Value *X, *Y;
+
bool IsTan = match(Op0, m_Intrinsic<Intrinsic::sin>(m_Value(X))) &&
match(Op1, m_Intrinsic<Intrinsic::cos>(m_Specific(X)));
- bool IsCot =
- !IsTan && match(Op0, m_Intrinsic<Intrinsic::cos>(m_Value(X))) &&
- match(Op1, m_Intrinsic<Intrinsic::sin>(m_Specific(X)));
+ bool IsCot = !IsTan &&
+ match(Op0, m_Intrinsic<Intrinsic::cos>(m_Value(X))) &&
+ match(Op1, m_Intrinsic<Intrinsic::sin>(m_Specific(X)));
- if ((IsTan || IsCot) && hasFloatFn(M, &TLI, I.getType(), LibFunc_tan,
- LibFunc_tanf, LibFunc_tanl)) {
+ auto GetReplacement = [&](Value *Arg, bool IsInv, LibFunc DoubleFunc,
+ LibFunc FloatFunc,
+ LibFunc LongDoubleFunc) -> Value * {
IRBuilder<> B(&I);
IRBuilder<>::FastMathFlagGuard FMFGuard(B);
B.setFastMathFlags(I.getFastMathFlags());
AttributeList Attrs =
cast<CallBase>(Op0)->getCalledFunction()->getAttributes();
- Value *Res = emitUnaryFloatFnCall(X, &TLI, LibFunc_tan, LibFunc_tanf,
- LibFunc_tanl, B, Attrs);
- if (IsCot)
+ Value *Res = emitUnaryFloatFnCall(Arg, &TLI, DoubleFunc, FloatFunc,
+ LongDoubleFunc, B, Attrs);
+
+ if (IsInv)
Res = B.CreateFDiv(ConstantFP::get(I.getType(), 1.0), Res);
+
+ return Res;
+ };
+
+ if ((IsTan || IsCot) && hasFloatFn(M, &TLI, I.getType(), LibFunc_tan,
+ LibFunc_tanf, LibFunc_tanl)) {
+
+ Value *Res =
+ GetReplacement(X, IsCot, LibFunc_tan, LibFunc_tanf, LibFunc_tanl);
+
return replaceInstUsesWith(I, Res);
}
+
+ if (isa<CallBase>(Op0) && isa<CallBase>(Op1)) {
+
+ CallBase *Op0AsCallBase = cast<CallBase>(Op0);
+ CallBase *Op1AsCallBase = cast<CallBase>(Op1);
+ LibFunc Op0LibFunc, Op1LibFunc;
+
+ TLI.getLibFunc(*Op1AsCallBase, Op1LibFunc);
+ TLI.getLibFunc(*Op0AsCallBase, Op0LibFunc);
+
+ bool ArgsMatch = match(Op0AsCallBase->getArgOperand(0), m_Value(Y)) &&
+ match(Op1AsCallBase->getArgOperand(0), m_Specific(Y));
+
+ bool IsTanH =
+ ArgsMatch &&
+ ((Op0LibFunc == LibFunc_sinh && Op1LibFunc == LibFunc_cosh) ||
+ (Op0LibFunc == LibFunc_sinhf && Op1LibFunc == LibFunc_coshf) ||
+ (Op0LibFunc == LibFunc_sinhl && Op1LibFunc == LibFunc_coshl));
+
+ bool IsCotH =
+ !IsTanH && ArgsMatch &&
+ ((Op1LibFunc == LibFunc_sinh && Op0LibFunc == LibFunc_cosh) ||
+ (Op1LibFunc == LibFunc_sinhf && Op0LibFunc == LibFunc_coshf) ||
+ (Op1LibFunc == LibFunc_sinhl && Op0LibFunc == LibFunc_coshl));
+
+ if ((IsTanH || IsCotH) && hasFloatFn(M, &TLI, I.getType(), LibFunc_tanh,
+ LibFunc_tanhf, LibFunc_tanhl)) {
+
+ Value *Res = GetReplacement(Y, IsCotH, LibFunc_tanh, LibFunc_tanhf,
+ LibFunc_tanhl);
+
+ Instruction *Replacement = replaceInstUsesWith(I, Res);
+
+ // Call instructions of sinh and cosh need to be erased manually
+ // since they might write to errno
+ if (!Op0AsCallBase->use_empty())
+ Op0AsCallBase->replaceAllUsesWith(
+ UndefValue::get(Op0AsCallBase->getType()));
+
+ if (!Op1AsCallBase->use_empty())
+ Op1AsCallBase->replaceAllUsesWith(
+ UndefValue::get(Op1AsCallBase->getType()));
+
+ Op0AsCallBase->eraseFromParent();
+ Op1AsCallBase->eraseFromParent();
+
+ return Replacement;
+ }
+ }
}
// X / (X * Y) --> 1.0 / Y
diff --git a/llvm/test/Transforms/InstCombine/fdiv-cosh-sinh.ll b/llvm/test/Transforms/InstCombine/fdiv-cosh-sinh.ll
new file mode 100644
index 00000000000000..12565626a87c51
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/fdiv-cosh-sinh.ll
@@ -0,0 +1,114 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+define double @fdiv_cosh_sinh(double %a) {
+; CHECK-LABEL: @fdiv_cosh_sinh(
+; CHECK-NEXT: [[TMP1:%.*]] = call double @cosh(double [[A:%.*]])
+; CHECK-NEXT: [[TMP2:%.*]] = call double @sinh(double [[A]])
+; CHECK-NEXT: [[DIV:%.*]] = fdiv double [[TMP1]], [[TMP2]]
+; CHECK-NEXT: ret double [[DIV]]
+;
+ %1 = call double @cosh(double %a)
+ %2 = call double @sinh(double %a)
+ %div = fdiv double %1, %2
+ ret double %div
+}
+
+define double @fdiv_strict_cosh_strict_sinh_reassoc(double %a) {
+; CHECK-LABEL: @fdiv_strict_cosh_strict_sinh_reassoc(
+; CHECK-NEXT: [[TMP1:%.*]] = call double @cosh(double [[A:%.*]])
+; CHECK-NEXT: [[TMP2:%.*]] = call reassoc double @sinh(double [[A]])
+; CHECK-NEXT: [[DIV:%.*]] = fdiv double [[TMP1]], [[TMP2]]
+; CHECK-NEXT: ret double [[DIV]]
+;
+ %1 = call double @cosh(double %a)
+ %2 = call reassoc double @sinh(double %a)
+ %div = fdiv double %1, %2
+ ret double %div
+}
+
+define double @fdiv_reassoc_cosh_strict_sinh_strict(double %a, ptr dereferenceable(2) %dummy) {
+; CHECK-LABEL: @fdiv_reassoc_cosh_strict_sinh_strict(
+; CHECK-NEXT: [[TANH:%.*]] = call reassoc double @tanh(double [[A]])
+; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc double 1.000000e+00, [[TANH]]
+; CHECK-NEXT: ret double [[DIV]]
+;
+ %1 = call double @cosh(double %a)
+ %2 = call double @sinh(double %a)
+ %div = fdiv reassoc double %1, %2
+ ret double %div
+}
+
+define double @fdiv_reassoc_cosh_reassoc_sinh_strict(double %a) {
+; CHECK-LABEL: @fdiv_reassoc_cosh_reassoc_sinh_strict(
+; CHECK-NEXT: [[TANH:%.*]] = call reassoc double @tanh(double [[A]])
+; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc double 1.000000e+00, [[TANH]]
+; CHECK-NEXT: ret double [[DIV]]
+;
+ %1 = call reassoc double @cosh(double %a)
+ %2 = call double @sinh(double %a)
+ %div = fdiv reassoc double %1, %2
+ ret double %div
+}
+
+define double @fdiv_cosh_sinh_reassoc_multiple_uses(double %a) {
+; CHECK-LABEL: @fdiv_cosh_sinh_reassoc_multiple_uses(
+; CHECK-NEXT: [[TMP1:%.*]] = call reassoc double @cosh(double [[A:%.*]])
+; CHECK-NEXT: [[TMP2:%.*]] = call reassoc double @sinh(double [[A]])
+; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc double [[TMP1]], [[TMP2]]
+; CHECK-NEXT: call void @use(double [[TMP2]])
+; CHECK-NEXT: ret double [[DIV]]
+;
+ %1 = call reassoc double @cosh(double %a)
+ %2 = call reassoc double @sinh(double %a)
+ %div = fdiv reassoc double %1, %2
+ call void @use(double %2)
+ ret double %div
+}
+
+define double @fdiv_cosh_sinh_reassoc(double %a){
+; CHECK-LABEL: @fdiv_cosh_sinh_reassoc(
+; CHECK-NEXT: [[TANH:%.*]] = call reassoc double @tanh(double [[A]])
+; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc double 1.000000e+00, [[TANH]]
+; CHECK-NEXT: ret double [[DIV]]
+;
+ %1 = call reassoc double @cosh(double %a)
+ %2 = call reassoc double @sinh(double %a)
+ %div = fdiv reassoc double %1, %2
+ ret double %div
+}
+
+define fp128 @fdiv_coshl_sinhl_reassoc(fp128 %a){
+; CHECK-LABEL: @fdiv_coshl_sinhl_reassoc(
+; CHECK-NEXT: [[TANH:%.*]] = call reassoc fp128 @tanhl(fp128 [[A]])
+; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc fp128 0xL00000000000000003FFF000000000000, [[TANH]]
+; CHECK-NEXT: ret fp128 [[DIV]]
+;
+ %1 = call reassoc fp128 @coshl(fp128 %a)
+ %2 = call reassoc fp128 @sinhl(fp128 %a)
+ %div = fdiv reassoc fp128 %1, %2
+ ret fp128 %div
+}
+
+
+define float @fdiv_coshf_sinhf_reassoc(float %a){
+; CHECK-LABEL: @fdiv_coshf_sinhf_reassoc(
+; CHECK-NEXT: [[TANH:%.*]] = call reassoc float @tanhf(float [[A]])
+; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc float 1.000000e+00, [[TANH]]
+; CHECK-NEXT: ret float [[DIV]]
+;
+ %1 = call reassoc float @coshf(float %a)
+ %2 = call reassoc float @sinhf(float %a)
+ %div = fdiv reassoc float %1, %2
+ ret float %div
+}
+
+declare double @cosh(double)
+declare float @coshf(float)
+declare fp128 @coshl(fp128)
+
+declare double @sinh(double)
+declare float @sinhf(float)
+declare fp128 @sinhl(fp128)
+
+declare void @use(double)
diff --git a/llvm/test/Transforms/InstCombine/fdiv-sinh-cosh.ll b/llvm/test/Transforms/InstCombine/fdiv-sinh-cosh.ll
new file mode 100644
index 00000000000000..e81b07ae6428e0
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/fdiv-sinh-cosh.ll
@@ -0,0 +1,111 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+define double @fdiv_sinh_cosh(double %a) {
+; CHECK-LABEL: @fdiv_sinh_cosh(
+; CHECK-NEXT: [[TMP1:%.*]] = call double @sinh(double [[A:%.*]])
+; CHECK-NEXT: [[TMP2:%.*]] = call double @cosh(double [[A]])
+; CHECK-NEXT: [[DIV:%.*]] = fdiv double [[TMP1]], [[TMP2]]
+; CHECK-NEXT: ret double [[DIV]]
+;
+ %1 = call double @sinh(double %a)
+ %2 = call double @cosh(double %a)
+ %div = fdiv double %1, %2
+ ret double %div
+}
+
+define double @fdiv_reassoc_sinh_strict_cosh_strict(double %a, ptr dereferenceable(2) %dummy) {
+; CHECK-LABEL: @fdiv_reassoc_sinh_strict_cosh_strict(
+; CHECK-NEXT: [[TANH:%.*]] = call reassoc double @tanh(double [[A]])
+; CHECK-NEXT: ret double [[TANH]]
+;
+ %1 = call double @sinh(double %a)
+ %2 = call double @cosh(double %a)
+ %div = fdiv reassoc double %1, %2
+ ret double %div
+}
+
+define double @fdiv_reassoc_sinh_reassoc_cosh_strict(double %a) {
+; CHECK-LABEL: @fdiv_reassoc_sinh_reassoc_cosh_strict(
+; CHECK-NEXT: [[TANH:%.*]] = call reassoc double @tanh(double [[A]])
+; CHECK-NEXT: ret double [[TANH]]
+;
+ %1 = call reassoc double @sinh(double %a)
+ %2 = call double @cosh(double %a)
+ %div = fdiv reassoc double %1, %2
+ ret double %div
+}
+
+define double @fdiv_sin_cos_reassoc_multiple_uses_sinh(double %a) {
+; CHECK-LABEL: @fdiv_sin_cos_reassoc_multiple_uses_sinh(
+; CHECK-NEXT: [[TMP1:%.*]] = call reassoc double @sinh(double [[A:%.*]])
+; CHECK-NEXT: [[TMP2:%.*]] = call reassoc double @cosh(double [[A]])
+; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc double [[TMP1]], [[TMP2]]
+; CHECK-NEXT: call void @use(double [[TMP1]])
+; CHECK-NEXT: ret double [[DIV]]
+;
+ %1 = call reassoc double @sinh(double %a)
+ %2 = call reassoc double @cosh(double %a)
+ %div = fdiv reassoc double %1, %2
+ call void @use(double %1)
+ ret double %div
+}
+
+define double @fdiv_sin_cos_reassoc_multiple_uses_cosh(double %a) {
+; CHECK-LABEL: @fdiv_sin_cos_reassoc_multiple_uses_cosh(
+; CHECK-NEXT: [[TMP1:%.*]] = call reassoc double @sinh(double [[A:%.*]])
+; CHECK-NEXT: [[TMP2:%.*]] = call reassoc double @cosh(double [[A]])
+; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc double [[TMP1]], [[TMP2]]
+; CHECK-NEXT: call void @use(double [[TMP2]])
+; CHECK-NEXT: ret double [[DIV]]
+;
+ %1 = call reassoc double @sinh(double %a)
+ %2 = call reassoc double @cosh(double %a)
+ %div = fdiv reassoc double %1, %2
+ call void @use(double %2)
+ ret double %div
+}
+
+
+define double @fdiv_sinh_cosh_reassoc(double %a) {
+; CHECK-LABEL: @fdiv_sinh_cosh_reassoc(
+; CHECK-NEXT: [[TANH:%.*]] = call reassoc double @tanh(double [[A]])
+; CHECK-NEXT: ret double [[TANH]]
+;
+ %1 = call reassoc double @sinh(double %a)
+ %2 = call reassoc double @cosh(double %a)
+ %div = fdiv reassoc double %1, %2
+ ret double %div
+}
+
+define float @fdiv_sinhf_coshf_reassoc(float %a) {
+; CHECK-LABEL: @fdiv_sinhf_coshf_reassoc(
+; CHECK-NEXT: [[TANH:%.*]] = call reassoc float @tanhf(float [[A]])
+; CHECK-NEXT: ret float [[TANH]]
+;
+ %1 = call reassoc float @sinhf(float %a)
+ %2 = call reassoc float @coshf(float %a)
+ %div = fdiv reassoc float %1, %2
+ ret float %div
+}
+
+define fp128 @fdiv_sinhl_coshl_reassoc(fp128 %a) {
+; CHECK-LABEL: @fdiv_sinhl_coshl_reassoc(
+; CHECK-NEXT: [[TANH:%.*]] = call reassoc fp128 @tanhl(fp128 [[A]])
+; CHECK-NEXT: ret fp128 [[TANH]]
+;
+ %1 = call reassoc fp128 @sinhl(fp128 %a)
+ %2 = call reassoc fp128 @coshl(fp128 %a)
+ %div = fdiv reassoc fp128 %1, %2
+ ret fp128 %div
+}
+
+declare double @cosh(double)
+declare float @coshf(float)
+declare fp128 @coshl(fp128)
+
+declare double @sinh(double)
+declare float @sinhf(float)
+declare fp128 @sinhl(fp128)
+
+declare void @use(double)
diff --git a/llvm/test/Transforms/SeparateConstOffsetFromGEP/NVPTX/lower-gep-reorder.ll b/llvm/test/Transforms/SeparateConstOffsetFromGEP/NVPTX/lower-gep-reorder.ll
index 516f395e061180..a91c8172177f9d 100644
--- a/llvm/test/Transforms/SeparateConstOffsetFromGEP/NVPTX/lower-gep-reorder.ll
+++ b/llvm/test/Transforms/SeparateConstOffsetFromGEP/NVPTX/lower-gep-reorder.ll
@@ -6,12 +6,15 @@ define protected amdgpu_kernel void @sink_addr(ptr %in.ptr, i64 %in.idx0, i64 %i
; CHECK-SAME: ptr [[IN_PTR:%.*]], i64 [[IN_IDX0:%.*]], i64 [[IN_IDX1:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[IDX0:%.*]] = getelementptr [8192 x i64], ptr [[IN_PTR]], i64 [[IN_IDX0]], i64 [[IN_IDX1]]
-; CHECK-NEXT: [[CONST1:%.*]] = getelementptr [8192 x i64], ptr [[IN_PTR]], i64 [[IN_IDX0]], i64 256
-; CHECK-NEXT: [[IDX1:%.*]] = getelementptr i64, ptr [[CONST1]], i64 [[IN_IDX1]]
-; CHECK-NEXT: [[CONST2:%.*]] = getelementptr [8192 x i64], ptr [[IN_PTR]], i64 [[IN_IDX0]], i64 512
-; CHECK-NEXT: [[IDX2:%.*]] = getelementptr i64, ptr [[CONST2]], i64 [[IN_IDX1]]
-; CHECK-NEXT: [[CONST3:%.*]] = getelementptr [8192 x i64], ptr [[IN_PTR]], i64 [[IN_IDX0]], i64 768
-; CHECK-NEXT: [[IDX3:%.*]] = getelementptr i64, ptr [[CONST3]], i64 [[IN_IDX1]]
+; CHECK-NEXT: [[TMP0:%.*]] = getelementptr [8192 x i64], ptr [[IN_PTR]], i64 [[IN_IDX0]], i64 0
+; CHECK-NEXT: [[CONST11:%.*]] = getelementptr i8, ptr [[TMP0]], i64 2048
+; CHECK-NEXT: [[IDX1:%.*]] = getelementptr i64, ptr [[CONST11]], i64 [[IN_IDX1]]
+; CHECK-NEXT: [[TMP1:%.*]] = getelementptr [8192 x i64], ptr [[IN_PTR]], i64 [[IN_IDX0]], i64 0
+; CHECK-NEXT: [[CONST22:%.*]] = getelementptr i8, ptr [[TMP1]], i64 4096
+; CHECK-NEXT: [[IDX2:%.*]] = getelementptr i64, ptr [[CONST22]], i64 [[IN_IDX1]]
+; CHECK-NEXT: [[TMP2:%.*]] = getelementptr [8192 x i64], ptr [[IN_PTR]], i64 [[IN_IDX0]], i64 0
+; CHECK-NEXT: [[CONST33:%.*]] = getelementptr i8, ptr [[TMP2]], i64 6144
+; CHECK-NEXT: [[IDX3:%.*]] = getelementptr i64, ptr [[CONST33]], i64 [[IN_IDX1]]
; CHECK-NEXT: [[CMP0:%.*]] = icmp eq i64 [[IN_IDX0]], 0
; CHECK-NEXT: br i1 [[CMP0]], label [[BB_1:%.*]], label [[END:%.*]]
; CHECK: bb.1:
More information about the llvm-commits
mailing list