You've already forked linux-packaging-mono
Imported Upstream version 5.18.0.207
Former-commit-id: 3b152f462918d427ce18620a2cbe4f8b79650449
This commit is contained in:
parent
8e12397d70
commit
eb85e2fc17
@ -1 +0,0 @@
|
||||
8e39f24d819ce03d6802b9858643636e4c0e90f9
|
@ -1,228 +0,0 @@
|
||||
//===- BoundsChecking.cpp - Instrumentation for run-time bounds checking --===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Transforms/Instrumentation/BoundsChecking.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Analysis/MemoryBuiltins.h"
|
||||
#include "llvm/Analysis/TargetFolder.h"
|
||||
#include "llvm/Analysis/TargetLibraryInfo.h"
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/InstrTypes.h"
|
||||
#include "llvm/IR/Instruction.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/Intrinsics.h"
|
||||
#include "llvm/IR/Value.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "bounds-checking"
|
||||
|
||||
static cl::opt<bool> SingleTrapBB("bounds-checking-single-trap",
|
||||
cl::desc("Use one trap block per function"));
|
||||
|
||||
STATISTIC(ChecksAdded, "Bounds checks added");
|
||||
STATISTIC(ChecksSkipped, "Bounds checks skipped");
|
||||
STATISTIC(ChecksUnable, "Bounds checks unable to add");
|
||||
|
||||
using BuilderTy = IRBuilder<TargetFolder>;
|
||||
|
||||
/// Adds run-time bounds checks to memory accessing instructions.
|
||||
///
|
||||
/// \p Ptr is the pointer that will be read/written, and \p InstVal is either
|
||||
/// the result from the load or the value being stored. It is used to determine
|
||||
/// the size of memory block that is touched.
|
||||
///
|
||||
/// \p GetTrapBB is a callable that returns the trap BB to use on failure.
|
||||
///
|
||||
/// Returns true if any change was made to the IR, false otherwise.
|
||||
template <typename GetTrapBBT>
|
||||
static bool instrumentMemAccess(Value *Ptr, Value *InstVal,
|
||||
const DataLayout &DL, TargetLibraryInfo &TLI,
|
||||
ObjectSizeOffsetEvaluator &ObjSizeEval,
|
||||
BuilderTy &IRB,
|
||||
GetTrapBBT GetTrapBB) {
|
||||
uint64_t NeededSize = DL.getTypeStoreSize(InstVal->getType());
|
||||
DEBUG(dbgs() << "Instrument " << *Ptr << " for " << Twine(NeededSize)
|
||||
<< " bytes\n");
|
||||
|
||||
SizeOffsetEvalType SizeOffset = ObjSizeEval.compute(Ptr);
|
||||
|
||||
if (!ObjSizeEval.bothKnown(SizeOffset)) {
|
||||
++ChecksUnable;
|
||||
return false;
|
||||
}
|
||||
|
||||
Value *Size = SizeOffset.first;
|
||||
Value *Offset = SizeOffset.second;
|
||||
ConstantInt *SizeCI = dyn_cast<ConstantInt>(Size);
|
||||
|
||||
Type *IntTy = DL.getIntPtrType(Ptr->getType());
|
||||
Value *NeededSizeVal = ConstantInt::get(IntTy, NeededSize);
|
||||
|
||||
// three checks are required to ensure safety:
|
||||
// . Offset >= 0 (since the offset is given from the base ptr)
|
||||
// . Size >= Offset (unsigned)
|
||||
// . Size - Offset >= NeededSize (unsigned)
|
||||
//
|
||||
// optimization: if Size >= 0 (signed), skip 1st check
|
||||
// FIXME: add NSW/NUW here? -- we dont care if the subtraction overflows
|
||||
Value *ObjSize = IRB.CreateSub(Size, Offset);
|
||||
Value *Cmp2 = IRB.CreateICmpULT(Size, Offset);
|
||||
Value *Cmp3 = IRB.CreateICmpULT(ObjSize, NeededSizeVal);
|
||||
Value *Or = IRB.CreateOr(Cmp2, Cmp3);
|
||||
if (!SizeCI || SizeCI->getValue().slt(0)) {
|
||||
Value *Cmp1 = IRB.CreateICmpSLT(Offset, ConstantInt::get(IntTy, 0));
|
||||
Or = IRB.CreateOr(Cmp1, Or);
|
||||
}
|
||||
|
||||
// check if the comparison is always false
|
||||
ConstantInt *C = dyn_cast_or_null<ConstantInt>(Or);
|
||||
if (C) {
|
||||
++ChecksSkipped;
|
||||
// If non-zero, nothing to do.
|
||||
if (!C->getZExtValue())
|
||||
return true;
|
||||
}
|
||||
++ChecksAdded;
|
||||
|
||||
BasicBlock::iterator SplitI = IRB.GetInsertPoint();
|
||||
BasicBlock *OldBB = SplitI->getParent();
|
||||
BasicBlock *Cont = OldBB->splitBasicBlock(SplitI);
|
||||
OldBB->getTerminator()->eraseFromParent();
|
||||
|
||||
if (C) {
|
||||
// If we have a constant zero, unconditionally branch.
|
||||
// FIXME: We should really handle this differently to bypass the splitting
|
||||
// the block.
|
||||
BranchInst::Create(GetTrapBB(IRB), OldBB);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create the conditional branch.
|
||||
BranchInst::Create(GetTrapBB(IRB), Cont, Or, OldBB);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI) {
|
||||
const DataLayout &DL = F.getParent()->getDataLayout();
|
||||
ObjectSizeOffsetEvaluator ObjSizeEval(DL, &TLI, F.getContext(),
|
||||
/*RoundToAlign=*/true);
|
||||
|
||||
// check HANDLE_MEMORY_INST in include/llvm/Instruction.def for memory
|
||||
// touching instructions
|
||||
std::vector<Instruction *> WorkList;
|
||||
for (Instruction &I : instructions(F)) {
|
||||
if (isa<LoadInst>(I) || isa<StoreInst>(I) || isa<AtomicCmpXchgInst>(I) ||
|
||||
isa<AtomicRMWInst>(I))
|
||||
WorkList.push_back(&I);
|
||||
}
|
||||
|
||||
// Create a trapping basic block on demand using a callback. Depending on
|
||||
// flags, this will either create a single block for the entire function or
|
||||
// will create a fresh block every time it is called.
|
||||
BasicBlock *TrapBB = nullptr;
|
||||
auto GetTrapBB = [&TrapBB](BuilderTy &IRB) {
|
||||
if (TrapBB && SingleTrapBB)
|
||||
return TrapBB;
|
||||
|
||||
Function *Fn = IRB.GetInsertBlock()->getParent();
|
||||
// FIXME: This debug location doesn't make a lot of sense in the
|
||||
// `SingleTrapBB` case.
|
||||
auto DebugLoc = IRB.getCurrentDebugLocation();
|
||||
IRBuilder<>::InsertPointGuard Guard(IRB);
|
||||
TrapBB = BasicBlock::Create(Fn->getContext(), "trap", Fn);
|
||||
IRB.SetInsertPoint(TrapBB);
|
||||
|
||||
auto *F = Intrinsic::getDeclaration(Fn->getParent(), Intrinsic::trap);
|
||||
CallInst *TrapCall = IRB.CreateCall(F, {});
|
||||
TrapCall->setDoesNotReturn();
|
||||
TrapCall->setDoesNotThrow();
|
||||
TrapCall->setDebugLoc(DebugLoc);
|
||||
IRB.CreateUnreachable();
|
||||
|
||||
return TrapBB;
|
||||
};
|
||||
|
||||
bool MadeChange = false;
|
||||
for (Instruction *Inst : WorkList) {
|
||||
BuilderTy IRB(Inst->getParent(), BasicBlock::iterator(Inst), TargetFolder(DL));
|
||||
if (LoadInst *LI = dyn_cast<LoadInst>(Inst)) {
|
||||
MadeChange |= instrumentMemAccess(LI->getPointerOperand(), LI, DL, TLI,
|
||||
ObjSizeEval, IRB, GetTrapBB);
|
||||
} else if (StoreInst *SI = dyn_cast<StoreInst>(Inst)) {
|
||||
MadeChange |=
|
||||
instrumentMemAccess(SI->getPointerOperand(), SI->getValueOperand(),
|
||||
DL, TLI, ObjSizeEval, IRB, GetTrapBB);
|
||||
} else if (AtomicCmpXchgInst *AI = dyn_cast<AtomicCmpXchgInst>(Inst)) {
|
||||
MadeChange |=
|
||||
instrumentMemAccess(AI->getPointerOperand(), AI->getCompareOperand(),
|
||||
DL, TLI, ObjSizeEval, IRB, GetTrapBB);
|
||||
} else if (AtomicRMWInst *AI = dyn_cast<AtomicRMWInst>(Inst)) {
|
||||
MadeChange |=
|
||||
instrumentMemAccess(AI->getPointerOperand(), AI->getValOperand(), DL,
|
||||
TLI, ObjSizeEval, IRB, GetTrapBB);
|
||||
} else {
|
||||
llvm_unreachable("unknown Instruction type");
|
||||
}
|
||||
}
|
||||
return MadeChange;
|
||||
}
|
||||
|
||||
PreservedAnalyses BoundsCheckingPass::run(Function &F, FunctionAnalysisManager &AM) {
|
||||
auto &TLI = AM.getResult<TargetLibraryAnalysis>(F);
|
||||
|
||||
if (!addBoundsChecking(F, TLI))
|
||||
return PreservedAnalyses::all();
|
||||
|
||||
return PreservedAnalyses::none();
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct BoundsCheckingLegacyPass : public FunctionPass {
|
||||
static char ID;
|
||||
|
||||
BoundsCheckingLegacyPass() : FunctionPass(ID) {
|
||||
initializeBoundsCheckingLegacyPassPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
bool runOnFunction(Function &F) override {
|
||||
auto &TLI = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
|
||||
return addBoundsChecking(F, TLI);
|
||||
}
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.addRequired<TargetLibraryInfoWrapperPass>();
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
char BoundsCheckingLegacyPass::ID = 0;
|
||||
INITIALIZE_PASS_BEGIN(BoundsCheckingLegacyPass, "bounds-checking",
|
||||
"Run-time bounds checking", false, false)
|
||||
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
|
||||
INITIALIZE_PASS_END(BoundsCheckingLegacyPass, "bounds-checking",
|
||||
"Run-time bounds checking", false, false)
|
||||
|
||||
FunctionPass *llvm::createBoundsCheckingLegacyPass() {
|
||||
return new BoundsCheckingLegacyPass();
|
||||
}
|
@ -1,290 +0,0 @@
|
||||
//===-- CFGMST.h - Minimum Spanning Tree for CFG ----------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements a Union-find algorithm to compute Minimum Spanning Tree
|
||||
// for a given CFG.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TRANSFORMS_INSTRUMENTATION_CFGMST_H
|
||||
#define LLVM_LIB_TRANSFORMS_INSTRUMENTATION_CFGMST_H
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Analysis/BlockFrequencyInfo.h"
|
||||
#include "llvm/Analysis/BranchProbabilityInfo.h"
|
||||
#include "llvm/Analysis/CFG.h"
|
||||
#include "llvm/Support/BranchProbability.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#define DEBUG_TYPE "cfgmst"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// \brief An union-find based Minimum Spanning Tree for CFG
|
||||
///
|
||||
/// Implements a Union-find algorithm to compute Minimum Spanning Tree
|
||||
/// for a given CFG.
|
||||
template <class Edge, class BBInfo> class CFGMST {
|
||||
public:
|
||||
Function &F;
|
||||
|
||||
// Store all the edges in CFG. It may contain some stale edges
|
||||
// when Removed is set.
|
||||
std::vector<std::unique_ptr<Edge>> AllEdges;
|
||||
|
||||
// This map records the auxiliary information for each BB.
|
||||
DenseMap<const BasicBlock *, std::unique_ptr<BBInfo>> BBInfos;
|
||||
|
||||
// Whehter the function has an exit block with no successors.
|
||||
// (For function with an infinite loop, this block may be absent)
|
||||
bool ExitBlockFound = false;
|
||||
|
||||
// Find the root group of the G and compress the path from G to the root.
|
||||
BBInfo *findAndCompressGroup(BBInfo *G) {
|
||||
if (G->Group != G)
|
||||
G->Group = findAndCompressGroup(static_cast<BBInfo *>(G->Group));
|
||||
return static_cast<BBInfo *>(G->Group);
|
||||
}
|
||||
|
||||
// Union BB1 and BB2 into the same group and return true.
|
||||
// Returns false if BB1 and BB2 are already in the same group.
|
||||
bool unionGroups(const BasicBlock *BB1, const BasicBlock *BB2) {
|
||||
BBInfo *BB1G = findAndCompressGroup(&getBBInfo(BB1));
|
||||
BBInfo *BB2G = findAndCompressGroup(&getBBInfo(BB2));
|
||||
|
||||
if (BB1G == BB2G)
|
||||
return false;
|
||||
|
||||
// Make the smaller rank tree a direct child or the root of high rank tree.
|
||||
if (BB1G->Rank < BB2G->Rank)
|
||||
BB1G->Group = BB2G;
|
||||
else {
|
||||
BB2G->Group = BB1G;
|
||||
// If the ranks are the same, increment root of one tree by one.
|
||||
if (BB1G->Rank == BB2G->Rank)
|
||||
BB1G->Rank++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Give BB, return the auxiliary information.
|
||||
BBInfo &getBBInfo(const BasicBlock *BB) const {
|
||||
auto It = BBInfos.find(BB);
|
||||
assert(It->second.get() != nullptr);
|
||||
return *It->second.get();
|
||||
}
|
||||
|
||||
// Give BB, return the auxiliary information if it's available.
|
||||
BBInfo *findBBInfo(const BasicBlock *BB) const {
|
||||
auto It = BBInfos.find(BB);
|
||||
if (It == BBInfos.end())
|
||||
return nullptr;
|
||||
return It->second.get();
|
||||
}
|
||||
|
||||
// Traverse the CFG using a stack. Find all the edges and assign the weight.
|
||||
// Edges with large weight will be put into MST first so they are less likely
|
||||
// to be instrumented.
|
||||
void buildEdges() {
|
||||
DEBUG(dbgs() << "Build Edge on " << F.getName() << "\n");
|
||||
|
||||
const BasicBlock *Entry = &(F.getEntryBlock());
|
||||
uint64_t EntryWeight = (BFI != nullptr ? BFI->getEntryFreq() : 2);
|
||||
Edge *EntryIncoming = nullptr, *EntryOutgoing = nullptr,
|
||||
*ExitOutgoing = nullptr, *ExitIncoming = nullptr;
|
||||
uint64_t MaxEntryOutWeight = 0, MaxExitOutWeight = 0, MaxExitInWeight = 0;
|
||||
|
||||
// Add a fake edge to the entry.
|
||||
EntryIncoming = &addEdge(nullptr, Entry, EntryWeight);
|
||||
DEBUG(dbgs() << " Edge: from fake node to " << Entry->getName()
|
||||
<< " w = " << EntryWeight << "\n");
|
||||
|
||||
// Special handling for single BB functions.
|
||||
if (succ_empty(Entry)) {
|
||||
addEdge(Entry, nullptr, EntryWeight);
|
||||
return;
|
||||
}
|
||||
|
||||
static const uint32_t CriticalEdgeMultiplier = 1000;
|
||||
|
||||
for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB) {
|
||||
TerminatorInst *TI = BB->getTerminator();
|
||||
uint64_t BBWeight =
|
||||
(BFI != nullptr ? BFI->getBlockFreq(&*BB).getFrequency() : 2);
|
||||
uint64_t Weight = 2;
|
||||
if (int successors = TI->getNumSuccessors()) {
|
||||
for (int i = 0; i != successors; ++i) {
|
||||
BasicBlock *TargetBB = TI->getSuccessor(i);
|
||||
bool Critical = isCriticalEdge(TI, i);
|
||||
uint64_t scaleFactor = BBWeight;
|
||||
if (Critical) {
|
||||
if (scaleFactor < UINT64_MAX / CriticalEdgeMultiplier)
|
||||
scaleFactor *= CriticalEdgeMultiplier;
|
||||
else
|
||||
scaleFactor = UINT64_MAX;
|
||||
}
|
||||
if (BPI != nullptr)
|
||||
Weight = BPI->getEdgeProbability(&*BB, TargetBB).scale(scaleFactor);
|
||||
auto *E = &addEdge(&*BB, TargetBB, Weight);
|
||||
E->IsCritical = Critical;
|
||||
DEBUG(dbgs() << " Edge: from " << BB->getName() << " to "
|
||||
<< TargetBB->getName() << " w=" << Weight << "\n");
|
||||
|
||||
// Keep track of entry/exit edges:
|
||||
if (&*BB == Entry) {
|
||||
if (Weight > MaxEntryOutWeight) {
|
||||
MaxEntryOutWeight = Weight;
|
||||
EntryOutgoing = E;
|
||||
}
|
||||
}
|
||||
|
||||
auto *TargetTI = TargetBB->getTerminator();
|
||||
if (TargetTI && !TargetTI->getNumSuccessors()) {
|
||||
if (Weight > MaxExitInWeight) {
|
||||
MaxExitInWeight = Weight;
|
||||
ExitIncoming = E;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ExitBlockFound = true;
|
||||
Edge *ExitO = &addEdge(&*BB, nullptr, BBWeight);
|
||||
if (BBWeight > MaxExitOutWeight) {
|
||||
MaxExitOutWeight = BBWeight;
|
||||
ExitOutgoing = ExitO;
|
||||
}
|
||||
DEBUG(dbgs() << " Edge: from " << BB->getName() << " to fake exit"
|
||||
<< " w = " << BBWeight << "\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Entry/exit edge adjustment heurisitic:
|
||||
// prefer instrumenting entry edge over exit edge
|
||||
// if possible. Those exit edges may never have a chance to be
|
||||
// executed (for instance the program is an event handling loop)
|
||||
// before the profile is asynchronously dumped.
|
||||
//
|
||||
// If EntryIncoming and ExitOutgoing has similar weight, make sure
|
||||
// ExitOutging is selected as the min-edge. Similarly, if EntryOutgoing
|
||||
// and ExitIncoming has similar weight, make sure ExitIncoming becomes
|
||||
// the min-edge.
|
||||
uint64_t EntryInWeight = EntryWeight;
|
||||
|
||||
if (EntryInWeight >= MaxExitOutWeight &&
|
||||
EntryInWeight * 2 < MaxExitOutWeight * 3) {
|
||||
EntryIncoming->Weight = MaxExitOutWeight;
|
||||
ExitOutgoing->Weight = EntryInWeight + 1;
|
||||
}
|
||||
|
||||
if (MaxEntryOutWeight >= MaxExitInWeight &&
|
||||
MaxEntryOutWeight * 2 < MaxExitInWeight * 3) {
|
||||
EntryOutgoing->Weight = MaxExitInWeight;
|
||||
ExitIncoming->Weight = MaxEntryOutWeight + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort CFG edges based on its weight.
|
||||
void sortEdgesByWeight() {
|
||||
std::stable_sort(AllEdges.begin(), AllEdges.end(),
|
||||
[](const std::unique_ptr<Edge> &Edge1,
|
||||
const std::unique_ptr<Edge> &Edge2) {
|
||||
return Edge1->Weight > Edge2->Weight;
|
||||
});
|
||||
}
|
||||
|
||||
// Traverse all the edges and compute the Minimum Weight Spanning Tree
|
||||
// using union-find algorithm.
|
||||
void computeMinimumSpanningTree() {
|
||||
// First, put all the critical edge with landing-pad as the Dest to MST.
|
||||
// This works around the insufficient support of critical edges split
|
||||
// when destination BB is a landing pad.
|
||||
for (auto &Ei : AllEdges) {
|
||||
if (Ei->Removed)
|
||||
continue;
|
||||
if (Ei->IsCritical) {
|
||||
if (Ei->DestBB && Ei->DestBB->isLandingPad()) {
|
||||
if (unionGroups(Ei->SrcBB, Ei->DestBB))
|
||||
Ei->InMST = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &Ei : AllEdges) {
|
||||
if (Ei->Removed)
|
||||
continue;
|
||||
// If we detect infinite loops, force
|
||||
// instrumenting the entry edge:
|
||||
if (!ExitBlockFound && Ei->SrcBB == nullptr)
|
||||
continue;
|
||||
if (unionGroups(Ei->SrcBB, Ei->DestBB))
|
||||
Ei->InMST = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Dump the Debug information about the instrumentation.
|
||||
void dumpEdges(raw_ostream &OS, const Twine &Message) const {
|
||||
if (!Message.str().empty())
|
||||
OS << Message << "\n";
|
||||
OS << " Number of Basic Blocks: " << BBInfos.size() << "\n";
|
||||
for (auto &BI : BBInfos) {
|
||||
const BasicBlock *BB = BI.first;
|
||||
OS << " BB: " << (BB == nullptr ? "FakeNode" : BB->getName()) << " "
|
||||
<< BI.second->infoString() << "\n";
|
||||
}
|
||||
|
||||
OS << " Number of Edges: " << AllEdges.size()
|
||||
<< " (*: Instrument, C: CriticalEdge, -: Removed)\n";
|
||||
uint32_t Count = 0;
|
||||
for (auto &EI : AllEdges)
|
||||
OS << " Edge " << Count++ << ": " << getBBInfo(EI->SrcBB).Index << "-->"
|
||||
<< getBBInfo(EI->DestBB).Index << EI->infoString() << "\n";
|
||||
}
|
||||
|
||||
// Add an edge to AllEdges with weight W.
|
||||
Edge &addEdge(const BasicBlock *Src, const BasicBlock *Dest, uint64_t W) {
|
||||
uint32_t Index = BBInfos.size();
|
||||
auto Iter = BBInfos.end();
|
||||
bool Inserted;
|
||||
std::tie(Iter, Inserted) = BBInfos.insert(std::make_pair(Src, nullptr));
|
||||
if (Inserted) {
|
||||
// Newly inserted, update the real info.
|
||||
Iter->second = std::move(llvm::make_unique<BBInfo>(Index));
|
||||
Index++;
|
||||
}
|
||||
std::tie(Iter, Inserted) = BBInfos.insert(std::make_pair(Dest, nullptr));
|
||||
if (Inserted)
|
||||
// Newly inserted, update the real info.
|
||||
Iter->second = std::move(llvm::make_unique<BBInfo>(Index));
|
||||
AllEdges.emplace_back(new Edge(Src, Dest, W));
|
||||
return *AllEdges.back();
|
||||
}
|
||||
|
||||
BranchProbabilityInfo *BPI;
|
||||
BlockFrequencyInfo *BFI;
|
||||
|
||||
public:
|
||||
CFGMST(Function &Func, BranchProbabilityInfo *BPI_ = nullptr,
|
||||
BlockFrequencyInfo *BFI_ = nullptr)
|
||||
: F(Func), BPI(BPI_), BFI(BFI_) {
|
||||
buildEdges();
|
||||
sortEdgesByWeight();
|
||||
computeMinimumSpanningTree();
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#undef DEBUG_TYPE // "cfgmst"
|
||||
|
||||
#endif // LLVM_LIB_TRANSFORMS_INSTRUMENTATION_CFGMST_H
|
@ -1,22 +0,0 @@
|
||||
add_llvm_library(LLVMInstrumentation
|
||||
AddressSanitizer.cpp
|
||||
BoundsChecking.cpp
|
||||
DataFlowSanitizer.cpp
|
||||
GCOVProfiling.cpp
|
||||
MemorySanitizer.cpp
|
||||
IndirectCallPromotion.cpp
|
||||
Instrumentation.cpp
|
||||
InstrProfiling.cpp
|
||||
PGOInstrumentation.cpp
|
||||
PGOMemOPSizeOpt.cpp
|
||||
SanitizerCoverage.cpp
|
||||
ThreadSanitizer.cpp
|
||||
EfficiencySanitizer.cpp
|
||||
HWAddressSanitizer.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms
|
||||
|
||||
DEPENDS
|
||||
intrinsics_gen
|
||||
)
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,338 +0,0 @@
|
||||
//===- HWAddressSanitizer.cpp - detector of uninitialized reads -------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
/// \file
|
||||
/// This file is a part of HWAddressSanitizer, an address sanity checker
|
||||
/// based on tagged addressing.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/IR/Attributes.h"
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
#include "llvm/IR/Constant.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/IR/DerivedTypes.h"
|
||||
#include "llvm/IR/MDBuilder.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/InlineAsm.h"
|
||||
#include "llvm/IR/InstVisitor.h"
|
||||
#include "llvm/IR/Instruction.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
#include "llvm/IR/Intrinsics.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Type.h"
|
||||
#include "llvm/IR/Value.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Transforms/Instrumentation.h"
|
||||
#include "llvm/Transforms/Utils/ModuleUtils.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "hwasan"
|
||||
|
||||
static const char *const kHwasanModuleCtorName = "hwasan.module_ctor";
|
||||
static const char *const kHwasanInitName = "__hwasan_init";
|
||||
|
||||
// Accesses sizes are powers of two: 1, 2, 4, 8, 16.
|
||||
static const size_t kNumberOfAccessSizes = 5;
|
||||
|
||||
static const size_t kShadowScale = 4;
|
||||
static const unsigned kPointerTagShift = 56;
|
||||
|
||||
static cl::opt<std::string> ClMemoryAccessCallbackPrefix(
|
||||
"hwasan-memory-access-callback-prefix",
|
||||
cl::desc("Prefix for memory access callbacks"), cl::Hidden,
|
||||
cl::init("__hwasan_"));
|
||||
|
||||
static cl::opt<bool>
|
||||
ClInstrumentWithCalls("hwasan-instrument-with-calls",
|
||||
cl::desc("instrument reads and writes with callbacks"),
|
||||
cl::Hidden, cl::init(false));
|
||||
|
||||
static cl::opt<bool> ClInstrumentReads("hwasan-instrument-reads",
|
||||
cl::desc("instrument read instructions"),
|
||||
cl::Hidden, cl::init(true));
|
||||
|
||||
static cl::opt<bool> ClInstrumentWrites(
|
||||
"hwasan-instrument-writes", cl::desc("instrument write instructions"),
|
||||
cl::Hidden, cl::init(true));
|
||||
|
||||
static cl::opt<bool> ClInstrumentAtomics(
|
||||
"hwasan-instrument-atomics",
|
||||
cl::desc("instrument atomic instructions (rmw, cmpxchg)"), cl::Hidden,
|
||||
cl::init(true));
|
||||
|
||||
static cl::opt<bool> ClRecover(
|
||||
"hwasan-recover",
|
||||
cl::desc("Enable recovery mode (continue-after-error)."),
|
||||
cl::Hidden, cl::init(false));
|
||||
|
||||
namespace {
|
||||
|
||||
/// \brief An instrumentation pass implementing detection of addressability bugs
|
||||
/// using tagged pointers.
|
||||
class HWAddressSanitizer : public FunctionPass {
|
||||
public:
|
||||
// Pass identification, replacement for typeid.
|
||||
static char ID;
|
||||
|
||||
HWAddressSanitizer(bool Recover = false)
|
||||
: FunctionPass(ID), Recover(Recover || ClRecover) {}
|
||||
|
||||
StringRef getPassName() const override { return "HWAddressSanitizer"; }
|
||||
|
||||
bool runOnFunction(Function &F) override;
|
||||
bool doInitialization(Module &M) override;
|
||||
|
||||
void initializeCallbacks(Module &M);
|
||||
void instrumentMemAccessInline(Value *PtrLong, bool IsWrite,
|
||||
unsigned AccessSizeIndex,
|
||||
Instruction *InsertBefore);
|
||||
bool instrumentMemAccess(Instruction *I);
|
||||
Value *isInterestingMemoryAccess(Instruction *I, bool *IsWrite,
|
||||
uint64_t *TypeSize, unsigned *Alignment,
|
||||
Value **MaybeMask);
|
||||
|
||||
private:
|
||||
LLVMContext *C;
|
||||
Type *IntptrTy;
|
||||
|
||||
bool Recover;
|
||||
|
||||
Function *HwasanCtorFunction;
|
||||
|
||||
Function *HwasanMemoryAccessCallback[2][kNumberOfAccessSizes];
|
||||
Function *HwasanMemoryAccessCallbackSized[2];
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
char HWAddressSanitizer::ID = 0;
|
||||
|
||||
INITIALIZE_PASS_BEGIN(
|
||||
HWAddressSanitizer, "hwasan",
|
||||
"HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false)
|
||||
INITIALIZE_PASS_END(
|
||||
HWAddressSanitizer, "hwasan",
|
||||
"HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false)
|
||||
|
||||
FunctionPass *llvm::createHWAddressSanitizerPass(bool Recover) {
|
||||
return new HWAddressSanitizer(Recover);
|
||||
}
|
||||
|
||||
/// \brief Module-level initialization.
|
||||
///
|
||||
/// inserts a call to __hwasan_init to the module's constructor list.
|
||||
bool HWAddressSanitizer::doInitialization(Module &M) {
|
||||
DEBUG(dbgs() << "Init " << M.getName() << "\n");
|
||||
auto &DL = M.getDataLayout();
|
||||
|
||||
Triple TargetTriple(M.getTargetTriple());
|
||||
|
||||
C = &(M.getContext());
|
||||
IRBuilder<> IRB(*C);
|
||||
IntptrTy = IRB.getIntPtrTy(DL);
|
||||
|
||||
std::tie(HwasanCtorFunction, std::ignore) =
|
||||
createSanitizerCtorAndInitFunctions(M, kHwasanModuleCtorName,
|
||||
kHwasanInitName,
|
||||
/*InitArgTypes=*/{},
|
||||
/*InitArgs=*/{});
|
||||
appendToGlobalCtors(M, HwasanCtorFunction, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void HWAddressSanitizer::initializeCallbacks(Module &M) {
|
||||
IRBuilder<> IRB(*C);
|
||||
for (size_t AccessIsWrite = 0; AccessIsWrite <= 1; AccessIsWrite++) {
|
||||
const std::string TypeStr = AccessIsWrite ? "store" : "load";
|
||||
const std::string EndingStr = Recover ? "_noabort" : "";
|
||||
|
||||
HwasanMemoryAccessCallbackSized[AccessIsWrite] =
|
||||
checkSanitizerInterfaceFunction(M.getOrInsertFunction(
|
||||
ClMemoryAccessCallbackPrefix + TypeStr + EndingStr,
|
||||
FunctionType::get(IRB.getVoidTy(), {IntptrTy, IntptrTy}, false)));
|
||||
|
||||
for (size_t AccessSizeIndex = 0; AccessSizeIndex < kNumberOfAccessSizes;
|
||||
AccessSizeIndex++) {
|
||||
HwasanMemoryAccessCallback[AccessIsWrite][AccessSizeIndex] =
|
||||
checkSanitizerInterfaceFunction(M.getOrInsertFunction(
|
||||
ClMemoryAccessCallbackPrefix + TypeStr +
|
||||
itostr(1ULL << AccessSizeIndex) + EndingStr,
|
||||
FunctionType::get(IRB.getVoidTy(), {IntptrTy}, false)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Value *HWAddressSanitizer::isInterestingMemoryAccess(Instruction *I,
|
||||
bool *IsWrite,
|
||||
uint64_t *TypeSize,
|
||||
unsigned *Alignment,
|
||||
Value **MaybeMask) {
|
||||
// Skip memory accesses inserted by another instrumentation.
|
||||
if (I->getMetadata("nosanitize")) return nullptr;
|
||||
|
||||
Value *PtrOperand = nullptr;
|
||||
const DataLayout &DL = I->getModule()->getDataLayout();
|
||||
if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
|
||||
if (!ClInstrumentReads) return nullptr;
|
||||
*IsWrite = false;
|
||||
*TypeSize = DL.getTypeStoreSizeInBits(LI->getType());
|
||||
*Alignment = LI->getAlignment();
|
||||
PtrOperand = LI->getPointerOperand();
|
||||
} else if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
|
||||
if (!ClInstrumentWrites) return nullptr;
|
||||
*IsWrite = true;
|
||||
*TypeSize = DL.getTypeStoreSizeInBits(SI->getValueOperand()->getType());
|
||||
*Alignment = SI->getAlignment();
|
||||
PtrOperand = SI->getPointerOperand();
|
||||
} else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) {
|
||||
if (!ClInstrumentAtomics) return nullptr;
|
||||
*IsWrite = true;
|
||||
*TypeSize = DL.getTypeStoreSizeInBits(RMW->getValOperand()->getType());
|
||||
*Alignment = 0;
|
||||
PtrOperand = RMW->getPointerOperand();
|
||||
} else if (AtomicCmpXchgInst *XCHG = dyn_cast<AtomicCmpXchgInst>(I)) {
|
||||
if (!ClInstrumentAtomics) return nullptr;
|
||||
*IsWrite = true;
|
||||
*TypeSize = DL.getTypeStoreSizeInBits(XCHG->getCompareOperand()->getType());
|
||||
*Alignment = 0;
|
||||
PtrOperand = XCHG->getPointerOperand();
|
||||
}
|
||||
|
||||
if (PtrOperand) {
|
||||
// Do not instrument acesses from different address spaces; we cannot deal
|
||||
// with them.
|
||||
Type *PtrTy = cast<PointerType>(PtrOperand->getType()->getScalarType());
|
||||
if (PtrTy->getPointerAddressSpace() != 0)
|
||||
return nullptr;
|
||||
|
||||
// Ignore swifterror addresses.
|
||||
// swifterror memory addresses are mem2reg promoted by instruction
|
||||
// selection. As such they cannot have regular uses like an instrumentation
|
||||
// function and it makes no sense to track them as memory.
|
||||
if (PtrOperand->isSwiftError())
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return PtrOperand;
|
||||
}
|
||||
|
||||
static size_t TypeSizeToSizeIndex(uint32_t TypeSize) {
|
||||
size_t Res = countTrailingZeros(TypeSize / 8);
|
||||
assert(Res < kNumberOfAccessSizes);
|
||||
return Res;
|
||||
}
|
||||
|
||||
void HWAddressSanitizer::instrumentMemAccessInline(Value *PtrLong, bool IsWrite,
|
||||
unsigned AccessSizeIndex,
|
||||
Instruction *InsertBefore) {
|
||||
IRBuilder<> IRB(InsertBefore);
|
||||
Value *PtrTag = IRB.CreateTrunc(IRB.CreateLShr(PtrLong, kPointerTagShift), IRB.getInt8Ty());
|
||||
Value *AddrLong =
|
||||
IRB.CreateAnd(PtrLong, ConstantInt::get(PtrLong->getType(),
|
||||
~(0xFFULL << kPointerTagShift)));
|
||||
Value *ShadowLong = IRB.CreateLShr(AddrLong, kShadowScale);
|
||||
Value *MemTag = IRB.CreateLoad(IRB.CreateIntToPtr(ShadowLong, IRB.getInt8PtrTy()));
|
||||
Value *TagMismatch = IRB.CreateICmpNE(PtrTag, MemTag);
|
||||
|
||||
TerminatorInst *CheckTerm =
|
||||
SplitBlockAndInsertIfThen(TagMismatch, InsertBefore, !Recover,
|
||||
MDBuilder(*C).createBranchWeights(1, 100000));
|
||||
|
||||
IRB.SetInsertPoint(CheckTerm);
|
||||
// The signal handler will find the data address in x0.
|
||||
InlineAsm *Asm = InlineAsm::get(
|
||||
FunctionType::get(IRB.getVoidTy(), {PtrLong->getType()}, false),
|
||||
"hlt #" +
|
||||
itostr(0x100 + Recover * 0x20 + IsWrite * 0x10 + AccessSizeIndex),
|
||||
"{x0}",
|
||||
/*hasSideEffects=*/true);
|
||||
IRB.CreateCall(Asm, PtrLong);
|
||||
}
|
||||
|
||||
bool HWAddressSanitizer::instrumentMemAccess(Instruction *I) {
|
||||
DEBUG(dbgs() << "Instrumenting: " << *I << "\n");
|
||||
bool IsWrite = false;
|
||||
unsigned Alignment = 0;
|
||||
uint64_t TypeSize = 0;
|
||||
Value *MaybeMask = nullptr;
|
||||
Value *Addr =
|
||||
isInterestingMemoryAccess(I, &IsWrite, &TypeSize, &Alignment, &MaybeMask);
|
||||
|
||||
if (!Addr)
|
||||
return false;
|
||||
|
||||
if (MaybeMask)
|
||||
return false; //FIXME
|
||||
|
||||
IRBuilder<> IRB(I);
|
||||
Value *AddrLong = IRB.CreatePointerCast(Addr, IntptrTy);
|
||||
if (isPowerOf2_64(TypeSize) &&
|
||||
(TypeSize / 8 <= (1UL << (kNumberOfAccessSizes - 1))) &&
|
||||
(Alignment >= (1UL << kShadowScale) || Alignment == 0 ||
|
||||
Alignment >= TypeSize / 8)) {
|
||||
size_t AccessSizeIndex = TypeSizeToSizeIndex(TypeSize);
|
||||
if (ClInstrumentWithCalls) {
|
||||
IRB.CreateCall(HwasanMemoryAccessCallback[IsWrite][AccessSizeIndex],
|
||||
AddrLong);
|
||||
} else {
|
||||
instrumentMemAccessInline(AddrLong, IsWrite, AccessSizeIndex, I);
|
||||
}
|
||||
} else {
|
||||
IRB.CreateCall(HwasanMemoryAccessCallbackSized[IsWrite],
|
||||
{AddrLong, ConstantInt::get(IntptrTy, TypeSize / 8)});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HWAddressSanitizer::runOnFunction(Function &F) {
|
||||
if (&F == HwasanCtorFunction)
|
||||
return false;
|
||||
|
||||
if (!F.hasFnAttribute(Attribute::SanitizeHWAddress))
|
||||
return false;
|
||||
|
||||
DEBUG(dbgs() << "Function: " << F.getName() << "\n");
|
||||
|
||||
initializeCallbacks(*F.getParent());
|
||||
|
||||
bool Changed = false;
|
||||
SmallVector<Instruction*, 16> ToInstrument;
|
||||
for (auto &BB : F) {
|
||||
for (auto &Inst : BB) {
|
||||
Value *MaybeMask = nullptr;
|
||||
bool IsWrite;
|
||||
unsigned Alignment;
|
||||
uint64_t TypeSize;
|
||||
Value *Addr = isInterestingMemoryAccess(&Inst, &IsWrite, &TypeSize,
|
||||
&Alignment, &MaybeMask);
|
||||
if (Addr || isa<MemIntrinsic>(Inst))
|
||||
ToInstrument.push_back(&Inst);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto Inst : ToInstrument)
|
||||
Changed |= instrumentMemAccess(Inst);
|
||||
|
||||
return Changed;
|
||||
}
|
@ -1,445 +0,0 @@
|
||||
//===- IndirectCallPromotion.cpp - Optimizations based on value profiling -===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the transformation that promotes indirect calls to
|
||||
// conditional direct calls when the indirect-call value profile metadata is
|
||||
// available.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Analysis/IndirectCallPromotionAnalysis.h"
|
||||
#include "llvm/Analysis/IndirectCallSiteVisitor.h"
|
||||
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
|
||||
#include "llvm/Analysis/ProfileSummaryInfo.h"
|
||||
#include "llvm/IR/Attributes.h"
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/IR/DerivedTypes.h"
|
||||
#include "llvm/IR/DiagnosticInfo.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/InstrTypes.h"
|
||||
#include "llvm/IR/Instruction.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/MDBuilder.h"
|
||||
#include "llvm/IR/PassManager.h"
|
||||
#include "llvm/IR/Type.h"
|
||||
#include "llvm/IR/Value.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/ProfileData/InstrProf.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Transforms/Instrumentation.h"
|
||||
#include "llvm/Transforms/PGOInstrumentation.h"
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
#include "llvm/Transforms/Utils/CallPromotionUtils.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "pgo-icall-prom"
|
||||
|
||||
STATISTIC(NumOfPGOICallPromotion, "Number of indirect call promotions.");
|
||||
STATISTIC(NumOfPGOICallsites, "Number of indirect call candidate sites.");
|
||||
|
||||
// Command line option to disable indirect-call promotion with the default as
|
||||
// false. This is for debug purpose.
|
||||
static cl::opt<bool> DisableICP("disable-icp", cl::init(false), cl::Hidden,
|
||||
cl::desc("Disable indirect call promotion"));
|
||||
|
||||
// Set the cutoff value for the promotion. If the value is other than 0, we
|
||||
// stop the transformation once the total number of promotions equals the cutoff
|
||||
// value.
|
||||
// For debug use only.
|
||||
static cl::opt<unsigned>
|
||||
ICPCutOff("icp-cutoff", cl::init(0), cl::Hidden, cl::ZeroOrMore,
|
||||
cl::desc("Max number of promotions for this compilation"));
|
||||
|
||||
// If ICPCSSkip is non zero, the first ICPCSSkip callsites will be skipped.
|
||||
// For debug use only.
|
||||
static cl::opt<unsigned>
|
||||
ICPCSSkip("icp-csskip", cl::init(0), cl::Hidden, cl::ZeroOrMore,
|
||||
cl::desc("Skip Callsite up to this number for this compilation"));
|
||||
|
||||
// Set if the pass is called in LTO optimization. The difference for LTO mode
|
||||
// is the pass won't prefix the source module name to the internal linkage
|
||||
// symbols.
|
||||
static cl::opt<bool> ICPLTOMode("icp-lto", cl::init(false), cl::Hidden,
|
||||
cl::desc("Run indirect-call promotion in LTO "
|
||||
"mode"));
|
||||
|
||||
// Set if the pass is called in SamplePGO mode. The difference for SamplePGO
|
||||
// mode is it will add prof metadatato the created direct call.
|
||||
static cl::opt<bool>
|
||||
ICPSamplePGOMode("icp-samplepgo", cl::init(false), cl::Hidden,
|
||||
cl::desc("Run indirect-call promotion in SamplePGO mode"));
|
||||
|
||||
// If the option is set to true, only call instructions will be considered for
|
||||
// transformation -- invoke instructions will be ignored.
|
||||
static cl::opt<bool>
|
||||
ICPCallOnly("icp-call-only", cl::init(false), cl::Hidden,
|
||||
cl::desc("Run indirect-call promotion for call instructions "
|
||||
"only"));
|
||||
|
||||
// If the option is set to true, only invoke instructions will be considered for
|
||||
// transformation -- call instructions will be ignored.
|
||||
static cl::opt<bool> ICPInvokeOnly("icp-invoke-only", cl::init(false),
|
||||
cl::Hidden,
|
||||
cl::desc("Run indirect-call promotion for "
|
||||
"invoke instruction only"));
|
||||
|
||||
// Dump the function level IR if the transformation happened in this
|
||||
// function. For debug use only.
|
||||
static cl::opt<bool>
|
||||
ICPDUMPAFTER("icp-dumpafter", cl::init(false), cl::Hidden,
|
||||
cl::desc("Dump IR after transformation happens"));
|
||||
|
||||
namespace {
|
||||
|
||||
class PGOIndirectCallPromotionLegacyPass : public ModulePass {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
PGOIndirectCallPromotionLegacyPass(bool InLTO = false, bool SamplePGO = false)
|
||||
: ModulePass(ID), InLTO(InLTO), SamplePGO(SamplePGO) {
|
||||
initializePGOIndirectCallPromotionLegacyPassPass(
|
||||
*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.addRequired<ProfileSummaryInfoWrapperPass>();
|
||||
}
|
||||
|
||||
StringRef getPassName() const override { return "PGOIndirectCallPromotion"; }
|
||||
|
||||
private:
|
||||
bool runOnModule(Module &M) override;
|
||||
|
||||
// If this pass is called in LTO. We need to special handling the PGOFuncName
|
||||
// for the static variables due to LTO's internalization.
|
||||
bool InLTO;
|
||||
|
||||
// If this pass is called in SamplePGO. We need to add the prof metadata to
|
||||
// the promoted direct call.
|
||||
bool SamplePGO;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
char PGOIndirectCallPromotionLegacyPass::ID = 0;
|
||||
|
||||
INITIALIZE_PASS_BEGIN(PGOIndirectCallPromotionLegacyPass, "pgo-icall-prom",
|
||||
"Use PGO instrumentation profile to promote indirect "
|
||||
"calls to direct calls.",
|
||||
false, false)
|
||||
INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass)
|
||||
INITIALIZE_PASS_END(PGOIndirectCallPromotionLegacyPass, "pgo-icall-prom",
|
||||
"Use PGO instrumentation profile to promote indirect "
|
||||
"calls to direct calls.",
|
||||
false, false)
|
||||
|
||||
ModulePass *llvm::createPGOIndirectCallPromotionLegacyPass(bool InLTO,
|
||||
bool SamplePGO) {
|
||||
return new PGOIndirectCallPromotionLegacyPass(InLTO, SamplePGO);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// The class for main data structure to promote indirect calls to conditional
|
||||
// direct calls.
|
||||
class ICallPromotionFunc {
|
||||
private:
|
||||
Function &F;
|
||||
Module *M;
|
||||
|
||||
// Symtab that maps indirect call profile values to function names and
|
||||
// defines.
|
||||
InstrProfSymtab *Symtab;
|
||||
|
||||
bool SamplePGO;
|
||||
|
||||
OptimizationRemarkEmitter &ORE;
|
||||
|
||||
// A struct that records the direct target and it's call count.
|
||||
struct PromotionCandidate {
|
||||
Function *TargetFunction;
|
||||
uint64_t Count;
|
||||
|
||||
PromotionCandidate(Function *F, uint64_t C) : TargetFunction(F), Count(C) {}
|
||||
};
|
||||
|
||||
// Check if the indirect-call call site should be promoted. Return the number
|
||||
// of promotions. Inst is the candidate indirect call, ValueDataRef
|
||||
// contains the array of value profile data for profiled targets,
|
||||
// TotalCount is the total profiled count of call executions, and
|
||||
// NumCandidates is the number of candidate entries in ValueDataRef.
|
||||
std::vector<PromotionCandidate> getPromotionCandidatesForCallSite(
|
||||
Instruction *Inst, const ArrayRef<InstrProfValueData> &ValueDataRef,
|
||||
uint64_t TotalCount, uint32_t NumCandidates);
|
||||
|
||||
// Promote a list of targets for one indirect-call callsite. Return
|
||||
// the number of promotions.
|
||||
uint32_t tryToPromote(Instruction *Inst,
|
||||
const std::vector<PromotionCandidate> &Candidates,
|
||||
uint64_t &TotalCount);
|
||||
|
||||
public:
|
||||
ICallPromotionFunc(Function &Func, Module *Modu, InstrProfSymtab *Symtab,
|
||||
bool SamplePGO, OptimizationRemarkEmitter &ORE)
|
||||
: F(Func), M(Modu), Symtab(Symtab), SamplePGO(SamplePGO), ORE(ORE) {}
|
||||
ICallPromotionFunc(const ICallPromotionFunc &) = delete;
|
||||
ICallPromotionFunc &operator=(const ICallPromotionFunc &) = delete;
|
||||
|
||||
bool processFunction(ProfileSummaryInfo *PSI);
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
// Indirect-call promotion heuristic. The direct targets are sorted based on
|
||||
// the count. Stop at the first target that is not promoted.
|
||||
std::vector<ICallPromotionFunc::PromotionCandidate>
|
||||
ICallPromotionFunc::getPromotionCandidatesForCallSite(
|
||||
Instruction *Inst, const ArrayRef<InstrProfValueData> &ValueDataRef,
|
||||
uint64_t TotalCount, uint32_t NumCandidates) {
|
||||
std::vector<PromotionCandidate> Ret;
|
||||
|
||||
DEBUG(dbgs() << " \nWork on callsite #" << NumOfPGOICallsites << *Inst
|
||||
<< " Num_targets: " << ValueDataRef.size()
|
||||
<< " Num_candidates: " << NumCandidates << "\n");
|
||||
NumOfPGOICallsites++;
|
||||
if (ICPCSSkip != 0 && NumOfPGOICallsites <= ICPCSSkip) {
|
||||
DEBUG(dbgs() << " Skip: User options.\n");
|
||||
return Ret;
|
||||
}
|
||||
|
||||
for (uint32_t I = 0; I < NumCandidates; I++) {
|
||||
uint64_t Count = ValueDataRef[I].Count;
|
||||
assert(Count <= TotalCount);
|
||||
uint64_t Target = ValueDataRef[I].Value;
|
||||
DEBUG(dbgs() << " Candidate " << I << " Count=" << Count
|
||||
<< " Target_func: " << Target << "\n");
|
||||
|
||||
if (ICPInvokeOnly && dyn_cast<CallInst>(Inst)) {
|
||||
DEBUG(dbgs() << " Not promote: User options.\n");
|
||||
ORE.emit([&]() {
|
||||
return OptimizationRemarkMissed(DEBUG_TYPE, "UserOptions", Inst)
|
||||
<< " Not promote: User options";
|
||||
});
|
||||
break;
|
||||
}
|
||||
if (ICPCallOnly && dyn_cast<InvokeInst>(Inst)) {
|
||||
DEBUG(dbgs() << " Not promote: User option.\n");
|
||||
ORE.emit([&]() {
|
||||
return OptimizationRemarkMissed(DEBUG_TYPE, "UserOptions", Inst)
|
||||
<< " Not promote: User options";
|
||||
});
|
||||
break;
|
||||
}
|
||||
if (ICPCutOff != 0 && NumOfPGOICallPromotion >= ICPCutOff) {
|
||||
DEBUG(dbgs() << " Not promote: Cutoff reached.\n");
|
||||
ORE.emit([&]() {
|
||||
return OptimizationRemarkMissed(DEBUG_TYPE, "CutOffReached", Inst)
|
||||
<< " Not promote: Cutoff reached";
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
Function *TargetFunction = Symtab->getFunction(Target);
|
||||
if (TargetFunction == nullptr) {
|
||||
DEBUG(dbgs() << " Not promote: Cannot find the target\n");
|
||||
ORE.emit([&]() {
|
||||
return OptimizationRemarkMissed(DEBUG_TYPE, "UnableToFindTarget", Inst)
|
||||
<< "Cannot promote indirect call: target not found";
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
const char *Reason = nullptr;
|
||||
if (!isLegalToPromote(CallSite(Inst), TargetFunction, &Reason)) {
|
||||
using namespace ore;
|
||||
|
||||
ORE.emit([&]() {
|
||||
return OptimizationRemarkMissed(DEBUG_TYPE, "UnableToPromote", Inst)
|
||||
<< "Cannot promote indirect call to "
|
||||
<< NV("TargetFunction", TargetFunction) << " with count of "
|
||||
<< NV("Count", Count) << ": " << Reason;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
Ret.push_back(PromotionCandidate(TargetFunction, Count));
|
||||
TotalCount -= Count;
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
Instruction *llvm::pgo::promoteIndirectCall(Instruction *Inst,
|
||||
Function *DirectCallee,
|
||||
uint64_t Count, uint64_t TotalCount,
|
||||
bool AttachProfToDirectCall,
|
||||
OptimizationRemarkEmitter *ORE) {
|
||||
|
||||
uint64_t ElseCount = TotalCount - Count;
|
||||
uint64_t MaxCount = (Count >= ElseCount ? Count : ElseCount);
|
||||
uint64_t Scale = calculateCountScale(MaxCount);
|
||||
MDBuilder MDB(Inst->getContext());
|
||||
MDNode *BranchWeights = MDB.createBranchWeights(
|
||||
scaleBranchCount(Count, Scale), scaleBranchCount(ElseCount, Scale));
|
||||
|
||||
Instruction *NewInst =
|
||||
promoteCallWithIfThenElse(CallSite(Inst), DirectCallee, BranchWeights);
|
||||
|
||||
if (AttachProfToDirectCall) {
|
||||
SmallVector<uint32_t, 1> Weights;
|
||||
Weights.push_back(Count);
|
||||
MDBuilder MDB(NewInst->getContext());
|
||||
NewInst->setMetadata(LLVMContext::MD_prof, MDB.createBranchWeights(Weights));
|
||||
}
|
||||
|
||||
using namespace ore;
|
||||
|
||||
if (ORE)
|
||||
ORE->emit([&]() {
|
||||
return OptimizationRemark(DEBUG_TYPE, "Promoted", Inst)
|
||||
<< "Promote indirect call to " << NV("DirectCallee", DirectCallee)
|
||||
<< " with count " << NV("Count", Count) << " out of "
|
||||
<< NV("TotalCount", TotalCount);
|
||||
});
|
||||
return NewInst;
|
||||
}
|
||||
|
||||
// Promote indirect-call to conditional direct-call for one callsite.
|
||||
uint32_t ICallPromotionFunc::tryToPromote(
|
||||
Instruction *Inst, const std::vector<PromotionCandidate> &Candidates,
|
||||
uint64_t &TotalCount) {
|
||||
uint32_t NumPromoted = 0;
|
||||
|
||||
for (auto &C : Candidates) {
|
||||
uint64_t Count = C.Count;
|
||||
pgo::promoteIndirectCall(Inst, C.TargetFunction, Count, TotalCount,
|
||||
SamplePGO, &ORE);
|
||||
assert(TotalCount >= Count);
|
||||
TotalCount -= Count;
|
||||
NumOfPGOICallPromotion++;
|
||||
NumPromoted++;
|
||||
}
|
||||
return NumPromoted;
|
||||
}
|
||||
|
||||
// Traverse all the indirect-call callsite and get the value profile
|
||||
// annotation to perform indirect-call promotion.
|
||||
bool ICallPromotionFunc::processFunction(ProfileSummaryInfo *PSI) {
|
||||
bool Changed = false;
|
||||
ICallPromotionAnalysis ICallAnalysis;
|
||||
for (auto &I : findIndirectCallSites(F)) {
|
||||
uint32_t NumVals, NumCandidates;
|
||||
uint64_t TotalCount;
|
||||
auto ICallProfDataRef = ICallAnalysis.getPromotionCandidatesForInstruction(
|
||||
I, NumVals, TotalCount, NumCandidates);
|
||||
if (!NumCandidates ||
|
||||
(PSI && PSI->hasProfileSummary() && !PSI->isHotCount(TotalCount)))
|
||||
continue;
|
||||
auto PromotionCandidates = getPromotionCandidatesForCallSite(
|
||||
I, ICallProfDataRef, TotalCount, NumCandidates);
|
||||
uint32_t NumPromoted = tryToPromote(I, PromotionCandidates, TotalCount);
|
||||
if (NumPromoted == 0)
|
||||
continue;
|
||||
|
||||
Changed = true;
|
||||
// Adjust the MD.prof metadata. First delete the old one.
|
||||
I->setMetadata(LLVMContext::MD_prof, nullptr);
|
||||
// If all promoted, we don't need the MD.prof metadata.
|
||||
if (TotalCount == 0 || NumPromoted == NumVals)
|
||||
continue;
|
||||
// Otherwise we need update with the un-promoted records back.
|
||||
annotateValueSite(*M, *I, ICallProfDataRef.slice(NumPromoted), TotalCount,
|
||||
IPVK_IndirectCallTarget, NumCandidates);
|
||||
}
|
||||
return Changed;
|
||||
}
|
||||
|
||||
// A wrapper function that does the actual work.
|
||||
static bool promoteIndirectCalls(Module &M, ProfileSummaryInfo *PSI,
|
||||
bool InLTO, bool SamplePGO,
|
||||
ModuleAnalysisManager *AM = nullptr) {
|
||||
if (DisableICP)
|
||||
return false;
|
||||
InstrProfSymtab Symtab;
|
||||
if (Error E = Symtab.create(M, InLTO)) {
|
||||
std::string SymtabFailure = toString(std::move(E));
|
||||
DEBUG(dbgs() << "Failed to create symtab: " << SymtabFailure << "\n");
|
||||
(void)SymtabFailure;
|
||||
return false;
|
||||
}
|
||||
bool Changed = false;
|
||||
for (auto &F : M) {
|
||||
if (F.isDeclaration())
|
||||
continue;
|
||||
if (F.hasFnAttribute(Attribute::OptimizeNone))
|
||||
continue;
|
||||
|
||||
std::unique_ptr<OptimizationRemarkEmitter> OwnedORE;
|
||||
OptimizationRemarkEmitter *ORE;
|
||||
if (AM) {
|
||||
auto &FAM =
|
||||
AM->getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
|
||||
ORE = &FAM.getResult<OptimizationRemarkEmitterAnalysis>(F);
|
||||
} else {
|
||||
OwnedORE = llvm::make_unique<OptimizationRemarkEmitter>(&F);
|
||||
ORE = OwnedORE.get();
|
||||
}
|
||||
|
||||
ICallPromotionFunc ICallPromotion(F, &M, &Symtab, SamplePGO, *ORE);
|
||||
bool FuncChanged = ICallPromotion.processFunction(PSI);
|
||||
if (ICPDUMPAFTER && FuncChanged) {
|
||||
DEBUG(dbgs() << "\n== IR Dump After =="; F.print(dbgs()));
|
||||
DEBUG(dbgs() << "\n");
|
||||
}
|
||||
Changed |= FuncChanged;
|
||||
if (ICPCutOff != 0 && NumOfPGOICallPromotion >= ICPCutOff) {
|
||||
DEBUG(dbgs() << " Stop: Cutoff reached.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Changed;
|
||||
}
|
||||
|
||||
bool PGOIndirectCallPromotionLegacyPass::runOnModule(Module &M) {
|
||||
ProfileSummaryInfo *PSI =
|
||||
getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI();
|
||||
|
||||
// Command-line option has the priority for InLTO.
|
||||
return promoteIndirectCalls(M, PSI, InLTO | ICPLTOMode,
|
||||
SamplePGO | ICPSamplePGOMode);
|
||||
}
|
||||
|
||||
PreservedAnalyses PGOIndirectCallPromotion::run(Module &M,
|
||||
ModuleAnalysisManager &AM) {
|
||||
ProfileSummaryInfo *PSI = &AM.getResult<ProfileSummaryAnalysis>(M);
|
||||
|
||||
if (!promoteIndirectCalls(M, PSI, InLTO | ICPLTOMode,
|
||||
SamplePGO | ICPSamplePGOMode, &AM))
|
||||
return PreservedAnalyses::all();
|
||||
|
||||
return PreservedAnalyses::none();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,80 +0,0 @@
|
||||
//===-- Instrumentation.cpp - TransformUtils Infrastructure ---------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the common initialization infrastructure for the
|
||||
// Instrumentation library.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Transforms/Instrumentation.h"
|
||||
#include "llvm-c/Initialization.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
#include "llvm/InitializePasses.h"
|
||||
#include "llvm/PassRegistry.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
/// Moves I before IP. Returns new insert point.
|
||||
static BasicBlock::iterator moveBeforeInsertPoint(BasicBlock::iterator I, BasicBlock::iterator IP) {
|
||||
// If I is IP, move the insert point down.
|
||||
if (I == IP)
|
||||
return ++IP;
|
||||
// Otherwise, move I before IP and return IP.
|
||||
I->moveBefore(&*IP);
|
||||
return IP;
|
||||
}
|
||||
|
||||
/// Instrumentation passes often insert conditional checks into entry blocks.
|
||||
/// Call this function before splitting the entry block to move instructions
|
||||
/// that must remain in the entry block up before the split point. Static
|
||||
/// allocas and llvm.localescape calls, for example, must remain in the entry
|
||||
/// block.
|
||||
BasicBlock::iterator llvm::PrepareToSplitEntryBlock(BasicBlock &BB,
|
||||
BasicBlock::iterator IP) {
|
||||
assert(&BB.getParent()->getEntryBlock() == &BB);
|
||||
for (auto I = IP, E = BB.end(); I != E; ++I) {
|
||||
bool KeepInEntry = false;
|
||||
if (auto *AI = dyn_cast<AllocaInst>(I)) {
|
||||
if (AI->isStaticAlloca())
|
||||
KeepInEntry = true;
|
||||
} else if (auto *II = dyn_cast<IntrinsicInst>(I)) {
|
||||
if (II->getIntrinsicID() == llvm::Intrinsic::localescape)
|
||||
KeepInEntry = true;
|
||||
}
|
||||
if (KeepInEntry)
|
||||
IP = moveBeforeInsertPoint(I, IP);
|
||||
}
|
||||
return IP;
|
||||
}
|
||||
|
||||
/// initializeInstrumentation - Initialize all passes in the TransformUtils
|
||||
/// library.
|
||||
void llvm::initializeInstrumentation(PassRegistry &Registry) {
|
||||
initializeAddressSanitizerPass(Registry);
|
||||
initializeAddressSanitizerModulePass(Registry);
|
||||
initializeBoundsCheckingLegacyPassPass(Registry);
|
||||
initializeGCOVProfilerLegacyPassPass(Registry);
|
||||
initializePGOInstrumentationGenLegacyPassPass(Registry);
|
||||
initializePGOInstrumentationUseLegacyPassPass(Registry);
|
||||
initializePGOIndirectCallPromotionLegacyPassPass(Registry);
|
||||
initializePGOMemOPSizeOptLegacyPassPass(Registry);
|
||||
initializeInstrProfilingLegacyPassPass(Registry);
|
||||
initializeMemorySanitizerPass(Registry);
|
||||
initializeHWAddressSanitizerPass(Registry);
|
||||
initializeThreadSanitizerPass(Registry);
|
||||
initializeSanitizerCoverageModulePass(Registry);
|
||||
initializeDataFlowSanitizerPass(Registry);
|
||||
initializeEfficiencySanitizerPass(Registry);
|
||||
}
|
||||
|
||||
/// LLVMInitializeInstrumentation - C binding for
|
||||
/// initializeInstrumentation.
|
||||
void LLVMInitializeInstrumentation(LLVMPassRegistryRef R) {
|
||||
initializeInstrumentation(*unwrap(R));
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
;===- ./lib/Transforms/Instrumentation/LLVMBuild.txt -----------*- Conf -*--===;
|
||||
;
|
||||
; The LLVM Compiler Infrastructure
|
||||
;
|
||||
; This file is distributed under the University of Illinois Open Source
|
||||
; License. See LICENSE.TXT for details.
|
||||
;
|
||||
;===------------------------------------------------------------------------===;
|
||||
;
|
||||
; This is an LLVMBuild description file for the components in this subdirectory.
|
||||
;
|
||||
; For more information on the LLVMBuild system, please see:
|
||||
;
|
||||
; http://llvm.org/docs/LLVMBuild.html
|
||||
;
|
||||
;===------------------------------------------------------------------------===;
|
||||
|
||||
[component_0]
|
||||
type = Library
|
||||
name = Instrumentation
|
||||
parent = Transforms
|
||||
required_libraries = Analysis Core MC Support TransformUtils ProfileData
|
@ -1,111 +0,0 @@
|
||||
//===- llvm/Analysis/MaximumSpanningTree.h - Interface ----------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This module provides means for calculating a maximum spanning tree for a
|
||||
// given set of weighted edges. The type parameter T is the type of a node.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TRANSFORMS_INSTRUMENTATION_MAXIMUMSPANNINGTREE_H
|
||||
#define LLVM_LIB_TRANSFORMS_INSTRUMENTATION_MAXIMUMSPANNINGTREE_H
|
||||
|
||||
#include "llvm/ADT/EquivalenceClasses.h"
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// MaximumSpanningTree - A MST implementation.
|
||||
/// The type parameter T determines the type of the nodes of the graph.
|
||||
template <typename T>
|
||||
class MaximumSpanningTree {
|
||||
public:
|
||||
typedef std::pair<const T*, const T*> Edge;
|
||||
typedef std::pair<Edge, double> EdgeWeight;
|
||||
typedef std::vector<EdgeWeight> EdgeWeights;
|
||||
protected:
|
||||
typedef std::vector<Edge> MaxSpanTree;
|
||||
|
||||
MaxSpanTree MST;
|
||||
|
||||
private:
|
||||
// A comparing class for comparing weighted edges.
|
||||
struct EdgeWeightCompare {
|
||||
static bool getBlockSize(const T *X) {
|
||||
const BasicBlock *BB = dyn_cast_or_null<BasicBlock>(X);
|
||||
return BB ? BB->size() : 0;
|
||||
}
|
||||
|
||||
bool operator()(EdgeWeight X, EdgeWeight Y) const {
|
||||
if (X.second > Y.second) return true;
|
||||
if (X.second < Y.second) return false;
|
||||
|
||||
// Equal edge weights: break ties by comparing block sizes.
|
||||
size_t XSizeA = getBlockSize(X.first.first);
|
||||
size_t YSizeA = getBlockSize(Y.first.first);
|
||||
if (XSizeA > YSizeA) return true;
|
||||
if (XSizeA < YSizeA) return false;
|
||||
|
||||
size_t XSizeB = getBlockSize(X.first.second);
|
||||
size_t YSizeB = getBlockSize(Y.first.second);
|
||||
if (XSizeB > YSizeB) return true;
|
||||
if (XSizeB < YSizeB) return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
static char ID; // Class identification, replacement for typeinfo
|
||||
|
||||
/// MaximumSpanningTree() - Takes a vector of weighted edges and returns a
|
||||
/// spanning tree.
|
||||
MaximumSpanningTree(EdgeWeights &EdgeVector) {
|
||||
|
||||
std::stable_sort(EdgeVector.begin(), EdgeVector.end(), EdgeWeightCompare());
|
||||
|
||||
// Create spanning tree, Forest contains a special data structure
|
||||
// that makes checking if two nodes are already in a common (sub-)tree
|
||||
// fast and cheap.
|
||||
EquivalenceClasses<const T*> Forest;
|
||||
for (typename EdgeWeights::iterator EWi = EdgeVector.begin(),
|
||||
EWe = EdgeVector.end(); EWi != EWe; ++EWi) {
|
||||
Edge e = (*EWi).first;
|
||||
|
||||
Forest.insert(e.first);
|
||||
Forest.insert(e.second);
|
||||
}
|
||||
|
||||
// Iterate over the sorted edges, biggest first.
|
||||
for (typename EdgeWeights::iterator EWi = EdgeVector.begin(),
|
||||
EWe = EdgeVector.end(); EWi != EWe; ++EWi) {
|
||||
Edge e = (*EWi).first;
|
||||
|
||||
if (Forest.findLeader(e.first) != Forest.findLeader(e.second)) {
|
||||
Forest.unionSets(e.first, e.second);
|
||||
// So we know now that the edge is not already in a subtree, so we push
|
||||
// the edge to the MST.
|
||||
MST.push_back(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typename MaxSpanTree::iterator begin() {
|
||||
return MST.begin();
|
||||
}
|
||||
|
||||
typename MaxSpanTree::iterator end() {
|
||||
return MST.end();
|
||||
}
|
||||
};
|
||||
|
||||
} // End llvm namespace
|
||||
|
||||
#endif // LLVM_LIB_TRANSFORMS_INSTRUMENTATION_MAXIMUMSPANNINGTREE_H
|
@ -1 +0,0 @@
|
||||
b3c39b5b166597bf6941fb470f9c13af29570314
|
File diff suppressed because it is too large
Load Diff
@ -1,431 +0,0 @@
|
||||
//===-- PGOMemOPSizeOpt.cpp - Optimizations based on value profiling ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the transformation that optimizes memory intrinsics
|
||||
// such as memcpy using the size value profile. When memory intrinsic size
|
||||
// value profile metadata is available, a single memory intrinsic is expanded
|
||||
// to a sequence of guarded specialized versions that are called with the
|
||||
// hottest size(s), for later expansion into more optimal inline sequences.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Analysis/BlockFrequencyInfo.h"
|
||||
#include "llvm/Analysis/GlobalsModRef.h"
|
||||
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/IR/DerivedTypes.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/InstVisitor.h"
|
||||
#include "llvm/IR/InstrTypes.h"
|
||||
#include "llvm/IR/Instruction.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/PassManager.h"
|
||||
#include "llvm/IR/Type.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/PassRegistry.h"
|
||||
#include "llvm/PassSupport.h"
|
||||
#include "llvm/ProfileData/InstrProf.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include "llvm/Transforms/Instrumentation.h"
|
||||
#include "llvm/Transforms/PGOInstrumentation.h"
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "pgo-memop-opt"
|
||||
|
||||
STATISTIC(NumOfPGOMemOPOpt, "Number of memop intrinsics optimized.");
|
||||
STATISTIC(NumOfPGOMemOPAnnotate, "Number of memop intrinsics annotated.");
|
||||
|
||||
// The minimum call count to optimize memory intrinsic calls.
|
||||
static cl::opt<unsigned>
|
||||
MemOPCountThreshold("pgo-memop-count-threshold", cl::Hidden, cl::ZeroOrMore,
|
||||
cl::init(1000),
|
||||
cl::desc("The minimum count to optimize memory "
|
||||
"intrinsic calls"));
|
||||
|
||||
// Command line option to disable memory intrinsic optimization. The default is
|
||||
// false. This is for debug purpose.
|
||||
static cl::opt<bool> DisableMemOPOPT("disable-memop-opt", cl::init(false),
|
||||
cl::Hidden, cl::desc("Disable optimize"));
|
||||
|
||||
// The percent threshold to optimize memory intrinsic calls.
|
||||
static cl::opt<unsigned>
|
||||
MemOPPercentThreshold("pgo-memop-percent-threshold", cl::init(40),
|
||||
cl::Hidden, cl::ZeroOrMore,
|
||||
cl::desc("The percentage threshold for the "
|
||||
"memory intrinsic calls optimization"));
|
||||
|
||||
// Maximum number of versions for optimizing memory intrinsic call.
|
||||
static cl::opt<unsigned>
|
||||
MemOPMaxVersion("pgo-memop-max-version", cl::init(3), cl::Hidden,
|
||||
cl::ZeroOrMore,
|
||||
cl::desc("The max version for the optimized memory "
|
||||
" intrinsic calls"));
|
||||
|
||||
// Scale the counts from the annotation using the BB count value.
|
||||
static cl::opt<bool>
|
||||
MemOPScaleCount("pgo-memop-scale-count", cl::init(true), cl::Hidden,
|
||||
cl::desc("Scale the memop size counts using the basic "
|
||||
" block count value"));
|
||||
|
||||
// This option sets the rangge of precise profile memop sizes.
|
||||
extern cl::opt<std::string> MemOPSizeRange;
|
||||
|
||||
// This option sets the value that groups large memop sizes
|
||||
extern cl::opt<unsigned> MemOPSizeLarge;
|
||||
|
||||
namespace {
|
||||
class PGOMemOPSizeOptLegacyPass : public FunctionPass {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
PGOMemOPSizeOptLegacyPass() : FunctionPass(ID) {
|
||||
initializePGOMemOPSizeOptLegacyPassPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
StringRef getPassName() const override { return "PGOMemOPSize"; }
|
||||
|
||||
private:
|
||||
bool runOnFunction(Function &F) override;
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.addRequired<BlockFrequencyInfoWrapperPass>();
|
||||
AU.addRequired<OptimizationRemarkEmitterWrapperPass>();
|
||||
AU.addPreserved<GlobalsAAWrapperPass>();
|
||||
}
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
char PGOMemOPSizeOptLegacyPass::ID = 0;
|
||||
INITIALIZE_PASS_BEGIN(PGOMemOPSizeOptLegacyPass, "pgo-memop-opt",
|
||||
"Optimize memory intrinsic using its size value profile",
|
||||
false, false)
|
||||
INITIALIZE_PASS_DEPENDENCY(BlockFrequencyInfoWrapperPass)
|
||||
INITIALIZE_PASS_END(PGOMemOPSizeOptLegacyPass, "pgo-memop-opt",
|
||||
"Optimize memory intrinsic using its size value profile",
|
||||
false, false)
|
||||
|
||||
FunctionPass *llvm::createPGOMemOPSizeOptLegacyPass() {
|
||||
return new PGOMemOPSizeOptLegacyPass();
|
||||
}
|
||||
|
||||
namespace {
|
||||
class MemOPSizeOpt : public InstVisitor<MemOPSizeOpt> {
|
||||
public:
|
||||
MemOPSizeOpt(Function &Func, BlockFrequencyInfo &BFI,
|
||||
OptimizationRemarkEmitter &ORE)
|
||||
: Func(Func), BFI(BFI), ORE(ORE), Changed(false) {
|
||||
ValueDataArray =
|
||||
llvm::make_unique<InstrProfValueData[]>(MemOPMaxVersion + 2);
|
||||
// Get the MemOPSize range information from option MemOPSizeRange,
|
||||
getMemOPSizeRangeFromOption(MemOPSizeRange, PreciseRangeStart,
|
||||
PreciseRangeLast);
|
||||
}
|
||||
bool isChanged() const { return Changed; }
|
||||
void perform() {
|
||||
WorkList.clear();
|
||||
visit(Func);
|
||||
|
||||
for (auto &MI : WorkList) {
|
||||
++NumOfPGOMemOPAnnotate;
|
||||
if (perform(MI)) {
|
||||
Changed = true;
|
||||
++NumOfPGOMemOPOpt;
|
||||
DEBUG(dbgs() << "MemOP call: " << MI->getCalledFunction()->getName()
|
||||
<< "is Transformed.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void visitMemIntrinsic(MemIntrinsic &MI) {
|
||||
Value *Length = MI.getLength();
|
||||
// Not perform on constant length calls.
|
||||
if (dyn_cast<ConstantInt>(Length))
|
||||
return;
|
||||
WorkList.push_back(&MI);
|
||||
}
|
||||
|
||||
private:
|
||||
Function &Func;
|
||||
BlockFrequencyInfo &BFI;
|
||||
OptimizationRemarkEmitter &ORE;
|
||||
bool Changed;
|
||||
std::vector<MemIntrinsic *> WorkList;
|
||||
// Start of the previse range.
|
||||
int64_t PreciseRangeStart;
|
||||
// Last value of the previse range.
|
||||
int64_t PreciseRangeLast;
|
||||
// The space to read the profile annotation.
|
||||
std::unique_ptr<InstrProfValueData[]> ValueDataArray;
|
||||
bool perform(MemIntrinsic *MI);
|
||||
|
||||
// This kind shows which group the value falls in. For PreciseValue, we have
|
||||
// the profile count for that value. LargeGroup groups the values that are in
|
||||
// range [LargeValue, +inf). NonLargeGroup groups the rest of values.
|
||||
enum MemOPSizeKind { PreciseValue, NonLargeGroup, LargeGroup };
|
||||
|
||||
MemOPSizeKind getMemOPSizeKind(int64_t Value) const {
|
||||
if (Value == MemOPSizeLarge && MemOPSizeLarge != 0)
|
||||
return LargeGroup;
|
||||
if (Value == PreciseRangeLast + 1)
|
||||
return NonLargeGroup;
|
||||
return PreciseValue;
|
||||
}
|
||||
};
|
||||
|
||||
static const char *getMIName(const MemIntrinsic *MI) {
|
||||
switch (MI->getIntrinsicID()) {
|
||||
case Intrinsic::memcpy:
|
||||
return "memcpy";
|
||||
case Intrinsic::memmove:
|
||||
return "memmove";
|
||||
case Intrinsic::memset:
|
||||
return "memset";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static bool isProfitable(uint64_t Count, uint64_t TotalCount) {
|
||||
assert(Count <= TotalCount);
|
||||
if (Count < MemOPCountThreshold)
|
||||
return false;
|
||||
if (Count < TotalCount * MemOPPercentThreshold / 100)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline uint64_t getScaledCount(uint64_t Count, uint64_t Num,
|
||||
uint64_t Denom) {
|
||||
if (!MemOPScaleCount)
|
||||
return Count;
|
||||
bool Overflowed;
|
||||
uint64_t ScaleCount = SaturatingMultiply(Count, Num, &Overflowed);
|
||||
return ScaleCount / Denom;
|
||||
}
|
||||
|
||||
bool MemOPSizeOpt::perform(MemIntrinsic *MI) {
|
||||
assert(MI);
|
||||
if (MI->getIntrinsicID() == Intrinsic::memmove)
|
||||
return false;
|
||||
|
||||
uint32_t NumVals, MaxNumPromotions = MemOPMaxVersion + 2;
|
||||
uint64_t TotalCount;
|
||||
if (!getValueProfDataFromInst(*MI, IPVK_MemOPSize, MaxNumPromotions,
|
||||
ValueDataArray.get(), NumVals, TotalCount))
|
||||
return false;
|
||||
|
||||
uint64_t ActualCount = TotalCount;
|
||||
uint64_t SavedTotalCount = TotalCount;
|
||||
if (MemOPScaleCount) {
|
||||
auto BBEdgeCount = BFI.getBlockProfileCount(MI->getParent());
|
||||
if (!BBEdgeCount)
|
||||
return false;
|
||||
ActualCount = *BBEdgeCount;
|
||||
}
|
||||
|
||||
ArrayRef<InstrProfValueData> VDs(ValueDataArray.get(), NumVals);
|
||||
DEBUG(dbgs() << "Read one memory intrinsic profile with count " << ActualCount
|
||||
<< "\n");
|
||||
DEBUG(
|
||||
for (auto &VD
|
||||
: VDs) { dbgs() << " (" << VD.Value << "," << VD.Count << ")\n"; });
|
||||
|
||||
if (ActualCount < MemOPCountThreshold)
|
||||
return false;
|
||||
// Skip if the total value profiled count is 0, in which case we can't
|
||||
// scale up the counts properly (and there is no profitable transformation).
|
||||
if (TotalCount == 0)
|
||||
return false;
|
||||
|
||||
TotalCount = ActualCount;
|
||||
if (MemOPScaleCount)
|
||||
DEBUG(dbgs() << "Scale counts: numerator = " << ActualCount
|
||||
<< " denominator = " << SavedTotalCount << "\n");
|
||||
|
||||
// Keeping track of the count of the default case:
|
||||
uint64_t RemainCount = TotalCount;
|
||||
uint64_t SavedRemainCount = SavedTotalCount;
|
||||
SmallVector<uint64_t, 16> SizeIds;
|
||||
SmallVector<uint64_t, 16> CaseCounts;
|
||||
uint64_t MaxCount = 0;
|
||||
unsigned Version = 0;
|
||||
// Default case is in the front -- save the slot here.
|
||||
CaseCounts.push_back(0);
|
||||
for (auto &VD : VDs) {
|
||||
int64_t V = VD.Value;
|
||||
uint64_t C = VD.Count;
|
||||
if (MemOPScaleCount)
|
||||
C = getScaledCount(C, ActualCount, SavedTotalCount);
|
||||
|
||||
// Only care precise value here.
|
||||
if (getMemOPSizeKind(V) != PreciseValue)
|
||||
continue;
|
||||
|
||||
// ValueCounts are sorted on the count. Break at the first un-profitable
|
||||
// value.
|
||||
if (!isProfitable(C, RemainCount))
|
||||
break;
|
||||
|
||||
SizeIds.push_back(V);
|
||||
CaseCounts.push_back(C);
|
||||
if (C > MaxCount)
|
||||
MaxCount = C;
|
||||
|
||||
assert(RemainCount >= C);
|
||||
RemainCount -= C;
|
||||
assert(SavedRemainCount >= VD.Count);
|
||||
SavedRemainCount -= VD.Count;
|
||||
|
||||
if (++Version > MemOPMaxVersion && MemOPMaxVersion != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (Version == 0)
|
||||
return false;
|
||||
|
||||
CaseCounts[0] = RemainCount;
|
||||
if (RemainCount > MaxCount)
|
||||
MaxCount = RemainCount;
|
||||
|
||||
uint64_t SumForOpt = TotalCount - RemainCount;
|
||||
|
||||
DEBUG(dbgs() << "Optimize one memory intrinsic call to " << Version
|
||||
<< " Versions (covering " << SumForOpt << " out of "
|
||||
<< TotalCount << ")\n");
|
||||
|
||||
// mem_op(..., size)
|
||||
// ==>
|
||||
// switch (size) {
|
||||
// case s1:
|
||||
// mem_op(..., s1);
|
||||
// goto merge_bb;
|
||||
// case s2:
|
||||
// mem_op(..., s2);
|
||||
// goto merge_bb;
|
||||
// ...
|
||||
// default:
|
||||
// mem_op(..., size);
|
||||
// goto merge_bb;
|
||||
// }
|
||||
// merge_bb:
|
||||
|
||||
BasicBlock *BB = MI->getParent();
|
||||
DEBUG(dbgs() << "\n\n== Basic Block Before ==\n");
|
||||
DEBUG(dbgs() << *BB << "\n");
|
||||
auto OrigBBFreq = BFI.getBlockFreq(BB);
|
||||
|
||||
BasicBlock *DefaultBB = SplitBlock(BB, MI);
|
||||
BasicBlock::iterator It(*MI);
|
||||
++It;
|
||||
assert(It != DefaultBB->end());
|
||||
BasicBlock *MergeBB = SplitBlock(DefaultBB, &(*It));
|
||||
MergeBB->setName("MemOP.Merge");
|
||||
BFI.setBlockFreq(MergeBB, OrigBBFreq.getFrequency());
|
||||
DefaultBB->setName("MemOP.Default");
|
||||
|
||||
auto &Ctx = Func.getContext();
|
||||
IRBuilder<> IRB(BB);
|
||||
BB->getTerminator()->eraseFromParent();
|
||||
Value *SizeVar = MI->getLength();
|
||||
SwitchInst *SI = IRB.CreateSwitch(SizeVar, DefaultBB, SizeIds.size());
|
||||
|
||||
// Clear the value profile data.
|
||||
MI->setMetadata(LLVMContext::MD_prof, nullptr);
|
||||
// If all promoted, we don't need the MD.prof metadata.
|
||||
if (SavedRemainCount > 0 || Version != NumVals)
|
||||
// Otherwise we need update with the un-promoted records back.
|
||||
annotateValueSite(*Func.getParent(), *MI, VDs.slice(Version),
|
||||
SavedRemainCount, IPVK_MemOPSize, NumVals);
|
||||
|
||||
DEBUG(dbgs() << "\n\n== Basic Block After==\n");
|
||||
|
||||
for (uint64_t SizeId : SizeIds) {
|
||||
BasicBlock *CaseBB = BasicBlock::Create(
|
||||
Ctx, Twine("MemOP.Case.") + Twine(SizeId), &Func, DefaultBB);
|
||||
Instruction *NewInst = MI->clone();
|
||||
// Fix the argument.
|
||||
MemIntrinsic * MemI = dyn_cast<MemIntrinsic>(NewInst);
|
||||
IntegerType *SizeType = dyn_cast<IntegerType>(MemI->getLength()->getType());
|
||||
assert(SizeType && "Expected integer type size argument.");
|
||||
ConstantInt *CaseSizeId = ConstantInt::get(SizeType, SizeId);
|
||||
MemI->setLength(CaseSizeId);
|
||||
CaseBB->getInstList().push_back(NewInst);
|
||||
IRBuilder<> IRBCase(CaseBB);
|
||||
IRBCase.CreateBr(MergeBB);
|
||||
SI->addCase(CaseSizeId, CaseBB);
|
||||
DEBUG(dbgs() << *CaseBB << "\n");
|
||||
}
|
||||
setProfMetadata(Func.getParent(), SI, CaseCounts, MaxCount);
|
||||
|
||||
DEBUG(dbgs() << *BB << "\n");
|
||||
DEBUG(dbgs() << *DefaultBB << "\n");
|
||||
DEBUG(dbgs() << *MergeBB << "\n");
|
||||
|
||||
ORE.emit([&]() {
|
||||
using namespace ore;
|
||||
return OptimizationRemark(DEBUG_TYPE, "memopt-opt", MI)
|
||||
<< "optimized " << NV("Intrinsic", StringRef(getMIName(MI)))
|
||||
<< " with count " << NV("Count", SumForOpt) << " out of "
|
||||
<< NV("Total", TotalCount) << " for " << NV("Versions", Version)
|
||||
<< " versions";
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
static bool PGOMemOPSizeOptImpl(Function &F, BlockFrequencyInfo &BFI,
|
||||
OptimizationRemarkEmitter &ORE) {
|
||||
if (DisableMemOPOPT)
|
||||
return false;
|
||||
|
||||
if (F.hasFnAttribute(Attribute::OptimizeForSize))
|
||||
return false;
|
||||
MemOPSizeOpt MemOPSizeOpt(F, BFI, ORE);
|
||||
MemOPSizeOpt.perform();
|
||||
return MemOPSizeOpt.isChanged();
|
||||
}
|
||||
|
||||
bool PGOMemOPSizeOptLegacyPass::runOnFunction(Function &F) {
|
||||
BlockFrequencyInfo &BFI =
|
||||
getAnalysis<BlockFrequencyInfoWrapperPass>().getBFI();
|
||||
auto &ORE = getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE();
|
||||
return PGOMemOPSizeOptImpl(F, BFI, ORE);
|
||||
}
|
||||
|
||||
namespace llvm {
|
||||
char &PGOMemOPSizeOptID = PGOMemOPSizeOptLegacyPass::ID;
|
||||
|
||||
PreservedAnalyses PGOMemOPSizeOpt::run(Function &F,
|
||||
FunctionAnalysisManager &FAM) {
|
||||
auto &BFI = FAM.getResult<BlockFrequencyAnalysis>(F);
|
||||
auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F);
|
||||
bool Changed = PGOMemOPSizeOptImpl(F, BFI, ORE);
|
||||
if (!Changed)
|
||||
return PreservedAnalyses::all();
|
||||
auto PA = PreservedAnalyses();
|
||||
PA.preserve<GlobalsAA>();
|
||||
return PA;
|
||||
}
|
||||
} // namespace llvm
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user