diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -4985,35 +4985,81 @@ AK_MUST_READ_WRITE = AK_MUST | AK_R | AK_W, }; + /// Helper to represent an access offset and size, with logic to deal with + /// uncertainty and check for overlapping accesses. + struct OffsetAndSize : public std::pair { + using BaseTy = std::pair; + OffsetAndSize(int64_t Offset, int64_t Size) : BaseTy(Offset, Size) {} + OffsetAndSize(const BaseTy &P) : BaseTy(P) {} + int64_t getOffset() const { return first; } + int64_t getSize() const { return second; } + static OffsetAndSize getUnknown() { + return OffsetAndSize(Unknown, Unknown); + } + + /// Return true if offset or size are unknown. + bool offsetOrSizeAreUnknown() const { + return getOffset() == OffsetAndSize::Unknown || + getSize() == OffsetAndSize::Unknown; + } + + /// Return true if this offset and size pair might describe an address that + /// overlaps with \p OAS. + bool mayOverlap(const OffsetAndSize &OAS) const { + // Any unknown value and we are giving up -> overlap. + if (offsetOrSizeAreUnknown() || OAS.offsetOrSizeAreUnknown()) + return true; + + // Check if one offset point is in the other interval [offset, + // offset+size]. + return OAS.getOffset() + OAS.getSize() > getOffset() && + OAS.getOffset() < getOffset() + getSize(); + } + + /// Constants used to represent special offsets or sizes. + static constexpr int64_t Unassigned = -1; + static constexpr int64_t Unknown = -2; + }; + /// An access description. struct Access { - Access(Instruction *I, Optional Content, AccessKind Kind, Type *Ty) - : LocalI(I), RemoteI(I), Content(Content), Kind(Kind), Ty(Ty) { + Access(Instruction *I, int64_t Offset, int64_t Size, + Optional Content, AccessKind Kind, Type *Ty) + : LocalI(I), RemoteI(I), Content(Content), Offset(Offset), Size(Size), + Kind(Kind), Ty(Ty) { verify(); } - Access(Instruction *LocalI, Instruction *RemoteI, Optional Content, - AccessKind Kind, Type *Ty) - : LocalI(LocalI), RemoteI(RemoteI), Content(Content), Kind(Kind), - Ty(Ty) { + Access(Instruction *LocalI, Instruction *RemoteI, int64_t Offset, + int64_t Size, Optional Content, AccessKind Kind, Type *Ty) + : LocalI(LocalI), RemoteI(RemoteI), Content(Content), Offset(Offset), + Size(Size), Kind(Kind), Ty(Ty) { verify(); } Access(const Access &Other) = default; Access(const Access &&Other) : LocalI(Other.LocalI), RemoteI(Other.RemoteI), Content(Other.Content), - Kind(Other.Kind), Ty(Other.Ty) {} + Offset(Other.Offset), Size(Other.Size), Kind(Other.Kind), + Ty(Other.Ty) {} Access &operator=(const Access &Other) = default; bool operator==(const Access &R) const { - return LocalI == R.LocalI && RemoteI == R.RemoteI && - Content == R.Content && Kind == R.Kind; + return LocalI == R.LocalI && RemoteI == R.RemoteI && Offset == R.Offset && + Size == R.Size && Content == R.Content && Kind == R.Kind; } bool operator!=(const Access &R) const { return !(*this == R); } Access &operator&=(const Access &R) { assert(RemoteI == R.RemoteI && "Expected same instruction!"); + assert(Size == R.Size); Content = AA::combineOptionalValuesInAAValueLatice(Content, R.Content, Ty); Kind = AccessKind(Kind | R.Kind); + if (Offset != R.Offset) + Offset = OffsetAndSize::Unknown; + if (Size == OffsetAndSize::Unknown || R.Size == OffsetAndSize::Unknown) + Size = OffsetAndSize::Unknown; + else + Size = std::max(Size, R.Size); return *this; } @@ -5061,6 +5107,9 @@ /// determined. Optional getContent() const { return Content; } + int64_t getOffset() const { return Offset; } + int64_t getSize() const { return Size; } + private: /// The instruction responsible for the access with respect to the local /// scope of the associated attribute. @@ -5073,6 +5122,10 @@ /// cannot be determined. Optional Content; + /// Set of potential offsets from the base pointer. + int64_t Offset = OffsetAndSize::Unassigned; + int64_t Size = OffsetAndSize::Unknown; + /// The access kind, e.g., READ, as bitset (could be more than one). AccessKind Kind; diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp --- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -740,12 +740,6 @@ /// A type to track pointer/struct usage and accesses for AAPointerInfo. struct AA::PointerInfo::State : public AbstractState { - ~State() { - // We do not delete the Accesses objects but need to destroy them still. - for (auto &It : AccessBins) - It.second->~Accesses(); - } - /// Return the best possible representable state. static State getBestState(const State &SIS) { return State(); } @@ -757,9 +751,7 @@ } State() = default; - State(State &&SIS) : AccessBins(std::move(SIS.AccessBins)) { - SIS.AccessBins.clear(); - } + State(State &&SIS) = default; const State &getAssumed() const { return *this; } @@ -785,7 +777,7 @@ if (this == &R) return *this; BS = R.BS; - AccessBins = R.AccessBins; + AllAccesses = R.AllAccesses; return *this; } @@ -793,98 +785,111 @@ if (this == &R) return *this; std::swap(BS, R.BS); - std::swap(AccessBins, R.AccessBins); + std::swap(AllAccesses, R.AllAccesses); return *this; } - bool operator==(const State &R) const { - if (BS != R.BS) - return false; - if (AccessBins.size() != R.AccessBins.size()) - return false; - auto It = begin(), RIt = R.begin(), E = end(); - while (It != E) { - if (It->getFirst() != RIt->getFirst()) - return false; - auto &Accs = It->getSecond(); - auto &RAccs = RIt->getSecond(); - if (Accs->size() != RAccs->size()) - return false; - for (const auto &ZipIt : llvm::zip(*Accs, *RAccs)) - if (std::get<0>(ZipIt) != std::get<1>(ZipIt)) - return false; - ++It; - ++RIt; - } - return true; - } - bool operator!=(const State &R) const { return !(*this == R); } - - /// We store accesses in a set with the instruction as key. - struct Accesses { - SmallVector Accesses; - DenseMap Map; + struct AccessesTy { + SmallVector AccessList; + DenseMap InstMap; + DenseMap> OffsetBins; - unsigned size() const { return Accesses.size(); } + unsigned size() const { return AccessList.size(); } - using vec_iterator = decltype(Accesses)::iterator; - vec_iterator begin() { return Accesses.begin(); } - vec_iterator end() { return Accesses.end(); } + // Lookup accesses for an Instruction + using const_inst_iterator = decltype(InstMap)::const_iterator; + const_inst_iterator find_inst(const Instruction *Inst) const { + return InstMap.find(Inst); + } + const_inst_iterator find_inst_end() const { return InstMap.end(); } + const AAPointerInfo::Access &get(const_inst_iterator &It) const { + return AccessList[It->getSecond()]; + } - using iterator = decltype(Map)::const_iterator; - iterator find(AAPointerInfo::Access &Acc) { - return Map.find(Acc.getRemoteInst()); + using inst_iterator = decltype(InstMap)::iterator; + inst_iterator find_inst(const Instruction *Inst) { + return InstMap.find(Inst); + } + inst_iterator find_inst_end() { return InstMap.end(); } + AAPointerInfo::Access &get(inst_iterator &It) { + return AccessList[It->getSecond()]; } - iterator find_end() { return Map.end(); } - AAPointerInfo::Access &get(iterator &It) { - return Accesses[It->getSecond()]; + // Lookup accesses binned by Offsets + using const_bin_iterator = decltype(OffsetBins)::const_iterator; + const_bin_iterator find_bin(OffsetAndSize OAS) const { + return OffsetBins.find(OAS); } + const_bin_iterator find_bin_end() const { return OffsetBins.end(); } + const SmallSet &get(const_bin_iterator &It) const { + return It->getSecond(); + } + + using bin_iterator = decltype(OffsetBins)::iterator; + bin_iterator find_bin(OffsetAndSize OAS) { return OffsetBins.find(OAS); } + bin_iterator find_bin_end() { return OffsetBins.end(); } + SmallSet &get(bin_iterator &It) { return It->getSecond(); } - void insert(AAPointerInfo::Access &Acc) { - Map[Acc.getRemoteInst()] = Accesses.size(); - Accesses.push_back(Acc); + // Lookup access with a direct index + const AAPointerInfo::Access &operator[](unsigned index) const { + return AccessList[index]; + } + AAPointerInfo::Access &operator[](unsigned index) { + return AccessList[index]; } - }; - /// We store all accesses in bins denoted by their offset and size. - using AccessBinsTy = DenseMap; + /// Add a new access to the state at offset \p Offset and with size \p Size. + /// The access is associated with \p I, writes \p Content (if anything), and + /// is of kind \p Kind. + /// \Returns CHANGED, if the state changed, UNCHANGED otherwise. + ChangeStatus addAccess(Attributor &A, int64_t Offset, int64_t Size, + Instruction &I, Optional Content, + AAPointerInfo::AccessKind Kind, Type *Ty, + Instruction *RemoteI = nullptr) { + RemoteI = RemoteI ? RemoteI : &I; + AAPointerInfo::Access Acc(&I, RemoteI, Offset, Size, Content, Kind, Ty); - AccessBinsTy::const_iterator begin() const { return AccessBins.begin(); } - AccessBinsTy::const_iterator end() const { return AccessBins.end(); } + // Check if we have an access for this instruction, if not, simply add it. + auto [AccIt, AccInserted] = + InstMap.try_emplace(Acc.getRemoteInst(), AccessList.size()); + auto Index = AccIt->getSecond(); + if (AccInserted) { + assert(Index == AccessList.size()); + AccessList.push_back(Acc); + } else { + // The new one will be combined with the existing one. + auto &Current = AccessList[Index]; + auto Before = Current; + Current &= Acc; + if (Current == Before) + return ChangeStatus::UNCHANGED; + + Acc = Current; + AAPointerInfo::OffsetAndSize Key{Before.getOffset(), Before.getSize()}; + assert(OffsetBins.count(Key)); + auto &Bin = OffsetBins[Key]; + assert(Bin.count(Index)); + Bin.erase(Index); + } -protected: - /// The bins with all the accesses for the associated pointer. - AccessBinsTy AccessBins; - - /// Add a new access to the state at offset \p Offset and with size \p Size. - /// The access is associated with \p I, writes \p Content (if anything), and - /// is of kind \p Kind. - /// \Returns CHANGED, if the state changed, UNCHANGED otherwise. - ChangeStatus addAccess(Attributor &A, int64_t Offset, int64_t Size, - Instruction &I, Optional Content, - AAPointerInfo::AccessKind Kind, Type *Ty, - Instruction *RemoteI = nullptr, - Accesses *BinPtr = nullptr) { - AA::OffsetAndSize Key{Offset, Size}; - Accesses *&Bin = BinPtr ? BinPtr : AccessBins[Key]; - if (!Bin) - Bin = new (A.Allocator) Accesses; - AAPointerInfo::Access Acc(&I, RemoteI ? RemoteI : &I, Content, Kind, Ty); - // Check if we have an access for this instruction in this bin, if not, - // simply add it. - auto It = Bin->find(Acc); - if (It == Bin->find_end()) { - Bin->insert(Acc); + AAPointerInfo::OffsetAndSize Key{Acc.getOffset(), Acc.getSize()}; + OffsetBins[Key].insert(Index); return ChangeStatus::CHANGED; } - // If the existing access is the same as then new one, nothing changed. - AAPointerInfo::Access &Current = Bin->get(It); - AAPointerInfo::Access Before = Current; - // The new one will be combined with the existing one. - Current &= Acc; - return Current == Before ? ChangeStatus::UNCHANGED : ChangeStatus::CHANGED; + }; + + AccessesTy::const_bin_iterator begin() const { + return AllAccesses.OffsetBins.begin(); } + AccessesTy::const_bin_iterator end() const { + return AllAccesses.OffsetBins.end(); + } + const AAPointerInfo::Access &getAccess(unsigned Index) const { + return AllAccesses[Index]; + } + +protected: + AccessesTy AllAccesses; /// See AAPointerInfo::forallInterferingAccesses. bool forallInterferingAccesses( @@ -893,14 +898,16 @@ if (!isValidState()) return false; - for (const auto &It : AccessBins) { + for (const auto &It : AllAccesses.OffsetBins) { AA::OffsetAndSize ItOAS = It.getFirst(); if (!OAS.mayOverlap(ItOAS)) continue; bool IsExact = OAS == ItOAS && !OAS.offsetOrSizeAreUnknown(); - for (auto &Access : *It.getSecond()) + for (auto Index : It.getSecond()) { + auto &Access = AllAccesses[Index]; if (!CB(Access, IsExact)) return false; + } } return true; } @@ -913,26 +920,15 @@ if (!isValidState()) return false; - // First find the offset and size of I. - AA::OffsetAndSize OAS = AA::OffsetAndSize::getUnassigned(); - for (const auto &It : AccessBins) { - for (auto &Access : *It.getSecond()) { - if (Access.getRemoteInst() == &I) { - OAS = It.getFirst(); - break; - } - } - if (OAS.getSize() != AA::OffsetAndSize::Unassigned) - break; - } + auto It = AllAccesses.find_inst(&I); + if (It == AllAccesses.find_inst_end()) + return true; + auto &Access = AllAccesses.get(It); + OffsetAndSize OAS{Access.getOffset(), Access.getSize()}; if (OASPtr) *OASPtr = OAS; - // No access for I was found, we are done. - if (OAS.getSize() == AA::OffsetAndSize::Unassigned) - return true; - // Now that we have an offset and size, find all overlapping ones and use // the callback on the accesses. return forallInterferingAccesses(OAS, CB); @@ -952,9 +948,10 @@ /// See AbstractAttribute::getAsStr(). const std::string getAsStr() const override { return std::string("PointerInfo ") + - (isValidState() ? (std::string("#") + - std::to_string(AccessBins.size()) + " bins") - : ""); + (isValidState() + ? (std::string("#") + + std::to_string(AllAccesses.OffsetBins.size()) + " bins") + : ""); } /// See AbstractAttribute::manifest(...). @@ -1154,13 +1151,14 @@ // Combine the accesses bin by bin. ChangeStatus Changed = ChangeStatus::UNCHANGED; - for (const auto &It : OtherAAImpl.getState()) { + const auto &State = OtherAAImpl.getState(); + for (const auto &It : State) { AA::OffsetAndSize OAS = AA::OffsetAndSize::getUnknown(); if (Offset != AA::OffsetAndSize::Unknown) OAS = AA::OffsetAndSize(It.first.getOffset() + Offset, It.first.getSize()); - Accesses *Bin = AccessBins.lookup(OAS); - for (const AAPointerInfo::Access &RAcc : *It.second) { + for (auto Index : It.getSecond()) { + const auto &RAcc = State.getAccess(Index); if (IsByval && !RAcc.isRead()) continue; bool UsedAssumedInformation = false; @@ -1173,9 +1171,9 @@ AccessKind(AK & (IsByval ? AccessKind::AK_R : AccessKind::AK_RW)); AK = AccessKind(AK | (RAcc.isMayAccess() ? AK_MAY : AK_MUST)); } - Changed = - Changed | addAccess(A, OAS.getOffset(), OAS.getSize(), CB, Content, - AK, RAcc.getType(), RAcc.getRemoteInst(), Bin); + Changed = Changed | AllAccesses.addAccess( + A, OAS.getOffset(), OAS.getSize(), CB, Content, + AK, RAcc.getType(), RAcc.getRemoteInst()); } } return Changed; @@ -1187,11 +1185,12 @@ /// Dump the state into \p O. void dumpState(raw_ostream &O) { - for (auto &It : AccessBins) { + for (auto &It : AllAccesses.OffsetBins) { O << "[" << It.first.getOffset() << "-" << It.first.getOffset() + It.first.getSize() - << "] : " << It.getSecond()->size() << "\n"; - for (auto &Acc : *It.getSecond()) { + << "] : " << It.getSecond().size() << "\n"; + for (auto AccIndex : It.getSecond()) { + auto &Acc = AllAccesses[AccIndex]; O << " - " << Acc.getKind() << " - " << *Acc.getLocalInst() << "\n"; if (Acc.getLocalInst() != Acc.getRemoteInst()) O << " --> " << *Acc.getRemoteInst() @@ -1225,7 +1224,8 @@ if (!AccessSize.isScalable()) Size = AccessSize.getFixedSize(); } - Changed = Changed | addAccess(A, Offset, Size, I, Content, Kind, Ty); + Changed = + Changed | AllAccesses.addAccess(A, Offset, Size, I, Content, Kind, Ty); return true; }; diff --git a/llvm/test/Transforms/Attributor/value-simplify-pointer-info.ll b/llvm/test/Transforms/Attributor/value-simplify-pointer-info.ll --- a/llvm/test/Transforms/Attributor/value-simplify-pointer-info.ll +++ b/llvm/test/Transforms/Attributor/value-simplify-pointer-info.ll @@ -111,26 +111,49 @@ ; TUNIT-NEXT: [[I:%.*]] = bitcast %struct.S* [[S]] to i8* ; TUNIT-NEXT: call void @llvm.lifetime.start.p0i8(i64 noundef 24, i8* nocapture nofree noundef nonnull align 4 dereferenceable(24) [[I]]) #[[ATTR15:[0-9]+]] ; TUNIT-NEXT: [[F1:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[S]], i64 0, i32 3 +; TUNIT-NEXT: store float 0x3FF19999A0000000, float* [[F1]], align 4, !tbaa [[TBAA7:![0-9]+]] ; TUNIT-NEXT: [[F2:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[S]], i64 0, i32 4 +; TUNIT-NEXT: store float 0x40019999A0000000, float* [[F2]], align 4, !tbaa [[TBAA10:![0-9]+]] ; TUNIT-NEXT: [[F3:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[S]], i64 0, i32 5 +; TUNIT-NEXT: store float 0x400A666660000000, float* [[F3]], align 4, !tbaa [[TBAA11:![0-9]+]] ; TUNIT-NEXT: [[I1:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[S]], i64 0, i32 0 ; TUNIT-NEXT: call void @write_arg(i32* nocapture nofree noundef nonnull writeonly align 4 dereferenceable(24) [[I1]], i32 noundef 1) #[[ATTR16:[0-9]+]] ; TUNIT-NEXT: [[I2:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[S]], i64 0, i32 1 ; TUNIT-NEXT: call void @write_arg(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(20) [[I2]], i32 noundef 2) #[[ATTR16]] ; TUNIT-NEXT: [[I3:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[S]], i64 0, i32 2 ; TUNIT-NEXT: call void @write_arg(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(16) [[I3]], i32 noundef 3) #[[ATTR16]] +; TUNIT-NEXT: [[F11:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[S]], i64 0, i32 3 +; TUNIT-NEXT: [[I4:%.*]] = load float, float* [[F11]], align 4, !tbaa [[TBAA7]] ; TUNIT-NEXT: [[F12:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[AGG_RESULT]], i64 0, i32 3 -; TUNIT-NEXT: store float 0x3FF19999A0000000, float* [[F12]], align 4, !tbaa [[TBAA7:![0-9]+]] +; TUNIT-NEXT: store float [[I4]], float* [[F12]], align 4, !tbaa [[TBAA7]] +; TUNIT-NEXT: [[F23:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[S]], i64 0, i32 4 +; TUNIT-NEXT: [[I5:%.*]] = load float, float* [[F23]], align 4, !tbaa [[TBAA10]] +; TUNIT-NEXT: [[MUL:%.*]] = fmul float [[I5]], 2.000000e+00 ; TUNIT-NEXT: [[F24:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[AGG_RESULT]], i64 0, i32 4 -; TUNIT-NEXT: store float 0x40119999A0000000, float* [[F24]], align 4, !tbaa [[TBAA10:![0-9]+]] +; TUNIT-NEXT: store float [[MUL]], float* [[F24]], align 4, !tbaa [[TBAA10]] +; TUNIT-NEXT: [[F35:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[S]], i64 0, i32 5 +; TUNIT-NEXT: [[I6:%.*]] = load float, float* [[F35]], align 4, !tbaa [[TBAA11]] +; TUNIT-NEXT: [[F16:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[S]], i64 0, i32 3 +; TUNIT-NEXT: [[I7:%.*]] = load float, float* [[F16]], align 4, !tbaa [[TBAA7]] +; TUNIT-NEXT: [[ADD:%.*]] = fadd float [[I6]], [[I7]] ; TUNIT-NEXT: [[F37:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[AGG_RESULT]], i64 0, i32 5 -; TUNIT-NEXT: store float 0x40119999A0000000, float* [[F37]], align 4, !tbaa [[TBAA11:![0-9]+]] +; TUNIT-NEXT: store float [[ADD]], float* [[F37]], align 4, !tbaa [[TBAA11]] +; TUNIT-NEXT: [[I18:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[S]], i64 0, i32 0 +; TUNIT-NEXT: [[I8:%.*]] = load i32, i32* [[I18]], align 4, !tbaa [[TBAA12:![0-9]+]] ; TUNIT-NEXT: [[I19:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[AGG_RESULT]], i64 0, i32 0 -; TUNIT-NEXT: store i32 1, i32* [[I19]], align 4, !tbaa [[TBAA12:![0-9]+]] +; TUNIT-NEXT: store i32 [[I8]], i32* [[I19]], align 4, !tbaa [[TBAA12]] +; TUNIT-NEXT: [[I210:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[S]], i64 0, i32 1 +; TUNIT-NEXT: [[I9:%.*]] = load i32, i32* [[I210]], align 4, !tbaa [[TBAA13:![0-9]+]] +; TUNIT-NEXT: [[MUL11:%.*]] = shl nsw i32 [[I9]], 1 ; TUNIT-NEXT: [[I212:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[AGG_RESULT]], i64 0, i32 1 -; TUNIT-NEXT: store i32 4, i32* [[I212]], align 4, !tbaa [[TBAA13:![0-9]+]] +; TUNIT-NEXT: store i32 [[MUL11]], i32* [[I212]], align 4, !tbaa [[TBAA13]] +; TUNIT-NEXT: [[I313:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[S]], i64 0, i32 2 +; TUNIT-NEXT: [[I10:%.*]] = load i32, i32* [[I313]], align 4, !tbaa [[TBAA14:![0-9]+]] +; TUNIT-NEXT: [[I114:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[S]], i64 0, i32 0 +; TUNIT-NEXT: [[I11:%.*]] = load i32, i32* [[I114]], align 4, !tbaa [[TBAA12]] +; TUNIT-NEXT: [[ADD15:%.*]] = add nsw i32 [[I10]], [[I11]] ; TUNIT-NEXT: [[I316:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[AGG_RESULT]], i64 0, i32 2 -; TUNIT-NEXT: store i32 4, i32* [[I316]], align 4, !tbaa [[TBAA14:![0-9]+]] +; TUNIT-NEXT: store i32 [[ADD15]], i32* [[I316]], align 4, !tbaa [[TBAA14]] ; TUNIT-NEXT: [[I12:%.*]] = bitcast %struct.S* [[S]] to i8* ; TUNIT-NEXT: call void @llvm.lifetime.end.p0i8(i64 noundef 24, i8* nocapture nofree noundef nonnull align 4 dereferenceable(24) [[I12]]) #[[ATTR15]] ; TUNIT-NEXT: ret void @@ -725,25 +748,40 @@ ; } ; define void @static_global_simplifiable_1(%struct.S* noalias sret(%struct.S) align 4 %agg.result) { -; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly +; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn ; TUNIT-LABEL: define {{[^@]+}}@static_global_simplifiable_1 -; TUNIT-SAME: (%struct.S* noalias nocapture nofree nonnull writeonly sret([[STRUCT_S:%.*]]) align 4 dereferenceable(24) [[AGG_RESULT:%.*]]) #[[ATTR5:[0-9]+]] { +; TUNIT-SAME: (%struct.S* noalias nocapture nofree nonnull writeonly sret([[STRUCT_S:%.*]]) align 4 dereferenceable(24) [[AGG_RESULT:%.*]]) #[[ATTR3]] { ; TUNIT-NEXT: entry: +; TUNIT-NEXT: store float 0x3FF19999A0000000, float* getelementptr inbounds ([[STRUCT_S]], %struct.S* @Gs1, i64 0, i32 3), align 4, !tbaa [[TBAA7]] +; TUNIT-NEXT: store float 0x40019999A0000000, float* getelementptr inbounds ([[STRUCT_S]], %struct.S* @Gs1, i64 0, i32 4), align 4, !tbaa [[TBAA10]] +; TUNIT-NEXT: store float 0x400A666660000000, float* getelementptr inbounds ([[STRUCT_S]], %struct.S* @Gs1, i64 0, i32 5), align 4, !tbaa [[TBAA11]] ; TUNIT-NEXT: call void @write_arg(i32* nocapture nofree noundef nonnull writeonly align 4 dereferenceable(24) getelementptr inbounds ([[STRUCT_S]], %struct.S* @Gs1, i32 0, i32 0), i32 noundef 1) #[[ATTR16]] ; TUNIT-NEXT: call void @write_arg(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(20) getelementptr inbounds ([[STRUCT_S]], %struct.S* @Gs1, i64 0, i32 1), i32 noundef 2) #[[ATTR16]] ; TUNIT-NEXT: call void @write_arg(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(16) getelementptr inbounds ([[STRUCT_S]], %struct.S* @Gs1, i64 0, i32 2), i32 noundef 3) #[[ATTR16]] +; TUNIT-NEXT: [[I:%.*]] = load float, float* getelementptr inbounds ([[STRUCT_S]], %struct.S* @Gs1, i64 0, i32 3), align 4, !tbaa [[TBAA7]] ; TUNIT-NEXT: [[F1:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[AGG_RESULT]], i64 0, i32 3 -; TUNIT-NEXT: store float 0x3FF19999A0000000, float* [[F1]], align 4, !tbaa [[TBAA7]] +; TUNIT-NEXT: store float [[I]], float* [[F1]], align 4, !tbaa [[TBAA7]] +; TUNIT-NEXT: [[I4:%.*]] = load float, float* getelementptr inbounds ([[STRUCT_S]], %struct.S* @Gs1, i64 0, i32 4), align 4, !tbaa [[TBAA10]] +; TUNIT-NEXT: [[MUL:%.*]] = fmul float [[I4]], 2.000000e+00 ; TUNIT-NEXT: [[F2:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[AGG_RESULT]], i64 0, i32 4 -; TUNIT-NEXT: store float 0x40119999A0000000, float* [[F2]], align 4, !tbaa [[TBAA10]] +; TUNIT-NEXT: store float [[MUL]], float* [[F2]], align 4, !tbaa [[TBAA10]] +; TUNIT-NEXT: [[I5:%.*]] = load float, float* getelementptr inbounds ([[STRUCT_S]], %struct.S* @Gs1, i64 0, i32 5), align 4, !tbaa [[TBAA11]] +; TUNIT-NEXT: [[I6:%.*]] = load float, float* getelementptr inbounds ([[STRUCT_S]], %struct.S* @Gs1, i64 0, i32 3), align 4, !tbaa [[TBAA7]] +; TUNIT-NEXT: [[ADD:%.*]] = fadd float [[I5]], [[I6]] ; TUNIT-NEXT: [[F3:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[AGG_RESULT]], i64 0, i32 5 -; TUNIT-NEXT: store float 0x40119999A0000000, float* [[F3]], align 4, !tbaa [[TBAA11]] +; TUNIT-NEXT: store float [[ADD]], float* [[F3]], align 4, !tbaa [[TBAA11]] +; TUNIT-NEXT: [[I7:%.*]] = load i32, i32* getelementptr inbounds ([[STRUCT_S]], %struct.S* @Gs1, i32 0, i32 0), align 4, !tbaa [[TBAA12]] ; TUNIT-NEXT: [[I1:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[AGG_RESULT]], i64 0, i32 0 -; TUNIT-NEXT: store i32 1, i32* [[I1]], align 4, !tbaa [[TBAA12]] +; TUNIT-NEXT: store i32 [[I7]], i32* [[I1]], align 4, !tbaa [[TBAA12]] +; TUNIT-NEXT: [[I8:%.*]] = load i32, i32* getelementptr inbounds ([[STRUCT_S]], %struct.S* @Gs1, i64 0, i32 1), align 4, !tbaa [[TBAA13]] +; TUNIT-NEXT: [[MUL1:%.*]] = shl nsw i32 [[I8]], 1 ; TUNIT-NEXT: [[I2:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[AGG_RESULT]], i64 0, i32 1 -; TUNIT-NEXT: store i32 4, i32* [[I2]], align 4, !tbaa [[TBAA13]] +; TUNIT-NEXT: store i32 [[MUL1]], i32* [[I2]], align 4, !tbaa [[TBAA13]] +; TUNIT-NEXT: [[I9:%.*]] = load i32, i32* getelementptr inbounds ([[STRUCT_S]], %struct.S* @Gs1, i64 0, i32 2), align 4, !tbaa [[TBAA14]] +; TUNIT-NEXT: [[I10:%.*]] = load i32, i32* getelementptr inbounds ([[STRUCT_S]], %struct.S* @Gs1, i32 0, i32 0), align 4, !tbaa [[TBAA12]] +; TUNIT-NEXT: [[ADD2:%.*]] = add nsw i32 [[I9]], [[I10]] ; TUNIT-NEXT: [[I3:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[AGG_RESULT]], i64 0, i32 2 -; TUNIT-NEXT: store i32 4, i32* [[I3]], align 4, !tbaa [[TBAA14]] +; TUNIT-NEXT: store i32 [[ADD2]], i32* [[I3]], align 4, !tbaa [[TBAA14]] ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: nofree nosync nounwind willreturn @@ -834,7 +872,7 @@ define void @static_global_simplifiable_2() { ; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly ; TUNIT-LABEL: define {{[^@]+}}@static_global_simplifiable_2 -; TUNIT-SAME: () #[[ATTR5]] { +; TUNIT-SAME: () #[[ATTR5:[0-9]+]] { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: br label [[FOR_COND:%.*]] ; TUNIT: for.cond: @@ -2223,6 +2261,7 @@ ; TUNIT: loop: ; TUNIT-NEXT: [[P:%.*]] = phi i8* [ bitcast (i32* @a2 to i8*), [[ENTRY:%.*]] ], [ [[G:%.*]], [[LOOP]] ] ; TUNIT-NEXT: [[I:%.*]] = phi i8 [ 0, [[ENTRY]] ], [ [[O:%.*]], [[LOOP]] ] +; TUNIT-NEXT: store i8 1, i8* [[P]], align 2 ; TUNIT-NEXT: [[G]] = getelementptr i8, i8* bitcast (i32* @a2 to i8*), i64 2 ; TUNIT-NEXT: [[O]] = add nsw i8 [[I]], 1 ; TUNIT-NEXT: [[C:%.*]] = icmp eq i8 [[O]], 7 @@ -2241,6 +2280,7 @@ ; CGSCC: loop: ; CGSCC-NEXT: [[P:%.*]] = phi i8* [ bitcast (i32* @a2 to i8*), [[ENTRY:%.*]] ], [ [[G:%.*]], [[LOOP]] ] ; CGSCC-NEXT: [[I:%.*]] = phi i8 [ 0, [[ENTRY]] ], [ [[O:%.*]], [[LOOP]] ] +; CGSCC-NEXT: store i8 1, i8* [[P]], align 2 ; CGSCC-NEXT: [[G]] = getelementptr i8, i8* bitcast (i32* @a2 to i8*), i64 2 ; CGSCC-NEXT: [[O]] = add nsw i8 [[I]], 1 ; CGSCC-NEXT: [[C:%.*]] = icmp eq i8 [[O]], 7 @@ -2281,6 +2321,7 @@ ; TUNIT: loop: ; TUNIT-NEXT: [[P:%.*]] = phi i8* [ bitcast (i32* @a3 to i8*), [[ENTRY:%.*]] ], [ [[G:%.*]], [[LOOP]] ] ; TUNIT-NEXT: [[I:%.*]] = phi i8 [ 0, [[ENTRY]] ], [ [[O:%.*]], [[LOOP]] ] +; TUNIT-NEXT: store i8 1, i8* [[P]], align 2 ; TUNIT-NEXT: [[G]] = getelementptr i8, i8* bitcast (i32* @a3 to i8*), i64 2 ; TUNIT-NEXT: [[O]] = add nsw i8 [[I]], 1 ; TUNIT-NEXT: [[C:%.*]] = icmp eq i8 [[O]], 7 @@ -2302,6 +2343,7 @@ ; CGSCC: loop: ; CGSCC-NEXT: [[P:%.*]] = phi i8* [ bitcast (i32* @a3 to i8*), [[ENTRY:%.*]] ], [ [[G:%.*]], [[LOOP]] ] ; CGSCC-NEXT: [[I:%.*]] = phi i8 [ 0, [[ENTRY]] ], [ [[O:%.*]], [[LOOP]] ] +; CGSCC-NEXT: store i8 1, i8* [[P]], align 2 ; CGSCC-NEXT: [[G]] = getelementptr i8, i8* bitcast (i32* @a3 to i8*), i64 2 ; CGSCC-NEXT: [[O]] = add nsw i8 [[I]], 1 ; CGSCC-NEXT: [[C:%.*]] = icmp eq i8 [[O]], 7