Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -1250,6 +1250,14 @@ epilogue, the backend should forcibly align the stack pointer. Specify the desired alignment, which must be a power of two, in parentheses. +``allocsize([, ])`` + This attribute indicates that the annotated function will always return at + least a given number of bytes (or null). Its arguments are base-0 parameter + numbers; if one argument is provided, then it's assumed that at least + ``CallSite.Args[n]`` bytes will be available at the returned pointer. If two + are provided, then it's assumed that ``CallSite.Args[n] * CallSite.Args[m]`` + bytes are available. The referenced parameters must be integer types. No + assumptions are made about the contents of the returned block of memory. ``alwaysinline`` This attribute indicates that the inliner should attempt to inline this function into callers whenever possible, ignoring any active Index: include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- include/llvm/Bitcode/LLVMBitCodes.h +++ include/llvm/Bitcode/LLVMBitCodes.h @@ -510,7 +510,8 @@ ATTR_KIND_SWIFT_ERROR = 47, ATTR_KIND_NO_RECURSE = 48, ATTR_KIND_INACCESSIBLEMEM_ONLY = 49, - ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEMONLY = 50 + ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEMONLY = 50, + ATTR_KIND_ALLOC_SIZE = 51 }; enum ComdatSelectionKindCodes { Index: include/llvm/IR/Attributes.h =================================================================== --- include/llvm/IR/Attributes.h +++ include/llvm/IR/Attributes.h @@ -18,6 +18,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/Optional.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include @@ -94,6 +95,9 @@ uint64_t Bytes); static Attribute getWithDereferenceableOrNullBytes(LLVMContext &Context, uint64_t Bytes); + static Attribute getWithAllocSizeArgs(LLVMContext &Context, + unsigned ElemSizeArg, + const Optional &NumElemsArg); //===--------------------------------------------------------------------===// // Attribute Accessors @@ -147,6 +151,10 @@ /// dereferenceable_or_null attribute. uint64_t getDereferenceableOrNullBytes() const; + /// Returns the argument numbers for the allocsize attribute (or pair(0, 0) + /// if not known). + std::pair> getAllocSizeArgs() const; + /// \brief The Attribute is converted to a string of equivalent mnemonic. This /// is, presumably, for writing out the mnemonics for the assembly writer. std::string getAsString(bool InAttrGrp = false) const; @@ -267,6 +275,12 @@ AttributeSet addDereferenceableOrNullAttr(LLVMContext &C, unsigned Index, uint64_t Bytes) const; + /// \brief Add the allocsize attribute to the attribute set at the given + /// index. Because attribute sets are immutable, this returns a new set. + AttributeSet addAllocSizeAttr(LLVMContext &C, unsigned Index, + unsigned ElemSizeArg, + const Optional &NumElemsArg); + //===--------------------------------------------------------------------===// // AttributeSet Accessors //===--------------------------------------------------------------------===// @@ -319,6 +333,10 @@ /// unknown). uint64_t getDereferenceableOrNullBytes(unsigned Index) const; + /// \brief Get the allocsize argument numbers (or pair(0, 0) if unknown). + std::pair> + getAllocSizeArgs(unsigned Index) const; + /// \brief Return the attributes at the index as a string. std::string getAsString(unsigned Index, bool InAttrGrp = false) const; @@ -400,19 +418,20 @@ uint64_t StackAlignment; uint64_t DerefBytes; uint64_t DerefOrNullBytes; + uint64_t AllocSizeArgs; public: AttrBuilder() : Attrs(0), Alignment(0), StackAlignment(0), DerefBytes(0), - DerefOrNullBytes(0) {} + DerefOrNullBytes(0), AllocSizeArgs(0) {} explicit AttrBuilder(uint64_t Val) : Attrs(0), Alignment(0), StackAlignment(0), DerefBytes(0), - DerefOrNullBytes(0) { + DerefOrNullBytes(0), AllocSizeArgs(0) { addRawValue(Val); } AttrBuilder(const Attribute &A) : Attrs(0), Alignment(0), StackAlignment(0), DerefBytes(0), - DerefOrNullBytes(0) { + DerefOrNullBytes(0), AllocSizeArgs(0) { addAttribute(A); } AttrBuilder(AttributeSet AS, unsigned Idx); @@ -481,6 +500,10 @@ /// dereferenceable_or_null attribute exists (zero is returned otherwise). uint64_t getDereferenceableOrNullBytes() const { return DerefOrNullBytes; } + /// \brief Retrieve the allocsize args, if the allocsize attribute exists. + /// If it doesn't exist, pair(0, 0) is returned. + std::pair> getAllocSizeArgs() const; + /// \brief This turns an int alignment (which must be a power of 2) into the /// form used internally in Attribute. AttrBuilder &addAlignmentAttr(unsigned Align); @@ -497,6 +520,15 @@ /// form used internally in Attribute. AttrBuilder &addDereferenceableOrNullAttr(uint64_t Bytes); + /// \brief This turns one (or two) ints into the form used internally in + /// Attribute. + AttrBuilder &addAllocSizeAttr(unsigned ElemSizeArg, + const Optional &NumElemsArg); + + /// \brief Add an allocsize attribute, using the representation returned by + /// Attribute.getIntValue(). + AttrBuilder &addAllocSizeAttrFromRawRepr(uint64_t RawAllocSizeRepr); + /// \brief Return true if the builder contains no target-independent /// attributes. bool empty() const { return Attrs.none(); } Index: include/llvm/IR/Attributes.td =================================================================== --- include/llvm/IR/Attributes.td +++ include/llvm/IR/Attributes.td @@ -16,6 +16,10 @@ /// 0 means unaligned (different from align(1)). def Alignment : EnumAttr<"align">; +/// The result of the function is guaranteed to point to a number of bytes that +/// we can determine if we know the value of the function's arguments. +def AllocSize : EnumAttr<"allocsize">; + /// inline=always. def AlwaysInline : EnumAttr<"alwaysinline">; Index: lib/AsmParser/LLLexer.cpp =================================================================== --- lib/AsmParser/LLLexer.cpp +++ lib/AsmParser/LLLexer.cpp @@ -606,6 +606,7 @@ KEYWORD(attributes); KEYWORD(alwaysinline); + KEYWORD(allocsize); KEYWORD(argmemonly); KEYWORD(builtin); KEYWORD(byval); Index: lib/AsmParser/LLParser.h =================================================================== --- lib/AsmParser/LLParser.h +++ lib/AsmParser/LLParser.h @@ -16,6 +16,7 @@ #include "LLLexer.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringMap.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/Instructions.h" @@ -247,7 +248,10 @@ bool ParseOptionalStackAlignment(unsigned &Alignment); bool ParseOptionalCommaAlign(unsigned &Alignment, bool &AteExtraComma); bool ParseOptionalCommaInAlloca(bool &IsInAlloca); - bool ParseIndexList(SmallVectorImpl &Indices,bool &AteExtraComma); + bool parseAllocSizeArguments(unsigned &ElemSizeArg, + Optional &HowManyArg); + bool ParseIndexList(SmallVectorImpl &Indices, + bool &AteExtraComma); bool ParseIndexList(SmallVectorImpl &Indices) { bool AteExtraComma; if (ParseIndexList(Indices, AteExtraComma)) return true; Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -14,6 +14,7 @@ #include "LLParser.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/AsmParser/SlotMapping.h" #include "llvm/IR/AutoUpgrade.h" #include "llvm/IR/CallingConv.h" @@ -1047,6 +1048,15 @@ B.addStackAlignmentAttr(Alignment); continue; } + case lltok::kw_allocsize: { + unsigned ElemSizeArg; + Optional NumElemsArg; + // inAttrGrp doesn't matter; we only support allocsize(a[, b]) + if (parseAllocSizeArguments(ElemSizeArg, NumElemsArg)) + return true; + B.addAllocSizeAttr(ElemSizeArg, NumElemsArg); + continue; + } case lltok::kw_alwaysinline: B.addAttribute(Attribute::AlwaysInline); break; case lltok::kw_argmemonly: B.addAttribute(Attribute::ArgMemOnly); break; case lltok::kw_builtin: B.addAttribute(Attribute::Builtin); break; @@ -1776,6 +1786,35 @@ return false; } +bool LLParser::parseAllocSizeArguments(unsigned &BaseSizeArg, + Optional &HowManyArg) { + Lex.Lex(); + + auto StartParen = Lex.getLoc(); + if (!EatIfPresent(lltok::lparen)) + return Error(StartParen, "expected '('"); + + if (ParseUInt32(BaseSizeArg)) + return true; + + if (EatIfPresent(lltok::comma)) { + auto HowManyAt = Lex.getLoc(); + unsigned HowMany; + if (ParseUInt32(HowMany)) + return true; + if (HowMany == BaseSizeArg) + return Error(HowManyAt, + "'allocsize' indices can't refer to the same parameter"); + HowManyArg = HowMany; + } else + HowManyArg = None; + + auto EndParen = Lex.getLoc(); + if (!EatIfPresent(lltok::rparen)) + return Error(EndParen, "expected ')'"); + return false; +} + /// ParseScopeAndOrdering /// if isAtomic: ::= 'singlethread'? AtomicOrdering /// else: ::= Index: lib/AsmParser/LLToken.h =================================================================== --- lib/AsmParser/LLToken.h +++ lib/AsmParser/LLToken.h @@ -109,6 +109,7 @@ // Attributes: kw_attributes, + kw_allocsize, kw_alwaysinline, kw_argmemonly, kw_sanitize_address, Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -1288,6 +1288,8 @@ return Attribute::Dereferenceable; case bitc::ATTR_KIND_DEREFERENCEABLE_OR_NULL: return Attribute::DereferenceableOrNull; + case bitc::ATTR_KIND_ALLOC_SIZE: + return Attribute::AllocSize; case bitc::ATTR_KIND_NO_RED_ZONE: return Attribute::NoRedZone; case bitc::ATTR_KIND_NO_RETURN: @@ -1412,6 +1414,8 @@ B.addDereferenceableAttr(Record[++i]); else if (Kind == Attribute::DereferenceableOrNull) B.addDereferenceableOrNullAttr(Record[++i]); + else if (Kind == Attribute::AllocSize) + B.addAllocSizeAttrFromRawRepr(Record[++i]); } else { // String attribute assert((Record[i] == 3 || Record[i] == 4) && "Invalid attribute group entry"); Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -171,6 +171,8 @@ switch (Kind) { case Attribute::Alignment: return bitc::ATTR_KIND_ALIGNMENT; + case Attribute::AllocSize: + return bitc::ATTR_KIND_ALLOC_SIZE; case Attribute::AlwaysInline: return bitc::ATTR_KIND_ALWAYS_INLINE; case Attribute::ArgMemOnly: Index: lib/IR/AttributeImpl.h =================================================================== --- lib/IR/AttributeImpl.h +++ lib/IR/AttributeImpl.h @@ -17,6 +17,7 @@ #define LLVM_LIB_IR_ATTRIBUTEIMPL_H #include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/Optional.h" #include "llvm/IR/Attributes.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/TrailingObjects.h" @@ -120,7 +121,8 @@ : EnumAttributeImpl(IntAttrEntry, Kind), Val(Val) { assert((Kind == Attribute::Alignment || Kind == Attribute::StackAlignment || Kind == Attribute::Dereferenceable || - Kind == Attribute::DereferenceableOrNull) && + Kind == Attribute::DereferenceableOrNull || + Kind == Attribute::AllocSize) && "Wrong kind for int attribute!"); } @@ -188,6 +190,7 @@ unsigned getStackAlignment() const; uint64_t getDereferenceableBytes() const; uint64_t getDereferenceableOrNullBytes() const; + std::pair> getAllocSizeArgs() const; std::string getAsString(bool InAttrGrp) const; typedef const Attribute *iterator; Index: lib/IR/Attributes.cpp =================================================================== --- lib/IR/Attributes.cpp +++ lib/IR/Attributes.cpp @@ -32,6 +32,36 @@ // Attribute Construction Methods //===----------------------------------------------------------------------===// +// allocsize has two integer arguments, but because they're both 32 bits, we can +// pack them into one 64-bit value, at the cost of making said value +// nonsensical. +// +// In order to do this, we need to reserve one value of the second (optional) +// allocsize argument to signify "not present." +LLVM_CONSTEXPR static unsigned AllocSizeNumElemsNotPresent = + std::numeric_limits::max(); + +static uint64_t packAllocSizeArgs(unsigned ElemSizeArg, + const Optional &NumElemsArg) { + assert((!NumElemsArg.hasValue() || + *NumElemsArg != AllocSizeNumElemsNotPresent) && + "Attempting to pack a reserved value"); + + return uint64_t(ElemSizeArg) << 32 | + NumElemsArg.getValueOr(AllocSizeNumElemsNotPresent); +} + +static std::pair> +unpackAllocSizeArgs(uint64_t Num) { + unsigned NumElems = Num & std::numeric_limits::max(); + unsigned ElemSizeArg = Num >> 32; + + Optional NumElemsArg; + if (NumElems != AllocSizeNumElemsNotPresent) + NumElemsArg = NumElems; + return std::make_pair(ElemSizeArg, NumElemsArg); +} + Attribute Attribute::get(LLVMContext &Context, Attribute::AttrKind Kind, uint64_t Val) { LLVMContextImpl *pImpl = Context.pImpl; @@ -101,6 +131,14 @@ return get(Context, DereferenceableOrNull, Bytes); } +Attribute +Attribute::getWithAllocSizeArgs(LLVMContext &Context, unsigned ElemSizeArg, + const Optional &NumElemsArg) { + assert(!(ElemSizeArg == 0 && NumElemsArg && *NumElemsArg == 0) && + "Invalid allocsize arguments -- given allocsize(0, 0)"); + return get(Context, AllocSize, packAllocSizeArgs(ElemSizeArg, NumElemsArg)); +} + //===----------------------------------------------------------------------===// // Attribute Accessor Methods //===----------------------------------------------------------------------===// @@ -180,6 +218,12 @@ return pImpl->getValueAsInt(); } +std::pair> Attribute::getAllocSizeArgs() const { + assert(hasAttribute(Attribute::AllocSize) && + "Trying to get allocsize args from non-allocsize attribute"); + return unpackAllocSizeArgs(pImpl->getValueAsInt()); +} + std::string Attribute::getAsString(bool InAttrGrp) const { if (!pImpl) return ""; @@ -312,6 +356,21 @@ if (hasAttribute(Attribute::DereferenceableOrNull)) return AttrWithBytesToString("dereferenceable_or_null"); + if (hasAttribute(Attribute::AllocSize)) { + unsigned ElemSize; + Optional NumElems; + std::tie(ElemSize, NumElems) = getAllocSizeArgs(); + + std::string Result = "allocsize("; + Result += utostr(ElemSize); + if (NumElems.hasValue()) { + Result += ','; + Result += utostr(*NumElems); + } + Result += ')'; + return Result; + } + // Convert target-dependent attributes to strings of the form: // // "kind" @@ -468,6 +527,9 @@ case Attribute::ArgMemOnly: llvm_unreachable("argmemonly attribute not supported in raw format"); break; + case Attribute::AllocSize: + llvm_unreachable("allocsize not supported in raw format"); + break; } llvm_unreachable("Unsupported attribute type"); } @@ -559,6 +621,14 @@ return 0; } +std::pair> +AttributeSetNode::getAllocSizeArgs() const { + for (iterator I = begin(), E = end(); I != E; ++I) + if (I->hasAttribute(Attribute::AllocSize)) + return I->getAllocSizeArgs(); + return std::make_pair(0, 0); +} + std::string AttributeSetNode::getAsString(bool InAttrGrp) const { std::string Str; for (iterator I = begin(), E = end(); I != E; ++I) { @@ -594,6 +664,8 @@ Mask |= (Log2_32(ASN->getStackAlignment()) + 1) << 26; else if (Kind == Attribute::Dereferenceable) llvm_unreachable("dereferenceable not supported in bit mask"); + else if (Kind == Attribute::AllocSize) + llvm_unreachable("allocsize not supported in bit mask"); else Mask |= AttributeImpl::getAttrMask(Kind); } @@ -709,6 +781,11 @@ Attr = Attribute::getWithDereferenceableOrNullBytes( C, B.getDereferenceableOrNullBytes()); break; + case Attribute::AllocSize: { + auto A = B.getAllocSizeArgs(); + Attr = Attribute::getWithAllocSizeArgs(C, A.first, A.second); + break; + } default: Attr = Attribute::get(C, Kind); } @@ -960,6 +1037,15 @@ return addAttributes(C, Index, AttributeSet::get(C, Index, B)); } +AttributeSet +AttributeSet::addAllocSizeAttr(LLVMContext &C, unsigned Index, + unsigned ElemSizeArg, + const Optional &NumElemsArg) { + llvm::AttrBuilder B; + B.addAllocSizeAttr(ElemSizeArg, NumElemsArg); + return addAttributes(C, Index, AttributeSet::get(C, Index, B)); +} + //===----------------------------------------------------------------------===// // AttributeSet Accessor Methods //===----------------------------------------------------------------------===// @@ -1057,8 +1143,13 @@ return ASN ? ASN->getDereferenceableOrNullBytes() : 0; } -std::string AttributeSet::getAsString(unsigned Index, - bool InAttrGrp) const { +std::pair> +AttributeSet::getAllocSizeArgs(unsigned Index) const { + AttributeSetNode *ASN = getAttributes(Index); + return ASN ? ASN->getAllocSizeArgs() : std::make_pair(0, 0); +} + +std::string AttributeSet::getAsString(unsigned Index, bool InAttrGrp) const { AttributeSetNode *ASN = getAttributes(Index); return ASN ? ASN->getAsString(InAttrGrp) : std::string(""); } @@ -1133,7 +1224,7 @@ AttrBuilder::AttrBuilder(AttributeSet AS, unsigned Index) : Attrs(0), Alignment(0), StackAlignment(0), DerefBytes(0), - DerefOrNullBytes(0) { + DerefOrNullBytes(0), AllocSizeArgs(0) { AttributeSetImpl *pImpl = AS.pImpl; if (!pImpl) return; @@ -1152,12 +1243,13 @@ Attrs.reset(); TargetDepAttrs.clear(); Alignment = StackAlignment = DerefBytes = DerefOrNullBytes = 0; + AllocSizeArgs = 0; } AttrBuilder &AttrBuilder::addAttribute(Attribute::AttrKind Val) { assert((unsigned)Val < Attribute::EndAttrKinds && "Attribute out of range!"); assert(Val != Attribute::Alignment && Val != Attribute::StackAlignment && - Val != Attribute::Dereferenceable && + Val != Attribute::Dereferenceable && Val != Attribute::AllocSize && "Adding integer attribute without adding a value!"); Attrs[Val] = true; return *this; @@ -1180,6 +1272,8 @@ DerefBytes = Attr.getDereferenceableBytes(); else if (Kind == Attribute::DereferenceableOrNull) DerefOrNullBytes = Attr.getDereferenceableOrNullBytes(); + else if (Kind == Attribute::AllocSize) + AllocSizeArgs = Attr.getValueAsInt(); return *this; } @@ -1200,6 +1294,8 @@ DerefBytes = 0; else if (Val == Attribute::DereferenceableOrNull) DerefOrNullBytes = 0; + else if (Val == Attribute::AllocSize) + AllocSizeArgs = 0; return *this; } @@ -1234,6 +1330,10 @@ return *this; } +std::pair> AttrBuilder::getAllocSizeArgs() const { + return unpackAllocSizeArgs(AllocSizeArgs); +} + AttrBuilder &AttrBuilder::addAlignmentAttr(unsigned Align) { if (Align == 0) return *this; @@ -1274,6 +1374,22 @@ return *this; } +AttrBuilder &AttrBuilder::addAllocSizeAttr(unsigned ElemSize, + const Optional &NumElems) { + return addAllocSizeAttrFromRawRepr(packAllocSizeArgs(ElemSize, NumElems)); +} + +AttrBuilder &AttrBuilder::addAllocSizeAttrFromRawRepr(uint64_t RawArgs) { + // (0, 0) is our "not present" value, so we need to check for it here. + assert(RawArgs && "Invalid allocsize arguments -- given allocsize(0, 0)"); + + Attrs[Attribute::AllocSize] = true; + // Reuse existing machinery to store this as a single 64-bit integer so we can + // save a few bytes over using a pair>. + AllocSizeArgs = RawArgs; + return *this; +} + AttrBuilder &AttrBuilder::merge(const AttrBuilder &B) { // FIXME: What if both have alignments, but they don't match?! if (!Alignment) @@ -1288,6 +1404,9 @@ if (!DerefOrNullBytes) DerefOrNullBytes = B.DerefOrNullBytes; + if (!AllocSizeArgs) + AllocSizeArgs = B.AllocSizeArgs; + Attrs |= B.Attrs; for (auto I : B.td_attrs()) @@ -1310,6 +1429,9 @@ if (B.DerefOrNullBytes) DerefOrNullBytes = 0; + if (B.AllocSizeArgs) + AllocSizeArgs = 0; + Attrs &= ~B.Attrs; for (auto I : B.td_attrs()) @@ -1388,7 +1510,8 @@ I = Attribute::AttrKind(I + 1)) { if (I == Attribute::Dereferenceable || I == Attribute::DereferenceableOrNull || - I == Attribute::ArgMemOnly) + I == Attribute::ArgMemOnly || + I == Attribute::AllocSize) continue; if (uint64_t A = (Val & AttributeImpl::getAttrMask(I))) { Attrs[I] = true; Index: lib/IR/Verifier.cpp =================================================================== --- lib/IR/Verifier.cpp +++ lib/IR/Verifier.cpp @@ -1313,7 +1313,8 @@ I->getKindAsEnum() == Attribute::ArgMemOnly || I->getKindAsEnum() == Attribute::NoRecurse || I->getKindAsEnum() == Attribute::InaccessibleMemOnly || - I->getKindAsEnum() == Attribute::InaccessibleMemOrArgMemOnly) { + I->getKindAsEnum() == Attribute::InaccessibleMemOrArgMemOnly || + I->getKindAsEnum() == Attribute::AllocSize) { if (!isFunction) { CheckFailed("Attribute '" + I->getAsString() + "' only applies to functions!", V); @@ -1545,6 +1546,33 @@ Assert(GV->hasUnnamedAddr(), "Attribute 'jumptable' requires 'unnamed_addr'", V); } + + if (Attrs.hasAttribute(AttributeSet::FunctionIndex, Attribute::AllocSize)) { + std::pair> Args = + Attrs.getAllocSizeArgs(AttributeSet::FunctionIndex); + + auto CheckParam = [&](StringRef Name, unsigned ParamNo) { + if (ParamNo >= FT->getNumParams()) { + CheckFailed("'allocsize' " + Name + " argument is out of bounds", V); + return false; + } + + if (!FT->getParamType(ParamNo)->isIntegerTy()) { + CheckFailed("'allocsize' " + Name + + " argument must refer to an integer parameter", + V); + return false; + } + + return true; + }; + + if (!CheckParam("element size", Args.first)) + return; + + if (Args.second && !CheckParam("number of elements", *Args.second)) + return; + } } void Verifier::verifyFunctionMetadata( Index: lib/Transforms/InstCombine/InstCombineCalls.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineCalls.cpp +++ lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -996,8 +996,13 @@ default: break; case Intrinsic::objectsize: { uint64_t Size; - if (getObjectSize(II->getArgOperand(0), Size, DL, TLI)) - return replaceInstUsesWith(CI, ConstantInt::get(CI.getType(), Size)); + if (getObjectSize(II->getArgOperand(0), Size, DL, TLI)) { + APInt APSize(II->getType()->getIntegerBitWidth(), Size); + // Equality check to be sure that `Size` can fit in a value of type + // `II->getType()` + if (APSize == Size) + return replaceInstUsesWith(CI, ConstantInt::get(II->getType(), APSize)); + } return nullptr; } case Intrinsic::bswap: { Index: test/Bitcode/attributes.ll =================================================================== --- test/Bitcode/attributes.ll +++ test/Bitcode/attributes.ll @@ -204,7 +204,7 @@ ; CHECK: define void @f34() { call void @nobuiltin() nobuiltin -; CHECK: call void @nobuiltin() #30 +; CHECK: call void @nobuiltin() #32 ret void; } @@ -318,6 +318,16 @@ ret float 1.0 } +; CHECK: define i8* @f54(i32) #30 +define i8* @f54(i32) allocsize(0) { + ret i8* null +} + +; CHECK: define i8* @f55(i32, i32) #31 +define i8* @f55(i32, i32) allocsize(0, 1) { + ret i8* null +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } @@ -348,4 +358,6 @@ ; CHECK: attributes #27 = { norecurse } ; CHECK: attributes #28 = { inaccessiblememonly } ; CHECK: attributes #29 = { inaccessiblemem_or_argmemonly } -; CHECK: attributes #30 = { nobuiltin } +; CHECK: attributes #30 = { allocsize(0) } +; CHECK: attributes #31 = { allocsize(0,1) } +; CHECK: attributes #32 = { nobuiltin } Index: test/Verifier/alloc-size-failedparse.ll =================================================================== --- /dev/null +++ test/Verifier/alloc-size-failedparse.ll @@ -0,0 +1,7 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s +; +; We handle allocsize with identical args in the parser, rather than the +; verifier. So, a seperate test is needed. + +; CHECK: 'allocsize' indices can't refer to the same parameter +declare i8* @a(i32, i32) allocsize(0, 0) Index: test/Verifier/allocsize.ll =================================================================== --- /dev/null +++ test/Verifier/allocsize.ll @@ -0,0 +1,16 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s + +; CHECK: 'allocsize' element size argument is out of bounds +declare i8* @a(i32) allocsize(1) + +; CHECK: 'allocsize' element size argument must refer to an integer parameter +declare i8* @b(i32*) allocsize(0) + +; CHECK: 'allocsize' number of elements argument is out of bounds +declare i8* @c(i32) allocsize(0, 1) + +; CHECK: 'allocsize' number of elements argument must refer to an integer parameter +declare i8* @d(i32, i32*) allocsize(0, 1) + +; CHECK: 'allocsize' number of elements argument is out of bounds +declare i8* @e(i32, i32) allocsize(1, 2)