From f2009dbddbcaab09bc6a0daa25780dc978708eb2 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 24 Feb 2003 20:37:56 +0000 Subject: [PATCH] This is a substantial rewrite of the AliasSetTracker class which now uses a union-find based algorithm, is significantly faster, and is more general. It will also scale to handle call instructions correctly, which is a nice added bonus. This includes a new pass -print-alias-sets which can be used to show how alias sets are formed for a particular analysis. llvm-svn: 5619 --- include/llvm/Analysis/AliasSetTracker.h | 238 ++++++++++++++--- lib/Analysis/AliasSetTracker.cpp | 331 ++++++++++++++++-------- 2 files changed, 416 insertions(+), 153 deletions(-) diff --git a/include/llvm/Analysis/AliasSetTracker.h b/include/llvm/Analysis/AliasSetTracker.h index b7de04ce27f..ad71d99ca27 100644 --- a/include/llvm/Analysis/AliasSetTracker.h +++ b/include/llvm/Analysis/AliasSetTracker.h @@ -10,80 +10,194 @@ #ifndef LLVM_ANALYSIS_ALIASSETTRACKER_H #define LLVM_ANALYSIS_ALIASSETTRACKER_H -#include +#include "llvm/Support/CallSite.h" +#include "Support/iterator" +#include "Support/hash_map" +#include "Support/ilist" class AliasAnalysis; class LoadInst; class StoreInst; -class CallInst; -class InvokeInst; -class Value; class AliasSetTracker; +class AliasSet; class AliasSet { friend class AliasSetTracker; - std::vector Loads; - std::vector Stores; - std::vector Calls; - std::vector Invokes; -public: + + struct PointerRec; + typedef std::pair HashNodePair; + + class PointerRec { + HashNodePair *NextInList; + AliasSet *AS; + public: + PointerRec() : NextInList(0), AS(0) {} + + HashNodePair *getNext() const { return NextInList; } + bool hasAliasSet() const { return AS != 0; } + + AliasSet *getAliasSet(AliasSetTracker &AST) { + assert(AS && "No AliasSet yet!"); + if (AS->Forward) { + AliasSet *OldAS = AS; + AS = OldAS->getForwardedTarget(AST); + if (--OldAS->RefCount == 0) + OldAS->removeFromTracker(AST); + AS->RefCount++; + } + return AS; + } + + void setAliasSet(AliasSet *as) { + assert(AS == 0 && "Already have an alias set!"); + AS = as; + } + void setTail(HashNodePair *T) { + assert(NextInList == 0 && "Already have tail!"); + NextInList = T; + } + }; + + HashNodePair *PtrListHead, *PtrListTail; // Singly linked list of nodes + AliasSet *Forward; // Forwarding pointer + AliasSet *Next, *Prev; // Doubly linked list of AliasSets + + std::vector CallSites; // All calls & invokes in this node + + // RefCount - Number of nodes pointing to this AliasSet plus the number of + // AliasSets forwarding to it. + unsigned RefCount : 29; + /// AccessType - Keep track of whether this alias set merely refers to the /// locations of memory, whether it modifies the memory, or whether it does - /// both. The lattice goes from "None" (alias set not present) to either Refs - /// or Mods, then to ModRef as neccesary. + /// both. The lattice goes from "NoModRef" to either Refs or Mods, then to + /// ModRef as neccesary. /// enum AccessType { - Refs, Mods, ModRef + NoModRef = 0, Refs = 1, // Ref = bit 1 + Mods = 2, ModRef = 3 // Mod = bit 2 }; + unsigned AccessTy : 2; /// AliasType - Keep track the relationships between the pointers in the set. /// Lattice goes from MustAlias to MayAlias. /// enum AliasType { - MustAlias, MayAlias + MustAlias = 0, MayAlias = 1 }; -private: - enum AccessType AccessTy; - enum AliasType AliasTy; + unsigned AliasTy : 1; + + /// Define an iterator for alias sets... this is just a forward iterator. + class iterator : public forward_iterator { + HashNodePair *CurNode; + public: + iterator(HashNodePair *CN = 0) : CurNode(CN) {} + + bool operator==(const iterator& x) const { + return CurNode == x.CurNode; + } + bool operator!=(const iterator& x) const { return !operator==(x); } + + const iterator &operator=(const iterator &I) { + CurNode = I.CurNode; + return *this; + } + + value_type operator*() const { + assert(CurNode && "Dereferencing AliasSet.end()!"); + return CurNode->first; + } + value_type operator->() const { return operator*(); } + + iterator& operator++() { // Preincrement + assert(CurNode && "Advancing past AliasSet.end()!"); + CurNode = CurNode->second.getNext(); + return *this; + } + iterator operator++(int) { // Postincrement + iterator tmp = *this; ++*this; return tmp; + } + }; + + friend class ilist_traits; + AliasSet *getPrev() const { return Prev; } + AliasSet *getNext() const { return Next; } + void setPrev(AliasSet *P) { Prev = P; } + void setNext(AliasSet *N) { Next = N; } + public: /// Accessors... - enum AccessType getAccessType() const { return AccessTy; } - enum AliasType getAliasType() const { return AliasTy; } - - // TODO: in the future, add a fixed size (4? 2?) cache of pointers that we - // know are in the alias set, to cut down time answering "pointeraliasesset" - // queries. - - /// pointerAliasesSet - Return true if the specified pointer "may" (or must) - /// alias one of the members in the set. - /// - bool pointerAliasesSet(const Value *Ptr, AliasAnalysis &AA) const; + bool isRef() const { return AccessTy & Refs; } + bool isMod() const { return AccessTy & Mods; } + bool isMustAlias() const { return AliasTy == MustAlias; } + bool isMayAlias() const { return AliasTy == MayAlias; } /// mergeSetIn - Merge the specified alias set into this alias set... /// - void mergeSetIn(const AliasSet &AS); + void mergeSetIn(AliasSet &AS); - const std::vector &getLoads() const { return Loads; } - const std::vector &getStores() const { return Stores; } - const std::vector &getCalls() const { return Calls; } - const std::vector &getInvokes() const { return Invokes; } + // Alias Set iteration - Allow access to all of the pointer which are part of + // this alias set... + iterator begin() const { return iterator(PtrListHead); } + iterator end() const { return iterator(); } + + void print(std::ostream &OS) const; + void dump() const; private: - AliasSet() : AliasTy(MustAlias) {} // Can only be created by AliasSetTracker - void updateAccessType(); - Value *getSomePointer() const; + // Can only be created by AliasSetTracker + AliasSet() : PtrListHead(0), PtrListTail(0), Forward(0), RefCount(0), + AccessTy(NoModRef), AliasTy(MustAlias) { + } + Value *getSomePointer() const { + return PtrListHead ? PtrListHead->first : 0; + } + + /// getForwardedTarget - Return the real alias set this represents. If this + /// has been merged with another set and is forwarding, return the ultimate + /// destination set. This also implements the union-find collapsing as well. + AliasSet *getForwardedTarget(AliasSetTracker &AST) { + if (!Forward) return this; + + AliasSet *Dest = Forward->getForwardedTarget(AST); + if (Dest != Forward) { + Dest->RefCount++; + if (--Forward->RefCount == 0) + Forward->removeFromTracker(AST); + Forward = Dest; + } + return Dest; + } + + void removeFromTracker(AliasSetTracker &AST); + + void addPointer(AliasSetTracker &AST, HashNodePair &Entry); + void addCallSite(CallSite CS); + + /// aliasesPointer - Return true if the specified pointer "may" (or must) + /// alias one of the members in the set. + /// + bool aliasesPointer(const Value *Ptr, AliasAnalysis &AA) const; + bool aliasesCallSite(CallSite CS, AliasAnalysis &AA) const; }; +inline std::ostream& operator<<(std::ostream &OS, const AliasSet &AS) { + AS.print(OS); + return OS; +} + class AliasSetTracker { AliasAnalysis &AA; - std::vector AliasSets; + ilist AliasSets; + + // Map from pointers to their node + hash_map PointerMap; public: /// AliasSetTracker ctor - Create an empty collection of AliasSets, and use /// the specified alias analysis object to disambiguate load and store /// addresses. AliasSetTracker(AliasAnalysis &aa) : AA(aa) {} - /// add methods - These methods are used to add different types of /// instructions to the alias sets. Adding a new instruction can result in /// one of three actions happening: @@ -95,15 +209,57 @@ public: /// void add(LoadInst *LI); void add(StoreInst *SI); - void add(CallInst *CI); - void add(InvokeInst *II); + void add(CallSite CS); // Call/Invoke instructions + void add(CallInst *CI) { add(CallSite(CI)); } + void add(InvokeInst *II) { add(CallSite(II)); } + void add(Instruction *I); // Dispatch to one of the other add methods... /// getAliasSets - Return the alias sets that are active. - const std::vector &getAliasSets() const { return AliasSets; } + const ilist &getAliasSets() const { return AliasSets; } + + /// getAliasSetForPointer - Return the alias set that the specified pointer + /// lives in... + AliasSet &getAliasSetForPointer(Value *P); + + /// getAliasAnalysis - Return the underlying alias analysis object used by + /// this tracker. + AliasAnalysis &getAliasAnalysis() const { return AA; } + + typedef ilist::iterator iterator; + typedef ilist::const_iterator const_iterator; + + const_iterator begin() const { return AliasSets.begin(); } + const_iterator end() const { return AliasSets.end(); } + + iterator begin() { return AliasSets.begin(); } + iterator end() { return AliasSets.end(); } + + void print(std::ostream &OS) const; + void dump() const; private: + friend class AliasSet; + void removeAliasSet(AliasSet *AS); + + AliasSet::HashNodePair &getEntryFor(Value *V) { + // Standard operator[], except that it returns the whole pair, not just + // ->second. + return *PointerMap.insert(AliasSet::HashNodePair(V, + AliasSet::PointerRec())).first; + } + + void addPointer(Value *P, AliasSet::AccessType E) { + AliasSet &AS = getAliasSetForPointer(P); + AS.AccessTy |= E; + } AliasSet *findAliasSetForPointer(const Value *Ptr); - void mergeAllSets(); + + AliasSet *findAliasSetForCallSite(CallSite CS); }; +inline std::ostream& operator<<(std::ostream &OS, const AliasSetTracker &AST) { + AST.print(OS); + return OS; +} + #endif diff --git a/lib/Analysis/AliasSetTracker.cpp b/lib/Analysis/AliasSetTracker.cpp index 1413660d92e..db704b654a2 100644 --- a/lib/Analysis/AliasSetTracker.cpp +++ b/lib/Analysis/AliasSetTracker.cpp @@ -9,164 +9,271 @@ #include "llvm/iMemory.h" #include "llvm/iOther.h" #include "llvm/iTerminators.h" - -/// updateAccessTypes - Depending on what type of accesses are in this set, -/// decide whether the set contains just references, just modifications, or a -/// mix. -/// -void AliasSet::updateAccessType() { - if (!Calls.empty() || !Invokes.empty()) { - AccessTy = ModRef; - } else if (!Loads.empty()) { - if (Stores.empty()) - AccessTy = Refs; - else - AccessTy = ModRef; - } else { - AccessTy = Mods; - } -} +#include "llvm/Pass.h" +#include "llvm/Assembly/Writer.h" +#include "llvm/Support/InstIterator.h" /// mergeSetIn - Merge the specified alias set into this alias set... /// -void AliasSet::mergeSetIn(const AliasSet &AS) { - // Merge instruction sets... - Loads.insert( Loads.end(), AS.Loads.begin() , AS.Loads.end()); - Stores.insert( Stores.end(), AS.Stores.begin() , AS.Stores.end()); - Calls.insert( Calls.end(), AS.Calls.begin() , AS.Calls.end()); - Invokes.insert(Invokes.end(), AS.Invokes.begin(), AS.Invokes.end()); +void AliasSet::mergeSetIn(AliasSet &AS) { + assert(!AS.Forward && "Alias set is already forwarding!"); + assert(!Forward && "This set is a forwarding set!!"); // Update the alias and access types of this set... - if (AS.getAliasType() == MayAlias) - AliasTy = MayAlias; - updateAccessType(); + AccessTy |= AS.AccessTy; + AliasTy |= AS.AliasTy; + + if (CallSites.empty()) { // Merge call sites... + if (!AS.CallSites.empty()) + std::swap(CallSites, AS.CallSites); + } else if (!AS.CallSites.empty()) { + CallSites.insert(CallSites.end(), AS.CallSites.begin(), AS.CallSites.end()); + AS.CallSites.clear(); + } + + // FIXME: If AS's refcount is zero, nuke it now... + assert(RefCount != 0); + + AS.Forward = this; // Forward across AS now... + RefCount++; // AS is now pointing to us... + + // Merge the list of constituent pointers... + PtrListTail->second.setTail(AS.PtrListHead); + PtrListTail = AS.PtrListTail; + AS.PtrListHead = AS.PtrListTail = 0; } -/// pointerAliasesSet - Return true if the specified pointer "may" (or must) +void AliasSetTracker::removeAliasSet(AliasSet *AS) { + AliasSets.erase(AS); +} + +void AliasSet::removeFromTracker(AliasSetTracker &AST) { + assert(RefCount == 0 && "Cannot remove non-dead alias set from tracker!"); + AST.removeAliasSet(this); +} + +void AliasSet::addPointer(AliasSetTracker &AST, HashNodePair &Entry){ + assert(!Entry.second.hasAliasSet() && "Entry already in set!"); + + AliasAnalysis &AA = AST.getAliasAnalysis(); + + if (isMustAlias()) // Check to see if we have to downgrade to _may_ alias + if (Value *V = getSomePointer()) + if (AA.alias(V, Entry.first) == AliasAnalysis::MayAlias) + AliasTy = MayAlias; + + Entry.second.setAliasSet(this); + + // Add it to the end of the list... + if (PtrListTail) + PtrListTail->second.setTail(&Entry); + else + PtrListHead = &Entry; + PtrListTail = &Entry; + RefCount++; // Entry points to alias set... +} + +void AliasSet::addCallSite(CallSite CS) { + CallSites.push_back(CS); + AliasTy = MayAlias; // FIXME: Too conservative +} + +/// aliasesPointer - Return true if the specified pointer "may" (or must) /// alias one of the members in the set. /// -bool AliasSet::pointerAliasesSet(const Value *Ptr, AliasAnalysis &AA) const { - if (!Calls.empty() || !Invokes.empty()) +bool AliasSet::aliasesPointer(const Value *Ptr, AliasAnalysis &AA) const { + if (AliasTy == MustAlias) { + assert(CallSites.empty() && "Illegal must alias set!"); + + // If this is a set of MustAliases, only check to see if the pointer aliases + // SOME value in the set... + Value *SomePtr = getSomePointer(); + assert(SomePtr && "Empty must-alias set??"); + return AA.alias(SomePtr, Ptr); + } + + // If this is a may-alias set, we have to check all of the pointers in the set + // to be sure it doesn't alias the set... + for (iterator I = begin(), E = end(); I != E; ++I) + if (AA.alias(Ptr, *I)) + return true; + + // Check the call sites list and invoke list... + if (!CallSites.empty()) + // FIXME: this is pessimistic! return true; - for (unsigned i = 0, e = Loads.size(); i != e; ++i) - if (AA.alias(Ptr, Loads[i]->getOperand(0))) - return true; - for (unsigned i = 0, e = Stores.size(); i != e; ++i) - if (AA.alias(Ptr, Stores[i]->getOperand(1))) - return true; + return false; } -/// getSomePointer - This method may only be called when the AliasType of the -/// set is MustAlias. This is used to return any old pointer (which must alias -/// all other pointers in the set) so that the caller can decide whether to turn -/// this set into a may alias set or not. -/// -Value *AliasSet::getSomePointer() const { - assert(getAliasType() == MustAlias && - "Cannot call getSomePointer on a 'MayAlias' set!"); - assert(Calls.empty() && Invokes.empty() && "Call/invokes mean may alias!"); - - if (!Loads.empty()) - return Loads[0]->getOperand(0); - assert(!Stores.empty() && "There are no instructions in this set!"); - return Stores[0]->getOperand(1); +bool AliasSet::aliasesCallSite(CallSite CS, AliasAnalysis &AA) const { + // FIXME: Too conservative! + return true; } - /// findAliasSetForPointer - Given a pointer, find the one alias set to put the /// instruction referring to the pointer into. If there are multiple alias sets /// that may alias the pointer, merge them together and return the unified set. /// AliasSet *AliasSetTracker::findAliasSetForPointer(const Value *Ptr) { AliasSet *FoundSet = 0; - for (unsigned i = 0; i != AliasSets.size(); ++i) { - if (AliasSets[i].pointerAliasesSet(Ptr, AA)) { + for (iterator I = begin(), E = end(); I != E; ++I) + if (I->aliasesPointer(Ptr, AA)) { if (FoundSet == 0) { // If this is the first alias set ptr can go into... - FoundSet = &AliasSets[i]; // Remember it. + FoundSet = I; // Remember it. } else { // Otherwise, we must merge the sets... - FoundSet->mergeSetIn(AliasSets[i]); // Merge in contents... - AliasSets.erase(AliasSets.begin()+i); // Remove the set... - --i; // Don't skip the next set + FoundSet->mergeSetIn(*I); // Merge in contents... + } + } + + return FoundSet; +} + +AliasSet *AliasSetTracker::findAliasSetForCallSite(CallSite CS) { + AliasSet *FoundSet = 0; + for (iterator I = begin(), E = end(); I != E; ++I) + if (I->aliasesCallSite(CS, AA)) { + if (FoundSet == 0) { // If this is the first alias set ptr can go into... + FoundSet = I; // Remember it. + } else { // Otherwise, we must merge the sets... + FoundSet->mergeSetIn(*I); // Merge in contents... } } - } return FoundSet; } -void AliasSetTracker::add(LoadInst *LI) { - Value *Pointer = LI->getOperand(0); - // Check to see if the loaded pointer aliases any sets... - AliasSet *AS = findAliasSetForPointer(Pointer); - if (AS) { - AS->Loads.push_back(LI); - // Check to see if we need to change this into a MayAlias set now... - if (AS->getAliasType() == AliasSet::MustAlias) - if (AA.alias(AS->getSomePointer(), Pointer) != AliasAnalysis::MustAlias) - AS->AliasTy = AliasSet::MayAlias; - AS->updateAccessType(); + +/// getAliasSetForPointer - Return the alias set that the specified pointer +/// lives in... +AliasSet &AliasSetTracker::getAliasSetForPointer(Value *Pointer) { + AliasSet::HashNodePair &Entry = getEntryFor(Pointer); + + // Check to see if the pointer is already known... + if (Entry.second.hasAliasSet()) { + // Return the set! + return *Entry.second.getAliasSet(*this)->getForwardedTarget(*this); + } else if (AliasSet *AS = findAliasSetForPointer(Pointer)) { + // Add it to the alias set it aliases... + AS->addPointer(*this, Entry); + return *AS; } else { - // Otherwise create a new alias set to hold the load... + // Otherwise create a new alias set to hold the loaded pointer... AliasSets.push_back(AliasSet()); - AliasSets.back().Loads.push_back(LI); - AliasSets.back().AccessTy = AliasSet::Refs; + AliasSets.back().addPointer(*this, Entry); + return AliasSets.back(); } } +void AliasSetTracker::add(LoadInst *LI) { + addPointer(LI->getOperand(0), AliasSet::Refs); +} + void AliasSetTracker::add(StoreInst *SI) { - Value *Pointer = SI->getOperand(1); + addPointer(SI->getOperand(1), AliasSet::Mods); +} - // Check to see if the loaded pointer aliases any sets... - AliasSet *AS = findAliasSetForPointer(Pointer); - if (AS) { - AS->Stores.push_back(SI); - // Check to see if we need to change this into a MayAlias set now... - if (AS->getAliasType() == AliasSet::MustAlias) - if (AA.alias(AS->getSomePointer(), Pointer) != AliasAnalysis::MustAlias) - AS->AliasTy = AliasSet::MayAlias; - AS->updateAccessType(); - } else { - // Otherwise create a new alias set to hold the load... +void AliasSetTracker::add(CallSite CS) { + AliasSet *AS = findAliasSetForCallSite(CS); + if (!AS) { AliasSets.push_back(AliasSet()); - AliasSets.back().Stores.push_back(SI); - AliasSets.back().AccessTy = AliasSet::Mods; + AS = &AliasSets.back(); } + AS->addCallSite(CS); } - -void AliasSetTracker::mergeAllSets() { - if (AliasSets.size() < 2) return; // Noop - - // Merge all of the sets into set #0 - for (unsigned i = 1, e = AliasSets.size(); i != e; ++i) - AliasSets[0].mergeSetIn(AliasSets[i]); - - // Delete extraneous sets... - AliasSets.erase(AliasSets.begin()+1, AliasSets.end()); +void AliasSetTracker::add(Instruction *I) { + // Dispatch to one of the other add methods... + if (LoadInst *LI = dyn_cast(I)) + add(LI); + else if (StoreInst *SI = dyn_cast(I)) + add(SI); + else if (CallInst *CI = dyn_cast(I)) + add(CI); + else if (InvokeInst *II = dyn_cast(I)) + add(II); } -void AliasSetTracker::add(CallInst *CI) { - if (!AliasSets.empty()) { - mergeAllSets(); - } else { - AliasSets.push_back(AliasSet()); +//===----------------------------------------------------------------------===// +// AliasSet/AliasSetTracker Printing Support +//===----------------------------------------------------------------------===// + +void AliasSet::print(std::ostream &OS) const { + OS << " AliasSet[" << (void*)this << "," << RefCount << "] "; + OS << (AliasTy == MustAlias ? "must" : "may ") << " alias, "; + switch (AccessTy) { + case NoModRef: OS << "No access "; break; + case Refs : OS << "Ref "; break; + case Mods : OS << "Mod "; break; + case ModRef : OS << "Mod/Ref "; break; + default: assert(0 && "Bad value for AccessTy!"); } - AliasSets[0].AccessTy = AliasSet::ModRef; - AliasSets[0].AliasTy = AliasSet::MayAlias; - AliasSets[0].Calls.push_back(CI); + if (Forward) + OS << " forwarding to " << (void*)Forward; + + + if (begin() != end()) { + OS << "Pointers: "; + for (iterator I = begin(), E = end(); I != E; ++I) { + if (I != begin()) OS << ", "; + WriteAsOperand(OS, *I); + } + } + if (!CallSites.empty()) { + OS << "\n " << CallSites.size() << " Call Sites: "; + for (unsigned i = 0, e = CallSites.size(); i != e; ++i) { + if (i) OS << ", "; + WriteAsOperand(OS, CallSites[i].getCalledValue()); + } + } + OS << "\n"; } -void AliasSetTracker::add(InvokeInst *II) { - if (!AliasSets.empty()) { - mergeAllSets(); - } else { - AliasSets.push_back(AliasSet()); - } - AliasSets[0].AccessTy = AliasSet::ModRef; - AliasSets[0].AliasTy = AliasSet::MayAlias; - AliasSets[0].Invokes.push_back(II); +void AliasSetTracker::print(std::ostream &OS) const { + OS << "Alias Set Tracker: " << AliasSets.size() << " alias sets for " + << PointerMap.size() << " pointer values.\n"; + for (const_iterator I = begin(), E = end(); I != E; ++I) + I->print(OS); + OS << "\n"; +} + +void AliasSet::dump() const { print (std::cerr); } +void AliasSetTracker::dump() const { print(std::cerr); } + + +//===----------------------------------------------------------------------===// +// AliasSetPrinter Pass +//===----------------------------------------------------------------------===// + +namespace { + class AliasSetPrinter : public FunctionPass { + AliasSetTracker *Tracker; + public: + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); + AU.addRequired(); + } + + virtual bool runOnFunction(Function &F) { + Tracker = new AliasSetTracker(getAnalysis()); + + for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) + Tracker->add(*I); + return false; + } + + /// print - Convert to human readable form + virtual void print(std::ostream &OS) const { + Tracker->print(OS); + } + + virtual void releaseMemory() { + delete Tracker; + } + }; + RegisterPass X("print-alias-sets", "Alias Set Printer", + PassInfo::Analysis | PassInfo::Optimization); }