Implement new LibCallSimplifier class

This patch implements the new LibCallSimplifier class as outlined in [1].
In addition to providing the new base library simplification infrastructure,
all the fortified library call simplifications were moved over to the new
infrastructure.  The rest of the library simplification optimizations will
be moved over with follow up patches.

NOTE: The original fortified library call simplifier located in the
SimplifyFortifiedLibCalls class was not removed because it is still
used by CodeGenPrepare.  This class will eventually go away too.

[1] http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-August/052283.html

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@165873 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Meador Inge 2012-10-13 16:45:24 +00:00
parent af89690760
commit 5e8904576a
6 changed files with 342 additions and 36 deletions

View File

@ -0,0 +1,43 @@
//===- SimplifyLibCalls.h - Library call simplifier -------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file exposes an interface to build some C language libcalls for
// optimization passes that need to call the various functions.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TRANSFORMS_UTILS_SIMPLIFYLIBCALLS_H
#define LLVM_TRANSFORMS_UTILS_SIMPLIFYLIBCALLS_H
namespace llvm {
class Value;
class CallInst;
class DataLayout;
class TargetLibraryInfo;
class LibCallSimplifierImpl;
/// LibCallSimplifier - This class implements a collection of optimizations
/// that replace well formed calls to library functions with a more optimal
/// form. For example, replacing 'printf("Hello!")' with 'puts("Hello!")'.
class LibCallSimplifier {
/// Impl - A pointer to the actual implementation of the library call
/// simplifier.
LibCallSimplifierImpl *Impl;
public:
LibCallSimplifier(const DataLayout *TD, const TargetLibraryInfo *TLI);
virtual ~LibCallSimplifier();
/// optimizeCall - Take the given call instruction and return a more
/// optimal value to replace the instruction with or 0 if a more
/// optimal form can't be found.
Value *optimizeCall(CallInst *CI);
};
} // End llvm namespace
#endif

View File

@ -18,6 +18,7 @@
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Support/InstVisitor.h"
#include "llvm/Support/TargetFolder.h"
#include "llvm/Transforms/Utils/SimplifyLibCalls.h"
namespace llvm {
class CallSite;
@ -74,6 +75,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner
DataLayout *TD;
TargetLibraryInfo *TLI;
bool MadeIRChange;
LibCallSimplifier *Simplifier;
public:
/// Worklist - All of the instructions that need to be simplified.
InstCombineWorklist Worklist;

View File

@ -778,39 +778,6 @@ static bool isSafeToEliminateVarargsCast(const CallSite CS,
return true;
}
namespace {
class InstCombineFortifiedLibCalls : public SimplifyFortifiedLibCalls {
InstCombiner *IC;
protected:
void replaceCall(Value *With) {
NewInstruction = IC->ReplaceInstUsesWith(*CI, With);
}
bool isFoldable(unsigned SizeCIOp, unsigned SizeArgOp, bool isString) const {
if (CI->getArgOperand(SizeCIOp) == CI->getArgOperand(SizeArgOp))
return true;
if (ConstantInt *SizeCI =
dyn_cast<ConstantInt>(CI->getArgOperand(SizeCIOp))) {
if (SizeCI->isAllOnesValue())
return true;
if (isString) {
uint64_t Len = GetStringLength(CI->getArgOperand(SizeArgOp));
// If the length is 0 we don't know how long it is and so we can't
// remove the check.
if (Len == 0) return false;
return SizeCI->getZExtValue() >= Len;
}
if (ConstantInt *Arg = dyn_cast<ConstantInt>(
CI->getArgOperand(SizeArgOp)))
return SizeCI->getZExtValue() >= Arg->getZExtValue();
}
return false;
}
public:
InstCombineFortifiedLibCalls(InstCombiner *IC) : IC(IC), NewInstruction(0) { }
Instruction *NewInstruction;
};
} // end anonymous namespace
// Try to fold some different type of calls here.
// Currently we're only working with the checking functions, memcpy_chk,
// mempcpy_chk, memmove_chk, memset_chk, strcpy_chk, stpcpy_chk, strncpy_chk,
@ -818,9 +785,10 @@ public:
Instruction *InstCombiner::tryOptimizeCall(CallInst *CI, const DataLayout *TD) {
if (CI->getCalledFunction() == 0) return 0;
InstCombineFortifiedLibCalls Simplifier(this);
Simplifier.fold(CI, TD, TLI);
return Simplifier.NewInstruction;
if (Value *With = Simplifier->optimizeCall(CI))
return ReplaceInstUsesWith(*CI, With);
return 0;
}
static IntrinsicInst *FindInitTrampolineFromAlloca(Value *TrampMem) {

View File

@ -2130,6 +2130,9 @@ bool InstCombiner::runOnFunction(Function &F) {
InstCombineIRInserter(Worklist));
Builder = &TheBuilder;
LibCallSimplifier TheSimplifier(TD, TLI);
Simplifier = &TheSimplifier;
bool EverMadeChange = false;
// Lower dbg.declare intrinsics otherwise their value may be clobbered

View File

@ -28,6 +28,7 @@ add_llvm_library(LLVMTransformUtils
SimplifyCFG.cpp
SimplifyIndVar.cpp
SimplifyInstructions.cpp
SimplifyLibCalls.cpp
UnifyFunctionExitNodes.cpp
Utils.cpp
ValueMapper.cpp

View File

@ -0,0 +1,289 @@
//===------ SimplifyLibCalls.cpp - Library calls simplifier ---------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This is a utility pass used for testing the InstructionSimplify analysis.
// The analysis is applied to every instruction, and if it simplifies then the
// instruction is replaced by the simplification. If you are looking for a pass
// that performs serious instruction folding, use the instcombine pass instead.
//
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Utils/SimplifyLibCalls.h"
#include "llvm/DataLayout.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Function.h"
#include "llvm/IRBuilder.h"
#include "llvm/LLVMContext.h"
#include "llvm/Target/TargetLibraryInfo.h"
#include "llvm/Transforms/Utils/BuildLibCalls.h"
using namespace llvm;
/// This class is the abstract base class for the set of optimizations that
/// corresponds to one library call.
namespace {
class LibCallOptimization {
protected:
Function *Caller;
const DataLayout *TD;
const TargetLibraryInfo *TLI;
LLVMContext* Context;
public:
LibCallOptimization() { }
virtual ~LibCallOptimization() {}
/// callOptimizer - This pure virtual method is implemented by base classes to
/// do various optimizations. If this returns null then no transformation was
/// performed. If it returns CI, then it transformed the call and CI is to be
/// deleted. If it returns something else, replace CI with the new value and
/// delete CI.
virtual Value *callOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B)
=0;
Value *optimizeCall(CallInst *CI, const DataLayout *TD,
const TargetLibraryInfo *TLI, IRBuilder<> &B) {
Caller = CI->getParent()->getParent();
this->TD = TD;
this->TLI = TLI;
if (CI->getCalledFunction())
Context = &CI->getCalledFunction()->getContext();
// We never change the calling convention.
if (CI->getCallingConv() != llvm::CallingConv::C)
return NULL;
return callOptimizer(CI->getCalledFunction(), CI, B);
}
};
//===----------------------------------------------------------------------===//
// Fortified Library Call Optimizations
//===----------------------------------------------------------------------===//
struct FortifiedLibCallOptimization : public LibCallOptimization {
protected:
virtual bool isFoldable(unsigned SizeCIOp, unsigned SizeArgOp,
bool isString) const = 0;
};
struct InstFortifiedLibCallOptimization : public FortifiedLibCallOptimization {
CallInst *CI;
bool isFoldable(unsigned SizeCIOp, unsigned SizeArgOp, bool isString) const {
if (CI->getArgOperand(SizeCIOp) == CI->getArgOperand(SizeArgOp))
return true;
if (ConstantInt *SizeCI =
dyn_cast<ConstantInt>(CI->getArgOperand(SizeCIOp))) {
if (SizeCI->isAllOnesValue())
return true;
if (isString) {
uint64_t Len = GetStringLength(CI->getArgOperand(SizeArgOp));
// If the length is 0 we don't know how long it is and so we can't
// remove the check.
if (Len == 0) return false;
return SizeCI->getZExtValue() >= Len;
}
if (ConstantInt *Arg = dyn_cast<ConstantInt>(
CI->getArgOperand(SizeArgOp)))
return SizeCI->getZExtValue() >= Arg->getZExtValue();
}
return false;
}
};
struct MemCpyChkOpt : public InstFortifiedLibCallOptimization {
virtual Value *callOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) {
this->CI = CI;
FunctionType *FT = Callee->getFunctionType();
LLVMContext &Context = CI->getParent()->getContext();
// Check if this has the right signature.
if (FT->getNumParams() != 4 || FT->getReturnType() != FT->getParamType(0) ||
!FT->getParamType(0)->isPointerTy() ||
!FT->getParamType(1)->isPointerTy() ||
FT->getParamType(2) != TD->getIntPtrType(Context) ||
FT->getParamType(3) != TD->getIntPtrType(Context))
return 0;
if (isFoldable(3, 2, false)) {
B.CreateMemCpy(CI->getArgOperand(0), CI->getArgOperand(1),
CI->getArgOperand(2), 1);
return CI->getArgOperand(0);
}
return 0;
}
};
struct MemMoveChkOpt : public InstFortifiedLibCallOptimization {
virtual Value *callOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) {
this->CI = CI;
FunctionType *FT = Callee->getFunctionType();
LLVMContext &Context = CI->getParent()->getContext();
// Check if this has the right signature.
if (FT->getNumParams() != 4 || FT->getReturnType() != FT->getParamType(0) ||
!FT->getParamType(0)->isPointerTy() ||
!FT->getParamType(1)->isPointerTy() ||
FT->getParamType(2) != TD->getIntPtrType(Context) ||
FT->getParamType(3) != TD->getIntPtrType(Context))
return 0;
if (isFoldable(3, 2, false)) {
B.CreateMemMove(CI->getArgOperand(0), CI->getArgOperand(1),
CI->getArgOperand(2), 1);
return CI->getArgOperand(0);
}
return 0;
}
};
struct MemSetChkOpt : public InstFortifiedLibCallOptimization {
virtual Value *callOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) {
this->CI = CI;
FunctionType *FT = Callee->getFunctionType();
LLVMContext &Context = CI->getParent()->getContext();
// Check if this has the right signature.
if (FT->getNumParams() != 4 || FT->getReturnType() != FT->getParamType(0) ||
!FT->getParamType(0)->isPointerTy() ||
!FT->getParamType(1)->isIntegerTy() ||
FT->getParamType(2) != TD->getIntPtrType(Context) ||
FT->getParamType(3) != TD->getIntPtrType(Context))
return 0;
if (isFoldable(3, 2, false)) {
Value *Val = B.CreateIntCast(CI->getArgOperand(1), B.getInt8Ty(),
false);
B.CreateMemSet(CI->getArgOperand(0), Val, CI->getArgOperand(2), 1);
return CI->getArgOperand(0);
}
return 0;
}
};
struct StrCpyChkOpt : public InstFortifiedLibCallOptimization {
virtual Value *callOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) {
this->CI = CI;
StringRef Name = Callee->getName();
FunctionType *FT = Callee->getFunctionType();
LLVMContext &Context = CI->getParent()->getContext();
// Check if this has the right signature.
if (FT->getNumParams() != 3 ||
FT->getReturnType() != FT->getParamType(0) ||
FT->getParamType(0) != FT->getParamType(1) ||
FT->getParamType(0) != Type::getInt8PtrTy(Context) ||
FT->getParamType(2) != TD->getIntPtrType(Context))
return 0;
// If a) we don't have any length information, or b) we know this will
// fit then just lower to a plain st[rp]cpy. Otherwise we'll keep our
// st[rp]cpy_chk call which may fail at runtime if the size is too long.
// TODO: It might be nice to get a maximum length out of the possible
// string lengths for varying.
if (isFoldable(2, 1, true)) {
Value *Ret = EmitStrCpy(CI->getArgOperand(0), CI->getArgOperand(1), B, TD,
TLI, Name.substr(2, 6));
return Ret;
}
return 0;
}
};
struct StrNCpyChkOpt : public InstFortifiedLibCallOptimization {
virtual Value *callOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) {
this->CI = CI;
StringRef Name = Callee->getName();
FunctionType *FT = Callee->getFunctionType();
LLVMContext &Context = CI->getParent()->getContext();
// Check if this has the right signature.
if (FT->getNumParams() != 4 || FT->getReturnType() != FT->getParamType(0) ||
FT->getParamType(0) != FT->getParamType(1) ||
FT->getParamType(0) != Type::getInt8PtrTy(Context) ||
!FT->getParamType(2)->isIntegerTy() ||
FT->getParamType(3) != TD->getIntPtrType(Context))
return 0;
if (isFoldable(3, 2, false)) {
Value *Ret = EmitStrNCpy(CI->getArgOperand(0), CI->getArgOperand(1),
CI->getArgOperand(2), B, TD, TLI,
Name.substr(2, 7));
return Ret;
}
return 0;
}
};
} // End anonymous namespace.
namespace llvm {
class LibCallSimplifierImpl {
LibCallSimplifier *Simplifier;
const DataLayout *TD;
const TargetLibraryInfo *TLI;
StringMap<LibCallOptimization*> Optimizations;
// Fortified library call optimizations.
MemCpyChkOpt MemCpyChk;
MemMoveChkOpt MemMoveChk;
MemSetChkOpt MemSetChk;
StrCpyChkOpt StrCpyChk;
StrNCpyChkOpt StrNCpyChk;
void initOptimizations();
public:
LibCallSimplifierImpl(const DataLayout *TD, const TargetLibraryInfo *TLI) {
this->TD = TD;
this->TLI = TLI;
}
Value *optimizeCall(CallInst *CI);
};
void LibCallSimplifierImpl::initOptimizations() {
// Fortified library call optimizations.
Optimizations["__memcpy_chk"] = &MemCpyChk;
Optimizations["__memmove_chk"] = &MemMoveChk;
Optimizations["__memset_chk"] = &MemSetChk;
Optimizations["__strcpy_chk"] = &StrCpyChk;
Optimizations["__stpcpy_chk"] = &StrCpyChk;
Optimizations["__strncpy_chk"] = &StrNCpyChk;
Optimizations["__stpncpy_chk"] = &StrNCpyChk;
}
Value *LibCallSimplifierImpl::optimizeCall(CallInst *CI) {
if (Optimizations.empty())
initOptimizations();
Function *Callee = CI->getCalledFunction();
LibCallOptimization *LCO = Optimizations.lookup(Callee->getName());
if (LCO) {
IRBuilder<> Builder(CI);
return LCO->optimizeCall(CI, TD, TLI, Builder);
}
return 0;
}
LibCallSimplifier::LibCallSimplifier(const DataLayout *TD,
const TargetLibraryInfo *TLI) {
Impl = new LibCallSimplifierImpl(TD, TLI);
}
LibCallSimplifier::~LibCallSimplifier() {
delete Impl;
}
Value *LibCallSimplifier::optimizeCall(CallInst *CI) {
return Impl->optimizeCall(CI);
}
}