Path: blob/main/contrib/llvm-project/llvm/lib/Transforms/Utils/LibCallsShrinkWrap.cpp
35271 views
//===-- LibCallsShrinkWrap.cpp ----------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This pass shrink-wraps a call to function if the result is not used.9// The call can set errno but is otherwise side effect free. For example:10// sqrt(val);11// is transformed to12// if (val < 0)13// sqrt(val);14// Even if the result of library call is not being used, the compiler cannot15// safely delete the call because the function can set errno on error16// conditions.17// Note in many functions, the error condition solely depends on the incoming18// parameter. In this optimization, we can generate the condition can lead to19// the errno to shrink-wrap the call. Since the chances of hitting the error20// condition is low, the runtime call is effectively eliminated.21//22// These partially dead calls are usually results of C++ abstraction penalty23// exposed by inlining.24//25//===----------------------------------------------------------------------===//2627#include "llvm/Transforms/Utils/LibCallsShrinkWrap.h"28#include "llvm/ADT/SmallVector.h"29#include "llvm/ADT/Statistic.h"30#include "llvm/Analysis/DomTreeUpdater.h"31#include "llvm/Analysis/GlobalsModRef.h"32#include "llvm/Analysis/TargetLibraryInfo.h"33#include "llvm/IR/Constants.h"34#include "llvm/IR/Dominators.h"35#include "llvm/IR/Function.h"36#include "llvm/IR/IRBuilder.h"37#include "llvm/IR/InstVisitor.h"38#include "llvm/IR/Instructions.h"39#include "llvm/IR/MDBuilder.h"40#include "llvm/Transforms/Utils/BasicBlockUtils.h"4142#include <cmath>4344using namespace llvm;4546#define DEBUG_TYPE "libcalls-shrinkwrap"4748STATISTIC(NumWrappedOneCond, "Number of One-Condition Wrappers Inserted");49STATISTIC(NumWrappedTwoCond, "Number of Two-Condition Wrappers Inserted");5051namespace {52class LibCallsShrinkWrap : public InstVisitor<LibCallsShrinkWrap> {53public:54LibCallsShrinkWrap(const TargetLibraryInfo &TLI, DomTreeUpdater &DTU)55: TLI(TLI), DTU(DTU){};56void visitCallInst(CallInst &CI) { checkCandidate(CI); }57bool perform() {58bool Changed = false;59for (auto &CI : WorkList) {60LLVM_DEBUG(dbgs() << "CDCE calls: " << CI->getCalledFunction()->getName()61<< "\n");62if (perform(CI)) {63Changed = true;64LLVM_DEBUG(dbgs() << "Transformed\n");65}66}67return Changed;68}6970private:71bool perform(CallInst *CI);72void checkCandidate(CallInst &CI);73void shrinkWrapCI(CallInst *CI, Value *Cond);74bool performCallDomainErrorOnly(CallInst *CI, const LibFunc &Func);75bool performCallErrors(CallInst *CI, const LibFunc &Func);76bool performCallRangeErrorOnly(CallInst *CI, const LibFunc &Func);77Value *generateOneRangeCond(CallInst *CI, const LibFunc &Func);78Value *generateTwoRangeCond(CallInst *CI, const LibFunc &Func);79Value *generateCondForPow(CallInst *CI, const LibFunc &Func);8081// Create an OR of two conditions with given Arg and Arg2.82Value *createOrCond(CallInst *CI, Value *Arg, CmpInst::Predicate Cmp,83float Val, Value *Arg2, CmpInst::Predicate Cmp2,84float Val2) {85IRBuilder<> BBBuilder(CI);86auto Cond2 = createCond(BBBuilder, Arg2, Cmp2, Val2);87auto Cond1 = createCond(BBBuilder, Arg, Cmp, Val);88return BBBuilder.CreateOr(Cond1, Cond2);89}9091// Create an OR of two conditions.92Value *createOrCond(CallInst *CI, CmpInst::Predicate Cmp, float Val,93CmpInst::Predicate Cmp2, float Val2) {94Value *Arg = CI->getArgOperand(0);95return createOrCond(CI, Arg, Cmp, Val, Arg, Cmp2, Val2);96}9798// Create a single condition using IRBuilder.99Value *createCond(IRBuilder<> &BBBuilder, Value *Arg, CmpInst::Predicate Cmp,100float Val) {101Constant *V = ConstantFP::get(BBBuilder.getContext(), APFloat(Val));102if (!Arg->getType()->isFloatTy())103V = ConstantFoldCastInstruction(Instruction::FPExt, V, Arg->getType());104if (BBBuilder.GetInsertBlock()->getParent()->hasFnAttribute(Attribute::StrictFP))105BBBuilder.setIsFPConstrained(true);106return BBBuilder.CreateFCmp(Cmp, Arg, V);107}108109// Create a single condition with given Arg.110Value *createCond(CallInst *CI, Value *Arg, CmpInst::Predicate Cmp,111float Val) {112IRBuilder<> BBBuilder(CI);113return createCond(BBBuilder, Arg, Cmp, Val);114}115116// Create a single condition.117Value *createCond(CallInst *CI, CmpInst::Predicate Cmp, float Val) {118Value *Arg = CI->getArgOperand(0);119return createCond(CI, Arg, Cmp, Val);120}121122const TargetLibraryInfo &TLI;123DomTreeUpdater &DTU;124SmallVector<CallInst *, 16> WorkList;125};126} // end anonymous namespace127128// Perform the transformation to calls with errno set by domain error.129bool LibCallsShrinkWrap::performCallDomainErrorOnly(CallInst *CI,130const LibFunc &Func) {131Value *Cond = nullptr;132133switch (Func) {134case LibFunc_acos: // DomainError: (x < -1 || x > 1)135case LibFunc_acosf: // Same as acos136case LibFunc_acosl: // Same as acos137case LibFunc_asin: // DomainError: (x < -1 || x > 1)138case LibFunc_asinf: // Same as asin139case LibFunc_asinl: // Same as asin140{141++NumWrappedTwoCond;142Cond = createOrCond(CI, CmpInst::FCMP_OLT, -1.0f, CmpInst::FCMP_OGT, 1.0f);143break;144}145case LibFunc_cos: // DomainError: (x == +inf || x == -inf)146case LibFunc_cosf: // Same as cos147case LibFunc_cosl: // Same as cos148case LibFunc_sin: // DomainError: (x == +inf || x == -inf)149case LibFunc_sinf: // Same as sin150case LibFunc_sinl: // Same as sin151{152++NumWrappedTwoCond;153Cond = createOrCond(CI, CmpInst::FCMP_OEQ, INFINITY, CmpInst::FCMP_OEQ,154-INFINITY);155break;156}157case LibFunc_acosh: // DomainError: (x < 1)158case LibFunc_acoshf: // Same as acosh159case LibFunc_acoshl: // Same as acosh160{161++NumWrappedOneCond;162Cond = createCond(CI, CmpInst::FCMP_OLT, 1.0f);163break;164}165case LibFunc_sqrt: // DomainError: (x < 0)166case LibFunc_sqrtf: // Same as sqrt167case LibFunc_sqrtl: // Same as sqrt168{169++NumWrappedOneCond;170Cond = createCond(CI, CmpInst::FCMP_OLT, 0.0f);171break;172}173default:174return false;175}176shrinkWrapCI(CI, Cond);177return true;178}179180// Perform the transformation to calls with errno set by range error.181bool LibCallsShrinkWrap::performCallRangeErrorOnly(CallInst *CI,182const LibFunc &Func) {183Value *Cond = nullptr;184185switch (Func) {186case LibFunc_cosh:187case LibFunc_coshf:188case LibFunc_coshl:189case LibFunc_exp:190case LibFunc_expf:191case LibFunc_expl:192case LibFunc_exp10:193case LibFunc_exp10f:194case LibFunc_exp10l:195case LibFunc_exp2:196case LibFunc_exp2f:197case LibFunc_exp2l:198case LibFunc_sinh:199case LibFunc_sinhf:200case LibFunc_sinhl: {201Cond = generateTwoRangeCond(CI, Func);202break;203}204case LibFunc_expm1: // RangeError: (709, inf)205case LibFunc_expm1f: // RangeError: (88, inf)206case LibFunc_expm1l: // RangeError: (11356, inf)207{208Cond = generateOneRangeCond(CI, Func);209break;210}211default:212return false;213}214shrinkWrapCI(CI, Cond);215return true;216}217218// Perform the transformation to calls with errno set by combination of errors.219bool LibCallsShrinkWrap::performCallErrors(CallInst *CI,220const LibFunc &Func) {221Value *Cond = nullptr;222223switch (Func) {224case LibFunc_atanh: // DomainError: (x < -1 || x > 1)225// PoleError: (x == -1 || x == 1)226// Overall Cond: (x <= -1 || x >= 1)227case LibFunc_atanhf: // Same as atanh228case LibFunc_atanhl: // Same as atanh229{230++NumWrappedTwoCond;231Cond = createOrCond(CI, CmpInst::FCMP_OLE, -1.0f, CmpInst::FCMP_OGE, 1.0f);232break;233}234case LibFunc_log: // DomainError: (x < 0)235// PoleError: (x == 0)236// Overall Cond: (x <= 0)237case LibFunc_logf: // Same as log238case LibFunc_logl: // Same as log239case LibFunc_log10: // Same as log240case LibFunc_log10f: // Same as log241case LibFunc_log10l: // Same as log242case LibFunc_log2: // Same as log243case LibFunc_log2f: // Same as log244case LibFunc_log2l: // Same as log245case LibFunc_logb: // Same as log246case LibFunc_logbf: // Same as log247case LibFunc_logbl: // Same as log248{249++NumWrappedOneCond;250Cond = createCond(CI, CmpInst::FCMP_OLE, 0.0f);251break;252}253case LibFunc_log1p: // DomainError: (x < -1)254// PoleError: (x == -1)255// Overall Cond: (x <= -1)256case LibFunc_log1pf: // Same as log1p257case LibFunc_log1pl: // Same as log1p258{259++NumWrappedOneCond;260Cond = createCond(CI, CmpInst::FCMP_OLE, -1.0f);261break;262}263case LibFunc_pow: // DomainError: x < 0 and y is noninteger264// PoleError: x == 0 and y < 0265// RangeError: overflow or underflow266case LibFunc_powf:267case LibFunc_powl: {268Cond = generateCondForPow(CI, Func);269if (Cond == nullptr)270return false;271break;272}273default:274return false;275}276assert(Cond && "performCallErrors should not see an empty condition");277shrinkWrapCI(CI, Cond);278return true;279}280281// Checks if CI is a candidate for shrinkwrapping and put it into work list if282// true.283void LibCallsShrinkWrap::checkCandidate(CallInst &CI) {284if (CI.isNoBuiltin())285return;286// A possible improvement is to handle the calls with the return value being287// used. If there is API for fast libcall implementation without setting288// errno, we can use the same framework to direct/wrap the call to the fast289// API in the error free path, and leave the original call in the slow path.290if (!CI.use_empty())291return;292293LibFunc Func;294Function *Callee = CI.getCalledFunction();295if (!Callee)296return;297if (!TLI.getLibFunc(*Callee, Func) || !TLI.has(Func))298return;299300if (CI.arg_empty())301return;302// TODO: Handle long double in other formats.303Type *ArgType = CI.getArgOperand(0)->getType();304if (!(ArgType->isFloatTy() || ArgType->isDoubleTy() ||305ArgType->isX86_FP80Ty()))306return;307308WorkList.push_back(&CI);309}310311// Generate the upper bound condition for RangeError.312Value *LibCallsShrinkWrap::generateOneRangeCond(CallInst *CI,313const LibFunc &Func) {314float UpperBound;315switch (Func) {316case LibFunc_expm1: // RangeError: (709, inf)317UpperBound = 709.0f;318break;319case LibFunc_expm1f: // RangeError: (88, inf)320UpperBound = 88.0f;321break;322case LibFunc_expm1l: // RangeError: (11356, inf)323UpperBound = 11356.0f;324break;325default:326llvm_unreachable("Unhandled library call!");327}328329++NumWrappedOneCond;330return createCond(CI, CmpInst::FCMP_OGT, UpperBound);331}332333// Generate the lower and upper bound condition for RangeError.334Value *LibCallsShrinkWrap::generateTwoRangeCond(CallInst *CI,335const LibFunc &Func) {336float UpperBound, LowerBound;337switch (Func) {338case LibFunc_cosh: // RangeError: (x < -710 || x > 710)339case LibFunc_sinh: // Same as cosh340LowerBound = -710.0f;341UpperBound = 710.0f;342break;343case LibFunc_coshf: // RangeError: (x < -89 || x > 89)344case LibFunc_sinhf: // Same as coshf345LowerBound = -89.0f;346UpperBound = 89.0f;347break;348case LibFunc_coshl: // RangeError: (x < -11357 || x > 11357)349case LibFunc_sinhl: // Same as coshl350LowerBound = -11357.0f;351UpperBound = 11357.0f;352break;353case LibFunc_exp: // RangeError: (x < -745 || x > 709)354LowerBound = -745.0f;355UpperBound = 709.0f;356break;357case LibFunc_expf: // RangeError: (x < -103 || x > 88)358LowerBound = -103.0f;359UpperBound = 88.0f;360break;361case LibFunc_expl: // RangeError: (x < -11399 || x > 11356)362LowerBound = -11399.0f;363UpperBound = 11356.0f;364break;365case LibFunc_exp10: // RangeError: (x < -323 || x > 308)366LowerBound = -323.0f;367UpperBound = 308.0f;368break;369case LibFunc_exp10f: // RangeError: (x < -45 || x > 38)370LowerBound = -45.0f;371UpperBound = 38.0f;372break;373case LibFunc_exp10l: // RangeError: (x < -4950 || x > 4932)374LowerBound = -4950.0f;375UpperBound = 4932.0f;376break;377case LibFunc_exp2: // RangeError: (x < -1074 || x > 1023)378LowerBound = -1074.0f;379UpperBound = 1023.0f;380break;381case LibFunc_exp2f: // RangeError: (x < -149 || x > 127)382LowerBound = -149.0f;383UpperBound = 127.0f;384break;385case LibFunc_exp2l: // RangeError: (x < -16445 || x > 11383)386LowerBound = -16445.0f;387UpperBound = 11383.0f;388break;389default:390llvm_unreachable("Unhandled library call!");391}392393++NumWrappedTwoCond;394return createOrCond(CI, CmpInst::FCMP_OGT, UpperBound, CmpInst::FCMP_OLT,395LowerBound);396}397398// For pow(x,y), We only handle the following cases:399// (1) x is a constant && (x >= 1) && (x < MaxUInt8)400// Cond is: (y > 127)401// (2) x is a value coming from an integer type.402// (2.1) if x's bit_size == 8403// Cond: (x <= 0 || y > 128)404// (2.2) if x's bit_size is 16405// Cond: (x <= 0 || y > 64)406// (2.3) if x's bit_size is 32407// Cond: (x <= 0 || y > 32)408// Support for powl(x,y) and powf(x,y) are TBD.409//410// Note that condition can be more conservative than the actual condition411// (i.e. we might invoke the calls that will not set the errno.).412//413Value *LibCallsShrinkWrap::generateCondForPow(CallInst *CI,414const LibFunc &Func) {415// FIXME: LibFunc_powf and powl TBD.416if (Func != LibFunc_pow) {417LLVM_DEBUG(dbgs() << "Not handled powf() and powl()\n");418return nullptr;419}420421Value *Base = CI->getArgOperand(0);422Value *Exp = CI->getArgOperand(1);423424// Constant Base case.425if (ConstantFP *CF = dyn_cast<ConstantFP>(Base)) {426double D = CF->getValueAPF().convertToDouble();427if (D < 1.0f || D > APInt::getMaxValue(8).getZExtValue()) {428LLVM_DEBUG(dbgs() << "Not handled pow(): constant base out of range\n");429return nullptr;430}431432++NumWrappedOneCond;433return createCond(CI, Exp, CmpInst::FCMP_OGT, 127.0f);434}435436// If the Base value coming from an integer type.437Instruction *I = dyn_cast<Instruction>(Base);438if (!I) {439LLVM_DEBUG(dbgs() << "Not handled pow(): FP type base\n");440return nullptr;441}442unsigned Opcode = I->getOpcode();443if (Opcode == Instruction::UIToFP || Opcode == Instruction::SIToFP) {444unsigned BW = I->getOperand(0)->getType()->getPrimitiveSizeInBits();445float UpperV = 0.0f;446if (BW == 8)447UpperV = 128.0f;448else if (BW == 16)449UpperV = 64.0f;450else if (BW == 32)451UpperV = 32.0f;452else {453LLVM_DEBUG(dbgs() << "Not handled pow(): type too wide\n");454return nullptr;455}456457++NumWrappedTwoCond;458return createOrCond(CI, Base, CmpInst::FCMP_OLE, 0.0f, Exp,459CmpInst::FCMP_OGT, UpperV);460}461LLVM_DEBUG(dbgs() << "Not handled pow(): base not from integer convert\n");462return nullptr;463}464465// Wrap conditions that can potentially generate errno to the library call.466void LibCallsShrinkWrap::shrinkWrapCI(CallInst *CI, Value *Cond) {467assert(Cond != nullptr && "ShrinkWrapCI is not expecting an empty call inst");468MDNode *BranchWeights =469MDBuilder(CI->getContext()).createUnlikelyBranchWeights();470471Instruction *NewInst =472SplitBlockAndInsertIfThen(Cond, CI, false, BranchWeights, &DTU);473BasicBlock *CallBB = NewInst->getParent();474CallBB->setName("cdce.call");475BasicBlock *SuccBB = CallBB->getSingleSuccessor();476assert(SuccBB && "The split block should have a single successor");477SuccBB->setName("cdce.end");478CI->removeFromParent();479CI->insertInto(CallBB, CallBB->getFirstInsertionPt());480LLVM_DEBUG(dbgs() << "== Basic Block After ==");481LLVM_DEBUG(dbgs() << *CallBB->getSinglePredecessor() << *CallBB482<< *CallBB->getSingleSuccessor() << "\n");483}484485// Perform the transformation to a single candidate.486bool LibCallsShrinkWrap::perform(CallInst *CI) {487LibFunc Func;488Function *Callee = CI->getCalledFunction();489assert(Callee && "perform() should apply to a non-empty callee");490TLI.getLibFunc(*Callee, Func);491assert(Func && "perform() is not expecting an empty function");492493if (performCallDomainErrorOnly(CI, Func) || performCallRangeErrorOnly(CI, Func))494return true;495return performCallErrors(CI, Func);496}497498static bool runImpl(Function &F, const TargetLibraryInfo &TLI,499DominatorTree *DT) {500if (F.hasFnAttribute(Attribute::OptimizeForSize))501return false;502DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Lazy);503LibCallsShrinkWrap CCDCE(TLI, DTU);504CCDCE.visit(F);505bool Changed = CCDCE.perform();506507// Verify the dominator after we've updated it locally.508assert(!DT ||509DTU.getDomTree().verify(DominatorTree::VerificationLevel::Fast));510return Changed;511}512513PreservedAnalyses LibCallsShrinkWrapPass::run(Function &F,514FunctionAnalysisManager &FAM) {515auto &TLI = FAM.getResult<TargetLibraryAnalysis>(F);516auto *DT = FAM.getCachedResult<DominatorTreeAnalysis>(F);517if (!runImpl(F, TLI, DT))518return PreservedAnalyses::all();519auto PA = PreservedAnalyses();520PA.preserve<DominatorTreeAnalysis>();521return PA;522}523524525