Index: include/llvm/IR/CallSite.h =================================================================== --- include/llvm/IR/CallSite.h +++ include/llvm/IR/CallSite.h @@ -163,6 +163,25 @@ bool arg_empty() const { return arg_end() == arg_begin(); } unsigned arg_size() const { return unsigned(arg_end() - arg_begin()); } + /// data_op_begin/data_op_end - Return iterators iterating over the call / + /// invoke argument list and bundle operands. For invokes, this is the set of + /// instruction operands except the invoke target and the two successor + /// blocks; and for calls this is the set of instruction operands except the + /// call target. + + IterTy data_op_begin() const { + assert(getInstruction() && "Not a call or invoke instruction!"); + return (*this)->op_begin(); + } + IterTy data_op_end() const { return (*this)->op_end() - (isCall() ? 1 : 3); } + iterator_range data_ops() const { + return iterator_range(data_op_begin(), data_op_end()); + } + bool data_op_empty() const { return data_op_end() == data_op_begin(); } + unsigned data_op_size() const { + return unsigned(data_op_end() - data_op_begin()); + } + /// getType - Return the type of the instruction that generated this call site /// Type *getType() const { return (*this)->getType(); } @@ -245,6 +264,12 @@ CALLSITE_DELEGATE_GETTER(paramHasAttr(i, A)); } + /// \brief Return true if the data operand (i.e. call / invoke argument or + /// bundle operand) at index \p i has the attribute \p A. + bool dataOperandHasImpliedAttr(unsigned i, Attribute::AttrKind A) const { + CALLSITE_DELEGATE_GETTER(dataOperandHasImpliedAttr(i, A)); + } + /// @brief Extract the alignment for a call or parameter (0=unknown). uint16_t getParamAlignment(uint16_t i) const { CALLSITE_DELEGATE_GETTER(getParamAlignment(i)); @@ -347,40 +372,41 @@ #undef CALLSITE_DELEGATE_GETTER #undef CALLSITE_DELEGATE_SETTER - /// @brief Determine whether this argument is not captured. - bool doesNotCapture(unsigned ArgNo) const { - return paramHasAttr(ArgNo + 1, Attribute::NoCapture); + /// @brief Determine whether this data operand is not captured. + bool doesNotCapture(unsigned OpNo) const { + return dataOperandHasImpliedAttr(OpNo + 1, Attribute::NoCapture); } - /// @brief Determine whether this argument is passed by value. - bool isByValArgument(unsigned ArgNo) const { - return paramHasAttr(ArgNo + 1, Attribute::ByVal); + /// @brief Determine whether this data operand is passed by value. + bool isByValArgument(unsigned OpNo) const { + return dataOperandHasImpliedAttr(OpNo + 1, Attribute::ByVal); } - /// @brief Determine whether this argument is passed in an alloca. - bool isInAllocaArgument(unsigned ArgNo) const { - return paramHasAttr(ArgNo + 1, Attribute::InAlloca); + /// @brief Determine whether this data operand is passed in an alloca. + bool isInAllocaArgument(unsigned OpNo) const { + return dataOperandHasImpliedAttr(OpNo + 1, Attribute::InAlloca); } - /// @brief Determine whether this argument is passed by value or in an alloca. - bool isByValOrInAllocaArgument(unsigned ArgNo) const { - return paramHasAttr(ArgNo + 1, Attribute::ByVal) || - paramHasAttr(ArgNo + 1, Attribute::InAlloca); + /// @brief Determine whether this data operand is passed by value or in an + /// alloca. + bool isByValOrInAllocaArgument(unsigned OpNo) const { + return dataOperandHasImpliedAttr(OpNo + 1, Attribute::ByVal) || + dataOperandHasImpliedAttr(OpNo + 1, Attribute::InAlloca); } - /// @brief Determine if there are is an inalloca argument. Only the last + /// @brief Determine if there are is an inalloca argument. Only the last call /// argument can have the inalloca attribute. bool hasInAllocaArgument() const { return paramHasAttr(arg_size(), Attribute::InAlloca); } - bool doesNotAccessMemory(unsigned ArgNo) const { - return paramHasAttr(ArgNo + 1, Attribute::ReadNone); + bool doesNotAccessMemory(unsigned OpNo) const { + return dataOperandHasImpliedAttr(OpNo + 1, Attribute::ReadNone); } - bool onlyReadsMemory(unsigned ArgNo) const { - return paramHasAttr(ArgNo + 1, Attribute::ReadOnly) || - paramHasAttr(ArgNo + 1, Attribute::ReadNone); + bool onlyReadsMemory(unsigned OpNo) const { + return dataOperandHasImpliedAttr(OpNo + 1, Attribute::ReadOnly) || + dataOperandHasImpliedAttr(OpNo + 1, Attribute::ReadNone); } /// @brief Return true if the return value is known to be not null. Index: include/llvm/IR/InstrTypes.h =================================================================== --- include/llvm/IR/InstrTypes.h +++ include/llvm/IR/InstrTypes.h @@ -1120,6 +1120,16 @@ OperandBundleUse() {} explicit OperandBundleUse(StringRef Tag, ArrayRef Inputs) : Tag(Tag), Inputs(Inputs) {} + + /// \brief Return true if all the operands in this operand bundle have the + /// attribute A. + /// + /// Currently there is no way to have attributes on operand bundles differ on + /// a per operand granularity. + bool operandsHaveAttr(Attribute::AttrKind A) const { + // Conservative answer: no operands have any attributes. + return false; + }; }; /// \brief A container for an operand bundle being viewed as a set of values @@ -1196,27 +1206,35 @@ /// \brief Return true if this User has any operand bundles. bool hasOperandBundles() const { return getNumOperandBundles() != 0; } + /// \brief Return the index of the first bundle operand in the Use array. + unsigned getBundleOperandsStartIndex() const { + assert(hasOperandBundles() && "Don't call otherwise!"); + return bundle_op_info_begin()->Begin; + } + + /// \brief Return the index of the last bundle operand in the Use array. + unsigned getBundleOperandsEndIndex() const { + assert(hasOperandBundles() && "Don't call otherwise!"); + return bundle_op_info_end()[-1].End; + } + /// \brief Return the total number operands (not operand bundles) used by /// every operand bundle in this OperandBundleUser. unsigned getNumTotalBundleOperands() const { if (!hasOperandBundles()) return 0; - auto *Begin = bundle_op_info_begin(); - auto *Back = bundle_op_info_end() - 1; - - assert(Begin <= Back && "hasOperandBundles() returned true!"); + unsigned Begin = getBundleOperandsStartIndex(); + unsigned End = getBundleOperandsEndIndex(); - return Back->End - Begin->Begin; + assert(Begin <= End && "Should be!"); + return End - Begin; } /// \brief Return the operand bundle at a specific index. OperandBundleUse getOperandBundle(unsigned Index) const { assert(Index < getNumOperandBundles() && "Index out of bounds!"); - auto *BOI = bundle_op_info_begin() + Index; - auto op_begin = static_cast(this)->op_begin(); - ArrayRef Inputs(op_begin + BOI->Begin, op_begin + BOI->End); - return OperandBundleUse(BOI->Tag->getKey(), Inputs); + return operandBundleFromBundleOpInfo(bundle_op_info_begin()[Index]); } /// \brief Return the number of operand bundles with the tag Name attached to @@ -1246,6 +1264,18 @@ return None; } + /// \brief Return the operand bundle for the operand at index OpIdx. + /// + /// It is an error to call this with an OpIdx that does not correspond to an + /// bundle operand. + OperandBundleUse getOperandBundleForOperand(unsigned OpIdx) const { + for (auto &BOI : bundle_op_infos()) + if (BOI.Begin <= OpIdx && OpIdx < BOI.End) + return operandBundleFromBundleOpInfo(BOI); + + llvm_unreachable("Did not find operand bundle for operand!"); + } + /// \brief Return true if this operand bundle user has operand bundles that /// may read from the heap. bool hasReadingOperandBundles() const { @@ -1309,6 +1339,15 @@ uint32_t End; }; + /// \brief Simple helper function to map a BundleOpInfo to an + /// OperandBundleUse. + OperandBundleUse + operandBundleFromBundleOpInfo(const BundleOpInfo &BOI) const { + auto op_begin = static_cast(this)->op_begin(); + ArrayRef Inputs(op_begin + BOI.Begin, op_begin + BOI.End); + return OperandBundleUse(BOI.Tag->getKey(), Inputs); + } + typedef BundleOpInfo *bundle_op_iterator; typedef const BundleOpInfo *const_bundle_op_iterator; Index: include/llvm/IR/Instructions.h =================================================================== --- include/llvm/IR/Instructions.h +++ include/llvm/IR/Instructions.h @@ -1603,6 +1603,18 @@ /// \brief Determine whether the call or the callee has the given attributes. bool paramHasAttr(unsigned i, Attribute::AttrKind A) const; + /// \brief Return true if the data operand at index \p i has the attribute \p + /// A. + /// + /// Data operands include call arguments and values used in operand bundles, + /// but does not include the callee operand. This routine dispatches to the + /// underlying AttributeList or the OperandBundleUser as appropriate. + /// + /// To check an attribute on the return value, set \p i to 0. To check an + /// attribute on the operand at index k (call argument, or bundle operand), + /// set \p i to k + 1. + bool dataOperandHasImpliedAttr(unsigned i, Attribute::AttrKind A) const; + /// \brief Extract the alignment for a call or parameter (0=unknown). unsigned getParamAlignment(unsigned i) const { return AttributeList.getParamAlignment(i); @@ -3474,6 +3486,19 @@ /// \brief Determine whether the call or the callee has the given attributes. bool paramHasAttr(unsigned i, Attribute::AttrKind A) const; + /// \brief Return true if the data operand at index \p i has the attribute \p + /// A. + /// + /// Data operands include invoke arguments and values used in operand bundles, + /// but does not include the invokee operand, or the two successor blocks. + /// This routine dispatches to the underlying AttributeList or the + /// OperandBundleUser as appropriate. + /// + /// To check an attribute on the return value, set \p i to 0. To check an + /// attribute on the operand at index k (invoke argument, or bundle operand), + /// set \p i to k + 1. + bool dataOperandHasImpliedAttr(unsigned i, Attribute::AttrKind A) const; + /// \brief Extract the alignment for a call or parameter (0=unknown). unsigned getParamAlignment(unsigned i) const { return AttributeList.getParamAlignment(i); Index: lib/IR/Instructions.cpp =================================================================== --- lib/IR/Instructions.cpp +++ lib/IR/Instructions.cpp @@ -340,6 +340,21 @@ return false; } +bool CallInst::dataOperandHasImpliedAttr(unsigned i, + Attribute::AttrKind A) const { + + // The attribute A can either be directly specified, if the operand in + // question is a call argument; or be indirectly implied by the kind of its + // containing operand bundle, if the operand is a bundle operand. + + if (i < (getNumArgOperands() + 1)) + return paramHasAttr(i, A); + + assert(hasOperandBundles() && i >= (getBundleOperandsStartIndex() + 1) && + "Must be either a call argument or an operand bundle!"); + return getOperandBundleForOperand(i - 1).operandsHaveAttr(A); +} + /// IsConstantOne - Return true only if val is constant int 1 static bool IsConstantOne(Value *val) { assert(val && "IsConstantOne does not work with nullptr val"); @@ -586,6 +601,20 @@ return false; } +bool InvokeInst::dataOperandHasImpliedAttr(unsigned i, + Attribute::AttrKind A) const { + // The attribute A can either be directly specified, if the operand in + // question is an invoke argument; or be indirectly implied by the kind of its + // containing operand bundle, if the operand is a bundle operand. + + if (i < (getNumArgOperands() + 1)) + return paramHasAttr(i, A); + + assert(hasOperandBundles() && i >= (getBundleOperandsStartIndex() + 1) && + "Must be either an invoke argument or an operand bundle!"); + return getOperandBundleForOperand(i - 1).operandsHaveAttr(A); +} + void InvokeInst::addAttribute(unsigned i, Attribute::AttrKind attr) { AttributeSet PAL = getAttributes(); PAL = PAL.addAttribute(getContext(), i, attr);