[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

llvm-svn: 252077
This commit is contained in:
Sanjoy Das 2015-11-04 21:05:24 +00:00
parent d7dcffaea4
commit a4bae3bb21
4 changed files with 129 additions and 8 deletions

View File

@ -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<IterTy> data_ops() const {
return iterator_range<IterTy>(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.

View File

@ -1120,6 +1120,16 @@ struct OperandBundleUse {
OperandBundleUse() {}
explicit OperandBundleUse(StringRef Tag, ArrayRef<Use> 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 {

View File

@ -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);

View File

@ -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);