diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h index f70fd02ca573cc..e2065b496e2105 100644 --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -103,7 +103,9 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetOperations.h" #include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator.h" #include "llvm/Analysis/AssumeBundleQueries.h" #include "llvm/Analysis/CFG.h" @@ -320,6 +322,10 @@ inline bool operator==(const RangeTy &A, const RangeTy &B) { return A.Offset == B.Offset && A.Size == B.Size; } +inline bool operator<(const RangeTy &A, const RangeTy &B) { + return A.Offset < B.Offset; +} + inline bool operator!=(const RangeTy &A, const RangeTy &B) { return !(A == B); } /// Return the initial value of \p Obj with type \p Ty if that is a constant. @@ -5785,6 +5791,139 @@ struct AAPointerInfo : public AbstractAttribute { AK_MUST_READ_WRITE = AK_MUST | AK_R | AK_W, }; + /// A helper containing a list of offsets computed for a Use. Ideally this + /// list should be strictly ascending, but we ensure that only when we + /// actually translate the list of offsets to a RangeList. + struct OffsetInfo { + using VecTy = SmallVector; + // A map to store depth 1 predecessors per offset. + using OriginsTy = SmallVector>; + using const_iterator = VecTy::const_iterator; + OriginsTy Origins; + VecTy Ranges; + + const_iterator begin() const { return Ranges.begin(); } + const_iterator end() const { return Ranges.end(); } + + bool operator==(const OffsetInfo &RHS) const { + return Ranges == RHS.Ranges && Origins == RHS.Origins; + } + + bool operator!=(const OffsetInfo &RHS) const { return !(*this == RHS); } + + // Insert a new Range and Origin + void insert(AA::RangeTy Range, Value &V) { + auto *It = std::find(Ranges.begin(), Ranges.end(), Range); + // Offset exists in Offsets map + if (It != Ranges.end()) { + size_t Index = It - Ranges.begin(); + if (Index < Origins.size()) + Origins[Index].insert(&V); + } else { + Ranges.push_back(Range); + Origins.emplace_back(); + Origins.back().insert(&V); + } + } + + // Set the size of the offset for all ranges. + void setSizeAll(uint64_t Size) { + for (auto &Range : Ranges) + Range.Size = Size; + } + + // Helper function to get just the offsets from Ranges. + void getOnlyOffsets(SmallVector &Offsets) { + for (auto &Range : Ranges) + Offsets.push_back(Range.Offset); + // ensure unique + sort(Offsets.begin(), Offsets.end()); + Offsets.erase(std::unique(Offsets.begin(), Offsets.end()), Offsets.end()); + } + + bool isUnassigned() const { return Ranges.empty(); } + + bool isUnknown() const { + if (isUnassigned()) + return false; + if (Ranges.size() == 1) + return Ranges.front().Offset == AA::RangeTy::Unknown; + return false; + } + + void setUnknown(Value &V) { + Ranges.clear(); + Origins.clear(); + insert(AA::RangeTy{AA::RangeTy::Unknown, AA::RangeTy::Unknown}, V); + } + + // Increment all ranges by Inc. + // Add an origin V to all offsets. + void addToAll(int64_t Inc, Value &V) { + for (auto &Range : Ranges) + Range.Offset += Inc; + + if (!Origins.empty()) { + for (auto &Origin : Origins) + Origin.insert(&V); + } else { + for (size_t Index = 0; Index < Ranges.size(); Index++) { + Origins.emplace_back(); + Origins[Index].insert(&V); + } + } + } + + // Increment all ranges by Inc. + void addToAll(int64_t Inc) { + for (auto &Range : Ranges) + Range.Offset += Inc; + } + + /// Copy offsets from \p R into the current list. + /// + /// Ideally all lists should be strictly ascending, but we defer that to the + /// actual use of the list. So we just blindly append here. + void merge(const OffsetInfo &R) { + Ranges.append(R.Ranges); + // ensure elements are unique. + sort(Ranges.begin(), Ranges.end()); + Ranges.erase(std::unique(Ranges.begin(), Ranges.end()), Ranges.end()); + + OriginsTy ToBeMergeOrigins = R.Origins; + for (auto &Origin : ToBeMergeOrigins) + Origins.emplace_back(Origin); + } + + // Merge two OffsetInfo structs. + // takes an additional origin argument + // and adds it to the corresponding offset in the + // origins map. + void mergeWithOffset(const OffsetInfo &R, Value &CurPtr) { + Ranges.append(R.Ranges); + // ensure elements are unique. + sort(Ranges.begin(), Ranges.end()); + Ranges.erase(std::unique(Ranges.begin(), Ranges.end()), Ranges.end()); + auto &ROffsets = R.Ranges; + for (auto Offset : ROffsets) { + auto *It = std::find(Ranges.begin(), Ranges.end(), Offset); + if (It == Ranges.end()) + continue; + size_t Index = It - Ranges.begin(); + if (Index >= Origins.size()) { + Origins.emplace_back(); + Origins.back().insert(&CurPtr); + } else { + Origins[Index].insert(&CurPtr); + } + } + } + }; + + using OffsetInfoMapTy = DenseMap; + using AccessPathTy = SmallVector; + using AccessPathSetTy = SmallPtrSet; + /// A container for a list of ranges. struct RangeList { // The set of ranges rarely contains more than one element, and is unlikely @@ -5939,15 +6078,17 @@ struct AAPointerInfo : public AbstractAttribute { /// An access description. struct Access { Access(Instruction *I, int64_t Offset, int64_t Size, - std::optional Content, AccessKind Kind, Type *Ty) + std::optional Content, AccessKind Kind, Type *Ty, + AccessPathSetTy *AccessPaths) : LocalI(I), RemoteI(I), Content(Content), Ranges(Offset, Size), - Kind(Kind), Ty(Ty) { + Kind(Kind), Ty(Ty), AccessPaths(AccessPaths) { verify(); } Access(Instruction *LocalI, Instruction *RemoteI, const RangeList &Ranges, - std::optional Content, AccessKind K, Type *Ty) + std::optional Content, AccessKind K, Type *Ty, + AccessPathSetTy *AccessPaths) : LocalI(LocalI), RemoteI(RemoteI), Content(Content), Ranges(Ranges), - Kind(K), Ty(Ty) { + Kind(K), Ty(Ty), AccessPaths(AccessPaths) { if (Ranges.size() > 1) { Kind = AccessKind(Kind | AK_MAY); Kind = AccessKind(Kind & ~AK_MUST); @@ -5956,9 +6097,9 @@ struct AAPointerInfo : public AbstractAttribute { } Access(Instruction *LocalI, Instruction *RemoteI, int64_t Offset, int64_t Size, std::optional Content, AccessKind Kind, - Type *Ty) + Type *Ty, AccessPathSetTy *AccessPaths) : LocalI(LocalI), RemoteI(RemoteI), Content(Content), - Ranges(Offset, Size), Kind(Kind), Ty(Ty) { + Ranges(Offset, Size), Kind(Kind), Ty(Ty), AccessPaths(AccessPaths) { verify(); } Access(const Access &Other) = default; @@ -5966,7 +6107,8 @@ struct AAPointerInfo : public AbstractAttribute { Access &operator=(const Access &Other) = default; bool operator==(const Access &R) const { return LocalI == R.LocalI && RemoteI == R.RemoteI && Ranges == R.Ranges && - Content == R.Content && Kind == R.Kind; + Content == R.Content && Kind == R.Kind && + checkAccessPathsAreSame(R.AccessPaths); } bool operator!=(const Access &R) const { return !(*this == R); } @@ -6078,11 +6220,53 @@ struct AAPointerInfo : public AbstractAttribute { } } + // Merge two access paths into one. + void mergeAccessPaths(const AccessPathSetTy *AccessPathsNew) const { + for (auto *Path : *AccessPathsNew) + if (!existsChain(Path)) + AccessPaths->insert(Path); + } + + // Check if the given access paths are same. + bool checkAccessPathsAreSame(const AccessPathSetTy *AccessPathsR) const { + bool IsSame = true; + if (AccessPaths->size() != AccessPathsR->size()) + return false; + + for (auto *Path : *AccessPathsR) { + if (!existsChain(Path)) + IsSame = false; + } + return IsSame; + } + + // Check if the chain exists in the AccessPathsSet. + bool existsChain(const AccessPathTy *NewPath) const { + for (auto *OldPath : *AccessPaths) + if (*OldPath == *NewPath) + return true; + + return false; + } + + void dumpAccessPaths(raw_ostream &O) const { + O << "Print all access paths found:" + << "\n"; + for (auto *It : *AccessPaths) { + O << "Backtrack a unique access path:\n"; + for (Value *Ins : *It) { + O << *Ins << "\n"; + } + } + } + + const AccessPathSetTy *getAccessChain() const { return AccessPaths; } const RangeList &getRanges() const { return Ranges; } using const_iterator = RangeList::const_iterator; const_iterator begin() const { return Ranges.begin(); } const_iterator end() const { return Ranges.end(); } + size_t size() const { return Ranges.size(); } private: /// The instruction responsible for the access with respect to the local @@ -6105,6 +6289,10 @@ struct AAPointerInfo : public AbstractAttribute { /// The type of the content, thus the type read/written, can be null if not /// available. Type *Ty; + + /// The full chain of instructions that participate in the Access. + /// There may be more than one access chain. + AccessPathSetTy *AccessPaths; }; /// Create an abstract attribute view for the position \p IRP. diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp index 2816a85743faad..ca7b6e555111c1 100644 --- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -11,6 +11,8 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/IR/GlobalVariable.h" #include "llvm/Transforms/IPO/Attributor.h" #include "llvm/ADT/APInt.h" @@ -850,8 +852,13 @@ struct AA::PointerInfo::State : public AbstractState { ChangeStatus addAccess(Attributor &A, const AAPointerInfo::RangeList &Ranges, Instruction &I, std::optional Content, AAPointerInfo::AccessKind Kind, Type *Ty, + AAPointerInfo::OffsetInfoMapTy &OffsetInfoMap, Instruction *RemoteI = nullptr); + AAPointerInfo::AccessPathSetTy * + findAllAccessPaths(AAPointerInfo::OffsetInfoMapTy &OffsetInfoMap, + Instruction *LocalI); + AAPointerInfo::const_bin_iterator begin() const { return OffsetBins.begin(); } AAPointerInfo::const_bin_iterator end() const { return OffsetBins.end(); } int64_t numOffsetBins() const { return OffsetBins.size(); } @@ -926,10 +933,95 @@ struct AA::PointerInfo::State : public AbstractState { BooleanState BS; }; +AAPointerInfo::AccessPathSetTy *AA::PointerInfo::State::findAllAccessPaths( + AAPointerInfo::OffsetInfoMapTy &OffsetInfoMap, Instruction *LocalI) { + AAPointerInfo::AccessPathSetTy *AccessPathsSet = + new AAPointerInfo::AccessPathSetTy(); + + // Store the instruction and its storage (i.e, which path it belongs to) + // on the stack. + // We also store the visited map on the stack. + // Since we want to find new paths, we want to make sure an instruction is + // not visited twice on the same path. However, we can visit the same + // instruction more that once if it exists on different paths. + using VisitedTy = SmallPtrSet; + using StackElementTy = + std::tuple; + + SmallVector Stack; + + // Populate the stack with elements. + for (auto *It = LocalI->op_begin(); It != LocalI->op_end(); It++) { + Value *V = cast(It); + if (!OffsetInfoMap.contains(V)) + continue; + + SmallPtrSet LocalVisitedMap; + AAPointerInfo::AccessPathTy *NewPath = new AAPointerInfo::AccessPathTy(); + AccessPathsSet->insert(NewPath); + NewPath->push_back(LocalI); + Stack.push_back(std::make_tuple(V, NewPath, LocalVisitedMap)); + } + + while (!Stack.empty()) { + auto Entry = Stack.pop_back_val(); + Value *Top = std::get<0>(Entry); + AAPointerInfo::AccessPathTy *CurrentChain = std::get<1>(Entry); + auto &Visited = std::get<2>(Entry); + + if (!OffsetInfoMap.contains(Top)) + continue; + + if (!Visited.insert(Top).second) + continue; + + CurrentChain->push_back(Top); + auto OI = OffsetInfoMap.lookup(Top); + auto &Origins = OI.Origins; + + SmallPtrSet Successors; + for (auto &Origin : Origins) { + for (auto *Val : Origin) { + // Since we store depth 1 predecessors in our Origins map + // We can be sure that we hit termination condition if the + // Successor is the current instruction. + if (Val != Top) + Successors.insert(Val); + } + } + + if (Successors.empty()) + continue; + + // Create new paths to be forked + SmallVector NewPaths; + NewPaths.push_back(CurrentChain); + for (size_t Index = 1; Index < Successors.size(); Index++) { + AAPointerInfo::AccessPathTy *NewPath = new AAPointerInfo::AccessPathTy( + CurrentChain->begin(), CurrentChain->end()); + NewPaths.push_back(NewPath); + } + + int Index = 0; + // Traverse the successors + for (auto *Successor : Successors) { + AAPointerInfo::AccessPathTy *NextChain = NewPaths[Index]; + AccessPathsSet->insert(NextChain); + // Push successors to traverse and their corresponding storage on + // stack. + VisitedTy NewVisitedSet(Visited.begin(), Visited.end()); + Stack.push_back(std::make_tuple(Successor, NextChain, NewVisitedSet)); + Index++; + } + } + + return AccessPathsSet; +} + ChangeStatus AA::PointerInfo::State::addAccess( Attributor &A, const AAPointerInfo::RangeList &Ranges, Instruction &I, std::optional Content, AAPointerInfo::AccessKind Kind, Type *Ty, - Instruction *RemoteI) { + AAPointerInfo::OffsetInfoMapTy &OffsetInfoMap, Instruction *RemoteI) { RemoteI = RemoteI ? RemoteI : &I; // Check if we have an access for this instruction, if not, simply add it. @@ -956,7 +1048,11 @@ ChangeStatus AA::PointerInfo::State::addAccess( }; if (!AccExists) { - AccessList.emplace_back(&I, RemoteI, Ranges, Content, Kind, Ty); + AAPointerInfo::AccessPathSetTy *AccessPaths = + AA::PointerInfo::State::findAllAccessPaths(OffsetInfoMap, &I); + AccessList.emplace_back(&I, RemoteI, Ranges, Content, Kind, Ty, + AccessPaths); + assert((AccessList.size() == AccIndex + 1) && "New Access should have been at AccIndex"); LocalList.push_back(AccIndex); @@ -966,13 +1062,18 @@ ChangeStatus AA::PointerInfo::State::addAccess( // Combine the new Access with the existing Access, and then update the // mapping in the offset bins. - AAPointerInfo::Access Acc(&I, RemoteI, Ranges, Content, Kind, Ty); + AAPointerInfo::AccessPathSetTy *AccessPaths = + AA::PointerInfo::State::findAllAccessPaths(OffsetInfoMap, &I); + AAPointerInfo::Access Acc(&I, RemoteI, Ranges, Content, Kind, Ty, + AccessPaths); auto &Current = AccessList[AccIndex]; auto Before = Current; Current &= Acc; if (Current == Before) return ChangeStatus::UNCHANGED; + // Merge the newly generated access paths with the old access paths. + Before.mergeAccessPaths(Acc.getAccessChain()); auto &ExistingRanges = Before.getRanges(); auto &NewRanges = Current.getRanges(); @@ -1002,60 +1103,19 @@ ChangeStatus AA::PointerInfo::State::addAccess( namespace { -/// A helper containing a list of offsets computed for a Use. Ideally this -/// list should be strictly ascending, but we ensure that only when we -/// actually translate the list of offsets to a RangeList. -struct OffsetInfo { - using VecTy = SmallVector; - using const_iterator = VecTy::const_iterator; - VecTy Offsets; - - const_iterator begin() const { return Offsets.begin(); } - const_iterator end() const { return Offsets.end(); } - - bool operator==(const OffsetInfo &RHS) const { - return Offsets == RHS.Offsets; - } - - bool operator!=(const OffsetInfo &RHS) const { return !(*this == RHS); } - - void insert(int64_t Offset) { Offsets.push_back(Offset); } - bool isUnassigned() const { return Offsets.size() == 0; } - - bool isUnknown() const { - if (isUnassigned()) - return false; - if (Offsets.size() == 1) - return Offsets.front() == AA::RangeTy::Unknown; - return false; - } - - void setUnknown() { - Offsets.clear(); - Offsets.push_back(AA::RangeTy::Unknown); - } - - void addToAll(int64_t Inc) { - for (auto &Offset : Offsets) { - Offset += Inc; - } - } - - /// Copy offsets from \p R into the current list. - /// - /// Ideally all lists should be strictly ascending, but we defer that to the - /// actual use of the list. So we just blindly append here. - void merge(const OffsetInfo &R) { Offsets.append(R.Offsets); } -}; - #ifndef NDEBUG -static raw_ostream &operator<<(raw_ostream &OS, const OffsetInfo &OI) { +static raw_ostream &operator<<(raw_ostream &OS, + const AAPointerInfo::OffsetInfo &OI) { ListSeparator LS; - OS << "["; + int I = 0; for (auto Offset : OI) { - OS << LS << Offset; + OS << LS << "[Offset, Size]: " << Offset << "\n"; + auto &Origin = OI.Origins[I]; + for (auto *Val : Origin) + OS << "Origin: " << *Val << "\n"; } - OS << "]"; + OS << "\n"; + return OS; } #endif // NDEBUG @@ -1365,7 +1425,8 @@ struct AAPointerInfoImpl ChangeStatus translateAndAddStateFromCallee(Attributor &A, const AAPointerInfo &OtherAA, - CallBase &CB) { + CallBase &CB, + OffsetInfoMapTy &OffsetInfoMap) { using namespace AA::PointerInfo; if (!OtherAA.getState().isValidState() || !isValidState()) return indicatePessimisticFixpoint(); @@ -1388,15 +1449,16 @@ struct AAPointerInfoImpl AK = AccessKind(AK & (IsByval ? AccessKind::AK_R : AccessKind::AK_RW)); AK = AccessKind(AK | (RAcc.isMayAccess() ? AK_MAY : AK_MUST)); - Changed |= addAccess(A, RAcc.getRanges(), CB, Content, AK, - RAcc.getType(), RAcc.getRemoteInst()); + Changed |= + addAccess(A, RAcc.getRanges(), CB, Content, AK, RAcc.getType(), + OffsetInfoMap, RAcc.getRemoteInst()); } } return Changed; } ChangeStatus translateAndAddState(Attributor &A, const AAPointerInfo &OtherAA, - const OffsetInfo &Offsets, CallBase &CB) { + const OffsetInfo &Ranges, CallBase &CB) { using namespace AA::PointerInfo; if (!OtherAA.getState().isValidState() || !isValidState()) return indicatePessimisticFixpoint(); @@ -1409,16 +1471,16 @@ struct AAPointerInfoImpl for (const auto &It : State) { for (auto Index : It.getSecond()) { const auto &RAcc = State.getAccess(Index); - for (auto Offset : Offsets) { - auto NewRanges = Offset == AA::RangeTy::Unknown + for (auto Range : Ranges) { + auto NewRanges = Range.Offset == AA::RangeTy::Unknown ? AA::RangeTy::getUnknown() : RAcc.getRanges(); if (!NewRanges.isUnknown()) { - NewRanges.addToAllOffsets(Offset); + NewRanges.addToAllOffsets(Range.Offset); } Changed |= addAccess(A, NewRanges, CB, RAcc.getContent(), RAcc.getKind(), - RAcc.getType(), RAcc.getRemoteInst()); + RAcc.getType(), OffsetInfoMap, RAcc.getRemoteInst()); } } } @@ -1449,9 +1511,11 @@ struct AAPointerInfoImpl else O << " - c: \n"; } + Acc.dumpAccessPaths(O); } } } + OffsetInfoMapTy OffsetInfoMap; }; struct AAPointerInfoFloating : public AAPointerInfoImpl { @@ -1462,8 +1526,8 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl { /// Deal with an access and signal if it was handled successfully. bool handleAccess(Attributor &A, Instruction &I, std::optional Content, AccessKind Kind, - SmallVectorImpl &Offsets, ChangeStatus &Changed, - Type &Ty) { + OffsetInfo &OI, ChangeStatus &Changed, Type &Ty, + OffsetInfoMapTy &OffsetInfoMap) { using namespace AA::PointerInfo; auto Size = AA::RangeTy::Unknown; const DataLayout &DL = A.getDataLayout(); @@ -1472,16 +1536,23 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl { Size = AccessSize.getFixedValue(); // Make a strictly ascending list of offsets as required by addAccess() - llvm::sort(Offsets); - auto *Last = llvm::unique(Offsets); - Offsets.erase(Last, Offsets.end()); + auto Ranges = OI.Ranges; + auto Origins = OI.Origins; + + llvm::sort(Ranges); + auto *Last = llvm::unique(Ranges); + Ranges.erase(Last, Ranges.end()); + + SmallVector OffsetsOnly; + OI.getOnlyOffsets(OffsetsOnly); VectorType *VT = dyn_cast(&Ty); if (!VT || VT->getElementCount().isScalable() || !Content.value_or(nullptr) || !isa(*Content) || (*Content)->getType() != VT || DL.getTypeStoreSize(VT->getElementType()).isScalable()) { - Changed = Changed | addAccess(A, {Offsets, Size}, I, Content, Kind, &Ty); + Changed = Changed | addAccess(A, {OffsetsOnly, Size}, I, Content, Kind, + &Ty, OffsetInfoMap); } else { // Handle vector stores with constant content element-wise. // TODO: We could look for the elements or create instructions @@ -1493,7 +1564,8 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl { int64_t ElementSize = DL.getTypeStoreSize(ElementType).getFixedValue(); auto *ConstContent = cast(*Content); Type *Int32Ty = Type::getInt32Ty(ElementType->getContext()); - SmallVector ElementOffsets(Offsets.begin(), Offsets.end()); + SmallVector ElementOffsets; + OI.getOnlyOffsets(ElementOffsets); for (int i = 0, e = VT->getElementCount().getFixedValue(); i != e; ++i) { Value *ElementContent = ConstantExpr::getExtractElement( @@ -1501,7 +1573,8 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl { // Add the element access. Changed = Changed | addAccess(A, {ElementOffsets, ElementSize}, I, - ElementContent, Kind, ElementType); + ElementContent, Kind, ElementType, + OffsetInfoMap); // Advance the offsets for the next element. for (auto &ElementOffset : ElementOffsets) @@ -1520,7 +1593,7 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl { /// \return true iff \p UsrOI is updated. bool collectConstantsForGEP(Attributor &A, const DataLayout &DL, OffsetInfo &UsrOI, const OffsetInfo &PtrOI, - const GEPOperator *GEP); + GEPOperator *GEP, Value *CurPtr); /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { @@ -1528,11 +1601,9 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl { } }; -bool AAPointerInfoFloating::collectConstantsForGEP(Attributor &A, - const DataLayout &DL, - OffsetInfo &UsrOI, - const OffsetInfo &PtrOI, - const GEPOperator *GEP) { +bool AAPointerInfoFloating::collectConstantsForGEP( + Attributor &A, const DataLayout &DL, OffsetInfo &UsrOI, + const OffsetInfo &PtrOI, GEPOperator *GEP, Value *CurPtr) { unsigned BitWidth = DL.getIndexTypeSizeInBits(GEP->getType()); MapVector VariableOffsets; APInt ConstantOffset(BitWidth, 0); @@ -1542,7 +1613,7 @@ bool AAPointerInfoFloating::collectConstantsForGEP(Attributor &A, "determined to be unknown."); if (!GEP->collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset)) { - UsrOI.setUnknown(); + UsrOI.setUnknown(*CurPtr); return true; } @@ -1551,7 +1622,9 @@ bool AAPointerInfoFloating::collectConstantsForGEP(Attributor &A, << *GEP << "\n"); auto Union = PtrOI; - Union.addToAll(ConstantOffset.getSExtValue()); + // clear the origins since we just want to keep only one predecessor. + Union.Origins.clear(); + Union.addToAll(ConstantOffset.getSExtValue(), *CurPtr); // Each VI in VariableOffsets has a set of potential constant values. Every // combination of elements, picked one each from these sets, is separately @@ -1560,7 +1633,7 @@ bool AAPointerInfoFloating::collectConstantsForGEP(Attributor &A, auto *PotentialConstantsAA = A.getAAFor( *this, IRPosition::value(*VI.first), DepClassTy::OPTIONAL); if (!PotentialConstantsAA || !PotentialConstantsAA->isValidState()) { - UsrOI.setUnknown(); + UsrOI.setUnknown(*CurPtr); return true; } @@ -1579,14 +1652,16 @@ bool AAPointerInfoFloating::collectConstantsForGEP(Attributor &A, OffsetInfo Product; for (const auto &ConstOffset : AssumedSet) { auto CopyPerOffset = Union; - CopyPerOffset.addToAll(ConstOffset.getSExtValue() * - VI.second.getZExtValue()); + CopyPerOffset.addToAll( + ConstOffset.getSExtValue() * VI.second.getZExtValue(), *CurPtr); Product.merge(CopyPerOffset); } Union = Product; } UsrOI = std::move(Union); + TypeSize Size = DL.getTypeAllocSize(GEP->getResultElementType()); + UsrOI.setSizeAll(Size); return true; } @@ -1595,9 +1670,27 @@ ChangeStatus AAPointerInfoFloating::updateImpl(Attributor &A) { ChangeStatus Changed = ChangeStatus::UNCHANGED; const DataLayout &DL = A.getDataLayout(); Value &AssociatedValue = getAssociatedValue(); - - DenseMap OffsetInfoMap; - OffsetInfoMap[&AssociatedValue].insert(0); + OffsetInfoMap.clear(); + + uint64_t Size; + Function *F = getAssociatedFunction(); + TargetLibraryInfo *TLI = nullptr; + if (F) + TLI = A.getInfoCache().getTargetLibraryInfoForFunction(*F); + + if (TLI && getObjectSize(&AssociatedValue, Size, DL, TLI)) { + OffsetInfoMap[&AssociatedValue].insert(AA::RangeTy(0, Size), + AssociatedValue); + } else if (isa(AssociatedValue)) { + auto &Glob = cast(AssociatedValue); + TypeSize SizeOfType = DL.getTypeAllocSize(Glob.getValueType()); + OffsetInfoMap[&AssociatedValue].insert(AA::RangeTy(0, SizeOfType), + AssociatedValue); + } else { + TypeSize SizeOfType = DL.getTypeAllocSize(AssociatedValue.getType()); + OffsetInfoMap[&AssociatedValue].insert(AA::RangeTy(0, SizeOfType), + AssociatedValue); + } auto HandlePassthroughUser = [&](Value *Usr, Value *CurPtr, bool &Follow) { // One does not simply walk into a map and assign a reference to a possibly @@ -1616,7 +1709,13 @@ ChangeStatus AAPointerInfoFloating::updateImpl(Attributor &A) { auto &PtrOI = OffsetInfoMap[CurPtr]; assert(!PtrOI.isUnassigned() && "Cannot pass through if the input Ptr was not visited!"); - UsrOI.merge(PtrOI); + if (isa(Usr) || isa(Usr)) { + UsrOI.mergeWithOffset(PtrOI, *CurPtr); + } else { + UsrOI = PtrOI; + UsrOI.Origins.clear(); + UsrOI.addToAll(0, *CurPtr); + } Follow = true; return true; }; @@ -1649,11 +1748,11 @@ ChangeStatus AAPointerInfoFloating::updateImpl(Attributor &A) { if (PtrOI.isUnknown()) { Follow = true; - UsrOI.setUnknown(); + UsrOI.setUnknown(*GEP); return true; } - Follow = collectConstantsForGEP(A, DL, UsrOI, PtrOI, GEP); + Follow = collectConstantsForGEP(A, DL, UsrOI, PtrOI, GEP, CurPtr); return true; } if (isa(Usr)) @@ -1677,7 +1776,7 @@ ChangeStatus AAPointerInfoFloating::updateImpl(Attributor &A) { LLVM_DEBUG(dbgs() << "[AAPointerInfo] PHI operand offset unknown " << *CurPtr << " in " << *PHI << "\n"); Follow = !UsrOI.isUnknown(); - UsrOI.setUnknown(); + UsrOI.setUnknown(*CurPtr); return true; } @@ -1688,7 +1787,6 @@ ChangeStatus AAPointerInfoFloating::updateImpl(Attributor &A) { LLVM_DEBUG(dbgs() << "[AAPointerInfo] PHI is invariant (so far)"); return true; } - // Check if the PHI operand can be traced back to AssociatedValue. APInt Offset( DL.getIndexSizeInBits(CurPtr->getType()->getPointerAddressSpace()), @@ -1700,7 +1798,7 @@ ChangeStatus AAPointerInfoFloating::updateImpl(Attributor &A) { LLVM_DEBUG(dbgs() << "[AAPointerInfo] PHI operand is too complex " << *CurPtr << " in " << *PHI << " (base: " << *CurPtrBase << ")\n"); - UsrOI.setUnknown(); + UsrOI.setUnknown(*CurPtr); Follow = true; return true; } @@ -1717,7 +1815,7 @@ ChangeStatus AAPointerInfoFloating::updateImpl(Attributor &A) { *PHI->getFunction()); if (mayBeInCycle(CI, cast(Usr), /* HeaderOnly */ true)) { auto BaseOI = It->getSecond(); - BaseOI.addToAll(Offset.getZExtValue()); + BaseOI.addToAll(Offset.getZExtValue(), *CurPtr); if (IsFirstPHIUser || BaseOI == UsrOI) { LLVM_DEBUG(dbgs() << "[AAPointerInfo] PHI is invariant " << *CurPtr << " in " << *Usr << "\n"); @@ -1727,12 +1825,12 @@ ChangeStatus AAPointerInfoFloating::updateImpl(Attributor &A) { LLVM_DEBUG( dbgs() << "[AAPointerInfo] PHI operand pointer offset mismatch " << *CurPtr << " in " << *PHI << "\n"); - UsrOI.setUnknown(); + UsrOI.setUnknown(*CurPtr); Follow = true; return true; } - UsrOI.merge(PtrOI); + UsrOI.mergeWithOffset(PtrOI, *CurPtr); Follow = true; return true; } @@ -1746,8 +1844,8 @@ ChangeStatus AAPointerInfoFloating::updateImpl(Attributor &A) { else AK = AccessKind(AK | AccessKind::AK_MAY); if (!handleAccess(A, *LoadI, /* Content */ nullptr, AK, - OffsetInfoMap[CurPtr].Offsets, Changed, - *LoadI->getType())) + OffsetInfoMap[CurPtr], Changed, *LoadI->getType(), + OffsetInfoMap)) return false; auto IsAssumption = [](Instruction &I) { @@ -1831,9 +1929,9 @@ ChangeStatus AAPointerInfoFloating::updateImpl(Attributor &A) { Content = A.getAssumedSimplified(*Assumption.first, *this, UsedAssumedInformation, AA::Interprocedural); - return handleAccess( - A, *Assumption.second, Content, AccessKind::AK_ASSUMPTION, - OffsetInfoMap[CurPtr].Offsets, Changed, *LoadI->getType()); + return handleAccess(A, *Assumption.second, Content, + AccessKind::AK_ASSUMPTION, OffsetInfoMap[CurPtr], + Changed, *LoadI->getType(), OffsetInfoMap); } auto HandleStoreLike = [&](Instruction &I, Value *ValueOp, Type &ValueTy, @@ -1859,8 +1957,8 @@ ChangeStatus AAPointerInfoFloating::updateImpl(Attributor &A) { if (ValueOp) Content = A.getAssumedSimplified( *ValueOp, *this, UsedAssumedInformation, AA::Interprocedural); - return handleAccess(A, I, Content, AK, OffsetInfoMap[CurPtr].Offsets, - Changed, ValueTy); + return handleAccess(A, I, Content, AK, OffsetInfoMap[CurPtr], Changed, + ValueTy, OffsetInfoMap); }; if (auto *StoreI = dyn_cast(Usr)) @@ -1983,8 +2081,8 @@ struct AAPointerInfoCallSiteArgument final : AAPointerInfoFloating { } else { auto Kind = ArgNo == 0 ? AccessKind::AK_MUST_WRITE : AccessKind::AK_MUST_READ; - Changed = - Changed | addAccess(A, {0, LengthVal}, *MI, nullptr, Kind, nullptr); + Changed = Changed | addAccess(A, {0, LengthVal}, *MI, nullptr, Kind, + nullptr, OffsetInfoMap); } LLVM_DEBUG({ dbgs() << "Accesses by bin after update:\n"; @@ -2004,8 +2102,8 @@ struct AAPointerInfoCallSiteArgument final : AAPointerInfoFloating { auto *ArgAA = A.getAAFor(*this, ArgPos, DepClassTy::REQUIRED); if (ArgAA && ArgAA->getState().isValidState()) - return translateAndAddStateFromCallee(A, *ArgAA, - *cast(getCtxI())); + return translateAndAddStateFromCallee( + A, *ArgAA, *cast(getCtxI()), OffsetInfoMap); if (!Arg->getParent()->isDeclaration()) return indicatePessimisticFixpoint(); } @@ -2022,7 +2120,7 @@ struct AAPointerInfoCallSiteArgument final : AAPointerInfoFloating { auto Kind = ReadOnly ? AccessKind::AK_MAY_READ : AccessKind::AK_MAY_READ_WRITE; return addAccess(A, AA::RangeTy::getUnknown(), *getCtxI(), nullptr, Kind, - nullptr); + nullptr, OffsetInfoMap); } /// See AbstractAttribute::trackStatistics() diff --git a/llvm/test/Transforms/Attributor/pointer-info-track-access-chain.ll b/llvm/test/Transforms/Attributor/pointer-info-track-access-chain.ll new file mode 100644 index 00000000000000..b7c3f1f33191ef --- /dev/null +++ b/llvm/test/Transforms/Attributor/pointer-info-track-access-chain.ll @@ -0,0 +1,387 @@ +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -debug-only=attributor -attributor-annotate-decl-cs -S < %s 2>&1 | FileCheck %s +; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -debug-only=attributor -attributor-annotate-decl-cs -S < %s 2>&1 | FileCheck %s +; REQUIRES: asserts + + +@globalBytes = internal global [1024 x i8] zeroinitializer, align 16 + +; CHECK: Accesses by bin after update: +; CHECK: [8-12] : 1 +; CHECK: - 5 - %1 = load i32, ptr %field22, align 4 +; CHECK: - c: +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: %1 = load i32, ptr %field22, align 4 +; CHECK: %field22 = getelementptr i32, ptr %field2, i32 0 +; CHECK: %field2 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 2 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: [4-5] : 1 +; CHECK: - 9 - store i8 10, ptr %field11, align 4 +; CHECK: - c: i8 10 +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: store i8 10, ptr %field11, align 4 +; CHECK: %field11 = getelementptr i32, ptr %field1, i32 0 +; CHECK: %field1 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 1 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: [32-36] : 1 +; CHECK: - 9 - store i32 %3, ptr %field8, align 4 +; CHECK: - c: %3 = load i32, ptr %val, align 4 +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: store i32 %3, ptr %field8, align 4 +; CHECK: %field8 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 8 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: [4-8] : 1 +; CHECK: - 5 - %0 = load i32, ptr %field11, align 4 +; CHECK: - c: +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: %0 = load i32, ptr %field11, align 4 +; CHECK: %field11 = getelementptr i32, ptr %field1, i32 0 +; CHECK: %field1 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 1 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: [8-9] : 1 +; CHECK: - 9 - store i8 12, ptr %field22, align 4 +; CHECK: - c: i8 12 +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: store i8 12, ptr %field22, align 4 +; CHECK: %field22 = getelementptr i32, ptr %field2, i32 0 +; CHECK: %field2 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 2 +; CHECK: %f = alloca [10 x i32], align 4 +define dso_local i32 @track_chain(ptr nocapture %val) #0 { +entry: + %f = alloca [10 x i32] + %field1 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 1 + %field2 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 2 + %field3 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 3 + %field8 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 8 + + %field11 = getelementptr i32, ptr %field1, i32 0 + %field22 = getelementptr i32, ptr %field2, i32 0 + store i8 10, ptr %field11, align 4 + store i8 12, ptr %field22, align 4 + + %1 = load i32, ptr %field11, align 4 + %2 = load i32, ptr %field22, align 4 + %3 = add i32 %1, %2 + + %4 = load i32, ptr %val, align 4 + store i32 %4, ptr %field8, align 4 + + %5 = add i32 %4, %3 + + ret i32 %5 +} + + +; CHECK: Accesses by bin after update: +; CHECK: [12-16] : 1 +; CHECK: - 5 - %0 = load i32, ptr %field11, align 4 +; CHECK: - c: +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: %0 = load i32, ptr %field11, align 4 +; CHECK: %field11 = getelementptr i32, ptr %field1, i32 2 +; CHECK: %field1 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 1 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: [16-17] : 1 +; CHECK: - 9 - store i8 12, ptr %field22, align 4 +; CHECK: - c: i8 12 +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: store i8 12, ptr %field22, align 4 +; CHECK: %field22 = getelementptr i32, ptr %field2, i32 2 +; CHECK: %field2 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 2 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: [32-36] : 1 +; CHECK: - 9 - store i32 %3, ptr %field8, align 4 +; CHECK: - c: %3 = load i32, ptr %val, align 4 +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: store i32 %3, ptr %field8, align 4 +; CHECK: %field8 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 8 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: [16-20] : 1 +; CHECK: - 5 - %1 = load i32, ptr %field22, align 4 +; CHECK: - c: +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: %1 = load i32, ptr %field22, align 4 +; CHECK: %field22 = getelementptr i32, ptr %field2, i32 2 +; CHECK: %field2 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 2 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: [12-13] : 1 +; CHECK: - 9 - store i8 10, ptr %field11, align 4 +; CHECK: - c: i8 10 +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: store i8 10, ptr %field11, align 4 +; CHECK: %field11 = getelementptr i32, ptr %field1, i32 2 +; CHECK: %field1 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 1 +; CHECK: %f = alloca [10 x i32], align 4 +define dso_local i32 @track_chain_2(ptr nocapture %val) #0 { +entry: + %f = alloca [10 x i32] + %field1 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 1 + %field2 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 2 + %field3 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 3 + %field8 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 8 + + %field11 = getelementptr i32, ptr %field1, i32 2 + %field22 = getelementptr i32, ptr %field2, i32 2 + store i8 10, ptr %field11, align 4 + store i8 12, ptr %field22, align 4 + + %1 = load i32, ptr %field11, align 4 + %2 = load i32, ptr %field22, align 4 + %3 = add i32 %1, %2 + + %4 = load i32, ptr %val, align 4 + store i32 %4, ptr %field8, align 4 + + %5 = add i32 %4, %3 + + ret i32 %5 +} + + +; CHECK: Accesses by bin after update: +; CHECK: [12-16] : 3 +; CHECK: - 5 - %0 = load i32, ptr %field11, align 4 +; CHECK: - c: +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: %0 = load i32, ptr %field11, align 4 +; CHECK: %field11 = getelementptr i32, ptr %field1, i32 2 +; CHECK: %field1 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 1 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: - 5 - %b = load i32, ptr %field3, align 4 +; CHECK: - c: +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: %b = load i32, ptr %field3, align 4 +; CHECK: %field3 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 3 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: - 10 - store i32 1000, ptr %6, align 4 +; CHECK: - c: i32 1000 +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: store i32 1000, ptr %6, align 4 +; CHECK: %6 = select i1 %cond, ptr %field3, ptr %field8 +; CHECK: %field3 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 3 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: Backtrack a unique access path: +; CHECK: store i32 1000, ptr %6, align 4 +; CHECK: %6 = select i1 %cond, ptr %field3, ptr %field8 +; CHECK: %field8 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 8 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: [16-17] : 1 +; CHECK: - 9 - store i8 12, ptr %field22, align 4 +; CHECK: - c: i8 12 +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: store i8 12, ptr %field22, align 4 +; CHECK: %field22 = getelementptr i32, ptr %field2, i32 2 +; CHECK: %field2 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 2 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: [32-36] : 4 +; CHECK: - 9 - store i32 %3, ptr %field8, align 4 +; CHECK: - c: %3 = load i32, ptr %val, align 4 +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: store i32 %3, ptr %field8, align 4 +; CHECK: %field8 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 8 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: - 5 - %a1 = load i32, ptr %field8, align 4 +; CHECK: - c: +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: %a1 = load i32, ptr %field8, align 4 +; CHECK: %field8 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 8 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: - 10 - store i32 1000, ptr %6, align 4 +; CHECK: - c: i32 1000 +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: store i32 1000, ptr %6, align 4 +; CHECK: %6 = select i1 %cond, ptr %field3, ptr %field8 +; CHECK: %field3 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 3 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: Backtrack a unique access path: +; CHECK: store i32 1000, ptr %6, align 4 +; CHECK: %6 = select i1 %cond, ptr %field3, ptr %field8 +; CHECK: %field8 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 8 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: - 5 - %8 = load i32, ptr %field8, align 4 +; CHECK: - c: +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: %8 = load i32, ptr %field8, align 4 +; CHECK: %field8 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 8 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: [16-20] : 1 +; CHECK: - 5 - %1 = load i32, ptr %field22, align 4 +; CHECK: - c: +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: %1 = load i32, ptr %field22, align 4 +; CHECK: %field22 = getelementptr i32, ptr %field2, i32 2 +; CHECK: %field2 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 2 +; CHECK: %f = alloca [10 x i32], align 4 +; CHECK: [12-13] : 1 +; CHECK: - 9 - store i8 10, ptr %field11, align 4 +; CHECK: - c: i8 10 +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: store i8 10, ptr %field11, align 4 +; CHECK: %field11 = getelementptr i32, ptr %field1, i32 2 +; CHECK: %field1 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 1 +; CHECK: %f = alloca [10 x i32], align 4 +define dso_local i32 @track_chain_3(ptr nocapture %val, i1 %cond) #0 { +entry: + %f = alloca [10 x i32] + %field1 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 1 + %field2 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 2 + %field3 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 3 + %field8 = getelementptr inbounds [10 x i32], ptr %f, i32 0, i32 8 + + %field11 = getelementptr i32, ptr %field1, i32 2 + %field22 = getelementptr i32, ptr %field2, i32 2 + store i8 10, ptr %field11, align 4 + store i8 12, ptr %field22, align 4 + %1 = load i32, ptr %field11, align 4 + %2 = load i32, ptr %field22, align 4 + %3 = add i32 %1, %2 + %4 = load i32, ptr %val, align 4 + store i32 %4, ptr %field8, align 4 + %5 = add i32 %4, %3 + %6 = load i32, ptr %val + %a1 = load i32, ptr %field8 + %a = add i32 %a1, %6 + %b = load i32, ptr %field3 + ;%b = sub i32 %b1, %6 + %7 = select i1 %cond, ptr %field3, ptr %field8 + store i32 1000, ptr %7 + %8 = add i32 %5, %b + %9 = load i32, ptr %field8 + %10 = add i32 %9, %8 + ret i32 %10 +} + +; CHECK: Accesses by bin after update: +; CHECK: [8-12] : 2 +; CHECK: - 9 - store i32 %0, ptr %field2, align 4 +; CHECK: - c: %0 = load i32, ptr %val, align 4 +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: store i32 %0, ptr %field2, align 4 +; CHECK: %field2 = getelementptr i32, ptr @globalBytes, i32 2 +; CHECK: @globalBytes = internal global [1024 x i8] zeroinitializer, align 16 +; CHECK: - 6 - %ret = load i32, ptr %x, align 4 +; CHECK: - c: +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: %ret = load i32, ptr %x, align 4 +; CHECK: %x = phi ptr [ %field2, %then ], [ %field8, %else ] +; CHECK: %field2 = getelementptr i32, ptr @globalBytes, i32 2 +; CHECK: @globalBytes = internal global [1024 x i8] zeroinitializer, align 16 +; CHECK: Backtrack a unique access path: +; CHECK: %ret = load i32, ptr %x, align 4 +; CHECK: %x = phi ptr [ %field2, %then ], [ %field8, %else ] +; CHECK: %field8 = getelementptr i32, ptr @globalBytes, i32 8 +; CHECK: @globalBytes = internal global [1024 x i8] zeroinitializer, align 16 +; CHECK: [32-36] : 5 +; CHECK: - 6 - %ret = load i32, ptr %x, align 4 +; CHECK: - c: +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: %ret = load i32, ptr %x, align 4 +; CHECK: %x = phi ptr [ %field2, %then ], [ %field8, %else ] +; CHECK: %field2 = getelementptr i32, ptr @globalBytes, i32 2 +; CHECK: @globalBytes = internal global [1024 x i8] zeroinitializer, align 16 +; CHECK: Backtrack a unique access path: +; CHECK: %ret = load i32, ptr %x, align 4 +; CHECK: %x = phi ptr [ %field2, %then ], [ %field8, %else ] +; CHECK: %field8 = getelementptr i32, ptr @globalBytes, i32 8 +; CHECK: @globalBytes = internal global [1024 x i8] zeroinitializer, align 16 +; CHECK: - 9 - store i32 %1, ptr %field8, align 4 +; CHECK: - c: %1 = load i32, ptr %val2, align 4 +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: store i32 %1, ptr %field8, align 4 +; CHECK: %field8 = getelementptr i32, ptr @globalBytes, i32 8 +; CHECK: @globalBytes = internal global [1024 x i8] zeroinitializer, align 16 +; CHECK: - 9 - store i32 %0, ptr %field2, align 4 +; CHECK: - c: %0 = load i32, ptr %val, align 4 +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: store i32 %0, ptr %field2, align 4 +; CHECK: %field2 = getelementptr i32, ptr @globalBytes, i32 8 +; CHECK: @globalBytes = internal global [1024 x i8] zeroinitializer, align 16 +; CHECK: - 6 - %ret = load i32, ptr %x, align 4 +; CHECK: - c: +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: %ret = load i32, ptr %x, align 4 +; CHECK: %x = phi ptr [ %field2, %then ], [ %field8, %else ] +; CHECK: %field2 = getelementptr i32, ptr @globalBytes, i32 8 +; CHECK: @globalBytes = internal global [1024 x i8] zeroinitializer, align 16 +; CHECK: Backtrack a unique access path: +; CHECK: %ret = load i32, ptr %x, align 4 +; CHECK: %x = phi ptr [ %field2, %then ], [ %field8, %else ] +; CHECK: %field8 = getelementptr i32, ptr @globalBytes, i32 8 +; CHECK: @globalBytes = internal global [1024 x i8] zeroinitializer, align 16 +; CHECK: - 9 - store i32 %1, ptr %field8, align 4 +; CHECK: - c: %1 = load i32, ptr %val2, align 4 +; CHECK: Print all access paths found: +; CHECK: Backtrack a unique access path: +; CHECK: store i32 %1, ptr %field8, align 4 +; CHECK: %field8 = getelementptr i32, ptr @globalBytes, i32 8 +; CHECK: @globalBytes = internal global [1024 x i8] zeroinitializer, align 16 + +define dso_local i32 @phi_different_offsets(ptr nocapture %val, ptr nocapture %val2, i1 %cmp) { +entry: + br i1 %cmp, label %then, label %else + +then: + %field2 = getelementptr i32, ptr @globalBytes, i32 2 + %0 = load i32, ptr %val + store i32 %0, ptr %field2 + br label %end + +else: + %field8 = getelementptr i32, ptr @globalBytes, i32 8 + %2 = load i32, ptr %val2 + store i32 %2, ptr %field8 + br label %end + +end: + %x = phi ptr [ %field2, %then ], [ %field8, %else ] + %ret = load i32, ptr %x + ret i32 %ret + +} + +define dso_local i32 @phi_same_offsets(ptr nocapture %val, ptr nocapture %val2, i1 %cmp) { +entry: + br i1 %cmp, label %then, label %else + +then: + %field2 = getelementptr i32, ptr @globalBytes, i32 8 + %0 = load i32, ptr %val + store i32 %0, ptr %field2 + br label %end + +else: + %field8 = getelementptr i32, ptr @globalBytes, i32 8 + %2 = load i32, ptr %val2 + store i32 %2, ptr %field8 + br label %end + +end: + %x = phi ptr [ %field2, %then ], [ %field8, %else ] + %ret = load i32, ptr %x + ret i32 %ret +} \ No newline at end of file