Add a dereferenceable attribute

This attribute indicates that the parameter or return pointer is
dereferenceable. Practically speaking, loads from such a pointer within the
associated byte range are safe to speculatively execute. Such pointer
parameters are common in source languages (C++ references, for example).

llvm-svn: 213385
This commit is contained in:
Hal Finkel 2014-07-18 15:51:28 +00:00
parent 9cf7ac7589
commit b0407ba071
22 changed files with 388 additions and 18 deletions

View File

@ -969,6 +969,17 @@ Currently, only the following parameter attributes are defined:
passed in is non-null, or the callee must ensure that the returned pointer passed in is non-null, or the callee must ensure that the returned pointer
is non-null. is non-null.
``dereferenceable(<n>)``
This indicates that the parameter or return pointer is dereferenceable. This
attribute may only be applied to pointer typed parameters. A pointer that
is dereferenceable can be loaded from speculatively without a risk of
trapping. The number of bytes known to be dereferenceable must be provided
in parentheses. It is legal for the number of bytes to be less than the
size of the pointee type. The ``nonnull`` attribute does not imply
dereferenceability (consider a pointer to one element past the end of an
array), however ``dereferenceable(<n>)`` does imply ``nonnull`` in
``addrspace(0)`` (which is the default address space).
.. _gc: .. _gc:
Garbage Collector Names Garbage Collector Names

View File

@ -168,6 +168,7 @@ typedef enum {
LLVMInAllocaAttribute = 1ULL << 36, LLVMInAllocaAttribute = 1ULL << 36,
LLVMNonNullAttribute = 1ULL << 37, LLVMNonNullAttribute = 1ULL << 37,
LLVMJumpTableAttribute = 1ULL << 38, LLVMJumpTableAttribute = 1ULL << 38,
LLVMDereferenceableAttribute = 1ULL << 39,
*/ */
} LLVMAttribute; } LLVMAttribute;

View File

@ -374,7 +374,8 @@ namespace bitc {
ATTR_KIND_OPTIMIZE_NONE = 37, ATTR_KIND_OPTIMIZE_NONE = 37,
ATTR_KIND_IN_ALLOCA = 38, ATTR_KIND_IN_ALLOCA = 38,
ATTR_KIND_NON_NULL = 39, ATTR_KIND_NON_NULL = 39,
ATTR_KIND_JUMP_TABLE = 40 ATTR_KIND_JUMP_TABLE = 40,
ATTR_KIND_DEREFERENCEABLE = 41
}; };
enum ComdatSelectionKindCodes { enum ComdatSelectionKindCodes {

View File

@ -56,9 +56,15 @@ public:
unsigned getArgNo() const; unsigned getArgNo() const;
/// \brief Return true if this argument has the nonnull attribute on it in /// \brief Return true if this argument has the nonnull attribute on it in
/// its containing function. /// its containing function. Also returns true if at least one byte is known
/// to be dereferenceable and the pointer is in addrspace(0).
bool hasNonNullAttr() const; bool hasNonNullAttr() const;
/// \brief If this argument has the dereferenceable attribute on it in its
/// containing function, return the number of bytes known to be
/// dereferenceable. Otherwise, zero is returned.
uint64_t getDereferenceableBytes() const;
/// \brief Return true if this argument has the byval attribute on it in its /// \brief Return true if this argument has the byval attribute on it in its
/// containing function. /// containing function.
bool hasByValAttr() const; bool hasByValAttr() const;

View File

@ -88,6 +88,7 @@ public:
NonLazyBind, ///< Function is called early and/or NonLazyBind, ///< Function is called early and/or
///< often, so lazy binding isn't worthwhile ///< often, so lazy binding isn't worthwhile
NonNull, ///< Pointer is known to be not null NonNull, ///< Pointer is known to be not null
Dereferenceable, ///< Pointer is known to be dereferenceable
NoRedZone, ///< Disable redzone NoRedZone, ///< Disable redzone
NoReturn, ///< Mark the function as not returning NoReturn, ///< Mark the function as not returning
NoUnwind, ///< Function doesn't unwind stack NoUnwind, ///< Function doesn't unwind stack
@ -133,6 +134,8 @@ public:
/// alignment set. /// alignment set.
static Attribute getWithAlignment(LLVMContext &Context, uint64_t Align); static Attribute getWithAlignment(LLVMContext &Context, uint64_t Align);
static Attribute getWithStackAlignment(LLVMContext &Context, uint64_t Align); static Attribute getWithStackAlignment(LLVMContext &Context, uint64_t Align);
static Attribute getWithDereferenceableBytes(LLVMContext &Context,
uint64_t Bytes);
//===--------------------------------------------------------------------===// //===--------------------------------------------------------------------===//
// Attribute Accessors // Attribute Accessors
@ -178,6 +181,10 @@ public:
/// alignment value. /// alignment value.
unsigned getStackAlignment() const; unsigned getStackAlignment() const;
/// \brief Returns the number of dereferenceable bytes from the
/// dereferenceable attribute (or zero if unknown).
uint64_t getDereferenceableBytes() const;
/// \brief The Attribute is converted to a string of equivalent mnemonic. This /// \brief The Attribute is converted to a string of equivalent mnemonic. This
/// is, presumably, for writing out the mnemonics for the assembly writer. /// is, presumably, for writing out the mnemonics for the assembly writer.
std::string getAsString(bool InAttrGrp = false) const; std::string getAsString(bool InAttrGrp = false) const;
@ -316,6 +323,9 @@ public:
/// \brief Get the stack alignment. /// \brief Get the stack alignment.
unsigned getStackAlignment(unsigned Index) const; unsigned getStackAlignment(unsigned Index) const;
/// \brief Get the number of dereferenceable bytes (or zero if unknown).
uint64_t getDereferenceableBytes(unsigned Index) const;
/// \brief Return the attributes at the index as a string. /// \brief Return the attributes at the index as a string.
std::string getAsString(unsigned Index, bool InAttrGrp = false) const; std::string getAsString(unsigned Index, bool InAttrGrp = false) const;
@ -395,13 +405,15 @@ class AttrBuilder {
std::map<std::string, std::string> TargetDepAttrs; std::map<std::string, std::string> TargetDepAttrs;
uint64_t Alignment; uint64_t Alignment;
uint64_t StackAlignment; uint64_t StackAlignment;
uint64_t DerefBytes;
public: public:
AttrBuilder() : Attrs(0), Alignment(0), StackAlignment(0) {} AttrBuilder() : Attrs(0), Alignment(0), StackAlignment(0), DerefBytes(0) {}
explicit AttrBuilder(uint64_t Val) explicit AttrBuilder(uint64_t Val)
: Attrs(0), Alignment(0), StackAlignment(0) { : Attrs(0), Alignment(0), StackAlignment(0), DerefBytes(0) {
addRawValue(Val); addRawValue(Val);
} }
AttrBuilder(const Attribute &A) : Attrs(0), Alignment(0), StackAlignment(0) { AttrBuilder(const Attribute &A)
: Attrs(0), Alignment(0), StackAlignment(0), DerefBytes(0) {
addAttribute(A); addAttribute(A);
} }
AttrBuilder(AttributeSet AS, unsigned Idx); AttrBuilder(AttributeSet AS, unsigned Idx);
@ -455,6 +467,10 @@ public:
/// \brief Retrieve the stack alignment attribute, if it exists. /// \brief Retrieve the stack alignment attribute, if it exists.
uint64_t getStackAlignment() const { return StackAlignment; } uint64_t getStackAlignment() const { return StackAlignment; }
/// \brief Retrieve the number of dereferenceable bytes, if the dereferenceable
/// attribute exists (zero is returned otherwise).
uint64_t getDereferenceableBytes() const { return DerefBytes; }
/// \brief This turns an int alignment (which must be a power of 2) into the /// \brief This turns an int alignment (which must be a power of 2) into the
/// form used internally in Attribute. /// form used internally in Attribute.
AttrBuilder &addAlignmentAttr(unsigned Align); AttrBuilder &addAlignmentAttr(unsigned Align);
@ -463,6 +479,10 @@ public:
/// the form used internally in Attribute. /// the form used internally in Attribute.
AttrBuilder &addStackAlignmentAttr(unsigned Align); AttrBuilder &addStackAlignmentAttr(unsigned Align);
/// \brief This turns the number of dereferenceable bytes into the form used
/// internally in Attribute.
AttrBuilder &addDereferenceableAttr(uint64_t Bytes);
/// \brief Return true if the builder contains no target-independent /// \brief Return true if the builder contains no target-independent
/// attributes. /// attributes.
bool empty() const { return Attrs.none(); } bool empty() const { return Attrs.none(); }

View File

@ -217,6 +217,12 @@ public:
CALLSITE_DELEGATE_GETTER(getParamAlignment(i)); CALLSITE_DELEGATE_GETTER(getParamAlignment(i));
} }
/// @brief Extract the number of dereferenceable bytes for a call or
/// parameter (0=unknown).
uint64_t getDereferenceableBytes(uint16_t i) const {
CALLSITE_DELEGATE_GETTER(getDereferenceableBytes(i));
}
/// \brief Return true if the call should not be treated as a call to a /// \brief Return true if the call should not be treated as a call to a
/// builtin. /// builtin.
bool isNoBuiltin() const { bool isNoBuiltin() const {
@ -302,6 +308,19 @@ public:
paramHasAttr(ArgNo + 1, Attribute::ReadNone); paramHasAttr(ArgNo + 1, Attribute::ReadNone);
} }
/// @brief Return true if the return value is known to be not null.
/// This may be because it has the nonnull attribute, or because at least
/// one byte is dereferenceable and the pointer is in addrspace(0).
bool isReturnNonNull() const {
if (paramHasAttr(0, Attribute::NonNull))
return true;
else if (getDereferenceableBytes(0) > 0 &&
getType()->getPointerAddressSpace() == 0)
return true;
return false;
}
/// hasArgument - Returns true if this CallSite passes the given Value* as an /// hasArgument - Returns true if this CallSite passes the given Value* as an
/// argument to the called function. /// argument to the called function.
bool hasArgument(const Value *Arg) const { bool hasArgument(const Value *Arg) const {

View File

@ -233,6 +233,12 @@ public:
return AttributeSets.getParamAlignment(i); return AttributeSets.getParamAlignment(i);
} }
/// @brief Extract the number of dereferenceable bytes for a call or
/// parameter (0=unknown).
uint64_t getDereferenceableBytes(unsigned i) const {
return AttributeSets.getDereferenceableBytes(i);
}
/// @brief Determine if the function does not access memory. /// @brief Determine if the function does not access memory.
bool doesNotAccessMemory() const { bool doesNotAccessMemory() const {
return AttributeSets.hasAttribute(AttributeSet::FunctionIndex, return AttributeSets.hasAttribute(AttributeSet::FunctionIndex,

View File

@ -1376,6 +1376,12 @@ public:
return AttributeList.getParamAlignment(i); return AttributeList.getParamAlignment(i);
} }
/// \brief Extract the number of dereferenceable bytes for a call or
/// parameter (0=unknown).
uint64_t getDereferenceableBytes(unsigned i) const {
return AttributeList.getDereferenceableBytes(i);
}
/// \brief Return true if the call should not be treated as a call to a /// \brief Return true if the call should not be treated as a call to a
/// builtin. /// builtin.
bool isNoBuiltin() const { bool isNoBuiltin() const {
@ -3051,6 +3057,12 @@ public:
return AttributeList.getParamAlignment(i); return AttributeList.getParamAlignment(i);
} }
/// \brief Extract the number of dereferenceable bytes for a call or
/// parameter (0=unknown).
uint64_t getDereferenceableBytes(unsigned i) const {
return AttributeList.getDereferenceableBytes(i);
}
/// \brief Return true if the call should not be treated as a call to a /// \brief Return true if the call should not be treated as a call to a
/// builtin. /// builtin.
bool isNoBuiltin() const { bool isNoBuiltin() const {

View File

@ -2086,7 +2086,7 @@ bool llvm::isKnownNonNull(const Value *V, const TargetLibraryInfo *TLI) {
return !GV->hasExternalWeakLinkage(); return !GV->hasExternalWeakLinkage();
if (ImmutableCallSite CS = V) if (ImmutableCallSite CS = V)
if (CS.paramHasAttr(0, Attribute::NonNull)) if (CS.isReturnNonNull())
return true; return true;
// operator new never returns null. // operator new never returns null.

View File

@ -612,6 +612,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(byval); KEYWORD(byval);
KEYWORD(inalloca); KEYWORD(inalloca);
KEYWORD(cold); KEYWORD(cold);
KEYWORD(dereferenceable);
KEYWORD(inlinehint); KEYWORD(inlinehint);
KEYWORD(inreg); KEYWORD(inreg);
KEYWORD(jumptable); KEYWORD(jumptable);

View File

@ -1052,6 +1052,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
"invalid use of attribute on a function"); "invalid use of attribute on a function");
break; break;
case lltok::kw_byval: case lltok::kw_byval:
case lltok::kw_dereferenceable:
case lltok::kw_inalloca: case lltok::kw_inalloca:
case lltok::kw_nest: case lltok::kw_nest:
case lltok::kw_noalias: case lltok::kw_noalias:
@ -1212,6 +1213,16 @@ bool LLParser::ParseUInt32(unsigned &Val) {
return false; return false;
} }
/// ParseUInt64
/// ::= uint64
bool LLParser::ParseUInt64(uint64_t &Val) {
if (Lex.getKind() != lltok::APSInt || Lex.getAPSIntVal().isSigned())
return TokError("expected integer");
Val = Lex.getAPSIntVal().getLimitedValue();
Lex.Lex();
return false;
}
/// ParseTLSModel /// ParseTLSModel
/// := 'localdynamic' /// := 'localdynamic'
/// := 'initialexec' /// := 'initialexec'
@ -1284,6 +1295,13 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
continue; continue;
} }
case lltok::kw_byval: B.addAttribute(Attribute::ByVal); break; case lltok::kw_byval: B.addAttribute(Attribute::ByVal); break;
case lltok::kw_dereferenceable: {
uint64_t Bytes;
if (ParseOptionalDereferenceableBytes(Bytes))
return true;
B.addDereferenceableAttr(Bytes);
continue;
}
case lltok::kw_inalloca: B.addAttribute(Attribute::InAlloca); break; case lltok::kw_inalloca: B.addAttribute(Attribute::InAlloca); break;
case lltok::kw_inreg: B.addAttribute(Attribute::InReg); break; case lltok::kw_inreg: B.addAttribute(Attribute::InReg); break;
case lltok::kw_nest: B.addAttribute(Attribute::Nest); break; case lltok::kw_nest: B.addAttribute(Attribute::Nest); break;
@ -1341,6 +1359,13 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
switch (Token) { switch (Token) {
default: // End of attributes. default: // End of attributes.
return HaveError; return HaveError;
case lltok::kw_dereferenceable: {
uint64_t Bytes;
if (ParseOptionalDereferenceableBytes(Bytes))
return true;
B.addDereferenceableAttr(Bytes);
continue;
}
case lltok::kw_inreg: B.addAttribute(Attribute::InReg); break; case lltok::kw_inreg: B.addAttribute(Attribute::InReg); break;
case lltok::kw_noalias: B.addAttribute(Attribute::NoAlias); break; case lltok::kw_noalias: B.addAttribute(Attribute::NoAlias); break;
case lltok::kw_nonnull: B.addAttribute(Attribute::NonNull); break; case lltok::kw_nonnull: B.addAttribute(Attribute::NonNull); break;
@ -1606,6 +1631,26 @@ bool LLParser::ParseOptionalAlignment(unsigned &Alignment) {
return false; return false;
} }
/// ParseOptionalDereferenceableBytes
/// ::= /* empty */
/// ::= 'dereferenceable' '(' 4 ')'
bool LLParser::ParseOptionalDereferenceableBytes(uint64_t &Bytes) {
Bytes = 0;
if (!EatIfPresent(lltok::kw_dereferenceable))
return false;
LocTy ParenLoc = Lex.getLoc();
if (!EatIfPresent(lltok::lparen))
return Error(ParenLoc, "expected '('");
LocTy DerefLoc = Lex.getLoc();
if (ParseUInt64(Bytes)) return true;
ParenLoc = Lex.getLoc();
if (!EatIfPresent(lltok::rparen))
return Error(ParenLoc, "expected ')'");
if (!Bytes)
return Error(DerefLoc, "dereferenceable bytes must be non-zero");
return false;
}
/// ParseOptionalCommaAlign /// ParseOptionalCommaAlign
/// ::= /// ::=
/// ::= ',' align 4 /// ::= ',' align 4

View File

@ -202,6 +202,11 @@ namespace llvm {
Loc = Lex.getLoc(); Loc = Lex.getLoc();
return ParseUInt32(Val); return ParseUInt32(Val);
} }
bool ParseUInt64(uint64_t &Val);
bool ParseUInt64(uint64_t &Val, LocTy &Loc) {
Loc = Lex.getLoc();
return ParseUInt64(Val);
}
bool ParseTLSModel(GlobalVariable::ThreadLocalMode &TLM); bool ParseTLSModel(GlobalVariable::ThreadLocalMode &TLM);
bool ParseOptionalThreadLocal(GlobalVariable::ThreadLocalMode &TLM); bool ParseOptionalThreadLocal(GlobalVariable::ThreadLocalMode &TLM);
@ -219,6 +224,7 @@ namespace llvm {
bool ParseOptionalDLLStorageClass(unsigned &DLLStorageClass); bool ParseOptionalDLLStorageClass(unsigned &DLLStorageClass);
bool ParseOptionalCallingConv(CallingConv::ID &CC); bool ParseOptionalCallingConv(CallingConv::ID &CC);
bool ParseOptionalAlignment(unsigned &Alignment); bool ParseOptionalAlignment(unsigned &Alignment);
bool ParseOptionalDereferenceableBytes(uint64_t &Bytes);
bool ParseScopeAndOrdering(bool isAtomic, SynchronizationScope &Scope, bool ParseScopeAndOrdering(bool isAtomic, SynchronizationScope &Scope,
AtomicOrdering &Ordering); AtomicOrdering &Ordering);
bool ParseOrdering(AtomicOrdering &Ordering); bool ParseOrdering(AtomicOrdering &Ordering);

View File

@ -106,6 +106,7 @@ namespace lltok {
kw_byval, kw_byval,
kw_inalloca, kw_inalloca,
kw_cold, kw_cold,
kw_dereferenceable,
kw_inlinehint, kw_inlinehint,
kw_inreg, kw_inreg,
kw_jumptable, kw_jumptable,

View File

@ -588,6 +588,8 @@ static Attribute::AttrKind GetAttrFromCode(uint64_t Code) {
return Attribute::NonLazyBind; return Attribute::NonLazyBind;
case bitc::ATTR_KIND_NON_NULL: case bitc::ATTR_KIND_NON_NULL:
return Attribute::NonNull; return Attribute::NonNull;
case bitc::ATTR_KIND_DEREFERENCEABLE:
return Attribute::Dereferenceable;
case bitc::ATTR_KIND_NO_RED_ZONE: case bitc::ATTR_KIND_NO_RED_ZONE:
return Attribute::NoRedZone; return Attribute::NoRedZone;
case bitc::ATTR_KIND_NO_RETURN: case bitc::ATTR_KIND_NO_RETURN:
@ -689,8 +691,10 @@ std::error_code BitcodeReader::ParseAttributeGroupBlock() {
return EC; return EC;
if (Kind == Attribute::Alignment) if (Kind == Attribute::Alignment)
B.addAlignmentAttr(Record[++i]); B.addAlignmentAttr(Record[++i]);
else else if (Kind == Attribute::StackAlignment)
B.addStackAlignmentAttr(Record[++i]); B.addStackAlignmentAttr(Record[++i]);
else if (Kind == Attribute::Dereferenceable)
B.addDereferenceableAttr(Record[++i]);
} else { // String attribute } else { // String attribute
assert((Record[i] == 3 || Record[i] == 4) && assert((Record[i] == 3 || Record[i] == 4) &&
"Invalid attribute group entry"); "Invalid attribute group entry");

View File

@ -201,6 +201,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
return bitc::ATTR_KIND_NON_LAZY_BIND; return bitc::ATTR_KIND_NON_LAZY_BIND;
case Attribute::NonNull: case Attribute::NonNull:
return bitc::ATTR_KIND_NON_NULL; return bitc::ATTR_KIND_NON_NULL;
case Attribute::Dereferenceable:
return bitc::ATTR_KIND_DEREFERENCEABLE;
case Attribute::NoRedZone: case Attribute::NoRedZone:
return bitc::ATTR_KIND_NO_RED_ZONE; return bitc::ATTR_KIND_NO_RED_ZONE;
case Attribute::NoReturn: case Attribute::NoReturn:

View File

@ -116,7 +116,8 @@ public:
IntAttributeImpl(Attribute::AttrKind Kind, uint64_t Val) IntAttributeImpl(Attribute::AttrKind Kind, uint64_t Val)
: EnumAttributeImpl(IntAttrEntry, Kind), Val(Val) { : EnumAttributeImpl(IntAttrEntry, Kind), Val(Val) {
assert( assert(
(Kind == Attribute::Alignment || Kind == Attribute::StackAlignment) && (Kind == Attribute::Alignment || Kind == Attribute::StackAlignment ||
Kind == Attribute::Dereferenceable) &&
"Wrong kind for int attribute!"); "Wrong kind for int attribute!");
} }
@ -164,6 +165,7 @@ public:
unsigned getAlignment() const; unsigned getAlignment() const;
unsigned getStackAlignment() const; unsigned getStackAlignment() const;
uint64_t getDereferenceableBytes() const;
std::string getAsString(bool InAttrGrp) const; std::string getAsString(bool InAttrGrp) const;
typedef const Attribute *iterator; typedef const Attribute *iterator;

View File

@ -88,6 +88,12 @@ Attribute Attribute::getWithStackAlignment(LLVMContext &Context,
return get(Context, StackAlignment, Align); return get(Context, StackAlignment, Align);
} }
Attribute Attribute::getWithDereferenceableBytes(LLVMContext &Context,
uint64_t Bytes) {
assert(Bytes && "Bytes must be non-zero.");
return get(Context, Dereferenceable, Bytes);
}
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Attribute Accessor Methods // Attribute Accessor Methods
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -156,6 +162,14 @@ unsigned Attribute::getStackAlignment() const {
return pImpl->getValueAsInt(); return pImpl->getValueAsInt();
} }
/// This returns the number of dereferenceable bytes.
uint64_t Attribute::getDereferenceableBytes() const {
assert(hasAttribute(Attribute::Dereferenceable) &&
"Trying to get dereferenceable bytes from "
"non-dereferenceable attribute!");
return pImpl->getValueAsInt();
}
std::string Attribute::getAsString(bool InAttrGrp) const { std::string Attribute::getAsString(bool InAttrGrp) const {
if (!pImpl) return ""; if (!pImpl) return "";
@ -263,6 +277,20 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
return Result; return Result;
} }
if (hasAttribute(Attribute::Dereferenceable)) {
std::string Result;
Result += "dereferenceable";
if (InAttrGrp) {
Result += "=";
Result += utostr(getValueAsInt());
} else {
Result += "(";
Result += utostr(getValueAsInt());
Result += ")";
}
return Result;
}
// Convert target-dependent attributes to strings of the form: // Convert target-dependent attributes to strings of the form:
// //
// "kind" // "kind"
@ -398,6 +426,8 @@ uint64_t AttributeImpl::getAttrMask(Attribute::AttrKind Val) {
case Attribute::InAlloca: return 1ULL << 43; case Attribute::InAlloca: return 1ULL << 43;
case Attribute::NonNull: return 1ULL << 44; case Attribute::NonNull: return 1ULL << 44;
case Attribute::JumpTable: return 1ULL << 45; case Attribute::JumpTable: return 1ULL << 45;
case Attribute::Dereferenceable:
llvm_unreachable("dereferenceable attribute not supported in raw format");
} }
llvm_unreachable("Unsupported attribute type"); llvm_unreachable("Unsupported attribute type");
} }
@ -482,6 +512,13 @@ unsigned AttributeSetNode::getStackAlignment() const {
return 0; return 0;
} }
uint64_t AttributeSetNode::getDereferenceableBytes() const {
for (iterator I = begin(), E = end(); I != E; ++I)
if (I->hasAttribute(Attribute::Dereferenceable))
return I->getDereferenceableBytes();
return 0;
}
std::string AttributeSetNode::getAsString(bool InAttrGrp) const { std::string AttributeSetNode::getAsString(bool InAttrGrp) const {
std::string Str; std::string Str;
for (iterator I = begin(), E = end(); I != E; ++I) { for (iterator I = begin(), E = end(); I != E; ++I) {
@ -515,6 +552,8 @@ uint64_t AttributeSetImpl::Raw(unsigned Index) const {
Mask |= (Log2_32(ASN->getAlignment()) + 1) << 16; Mask |= (Log2_32(ASN->getAlignment()) + 1) << 16;
else if (Kind == Attribute::StackAlignment) else if (Kind == Attribute::StackAlignment)
Mask |= (Log2_32(ASN->getStackAlignment()) + 1) << 26; Mask |= (Log2_32(ASN->getStackAlignment()) + 1) << 26;
else if (Kind == Attribute::Dereferenceable)
llvm_unreachable("dereferenceable not supported in bit mask");
else else
Mask |= AttributeImpl::getAttrMask(Kind); Mask |= AttributeImpl::getAttrMask(Kind);
} }
@ -620,6 +659,10 @@ AttributeSet AttributeSet::get(LLVMContext &C, unsigned Index,
else if (Kind == Attribute::StackAlignment) else if (Kind == Attribute::StackAlignment)
Attrs.push_back(std::make_pair(Index, Attribute:: Attrs.push_back(std::make_pair(Index, Attribute::
getWithStackAlignment(C, B.getStackAlignment()))); getWithStackAlignment(C, B.getStackAlignment())));
else if (Kind == Attribute::Dereferenceable)
Attrs.push_back(std::make_pair(Index,
Attribute::getWithDereferenceableBytes(C,
B.getDereferenceableBytes())));
else else
Attrs.push_back(std::make_pair(Index, Attribute::get(C, Kind))); Attrs.push_back(std::make_pair(Index, Attribute::get(C, Kind)));
} }
@ -877,6 +920,11 @@ unsigned AttributeSet::getStackAlignment(unsigned Index) const {
return ASN ? ASN->getStackAlignment() : 0; return ASN ? ASN->getStackAlignment() : 0;
} }
uint64_t AttributeSet::getDereferenceableBytes(unsigned Index) const {
AttributeSetNode *ASN = getAttributes(Index);
return ASN ? ASN->getDereferenceableBytes() : 0;
}
std::string AttributeSet::getAsString(unsigned Index, std::string AttributeSet::getAsString(unsigned Index,
bool InAttrGrp) const { bool InAttrGrp) const {
AttributeSetNode *ASN = getAttributes(Index); AttributeSetNode *ASN = getAttributes(Index);
@ -956,7 +1004,7 @@ void AttributeSet::dump() const {
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
AttrBuilder::AttrBuilder(AttributeSet AS, unsigned Index) AttrBuilder::AttrBuilder(AttributeSet AS, unsigned Index)
: Attrs(0), Alignment(0), StackAlignment(0) { : Attrs(0), Alignment(0), StackAlignment(0), DerefBytes(0) {
AttributeSetImpl *pImpl = AS.pImpl; AttributeSetImpl *pImpl = AS.pImpl;
if (!pImpl) return; if (!pImpl) return;
@ -973,13 +1021,14 @@ AttrBuilder::AttrBuilder(AttributeSet AS, unsigned Index)
void AttrBuilder::clear() { void AttrBuilder::clear() {
Attrs.reset(); Attrs.reset();
Alignment = StackAlignment = 0; Alignment = StackAlignment = DerefBytes = 0;
} }
AttrBuilder &AttrBuilder::addAttribute(Attribute::AttrKind Val) { AttrBuilder &AttrBuilder::addAttribute(Attribute::AttrKind Val) {
assert((unsigned)Val < Attribute::EndAttrKinds && "Attribute out of range!"); assert((unsigned)Val < Attribute::EndAttrKinds && "Attribute out of range!");
assert(Val != Attribute::Alignment && Val != Attribute::StackAlignment && assert(Val != Attribute::Alignment && Val != Attribute::StackAlignment &&
"Adding alignment attribute without adding alignment value!"); Val != Attribute::Dereferenceable &&
"Adding integer attribute without adding a value!");
Attrs[Val] = true; Attrs[Val] = true;
return *this; return *this;
} }
@ -997,6 +1046,8 @@ AttrBuilder &AttrBuilder::addAttribute(Attribute Attr) {
Alignment = Attr.getAlignment(); Alignment = Attr.getAlignment();
else if (Kind == Attribute::StackAlignment) else if (Kind == Attribute::StackAlignment)
StackAlignment = Attr.getStackAlignment(); StackAlignment = Attr.getStackAlignment();
else if (Kind == Attribute::Dereferenceable)
DerefBytes = Attr.getDereferenceableBytes();
return *this; return *this;
} }
@ -1013,6 +1064,8 @@ AttrBuilder &AttrBuilder::removeAttribute(Attribute::AttrKind Val) {
Alignment = 0; Alignment = 0;
else if (Val == Attribute::StackAlignment) else if (Val == Attribute::StackAlignment)
StackAlignment = 0; StackAlignment = 0;
else if (Val == Attribute::Dereferenceable)
DerefBytes = 0;
return *this; return *this;
} }
@ -1037,6 +1090,8 @@ AttrBuilder &AttrBuilder::removeAttributes(AttributeSet A, uint64_t Index) {
Alignment = 0; Alignment = 0;
else if (Kind == Attribute::StackAlignment) else if (Kind == Attribute::StackAlignment)
StackAlignment = 0; StackAlignment = 0;
else if (Kind == Attribute::Dereferenceable)
DerefBytes = 0;
} else { } else {
assert(Attr.isStringAttribute() && "Invalid attribute type!"); assert(Attr.isStringAttribute() && "Invalid attribute type!");
std::map<std::string, std::string>::iterator std::map<std::string, std::string>::iterator
@ -1079,6 +1134,14 @@ AttrBuilder &AttrBuilder::addStackAlignmentAttr(unsigned Align) {
return *this; return *this;
} }
AttrBuilder &AttrBuilder::addDereferenceableAttr(uint64_t Bytes) {
if (Bytes == 0) return *this;
Attrs[Attribute::Dereferenceable] = true;
DerefBytes = Bytes;
return *this;
}
AttrBuilder &AttrBuilder::merge(const AttrBuilder &B) { AttrBuilder &AttrBuilder::merge(const AttrBuilder &B) {
// FIXME: What if both have alignments, but they don't match?! // FIXME: What if both have alignments, but they don't match?!
if (!Alignment) if (!Alignment)
@ -1087,6 +1150,9 @@ AttrBuilder &AttrBuilder::merge(const AttrBuilder &B) {
if (!StackAlignment) if (!StackAlignment)
StackAlignment = B.StackAlignment; StackAlignment = B.StackAlignment;
if (!DerefBytes)
DerefBytes = B.DerefBytes;
Attrs |= B.Attrs; Attrs |= B.Attrs;
for (td_const_iterator I = B.TargetDepAttrs.begin(), for (td_const_iterator I = B.TargetDepAttrs.begin(),
@ -1142,7 +1208,8 @@ bool AttrBuilder::operator==(const AttrBuilder &B) {
if (B.TargetDepAttrs.find(I->first) == B.TargetDepAttrs.end()) if (B.TargetDepAttrs.find(I->first) == B.TargetDepAttrs.end())
return false; return false;
return Alignment == B.Alignment && StackAlignment == B.StackAlignment; return Alignment == B.Alignment && StackAlignment == B.StackAlignment &&
DerefBytes == B.DerefBytes;
} }
AttrBuilder &AttrBuilder::addRawValue(uint64_t Val) { AttrBuilder &AttrBuilder::addRawValue(uint64_t Val) {
@ -1151,6 +1218,8 @@ AttrBuilder &AttrBuilder::addRawValue(uint64_t Val) {
for (Attribute::AttrKind I = Attribute::None; I != Attribute::EndAttrKinds; for (Attribute::AttrKind I = Attribute::None; I != Attribute::EndAttrKinds;
I = Attribute::AttrKind(I + 1)) { I = Attribute::AttrKind(I + 1)) {
if (I == Attribute::Dereferenceable)
continue;
if (uint64_t A = (Val & AttributeImpl::getAttrMask(I))) { if (uint64_t A = (Val & AttributeImpl::getAttrMask(I))) {
Attrs[I] = true; Attrs[I] = true;
@ -1184,6 +1253,7 @@ AttributeSet AttributeFuncs::typeIncompatible(Type *Ty, uint64_t Index) {
.addAttribute(Attribute::NoAlias) .addAttribute(Attribute::NoAlias)
.addAttribute(Attribute::NoCapture) .addAttribute(Attribute::NoCapture)
.addAttribute(Attribute::NonNull) .addAttribute(Attribute::NonNull)
.addDereferenceableAttr(1) // the int here is ignored
.addAttribute(Attribute::ReadNone) .addAttribute(Attribute::ReadNone)
.addAttribute(Attribute::ReadOnly) .addAttribute(Attribute::ReadOnly)
.addAttribute(Attribute::StructRet) .addAttribute(Attribute::StructRet)

View File

@ -77,11 +77,17 @@ unsigned Argument::getArgNo() const {
} }
/// hasNonNullAttr - Return true if this argument has the nonnull attribute on /// hasNonNullAttr - Return true if this argument has the nonnull attribute on
/// it in its containing function. /// it in its containing function. Also returns true if at least one byte is
/// known to be dereferenceable and the pointer is in addrspace(0).
bool Argument::hasNonNullAttr() const { bool Argument::hasNonNullAttr() const {
if (!getType()->isPointerTy()) return false; if (!getType()->isPointerTy()) return false;
return getParent()->getAttributes(). if (getParent()->getAttributes().
hasAttribute(getArgNo()+1, Attribute::NonNull); hasAttribute(getArgNo()+1, Attribute::NonNull))
return true;
else if (getDereferenceableBytes() > 0 &&
getType()->getPointerAddressSpace() == 0)
return true;
return false;
} }
/// hasByValAttr - Return true if this argument has the byval attribute on it /// hasByValAttr - Return true if this argument has the byval attribute on it
@ -113,6 +119,12 @@ unsigned Argument::getParamAlignment() const {
} }
uint64_t Argument::getDereferenceableBytes() const {
assert(getType()->isPointerTy() &&
"Only pointers have dereferenceable bytes");
return getParent()->getDereferenceableBytes(getArgNo()+1);
}
/// hasNestAttr - Return true if this argument has the nest attribute on /// hasNestAttr - Return true if this argument has the nest attribute on
/// it in its containing function. /// it in its containing function.
bool Argument::hasNestAttr() const { bool Argument::hasNestAttr() const {

View File

@ -15,6 +15,7 @@
#include "LLVMContextImpl.h" #include "LLVMContextImpl.h"
#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallString.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/Constant.h" #include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h" #include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h" #include "llvm/IR/DataLayout.h"
@ -504,9 +505,29 @@ static bool isDereferenceablePointer(const Value *V, const DataLayout *DL,
if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(V)) if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(V))
return !GV->hasExternalWeakLinkage(); return !GV->hasExternalWeakLinkage();
// byval arguments are ok. // byval arguments are okay. Arguments specifically marked as
if (const Argument *A = dyn_cast<Argument>(V)) // dereferenceable are okay too.
return A->hasByValAttr(); if (const Argument *A = dyn_cast<Argument>(V)) {
if (A->hasByValAttr())
return true;
else if (uint64_t Bytes = A->getDereferenceableBytes()) {
Type *Ty = V->getType()->getPointerElementType();
if (Ty->isSized() && DL && DL->getTypeStoreSize(Ty) <= Bytes)
return true;
}
return false;
}
// Return values from call sites specifically marked as dereferenceable are
// also okay.
if (ImmutableCallSite CS = V) {
if (uint64_t Bytes = CS.getDereferenceableBytes(0)) {
Type *Ty = V->getType()->getPointerElementType();
if (Ty->isSized() && DL && DL->getTypeStoreSize(Ty) <= Bytes)
return true;
}
}
// For GEPs, determine if the indexing lands within the allocated object. // For GEPs, determine if the indexing lands within the allocated object.
if (const GEPOperator *GEP = dyn_cast<GEPOperator>(V)) { if (const GEPOperator *GEP = dyn_cast<GEPOperator>(V)) {

View File

@ -229,6 +229,16 @@ define void @f38() unnamed_addr jumptable {
unreachable unreachable
} }
define dereferenceable(2) i8* @f39(i8* dereferenceable(1) %a) {
; CHECK: define dereferenceable(2) i8* @f39(i8* dereferenceable(1) %a) {
ret i8* %a
}
define dereferenceable(18446744073709551606) i8* @f40(i8* dereferenceable(18446744073709551615) %a) {
; CHECK: define dereferenceable(18446744073709551606) i8* @f40(i8* dereferenceable(18446744073709551615) %a) {
ret i8* %a
}
; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #0 = { noreturn }
; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #1 = { nounwind }
; CHECK: attributes #2 = { readnone } ; CHECK: attributes #2 = { readnone }

View File

@ -874,6 +874,21 @@ define i1 @nonnull_arg(i32* nonnull %i) {
; CHECK: ret i1 false ; CHECK: ret i1 false
} }
define i1 @nonnull_deref_arg(i32* dereferenceable(4) %i) {
%cmp = icmp eq i32* %i, null
ret i1 %cmp
; CHECK-LABEL: @nonnull_deref_arg
; CHECK: ret i1 false
}
define i1 @nonnull_deref_as_arg(i32 addrspace(1)* dereferenceable(4) %i) {
%cmp = icmp eq i32 addrspace(1)* %i, null
ret i1 %cmp
; CHECK-LABEL: @nonnull_deref_as_arg
; CHECK: icmp
; CHECK ret
}
declare nonnull i32* @returns_nonnull_helper() declare nonnull i32* @returns_nonnull_helper()
define i1 @returns_nonnull() { define i1 @returns_nonnull() {
%call = call nonnull i32* @returns_nonnull_helper() %call = call nonnull i32* @returns_nonnull_helper()
@ -883,6 +898,25 @@ define i1 @returns_nonnull() {
; CHECK: ret i1 false ; CHECK: ret i1 false
} }
declare dereferenceable(4) i32* @returns_nonnull_deref_helper()
define i1 @returns_nonnull_deref() {
%call = call dereferenceable(4) i32* @returns_nonnull_deref_helper()
%cmp = icmp eq i32* %call, null
ret i1 %cmp
; CHECK-LABEL: @returns_nonnull_deref
; CHECK: ret i1 false
}
declare dereferenceable(4) i32 addrspace(1)* @returns_nonnull_deref_as_helper()
define i1 @returns_nonnull_as_deref() {
%call = call dereferenceable(4) i32 addrspace(1)* @returns_nonnull_deref_as_helper()
%cmp = icmp eq i32 addrspace(1)* %call, null
ret i1 %cmp
; CHECK-LABEL: @returns_nonnull_as_deref
; CHECK: icmp
; CHECK: ret
}
; If a bit is known to be zero for A and known to be one for B, ; If a bit is known to be zero for A and known to be one for B,
; then A and B cannot be equal. ; then A and B cannot be equal.
define i1 @icmp_eq_const(i32 %a) nounwind { define i1 @icmp_eq_const(i32 %a) nounwind {

View File

@ -0,0 +1,86 @@
; RUN: opt -S -basicaa -licm < %s | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; This test represents the following function:
; void test1(int * __restrict__ a, int * __restrict__ b, int &c, int n) {
; for (int i = 0; i < n; ++i)
; if (a[i] > 0)
; a[i] = c*b[i];
; }
; and we want to hoist the load of %c out of the loop. This can be done only
; because the dereferenceable attribute is on %c.
; CHECK-LABEL: @test1
; CHECK: load i32* %c, align 4
; CHECK: for.body:
define void @test1(i32* noalias nocapture %a, i32* noalias nocapture readonly %b, i32* nocapture readonly nonnull dereferenceable(4) %c, i32 %n) #0 {
entry:
%cmp11 = icmp sgt i32 %n, 0
br i1 %cmp11, label %for.body, label %for.end
for.body: ; preds = %entry, %for.inc
%indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ]
%arrayidx = getelementptr inbounds i32* %a, i64 %indvars.iv
%0 = load i32* %arrayidx, align 4
%cmp1 = icmp sgt i32 %0, 0
br i1 %cmp1, label %if.then, label %for.inc
if.then: ; preds = %for.body
%1 = load i32* %c, align 4
%arrayidx3 = getelementptr inbounds i32* %b, i64 %indvars.iv
%2 = load i32* %arrayidx3, align 4
%mul = mul nsw i32 %2, %1
store i32 %mul, i32* %arrayidx, align 4
br label %for.inc
for.inc: ; preds = %for.body, %if.then
%indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
%lftr.wideiv = trunc i64 %indvars.iv.next to i32
%exitcond = icmp eq i32 %lftr.wideiv, %n
br i1 %exitcond, label %for.end, label %for.body
for.end: ; preds = %for.inc, %entry
ret void
}
; This is the same as @test1, but without the dereferenceable attribute on %c.
; Without this attribute, we should not hoist the load of %c.
; CHECK-LABEL: @test2
; CHECK: if.then:
; CHECK: load i32* %c, align 4
define void @test2(i32* noalias nocapture %a, i32* noalias nocapture readonly %b, i32* nocapture readonly nonnull %c, i32 %n) #0 {
entry:
%cmp11 = icmp sgt i32 %n, 0
br i1 %cmp11, label %for.body, label %for.end
for.body: ; preds = %entry, %for.inc
%indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ]
%arrayidx = getelementptr inbounds i32* %a, i64 %indvars.iv
%0 = load i32* %arrayidx, align 4
%cmp1 = icmp sgt i32 %0, 0
br i1 %cmp1, label %if.then, label %for.inc
if.then: ; preds = %for.body
%1 = load i32* %c, align 4
%arrayidx3 = getelementptr inbounds i32* %b, i64 %indvars.iv
%2 = load i32* %arrayidx3, align 4
%mul = mul nsw i32 %2, %1
store i32 %mul, i32* %arrayidx, align 4
br label %for.inc
for.inc: ; preds = %for.body, %if.then
%indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
%lftr.wideiv = trunc i64 %indvars.iv.next to i32
%exitcond = icmp eq i32 %lftr.wideiv, %n
br i1 %exitcond, label %for.end, label %for.body
for.end: ; preds = %for.inc, %entry
ret void
}
attributes #0 = { nounwind uwtable }