diff --git a/llvm/include/llvm/Analysis/MustExecute.h b/llvm/include/llvm/Analysis/MustExecute.h --- a/llvm/include/llvm/Analysis/MustExecute.h +++ b/llvm/include/llvm/Analysis/MustExecute.h @@ -24,12 +24,15 @@ #define LLVM_ANALYSIS_MUSTEXECUTE_H #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/Analysis/EHPersonalities.h" #include "llvm/Analysis/InstructionPrecedenceTracking.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Instruction.h" +#include "llvm/Support/Allocator.h" namespace llvm { @@ -182,9 +185,318 @@ FORWARD = 1, }; -/// Must be executed iterators visit stretches of instructions that are -/// guaranteed to be executed together, potentially with other instruction -/// executed in-between. +/// Must be executed intervals are stretches of instructions that are +/// guaranteed to be executed together without any other instruction +/// executed in-between. In addition, an interval `I` can end in a link into +/// another interval `J`. This means that the instructions of the interval +/// `J`, starting from the position the link points to, are always executed +/// before or respectively after the instruction in `I`. Note that there +/// might be other instructions executed in-between linked intervals. +/// +/// Given the following code, and assuming all statements are single +/// instructions which transfer execution to the successor (see +/// isGuaranteedToTransferExecutionToSuccessor), there are three intervals as +/// shown in the table below. +/// +/// \code +/// A; +/// B; +/// if (...) { +/// C; +/// D; +/// } +/// E; +/// \endcode +/// +/// Int | Previous | Instructions | Next +/// (1) | None | A ; B | (3) +/// (2) | (1) | C ; D | (3) +/// (3) | (1) | E | None +/// +/// From the above table one can derive that C and D are always executed with +/// A, B, and E. However, A, B, and E, are not necessarily executed with C and +/// D but always together. +/// +/// +/// Below is an extended example with instructions F and G and we assume F might +/// not transfer execution to it's successor G. The resulting intervals are +/// shown in the table below. +/// +/// \code +/// A; +/// B; +/// if (...) { +/// C; +/// D; +/// } +/// E; +/// F; // Might not transfer execution to its successor G. +/// G; +/// \endcode +/// +/// Int | Previous | Instructions | Next +/// (1) | None | A ; B | (3) +/// (2) | (1) | C ; D | (3) +/// (3) | (1) | E ; F | None +/// (4) | (3) | G | None +/// +/// As indicated by the table, E and F are executed always together with A and B +/// but not C, D, or G. However, G is always executed with A, B, E, and F. +/// +/// +/// A more complex example involving conditionals, loops, break, and continue +/// is shown below. We again assume all instructions will transmit control to +/// the successor and we assume we can prove the loops to be finite. We omit +/// non-trivial branch conditions as the exploration is oblivious to them. +/// Constant branches are assumed to be unconditional in the CFG. The resulting +/// intervals are shown in the table below. +/// +/// \code +/// A; +/// while (true) { +/// B; +/// if (...) +/// C; +/// if (...) +/// continue; +/// D; +/// if (...) +/// break; +/// do { +/// if (...) +/// continue; +/// E; +/// } while (...) +/// F; +/// } +/// G; +/// \endcode +/// +/// Int | Previous | Instructions | Next +/// (1) | None | A | (7) +/// (2) | (1) | B | (4) +/// (3) | (2) | C | (4) +/// (4) | (2) | D | (7) +/// (5) | (4) | E | (6) +/// (6) | (4) | F | (2) +/// (7) | (1) | G | None +/// +/// +/// Note that the examples show optimal intervals but not necessarily the ones +/// derived by the implementation. Also note that an interval has a direction +/// so there would be an interval for the forward direction and one for the +/// backward direction. The former is linked as described by the Next column in +/// the examples the latter as described by the Previous column. +/// +struct MustBeExecutedInterval { + + /// A position on an interval. + struct Position { + + Position(MustBeExecutedInterval *Interval = nullptr) + : Interval(Interval), Offset(0) {} + + /// Return the instruction at this position. + const Instruction *getInstruction() const { + assert(bool(*this) && + "Invalid positions do not have an associated instruction!"); + return (*Interval)[Offset]; + } + + /// Return the interval at this position. + MustBeExecutedInterval *getInterval() const { return Interval; } + unsigned getOffset() const { return Offset; } + + /// Allow valid positions to evaluate to 'true' and invalid ones to 'false' + /// when a position is converted to a boolean. + operator bool() const { return Interval; } + + /// Equality operator. + bool operator==(const Position &Other) const { + return Interval == Other.Interval && Offset == Other.Offset; + } + + /// Advance the position in the direction \p Dir. + /// + /// If there is no next instruction in the direction, this will result in + /// an invalid position, hence the interval will be a nullptr. + ///{ + Position &operator++() { return (*this) += 1; } + + Position operator++(int) { + Position tmp(*this); + ++(*this); + return tmp; + } + + Position &operator+=(unsigned N) { + if (!bool(*this)) + return *this; + + // If the offset adjusted wrt. direction is still in the associated + // interval, we adjust the offset and are done. Otherwise, we try to go to + // the linked interval in the direction and set the offset accordingly, + // hence to the very beginning or end depending on the direction. If there + // is no linked interval, all members will be set to NULL and the position + // becomes invalid. + if (Interval->isInbounds(Offset + N)) { + Offset += N; + return *this; + } + + if (Interval->getPosInNextInterval()) { + unsigned Advanced = Interval->size() - Offset - 1; + assert(Interval->size() >= Offset + 1 && "Unexpected offset!"); + assert(N >= Advanced + 1 && "Unexpected offset!"); + *this = Interval->getPosInNextInterval(); + return (*this) += N - Advanced - 1; + } + + Interval = nullptr; + Offset = 0; + return *this; + } + ///} + + /// Print method for debugging purposes. + void print(raw_ostream &OS, bool Detailed = false) const { + OS << "{" << Offset << "@"; + if (Interval) + Interval->print(OS, false); + else + OS << "[NONE:NONE:N]"; + OS << "}"; + } + + private: + /// The interval this position is currently associated with. + MustBeExecutedInterval *Interval; + + /// The current offset from the beginning of the associated interval. + unsigned Offset; + + friend struct MustBeExecutedContextExplorer; + }; + + /// Constructor for a new interval centered around \p I. + MustBeExecutedInterval(const Instruction &I, ExplorationDirection Dir) + : Dir(Dir) { + Insts.insert(&I); + } + + /// Return the interval that contains the instructions executed after/before + /// the ones in this interval. + MustBeExecutedInterval *getNextInterval() const { + return PosInNextInterval.getInterval(); + } + + /// Return the position in the next interval at which execution is known to + /// continue. + const Position &getPosInNextInterval() const { return PosInNextInterval; } + + /// Return the execution direction of this interval. + ExplorationDirection getDirection() const { return Dir; } + + /// Return true if \p Offset is part of this interval, false otherwise. + bool isInbounds(unsigned Offset) { return Offset < size(); } + + /// Subscript operator to allow easy access to the instructions based on their + /// offset. + const Instruction *operator[](unsigned Offset) const { return Insts[Offset]; } + + /// Return true if \p I is in this interval + bool count(const Instruction &I) const { return Insts.count(&I); } + + /// Print method for debugging purposes. + void print(raw_ostream &OS, bool Detailed = false) const { + OS << "[" << this << "(" << size() << ") + "; + PosInNextInterval.print(OS); + OS << " : " << unsigned(getDirection()) << "]"; + if (Detailed) { + OS << "\n"; + for (auto *I : Insts) + OS << "- " << *I << "\n"; + } + } + + /// Return the number of instructions contained in this interval. + unsigned size() const { return Insts.size(); } + + /// Return true if this interval is final, that is no more extensions are + /// possible. + bool isFinalized() const { return IsFinalized; } + +private: + /// Indicate the interval is not going to be modified later on. + void finalize() { + assert(!IsFinalized && "Interval was marked finalized already!"); + IsFinalized = true; + } + + /// Set the next interval, return true if successful, false otherwise. + bool setPosInNextInterval(const Position &P) { + assert(!PosInNextInterval && "Interval already has a link to another one!"); + assert(!IsFinalized && "Interval was marked finalized already!"); + + // Do not create cyclic lists. The check is potentially expensive but should + // not be in practise because we usually explore the context to the fullest + // which will prevent an extension after a "PrevInterval" was set. However, + // to prevent the quadratic worst case, we have a cut-off. + MustBeExecutedInterval *MBEI = P.getInterval(); + if (HasPrevInterval || MBEI == this) { + unsigned MaxChainLength = 6; + while (MBEI) { + if (MBEI == this || (--MaxChainLength == 0)) + return false; + MBEI = MBEI->getNextInterval(); + } + } + + PosInNextInterval = P; + PosInNextInterval.getInterval()->HasPrevInterval = true; + return true; + } + + /// Extend the interval by \p I + bool extend(const Instruction &I) { + assert(!PosInNextInterval && + "Cannot advance the front if a next interval is already set!"); + assert(!IsFinalized && "Interval was marked finalized already!"); + + // If we have seen the instruction we will not add it. This happens when we + // encountered an endless loop contained entirely in this interval. + return Insts.insert(&I); + } + + /// The vectors that represent the instructions in the interval. + SmallSetVector Insts; + + /// The instruction executed next after the ones in Insts. + Position PosInNextInterval; + + /// The direction in which the instruction in this interval are executed. + /// Forward means Insts[i] is executed before Insts[i+1], backward means it is + /// the other way around. + ExplorationDirection Dir; + + /// A flag to indicate the interval is not going to change anymore. Used to + /// prevent us from trying to extend it after an earlier attempted failed. + bool IsFinalized = false; + + /// Flag to indicate that another interval points into this one. Used to + /// determine if we need to check for potential endless loops or if there + /// cannot be any. + bool HasPrevInterval = false; + + friend struct MustBeExecutedContextExplorer; +}; + +/// Must be executed iterators visit stretches of instructions, so called +/// must-be-executed-intervals, that are guaranteed to be executed together, +/// potentially with other instruction executed in-between. +/// +/// See MustBeExecutedInterval for a discussion about the intervals of these +/// examples. /// /// Given the following code, and assuming all statements are single /// instructions which transfer execution to the successor (see @@ -272,6 +584,11 @@ /// derived by the explorer depending on the available CFG analyses (see /// MustBeExecutedContextExplorer). Also note that we, depending on the options, /// the visit set can contain instructions from other functions. +/// +/// Depending on the template parameter \p CachedOnly, the iterator will either +/// only visit existing instructions in the interval (\p CachedOnly == true) or +/// also use the MustBeExecutedContextExplorer to extend the intervals if +/// needed. struct MustBeExecutedIterator { /// Type declarations that make his class an input iterator. ///{ @@ -282,32 +599,12 @@ typedef std::input_iterator_tag iterator_category; ///} - using ExplorerTy = MustBeExecutedContextExplorer; - - MustBeExecutedIterator(const MustBeExecutedIterator &Other) - : Visited(Other.Visited), Explorer(Other.Explorer), - CurInst(Other.CurInst), Head(Other.Head), Tail(Other.Tail) {} - - MustBeExecutedIterator(MustBeExecutedIterator &&Other) - : Visited(std::move(Other.Visited)), Explorer(Other.Explorer), - CurInst(Other.CurInst), Head(Other.Head), Tail(Other.Tail) {} - - MustBeExecutedIterator &operator=(MustBeExecutedIterator &&Other) { - if (this != &Other) { - std::swap(Visited, Other.Visited); - std::swap(CurInst, Other.CurInst); - std::swap(Head, Other.Head); - std::swap(Tail, Other.Tail); - } - return *this; - } - - ~MustBeExecutedIterator() {} - - /// Pre- and post-increment operators. + /// Pre- and post-increment operators. Both can cause the underlying intervals + /// to be extended if this is not a cached-only iterator, that is if an + /// explorer was provided at creation time. ///{ MustBeExecutedIterator &operator++() { - CurInst = advance(); + advance(); return *this; } @@ -321,7 +618,7 @@ /// Equality and inequality operators. Note that we ignore the history here. ///{ bool operator==(const MustBeExecutedIterator &Other) const { - return CurInst == Other.CurInst && Head == Other.Head && Tail == Other.Tail; + return Pos[0] == Other.Pos[0] && Pos[1] == Other.Pos[1]; } bool operator!=(const MustBeExecutedIterator &Other) const { @@ -330,50 +627,58 @@ ///} /// Return the underlying instruction. - const Instruction *&operator*() { return CurInst; } - const Instruction *getCurrentInst() const { return CurInst; } + const Instruction *operator*() const { return getCurrentInst(); } + const Instruction *getCurrentInst() const { return Pos[0].getInstruction(); } + + /// Return true if \p I is known to be found by this iterator, thus to be + /// executed with the instruction this iterator was created for. If the known + /// context does not contain \p I, the non-const version will try to use the + /// Explorer, if available, to extend it. + bool isExecutedWith(const Instruction &I) const; - /// Return true if \p I was encountered by this iterator already. - bool count(const Instruction *I) const { - return Visited.count({I, ExplorationDirection::FORWARD}) || - Visited.count({I, ExplorationDirection::BACKWARD}); + /// Configurable print method for debugging purposes. + /// + /// \param OS The stream we print to. + /// \param PrintInst If set, the current instruction is printed. + /// \param PrintInterval If set, the interval containing the current + /// instruction is printed. + void print(raw_ostream &OS, bool PrintInst, bool PrintInterval) const { + if (PrintInst && getCurrentInst()) + OS << "[" << *getCurrentInst() << "]"; + + if (PrintInterval) + Pos[0].print(OS, false); } private: - using VisitedSetTy = - DenseSet>; - /// Private constructors. - MustBeExecutedIterator(ExplorerTy &Explorer, const Instruction *I); - - /// Reset the iterator to its initial state pointing at \p I. - void reset(const Instruction *I); + MustBeExecutedIterator() {} + MustBeExecutedIterator(const MustBeExecutedInterval::Position &FwdPos, + const MustBeExecutedInterval::Position &BwdPos, + MustBeExecutedContextExplorer *Explorer) + : Explorer(Explorer) { + Pos[0] = FwdPos; + Pos[1] = BwdPos; + } - /// Reset the iterator to point at \p I, keep cached state. - void resetInstruction(const Instruction *I); + /// Advance one of the underlying positions (Head or Tail) potentially after + /// exploring the context further (using Explorer). If this is not possible + /// the positions (Pos) are invalidated. + bool advance(); - /// Try to advance one of the underlying positions (Head or Tail). + /// The explorer that can be used to explore the context further if an end is + /// found. If none is given the iterator will only traverse the + /// existing/cached context otherwise the explorer is used to explore further. /// - /// \return The next instruction in the must be executed context, or nullptr - /// if none was found. - const Instruction *advance(); - - /// A set to track the visited instructions in order to deal with endless - /// loops and recursion. - VisitedSetTy Visited; - - /// A reference to the explorer that created this iterator. - ExplorerTy &Explorer; + /// TODO: Determine if we should pass the explorer where needed. + MustBeExecutedContextExplorer *Explorer = nullptr; - /// The instruction we are currently exposing to the user. There is always an - /// instruction that we know is executed with the given program point, - /// initially the program point itself. - const Instruction *CurInst; - - /// Two positions that mark the program points where this iterator will look - /// for the next instruction. Note that the current instruction is either the - /// one pointed to by Head, Tail, or both. - const Instruction *Head, *Tail; + /// Two interval positions that mark the program points where this iterator + /// will look for the next instruction. Note that the current instruction is + /// the one pointed to by Pos[0]. Once Pos[0] is explored to the fullest and + /// becomes invalid we swap the two positions. If both positions are invalid + /// the iterator traversed the entire context. + MustBeExecutedInterval::Position Pos[2]; friend struct MustBeExecutedContextExplorer; }; @@ -389,6 +694,7 @@ /// the expected use case involves few iterators for "far apart" instructions. /// If that changes, we should consider caching more intermediate results. struct MustBeExecutedContextExplorer { + using IntervalPosition = MustBeExecutedInterval::Position; /// In the description of the parameters we use PP to denote a program point /// for which the must be executed context is explored, or put differently, @@ -414,11 +720,12 @@ : ExploreInterBlock(ExploreInterBlock), ExploreCFGForward(ExploreCFGForward), ExploreCFGBackward(ExploreCFGBackward), LIGetter(LIGetter), - DTGetter(DTGetter), PDTGetter(PDTGetter), EndIterator(*this, nullptr) {} + DTGetter(DTGetter), PDTGetter(PDTGetter) {} - /// Clean up the dynamically allocated iterators. + /// Clean up the dynamically allocated intervals. ~MustBeExecutedContextExplorer() { - DeleteContainerSeconds(InstructionIteratorMap); + for (MustBeExecutedInterval *Interval : Intervals) + Interval->~MustBeExecutedInterval(); } /// Iterator-based interface. \see MustBeExecutedIterator. @@ -427,35 +734,39 @@ using const_iterator = const MustBeExecutedIterator; /// Return an iterator to explore the context around \p PP. - iterator &begin(const Instruction *PP) { - auto *&It = InstructionIteratorMap[PP]; - if (!It) - It = new iterator(*this, PP); - return *It; + iterator begin(const Instruction &PP) { + return iterator( + getOrCreateIntervalPosition(PP, ExplorationDirection::FORWARD), + getOrCreateIntervalPosition(PP, ExplorationDirection::BACKWARD), this); } /// Return an iterator to explore the cached context around \p PP. - const_iterator &begin(const Instruction *PP) const { - return *InstructionIteratorMap.lookup(PP); + const_iterator cached_begin(const Instruction &PP) const { + return iterator(lookupIntervalPosition(PP, ExplorationDirection::FORWARD), + lookupIntervalPosition(PP, ExplorationDirection::BACKWARD), + nullptr); } /// Return an universal end iterator. ///{ - iterator &end() { return EndIterator; } - iterator &end(const Instruction *) { return EndIterator; } + iterator end() { return iterator(); } + iterator end(const Instruction &) { return iterator(); } - const_iterator &end() const { return EndIterator; } - const_iterator &end(const Instruction *) const { return EndIterator; } + const_iterator cached_end() const { return const_iterator(); } + const_iterator cached_end(const Instruction &) const { + return const_iterator(); + } ///} /// Return an iterator range to explore the context around \p PP. - llvm::iterator_range range(const Instruction *PP) { + llvm::iterator_range range(const Instruction &PP) { return llvm::make_range(begin(PP), end(PP)); } /// Return an iterator range to explore the cached context around \p PP. - llvm::iterator_range range(const Instruction *PP) const { - return llvm::make_range(begin(PP), end(PP)); + llvm::iterator_range + cached_range(const Instruction &PP) const { + return llvm::make_range(cached_begin(PP), cached_end(PP)); } ///} @@ -463,56 +774,52 @@ /// /// This method will evaluate \p Pred and return /// true if \p Pred holds in every instruction. - bool checkForAllContext(const Instruction *PP, - function_ref Pred) { - for (auto EIt = begin(PP), EEnd = end(PP); EIt != EEnd; ++EIt) - if (!Pred(*EIt)) - return false; - return true; - } - - /// Helper to look for \p I in the context of \p PP. /// - /// The context is expanded until \p I was found or no more expansion is - /// possible. - /// - /// \returns True, iff \p I was found. - bool findInContextOf(const Instruction *I, const Instruction *PP) { - auto EIt = begin(PP), EEnd = end(PP); - return findInContextOf(I, EIt, EEnd); + ///{ + bool checkForAllInContext(const Instruction &PP, + function_ref Pred) { + return llvm::all_of(range(PP), Pred); + } + bool + checkForCachedInContext(const Instruction &PP, + function_ref Pred) const { + return llvm::all_of(cached_range(PP), Pred); } + ///} - /// Helper to look for \p I in the context defined by \p EIt and \p EEnd. + /// Helper to look for \p I in the context of \p PP. /// - /// The context is expanded until \p I was found or no more expansion is - /// possible. + /// In the non-const variant the context is expanded until \p I was found or + /// no more expansion is possible. If a dominator tree getter was provided it + /// is used to circumvent the search. /// /// \returns True, iff \p I was found. - bool findInContextOf(const Instruction *I, iterator &EIt, iterator &EEnd) { - bool Found = EIt.count(I); - while (!Found && EIt != EEnd) - Found = (++EIt).getCurrentInst() == I; - return Found; + bool findInContextOf(const Instruction &I, const Instruction &PP) { + const DominatorTree *DT = DTGetter(*I.getFunction()); + if (DT && DT->dominates(&I, &PP)) + return true; + return begin(PP).isExecutedWith(I); } /// Return the next instruction that is guaranteed to be executed after \p PP. /// - /// \param It The iterator that is used to traverse the must be - /// executed context. /// \param PP The program point for which the next instruction /// that is guaranteed to execute is determined. - const Instruction * - getMustBeExecutedNextInstruction(MustBeExecutedIterator &It, - const Instruction *PP); + const Instruction *getMustBeExecutedNextInstruction(const Instruction &PP) { + const IntervalPosition &Pos = + getOrCreateIntervalPosition(PP, ExplorationDirection::FORWARD); + return (++iterator(Pos, IntervalPosition(), this)).getCurrentInst(); + } + /// Return the previous instr. that is guaranteed to be executed before \p PP. /// - /// \param It The iterator that is used to traverse the must be - /// executed context. /// \param PP The program point for which the previous instr. /// that is guaranteed to execute is determined. - const Instruction * - getMustBeExecutedPrevInstruction(MustBeExecutedIterator &It, - const Instruction *PP); + const Instruction *getMustBeExecutedPrevInstruction(const Instruction &PP) { + const IntervalPosition &Pos = + getOrCreateIntervalPosition(PP, ExplorationDirection::FORWARD); + return (++iterator(Pos, IntervalPosition(), this)).getCurrentInst(); + } /// Find the next join point from \p InitBB in forward direction. const BasicBlock *findForwardJoinPoint(const BasicBlock *InitBB); @@ -528,7 +835,49 @@ const bool ExploreCFGBackward; ///} + /// Return the next position that is guaranteed to be executed (at some + /// point but for sure) after \p P in the direction of the underlying + /// interval. If none is known returns an invalid position. Note that \p P has + /// to be a valid position. + IntervalPosition exploreNextPosition(const IntervalPosition &P); + private: + /// Return an instruction that is known to be executed after \p PP. + const Instruction *exploreNextExecutedInstruction(const Instruction &PP); + + /// Return an instruction that is known to be executed before \p PP. + const Instruction *explorePrevExecutedInstruction(const Instruction &PP); + + /// Lookup the interval position for the instruction \p PP. + IntervalPosition lookupIntervalPosition(const Instruction &PP, + ExplorationDirection Dir) const { + return InstPositionMap[unsigned(Dir)].lookup(&PP); + } + + /// Lookup or create a new interval position for the instruction \p PP. + /// + /// If \p PP was explored before, its interval position already exists and it + /// is returned. If \p PP was not explored before, we create a new interval + /// for it and make \p PP the center of the interval, thus the position at + /// offset 0. The new position is cached. + /// + /// \returns A position for which the current instruction is \p PP. + IntervalPosition getOrCreateIntervalPosition(const Instruction &PP, + ExplorationDirection Dir) { + IntervalPosition &Pos = InstPositionMap[unsigned(Dir)][&PP]; + + if (!Pos) { + MustBeExecutedInterval *Interval = + new (Allocator) MustBeExecutedInterval(PP, Dir); + Intervals.push_back(Interval); + Pos = IntervalPosition(Interval); + } + + assert(Pos.getInstruction() == &PP && + "Unexpected instruction at position!"); + return Pos; + } + /// Getters for common CFG analyses: LoopInfo, DominatorTree, and /// PostDominatorTree. ///{ @@ -537,18 +886,17 @@ GetterTy PDTGetter; ///} - /// Map to cache isGuaranteedToTransferExecutionToSuccessor results. - DenseMap> BlockTransferMap; - /// Map to cache containsIrreducibleCFG results. - DenseMap> IrreducibleControlMap; + DenseMap> IrreducibleControlMap; + + /// Two map from instructions to associated positions, one for each direction. + DenseMap InstPositionMap[2]; - /// Map from instructions to associated must be executed iterators. - DenseMap - InstructionIteratorMap; + /// All intervals allocated with the BumpPtrAllocator Allocator. + SmallVector Intervals; - /// A unique end iterator. - MustBeExecutedIterator EndIterator; + /// The allocator used to allocate memory, e.g. for intervals. + BumpPtrAllocator Allocator; }; } // namespace llvm diff --git a/llvm/lib/Analysis/MustExecute.cpp b/llvm/lib/Analysis/MustExecute.cpp --- a/llvm/lib/Analysis/MustExecute.cpp +++ b/llvm/lib/Analysis/MustExecute.cpp @@ -386,7 +386,7 @@ for (Function &F : M) { for (Instruction &I : instructions(F)) { dbgs() << "-- Explore context of: " << I << "\n"; - for (const Instruction *CI : Explorer.range(&I)) + for (const Instruction *CI : Explorer.range(I)) dbgs() << " [F: " << CI->getFunction()->getName() << "] " << *CI << "\n"; } @@ -597,10 +597,6 @@ // for futher checks. if (!F.hasFnAttribute(Attribute::WillReturn) || !F.doesNotThrow()) { - auto BlockTransfersExecutionToSuccessor = [](const BasicBlock *BB) { - return isGuaranteedToTransferExecutionToSuccessor(BB); - }; - SmallPtrSet Visited; while (!Worklist.empty()) { const BasicBlock *ToBB = Worklist.pop_back_val(); @@ -628,8 +624,8 @@ // Make sure the block has no instructions that could stop control // transfer. - bool TransfersExecution = getOrCreateCachedOptional( - ToBB, BlockTransferMap, BlockTransfersExecutionToSuccessor, ToBB); + bool TransfersExecution = + isGuaranteedToTransferExecutionToSuccessor(ToBB); if (!TransfersExecution) return nullptr; @@ -641,6 +637,7 @@ LLVM_DEBUG(dbgs() << "\tJoin block: " << JoinBB->getName() << "\n"); return JoinBB; } + const BasicBlock * MustBeExecutedContextExplorer::findBackwardJoinPoint(const BasicBlock *InitBB) { const LoopInfo *LI = LIGetter(*InitBB->getParent()); @@ -709,14 +706,12 @@ } const Instruction * -MustBeExecutedContextExplorer::getMustBeExecutedNextInstruction( - MustBeExecutedIterator &It, const Instruction *PP) { - if (!PP) - return PP; - LLVM_DEBUG(dbgs() << "Find next instruction for " << *PP << "\n"); +MustBeExecutedContextExplorer::exploreNextExecutedInstruction( + const Instruction &PP) { + LLVM_DEBUG(dbgs() << "Find next instruction for " << PP << "\n"); // If we explore only inside a given basic block we stop at terminators. - if (!ExploreInterBlock && PP->isTerminator()) { + if (!ExploreInterBlock && PP.isTerminator()) { LLVM_DEBUG(dbgs() << "\tReached terminator in intra-block mode, done\n"); return nullptr; } @@ -724,7 +719,7 @@ // If we do not traverse the call graph we check if we can make progress in // the current function. First, check if the instruction is guaranteed to // transfer execution to the successor. - bool TransfersExecution = isGuaranteedToTransferExecutionToSuccessor(PP); + bool TransfersExecution = isGuaranteedToTransferExecutionToSuccessor(&PP); if (!TransfersExecution) return nullptr; @@ -732,33 +727,36 @@ // after this one that is executed next if control is transfered. If not, // we can try to go back to a call site we entered earlier. If none exists, we // do not know any instruction that has to be executd next. - if (!PP->isTerminator()) { - const Instruction *NextPP = PP->getNextNode(); - LLVM_DEBUG(dbgs() << "\tIntermediate instruction does transfer control\n"); + if (!PP.isTerminator()) { + const Instruction *NextPP = PP.getNextNode(); + LLVM_DEBUG(dbgs() << "\tIntermediate instruction does transfer control: " + << *NextPP << "\n"); return NextPP; } // Finally, we have to handle terminators, trivial ones first. - assert(PP->isTerminator() && "Expected a terminator!"); + assert(PP.isTerminator() && "Expected a terminator!"); // A terminator without a successor is not handled yet. - if (PP->getNumSuccessors() == 0) { + unsigned NumSuccBBs = PP.getNumSuccessors(); + if (NumSuccBBs == 0) { LLVM_DEBUG(dbgs() << "\tUnhandled terminator\n"); return nullptr; } // A terminator with a single successor, we will continue at the beginning of // that one. - if (PP->getNumSuccessors() == 1) { - LLVM_DEBUG( - dbgs() << "\tUnconditional terminator, continue with successor\n"); - return &PP->getSuccessor(0)->front(); + if (NumSuccBBs == 1) { + const Instruction *NextPP = &PP.getSuccessor(0)->front(); + LLVM_DEBUG(dbgs() << "\tUnconditional terminator, continue with successor: " + << *NextPP << "\n"); + return NextPP; } // Multiple successors mean we need to find the join point where control flow // converges again. We use the findForwardJoinPoint helper function with // information about the function and helper analyses, if available. - if (const BasicBlock *JoinBB = findForwardJoinPoint(PP->getParent())) + if (const BasicBlock *JoinBB = findForwardJoinPoint(PP.getParent())) return &JoinBB->front(); LLVM_DEBUG(dbgs() << "\tNo join point found\n"); @@ -766,32 +764,28 @@ } const Instruction * -MustBeExecutedContextExplorer::getMustBeExecutedPrevInstruction( - MustBeExecutedIterator &It, const Instruction *PP) { - if (!PP) - return PP; +MustBeExecutedContextExplorer::explorePrevExecutedInstruction( + const Instruction &PP) { - bool IsFirst = !(PP->getPrevNode()); - LLVM_DEBUG(dbgs() << "Find next instruction for " << *PP - << (IsFirst ? " [IsFirst]" : "") << "\n"); + const Instruction *PrevPP = PP.getPrevNode(); + LLVM_DEBUG(dbgs() << "Find previous instruction for " << PP + << (!PrevPP ? " [IsFirst]" : "") << "\n"); // If we explore only inside a given basic block we stop at the first // instruction. - if (!ExploreInterBlock && IsFirst) { + if (!ExploreInterBlock && !PrevPP) { LLVM_DEBUG(dbgs() << "\tReached block front in intra-block mode, done\n"); return nullptr; } // The block and function that contains the current position. - const BasicBlock *PPBlock = PP->getParent(); + const BasicBlock *PPBlock = PP.getParent(); // If we are inside a block we know what instruction was executed before, the // previous one. - if (!IsFirst) { - const Instruction *PrevPP = PP->getPrevNode(); - LLVM_DEBUG( - dbgs() << "\tIntermediate instruction, continue with previous\n"); - // We did not enter a callee so we simply return the previous instruction. + if (PrevPP) { + LLVM_DEBUG(dbgs() << "\tIntermediate instruction, continue with previous: " + << *PrevPP << "\n"); return PrevPP; } @@ -806,38 +800,79 @@ return nullptr; } -MustBeExecutedIterator::MustBeExecutedIterator( - MustBeExecutedContextExplorer &Explorer, const Instruction *I) - : Explorer(Explorer), CurInst(I) { - reset(I); -} +MustBeExecutedContextExplorer::IntervalPosition +MustBeExecutedContextExplorer::exploreNextPosition(const IntervalPosition &P) { + assert(P.getInterval() && P.getInstruction() && "Unexpected position state!"); + + assert(!(++IntervalPosition(P)) && + "Exploration called on already explored interval"); + + MustBeExecutedInterval &Interval = *P.getInterval(); + assert(!Interval.getNextInterval() && + "Exploration called on already explored interval"); + if (Interval.isFinalized()) + return IntervalPosition(); + + const Instruction *NewI = nullptr; + ExplorationDirection Dir = Interval.getDirection(); + if (Dir == ExplorationDirection::FORWARD) + NewI = exploreNextExecutedInstruction(*P.getInstruction()); + else + NewI = explorePrevExecutedInstruction(*P.getInstruction()); + + if (NewI) { + // We found an instruction executed after/before the given position P. If + // the instruction is not part of an interval yet we add it to the one of + // the position P, otherwise the one of the position Pis linked to the + // position of the instruction executed after/before. + IntervalPosition &NewIPos = InstPositionMap[unsigned(Dir)][NewI]; + if (!NewIPos && Interval.extend(*NewI)) { + NewIPos = P; + return ++NewIPos; + } -void MustBeExecutedIterator::reset(const Instruction *I) { - Visited.clear(); - resetInstruction(I); -} + if (NewIPos && Interval.setPosInNextInterval(NewIPos)) + return NewIPos; + } -void MustBeExecutedIterator::resetInstruction(const Instruction *I) { - CurInst = I; - Head = Tail = nullptr; - Visited.insert({I, ExplorationDirection::FORWARD}); - Visited.insert({I, ExplorationDirection::BACKWARD}); - if (Explorer.ExploreCFGForward) - Head = I; - if (Explorer.ExploreCFGBackward) - Tail = I; + // We failed to extend the interval so we finalize it and never try again. + Interval.finalize(); + return IntervalPosition(); } -const Instruction *MustBeExecutedIterator::advance() { - assert(CurInst && "Cannot advance an end iterator!"); - Head = Explorer.getMustBeExecutedNextInstruction(*this, Head); - if (Head && Visited.insert({Head, ExplorationDirection ::FORWARD}).second) - return Head; - Head = nullptr; +bool MustBeExecutedIterator::advance() { + if (!Pos[0]) { + assert(!Pos[1] && "Unexpected iterator state!"); + return false; + } - Tail = Explorer.getMustBeExecutedPrevInstruction(*this, Tail); - if (Tail && Visited.insert({Tail, ExplorationDirection ::BACKWARD}).second) - return Tail; - Tail = nullptr; - return nullptr; + // First, check if we explored a new position already. We have to be careful + // not to forget the old position if we cannot advance it. + MustBeExecutedInterval::Position Tmp = Pos[0]; + if (++Tmp) { + Pos[0] = Tmp; + return true; + } + + // We reached the boundaries of the known underlying interval and need to + // explore further. Note that exploration can fail, e.g., there is no known + // "next" instruction. If it does succeed, the interval is either extended + // or linked to another one by the explorer. In either case we need to + // advance to the new position. + if (Explorer) + if ((Pos[0] = Explorer->exploreNextPosition(Pos[0]))) + return true; + + assert(!Pos[0] && "Inconsistent state!"); + std::swap(Pos[0], Pos[1]); + return advance(); +} + +bool MustBeExecutedIterator::isExecutedWith(const Instruction &I) const { + auto Tmp = *this; + do { + if (Tmp.getCurrentInst() == &I) + return true; + } while (Tmp.advance()); + return false; } diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -393,9 +393,9 @@ unsigned AttrsSize = Attrs.size(); MustBeExecutedContextExplorer &Explorer = A.getInfoCache().getMustBeExecutedContextExplorer(); - auto EIt = Explorer.begin(getCtxI()), EEnd = Explorer.end(getCtxI()); + Instruction &CtxI = *getCtxI(); for (auto &It : A2K) - if (Explorer.findInContextOf(It.first, EIt, EEnd)) + if (Explorer.findInContextOf(*It.first, CtxI)) Attrs.push_back(Attribute::get(Ctx, AK, It.second.Max)); return AttrsSize != Attrs.size(); } 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 @@ -599,11 +599,10 @@ const Instruction *CtxI, SetVector &Uses, StateType &State) { - auto EIt = Explorer.begin(CtxI), EEnd = Explorer.end(CtxI); for (unsigned u = 0; u < Uses.size(); ++u) { const Use *U = Uses[u]; if (const Instruction *UserI = dyn_cast(U->getUser())) { - bool Found = Explorer.findInContextOf(UserI, EIt, EEnd); + bool Found = Explorer.findInContextOf(*UserI, *CtxI); if (Found && AA.followUseInMBEC(A, U, UserI, State)) for (const Use &Us : UserI->uses()) Uses.insert(&Us); @@ -677,7 +676,7 @@ // } // } - Explorer.checkForAllContext(&CtxI, Pred); + Explorer.checkForAllInContext(CtxI, Pred); for (const BranchInst *Br : BrInsts) { StateType ParentState; @@ -4760,7 +4759,7 @@ if (Frees.size() != 1) return false; Instruction *UniqueFree = *Frees.begin(); - return Explorer.findInContextOf(UniqueFree, I.getNextNode()); + return Explorer.findInContextOf(*UniqueFree, *I.getNextNode()); }; auto UsesCheck = [&](Instruction &I) { diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll b/llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll --- a/llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll @@ -75,15 +75,25 @@ ; IS__TUNIT_NPM-NEXT: [[TMP7:%.*]] = add i32 [[TMP6]], [[TMP3]] ; IS__TUNIT_NPM-NEXT: ret i32 [[TMP7]] ; -; IS__CGSCC____-LABEL: define {{[^@]+}}@vfu2() -; IS__CGSCC____-NEXT: entry: -; IS__CGSCC____-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_MYSTR:%.*]], %struct.MYstr* @mystr, i32 0, i32 1 -; IS__CGSCC____-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP0]], align 4 -; IS__CGSCC____-NEXT: [[TMP2:%.*]] = getelementptr [[STRUCT_MYSTR]], %struct.MYstr* @mystr, i32 0, i32 0 -; IS__CGSCC____-NEXT: [[TMP3:%.*]] = load i8, i8* [[TMP2]], align 4 -; IS__CGSCC____-NEXT: [[TMP4:%.*]] = zext i8 [[TMP3]] to i32 -; IS__CGSCC____-NEXT: [[TMP5:%.*]] = add i32 [[TMP4]], [[TMP1]] -; IS__CGSCC____-NEXT: ret i32 [[TMP5]] +; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@vfu2() +; IS__CGSCC_OPM-NEXT: entry: +; IS__CGSCC_OPM-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_MYSTR:%.*]], %struct.MYstr* @mystr, i32 0, i32 1 +; IS__CGSCC_OPM-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP0]], align 4 +; IS__CGSCC_OPM-NEXT: [[TMP2:%.*]] = getelementptr [[STRUCT_MYSTR]], %struct.MYstr* @mystr, i32 0, i32 0 +; IS__CGSCC_OPM-NEXT: [[TMP3:%.*]] = load i8, i8* [[TMP2]], align 4 +; IS__CGSCC_OPM-NEXT: [[TMP4:%.*]] = zext i8 [[TMP3]] to i32 +; IS__CGSCC_OPM-NEXT: [[TMP5:%.*]] = add i32 [[TMP4]], [[TMP1]] +; IS__CGSCC_OPM-NEXT: ret i32 [[TMP5]] +; +; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@vfu2() +; IS__CGSCC_NPM-NEXT: entry: +; IS__CGSCC_NPM-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_MYSTR:%.*]], %struct.MYstr* @mystr, i32 0, i32 1 +; IS__CGSCC_NPM-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP0]], align 4 +; IS__CGSCC_NPM-NEXT: [[TMP2:%.*]] = getelementptr [[STRUCT_MYSTR]], %struct.MYstr* @mystr, i32 0, i32 0 +; IS__CGSCC_NPM-NEXT: [[TMP3:%.*]] = load i8, i8* [[TMP2]], align 8 +; IS__CGSCC_NPM-NEXT: [[TMP4:%.*]] = zext i8 [[TMP3]] to i32 +; IS__CGSCC_NPM-NEXT: [[TMP5:%.*]] = add i32 [[TMP4]], [[TMP1]] +; IS__CGSCC_NPM-NEXT: ret i32 [[TMP5]] ; entry: %0 = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 1 ; [#uses=1] diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll b/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll --- a/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt -attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM -; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM ; RUN: opt -attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM ; diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/musttail-call.ll b/llvm/test/Transforms/Attributor/IPConstantProp/musttail-call.ll --- a/llvm/test/Transforms/Attributor/IPConstantProp/musttail-call.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/musttail-call.ll @@ -25,22 +25,39 @@ ; IS__TUNIT____-NEXT: [[CA2:%.*]] = musttail call i8* @dont_zap_me(i8 undef) ; IS__TUNIT____-NEXT: ret i8* [[CA2]] ; -; IS__CGSCC____-LABEL: define {{[^@]+}}@start -; IS__CGSCC____-SAME: (i8 [[V:%.*]]) -; IS__CGSCC____-NEXT: [[C1:%.*]] = icmp eq i8 [[V]], 0 -; IS__CGSCC____-NEXT: br i1 [[C1]], label [[TRUE:%.*]], label [[FALSE:%.*]] -; IS__CGSCC____: true: -; IS__CGSCC____-NEXT: [[CA:%.*]] = musttail call noalias align 536870912 i8* @side_effects(i8 [[V]]) -; IS__CGSCC____-NEXT: ret i8* [[CA]] -; IS__CGSCC____: false: -; IS__CGSCC____-NEXT: [[C2:%.*]] = icmp eq i8 [[V]], 1 -; IS__CGSCC____-NEXT: br i1 [[C2]], label [[C2_TRUE:%.*]], label [[C2_FALSE:%.*]] -; IS__CGSCC____: c2_true: -; IS__CGSCC____-NEXT: [[CA1:%.*]] = musttail call noalias align 536870912 i8* @no_side_effects(i8 [[V]]) -; IS__CGSCC____-NEXT: ret i8* [[CA1]] -; IS__CGSCC____: c2_false: -; IS__CGSCC____-NEXT: [[CA2:%.*]] = musttail call noalias align 536870912 i8* @dont_zap_me(i8 [[V]]) -; IS__CGSCC____-NEXT: ret i8* [[CA2]] +; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@start +; IS__CGSCC_OPM-SAME: (i8 [[V:%.*]]) +; IS__CGSCC_OPM-NEXT: [[C1:%.*]] = icmp eq i8 [[V]], 0 +; IS__CGSCC_OPM-NEXT: br i1 [[C1]], label [[TRUE:%.*]], label [[FALSE:%.*]] +; IS__CGSCC_OPM: true: +; IS__CGSCC_OPM-NEXT: [[CA:%.*]] = musttail call noalias align 536870912 i8* @side_effects(i8 [[V]]) +; IS__CGSCC_OPM-NEXT: ret i8* [[CA]] +; IS__CGSCC_OPM: false: +; IS__CGSCC_OPM-NEXT: [[C2:%.*]] = icmp eq i8 [[V]], 1 +; IS__CGSCC_OPM-NEXT: br i1 [[C2]], label [[C2_TRUE:%.*]], label [[C2_FALSE:%.*]] +; IS__CGSCC_OPM: c2_true: +; IS__CGSCC_OPM-NEXT: [[CA1:%.*]] = musttail call noalias align 536870912 i8* @no_side_effects(i8 [[V]]) +; IS__CGSCC_OPM-NEXT: ret i8* [[CA1]] +; IS__CGSCC_OPM: c2_false: +; IS__CGSCC_OPM-NEXT: [[CA2:%.*]] = musttail call noalias align 536870912 i8* @dont_zap_me(i8 [[V]]) +; IS__CGSCC_OPM-NEXT: ret i8* [[CA2]] +; +; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@start +; IS__CGSCC_NPM-SAME: (i8 [[V:%.*]]) +; IS__CGSCC_NPM-NEXT: [[C1:%.*]] = icmp eq i8 [[V]], 0 +; IS__CGSCC_NPM-NEXT: br i1 [[C1]], label [[TRUE:%.*]], label [[FALSE:%.*]] +; IS__CGSCC_NPM: true: +; IS__CGSCC_NPM-NEXT: [[CA:%.*]] = musttail call noalias align 536870912 i8* @side_effects(i8 [[V]]) +; IS__CGSCC_NPM-NEXT: ret i8* [[CA]] +; IS__CGSCC_NPM: false: +; IS__CGSCC_NPM-NEXT: [[C2:%.*]] = icmp eq i8 [[V]], 1 +; IS__CGSCC_NPM-NEXT: br i1 [[C2]], label [[C2_TRUE:%.*]], label [[C2_FALSE:%.*]] +; IS__CGSCC_NPM: c2_true: +; IS__CGSCC_NPM-NEXT: [[CA1:%.*]] = musttail call noalias nonnull align 536870912 i8* @no_side_effects(i8 [[V]]) +; IS__CGSCC_NPM-NEXT: ret i8* [[CA1]] +; IS__CGSCC_NPM: c2_false: +; IS__CGSCC_NPM-NEXT: [[CA2:%.*]] = musttail call noalias align 536870912 i8* @dont_zap_me(i8 [[V]]) +; IS__CGSCC_NPM-NEXT: ret i8* [[CA2]] ; %c1 = icmp eq i8 %v, 0 br i1 %c1, label %true, label %false @@ -67,11 +84,17 @@ ; IS__TUNIT____-NEXT: [[CA:%.*]] = musttail call i8* @start(i8 [[V]]) ; IS__TUNIT____-NEXT: ret i8* [[CA]] ; -; IS__CGSCC____-LABEL: define {{[^@]+}}@side_effects -; IS__CGSCC____-SAME: (i8 [[V:%.*]]) -; IS__CGSCC____-NEXT: [[I1:%.*]] = call i32 @external() -; IS__CGSCC____-NEXT: [[CA:%.*]] = musttail call noalias align 536870912 i8* @start(i8 [[V]]) -; IS__CGSCC____-NEXT: ret i8* [[CA]] +; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@side_effects +; IS__CGSCC_OPM-SAME: (i8 [[V:%.*]]) +; IS__CGSCC_OPM-NEXT: [[I1:%.*]] = call i32 @external() +; IS__CGSCC_OPM-NEXT: [[CA:%.*]] = musttail call noalias align 536870912 i8* @start(i8 [[V]]) +; IS__CGSCC_OPM-NEXT: ret i8* [[CA]] +; +; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@side_effects +; IS__CGSCC_NPM-SAME: (i8 [[V:%.*]]) +; IS__CGSCC_NPM-NEXT: [[I1:%.*]] = call i32 @external() +; IS__CGSCC_NPM-NEXT: [[CA:%.*]] = musttail call noalias nonnull align 536870912 i8* @start(i8 [[V]]) +; IS__CGSCC_NPM-NEXT: ret i8* [[CA]] ; %i1 = call i32 @external() diff --git a/llvm/test/Transforms/Attributor/align.ll b/llvm/test/Transforms/Attributor/align.ll --- a/llvm/test/Transforms/Attributor/align.ll +++ b/llvm/test/Transforms/Attributor/align.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt -attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=7 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM -; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=7 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=10 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM ; RUN: opt -attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM diff --git a/llvm/test/Transforms/Attributor/memory_locations.ll b/llvm/test/Transforms/Attributor/memory_locations.ll --- a/llvm/test/Transforms/Attributor/memory_locations.ll +++ b/llvm/test/Transforms/Attributor/memory_locations.ll @@ -65,11 +65,17 @@ ; CHECK: Function Attrs: inaccessiblememonly define dso_local i8* @internal_only_rec_static_helper(i32 %arg) { -; CHECK-LABEL: define {{[^@]+}}@internal_only_rec_static_helper -; CHECK-SAME: (i32 [[ARG:%.*]]) -; CHECK-NEXT: entry: -; CHECK-NEXT: [[CALL:%.*]] = call noalias i8* @internal_only_rec_static(i32 [[ARG]]) -; CHECK-NEXT: ret i8* [[CALL]] +; IS________OPM-LABEL: define {{[^@]+}}@internal_only_rec_static_helper +; IS________OPM-SAME: (i32 [[ARG:%.*]]) +; IS________OPM-NEXT: entry: +; IS________OPM-NEXT: [[CALL:%.*]] = call noalias i8* @internal_only_rec_static(i32 [[ARG]]) +; IS________OPM-NEXT: ret i8* [[CALL]] +; +; IS________NPM-LABEL: define {{[^@]+}}@internal_only_rec_static_helper +; IS________NPM-SAME: (i32 [[ARG:%.*]]) +; IS________NPM-NEXT: entry: +; IS________NPM-NEXT: [[CALL:%.*]] = call noalias nonnull i8* @internal_only_rec_static(i32 [[ARG]]) +; IS________NPM-NEXT: ret i8* [[CALL]] ; entry: %call = call i8* @internal_only_rec_static(i32 %arg) @@ -118,11 +124,17 @@ define dso_local i8* @internal_only_rec_static_helper_malloc_noescape(i32 %arg) { ; FIXME: This is actually inaccessiblememonly because the malloced memory does not escape -; CHECK-LABEL: define {{[^@]+}}@internal_only_rec_static_helper_malloc_noescape -; CHECK-SAME: (i32 [[ARG:%.*]]) -; CHECK-NEXT: entry: -; CHECK-NEXT: [[CALL:%.*]] = call noalias i8* @internal_only_rec_static_malloc_noescape(i32 [[ARG]]) -; CHECK-NEXT: ret i8* [[CALL]] +; IS________OPM-LABEL: define {{[^@]+}}@internal_only_rec_static_helper_malloc_noescape +; IS________OPM-SAME: (i32 [[ARG:%.*]]) +; IS________OPM-NEXT: entry: +; IS________OPM-NEXT: [[CALL:%.*]] = call noalias i8* @internal_only_rec_static_malloc_noescape(i32 [[ARG]]) +; IS________OPM-NEXT: ret i8* [[CALL]] +; +; IS________NPM-LABEL: define {{[^@]+}}@internal_only_rec_static_helper_malloc_noescape +; IS________NPM-SAME: (i32 [[ARG:%.*]]) +; IS________NPM-NEXT: entry: +; IS________NPM-NEXT: [[CALL:%.*]] = call noalias nonnull i8* @internal_only_rec_static_malloc_noescape(i32 [[ARG]]) +; IS________NPM-NEXT: ret i8* [[CALL]] ; entry: %call = call i8* @internal_only_rec_static_malloc_noescape(i32 %arg) @@ -205,17 +217,29 @@ ; CHECK: Function Attrs: inaccessiblemem_or_argmemonly define dso_local i8* @internal_argmem_only_rec(i32* %arg) { -; IS__TUNIT____-LABEL: define {{[^@]+}}@internal_argmem_only_rec -; IS__TUNIT____-SAME: (i32* nocapture align 4 [[ARG:%.*]]) -; IS__TUNIT____-NEXT: entry: -; IS__TUNIT____-NEXT: [[CALL:%.*]] = call noalias i8* @internal_argmem_only_rec_1(i32* nocapture align 4 [[ARG]]) -; IS__TUNIT____-NEXT: ret i8* [[CALL]] +; IS__TUNIT_OPM-LABEL: define {{[^@]+}}@internal_argmem_only_rec +; IS__TUNIT_OPM-SAME: (i32* nocapture align 4 [[ARG:%.*]]) +; IS__TUNIT_OPM-NEXT: entry: +; IS__TUNIT_OPM-NEXT: [[CALL:%.*]] = call noalias i8* @internal_argmem_only_rec_1(i32* nocapture align 4 [[ARG]]) +; IS__TUNIT_OPM-NEXT: ret i8* [[CALL]] ; -; IS__CGSCC____-LABEL: define {{[^@]+}}@internal_argmem_only_rec -; IS__CGSCC____-SAME: (i32* nocapture nonnull align 4 dereferenceable(4) [[ARG:%.*]]) -; IS__CGSCC____-NEXT: entry: -; IS__CGSCC____-NEXT: [[CALL:%.*]] = call noalias i8* @internal_argmem_only_rec_1(i32* nocapture nonnull align 4 dereferenceable(4) [[ARG]]) -; IS__CGSCC____-NEXT: ret i8* [[CALL]] +; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@internal_argmem_only_rec +; IS__TUNIT_NPM-SAME: (i32* nocapture align 4 [[ARG:%.*]]) +; IS__TUNIT_NPM-NEXT: entry: +; IS__TUNIT_NPM-NEXT: [[CALL:%.*]] = call noalias nonnull i8* @internal_argmem_only_rec_1(i32* nocapture align 4 [[ARG]]) +; IS__TUNIT_NPM-NEXT: ret i8* [[CALL]] +; +; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@internal_argmem_only_rec +; IS__CGSCC_OPM-SAME: (i32* nocapture nonnull align 4 dereferenceable(4) [[ARG:%.*]]) +; IS__CGSCC_OPM-NEXT: entry: +; IS__CGSCC_OPM-NEXT: [[CALL:%.*]] = call noalias i8* @internal_argmem_only_rec_1(i32* nocapture nonnull align 4 dereferenceable(4) [[ARG]]) +; IS__CGSCC_OPM-NEXT: ret i8* [[CALL]] +; +; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@internal_argmem_only_rec +; IS__CGSCC_NPM-SAME: (i32* nocapture nonnull align 4 dereferenceable(4) [[ARG:%.*]]) +; IS__CGSCC_NPM-NEXT: entry: +; IS__CGSCC_NPM-NEXT: [[CALL:%.*]] = call noalias nonnull i8* @internal_argmem_only_rec_1(i32* nocapture nonnull align 4 dereferenceable(4) [[ARG]]) +; IS__CGSCC_NPM-NEXT: ret i8* [[CALL]] ; entry: %call = call i8* @internal_argmem_only_rec_1(i32* %arg) @@ -280,13 +304,21 @@ ; CHECK: Function Attrs: inaccessiblemem_or_argmemonly define internal i8* @internal_argmem_only_rec_2(i32* %arg) { -; CHECK-LABEL: define {{[^@]+}}@internal_argmem_only_rec_2 -; CHECK-SAME: (i32* nocapture nonnull align 4 dereferenceable(4) [[ARG:%.*]]) -; CHECK-NEXT: entry: -; CHECK-NEXT: store i32 0, i32* [[ARG]], align 4 -; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, i32* [[ARG]], i64 -1 -; CHECK-NEXT: [[CALL:%.*]] = call noalias i8* @internal_argmem_only_rec_1(i32* nocapture nonnull align 4 dereferenceable(4) [[ADD_PTR]]) -; CHECK-NEXT: ret i8* [[CALL]] +; IS________OPM-LABEL: define {{[^@]+}}@internal_argmem_only_rec_2 +; IS________OPM-SAME: (i32* nocapture nonnull align 4 dereferenceable(4) [[ARG:%.*]]) +; IS________OPM-NEXT: entry: +; IS________OPM-NEXT: store i32 0, i32* [[ARG]], align 4 +; IS________OPM-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, i32* [[ARG]], i64 -1 +; IS________OPM-NEXT: [[CALL:%.*]] = call noalias i8* @internal_argmem_only_rec_1(i32* nocapture nonnull align 4 dereferenceable(4) [[ADD_PTR]]) +; IS________OPM-NEXT: ret i8* [[CALL]] +; +; IS________NPM-LABEL: define {{[^@]+}}@internal_argmem_only_rec_2 +; IS________NPM-SAME: (i32* nocapture nonnull align 4 dereferenceable(4) [[ARG:%.*]]) +; IS________NPM-NEXT: entry: +; IS________NPM-NEXT: store i32 0, i32* [[ARG]], align 4 +; IS________NPM-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, i32* [[ARG]], i64 -1 +; IS________NPM-NEXT: [[CALL:%.*]] = call noalias nonnull i8* @internal_argmem_only_rec_1(i32* nocapture nonnull align 4 dereferenceable(4) [[ADD_PTR]]) +; IS________NPM-NEXT: ret i8* [[CALL]] ; entry: store i32 0, i32* %arg, align 4 diff --git a/llvm/test/Transforms/Attributor/misc.ll b/llvm/test/Transforms/Attributor/misc.ll --- a/llvm/test/Transforms/Attributor/misc.ll +++ b/llvm/test/Transforms/Attributor/misc.ll @@ -9,31 +9,31 @@ define internal void @internal(void (i8*)* %fp) { ; ; -; IS__TUNIT____-LABEL: define {{[^@]+}}@internal -; IS__TUNIT____-SAME: (void (i8*)* nonnull [[FP:%.*]]) -; IS__TUNIT____-NEXT: entry: -; IS__TUNIT____-NEXT: [[A:%.*]] = alloca i32, align 4 -; IS__TUNIT____-NEXT: call void @foo(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) -; IS__TUNIT____-NEXT: call void [[FP]](i8* bitcast (void (i32*)* @foo to i8*)) -; IS__TUNIT____-NEXT: call void @callback1(void (i32*)* nonnull @foo) -; IS__TUNIT____-NEXT: call void @callback2(void (i8*)* nonnull bitcast (void (i32*)* @foo to void (i8*)*)) -; IS__TUNIT____-NEXT: call void @callback2(void (i8*)* nonnull [[FP]]) -; IS__TUNIT____-NEXT: [[TMP1:%.*]] = bitcast i32* [[A]] to i8* -; IS__TUNIT____-NEXT: call void [[FP]](i8* [[TMP1]]) -; IS__TUNIT____-NEXT: ret void +; NOT_CGSCC_OPM-LABEL: define {{[^@]+}}@internal +; NOT_CGSCC_OPM-SAME: (void (i8*)* nonnull [[FP:%.*]]) +; NOT_CGSCC_OPM-NEXT: entry: +; NOT_CGSCC_OPM-NEXT: [[A:%.*]] = alloca i32, align 4 +; NOT_CGSCC_OPM-NEXT: call void @foo(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) +; NOT_CGSCC_OPM-NEXT: call void [[FP]](i8* bitcast (void (i32*)* @foo to i8*)) +; NOT_CGSCC_OPM-NEXT: call void @callback1(void (i32*)* nonnull @foo) +; NOT_CGSCC_OPM-NEXT: call void @callback2(void (i8*)* nonnull bitcast (void (i32*)* @foo to void (i8*)*)) +; NOT_CGSCC_OPM-NEXT: call void @callback2(void (i8*)* nonnull [[FP]]) +; NOT_CGSCC_OPM-NEXT: [[TMP1:%.*]] = bitcast i32* [[A]] to i8* +; NOT_CGSCC_OPM-NEXT: call void [[FP]](i8* [[TMP1]]) +; NOT_CGSCC_OPM-NEXT: ret void ; -; IS__CGSCC____-LABEL: define {{[^@]+}}@internal -; IS__CGSCC____-SAME: (void (i8*)* nonnull [[FP:%.*]]) -; IS__CGSCC____-NEXT: entry: -; IS__CGSCC____-NEXT: [[A:%.*]] = alloca i32, align 4 -; IS__CGSCC____-NEXT: call void @foo(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) -; IS__CGSCC____-NEXT: call void [[FP]](i8* bitcast (void (i32*)* @foo to i8*)) -; IS__CGSCC____-NEXT: call void @callback1(void (i32*)* nonnull @foo) -; IS__CGSCC____-NEXT: call void @callback2(void (i8*)* bitcast (void (i32*)* @foo to void (i8*)*)) -; IS__CGSCC____-NEXT: call void @callback2(void (i8*)* nonnull [[FP]]) -; IS__CGSCC____-NEXT: [[TMP1:%.*]] = bitcast i32* [[A]] to i8* -; IS__CGSCC____-NEXT: call void [[FP]](i8* [[TMP1]]) -; IS__CGSCC____-NEXT: ret void +; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@internal +; IS__CGSCC_OPM-SAME: (void (i8*)* nonnull [[FP:%.*]]) +; IS__CGSCC_OPM-NEXT: entry: +; IS__CGSCC_OPM-NEXT: [[A:%.*]] = alloca i32, align 4 +; IS__CGSCC_OPM-NEXT: call void @foo(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) +; IS__CGSCC_OPM-NEXT: call void [[FP]](i8* bitcast (void (i32*)* @foo to i8*)) +; IS__CGSCC_OPM-NEXT: call void @callback1(void (i32*)* nonnull @foo) +; IS__CGSCC_OPM-NEXT: call void @callback2(void (i8*)* bitcast (void (i32*)* @foo to void (i8*)*)) +; IS__CGSCC_OPM-NEXT: call void @callback2(void (i8*)* nonnull [[FP]]) +; IS__CGSCC_OPM-NEXT: [[TMP1:%.*]] = bitcast i32* [[A]] to i8* +; IS__CGSCC_OPM-NEXT: call void [[FP]](i8* [[TMP1]]) +; IS__CGSCC_OPM-NEXT: ret void ; entry: %a = alloca i32, align 4 @@ -51,33 +51,33 @@ define void @external(void (i8*)* %fp) { ; ; -; IS__TUNIT____-LABEL: define {{[^@]+}}@external -; IS__TUNIT____-SAME: (void (i8*)* [[FP:%.*]]) -; IS__TUNIT____-NEXT: entry: -; IS__TUNIT____-NEXT: [[A:%.*]] = alloca i32, align 4 -; IS__TUNIT____-NEXT: call void @foo(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) -; IS__TUNIT____-NEXT: call void @callback1(void (i32*)* nonnull @foo) -; IS__TUNIT____-NEXT: call void @callback2(void (i8*)* nonnull bitcast (void (i32*)* @foo to void (i8*)*)) -; IS__TUNIT____-NEXT: call void @callback2(void (i8*)* [[FP]]) -; IS__TUNIT____-NEXT: call void [[FP]](i8* bitcast (void (i32*)* @foo to i8*)) -; IS__TUNIT____-NEXT: [[TMP1:%.*]] = bitcast i32* [[A]] to i8* -; IS__TUNIT____-NEXT: call void [[FP]](i8* [[TMP1]]) -; IS__TUNIT____-NEXT: call void @internal(void (i8*)* nonnull [[FP]]) -; IS__TUNIT____-NEXT: ret void +; NOT_CGSCC_OPM-LABEL: define {{[^@]+}}@external +; NOT_CGSCC_OPM-SAME: (void (i8*)* [[FP:%.*]]) +; NOT_CGSCC_OPM-NEXT: entry: +; NOT_CGSCC_OPM-NEXT: [[A:%.*]] = alloca i32, align 4 +; NOT_CGSCC_OPM-NEXT: call void @foo(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) +; NOT_CGSCC_OPM-NEXT: call void @callback1(void (i32*)* nonnull @foo) +; NOT_CGSCC_OPM-NEXT: call void @callback2(void (i8*)* nonnull bitcast (void (i32*)* @foo to void (i8*)*)) +; NOT_CGSCC_OPM-NEXT: call void @callback2(void (i8*)* [[FP]]) +; NOT_CGSCC_OPM-NEXT: call void [[FP]](i8* bitcast (void (i32*)* @foo to i8*)) +; NOT_CGSCC_OPM-NEXT: [[TMP1:%.*]] = bitcast i32* [[A]] to i8* +; NOT_CGSCC_OPM-NEXT: call void [[FP]](i8* [[TMP1]]) +; NOT_CGSCC_OPM-NEXT: call void @internal(void (i8*)* nonnull [[FP]]) +; NOT_CGSCC_OPM-NEXT: ret void ; -; IS__CGSCC____-LABEL: define {{[^@]+}}@external -; IS__CGSCC____-SAME: (void (i8*)* [[FP:%.*]]) -; IS__CGSCC____-NEXT: entry: -; IS__CGSCC____-NEXT: [[A:%.*]] = alloca i32, align 4 -; IS__CGSCC____-NEXT: call void @foo(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) -; IS__CGSCC____-NEXT: call void @callback1(void (i32*)* nonnull @foo) -; IS__CGSCC____-NEXT: call void @callback2(void (i8*)* bitcast (void (i32*)* @foo to void (i8*)*)) -; IS__CGSCC____-NEXT: call void @callback2(void (i8*)* [[FP]]) -; IS__CGSCC____-NEXT: call void [[FP]](i8* bitcast (void (i32*)* @foo to i8*)) -; IS__CGSCC____-NEXT: [[TMP1:%.*]] = bitcast i32* [[A]] to i8* -; IS__CGSCC____-NEXT: call void [[FP]](i8* [[TMP1]]) -; IS__CGSCC____-NEXT: call void @internal(void (i8*)* nonnull [[FP]]) -; IS__CGSCC____-NEXT: ret void +; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@external +; IS__CGSCC_OPM-SAME: (void (i8*)* [[FP:%.*]]) +; IS__CGSCC_OPM-NEXT: entry: +; IS__CGSCC_OPM-NEXT: [[A:%.*]] = alloca i32, align 4 +; IS__CGSCC_OPM-NEXT: call void @foo(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) +; IS__CGSCC_OPM-NEXT: call void @callback1(void (i32*)* nonnull @foo) +; IS__CGSCC_OPM-NEXT: call void @callback2(void (i8*)* bitcast (void (i32*)* @foo to void (i8*)*)) +; IS__CGSCC_OPM-NEXT: call void @callback2(void (i8*)* [[FP]]) +; IS__CGSCC_OPM-NEXT: call void [[FP]](i8* bitcast (void (i32*)* @foo to i8*)) +; IS__CGSCC_OPM-NEXT: [[TMP1:%.*]] = bitcast i32* [[A]] to i8* +; IS__CGSCC_OPM-NEXT: call void [[FP]](i8* [[TMP1]]) +; IS__CGSCC_OPM-NEXT: call void @internal(void (i8*)* nonnull [[FP]]) +; IS__CGSCC_OPM-NEXT: ret void ; entry: %a = alloca i32, align 4 diff --git a/llvm/test/Transforms/Attributor/nocapture-2.ll b/llvm/test/Transforms/Attributor/nocapture-2.ll --- a/llvm/test/Transforms/Attributor/nocapture-2.ll +++ b/llvm/test/Transforms/Attributor/nocapture-2.ll @@ -162,26 +162,47 @@ ; return scc_A((int*)(scc_A(a) ? scc_B((double*)a) : scc_C(a))); ; } define float* @scc_A(i32* dereferenceable_or_null(4) %a) { -; CHECK-LABEL: define {{[^@]+}}@scc_A -; CHECK-SAME: (i32* nofree readnone returned dereferenceable_or_null(4) "no-capture-maybe-returned" [[A:%.*]]) -; CHECK-NEXT: entry: -; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32* [[A]], null -; CHECK-NEXT: br i1 [[TOBOOL]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] -; CHECK: cond.true: -; CHECK-NEXT: [[TMP0:%.*]] = bitcast i32* [[A]] to i16* -; CHECK-NEXT: [[CALL:%.*]] = call dereferenceable_or_null(4) i8* @scc_C(i16* noalias nofree nonnull readnone dereferenceable(4) "no-capture-maybe-returned" [[TMP0]]) -; CHECK-NEXT: [[TMP1:%.*]] = bitcast i8* [[CALL]] to double* -; CHECK-NEXT: [[CALL1:%.*]] = call dereferenceable_or_null(8) i64* @scc_B(double* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP1]]) -; CHECK-NEXT: [[TMP2:%.*]] = bitcast i64* [[CALL1]] to i32* -; CHECK-NEXT: [[CALL2:%.*]] = call float* @scc_A(i32* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP2]]) -; CHECK-NEXT: [[TMP3:%.*]] = bitcast float* [[CALL2]] to i32* -; CHECK-NEXT: br label [[COND_END:%.*]] -; CHECK: cond.false: -; CHECK-NEXT: br label [[COND_END]] -; CHECK: cond.end: -; CHECK-NEXT: [[COND:%.*]] = phi i32* [ [[TMP3]], [[COND_TRUE]] ], [ [[A]], [[COND_FALSE]] ] -; CHECK-NEXT: [[TMP4:%.*]] = bitcast i32* [[COND]] to float* -; CHECK-NEXT: ret float* [[TMP4]] +; IS________OPM-LABEL: define {{[^@]+}}@scc_A +; IS________OPM-SAME: (i32* nofree readnone returned dereferenceable_or_null(4) "no-capture-maybe-returned" [[A:%.*]]) +; IS________OPM-NEXT: entry: +; IS________OPM-NEXT: [[TOBOOL:%.*]] = icmp ne i32* [[A]], null +; IS________OPM-NEXT: br i1 [[TOBOOL]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] +; IS________OPM: cond.true: +; IS________OPM-NEXT: [[TMP0:%.*]] = bitcast i32* [[A]] to i16* +; IS________OPM-NEXT: [[CALL:%.*]] = call dereferenceable_or_null(4) i8* @scc_C(i16* noalias nofree nonnull readnone dereferenceable(4) "no-capture-maybe-returned" [[TMP0]]) +; IS________OPM-NEXT: [[TMP1:%.*]] = bitcast i8* [[CALL]] to double* +; IS________OPM-NEXT: [[CALL1:%.*]] = call dereferenceable_or_null(8) i64* @scc_B(double* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP1]]) +; IS________OPM-NEXT: [[TMP2:%.*]] = bitcast i64* [[CALL1]] to i32* +; IS________OPM-NEXT: [[CALL2:%.*]] = call float* @scc_A(i32* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP2]]) +; IS________OPM-NEXT: [[TMP3:%.*]] = bitcast float* [[CALL2]] to i32* +; IS________OPM-NEXT: br label [[COND_END:%.*]] +; IS________OPM: cond.false: +; IS________OPM-NEXT: br label [[COND_END]] +; IS________OPM: cond.end: +; IS________OPM-NEXT: [[COND:%.*]] = phi i32* [ [[TMP3]], [[COND_TRUE]] ], [ [[A]], [[COND_FALSE]] ] +; IS________OPM-NEXT: [[TMP4:%.*]] = bitcast i32* [[COND]] to float* +; IS________OPM-NEXT: ret float* [[TMP4]] +; +; IS________NPM-LABEL: define {{[^@]+}}@scc_A +; IS________NPM-SAME: (i32* nofree readnone returned dereferenceable_or_null(4) "no-capture-maybe-returned" [[A:%.*]]) +; IS________NPM-NEXT: entry: +; IS________NPM-NEXT: [[TOBOOL:%.*]] = icmp ne i32* [[A]], null +; IS________NPM-NEXT: br i1 [[TOBOOL]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] +; IS________NPM: cond.true: +; IS________NPM-NEXT: [[TMP0:%.*]] = bitcast i32* [[A]] to i16* +; IS________NPM-NEXT: [[CALL:%.*]] = call nonnull dereferenceable(4) i8* @scc_C(i16* noalias nofree nonnull readnone dereferenceable(4) "no-capture-maybe-returned" [[TMP0]]) +; IS________NPM-NEXT: [[TMP1:%.*]] = bitcast i8* [[CALL]] to double* +; IS________NPM-NEXT: [[CALL1:%.*]] = call nonnull dereferenceable(8) i64* @scc_B(double* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP1]]) +; IS________NPM-NEXT: [[TMP2:%.*]] = bitcast i64* [[CALL1]] to i32* +; IS________NPM-NEXT: [[CALL2:%.*]] = call float* @scc_A(i32* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP2]]) +; IS________NPM-NEXT: [[TMP3:%.*]] = bitcast float* [[CALL2]] to i32* +; IS________NPM-NEXT: br label [[COND_END:%.*]] +; IS________NPM: cond.false: +; IS________NPM-NEXT: br label [[COND_END]] +; IS________NPM: cond.end: +; IS________NPM-NEXT: [[COND:%.*]] = phi i32* [ [[TMP3]], [[COND_TRUE]] ], [ [[A]], [[COND_FALSE]] ] +; IS________NPM-NEXT: [[TMP4:%.*]] = bitcast i32* [[COND]] to float* +; IS________NPM-NEXT: ret float* [[TMP4]] ; entry: %tobool = icmp ne i32* %a, null @@ -207,26 +228,47 @@ } define i64* @scc_B(double* dereferenceable_or_null(8) %a) { -; CHECK-LABEL: define {{[^@]+}}@scc_B -; CHECK-SAME: (double* nofree readnone returned dereferenceable_or_null(8) "no-capture-maybe-returned" [[A:%.*]]) -; CHECK-NEXT: entry: -; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne double* [[A]], null -; CHECK-NEXT: br i1 [[TOBOOL]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] -; CHECK: cond.true: -; CHECK-NEXT: [[TMP0:%.*]] = bitcast double* [[A]] to i32* -; CHECK-NEXT: [[CALL:%.*]] = call dereferenceable_or_null(4) float* @scc_A(i32* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP0]]) -; CHECK-NEXT: [[TMP1:%.*]] = bitcast float* [[CALL]] to double* -; CHECK-NEXT: [[CALL1:%.*]] = call dereferenceable_or_null(8) i64* @scc_B(double* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP1]]) -; CHECK-NEXT: [[TMP2:%.*]] = bitcast i64* [[CALL1]] to i16* -; CHECK-NEXT: [[CALL2:%.*]] = call i8* @scc_C(i16* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP2]]) -; CHECK-NEXT: br label [[COND_END:%.*]] -; CHECK: cond.false: -; CHECK-NEXT: [[TMP3:%.*]] = bitcast double* [[A]] to i8* -; CHECK-NEXT: br label [[COND_END]] -; CHECK: cond.end: -; CHECK-NEXT: [[COND:%.*]] = phi i8* [ [[CALL2]], [[COND_TRUE]] ], [ [[TMP3]], [[COND_FALSE]] ] -; CHECK-NEXT: [[TMP4:%.*]] = bitcast i8* [[COND]] to i64* -; CHECK-NEXT: ret i64* [[TMP4]] +; IS________OPM-LABEL: define {{[^@]+}}@scc_B +; IS________OPM-SAME: (double* nofree readnone returned dereferenceable_or_null(8) "no-capture-maybe-returned" [[A:%.*]]) +; IS________OPM-NEXT: entry: +; IS________OPM-NEXT: [[TOBOOL:%.*]] = icmp ne double* [[A]], null +; IS________OPM-NEXT: br i1 [[TOBOOL]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] +; IS________OPM: cond.true: +; IS________OPM-NEXT: [[TMP0:%.*]] = bitcast double* [[A]] to i32* +; IS________OPM-NEXT: [[CALL:%.*]] = call dereferenceable_or_null(4) float* @scc_A(i32* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP0]]) +; IS________OPM-NEXT: [[TMP1:%.*]] = bitcast float* [[CALL]] to double* +; IS________OPM-NEXT: [[CALL1:%.*]] = call dereferenceable_or_null(8) i64* @scc_B(double* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP1]]) +; IS________OPM-NEXT: [[TMP2:%.*]] = bitcast i64* [[CALL1]] to i16* +; IS________OPM-NEXT: [[CALL2:%.*]] = call i8* @scc_C(i16* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP2]]) +; IS________OPM-NEXT: br label [[COND_END:%.*]] +; IS________OPM: cond.false: +; IS________OPM-NEXT: [[TMP3:%.*]] = bitcast double* [[A]] to i8* +; IS________OPM-NEXT: br label [[COND_END]] +; IS________OPM: cond.end: +; IS________OPM-NEXT: [[COND:%.*]] = phi i8* [ [[CALL2]], [[COND_TRUE]] ], [ [[TMP3]], [[COND_FALSE]] ] +; IS________OPM-NEXT: [[TMP4:%.*]] = bitcast i8* [[COND]] to i64* +; IS________OPM-NEXT: ret i64* [[TMP4]] +; +; IS________NPM-LABEL: define {{[^@]+}}@scc_B +; IS________NPM-SAME: (double* nofree readnone returned dereferenceable_or_null(8) "no-capture-maybe-returned" [[A:%.*]]) +; IS________NPM-NEXT: entry: +; IS________NPM-NEXT: [[TOBOOL:%.*]] = icmp ne double* [[A]], null +; IS________NPM-NEXT: br i1 [[TOBOOL]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] +; IS________NPM: cond.true: +; IS________NPM-NEXT: [[TMP0:%.*]] = bitcast double* [[A]] to i32* +; IS________NPM-NEXT: [[CALL:%.*]] = call nonnull dereferenceable(4) float* @scc_A(i32* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP0]]) +; IS________NPM-NEXT: [[TMP1:%.*]] = bitcast float* [[CALL]] to double* +; IS________NPM-NEXT: [[CALL1:%.*]] = call nonnull dereferenceable(8) i64* @scc_B(double* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP1]]) +; IS________NPM-NEXT: [[TMP2:%.*]] = bitcast i64* [[CALL1]] to i16* +; IS________NPM-NEXT: [[CALL2:%.*]] = call i8* @scc_C(i16* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP2]]) +; IS________NPM-NEXT: br label [[COND_END:%.*]] +; IS________NPM: cond.false: +; IS________NPM-NEXT: [[TMP3:%.*]] = bitcast double* [[A]] to i8* +; IS________NPM-NEXT: br label [[COND_END]] +; IS________NPM: cond.end: +; IS________NPM-NEXT: [[COND:%.*]] = phi i8* [ [[CALL2]], [[COND_TRUE]] ], [ [[TMP3]], [[COND_FALSE]] ] +; IS________NPM-NEXT: [[TMP4:%.*]] = bitcast i8* [[COND]] to i64* +; IS________NPM-NEXT: ret i64* [[TMP4]] ; entry: %tobool = icmp ne double* %a, null @@ -252,28 +294,51 @@ } define i8* @scc_C(i16* dereferenceable_or_null(2) %a) { -; CHECK-LABEL: define {{[^@]+}}@scc_C -; CHECK-SAME: (i16* nofree readnone returned dereferenceable_or_null(4) "no-capture-maybe-returned" [[A:%.*]]) -; CHECK-NEXT: entry: -; CHECK-NEXT: [[BC:%.*]] = bitcast i16* [[A]] to i32* -; CHECK-NEXT: [[CALL:%.*]] = call dereferenceable_or_null(4) float* @scc_A(i32* noalias nofree nonnull readnone dereferenceable(4) "no-capture-maybe-returned" [[BC]]) -; CHECK-NEXT: [[BC2:%.*]] = bitcast float* [[CALL]] to i8* -; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i8* [[BC2]], null -; CHECK-NEXT: br i1 [[TOBOOL]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] -; CHECK: cond.true: -; CHECK-NEXT: [[TMP0:%.*]] = bitcast i16* [[A]] to double* -; CHECK-NEXT: [[CALL1:%.*]] = call dereferenceable_or_null(8) i64* @scc_B(double* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP0]]) -; CHECK-NEXT: [[TMP1:%.*]] = bitcast i64* [[CALL1]] to i8* -; CHECK-NEXT: br label [[COND_END:%.*]] -; CHECK: cond.false: -; CHECK-NEXT: [[CALL2:%.*]] = call dereferenceable_or_null(4) i8* @scc_C(i16* noalias nofree nonnull readnone dereferenceable(4) "no-capture-maybe-returned" [[A]]) -; CHECK-NEXT: br label [[COND_END]] -; CHECK: cond.end: -; CHECK-NEXT: [[COND:%.*]] = phi i8* [ [[TMP1]], [[COND_TRUE]] ], [ [[CALL2]], [[COND_FALSE]] ] -; CHECK-NEXT: [[TMP2:%.*]] = bitcast i8* [[COND]] to i32* -; CHECK-NEXT: [[CALL3:%.*]] = call float* @scc_A(i32* noalias nofree nonnull readnone dereferenceable(4) "no-capture-maybe-returned" [[TMP2]]) -; CHECK-NEXT: [[TMP3:%.*]] = bitcast float* [[CALL3]] to i8* -; CHECK-NEXT: ret i8* [[TMP3]] +; IS________OPM-LABEL: define {{[^@]+}}@scc_C +; IS________OPM-SAME: (i16* nofree readnone returned dereferenceable_or_null(4) "no-capture-maybe-returned" [[A:%.*]]) +; IS________OPM-NEXT: entry: +; IS________OPM-NEXT: [[BC:%.*]] = bitcast i16* [[A]] to i32* +; IS________OPM-NEXT: [[CALL:%.*]] = call dereferenceable_or_null(4) float* @scc_A(i32* noalias nofree nonnull readnone dereferenceable(4) "no-capture-maybe-returned" [[BC]]) +; IS________OPM-NEXT: [[BC2:%.*]] = bitcast float* [[CALL]] to i8* +; IS________OPM-NEXT: [[TOBOOL:%.*]] = icmp ne i8* [[BC2]], null +; IS________OPM-NEXT: br i1 [[TOBOOL]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] +; IS________OPM: cond.true: +; IS________OPM-NEXT: [[TMP0:%.*]] = bitcast i16* [[A]] to double* +; IS________OPM-NEXT: [[CALL1:%.*]] = call dereferenceable_or_null(8) i64* @scc_B(double* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP0]]) +; IS________OPM-NEXT: [[TMP1:%.*]] = bitcast i64* [[CALL1]] to i8* +; IS________OPM-NEXT: br label [[COND_END:%.*]] +; IS________OPM: cond.false: +; IS________OPM-NEXT: [[CALL2:%.*]] = call dereferenceable_or_null(4) i8* @scc_C(i16* noalias nofree nonnull readnone dereferenceable(4) "no-capture-maybe-returned" [[A]]) +; IS________OPM-NEXT: br label [[COND_END]] +; IS________OPM: cond.end: +; IS________OPM-NEXT: [[COND:%.*]] = phi i8* [ [[TMP1]], [[COND_TRUE]] ], [ [[CALL2]], [[COND_FALSE]] ] +; IS________OPM-NEXT: [[TMP2:%.*]] = bitcast i8* [[COND]] to i32* +; IS________OPM-NEXT: [[CALL3:%.*]] = call float* @scc_A(i32* noalias nofree nonnull readnone dereferenceable(4) "no-capture-maybe-returned" [[TMP2]]) +; IS________OPM-NEXT: [[TMP3:%.*]] = bitcast float* [[CALL3]] to i8* +; IS________OPM-NEXT: ret i8* [[TMP3]] +; +; IS________NPM-LABEL: define {{[^@]+}}@scc_C +; IS________NPM-SAME: (i16* nofree readnone returned dereferenceable_or_null(4) "no-capture-maybe-returned" [[A:%.*]]) +; IS________NPM-NEXT: entry: +; IS________NPM-NEXT: [[BC:%.*]] = bitcast i16* [[A]] to i32* +; IS________NPM-NEXT: [[CALL:%.*]] = call nonnull dereferenceable(4) float* @scc_A(i32* noalias nofree nonnull readnone dereferenceable(4) "no-capture-maybe-returned" [[BC]]) +; IS________NPM-NEXT: [[BC2:%.*]] = bitcast float* [[CALL]] to i8* +; IS________NPM-NEXT: [[TOBOOL:%.*]] = icmp ne i8* [[BC2]], null +; IS________NPM-NEXT: br i1 [[TOBOOL]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] +; IS________NPM: cond.true: +; IS________NPM-NEXT: [[TMP0:%.*]] = bitcast i16* [[A]] to double* +; IS________NPM-NEXT: [[CALL1:%.*]] = call nonnull dereferenceable(8) i64* @scc_B(double* noalias nofree nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[TMP0]]) +; IS________NPM-NEXT: [[TMP1:%.*]] = bitcast i64* [[CALL1]] to i8* +; IS________NPM-NEXT: br label [[COND_END:%.*]] +; IS________NPM: cond.false: +; IS________NPM-NEXT: [[CALL2:%.*]] = call nonnull dereferenceable(4) i8* @scc_C(i16* noalias nofree nonnull readnone dereferenceable(4) "no-capture-maybe-returned" [[A]]) +; IS________NPM-NEXT: br label [[COND_END]] +; IS________NPM: cond.end: +; IS________NPM-NEXT: [[COND:%.*]] = phi i8* [ [[TMP1]], [[COND_TRUE]] ], [ [[CALL2]], [[COND_FALSE]] ] +; IS________NPM-NEXT: [[TMP2:%.*]] = bitcast i8* [[COND]] to i32* +; IS________NPM-NEXT: [[CALL3:%.*]] = call float* @scc_A(i32* noalias nofree nonnull readnone dereferenceable(4) "no-capture-maybe-returned" [[TMP2]]) +; IS________NPM-NEXT: [[TMP3:%.*]] = bitcast float* [[CALL3]] to i8* +; IS________NPM-NEXT: ret i8* [[TMP3]] ; entry: %bc = bitcast i16* %a to i32* diff --git a/llvm/test/Transforms/Attributor/nonnull.ll b/llvm/test/Transforms/Attributor/nonnull.ll --- a/llvm/test/Transforms/Attributor/nonnull.ll +++ b/llvm/test/Transforms/Attributor/nonnull.ll @@ -291,26 +291,47 @@ define internal i32* @f1(i32* %arg) { ; FIXME: missing nonnull It should be nonnull @f1(i32* nonnull readonly %arg) -; CHECK-LABEL: define {{[^@]+}}@f1 -; CHECK-SAME: (i32* nofree readonly [[ARG:%.*]]) -; CHECK-NEXT: bb: -; CHECK-NEXT: [[TMP:%.*]] = icmp eq i32* [[ARG]], null -; CHECK-NEXT: br i1 [[TMP]], label [[BB9:%.*]], label [[BB1:%.*]] -; CHECK: bb1: -; CHECK-NEXT: [[TMP2:%.*]] = load i32, i32* [[ARG]], align 4 -; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0 -; CHECK-NEXT: br i1 [[TMP3]], label [[BB6:%.*]], label [[BB4:%.*]] -; CHECK: bb4: -; CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds i32, i32* [[ARG]], i64 1 -; CHECK-NEXT: [[TMP5B:%.*]] = tail call nonnull i32* @f3(i32* nofree nonnull readonly [[TMP5]]) -; CHECK-NEXT: [[TMP5C:%.*]] = getelementptr inbounds i32, i32* [[TMP5B]], i64 -1 -; CHECK-NEXT: br label [[BB9]] -; CHECK: bb6: -; CHECK-NEXT: [[TMP7:%.*]] = tail call nonnull i32* @f2(i32* nofree nonnull readonly align 4 dereferenceable(4) [[ARG]]) -; CHECK-NEXT: ret i32* [[TMP7]] -; CHECK: bb9: -; CHECK-NEXT: [[TMP10:%.*]] = phi i32* [ [[TMP5C]], [[BB4]] ], [ inttoptr (i64 4 to i32*), [[BB:%.*]] ] -; CHECK-NEXT: ret i32* [[TMP10]] +; IS________OPM-LABEL: define {{[^@]+}}@f1 +; IS________OPM-SAME: (i32* nofree readonly [[ARG:%.*]]) +; IS________OPM-NEXT: bb: +; IS________OPM-NEXT: [[TMP:%.*]] = icmp eq i32* [[ARG]], null +; IS________OPM-NEXT: br i1 [[TMP]], label [[BB9:%.*]], label [[BB1:%.*]] +; IS________OPM: bb1: +; IS________OPM-NEXT: [[TMP2:%.*]] = load i32, i32* [[ARG]], align 4 +; IS________OPM-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0 +; IS________OPM-NEXT: br i1 [[TMP3]], label [[BB6:%.*]], label [[BB4:%.*]] +; IS________OPM: bb4: +; IS________OPM-NEXT: [[TMP5:%.*]] = getelementptr inbounds i32, i32* [[ARG]], i64 1 +; IS________OPM-NEXT: [[TMP5B:%.*]] = tail call nonnull i32* @f3(i32* nofree nonnull readonly [[TMP5]]) +; IS________OPM-NEXT: [[TMP5C:%.*]] = getelementptr inbounds i32, i32* [[TMP5B]], i64 -1 +; IS________OPM-NEXT: br label [[BB9]] +; IS________OPM: bb6: +; IS________OPM-NEXT: [[TMP7:%.*]] = tail call nonnull i32* @f2(i32* nofree nonnull readonly align 4 dereferenceable(4) [[ARG]]) +; IS________OPM-NEXT: ret i32* [[TMP7]] +; IS________OPM: bb9: +; IS________OPM-NEXT: [[TMP10:%.*]] = phi i32* [ [[TMP5C]], [[BB4]] ], [ inttoptr (i64 4 to i32*), [[BB:%.*]] ] +; IS________OPM-NEXT: ret i32* [[TMP10]] +; +; IS________NPM-LABEL: define {{[^@]+}}@f1 +; IS________NPM-SAME: (i32* nofree readonly [[ARG:%.*]]) +; IS________NPM-NEXT: bb: +; IS________NPM-NEXT: [[TMP:%.*]] = icmp eq i32* [[ARG]], null +; IS________NPM-NEXT: br i1 [[TMP]], label [[BB9:%.*]], label [[BB1:%.*]] +; IS________NPM: bb1: +; IS________NPM-NEXT: [[TMP2:%.*]] = load i32, i32* [[ARG]], align 4 +; IS________NPM-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0 +; IS________NPM-NEXT: br i1 [[TMP3]], label [[BB6:%.*]], label [[BB4:%.*]] +; IS________NPM: bb4: +; IS________NPM-NEXT: [[TMP5:%.*]] = getelementptr inbounds i32, i32* [[ARG]], i64 1 +; IS________NPM-NEXT: [[TMP5B:%.*]] = tail call i32* @f3(i32* nofree nonnull readonly [[TMP5]]) +; IS________NPM-NEXT: [[TMP5C:%.*]] = getelementptr inbounds i32, i32* [[TMP5B]], i64 -1 +; IS________NPM-NEXT: br label [[BB9]] +; IS________NPM: bb6: +; IS________NPM-NEXT: [[TMP7:%.*]] = tail call i32* @f2(i32* nofree nonnull readonly align 4 dereferenceable(4) [[ARG]]) +; IS________NPM-NEXT: ret i32* [[TMP7]] +; IS________NPM: bb9: +; IS________NPM-NEXT: [[TMP10:%.*]] = phi i32* [ [[TMP5C]], [[BB4]] ], [ inttoptr (i64 4 to i32*), [[BB:%.*]] ] +; IS________NPM-NEXT: ret i32* [[TMP10]] ; bb: @@ -351,11 +372,17 @@ define dso_local noalias i32* @f3(i32* %arg) { ; FIXME: missing nonnull. It should be nonnull @f3(i32* nonnull readonly %arg) -; CHECK-LABEL: define {{[^@]+}}@f3 -; CHECK-SAME: (i32* nofree readonly [[ARG:%.*]]) -; CHECK-NEXT: bb: -; CHECK-NEXT: [[TMP:%.*]] = call nonnull i32* @f1(i32* nofree readonly [[ARG]]) -; CHECK-NEXT: ret i32* [[TMP]] +; IS________OPM-LABEL: define {{[^@]+}}@f3 +; IS________OPM-SAME: (i32* nofree readonly [[ARG:%.*]]) +; IS________OPM-NEXT: bb: +; IS________OPM-NEXT: [[TMP:%.*]] = call nonnull i32* @f1(i32* nofree readonly [[ARG]]) +; IS________OPM-NEXT: ret i32* [[TMP]] +; +; IS________NPM-LABEL: define {{[^@]+}}@f3 +; IS________NPM-SAME: (i32* nofree readonly [[ARG:%.*]]) +; IS________NPM-NEXT: bb: +; IS________NPM-NEXT: [[TMP:%.*]] = call i32* @f1(i32* nofree readonly [[ARG]]) +; IS________NPM-NEXT: ret i32* [[TMP]] ; bb: ; FIXME: missing nonnull. It should be @f1(i32* nonnull readonly %arg) diff --git a/llvm/test/Transforms/Attributor/returned.ll b/llvm/test/Transforms/Attributor/returned.ll --- a/llvm/test/Transforms/Attributor/returned.ll +++ b/llvm/test/Transforms/Attributor/returned.ll @@ -292,12 +292,19 @@ } define double* @ptr_scc_r1(double* %a, double* %r, double* %b) #0 { -; IS__TUNIT____-LABEL: define {{[^@]+}}@ptr_scc_r1 -; IS__TUNIT____-SAME: (double* nofree readnone [[A:%.*]], double* nofree readnone returned [[R:%.*]], double* nocapture nofree readnone [[B:%.*]]) -; IS__TUNIT____-NEXT: entry: -; IS__TUNIT____-NEXT: [[CALL:%.*]] = call double* @ptr_sink_r0(double* noalias nofree readnone "no-capture-maybe-returned" [[R]]) -; IS__TUNIT____-NEXT: [[CALL1:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[R]], double* noalias nofree readnone [[A]], double* noalias nofree readnone [[CALL]]) -; IS__TUNIT____-NEXT: ret double* [[CALL1]] +; IS__TUNIT_OPM-LABEL: define {{[^@]+}}@ptr_scc_r1 +; IS__TUNIT_OPM-SAME: (double* nofree readnone [[A:%.*]], double* nofree readnone returned [[R:%.*]], double* nocapture nofree readnone [[B:%.*]]) +; IS__TUNIT_OPM-NEXT: entry: +; IS__TUNIT_OPM-NEXT: [[CALL:%.*]] = call double* @ptr_sink_r0(double* noalias nofree readnone "no-capture-maybe-returned" [[R]]) +; IS__TUNIT_OPM-NEXT: [[CALL1:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[R]], double* noalias nofree readnone [[A]], double* noalias nofree readnone [[CALL]]) +; IS__TUNIT_OPM-NEXT: ret double* [[CALL1]] +; +; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@ptr_scc_r1 +; IS__TUNIT_NPM-SAME: (double* nofree readnone [[A:%.*]], double* nofree readnone returned [[R:%.*]], double* nocapture nofree readnone [[B:%.*]]) +; IS__TUNIT_NPM-NEXT: entry: +; IS__TUNIT_NPM-NEXT: [[CALL:%.*]] = call nonnull double* @ptr_sink_r0(double* noalias nofree readnone "no-capture-maybe-returned" [[R]]) +; IS__TUNIT_NPM-NEXT: [[CALL1:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[R]], double* noalias nofree readnone [[A]], double* noalias nofree nonnull readnone [[CALL]]) +; IS__TUNIT_NPM-NEXT: ret double* [[CALL1]] ; ; IS__CGSCC____-LABEL: define {{[^@]+}}@ptr_scc_r1 ; IS__CGSCC____-SAME: (double* nofree readnone [[A:%.*]], double* nofree readnone returned [[R:%.*]], double* nocapture nofree readnone [[B:%.*]]) @@ -313,77 +320,149 @@ } define double* @ptr_scc_r2(double* %a, double* %b, double* %r) #0 { -; IS__TUNIT____-LABEL: define {{[^@]+}}@ptr_scc_r2 -; IS__TUNIT____-SAME: (double* nofree readnone [[A:%.*]], double* nofree readnone [[B:%.*]], double* nofree readnone returned [[R:%.*]]) -; IS__TUNIT____-NEXT: entry: -; IS__TUNIT____-NEXT: [[CMP:%.*]] = icmp ugt double* [[A]], [[B]] -; IS__TUNIT____-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] -; IS__TUNIT____: if.then: -; IS__TUNIT____-NEXT: [[CALL:%.*]] = call double* @ptr_sink_r0(double* noalias nofree readnone "no-capture-maybe-returned" [[R]]) -; IS__TUNIT____-NEXT: [[CALL1:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[B]], double* noalias nofree readnone [[A]], double* noalias nofree readnone [[CALL]]) -; IS__TUNIT____-NEXT: br label [[RETURN:%.*]] -; IS__TUNIT____: if.end: -; IS__TUNIT____-NEXT: [[CMP2:%.*]] = icmp ult double* [[A]], [[B]] -; IS__TUNIT____-NEXT: br i1 [[CMP2]], label [[IF_THEN3:%.*]], label [[IF_END12:%.*]] -; IS__TUNIT____: if.then3: -; IS__TUNIT____-NEXT: [[CALL4:%.*]] = call double* @ptr_sink_r0(double* noalias nofree readnone [[B]]) -; IS__TUNIT____-NEXT: [[CALL5:%.*]] = call double* @ptr_scc_r1(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone undef) -; IS__TUNIT____-NEXT: [[CALL6:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[R]], double* noalias nofree readnone [[R]], double* noalias nofree readnone [[R]]) -; IS__TUNIT____-NEXT: [[CALL7:%.*]] = call double* @ptr_scc_r1(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[CALL6]], double* noalias nofree readnone undef) -; IS__TUNIT____-NEXT: [[CALL8:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone [[R]]) -; IS__TUNIT____-NEXT: [[CALL9:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[CALL5]], double* noalias nofree readnone [[CALL7]], double* noalias nofree readnone [[CALL8]]) -; IS__TUNIT____-NEXT: [[CALL11:%.*]] = call double* @ptr_scc_r1(double* noalias nofree readnone [[CALL4]], double* noalias nofree readnone [[CALL9]], double* noalias nofree readnone undef) -; IS__TUNIT____-NEXT: br label [[RETURN]] -; IS__TUNIT____: if.end12: -; IS__TUNIT____-NEXT: [[CMP13:%.*]] = icmp eq double* [[A]], [[B]] -; IS__TUNIT____-NEXT: br i1 [[CMP13]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] -; IS__TUNIT____: cond.true: -; IS__TUNIT____-NEXT: br label [[COND_END:%.*]] -; IS__TUNIT____: cond.false: -; IS__TUNIT____-NEXT: [[CALL14:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone [[R]]) -; IS__TUNIT____-NEXT: br label [[COND_END]] -; IS__TUNIT____: cond.end: -; IS__TUNIT____-NEXT: [[COND:%.*]] = phi double* [ [[R]], [[COND_TRUE]] ], [ [[CALL14]], [[COND_FALSE]] ] -; IS__TUNIT____-NEXT: br label [[RETURN]] -; IS__TUNIT____: return: -; IS__TUNIT____-NEXT: [[RETVAL_0:%.*]] = phi double* [ [[CALL1]], [[IF_THEN]] ], [ [[CALL11]], [[IF_THEN3]] ], [ [[COND]], [[COND_END]] ] -; IS__TUNIT____-NEXT: ret double* [[RETVAL_0]] -; -; IS__CGSCC____-LABEL: define {{[^@]+}}@ptr_scc_r2 -; IS__CGSCC____-SAME: (double* nofree readnone [[A:%.*]], double* nofree readnone [[B:%.*]], double* nofree readnone returned [[R:%.*]]) -; IS__CGSCC____-NEXT: entry: -; IS__CGSCC____-NEXT: [[CMP:%.*]] = icmp ugt double* [[A]], [[B]] -; IS__CGSCC____-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] -; IS__CGSCC____: if.then: -; IS__CGSCC____-NEXT: [[CALL:%.*]] = call double* @ptr_sink_r0(double* noalias nofree readnone [[R]]) -; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[B]], double* noalias nofree readnone [[A]], double* noalias nofree readnone [[CALL]]) -; IS__CGSCC____-NEXT: br label [[RETURN:%.*]] -; IS__CGSCC____: if.end: -; IS__CGSCC____-NEXT: [[CMP2:%.*]] = icmp ult double* [[A]], [[B]] -; IS__CGSCC____-NEXT: br i1 [[CMP2]], label [[IF_THEN3:%.*]], label [[IF_END12:%.*]] -; IS__CGSCC____: if.then3: -; IS__CGSCC____-NEXT: [[CALL4:%.*]] = call double* @ptr_sink_r0(double* noalias nofree readnone [[B]]) -; IS__CGSCC____-NEXT: [[CALL5:%.*]] = call double* @ptr_scc_r1(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone undef) -; IS__CGSCC____-NEXT: [[CALL6:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[R]], double* noalias nofree readnone [[R]], double* noalias nofree readnone [[R]]) -; IS__CGSCC____-NEXT: [[CALL7:%.*]] = call double* @ptr_scc_r1(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[CALL6]], double* noalias nofree readnone undef) -; IS__CGSCC____-NEXT: [[CALL8:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone [[R]]) -; IS__CGSCC____-NEXT: [[CALL9:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[CALL5]], double* noalias nofree readnone [[CALL7]], double* noalias nofree readnone [[CALL8]]) -; IS__CGSCC____-NEXT: [[CALL11:%.*]] = call double* @ptr_scc_r1(double* noalias nofree readnone [[CALL4]], double* noalias nofree readnone [[CALL9]], double* noalias nofree readnone undef) -; IS__CGSCC____-NEXT: br label [[RETURN]] -; IS__CGSCC____: if.end12: -; IS__CGSCC____-NEXT: [[CMP13:%.*]] = icmp eq double* [[A]], [[B]] -; IS__CGSCC____-NEXT: br i1 [[CMP13]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] -; IS__CGSCC____: cond.true: -; IS__CGSCC____-NEXT: br label [[COND_END:%.*]] -; IS__CGSCC____: cond.false: -; IS__CGSCC____-NEXT: [[CALL14:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone [[R]]) -; IS__CGSCC____-NEXT: br label [[COND_END]] -; IS__CGSCC____: cond.end: -; IS__CGSCC____-NEXT: [[COND:%.*]] = phi double* [ [[R]], [[COND_TRUE]] ], [ [[CALL14]], [[COND_FALSE]] ] -; IS__CGSCC____-NEXT: br label [[RETURN]] -; IS__CGSCC____: return: -; IS__CGSCC____-NEXT: [[RETVAL_0:%.*]] = phi double* [ [[CALL1]], [[IF_THEN]] ], [ [[CALL11]], [[IF_THEN3]] ], [ [[COND]], [[COND_END]] ] -; IS__CGSCC____-NEXT: ret double* [[RETVAL_0]] +; IS__TUNIT_OPM-LABEL: define {{[^@]+}}@ptr_scc_r2 +; IS__TUNIT_OPM-SAME: (double* nofree readnone [[A:%.*]], double* nofree readnone [[B:%.*]], double* nofree readnone returned [[R:%.*]]) +; IS__TUNIT_OPM-NEXT: entry: +; IS__TUNIT_OPM-NEXT: [[CMP:%.*]] = icmp ugt double* [[A]], [[B]] +; IS__TUNIT_OPM-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; IS__TUNIT_OPM: if.then: +; IS__TUNIT_OPM-NEXT: [[CALL:%.*]] = call double* @ptr_sink_r0(double* noalias nofree readnone "no-capture-maybe-returned" [[R]]) +; IS__TUNIT_OPM-NEXT: [[CALL1:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[B]], double* noalias nofree readnone [[A]], double* noalias nofree readnone [[CALL]]) +; IS__TUNIT_OPM-NEXT: br label [[RETURN:%.*]] +; IS__TUNIT_OPM: if.end: +; IS__TUNIT_OPM-NEXT: [[CMP2:%.*]] = icmp ult double* [[A]], [[B]] +; IS__TUNIT_OPM-NEXT: br i1 [[CMP2]], label [[IF_THEN3:%.*]], label [[IF_END12:%.*]] +; IS__TUNIT_OPM: if.then3: +; IS__TUNIT_OPM-NEXT: [[CALL4:%.*]] = call double* @ptr_sink_r0(double* noalias nofree readnone [[B]]) +; IS__TUNIT_OPM-NEXT: [[CALL5:%.*]] = call double* @ptr_scc_r1(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone undef) +; IS__TUNIT_OPM-NEXT: [[CALL6:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[R]], double* noalias nofree readnone [[R]], double* noalias nofree readnone [[R]]) +; IS__TUNIT_OPM-NEXT: [[CALL7:%.*]] = call double* @ptr_scc_r1(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[CALL6]], double* noalias nofree readnone undef) +; IS__TUNIT_OPM-NEXT: [[CALL8:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone [[R]]) +; IS__TUNIT_OPM-NEXT: [[CALL9:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[CALL5]], double* noalias nofree readnone [[CALL7]], double* noalias nofree readnone [[CALL8]]) +; IS__TUNIT_OPM-NEXT: [[CALL11:%.*]] = call double* @ptr_scc_r1(double* noalias nofree readnone [[CALL4]], double* noalias nofree readnone [[CALL9]], double* noalias nofree readnone undef) +; IS__TUNIT_OPM-NEXT: br label [[RETURN]] +; IS__TUNIT_OPM: if.end12: +; IS__TUNIT_OPM-NEXT: [[CMP13:%.*]] = icmp eq double* [[A]], [[B]] +; IS__TUNIT_OPM-NEXT: br i1 [[CMP13]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] +; IS__TUNIT_OPM: cond.true: +; IS__TUNIT_OPM-NEXT: br label [[COND_END:%.*]] +; IS__TUNIT_OPM: cond.false: +; IS__TUNIT_OPM-NEXT: [[CALL14:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone [[R]]) +; IS__TUNIT_OPM-NEXT: br label [[COND_END]] +; IS__TUNIT_OPM: cond.end: +; IS__TUNIT_OPM-NEXT: [[COND:%.*]] = phi double* [ [[R]], [[COND_TRUE]] ], [ [[CALL14]], [[COND_FALSE]] ] +; IS__TUNIT_OPM-NEXT: br label [[RETURN]] +; IS__TUNIT_OPM: return: +; IS__TUNIT_OPM-NEXT: [[RETVAL_0:%.*]] = phi double* [ [[CALL1]], [[IF_THEN]] ], [ [[CALL11]], [[IF_THEN3]] ], [ [[COND]], [[COND_END]] ] +; IS__TUNIT_OPM-NEXT: ret double* [[RETVAL_0]] +; +; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@ptr_scc_r2 +; IS__TUNIT_NPM-SAME: (double* nofree readnone [[A:%.*]], double* nofree readnone [[B:%.*]], double* nofree readnone returned [[R:%.*]]) +; IS__TUNIT_NPM-NEXT: entry: +; IS__TUNIT_NPM-NEXT: [[CMP:%.*]] = icmp ugt double* [[A]], [[B]] +; IS__TUNIT_NPM-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; IS__TUNIT_NPM: if.then: +; IS__TUNIT_NPM-NEXT: [[CALL:%.*]] = call nonnull double* @ptr_sink_r0(double* noalias nofree readnone "no-capture-maybe-returned" [[R]]) +; IS__TUNIT_NPM-NEXT: [[CALL1:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[B]], double* noalias nofree readnone [[A]], double* noalias nofree nonnull readnone [[CALL]]) +; IS__TUNIT_NPM-NEXT: br label [[RETURN:%.*]] +; IS__TUNIT_NPM: if.end: +; IS__TUNIT_NPM-NEXT: [[CMP2:%.*]] = icmp ult double* [[A]], [[B]] +; IS__TUNIT_NPM-NEXT: br i1 [[CMP2]], label [[IF_THEN3:%.*]], label [[IF_END12:%.*]] +; IS__TUNIT_NPM: if.then3: +; IS__TUNIT_NPM-NEXT: [[CALL4:%.*]] = call nonnull double* @ptr_sink_r0(double* noalias nofree readnone [[B]]) +; IS__TUNIT_NPM-NEXT: [[CALL5:%.*]] = call nonnull double* @ptr_scc_r1(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone undef) +; IS__TUNIT_NPM-NEXT: [[CALL6:%.*]] = call nonnull double* @ptr_scc_r2(double* noalias nofree readnone [[R]], double* noalias nofree readnone [[R]], double* noalias nofree readnone [[R]]) +; IS__TUNIT_NPM-NEXT: [[CALL7:%.*]] = call nonnull double* @ptr_scc_r1(double* noalias nofree readnone [[A]], double* noalias nofree nonnull readnone [[CALL6]], double* noalias nofree readnone undef) +; IS__TUNIT_NPM-NEXT: [[CALL8:%.*]] = call nonnull double* @ptr_scc_r2(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone [[R]]) +; IS__TUNIT_NPM-NEXT: [[CALL9:%.*]] = call nonnull double* @ptr_scc_r2(double* noalias nofree nonnull readnone [[CALL5]], double* noalias nofree nonnull readnone [[CALL7]], double* noalias nofree nonnull readnone [[CALL8]]) +; IS__TUNIT_NPM-NEXT: [[CALL11:%.*]] = call double* @ptr_scc_r1(double* noalias nofree nonnull readnone [[CALL4]], double* noalias nofree nonnull readnone [[CALL9]], double* noalias nofree nonnull readnone undef) +; IS__TUNIT_NPM-NEXT: br label [[RETURN]] +; IS__TUNIT_NPM: if.end12: +; IS__TUNIT_NPM-NEXT: [[CMP13:%.*]] = icmp eq double* [[A]], [[B]] +; IS__TUNIT_NPM-NEXT: br i1 [[CMP13]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] +; IS__TUNIT_NPM: cond.true: +; IS__TUNIT_NPM-NEXT: br label [[COND_END:%.*]] +; IS__TUNIT_NPM: cond.false: +; IS__TUNIT_NPM-NEXT: [[CALL14:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone [[R]]) +; IS__TUNIT_NPM-NEXT: br label [[COND_END]] +; IS__TUNIT_NPM: cond.end: +; IS__TUNIT_NPM-NEXT: [[COND:%.*]] = phi double* [ [[R]], [[COND_TRUE]] ], [ [[CALL14]], [[COND_FALSE]] ] +; IS__TUNIT_NPM-NEXT: br label [[RETURN]] +; IS__TUNIT_NPM: return: +; IS__TUNIT_NPM-NEXT: [[RETVAL_0:%.*]] = phi double* [ [[CALL1]], [[IF_THEN]] ], [ [[CALL11]], [[IF_THEN3]] ], [ [[COND]], [[COND_END]] ] +; IS__TUNIT_NPM-NEXT: ret double* [[RETVAL_0]] +; +; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@ptr_scc_r2 +; IS__CGSCC_OPM-SAME: (double* nofree readnone [[A:%.*]], double* nofree readnone [[B:%.*]], double* nofree readnone returned [[R:%.*]]) +; IS__CGSCC_OPM-NEXT: entry: +; IS__CGSCC_OPM-NEXT: [[CMP:%.*]] = icmp ugt double* [[A]], [[B]] +; IS__CGSCC_OPM-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; IS__CGSCC_OPM: if.then: +; IS__CGSCC_OPM-NEXT: [[CALL:%.*]] = call double* @ptr_sink_r0(double* noalias nofree readnone [[R]]) +; IS__CGSCC_OPM-NEXT: [[CALL1:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[B]], double* noalias nofree readnone [[A]], double* noalias nofree readnone [[CALL]]) +; IS__CGSCC_OPM-NEXT: br label [[RETURN:%.*]] +; IS__CGSCC_OPM: if.end: +; IS__CGSCC_OPM-NEXT: [[CMP2:%.*]] = icmp ult double* [[A]], [[B]] +; IS__CGSCC_OPM-NEXT: br i1 [[CMP2]], label [[IF_THEN3:%.*]], label [[IF_END12:%.*]] +; IS__CGSCC_OPM: if.then3: +; IS__CGSCC_OPM-NEXT: [[CALL4:%.*]] = call double* @ptr_sink_r0(double* noalias nofree readnone [[B]]) +; IS__CGSCC_OPM-NEXT: [[CALL5:%.*]] = call double* @ptr_scc_r1(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone undef) +; IS__CGSCC_OPM-NEXT: [[CALL6:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[R]], double* noalias nofree readnone [[R]], double* noalias nofree readnone [[R]]) +; IS__CGSCC_OPM-NEXT: [[CALL7:%.*]] = call double* @ptr_scc_r1(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[CALL6]], double* noalias nofree readnone undef) +; IS__CGSCC_OPM-NEXT: [[CALL8:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone [[R]]) +; IS__CGSCC_OPM-NEXT: [[CALL9:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[CALL5]], double* noalias nofree readnone [[CALL7]], double* noalias nofree readnone [[CALL8]]) +; IS__CGSCC_OPM-NEXT: [[CALL11:%.*]] = call double* @ptr_scc_r1(double* noalias nofree readnone [[CALL4]], double* noalias nofree readnone [[CALL9]], double* noalias nofree readnone undef) +; IS__CGSCC_OPM-NEXT: br label [[RETURN]] +; IS__CGSCC_OPM: if.end12: +; IS__CGSCC_OPM-NEXT: [[CMP13:%.*]] = icmp eq double* [[A]], [[B]] +; IS__CGSCC_OPM-NEXT: br i1 [[CMP13]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] +; IS__CGSCC_OPM: cond.true: +; IS__CGSCC_OPM-NEXT: br label [[COND_END:%.*]] +; IS__CGSCC_OPM: cond.false: +; IS__CGSCC_OPM-NEXT: [[CALL14:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone [[R]]) +; IS__CGSCC_OPM-NEXT: br label [[COND_END]] +; IS__CGSCC_OPM: cond.end: +; IS__CGSCC_OPM-NEXT: [[COND:%.*]] = phi double* [ [[R]], [[COND_TRUE]] ], [ [[CALL14]], [[COND_FALSE]] ] +; IS__CGSCC_OPM-NEXT: br label [[RETURN]] +; IS__CGSCC_OPM: return: +; IS__CGSCC_OPM-NEXT: [[RETVAL_0:%.*]] = phi double* [ [[CALL1]], [[IF_THEN]] ], [ [[CALL11]], [[IF_THEN3]] ], [ [[COND]], [[COND_END]] ] +; IS__CGSCC_OPM-NEXT: ret double* [[RETVAL_0]] +; +; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@ptr_scc_r2 +; IS__CGSCC_NPM-SAME: (double* nofree readnone [[A:%.*]], double* nofree readnone [[B:%.*]], double* nofree readnone returned [[R:%.*]]) +; IS__CGSCC_NPM-NEXT: entry: +; IS__CGSCC_NPM-NEXT: [[CMP:%.*]] = icmp ugt double* [[A]], [[B]] +; IS__CGSCC_NPM-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; IS__CGSCC_NPM: if.then: +; IS__CGSCC_NPM-NEXT: [[CALL:%.*]] = call double* @ptr_sink_r0(double* noalias nofree readnone [[R]]) +; IS__CGSCC_NPM-NEXT: [[CALL1:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[B]], double* noalias nofree readnone [[A]], double* noalias nofree readnone [[CALL]]) +; IS__CGSCC_NPM-NEXT: br label [[RETURN:%.*]] +; IS__CGSCC_NPM: if.end: +; IS__CGSCC_NPM-NEXT: [[CMP2:%.*]] = icmp ult double* [[A]], [[B]] +; IS__CGSCC_NPM-NEXT: br i1 [[CMP2]], label [[IF_THEN3:%.*]], label [[IF_END12:%.*]] +; IS__CGSCC_NPM: if.then3: +; IS__CGSCC_NPM-NEXT: [[CALL4:%.*]] = call double* @ptr_sink_r0(double* noalias nofree readnone [[B]]) +; IS__CGSCC_NPM-NEXT: [[CALL5:%.*]] = call nonnull double* @ptr_scc_r1(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone undef) +; IS__CGSCC_NPM-NEXT: [[CALL6:%.*]] = call nonnull double* @ptr_scc_r2(double* noalias nofree readnone [[R]], double* noalias nofree readnone [[R]], double* noalias nofree readnone [[R]]) +; IS__CGSCC_NPM-NEXT: [[CALL7:%.*]] = call nonnull double* @ptr_scc_r1(double* noalias nofree readnone [[A]], double* noalias nofree nonnull readnone [[CALL6]], double* noalias nofree readnone undef) +; IS__CGSCC_NPM-NEXT: [[CALL8:%.*]] = call nonnull double* @ptr_scc_r2(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone [[R]]) +; IS__CGSCC_NPM-NEXT: [[CALL9:%.*]] = call nonnull double* @ptr_scc_r2(double* noalias nofree nonnull readnone [[CALL5]], double* noalias nofree nonnull readnone [[CALL7]], double* noalias nofree nonnull readnone [[CALL8]]) +; IS__CGSCC_NPM-NEXT: [[CALL11:%.*]] = call double* @ptr_scc_r1(double* noalias nofree readnone [[CALL4]], double* noalias nofree nonnull readnone [[CALL9]], double* noalias nofree nonnull readnone undef) +; IS__CGSCC_NPM-NEXT: br label [[RETURN]] +; IS__CGSCC_NPM: if.end12: +; IS__CGSCC_NPM-NEXT: [[CMP13:%.*]] = icmp eq double* [[A]], [[B]] +; IS__CGSCC_NPM-NEXT: br i1 [[CMP13]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] +; IS__CGSCC_NPM: cond.true: +; IS__CGSCC_NPM-NEXT: br label [[COND_END:%.*]] +; IS__CGSCC_NPM: cond.false: +; IS__CGSCC_NPM-NEXT: [[CALL14:%.*]] = call double* @ptr_scc_r2(double* noalias nofree readnone [[A]], double* noalias nofree readnone [[B]], double* noalias nofree readnone [[R]]) +; IS__CGSCC_NPM-NEXT: br label [[COND_END]] +; IS__CGSCC_NPM: cond.end: +; IS__CGSCC_NPM-NEXT: [[COND:%.*]] = phi double* [ [[R]], [[COND_TRUE]] ], [ [[CALL14]], [[COND_FALSE]] ] +; IS__CGSCC_NPM-NEXT: br label [[RETURN]] +; IS__CGSCC_NPM: return: +; IS__CGSCC_NPM-NEXT: [[RETVAL_0:%.*]] = phi double* [ [[CALL1]], [[IF_THEN]] ], [ [[CALL11]], [[IF_THEN3]] ], [ [[COND]], [[COND_END]] ] +; IS__CGSCC_NPM-NEXT: ret double* [[RETVAL_0]] ; entry: %cmp = icmp ugt double* %a, %b diff --git a/llvm/test/Transforms/Attributor/value-simplify.ll b/llvm/test/Transforms/Attributor/value-simplify.ll --- a/llvm/test/Transforms/Attributor/value-simplify.ll +++ b/llvm/test/Transforms/Attributor/value-simplify.ll @@ -286,15 +286,15 @@ define internal void @test_sret(%struct.X* sret %a, %struct.X** %b) { ; -; IS__TUNIT____-LABEL: define {{[^@]+}}@test_sret -; IS__TUNIT____-SAME: (%struct.X* noalias nofree sret writeonly align 536870912 [[A:%.*]], %struct.X** nocapture nofree nonnull writeonly align 8 dereferenceable(8) [[B:%.*]]) -; IS__TUNIT____-NEXT: store %struct.X* [[A]], %struct.X** [[B]], align 8 -; IS__TUNIT____-NEXT: ret void -; -; IS__CGSCC____-LABEL: define {{[^@]+}}@test_sret -; IS__CGSCC____-SAME: (%struct.X* noalias nofree sret writeonly [[A:%.*]], %struct.X** nocapture nofree nonnull writeonly align 8 dereferenceable(8) [[B:%.*]]) -; IS__CGSCC____-NEXT: store %struct.X* [[A]], %struct.X** [[B]], align 8 -; IS__CGSCC____-NEXT: ret void +; NOT_CGSCC_OPM-LABEL: define {{[^@]+}}@test_sret +; NOT_CGSCC_OPM-SAME: (%struct.X* noalias nofree sret writeonly align 536870912 [[A:%.*]], %struct.X** nocapture nofree nonnull writeonly align 8 dereferenceable(8) [[B:%.*]]) +; NOT_CGSCC_OPM-NEXT: store %struct.X* [[A]], %struct.X** [[B]], align 8 +; NOT_CGSCC_OPM-NEXT: ret void +; +; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@test_sret +; IS__CGSCC_OPM-SAME: (%struct.X* noalias nofree sret writeonly [[A:%.*]], %struct.X** nocapture nofree nonnull writeonly align 8 dereferenceable(8) [[B:%.*]]) +; IS__CGSCC_OPM-NEXT: store %struct.X* [[A]], %struct.X** [[B]], align 8 +; IS__CGSCC_OPM-NEXT: ret void ; store %struct.X* %a, %struct.X** %b ret void @@ -376,9 +376,13 @@ ret i8* %l } define i8* @complicated_args_byval2() { -; CHECK-LABEL: define {{[^@]+}}@complicated_args_byval2() -; CHECK-NEXT: [[C:%.*]] = call i8* @test_byval2() -; CHECK-NEXT: ret i8* [[C]] +; IS________OPM-LABEL: define {{[^@]+}}@complicated_args_byval2() +; IS________OPM-NEXT: [[C:%.*]] = call i8* @test_byval2() +; IS________OPM-NEXT: ret i8* [[C]] +; +; IS________NPM-LABEL: define {{[^@]+}}@complicated_args_byval2() +; IS________NPM-NEXT: [[C:%.*]] = call nonnull i8* @test_byval2() +; IS________NPM-NEXT: ret i8* [[C]] ; %c = call i8* @test_byval2(%struct.X* @S) ret i8* %c