llvm-project/llvm/lib/Analysis/StackSafetyAnalysis.cpp
Reid Kleckner 05da2fe521 Sink all InitializePasses.h includes
This file lists every pass in LLVM, and is included by Pass.h, which is
very popular. Every time we add, remove, or rename a pass in LLVM, it
caused lots of recompilation.

I found this fact by looking at this table, which is sorted by the
number of times a file was changed over the last 100,000 git commits
multiplied by the number of object files that depend on it in the
current checkout:
  recompiles    touches affected_files  header
  342380        95      3604    llvm/include/llvm/ADT/STLExtras.h
  314730        234     1345    llvm/include/llvm/InitializePasses.h
  307036        118     2602    llvm/include/llvm/ADT/APInt.h
  213049        59      3611    llvm/include/llvm/Support/MathExtras.h
  170422        47      3626    llvm/include/llvm/Support/Compiler.h
  162225        45      3605    llvm/include/llvm/ADT/Optional.h
  158319        63      2513    llvm/include/llvm/ADT/Triple.h
  140322        39      3598    llvm/include/llvm/ADT/StringRef.h
  137647        59      2333    llvm/include/llvm/Support/Error.h
  131619        73      1803    llvm/include/llvm/Support/FileSystem.h

Before this change, touching InitializePasses.h would cause 1345 files
to recompile. After this change, touching it only causes 550 compiles in
an incremental rebuild.

Reviewers: bkramer, asbirlea, bollu, jdoerfert

Differential Revision: https://reviews.llvm.org/D70211
2019-11-13 16:34:37 -08:00

678 lines
22 KiB
C++

//===- StackSafetyAnalysis.cpp - Stack memory safety analysis -------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/StackSafetyAnalysis.h"
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
#define DEBUG_TYPE "stack-safety"
static cl::opt<int> StackSafetyMaxIterations("stack-safety-max-iterations",
cl::init(20), cl::Hidden);
namespace {
/// Rewrite an SCEV expression for a memory access address to an expression that
/// represents offset from the given alloca.
class AllocaOffsetRewriter : public SCEVRewriteVisitor<AllocaOffsetRewriter> {
const Value *AllocaPtr;
public:
AllocaOffsetRewriter(ScalarEvolution &SE, const Value *AllocaPtr)
: SCEVRewriteVisitor(SE), AllocaPtr(AllocaPtr) {}
const SCEV *visit(const SCEV *Expr) {
// Only re-write the expression if the alloca is used in an addition
// expression (it can be used in other types of expressions if it's cast to
// an int and passed as an argument.)
if (!isa<SCEVAddRecExpr>(Expr) && !isa<SCEVAddExpr>(Expr) &&
!isa<SCEVUnknown>(Expr))
return Expr;
return SCEVRewriteVisitor<AllocaOffsetRewriter>::visit(Expr);
}
const SCEV *visitUnknown(const SCEVUnknown *Expr) {
// FIXME: look through one or several levels of definitions?
// This can be inttoptr(AllocaPtr) and SCEV would not unwrap
// it for us.
if (Expr->getValue() == AllocaPtr)
return SE.getZero(Expr->getType());
return Expr;
}
};
/// Describes use of address in as a function call argument.
struct PassAsArgInfo {
/// Function being called.
const GlobalValue *Callee = nullptr;
/// Index of argument which pass address.
size_t ParamNo = 0;
// Offset range of address from base address (alloca or calling function
// argument).
// Range should never set to empty-set, that is an invalid access range
// that can cause empty-set to be propagated with ConstantRange::add
ConstantRange Offset;
PassAsArgInfo(const GlobalValue *Callee, size_t ParamNo, ConstantRange Offset)
: Callee(Callee), ParamNo(ParamNo), Offset(Offset) {}
StringRef getName() const { return Callee->getName(); }
};
raw_ostream &operator<<(raw_ostream &OS, const PassAsArgInfo &P) {
return OS << "@" << P.getName() << "(arg" << P.ParamNo << ", " << P.Offset
<< ")";
}
/// Describe uses of address (alloca or parameter) inside of the function.
struct UseInfo {
// Access range if the address (alloca or parameters).
// It is allowed to be empty-set when there are no known accesses.
ConstantRange Range;
// List of calls which pass address as an argument.
SmallVector<PassAsArgInfo, 4> Calls;
explicit UseInfo(unsigned PointerSize) : Range{PointerSize, false} {}
void updateRange(ConstantRange R) { Range = Range.unionWith(R); }
};
raw_ostream &operator<<(raw_ostream &OS, const UseInfo &U) {
OS << U.Range;
for (auto &Call : U.Calls)
OS << ", " << Call;
return OS;
}
struct AllocaInfo {
const AllocaInst *AI = nullptr;
uint64_t Size = 0;
UseInfo Use;
AllocaInfo(unsigned PointerSize, const AllocaInst *AI, uint64_t Size)
: AI(AI), Size(Size), Use(PointerSize) {}
StringRef getName() const { return AI->getName(); }
};
raw_ostream &operator<<(raw_ostream &OS, const AllocaInfo &A) {
return OS << A.getName() << "[" << A.Size << "]: " << A.Use;
}
struct ParamInfo {
const Argument *Arg = nullptr;
UseInfo Use;
explicit ParamInfo(unsigned PointerSize, const Argument *Arg)
: Arg(Arg), Use(PointerSize) {}
StringRef getName() const { return Arg ? Arg->getName() : "<N/A>"; }
};
raw_ostream &operator<<(raw_ostream &OS, const ParamInfo &P) {
return OS << P.getName() << "[]: " << P.Use;
}
/// Calculate the allocation size of a given alloca. Returns 0 if the
/// size can not be statically determined.
uint64_t getStaticAllocaAllocationSize(const AllocaInst *AI) {
const DataLayout &DL = AI->getModule()->getDataLayout();
uint64_t Size = DL.getTypeAllocSize(AI->getAllocatedType());
if (AI->isArrayAllocation()) {
auto C = dyn_cast<ConstantInt>(AI->getArraySize());
if (!C)
return 0;
Size *= C->getZExtValue();
}
return Size;
}
} // end anonymous namespace
/// Describes uses of allocas and parameters inside of a single function.
struct StackSafetyInfo::FunctionInfo {
// May be a Function or a GlobalAlias
const GlobalValue *GV = nullptr;
// Informations about allocas uses.
SmallVector<AllocaInfo, 4> Allocas;
// Informations about parameters uses.
SmallVector<ParamInfo, 4> Params;
// TODO: describe return value as depending on one or more of its arguments.
// StackSafetyDataFlowAnalysis counter stored here for faster access.
int UpdateCount = 0;
FunctionInfo(const StackSafetyInfo &SSI) : FunctionInfo(*SSI.Info) {}
explicit FunctionInfo(const Function *F) : GV(F){};
// Creates FunctionInfo that forwards all the parameters to the aliasee.
explicit FunctionInfo(const GlobalAlias *A);
FunctionInfo(FunctionInfo &&) = default;
bool IsDSOLocal() const { return GV->isDSOLocal(); };
bool IsInterposable() const { return GV->isInterposable(); };
StringRef getName() const { return GV->getName(); }
void print(raw_ostream &O) const {
// TODO: Consider different printout format after
// StackSafetyDataFlowAnalysis. Calls and parameters are irrelevant then.
O << " @" << getName() << (IsDSOLocal() ? "" : " dso_preemptable")
<< (IsInterposable() ? " interposable" : "") << "\n";
O << " args uses:\n";
for (auto &P : Params)
O << " " << P << "\n";
O << " allocas uses:\n";
for (auto &AS : Allocas)
O << " " << AS << "\n";
}
private:
FunctionInfo(const FunctionInfo &) = default;
};
StackSafetyInfo::FunctionInfo::FunctionInfo(const GlobalAlias *A) : GV(A) {
unsigned PointerSize = A->getParent()->getDataLayout().getPointerSizeInBits();
const GlobalObject *Aliasee = A->getBaseObject();
const FunctionType *Type = cast<FunctionType>(Aliasee->getValueType());
// 'Forward' all parameters to this alias to the aliasee
for (unsigned ArgNo = 0; ArgNo < Type->getNumParams(); ArgNo++) {
Params.emplace_back(PointerSize, nullptr);
UseInfo &US = Params.back().Use;
US.Calls.emplace_back(Aliasee, ArgNo, ConstantRange(APInt(PointerSize, 0)));
}
}
namespace {
class StackSafetyLocalAnalysis {
const Function &F;
const DataLayout &DL;
ScalarEvolution &SE;
unsigned PointerSize = 0;
const ConstantRange UnknownRange;
ConstantRange offsetFromAlloca(Value *Addr, const Value *AllocaPtr);
ConstantRange getAccessRange(Value *Addr, const Value *AllocaPtr,
uint64_t AccessSize);
ConstantRange getMemIntrinsicAccessRange(const MemIntrinsic *MI, const Use &U,
const Value *AllocaPtr);
bool analyzeAllUses(const Value *Ptr, UseInfo &AS);
ConstantRange getRange(uint64_t Lower, uint64_t Upper) const {
return ConstantRange(APInt(PointerSize, Lower), APInt(PointerSize, Upper));
}
public:
StackSafetyLocalAnalysis(const Function &F, ScalarEvolution &SE)
: F(F), DL(F.getParent()->getDataLayout()), SE(SE),
PointerSize(DL.getPointerSizeInBits()),
UnknownRange(PointerSize, true) {}
// Run the transformation on the associated function.
StackSafetyInfo run();
};
ConstantRange
StackSafetyLocalAnalysis::offsetFromAlloca(Value *Addr,
const Value *AllocaPtr) {
if (!SE.isSCEVable(Addr->getType()))
return UnknownRange;
AllocaOffsetRewriter Rewriter(SE, AllocaPtr);
const SCEV *Expr = Rewriter.visit(SE.getSCEV(Addr));
ConstantRange Offset = SE.getUnsignedRange(Expr).zextOrTrunc(PointerSize);
assert(!Offset.isEmptySet());
return Offset;
}
ConstantRange StackSafetyLocalAnalysis::getAccessRange(Value *Addr,
const Value *AllocaPtr,
uint64_t AccessSize) {
if (!SE.isSCEVable(Addr->getType()))
return UnknownRange;
AllocaOffsetRewriter Rewriter(SE, AllocaPtr);
const SCEV *Expr = Rewriter.visit(SE.getSCEV(Addr));
ConstantRange AccessStartRange =
SE.getUnsignedRange(Expr).zextOrTrunc(PointerSize);
ConstantRange SizeRange = getRange(0, AccessSize);
ConstantRange AccessRange = AccessStartRange.add(SizeRange);
assert(!AccessRange.isEmptySet());
return AccessRange;
}
ConstantRange StackSafetyLocalAnalysis::getMemIntrinsicAccessRange(
const MemIntrinsic *MI, const Use &U, const Value *AllocaPtr) {
if (auto MTI = dyn_cast<MemTransferInst>(MI)) {
if (MTI->getRawSource() != U && MTI->getRawDest() != U)
return getRange(0, 1);
} else {
if (MI->getRawDest() != U)
return getRange(0, 1);
}
const auto *Len = dyn_cast<ConstantInt>(MI->getLength());
// Non-constant size => unsafe. FIXME: try SCEV getRange.
if (!Len)
return UnknownRange;
ConstantRange AccessRange = getAccessRange(U, AllocaPtr, Len->getZExtValue());
return AccessRange;
}
/// The function analyzes all local uses of Ptr (alloca or argument) and
/// calculates local access range and all function calls where it was used.
bool StackSafetyLocalAnalysis::analyzeAllUses(const Value *Ptr, UseInfo &US) {
SmallPtrSet<const Value *, 16> Visited;
SmallVector<const Value *, 8> WorkList;
WorkList.push_back(Ptr);
// A DFS search through all uses of the alloca in bitcasts/PHI/GEPs/etc.
while (!WorkList.empty()) {
const Value *V = WorkList.pop_back_val();
for (const Use &UI : V->uses()) {
auto I = cast<const Instruction>(UI.getUser());
assert(V == UI.get());
switch (I->getOpcode()) {
case Instruction::Load: {
US.updateRange(
getAccessRange(UI, Ptr, DL.getTypeStoreSize(I->getType())));
break;
}
case Instruction::VAArg:
// "va-arg" from a pointer is safe.
break;
case Instruction::Store: {
if (V == I->getOperand(0)) {
// Stored the pointer - conservatively assume it may be unsafe.
US.updateRange(UnknownRange);
return false;
}
US.updateRange(getAccessRange(
UI, Ptr, DL.getTypeStoreSize(I->getOperand(0)->getType())));
break;
}
case Instruction::Ret:
// Information leak.
// FIXME: Process parameters correctly. This is a leak only if we return
// alloca.
US.updateRange(UnknownRange);
return false;
case Instruction::Call:
case Instruction::Invoke: {
ImmutableCallSite CS(I);
if (I->isLifetimeStartOrEnd())
break;
if (const MemIntrinsic *MI = dyn_cast<MemIntrinsic>(I)) {
US.updateRange(getMemIntrinsicAccessRange(MI, UI, Ptr));
break;
}
// FIXME: consult devirt?
// Do not follow aliases, otherwise we could inadvertently follow
// dso_preemptable aliases or aliases with interposable linkage.
const GlobalValue *Callee =
dyn_cast<GlobalValue>(CS.getCalledValue()->stripPointerCasts());
if (!Callee) {
US.updateRange(UnknownRange);
return false;
}
assert(isa<Function>(Callee) || isa<GlobalAlias>(Callee));
ImmutableCallSite::arg_iterator B = CS.arg_begin(), E = CS.arg_end();
for (ImmutableCallSite::arg_iterator A = B; A != E; ++A) {
if (A->get() == V) {
ConstantRange Offset = offsetFromAlloca(UI, Ptr);
US.Calls.emplace_back(Callee, A - B, Offset);
}
}
break;
}
default:
if (Visited.insert(I).second)
WorkList.push_back(cast<const Instruction>(I));
}
}
}
return true;
}
StackSafetyInfo StackSafetyLocalAnalysis::run() {
StackSafetyInfo::FunctionInfo Info(&F);
assert(!F.isDeclaration() &&
"Can't run StackSafety on a function declaration");
LLVM_DEBUG(dbgs() << "[StackSafety] " << F.getName() << "\n");
for (auto &I : instructions(F)) {
if (auto AI = dyn_cast<AllocaInst>(&I)) {
Info.Allocas.emplace_back(PointerSize, AI,
getStaticAllocaAllocationSize(AI));
AllocaInfo &AS = Info.Allocas.back();
analyzeAllUses(AI, AS.Use);
}
}
for (const Argument &A : make_range(F.arg_begin(), F.arg_end())) {
Info.Params.emplace_back(PointerSize, &A);
ParamInfo &PS = Info.Params.back();
analyzeAllUses(&A, PS.Use);
}
LLVM_DEBUG(dbgs() << "[StackSafety] done\n");
LLVM_DEBUG(Info.print(dbgs()));
return StackSafetyInfo(std::move(Info));
}
class StackSafetyDataFlowAnalysis {
using FunctionMap =
std::map<const GlobalValue *, StackSafetyInfo::FunctionInfo>;
FunctionMap Functions;
// Callee-to-Caller multimap.
DenseMap<const GlobalValue *, SmallVector<const GlobalValue *, 4>> Callers;
SetVector<const GlobalValue *> WorkList;
unsigned PointerSize = 0;
const ConstantRange UnknownRange;
ConstantRange getArgumentAccessRange(const GlobalValue *Callee,
unsigned ParamNo) const;
bool updateOneUse(UseInfo &US, bool UpdateToFullSet);
void updateOneNode(const GlobalValue *Callee,
StackSafetyInfo::FunctionInfo &FS);
void updateOneNode(const GlobalValue *Callee) {
updateOneNode(Callee, Functions.find(Callee)->second);
}
void updateAllNodes() {
for (auto &F : Functions)
updateOneNode(F.first, F.second);
}
void runDataFlow();
#ifndef NDEBUG
void verifyFixedPoint();
#endif
public:
StackSafetyDataFlowAnalysis(
Module &M, std::function<const StackSafetyInfo &(Function &)> FI);
StackSafetyGlobalInfo run();
};
StackSafetyDataFlowAnalysis::StackSafetyDataFlowAnalysis(
Module &M, std::function<const StackSafetyInfo &(Function &)> FI)
: PointerSize(M.getDataLayout().getPointerSizeInBits()),
UnknownRange(PointerSize, true) {
// Without ThinLTO, run the local analysis for every function in the TU and
// then run the DFA.
for (auto &F : M.functions())
if (!F.isDeclaration())
Functions.emplace(&F, FI(F));
for (auto &A : M.aliases())
if (isa<Function>(A.getBaseObject()))
Functions.emplace(&A, StackSafetyInfo::FunctionInfo(&A));
}
ConstantRange
StackSafetyDataFlowAnalysis::getArgumentAccessRange(const GlobalValue *Callee,
unsigned ParamNo) const {
auto IT = Functions.find(Callee);
// Unknown callee (outside of LTO domain or an indirect call).
if (IT == Functions.end())
return UnknownRange;
const StackSafetyInfo::FunctionInfo &FS = IT->second;
// The definition of this symbol may not be the definition in this linkage
// unit.
if (!FS.IsDSOLocal() || FS.IsInterposable())
return UnknownRange;
if (ParamNo >= FS.Params.size()) // possibly vararg
return UnknownRange;
return FS.Params[ParamNo].Use.Range;
}
bool StackSafetyDataFlowAnalysis::updateOneUse(UseInfo &US,
bool UpdateToFullSet) {
bool Changed = false;
for (auto &CS : US.Calls) {
assert(!CS.Offset.isEmptySet() &&
"Param range can't be empty-set, invalid offset range");
ConstantRange CalleeRange = getArgumentAccessRange(CS.Callee, CS.ParamNo);
CalleeRange = CalleeRange.add(CS.Offset);
if (!US.Range.contains(CalleeRange)) {
Changed = true;
if (UpdateToFullSet)
US.Range = UnknownRange;
else
US.Range = US.Range.unionWith(CalleeRange);
}
}
return Changed;
}
void StackSafetyDataFlowAnalysis::updateOneNode(
const GlobalValue *Callee, StackSafetyInfo::FunctionInfo &FS) {
bool UpdateToFullSet = FS.UpdateCount > StackSafetyMaxIterations;
bool Changed = false;
for (auto &AS : FS.Allocas)
Changed |= updateOneUse(AS.Use, UpdateToFullSet);
for (auto &PS : FS.Params)
Changed |= updateOneUse(PS.Use, UpdateToFullSet);
if (Changed) {
LLVM_DEBUG(dbgs() << "=== update [" << FS.UpdateCount
<< (UpdateToFullSet ? ", full-set" : "") << "] "
<< FS.getName() << "\n");
// Callers of this function may need updating.
for (auto &CallerID : Callers[Callee])
WorkList.insert(CallerID);
++FS.UpdateCount;
}
}
void StackSafetyDataFlowAnalysis::runDataFlow() {
Callers.clear();
WorkList.clear();
SmallVector<const GlobalValue *, 16> Callees;
for (auto &F : Functions) {
Callees.clear();
StackSafetyInfo::FunctionInfo &FS = F.second;
for (auto &AS : FS.Allocas)
for (auto &CS : AS.Use.Calls)
Callees.push_back(CS.Callee);
for (auto &PS : FS.Params)
for (auto &CS : PS.Use.Calls)
Callees.push_back(CS.Callee);
llvm::sort(Callees);
Callees.erase(std::unique(Callees.begin(), Callees.end()), Callees.end());
for (auto &Callee : Callees)
Callers[Callee].push_back(F.first);
}
updateAllNodes();
while (!WorkList.empty()) {
const GlobalValue *Callee = WorkList.back();
WorkList.pop_back();
updateOneNode(Callee);
}
}
#ifndef NDEBUG
void StackSafetyDataFlowAnalysis::verifyFixedPoint() {
WorkList.clear();
updateAllNodes();
assert(WorkList.empty());
}
#endif
StackSafetyGlobalInfo StackSafetyDataFlowAnalysis::run() {
runDataFlow();
LLVM_DEBUG(verifyFixedPoint());
StackSafetyGlobalInfo SSI;
for (auto &F : Functions)
SSI.emplace(F.first, std::move(F.second));
return SSI;
}
void print(const StackSafetyGlobalInfo &SSI, raw_ostream &O, const Module &M) {
size_t Count = 0;
for (auto &F : M.functions())
if (!F.isDeclaration()) {
SSI.find(&F)->second.print(O);
O << "\n";
++Count;
}
for (auto &A : M.aliases()) {
SSI.find(&A)->second.print(O);
O << "\n";
++Count;
}
assert(Count == SSI.size() && "Unexpected functions in the result");
}
} // end anonymous namespace
StackSafetyInfo::StackSafetyInfo() = default;
StackSafetyInfo::StackSafetyInfo(StackSafetyInfo &&) = default;
StackSafetyInfo &StackSafetyInfo::operator=(StackSafetyInfo &&) = default;
StackSafetyInfo::StackSafetyInfo(FunctionInfo &&Info)
: Info(new FunctionInfo(std::move(Info))) {}
StackSafetyInfo::~StackSafetyInfo() = default;
void StackSafetyInfo::print(raw_ostream &O) const { Info->print(O); }
AnalysisKey StackSafetyAnalysis::Key;
StackSafetyInfo StackSafetyAnalysis::run(Function &F,
FunctionAnalysisManager &AM) {
StackSafetyLocalAnalysis SSLA(F, AM.getResult<ScalarEvolutionAnalysis>(F));
return SSLA.run();
}
PreservedAnalyses StackSafetyPrinterPass::run(Function &F,
FunctionAnalysisManager &AM) {
OS << "'Stack Safety Local Analysis' for function '" << F.getName() << "'\n";
AM.getResult<StackSafetyAnalysis>(F).print(OS);
return PreservedAnalyses::all();
}
char StackSafetyInfoWrapperPass::ID = 0;
StackSafetyInfoWrapperPass::StackSafetyInfoWrapperPass() : FunctionPass(ID) {
initializeStackSafetyInfoWrapperPassPass(*PassRegistry::getPassRegistry());
}
void StackSafetyInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<ScalarEvolutionWrapperPass>();
AU.setPreservesAll();
}
void StackSafetyInfoWrapperPass::print(raw_ostream &O, const Module *M) const {
SSI.print(O);
}
bool StackSafetyInfoWrapperPass::runOnFunction(Function &F) {
StackSafetyLocalAnalysis SSLA(
F, getAnalysis<ScalarEvolutionWrapperPass>().getSE());
SSI = StackSafetyInfo(SSLA.run());
return false;
}
AnalysisKey StackSafetyGlobalAnalysis::Key;
StackSafetyGlobalInfo
StackSafetyGlobalAnalysis::run(Module &M, ModuleAnalysisManager &AM) {
FunctionAnalysisManager &FAM =
AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
StackSafetyDataFlowAnalysis SSDFA(
M, [&FAM](Function &F) -> const StackSafetyInfo & {
return FAM.getResult<StackSafetyAnalysis>(F);
});
return SSDFA.run();
}
PreservedAnalyses StackSafetyGlobalPrinterPass::run(Module &M,
ModuleAnalysisManager &AM) {
OS << "'Stack Safety Analysis' for module '" << M.getName() << "'\n";
print(AM.getResult<StackSafetyGlobalAnalysis>(M), OS, M);
return PreservedAnalyses::all();
}
char StackSafetyGlobalInfoWrapperPass::ID = 0;
StackSafetyGlobalInfoWrapperPass::StackSafetyGlobalInfoWrapperPass()
: ModulePass(ID) {
initializeStackSafetyGlobalInfoWrapperPassPass(
*PassRegistry::getPassRegistry());
}
void StackSafetyGlobalInfoWrapperPass::print(raw_ostream &O,
const Module *M) const {
::print(SSI, O, *M);
}
void StackSafetyGlobalInfoWrapperPass::getAnalysisUsage(
AnalysisUsage &AU) const {
AU.addRequired<StackSafetyInfoWrapperPass>();
}
bool StackSafetyGlobalInfoWrapperPass::runOnModule(Module &M) {
StackSafetyDataFlowAnalysis SSDFA(
M, [this](Function &F) -> const StackSafetyInfo & {
return getAnalysis<StackSafetyInfoWrapperPass>(F).getResult();
});
SSI = SSDFA.run();
return false;
}
static const char LocalPassArg[] = "stack-safety-local";
static const char LocalPassName[] = "Stack Safety Local Analysis";
INITIALIZE_PASS_BEGIN(StackSafetyInfoWrapperPass, LocalPassArg, LocalPassName,
false, true)
INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass)
INITIALIZE_PASS_END(StackSafetyInfoWrapperPass, LocalPassArg, LocalPassName,
false, true)
static const char GlobalPassName[] = "Stack Safety Analysis";
INITIALIZE_PASS_BEGIN(StackSafetyGlobalInfoWrapperPass, DEBUG_TYPE,
GlobalPassName, false, false)
INITIALIZE_PASS_DEPENDENCY(StackSafetyInfoWrapperPass)
INITIALIZE_PASS_END(StackSafetyGlobalInfoWrapperPass, DEBUG_TYPE,
GlobalPassName, false, false)