304 changes: 186 additions & 118 deletions llvm/lib/Analysis/StackSafetyAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ static cl::opt<int> StackSafetyMaxIterations("stack-safety-max-iterations",

namespace {

using GVToSSI = StackSafetyGlobalInfo::GVToSSI;

/// Rewrite an SCEV expression for a memory access address to an expression that
/// represents offset from the given alloca.
class AllocaOffsetRewriter : public SCEVRewriteVisitor<AllocaOffsetRewriter> {
Expand Down Expand Up @@ -137,31 +135,19 @@ ConstantRange getStaticAllocaSizeRange(const AllocaInst &AI) {
return R;
}

/// Describes uses of allocas and parameters inside of a single function.
struct FunctionInfo {
SmallVector<UseInfo, 4> Allocas;
SmallVector<UseInfo, 4> Params;
const GlobalValue *GV = nullptr;
// TODO: describe return value as depending on one or more of its arguments.

// StackSafetyDataFlowAnalysis counter stored here for faster access.
int UpdateCount = 0;

FunctionInfo() = default;
FunctionInfo(const Function *F) : GV(F){};
explicit FunctionInfo(const GlobalAlias *A);

bool IsDSOLocal() const { return GV->isDSOLocal(); };

bool IsInterposable() const { return GV->isInterposable(); };

StringRef getName() const { return GV->getName(); }

void print(raw_ostream &O, StringRef Name, const Function *F) const {
// TODO: Consider different printout format after
// StackSafetyDataFlowAnalysis. Calls and parameters are irrelevant then.
O << " @" << Name << (IsDSOLocal() ? "" : " dso_preemptable")
<< (IsInterposable() ? " interposable" : "") << "\n";
O << " @" << Name << ((F && F->isDSOLocal()) ? "" : " dso_preemptable")
<< ((F && F->isInterposable()) ? " interposable" : "") << "\n";

O << " args uses:\n";
size_t Pos = 0;
Expand Down Expand Up @@ -190,27 +176,17 @@ struct FunctionInfo {
}
};

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);
UseInfo &US = Params.back();
US.Calls.emplace_back(Aliasee, ArgNo, ConstantRange(APInt(PointerSize, 0)));
}
}
using GVToSSI = std::map<const GlobalValue *, FunctionInfo>;

} // namespace

struct StackSafetyInfo::InfoTy {
FunctionInfo Info;
};

StackSafetyInfo makeSSI(FunctionInfo Info) {
return StackSafetyInfo(StackSafetyInfo::InfoTy{std::move(Info)});
}
struct StackSafetyGlobalInfo::InfoTy {
GVToSSI Info;
};

namespace {

Expand Down Expand Up @@ -404,7 +380,7 @@ bool StackSafetyLocalAnalysis::analyzeAllUses(Value *Ptr, UseInfo &US) {
}

FunctionInfo StackSafetyLocalAnalysis::run() {
FunctionInfo Info(&F);
FunctionInfo Info;
assert(!F.isDeclaration() &&
"Can't run StackSafety on a function declaration");

Expand Down Expand Up @@ -433,15 +409,13 @@ class StackSafetyDataFlowAnalysis {
using FunctionMap = std::map<const GlobalValue *, FunctionInfo>;

FunctionMap Functions;
const ConstantRange UnknownRange;

// 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, FunctionInfo &FS);
void updateOneNode(const GlobalValue *Callee) {
Expand All @@ -457,40 +431,36 @@ class StackSafetyDataFlowAnalysis {
#endif

public:
StackSafetyDataFlowAnalysis(
Module &M, std::function<const FunctionInfo &(Function &)> FI);
GVToSSI run();
};
StackSafetyDataFlowAnalysis(uint32_t PointerBitWidth, FunctionMap Functions)
: Functions(std::move(Functions)),
UnknownRange(ConstantRange::getFull(PointerBitWidth)) {}

StackSafetyDataFlowAnalysis::StackSafetyDataFlowAnalysis(
Module &M, std::function<const FunctionInfo &(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, FunctionInfo(&A));
}
const FunctionMap &run();

ConstantRange
StackSafetyDataFlowAnalysis::getArgumentAccessRange(const GlobalValue *Callee,
unsigned ParamNo) const {
ConstantRange getArgumentAccessRange(const GlobalValue *Callee,
unsigned ParamNo,
const ConstantRange &Offsets) const;
};

ConstantRange StackSafetyDataFlowAnalysis::getArgumentAccessRange(
const GlobalValue *Callee, unsigned ParamNo,
const ConstantRange &Offsets) const {
auto IT = Functions.find(Callee);
// Unknown callee (outside of LTO domain or an indirect call).
if (IT == Functions.end())
return UnknownRange;
const 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].Range;
auto &Access = FS.Params[ParamNo].Range;
if (Access.isEmptySet())
return Access;
if (Access.isFullSet() || Offsets.isFullSet())
return UnknownRange;
if (Offsets.signedAddMayOverflow(Access) !=
ConstantRange::OverflowResult::NeverOverflows)
return UnknownRange;
return Access.add(Offsets);
}

bool StackSafetyDataFlowAnalysis::updateOneUse(UseInfo &US,
Expand All @@ -500,8 +470,8 @@ bool StackSafetyDataFlowAnalysis::updateOneUse(UseInfo &US,
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);
ConstantRange CalleeRange =
getArgumentAccessRange(CS.Callee, CS.ParamNo, CS.Offset);
if (!US.Range.contains(CalleeRange)) {
Changed = true;
if (UpdateToFullSet)
Expand All @@ -517,8 +487,6 @@ void StackSafetyDataFlowAnalysis::updateOneNode(const GlobalValue *Callee,
FunctionInfo &FS) {
bool UpdateToFullSet = FS.UpdateCount > StackSafetyMaxIterations;
bool Changed = false;
for (auto &AS : FS.Allocas)
Changed |= updateOneUse(AS, UpdateToFullSet);
for (auto &PS : FS.Params)
Changed |= updateOneUse(PS, UpdateToFullSet);

Expand All @@ -542,9 +510,6 @@ void StackSafetyDataFlowAnalysis::runDataFlow() {
for (auto &F : Functions) {
Callees.clear();
FunctionInfo &FS = F.second;
for (auto &AS : FS.Allocas)
for (auto &CS : AS.Calls)
Callees.push_back(CS.Callee);
for (auto &PS : FS.Params)
for (auto &CS : PS.Calls)
Callees.push_back(CS.Callee);
Expand Down Expand Up @@ -573,14 +538,11 @@ void StackSafetyDataFlowAnalysis::verifyFixedPoint() {
}
#endif

GVToSSI StackSafetyDataFlowAnalysis::run() {
const StackSafetyDataFlowAnalysis::FunctionMap &
StackSafetyDataFlowAnalysis::run() {
runDataFlow();
LLVM_DEBUG(verifyFixedPoint());

GVToSSI SSI;
for (auto &F : Functions)
SSI.emplace(F.first, makeSSI(F.second));
return SSI;
return Functions;
}

bool setStackSafetyMetadata(Module &M, const GVToSSI &SSGI) {
Expand All @@ -591,7 +553,7 @@ bool setStackSafetyMetadata(Module &M, const GVToSSI &SSGI) {
auto Iter = SSGI.find(&F);
if (Iter == SSGI.end())
continue;
const FunctionInfo &Summary = Iter->second.getInfo().Info;
const FunctionInfo &Summary = Iter->second;
size_t Pos = 0;
for (auto &I : instructions(F)) {
if (auto AI = dyn_cast<AllocaInst>(&I)) {
Expand All @@ -608,38 +570,152 @@ bool setStackSafetyMetadata(Module &M, const GVToSSI &SSGI) {
return Changed;
}

const Function *FindCalleeInModule(const GlobalValue *GV) {
while (GV) {
if (GV->isInterposable() || !GV->isDSOLocal())
return nullptr;
if (const Function *F = dyn_cast<Function>(GV))
return F;
const GlobalAlias *A = dyn_cast<GlobalAlias>(GV);
if (!A)
return nullptr;
GV = A->getBaseObject();
if (GV == A)
return nullptr;
}
return nullptr;
}

void ResolveAllCalls(UseInfo &Use) {
ConstantRange FullSet(Use.Range.getBitWidth(), true);
for (auto &C : Use.Calls) {
const Function *F = FindCalleeInModule(C.Callee);
if (F) {
C.Callee = F;
continue;
}

return Use.updateRange(FullSet);
}
}

void ResolveAllCalls(SmallVectorImpl<UseInfo> &Values) {
for (auto &V : Values)
ResolveAllCalls(V);
}

GVToSSI createGlobalStackSafetyInfo(
std::map<const GlobalValue *, FunctionInfo> Functions) {
GVToSSI SSI;
if (Functions.empty())
return SSI;

// FIXME: Simplify printing and remove copying here.
auto Copy = Functions;

for (auto &FI : Copy)
ResolveAllCalls(FI.second.Params);

uint32_t PointerSize = Copy.begin()
->first->getParent()
->getDataLayout()
.getMaxPointerSizeInBits();
StackSafetyDataFlowAnalysis SSDFA(PointerSize, std::move(Copy));

for (auto &F : SSDFA.run()) {
auto FI = F.second;
size_t Pos = 0;
auto &SrcF = Functions[F.first];
for (auto &A : FI.Allocas) {
ResolveAllCalls(A);
for (auto &C : A.Calls) {
A.updateRange(
SSDFA.getArgumentAccessRange(C.Callee, C.ParamNo, C.Offset));
}
// FIXME: This is needed only to preserve calls in print() results.
A.Calls = SrcF.Allocas[Pos].Calls;
++Pos;
}
Pos = 0;
for (auto &P : FI.Params) {
P.Calls = SrcF.Params[Pos].Calls;
++Pos;
}
SSI[F.first] = std::move(FI);
}

return SSI;
}

} // end anonymous namespace

StackSafetyInfo::StackSafetyInfo() = default;

StackSafetyInfo::StackSafetyInfo(Function *F,
std::function<ScalarEvolution &()> GetSE)
: F(F), GetSE(GetSE) {}

StackSafetyInfo::StackSafetyInfo(StackSafetyInfo &&) = default;
StackSafetyInfo &StackSafetyInfo::operator=(StackSafetyInfo &&) = default;

StackSafetyInfo::StackSafetyInfo(InfoTy Info)
: Info(new InfoTy(std::move(Info))) {}
StackSafetyInfo &StackSafetyInfo::operator=(StackSafetyInfo &&) = default;

StackSafetyInfo::~StackSafetyInfo() = default;

void StackSafetyInfo::print(raw_ostream &O, const GlobalValue &F) const {
Info->Info.print(O, F.getName(), dyn_cast<Function>(&F));
const StackSafetyInfo::InfoTy &StackSafetyInfo::getInfo() const {
if (!Info) {
StackSafetyLocalAnalysis SSLA(*F, GetSE());
Info.reset(new InfoTy{SSLA.run()});
}
return *Info;
}

void StackSafetyInfo::print(raw_ostream &O) const {
getInfo().Info.print(O, F->getName(), dyn_cast<Function>(F));
}

const StackSafetyGlobalInfo::InfoTy &StackSafetyGlobalInfo::getInfo() const {
if (!Info) {
std::map<const GlobalValue *, FunctionInfo> Functions;
for (auto &F : M->functions()) {
if (!F.isDeclaration()) {
auto FI = GetSSI(F).getInfo().Info;
Functions.emplace(&F, std::move(FI));
}
}
Info.reset(new InfoTy{createGlobalStackSafetyInfo(std::move(Functions))});
}
return *Info;
}

StackSafetyGlobalInfo::StackSafetyGlobalInfo() = default;

StackSafetyGlobalInfo::StackSafetyGlobalInfo(
Module *M, std::function<const StackSafetyInfo &(Function &F)> GetSSI)
: M(M), GetSSI(GetSSI) {}

StackSafetyGlobalInfo::StackSafetyGlobalInfo(StackSafetyGlobalInfo &&) =
default;

StackSafetyGlobalInfo &
StackSafetyGlobalInfo::operator=(StackSafetyGlobalInfo &&) = default;

StackSafetyGlobalInfo::~StackSafetyGlobalInfo() = default;

bool StackSafetyGlobalInfo::setMetadata(Module &M) const {
return setStackSafetyMetadata(M, SSGI);
return setStackSafetyMetadata(M, getInfo().Info);
}

void StackSafetyGlobalInfo::print(raw_ostream &O) const {
if (SSGI.empty())
auto &SSI = getInfo().Info;
if (SSI.empty())
return;
const Module &M = *SSGI.begin()->first->getParent();
const Module &M = *SSI.begin()->first->getParent();
for (auto &F : M.functions()) {
if (!F.isDeclaration()) {
SSGI.find(&F)->second.print(O, F);
SSI.find(&F)->second.print(O, F.getName(), &F);
O << "\n";
}
}
for (auto &A : M.aliases()) {
SSGI.find(&A)->second.print(O, A);
O << "\n";
}
}

LLVM_DUMP_METHOD void StackSafetyGlobalInfo::dump() const { print(dbgs()); }
Expand All @@ -648,14 +724,15 @@ AnalysisKey StackSafetyAnalysis::Key;

StackSafetyInfo StackSafetyAnalysis::run(Function &F,
FunctionAnalysisManager &AM) {
StackSafetyLocalAnalysis SSLA(F, AM.getResult<ScalarEvolutionAnalysis>(F));
return makeSSI(SSLA.run());
return StackSafetyInfo(&F, [&AM, &F]() -> ScalarEvolution & {
return AM.getResult<ScalarEvolutionAnalysis>(F);
});
}

PreservedAnalyses StackSafetyPrinterPass::run(Function &F,
FunctionAnalysisManager &AM) {
OS << "'Stack Safety Local Analysis' for function '" << F.getName() << "'\n";
AM.getResult<StackSafetyAnalysis>(F).print(OS, F);
AM.getResult<StackSafetyAnalysis>(F).print(OS);
return PreservedAnalyses::all();
}

Expand All @@ -666,19 +743,17 @@ StackSafetyInfoWrapperPass::StackSafetyInfoWrapperPass() : FunctionPass(ID) {
}

void StackSafetyInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<ScalarEvolutionWrapperPass>();
AU.addRequiredTransitive<ScalarEvolutionWrapperPass>();
AU.setPreservesAll();
}

void StackSafetyInfoWrapperPass::print(raw_ostream &O, const Module *M) const {
SSI->print(O, *F);
SSI.print(O);
}

bool StackSafetyInfoWrapperPass::runOnFunction(Function &F) {
StackSafetyLocalAnalysis SSLA(
F, getAnalysis<ScalarEvolutionWrapperPass>().getSE());
SSI = makeSSI(SSLA.run());
this->F = &F;
auto *SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE();
SSI = {&F, [SE]() -> ScalarEvolution & { return *SE; }};
return false;
}

Expand All @@ -688,12 +763,9 @@ StackSafetyGlobalInfo
StackSafetyGlobalAnalysis::run(Module &M, ModuleAnalysisManager &AM) {
FunctionAnalysisManager &FAM =
AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();

StackSafetyDataFlowAnalysis SSDFA(
M, [&FAM](Function &F) -> const FunctionInfo & {
return FAM.getResult<StackSafetyAnalysis>(F).getInfo().Info;
});
return SSDFA.run();
return {&M, [&FAM](Function &F) -> const StackSafetyInfo & {
return FAM.getResult<StackSafetyAnalysis>(F);
}};
}

PreservedAnalyses StackSafetyGlobalPrinterPass::run(Module &M,
Expand All @@ -712,13 +784,14 @@ StackSafetyGlobalAnnotatorPass::run(Module &M, ModuleAnalysisManager &AM) {

char StackSafetyGlobalInfoWrapperPass::ID = 0;

StackSafetyGlobalInfoWrapperPass::StackSafetyGlobalInfoWrapperPass(
bool SetMetadata)
: ModulePass(ID), SetMetadata(SetMetadata) {
StackSafetyGlobalInfoWrapperPass::StackSafetyGlobalInfoWrapperPass()
: ModulePass(ID) {
initializeStackSafetyGlobalInfoWrapperPassPass(
*PassRegistry::getPassRegistry());
}

StackSafetyGlobalInfoWrapperPass::~StackSafetyGlobalInfoWrapperPass() = default;

void StackSafetyGlobalInfoWrapperPass::print(raw_ostream &O,
const Module *M) const {
SSGI.print(O);
Expand All @@ -730,19 +803,14 @@ void StackSafetyGlobalInfoWrapperPass::getAnalysisUsage(
}

bool StackSafetyGlobalInfoWrapperPass::runOnModule(Module &M) {
StackSafetyDataFlowAnalysis SSDFA(
M, [this](Function &F) -> const FunctionInfo & {
return getAnalysis<StackSafetyInfoWrapperPass>(F)
.getResult()
.getInfo()
.Info;
});
SSGI = SSDFA.run();
return SetMetadata ? SSGI.setMetadata(M) : false;
}

ModulePass *llvm::createStackSafetyGlobalInfoWrapperPass(bool SetMetadata) {
return new StackSafetyGlobalInfoWrapperPass(SetMetadata);
SSGI = {&M, [this](Function &F) -> const StackSafetyInfo & {
return getAnalysis<StackSafetyInfoWrapperPass>(F).getResult();
}};
return SSGI.setMetadata(M);
}

ModulePass *llvm::createStackSafetyGlobalInfoWrapperPass() {
return new StackSafetyGlobalInfoWrapperPass();
}

static const char LocalPassArg[] = "stack-safety-local";
Expand Down
36 changes: 0 additions & 36 deletions llvm/test/Analysis/StackSafetyAnalysis/ipa-alias.ll
Original file line number Diff line number Diff line change
Expand Up @@ -95,39 +95,3 @@ entry:
; CHECK-NEXT: p[]: [0,1){{$}}
; CHECK-NEXT: allocas uses:
; CHECK-NOT: ]:

; GLOBAL-LABEL: @InterposableAliasWrite1 interposable{{$}}
; GLOBAL-NEXT: args uses:
; GLOBAL-NEXT: <N/A>[]: [0,1), @Write1(arg0, [0,1)){{$}}
; GLOBAL-NEXT: allocas uses:
; GLOBAL-NOT: ]:

; GLOBAL-LABEL: @PreemptableAliasWrite1 dso_preemptable{{$}}
; GLOBAL-NEXT: args uses:
; GLOBAL-NEXT: <N/A>[]: [0,1), @Write1(arg0, [0,1)){{$}}
; GLOBAL-NEXT: allocas uses:
; GLOBAL-NOT: ]:

; GLOBAL-LABEL: @AliasToPreemptableAliasWrite1{{$}}
; GLOBAL-NEXT: args uses:
; GLOBAL-NEXT: <N/A>[]: [0,1), @Write1(arg0, [0,1)){{$}}
; GLOBAL-NEXT: allocas uses:
; GLOBAL-NOT: ]:

; GLOBAL-LABEL: @AliasWrite1{{$}}
; GLOBAL-NEXT: args uses:
; GLOBAL-NEXT: <N/A>[]: [0,1), @Write1(arg0, [0,1)){{$}}
; GLOBAL-NEXT: allocas uses:
; GLOBAL-NOT: ]:

; GLOBAL-LABEL: @BitcastAliasWrite1{{$}}
; GLOBAL-NEXT: args uses:
; GLOBAL-NEXT: <N/A>[]: [0,1), @Write1(arg0, [0,1)){{$}}
; GLOBAL-NEXT: allocas uses:
; GLOBAL-NOT: ]:

; GLOBAL-LABEL: @AliasToBitcastAliasWrite1{{$}}
; GLOBAL-NEXT: args uses:
; GLOBAL-NEXT: <N/A>[]: [0,1), @Write1(arg0, [0,1)){{$}}
; GLOBAL-NEXT: allocas uses:
; GLOBAL-NOT: ]: