#include "llvm/Transforms/Utils/LibCallsShrinkWrap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/GlobalsModRef.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
using namespace llvm;
#define DEBUG_TYPE "libcalls-shrinkwrap"
STATISTIC(NumWrappedOneCond, "Number of One-Condition Wrappers Inserted");
STATISTIC(NumWrappedTwoCond, "Number of Two-Condition Wrappers Inserted");
namespace {
class LibCallsShrinkWrapLegacyPass : public FunctionPass {
public:
static char ID; explicit LibCallsShrinkWrapLegacyPass() : FunctionPass(ID) {
initializeLibCallsShrinkWrapLegacyPassPass(
*PassRegistry::getPassRegistry());
}
void getAnalysisUsage(AnalysisUsage &AU) const override;
bool runOnFunction(Function &F) override;
};
}
char LibCallsShrinkWrapLegacyPass::ID = 0;
INITIALIZE_PASS_BEGIN(LibCallsShrinkWrapLegacyPass, "libcalls-shrinkwrap",
"Conditionally eliminate dead library calls", false,
false)
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
INITIALIZE_PASS_END(LibCallsShrinkWrapLegacyPass, "libcalls-shrinkwrap",
"Conditionally eliminate dead library calls", false, false)
namespace {
class LibCallsShrinkWrap : public InstVisitor<LibCallsShrinkWrap> {
public:
LibCallsShrinkWrap(const TargetLibraryInfo &TLI, DominatorTree *DT)
: TLI(TLI), DT(DT){};
void visitCallInst(CallInst &CI) { checkCandidate(CI); }
bool perform() {
bool Changed = false;
for (auto &CI : WorkList) {
LLVM_DEBUG(dbgs() << "CDCE calls: " << CI->getCalledFunction()->getName()
<< "\n");
if (perform(CI)) {
Changed = true;
LLVM_DEBUG(dbgs() << "Transformed\n");
}
}
return Changed;
}
private:
bool perform(CallInst *CI);
void checkCandidate(CallInst &CI);
void shrinkWrapCI(CallInst *CI, Value *Cond);
bool performCallDomainErrorOnly(CallInst *CI, const LibFunc &Func);
bool performCallErrors(CallInst *CI, const LibFunc &Func);
bool performCallRangeErrorOnly(CallInst *CI, const LibFunc &Func);
Value *generateOneRangeCond(CallInst *CI, const LibFunc &Func);
Value *generateTwoRangeCond(CallInst *CI, const LibFunc &Func);
Value *generateCondForPow(CallInst *CI, const LibFunc &Func);
Value *createOrCond(CallInst *CI, CmpInst::Predicate Cmp, float Val,
CmpInst::Predicate Cmp2, float Val2) {
IRBuilder<> BBBuilder(CI);
Value *Arg = CI->getArgOperand(0);
auto Cond2 = createCond(BBBuilder, Arg, Cmp2, Val2);
auto Cond1 = createCond(BBBuilder, Arg, Cmp, Val);
return BBBuilder.CreateOr(Cond1, Cond2);
}
Value *createCond(IRBuilder<> &BBBuilder, Value *Arg, CmpInst::Predicate Cmp,
float Val) {
Constant *V = ConstantFP::get(BBBuilder.getContext(), APFloat(Val));
if (!Arg->getType()->isFloatTy())
V = ConstantExpr::getFPExtend(V, Arg->getType());
return BBBuilder.CreateFCmp(Cmp, Arg, V);
}
Value *createCond(CallInst *CI, CmpInst::Predicate Cmp, float Val) {
IRBuilder<> BBBuilder(CI);
Value *Arg = CI->getArgOperand(0);
return createCond(BBBuilder, Arg, Cmp, Val);
}
const TargetLibraryInfo &TLI;
DominatorTree *DT;
SmallVector<CallInst *, 16> WorkList;
};
}
bool LibCallsShrinkWrap::performCallDomainErrorOnly(CallInst *CI,
const LibFunc &Func) {
Value *Cond = nullptr;
switch (Func) {
case LibFunc_acos: case LibFunc_acosf: case LibFunc_acosl: case LibFunc_asin: case LibFunc_asinf: case LibFunc_asinl: {
++NumWrappedTwoCond;
Cond = createOrCond(CI, CmpInst::FCMP_OLT, -1.0f, CmpInst::FCMP_OGT, 1.0f);
break;
}
case LibFunc_cos: case LibFunc_cosf: case LibFunc_cosl: case LibFunc_sin: case LibFunc_sinf: case LibFunc_sinl: {
++NumWrappedTwoCond;
Cond = createOrCond(CI, CmpInst::FCMP_OEQ, INFINITY, CmpInst::FCMP_OEQ,
-INFINITY);
break;
}
case LibFunc_acosh: case LibFunc_acoshf: case LibFunc_acoshl: {
++NumWrappedOneCond;
Cond = createCond(CI, CmpInst::FCMP_OLT, 1.0f);
break;
}
case LibFunc_sqrt: case LibFunc_sqrtf: case LibFunc_sqrtl: {
++NumWrappedOneCond;
Cond = createCond(CI, CmpInst::FCMP_OLT, 0.0f);
break;
}
default:
return false;
}
shrinkWrapCI(CI, Cond);
return true;
}
bool LibCallsShrinkWrap::performCallRangeErrorOnly(CallInst *CI,
const LibFunc &Func) {
Value *Cond = nullptr;
switch (Func) {
case LibFunc_cosh:
case LibFunc_coshf:
case LibFunc_coshl:
case LibFunc_exp:
case LibFunc_expf:
case LibFunc_expl:
case LibFunc_exp10:
case LibFunc_exp10f:
case LibFunc_exp10l:
case LibFunc_exp2:
case LibFunc_exp2f:
case LibFunc_exp2l:
case LibFunc_sinh:
case LibFunc_sinhf:
case LibFunc_sinhl: {
Cond = generateTwoRangeCond(CI, Func);
break;
}
case LibFunc_expm1: case LibFunc_expm1f: case LibFunc_expm1l: {
Cond = generateOneRangeCond(CI, Func);
break;
}
default:
return false;
}
shrinkWrapCI(CI, Cond);
return true;
}
bool LibCallsShrinkWrap::performCallErrors(CallInst *CI,
const LibFunc &Func) {
Value *Cond = nullptr;
switch (Func) {
case LibFunc_atanh: case LibFunc_atanhf: case LibFunc_atanhl: {
++NumWrappedTwoCond;
Cond = createOrCond(CI, CmpInst::FCMP_OLE, -1.0f, CmpInst::FCMP_OGE, 1.0f);
break;
}
case LibFunc_log: case LibFunc_logf: case LibFunc_logl: case LibFunc_log10: case LibFunc_log10f: case LibFunc_log10l: case LibFunc_log2: case LibFunc_log2f: case LibFunc_log2l: case LibFunc_logb: case LibFunc_logbf: case LibFunc_logbl: {
++NumWrappedOneCond;
Cond = createCond(CI, CmpInst::FCMP_OLE, 0.0f);
break;
}
case LibFunc_log1p: case LibFunc_log1pf: case LibFunc_log1pl: {
++NumWrappedOneCond;
Cond = createCond(CI, CmpInst::FCMP_OLE, -1.0f);
break;
}
case LibFunc_pow: case LibFunc_powf:
case LibFunc_powl: {
Cond = generateCondForPow(CI, Func);
if (Cond == nullptr)
return false;
break;
}
default:
return false;
}
assert(Cond && "performCallErrors should not see an empty condition");
shrinkWrapCI(CI, Cond);
return true;
}
void LibCallsShrinkWrap::checkCandidate(CallInst &CI) {
if (CI.isNoBuiltin())
return;
if (!CI.use_empty())
return;
LibFunc Func;
Function *Callee = CI.getCalledFunction();
if (!Callee)
return;
if (!TLI.getLibFunc(*Callee, Func) || !TLI.has(Func))
return;
if (CI.arg_empty())
return;
Type *ArgType = CI.getArgOperand(0)->getType();
if (!(ArgType->isFloatTy() || ArgType->isDoubleTy() ||
ArgType->isX86_FP80Ty()))
return;
WorkList.push_back(&CI);
}
Value *LibCallsShrinkWrap::generateOneRangeCond(CallInst *CI,
const LibFunc &Func) {
float UpperBound;
switch (Func) {
case LibFunc_expm1: UpperBound = 709.0f;
break;
case LibFunc_expm1f: UpperBound = 88.0f;
break;
case LibFunc_expm1l: UpperBound = 11356.0f;
break;
default:
llvm_unreachable("Unhandled library call!");
}
++NumWrappedOneCond;
return createCond(CI, CmpInst::FCMP_OGT, UpperBound);
}
Value *LibCallsShrinkWrap::generateTwoRangeCond(CallInst *CI,
const LibFunc &Func) {
float UpperBound, LowerBound;
switch (Func) {
case LibFunc_cosh: case LibFunc_sinh: LowerBound = -710.0f;
UpperBound = 710.0f;
break;
case LibFunc_coshf: case LibFunc_sinhf: LowerBound = -89.0f;
UpperBound = 89.0f;
break;
case LibFunc_coshl: case LibFunc_sinhl: LowerBound = -11357.0f;
UpperBound = 11357.0f;
break;
case LibFunc_exp: LowerBound = -745.0f;
UpperBound = 709.0f;
break;
case LibFunc_expf: LowerBound = -103.0f;
UpperBound = 88.0f;
break;
case LibFunc_expl: LowerBound = -11399.0f;
UpperBound = 11356.0f;
break;
case LibFunc_exp10: LowerBound = -323.0f;
UpperBound = 308.0f;
break;
case LibFunc_exp10f: LowerBound = -45.0f;
UpperBound = 38.0f;
break;
case LibFunc_exp10l: LowerBound = -4950.0f;
UpperBound = 4932.0f;
break;
case LibFunc_exp2: LowerBound = -1074.0f;
UpperBound = 1023.0f;
break;
case LibFunc_exp2f: LowerBound = -149.0f;
UpperBound = 127.0f;
break;
case LibFunc_exp2l: LowerBound = -16445.0f;
UpperBound = 11383.0f;
break;
default:
llvm_unreachable("Unhandled library call!");
}
++NumWrappedTwoCond;
return createOrCond(CI, CmpInst::FCMP_OGT, UpperBound, CmpInst::FCMP_OLT,
LowerBound);
}
Value *LibCallsShrinkWrap::generateCondForPow(CallInst *CI,
const LibFunc &Func) {
if (Func != LibFunc_pow) {
LLVM_DEBUG(dbgs() << "Not handled powf() and powl()\n");
return nullptr;
}
Value *Base = CI->getArgOperand(0);
Value *Exp = CI->getArgOperand(1);
IRBuilder<> BBBuilder(CI);
if (ConstantFP *CF = dyn_cast<ConstantFP>(Base)) {
double D = CF->getValueAPF().convertToDouble();
if (D < 1.0f || D > APInt::getMaxValue(8).getZExtValue()) {
LLVM_DEBUG(dbgs() << "Not handled pow(): constant base out of range\n");
return nullptr;
}
++NumWrappedOneCond;
Constant *V = ConstantFP::get(CI->getContext(), APFloat(127.0f));
if (!Exp->getType()->isFloatTy())
V = ConstantExpr::getFPExtend(V, Exp->getType());
return BBBuilder.CreateFCmp(CmpInst::FCMP_OGT, Exp, V);
}
Instruction *I = dyn_cast<Instruction>(Base);
if (!I) {
LLVM_DEBUG(dbgs() << "Not handled pow(): FP type base\n");
return nullptr;
}
unsigned Opcode = I->getOpcode();
if (Opcode == Instruction::UIToFP || Opcode == Instruction::SIToFP) {
unsigned BW = I->getOperand(0)->getType()->getPrimitiveSizeInBits();
float UpperV = 0.0f;
if (BW == 8)
UpperV = 128.0f;
else if (BW == 16)
UpperV = 64.0f;
else if (BW == 32)
UpperV = 32.0f;
else {
LLVM_DEBUG(dbgs() << "Not handled pow(): type too wide\n");
return nullptr;
}
++NumWrappedTwoCond;
Constant *V = ConstantFP::get(CI->getContext(), APFloat(UpperV));
Constant *V0 = ConstantFP::get(CI->getContext(), APFloat(0.0f));
if (!Exp->getType()->isFloatTy())
V = ConstantExpr::getFPExtend(V, Exp->getType());
if (!Base->getType()->isFloatTy())
V0 = ConstantExpr::getFPExtend(V0, Exp->getType());
Value *Cond = BBBuilder.CreateFCmp(CmpInst::FCMP_OGT, Exp, V);
Value *Cond0 = BBBuilder.CreateFCmp(CmpInst::FCMP_OLE, Base, V0);
return BBBuilder.CreateOr(Cond0, Cond);
}
LLVM_DEBUG(dbgs() << "Not handled pow(): base not from integer convert\n");
return nullptr;
}
void LibCallsShrinkWrap::shrinkWrapCI(CallInst *CI, Value *Cond) {
assert(Cond != nullptr && "ShrinkWrapCI is not expecting an empty call inst");
MDNode *BranchWeights =
MDBuilder(CI->getContext()).createBranchWeights(1, 2000);
Instruction *NewInst =
SplitBlockAndInsertIfThen(Cond, CI, false, BranchWeights, DT);
BasicBlock *CallBB = NewInst->getParent();
CallBB->setName("cdce.call");
BasicBlock *SuccBB = CallBB->getSingleSuccessor();
assert(SuccBB && "The split block should have a single successor");
SuccBB->setName("cdce.end");
CI->removeFromParent();
CallBB->getInstList().insert(CallBB->getFirstInsertionPt(), CI);
LLVM_DEBUG(dbgs() << "== Basic Block After ==");
LLVM_DEBUG(dbgs() << *CallBB->getSinglePredecessor() << *CallBB
<< *CallBB->getSingleSuccessor() << "\n");
}
bool LibCallsShrinkWrap::perform(CallInst *CI) {
LibFunc Func;
Function *Callee = CI->getCalledFunction();
assert(Callee && "perform() should apply to a non-empty callee");
TLI.getLibFunc(*Callee, Func);
assert(Func && "perform() is not expecting an empty function");
if (performCallDomainErrorOnly(CI, Func) || performCallRangeErrorOnly(CI, Func))
return true;
return performCallErrors(CI, Func);
}
void LibCallsShrinkWrapLegacyPass::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addPreserved<DominatorTreeWrapperPass>();
AU.addPreserved<GlobalsAAWrapperPass>();
AU.addRequired<TargetLibraryInfoWrapperPass>();
}
static bool runImpl(Function &F, const TargetLibraryInfo &TLI,
DominatorTree *DT) {
if (F.hasFnAttribute(Attribute::OptimizeForSize))
return false;
LibCallsShrinkWrap CCDCE(TLI, DT);
CCDCE.visit(F);
bool Changed = CCDCE.perform();
assert(!DT || DT->verify(DominatorTree::VerificationLevel::Fast));
return Changed;
}
bool LibCallsShrinkWrapLegacyPass::runOnFunction(Function &F) {
auto &TLI = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F);
auto *DTWP = getAnalysisIfAvailable<DominatorTreeWrapperPass>();
auto *DT = DTWP ? &DTWP->getDomTree() : nullptr;
return runImpl(F, TLI, DT);
}
namespace llvm {
char &LibCallsShrinkWrapPassID = LibCallsShrinkWrapLegacyPass::ID;
FunctionPass *createLibCallsShrinkWrapPass() {
return new LibCallsShrinkWrapLegacyPass();
}
PreservedAnalyses LibCallsShrinkWrapPass::run(Function &F,
FunctionAnalysisManager &FAM) {
auto &TLI = FAM.getResult<TargetLibraryAnalysis>(F);
auto *DT = FAM.getCachedResult<DominatorTreeAnalysis>(F);
if (!runImpl(F, TLI, DT))
return PreservedAnalyses::all();
auto PA = PreservedAnalyses();
PA.preserve<DominatorTreeAnalysis>();
return PA;
}
}