diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1245,6 +1245,10 @@ ``dereferenceable()``). This attribute may only be applied to pointer typed parameters. +``maxobjsize()`` + This indicates that the parameter or return pointer is a pointer to an object + that has at most ``n`` bytes. + ``swiftself`` This indicates that the parameter is the self/context parameter. This is not a valid attribute for return values and can only be applied to one diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -650,6 +650,7 @@ ATTR_KIND_NULL_POINTER_IS_VALID = 67, ATTR_KIND_NOUNDEF = 68, ATTR_KIND_BYREF = 69, + ATTR_KIND_MAX_OBJ_SIZE = 70 }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/IR/Argument.h b/llvm/include/llvm/IR/Argument.h --- a/llvm/include/llvm/IR/Argument.h +++ b/llvm/include/llvm/IR/Argument.h @@ -54,6 +54,10 @@ /// addrspace(0). bool hasNonNullAttr() const; + /// If this argument has the maxobjsize attribute, return the number of bytes + /// known to be maximal for the pointee. Otherwise, 0 is returned. + uint64_t getMaxObjSizeBytes() const; + /// If this argument has the dereferenceable attribute, return the number of /// bytes known to be dereferenceable. Otherwise, zero is returned. uint64_t getDereferenceableBytes() const; diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -100,6 +100,7 @@ /// alignment set. static Attribute getWithAlignment(LLVMContext &Context, Align Alignment); static Attribute getWithStackAlignment(LLVMContext &Context, Align Alignment); + static Attribute getWithMaxObjSizeBytes(LLVMContext &Context, uint64_t Bytes); static Attribute getWithDereferenceableBytes(LLVMContext &Context, uint64_t Bytes); static Attribute getWithDereferenceableOrNullBytes(LLVMContext &Context, @@ -176,6 +177,9 @@ /// alignment value. MaybeAlign getStackAlignment() const; + /// Returns the maximum number of bytes of the pointee. + uint64_t getMaxObjSizeBytes() const; + /// Returns the number of dereferenceable bytes from the /// dereferenceable attribute. uint64_t getDereferenceableBytes() const; @@ -304,6 +308,7 @@ MaybeAlign getAlignment() const; MaybeAlign getStackAlignment() const; + uint64_t getMaxObjSizeBytes() const; uint64_t getDereferenceableBytes() const; uint64_t getDereferenceableOrNullBytes() const; Type *getByValType() const; @@ -508,6 +513,11 @@ return removeAttributes(C, ArgNo + FirstArgIndex); } + /// \brief Add the maxobjsize attribute to the attribute set at the given + /// index. Returns a new list because attribute lists are immutable. + LLVM_NODISCARD AttributeList addMaxObjSizeAttr(LLVMContext &C, unsigned Index, + uint64_t Bytes) const; + /// \brief Add the dereferenceable attribute to the attribute set at the given /// index. Returns a new list because attribute lists are immutable. LLVM_NODISCARD AttributeList addDereferenceableAttr(LLVMContext &C, @@ -640,6 +650,14 @@ /// Get the stack alignment. MaybeAlign getStackAlignment(unsigned Index) const; + /// Get the maximal number of bytes for the underlying object. + uint64_t getMaxObjSizeBytes(unsigned Index) const; + + /// Get the number of maxobjsize bytes (or 0 if unknown) of an arg. + uint64_t getParamMaxObjSizeBytes(unsigned ArgNo) const { + return getMaxObjSizeBytes(ArgNo + FirstArgIndex); + } + /// Get the number of dereferenceable bytes (or zero if unknown). uint64_t getDereferenceableBytes(unsigned Index) const; @@ -733,6 +751,7 @@ std::map> TargetDepAttrs; MaybeAlign Alignment; MaybeAlign StackAlignment; + uint64_t MaxObjSizeBytes = 0; uint64_t DerefBytes = 0; uint64_t DerefOrNullBytes = 0; uint64_t AllocSizeArgs = 0; @@ -813,6 +832,9 @@ /// Retrieve the stack alignment attribute, if it exists. MaybeAlign getStackAlignment() const { return StackAlignment; } + /// Retreive the number of bytes the underlying object can have. + uint64_t getMaxObjSizeBytes() const { return MaxObjSizeBytes; } + /// Retrieve the number of dereferenceable bytes, if the /// dereferenceable attribute exists (zero is returned otherwise). uint64_t getDereferenceableBytes() const { return DerefBytes; } @@ -858,6 +880,10 @@ return addStackAlignmentAttr(MaybeAlign(Align)); } + /// This turns the number of max object size bytes into the form used + /// internally in Attribute. + AttrBuilder &addMaxObjSizeAttr(uint64_t Bytes); + /// This turns the number of dereferenceable bytes into the form used /// internally in Attribute. AttrBuilder &addDereferenceableAttr(uint64_t Bytes); diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -51,6 +51,9 @@ /// Can only be moved to control-equivalent blocks. def Convergent : EnumAttr<"convergent">; +/// Pointee is known to have a maximal size. +def MaxObjSize : IntAttr<"maxobjsize">; + /// Pointer is known to be dereferenceable. def Dereferenceable : IntAttr<"dereferenceable">; diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h --- a/llvm/include/llvm/IR/Function.h +++ b/llvm/include/llvm/IR/Function.h @@ -483,6 +483,12 @@ return AttributeSets.getParamByRefType(ArgNo); } + /// Extract the number of maxobjsize bytes for a parameter. + /// @param ArgNo Index of an argument, with 0 being the first function arg. + uint64_t getParamMaxObjSizeBytes(unsigned ArgNo) const { + return AttributeSets.getParamMaxObjSizeBytes(ArgNo); + } + /// Extract the number of dereferenceable bytes for a call or /// parameter (0=unknown). /// @param i AttributeList index, referring to a return value or argument. diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -1637,6 +1637,12 @@ return Ty ? Ty : getArgOperand(ArgNo)->getType()->getPointerElementType(); } + /// Extract the number of maxobjsize bytes for a call or parameter + /// (0=unknown) + uint64_t getMaxObjSizeBytes(unsigned ArgNo) const { + return Attrs.getMaxObjSizeBytes(ArgNo); + } + /// Extract the number of dereferenceable bytes for a call or /// parameter (0=unknown). uint64_t getDereferenceableBytes(unsigned i) const { diff --git a/llvm/include/llvm/IR/Value.h b/llvm/include/llvm/IR/Value.h --- a/llvm/include/llvm/IR/Value.h +++ b/llvm/include/llvm/IR/Value.h @@ -670,6 +670,10 @@ static_cast(this)->stripInBoundsOffsets(Func)); } + /// Returns the number of bytes known to be the maximal extend for the + /// pointer value. + uint64_t getPointerMaxObjSizeBytes(const DataLayout &DL) const; + /// Returns the number of bytes known to be dereferenceable for the /// pointer value. /// diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -639,6 +639,7 @@ KEYWORD(inalloca); KEYWORD(cold); KEYWORD(convergent); + KEYWORD(maxobjsize); KEYWORD(dereferenceable); KEYWORD(dereferenceable_or_null); KEYWORD(inaccessiblememonly); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1376,6 +1376,7 @@ "invalid use of attribute on a function"); break; case lltok::kw_byval: + case lltok::kw_maxobjsize: case lltok::kw_dereferenceable: case lltok::kw_dereferenceable_or_null: case lltok::kw_inalloca: @@ -1669,6 +1670,13 @@ B.addPreallocatedAttr(Ty); continue; } + case lltok::kw_maxobjsize: { + uint64_t Bytes; + if (ParseOptionalDerefAttrBytes(lltok::kw_maxobjsize, Bytes)) + return true; + B.addMaxObjSizeAttr(Bytes); + continue; + } case lltok::kw_dereferenceable: { uint64_t Bytes; if (ParseOptionalDerefAttrBytes(lltok::kw_dereferenceable, Bytes)) @@ -1770,6 +1778,13 @@ return true; continue; } + case lltok::kw_maxobjsize: { + uint64_t Bytes; + if (ParseOptionalDerefAttrBytes(lltok::kw_maxobjsize, Bytes)) + return true; + B.addMaxObjSizeAttr(Bytes); + continue; + } case lltok::kw_dereferenceable: { uint64_t Bytes; if (ParseOptionalDerefAttrBytes(lltok::kw_dereferenceable, Bytes)) @@ -2186,7 +2201,8 @@ bool LLParser::ParseOptionalDerefAttrBytes(lltok::Kind AttrKind, uint64_t &Bytes) { assert((AttrKind == lltok::kw_dereferenceable || - AttrKind == lltok::kw_dereferenceable_or_null) && + AttrKind == lltok::kw_dereferenceable_or_null || + AttrKind == lltok::kw_maxobjsize) && "contract!"); Bytes = 0; diff --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -185,6 +185,7 @@ kw_inalloca, kw_cold, kw_convergent, + kw_maxobjsize, kw_dereferenceable, kw_dereferenceable_or_null, kw_inaccessiblememonly, diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1453,6 +1453,8 @@ return Attribute::NonLazyBind; case bitc::ATTR_KIND_NON_NULL: return Attribute::NonNull; + case bitc::ATTR_KIND_MAX_OBJ_SIZE: + return Attribute::MaxObjSize; case bitc::ATTR_KIND_DEREFERENCEABLE: return Attribute::Dereferenceable; case bitc::ATTR_KIND_DEREFERENCEABLE_OR_NULL: @@ -1621,6 +1623,8 @@ B.addAlignmentAttr(Record[++i]); else if (Kind == Attribute::StackAlignment) B.addStackAlignmentAttr(Record[++i]); + else if (Kind == Attribute::MaxObjSize) + B.addMaxObjSizeAttr(Record[++i]); else if (Kind == Attribute::Dereferenceable) B.addDereferenceableAttr(Record[++i]); else if (Kind == Attribute::DereferenceableOrNull) diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -661,6 +661,8 @@ return bitc::ATTR_KIND_NON_LAZY_BIND; case Attribute::NonNull: return bitc::ATTR_KIND_NON_NULL; + case Attribute::MaxObjSize: + return bitc::ATTR_KIND_MAX_OBJ_SIZE; case Attribute::Dereferenceable: return bitc::ATTR_KIND_DEREFERENCEABLE; case Attribute::DereferenceableOrNull: diff --git a/llvm/lib/IR/AttributeImpl.h b/llvm/lib/IR/AttributeImpl.h --- a/llvm/lib/IR/AttributeImpl.h +++ b/llvm/lib/IR/AttributeImpl.h @@ -249,6 +249,7 @@ MaybeAlign getAlignment() const; MaybeAlign getStackAlignment() const; + uint64_t getMaxObjSizeBytes() const; uint64_t getDereferenceableBytes() const; uint64_t getDereferenceableOrNullBytes() const; std::pair> getAllocSizeArgs() const; diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -156,6 +156,12 @@ return get(Context, StackAlignment, A.value()); } +Attribute Attribute::getWithMaxObjSizeBytes(LLVMContext &Context, + uint64_t Bytes) { + assert(Bytes != uint64_t(0) && "Bytes must be != 0"); + return get(Context, MaxObjSize, Bytes); +} + Attribute Attribute::getWithDereferenceableBytes(LLVMContext &Context, uint64_t Bytes) { assert(Bytes && "Bytes must be non-zero."); @@ -214,6 +220,7 @@ bool Attribute::doesAttrKindHaveArgument(Attribute::AttrKind AttrKind) { return AttrKind == Attribute::Alignment || AttrKind == Attribute::StackAlignment || + AttrKind == Attribute::MaxObjSize || AttrKind == Attribute::Dereferenceable || AttrKind == Attribute::AllocSize || AttrKind == Attribute::DereferenceableOrNull; @@ -304,6 +311,13 @@ return MaybeAlign(pImpl->getValueAsInt()); } +uint64_t Attribute::getMaxObjSizeBytes() const { + assert(hasAttribute(Attribute::MaxObjSize) && + "Trying to get the maximal object size from " + "non-maxobjsize attribute!"); + return pImpl->getValueAsInt(); +} + uint64_t Attribute::getDereferenceableBytes() const { assert(hasAttribute(Attribute::Dereferenceable) && "Trying to get dereferenceable bytes from " @@ -504,6 +518,9 @@ if (hasAttribute(Attribute::StackAlignment)) return AttrWithBytesToString("alignstack"); + if (hasAttribute(Attribute::MaxObjSize)) + return AttrWithBytesToString("maxobjsize"); + if (hasAttribute(Attribute::Dereferenceable)) return AttrWithBytesToString("dereferenceable"); @@ -738,6 +755,10 @@ return SetNode ? SetNode->getStackAlignment() : None; } +uint64_t AttributeSet::getMaxObjSizeBytes() const { + return SetNode ? SetNode->getMaxObjSizeBytes() : ~uint64_t(0); +} + uint64_t AttributeSet::getDereferenceableBytes() const { return SetNode ? SetNode->getDereferenceableBytes() : 0; } @@ -864,6 +885,10 @@ assert(B.getStackAlignment() && "StackAlignment must be set"); Attr = Attribute::getWithStackAlignment(C, *B.getStackAlignment()); break; + case Attribute::MaxObjSize: + assert(B.getMaxObjSizeBytes() && "MaxObjSize must be set"); + Attr = Attribute::getWithMaxObjSizeBytes(C, B.getMaxObjSizeBytes()); + break; case Attribute::Dereferenceable: Attr = Attribute::getWithDereferenceableBytes( C, B.getDereferenceableBytes()); @@ -951,6 +976,12 @@ return nullptr; } +uint64_t AttributeSetNode::getMaxObjSizeBytes() const { + if (auto A = findEnumAttribute(Attribute::MaxObjSize)) + return A->getMaxObjSizeBytes(); + return ~uint64_t(0); +} + uint64_t AttributeSetNode::getDereferenceableBytes() const { if (auto A = findEnumAttribute(Attribute::Dereferenceable)) return A->getDereferenceableBytes(); @@ -1373,6 +1404,13 @@ return getImpl(C, AttrSets); } +AttributeList AttributeList::addMaxObjSizeAttr(LLVMContext &C, unsigned Index, + uint64_t Bytes) const { + AttrBuilder B; + B.addMaxObjSizeAttr(Bytes); + return addAttributes(C, Index, B); +} + AttributeList AttributeList::addDereferenceableAttr(LLVMContext &C, unsigned Index, uint64_t Bytes) const { @@ -1478,6 +1516,10 @@ return getAttributes(Index).getStackAlignment(); } +uint64_t AttributeList::getMaxObjSizeBytes(unsigned Index) const { + return getAttributes(Index).getMaxObjSizeBytes(); +} + uint64_t AttributeList::getDereferenceableBytes(unsigned Index) const { return getAttributes(Index).getDereferenceableBytes(); } @@ -1552,6 +1594,7 @@ TargetDepAttrs.clear(); Alignment.reset(); StackAlignment.reset(); + MaxObjSizeBytes = 0; DerefBytes = DerefOrNullBytes = 0; AllocSizeArgs = 0; ByValType = nullptr; @@ -1578,6 +1621,8 @@ ByRefType = Attr.getValueAsType(); else if (Kind == Attribute::Preallocated) PreallocatedType = Attr.getValueAsType(); + else if (Kind == Attribute::MaxObjSize) + MaxObjSizeBytes = Attr.getMaxObjSizeBytes(); else if (Kind == Attribute::Dereferenceable) DerefBytes = Attr.getDereferenceableBytes(); else if (Kind == Attribute::DereferenceableOrNull) @@ -1606,6 +1651,8 @@ ByRefType = nullptr; else if (Val == Attribute::Preallocated) PreallocatedType = nullptr; + else if (Val == Attribute::MaxObjSize) + MaxObjSizeBytes = 0; else if (Val == Attribute::Dereferenceable) DerefBytes = 0; else if (Val == Attribute::DereferenceableOrNull) @@ -1655,6 +1702,15 @@ return *this; } +AttrBuilder &AttrBuilder::addMaxObjSizeAttr(uint64_t Bytes) { + if (Bytes == 0U) + return *this; + + Attrs[Attribute::MaxObjSize] = true; + MaxObjSizeBytes = Bytes; + return *this; +} + AttrBuilder &AttrBuilder::addDereferenceableAttr(uint64_t Bytes) { if (Bytes == 0) return *this; @@ -1714,6 +1770,9 @@ if (!StackAlignment) StackAlignment = B.StackAlignment; + if (MaxObjSizeBytes == 0) + MaxObjSizeBytes = B.MaxObjSizeBytes; + if (!DerefBytes) DerefBytes = B.DerefBytes; @@ -1748,6 +1807,9 @@ if (B.StackAlignment) StackAlignment.reset(); + if (B.MaxObjSizeBytes == 0) + MaxObjSizeBytes = 0; + if (B.DerefBytes) DerefBytes = 0; @@ -1826,7 +1888,9 @@ return Alignment == B.Alignment && StackAlignment == B.StackAlignment && DerefBytes == B.DerefBytes && ByValType == B.ByValType && - ByRefType == B.ByRefType && PreallocatedType == B.PreallocatedType; + ByRefType == B.ByRefType && + PreallocatedType == B.PreallocatedType && + MaxObjSizeBytes == B.MaxObjSizeBytes; } //===----------------------------------------------------------------------===// @@ -1849,6 +1913,7 @@ .addAttribute(Attribute::NoCapture) .addAttribute(Attribute::NonNull) .addAlignmentAttr(1) // the int here is ignored + .addMaxObjSizeAttr(1) // the int here is ignored .addDereferenceableAttr(1) // the int here is ignored .addDereferenceableOrNullAttr(1) // the int here is ignored .addAttribute(Attribute::ReadNone) diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -208,6 +208,11 @@ return getParent()->getParamByRefType(getArgNo()); } +uint64_t Argument::getMaxObjSizeBytes() const { + assert(getType()->isPointerTy() && "Only pointers have maxobjsize bytes"); + return getParent()->getParamMaxObjSizeBytes(getArgNo()); +} + uint64_t Argument::getDereferenceableBytes() const { assert(getType()->isPointerTy() && "Only pointers have dereferenceable bytes"); diff --git a/llvm/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp --- a/llvm/lib/IR/Value.cpp +++ b/llvm/lib/IR/Value.cpp @@ -704,6 +704,31 @@ return stripPointerCastsAndOffsets(this, Func); } +uint64_t Value::getPointerMaxObjSizeBytes(const DataLayout &DL) const { + assert(getType()->isPointerTy() && "must be pointer"); + + uint64_t MaxObjSizeBytes = ~0U; + if (const Argument *A = dyn_cast(this)) { + MaxObjSizeBytes = A->getMaxObjSizeBytes(); + if ((A->hasByValAttr() || A->hasStructRetAttr())) { + Type *PT = cast(A->getType())->getElementType(); + if (PT->isSized()) + MaxObjSizeBytes = + std::min(MaxObjSizeBytes, uint64_t(DL.getTypeStoreSize(PT))); + } + } else if (const auto *Call = dyn_cast(this)) { + MaxObjSizeBytes = Call->getMaxObjSizeBytes(AttributeList::ReturnIndex); + } else if (const auto *AI = dyn_cast(this)) { + if (!AI->isArrayAllocation()) { + MaxObjSizeBytes = DL.getTypeStoreSize(AI->getAllocatedType()); + } + } else if (const auto *GV = dyn_cast(this)) { + if (GV->getValueType()->isSized()) + MaxObjSizeBytes = DL.getTypeStoreSize(GV->getValueType()); + } + return MaxObjSizeBytes; +} + uint64_t Value::getPointerDereferenceableBytes(const DataLayout &DL, bool &CanBeNull) const { assert(getType()->isPointerTy() && "must be pointer"); diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -862,6 +862,7 @@ case Attribute::Builtin: case Attribute::ByVal: case Attribute::Convergent: + case Attribute::MaxObjSize: case Attribute::Dereferenceable: case Attribute::DereferenceableOrNull: case Attribute::InAlloca: diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll --- a/llvm/test/Bitcode/attributes.ll +++ b/llvm/test/Bitcode/attributes.ll @@ -398,6 +398,12 @@ ret void } +; CHECK: define maxobjsize(8) i32* @f68(i32* maxobjsize(4) %0) +define maxobjsize(8) i32* @f68(i32* maxobjsize(4)) +{ + ret i32* %0; +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone }