1 //===- StackSafetyAnalysis.cpp - Stack memory safety analysis -------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 //===----------------------------------------------------------------------===//
11 #include "llvm/Analysis/StackSafetyAnalysis.h"
12 #include "llvm/Analysis/ScalarEvolutionExpressions.h"
13 #include "llvm/IR/CallSite.h"
14 #include "llvm/IR/InstIterator.h"
15 #include "llvm/IR/IntrinsicInst.h"
16 #include "llvm/Support/raw_ostream.h"
20 #define DEBUG_TYPE "stack-safety"
22 static cl::opt
<int> StackSafetyMaxIterations("stack-safety-max-iterations",
23 cl::init(20), cl::Hidden
);
27 /// Rewrite an SCEV expression for a memory access address to an expression that
28 /// represents offset from the given alloca.
29 class AllocaOffsetRewriter
: public SCEVRewriteVisitor
<AllocaOffsetRewriter
> {
30 const Value
*AllocaPtr
;
33 AllocaOffsetRewriter(ScalarEvolution
&SE
, const Value
*AllocaPtr
)
34 : SCEVRewriteVisitor(SE
), AllocaPtr(AllocaPtr
) {}
36 const SCEV
*visit(const SCEV
*Expr
) {
37 // Only re-write the expression if the alloca is used in an addition
38 // expression (it can be used in other types of expressions if it's cast to
39 // an int and passed as an argument.)
40 if (!isa
<SCEVAddRecExpr
>(Expr
) && !isa
<SCEVAddExpr
>(Expr
) &&
41 !isa
<SCEVUnknown
>(Expr
))
43 return SCEVRewriteVisitor
<AllocaOffsetRewriter
>::visit(Expr
);
46 const SCEV
*visitUnknown(const SCEVUnknown
*Expr
) {
47 // FIXME: look through one or several levels of definitions?
48 // This can be inttoptr(AllocaPtr) and SCEV would not unwrap
50 if (Expr
->getValue() == AllocaPtr
)
51 return SE
.getZero(Expr
->getType());
56 /// Describes use of address in as a function call argument.
57 struct PassAsArgInfo
{
58 /// Function being called.
59 const GlobalValue
*Callee
= nullptr;
60 /// Index of argument which pass address.
62 // Offset range of address from base address (alloca or calling function
64 // Range should never set to empty-set, that is an invalid access range
65 // that can cause empty-set to be propagated with ConstantRange::add
67 PassAsArgInfo(const GlobalValue
*Callee
, size_t ParamNo
, ConstantRange Offset
)
68 : Callee(Callee
), ParamNo(ParamNo
), Offset(Offset
) {}
70 StringRef
getName() const { return Callee
->getName(); }
73 raw_ostream
&operator<<(raw_ostream
&OS
, const PassAsArgInfo
&P
) {
74 return OS
<< "@" << P
.getName() << "(arg" << P
.ParamNo
<< ", " << P
.Offset
78 /// Describe uses of address (alloca or parameter) inside of the function.
80 // Access range if the address (alloca or parameters).
81 // It is allowed to be empty-set when there are no known accesses.
84 // List of calls which pass address as an argument.
85 SmallVector
<PassAsArgInfo
, 4> Calls
;
87 explicit UseInfo(unsigned PointerSize
) : Range
{PointerSize
, false} {}
89 void updateRange(ConstantRange R
) { Range
= Range
.unionWith(R
); }
92 raw_ostream
&operator<<(raw_ostream
&OS
, const UseInfo
&U
) {
94 for (auto &Call
: U
.Calls
)
100 const AllocaInst
*AI
= nullptr;
104 AllocaInfo(unsigned PointerSize
, const AllocaInst
*AI
, uint64_t Size
)
105 : AI(AI
), Size(Size
), Use(PointerSize
) {}
107 StringRef
getName() const { return AI
->getName(); }
110 raw_ostream
&operator<<(raw_ostream
&OS
, const AllocaInfo
&A
) {
111 return OS
<< A
.getName() << "[" << A
.Size
<< "]: " << A
.Use
;
115 const Argument
*Arg
= nullptr;
118 explicit ParamInfo(unsigned PointerSize
, const Argument
*Arg
)
119 : Arg(Arg
), Use(PointerSize
) {}
121 StringRef
getName() const { return Arg
? Arg
->getName() : "<N/A>"; }
124 raw_ostream
&operator<<(raw_ostream
&OS
, const ParamInfo
&P
) {
125 return OS
<< P
.getName() << "[]: " << P
.Use
;
128 /// Calculate the allocation size of a given alloca. Returns 0 if the
129 /// size can not be statically determined.
130 uint64_t getStaticAllocaAllocationSize(const AllocaInst
*AI
) {
131 const DataLayout
&DL
= AI
->getModule()->getDataLayout();
132 uint64_t Size
= DL
.getTypeAllocSize(AI
->getAllocatedType());
133 if (AI
->isArrayAllocation()) {
134 auto C
= dyn_cast
<ConstantInt
>(AI
->getArraySize());
137 Size
*= C
->getZExtValue();
142 } // end anonymous namespace
144 /// Describes uses of allocas and parameters inside of a single function.
145 struct StackSafetyInfo::FunctionInfo
{
146 // May be a Function or a GlobalAlias
147 const GlobalValue
*GV
= nullptr;
148 // Informations about allocas uses.
149 SmallVector
<AllocaInfo
, 4> Allocas
;
150 // Informations about parameters uses.
151 SmallVector
<ParamInfo
, 4> Params
;
152 // TODO: describe return value as depending on one or more of its arguments.
154 // StackSafetyDataFlowAnalysis counter stored here for faster access.
157 FunctionInfo(const StackSafetyInfo
&SSI
) : FunctionInfo(*SSI
.Info
) {}
159 explicit FunctionInfo(const Function
*F
) : GV(F
){};
160 // Creates FunctionInfo that forwards all the parameters to the aliasee.
161 explicit FunctionInfo(const GlobalAlias
*A
);
163 FunctionInfo(FunctionInfo
&&) = default;
165 bool IsDSOLocal() const { return GV
->isDSOLocal(); };
167 bool IsInterposable() const { return GV
->isInterposable(); };
169 StringRef
getName() const { return GV
->getName(); }
171 void print(raw_ostream
&O
) const {
172 // TODO: Consider different printout format after
173 // StackSafetyDataFlowAnalysis. Calls and parameters are irrelevant then.
174 O
<< " @" << getName() << (IsDSOLocal() ? "" : " dso_preemptable")
175 << (IsInterposable() ? " interposable" : "") << "\n";
176 O
<< " args uses:\n";
177 for (auto &P
: Params
)
178 O
<< " " << P
<< "\n";
179 O
<< " allocas uses:\n";
180 for (auto &AS
: Allocas
)
181 O
<< " " << AS
<< "\n";
185 FunctionInfo(const FunctionInfo
&) = default;
188 StackSafetyInfo::FunctionInfo::FunctionInfo(const GlobalAlias
*A
) : GV(A
) {
189 unsigned PointerSize
= A
->getParent()->getDataLayout().getPointerSizeInBits();
190 const GlobalObject
*Aliasee
= A
->getBaseObject();
191 const FunctionType
*Type
= cast
<FunctionType
>(Aliasee
->getValueType());
192 // 'Forward' all parameters to this alias to the aliasee
193 for (unsigned ArgNo
= 0; ArgNo
< Type
->getNumParams(); ArgNo
++) {
194 Params
.emplace_back(PointerSize
, nullptr);
195 UseInfo
&US
= Params
.back().Use
;
196 US
.Calls
.emplace_back(Aliasee
, ArgNo
, ConstantRange(APInt(PointerSize
, 0)));
202 class StackSafetyLocalAnalysis
{
204 const DataLayout
&DL
;
206 unsigned PointerSize
= 0;
208 const ConstantRange UnknownRange
;
210 ConstantRange
offsetFromAlloca(Value
*Addr
, const Value
*AllocaPtr
);
211 ConstantRange
getAccessRange(Value
*Addr
, const Value
*AllocaPtr
,
212 uint64_t AccessSize
);
213 ConstantRange
getMemIntrinsicAccessRange(const MemIntrinsic
*MI
, const Use
&U
,
214 const Value
*AllocaPtr
);
216 bool analyzeAllUses(const Value
*Ptr
, UseInfo
&AS
);
218 ConstantRange
getRange(uint64_t Lower
, uint64_t Upper
) const {
219 return ConstantRange(APInt(PointerSize
, Lower
), APInt(PointerSize
, Upper
));
223 StackSafetyLocalAnalysis(const Function
&F
, ScalarEvolution
&SE
)
224 : F(F
), DL(F
.getParent()->getDataLayout()), SE(SE
),
225 PointerSize(DL
.getPointerSizeInBits()),
226 UnknownRange(PointerSize
, true) {}
228 // Run the transformation on the associated function.
229 StackSafetyInfo
run();
233 StackSafetyLocalAnalysis::offsetFromAlloca(Value
*Addr
,
234 const Value
*AllocaPtr
) {
235 if (!SE
.isSCEVable(Addr
->getType()))
238 AllocaOffsetRewriter
Rewriter(SE
, AllocaPtr
);
239 const SCEV
*Expr
= Rewriter
.visit(SE
.getSCEV(Addr
));
240 ConstantRange Offset
= SE
.getUnsignedRange(Expr
).zextOrTrunc(PointerSize
);
241 assert(!Offset
.isEmptySet());
245 ConstantRange
StackSafetyLocalAnalysis::getAccessRange(Value
*Addr
,
246 const Value
*AllocaPtr
,
247 uint64_t AccessSize
) {
248 if (!SE
.isSCEVable(Addr
->getType()))
251 AllocaOffsetRewriter
Rewriter(SE
, AllocaPtr
);
252 const SCEV
*Expr
= Rewriter
.visit(SE
.getSCEV(Addr
));
254 ConstantRange AccessStartRange
=
255 SE
.getUnsignedRange(Expr
).zextOrTrunc(PointerSize
);
256 ConstantRange SizeRange
= getRange(0, AccessSize
);
257 ConstantRange AccessRange
= AccessStartRange
.add(SizeRange
);
258 assert(!AccessRange
.isEmptySet());
262 ConstantRange
StackSafetyLocalAnalysis::getMemIntrinsicAccessRange(
263 const MemIntrinsic
*MI
, const Use
&U
, const Value
*AllocaPtr
) {
264 if (auto MTI
= dyn_cast
<MemTransferInst
>(MI
)) {
265 if (MTI
->getRawSource() != U
&& MTI
->getRawDest() != U
)
266 return getRange(0, 1);
268 if (MI
->getRawDest() != U
)
269 return getRange(0, 1);
271 const auto *Len
= dyn_cast
<ConstantInt
>(MI
->getLength());
272 // Non-constant size => unsafe. FIXME: try SCEV getRange.
275 ConstantRange AccessRange
= getAccessRange(U
, AllocaPtr
, Len
->getZExtValue());
279 /// The function analyzes all local uses of Ptr (alloca or argument) and
280 /// calculates local access range and all function calls where it was used.
281 bool StackSafetyLocalAnalysis::analyzeAllUses(const Value
*Ptr
, UseInfo
&US
) {
282 SmallPtrSet
<const Value
*, 16> Visited
;
283 SmallVector
<const Value
*, 8> WorkList
;
284 WorkList
.push_back(Ptr
);
286 // A DFS search through all uses of the alloca in bitcasts/PHI/GEPs/etc.
287 while (!WorkList
.empty()) {
288 const Value
*V
= WorkList
.pop_back_val();
289 for (const Use
&UI
: V
->uses()) {
290 auto I
= cast
<const Instruction
>(UI
.getUser());
291 assert(V
== UI
.get());
293 switch (I
->getOpcode()) {
294 case Instruction::Load
: {
296 getAccessRange(UI
, Ptr
, DL
.getTypeStoreSize(I
->getType())));
300 case Instruction::VAArg
:
301 // "va-arg" from a pointer is safe.
303 case Instruction::Store
: {
304 if (V
== I
->getOperand(0)) {
305 // Stored the pointer - conservatively assume it may be unsafe.
306 US
.updateRange(UnknownRange
);
309 US
.updateRange(getAccessRange(
310 UI
, Ptr
, DL
.getTypeStoreSize(I
->getOperand(0)->getType())));
314 case Instruction::Ret
:
316 // FIXME: Process parameters correctly. This is a leak only if we return
318 US
.updateRange(UnknownRange
);
321 case Instruction::Call
:
322 case Instruction::Invoke
: {
323 ImmutableCallSite
CS(I
);
325 if (I
->isLifetimeStartOrEnd())
328 if (const MemIntrinsic
*MI
= dyn_cast
<MemIntrinsic
>(I
)) {
329 US
.updateRange(getMemIntrinsicAccessRange(MI
, UI
, Ptr
));
333 // FIXME: consult devirt?
334 // Do not follow aliases, otherwise we could inadvertently follow
335 // dso_preemptable aliases or aliases with interposable linkage.
336 const GlobalValue
*Callee
= dyn_cast
<GlobalValue
>(
337 CS
.getCalledValue()->stripPointerCastsNoFollowAliases());
339 US
.updateRange(UnknownRange
);
343 assert(isa
<Function
>(Callee
) || isa
<GlobalAlias
>(Callee
));
345 ImmutableCallSite::arg_iterator B
= CS
.arg_begin(), E
= CS
.arg_end();
346 for (ImmutableCallSite::arg_iterator A
= B
; A
!= E
; ++A
) {
348 ConstantRange Offset
= offsetFromAlloca(UI
, Ptr
);
349 US
.Calls
.emplace_back(Callee
, A
- B
, Offset
);
357 if (Visited
.insert(I
).second
)
358 WorkList
.push_back(cast
<const Instruction
>(I
));
366 StackSafetyInfo
StackSafetyLocalAnalysis::run() {
367 StackSafetyInfo::FunctionInfo
Info(&F
);
368 assert(!F
.isDeclaration() &&
369 "Can't run StackSafety on a function declaration");
371 LLVM_DEBUG(dbgs() << "[StackSafety] " << F
.getName() << "\n");
373 for (auto &I
: instructions(F
)) {
374 if (auto AI
= dyn_cast
<AllocaInst
>(&I
)) {
375 Info
.Allocas
.emplace_back(PointerSize
, AI
,
376 getStaticAllocaAllocationSize(AI
));
377 AllocaInfo
&AS
= Info
.Allocas
.back();
378 analyzeAllUses(AI
, AS
.Use
);
382 for (const Argument
&A
: make_range(F
.arg_begin(), F
.arg_end())) {
383 Info
.Params
.emplace_back(PointerSize
, &A
);
384 ParamInfo
&PS
= Info
.Params
.back();
385 analyzeAllUses(&A
, PS
.Use
);
388 LLVM_DEBUG(dbgs() << "[StackSafety] done\n");
389 LLVM_DEBUG(Info
.print(dbgs()));
390 return StackSafetyInfo(std::move(Info
));
393 class StackSafetyDataFlowAnalysis
{
395 std::map
<const GlobalValue
*, StackSafetyInfo::FunctionInfo
>;
397 FunctionMap Functions
;
398 // Callee-to-Caller multimap.
399 DenseMap
<const GlobalValue
*, SmallVector
<const GlobalValue
*, 4>> Callers
;
400 SetVector
<const GlobalValue
*> WorkList
;
402 unsigned PointerSize
= 0;
403 const ConstantRange UnknownRange
;
405 ConstantRange
getArgumentAccessRange(const GlobalValue
*Callee
,
406 unsigned ParamNo
) const;
407 bool updateOneUse(UseInfo
&US
, bool UpdateToFullSet
);
408 void updateOneNode(const GlobalValue
*Callee
,
409 StackSafetyInfo::FunctionInfo
&FS
);
410 void updateOneNode(const GlobalValue
*Callee
) {
411 updateOneNode(Callee
, Functions
.find(Callee
)->second
);
413 void updateAllNodes() {
414 for (auto &F
: Functions
)
415 updateOneNode(F
.first
, F
.second
);
419 void verifyFixedPoint();
423 StackSafetyDataFlowAnalysis(
424 Module
&M
, std::function
<const StackSafetyInfo
&(Function
&)> FI
);
425 StackSafetyGlobalInfo
run();
428 StackSafetyDataFlowAnalysis::StackSafetyDataFlowAnalysis(
429 Module
&M
, std::function
<const StackSafetyInfo
&(Function
&)> FI
)
430 : PointerSize(M
.getDataLayout().getPointerSizeInBits()),
431 UnknownRange(PointerSize
, true) {
432 // Without ThinLTO, run the local analysis for every function in the TU and
434 for (auto &F
: M
.functions())
435 if (!F
.isDeclaration())
436 Functions
.emplace(&F
, FI(F
));
437 for (auto &A
: M
.aliases())
438 if (isa
<Function
>(A
.getBaseObject()))
439 Functions
.emplace(&A
, StackSafetyInfo::FunctionInfo(&A
));
443 StackSafetyDataFlowAnalysis::getArgumentAccessRange(const GlobalValue
*Callee
,
444 unsigned ParamNo
) const {
445 auto IT
= Functions
.find(Callee
);
446 // Unknown callee (outside of LTO domain or an indirect call).
447 if (IT
== Functions
.end())
449 const StackSafetyInfo::FunctionInfo
&FS
= IT
->second
;
450 // The definition of this symbol may not be the definition in this linkage
452 if (!FS
.IsDSOLocal() || FS
.IsInterposable())
454 if (ParamNo
>= FS
.Params
.size()) // possibly vararg
456 return FS
.Params
[ParamNo
].Use
.Range
;
459 bool StackSafetyDataFlowAnalysis::updateOneUse(UseInfo
&US
,
460 bool UpdateToFullSet
) {
461 bool Changed
= false;
462 for (auto &CS
: US
.Calls
) {
463 assert(!CS
.Offset
.isEmptySet() &&
464 "Param range can't be empty-set, invalid offset range");
466 ConstantRange CalleeRange
= getArgumentAccessRange(CS
.Callee
, CS
.ParamNo
);
467 CalleeRange
= CalleeRange
.add(CS
.Offset
);
468 if (!US
.Range
.contains(CalleeRange
)) {
471 US
.Range
= UnknownRange
;
473 US
.Range
= US
.Range
.unionWith(CalleeRange
);
479 void StackSafetyDataFlowAnalysis::updateOneNode(
480 const GlobalValue
*Callee
, StackSafetyInfo::FunctionInfo
&FS
) {
481 bool UpdateToFullSet
= FS
.UpdateCount
> StackSafetyMaxIterations
;
482 bool Changed
= false;
483 for (auto &AS
: FS
.Allocas
)
484 Changed
|= updateOneUse(AS
.Use
, UpdateToFullSet
);
485 for (auto &PS
: FS
.Params
)
486 Changed
|= updateOneUse(PS
.Use
, UpdateToFullSet
);
489 LLVM_DEBUG(dbgs() << "=== update [" << FS
.UpdateCount
490 << (UpdateToFullSet
? ", full-set" : "") << "] "
491 << FS
.getName() << "\n");
492 // Callers of this function may need updating.
493 for (auto &CallerID
: Callers
[Callee
])
494 WorkList
.insert(CallerID
);
500 void StackSafetyDataFlowAnalysis::runDataFlow() {
504 SmallVector
<const GlobalValue
*, 16> Callees
;
505 for (auto &F
: Functions
) {
507 StackSafetyInfo::FunctionInfo
&FS
= F
.second
;
508 for (auto &AS
: FS
.Allocas
)
509 for (auto &CS
: AS
.Use
.Calls
)
510 Callees
.push_back(CS
.Callee
);
511 for (auto &PS
: FS
.Params
)
512 for (auto &CS
: PS
.Use
.Calls
)
513 Callees
.push_back(CS
.Callee
);
516 Callees
.erase(std::unique(Callees
.begin(), Callees
.end()), Callees
.end());
518 for (auto &Callee
: Callees
)
519 Callers
[Callee
].push_back(F
.first
);
524 while (!WorkList
.empty()) {
525 const GlobalValue
*Callee
= WorkList
.back();
527 updateOneNode(Callee
);
532 void StackSafetyDataFlowAnalysis::verifyFixedPoint() {
535 assert(WorkList
.empty());
539 StackSafetyGlobalInfo
StackSafetyDataFlowAnalysis::run() {
541 LLVM_DEBUG(verifyFixedPoint());
543 StackSafetyGlobalInfo SSI
;
544 for (auto &F
: Functions
)
545 SSI
.emplace(F
.first
, std::move(F
.second
));
549 void print(const StackSafetyGlobalInfo
&SSI
, raw_ostream
&O
, const Module
&M
) {
551 for (auto &F
: M
.functions())
552 if (!F
.isDeclaration()) {
553 SSI
.find(&F
)->second
.print(O
);
557 for (auto &A
: M
.aliases()) {
558 SSI
.find(&A
)->second
.print(O
);
562 assert(Count
== SSI
.size() && "Unexpected functions in the result");
565 } // end anonymous namespace
567 StackSafetyInfo::StackSafetyInfo() = default;
568 StackSafetyInfo::StackSafetyInfo(StackSafetyInfo
&&) = default;
569 StackSafetyInfo
&StackSafetyInfo::operator=(StackSafetyInfo
&&) = default;
571 StackSafetyInfo::StackSafetyInfo(FunctionInfo
&&Info
)
572 : Info(new FunctionInfo(std::move(Info
))) {}
574 StackSafetyInfo::~StackSafetyInfo() = default;
576 void StackSafetyInfo::print(raw_ostream
&O
) const { Info
->print(O
); }
578 AnalysisKey
StackSafetyAnalysis::Key
;
580 StackSafetyInfo
StackSafetyAnalysis::run(Function
&F
,
581 FunctionAnalysisManager
&AM
) {
582 StackSafetyLocalAnalysis
SSLA(F
, AM
.getResult
<ScalarEvolutionAnalysis
>(F
));
586 PreservedAnalyses
StackSafetyPrinterPass::run(Function
&F
,
587 FunctionAnalysisManager
&AM
) {
588 OS
<< "'Stack Safety Local Analysis' for function '" << F
.getName() << "'\n";
589 AM
.getResult
<StackSafetyAnalysis
>(F
).print(OS
);
590 return PreservedAnalyses::all();
593 char StackSafetyInfoWrapperPass::ID
= 0;
595 StackSafetyInfoWrapperPass::StackSafetyInfoWrapperPass() : FunctionPass(ID
) {
596 initializeStackSafetyInfoWrapperPassPass(*PassRegistry::getPassRegistry());
599 void StackSafetyInfoWrapperPass::getAnalysisUsage(AnalysisUsage
&AU
) const {
600 AU
.addRequired
<ScalarEvolutionWrapperPass
>();
601 AU
.setPreservesAll();
604 void StackSafetyInfoWrapperPass::print(raw_ostream
&O
, const Module
*M
) const {
608 bool StackSafetyInfoWrapperPass::runOnFunction(Function
&F
) {
609 StackSafetyLocalAnalysis
SSLA(
610 F
, getAnalysis
<ScalarEvolutionWrapperPass
>().getSE());
611 SSI
= StackSafetyInfo(SSLA
.run());
615 AnalysisKey
StackSafetyGlobalAnalysis::Key
;
617 StackSafetyGlobalInfo
618 StackSafetyGlobalAnalysis::run(Module
&M
, ModuleAnalysisManager
&AM
) {
619 FunctionAnalysisManager
&FAM
=
620 AM
.getResult
<FunctionAnalysisManagerModuleProxy
>(M
).getManager();
622 StackSafetyDataFlowAnalysis
SSDFA(
623 M
, [&FAM
](Function
&F
) -> const StackSafetyInfo
& {
624 return FAM
.getResult
<StackSafetyAnalysis
>(F
);
629 PreservedAnalyses
StackSafetyGlobalPrinterPass::run(Module
&M
,
630 ModuleAnalysisManager
&AM
) {
631 OS
<< "'Stack Safety Analysis' for module '" << M
.getName() << "'\n";
632 print(AM
.getResult
<StackSafetyGlobalAnalysis
>(M
), OS
, M
);
633 return PreservedAnalyses::all();
636 char StackSafetyGlobalInfoWrapperPass::ID
= 0;
638 StackSafetyGlobalInfoWrapperPass::StackSafetyGlobalInfoWrapperPass()
640 initializeStackSafetyGlobalInfoWrapperPassPass(
641 *PassRegistry::getPassRegistry());
644 void StackSafetyGlobalInfoWrapperPass::print(raw_ostream
&O
,
645 const Module
*M
) const {
649 void StackSafetyGlobalInfoWrapperPass::getAnalysisUsage(
650 AnalysisUsage
&AU
) const {
651 AU
.addRequired
<StackSafetyInfoWrapperPass
>();
654 bool StackSafetyGlobalInfoWrapperPass::runOnModule(Module
&M
) {
655 StackSafetyDataFlowAnalysis
SSDFA(
656 M
, [this](Function
&F
) -> const StackSafetyInfo
& {
657 return getAnalysis
<StackSafetyInfoWrapperPass
>(F
).getResult();
663 static const char LocalPassArg
[] = "stack-safety-local";
664 static const char LocalPassName
[] = "Stack Safety Local Analysis";
665 INITIALIZE_PASS_BEGIN(StackSafetyInfoWrapperPass
, LocalPassArg
, LocalPassName
,
667 INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass
)
668 INITIALIZE_PASS_END(StackSafetyInfoWrapperPass
, LocalPassArg
, LocalPassName
,
671 static const char GlobalPassName
[] = "Stack Safety Analysis";
672 INITIALIZE_PASS_BEGIN(StackSafetyGlobalInfoWrapperPass
, DEBUG_TYPE
,
673 GlobalPassName
, false, false)
674 INITIALIZE_PASS_DEPENDENCY(StackSafetyInfoWrapperPass
)
675 INITIALIZE_PASS_END(StackSafetyGlobalInfoWrapperPass
, DEBUG_TYPE
,
676 GlobalPassName
, false, false)