Remove context sensitivity concerns from interprocedural-basic-aa, and

make it more aggressive in cases where both pointers are known to live
in the same function.

llvm-svn: 107420
This commit is contained in:
Dan Gohman 2010-07-01 20:08:40 +00:00
parent 116be5347e
commit 84f90a387d
3 changed files with 1794 additions and 42 deletions

View File

@ -55,10 +55,9 @@ static bool isKnownNonNull(const Value *V) {
/// isNonEscapingLocalObject - Return true if the pointer is to a function-local /// isNonEscapingLocalObject - Return true if the pointer is to a function-local
/// object that never escapes from the function. /// object that never escapes from the function.
static bool isNonEscapingLocalObject(const Value *V, bool Interprocedural) { static bool isNonEscapingLocalObject(const Value *V) {
// If this is a local allocation, check to see if it escapes. // If this is a local allocation, check to see if it escapes.
if (isa<AllocaInst>(V) || if (isa<AllocaInst>(V) || isNoAliasCall(V))
(!Interprocedural && isNoAliasCall(V)))
// Set StoreCaptures to True so that we can assume in our callers that the // Set StoreCaptures to True so that we can assume in our callers that the
// pointer is not the result of a load instruction. Currently // pointer is not the result of a load instruction. Currently
// PointerMayBeCaptured doesn't have any special analysis for the // PointerMayBeCaptured doesn't have any special analysis for the
@ -69,23 +68,21 @@ static bool isNonEscapingLocalObject(const Value *V, bool Interprocedural) {
// If this is an argument that corresponds to a byval or noalias argument, // If this is an argument that corresponds to a byval or noalias argument,
// then it has not escaped before entering the function. Check if it escapes // then it has not escaped before entering the function. Check if it escapes
// inside the function. // inside the function.
if (!Interprocedural) if (const Argument *A = dyn_cast<Argument>(V))
if (const Argument *A = dyn_cast<Argument>(V)) if (A->hasByValAttr() || A->hasNoAliasAttr()) {
if (A->hasByValAttr() || A->hasNoAliasAttr()) { // Don't bother analyzing arguments already known not to escape.
// Don't bother analyzing arguments already known not to escape. if (A->hasNoCaptureAttr())
if (A->hasNoCaptureAttr()) return true;
return true; return !PointerMayBeCaptured(V, false, /*StoreCaptures=*/true);
return !PointerMayBeCaptured(V, false, /*StoreCaptures=*/true); }
}
return false; return false;
} }
/// isEscapeSource - Return true if the pointer is one which would have /// isEscapeSource - Return true if the pointer is one which would have
/// been considered an escape by isNonEscapingLocalObject. /// been considered an escape by isNonEscapingLocalObject.
static bool isEscapeSource(const Value *V, bool Interprocedural) { static bool isEscapeSource(const Value *V) {
if (!Interprocedural) if (isa<CallInst>(V) || isa<InvokeInst>(V) || isa<Argument>(V))
if (isa<CallInst>(V) || isa<InvokeInst>(V) || isa<Argument>(V)) return true;
return true;
// The load case works because isNonEscapingLocalObject considers all // The load case works because isNonEscapingLocalObject considers all
// stores to be escapes (it passes true for the StoreCaptures argument // stores to be escapes (it passes true for the StoreCaptures argument
@ -197,7 +194,6 @@ ImmutablePass *llvm::createNoAAPass() { return new NoAA(); }
// BasicAliasAnalysis Pass // BasicAliasAnalysis Pass
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#ifdef XDEBUG
static const Function *getParent(const Value *V) { static const Function *getParent(const Value *V) {
if (const Instruction *inst = dyn_cast<Instruction>(V)) if (const Instruction *inst = dyn_cast<Instruction>(V))
return inst->getParent()->getParent(); return inst->getParent()->getParent();
@ -213,6 +209,15 @@ static bool sameParent(const Value *O1, const Value *O2) {
const Function *F1 = getParent(O1); const Function *F1 = getParent(O1);
const Function *F2 = getParent(O2); const Function *F2 = getParent(O2);
return F1 && F1 == F2;
}
#ifdef XDEBUG
static bool notDifferentParent(const Value *O1, const Value *O2) {
const Function *F1 = getParent(O1);
const Function *F2 = getParent(O2);
return !F1 || !F2 || F1 == F2; return !F1 || !F2 || F1 == F2;
} }
#endif #endif
@ -236,7 +241,7 @@ namespace {
const Value *V2, unsigned V2Size) { const Value *V2, unsigned V2Size) {
assert(Visited.empty() && "Visited must be cleared after use!"); assert(Visited.empty() && "Visited must be cleared after use!");
#ifdef XDEBUG #ifdef XDEBUG
assert((Interprocedural || sameParent(V1, V2)) && assert((Interprocedural || notDifferentParent(V1, V2)) &&
"BasicAliasAnalysis (-basicaa) doesn't support interprocedural " "BasicAliasAnalysis (-basicaa) doesn't support interprocedural "
"queries; use InterproceduralAliasAnalysis " "queries; use InterproceduralAliasAnalysis "
"(-interprocedural-basic-aa) instead."); "(-interprocedural-basic-aa) instead.");
@ -331,11 +336,17 @@ BasicAliasAnalysis::getModRefInfo(CallSite CS, Value *P, unsigned Size) {
if (CI->isTailCall()) if (CI->isTailCall())
return NoModRef; return NoModRef;
// If we can identify an object and it's known to be within the
// same function as the call, we can ignore interprocedural concerns.
bool EffectivelyInterprocedural =
Interprocedural && !sameParent(Object, CS.getInstruction());
// If the pointer is to a locally allocated object that does not escape, // If the pointer is to a locally allocated object that does not escape,
// then the call can not mod/ref the pointer unless the call takes the pointer // then the call can not mod/ref the pointer unless the call takes the pointer
// as an argument, and itself doesn't capture it. // as an argument, and itself doesn't capture it.
if (!isa<Constant>(Object) && CS.getInstruction() != Object && if (!isa<Constant>(Object) && CS.getInstruction() != Object &&
isNonEscapingLocalObject(Object, Interprocedural)) { !EffectivelyInterprocedural &&
isNonEscapingLocalObject(Object)) {
bool PassedAsArg = false; bool PassedAsArg = false;
unsigned ArgNo = 0; unsigned ArgNo = 0;
for (CallSite::arg_iterator CI = CS.arg_begin(), CE = CS.arg_end(); for (CallSite::arg_iterator CI = CS.arg_begin(), CE = CS.arg_end();
@ -754,27 +765,32 @@ BasicAliasAnalysis::aliasCheck(const Value *V1, unsigned V1Size,
if (CPN->getType()->getAddressSpace() == 0) if (CPN->getType()->getAddressSpace() == 0)
return NoAlias; return NoAlias;
// If we can identify two objects and they're known to be within the
// same function, we can ignore interprocedural concerns.
bool EffectivelyInterprocedural =
Interprocedural && !sameParent(O1, O2);
if (O1 != O2) { if (O1 != O2) {
// If V1/V2 point to two different objects we know that we have no alias. // If V1/V2 point to two different objects we know that we have no alias.
if (isIdentifiedObject(O1, Interprocedural) && if (isIdentifiedObject(O1, EffectivelyInterprocedural) &&
isIdentifiedObject(O2, Interprocedural)) isIdentifiedObject(O2, EffectivelyInterprocedural))
return NoAlias; return NoAlias;
// Constant pointers can't alias with non-const isIdentifiedObject objects. // Constant pointers can't alias with non-const isIdentifiedObject objects.
if ((isa<Constant>(O1) && if ((isa<Constant>(O1) &&
isIdentifiedObject(O2, Interprocedural) && isIdentifiedObject(O2, EffectivelyInterprocedural) &&
!isa<Constant>(O2)) || !isa<Constant>(O2)) ||
(isa<Constant>(O2) && (isa<Constant>(O2) &&
isIdentifiedObject(O1, Interprocedural) && isIdentifiedObject(O1, EffectivelyInterprocedural) &&
!isa<Constant>(O1))) !isa<Constant>(O1)))
return NoAlias; return NoAlias;
// Arguments can't alias with local allocations or noalias calls, unless // Arguments can't alias with local allocations or noalias calls
// we have to consider interprocedural aliasing. // in the same function.
if (!Interprocedural) if (!EffectivelyInterprocedural &&
if ((isa<Argument>(O1) && (isa<AllocaInst>(O2) || isNoAliasCall(O2))) || ((isa<Argument>(O1) && (isa<AllocaInst>(O2) || isNoAliasCall(O2))) ||
(isa<Argument>(O2) && (isa<AllocaInst>(O1) || isNoAliasCall(O1)))) (isa<Argument>(O2) && (isa<AllocaInst>(O1) || isNoAliasCall(O1)))))
return NoAlias; return NoAlias;
// Most objects can't alias null. // Most objects can't alias null.
if ((isa<ConstantPointerNull>(V2) && isKnownNonNull(O1)) || if ((isa<ConstantPointerNull>(V2) && isKnownNonNull(O1)) ||
@ -790,14 +806,18 @@ BasicAliasAnalysis::aliasCheck(const Value *V1, unsigned V1Size,
return NoAlias; return NoAlias;
// If one pointer is the result of a call/invoke or load and the other is a // If one pointer is the result of a call/invoke or load and the other is a
// non-escaping local object, then we know the object couldn't escape to a // non-escaping local object within the same function, then we know the
// point where the call could return it. // object couldn't escape to a point where the call could return it.
if (O1 != O2) { //
if (isEscapeSource(O1, Interprocedural) && // Note that if the pointers are in different functions, there are a
isNonEscapingLocalObject(O2, Interprocedural)) // variety of complications. A call with a nocapture argument may still
// temporary store the nocapture argument's value in a temporary memory
// location if that memory location doesn't escape. Or it may pass a
// nocapture value to other functions as long as they don't capture it.
if (O1 != O2 && !EffectivelyInterprocedural) {
if (isEscapeSource(O1) && isNonEscapingLocalObject(O2))
return NoAlias; return NoAlias;
if (isEscapeSource(O2, Interprocedural) && if (isEscapeSource(O2) && isNonEscapingLocalObject(O1))
isNonEscapingLocalObject(O1, Interprocedural))
return NoAlias; return NoAlias;
} }

File diff suppressed because it is too large Load Diff

View File

@ -27,23 +27,33 @@ define void @s1() {
ret void ret void
} }
; An alloca can alias an argument in a recursive function. ; An alloca does not alias an argument in the same function.
; CHECK: MayAlias: i64* %t, i64* %u ; CHECK: NoAlias: i64* %t, i64* %u
; CHECK: NoAlias: i64* %a, i64* %u
; CHECK: NoAlias: i64* %a, i64* %t
; CHECK: MayAlias: i64* %u, i64* %v ; CHECK: MayAlias: i64* %u, i64* %v
; CHECK: MayAlias: i64* %t, i64* %v ; CHECK: MayAlias: i64* %t, i64* %v
; CHECK: NoAlias: i64* %a, i64* %v
; CHECK: MayAlias: i64* %b, i64* %u
; CHECK: MayAlias: i64* %b, i64* %t
; CHECK: MayAlias: i64* %b, i64* %v
declare i64* @r0_callee(i64*)
define i64* @r0(i64* %u) { define i64* @r0(i64* %u) {
%t = alloca i64, i32 10 %t = alloca i64, i32 10
%v = call i64* @r0(i64* %t) %a = alloca i64, i32 10
%v = call i64* @r0_callee(i64* %t)
%b = call i64* @r0_callee(i64* %t)
store i64 0, i64* %t store i64 0, i64* %t
store i64 0, i64* %u store i64 0, i64* %u
store i64 0, i64* %v store i64 0, i64* %v
store i64 0, i64* %a
store i64 0, i64* %b
ret i64* %t ret i64* %t
} }
; The noalias attribute is not necessarily safe in an interprocedural context even ; The noalias attribute is safe when both arguments belong to the same function
; in comparison to other noalias arguments in the same function. ; even in an interprocedural context.
; CHECK: MayAlias: i8* %w, i8* %x ; CHECK: NoAlias: i8* %w, i8* %x
define void @q0(i8* noalias %w, i8* noalias %x) { define void @q0(i8* noalias %w, i8* noalias %x) {
store i8 0, i8* %w store i8 0, i8* %w