diff --git a/include/llvm/Transforms/IPO/Attributor.h b/include/llvm/Transforms/IPO/Attributor.h index 6e1aabd4463..34b3ea9a6bd 100644 --- a/include/llvm/Transforms/IPO/Attributor.h +++ b/include/llvm/Transforms/IPO/Attributor.h @@ -119,6 +119,368 @@ ChangeStatus operator|(ChangeStatus l, ChangeStatus r); ChangeStatus operator&(ChangeStatus l, ChangeStatus r); ///} +/// Helper to describe and deal with positions in the LLVM-IR. +/// +/// A position in the IR is described by an anchor value and an "offset" that +/// could be the argument number, for call sites and arguments, or an indicator +/// of the "position kind". The kinds, specified in the Kind enum below, include +/// the locations in the attribute list, i.a., function scope and return value, +/// as well as a distinction between call sites and functions. Finally, there +/// are floating values that do not have a corresponding attribute list +/// position. +struct IRPosition { + virtual ~IRPosition() {} + + /// The positions we distinguish in the IR. + /// + /// The values are chosen such that the KindOrArgNo member has a value >= 1 + /// if it is an argument or call site argument while a value < 1 indicates the + /// respective kind of that value. + enum Kind : int { + IRP_INVALID = -6, ///< An invalid position. + IRP_FLOAT = -5, ///< A position that is not associated with a spot suitable + ///< for attributes. This could be any value or instruction. + IRP_RETURNED = -4, ///< An attribute for the function return value. + IRP_CALL_SITE_RETURNED = -3, ///< An attribute for a call site return value. + IRP_FUNCTION = -2, ///< An attribute for a function (scope). + IRP_CALL_SITE = -1, ///< An attribute for a call site (function scope). + IRP_ARGUMENT = 0, ///< An attribute for a function argument. + IRP_CALL_SITE_ARGUMENT = 1, ///< An attribute for a call site argument. + }; + + /// Default constructor available to create invalid positions implicitly. All + /// other positions need to be created explicitly through the appropriate + /// static member function. + IRPosition() : AnchorVal(nullptr), KindOrArgNo(IRP_INVALID) { verify(); } + + /// Create a position describing the value of \p V. + static const IRPosition value(const Value &V) { + if (auto *Arg = dyn_cast(&V)) + return IRPosition::argument(*Arg); + if (auto *CB = dyn_cast(&V)) + return IRPosition::callsite_returned(*CB); + return IRPosition(const_cast(V), IRP_FLOAT); + } + + /// Create a position describing the function scope of \p F. + static const IRPosition function(const Function &F) { + return IRPosition(const_cast(F), IRP_FUNCTION); + } + + /// Create a position describing the returned value of \p F. + static const IRPosition returned(const Function &F) { + return IRPosition(const_cast(F), IRP_RETURNED); + } + + /// Create a position describing the argument \p Arg. + static const IRPosition argument(const Argument &Arg) { + return IRPosition(const_cast(Arg), Kind(Arg.getArgNo())); + } + + /// Create a position describing the function scope of \p CB. + static const IRPosition callsite_function(const CallBase &CB) { + return IRPosition(const_cast(CB), IRP_CALL_SITE); + } + + /// Create a position describing the returned value of \p CB. + static const IRPosition callsite_returned(const CallBase &CB) { + return IRPosition(const_cast(CB), IRP_CALL_SITE_RETURNED); + } + + /// Create a position describing the argument of \p CB at position \p ArgNo. + static const IRPosition callsite_argument(const CallBase &CB, + unsigned ArgNo) { + return IRPosition(const_cast(CB), Kind(ArgNo)); + } + + /// Create a position describing the function scope of \p ICS. + static const IRPosition callsite_function(ImmutableCallSite ICS) { + return IRPosition::callsite_function(cast(*ICS.getInstruction())); + } + + /// Create a position describing the returned value of \p ICS. + static const IRPosition callsite_returned(ImmutableCallSite ICS) { + return IRPosition::callsite_returned(cast(*ICS.getInstruction())); + } + + /// Create a position describing the argument of \p ICS at position \p ArgNo. + static const IRPosition callsite_argument(ImmutableCallSite ICS, + unsigned ArgNo) { + return IRPosition::callsite_argument(cast(*ICS.getInstruction()), + ArgNo); + } + + /// Create a position with function scope matching the "context" of \p IRP. + /// If \p IRP is a call site (see isAnyCallSitePosition()) then the result + /// will be a call site position, otherwise the function position of the + /// associated function. + static const IRPosition function_scope(const IRPosition &IRP) { + if (IRP.isAnyCallSitePosition()) { + return IRPosition::callsite_function( + cast(IRP.getAnchorValue())); + } + assert(IRP.getAssociatedFunction()); + return IRPosition::function(*IRP.getAssociatedFunction()); + } + + bool operator==(const IRPosition &RHS) const { + return (AnchorVal == RHS.AnchorVal) && (KindOrArgNo == RHS.KindOrArgNo); + } + bool operator!=(const IRPosition &RHS) const { return !(*this == RHS); } + + /// Return the value this abstract attribute is anchored with. + /// + /// The anchor value might not be the associated value if the latter is not + /// sufficient to determine where arguments will be manifested. This is, so + /// far, only the case for call site arguments as the value is not sufficient + /// to pinpoint them. Instead, we can use the call site as an anchor. + /// + ///{ + Value &getAnchorValue() { + assert(KindOrArgNo != IRP_INVALID && + "Invalid position does not have an anchor value!"); + return *AnchorVal; + } + const Value &getAnchorValue() const { + return const_cast(this)->getAnchorValue(); + } + ///} + + /// Return the associated function, if any. + /// + ///{ + Function *getAssociatedFunction() { + if (auto *CB = dyn_cast(AnchorVal)) + return CB->getCalledFunction(); + assert(KindOrArgNo != IRP_INVALID && + "Invalid position does not have an anchor scope!"); + Value &V = getAnchorValue(); + if (isa(V)) + return &cast(V); + if (isa(V)) + return cast(V).getParent(); + if (isa(V)) + return cast(V).getFunction(); + return nullptr; + } + const Function *getAssociatedFunction() const { + return const_cast(this)->getAssociatedFunction(); + } + ///} + + /// Return the Function surrounding the anchor value. + /// + ///{ + Function &getAnchorScope() { + Value &V = getAnchorValue(); + if (isa(V)) + return cast(V); + if (isa(V)) + return *cast(V).getParent(); + if (isa(V)) + return *cast(V).getFunction(); + llvm_unreachable("No anchor scope"); + } + const Function &getAnchorScope() const { + return const_cast(this)->getAnchorScope(); + } + ///} + + /// Return the context instruction, if any. + /// + ///{ + Instruction *getCtxI() { + Value &V = getAnchorValue(); + if (auto *I = dyn_cast(&V)) + return I; + if (auto *Arg = dyn_cast(&V)) + if (!Arg->getParent()->isDeclaration()) + return &Arg->getParent()->getEntryBlock().front(); + return nullptr; + } + const Instruction *getCtxI() const { + return const_cast(this)->getCtxI(); + } + ///} + + /// Return the value this abstract attribute is associated with. + /// + ///{ + Value &getAssociatedValue() { + assert(KindOrArgNo != IRP_INVALID && + "Invalid position does not have an associated value!"); + if (getArgNo() < 0 || isa(AnchorVal)) + return *AnchorVal; + assert(isa(AnchorVal) && "Expected a call base!"); + return *cast(AnchorVal)->getArgOperand(getArgNo()); + } + const Value &getAssociatedValue() const { + return const_cast(this)->getAssociatedValue(); + } + ///} + + /// Return the argument number of the associated value if it is an argument or + /// call site argument, otherwise a negative value. + int getArgNo() const { return KindOrArgNo; } + + /// Return the index in the attribute list for this position. + unsigned getAttrIdx() const { + switch (getPositionKind()) { + case IRPosition::IRP_INVALID: + case IRPosition::IRP_FLOAT: + break; + case IRPosition::IRP_FUNCTION: + case IRPosition::IRP_CALL_SITE: + return AttributeList::FunctionIndex; + case IRPosition::IRP_RETURNED: + case IRPosition::IRP_CALL_SITE_RETURNED: + return AttributeList::ReturnIndex; + case IRPosition::IRP_ARGUMENT: + case IRPosition::IRP_CALL_SITE_ARGUMENT: + return KindOrArgNo + AttributeList::FirstArgIndex; + } + llvm_unreachable( + "There is no attribute index for a floating or invalid position!"); + } + + /// Return the associated position kind. + Kind getPositionKind() const { + if (getArgNo() >= 0) { + assert(((isa(getAnchorValue()) && + isa(getAssociatedValue())) || + isa(getAnchorValue())) && + "Expected argument or call base due to argument number!"); + if (isa(getAnchorValue())) + return IRP_CALL_SITE_ARGUMENT; + return IRP_ARGUMENT; + } + + assert(KindOrArgNo < 0 && + "Expected (call site) arguments to never reach this point!"); + assert(!isa(getAnchorValue()) && + "Expected arguments to have an associated argument position!"); + return Kind(KindOrArgNo); + } + + /// TODO: Figure out if the attribute related helper functions should live + /// here or somewhere else. + + /// Return true if any kind in \p AKs existing in the IR at a position that + /// will affect this one. See also getAttrs(...). + bool hasAttr(ArrayRef AKs) const; + + /// Return the attributes of any kind in \p AKs existing in the IR at a + /// position that will affect this one. While each position can only have a + /// single attribute of any kind in \p AKs, there are "subsuming" positions + /// that could have an attribute as well. This method returns all attributes + /// found in \p Attrs. + void getAttrs(ArrayRef AKs, + SmallVectorImpl &Attrs) const; + + /// Return the attribute of kind \p AK existing in the IR at this position. + Attribute getAttr(Attribute::AttrKind AK) const { + if (getPositionKind() == IRP_INVALID || getPositionKind() == IRP_FLOAT) + return Attribute(); + + AttributeList AttrList; + if (ImmutableCallSite ICS = ImmutableCallSite(&getAnchorValue())) + AttrList = ICS.getAttributes(); + else + AttrList = getAssociatedFunction()->getAttributes(); + + if (AttrList.hasAttribute(getAttrIdx(), AK)) + return AttrList.getAttribute(getAttrIdx(), AK); + return Attribute(); + } + + bool isAnyCallSitePosition() const { + switch (getPositionKind()) { + case IRPosition::IRP_CALL_SITE: + case IRPosition::IRP_CALL_SITE_RETURNED: + case IRPosition::IRP_CALL_SITE_ARGUMENT: + return true; + default: + return false; + } + } + + /// Special DenseMap key values. + /// + ///{ + static const IRPosition EmptyKey; + static const IRPosition TombstoneKey; + ///} + +private: + /// Private constructor for special values only! + explicit IRPosition(int KindOrArgNo) + : AnchorVal(0), KindOrArgNo(KindOrArgNo) {} + + /// IRPosition anchored at \p AnchorVal with kind/argument numbet \p PK. + explicit IRPosition(Value &AnchorVal, Kind PK) + : AnchorVal(&AnchorVal), KindOrArgNo(PK) { + verify(); + } + + /// Verify internal invariants. + void verify(); + + /// The value this position is anchored at. + Value *AnchorVal; + + /// The argument number, if non-negative, or the position "kind". + int KindOrArgNo; +}; + +/// Helper that allows IRPosition as a key in a DenseMap. +template <> struct DenseMapInfo { + static inline IRPosition getEmptyKey() { return IRPosition::EmptyKey; } + static inline IRPosition getTombstoneKey() { + return IRPosition::TombstoneKey; + } + static unsigned getHashValue(const IRPosition &IRP) { + return (DenseMapInfo::getHashValue(&IRP.getAnchorValue()) << 4) ^ + (unsigned(IRP.getArgNo())); + } + static bool isEqual(const IRPosition &LHS, const IRPosition &RHS) { + return LHS == RHS; + } +}; + +/// A visitor class for IR positions. +/// +/// Given a position P, the SubsumingPositionIterator allows to visit "subsuming +/// positions" wrt. attributes/information. Thus, if a piece of information +/// holds for a subsuming position, it also holds for the position P. +/// +/// The subsuming positions always include the initial position and then, +/// depending on the position kind, additionally the following ones: +/// - for IRP_RETURNED: +/// - the function (IRP_FUNCTION) +/// - for IRP_ARGUMENT: +/// - the function (IRP_FUNCTION) +/// - for IRP_CALL_SITE: +/// - the callee (IRP_FUNCTION), if known +/// - for IRP_CALL_SITE_RETURNED: +/// - the callee (IRP_RETURNED), if known +/// - the call site (IRP_FUNCTION) +/// - the callee (IRP_FUNCTION), if known +/// - for IRP_CALL_SITE_ARGUMENT: +/// - the argument of the callee (IRP_ARGUMENT), if known +/// - the callee (IRP_FUNCTION), if known +/// - the position the call site argument is associated with if it is not +/// anchored to the call site, e.g., if it is an arugment then the argument +/// (IRP_ARGUMENT) +class SubsumingPositionIterator { + SmallVector IRPositions; + using iterator = decltype(IRPositions)::iterator; + +public: + SubsumingPositionIterator(const IRPosition &IRP); + iterator begin() { return IRPositions.begin(); } + iterator end() { return IRPositions.end(); } +}; + /// Data structure to hold cached (LLVM-IR) information. /// /// All attributes are given an InformationCache object at creation time to @@ -212,10 +574,11 @@ struct Attributor { /// \Returns CHANGED if the IR was changed, otherwise UNCHANGED. ChangeStatus run(); - /// Lookup an abstract attribute of type \p AAType anchored at value \p V and - /// argument number \p ArgNo. If no attribute is found and \p V is a call base - /// instruction, the called function is tried as a value next. Thus, the - /// returned abstract attribute might be anchored at the callee of \p V. + /// Lookup an abstract attribute of type \p AAType at position \p IRP. While + /// no abstract attribute is found equivalent positions are checked, see + /// SubsumingPositionIterator. Thus, the returned abstract attribute + /// might be anchored at a different position, e.g., the callee if \p IRP is a + /// call base. /// /// This method is the only (supported) way an abstract attribute can retrieve /// information from another abstract attribute. As an example, take an @@ -225,46 +588,30 @@ struct Attributor { /// the one reasoning about the "captured" state for the argument or the one /// reasoning on the memory access behavior of the function as a whole. template - const AAType *getAAFor(const AbstractAttribute &QueryingAA, const Value &V, - int ArgNo = -1) { + const AAType *getAAFor(const AbstractAttribute &QueryingAA, + const IRPosition &IRP) { static_assert(std::is_base_of::value, "Cannot query an attribute with a type not derived from " "'AbstractAttribute'!"); - // Determine the argument number automatically for llvm::Arguments if none - // is set. Do not override a given one as it could be a use of the argument - // in a call site. - if (ArgNo == -1) - if (auto *Arg = dyn_cast(&V)) - ArgNo = Arg->getArgNo(); - - // If a function was given together with an argument number, perform the - // lookup for the actual argument instead. Don't do it for variadic - // arguments. - if (ArgNo >= 0 && isa(&V) && - cast(&V)->arg_size() > (size_t)ArgNo) - return getAAFor( - QueryingAA, *(cast(&V)->arg_begin() + ArgNo), ArgNo); - - // Lookup the abstract attribute of type AAType. If found, return it after - // registering a dependence of QueryingAA on the one returned attribute. - const auto &KindToAbstractAttributeMap = AAMap.lookup({&V, ArgNo}); - if (AAType *AA = static_cast( - KindToAbstractAttributeMap.lookup(&AAType::ID))) { - // Do not return an attribute with an invalid state. This minimizes checks - // at the calls sites and allows the fallback below to kick in. - if (AA->getState().isValidState()) { - QueryMap[AA].insert(const_cast(&QueryingAA)); - return AA; + // Let's try an equivalent position if available, see + // SubsumingPositionIterator for more information. + for (const IRPosition &EquivIRP : SubsumingPositionIterator(IRP)) { + // Lookup the abstract attribute of type AAType. If found, return it after + // registering a dependence of QueryingAA on the one returned attribute. + const auto &KindToAbstractAttributeMap = + AAMap.lookup(const_cast(EquivIRP)); + if (AAType *AA = static_cast( + KindToAbstractAttributeMap.lookup(&AAType::ID))) { + // Do not return an attribute with an invalid state. This minimizes + // checks at the calls sites and allows the fallback below to kick in. + if (AA->getState().isValidState()) { + QueryMap[AA].insert(const_cast(&QueryingAA)); + return AA; + } } } - // If no abstract attribute was found and we look for a call site argument, - // defer to the actual argument instead. - ImmutableCallSite ICS(&V); - if (ICS && ICS.getCalledValue()) - return getAAFor(QueryingAA, *ICS.getCalledValue(), ArgNo); - // No matching attribute found return nullptr; } @@ -274,27 +621,16 @@ struct Attributor { /// Note that ownership of the attribute is given to the Attributor. It will /// invoke delete for the Attributor on destruction of the Attributor. /// - /// Attributes are identified by - /// (1) their anchored value (see AA.getAnchoredValue()), - /// (2) their argument number (\p ArgNo, or Argument::getArgNo()), and - /// (3) the address of their static member (see AAType::ID). - template AAType ®isterAA(AAType &AA, int ArgNo = -1) { + /// Attributes are identified by their IR position (AAType::getIRPosition()) + /// and the address of their static member (see AAType::ID). + template AAType ®isterAA(AAType &AA) { static_assert(std::is_base_of::value, "Cannot register an attribute with a type not derived from " "'AbstractAttribute'!"); - - // Determine the anchor value and the argument number which are used to - // lookup the attribute together with AAType::ID. If passed an argument, - // use its argument number but do not override a given one as it could be a - // use of the argument at a call site. - Value &AnchorVal = AA.getIRPosition().getAnchorValue(); - if (ArgNo == -1) - if (auto *Arg = dyn_cast(&AnchorVal)) - ArgNo = Arg->getArgNo(); - // Put the attribute in the lookup map structure and the container we use to // keep track of all attributes. - AAMap[{&AnchorVal, ArgNo}][&AAType::ID] = &AA; + IRPosition &IRP = AA.getIRPosition(); + AAMap[IRP][&AAType::ID] = &AA; AllAbstractAttributes.push_back(&AA); return AA; } @@ -321,34 +657,33 @@ struct Attributor { /// This method will evaluate \p Pred on call sites and return /// true if \p Pred holds in every call sites. However, this is only possible /// all call sites are known, hence the function has internal linkage. - bool checkForAllCallSites(Function &F, std::function &Pred, + bool checkForAllCallSites(const function_ref &Pred, const AbstractAttribute &QueryingAA, bool RequireAllCallSites); /// Check \p Pred on all values potentially returned by \p F. /// /// This method will evaluate \p Pred on all values potentially returned by - /// \p F associated to their respective return instructions. Return true if - /// \p Pred holds on all of them. + /// the function associated with \p QueryingAA. The returned values are + /// matched with their respective return instructions. Returns true if \p Pred + /// holds on all of them. bool checkForAllReturnedValuesAndReturnInsts( - const Function &F, const function_ref &)> &Pred, const AbstractAttribute &QueryingAA); - /// Check \p Pred on all values potentially returned by \p F. + /// Check \p Pred on all values potentially returned by the function + /// associated with \p QueryingAA. /// /// This is the context insensitive version of the method above. - bool checkForAllReturnedValues(const Function &F, - const function_ref &Pred, + bool checkForAllReturnedValues(const function_ref &Pred, const AbstractAttribute &QueryingAA); /// Check \p Pred on all instructions with an opcode present in \p Opcodes. /// /// This method will evaluate \p Pred on all instructions with an opcode /// present in \p Opcode and return true if \p Pred holds on all of them. - bool checkForAllInstructions(const Function &F, - const function_ref &Pred, + bool checkForAllInstructions(const function_ref &Pred, const AbstractAttribute &QueryingAA, const ArrayRef &Opcodes); @@ -356,10 +691,9 @@ struct Attributor { /// /// See checkForAllCallLikeInstructions(...) for more information. bool - checkForAllCallLikeInstructions(const Function &F, - const function_ref &Pred, + checkForAllCallLikeInstructions(const function_ref &Pred, const AbstractAttribute &QueryingAA) { - return checkForAllInstructions(F, Pred, QueryingAA, + return checkForAllInstructions(Pred, QueryingAA, {(unsigned)Instruction::Invoke, (unsigned)Instruction::CallBr, (unsigned)Instruction::Call}); @@ -371,7 +705,7 @@ struct Attributor { /// to memory present in the information cache and return true if \p Pred /// holds on all of them. bool checkForAllReadWriteInstructions( - const Function &F, const llvm::function_ref &Pred, + const llvm::function_ref &Pred, AbstractAttribute &QueryingAA); /// Return the data layout associated with the anchor scope. @@ -384,13 +718,13 @@ private: AAVector AllAbstractAttributes; ///} - /// A nested map to lookup abstract attributes based on the anchored value and - /// an argument positions (or -1) on the outer level, and the addresses of the - /// static member (AAType::ID) on the inner level. + /// A nested map to lookup abstract attributes based on the argument position + /// on the outer level, and the addresses of the static member (AAType::ID) on + /// the inner level. ///{ using KindToAbstractAttributeMap = DenseMap; - DenseMap, KindToAbstractAttributeMap> AAMap; + DenseMap AAMap; ///} /// A map from abstract attributes to the ones that queried them through calls @@ -560,142 +894,6 @@ struct BooleanState : public IntegerState { BooleanState() : IntegerState(1){}; }; -/// Struct to encode the position in the LLVM-IR with regards to the associated -/// value but also the attribute lists. -struct IRPosition { - - /// The positions attributes can be manifested in. - enum Kind { - IRP_FUNCTION = AttributeList::FunctionIndex, ///< An attribute for a - ///< function as a whole. - IRP_RETURNED = AttributeList::ReturnIndex, ///< An attribute for the - ///< function return value. - IRP_ARGUMENT, ///< An attribute for a function argument. - IRP_CALL_SITE_ARGUMENT, ///< An attribute for a call site argument. - }; - - /// Create an IRPosition for an argument. - explicit IRPosition(Argument &Arg) : IRPosition(&Arg, Arg, Arg.getArgNo()) {} - - /// Create an IRPosition for a function return or function body position. - /// - /// \param Fn The value this abstract attributes is anchored at and - /// associated with. - /// \param PK The kind of attribute position, can not be a (call site) - /// argument. - explicit IRPosition(Function &Fn, Kind PK) - : AssociatedVal(&Fn), AnchorVal(Fn), AttributeIdx(PK) { - assert((PK == IRP_RETURNED || PK == IRP_FUNCTION) && - "Expected non-argument position!"); - } - - /// An abstract attribute associated with \p AssociatedVal and anchored at - /// \p AnchorVal. - /// - /// \param AssociatedVal The value this abstract attribute is associated with. - /// \param AnchorVal The value this abstract attributes is anchored at. - /// \param ArgumentNo The index in the attribute list, encodes also the - /// argument number if this is one. - explicit IRPosition(Value *AssociatedVal, Value &AnchorVal, - unsigned ArgumentNo) - : AssociatedVal(AssociatedVal), AnchorVal(AnchorVal), - AttributeIdx(ArgumentNo + AttributeList::FirstArgIndex) { - assert(((isa(&AnchorVal) && - cast(&AnchorVal)->arg_size() > ArgumentNo) || - (isa(AnchorVal) && - cast(AnchorVal).getArgNo() == ArgumentNo)) && - "Expected a valid argument index!"); - } - -#define IRPositionConstructorForward(NAME, BASE) \ - explicit NAME(Argument &Arg) : BASE(Arg) {} \ - explicit NAME(Function &Fn, IRPosition::Kind PK) : BASE(Fn, PK) {} \ - NAME(Value *AssociatedVal, Value &AnchorVal, unsigned ArgumentNo) \ - : BASE(AssociatedVal, AnchorVal, ArgumentNo) {} - - IRPosition(const IRPosition &AAP) - : IRPosition(AAP.AssociatedVal, AAP.AnchorVal, AAP.AttributeIdx) {} - - /// Virtual destructor. - virtual ~IRPosition() {} - - /// Return the value this abstract attribute is anchored with. - /// - /// The anchored value might not be the associated value if the latter is not - /// sufficient to determine where arguments will be manifested. This is mostly - /// the case for call site arguments as the value is not sufficient to - /// pinpoint them. Instead, we can use the call site as an anchor. - /// - ///{ - Value &getAnchorValue() { return AnchorVal; } - const Value &getAnchorValue() const { return AnchorVal; } - ///} - - /// Return the llvm::Function surrounding the anchored value. - /// - ///{ - Function &getAnchorScope() { - Value &V = getAnchorValue(); - if (isa(V)) - return cast(V); - if (isa(V)) - return *cast(V).getParent(); - if (isa(V)) - return *cast(V).getFunction(); - llvm_unreachable("No scope for anchored value found!"); - } - const Function &getAnchorScope() const { - return const_cast(this)->getAnchorScope(); - } - ///} - - /// Return the value this abstract attribute is associated with. - /// - /// The abstract state usually represents this value. - /// - ///{ - Value *getAssociatedValue() { return AssociatedVal; } - const Value *getAssociatedValue() const { return AssociatedVal; } - ///} - - /// Return the argument number of the associated value if it is an argument or - /// call site argument, otherwise a negative value. - int getArgNo() const { return AttributeIdx - AttributeList::FirstArgIndex; } - - /// Return the position this abstract state is manifested in. - Kind getPositionKind() const { - if (getArgNo() >= 0) { - if (isa(getAnchorValue())) - return IRP_CALL_SITE_ARGUMENT; - assert((isa(getAnchorValue()) || - isa_and_nonnull(getAssociatedValue()) || - (!getAssociatedValue() && isa(getAnchorValue()))) && - "Expected argument or call base due to argument number!"); - return IRP_ARGUMENT; - } - return (Kind)AttributeIdx; - } - - /// Return the index in the attribute list for this position. - int getAttrIdx() const { return AttributeIdx; } - - /// Change the associated value. - void setAssociatedValue(Value *V) { AssociatedVal = V; } - - /// Change the associated attribue list position. - void setAttributeIdx(int AttrIdx) { AttributeIdx = AttrIdx; } - -protected: - /// The value this abstract attribute is associated with. - Value *AssociatedVal; - - /// The value this abstract attribute is anchored at. - Value &AnchorVal; - - /// The index in the attribute list. - int AttributeIdx; -}; - /// Helper struct necessary as the modular build fails if the virtual method /// IRAttribute::manifest is defined in the Attributor.cpp. struct IRAttributeManifest { @@ -719,18 +917,13 @@ struct StateWrapper : public StateTy, public Base { /// Helper class that provides common functionality to manifest IR attributes. template struct IRAttribute : public IRPosition, public Base { + IRAttribute(const IRPosition &IRP) : IRPosition(IRP) {} ~IRAttribute() {} - /// Constructors for the IRPosition. - /// - ///{ - IRPositionConstructorForward(IRAttribute, IRPosition); - ///} - /// See AbstractAttribute::manifest(...). - ChangeStatus manifest(Attributor &A) { + ChangeStatus manifest(Attributor &A) override { SmallVector DeducedAttrs; - getDeducedAttributes(getAnchorScope().getContext(), DeducedAttrs); + getDeducedAttributes(getAnchorValue().getContext(), DeducedAttrs); return IRAttributeManifest::manifestAttrs(A, getIRPosition(), DeducedAttrs); } @@ -746,8 +939,8 @@ struct IRAttribute : public IRPosition, public Base { /// Return an IR position, see struct IRPosition. /// ///{ - IRPosition &getIRPosition() { return *this; } - const IRPosition &getIRPosition() const { return *this; } + IRPosition &getIRPosition() override { return *this; } + const IRPosition &getIRPosition() const override { return *this; } ///} }; @@ -890,7 +1083,7 @@ Pass *createAttributorLegacyPass(); /// An abstract attribute for the returned values of a function. struct AAReturnedValues : public IRAttribute { - IRPositionConstructorForward(AAReturnedValues, IRAttribute); + AAReturnedValues(const IRPosition &IRP) : IRAttribute(IRP) {} /// Check \p Pred on all returned values. /// @@ -911,7 +1104,7 @@ struct AAReturnedValues struct AANoUnwind : public IRAttribute> { - IRPositionConstructorForward(AANoUnwind, IRAttribute); + AANoUnwind(const IRPosition &IRP) : IRAttribute(IRP) {} /// Returns true if nounwind is assumed. bool isAssumedNoUnwind() const { return getAssumed(); } @@ -926,7 +1119,7 @@ struct AANoUnwind struct AANoSync : public IRAttribute> { - IRPositionConstructorForward(AANoSync, IRAttribute); + AANoSync(const IRPosition &IRP) : IRAttribute(IRP) {} /// Returns true if "nosync" is assumed. bool isAssumedNoSync() const { return getAssumed(); } @@ -942,7 +1135,7 @@ struct AANoSync struct AANonNull : public IRAttribute> { - IRPositionConstructorForward(AANonNull, IRAttribute); + AANonNull(const IRPosition &IRP) : IRAttribute(IRP) {} /// Return true if we assume that the underlying value is nonnull. bool isAssumedNonNull() const { return getAssumed(); } @@ -958,7 +1151,7 @@ struct AANonNull struct AANoRecurse : public IRAttribute> { - IRPositionConstructorForward(AANoRecurse, IRAttribute); + AANoRecurse(const IRPosition &IRP) : IRAttribute(IRP) {} /// Return true if "norecurse" is assumed. bool isAssumedNoRecurse() const { return getAssumed(); } @@ -974,7 +1167,7 @@ struct AANoRecurse struct AAWillReturn : public IRAttribute> { - IRPositionConstructorForward(AAWillReturn, IRAttribute); + AAWillReturn(const IRPosition &IRP) : IRAttribute(IRP) {} /// Return true if "willreturn" is assumed. bool isAssumedWillReturn() const { return getAssumed(); } @@ -990,7 +1183,7 @@ struct AAWillReturn struct AANoAlias : public IRAttribute> { - IRPositionConstructorForward(AANoAlias, IRAttribute); + AANoAlias(const IRPosition &IRP) : IRAttribute(IRP) {} /// Return true if we assume that the underlying value is alias. bool isAssumedNoAlias() const { return getAssumed(); } @@ -1006,7 +1199,7 @@ struct AANoAlias struct AANoFree : public IRAttribute> { - IRPositionConstructorForward(AANoFree, IRAttribute); + AANoFree(const IRPosition &IRP) : IRAttribute(IRP) {} /// Return true if "nofree" is assumed. bool isAssumedNoFree() const { return getAssumed(); } @@ -1022,7 +1215,7 @@ struct AANoFree struct AANoReturn : public IRAttribute> { - IRPositionConstructorForward(AANoReturn, IRAttribute); + AANoReturn(const IRPosition &IRP) : IRAttribute(IRP) {} /// Return true if the underlying object is assumed to never return. bool isAssumedNoReturn() const { return getAssumed(); } @@ -1037,7 +1230,7 @@ struct AANoReturn /// An abstract interface for liveness abstract attribute. struct AAIsDead : public StateWrapper, public IRPosition { - IRPositionConstructorForward(AAIsDead, IRPosition); + AAIsDead(const IRPosition &IRP) : IRPosition(IRP) {} /// Returns true if \p BB is assumed dead. virtual bool isAssumedDead(const BasicBlock *BB) const = 0; @@ -1079,7 +1272,7 @@ struct AAIsDead : public StateWrapper, /// An abstract interface for all dereferenceable attribute. struct AADereferenceable : public IRAttribute { - IRPositionConstructorForward(AADereferenceable, IRAttribute); + AADereferenceable(const IRPosition &IRP) : IRAttribute(IRP) {} /// Return true if we assume that the underlying value is nonnull. virtual bool isAssumedNonNull() const = 0; @@ -1109,7 +1302,7 @@ struct AADereferenceable struct AAAlign : public IRAttribute> { - IRPositionConstructorForward(AAAlign, IRAttribute); + AAAlign(const IRPosition &IRP) : IRAttribute(IRP) {} /// Return assumed alignment. unsigned getAssumedAlign() const { return getAssumed(); } diff --git a/lib/Transforms/IPO/Attributor.cpp b/lib/Transforms/IPO/Attributor.cpp index ba7626276c8..33a49a5f954 100644 --- a/lib/Transforms/IPO/Attributor.cpp +++ b/lib/Transforms/IPO/Attributor.cpp @@ -265,13 +265,9 @@ ChangeStatus AbstractAttribute::update(Attributor &A) { ChangeStatus IRAttributeManifest::manifestAttrs(Attributor &A, IRPosition &IRP, const ArrayRef &DeducedAttrs) { - assert(IRP.getAssociatedValue() && - "Attempted to manifest an attribute without associated value!"); - ChangeStatus HasChanged = ChangeStatus::UNCHANGED; - Function &ScopeFn = IRP.getAnchorScope(); - LLVMContext &Ctx = ScopeFn.getContext(); + Function *ScopeFn = IRP.getAssociatedFunction(); IRPosition::Kind PK = IRP.getPositionKind(); // In the following some generic code that will manifest attributes in @@ -280,16 +276,22 @@ IRAttributeManifest::manifestAttrs(Attributor &A, IRPosition &IRP, AttributeList Attrs; switch (PK) { + case IRPosition::IRP_INVALID: + case IRPosition::IRP_FLOAT: + llvm_unreachable("Cannot manifest at a floating or invalid position!"); case IRPosition::IRP_ARGUMENT: case IRPosition::IRP_FUNCTION: case IRPosition::IRP_RETURNED: - Attrs = ScopeFn.getAttributes(); + Attrs = ScopeFn->getAttributes(); break; + case IRPosition::IRP_CALL_SITE: + case IRPosition::IRP_CALL_SITE_RETURNED: case IRPosition::IRP_CALL_SITE_ARGUMENT: Attrs = ImmutableCallSite(&IRP.getAnchorValue()).getAttributes(); break; } + LLVMContext &Ctx = IRP.getAnchorValue().getContext(); for (const Attribute &Attr : DeducedAttrs) { if (!addIfNotExistent(Ctx, Attr, Attrs, IRP.getAttrIdx())) continue; @@ -304,19 +306,147 @@ IRAttributeManifest::manifestAttrs(Attributor &A, IRPosition &IRP, case IRPosition::IRP_ARGUMENT: case IRPosition::IRP_FUNCTION: case IRPosition::IRP_RETURNED: - ScopeFn.setAttributes(Attrs); + ScopeFn->setAttributes(Attrs); break; + case IRPosition::IRP_CALL_SITE: + case IRPosition::IRP_CALL_SITE_RETURNED: case IRPosition::IRP_CALL_SITE_ARGUMENT: CallSite(&IRP.getAnchorValue()).setAttributes(Attrs); + case IRPosition::IRP_FLOAT: + case IRPosition::IRP_INVALID: + break; } return HasChanged; } +const IRPosition IRPosition::EmptyKey(255); +const IRPosition IRPosition::TombstoneKey(256); + +SubsumingPositionIterator::SubsumingPositionIterator(const IRPosition &IRP) { + IRPositions.emplace_back(IRP); + + ImmutableCallSite ICS(&IRP.getAnchorValue()); + switch (IRP.getPositionKind()) { + case IRPosition::IRP_INVALID: + case IRPosition::IRP_FLOAT: + case IRPosition::IRP_FUNCTION: + return; + case IRPosition::IRP_ARGUMENT: + case IRPosition::IRP_RETURNED: + IRPositions.emplace_back( + IRPosition::function(*IRP.getAssociatedFunction())); + return; + case IRPosition::IRP_CALL_SITE: + assert(ICS && "Expected call site!"); + // TODO: We need to look at the operand bundles similar to the redirection + // in CallBase. + if (!ICS.hasOperandBundles()) + if (const Function *Callee = ICS.getCalledFunction()) + IRPositions.emplace_back(IRPosition::function(*Callee)); + return; + case IRPosition::IRP_CALL_SITE_RETURNED: + assert(ICS && "Expected call site!"); + // TODO: We need to look at the operand bundles similar to the redirection + // in CallBase. + if (!ICS.hasOperandBundles()) { + if (const Function *Callee = ICS.getCalledFunction()) { + IRPositions.emplace_back(IRPosition::returned(*Callee)); + IRPositions.emplace_back(IRPosition::function(*Callee)); + } + } + IRPositions.emplace_back( + IRPosition::callsite_function(cast(*ICS.getInstruction()))); + return; + case IRPosition::IRP_CALL_SITE_ARGUMENT: { + int ArgNo = IRP.getArgNo(); + assert(ICS && ArgNo >= 0 && "Expected call site!"); + // TODO: We need to look at the operand bundles similar to the redirection + // in CallBase. + if (!ICS.hasOperandBundles()) { + const Function *Callee = ICS.getCalledFunction(); + if (Callee && Callee->arg_size() > unsigned(ArgNo)) + IRPositions.emplace_back(IRPosition::argument(*Callee->getArg(ArgNo))); + if (Callee) + IRPositions.emplace_back(IRPosition::function(*Callee)); + } + IRPositions.emplace_back(IRPosition::value(IRP.getAssociatedValue())); + return; + } + } +} + +bool IRPosition::hasAttr(ArrayRef AKs) const { + for (const IRPosition &EquivIRP : SubsumingPositionIterator(*this)) + for (Attribute::AttrKind AK : AKs) + if (EquivIRP.getAttr(AK).getKindAsEnum() == AK) + return true; + return false; +} + +void IRPosition::getAttrs(ArrayRef AKs, + SmallVectorImpl &Attrs) const { + for (const IRPosition &EquivIRP : SubsumingPositionIterator(*this)) + for (Attribute::AttrKind AK : AKs) { + const Attribute &Attr = EquivIRP.getAttr(AK); + if (Attr.getKindAsEnum() == AK) + Attrs.push_back(Attr); + } +} + +void IRPosition::verify() { + switch (KindOrArgNo) { + default: + assert(KindOrArgNo >= 0 && "Expected argument or call site argument!"); + assert((isa(AnchorVal) || isa(AnchorVal)) && + "Expected call base or argument for positive attribute index!"); + if (auto *Arg = dyn_cast(AnchorVal)) { + assert(Arg->getArgNo() == unsigned(getArgNo()) && + "Argument number mismatch!"); + assert(Arg == &getAssociatedValue() && "Associated value mismatch!"); + } else { + auto &CB = cast(*AnchorVal); + assert(CB.arg_size() > unsigned(getArgNo()) && + "Call site argument number mismatch!"); + assert(CB.getArgOperand(getArgNo()) == &getAssociatedValue() && + "Associated value mismatch!"); + } + break; + case IRP_INVALID: + assert(!AnchorVal && "Expected no value for an invalid position!"); + break; + case IRP_FLOAT: + assert((!isa(&getAssociatedValue()) && + !isa(&getAssociatedValue())) && + "Expected specialized kind for call base and argument values!"); + break; + case IRP_RETURNED: + assert(isa(AnchorVal) && + "Expected function for a 'returned' position!"); + assert(AnchorVal == &getAssociatedValue() && "Associated value mismatch!"); + break; + case IRP_CALL_SITE_RETURNED: + assert((isa(AnchorVal)) && + "Expected call base for 'call site returned' position!"); + assert(AnchorVal == &getAssociatedValue() && "Associated value mismatch!"); + break; + case IRP_CALL_SITE: + assert((isa(AnchorVal)) && + "Expected call base for 'call site function' position!"); + assert(AnchorVal == &getAssociatedValue() && "Associated value mismatch!"); + break; + case IRP_FUNCTION: + assert(isa(AnchorVal) && + "Expected function for a 'function' position!"); + assert(AnchorVal == &getAssociatedValue() && "Associated value mismatch!"); + break; + } +} + /// -----------------------NoUnwind Function Attribute-------------------------- struct AANoUnwindImpl : AANoUnwind { - IRPositionConstructorForward(AANoUnwindImpl, AANoUnwind); + AANoUnwindImpl(const IRPosition &IRP) : AANoUnwind(IRP) {} const std::string getAsStr() const override { return getAssumed() ? "nounwind" : "may-unwind"; @@ -327,7 +457,7 @@ struct AANoUnwindImpl : AANoUnwind { }; struct AANoUnwindFunction final : public AANoUnwindImpl { - AANoUnwindFunction(Function &F) : AANoUnwindImpl(F, IRP_FUNCTION) {} + AANoUnwindFunction(const IRPosition &IRP) : AANoUnwindImpl(IRP) {} /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { @@ -336,7 +466,6 @@ struct AANoUnwindFunction final : public AANoUnwindImpl { }; ChangeStatus AANoUnwindImpl::updateImpl(Attributor &A) { - Function &F = getAnchorScope(); // The map from instruction opcodes to those instructions in the function. auto Opcodes = { @@ -348,11 +477,11 @@ ChangeStatus AANoUnwindImpl::updateImpl(Attributor &A) { if (!I.mayThrow()) return true; - auto *NoUnwindAA = A.getAAFor(*this, I); + auto *NoUnwindAA = A.getAAFor(*this, IRPosition::value(I)); return NoUnwindAA && NoUnwindAA->isAssumedNoUnwind(); }; - if (!A.checkForAllInstructions(F, CheckForNoUnwind, *this, Opcodes)) + if (!A.checkForAllInstructions(CheckForNoUnwind, *this, Opcodes)) return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; @@ -405,12 +534,11 @@ class AAReturnedValuesImpl : public AAReturnedValues, public AbstractState { } public: - IRPositionConstructorForward(AAReturnedValuesImpl, AAReturnedValues); + AAReturnedValuesImpl(const IRPosition &IRP) : AAReturnedValues(IRP) {} /// See AbstractAttribute::initialize(...). void initialize(Attributor &A) override { // Reset the state. - setAssociatedValue(nullptr); IsFixed = false; IsValidState = true; HasOverdefinedReturnedCalls = false; @@ -494,8 +622,7 @@ public: }; struct AAReturnedValuesFunction final : public AAReturnedValuesImpl { - AAReturnedValuesFunction(Function &F) - : AAReturnedValuesImpl(F, IRP_FUNCTION) {} + AAReturnedValuesFunction(const IRPosition &IRP) : AAReturnedValuesImpl(IRP) {} /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { @@ -523,8 +650,7 @@ ChangeStatus AAReturnedValuesImpl::manifest(Attributor &A) { // If the assumed unique return value is an argument, annotate it. if (auto *UniqueRVArg = dyn_cast(UniqueRV.getValue())) { - setAssociatedValue(UniqueRVArg); - setAttributeIdx(UniqueRVArg->getArgNo() + AttributeList::FirstArgIndex); + getIRPosition() = IRPosition::argument(*UniqueRVArg); Changed = IRAttribute::manifest(A) | Changed; } @@ -563,7 +689,7 @@ AAReturnedValuesImpl::getAssumedUniqueReturnValue(Attributor &A) const { return true; }; - if (!A.checkForAllReturnedValues(getAnchorScope(), Pred, *this)) + if (!A.checkForAllReturnedValues(Pred, *this)) UniqueRV = nullptr; return UniqueRV; @@ -608,7 +734,7 @@ ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) { // Keep track of any change to trigger updates on dependent attributes. ChangeStatus Changed = ChangeStatus::UNCHANGED; - auto *LivenessAA = A.getAAFor(*this, getAnchorScope()); + auto *LivenessAA = A.getAAFor(*this, getIRPosition()); // Look at all returned call sites. for (auto &It : ReturnedValues) { @@ -637,7 +763,8 @@ ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) { } // Try to find a assumed unique return value for the called function. - auto *RetCSAA = A.getAAFor(*this, *RV); + auto *RetCSAA = A.getAAFor( + *this, IRPosition::callsite_returned(RetCS)); if (!RetCSAA) { if (!HasOverdefinedReturnedCalls) Changed = ChangeStatus::CHANGED; @@ -653,9 +780,9 @@ ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) { // If no assumed unique return value was found due to the lack of // candidates, we may need to resolve more calls (through more update - // iterations) or the called function will not return. Either way, we simply - // stick with the call sites as return values. Because there were not - // multiple possibilities, we do not treat it as overdefined. + // iterations) or the called function will not return. Either way, we + // simply stick with the call sites as return values. Because there were + // not multiple possibilities, we do not treat it as overdefined. if (!AssumedUniqueRV.hasValue()) continue; @@ -682,8 +809,9 @@ ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) { // If the assumed unique return value is an argument, lookup the matching // call site operand and recursively collect new returned values. - // If it is not an argument, it is just put into the set of returned values - // as we would have already looked through casts, phis, and similar values. + // If it is not an argument, it is just put into the set of returned + // values as we would have already looked through casts, phis, and similar + // values. if (Argument *AssumedRetArg = dyn_cast(AssumedRetVal)) collectValuesRecursively(A, RetCS.getArgOperand(AssumedRetArg->getArgNo()), @@ -715,7 +843,7 @@ ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) { /// ------------------------ NoSync Function Attribute ------------------------- struct AANoSyncImpl : AANoSync { - IRPositionConstructorForward(AANoSyncImpl, AANoSync); + AANoSyncImpl(const IRPosition &IRP) : AANoSync(IRP) {} const std::string getAsStr() const override { return getAssumed() ? "nosync" : "may-sync"; @@ -738,7 +866,7 @@ struct AANoSyncImpl : AANoSync { }; struct AANoSyncFunction final : public AANoSyncImpl { - AANoSyncFunction(Function &F) : AANoSyncImpl(F, IRP_FUNCTION) {} + AANoSyncFunction(const IRPosition &IRP) : AANoSyncImpl(IRP) {} /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { STATS_DECL_AND_TRACK_FN_ATTR(nosync) } @@ -834,24 +962,24 @@ bool AANoSyncImpl::isVolatile(Instruction *I) { } ChangeStatus AANoSyncImpl::updateImpl(Attributor &A) { - Function &F = getAnchorScope(); auto CheckRWInstForNoSync = [&](Instruction &I) { /// We are looking for volatile instructions or Non-Relaxed atomics. /// FIXME: We should ipmrove the handling of intrinsics. - ImmutableCallSite ICS(&I); - auto *NoSyncAA = A.getAAFor(*this, I); - if (isa(&I) && isNoSyncIntrinsic(&I)) return true; - if (ICS && (!NoSyncAA || !NoSyncAA->isAssumedNoSync()) && - !ICS.hasFnAttr(Attribute::NoSync)) - return false; + if (ImmutableCallSite ICS = ImmutableCallSite(&I)) { + if (ICS.hasFnAttr(Attribute::NoSync)) + return true; - if (ICS) - return true; + auto *NoSyncAA = + A.getAAFor(*this, IRPosition::callsite_function(ICS)); + if (NoSyncAA && NoSyncAA->isAssumedNoSync()) + return true; + return false; + } if (!isVolatile(&I) && !isNonRelaxedAtomic(&I)) return true; @@ -869,8 +997,8 @@ ChangeStatus AANoSyncImpl::updateImpl(Attributor &A) { return !ImmutableCallSite(&I).isConvergent(); }; - if (!A.checkForAllReadWriteInstructions(F, CheckRWInstForNoSync, *this) || - !A.checkForAllCallLikeInstructions(F, CheckForNoSync, *this)) + if (!A.checkForAllReadWriteInstructions(CheckRWInstForNoSync, *this) || + !A.checkForAllCallLikeInstructions(CheckForNoSync, *this)) return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; @@ -879,7 +1007,7 @@ ChangeStatus AANoSyncImpl::updateImpl(Attributor &A) { /// ------------------------ No-Free Attributes ---------------------------- struct AANoFreeImpl : public AANoFree { - IRPositionConstructorForward(AANoFreeImpl, AANoFree); + AANoFreeImpl(const IRPosition &IRP) : AANoFree(IRP) {} /// See AbstractAttribute::getAsStr(). const std::string getAsStr() const override { @@ -891,37 +1019,44 @@ struct AANoFreeImpl : public AANoFree { }; struct AANoFreeFunction final : public AANoFreeImpl { - AANoFreeFunction(Function &F) : AANoFreeImpl(F, IRP_FUNCTION) {} + AANoFreeFunction(const IRPosition &IRP) : AANoFreeImpl(IRP) {} /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { STATS_DECL_AND_TRACK_FN_ATTR(nofree) } }; ChangeStatus AANoFreeImpl::updateImpl(Attributor &A) { - Function &F = getAnchorScope(); auto CheckForNoFree = [&](Instruction &I) { - if (ImmutableCallSite(&I).hasFnAttr(Attribute::NoFree)) + ImmutableCallSite ICS(&I); + if (ICS.hasFnAttr(Attribute::NoFree)) return true; - auto *NoFreeAA = A.getAAFor(*this, I); + auto *NoFreeAA = + A.getAAFor(*this, IRPosition::callsite_function(ICS)); return NoFreeAA && NoFreeAA->isAssumedNoFree(); }; - if (!A.checkForAllCallLikeInstructions(F, CheckForNoFree, *this)) + if (!A.checkForAllCallLikeInstructions(CheckForNoFree, *this)) return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; } /// ------------------------ NonNull Argument Attribute ------------------------ struct AANonNullImpl : AANonNull { - IRPositionConstructorForward(AANonNullImpl, AANonNull); + AANonNullImpl(const IRPosition &IRP) : AANonNull(IRP) {} /// See AbstractAttribute::getAsStr(). const std::string getAsStr() const override { return getAssumed() ? "nonnull" : "may-null"; } + /// See AbstractAttribute::initialize(...). + void initialize(Attributor &A) override { + if (hasAttr({Attribute::NonNull, Attribute::Dereferenceable})) + indicateOptimisticFixpoint(); + } + /// Generate a predicate that checks if a given value is assumed nonnull. /// The generated function returns true if a value satisfies any of /// following conditions. @@ -945,15 +1080,12 @@ AANonNullImpl::generatePredicate(Attributor &A) { if (isKnownNonZero(&RV, A.getDataLayout())) return true; - auto *NonNullAA = A.getAAFor(*this, RV); + if (ImmutableCallSite ICS = ImmutableCallSite(&RV)) + if (ICS.hasRetAttr(Attribute::NonNull)) + return true; - ImmutableCallSite ICS(&RV); - - if ((!NonNullAA || !NonNullAA->isAssumedNonNull()) && - (!ICS || !ICS.hasRetAttr(Attribute::NonNull))) - return false; - - return true; + auto *NonNullAA = A.getAAFor(*this, IRPosition::value(RV)); + return (NonNullAA && NonNullAA->isAssumedNonNull()); }; return Pred; @@ -961,19 +1093,7 @@ AANonNullImpl::generatePredicate(Attributor &A) { /// NonNull attribute for function return value. struct AANonNullReturned final : AANonNullImpl { - AANonNullReturned(Function &F) : AANonNullImpl(F, IRP_RETURNED) {} - - /// See AbstractAttribute::initialize(...). - void initialize(Attributor &A) override { - Function &F = getAnchorScope(); - - // Already nonnull. - if (F.getAttributes().hasAttribute(AttributeList::ReturnIndex, - Attribute::NonNull) || - F.getAttributes().hasAttribute(AttributeList::ReturnIndex, - Attribute::Dereferenceable)) - indicateOptimisticFixpoint(); - } + AANonNullReturned(const IRPosition &IRP) : AANonNullImpl(IRP) {} /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override; @@ -985,26 +1105,18 @@ struct AANonNullReturned final : AANonNullImpl { }; ChangeStatus AANonNullReturned::updateImpl(Attributor &A) { - Function &F = getAnchorScope(); std::function &)> Pred = this->generatePredicate(A); - if (!A.checkForAllReturnedValuesAndReturnInsts(F, Pred, *this)) + if (!A.checkForAllReturnedValuesAndReturnInsts(Pred, *this)) return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; } /// NonNull attribute for function argument. struct AANonNullArgument final : AANonNullImpl { - AANonNullArgument(Argument &A) : AANonNullImpl(A) {} - - /// See AbstractAttriubute::initialize(...). - void initialize(Attributor &A) override { - Argument *Arg = cast(getAssociatedValue()); - if (Arg->hasNonNullAttr()) - indicateOptimisticFixpoint(); - } + AANonNullArgument(const IRPosition &IRP) : AANonNullImpl(IRP) {} /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override; @@ -1017,15 +1129,13 @@ struct AANonNullArgument final : AANonNullImpl { /// NonNull attribute for a call site argument. struct AANonNullCallSiteArgument final : AANonNullImpl { - AANonNullCallSiteArgument(Instruction &I, unsigned ArgNo) - : AANonNullImpl(CallSite(&I).getArgOperand(ArgNo), I, ArgNo) {} + AANonNullCallSiteArgument(const IRPosition &IRP) : AANonNullImpl(IRP) {} /// See AbstractAttribute::initialize(...). void initialize(Attributor &A) override { - CallSite CS(&getAnchorValue()); - if (CS.paramHasAttr(getArgNo(), getAttrKind()) || - CS.paramHasAttr(getArgNo(), Attribute::Dereferenceable) || - isKnownNonZero(getAssociatedValue(), A.getDataLayout())) + AANonNullImpl::initialize(A); + if (!isKnownNonNull() && + isKnownNonZero(&getAssociatedValue(), A.getDataLayout())) indicateOptimisticFixpoint(); } @@ -1039,34 +1149,31 @@ struct AANonNullCallSiteArgument final : AANonNullImpl { }; ChangeStatus AANonNullArgument::updateImpl(Attributor &A) { - Function &F = getAnchorScope(); unsigned ArgNo = getArgNo(); // Callback function std::function CallSiteCheck = [&](CallSite CS) { assert(CS && "Sanity check: Call site was not initialized properly!"); - auto *NonNullAA = - A.getAAFor(*this, *CS.getInstruction(), ArgNo); + IRPosition CSArgPos = IRPosition::callsite_argument(CS, ArgNo); + if (CSArgPos.hasAttr({Attribute::NonNull, Attribute::Dereferenceable})) + return true; // Check that NonNullAA is AANonNullCallSiteArgument. - if (NonNullAA) { + if (auto *NonNullAA = A.getAAFor(*this, CSArgPos)) { ImmutableCallSite ICS(&NonNullAA->getAnchorValue()); if (ICS && CS.getInstruction() == ICS.getInstruction()) return NonNullAA->isAssumedNonNull(); return false; } - if (CS.paramHasAttr(ArgNo, Attribute::NonNull)) - return true; - Value *V = CS.getArgOperand(ArgNo); if (isKnownNonZero(V, A.getDataLayout())) return true; return false; }; - if (!A.checkForAllCallSites(F, CallSiteCheck, *this, true)) + if (!A.checkForAllCallSites(CallSiteCheck, *this, true)) return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; } @@ -1075,9 +1182,8 @@ ChangeStatus AANonNullCallSiteArgument::updateImpl(Attributor &A) { // NOTE: Never look at the argument of the callee in this method. // If we do this, "nonnull" is always deduced because of the assumption. - Value &V = *getAssociatedValue(); - - auto *NonNullAA = A.getAAFor(*this, V); + Value &V = getAssociatedValue(); + auto *NonNullAA = A.getAAFor(*this, IRPosition::value(V)); if (!NonNullAA || !NonNullAA->isAssumedNonNull()) return indicatePessimisticFixpoint(); @@ -1088,7 +1194,7 @@ ChangeStatus AANonNullCallSiteArgument::updateImpl(Attributor &A) { /// ------------------------ Will-Return Attributes ---------------------------- struct AAWillReturnImpl : public AAWillReturn { - IRPositionConstructorForward(AAWillReturnImpl, AAWillReturn); + AAWillReturnImpl(const IRPosition &IRP) : AAWillReturn(IRP) {} /// See AbstractAttribute::getAsStr() const std::string getAsStr() const override { @@ -1097,7 +1203,7 @@ struct AAWillReturnImpl : public AAWillReturn { }; struct AAWillReturnFunction final : AAWillReturnImpl { - AAWillReturnFunction(Function &F) : AAWillReturnImpl(F, IRP_FUNCTION) {} + AAWillReturnFunction(const IRPosition &IRP) : AAWillReturnImpl(IRP) {} /// See AbstractAttribute::initialize(...). void initialize(Attributor &A) override; @@ -1141,7 +1247,6 @@ void AAWillReturnFunction::initialize(Attributor &A) { } ChangeStatus AAWillReturnFunction::updateImpl(Attributor &A) { - const Function &F = getAnchorScope(); // The map from instruction opcodes to those instructions in the function. auto CheckForWillReturn = [&](Instruction &I) { @@ -1149,7 +1254,8 @@ ChangeStatus AAWillReturnFunction::updateImpl(Attributor &A) { if (ICS.hasFnAttr(Attribute::WillReturn)) return true; - auto *WillReturnAA = A.getAAFor(*this, I); + IRPosition IPos = IRPosition::callsite_function(ICS); + auto *WillReturnAA = A.getAAFor(*this, IPos); if (!WillReturnAA || !WillReturnAA->isAssumedWillReturn()) return false; @@ -1157,11 +1263,11 @@ ChangeStatus AAWillReturnFunction::updateImpl(Attributor &A) { if (ICS.hasFnAttr(Attribute::NoRecurse)) return true; - auto *NoRecurseAA = A.getAAFor(*this, I); + auto *NoRecurseAA = A.getAAFor(*this, IPos); return NoRecurseAA && NoRecurseAA->isAssumedNoRecurse(); }; - if (!A.checkForAllCallLikeInstructions(F, CheckForWillReturn, *this)) + if (!A.checkForAllCallLikeInstructions(CheckForWillReturn, *this)) return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; @@ -1170,7 +1276,7 @@ ChangeStatus AAWillReturnFunction::updateImpl(Attributor &A) { /// ------------------------ NoAlias Argument Attribute ------------------------ struct AANoAliasImpl : AANoAlias { - IRPositionConstructorForward(AANoAliasImpl, AANoAlias); + AANoAliasImpl(const IRPosition &IRP) : AANoAlias(IRP) {} const std::string getAsStr() const override { return getAssumed() ? "noalias" : "may-alias"; @@ -1179,7 +1285,7 @@ struct AANoAliasImpl : AANoAlias { /// NoAlias attribute for function return value. struct AANoAliasReturned final : AANoAliasImpl { - AANoAliasReturned(Function &F) : AANoAliasImpl(F, IRP_RETURNED) {} + AANoAliasReturned(const IRPosition &IRP) : AANoAliasImpl(IRP) {} /// See AbstractAttriubute::initialize(...). void initialize(Attributor &A) override { @@ -1202,7 +1308,6 @@ struct AANoAliasReturned final : AANoAliasImpl { }; ChangeStatus AANoAliasReturned::updateImpl(Attributor &A) { - Function &F = getAnchorScope(); auto CheckReturnValue = [&](Value &RV) -> bool { if (Constant *C = dyn_cast(&RV)) @@ -1216,7 +1321,8 @@ ChangeStatus AANoAliasReturned::updateImpl(Attributor &A) { return false; if (!ICS.returnDoesNotAlias()) { - auto *NoAliasAA = A.getAAFor(*this, RV); + auto *NoAliasAA = + A.getAAFor(*this, IRPosition::callsite_returned(ICS)); if (!NoAliasAA || !NoAliasAA->isAssumedNoAlias()) return false; } @@ -1231,7 +1337,7 @@ ChangeStatus AANoAliasReturned::updateImpl(Attributor &A) { return true; }; - if (!A.checkForAllReturnedValues(F, CheckReturnValue, *this)) + if (!A.checkForAllReturnedValues(CheckReturnValue, *this)) return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; @@ -1240,7 +1346,7 @@ ChangeStatus AANoAliasReturned::updateImpl(Attributor &A) { /// -------------------AAIsDead Function Attribute----------------------- struct AAIsDeadImpl : public AAIsDead { - IRPositionConstructorForward(AAIsDeadImpl, AAIsDead); + AAIsDeadImpl(const IRPosition &IRP) : AAIsDead(IRP) {} void initialize(Attributor &A) override { const Function &F = getAnchorScope(); @@ -1299,7 +1405,8 @@ struct AAIsDeadImpl : public AAIsDead { /// and only place an unreachable in the normal successor. if (Invoke2CallAllowed) { if (Function *Callee = II->getCalledFunction()) { - auto *AANoUnw = A.getAAFor(*this, *Callee); + auto *AANoUnw = + A.getAAFor(*this, IRPosition::function(*Callee)); if (Callee->hasFnAttribute(Attribute::NoUnwind) || (AANoUnw && AANoUnw->isAssumedNoUnwind())) { LLVM_DEBUG(dbgs() @@ -1391,7 +1498,7 @@ struct AAIsDeadImpl : public AAIsDead { }; struct AAIsDeadFunction final : public AAIsDeadImpl { - AAIsDeadFunction(Function &F) : AAIsDeadImpl(F, IRP_FUNCTION) {} + AAIsDeadFunction(const IRPosition &IRP) : AAIsDeadImpl(IRP) {} /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { @@ -1434,12 +1541,13 @@ const Instruction *AAIsDeadImpl::findNextNoReturn(Attributor &A, ImmutableCallSite ICS(I); if (ICS) { + const IRPosition &IPos = IRPosition::callsite_function(ICS); // Regarless of the no-return property of an invoke instruction we only // learn that the regular successor is not reachable through this // instruction but the unwind block might still be. if (auto *Invoke = dyn_cast(I)) { // Use nounwind to justify the unwind block is dead as well. - auto *AANoUnw = A.getAAFor(*this, *Invoke); + auto *AANoUnw = A.getAAFor(*this, IPos); if (!Invoke2CallAllowed || (!AANoUnw || !AANoUnw->isAssumedNoUnwind())) { AssumedLiveBlocks.insert(Invoke->getUnwindDest()); @@ -1447,7 +1555,7 @@ const Instruction *AAIsDeadImpl::findNextNoReturn(Attributor &A, } } - auto *NoReturnAA = A.getAAFor(*this, *I); + auto *NoReturnAA = A.getAAFor(*this, IPos); if (ICS.hasFnAttr(Attribute::NoReturn) || (NoReturnAA && NoReturnAA->isAssumedNoReturn())) return I; @@ -1577,7 +1685,7 @@ struct DerefState : AbstractState { }; struct AADereferenceableImpl : AADereferenceable, DerefState { - IRPositionConstructorForward(AADereferenceableImpl, AADereferenceable); + AADereferenceableImpl(const IRPosition &IRP) : AADereferenceable(IRP) {} using StateType = DerefState; /// See AbstractAttribute::getState() @@ -1644,13 +1752,11 @@ struct AADereferenceableImpl : AADereferenceable, DerefState { bool &IsNonNull, bool &IsGlobal); void initialize(Attributor &A) override { - Function &F = getAnchorScope(); - unsigned AttrIdx = getIRPosition().getAttrIdx(); - - for (Attribute::AttrKind AK : - {Attribute::Dereferenceable, Attribute::DereferenceableOrNull}) - if (F.getAttributes().hasAttribute(AttrIdx, AK)) - takeKnownDerefBytesMaximum(F.getAttribute(AttrIdx, AK).getValueAsInt()); + SmallVector Attrs; + getAttrs({Attribute::Dereferenceable, Attribute::DereferenceableOrNull}, + Attrs); + for (const Attribute &Attr : Attrs) + takeKnownDerefBytesMaximum(Attr.getValueAsInt()); } /// See AbstractAttribute::getAsStr(). @@ -1666,8 +1772,8 @@ struct AADereferenceableImpl : AADereferenceable, DerefState { }; struct AADereferenceableReturned final : AADereferenceableImpl { - AADereferenceableReturned(Function &F) - : AADereferenceableImpl(F, IRP_RETURNED) {} + AADereferenceableReturned(const IRPosition &IRP) + : AADereferenceableImpl(IRP) {} /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override; @@ -1692,7 +1798,8 @@ uint64_t AADereferenceableImpl::computeAssumedDerefenceableBytes( IsGlobal = false; // First, we try to get information about V from Attributor. - if (auto *DerefAA = A.getAAFor(*this, V)) { + if (auto *DerefAA = + A.getAAFor(*this, IRPosition::value(V))) { IsNonNull &= DerefAA->isAssumedNonNull(); return DerefAA->getAssumedDereferenceableBytes(); } @@ -1704,7 +1811,8 @@ uint64_t AADereferenceableImpl::computeAssumedDerefenceableBytes( APInt Offset(IdxWidth, 0); Value *Base = V.stripAndAccumulateInBoundsConstantOffsets(DL, Offset); - if (auto *BaseDerefAA = A.getAAFor(*this, *Base)) { + if (auto *BaseDerefAA = + A.getAAFor(*this, IRPosition::value(*Base))) { IsNonNull &= Offset != 0; return calcDifferenceIfBaseIsNonNull( BaseDerefAA->getAssumedDereferenceableBytes(), Offset.getSExtValue(), @@ -1725,10 +1833,9 @@ uint64_t AADereferenceableImpl::computeAssumedDerefenceableBytes( } ChangeStatus AADereferenceableReturned::updateImpl(Attributor &A) { - Function &F = getAnchorScope(); auto BeforeState = static_cast(*this); - syncNonNull(A.getAAFor(*this, F)); + syncNonNull(A.getAAFor(*this, getIRPosition())); bool IsNonNull = isAssumedNonNull(); bool IsGlobal = isAssumedGlobal(); @@ -1739,7 +1846,7 @@ ChangeStatus AADereferenceableReturned::updateImpl(Attributor &A) { return isValidState(); }; - if (A.checkForAllReturnedValues(F, CheckReturnValue, *this)) { + if (A.checkForAllReturnedValues(CheckReturnValue, *this)) { updateAssumedNonNullGlobalState(IsNonNull, IsGlobal); return BeforeState == static_cast(*this) ? ChangeStatus::UNCHANGED @@ -1749,7 +1856,8 @@ ChangeStatus AADereferenceableReturned::updateImpl(Attributor &A) { } struct AADereferenceableArgument final : AADereferenceableImpl { - AADereferenceableArgument(Argument &A) : AADereferenceableImpl(A) {} + AADereferenceableArgument(const IRPosition &IRP) + : AADereferenceableImpl(IRP) {} /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override; @@ -1761,14 +1869,13 @@ struct AADereferenceableArgument final : AADereferenceableImpl { }; ChangeStatus AADereferenceableArgument::updateImpl(Attributor &A) { - Function &F = getAnchorScope(); Argument &Arg = cast(getAnchorValue()); auto BeforeState = static_cast(*this); unsigned ArgNo = Arg.getArgNo(); - syncNonNull(A.getAAFor(*this, F, ArgNo)); + syncNonNull(A.getAAFor(*this, getIRPosition())); bool IsNonNull = isAssumedNonNull(); bool IsGlobal = isAssumedGlobal(); @@ -1778,8 +1885,8 @@ ChangeStatus AADereferenceableArgument::updateImpl(Attributor &A) { assert(CS && "Sanity check: Call site was not initialized properly!"); // Check that DereferenceableAA is AADereferenceableCallSiteArgument. - if (auto *DereferenceableAA = - A.getAAFor(*this, *CS.getInstruction(), ArgNo)) { + if (auto *DereferenceableAA = A.getAAFor( + *this, IRPosition::callsite_argument(CS, ArgNo))) { ImmutableCallSite ICS( &DereferenceableAA->getIRPosition().getAnchorValue()); if (ICS && CS.getInstruction() == ICS.getInstruction()) { @@ -1797,7 +1904,7 @@ ChangeStatus AADereferenceableArgument::updateImpl(Attributor &A) { return isValidState(); }; - if (!A.checkForAllCallSites(F, CallSiteCheck, *this, true)) + if (!A.checkForAllCallSites(CallSiteCheck, *this, true)) return indicatePessimisticFixpoint(); updateAssumedNonNullGlobalState(IsNonNull, IsGlobal); @@ -1808,18 +1915,8 @@ ChangeStatus AADereferenceableArgument::updateImpl(Attributor &A) { /// Dereferenceable attribute for a call site argument. struct AADereferenceableCallSiteArgument final : AADereferenceableImpl { - AADereferenceableCallSiteArgument(Instruction &I, unsigned ArgNo) - : AADereferenceableImpl(CallSite(&I).getArgOperand(ArgNo), I, ArgNo) {} - - /// See AbstractAttribute::initialize(...). - void initialize(Attributor &A) override { - CallSite CS(&getAnchorValue()); - if (CS.paramHasAttr(getArgNo(), Attribute::Dereferenceable)) - takeKnownDerefBytesMaximum(CS.getDereferenceableBytes(getArgNo())); - - if (CS.paramHasAttr(getArgNo(), Attribute::DereferenceableOrNull)) - takeKnownDerefBytesMaximum(CS.getDereferenceableOrNullBytes(getArgNo())); - } + AADereferenceableCallSiteArgument(const IRPosition &IRP) + : AADereferenceableImpl(IRP) {} /// See AbstractAttribute::updateImpl(Attributor &A). ChangeStatus updateImpl(Attributor &A) override; @@ -1835,11 +1932,11 @@ ChangeStatus AADereferenceableCallSiteArgument::updateImpl(Attributor &A) { // If we do this, "dereferenceable" is always deduced because of the // assumption. - Value &V = *getAssociatedValue(); + Value &V = getAssociatedValue(); auto BeforeState = static_cast(*this); - syncNonNull(A.getAAFor(*this, getAnchorValue(), getArgNo())); + syncNonNull(A.getAAFor(*this, getIRPosition())); bool IsNonNull = isAssumedNonNull(); bool IsGlobal = isKnownGlobal(); @@ -1854,7 +1951,7 @@ ChangeStatus AADereferenceableCallSiteArgument::updateImpl(Attributor &A) { // ------------------------ Align Argument Attribute ------------------------ struct AAAlignImpl : AAAlign { - IRPositionConstructorForward(AAAlignImpl, AAAlign); + AAAlignImpl(const IRPosition &IRP) : AAAlign(IRP) {} // Max alignemnt value allowed in IR static const unsigned MAX_ALIGN = 1U << 29; @@ -1869,13 +1966,10 @@ struct AAAlignImpl : AAAlign { void initialize(Attributor &A) override { takeAssumedMinimum(MAX_ALIGN); - Function &F = getAnchorScope(); - - unsigned AttrIdx = getAttrIdx(); - // Already the function has align attribute on return value or argument. - if (F.getAttributes().hasAttribute(AttrIdx, Attribute::Alignment)) - addKnownBits( - F.getAttribute(AttrIdx, Attribute::Alignment).getAlignment()); + SmallVector Attrs; + getAttrs({Attribute::Alignment}, Attrs); + for (const Attribute &Attr : Attrs) + takeKnownMaximum(Attr.getValueAsInt()); } /// See AbstractAttribute::getDeducedAttributes @@ -1888,7 +1982,7 @@ struct AAAlignImpl : AAAlign { /// Align attribute for function return value. struct AAAlignReturned final : AAAlignImpl { - AAAlignReturned(Function &F) : AAAlignImpl(F, IRP_RETURNED) {} + AAAlignReturned(const IRPosition &IRP) : AAAlignImpl(IRP) {} /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override; @@ -1900,7 +1994,6 @@ struct AAAlignReturned final : AAAlignImpl { }; ChangeStatus AAAlignReturned::updateImpl(Attributor &A) { - Function &F = getAnchorScope(); // Currently, align is deduced if alignments in return values are assumed // as greater than n. We reach pessimistic fixpoint if any of the return value @@ -1910,7 +2003,7 @@ ChangeStatus AAAlignReturned::updateImpl(Attributor &A) { base_t BeforeState = getAssumed(); auto CheckReturnValue = [&](Value &RV, const SmallPtrSetImpl &RetInsts) -> bool { - auto *AlignAA = A.getAAFor(*this, RV); + auto *AlignAA = A.getAAFor(*this, IRPosition::value(RV)); if (AlignAA) takeAssumedMinimum(AlignAA->getAssumedAlign()); @@ -1921,7 +2014,7 @@ ChangeStatus AAAlignReturned::updateImpl(Attributor &A) { return isValidState(); }; - if (!A.checkForAllReturnedValuesAndReturnInsts(F, CheckReturnValue, *this)) + if (!A.checkForAllReturnedValuesAndReturnInsts(CheckReturnValue, *this)) return indicatePessimisticFixpoint(); return (getAssumed() != BeforeState) ? ChangeStatus::CHANGED @@ -1930,7 +2023,7 @@ ChangeStatus AAAlignReturned::updateImpl(Attributor &A) { /// Align attribute for function argument. struct AAAlignArgument final : AAAlignImpl { - AAAlignArgument(Argument &A) : AAAlignImpl(A) {} + AAAlignArgument(const IRPosition &IRP) : AAAlignImpl(IRP) {} /// See AbstractAttribute::updateImpl(...). virtual ChangeStatus updateImpl(Attributor &A) override; @@ -1941,7 +2034,6 @@ struct AAAlignArgument final : AAAlignImpl { ChangeStatus AAAlignArgument::updateImpl(Attributor &A) { - Function &F = getAnchorScope(); Argument &Arg = cast(getAnchorValue()); unsigned ArgNo = Arg.getArgNo(); @@ -1953,7 +2045,8 @@ ChangeStatus AAAlignArgument::updateImpl(Attributor &A) { std::function CallSiteCheck = [&](CallSite CS) { assert(CS && "Sanity check: Call site was not initialized properly!"); - auto *AlignAA = A.getAAFor(*this, *CS.getInstruction(), ArgNo); + auto *AlignAA = + A.getAAFor(*this, IRPosition::callsite_argument(CS, ArgNo)); // Check that AlignAA is AAAlignCallSiteArgument. if (AlignAA) { @@ -1969,7 +2062,7 @@ ChangeStatus AAAlignArgument::updateImpl(Attributor &A) { return isValidState(); }; - if (!A.checkForAllCallSites(F, CallSiteCheck, *this, true)) + if (!A.checkForAllCallSites(CallSiteCheck, *this, true)) indicatePessimisticFixpoint(); return BeforeState == getAssumed() ? ChangeStatus::UNCHANGED @@ -1977,14 +2070,12 @@ ChangeStatus AAAlignArgument::updateImpl(Attributor &A) { } struct AAAlignCallSiteArgument final : AAAlignImpl { - AAAlignCallSiteArgument(Instruction &I, unsigned ArgNo) - : AAAlignImpl(CallSite(&I).getArgOperand(ArgNo), I, ArgNo) {} + AAAlignCallSiteArgument(const IRPosition &IRP) : AAAlignImpl(IRP) {} /// See AbstractAttribute::initialize(...). void initialize(Attributor &A) override { - CallSite CS(&getAnchorValue()); takeKnownMaximum( - getAssociatedValue()->getPointerAlignment(A.getDataLayout())); + getAssociatedValue().getPointerAlignment(A.getDataLayout())); } /// See AbstractAttribute::updateImpl(Attributor &A). @@ -2002,9 +2093,8 @@ ChangeStatus AAAlignCallSiteArgument::updateImpl(Attributor &A) { auto BeforeState = getAssumed(); - Value &V = *getAssociatedValue(); - - auto *AlignAA = A.getAAFor(*this, V); + Value &V = getAssociatedValue(); + auto *AlignAA = A.getAAFor(*this, IRPosition::value(V)); if (AlignAA) takeAssumedMinimum(AlignAA->getAssumedAlign()); @@ -2017,7 +2107,7 @@ ChangeStatus AAAlignCallSiteArgument::updateImpl(Attributor &A) { /// ------------------ Function No-Return Attribute ---------------------------- struct AANoReturnImpl : public AANoReturn { - IRPositionConstructorForward(AANoReturnImpl, AANoReturn); + AANoReturnImpl(const IRPosition &IRP) : AANoReturn(IRP) {} /// See AbstractAttribute::getAsStr(). const std::string getAsStr() const override { @@ -2026,16 +2116,14 @@ struct AANoReturnImpl : public AANoReturn { /// See AbstractAttribute::initialize(...). void initialize(Attributor &A) override { - Function &F = getAnchorScope(); - if (F.hasFnAttribute(getAttrKind())) + if (hasAttr({getAttrKind()})) indicateOptimisticFixpoint(); } /// See AbstractAttribute::updateImpl(Attributor &A). virtual ChangeStatus updateImpl(Attributor &A) override { - const Function &F = getAnchorScope(); auto CheckForNoReturn = [](Instruction &) { return false; }; - if (!A.checkForAllInstructions(F, CheckForNoReturn, *this, + if (!A.checkForAllInstructions(CheckForNoReturn, *this, {(unsigned)Instruction::Ret})) return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; @@ -2043,7 +2131,7 @@ struct AANoReturnImpl : public AANoReturn { }; struct AANoReturnFunction final : AANoReturnImpl { - AANoReturnFunction(Function &F) : AANoReturnImpl(F, IRP_FUNCTION) {} + AANoReturnFunction(const IRPosition &IRP) : AANoReturnImpl(IRP) {} /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { @@ -2055,26 +2143,31 @@ struct AANoReturnFunction final : AANoReturnImpl { /// Attributor /// ---------------------------------------------------------------------------- -bool Attributor::checkForAllCallSites(Function &F, - std::function &Pred, +bool Attributor::checkForAllCallSites(const function_ref &Pred, const AbstractAttribute &QueryingAA, bool RequireAllCallSites) { // We can try to determine information from // the call sites. However, this is only possible all call sites are known, // hence the function has internal linkage. - if (RequireAllCallSites && !F.hasInternalLinkage()) { + const IRPosition &IRP = QueryingAA.getIRPosition(); + const Function *AssociatedFunction = IRP.getAssociatedFunction(); + if (!AssociatedFunction) + return false; + + if (RequireAllCallSites && !AssociatedFunction->hasInternalLinkage()) { LLVM_DEBUG( dbgs() - << "Attributor: Function " << F.getName() + << "Attributor: Function " << AssociatedFunction->getName() << " has no internal linkage, hence not all call sites are known\n"); return false; } - for (const Use &U : F.uses()) { + for (const Use &U : AssociatedFunction->uses()) { Instruction *I = cast(U.getUser()); - Function *AnchorValue = I->getParent()->getParent(); + Function *Caller = I->getFunction(); - auto *LivenessAA = getAAFor(QueryingAA, *AnchorValue); + auto *LivenessAA = + getAAFor(QueryingAA, IRPosition::function(*Caller)); // Skip dead calls. if (LivenessAA && LivenessAA->isAssumedDead(I)) @@ -2086,7 +2179,8 @@ bool Attributor::checkForAllCallSites(Function &F, continue; LLVM_DEBUG(dbgs() << "Attributor: User " << *U.getUser() - << " is an invalid use of " << F.getName() << "\n"); + << " is an invalid use of " + << AssociatedFunction->getName() << "\n"); return false; } @@ -2102,16 +2196,26 @@ bool Attributor::checkForAllCallSites(Function &F, } bool Attributor::checkForAllReturnedValuesAndReturnInsts( - const Function &F, const function_ref &)> &Pred, const AbstractAttribute &QueryingAA) { - auto *AARetVal = getAAFor(QueryingAA, F); - if (!AARetVal) + const IRPosition &IRP = QueryingAA.getIRPosition(); + // Since we need to provide return instructions we have to have an exact + // definition. + const Function *AssociatedFunction = IRP.getAssociatedFunction(); + if (!AssociatedFunction || !AssociatedFunction->hasExactDefinition()) return false; - auto *LivenessAA = getAAFor(QueryingAA, F); + // If this is a call site query we use the call site specific return values + // and liveness information. + const IRPosition &QueryIRP = IRPosition::function_scope(IRP); + const auto &AARetVal = getAAFor(QueryingAA, QueryIRP); + if (!AARetVal || !AARetVal->getState().isValidState()) + return false; + + auto *LivenessAA = + getAAFor(QueryingAA, IRPosition::function(*AssociatedFunction)); if (!LivenessAA) return AARetVal->checkForAllReturnedValuesAndReturnInsts(Pred); @@ -2130,14 +2234,21 @@ bool Attributor::checkForAllReturnedValuesAndReturnInsts( } bool Attributor::checkForAllReturnedValues( - const Function &F, const function_ref &Pred, + const function_ref &Pred, const AbstractAttribute &QueryingAA) { - auto *AARetVal = getAAFor(QueryingAA, F); - if (!AARetVal) + const IRPosition &IRP = QueryingAA.getIRPosition(); + const Function *AssociatedFunction = IRP.getAssociatedFunction(); + if (!AssociatedFunction || !AssociatedFunction->hasExactDefinition()) return false; - auto *LivenessAA = getAAFor(QueryingAA, F); + const IRPosition &QueryIRP = IRPosition::function_scope(IRP); + const auto &AARetVal = getAAFor(QueryingAA, QueryIRP); + if (!AARetVal || !AARetVal->getState().isValidState()) + return false; + + auto *LivenessAA = + getAAFor(QueryingAA, IRPosition::function(*AssociatedFunction)); if (!LivenessAA) return AARetVal->checkForAllReturnedValuesAndReturnInsts( [&](Value &RV, const SmallPtrSetImpl &) { @@ -2155,12 +2266,20 @@ bool Attributor::checkForAllReturnedValues( } bool Attributor::checkForAllInstructions( - const Function &F, const llvm::function_ref &Pred, + const llvm::function_ref &Pred, const AbstractAttribute &QueryingAA, const ArrayRef &Opcodes) { - auto *LivenessAA = getAAFor(QueryingAA, F); + const IRPosition &IRP = QueryingAA.getIRPosition(); + // Since we need to provide instructions we have to have an exact definition. + const Function *AssociatedFunction = IRP.getAssociatedFunction(); + if (!AssociatedFunction || !AssociatedFunction->hasExactDefinition()) + return false; - auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F); + const IRPosition &QueryIRP = IRPosition::function_scope(IRP); + const auto &LivenessAA = getAAFor(QueryingAA, QueryIRP); + + auto &OpcodeInstMap = + InfoCache.getOpcodeInstMapForFunction(*AssociatedFunction); for (unsigned Opcode : Opcodes) { for (Instruction *I : OpcodeInstMap[Opcode]) { // Skip dead instructions. @@ -2176,12 +2295,19 @@ bool Attributor::checkForAllInstructions( } bool Attributor::checkForAllReadWriteInstructions( - const Function &F, const llvm::function_ref &Pred, + const llvm::function_ref &Pred, AbstractAttribute &QueryingAA) { - auto *LivenessAA = getAAFor(QueryingAA, F); + const Function *AssociatedFunction = + QueryingAA.getIRPosition().getAssociatedFunction(); + if (!AssociatedFunction) + return false; - for (Instruction *I : InfoCache.getReadOrWriteInstsForFunction(F)) { + const auto &LivenessAA = + getAAFor(QueryingAA, QueryingAA.getIRPosition()); + + for (Instruction *I : + InfoCache.getReadOrWriteInstsForFunction(*AssociatedFunction)) { // Skip dead instructions. if (LivenessAA && LivenessAA->isAssumedDead(I)) continue; @@ -2331,83 +2457,83 @@ ChangeStatus Attributor::run() { } /// Helper function that checks if an abstract attribute of type \p AAType -/// should be created for \p V (with argument number \p ArgNo) and if so creates -/// and registers it with the Attributor \p A. +/// should be created for IR position \p IRP and if so creates and registers it +/// with the Attributor \p A. /// /// This method will look at the provided whitelist. If one is given and the /// kind \p AAType::ID is not contained, no abstract attribute is created. /// /// \returns The created abstract argument, or nullptr if none was created. -template -static AAType *checkAndRegisterAA(const Function &F, Attributor &A, - DenseSet *Whitelist, - ValueType &V, int ArgNo, ArgsTy... Args) { +template +static AAType *checkAndRegisterAA(IRPosition &IRP, Attributor &A, + DenseSet *Whitelist) { if (Whitelist && !Whitelist->count(&AAType::ID)) return nullptr; - return &A.registerAA(*new AAType(V, Args...), ArgNo); + return &A.registerAA(*new AAType(IRP)); } void Attributor::identifyDefaultAbstractAttributes( Function &F, DenseSet *Whitelist) { + IRPosition FPos = IRPosition::function(F); + // Check for dead BasicBlocks in every function. // We need dead instruction detection because we do not want to deal with // broken IR in which SSA rules do not apply. - checkAndRegisterAA(F, *this, /* Whitelist */ nullptr, F, - -1); + checkAndRegisterAA(FPos, *this, /* Whitelist */ nullptr); // Every function might be "will-return". - checkAndRegisterAA(F, *this, Whitelist, F, -1); + checkAndRegisterAA(FPos, *this, Whitelist); // Every function can be nounwind. - checkAndRegisterAA(F, *this, Whitelist, F, -1); + checkAndRegisterAA(FPos, *this, Whitelist); // Every function might be marked "nosync" - checkAndRegisterAA(F, *this, Whitelist, F, -1); + checkAndRegisterAA(FPos, *this, Whitelist); // Every function might be "no-free". - checkAndRegisterAA(F, *this, Whitelist, F, -1); + checkAndRegisterAA(FPos, *this, Whitelist); // Every function might be "no-return". - checkAndRegisterAA(F, *this, Whitelist, F, -1); + checkAndRegisterAA(FPos, *this, Whitelist); // Return attributes are only appropriate if the return type is non void. Type *ReturnType = F.getReturnType(); if (!ReturnType->isVoidTy()) { // Argument attribute "returned" --- Create only one per function even // though it is an argument attribute. - checkAndRegisterAA(F, *this, Whitelist, F, -1); + checkAndRegisterAA(FPos, *this, Whitelist); if (ReturnType->isPointerTy()) { + IRPosition RetPos = IRPosition::returned(F); + // Every function with pointer return type might be marked align. - checkAndRegisterAA(F, *this, Whitelist, F, -1); + checkAndRegisterAA(RetPos, *this, Whitelist); // Every function with pointer return type might be marked nonnull. - checkAndRegisterAA(F, *this, Whitelist, F, -1); + checkAndRegisterAA(RetPos, *this, Whitelist); // Every function with pointer return type might be marked noalias. - checkAndRegisterAA(F, *this, Whitelist, F, -1); + checkAndRegisterAA(RetPos, *this, Whitelist); // Every function with pointer return type might be marked // dereferenceable. - checkAndRegisterAA(F, *this, Whitelist, F, -1); + checkAndRegisterAA(RetPos, *this, Whitelist); } } for (Argument &Arg : F.args()) { if (Arg.getType()->isPointerTy()) { + IRPosition ArgPos = IRPosition::argument(Arg); // Every argument with pointer type might be marked nonnull. - checkAndRegisterAA(F, *this, Whitelist, Arg, - Arg.getArgNo()); + checkAndRegisterAA(ArgPos, *this, Whitelist); // Every argument with pointer type might be marked dereferenceable. - checkAndRegisterAA(F, *this, Whitelist, Arg, - Arg.getArgNo()); + checkAndRegisterAA(ArgPos, *this, Whitelist); // Every argument with pointer type might be marked align. - checkAndRegisterAA(F, *this, Whitelist, Arg, - Arg.getArgNo()); + checkAndRegisterAA(ArgPos, *this, Whitelist); } } @@ -2450,18 +2576,18 @@ void Attributor::identifyDefaultAbstractAttributes( for (int i = 0, e = CS.getCalledFunction()->arg_size(); i < e; i++) { if (!CS.getArgument(i)->getType()->isPointerTy()) continue; + IRPosition CSArgPos = IRPosition::callsite_argument(CS, i); // Call site argument attribute "non-null". - checkAndRegisterAA(F, *this, Whitelist, I, i, - i); + checkAndRegisterAA(CSArgPos, *this, + Whitelist); // Call site argument attribute "dereferenceable". - checkAndRegisterAA( - F, *this, Whitelist, I, i, i); + checkAndRegisterAA(CSArgPos, *this, + Whitelist); // Call site argument attribute "align". - checkAndRegisterAA(F, *this, Whitelist, I, i, - i); + checkAndRegisterAA(CSArgPos, *this, Whitelist); } } } @@ -2476,22 +2602,29 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, ChangeStatus S) { raw_ostream &llvm::operator<<(raw_ostream &OS, IRPosition::Kind AP) { switch (AP) { + case IRPosition::IRP_INVALID: + return OS << "inv"; + case IRPosition::IRP_FLOAT: + return OS << "flt"; + case IRPosition::IRP_RETURNED: + return OS << "fn_ret"; + case IRPosition::IRP_CALL_SITE_RETURNED: + return OS << "cs_ret"; + case IRPosition::IRP_FUNCTION: + return OS << "fn"; + case IRPosition::IRP_CALL_SITE: + return OS << "cs"; case IRPosition::IRP_ARGUMENT: return OS << "arg"; case IRPosition::IRP_CALL_SITE_ARGUMENT: return OS << "cs_arg"; - case IRPosition::IRP_FUNCTION: - return OS << "fn"; - case IRPosition::IRP_RETURNED: - return OS << "fn_ret"; } llvm_unreachable("Unknown attribute position!"); } raw_ostream &llvm::operator<<(raw_ostream &OS, const IRPosition &Pos) { - const Value *AV = Pos.getAssociatedValue(); - return OS << "{" << Pos.getPositionKind() << ":" - << (AV ? AV->getName() : "n/a") << " [" + const Value &AV = Pos.getAssociatedValue(); + return OS << "{" << Pos.getPositionKind() << ":" << AV.getName() << " [" << Pos.getAnchorValue().getName() << "@" << Pos.getArgNo() << "]}"; }