Skip to content

Commit

Permalink
Merge pull request #11 from nadavrot/alias_analysis_cleanups
Browse files Browse the repository at this point in the history
Alias Analysis cleanups
  • Loading branch information
nadavrot committed Nov 23, 2015
2 parents 5ed14f1 + 971aa23 commit cdacda9
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 139 deletions.
108 changes: 56 additions & 52 deletions include/swift/SILAnalysis/AliasAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,24 @@ class SideEffectAnalysis;
/// needed since we do not have an "analysis" infrastructure.
class AliasAnalysis : public SILAnalysis {
public:
/// The result of an alias query. This is based off of LLVM's alias
/// analysis so see LLVM's documentation for more information.

/// This enum describes the different kinds of aliasing relations between
/// pointers.
///
/// NoAlias: There is never dependence between memory referenced by the two
/// pointers. Example: Two pointers pointing to non-overlapping
/// memory ranges.
///
/// MayAlias: Two pointers might refer to the same memory location.
///
///
/// PartialAlias: The two memory locations are known to be overlapping
/// but do not start at the same address.
///
///
/// MustAlias: The two memory locations always start at exactly the same
/// location. The pointers are equal.
///
/// FIXME: PartialAlias?
enum class AliasResult : unsigned {
NoAlias=0, ///< The two values have no dependencies on each
/// other.
Expand All @@ -42,15 +56,11 @@ class AliasAnalysis : public SILAnalysis {
};

private:
using AliasCacheKey = std::pair<SILValue, SILValue>;
llvm::DenseMap<AliasCacheKey, AliasResult> AliasCache;
SILModule *Mod;
SideEffectAnalysis *SEA;

using MemoryBehavior = SILInstruction::MemoryBehavior;

AliasResult cacheValue(AliasCacheKey Key, AliasResult Result);

AliasResult aliasAddressProjection(SILValue V1, SILValue V2,
SILValue O1, SILValue O2);

Expand All @@ -70,84 +80,77 @@ class AliasAnalysis : public SILAnalysis {

SideEffectAnalysis *getSideEffectAnalysis() const { return SEA; }

/// Perform an alias query to see if V1, V2 refer to the same values.
AliasResult alias(SILValue V1, SILValue V2, SILType TBAAType1 = SILType(),
/// Compute the alias properties of the pointers \p V1 and \p V2.
/// The optional arguments \pTBAAType1 and \p TBAAType2 specify the exact
/// types that the pointers access.
AliasResult alias(SILValue V1, SILValue V2,
SILType TBAAType1 = SILType(),
SILType TBAAType2 = SILType());

/// Convenience method that returns true if V1 and V2 must alias.
bool isMustAlias(SILValue V1, SILValue V2, SILType TBAAType1 = SILType(),
SILType TBAAType2 = SILType()) {
return alias(V1, V2, TBAAType1, TBAAType2) == AliasResult::MustAlias;
}

/// Convenience method that returns true if V1 and V2 partially alias.
bool isPartialAlias(SILValue V1, SILValue V2, SILType TBAAType1 = SILType(),
SILType TBAAType2 = SILType()) {
return alias(V1, V2, TBAAType1, TBAAType2) == AliasResult::PartialAlias;
#define ALIAS_PROPERTY_CONVENIENCE_METHOD(KIND) \
bool is##KIND(SILValue V1, SILValue V2, \
SILType TBAAType1 = SILType(), \
SILType TBAAType2 = SILType()) { \
return alias(V1, V2, TBAAType1, TBAAType2) == AliasResult::KIND; \
}

/// Convenience method that returns true if V1, V2 can not alias.
bool isNoAlias(SILValue V1, SILValue V2, SILType TBAAType1 = SILType(),
SILType TBAAType2 = SILType()) {
return alias(V1, V2, TBAAType1, TBAAType2) == AliasResult::NoAlias;
}
ALIAS_PROPERTY_CONVENIENCE_METHOD(MustAlias)
ALIAS_PROPERTY_CONVENIENCE_METHOD(PartialAlias)
ALIAS_PROPERTY_CONVENIENCE_METHOD(NoAlias)
ALIAS_PROPERTY_CONVENIENCE_METHOD(MayAlias)

/// Convenience method that returns true if V1, V2 may alias.
bool isMayAlias(SILValue V1, SILValue V2, SILType TBAAType1 = SILType(),
SILType TBAAType2 = SILType()) {
return alias(V1, V2, TBAAType1, TBAAType2) == AliasResult::MayAlias;
}
#undef ALIAS_PROPERTY_CONVENIENCE_METHOD

/// Use the alias analysis to determine the memory behavior of Inst with
/// respect to V.
///
/// TODO: When ref count behavior is separated from generic memory behavior,
/// the IgnoreRefCountIncrements flag will be unnecessary.
MemoryBehavior getMemoryBehavior(SILInstruction *Inst, SILValue V,
RetainObserveKind =
RetainObserveKind::ObserveRetains);
MemoryBehavior computeMemoryBehavior(SILInstruction *Inst, SILValue V,
RetainObserveKind);

/// Returns true if Inst may read from memory in a manner that affects V.
/// Returns true if \p Inst may read from memory in a manner that
/// affects V.
bool mayReadFromMemory(SILInstruction *Inst, SILValue V) {
MemoryBehavior B = getMemoryBehavior(Inst, V,
RetainObserveKind::IgnoreRetains);
auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::IgnoreRetains);
return B == MemoryBehavior::MayRead ||
B == MemoryBehavior::MayReadWrite ||
B == MemoryBehavior::MayHaveSideEffects;
B == MemoryBehavior::MayReadWrite ||
B == MemoryBehavior::MayHaveSideEffects;
}

/// Returns true if Inst may write to memory in a manner that affects V.
/// Returns true if \p Inst may write to memory in a manner that
/// affects V.
bool mayWriteToMemory(SILInstruction *Inst, SILValue V) {
MemoryBehavior B = getMemoryBehavior(Inst, V,
RetainObserveKind::IgnoreRetains);
auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::IgnoreRetains);
return B == MemoryBehavior::MayWrite ||
B == MemoryBehavior::MayReadWrite ||
B == MemoryBehavior::MayHaveSideEffects;
B == MemoryBehavior::MayReadWrite ||
B == MemoryBehavior::MayHaveSideEffects;
}

/// Returns true if Inst may read or write to memory in a manner that affects
/// V.
/// Returns true if \p Inst may read or write to memory in a manner that
/// affects V.
bool mayReadOrWriteMemory(SILInstruction *Inst, SILValue V) {
auto B = getMemoryBehavior(Inst, V, RetainObserveKind::IgnoreRetains);
return B != MemoryBehavior::None;
auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::IgnoreRetains);
return MemoryBehavior::None != B;
}

/// Returns true if Inst may have side effects in a manner that affects V.
bool mayHaveSideEffects(SILInstruction *Inst, SILValue V) {
MemoryBehavior B = getMemoryBehavior(Inst, V);
auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::ObserveRetains);
return B == MemoryBehavior::MayWrite ||
B == MemoryBehavior::MayReadWrite ||
B == MemoryBehavior::MayHaveSideEffects;
B == MemoryBehavior::MayReadWrite ||
B == MemoryBehavior::MayHaveSideEffects;
}

/// Returns true if Inst may have side effects in a manner that affects
/// V. This is independent of whether or not Inst may write to V and is meant
/// to encode notions such as ref count modifications.
bool mayHavePureSideEffects(SILInstruction *Inst, SILValue V) {
return getMemoryBehavior(Inst, V) == MemoryBehavior::MayHaveSideEffects;
auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::ObserveRetains);
return MemoryBehavior::MayHaveSideEffects == B;
}

virtual void invalidate(SILAnalysis::InvalidationKind K) { AliasCache.clear(); }
virtual void invalidate(SILAnalysis::InvalidationKind K) { }

virtual void invalidate(SILFunction *, SILAnalysis::InvalidationKind K) {
invalidate(K);
Expand All @@ -161,7 +164,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
/// Otherwise, return an empty type.
SILType computeTBAAType(SILValue V);

/// Check if V points to a let-variable.
/// Check if \p V points to a let-member.
/// Nobody can write into let members.
bool isLetPointer(SILValue V);

} // end namespace swift
Expand Down
102 changes: 31 additions & 71 deletions lib/SILAnalysis/AliasAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,26 +76,18 @@ static inline bool shouldRunBasicAA() {

#endif

static llvm::cl::opt<bool>
CacheAAResults("cache-aa-results",
llvm::cl::desc("Should AA results be cached"),
llvm::cl::init(true));

//===----------------------------------------------------------------------===//
// Utilities
//===----------------------------------------------------------------------===//

llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &OS,
AliasAnalysis::AliasResult R) {
using AliasResult = AliasAnalysis::AliasResult;

llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &OS, AliasResult R) {
switch (R) {
case AliasAnalysis::AliasResult::NoAlias:
return OS << "NoAlias";
case AliasAnalysis::AliasResult::MayAlias:
return OS << "MayAlias";
case AliasAnalysis::AliasResult::PartialAlias:
return OS << "PartialAlias";
case AliasAnalysis::AliasResult::MustAlias:
return OS << "MustAlias";
case AliasResult::NoAlias: return OS << "NoAlias";
case AliasResult::MayAlias: return OS << "MayAlias";
case AliasResult::PartialAlias: return OS << "PartialAlias";
case AliasResult::MustAlias: return OS << "MustAlias";
}
}

Expand Down Expand Up @@ -265,9 +257,8 @@ static bool aliasUnequalObjects(SILValue O1, SILValue O2) {
/// Uses a bunch of ad-hoc rules to disambiguate a GEP instruction against
/// another pointer. We know that V1 is a GEP, but we don't know anything about
/// V2. O1, O2 are getUnderlyingObject of V1, V2 respectively.
AliasAnalysis::AliasResult
AliasAnalysis::aliasAddressProjection(SILValue V1, SILValue V2,
SILValue O1, SILValue O2) {
AliasResult AliasAnalysis::aliasAddressProjection(SILValue V1, SILValue V2,
SILValue O1, SILValue O2) {

// If V2 is also a gep instruction with a must-alias or not-aliasing base
// pointer, figure out if the indices of the GEPs tell us anything about the
Expand All @@ -278,9 +269,9 @@ AliasAnalysis::aliasAddressProjection(SILValue V1, SILValue V2,
// must alias relation on O1. This ensures that given an alloc_stack and a
// gep from that alloc_stack, we say that they partially alias.
if (isSameValueOrGlobal(O1, V2.stripCasts()))
return AliasAnalysis::AliasResult::PartialAlias;
return AliasResult::PartialAlias;

return AliasAnalysis::AliasResult::MayAlias;
return AliasResult::MayAlias;
}

assert(!Projection::isAddrProjection(O1) &&
Expand All @@ -289,12 +280,12 @@ AliasAnalysis::aliasAddressProjection(SILValue V1, SILValue V2,
"underlying object may not be a projection");

// Do the base pointers alias?
AliasAnalysis::AliasResult BaseAlias = aliasInner(O1, O2);
AliasResult BaseAlias = aliasInner(O1, O2);

// If the underlying objects are not aliased, the projected values are also
// not aliased.
if (BaseAlias == AliasAnalysis::AliasResult::NoAlias)
return AliasAnalysis::AliasResult::NoAlias;
if (BaseAlias == AliasResult::NoAlias)
return AliasResult::NoAlias;

// Let's do alias checking based on projections.
auto V1Path = ProjectionPath::getAddrProjectionPath(O1, V1, true);
Expand All @@ -311,32 +302,32 @@ AliasAnalysis::aliasAddressProjection(SILValue V1, SILValue V2,
// horizon) or enable Projection to represent a cast as a special sort of
// projection.
if (!V1Path || !V2Path)
return AliasAnalysis::AliasResult::MayAlias;
return AliasResult::MayAlias;

auto R = V1Path->computeSubSeqRelation(*V2Path);

// If all of the projections are equal (and they have the same base pointer),
// the two GEPs must be the same.
if (BaseAlias == AliasAnalysis::AliasResult::MustAlias &&
if (BaseAlias == AliasResult::MustAlias &&
R == SubSeqRelation_t::Equal)
return AliasAnalysis::AliasResult::MustAlias;
return AliasResult::MustAlias;

// The two GEPs do not alias if they are accessing different fields, even if
// we don't know the base pointers. Different fields should not overlap.
//
// TODO: Replace this with a check on the computed subseq relation. See the
// TODO in computeSubSeqRelation.
if (V1Path->hasNonEmptySymmetricDifference(V2Path.getValue()))
return AliasAnalysis::AliasResult::NoAlias;
return AliasResult::NoAlias;

// If one of the GEPs is a super path of the other then they partially
// alias. W
if (BaseAlias == AliasAnalysis::AliasResult::MustAlias &&
if (BaseAlias == AliasResult::MustAlias &&
isStrictSubSeqRelation(R))
return AliasAnalysis::AliasResult::PartialAlias;
return AliasResult::PartialAlias;

// We failed to prove anything. Be conservative and return MayAlias.
return AliasAnalysis::AliasResult::MayAlias;
return AliasResult::MayAlias;
}


Expand Down Expand Up @@ -606,19 +597,17 @@ static bool typesMayAlias(SILType T1, SILType T2, SILType TBAA1Ty,

/// The main AA entry point. Performs various analyses on V1, V2 in an attempt
/// to disambiguate the two values.
AliasAnalysis::AliasResult AliasAnalysis::alias(SILValue V1, SILValue V2,
SILType TBAAType1,
SILType TBAAType2) {
auto Result = aliasInner(V1, V2, TBAAType1, TBAAType2);
AliasCache.clear();
return Result;
AliasResult AliasAnalysis::alias(SILValue V1, SILValue V2,
SILType TBAAType1,
SILType TBAAType2) {
return aliasInner(V1, V2, TBAAType1, TBAAType2);
}

/// The main AA entry point. Performs various analyses on V1, V2 in an attempt
/// to disambiguate the two values.
AliasAnalysis::AliasResult AliasAnalysis::aliasInner(SILValue V1, SILValue V2,
SILType TBAAType1,
SILType TBAAType2) {
AliasResult AliasAnalysis::aliasInner(SILValue V1, SILValue V2,
SILType TBAAType1,
SILType TBAAType2) {
#ifndef NDEBUG
// If alias analysis is disabled, always return may alias.
if (!shouldRunAA())
Expand Down Expand Up @@ -648,24 +637,6 @@ AliasAnalysis::AliasResult AliasAnalysis::aliasInner(SILValue V1, SILValue V2,
DEBUG(llvm::dbgs() << " After Cast Stripping V1:" << *V1.getDef());
DEBUG(llvm::dbgs() << " After Cast Stripping V2:" << *V2.getDef());

// Create a key to lookup if we have already computed an alias result for V1,
// V2. Canonicalize our cache keys so that the pointer with the lower address
// is always the first element of the pair. This ensures we do not pollute our
// cache with two entries with the same key, albeit with the key's fields
// swapped.
auto Key = V1 < V2? std::make_pair(V1, V2) : std::make_pair(V2, V1);

// If we find our key in the cache, just return the alias result.
if (CacheAAResults) {
auto Pair = AliasCache.find(Key);
if (Pair != AliasCache.end()) {
DEBUG(llvm::dbgs() << " Found value in the cache: " << Pair->second
<< "\n");

return Pair->second;
}
}

// Ok, we need to actually compute an Alias Analysis result for V1, V2. Begin
// by finding the "base" of V1, V2 by stripping off all casts and GEPs.
SILValue O1 = getUnderlyingObject(V1);
Expand All @@ -676,7 +647,7 @@ AliasAnalysis::AliasResult AliasAnalysis::aliasInner(SILValue V1, SILValue V2,
// If O1 and O2 do not equal, see if we can prove that they can not be the
// same object. If we can, return No Alias.
if (O1 != O2 && aliasUnequalObjects(O1, O2))
return cacheValue(Key, AliasResult::NoAlias);
return AliasResult::NoAlias;

// Ok, either O1, O2 are the same or we could not prove anything based off of
// their inequality. Now we climb up use-def chains and attempt to do tricks
Expand All @@ -694,19 +665,16 @@ AliasAnalysis::AliasResult AliasAnalysis::aliasInner(SILValue V1, SILValue V2,
if (Projection::isAddrProjection(V1)) {
AliasResult Result = aliasAddressProjection(V1, V2, O1, O2);
if (Result != AliasResult::MayAlias)
return cacheValue(Key, Result);
return Result;
}


// We could not prove anything. Be conservative and return that V1, V2 may
// alias.
return AliasResult::MayAlias;
}

/// Check if this is the address of a "let" member.
/// Nobody can write into let members.
bool swift::isLetPointer(SILValue V) {
// Traverse the "access" path for V and check that starts with "let"
// Traverse the "access" path for V and check that it starts with "let"
// and everything along this path is a value-type (i.e. struct or tuple).

// Is this an address of a "let" class member?
Expand All @@ -733,14 +701,6 @@ bool swift::isLetPointer(SILValue V) {
return false;
}

AliasAnalysis::AliasResult AliasAnalysis::cacheValue(AliasCacheKey Key,
AliasResult Result) {
if (!CacheAAResults)
return Result;
DEBUG(llvm::dbgs() << "Caching Alias Result: " << Result << "\n" << Key.first
<< Key.second << "\n");
return AliasCache[Key] = Result;
}

void AliasAnalysis::initialize(SILPassManager *PM) {
SEA = PM->getAnalysis<SideEffectAnalysis>();
Expand Down
Loading

0 comments on commit cdacda9

Please sign in to comment.