Index: include/llvm/Analysis/TargetLibraryInfo.h =================================================================== --- include/llvm/Analysis/TargetLibraryInfo.h +++ include/llvm/Analysis/TargetLibraryInfo.h @@ -13,6 +13,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/Triple.h" #include "llvm/IR/Function.h" #include "llvm/IR/Module.h" @@ -49,6 +50,7 @@ unsigned char AvailableArray[(LibFunc::NumLibFuncs+3)/4]; llvm::DenseMap CustomNames; + llvm::StringMap CustomNameFuncs; static const char *const StandardNames[LibFunc::NumLibFuncs]; enum AvailabilityState { @@ -115,6 +117,7 @@ setState(F, CustomName); CustomNames[F] = Name; assert(CustomNames.find(F) != CustomNames.end()); + CustomNameFuncs[Name] = F; } else { setState(F, StandardName); } Index: include/llvm/Analysis/TargetLibraryInfo.def =================================================================== --- include/llvm/Analysis/TargetLibraryInfo.def +++ include/llvm/Analysis/TargetLibraryInfo.def @@ -180,6 +180,33 @@ /// void __cxa_guard_release(guard_t *guard); TLI_DEFINE_ENUM_INTERNAL(cxa_guard_release) TLI_DEFINE_STRING_INTERNAL("__cxa_guard_release") +/// int __isfinite(double x); +TLI_DEFINE_ENUM_INTERNAL(isfinite) +TLI_DEFINE_STRING_INTERNAL("__isfinite") +/// int __isfinitef(double x); +TLI_DEFINE_ENUM_INTERNAL(isfinitef) +TLI_DEFINE_STRING_INTERNAL("__isfinitef") +/// int __isfinitel(long double x); +TLI_DEFINE_ENUM_INTERNAL(isfinitel) +TLI_DEFINE_STRING_INTERNAL("__isfinitel") +/// int __isinf(double x); +TLI_DEFINE_ENUM_INTERNAL(isinf) +TLI_DEFINE_STRING_INTERNAL("__isinf") +/// int __isinff(double x); +TLI_DEFINE_ENUM_INTERNAL(isinff) +TLI_DEFINE_STRING_INTERNAL("__isinff") +/// int __isinfl(long double x); +TLI_DEFINE_ENUM_INTERNAL(isinfl) +TLI_DEFINE_STRING_INTERNAL("__isinfl") +/// int __isnan(double x); +TLI_DEFINE_ENUM_INTERNAL(isnan) +TLI_DEFINE_STRING_INTERNAL("__isnan") +/// int __isnanf(double x); +TLI_DEFINE_ENUM_INTERNAL(isnanf) +TLI_DEFINE_STRING_INTERNAL("__isnanf") +/// int __isnanl(long double x); +TLI_DEFINE_ENUM_INTERNAL(isnanl) +TLI_DEFINE_STRING_INTERNAL("__isnanl") /// int __isoc99_scanf (const char *format, ...) TLI_DEFINE_ENUM_INTERNAL(dunder_isoc99_scanf) TLI_DEFINE_STRING_INTERNAL("__isoc99_scanf") Index: include/llvm/Transforms/Utils/SimplifyLibCalls.h =================================================================== --- include/llvm/Transforms/Utils/SimplifyLibCalls.h +++ include/llvm/Transforms/Utils/SimplifyLibCalls.h @@ -130,6 +130,8 @@ Value *optimizeExp2(CallInst *CI, IRBuilder<> &B); Value *optimizeFabs(CallInst *CI, IRBuilder<> &B); Value *optimizeFMinFMax(CallInst *CI, IRBuilder<> &B); + Value *optimizeFPClassification(CallInst *CI, IRBuilder<> &B, + LibFunc::Func Func); Value *optimizeLog(CallInst *CI, IRBuilder<> &B); Value *optimizeSqrt(CallInst *CI, IRBuilder<> &B); Value *optimizeSinCosPi(CallInst *CI, IRBuilder<> &B); Index: lib/Analysis/TargetLibraryInfo.cpp =================================================================== --- lib/Analysis/TargetLibraryInfo.cpp +++ lib/Analysis/TargetLibraryInfo.cpp @@ -385,6 +385,19 @@ TLI.setUnavailable(LibFunc::tmpfile64); } + if (T.isOSLinux()) { + // On Linux (GLIBC), __isfinite* is just __finite*. + TLI.setAvailableWithName(LibFunc::isfinite, "__finite"); + TLI.setAvailableWithName(LibFunc::isfinitef, "__finitef"); + TLI.setAvailableWithName(LibFunc::isfinitel, "__finitel"); + } else if (T.isOSDarwin() || T.isOSNetBSD()) { + // On Darwin and NetBSD, the double-precision FP classification functions + // end with a 'd'. + TLI.setAvailableWithName(LibFunc::isfinite, "__isfinited"); + TLI.setAvailableWithName(LibFunc::isinf, "__isinfd"); + TLI.setAvailableWithName(LibFunc::isnan, "__isnand"); + } + // As currently implemented in clang, NVPTX code has no standard library to // speak of. Headers provide a standard-ish library implementation, but many // of the signatures are wrong -- for example, many libm functions are not @@ -417,14 +430,15 @@ } TargetLibraryInfoImpl::TargetLibraryInfoImpl(const TargetLibraryInfoImpl &TLI) - : CustomNames(TLI.CustomNames) { + : CustomNames(TLI.CustomNames), CustomNameFuncs(TLI.CustomNameFuncs) { memcpy(AvailableArray, TLI.AvailableArray, sizeof(AvailableArray)); VectorDescs = TLI.VectorDescs; ScalarDescs = TLI.ScalarDescs; } TargetLibraryInfoImpl::TargetLibraryInfoImpl(TargetLibraryInfoImpl &&TLI) - : CustomNames(std::move(TLI.CustomNames)) { + : CustomNames(std::move(TLI.CustomNames)), + CustomNameFuncs(std::move(TLI.CustomNameFuncs)) { std::move(std::begin(TLI.AvailableArray), std::end(TLI.AvailableArray), AvailableArray); VectorDescs = TLI.VectorDescs; @@ -433,12 +447,14 @@ TargetLibraryInfoImpl &TargetLibraryInfoImpl::operator=(const TargetLibraryInfoImpl &TLI) { CustomNames = TLI.CustomNames; + CustomNameFuncs = TLI.CustomNameFuncs; memcpy(AvailableArray, TLI.AvailableArray, sizeof(AvailableArray)); return *this; } TargetLibraryInfoImpl &TargetLibraryInfoImpl::operator=(TargetLibraryInfoImpl &&TLI) { CustomNames = std::move(TLI.CustomNames); + CustomNameFuncs = std::move(TLI.CustomNameFuncs); std::move(std::begin(TLI.AvailableArray), std::end(TLI.AvailableArray), AvailableArray); return *this; @@ -472,6 +488,13 @@ F = (LibFunc::Func)(I - Start); return true; } + + auto CNFI = CustomNameFuncs.find(funcName); + if (CNFI != CustomNameFuncs.end()) { + F = (LibFunc::Func) CNFI->second; + return true; + } + return false; } Index: lib/Transforms/Utils/SimplifyLibCalls.cpp =================================================================== --- lib/Transforms/Utils/SimplifyLibCalls.cpp +++ lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -1393,6 +1393,54 @@ return B.CreateSelect(Cmp, Op0, Op1); } +Value *LibCallSimplifier::optimizeFPClassification(CallInst *CI, + IRBuilder<> &B, + LibFunc::Func Func) { + // isfinite*, isnan*, insinf* all take one floating-point argument and return + // an integer. + Function *Callee = CI->getCalledFunction(); + FunctionType *FT = Callee->getFunctionType(); + if (!FT->getReturnType()->isIntegerTy(32)) + return nullptr; + if (FT->getNumParams() != 1) + return nullptr; + if (!FT->getParamType(0)->isFloatingPointTy()) + return nullptr; + + bool HasFunNoNaNAttr = false, HasFunNoInfAttr = false; + Function &F = *B.GetInsertBlock()->getParent(); + if (F.hasFnAttribute("no-nans-fp-math")) + HasFunNoNaNAttr = + F.getFnAttribute("no-nans-fp-math").getValueAsString() == "true"; + if (F.hasFnAttribute("no-infs-fp-math")) + HasFunNoInfAttr = + F.getFnAttribute("no-infs-fp-math").getValueAsString() == "true"; + + switch (Func) { + default: llvm_unreachable("Unknown FP classification function"); + case LibFunc::isfinitef: + case LibFunc::isfinite: + case LibFunc::isfinitel: + if (!HasFunNoNaNAttr || !HasFunNoInfAttr) + return nullptr; + return ConstantInt::get(B.getInt32Ty(), 1); + case LibFunc::isnanf: + case LibFunc::isnan: + case LibFunc::isnanl: + if (!HasFunNoNaNAttr) + return nullptr; + break; + case LibFunc::isinff: + case LibFunc::isinf: + case LibFunc::isinfl: + if (!HasFunNoInfAttr) + return nullptr; + break; + } + + return ConstantInt::get(B.getInt32Ty(), 0); +} + Value *LibCallSimplifier::optimizeLog(CallInst *CI, IRBuilder<> &B) { Function *Callee = CI->getCalledFunction(); if (!matchesFPLibFunctionSignature(Callee, 1, false)) @@ -2389,6 +2437,16 @@ case LibFunc::fmax: case LibFunc::fmaxl: return optimizeFMinFMax(CI, Builder); + case LibFunc::isfinitef: + case LibFunc::isfinite: + case LibFunc::isfinitel: + case LibFunc::isnanf: + case LibFunc::isnan: + case LibFunc::isnanl: + case LibFunc::isinff: + case LibFunc::isinf: + case LibFunc::isinfl: + return optimizeFPClassification(CI, Builder, Func); default: return nullptr; } Index: test/Transforms/InstCombine/fp-classify-libcalls.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/fp-classify-libcalls.ll @@ -0,0 +1,85 @@ +; RUN: opt -S < %s -instcombine | FileCheck %s +target datalayout = "E-m:e-i64:64-n32:64" +target triple = "powerpc64-unknown-linux-gnu" + +; Function Attrs: nounwind readnone +define zeroext i1 @_Z2t1f(float %x) #0 { +entry: + %call = tail call signext i32 @__finitef(float %x) #1 + %tobool = icmp ne i32 %call, 0 + ret i1 %tobool +; CHECK-LABEL: @_Z2t1f +; CHECK: ret i1 true +} + +; Function Attrs: nounwind readnone +declare signext i32 @__finitef(float) #0 + +; Function Attrs: nounwind readnone +define zeroext i1 @_Z2t2f(float %x) #0 { +entry: + %call = tail call signext i32 @__isnanf(float %x) #1 + %tobool = icmp ne i32 %call, 0 + ret i1 %tobool +; CHECK-LABEL: @_Z2t2f +; CHECK: ret i1 false +} + +; Function Attrs: nounwind readnone +declare signext i32 @__isnanf(float) #0 + +; Function Attrs: nounwind readnone +define zeroext i1 @_Z2t3f(float %x) #0 { +entry: + %call = tail call signext i32 @__isinff(float %x) #1 + %tobool = icmp ne i32 %call, 0 + ret i1 %tobool +; CHECK-LABEL: @_Z2t3f +; CHECK: ret i1 false +} + +; Function Attrs: nounwind readnone +declare signext i32 @__isinff(float) #0 + +; Function Attrs: nounwind readnone +define zeroext i1 @_Z3t1dd(double %x) #0 { +entry: + %call = tail call signext i32 @__finite(double %x) #1 + %tobool = icmp ne i32 %call, 0 + ret i1 %tobool +; CHECK-LABEL: @_Z3t1dd +; CHECK: ret i1 true +} + +; Function Attrs: nounwind readnone +declare signext i32 @__finite(double) #0 + +; Function Attrs: nounwind readnone +define zeroext i1 @_Z3t2dd(double %x) #0 { +entry: + %call = tail call signext i32 @__isnan(double %x) #1 + %tobool = icmp ne i32 %call, 0 + ret i1 %tobool +; CHECK-LABEL: @_Z3t2dd +; CHECK: ret i1 false +} + +; Function Attrs: nounwind readnone +declare signext i32 @__isnan(double) #0 + +; Function Attrs: nounwind readnone +define zeroext i1 @_Z3t3dd(double %x) #0 { +entry: + %call = tail call signext i32 @__isinf(double %x) #1 + %tobool = icmp ne i32 %call, 0 + ret i1 %tobool +; CHECK-LABEL: @_Z3t3dd +; CHECK: ret i1 false +} + +; Function Attrs: nounwind readnone +declare signext i32 @__isinf(double) #0 + +attributes #0 = { nounwind readnone "no-infs-fp-math"="true" "no-nans-fp-math"="true" "unsafe-fp-math"="true" } +attributes #1 = { nounwind readnone } +