Index: llvm/trunk/include/llvm/Analysis/ConstantFolding.h =================================================================== --- llvm/trunk/include/llvm/Analysis/ConstantFolding.h +++ llvm/trunk/include/llvm/Analysis/ConstantFolding.h @@ -23,6 +23,7 @@ namespace llvm { class APInt; template class ArrayRef; +class CallSite; class Constant; class ConstantExpr; class ConstantVector; @@ -125,6 +126,10 @@ /// with the specified arguments, returning null if unsuccessful. Constant *ConstantFoldCall(Function *F, ArrayRef Operands, const TargetLibraryInfo *TLI = nullptr); + +/// \brief Check whether the given call has no side-effects. +/// Specifically checks for math routimes which sometimes set errno. +bool isMathLibCallNoop(CallSite CS, const TargetLibraryInfo *TLI); } #endif Index: llvm/trunk/lib/Analysis/ConstantFolding.cpp =================================================================== --- llvm/trunk/lib/Analysis/ConstantFolding.cpp +++ llvm/trunk/lib/Analysis/ConstantFolding.cpp @@ -1967,3 +1967,152 @@ return ConstantFoldScalarCall(Name, F->getIntrinsicID(), Ty, Operands, TLI); } + +bool llvm::isMathLibCallNoop(CallSite CS, const TargetLibraryInfo *TLI) { + // FIXME: Refactor this code; this duplicates logic in LibCallsShrinkWrap + // (and to some extent ConstantFoldScalarCall). + Function *F = CS.getCalledFunction(); + if (!F) + return false; + + LibFunc::Func Func; + if (!TLI || !TLI->getLibFunc(*F, Func)) + return false; + + if (CS.getNumArgOperands() == 1) { + if (ConstantFP *OpC = dyn_cast(CS.getArgOperand(0))) { + const APFloat &Op = OpC->getValueAPF(); + switch (Func) { + case LibFunc::logl: + case LibFunc::log: + case LibFunc::logf: + case LibFunc::log2l: + case LibFunc::log2: + case LibFunc::log2f: + case LibFunc::log10l: + case LibFunc::log10: + case LibFunc::log10f: + return Op.isNaN() || (!Op.isZero() && !Op.isNegative()); + + case LibFunc::expl: + case LibFunc::exp: + case LibFunc::expf: + // FIXME: These boundaries are slightly conservative. + if (OpC->getType()->isDoubleTy()) + return Op.compare(APFloat(-745.0)) != APFloat::cmpLessThan && + Op.compare(APFloat(709.0)) != APFloat::cmpGreaterThan; + if (OpC->getType()->isFloatTy()) + return Op.compare(APFloat(-103.0f)) != APFloat::cmpLessThan && + Op.compare(APFloat(88.0f)) != APFloat::cmpGreaterThan; + break; + + case LibFunc::exp2l: + case LibFunc::exp2: + case LibFunc::exp2f: + // FIXME: These boundaries are slightly conservative. + if (OpC->getType()->isDoubleTy()) + return Op.compare(APFloat(-1074.0)) != APFloat::cmpLessThan && + Op.compare(APFloat(1023.0)) != APFloat::cmpGreaterThan; + if (OpC->getType()->isFloatTy()) + return Op.compare(APFloat(-149.0f)) != APFloat::cmpLessThan && + Op.compare(APFloat(127.0f)) != APFloat::cmpGreaterThan; + break; + + case LibFunc::sinl: + case LibFunc::sin: + case LibFunc::sinf: + case LibFunc::cosl: + case LibFunc::cos: + case LibFunc::cosf: + return !Op.isInfinity(); + + case LibFunc::tanl: + case LibFunc::tan: + case LibFunc::tanf: { + // FIXME: Stop using the host math library. + // FIXME: The computation isn't done in the right precision. + Type *Ty = OpC->getType(); + if (Ty->isDoubleTy() || Ty->isFloatTy() || Ty->isHalfTy()) { + double OpV = getValueAsDouble(OpC); + return ConstantFoldFP(tan, OpV, Ty) != nullptr; + } + break; + } + + case LibFunc::asinl: + case LibFunc::asin: + case LibFunc::asinf: + case LibFunc::acosl: + case LibFunc::acos: + case LibFunc::acosf: + return Op.compare(APFloat(Op.getSemantics(), "-1")) != + APFloat::cmpLessThan && + Op.compare(APFloat(Op.getSemantics(), "1")) != + APFloat::cmpGreaterThan; + + case LibFunc::sinh: + case LibFunc::cosh: + case LibFunc::sinhf: + case LibFunc::coshf: + case LibFunc::sinhl: + case LibFunc::coshl: + // FIXME: These boundaries are slightly conservative. + if (OpC->getType()->isDoubleTy()) + return Op.compare(APFloat(-710.0)) != APFloat::cmpLessThan && + Op.compare(APFloat(710.0)) != APFloat::cmpGreaterThan; + if (OpC->getType()->isFloatTy()) + return Op.compare(APFloat(-89.0f)) != APFloat::cmpLessThan && + Op.compare(APFloat(89.0f)) != APFloat::cmpGreaterThan; + break; + + case LibFunc::sqrtl: + case LibFunc::sqrt: + case LibFunc::sqrtf: + return Op.isNaN() || Op.isZero() || !Op.isNegative(); + + // FIXME: Add more functions: sqrt_finite, atanh, expm1, log1p, + // maybe others? + default: + break; + } + } + } + + if (CS.getNumArgOperands() == 2) { + ConstantFP *Op0C = dyn_cast(CS.getArgOperand(0)); + ConstantFP *Op1C = dyn_cast(CS.getArgOperand(1)); + if (Op0C && Op1C) { + const APFloat &Op0 = Op0C->getValueAPF(); + const APFloat &Op1 = Op1C->getValueAPF(); + + switch (Func) { + case LibFunc::powl: + case LibFunc::pow: + case LibFunc::powf: { + // FIXME: Stop using the host math library. + // FIXME: The computation isn't done in the right precision. + Type *Ty = Op0C->getType(); + if (Ty->isDoubleTy() || Ty->isFloatTy() || Ty->isHalfTy()) { + if (Ty == Op1C->getType()) { + double Op0V = getValueAsDouble(Op0C); + double Op1V = getValueAsDouble(Op1C); + return ConstantFoldBinaryFP(pow, Op0V, Op1V, Ty) != nullptr; + } + } + break; + } + + case LibFunc::fmodl: + case LibFunc::fmod: + case LibFunc::fmodf: + return Op0.isNaN() || Op1.isNaN() || + (!Op0.isInfinity() && !Op1.isZero()); + + default: + break; + } + } + } + + return false; +} Index: llvm/trunk/lib/Transforms/Utils/Local.cpp =================================================================== --- llvm/trunk/lib/Transforms/Utils/Local.cpp +++ llvm/trunk/lib/Transforms/Utils/Local.cpp @@ -340,6 +340,10 @@ if (Constant *C = dyn_cast(CI->getArgOperand(0))) return C->isNullValue() || isa(C); + if (CallSite CS = CallSite(I)) + if (isMathLibCallNoop(CS, TLI)) + return true; + return false; } Index: llvm/trunk/test/Transforms/DCE/calls-errno.ll =================================================================== --- llvm/trunk/test/Transforms/DCE/calls-errno.ll +++ llvm/trunk/test/Transforms/DCE/calls-errno.ll @@ -0,0 +1,91 @@ +; RUN: opt < %s -dce -S | FileCheck %s + +declare double @acos(double) nounwind +declare double @asin(double) nounwind +declare double @atan(double) nounwind +declare double @atan2(double, double) nounwind +declare double @ceil(double) nounwind +declare double @cos(double) nounwind +declare double @cosh(double) nounwind +declare double @exp(double) nounwind +declare double @exp2(double) nounwind +declare double @fabs(double) nounwind +declare double @floor(double) nounwind +declare double @fmod(double, double) nounwind +declare double @log(double) nounwind +declare double @log10(double) nounwind +declare double @pow(double, double) nounwind +declare double @sin(double) nounwind +declare double @sinh(double) nounwind +declare double @sqrt(double) nounwind +declare double @tan(double) nounwind +declare double @tanh(double) nounwind + +declare float @acosf(float) nounwind +declare float @asinf(float) nounwind +declare float @atanf(float) nounwind +declare float @atan2f(float, float) nounwind +declare float @ceilf(float) nounwind +declare float @cosf(float) nounwind +declare float @coshf(float) nounwind +declare float @expf(float) nounwind +declare float @exp2f(float) nounwind +declare float @fabsf(float) nounwind +declare float @floorf(float) nounwind +declare float @fmodf(float, float) nounwind +declare float @logf(float) nounwind +declare float @log10f(float) nounwind +declare float @powf(float, float) nounwind +declare float @sinf(float) nounwind +declare float @sinhf(float) nounwind +declare float @sqrtf(float) nounwind +declare float @tanf(float) nounwind +declare float @tanhf(float) nounwind + +define void @T() { +entry: +; CHECK-LABEL: @T( +; CHECK-NEXT: entry: + +; log(0) produces a pole error +; CHECK-NEXT: %log1 = call double @log(double 0.000000e+00) + %log1 = call double @log(double 0.000000e+00) + +; log(-1) produces a domain error +; CHECK-NEXT: %log2 = call double @log(double -1.000000e+00) + %log2 = call double @log(double -1.000000e+00) + +; log(1) is 0 + %log3 = call double @log(double 1.000000e+00) + +; exp(100) is roughly 2.6e+43 + %exp1 = call double @exp(double 1.000000e+02) + +; exp(1000) is a range error +; CHECK-NEXT: %exp2 = call double @exp(double 1.000000e+03) + %exp2 = call double @exp(double 1.000000e+03) + +; cos(0) is 1 + %cos1 = call double @cos(double 0.000000e+00) + +; cos(inf) is a domain error +; CHECK-NEXT: %cos2 = call double @cos(double 0x7FF0000000000000) + %cos2 = call double @cos(double 0x7FF0000000000000) + +; pow(0, 1) is 0 + %pow1 = call double @pow(double 0x7FF0000000000000, double 1.000000e+00) + +; pow(0, -1) is a pole error +; CHECK-NEXT: %pow2 = call double @pow(double 0.000000e+00, double -1.000000e+00) + %pow2 = call double @pow(double 0.000000e+00, double -1.000000e+00) + +; fmod(inf, nan) is nan + %fmod1 = call double @fmod(double 0x7FF0000000000000, double 0x7FF0000000000001) + +; fmod(inf, 1) is a domain error +; CHECK-NEXT: %fmod2 = call double @fmod(double 0x7FF0000000000000, double 1.000000e+00) + %fmod2 = call double @fmod(double 0x7FF0000000000000, double 1.000000e+00) + +; CHECK-NEXT: ret void + ret void +}