Skip to content

Commit

Permalink
[InstCombine] Missed optimization in math expression: sin(x) / cos(x)…
Browse files Browse the repository at this point in the history
… => tan(x)

Summary: This patch enables folding sin(x) / cos(x) -> tan(x), cos(x) / sin(x) -> 1 / tan(x) under -ffast-math flag

Reviewers: hfinkel, spatel

Reviewed By: spatel

Subscribers: andrew.w.kaylor, efriedma, scanon, llvm-commits

Differential Revision: https://reviews.llvm.org/D41286

llvm-svn: 322255
  • Loading branch information
Quolyk committed Jan 11, 2018
1 parent 0b59034 commit e5fbf59
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 15 deletions.
7 changes: 7 additions & 0 deletions llvm/include/llvm/Transforms/Utils/BuildLibCalls.h
Expand Up @@ -15,6 +15,7 @@
#ifndef LLVM_TRANSFORMS_UTILS_BUILDLIBCALLS_H
#define LLVM_TRANSFORMS_UTILS_BUILDLIBCALLS_H

#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/IR/IRBuilder.h"

namespace llvm {
Expand All @@ -29,6 +30,12 @@ namespace llvm {
/// Returns true if any attributes were set and false otherwise.
bool inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI);

/// Check whether the overloaded unary floating point function
/// corresponding to \a Ty is available.
bool hasUnaryFloatFn(const TargetLibraryInfo *TLI, Type *Ty,
LibFunc DoubleFn, LibFunc FloatFn,
LibFunc LongDoubleFn);

/// Return V if it is an i8*, otherwise cast it to i8*.
Value *castToCStr(Value *V, IRBuilder<> &B);

Expand Down
35 changes: 35 additions & 0 deletions llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
Expand Up @@ -33,6 +33,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/KnownBits.h"
#include "llvm/Transforms/InstCombine/InstCombineWorklist.h"
#include "llvm/Transforms/Utils/BuildLibCalls.h"
#include <cassert>
#include <cstddef>
#include <cstdint>
Expand Down Expand Up @@ -1468,6 +1469,40 @@ Instruction *InstCombiner::visitFDiv(BinaryOperator &I) {
}
}

if (AllowReassociate &&
Op0->hasOneUse() && Op1->hasOneUse()) {
Value *A;
// sin(a) / cos(a) -> tan(a)
if (match(Op0, m_Intrinsic<Intrinsic::sin>(m_Value(A))) &&
match(Op1, m_Intrinsic<Intrinsic::cos>(m_Specific(A)))) {
if (hasUnaryFloatFn(&TLI, I.getType(), LibFunc_tan,
LibFunc_tanf, LibFunc_tanl)) {
IRBuilder<> B(&I);
IRBuilder<>::FastMathFlagGuard Guard(B);
B.setFastMathFlags(I.getFastMathFlags());
Value *Tan = emitUnaryFloatFnCall(A, TLI.getName(LibFunc_tan),
B, I.getFunction()->getAttributes());
return replaceInstUsesWith(I, Tan);
}
}

// cos(a) / sin(a) -> 1/tan(a)
if (match(Op0, m_Intrinsic<Intrinsic::cos>(m_Value(A))) &&
match(Op1, m_Intrinsic<Intrinsic::sin>(m_Specific(A)))) {
if (hasUnaryFloatFn(&TLI, I.getType(), LibFunc_tan,
LibFunc_tanf, LibFunc_tanl)) {
IRBuilder<> B(&I);
IRBuilder<>::FastMathFlagGuard Guard(B);
B.setFastMathFlags(I.getFastMathFlags());
Value *Tan = emitUnaryFloatFnCall(A, TLI.getName(LibFunc_tan),
B, I.getFunction()->getAttributes());
Value *One = ConstantFP::get(Tan->getType(), 1.0);
Value *Div = B.CreateFDiv(One, Tan);
return replaceInstUsesWith(I, Div);
}
}
}

Value *LHS;
Value *RHS;

Expand Down
13 changes: 13 additions & 0 deletions llvm/lib/Transforms/Utils/BuildLibCalls.cpp
Expand Up @@ -709,6 +709,19 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
}
}

bool llvm::hasUnaryFloatFn(const TargetLibraryInfo *TLI, Type *Ty,
LibFunc DoubleFn, LibFunc FloatFn,
LibFunc LongDoubleFn) {
switch (Ty->getTypeID()) {
case Type::FloatTyID:
return TLI->has(FloatFn);
case Type::DoubleTyID:
return TLI->has(DoubleFn);
default:
return TLI->has(LongDoubleFn);
}
}

//- Emit LibCalls ------------------------------------------------------------//

Value *llvm::castToCStr(Value *V, IRBuilder<> &B) {
Expand Down
15 changes: 0 additions & 15 deletions llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
Expand Up @@ -104,21 +104,6 @@ static bool callHasFloatingPointArgument(const CallInst *CI) {
});
}

/// \brief Check whether the overloaded unary floating point function
/// corresponding to \a Ty is available.
static bool hasUnaryFloatFn(const TargetLibraryInfo *TLI, Type *Ty,
LibFunc DoubleFn, LibFunc FloatFn,
LibFunc LongDoubleFn) {
switch (Ty->getTypeID()) {
case Type::FloatTyID:
return TLI->has(FloatFn);
case Type::DoubleTyID:
return TLI->has(DoubleFn);
default:
return TLI->has(LongDoubleFn);
}
}

//===----------------------------------------------------------------------===//
// String and Memory Library Call Optimizations
//===----------------------------------------------------------------------===//
Expand Down
113 changes: 113 additions & 0 deletions llvm/test/Transforms/InstCombine/fdiv-cos-sin.ll
@@ -0,0 +1,113 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -instcombine < %s | FileCheck %s

define double @fdiv_cos_sin(double %a) {
; CHECK-LABEL: @fdiv_cos_sin(
; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.cos.f64(double [[A:%.*]])
; CHECK-NEXT: [[TMP2:%.*]] = call double @llvm.sin.f64(double [[A]])
; CHECK-NEXT: [[DIV:%.*]] = fdiv double [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret double [[DIV]]
;
%1 = call double @llvm.cos.f64(double %a)
%2 = call double @llvm.sin.f64(double %a)
%div = fdiv double %1, %2
ret double %div
}

define double @fdiv_strict_cos_strict_sin_fast(double %a) {
; CHECK-LABEL: @fdiv_strict_cos_strict_sin_fast(
; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.cos.f64(double [[A:%.*]])
; CHECK-NEXT: [[TMP2:%.*]] = call fast double @llvm.sin.f64(double [[A]])
; CHECK-NEXT: [[DIV:%.*]] = fdiv double [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret double [[DIV]]
;
%1 = call double @llvm.cos.f64(double %a)
%2 = call fast double @llvm.sin.f64(double %a)
%div = fdiv double %1, %2
ret double %div
}

define double @fdiv_fast_cos_strict_sin_strict(double %a) {
; CHECK-LABEL: @fdiv_fast_cos_strict_sin_strict(
; CHECK-NEXT: [[TAN:%.*]] = call fast double @tan(double [[A:%.*]])
; CHECK-NEXT: [[TMP1:%.*]] = fdiv fast double 1.000000e+00, [[TAN]]
; CHECK-NEXT: ret double [[TMP1]]
;
%1 = call double @llvm.cos.f64(double %a)
%2 = call double @llvm.sin.f64(double %a)
%div = fdiv fast double %1, %2
ret double %div
}

define double @fdiv_fast_cos_fast_sin_strict(double %a) {
; CHECK-LABEL: @fdiv_fast_cos_fast_sin_strict(
; CHECK-NEXT: [[TAN:%.*]] = call fast double @tan(double [[A:%.*]])
; CHECK-NEXT: [[TMP1:%.*]] = fdiv fast double 1.000000e+00, [[TAN]]
; CHECK-NEXT: ret double [[TMP1]]
;
%1 = call fast double @llvm.cos.f64(double %a)
%2 = call double @llvm.sin.f64(double %a)
%div = fdiv fast double %1, %2
ret double %div
}

define double @fdiv_cos_sin_fast_multiple_uses(double %a) {
; CHECK-LABEL: @fdiv_cos_sin_fast_multiple_uses(
; CHECK-NEXT: [[TMP1:%.*]] = call fast double @llvm.cos.f64(double [[A:%.*]])
; CHECK-NEXT: [[TMP2:%.*]] = call fast double @llvm.sin.f64(double [[A]])
; CHECK-NEXT: [[DIV:%.*]] = fdiv fast double [[TMP1]], [[TMP2]]
; CHECK-NEXT: call void @use(double [[TMP2]])
; CHECK-NEXT: ret double [[DIV]]
;
%1 = call fast double @llvm.cos.f64(double %a)
%2 = call fast double @llvm.sin.f64(double %a)
%div = fdiv fast double %1, %2
call void @use(double %2)
ret double %div
}

define double @fdiv_cos_sin_fast(double %a) {
; CHECK-LABEL: @fdiv_cos_sin_fast(
; CHECK-NEXT: [[TAN:%.*]] = call fast double @tan(double [[A:%.*]])
; CHECK-NEXT: [[TMP1:%.*]] = fdiv fast double 1.000000e+00, [[TAN]]
; CHECK-NEXT: ret double [[TMP1]]
;
%1 = call fast double @llvm.cos.f64(double %a)
%2 = call fast double @llvm.sin.f64(double %a)
%div = fdiv fast double %1, %2
ret double %div
}

define float @fdiv_cosf_sinf_fast(float %a) {
; CHECK-LABEL: @fdiv_cosf_sinf_fast(
; CHECK-NEXT: [[TANF:%.*]] = call fast float @tanf(float [[A:%.*]])
; CHECK-NEXT: [[TMP1:%.*]] = fdiv fast float 1.000000e+00, [[TANF]]
; CHECK-NEXT: ret float [[TMP1]]
;
%1 = call fast float @llvm.cos.f32(float %a)
%2 = call fast float @llvm.sin.f32(float %a)
%div = fdiv fast float %1, %2
ret float %div
}

define fp128 @fdiv_cosfp128_sinfp128_fast(fp128 %a) {
; CHECK-LABEL: @fdiv_cosfp128_sinfp128_fast(
; CHECK-NEXT: [[TANL:%.*]] = call fast fp128 @tanl(fp128 [[A:%.*]])
; CHECK-NEXT: [[TMP1:%.*]] = fdiv fast fp128 0xL00000000000000003FFF000000000000, [[TANL]]
; CHECK-NEXT: ret fp128 [[TMP1]]
;
%1 = call fast fp128 @llvm.cos.fp128(fp128 %a)
%2 = call fast fp128 @llvm.sin.fp128(fp128 %a)
%div = fdiv fast fp128 %1, %2
ret fp128 %div
}

declare double @llvm.cos.f64(double)
declare float @llvm.cos.f32(float)
declare fp128 @llvm.cos.fp128(fp128)

declare double @llvm.sin.f64(double)
declare float @llvm.sin.f32(float)
declare fp128 @llvm.sin.fp128(fp128)

declare void @use(double)
108 changes: 108 additions & 0 deletions llvm/test/Transforms/InstCombine/fdiv-sin-cos.ll
@@ -0,0 +1,108 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -instcombine < %s | FileCheck %s

define double @fdiv_sin_cos(double %a) {
; CHECK-LABEL: @fdiv_sin_cos(
; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.sin.f64(double [[A:%.*]])
; CHECK-NEXT: [[TMP2:%.*]] = call double @llvm.cos.f64(double [[A]])
; CHECK-NEXT: [[DIV:%.*]] = fdiv double [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret double [[DIV]]
;
%1 = call double @llvm.sin.f64(double %a)
%2 = call double @llvm.cos.f64(double %a)
%div = fdiv double %1, %2
ret double %div
}

define double @fdiv_strict_sin_strict_cos_fast(double %a) {
; CHECK-LABEL: @fdiv_strict_sin_strict_cos_fast(
; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.sin.f64(double [[A:%.*]])
; CHECK-NEXT: [[TMP2:%.*]] = call fast double @llvm.cos.f64(double [[A]])
; CHECK-NEXT: [[DIV:%.*]] = fdiv double [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret double [[DIV]]
;
%1 = call double @llvm.sin.f64(double %a)
%2 = call fast double @llvm.cos.f64(double %a)
%div = fdiv double %1, %2
ret double %div
}

define double @fdiv_fast_sin_strict_cos_strict(double %a) {
; CHECK-LABEL: @fdiv_fast_sin_strict_cos_strict(
; CHECK-NEXT: [[TAN:%.*]] = call fast double @tan(double [[A:%.*]])
; CHECK-NEXT: ret double [[TAN]]
;
%1 = call double @llvm.sin.f64(double %a)
%2 = call double @llvm.cos.f64(double %a)
%div = fdiv fast double %1, %2
ret double %div
}

define double @fdiv_fast_sin_fast_cos_strict(double %a) {
; CHECK-LABEL: @fdiv_fast_sin_fast_cos_strict(
; CHECK-NEXT: [[TAN:%.*]] = call fast double @tan(double [[A:%.*]])
; CHECK-NEXT: ret double [[TAN]]
;
%1 = call fast double @llvm.sin.f64(double %a)
%2 = call double @llvm.cos.f64(double %a)
%div = fdiv fast double %1, %2
ret double %div
}

define double @fdiv_sin_cos_fast_multiple_uses(double %a) {
; CHECK-LABEL: @fdiv_sin_cos_fast_multiple_uses(
; CHECK-NEXT: [[TMP1:%.*]] = call fast double @llvm.sin.f64(double [[A:%.*]])
; CHECK-NEXT: [[TMP2:%.*]] = call fast double @llvm.cos.f64(double [[A]])
; CHECK-NEXT: [[DIV:%.*]] = fdiv fast double [[TMP1]], [[TMP2]]
; CHECK-NEXT: call void @use(double [[TMP2]])
; CHECK-NEXT: ret double [[DIV]]
;
%1 = call fast double @llvm.sin.f64(double %a)
%2 = call fast double @llvm.cos.f64(double %a)
%div = fdiv fast double %1, %2
call void @use(double %2)
ret double %div
}

define double @fdiv_sin_cos_fast(double %a) {
; CHECK-LABEL: @fdiv_sin_cos_fast(
; CHECK-NEXT: [[TMP1:%.*]] = call fast double @tan(double [[A:%.*]])
; CHECK-NEXT: ret double [[TMP1]]
;
%1 = call fast double @llvm.sin.f64(double %a)
%2 = call fast double @llvm.cos.f64(double %a)
%div = fdiv fast double %1, %2
ret double %div
}

define float @fdiv_sinf_cosf_fast(float %a) {
; CHECK-LABEL: @fdiv_sinf_cosf_fast(
; CHECK-NEXT: [[TMP1:%.*]] = call fast float @tanf(float [[A:%.*]])
; CHECK-NEXT: ret float [[TMP1]]
;
%1 = call fast float @llvm.sin.f32(float %a)
%2 = call fast float @llvm.cos.f32(float %a)
%div = fdiv fast float %1, %2
ret float %div
}

define fp128 @fdiv_sinfp128_cosfp128_fast(fp128 %a) {
; CHECK-LABEL: @fdiv_sinfp128_cosfp128_fast(
; CHECK-NEXT: [[TMP0:%.*]] = call fast fp128 @tanl(fp128 [[A:%.*]])
; CHECK-NEXT: ret fp128 [[TMP0]]
;
%1 = call fast fp128 @llvm.sin.fp128(fp128 %a)
%2 = call fast fp128 @llvm.cos.fp128(fp128 %a)
%div = fdiv fast fp128 %1, %2
ret fp128 %div
}

declare double @llvm.sin.f64(double)
declare float @llvm.sin.f32(float)
declare fp128 @llvm.sin.fp128(fp128)

declare double @llvm.cos.f64(double)
declare float @llvm.cos.f32(float)
declare fp128 @llvm.cos.fp128(fp128)

declare void @use(double)

0 comments on commit e5fbf59

Please sign in to comment.