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<Argument>(&V))
+ return IRPosition::argument(*Arg);
+ if (auto *CB = dyn_cast<CallBase>(&V))
+ return IRPosition::callsite_returned(*CB);
+ return IRPosition(const_cast<Value &>(V), IRP_FLOAT);
+ }
+
+ /// Create a position describing the function scope of \p F.
+ static const IRPosition function(const Function &F) {
+ return IRPosition(const_cast<Function &>(F), IRP_FUNCTION);
+ }
+
+ /// Create a position describing the returned value of \p F.
+ static const IRPosition returned(const Function &F) {
+ return IRPosition(const_cast<Function &>(F), IRP_RETURNED);
+ }
+
+ /// Create a position describing the argument \p Arg.
+ static const IRPosition argument(const Argument &Arg) {
+ return IRPosition(const_cast<Argument &>(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<CallBase &>(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<CallBase &>(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<CallBase &>(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<CallBase>(*ICS.getInstruction()));
+ }
+
+ /// Create a position describing the returned value of \p ICS.
+ static const IRPosition callsite_returned(ImmutableCallSite ICS) {
+ return IRPosition::callsite_returned(cast<CallBase>(*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<CallBase>(*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<CallBase>(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<IRPosition *>(this)->getAnchorValue();
+ }
+ ///}
+
+ /// Return the associated function, if any.
+ ///
+ ///{
+ Function *getAssociatedFunction() {
+ if (auto *CB = dyn_cast<CallBase>(AnchorVal))
+ return CB->getCalledFunction();
+ assert(KindOrArgNo != IRP_INVALID &&
+ "Invalid position does not have an anchor scope!");
+ Value &V = getAnchorValue();
+ if (isa<Function>(V))
+ return &cast<Function>(V);
+ if (isa<Argument>(V))
+ return cast<Argument>(V).getParent();
+ if (isa<Instruction>(V))
+ return cast<Instruction>(V).getFunction();
+ return nullptr;
+ }
+ const Function *getAssociatedFunction() const {
+ return const_cast<IRPosition *>(this)->getAssociatedFunction();
+ }
+ ///}
+
+ /// Return the Function surrounding the anchor value.
+ ///
+ ///{
+ Function &getAnchorScope() {
+ Value &V = getAnchorValue();
+ if (isa<Function>(V))
+ return cast<Function>(V);
+ if (isa<Argument>(V))
+ return *cast<Argument>(V).getParent();
+ if (isa<Instruction>(V))
+ return *cast<Instruction>(V).getFunction();
+ llvm_unreachable("No anchor scope");
+ }
+ const Function &getAnchorScope() const {
+ return const_cast<IRPosition *>(this)->getAnchorScope();
+ }
+ ///}
+
+ /// Return the context instruction, if any.
+ ///
+ ///{
+ Instruction *getCtxI() {
+ Value &V = getAnchorValue();
+ if (auto *I = dyn_cast<Instruction>(&V))
+ return I;
+ if (auto *Arg = dyn_cast<Argument>(&V))
+ if (!Arg->getParent()->isDeclaration())
+ return &Arg->getParent()->getEntryBlock().front();
+ return nullptr;
+ }
+ const Instruction *getCtxI() const {
+ return const_cast<IRPosition *>(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<Argument>(AnchorVal))
+ return *AnchorVal;
+ assert(isa<CallBase>(AnchorVal) && "Expected a call base!");
+ return *cast<CallBase>(AnchorVal)->getArgOperand(getArgNo());
+ }
+ const Value &getAssociatedValue() const {
+ return const_cast<IRPosition *>(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<Argument>(getAnchorValue()) &&
+ isa<Argument>(getAssociatedValue())) ||
+ isa<CallBase>(getAnchorValue())) &&
+ "Expected argument or call base due to argument number!");
+ if (isa<CallBase>(getAnchorValue()))
+ return IRP_CALL_SITE_ARGUMENT;
+ return IRP_ARGUMENT;
+ }
+
+ assert(KindOrArgNo < 0 &&
+ "Expected (call site) arguments to never reach this point!");
+ assert(!isa<Argument>(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<Attribute::AttrKind> 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<Attribute::AttrKind> AKs,
+ SmallVectorImpl<Attribute> &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<IRPosition> {
+ static inline IRPosition getEmptyKey() { return IRPosition::EmptyKey; }
+ static inline IRPosition getTombstoneKey() {
+ return IRPosition::TombstoneKey;
+ }
+ static unsigned getHashValue(const IRPosition &IRP) {
+ return (DenseMapInfo<Value *>::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<IRPosition, 4> 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
/// \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
/// 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 <typename AAType>
- 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<AbstractAttribute, AAType>::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<Argument>(&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<Function>(&V) &&
- cast<Function>(&V)->arg_size() > (size_t)ArgNo)
- return getAAFor<AAType>(
- QueryingAA, *(cast<Function>(&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<AAType *>(
- 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<AbstractAttribute *>(&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<IRPosition &>(EquivIRP));
+ if (AAType *AA = static_cast<AAType *>(
+ 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<AbstractAttribute *>(&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<AAType>(QueryingAA, *ICS.getCalledValue(), ArgNo);
-
// No matching attribute found
return nullptr;
}
/// 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 <typename AAType> 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 <typename AAType> AAType ®isterAA(AAType &AA) {
static_assert(std::is_base_of<AbstractAttribute, AAType>::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<Argument>(&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;
}
/// 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<bool(CallSite)> &Pred,
+ bool checkForAllCallSites(const function_ref<bool(CallSite)> &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<bool(Value &, const SmallPtrSetImpl<ReturnInst *> &)>
&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<bool(Value &)> &Pred,
+ bool checkForAllReturnedValues(const function_ref<bool(Value &)> &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<bool(Instruction &)> &Pred,
+ bool checkForAllInstructions(const function_ref<bool(Instruction &)> &Pred,
const AbstractAttribute &QueryingAA,
const ArrayRef<unsigned> &Opcodes);
///
/// See checkForAllCallLikeInstructions(...) for more information.
bool
- checkForAllCallLikeInstructions(const Function &F,
- const function_ref<bool(Instruction &)> &Pred,
+ checkForAllCallLikeInstructions(const function_ref<bool(Instruction &)> &Pred,
const AbstractAttribute &QueryingAA) {
- return checkForAllInstructions(F, Pred, QueryingAA,
+ return checkForAllInstructions(Pred, QueryingAA,
{(unsigned)Instruction::Invoke,
(unsigned)Instruction::CallBr,
(unsigned)Instruction::Call});
/// 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<bool(Instruction &)> &Pred,
+ const llvm::function_ref<bool(Instruction &)> &Pred,
AbstractAttribute &QueryingAA);
/// Return the data layout associated with the anchor scope.
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<const char *, AbstractAttribute *>;
- DenseMap<std::pair<const Value *, int>, KindToAbstractAttributeMap> AAMap;
+ DenseMap<IRPosition, KindToAbstractAttributeMap> AAMap;
///}
/// A map from abstract attributes to the ones that queried them through calls
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<CallBase>(&AnchorVal) &&
- cast<CallBase>(&AnchorVal)->arg_size() > ArgumentNo) ||
- (isa<Argument>(AnchorVal) &&
- cast<Argument>(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<Function>(V))
- return cast<Function>(V);
- if (isa<Argument>(V))
- return *cast<Argument>(V).getParent();
- if (isa<Instruction>(V))
- return *cast<Instruction>(V).getFunction();
- llvm_unreachable("No scope for anchored value found!");
- }
- const Function &getAnchorScope() const {
- return const_cast<IRPosition *>(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<CallBase>(getAnchorValue()))
- return IRP_CALL_SITE_ARGUMENT;
- assert((isa<Argument>(getAnchorValue()) ||
- isa_and_nonnull<Argument>(getAssociatedValue()) ||
- (!getAssociatedValue() && isa<Function>(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 {
/// Helper class that provides common functionality to manifest IR attributes.
template <Attribute::AttrKind AK, typename Base>
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<Attribute, 4> DeducedAttrs;
- getDeducedAttributes(getAnchorScope().getContext(), DeducedAttrs);
+ getDeducedAttributes(getAnchorValue().getContext(), DeducedAttrs);
return IRAttributeManifest::manifestAttrs(A, getIRPosition(), DeducedAttrs);
}
/// 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; }
///}
};
/// An abstract attribute for the returned values of a function.
struct AAReturnedValues
: public IRAttribute<Attribute::Returned, AbstractAttribute> {
- IRPositionConstructorForward(AAReturnedValues, IRAttribute);
+ AAReturnedValues(const IRPosition &IRP) : IRAttribute(IRP) {}
/// Check \p Pred on all returned values.
///
struct AANoUnwind
: public IRAttribute<Attribute::NoUnwind,
StateWrapper<BooleanState, AbstractAttribute>> {
- IRPositionConstructorForward(AANoUnwind, IRAttribute);
+ AANoUnwind(const IRPosition &IRP) : IRAttribute(IRP) {}
/// Returns true if nounwind is assumed.
bool isAssumedNoUnwind() const { return getAssumed(); }
struct AANoSync
: public IRAttribute<Attribute::NoSync,
StateWrapper<BooleanState, AbstractAttribute>> {
- IRPositionConstructorForward(AANoSync, IRAttribute);
+ AANoSync(const IRPosition &IRP) : IRAttribute(IRP) {}
/// Returns true if "nosync" is assumed.
bool isAssumedNoSync() const { return getAssumed(); }
struct AANonNull
: public IRAttribute<Attribute::NonNull,
StateWrapper<BooleanState, AbstractAttribute>> {
- 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(); }
struct AANoRecurse
: public IRAttribute<Attribute::NoRecurse,
StateWrapper<BooleanState, AbstractAttribute>> {
- IRPositionConstructorForward(AANoRecurse, IRAttribute);
+ AANoRecurse(const IRPosition &IRP) : IRAttribute(IRP) {}
/// Return true if "norecurse" is assumed.
bool isAssumedNoRecurse() const { return getAssumed(); }
struct AAWillReturn
: public IRAttribute<Attribute::WillReturn,
StateWrapper<BooleanState, AbstractAttribute>> {
- IRPositionConstructorForward(AAWillReturn, IRAttribute);
+ AAWillReturn(const IRPosition &IRP) : IRAttribute(IRP) {}
/// Return true if "willreturn" is assumed.
bool isAssumedWillReturn() const { return getAssumed(); }
struct AANoAlias
: public IRAttribute<Attribute::NoAlias,
StateWrapper<BooleanState, AbstractAttribute>> {
- 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(); }
struct AANoFree
: public IRAttribute<Attribute::NoFree,
StateWrapper<BooleanState, AbstractAttribute>> {
- IRPositionConstructorForward(AANoFree, IRAttribute);
+ AANoFree(const IRPosition &IRP) : IRAttribute(IRP) {}
/// Return true if "nofree" is assumed.
bool isAssumedNoFree() const { return getAssumed(); }
struct AANoReturn
: public IRAttribute<Attribute::NoReturn,
StateWrapper<BooleanState, AbstractAttribute>> {
- 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(); }
/// An abstract interface for liveness abstract attribute.
struct AAIsDead : public StateWrapper<BooleanState, AbstractAttribute>,
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;
/// An abstract interface for all dereferenceable attribute.
struct AADereferenceable
: public IRAttribute<Attribute::Dereferenceable, AbstractAttribute> {
- 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;
struct AAAlign
: public IRAttribute<Attribute::Alignment,
StateWrapper<IntegerState, AbstractAttribute>> {
- IRPositionConstructorForward(AAAlign, IRAttribute);
+ AAAlign(const IRPosition &IRP) : IRAttribute(IRP) {}
/// Return assumed alignment.
unsigned getAssumedAlign() const { return getAssumed(); }
ChangeStatus
IRAttributeManifest::manifestAttrs(Attributor &A, IRPosition &IRP,
const ArrayRef<Attribute> &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
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;
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<CallBase>(*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<Attribute::AttrKind> 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<Attribute::AttrKind> AKs,
+ SmallVectorImpl<Attribute> &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<CallBase>(AnchorVal) || isa<Argument>(AnchorVal)) &&
+ "Expected call base or argument for positive attribute index!");
+ if (auto *Arg = dyn_cast<Argument>(AnchorVal)) {
+ assert(Arg->getArgNo() == unsigned(getArgNo()) &&
+ "Argument number mismatch!");
+ assert(Arg == &getAssociatedValue() && "Associated value mismatch!");
+ } else {
+ auto &CB = cast<CallBase>(*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<CallBase>(&getAssociatedValue()) &&
+ !isa<Argument>(&getAssociatedValue())) &&
+ "Expected specialized kind for call base and argument values!");
+ break;
+ case IRP_RETURNED:
+ assert(isa<Function>(AnchorVal) &&
+ "Expected function for a 'returned' position!");
+ assert(AnchorVal == &getAssociatedValue() && "Associated value mismatch!");
+ break;
+ case IRP_CALL_SITE_RETURNED:
+ assert((isa<CallBase>(AnchorVal)) &&
+ "Expected call base for 'call site returned' position!");
+ assert(AnchorVal == &getAssociatedValue() && "Associated value mismatch!");
+ break;
+ case IRP_CALL_SITE:
+ assert((isa<CallBase>(AnchorVal)) &&
+ "Expected call base for 'call site function' position!");
+ assert(AnchorVal == &getAssociatedValue() && "Associated value mismatch!");
+ break;
+ case IRP_FUNCTION:
+ assert(isa<Function>(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";
};
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 {
};
ChangeStatus AANoUnwindImpl::updateImpl(Attributor &A) {
- Function &F = getAnchorScope();
// The map from instruction opcodes to those instructions in the function.
auto Opcodes = {
if (!I.mayThrow())
return true;
- auto *NoUnwindAA = A.getAAFor<AANoUnwind>(*this, I);
+ auto *NoUnwindAA = A.getAAFor<AANoUnwind>(*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;
}
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;
};
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 {
// If the assumed unique return value is an argument, annotate it.
if (auto *UniqueRVArg = dyn_cast<Argument>(UniqueRV.getValue())) {
- setAssociatedValue(UniqueRVArg);
- setAttributeIdx(UniqueRVArg->getArgNo() + AttributeList::FirstArgIndex);
+ getIRPosition() = IRPosition::argument(*UniqueRVArg);
Changed = IRAttribute::manifest(A) | Changed;
}
return true;
};
- if (!A.checkForAllReturnedValues(getAnchorScope(), Pred, *this))
+ if (!A.checkForAllReturnedValues(Pred, *this))
UniqueRV = nullptr;
return UniqueRV;
// Keep track of any change to trigger updates on dependent attributes.
ChangeStatus Changed = ChangeStatus::UNCHANGED;
- auto *LivenessAA = A.getAAFor<AAIsDead>(*this, getAnchorScope());
+ auto *LivenessAA = A.getAAFor<AAIsDead>(*this, getIRPosition());
// Look at all returned call sites.
for (auto &It : ReturnedValues) {
}
// Try to find a assumed unique return value for the called function.
- auto *RetCSAA = A.getAAFor<AAReturnedValuesImpl>(*this, *RV);
+ auto *RetCSAA = A.getAAFor<AAReturnedValuesImpl>(
+ *this, IRPosition::callsite_returned(RetCS));
if (!RetCSAA) {
if (!HasOverdefinedReturnedCalls)
Changed = ChangeStatus::CHANGED;
// 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;
// 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<Argument>(AssumedRetVal))
collectValuesRecursively(A,
RetCS.getArgOperand(AssumedRetArg->getArgNo()),
/// ------------------------ 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";
};
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) }
}
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<AANoSyncImpl>(*this, I);
-
if (isa<IntrinsicInst>(&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<AANoSyncImpl>(*this, IRPosition::callsite_function(ICS));
+ if (NoSyncAA && NoSyncAA->isAssumedNoSync())
+ return true;
+ return false;
+ }
if (!isVolatile(&I) && !isNonRelaxedAtomic(&I))
return true;
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;
/// ------------------------ 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 {
};
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<AANoFreeImpl>(*this, I);
+ auto *NoFreeAA =
+ A.getAAFor<AANoFreeImpl>(*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.
if (isKnownNonZero(&RV, A.getDataLayout()))
return true;
- auto *NonNullAA = A.getAAFor<AANonNull>(*this, RV);
-
- ImmutableCallSite ICS(&RV);
-
- if ((!NonNullAA || !NonNullAA->isAssumedNonNull()) &&
- (!ICS || !ICS.hasRetAttr(Attribute::NonNull)))
- return false;
+ if (ImmutableCallSite ICS = ImmutableCallSite(&RV))
+ if (ICS.hasRetAttr(Attribute::NonNull))
+ return true;
- return true;
+ auto *NonNullAA = A.getAAFor<AANonNull>(*this, IRPosition::value(RV));
+ return (NonNullAA && NonNullAA->isAssumedNonNull());
};
return Pred;
/// 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;
};
ChangeStatus AANonNullReturned::updateImpl(Attributor &A) {
- Function &F = getAnchorScope();
std::function<bool(Value &, const SmallPtrSetImpl<ReturnInst *> &)> 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<Argument>(getAssociatedValue());
- if (Arg->hasNonNullAttr())
- indicateOptimisticFixpoint();
- }
+ AANonNullArgument(const IRPosition &IRP) : AANonNullImpl(IRP) {}
/// See AbstractAttribute::updateImpl(...).
ChangeStatus updateImpl(Attributor &A) override;
/// 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();
}
};
ChangeStatus AANonNullArgument::updateImpl(Attributor &A) {
- Function &F = getAnchorScope();
unsigned ArgNo = getArgNo();
// Callback function
std::function<bool(CallSite)> CallSiteCheck = [&](CallSite CS) {
assert(CS && "Sanity check: Call site was not initialized properly!");
- auto *NonNullAA =
- A.getAAFor<AANonNullImpl>(*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<AANonNullImpl>(*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;
}
// 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<AANonNull>(*this, V);
+ Value &V = getAssociatedValue();
+ auto *NonNullAA = A.getAAFor<AANonNull>(*this, IRPosition::value(V));
if (!NonNullAA || !NonNullAA->isAssumedNonNull())
return indicatePessimisticFixpoint();
/// ------------------------ 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 {
};
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;
}
ChangeStatus AAWillReturnFunction::updateImpl(Attributor &A) {
- const Function &F = getAnchorScope();
// The map from instruction opcodes to those instructions in the function.
auto CheckForWillReturn = [&](Instruction &I) {
if (ICS.hasFnAttr(Attribute::WillReturn))
return true;
- auto *WillReturnAA = A.getAAFor<AAWillReturn>(*this, I);
+ IRPosition IPos = IRPosition::callsite_function(ICS);
+ auto *WillReturnAA = A.getAAFor<AAWillReturn>(*this, IPos);
if (!WillReturnAA || !WillReturnAA->isAssumedWillReturn())
return false;
if (ICS.hasFnAttr(Attribute::NoRecurse))
return true;
- auto *NoRecurseAA = A.getAAFor<AANoRecurse>(*this, I);
+ auto *NoRecurseAA = A.getAAFor<AANoRecurse>(*this, IPos);
return NoRecurseAA && NoRecurseAA->isAssumedNoRecurse();
};
- if (!A.checkForAllCallLikeInstructions(F, CheckForWillReturn, *this))
+ if (!A.checkForAllCallLikeInstructions(CheckForWillReturn, *this))
return indicatePessimisticFixpoint();
return ChangeStatus::UNCHANGED;
/// ------------------------ 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";
/// 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 {
};
ChangeStatus AANoAliasReturned::updateImpl(Attributor &A) {
- Function &F = getAnchorScope();
auto CheckReturnValue = [&](Value &RV) -> bool {
if (Constant *C = dyn_cast<Constant>(&RV))
return false;
if (!ICS.returnDoesNotAlias()) {
- auto *NoAliasAA = A.getAAFor<AANoAlias>(*this, RV);
+ auto *NoAliasAA =
+ A.getAAFor<AANoAlias>(*this, IRPosition::callsite_returned(ICS));
if (!NoAliasAA || !NoAliasAA->isAssumedNoAlias())
return false;
}
return true;
};
- if (!A.checkForAllReturnedValues(F, CheckReturnValue, *this))
+ if (!A.checkForAllReturnedValues(CheckReturnValue, *this))
return indicatePessimisticFixpoint();
return ChangeStatus::UNCHANGED;
/// -------------------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();
/// and only place an unreachable in the normal successor.
if (Invoke2CallAllowed) {
if (Function *Callee = II->getCalledFunction()) {
- auto *AANoUnw = A.getAAFor<AANoUnwind>(*this, *Callee);
+ auto *AANoUnw =
+ A.getAAFor<AANoUnwind>(*this, IRPosition::function(*Callee));
if (Callee->hasFnAttribute(Attribute::NoUnwind) ||
(AANoUnw && AANoUnw->isAssumedNoUnwind())) {
LLVM_DEBUG(dbgs()
};
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 {
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<InvokeInst>(I)) {
// Use nounwind to justify the unwind block is dead as well.
- auto *AANoUnw = A.getAAFor<AANoUnwind>(*this, *Invoke);
+ auto *AANoUnw = A.getAAFor<AANoUnwind>(*this, IPos);
if (!Invoke2CallAllowed ||
(!AANoUnw || !AANoUnw->isAssumedNoUnwind())) {
AssumedLiveBlocks.insert(Invoke->getUnwindDest());
}
}
- auto *NoReturnAA = A.getAAFor<AANoReturn>(*this, *I);
+ auto *NoReturnAA = A.getAAFor<AANoReturn>(*this, IPos);
if (ICS.hasFnAttr(Attribute::NoReturn) ||
(NoReturnAA && NoReturnAA->isAssumedNoReturn()))
return I;
};
struct AADereferenceableImpl : AADereferenceable, DerefState {
- IRPositionConstructorForward(AADereferenceableImpl, AADereferenceable);
+ AADereferenceableImpl(const IRPosition &IRP) : AADereferenceable(IRP) {}
using StateType = DerefState;
/// See AbstractAttribute::getState()
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<Attribute, 4> Attrs;
+ getAttrs({Attribute::Dereferenceable, Attribute::DereferenceableOrNull},
+ Attrs);
+ for (const Attribute &Attr : Attrs)
+ takeKnownDerefBytesMaximum(Attr.getValueAsInt());
}
/// See AbstractAttribute::getAsStr().
};
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;
IsGlobal = false;
// First, we try to get information about V from Attributor.
- if (auto *DerefAA = A.getAAFor<AADereferenceable>(*this, V)) {
+ if (auto *DerefAA =
+ A.getAAFor<AADereferenceable>(*this, IRPosition::value(V))) {
IsNonNull &= DerefAA->isAssumedNonNull();
return DerefAA->getAssumedDereferenceableBytes();
}
APInt Offset(IdxWidth, 0);
Value *Base = V.stripAndAccumulateInBoundsConstantOffsets(DL, Offset);
- if (auto *BaseDerefAA = A.getAAFor<AADereferenceable>(*this, *Base)) {
+ if (auto *BaseDerefAA =
+ A.getAAFor<AADereferenceable>(*this, IRPosition::value(*Base))) {
IsNonNull &= Offset != 0;
return calcDifferenceIfBaseIsNonNull(
BaseDerefAA->getAssumedDereferenceableBytes(), Offset.getSExtValue(),
}
ChangeStatus AADereferenceableReturned::updateImpl(Attributor &A) {
- Function &F = getAnchorScope();
auto BeforeState = static_cast<DerefState>(*this);
- syncNonNull(A.getAAFor<AANonNull>(*this, F));
+ syncNonNull(A.getAAFor<AANonNull>(*this, getIRPosition()));
bool IsNonNull = isAssumedNonNull();
bool IsGlobal = isAssumedGlobal();
return isValidState();
};
- if (A.checkForAllReturnedValues(F, CheckReturnValue, *this)) {
+ if (A.checkForAllReturnedValues(CheckReturnValue, *this)) {
updateAssumedNonNullGlobalState(IsNonNull, IsGlobal);
return BeforeState == static_cast<DerefState>(*this)
? ChangeStatus::UNCHANGED
}
struct AADereferenceableArgument final : AADereferenceableImpl {
- AADereferenceableArgument(Argument &A) : AADereferenceableImpl(A) {}
+ AADereferenceableArgument(const IRPosition &IRP)
+ : AADereferenceableImpl(IRP) {}
/// See AbstractAttribute::updateImpl(...).
ChangeStatus updateImpl(Attributor &A) override;
};
ChangeStatus AADereferenceableArgument::updateImpl(Attributor &A) {
- Function &F = getAnchorScope();
Argument &Arg = cast<Argument>(getAnchorValue());
auto BeforeState = static_cast<DerefState>(*this);
unsigned ArgNo = Arg.getArgNo();
- syncNonNull(A.getAAFor<AANonNull>(*this, F, ArgNo));
+ syncNonNull(A.getAAFor<AANonNull>(*this, getIRPosition()));
bool IsNonNull = isAssumedNonNull();
bool IsGlobal = isAssumedGlobal();
assert(CS && "Sanity check: Call site was not initialized properly!");
// Check that DereferenceableAA is AADereferenceableCallSiteArgument.
- if (auto *DereferenceableAA =
- A.getAAFor<AADereferenceable>(*this, *CS.getInstruction(), ArgNo)) {
+ if (auto *DereferenceableAA = A.getAAFor<AADereferenceable>(
+ *this, IRPosition::callsite_argument(CS, ArgNo))) {
ImmutableCallSite ICS(
&DereferenceableAA->getIRPosition().getAnchorValue());
if (ICS && CS.getInstruction() == ICS.getInstruction()) {
return isValidState();
};
- if (!A.checkForAllCallSites(F, CallSiteCheck, *this, true))
+ if (!A.checkForAllCallSites(CallSiteCheck, *this, true))
return indicatePessimisticFixpoint();
updateAssumedNonNullGlobalState(IsNonNull, IsGlobal);
/// 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;
// If we do this, "dereferenceable" is always deduced because of the
// assumption.
- Value &V = *getAssociatedValue();
+ Value &V = getAssociatedValue();
auto BeforeState = static_cast<DerefState>(*this);
- syncNonNull(A.getAAFor<AANonNull>(*this, getAnchorValue(), getArgNo()));
+ syncNonNull(A.getAAFor<AANonNull>(*this, getIRPosition()));
bool IsNonNull = isAssumedNonNull();
bool IsGlobal = isKnownGlobal();
// ------------------------ 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;
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<Attribute, 4> Attrs;
+ getAttrs({Attribute::Alignment}, Attrs);
+ for (const Attribute &Attr : Attrs)
+ takeKnownMaximum(Attr.getValueAsInt());
}
/// See AbstractAttribute::getDeducedAttributes
/// 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;
};
ChangeStatus AAAlignReturned::updateImpl(Attributor &A) {
- Function &F = getAnchorScope();
// Currently, align<n> is deduced if alignments in return values are assumed
// as greater than n. We reach pessimistic fixpoint if any of the return value
base_t BeforeState = getAssumed();
auto CheckReturnValue =
[&](Value &RV, const SmallPtrSetImpl<ReturnInst *> &RetInsts) -> bool {
- auto *AlignAA = A.getAAFor<AAAlign>(*this, RV);
+ auto *AlignAA = A.getAAFor<AAAlign>(*this, IRPosition::value(RV));
if (AlignAA)
takeAssumedMinimum(AlignAA->getAssumedAlign());
return isValidState();
};
- if (!A.checkForAllReturnedValuesAndReturnInsts(F, CheckReturnValue, *this))
+ if (!A.checkForAllReturnedValuesAndReturnInsts(CheckReturnValue, *this))
return indicatePessimisticFixpoint();
return (getAssumed() != BeforeState) ? ChangeStatus::CHANGED
/// 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;
ChangeStatus AAAlignArgument::updateImpl(Attributor &A) {
- Function &F = getAnchorScope();
Argument &Arg = cast<Argument>(getAnchorValue());
unsigned ArgNo = Arg.getArgNo();
std::function<bool(CallSite)> CallSiteCheck = [&](CallSite CS) {
assert(CS && "Sanity check: Call site was not initialized properly!");
- auto *AlignAA = A.getAAFor<AAAlign>(*this, *CS.getInstruction(), ArgNo);
+ auto *AlignAA =
+ A.getAAFor<AAAlign>(*this, IRPosition::callsite_argument(CS, ArgNo));
// Check that AlignAA is AAAlignCallSiteArgument.
if (AlignAA) {
return isValidState();
};
- if (!A.checkForAllCallSites(F, CallSiteCheck, *this, true))
+ if (!A.checkForAllCallSites(CallSiteCheck, *this, true))
indicatePessimisticFixpoint();
return BeforeState == getAssumed() ? ChangeStatus::UNCHANGED
}
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).
auto BeforeState = getAssumed();
- Value &V = *getAssociatedValue();
-
- auto *AlignAA = A.getAAFor<AAAlign>(*this, V);
+ Value &V = getAssociatedValue();
+ auto *AlignAA = A.getAAFor<AAAlign>(*this, IRPosition::value(V));
if (AlignAA)
takeAssumedMinimum(AlignAA->getAssumedAlign());
/// ------------------ 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 {
/// 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;
};
struct AANoReturnFunction final : AANoReturnImpl {
- AANoReturnFunction(Function &F) : AANoReturnImpl(F, IRP_FUNCTION) {}
+ AANoReturnFunction(const IRPosition &IRP) : AANoReturnImpl(IRP) {}
/// See AbstractAttribute::trackStatistics()
void trackStatistics() const override {
/// Attributor
/// ----------------------------------------------------------------------------
-bool Attributor::checkForAllCallSites(Function &F,
- std::function<bool(CallSite)> &Pred,
+bool Attributor::checkForAllCallSites(const function_ref<bool(CallSite)> &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<Instruction>(U.getUser());
- Function *AnchorValue = I->getParent()->getParent();
+ Function *Caller = I->getFunction();
- auto *LivenessAA = getAAFor<AAIsDead>(QueryingAA, *AnchorValue);
+ auto *LivenessAA =
+ getAAFor<AAIsDead>(QueryingAA, IRPosition::function(*Caller));
// Skip dead calls.
if (LivenessAA && LivenessAA->isAssumedDead(I))
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;
}
}
bool Attributor::checkForAllReturnedValuesAndReturnInsts(
- const Function &F,
const function_ref<bool(Value &, const SmallPtrSetImpl<ReturnInst *> &)>
&Pred,
const AbstractAttribute &QueryingAA) {
- auto *AARetVal = getAAFor<AAReturnedValues>(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<AAIsDead>(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<AAReturnedValues>(QueryingAA, QueryIRP);
+ if (!AARetVal || !AARetVal->getState().isValidState())
+ return false;
+
+ auto *LivenessAA =
+ getAAFor<AAIsDead>(QueryingAA, IRPosition::function(*AssociatedFunction));
if (!LivenessAA)
return AARetVal->checkForAllReturnedValuesAndReturnInsts(Pred);
}
bool Attributor::checkForAllReturnedValues(
- const Function &F, const function_ref<bool(Value &)> &Pred,
+ const function_ref<bool(Value &)> &Pred,
const AbstractAttribute &QueryingAA) {
- auto *AARetVal = getAAFor<AAReturnedValues>(QueryingAA, F);
- if (!AARetVal)
+ const IRPosition &IRP = QueryingAA.getIRPosition();
+ const Function *AssociatedFunction = IRP.getAssociatedFunction();
+ if (!AssociatedFunction || !AssociatedFunction->hasExactDefinition())
+ return false;
+
+ const IRPosition &QueryIRP = IRPosition::function_scope(IRP);
+ const auto &AARetVal = getAAFor<AAReturnedValues>(QueryingAA, QueryIRP);
+ if (!AARetVal || !AARetVal->getState().isValidState())
return false;
- auto *LivenessAA = getAAFor<AAIsDead>(QueryingAA, F);
+ auto *LivenessAA =
+ getAAFor<AAIsDead>(QueryingAA, IRPosition::function(*AssociatedFunction));
if (!LivenessAA)
return AARetVal->checkForAllReturnedValuesAndReturnInsts(
[&](Value &RV, const SmallPtrSetImpl<ReturnInst *> &) {
}
bool Attributor::checkForAllInstructions(
- const Function &F, const llvm::function_ref<bool(Instruction &)> &Pred,
+ const llvm::function_ref<bool(Instruction &)> &Pred,
const AbstractAttribute &QueryingAA, const ArrayRef<unsigned> &Opcodes) {
- auto *LivenessAA = getAAFor<AAIsDead>(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;
+
+ const IRPosition &QueryIRP = IRPosition::function_scope(IRP);
+ const auto &LivenessAA = getAAFor<AAIsDead>(QueryingAA, QueryIRP);
- auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F);
+ auto &OpcodeInstMap =
+ InfoCache.getOpcodeInstMapForFunction(*AssociatedFunction);
for (unsigned Opcode : Opcodes) {
for (Instruction *I : OpcodeInstMap[Opcode]) {
// Skip dead instructions.
}
bool Attributor::checkForAllReadWriteInstructions(
- const Function &F, const llvm::function_ref<bool(Instruction &)> &Pred,
+ const llvm::function_ref<bool(Instruction &)> &Pred,
AbstractAttribute &QueryingAA) {
- auto *LivenessAA = getAAFor<AAIsDead>(QueryingAA, F);
+ const Function *AssociatedFunction =
+ QueryingAA.getIRPosition().getAssociatedFunction();
+ if (!AssociatedFunction)
+ return false;
+
+ const auto &LivenessAA =
+ getAAFor<AAIsDead>(QueryingAA, QueryingAA.getIRPosition());
- for (Instruction *I : InfoCache.getReadOrWriteInstsForFunction(F)) {
+ for (Instruction *I :
+ InfoCache.getReadOrWriteInstsForFunction(*AssociatedFunction)) {
// Skip dead instructions.
if (LivenessAA && LivenessAA->isAssumedDead(I))
continue;
}
/// 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 <typename AAType, typename ValueType, typename... ArgsTy>
-static AAType *checkAndRegisterAA(const Function &F, Attributor &A,
- DenseSet<const char *> *Whitelist,
- ValueType &V, int ArgNo, ArgsTy... Args) {
+template <typename AAType>
+static AAType *checkAndRegisterAA(IRPosition &IRP, Attributor &A,
+ DenseSet<const char *> *Whitelist) {
if (Whitelist && !Whitelist->count(&AAType::ID))
return nullptr;
- return &A.registerAA<AAType>(*new AAType(V, Args...), ArgNo);
+ return &A.registerAA<AAType>(*new AAType(IRP));
}
void Attributor::identifyDefaultAbstractAttributes(
Function &F, DenseSet<const char *> *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<AAIsDeadFunction>(F, *this, /* Whitelist */ nullptr, F,
- -1);
+ checkAndRegisterAA<AAIsDeadFunction>(FPos, *this, /* Whitelist */ nullptr);
// Every function might be "will-return".
- checkAndRegisterAA<AAWillReturnFunction>(F, *this, Whitelist, F, -1);
+ checkAndRegisterAA<AAWillReturnFunction>(FPos, *this, Whitelist);
// Every function can be nounwind.
- checkAndRegisterAA<AANoUnwindFunction>(F, *this, Whitelist, F, -1);
+ checkAndRegisterAA<AANoUnwindFunction>(FPos, *this, Whitelist);
// Every function might be marked "nosync"
- checkAndRegisterAA<AANoSyncFunction>(F, *this, Whitelist, F, -1);
+ checkAndRegisterAA<AANoSyncFunction>(FPos, *this, Whitelist);
// Every function might be "no-free".
- checkAndRegisterAA<AANoFreeFunction>(F, *this, Whitelist, F, -1);
+ checkAndRegisterAA<AANoFreeFunction>(FPos, *this, Whitelist);
// Every function might be "no-return".
- checkAndRegisterAA<AANoReturnFunction>(F, *this, Whitelist, F, -1);
+ checkAndRegisterAA<AANoReturnFunction>(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<AAReturnedValuesFunction>(F, *this, Whitelist, F, -1);
+ checkAndRegisterAA<AAReturnedValuesFunction>(FPos, *this, Whitelist);
if (ReturnType->isPointerTy()) {
+ IRPosition RetPos = IRPosition::returned(F);
+
// Every function with pointer return type might be marked align.
- checkAndRegisterAA<AAAlignReturned>(F, *this, Whitelist, F, -1);
+ checkAndRegisterAA<AAAlignReturned>(RetPos, *this, Whitelist);
// Every function with pointer return type might be marked nonnull.
- checkAndRegisterAA<AANonNullReturned>(F, *this, Whitelist, F, -1);
+ checkAndRegisterAA<AANonNullReturned>(RetPos, *this, Whitelist);
// Every function with pointer return type might be marked noalias.
- checkAndRegisterAA<AANoAliasReturned>(F, *this, Whitelist, F, -1);
+ checkAndRegisterAA<AANoAliasReturned>(RetPos, *this, Whitelist);
// Every function with pointer return type might be marked
// dereferenceable.
- checkAndRegisterAA<AADereferenceableReturned>(F, *this, Whitelist, F, -1);
+ checkAndRegisterAA<AADereferenceableReturned>(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<AANonNullArgument>(F, *this, Whitelist, Arg,
- Arg.getArgNo());
+ checkAndRegisterAA<AANonNullArgument>(ArgPos, *this, Whitelist);
// Every argument with pointer type might be marked dereferenceable.
- checkAndRegisterAA<AADereferenceableArgument>(F, *this, Whitelist, Arg,
- Arg.getArgNo());
+ checkAndRegisterAA<AADereferenceableArgument>(ArgPos, *this, Whitelist);
// Every argument with pointer type might be marked align.
- checkAndRegisterAA<AAAlignArgument>(F, *this, Whitelist, Arg,
- Arg.getArgNo());
+ checkAndRegisterAA<AAAlignArgument>(ArgPos, *this, Whitelist);
}
}
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<AANonNullCallSiteArgument>(F, *this, Whitelist, I, i,
- i);
+ checkAndRegisterAA<AANonNullCallSiteArgument>(CSArgPos, *this,
+ Whitelist);
// Call site argument attribute "dereferenceable".
- checkAndRegisterAA<AADereferenceableCallSiteArgument>(
- F, *this, Whitelist, I, i, i);
+ checkAndRegisterAA<AADereferenceableCallSiteArgument>(CSArgPos, *this,
+ Whitelist);
// Call site argument attribute "align".
- checkAndRegisterAA<AAAlignCallSiteArgument>(F, *this, Whitelist, I, i,
- i);
+ checkAndRegisterAA<AAAlignCallSiteArgument>(CSArgPos, *this, Whitelist);
}
}
}
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() << "]}";
}