Please use GitHub pull requests for new patches. Phabricator shutdown timeline
Changeset View
Standalone View
lib/Transforms/IPO/FunctionAttrs.cpp
Show All 21 Lines | |||||
#include "llvm/Analysis/AliasAnalysis.h" | #include "llvm/Analysis/AliasAnalysis.h" | ||||
#include "llvm/Analysis/AssumptionCache.h" | #include "llvm/Analysis/AssumptionCache.h" | ||||
#include "llvm/Analysis/BasicAliasAnalysis.h" | #include "llvm/Analysis/BasicAliasAnalysis.h" | ||||
#include "llvm/Analysis/CGSCCPassManager.h" | #include "llvm/Analysis/CGSCCPassManager.h" | ||||
#include "llvm/Analysis/CallGraph.h" | #include "llvm/Analysis/CallGraph.h" | ||||
#include "llvm/Analysis/CallGraphSCCPass.h" | #include "llvm/Analysis/CallGraphSCCPass.h" | ||||
#include "llvm/Analysis/CaptureTracking.h" | #include "llvm/Analysis/CaptureTracking.h" | ||||
#include "llvm/Analysis/LazyCallGraph.h" | #include "llvm/Analysis/LazyCallGraph.h" | ||||
#include "llvm/Analysis/MemoryBuiltins.h" | |||||
#include "llvm/Analysis/MemoryLocation.h" | #include "llvm/Analysis/MemoryLocation.h" | ||||
#include "llvm/Analysis/TargetLibraryInfo.h" | |||||
#include "llvm/Analysis/ValueTracking.h" | #include "llvm/Analysis/ValueTracking.h" | ||||
#include "llvm/IR/Argument.h" | #include "llvm/IR/Argument.h" | ||||
#include "llvm/IR/Attributes.h" | #include "llvm/IR/Attributes.h" | ||||
#include "llvm/IR/BasicBlock.h" | #include "llvm/IR/BasicBlock.h" | ||||
#include "llvm/IR/CallSite.h" | #include "llvm/IR/CallSite.h" | ||||
#include "llvm/IR/Constant.h" | #include "llvm/IR/Constant.h" | ||||
#include "llvm/IR/Constants.h" | #include "llvm/IR/Constants.h" | ||||
#include "llvm/IR/Function.h" | #include "llvm/IR/Function.h" | ||||
Show All 31 Lines | |||||
STATISTIC(NumNoCapture, "Number of arguments marked nocapture"); | STATISTIC(NumNoCapture, "Number of arguments marked nocapture"); | ||||
STATISTIC(NumReturned, "Number of arguments marked returned"); | STATISTIC(NumReturned, "Number of arguments marked returned"); | ||||
STATISTIC(NumReadNoneArg, "Number of arguments marked readnone"); | STATISTIC(NumReadNoneArg, "Number of arguments marked readnone"); | ||||
STATISTIC(NumReadOnlyArg, "Number of arguments marked readonly"); | STATISTIC(NumReadOnlyArg, "Number of arguments marked readonly"); | ||||
STATISTIC(NumNoAlias, "Number of function returns marked noalias"); | STATISTIC(NumNoAlias, "Number of function returns marked noalias"); | ||||
STATISTIC(NumNonNullReturn, "Number of function returns marked nonnull"); | STATISTIC(NumNonNullReturn, "Number of function returns marked nonnull"); | ||||
STATISTIC(NumNoRecurse, "Number of functions marked as norecurse"); | STATISTIC(NumNoRecurse, "Number of functions marked as norecurse"); | ||||
STATISTIC(NumNoUnwind, "Number of functions marked as nounwind"); | STATISTIC(NumNoUnwind, "Number of functions marked as nounwind"); | ||||
STATISTIC(NumNoFree, "Number of functions marked as nofree"); | |||||
// FIXME: This is disabled by default to avoid exposing security vulnerabilities | // FIXME: This is disabled by default to avoid exposing security vulnerabilities | ||||
// in C/C++ code compiled by clang: | // in C/C++ code compiled by clang: | ||||
// http://lists.llvm.org/pipermail/cfe-dev/2017-January/052066.html | // http://lists.llvm.org/pipermail/cfe-dev/2017-January/052066.html | ||||
static cl::opt<bool> EnableNonnullArgPropagation( | static cl::opt<bool> EnableNonnullArgPropagation( | ||||
"enable-nonnull-arg-prop", cl::Hidden, | "enable-nonnull-arg-prop", cl::Hidden, | ||||
cl::desc("Try to propagate nonnull argument attributes from callsites to " | cl::desc("Try to propagate nonnull argument attributes from callsites to " | ||||
"caller functions.")); | "caller functions.")); | ||||
static cl::opt<bool> DisableNoUnwindInference( | static cl::opt<bool> DisableNoUnwindInference( | ||||
"disable-nounwind-inference", cl::Hidden, | "disable-nounwind-inference", cl::Hidden, | ||||
cl::desc("Stop inferring nounwind attribute during function-attrs pass")); | cl::desc("Stop inferring nounwind attribute during function-attrs pass")); | ||||
static cl::opt<bool> DisableNoFreeInference( | |||||
"disable-nofree-inference", cl::Hidden, | |||||
cl::desc("Stop inferring nofree attribute during function-attrs pass")); | |||||
namespace { | namespace { | ||||
using SCCNodeSet = SmallSetVector<Function *, 8>; | using SCCNodeSet = SmallSetVector<Function *, 8>; | ||||
} // end anonymous namespace | } // end anonymous namespace | ||||
/// Returns the memory access attribute for function F using AAR for AA results, | /// Returns the memory access attribute for function F using AAR for AA results, | ||||
/// where SCCNodes is the current SCC. | /// where SCCNodes is the current SCC. | ||||
▲ Show 20 Lines • Show All 1,123 Lines • ▼ Show 20 Lines | if (Function *Callee = CI->getCalledFunction()) { | ||||
// just have to scan that other function. | // just have to scan that other function. | ||||
if (SCCNodes.count(Callee) > 0) | if (SCCNodes.count(Callee) > 0) | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
/// Helper for NoFree inference predicate InstrBreaksAttribute. | |||||
static bool InstrBreaksNoFree(Instruction &I, const SCCNodeSet &SCCNodes, | |||||
const TargetLibraryInfo &TLI) { | |||||
// A call to free, or similar, or realloc. | |||||
if (isFreeCall(&I, &TLI) || isReallocLikeFn(&I, &TLI)) | |||||
return true; | |||||
jdoerfert: Why is this needed? The below logic should be sufficient because we want to check for "must not… | |||||
if (!isa<CallInst>(I) && !isa<InvokeInst>(I)) | |||||
jdoerfertUnsubmitted Check if(!CS) (below) instead of these isa's which are not exhaustive. jdoerfert: Check `if(!CS)` (below) instead of these `isa's` which are not exhaustive. | |||||
return false; | |||||
CallSite CS(&I); | |||||
Function *Callee = CS.getCalledFunction(); | |||||
if (!Callee) | |||||
return true; | |||||
if (Callee->doesNotFreeMemory()) | |||||
return false; | |||||
jdoerfertUnsubmitted Not Done ReplyInline ActionsI'd like to "simply" ask the call site for nofree and doesNotReadMemory, or make doesNotFreeMemory a CallBase method with redirects in CallSite. That allows to annotate the call site directly. jdoerfert: I'd like to "simply" ask the call site for `nofree` and `doesNotReadMemory`, or make… | |||||
if (SCCNodes.count(Callee) > 0) | |||||
return false; | |||||
// A call to a function which we recognize, but does not free memory, is | |||||
// okay. Otherwise, we need to assume that the function might free memory. | |||||
LibFunc TLIFn; | |||||
StringRef FnName = Callee->getName(); | |||||
if (TLI.getLibFunc(FnName, TLIFn)) | |||||
Not Done ReplyInline ActionsThis seems delicate; there are some C library functions which aren't "free", but can still free memory (like mmap). Not sure we recognize any functions like that at the moment, but we could add them. I'd prefer to add explicit logic to inferLibFuncAttributes. If you are going to call getLibFunc, use the overload that takes a CallSite. efriedma: This seems delicate; there are some C library functions which aren't "free", but can still free… | |||||
Not Done ReplyInline Actions
This also makes me uneasy. On the other hand, maintaining a separate list of functions which don't free memory, which is nearly all of them, also seems bad. My best suggestion is to add a strongly-worded note to include/llvm/Analysis/TargetLibraryInfo.def about what to do if you add such a function that frees memory. Would that be okay? hfinkel: > This seems delicate; there are some C library functions which aren't "free", but can still… | |||||
Not Done ReplyInline ActionsI guess it's okay to default all known functions which aren't free-like/realloc-like to infer nofree, with an appropriate comment. I'd still prefer to move the code to infer nofree on library calls to inferLibFuncAttributes. efriedma: I guess it's okay to default all known functions which aren't free-like/realloc-like to infer… | |||||
Not Done ReplyInline ActionsI also think we should move deduction logic for library functions to inferLibFuncAttributes and annotate intrinsics properly. jdoerfert: I also think we should move deduction logic for library functions to `inferLibFuncAttributes`… | |||||
return false; | |||||
return true; | |||||
} | |||||
/// Infer attributes from all functions in the SCC by scanning every | /// Infer attributes from all functions in the SCC by scanning every | ||||
/// instruction for compliance to the attribute assumptions. Currently it | /// instruction for compliance to the attribute assumptions. Currently it | ||||
/// does: | /// does: | ||||
/// - removal of Convergent attribute | /// - removal of Convergent attribute | ||||
/// - addition of NoUnwind attribute | /// - addition of NoUnwind attribute | ||||
/// | /// | ||||
/// Returns true if any changes to function attributes were made. | /// Returns true if any changes to function attributes were made. | ||||
static bool inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes) { | static bool inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes, | ||||
const TargetLibraryInfo &TLI) { | |||||
AttributeInferer AI; | AttributeInferer AI; | ||||
// Request to remove the convergent attribute from all functions in the SCC | // Request to remove the convergent attribute from all functions in the SCC | ||||
// if every callsite within the SCC is not convergent (except for calls | // if every callsite within the SCC is not convergent (except for calls | ||||
// to functions within the SCC). | // to functions within the SCC). | ||||
// Note: Removal of the attr from the callsites will happen in | // Note: Removal of the attr from the callsites will happen in | ||||
// InstCombineCalls separately. | // InstCombineCalls separately. | ||||
Show All 29 Lines | AI.registerAttrInference(AttributeInferer::InferenceDescriptor{ | ||||
[](Function &F) { | [](Function &F) { | ||||
LLVM_DEBUG(dbgs() | LLVM_DEBUG(dbgs() | ||||
<< "Adding nounwind attr to fn " << F.getName() << "\n"); | << "Adding nounwind attr to fn " << F.getName() << "\n"); | ||||
F.setDoesNotThrow(); | F.setDoesNotThrow(); | ||||
++NumNoUnwind; | ++NumNoUnwind; | ||||
}, | }, | ||||
/* RequiresExactDefinition= */ true}); | /* RequiresExactDefinition= */ true}); | ||||
if (!DisableNoFreeInference) | |||||
// Request to infer nofree attribute for all the functions in the SCC if | |||||
// every callsite within the SCC does not directly or indirectly free | |||||
// memory (except for calls to functions within the SCC). Note that nofree | |||||
// attribute suffers from derefinement - results may change depending on | |||||
// how functions are optimized. Thus it can be inferred only from exact | |||||
// definitions. | |||||
AI.registerAttrInference(AttributeInferer::InferenceDescriptor{ | |||||
Attribute::NoFree, | |||||
// Skip functions known not to free memory. | |||||
[](const Function &F) { return F.doesNotFreeMemory(); }, | |||||
// Instructions that break non-deallocating assumption. | |||||
[SCCNodes, TLI](Instruction &I) { | |||||
return InstrBreaksNoFree(I, SCCNodes, TLI); | |||||
}, | |||||
[](Function &F) { | |||||
LLVM_DEBUG(dbgs() | |||||
<< "Adding nofree attr to fn " << F.getName() << "\n"); | |||||
F.setDoesNotFreeMemory(); | |||||
++NumNoFree; | |||||
}, | |||||
/* RequiresExactDefinition= */ true}); | |||||
// Perform all the requested attribute inference actions. | // Perform all the requested attribute inference actions. | ||||
return AI.run(SCCNodes); | return AI.run(SCCNodes); | ||||
} | } | ||||
static bool setDoesNotRecurse(Function &F) { | static bool setDoesNotRecurse(Function &F) { | ||||
if (F.doesNotRecurse()) | if (F.doesNotRecurse()) | ||||
return false; | return false; | ||||
F.setDoesNotRecurse(); | F.setDoesNotRecurse(); | ||||
Show All 26 Lines | static bool addNoRecurseAttrs(const SCCNodeSet &SCCNodes) { | ||||
// Every call was to a non-recursive function other than this function, and | // Every call was to a non-recursive function other than this function, and | ||||
// we have no indirect recursion as the SCC size is one. This function cannot | // we have no indirect recursion as the SCC size is one. This function cannot | ||||
// recurse. | // recurse. | ||||
return setDoesNotRecurse(*F); | return setDoesNotRecurse(*F); | ||||
} | } | ||||
template <typename AARGetterT> | template <typename AARGetterT> | ||||
static bool deriveAttrsInPostOrder(SCCNodeSet &SCCNodes, AARGetterT &&AARGetter, | static bool deriveAttrsInPostOrder(SCCNodeSet &SCCNodes, | ||||
const TargetLibraryInfo &TLI, | |||||
AARGetterT &&AARGetter, | |||||
bool HasUnknownCall) { | bool HasUnknownCall) { | ||||
bool Changed = false; | bool Changed = false; | ||||
// Bail if the SCC only contains optnone functions. | // Bail if the SCC only contains optnone functions. | ||||
if (SCCNodes.empty()) | if (SCCNodes.empty()) | ||||
return Changed; | return Changed; | ||||
Changed |= addArgumentReturnedAttrs(SCCNodes); | Changed |= addArgumentReturnedAttrs(SCCNodes); | ||||
Changed |= addReadAttrs(SCCNodes, AARGetter); | Changed |= addReadAttrs(SCCNodes, AARGetter); | ||||
Changed |= addArgumentAttrs(SCCNodes); | Changed |= addArgumentAttrs(SCCNodes); | ||||
// If we have no external nodes participating in the SCC, we can deduce some | // If we have no external nodes participating in the SCC, we can deduce some | ||||
// more precise attributes as well. | // more precise attributes as well. | ||||
if (!HasUnknownCall) { | if (!HasUnknownCall) { | ||||
Changed |= addNoAliasAttrs(SCCNodes); | Changed |= addNoAliasAttrs(SCCNodes); | ||||
Changed |= addNonNullAttrs(SCCNodes); | Changed |= addNonNullAttrs(SCCNodes); | ||||
Changed |= inferAttrsFromFunctionBodies(SCCNodes); | Changed |= inferAttrsFromFunctionBodies(SCCNodes, TLI); | ||||
Changed |= addNoRecurseAttrs(SCCNodes); | Changed |= addNoRecurseAttrs(SCCNodes); | ||||
} | } | ||||
return Changed; | return Changed; | ||||
} | } | ||||
PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C, | PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C, | ||||
CGSCCAnalysisManager &AM, | CGSCCAnalysisManager &AM, | ||||
LazyCallGraph &CG, | LazyCallGraph &CG, | ||||
CGSCCUpdateResult &) { | CGSCCUpdateResult &) { | ||||
FunctionAnalysisManager &FAM = | FunctionAnalysisManager &FAM = | ||||
AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager(); | AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager(); | ||||
const ModuleAnalysisManager &MAM = | |||||
AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); | |||||
assert(C.size() > 0 && "Cannot handle an empty SCC!"); | |||||
Module &M = *C.begin()->getFunction().getParent(); | |||||
auto &TLI = *MAM.getCachedResult<TargetLibraryAnalysis>(M); | |||||
// We pass a lambda into functions to wire them up to the analysis manager | // We pass a lambda into functions to wire them up to the analysis manager | ||||
// for getting function analyses. | // for getting function analyses. | ||||
auto AARGetter = [&](Function &F) -> AAResults & { | auto AARGetter = [&](Function &F) -> AAResults & { | ||||
return FAM.getResult<AAManager>(F); | return FAM.getResult<AAManager>(F); | ||||
}; | }; | ||||
// Fill SCCNodes with the elements of the SCC. Also track whether there are | // Fill SCCNodes with the elements of the SCC. Also track whether there are | ||||
// any external or opt-none nodes that will prevent us from optimizing any | // // any external or opt-none nodes that will prevent us from optimizing any | ||||
// part of the SCC. | // // part of the SCC. | ||||
SCCNodeSet SCCNodes; | SCCNodeSet SCCNodes; | ||||
bool HasUnknownCall = false; | bool HasUnknownCall = false; | ||||
for (LazyCallGraph::Node &N : C) { | for (LazyCallGraph::Node &N : C) { | ||||
Function &F = N.getFunction(); | Function &F = N.getFunction(); | ||||
if (F.hasOptNone() || F.hasFnAttribute(Attribute::Naked)) { | if (F.hasOptNone() || F.hasFnAttribute(Attribute::Naked)) { | ||||
// Treat any function we're trying not to optimize as if it were an | // Treat any function we're trying not to optimize as if it were an | ||||
// indirect call and omit it from the node set used below. | // indirect call and omit it from the node set used below. | ||||
HasUnknownCall = true; | HasUnknownCall = true; | ||||
Show All 9 Lines | if (!HasUnknownCall) | ||||
if (!CS.getCalledFunction()) { | if (!CS.getCalledFunction()) { | ||||
HasUnknownCall = true; | HasUnknownCall = true; | ||||
break; | break; | ||||
} | } | ||||
SCCNodes.insert(&F); | SCCNodes.insert(&F); | ||||
} | } | ||||
if (deriveAttrsInPostOrder(SCCNodes, AARGetter, HasUnknownCall)) | if (deriveAttrsInPostOrder(SCCNodes, TLI, AARGetter, HasUnknownCall)) | ||||
return PreservedAnalyses::none(); | return PreservedAnalyses::none(); | ||||
return PreservedAnalyses::all(); | return PreservedAnalyses::all(); | ||||
} | } | ||||
namespace { | namespace { | ||||
struct PostOrderFunctionAttrsLegacyPass : public CallGraphSCCPass { | struct PostOrderFunctionAttrsLegacyPass : public CallGraphSCCPass { | ||||
// Pass identification, replacement for typeid | // Pass identification, replacement for typeid | ||||
static char ID; | static char ID; | ||||
PostOrderFunctionAttrsLegacyPass() : CallGraphSCCPass(ID) { | PostOrderFunctionAttrsLegacyPass() : CallGraphSCCPass(ID) { | ||||
initializePostOrderFunctionAttrsLegacyPassPass( | initializePostOrderFunctionAttrsLegacyPassPass( | ||||
*PassRegistry::getPassRegistry()); | *PassRegistry::getPassRegistry()); | ||||
} | } | ||||
bool runOnSCC(CallGraphSCC &SCC) override; | bool runOnSCC(CallGraphSCC &SCC) override; | ||||
void getAnalysisUsage(AnalysisUsage &AU) const override { | void getAnalysisUsage(AnalysisUsage &AU) const override { | ||||
AU.setPreservesCFG(); | AU.setPreservesCFG(); | ||||
AU.addRequired<TargetLibraryInfoWrapperPass>(); | |||||
AU.addRequired<AssumptionCacheTracker>(); | AU.addRequired<AssumptionCacheTracker>(); | ||||
getAAResultsAnalysisUsage(AU); | getAAResultsAnalysisUsage(AU); | ||||
CallGraphSCCPass::getAnalysisUsage(AU); | CallGraphSCCPass::getAnalysisUsage(AU); | ||||
} | } | ||||
}; | }; | ||||
} // end anonymous namespace | } // end anonymous namespace | ||||
char PostOrderFunctionAttrsLegacyPass::ID = 0; | char PostOrderFunctionAttrsLegacyPass::ID = 0; | ||||
INITIALIZE_PASS_BEGIN(PostOrderFunctionAttrsLegacyPass, "functionattrs", | INITIALIZE_PASS_BEGIN(PostOrderFunctionAttrsLegacyPass, "functionattrs", | ||||
"Deduce function attributes", false, false) | "Deduce function attributes", false, false) | ||||
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) | |||||
INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker) | INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker) | ||||
INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) | INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) | ||||
INITIALIZE_PASS_END(PostOrderFunctionAttrsLegacyPass, "functionattrs", | INITIALIZE_PASS_END(PostOrderFunctionAttrsLegacyPass, "functionattrs", | ||||
"Deduce function attributes", false, false) | "Deduce function attributes", false, false) | ||||
Pass *llvm::createPostOrderFunctionAttrsLegacyPass() { | Pass *llvm::createPostOrderFunctionAttrsLegacyPass() { | ||||
return new PostOrderFunctionAttrsLegacyPass(); | return new PostOrderFunctionAttrsLegacyPass(); | ||||
} | } | ||||
template <typename AARGetterT> | template <typename AARGetterT> | ||||
static bool runImpl(CallGraphSCC &SCC, AARGetterT AARGetter) { | static bool runImpl(CallGraphSCC &SCC, const TargetLibraryInfo &TLI, | ||||
AARGetterT AARGetter) { | |||||
// Fill SCCNodes with the elements of the SCC. Used for quickly looking up | // Fill SCCNodes with the elements of the SCC. Used for quickly looking up | ||||
// whether a given CallGraphNode is in this SCC. Also track whether there are | // whether a given CallGraphNode is in this SCC. Also track whether there are | ||||
// any external or opt-none nodes that will prevent us from optimizing any | // any external or opt-none nodes that will prevent us from optimizing any | ||||
// part of the SCC. | // part of the SCC. | ||||
SCCNodeSet SCCNodes; | SCCNodeSet SCCNodes; | ||||
bool ExternalNode = false; | bool ExternalNode = false; | ||||
for (CallGraphNode *I : SCC) { | for (CallGraphNode *I : SCC) { | ||||
Function *F = I->getFunction(); | Function *F = I->getFunction(); | ||||
if (!F || F->hasOptNone() || F->hasFnAttribute(Attribute::Naked)) { | if (!F || F->hasOptNone() || F->hasFnAttribute(Attribute::Naked)) { | ||||
// External node or function we're trying not to optimize - we both avoid | // External node or function we're trying not to optimize - we both avoid | ||||
// transform them and avoid leveraging information they provide. | // transform them and avoid leveraging information they provide. | ||||
ExternalNode = true; | ExternalNode = true; | ||||
continue; | continue; | ||||
} | } | ||||
SCCNodes.insert(F); | SCCNodes.insert(F); | ||||
} | } | ||||
return deriveAttrsInPostOrder(SCCNodes, AARGetter, ExternalNode); | return deriveAttrsInPostOrder(SCCNodes, TLI, AARGetter, ExternalNode); | ||||
} | } | ||||
bool PostOrderFunctionAttrsLegacyPass::runOnSCC(CallGraphSCC &SCC) { | bool PostOrderFunctionAttrsLegacyPass::runOnSCC(CallGraphSCC &SCC) { | ||||
if (skipSCC(SCC)) | if (skipSCC(SCC)) | ||||
return false; | return false; | ||||
return runImpl(SCC, LegacyAARGetter(*this)); | auto &TLI = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(); | ||||
return runImpl(SCC, TLI, LegacyAARGetter(*this)); | |||||
Not Done ReplyInline ActionsTLI should be unused in this file and all related changes should be undone. jdoerfert: TLI should be unused in this file and all related changes should be undone. | |||||
} | } | ||||
namespace { | namespace { | ||||
struct ReversePostOrderFunctionAttrsLegacyPass : public ModulePass { | struct ReversePostOrderFunctionAttrsLegacyPass : public ModulePass { | ||||
// Pass identification, replacement for typeid | // Pass identification, replacement for typeid | ||||
static char ID; | static char ID; | ||||
▲ Show 20 Lines • Show All 103 Lines • Show Last 20 Lines |
Why is this needed? The below logic should be sufficient because we want to check for "must not free" and therefore it doesn't matter if a function is "must-free" or "may-free".