Index: lib/Transforms/InstCombine/InstCombineInternal.h =================================================================== --- lib/Transforms/InstCombine/InstCombineInternal.h +++ lib/Transforms/InstCombine/InstCombineInternal.h @@ -678,6 +678,7 @@ Value *SimplifyVectorOp(BinaryOperator &Inst); + Value *SimplifyTrigOp(Instruction &Inst); /// Given a binary operator, cast instruction, or select which has a PHI node /// as operand #0, see if we can fold the instruction into the PHI (which is Index: lib/Transforms/InstCombine/InstCombineMulDivRem.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineMulDivRem.cpp +++ lib/Transforms/InstCombine/InstCombineMulDivRem.cpp @@ -193,6 +193,9 @@ bool Changed = SimplifyAssociativeOrCommutative(I); Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1); + if (Value *V = SimplifyTrigOp(I)) + return replaceInstUsesWith(I, V); + if (Value *V = SimplifyVectorOp(I)) return replaceInstUsesWith(I, V); @@ -613,6 +616,9 @@ bool Changed = SimplifyAssociativeOrCommutative(I); Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1); + if (Value *V = SimplifyTrigOp(I)) + return replaceInstUsesWith(I, V); + if (Value *V = SimplifyVectorOp(I)) return replaceInstUsesWith(I, V); @@ -1167,6 +1173,9 @@ Instruction *InstCombiner::visitUDiv(BinaryOperator &I) { Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1); + if (Value *V = SimplifyTrigOp(I)) + return replaceInstUsesWith(I, V); + if (Value *V = SimplifyVectorOp(I)) return replaceInstUsesWith(I, V); @@ -1236,6 +1245,9 @@ Instruction *InstCombiner::visitSDiv(BinaryOperator &I) { Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1); + if (Value *V = SimplifyTrigOp(I)) + return replaceInstUsesWith(I, V); + if (Value *V = SimplifyVectorOp(I)) return replaceInstUsesWith(I, V); @@ -1347,6 +1359,9 @@ Instruction *InstCombiner::visitFDiv(BinaryOperator &I) { Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1); + if (Value *V = SimplifyTrigOp(I)) + return replaceInstUsesWith(I, V); + if (Value *V = SimplifyVectorOp(I)) return replaceInstUsesWith(I, V); Index: lib/Transforms/InstCombine/InstructionCombining.cpp =================================================================== --- lib/Transforms/InstCombine/InstructionCombining.cpp +++ lib/Transforms/InstCombine/InstructionCombining.cpp @@ -1419,6 +1419,137 @@ return BO; } +static bool isTrigLibCall(CallInst *CI) { + // We can only hope to do anything useful if we can ignore things like errno + // and floating-point exceptions. + // We already checked the prototype. + return CI->hasFnAttr(Attribute::NoUnwind) && + CI->hasFnAttr(Attribute::ReadNone); +} + +/// Replaces the division or multiplication operations on optimizable trigonometric +/// functions to equivalent trigonometric operation + +Value *InstCombiner::SimplifyTrigOp(Instruction &I) { + + if (((I.getOpcode() == Instruction::FMul) || + (I.getOpcode() == Instruction::Mul))) { + if (isa(I.getOperand(0)) && isa(I.getOperand(1))) { + CallInst *call1 = dyn_cast(I.getOperand(0)); + CallInst *call2 = dyn_cast(I.getOperand(1)); + + // return nullptr if argument to calls are not same. eg tan(x)*tan(y) etc. + if (!(call1->getOperand(0) == call2->getOperand(0))) + return nullptr; + + // tan(x)*cos(x)=sin(x); + + Value *Op1 = call1->getArgOperand(1); + Value *Op2 = call2->getArgOperand(1); + + if ((isTrigLibCall(call1) && + ((Op1->getName() == "tan") || (Op1->getName() == "tanf") || + (Op1->getName() == "tanf")) && + (Op2->getName().startswith("llvm.cos"))) || + (isTrigLibCall(call2) && (Op1->getName().startswith("llvm.cos")) && + ((Op2->getName() == "tan") || (Op2->getName() == "tanf") || + (Op2->getName() == "tanl")))) { + IRBuilder<>::FastMathFlagGuard Guard(Builder); + Builder.setFastMathFlags(I.getFastMathFlags()); + Function *fun = Intrinsic::getDeclaration(I.getModule(), Intrinsic::sin, + I.getType()); + CallInst *SinCall = Builder.CreateCall(fun, call2->getOperand(0)); + + SinCall->takeName(&I); + SinCall->copyFastMathFlags(&I); + + return SinCall; + } + + // sin(x)*cos(x)=sin(2x)*0.5 + + else if (((call1->getOperand(1)->getName().startswith("llvm.sin")) && + (call2->getOperand(1)->getName().startswith("llvm.cos"))) || + ((call1->getOperand(1)->getName().startswith("llvm.cos")) && + (call2->getOperand(1)->getName().startswith("llvm.sin")))) { + IRBuilder<>::FastMathFlagGuard Guard(Builder); + Builder.setFastMathFlags(I.getFastMathFlags()); + Function *fun = Intrinsic::getDeclaration(I.getModule(), Intrinsic::sin, + I.getType()); + + Constant *ConstFloatTwo = + ConstantFP::get(Type::getDoubleTy(I.getContext()), 2.0); + Instruction *MulIns = dyn_cast( + Builder.CreateFMul(ConstFloatTwo, call1->getOperand(0))); + CallInst *SinCall = Builder.CreateCall(fun, MulIns); + Instruction *DivIns = + dyn_cast(Builder.CreateFDiv(SinCall, ConstFloatTwo)); + DivIns->takeName(&I); + DivIns->copyFastMathFlags(&I); + + return DivIns; + } + } + } + + else if ((I.getOpcode() == Instruction::FDiv) || + (I.getOpcode() == Instruction::SDiv) || + (I.getOpcode() == Instruction::UDiv)) { + if (isa(I.getOperand(0)) && isa(I.getOperand(1))) { + CallInst *call1 = dyn_cast(I.getOperand(0)); + CallInst *call2 = dyn_cast(I.getOperand(1)); + + // return nullptr if argument to calls are not same + + if (!(call1->getOperand(0) == call2->getOperand(0))) + return nullptr; + + Value *Op1 = call1->getArgOperand(1); + Value *Op2 = call2->getArgOperand(1); + + // sin/tan=cos; + if ((Op1->getName().startswith("llvm.sin")) && + + (isTrigLibCall(call2) && + ((Op2->getName() == "tan") || (Op2->getName() == "tanf") || + (Op2->getName() == "tanl")))) { + IRBuilder<>::FastMathFlagGuard Guard(Builder); + Builder.setFastMathFlags(I.getFastMathFlags()); + Function *fun = Intrinsic::getDeclaration(I.getModule(), Intrinsic::cos, + I.getType()); + CallInst *CosCall = Builder.CreateCall(fun, call1->getOperand(0)); + CosCall->takeName(&I); + CosCall->copyFastMathFlags(&I); + return CosCall; + } + + // tan/sin=1/cos; + else if ((Op2->getName().startswith("llvm.sin")) && + + (isTrigLibCall(call1) && + ((Op1->getName() == "tan") || (Op1->getName() == "tanf") || + (Op1->getName() == "tanl")))) { + IRBuilder<>::FastMathFlagGuard Guard(Builder); + Builder.setFastMathFlags(I.getFastMathFlags()); + Function *fun = Intrinsic::getDeclaration(I.getModule(), Intrinsic::cos, + I.getType()); + CallInst *CosCall = Builder.CreateCall(fun, call1->getOperand(0)); + Constant *ConstFloatOne = + ConstantFP::get(Type::getDoubleTy(I.getContext()), 1.0); + + Instruction *DivIns = + dyn_cast(Builder.CreateFDiv(ConstFloatOne, CosCall)); + DivIns->takeName(&I); + DivIns->copyFastMathFlags(&I); + + return DivIns; + } + } + } + + return nullptr; +} + /// \brief Makes transformation of binary operation specific for vector types. /// \param Inst Binary operator to transform. /// \return Pointer to node that must replace the original binary operator, or Index: test/Transforms/InstCombine/combined.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/combined.ll @@ -0,0 +1,24 @@ +; RUN: opt < %s -instcombine -S | FileCheck %s + +declare double @tan(double) +declare double @llvm.cos.f64(double) +declare double @llvm.sin.f64(double) + +define double @_Z8combinedd(double) { + %2 = call fast double @llvm.sin.f64(double %0) + %3 = call fast double @tan(double %0) #8 + %4 = fdiv fast double %2, %3 + %5 = call fast double @tan(double %0) #8 + %6 = call fast double @llvm.cos.f64(double %0) + %7 = fmul fast double %5, %6 + %8 = fmul fast double %4, %7 + ret double %8 + +; CHECK-LABEL: @_Z8combinedd( +; CHECK-NEXT: %2 = fmul fast double %0, 2.000000e+00 +; CHECK-NEXT: %3 = call fast double @llvm.sin.f64(double %2) +; CHECK-NEXT: %4 = fmul fast double %3, 5.000000e-01 +; CHECK-NEXT: ret double %4 +} + +attributes #8 = { nounwind readnone } Index: test/Transforms/InstCombine/combined_different_arguments.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/combined_different_arguments.ll @@ -0,0 +1,24 @@ +; RUN: opt < %s -instcombine -S | FileCheck %s + +declare double @tan(double) +declare double @llvm.cos.f64(double) +declare double @llvm.sin.f64(double) + +define double @_Z19different_argumentsdd(double, double) { + %3 = call fast double @llvm.sin.f64(double %0) + %4 = call fast double @tan(double %0) #8 + %5 = fdiv fast double %3, %4 + %6 = call fast double @tan(double %1) #8 + %7 = call fast double @llvm.cos.f64(double %1) + %8 = fmul fast double %6, %7 + %9 = fmul fast double %5, %8 + ret double %9 + +; CHECK-LABEL: @_Z19different_argumentsdd( +; CHECK-NEXT: %3 = call fast double @llvm.cos.f64(double %0) +; CHECK-NEXT: %4 = call fast double @llvm.sin.f64(double %1) +; CHECK-NEXT: %5 = fmul fast double %3, %4 +; CHECK-NEXT: ret double %5 +} + +attributes #8 = { nounwind readnone } Index: test/Transforms/InstCombine/sin_div_tan.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/sin_div_tan.ll @@ -0,0 +1,18 @@ +; RUN: opt < %s -instcombine -S | FileCheck %s + +declare double @tan(double) +declare double @llvm.cos.f64(double) +declare double @llvm.sin.f64(double) + +define double @_Z11sin_div_tand(double) { + %2 = call fast double @llvm.sin.f64(double %0) + %3 = call fast double @tan(double %0) #8 + %4 = fdiv fast double %2, %3 + ret double %4 + +; CHECK-LABEL: @_Z11sin_div_tand( +; CHECK-NEXT: %2 = call fast double @llvm.cos.f64(double %0) +; CHECK-NEXT: ret double %2 +} + +attributes #8 = { nounwind readnone } Index: test/Transforms/InstCombine/sin_mul_cos.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/sin_mul_cos.ll @@ -0,0 +1,18 @@ +; RUN: opt < %s -instcombine -S | FileCheck %s + +declare double @llvm.cos.f64(double) +declare double @llvm.sin.f64(double) + +define double @_Z11sin_mul_cosd(double) { + %2 = call fast double @llvm.sin.f64(double %0) + %3 = call fast double @llvm.cos.f64(double %0) + %4 = fmul fast double %2, %3 + ret double %4 + +; CHECK-LABEL: @_Z11sin_mul_cosd( +; CHECK-NEXT: %2 = fmul fast double %0, 2.000000e+00 +; CHECK-NEXT: %3 = call fast double @llvm.sin.f64(double %2) +; CHECK-NEXT: %4 = fmul fast double %3, 5.000000e-01 +; CHECK-NEXT: ret double %4 +} + Index: test/Transforms/InstCombine/tan_div_sin.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/tan_div_sin.ll @@ -0,0 +1,19 @@ +; RUN: opt < %s -instcombine -S | FileCheck %s + +declare double @tan(double) +declare double @llvm.cos.f64(double) +declare double @llvm.sin.f64(double) + +define double @_Z11tan_div_sind(double) { + %2 = call fast double @tan(double %0) #8 + %3 = call fast double @llvm.sin.f64(double %0) + %4 = fdiv fast double %2, %3 + ret double %4 + +; CHECK-LABEL: @_Z11tan_div_sind( +; CHECK-NEXT: %2 = call fast double @llvm.cos.f64(double %0) +; CHECK-NEXT: %3 = fdiv fast double 1.000000e+00, %2 +; CHECK-NEXT: ret double %3 +} + +attributes #8 = { nounwind readnone } Index: test/Transforms/InstCombine/tan_mul_cos.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/tan_mul_cos.ll @@ -0,0 +1,19 @@ +; RUN: opt < %s -instcombine -S | FileCheck %s + +declare double @tan(double) +declare double @llvm.cos.f64(double) +declare double @llvm.sin.f64(double) + +define double @_Z11tan_mul_cosd(double) { + %2 = call fast double @tan(double %0) #8 + %3 = call fast double @llvm.cos.f64(double %0) + %4 = fmul fast double %2, %3 + ret double %4 + +; CHECK-LABEL:@_Z11tan_mul_cosd( +; CHECK-NEXT: %2 = call fast double @llvm.sin.f64(double %0) +; CHECK-NEXT: ret double %2 + +} + +attributes #8 = { nounwind readnone }