Refactor capture tracking (which already had a couple flags for whether returns

and stores capture) to permit the caller to see each capture point and decide
whether to continue looking.

Use this inside memdep to do an analysis that basicaa won't do. This lets us
solve another devirtualization case, fixing PR8908!

llvm-svn: 144580
This commit is contained in:
Nick Lewycky 2011-11-14 22:49:42 +00:00
parent 4e88fbebde
commit 7013a19e8a
5 changed files with 271 additions and 118 deletions

View File

@ -14,9 +14,14 @@
#ifndef LLVM_ANALYSIS_CAPTURETRACKING_H
#define LLVM_ANALYSIS_CAPTURETRACKING_H
namespace llvm {
class Value;
#include "llvm/Constants.h"
#include "llvm/Instructions.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/CallSite.h"
namespace llvm {
/// PointerMayBeCaptured - Return true if this pointer value may be captured
/// by the enclosing function (which is required to exist). This routine can
/// be expensive, so consider caching the results. The boolean ReturnCaptures
@ -28,6 +33,132 @@ namespace llvm {
bool ReturnCaptures,
bool StoreCaptures);
/// PointerMayBeCaptured - Visit the value and the values derived from it and
/// find values which appear to be capturing the pointer value. This feeds
/// results into and is controlled by the templated CaptureTracker object:
///
/// struct YourCaptureTracker {
/// /// tooManyUses - The depth of traversal has breached a limit.
/// /// The tracker should conservatively assume that the value is captured.
/// void tooManyUses();
///
/// /// shouldExplore - This is the use of a value derived from the pointer.
/// /// Return false to prune the search (ie., assume that none of its users
/// /// could possibly capture) return false. To search it, return true.
/// ///
/// /// Also, U->getUser() is guaranteed to be an Instruction.
/// bool shouldExplore(Use *U);
///
/// /// captured - The instruction I captured the pointer. Return true to
/// /// stop the traversal or false to continue looking for more capturing
/// /// instructions.
/// bool captured(Instruction *I);
///
/// /// Provide your own getters for the state.
/// };
template<typename CaptureTracker>
void PointerMayBeCaptured(const Value *V, CaptureTracker &Tracker);
} // end namespace llvm
template<typename CaptureTracker>
void llvm::PointerMayBeCaptured(const llvm::Value *V, CaptureTracker &Tracker) {
assert(V->getType()->isPointerTy() && "Capture is for pointers only!");
SmallVector<Use*, 20> Worklist;
SmallSet<Use*, 20> Visited;
int Count = 0;
for (Value::const_use_iterator UI = V->use_begin(), UE = V->use_end();
UI != UE; ++UI) {
// If there are lots of uses, conservatively say that the value
// is captured to avoid taking too much compile time.
if (Count++ >= 20)
return Tracker.tooManyUses();
Use *U = &UI.getUse();
if (!Tracker.shouldExplore(U)) continue;
Visited.insert(U);
Worklist.push_back(U);
}
while (!Worklist.empty()) {
Use *U = Worklist.pop_back_val();
Instruction *I = cast<Instruction>(U->getUser());
V = U->get();
switch (I->getOpcode()) {
case Instruction::Call:
case Instruction::Invoke: {
CallSite CS(I);
// Not captured if the callee is readonly, doesn't return a copy through
// its return value and doesn't unwind (a readonly function can leak bits
// by throwing an exception or not depending on the input value).
if (CS.onlyReadsMemory() && CS.doesNotThrow() && I->getType()->isVoidTy())
break;
// Not captured if only passed via 'nocapture' arguments. Note that
// calling a function pointer does not in itself cause the pointer to
// be captured. This is a subtle point considering that (for example)
// the callee might return its own address. It is analogous to saying
// that loading a value from a pointer does not cause the pointer to be
// captured, even though the loaded value might be the pointer itself
// (think of self-referential objects).
CallSite::arg_iterator B = CS.arg_begin(), E = CS.arg_end();
for (CallSite::arg_iterator A = B; A != E; ++A)
if (A->get() == V && !CS.paramHasAttr(A - B + 1, Attribute::NoCapture))
// The parameter is not marked 'nocapture' - captured.
if (Tracker.captured(I))
return;
break;
}
case Instruction::Load:
// Loading from a pointer does not cause it to be captured.
break;
case Instruction::VAArg:
// "va-arg" from a pointer does not cause it to be captured.
break;
case Instruction::Store:
if (V == I->getOperand(0))
// Stored the pointer - conservatively assume it may be captured.
if (Tracker.captured(I))
return;
// Storing to the pointee does not cause the pointer to be captured.
break;
case Instruction::BitCast:
case Instruction::GetElementPtr:
case Instruction::PHI:
case Instruction::Select:
// The original value is not captured via this if the new value isn't.
for (Instruction::use_iterator UI = I->use_begin(), UE = I->use_end();
UI != UE; ++UI) {
Use *U = &UI.getUse();
if (Visited.insert(U))
if (Tracker.shouldExplore(U))
Worklist.push_back(U);
}
break;
case Instruction::ICmp:
// Don't count comparisons of a no-alias return value against null as
// captures. This allows us to ignore comparisons of malloc results
// with null, for example.
if (isNoAliasCall(V->stripPointerCasts()))
if (ConstantPointerNull *CPN =
dyn_cast<ConstantPointerNull>(I->getOperand(1)))
if (CPN->getType()->getAddressSpace() == 0)
break;
// Otherwise, be conservative. There are crazy ways to capture pointers
// using comparisons.
if (Tracker.captured(I))
return;
break;
default:
// Something else - be conservative and say it is captured.
if (Tracker.captured(I))
return;
break;
}
}
// All uses examined.
}
#endif

View File

@ -324,6 +324,7 @@ namespace llvm {
/// Current AA implementation, just a cache.
AliasAnalysis *AA;
TargetData *TD;
DominatorTree *DT;
OwningPtr<PredIteratorCache> PredCache;
public:
MemoryDependenceAnalysis();
@ -430,6 +431,9 @@ namespace llvm {
void RemoveCachedNonLocalPointerDependencies(ValueIsLoadPair P);
AliasAnalysis::ModRefResult
getModRefInfo(const Instruction *Inst, const AliasAnalysis::Location &Loc);
/// verifyRemoved - Verify that the specified instruction does not occur
/// in our internal data structures.
void verifyRemoved(Instruction *Inst) const;

View File

@ -17,24 +17,30 @@
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/CaptureTracking.h"
#include "llvm/Constants.h"
#include "llvm/Instructions.h"
#include "llvm/Value.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/CallSite.h"
using namespace llvm;
/// As its comment mentions, PointerMayBeCaptured can be expensive.
/// However, it's not easy for BasicAA to cache the result, because
/// it's an ImmutablePass. To work around this, bound queries at a
/// fixed number of uses.
///
/// TODO: Write a new FunctionPass AliasAnalysis so that it can keep
/// a cache. Then we can move the code from BasicAliasAnalysis into
/// that path, and remove this threshold.
static int const Threshold = 20;
namespace {
struct SimpleCaptureTracker {
explicit SimpleCaptureTracker(bool ReturnCaptures)
: ReturnCaptures(ReturnCaptures), Captured(false) {}
void tooManyUses() { Captured = true; }
bool shouldExplore(Use *U) { return true; }
bool captured(Instruction *I) {
if (isa<ReturnInst>(I) && !ReturnCaptures)
return false;
Captured = true;
return true;
}
bool ReturnCaptures;
bool Captured;
};
}
/// PointerMayBeCaptured - Return true if this pointer value may be captured
/// by the enclosing function (which is required to exist). This routine can
@ -45,104 +51,13 @@ static int const Threshold = 20;
/// counts as capturing it or not.
bool llvm::PointerMayBeCaptured(const Value *V,
bool ReturnCaptures, bool StoreCaptures) {
assert(V->getType()->isPointerTy() && "Capture is for pointers only!");
SmallVector<Use*, Threshold> Worklist;
SmallSet<Use*, Threshold> Visited;
int Count = 0;
// TODO: If StoreCaptures is not true, we could do Fancy analysis
// to determine whether this store is not actually an escape point.
// In that case, BasicAliasAnalysis should be updated as well to
// take advantage of this.
(void)StoreCaptures;
for (Value::const_use_iterator UI = V->use_begin(), UE = V->use_end();
UI != UE; ++UI) {
// If there are lots of uses, conservatively say that the value
// is captured to avoid taking too much compile time.
if (Count++ >= Threshold)
return true;
Use *U = &UI.getUse();
Visited.insert(U);
Worklist.push_back(U);
}
while (!Worklist.empty()) {
Use *U = Worklist.pop_back_val();
Instruction *I = cast<Instruction>(U->getUser());
V = U->get();
switch (I->getOpcode()) {
case Instruction::Call:
case Instruction::Invoke: {
CallSite CS(I);
// Not captured if the callee is readonly, doesn't return a copy through
// its return value and doesn't unwind (a readonly function can leak bits
// by throwing an exception or not depending on the input value).
if (CS.onlyReadsMemory() && CS.doesNotThrow() && I->getType()->isVoidTy())
break;
// Not captured if only passed via 'nocapture' arguments. Note that
// calling a function pointer does not in itself cause the pointer to
// be captured. This is a subtle point considering that (for example)
// the callee might return its own address. It is analogous to saying
// that loading a value from a pointer does not cause the pointer to be
// captured, even though the loaded value might be the pointer itself
// (think of self-referential objects).
CallSite::arg_iterator B = CS.arg_begin(), E = CS.arg_end();
for (CallSite::arg_iterator A = B; A != E; ++A)
if (A->get() == V && !CS.paramHasAttr(A - B + 1, Attribute::NoCapture))
// The parameter is not marked 'nocapture' - captured.
return true;
// Only passed via 'nocapture' arguments, or is the called function - not
// captured.
break;
}
case Instruction::Load:
// Loading from a pointer does not cause it to be captured.
break;
case Instruction::VAArg:
// "va-arg" from a pointer does not cause it to be captured.
break;
case Instruction::Ret:
if (ReturnCaptures)
return true;
break;
case Instruction::Store:
if (V == I->getOperand(0))
// Stored the pointer - conservatively assume it may be captured.
// TODO: If StoreCaptures is not true, we could do Fancy analysis
// to determine whether this store is not actually an escape point.
// In that case, BasicAliasAnalysis should be updated as well to
// take advantage of this.
return true;
// Storing to the pointee does not cause the pointer to be captured.
break;
case Instruction::BitCast:
case Instruction::GetElementPtr:
case Instruction::PHI:
case Instruction::Select:
// The original value is not captured via this if the new value isn't.
for (Instruction::use_iterator UI = I->use_begin(), UE = I->use_end();
UI != UE; ++UI) {
Use *U = &UI.getUse();
if (Visited.insert(U))
Worklist.push_back(U);
}
break;
case Instruction::ICmp:
// Don't count comparisons of a no-alias return value against null as
// captures. This allows us to ignore comparisons of malloc results
// with null, for example.
if (isNoAliasCall(V->stripPointerCasts()))
if (ConstantPointerNull *CPN =
dyn_cast<ConstantPointerNull>(I->getOperand(1)))
if (CPN->getType()->getAddressSpace() == 0)
break;
// Otherwise, be conservative. There are crazy ways to capture pointers
// using comparisons.
return true;
default:
// Something else - be conservative and say it is captured.
return true;
}
}
// All uses examined - not captured.
return false;
SimpleCaptureTracker SCT(ReturnCaptures);
PointerMayBeCaptured(V, SCT);
return SCT.Captured;
}

View File

@ -22,6 +22,7 @@
#include "llvm/Function.h"
#include "llvm/LLVMContext.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CaptureTracking.h"
#include "llvm/Analysis/Dominators.h"
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/MemoryBuiltins.h"
@ -91,6 +92,7 @@ void MemoryDependenceAnalysis::getAnalysisUsage(AnalysisUsage &AU) const {
bool MemoryDependenceAnalysis::runOnFunction(Function &) {
AA = &getAnalysis<AliasAnalysis>();
TD = getAnalysisIfAvailable<TargetData>();
DT = getAnalysisIfAvailable<DominatorTree>();
if (PredCache == 0)
PredCache.reset(new PredIteratorCache());
return false;
@ -331,6 +333,82 @@ getLoadLoadClobberFullWidthSize(const Value *MemLocBase, int64_t MemLocOffs,
return 0;
}
namespace {
/// Only find pointer captures which happen before the given instruction. Uses
/// the dominator tree to determine whether one instruction is before another.
struct CapturesBefore {
CapturesBefore(const Instruction *I, DominatorTree *DT)
: BeforeHere(I), DT(DT), Captured(false) {}
void tooManyUses() { Captured = true; }
bool shouldExplore(Use *U) {
Instruction *I = cast<Instruction>(U->getUser());
if (BeforeHere != I && DT->dominates(BeforeHere, I))
return false;
return true;
}
bool captured(Instruction *I) {
if (BeforeHere != I && DT->dominates(BeforeHere, I))
return false;
Captured = true;
return true;
}
const Instruction *BeforeHere;
DominatorTree *DT;
bool Captured;
};
}
AliasAnalysis::ModRefResult
MemoryDependenceAnalysis::getModRefInfo(const Instruction *Inst,
const AliasAnalysis::Location &MemLoc) {
AliasAnalysis::ModRefResult MR = AA->getModRefInfo(Inst, MemLoc);
if (MR != AliasAnalysis::ModRef) return MR;
// FIXME: this is really just shoring-up a deficiency in alias analysis.
// BasicAA isn't willing to spend linear time determining whether an alloca
// was captured before or after this particular call, while we are. However,
// with a smarter AA in place, this test is just wasting compile time.
if (!DT) return AliasAnalysis::ModRef;
const Value *Object = GetUnderlyingObject(MemLoc.Ptr, TD);
if (!isIdentifiedObject(Object) || isa<GlobalVariable>(Object))
return AliasAnalysis::ModRef;
ImmutableCallSite CS(Inst);
if (!CS.getInstruction()) return AliasAnalysis::ModRef;
CapturesBefore CB(Inst, DT);
llvm::PointerMayBeCaptured(Object, CB);
if (isa<Constant>(Object) || CS.getInstruction() == Object || CB.Captured)
return AliasAnalysis::ModRef;
unsigned ArgNo = 0;
for (ImmutableCallSite::arg_iterator CI = CS.arg_begin(), CE = CS.arg_end();
CI != CE; ++CI, ++ArgNo) {
// Only look at the no-capture or byval pointer arguments. If this
// pointer were passed to arguments that were neither of these, then it
// couldn't be no-capture.
if (!(*CI)->getType()->isPointerTy() ||
(!CS.paramHasAttr(ArgNo+1, Attribute::NoCapture) &&
!CS.paramHasAttr(ArgNo+1, Attribute::ByVal)))
continue;
// If this is a no-capture pointer argument, see if we can tell that it
// is impossible to alias the pointer we're checking. If not, we have to
// assume that the call could touch the pointer, even though it doesn't
// escape.
if (!AA->isNoAlias(AliasAnalysis::Location(*CI),
AliasAnalysis::Location(Object))) {
return AliasAnalysis::ModRef;
}
}
return AliasAnalysis::NoModRef;
}
/// getPointerDependencyFrom - Return the instruction on which a memory
/// location depends. If isLoad is true, this routine ignores may-aliases with
/// read-only operations. If isLoad is false, this routine ignores may-aliases
@ -478,7 +556,7 @@ getPointerDependencyFrom(const AliasAnalysis::Location &MemLoc, bool isLoad,
}
// See if this instruction (e.g. a call or vaarg) mod/ref's the pointer.
switch (AA->getModRefInfo(Inst, MemLoc)) {
switch (getModRefInfo(Inst, MemLoc)) {
case AliasAnalysis::NoModRef:
// If the call has no effect on the queried pointer, just ignore it.
continue;

View File

@ -642,3 +642,28 @@ declare void @llvm.memset.p0i8.i64(i8* nocapture, i8, i64, i32, i1) nounwind
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture, i64, i32, i1) nounwind
;;===----------------------------------------------------------------------===;;
;; Load -> Store dependency which isn't interfered with by a call that happens
;; before the pointer was captured.
;;===----------------------------------------------------------------------===;;
%class.X = type { [8 x i8] }
@_ZTV1X = weak_odr constant [5 x i8*] zeroinitializer
@_ZTV1Y = weak_odr constant [5 x i8*] zeroinitializer
declare void @use()
declare void @use3(i8***, i8**)
; PR8908
define void @test_escape1() nounwind {
%x = alloca i8**, align 8
store i8** getelementptr inbounds ([5 x i8*]* @_ZTV1X, i64 0, i64 2), i8*** %x, align 8
call void @use() nounwind
%DEAD = load i8*** %x, align 8
call void @use3(i8*** %x, i8** %DEAD) nounwind
ret void
; CHECK: test_escape1
; CHECK-NOT: DEAD
; CHECK: ret
}