#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/ConstantFolding.h"
#include "llvm/Analysis/DomTreeUpdater.h"
#include "llvm/Analysis/LazyBlockFrequencyInfo.h"
#include "llvm/Analysis/ProfileSummaryInfo.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/InitializePasses.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/SizeOpts.h"
using namespace llvm;
namespace llvm {
class TargetLowering;
}
#define DEBUG_TYPE "expandmemcmp"
STATISTIC(NumMemCmpCalls, "Number of memcmp calls");
STATISTIC(NumMemCmpNotConstant, "Number of memcmp calls without constant size");
STATISTIC(NumMemCmpGreaterThanMax,
"Number of memcmp calls with size greater than max size");
STATISTIC(NumMemCmpInlined, "Number of inlined memcmp calls");
static cl::opt<unsigned> MemCmpEqZeroNumLoadsPerBlock(
"memcmp-num-loads-per-block", cl::Hidden, cl::init(1),
cl::desc("The number of loads per basic block for inline expansion of "
"memcmp that is only being compared against zero."));
static cl::opt<unsigned> MaxLoadsPerMemcmp(
"max-loads-per-memcmp", cl::Hidden,
cl::desc("Set maximum number of loads used in expanded memcmp"));
static cl::opt<unsigned> MaxLoadsPerMemcmpOptSize(
"max-loads-per-memcmp-opt-size", cl::Hidden,
cl::desc("Set maximum number of loads used in expanded memcmp for -Os/Oz"));
namespace {
class MemCmpExpansion {
struct ResultBlock {
BasicBlock *BB = nullptr;
PHINode *PhiSrc1 = nullptr;
PHINode *PhiSrc2 = nullptr;
ResultBlock() = default;
};
CallInst *const CI;
ResultBlock ResBlock;
const uint64_t Size;
unsigned MaxLoadSize = 0;
uint64_t NumLoadsNonOneByte = 0;
const uint64_t NumLoadsPerBlockForZeroCmp;
std::vector<BasicBlock *> LoadCmpBlocks;
BasicBlock *EndBlock;
PHINode *PhiRes;
const bool IsUsedForZeroCmp;
const DataLayout &DL;
DomTreeUpdater *DTU;
IRBuilder<> Builder;
struct LoadEntry {
LoadEntry(unsigned LoadSize, uint64_t Offset)
: LoadSize(LoadSize), Offset(Offset) {
}
unsigned LoadSize;
uint64_t Offset;
};
using LoadEntryVector = SmallVector<LoadEntry, 8>;
LoadEntryVector LoadSequence;
void createLoadCmpBlocks();
void createResultBlock();
void setupResultBlockPHINodes();
void setupEndBlockPHINodes();
Value *getCompareLoadPairs(unsigned BlockIndex, unsigned &LoadIndex);
void emitLoadCompareBlock(unsigned BlockIndex);
void emitLoadCompareBlockMultipleLoads(unsigned BlockIndex,
unsigned &LoadIndex);
void emitLoadCompareByteBlock(unsigned BlockIndex, unsigned OffsetBytes);
void emitMemCmpResultBlock();
Value *getMemCmpExpansionZeroCase();
Value *getMemCmpEqZeroOneBlock();
Value *getMemCmpOneBlock();
struct LoadPair {
Value *Lhs = nullptr;
Value *Rhs = nullptr;
};
LoadPair getLoadPair(Type *LoadSizeType, bool NeedsBSwap, Type *CmpSizeType,
unsigned OffsetBytes);
static LoadEntryVector
computeGreedyLoadSequence(uint64_t Size, llvm::ArrayRef<unsigned> LoadSizes,
unsigned MaxNumLoads, unsigned &NumLoadsNonOneByte);
static LoadEntryVector
computeOverlappingLoadSequence(uint64_t Size, unsigned MaxLoadSize,
unsigned MaxNumLoads,
unsigned &NumLoadsNonOneByte);
public:
MemCmpExpansion(CallInst *CI, uint64_t Size,
const TargetTransformInfo::MemCmpExpansionOptions &Options,
const bool IsUsedForZeroCmp, const DataLayout &TheDataLayout,
DomTreeUpdater *DTU);
unsigned getNumBlocks();
uint64_t getNumLoads() const { return LoadSequence.size(); }
Value *getMemCmpExpansion();
};
MemCmpExpansion::LoadEntryVector MemCmpExpansion::computeGreedyLoadSequence(
uint64_t Size, llvm::ArrayRef<unsigned> LoadSizes,
const unsigned MaxNumLoads, unsigned &NumLoadsNonOneByte) {
NumLoadsNonOneByte = 0;
LoadEntryVector LoadSequence;
uint64_t Offset = 0;
while (Size && !LoadSizes.empty()) {
const unsigned LoadSize = LoadSizes.front();
const uint64_t NumLoadsForThisSize = Size / LoadSize;
if (LoadSequence.size() + NumLoadsForThisSize > MaxNumLoads) {
return {};
}
if (NumLoadsForThisSize > 0) {
for (uint64_t I = 0; I < NumLoadsForThisSize; ++I) {
LoadSequence.push_back({LoadSize, Offset});
Offset += LoadSize;
}
if (LoadSize > 1)
++NumLoadsNonOneByte;
Size = Size % LoadSize;
}
LoadSizes = LoadSizes.drop_front();
}
return LoadSequence;
}
MemCmpExpansion::LoadEntryVector
MemCmpExpansion::computeOverlappingLoadSequence(uint64_t Size,
const unsigned MaxLoadSize,
const unsigned MaxNumLoads,
unsigned &NumLoadsNonOneByte) {
if (Size < 2 || MaxLoadSize < 2)
return {};
const uint64_t NumNonOverlappingLoads = Size / MaxLoadSize;
assert(NumNonOverlappingLoads && "there must be at least one load");
Size = Size - NumNonOverlappingLoads * MaxLoadSize;
if (Size == 0)
return {};
if ((NumNonOverlappingLoads + 1) > MaxNumLoads)
return {};
LoadEntryVector LoadSequence;
uint64_t Offset = 0;
for (uint64_t I = 0; I < NumNonOverlappingLoads; ++I) {
LoadSequence.push_back({MaxLoadSize, Offset});
Offset += MaxLoadSize;
}
assert(Size > 0 && Size < MaxLoadSize && "broken invariant");
LoadSequence.push_back({MaxLoadSize, Offset - (MaxLoadSize - Size)});
NumLoadsNonOneByte = 1;
return LoadSequence;
}
MemCmpExpansion::MemCmpExpansion(
CallInst *const CI, uint64_t Size,
const TargetTransformInfo::MemCmpExpansionOptions &Options,
const bool IsUsedForZeroCmp, const DataLayout &TheDataLayout,
DomTreeUpdater *DTU)
: CI(CI), Size(Size), NumLoadsPerBlockForZeroCmp(Options.NumLoadsPerBlock),
IsUsedForZeroCmp(IsUsedForZeroCmp), DL(TheDataLayout), DTU(DTU),
Builder(CI) {
assert(Size > 0 && "zero blocks");
llvm::ArrayRef<unsigned> LoadSizes(Options.LoadSizes);
while (!LoadSizes.empty() && LoadSizes.front() > Size) {
LoadSizes = LoadSizes.drop_front();
}
assert(!LoadSizes.empty() && "cannot load Size bytes");
MaxLoadSize = LoadSizes.front();
unsigned GreedyNumLoadsNonOneByte = 0;
LoadSequence = computeGreedyLoadSequence(Size, LoadSizes, Options.MaxNumLoads,
GreedyNumLoadsNonOneByte);
NumLoadsNonOneByte = GreedyNumLoadsNonOneByte;
assert(LoadSequence.size() <= Options.MaxNumLoads && "broken invariant");
if (Options.AllowOverlappingLoads &&
(LoadSequence.empty() || LoadSequence.size() > 2)) {
unsigned OverlappingNumLoadsNonOneByte = 0;
auto OverlappingLoads = computeOverlappingLoadSequence(
Size, MaxLoadSize, Options.MaxNumLoads, OverlappingNumLoadsNonOneByte);
if (!OverlappingLoads.empty() &&
(LoadSequence.empty() ||
OverlappingLoads.size() < LoadSequence.size())) {
LoadSequence = OverlappingLoads;
NumLoadsNonOneByte = OverlappingNumLoadsNonOneByte;
}
}
assert(LoadSequence.size() <= Options.MaxNumLoads && "broken invariant");
}
unsigned MemCmpExpansion::getNumBlocks() {
if (IsUsedForZeroCmp)
return getNumLoads() / NumLoadsPerBlockForZeroCmp +
(getNumLoads() % NumLoadsPerBlockForZeroCmp != 0 ? 1 : 0);
return getNumLoads();
}
void MemCmpExpansion::createLoadCmpBlocks() {
for (unsigned i = 0; i < getNumBlocks(); i++) {
BasicBlock *BB = BasicBlock::Create(CI->getContext(), "loadbb",
EndBlock->getParent(), EndBlock);
LoadCmpBlocks.push_back(BB);
}
}
void MemCmpExpansion::createResultBlock() {
ResBlock.BB = BasicBlock::Create(CI->getContext(), "res_block",
EndBlock->getParent(), EndBlock);
}
MemCmpExpansion::LoadPair MemCmpExpansion::getLoadPair(Type *LoadSizeType,
bool NeedsBSwap,
Type *CmpSizeType,
unsigned OffsetBytes) {
Value *LhsSource = CI->getArgOperand(0);
Value *RhsSource = CI->getArgOperand(1);
Align LhsAlign = LhsSource->getPointerAlignment(DL);
Align RhsAlign = RhsSource->getPointerAlignment(DL);
if (OffsetBytes > 0) {
auto *ByteType = Type::getInt8Ty(CI->getContext());
LhsSource = Builder.CreateConstGEP1_64(
ByteType, Builder.CreateBitCast(LhsSource, ByteType->getPointerTo()),
OffsetBytes);
RhsSource = Builder.CreateConstGEP1_64(
ByteType, Builder.CreateBitCast(RhsSource, ByteType->getPointerTo()),
OffsetBytes);
LhsAlign = commonAlignment(LhsAlign, OffsetBytes);
RhsAlign = commonAlignment(RhsAlign, OffsetBytes);
}
LhsSource = Builder.CreateBitCast(LhsSource, LoadSizeType->getPointerTo());
RhsSource = Builder.CreateBitCast(RhsSource, LoadSizeType->getPointerTo());
Value *Lhs = nullptr;
if (auto *C = dyn_cast<Constant>(LhsSource))
Lhs = ConstantFoldLoadFromConstPtr(C, LoadSizeType, DL);
if (!Lhs)
Lhs = Builder.CreateAlignedLoad(LoadSizeType, LhsSource, LhsAlign);
Value *Rhs = nullptr;
if (auto *C = dyn_cast<Constant>(RhsSource))
Rhs = ConstantFoldLoadFromConstPtr(C, LoadSizeType, DL);
if (!Rhs)
Rhs = Builder.CreateAlignedLoad(LoadSizeType, RhsSource, RhsAlign);
if (NeedsBSwap) {
Function *Bswap = Intrinsic::getDeclaration(CI->getModule(),
Intrinsic::bswap, LoadSizeType);
Lhs = Builder.CreateCall(Bswap, Lhs);
Rhs = Builder.CreateCall(Bswap, Rhs);
}
if (CmpSizeType != nullptr && CmpSizeType != LoadSizeType) {
Lhs = Builder.CreateZExt(Lhs, CmpSizeType);
Rhs = Builder.CreateZExt(Rhs, CmpSizeType);
}
return {Lhs, Rhs};
}
void MemCmpExpansion::emitLoadCompareByteBlock(unsigned BlockIndex,
unsigned OffsetBytes) {
BasicBlock *BB = LoadCmpBlocks[BlockIndex];
Builder.SetInsertPoint(BB);
const LoadPair Loads =
getLoadPair(Type::getInt8Ty(CI->getContext()), false,
Type::getInt32Ty(CI->getContext()), OffsetBytes);
Value *Diff = Builder.CreateSub(Loads.Lhs, Loads.Rhs);
PhiRes->addIncoming(Diff, BB);
if (BlockIndex < (LoadCmpBlocks.size() - 1)) {
Value *Cmp = Builder.CreateICmp(ICmpInst::ICMP_NE, Diff,
ConstantInt::get(Diff->getType(), 0));
BranchInst *CmpBr =
BranchInst::Create(EndBlock, LoadCmpBlocks[BlockIndex + 1], Cmp);
Builder.Insert(CmpBr);
if (DTU)
DTU->applyUpdates(
{{DominatorTree::Insert, BB, EndBlock},
{DominatorTree::Insert, BB, LoadCmpBlocks[BlockIndex + 1]}});
} else {
BranchInst *CmpBr = BranchInst::Create(EndBlock);
Builder.Insert(CmpBr);
if (DTU)
DTU->applyUpdates({{DominatorTree::Insert, BB, EndBlock}});
}
}
Value *MemCmpExpansion::getCompareLoadPairs(unsigned BlockIndex,
unsigned &LoadIndex) {
assert(LoadIndex < getNumLoads() &&
"getCompareLoadPairs() called with no remaining loads");
std::vector<Value *> XorList, OrList;
Value *Diff = nullptr;
const unsigned NumLoads =
std::min(getNumLoads() - LoadIndex, NumLoadsPerBlockForZeroCmp);
if (LoadCmpBlocks.empty())
Builder.SetInsertPoint(CI);
else
Builder.SetInsertPoint(LoadCmpBlocks[BlockIndex]);
Value *Cmp = nullptr;
IntegerType *const MaxLoadType =
NumLoads == 1 ? nullptr
: IntegerType::get(CI->getContext(), MaxLoadSize * 8);
for (unsigned i = 0; i < NumLoads; ++i, ++LoadIndex) {
const LoadEntry &CurLoadEntry = LoadSequence[LoadIndex];
const LoadPair Loads = getLoadPair(
IntegerType::get(CI->getContext(), CurLoadEntry.LoadSize * 8),
false, MaxLoadType, CurLoadEntry.Offset);
if (NumLoads != 1) {
Diff = Builder.CreateXor(Loads.Lhs, Loads.Rhs);
Diff = Builder.CreateZExt(Diff, MaxLoadType);
XorList.push_back(Diff);
} else {
Cmp = Builder.CreateICmpNE(Loads.Lhs, Loads.Rhs);
}
}
auto pairWiseOr = [&](std::vector<Value *> &InList) -> std::vector<Value *> {
std::vector<Value *> OutList;
for (unsigned i = 0; i < InList.size() - 1; i = i + 2) {
Value *Or = Builder.CreateOr(InList[i], InList[i + 1]);
OutList.push_back(Or);
}
if (InList.size() % 2 != 0)
OutList.push_back(InList.back());
return OutList;
};
if (!Cmp) {
OrList = pairWiseOr(XorList);
while (OrList.size() != 1) {
OrList = pairWiseOr(OrList);
}
assert(Diff && "Failed to find comparison diff");
Cmp = Builder.CreateICmpNE(OrList[0], ConstantInt::get(Diff->getType(), 0));
}
return Cmp;
}
void MemCmpExpansion::emitLoadCompareBlockMultipleLoads(unsigned BlockIndex,
unsigned &LoadIndex) {
Value *Cmp = getCompareLoadPairs(BlockIndex, LoadIndex);
BasicBlock *NextBB = (BlockIndex == (LoadCmpBlocks.size() - 1))
? EndBlock
: LoadCmpBlocks[BlockIndex + 1];
BasicBlock *BB = Builder.GetInsertBlock();
BranchInst *CmpBr = BranchInst::Create(ResBlock.BB, NextBB, Cmp);
Builder.Insert(CmpBr);
if (DTU)
DTU->applyUpdates({{DominatorTree::Insert, BB, ResBlock.BB},
{DominatorTree::Insert, BB, NextBB}});
if (BlockIndex == LoadCmpBlocks.size() - 1) {
Value *Zero = ConstantInt::get(Type::getInt32Ty(CI->getContext()), 0);
PhiRes->addIncoming(Zero, LoadCmpBlocks[BlockIndex]);
}
}
void MemCmpExpansion::emitLoadCompareBlock(unsigned BlockIndex) {
const LoadEntry &CurLoadEntry = LoadSequence[BlockIndex];
if (CurLoadEntry.LoadSize == 1) {
MemCmpExpansion::emitLoadCompareByteBlock(BlockIndex, CurLoadEntry.Offset);
return;
}
Type *LoadSizeType =
IntegerType::get(CI->getContext(), CurLoadEntry.LoadSize * 8);
Type *MaxLoadType = IntegerType::get(CI->getContext(), MaxLoadSize * 8);
assert(CurLoadEntry.LoadSize <= MaxLoadSize && "Unexpected load type");
Builder.SetInsertPoint(LoadCmpBlocks[BlockIndex]);
const LoadPair Loads =
getLoadPair(LoadSizeType, DL.isLittleEndian(), MaxLoadType,
CurLoadEntry.Offset);
if (!IsUsedForZeroCmp) {
ResBlock.PhiSrc1->addIncoming(Loads.Lhs, LoadCmpBlocks[BlockIndex]);
ResBlock.PhiSrc2->addIncoming(Loads.Rhs, LoadCmpBlocks[BlockIndex]);
}
Value *Cmp = Builder.CreateICmp(ICmpInst::ICMP_EQ, Loads.Lhs, Loads.Rhs);
BasicBlock *NextBB = (BlockIndex == (LoadCmpBlocks.size() - 1))
? EndBlock
: LoadCmpBlocks[BlockIndex + 1];
BasicBlock *BB = Builder.GetInsertBlock();
BranchInst *CmpBr = BranchInst::Create(NextBB, ResBlock.BB, Cmp);
Builder.Insert(CmpBr);
if (DTU)
DTU->applyUpdates({{DominatorTree::Insert, BB, NextBB},
{DominatorTree::Insert, BB, ResBlock.BB}});
if (BlockIndex == LoadCmpBlocks.size() - 1) {
Value *Zero = ConstantInt::get(Type::getInt32Ty(CI->getContext()), 0);
PhiRes->addIncoming(Zero, LoadCmpBlocks[BlockIndex]);
}
}
void MemCmpExpansion::emitMemCmpResultBlock() {
if (IsUsedForZeroCmp) {
BasicBlock::iterator InsertPt = ResBlock.BB->getFirstInsertionPt();
Builder.SetInsertPoint(ResBlock.BB, InsertPt);
Value *Res = ConstantInt::get(Type::getInt32Ty(CI->getContext()), 1);
PhiRes->addIncoming(Res, ResBlock.BB);
BranchInst *NewBr = BranchInst::Create(EndBlock);
Builder.Insert(NewBr);
if (DTU)
DTU->applyUpdates({{DominatorTree::Insert, ResBlock.BB, EndBlock}});
return;
}
BasicBlock::iterator InsertPt = ResBlock.BB->getFirstInsertionPt();
Builder.SetInsertPoint(ResBlock.BB, InsertPt);
Value *Cmp = Builder.CreateICmp(ICmpInst::ICMP_ULT, ResBlock.PhiSrc1,
ResBlock.PhiSrc2);
Value *Res =
Builder.CreateSelect(Cmp, ConstantInt::get(Builder.getInt32Ty(), -1),
ConstantInt::get(Builder.getInt32Ty(), 1));
PhiRes->addIncoming(Res, ResBlock.BB);
BranchInst *NewBr = BranchInst::Create(EndBlock);
Builder.Insert(NewBr);
if (DTU)
DTU->applyUpdates({{DominatorTree::Insert, ResBlock.BB, EndBlock}});
}
void MemCmpExpansion::setupResultBlockPHINodes() {
Type *MaxLoadType = IntegerType::get(CI->getContext(), MaxLoadSize * 8);
Builder.SetInsertPoint(ResBlock.BB);
ResBlock.PhiSrc1 =
Builder.CreatePHI(MaxLoadType, NumLoadsNonOneByte, "phi.src1");
ResBlock.PhiSrc2 =
Builder.CreatePHI(MaxLoadType, NumLoadsNonOneByte, "phi.src2");
}
void MemCmpExpansion::setupEndBlockPHINodes() {
Builder.SetInsertPoint(&EndBlock->front());
PhiRes = Builder.CreatePHI(Type::getInt32Ty(CI->getContext()), 2, "phi.res");
}
Value *MemCmpExpansion::getMemCmpExpansionZeroCase() {
unsigned LoadIndex = 0;
for (unsigned I = 0; I < getNumBlocks(); ++I) {
emitLoadCompareBlockMultipleLoads(I, LoadIndex);
}
emitMemCmpResultBlock();
return PhiRes;
}
Value *MemCmpExpansion::getMemCmpEqZeroOneBlock() {
unsigned LoadIndex = 0;
Value *Cmp = getCompareLoadPairs(0, LoadIndex);
assert(LoadIndex == getNumLoads() && "some entries were not consumed");
return Builder.CreateZExt(Cmp, Type::getInt32Ty(CI->getContext()));
}
Value *MemCmpExpansion::getMemCmpOneBlock() {
Type *LoadSizeType = IntegerType::get(CI->getContext(), Size * 8);
bool NeedsBSwap = DL.isLittleEndian() && Size != 1;
if (Size < 4) {
const LoadPair Loads =
getLoadPair(LoadSizeType, NeedsBSwap, Builder.getInt32Ty(),
0);
return Builder.CreateSub(Loads.Lhs, Loads.Rhs);
}
const LoadPair Loads = getLoadPair(LoadSizeType, NeedsBSwap, LoadSizeType,
0);
Value *CmpUGT = Builder.CreateICmpUGT(Loads.Lhs, Loads.Rhs);
Value *CmpULT = Builder.CreateICmpULT(Loads.Lhs, Loads.Rhs);
Value *ZextUGT = Builder.CreateZExt(CmpUGT, Builder.getInt32Ty());
Value *ZextULT = Builder.CreateZExt(CmpULT, Builder.getInt32Ty());
return Builder.CreateSub(ZextUGT, ZextULT);
}
Value *MemCmpExpansion::getMemCmpExpansion() {
if (getNumBlocks() != 1) {
BasicBlock *StartBlock = CI->getParent();
EndBlock = SplitBlock(StartBlock, CI, DTU, nullptr,
nullptr, "endblock");
setupEndBlockPHINodes();
createResultBlock();
if (!IsUsedForZeroCmp) setupResultBlockPHINodes();
createLoadCmpBlocks();
StartBlock->getTerminator()->setSuccessor(0, LoadCmpBlocks[0]);
if (DTU)
DTU->applyUpdates({{DominatorTree::Insert, StartBlock, LoadCmpBlocks[0]},
{DominatorTree::Delete, StartBlock, EndBlock}});
}
Builder.SetCurrentDebugLocation(CI->getDebugLoc());
if (IsUsedForZeroCmp)
return getNumBlocks() == 1 ? getMemCmpEqZeroOneBlock()
: getMemCmpExpansionZeroCase();
if (getNumBlocks() == 1)
return getMemCmpOneBlock();
for (unsigned I = 0; I < getNumBlocks(); ++I) {
emitLoadCompareBlock(I);
}
emitMemCmpResultBlock();
return PhiRes;
}
static bool expandMemCmp(CallInst *CI, const TargetTransformInfo *TTI,
const TargetLowering *TLI, const DataLayout *DL,
ProfileSummaryInfo *PSI, BlockFrequencyInfo *BFI,
DomTreeUpdater *DTU, const bool IsBCmp) {
NumMemCmpCalls++;
if (CI->getFunction()->hasMinSize())
return false;
ConstantInt *SizeCast = dyn_cast<ConstantInt>(CI->getArgOperand(2));
if (!SizeCast) {
NumMemCmpNotConstant++;
return false;
}
const uint64_t SizeVal = SizeCast->getZExtValue();
if (SizeVal == 0) {
return false;
}
const bool IsUsedForZeroCmp =
IsBCmp || isOnlyUsedInZeroEqualityComparison(CI);
bool OptForSize = CI->getFunction()->hasOptSize() ||
llvm::shouldOptimizeForSize(CI->getParent(), PSI, BFI);
auto Options = TTI->enableMemCmpExpansion(OptForSize,
IsUsedForZeroCmp);
if (!Options) return false;
if (MemCmpEqZeroNumLoadsPerBlock.getNumOccurrences())
Options.NumLoadsPerBlock = MemCmpEqZeroNumLoadsPerBlock;
if (OptForSize &&
MaxLoadsPerMemcmpOptSize.getNumOccurrences())
Options.MaxNumLoads = MaxLoadsPerMemcmpOptSize;
if (!OptForSize && MaxLoadsPerMemcmp.getNumOccurrences())
Options.MaxNumLoads = MaxLoadsPerMemcmp;
MemCmpExpansion Expansion(CI, SizeVal, Options, IsUsedForZeroCmp, *DL, DTU);
if (Expansion.getNumLoads() == 0) {
NumMemCmpGreaterThanMax++;
return false;
}
NumMemCmpInlined++;
Value *Res = Expansion.getMemCmpExpansion();
CI->replaceAllUsesWith(Res);
CI->eraseFromParent();
return true;
}
class ExpandMemCmpPass : public FunctionPass {
public:
static char ID;
ExpandMemCmpPass() : FunctionPass(ID) {
initializeExpandMemCmpPassPass(*PassRegistry::getPassRegistry());
}
bool runOnFunction(Function &F) override {
if (skipFunction(F)) return false;
auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
if (!TPC) {
return false;
}
const TargetLowering* TL =
TPC->getTM<TargetMachine>().getSubtargetImpl(F)->getTargetLowering();
const TargetLibraryInfo *TLI =
&getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F);
const TargetTransformInfo *TTI =
&getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
auto *PSI = &getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI();
auto *BFI = (PSI && PSI->hasProfileSummary()) ?
&getAnalysis<LazyBlockFrequencyInfoPass>().getBFI() :
nullptr;
DominatorTree *DT = nullptr;
if (auto *DTWP = getAnalysisIfAvailable<DominatorTreeWrapperPass>())
DT = &DTWP->getDomTree();
auto PA = runImpl(F, TLI, TTI, TL, PSI, BFI, DT);
return !PA.areAllPreserved();
}
private:
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<TargetLibraryInfoWrapperPass>();
AU.addRequired<TargetTransformInfoWrapperPass>();
AU.addRequired<ProfileSummaryInfoWrapperPass>();
AU.addPreserved<DominatorTreeWrapperPass>();
LazyBlockFrequencyInfoPass::getLazyBFIAnalysisUsage(AU);
FunctionPass::getAnalysisUsage(AU);
}
PreservedAnalyses runImpl(Function &F, const TargetLibraryInfo *TLI,
const TargetTransformInfo *TTI,
const TargetLowering *TL, ProfileSummaryInfo *PSI,
BlockFrequencyInfo *BFI, DominatorTree *DT);
bool runOnBlock(BasicBlock &BB, const TargetLibraryInfo *TLI,
const TargetTransformInfo *TTI, const TargetLowering *TL,
const DataLayout &DL, ProfileSummaryInfo *PSI,
BlockFrequencyInfo *BFI, DomTreeUpdater *DTU);
};
bool ExpandMemCmpPass::runOnBlock(BasicBlock &BB, const TargetLibraryInfo *TLI,
const TargetTransformInfo *TTI,
const TargetLowering *TL,
const DataLayout &DL, ProfileSummaryInfo *PSI,
BlockFrequencyInfo *BFI,
DomTreeUpdater *DTU) {
for (Instruction& I : BB) {
CallInst *CI = dyn_cast<CallInst>(&I);
if (!CI) {
continue;
}
LibFunc Func;
if (TLI->getLibFunc(*CI, Func) &&
(Func == LibFunc_memcmp || Func == LibFunc_bcmp) &&
expandMemCmp(CI, TTI, TL, &DL, PSI, BFI, DTU, Func == LibFunc_bcmp)) {
return true;
}
}
return false;
}
PreservedAnalyses
ExpandMemCmpPass::runImpl(Function &F, const TargetLibraryInfo *TLI,
const TargetTransformInfo *TTI,
const TargetLowering *TL, ProfileSummaryInfo *PSI,
BlockFrequencyInfo *BFI, DominatorTree *DT) {
Optional<DomTreeUpdater> DTU;
if (DT)
DTU.emplace(DT, DomTreeUpdater::UpdateStrategy::Lazy);
const DataLayout& DL = F.getParent()->getDataLayout();
bool MadeChanges = false;
for (auto BBIt = F.begin(); BBIt != F.end();) {
if (runOnBlock(*BBIt, TLI, TTI, TL, DL, PSI, BFI,
DTU ? DTU.getPointer() : nullptr)) {
MadeChanges = true;
BBIt = F.begin();
} else {
++BBIt;
}
}
if (MadeChanges)
for (BasicBlock &BB : F)
SimplifyInstructionsInBlock(&BB);
if (!MadeChanges)
return PreservedAnalyses::all();
PreservedAnalyses PA;
PA.preserve<DominatorTreeAnalysis>();
return PA;
}
}
char ExpandMemCmpPass::ID = 0;
INITIALIZE_PASS_BEGIN(ExpandMemCmpPass, "expandmemcmp",
"Expand memcmp() to load/stores", false, false)
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(LazyBlockFrequencyInfoPass)
INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
INITIALIZE_PASS_END(ExpandMemCmpPass, "expandmemcmp",
"Expand memcmp() to load/stores", false, false)
FunctionPass *llvm::createExpandMemCmpPass() {
return new ExpandMemCmpPass();
}