From a6abdebd6a1ddf26c963c6f19b50cc75e5b83f4a Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Wed, 4 Nov 2015 21:05:24 +0000 Subject: [PATCH] [IR] Add a `data_operand` abstraction Summary: Data operands of a call or invoke consist of the call arguments, and the bundle operands associated with the `call` (or `invoke`) instruction. The motivation for this change is that we'd like to be able to query "argument attributes" like `readonly` and `nocapture` for bundle operands naturally. This change also provides a conservative "implementation" for these attributes for any bundle operand, and an extension point for future work. Reviewers: chandlerc, majnemer, reames Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D14305 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@252077 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/IR/CallSite.h | 55 +++++++++++++++++++++++++++++----- include/llvm/IR/InstrTypes.h | 22 ++++++++++++++ include/llvm/IR/Instructions.h | 31 +++++++++++++++++++ lib/IR/Instructions.cpp | 29 ++++++++++++++++++ 4 files changed, 129 insertions(+), 8 deletions(-) diff --git a/include/llvm/IR/CallSite.h b/include/llvm/IR/CallSite.h index 235d4259a13..dd3ebf228a0 100644 --- a/include/llvm/IR/CallSite.h +++ b/include/llvm/IR/CallSite.h @@ -163,6 +163,34 @@ public: bool arg_empty() const { return arg_end() == arg_begin(); } unsigned arg_size() const { return unsigned(arg_end() - arg_begin()); } + /// Type of iterator to use when looping over data operands at this call site + /// (see below). + typedef IterTy data_operand_iterator; + + /// data_operands_begin/data_operands_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_operands_begin() const { + assert(getInstruction() && "Not a call or invoke instruction!"); + return (*this)->op_begin(); + } + IterTy data_operands_end() const { + assert(getInstruction() && "Not a call or invoke instruction!"); + return (*this)->op_end() - (isCall() ? 1 : 3); + } + iterator_range data_ops() const { + return iterator_range(data_operands_begin(), data_operands_end()); + } + bool data_operands_empty() const { + return data_operands_end() == data_operands_begin(); + } + unsigned data_operands_size() const { + return std::distance(data_operands_begin(), data_operands_end()); + } + /// getType - Return the type of the instruction that generated this call site /// Type *getType() const { return (*this)->getType(); } @@ -245,6 +273,17 @@ public: CALLSITE_DELEGATE_GETTER(paramHasAttr(i, A)); } + /// \brief Return true if the data operand at index \p i directly or + /// indirectly has the attribute \p A. + /// + /// Normal call or invoke arguments have per operand attributes, as specified + /// in the attribute set attached to this instruction, while operand bundle + /// operands may have some attributes implied by the type of its containing + /// operand bundle. + 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,9 +386,9 @@ public: #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. @@ -374,13 +413,13 @@ public: 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. diff --git a/include/llvm/IR/InstrTypes.h b/include/llvm/IR/InstrTypes.h index 412eb83b0f9..3fd6e1b678a 100644 --- a/include/llvm/IR/InstrTypes.h +++ b/include/llvm/IR/InstrTypes.h @@ -1120,6 +1120,16 @@ struct OperandBundleUse { 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 @@ -1254,6 +1264,18 @@ public: 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 { diff --git a/include/llvm/IR/Instructions.h b/include/llvm/IR/Instructions.h index 509753bb96c..c2ade1994f5 100644 --- a/include/llvm/IR/Instructions.h +++ b/include/llvm/IR/Instructions.h @@ -1603,6 +1603,21 @@ public: /// \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. + /// + /// The index \p i is interpreted as + /// + /// \p i == Attribute::ReturnIndex -> the return value + /// \p i in [1, arg_size + 1) -> argument number (\p i - 1) + /// \p i in [arg_size + 1, data_operand_size + 1) -> bundle operand at index + /// (\p i - 1) in the operand list. + 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 +3489,22 @@ public: /// \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. + /// + /// The index \p i is interpreted as + /// + /// \p i == Attribute::ReturnIndex -> the return value + /// \p i in [1, arg_size + 1) -> argument number (\p i - 1) + /// \p i in [arg_size + 1, data_operand_size + 1) -> bundle operand at index + /// (\p i - 1) in the operand list. + 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); diff --git a/lib/IR/Instructions.cpp b/lib/IR/Instructions.cpp index 3394355cfb4..7c3695b1531 100644 --- a/lib/IR/Instructions.cpp +++ b/lib/IR/Instructions.cpp @@ -340,6 +340,21 @@ bool CallInst::paramHasAttr(unsigned i, Attribute::AttrKind A) const { 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 @@ bool InvokeInst::paramHasAttr(unsigned i, Attribute::AttrKind A) const { 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);