Skip to content

Commit

Permalink
DCE math library calls with a constant operand.
Browse files Browse the repository at this point in the history
On platforms which use -fmath-errno, math libcalls without any uses
require some extra checks to figure out if they are actually dead.

Fixes https://llvm.org/bugs/show_bug.cgi?id=30464 .

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

llvm-svn: 285857
  • Loading branch information
Eli Friedman committed Nov 2, 2016
1 parent ce65364 commit b6befc3
Show file tree
Hide file tree
Showing 4 changed files with 249 additions and 0 deletions.
5 changes: 5 additions & 0 deletions llvm/include/llvm/Analysis/ConstantFolding.h
Expand Up @@ -23,6 +23,7 @@
namespace llvm {
class APInt;
template <typename T> class ArrayRef;
class CallSite;
class Constant;
class ConstantExpr;
class ConstantVector;
Expand Down Expand Up @@ -125,6 +126,10 @@ bool canConstantFoldCallTo(const Function *F);
/// with the specified arguments, returning null if unsuccessful.
Constant *ConstantFoldCall(Function *F, ArrayRef<Constant *> 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
149 changes: 149 additions & 0 deletions llvm/lib/Analysis/ConstantFolding.cpp
Expand Up @@ -1967,3 +1967,152 @@ llvm::ConstantFoldCall(Function *F, ArrayRef<Constant *> Operands,

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<ConstantFP>(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<ConstantFP>(CS.getArgOperand(0));
ConstantFP *Op1C = dyn_cast<ConstantFP>(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;
}
4 changes: 4 additions & 0 deletions llvm/lib/Transforms/Utils/Local.cpp
Expand Up @@ -340,6 +340,10 @@ bool llvm::isInstructionTriviallyDead(Instruction *I,
if (Constant *C = dyn_cast<Constant>(CI->getArgOperand(0)))
return C->isNullValue() || isa<UndefValue>(C);

if (CallSite CS = CallSite(I))
if (isMathLibCallNoop(CS, TLI))
return true;

return false;
}

Expand Down
91 changes: 91 additions & 0 deletions llvm/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
}

0 comments on commit b6befc3

Please sign in to comment.