diff --git a/docs/LangRef.rst b/docs/LangRef.rst index 86d8c62af2b..0c1039f60c0 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -1066,6 +1066,30 @@ Currently, only the following parameter attributes are defined: site. If the alignment is not specified, then the code generator makes a target-specific assumption. +.. _attr_byref: + +``byref()`` + + The ``byref`` argument attribute allows specifying the pointee + memory type of an argument. This is similar to ``byval``, but does + not imply a copy is made anywhere, or that the argument is passed + on the stack. This implies the pointer is dereferenceable up to + the storage size of the type. + + It is not generally permissible to introduce a write to an + ``byref`` pointer. The pointer may have any address space and may + be read only. + + This is not a valid attribute for return values. + + The alignment for an ``byref`` parameter can be explicitly + specified by combining it with the ``align`` attribute, similar to + ``byval``. If the alignment is not specified, then the code generator + makes a target-specific assumption. + + This is intended for representing ABI constraints, and is not + intended to be inferred for optimization use. + .. _attr_preallocated: ``preallocated()`` diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index 6293c82e353..42867a89da6 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -59,6 +59,9 @@ Changes to the LLVM IR * ... +* Added the ``byref`` attribute to better represent argument passing + for the `amdgpu_kernel` calling convention. + Changes to building LLVM ------------------------ @@ -88,6 +91,9 @@ Changes to the AMDGPU Target During this release ... +* The new ``byref`` attribute is now the preferred method for + representing aggregate kernel arguments. + Changes to the AVR Target ----------------------------- diff --git a/include/llvm/Bitcode/LLVMBitCodes.h b/include/llvm/Bitcode/LLVMBitCodes.h index de4fe663032..452918cde64 100644 --- a/include/llvm/Bitcode/LLVMBitCodes.h +++ b/include/llvm/Bitcode/LLVMBitCodes.h @@ -644,6 +644,7 @@ enum AttributeKindCodes { ATTR_KIND_NO_MERGE = 66, ATTR_KIND_NULL_POINTER_IS_VALID = 67, ATTR_KIND_NOUNDEF = 68, + ATTR_KIND_BYREF = 69, }; enum ComdatSelectionKindCodes { diff --git a/include/llvm/IR/Argument.h b/include/llvm/IR/Argument.h index 2ca18c61034..2dd7c6cbc8b 100644 --- a/include/llvm/IR/Argument.h +++ b/include/llvm/IR/Argument.h @@ -65,6 +65,9 @@ public: /// Return true if this argument has the byval attribute. bool hasByValAttr() const; + /// Return true if this argument has the byref attribute. + bool hasByRefAttr() const; + /// Return true if this argument has the swiftself attribute. bool hasSwiftSelfAttr() const; @@ -80,6 +83,15 @@ public: /// in-memory ABI size copied to the stack for the call. Otherwise, return 0. uint64_t getPassPointeeByValueCopySize(const DataLayout &DL) const; + /// Return true if this argument has the byval, inalloca, preallocated, or + /// byref attribute. These attributes represent arguments being passed by + /// value (which may or may not involve a stack copy) + bool hasPointeeInMemoryValueAttr() const; + + /// If hasPointeeInMemoryValueAttr returns true, the in-memory ABI type is + /// returned. Otherwise, nullptr. + Type *getPointeeInMemoryValueType() const; + /// If this is a byval or inalloca argument, return its alignment. /// FIXME: Remove this function once transition to Align is over. /// Use getParamAlign() instead. @@ -91,6 +103,9 @@ public: /// If this is a byval argument, return its type. Type *getParamByValType() const; + /// If this is a byref argument, return its type. + Type *getParamByRefType() const; + /// Return true if this argument has the nest attribute. bool hasNestAttr() const; diff --git a/include/llvm/IR/Attributes.h b/include/llvm/IR/Attributes.h index 58365aa2b76..03f2fc123cb 100644 --- a/include/llvm/IR/Attributes.h +++ b/include/llvm/IR/Attributes.h @@ -108,6 +108,7 @@ public: unsigned ElemSizeArg, const Optional &NumElemsArg); static Attribute getWithByValType(LLVMContext &Context, Type *Ty); + static Attribute getWithByRefType(LLVMContext &Context, Type *Ty); static Attribute getWithPreallocatedType(LLVMContext &Context, Type *Ty); static Attribute::AttrKind getAttrKindFromName(StringRef AttrName); @@ -303,6 +304,7 @@ public: uint64_t getDereferenceableBytes() const; uint64_t getDereferenceableOrNullBytes() const; Type *getByValType() const; + Type *getByRefType() const; Type *getPreallocatedType() const; std::pair> getAllocSizeArgs() const; std::string getAsString(bool InAttrGrp = false) const; @@ -626,6 +628,9 @@ public: /// Return the byval type for the specified function parameter. Type *getParamByValType(unsigned ArgNo) const; + /// Return the byref type for the specified function parameter. + Type *getParamByRefType(unsigned ArgNo) const; + /// Return the preallocated type for the specified function parameter. Type *getParamPreallocatedType(unsigned ArgNo) const; @@ -729,6 +734,7 @@ class AttrBuilder { uint64_t DerefOrNullBytes = 0; uint64_t AllocSizeArgs = 0; Type *ByValType = nullptr; + Type *ByRefType = nullptr; Type *PreallocatedType = nullptr; public: @@ -808,6 +814,9 @@ public: /// Retrieve the byval type. Type *getByValType() const { return ByValType; } + /// Retrieve the byref type. + Type *getByRefType() const { return ByRefType; } + /// Retrieve the preallocated type. Type *getPreallocatedType() const { return PreallocatedType; } @@ -854,6 +863,9 @@ public: /// This turns a byval type into the form used internally in Attribute. AttrBuilder &addByValAttr(Type *Ty); + /// This turns a byref type into the form used internally in Attribute. + AttrBuilder &addByRefAttr(Type *Ty); + /// This turns a preallocated type into the form used internally in Attribute. AttrBuilder &addPreallocatedAttr(Type *Ty); diff --git a/include/llvm/IR/Attributes.td b/include/llvm/IR/Attributes.td index 395f9dbfb17..214eb84b6a4 100644 --- a/include/llvm/IR/Attributes.td +++ b/include/llvm/IR/Attributes.td @@ -39,6 +39,9 @@ def Builtin : EnumAttr<"builtin">; /// Pass structure by value. def ByVal : TypeAttr<"byval">; +/// Mark in-memory ABI type. +def ByRef : TypeAttr<"byref">; + /// Parameter or return value may not contain uninitialized or poison bits. def NoUndef : EnumAttr<"noundef">; diff --git a/include/llvm/IR/Function.h b/include/llvm/IR/Function.h index bb4ec13c761..9bbd0cb6b20 100644 --- a/include/llvm/IR/Function.h +++ b/include/llvm/IR/Function.h @@ -467,6 +467,11 @@ public: return Ty ? Ty : (arg_begin() + ArgNo)->getType()->getPointerElementType(); } + /// Extract the byref type for a parameter. + Type *getParamByRefType(unsigned ArgNo) const { + return AttributeSets.getParamByRefType(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/lib/Analysis/MemoryBuiltins.cpp b/lib/Analysis/MemoryBuiltins.cpp index 204f855d28b..402a2bcadf5 100644 --- a/lib/Analysis/MemoryBuiltins.cpp +++ b/lib/Analysis/MemoryBuiltins.cpp @@ -676,13 +676,14 @@ SizeOffsetType ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) { } SizeOffsetType ObjectSizeOffsetVisitor::visitArgument(Argument &A) { + Type *MemoryTy = A.getPointeeInMemoryValueType(); // No interprocedural analysis is done at the moment. - if (!A.hasPassPointeeByValueCopyAttr()) { + if (!MemoryTy) { ++ObjectVisitorArgument; return unknown(); } - PointerType *PT = cast(A.getType()); - APInt Size(IntTyBits, DL.getTypeAllocSize(PT->getElementType())); + + APInt Size(IntTyBits, DL.getTypeAllocSize(MemoryTy)); return std::make_pair(align(Size, A.getParamAlignment()), Zero); } diff --git a/lib/AsmParser/LLLexer.cpp b/lib/AsmParser/LLLexer.cpp index 777ce3abddd..591c6c3be2f 100644 --- a/lib/AsmParser/LLLexer.cpp +++ b/lib/AsmParser/LLLexer.cpp @@ -697,6 +697,7 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(writeonly); KEYWORD(zeroext); KEYWORD(immarg); + KEYWORD(byref); KEYWORD(type); KEYWORD(opaque); diff --git a/lib/AsmParser/LLParser.cpp b/lib/AsmParser/LLParser.cpp index c9f21ee8382..1a1c1d8eec9 100644 --- a/lib/AsmParser/LLParser.cpp +++ b/lib/AsmParser/LLParser.cpp @@ -1382,6 +1382,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B, case lltok::kw_swifterror: case lltok::kw_swiftself: case lltok::kw_immarg: + case lltok::kw_byref: HaveError |= Error(Lex.getLoc(), "invalid use of parameter-only attribute on a function"); @@ -1675,6 +1676,13 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) { B.addDereferenceableOrNullAttr(Bytes); continue; } + case lltok::kw_byref: { + Type *Ty; + if (ParseByRef(Ty)) + return true; + B.addByRefAttr(Ty); + continue; + } case lltok::kw_inalloca: B.addAttribute(Attribute::InAlloca); break; case lltok::kw_inreg: B.addAttribute(Attribute::InReg); break; case lltok::kw_nest: B.addAttribute(Attribute::Nest); break; @@ -1795,6 +1803,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) { case lltok::kw_swifterror: case lltok::kw_swiftself: case lltok::kw_immarg: + case lltok::kw_byref: HaveError |= Error(Lex.getLoc(), "invalid use of parameter-only attribute"); break; @@ -2568,11 +2577,11 @@ bool LLParser::ParseByValWithOptionalType(Type *&Result) { return false; } -/// ParsePreallocated -/// ::= preallocated() -bool LLParser::ParsePreallocated(Type *&Result) { +/// ParseRequiredTypeAttr +/// ::= attrname() +bool LLParser::ParseRequiredTypeAttr(Type *&Result, lltok::Kind AttrName) { Result = nullptr; - if (!EatIfPresent(lltok::kw_preallocated)) + if (!EatIfPresent(AttrName)) return true; if (!EatIfPresent(lltok::lparen)) return Error(Lex.getLoc(), "expected '('"); @@ -2583,6 +2592,18 @@ bool LLParser::ParsePreallocated(Type *&Result) { return false; } +/// ParsePreallocated +/// ::= preallocated() +bool LLParser::ParsePreallocated(Type *&Result) { + return ParseRequiredTypeAttr(Result, lltok::kw_preallocated); +} + +/// ParseByRef +/// ::= byref() +bool LLParser::ParseByRef(Type *&Result) { + return ParseRequiredTypeAttr(Result, lltok::kw_byref); +} + /// ParseOptionalOperandBundles /// ::= /*empty*/ /// ::= '[' OperandBundle [, OperandBundle ]* ']' diff --git a/lib/AsmParser/LLParser.h b/lib/AsmParser/LLParser.h index ebd8655dc35..333b8a35cfc 100644 --- a/lib/AsmParser/LLParser.h +++ b/lib/AsmParser/LLParser.h @@ -333,7 +333,10 @@ namespace llvm { std::vector &FwdRefAttrGrps, bool inAttrGrp, LocTy &BuiltinLoc); bool ParseByValWithOptionalType(Type *&Result); + + bool ParseRequiredTypeAttr(Type *&Result, lltok::Kind AttrName); bool ParsePreallocated(Type *&Result); + bool ParseByRef(Type *&Result); // Module Summary Index Parsing. bool SkipModuleSummaryEntry(); diff --git a/lib/AsmParser/LLToken.h b/lib/AsmParser/LLToken.h index 0fb3bae77dd..8bdeb57999c 100644 --- a/lib/AsmParser/LLToken.h +++ b/lib/AsmParser/LLToken.h @@ -240,6 +240,7 @@ enum Kind { kw_writeonly, kw_zeroext, kw_immarg, + kw_byref, kw_type, kw_opaque, diff --git a/lib/Bitcode/Reader/BitcodeReader.cpp b/lib/Bitcode/Reader/BitcodeReader.cpp index 659e26c2bd2..86b8f5ca5b8 100644 --- a/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1532,6 +1532,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) { return Attribute::Preallocated; case bitc::ATTR_KIND_NOUNDEF: return Attribute::NoUndef; + case bitc::ATTR_KIND_BYREF: + return Attribute::ByRef; } } @@ -1649,6 +1651,8 @@ Error BitcodeReader::parseAttributeGroupBlock() { return Err; if (Kind == Attribute::ByVal) { B.addByValAttr(HasType ? getTypeByID(Record[++i]) : nullptr); + } else if (Kind == Attribute::ByRef) { + B.addByRefAttr(getTypeByID(Record[++i])); } else if (Kind == Attribute::Preallocated) { B.addPreallocatedAttr(getTypeByID(Record[++i])); } diff --git a/lib/Bitcode/Writer/BitcodeWriter.cpp b/lib/Bitcode/Writer/BitcodeWriter.cpp index 9c15a5f9f19..4cab4e06bc6 100644 --- a/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -733,6 +733,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) { return bitc::ATTR_KIND_PREALLOCATED; case Attribute::NoUndef: return bitc::ATTR_KIND_NOUNDEF; + case Attribute::ByRef: + return bitc::ATTR_KIND_BYREF; case Attribute::EndAttrKinds: llvm_unreachable("Can not encode end-attribute kinds marker."); case Attribute::None: diff --git a/lib/IR/AsmWriter.cpp b/lib/IR/AsmWriter.cpp index fd08310316b..83ad4e2b273 100644 --- a/lib/IR/AsmWriter.cpp +++ b/lib/IR/AsmWriter.cpp @@ -4269,11 +4269,14 @@ void AssemblyWriter::writeAttribute(const Attribute &Attr, bool InAttrGroup) { } assert((Attr.hasAttribute(Attribute::ByVal) || + Attr.hasAttribute(Attribute::ByRef) || Attr.hasAttribute(Attribute::Preallocated)) && "unexpected type attr"); if (Attr.hasAttribute(Attribute::ByVal)) { Out << "byval"; + } else if (Attr.hasAttribute(Attribute::ByRef)) { + Out << "byref"; } else { Out << "preallocated"; } diff --git a/lib/IR/AttributeImpl.h b/lib/IR/AttributeImpl.h index 5c334348cde..b013474d927 100644 --- a/lib/IR/AttributeImpl.h +++ b/lib/IR/AttributeImpl.h @@ -251,6 +251,7 @@ public: std::pair> getAllocSizeArgs() const; std::string getAsString(bool InAttrGrp) const; Type *getByValType() const; + Type *getByRefType() const; Type *getPreallocatedType() const; using iterator = const Attribute *; diff --git a/lib/IR/Attributes.cpp b/lib/IR/Attributes.cpp index 8bf4e82357c..43074e8193b 100644 --- a/lib/IR/Attributes.cpp +++ b/lib/IR/Attributes.cpp @@ -172,6 +172,10 @@ Attribute Attribute::getWithByValType(LLVMContext &Context, Type *Ty) { return get(Context, ByVal, Ty); } +Attribute Attribute::getWithByRefType(LLVMContext &Context, Type *Ty) { + return get(Context, ByRef, Ty); +} + Attribute Attribute::getWithPreallocatedType(LLVMContext &Context, Type *Ty) { return get(Context, Preallocated, Ty); } @@ -459,9 +463,9 @@ std::string Attribute::getAsString(bool InAttrGrp) const { return Result; } - if (hasAttribute(Attribute::Preallocated)) { - std::string Result; - Result += "preallocated"; + const bool IsByRef = hasAttribute(Attribute::ByRef); + if (IsByRef || hasAttribute(Attribute::Preallocated)) { + std::string Result = IsByRef ? "byref" : "preallocated"; raw_string_ostream OS(Result); Result += '('; getValueAsType()->print(OS, false, true); @@ -742,6 +746,10 @@ uint64_t AttributeSet::getDereferenceableOrNullBytes() const { return SetNode ? SetNode->getDereferenceableOrNullBytes() : 0; } +Type *AttributeSet::getByRefType() const { + return SetNode ? SetNode->getByRefType() : nullptr; +} + Type *AttributeSet::getByValType() const { return SetNode ? SetNode->getByValType() : nullptr; } @@ -842,6 +850,9 @@ AttributeSetNode *AttributeSetNode::get(LLVMContext &C, const AttrBuilder &B) { case Attribute::ByVal: Attr = Attribute::getWithByValType(C, B.getByValType()); break; + case Attribute::ByRef: + Attr = Attribute::getWithByRefType(C, B.getByRefType()); + break; case Attribute::Preallocated: Attr = Attribute::getWithPreallocatedType(C, B.getPreallocatedType()); break; @@ -928,6 +939,12 @@ Type *AttributeSetNode::getByValType() const { return nullptr; } +Type *AttributeSetNode::getByRefType() const { + if (auto A = findEnumAttribute(Attribute::ByRef)) + return A->getValueAsType(); + return nullptr; +} + Type *AttributeSetNode::getPreallocatedType() const { if (auto A = findEnumAttribute(Attribute::Preallocated)) return A->getValueAsType(); @@ -1449,6 +1466,10 @@ Type *AttributeList::getParamByValType(unsigned Index) const { return getAttributes(Index+FirstArgIndex).getByValType(); } +Type *AttributeList::getParamByRefType(unsigned Index) const { + return getAttributes(Index + FirstArgIndex).getByRefType(); +} + Type *AttributeList::getParamPreallocatedType(unsigned Index) const { return getAttributes(Index + FirstArgIndex).getPreallocatedType(); } @@ -1534,6 +1555,7 @@ void AttrBuilder::clear() { DerefBytes = DerefOrNullBytes = 0; AllocSizeArgs = 0; ByValType = nullptr; + ByRefType = nullptr; PreallocatedType = nullptr; } @@ -1560,6 +1582,8 @@ AttrBuilder &AttrBuilder::addAttribute(Attribute Attr) { StackAlignment = Attr.getStackAlignment(); else if (Kind == Attribute::ByVal) ByValType = Attr.getValueAsType(); + else if (Kind == Attribute::ByRef) + ByRefType = Attr.getValueAsType(); else if (Kind == Attribute::Preallocated) PreallocatedType = Attr.getValueAsType(); else if (Kind == Attribute::Dereferenceable) @@ -1586,6 +1610,8 @@ AttrBuilder &AttrBuilder::removeAttribute(Attribute::AttrKind Val) { StackAlignment.reset(); else if (Val == Attribute::ByVal) ByValType = nullptr; + else if (Val == Attribute::ByRef) + ByRefType = nullptr; else if (Val == Attribute::Preallocated) PreallocatedType = nullptr; else if (Val == Attribute::Dereferenceable) @@ -1676,6 +1702,12 @@ AttrBuilder &AttrBuilder::addByValAttr(Type *Ty) { return *this; } +AttrBuilder &AttrBuilder::addByRefAttr(Type *Ty) { + Attrs[Attribute::ByRef] = true; + ByRefType = Ty; + return *this; +} + AttrBuilder &AttrBuilder::addPreallocatedAttr(Type *Ty) { Attrs[Attribute::Preallocated] = true; PreallocatedType = Ty; @@ -1702,6 +1734,9 @@ AttrBuilder &AttrBuilder::merge(const AttrBuilder &B) { if (!ByValType) ByValType = B.ByValType; + if (!ByRefType) + ByRefType = B.ByRefType; + if (!PreallocatedType) PreallocatedType = B.PreallocatedType; @@ -1733,6 +1768,9 @@ AttrBuilder &AttrBuilder::remove(const AttrBuilder &B) { if (B.ByValType) ByValType = nullptr; + if (B.ByRefType) + ByRefType = nullptr; + if (B.PreallocatedType) PreallocatedType = nullptr; @@ -1796,7 +1834,7 @@ bool AttrBuilder::operator==(const AttrBuilder &B) { return Alignment == B.Alignment && StackAlignment == B.StackAlignment && DerefBytes == B.DerefBytes && ByValType == B.ByValType && - PreallocatedType == B.PreallocatedType; + ByRefType == B.ByRefType && PreallocatedType == B.PreallocatedType; } //===----------------------------------------------------------------------===// @@ -1825,7 +1863,8 @@ AttrBuilder AttributeFuncs::typeIncompatible(Type *Ty) { .addAttribute(Attribute::StructRet) .addAttribute(Attribute::InAlloca) .addPreallocatedAttr(Ty) - .addByValAttr(Ty); + .addByValAttr(Ty) + .addByRefAttr(Ty); return Incompatible; } diff --git a/lib/IR/Function.cpp b/lib/IR/Function.cpp index 8db2389ef54..35472f7473b 100644 --- a/lib/IR/Function.cpp +++ b/lib/IR/Function.cpp @@ -102,6 +102,12 @@ bool Argument::hasByValAttr() const { return hasAttribute(Attribute::ByVal); } +bool Argument::hasByRefAttr() const { + if (!getType()->isPointerTy()) + return false; + return hasAttribute(Attribute::ByRef); +} + bool Argument::hasSwiftSelfAttr() const { return getParent()->hasParamAttribute(getArgNo(), Attribute::SwiftSelf); } @@ -129,27 +135,52 @@ bool Argument::hasPassPointeeByValueCopyAttr() const { Attrs.hasParamAttribute(getArgNo(), Attribute::Preallocated); } -uint64_t Argument::getPassPointeeByValueCopySize(const DataLayout &DL) const { - AttributeSet ParamAttrs - = getParent()->getAttributes().getParamAttributes(getArgNo()); +bool Argument::hasPointeeInMemoryValueAttr() const { + if (!getType()->isPointerTy()) + return false; + AttributeList Attrs = getParent()->getAttributes(); + return Attrs.hasParamAttribute(getArgNo(), Attribute::ByVal) || + Attrs.hasParamAttribute(getArgNo(), Attribute::InAlloca) || + Attrs.hasParamAttribute(getArgNo(), Attribute::Preallocated) || + Attrs.hasParamAttribute(getArgNo(), Attribute::ByRef); +} +/// For a byval, inalloca, or preallocated parameter, get the in-memory +/// parameter type. +static Type *getMemoryParamAllocType(AttributeSet ParamAttrs, Type *ArgTy) { // FIXME: All the type carrying attributes are mutually exclusive, so there // should be a single query to get the stored type that handles any of them. if (Type *ByValTy = ParamAttrs.getByValType()) - return DL.getTypeAllocSize(ByValTy); + return ByValTy; + if (Type *ByRefTy = ParamAttrs.getByRefType()) + return ByRefTy; if (Type *PreAllocTy = ParamAttrs.getPreallocatedType()) - return DL.getTypeAllocSize(PreAllocTy); + return PreAllocTy; // FIXME: inalloca always depends on pointee element type. It's also possible // for byval to miss it. if (ParamAttrs.hasAttribute(Attribute::InAlloca) || ParamAttrs.hasAttribute(Attribute::ByVal) || ParamAttrs.hasAttribute(Attribute::Preallocated)) - return DL.getTypeAllocSize(cast(getType())->getElementType()); + return cast(ArgTy)->getElementType(); + return nullptr; +} + +uint64_t Argument::getPassPointeeByValueCopySize(const DataLayout &DL) const { + AttributeSet ParamAttrs = + getParent()->getAttributes().getParamAttributes(getArgNo()); + if (Type *MemTy = getMemoryParamAllocType(ParamAttrs, getType())) + return DL.getTypeAllocSize(MemTy); return 0; } +Type *Argument::getPointeeInMemoryValueType() const { + AttributeSet ParamAttrs = + getParent()->getAttributes().getParamAttributes(getArgNo()); + return getMemoryParamAllocType(ParamAttrs, getType()); +} + unsigned Argument::getParamAlignment() const { assert(getType()->isPointerTy() && "Only pointers have alignments"); return getParent()->getParamAlignment(getArgNo()); @@ -165,6 +196,11 @@ Type *Argument::getParamByValType() const { return getParent()->getParamByValType(getArgNo()); } +Type *Argument::getParamByRefType() const { + assert(getType()->isPointerTy() && "Only pointers have byval types"); + return getParent()->getParamByRefType(getArgNo()); +} + uint64_t Argument::getDereferenceableBytes() const { assert(getType()->isPointerTy() && "Only pointers have dereferenceable bytes"); diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp index c518ae87ea9..ea562f64da2 100644 --- a/lib/IR/Verifier.cpp +++ b/lib/IR/Verifier.cpp @@ -1652,9 +1652,10 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty, AttrCount += Attrs.hasAttribute(Attribute::StructRet) || Attrs.hasAttribute(Attribute::InReg); AttrCount += Attrs.hasAttribute(Attribute::Nest); + AttrCount += Attrs.hasAttribute(Attribute::ByRef); Assert(AttrCount <= 1, "Attributes 'byval', 'inalloca', 'preallocated', 'inreg', 'nest', " - "and 'sret' are incompatible!", + "'byref', and 'sret' are incompatible!", V); Assert(!(Attrs.hasAttribute(Attribute::InAlloca) && @@ -1720,9 +1721,10 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty, SmallPtrSet Visited; if (!PTy->getElementType()->isSized(&Visited)) { Assert(!Attrs.hasAttribute(Attribute::ByVal) && - !Attrs.hasAttribute(Attribute::InAlloca) && - !Attrs.hasAttribute(Attribute::Preallocated), - "Attributes 'byval', 'inalloca', and 'preallocated' do not " + !Attrs.hasAttribute(Attribute::ByRef) && + !Attrs.hasAttribute(Attribute::InAlloca) && + !Attrs.hasAttribute(Attribute::Preallocated), + "Attributes 'byval', 'byref', 'inalloca', and 'preallocated' do not " "support unsized types!", V); } @@ -1731,10 +1733,18 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty, "Attribute 'swifterror' only applies to parameters " "with pointer to pointer type!", V); + + if (Attrs.hasAttribute(Attribute::ByRef)) { + Assert(Attrs.getByRefType() == PTy->getElementType(), + "Attribute 'byref' type does not match parameter!", V); + } } else { Assert(!Attrs.hasAttribute(Attribute::ByVal), "Attribute 'byval' only applies to parameters with pointer type!", V); + Assert(!Attrs.hasAttribute(Attribute::ByRef), + "Attribute 'byref' only applies to parameters with pointer type!", + V); Assert(!Attrs.hasAttribute(Attribute::SwiftError), "Attribute 'swifterror' only applies to parameters " "with pointer type!", @@ -1765,10 +1775,11 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs, !RetAttrs.hasAttribute(Attribute::Returned) && !RetAttrs.hasAttribute(Attribute::InAlloca) && !RetAttrs.hasAttribute(Attribute::Preallocated) && + !RetAttrs.hasAttribute(Attribute::ByRef) && !RetAttrs.hasAttribute(Attribute::SwiftSelf) && !RetAttrs.hasAttribute(Attribute::SwiftError)), - "Attributes 'byval', 'inalloca', 'preallocated', 'nest', 'sret', " - "'nocapture', 'nofree', " + "Attributes 'byval', 'inalloca', 'preallocated', 'byref', " + "'nest', 'sret', 'nocapture', 'nofree', " "'returned', 'swiftself', and 'swifterror' do not apply to return " "values!", V); @@ -3174,17 +3185,19 @@ static bool isTypeCongruent(Type *L, Type *R) { static AttrBuilder getParameterABIAttributes(int I, AttributeList Attrs) { static const Attribute::AttrKind ABIAttrs[] = { - Attribute::StructRet, Attribute::ByVal, Attribute::InAlloca, - Attribute::InReg, Attribute::SwiftSelf, Attribute::SwiftError, - Attribute::Preallocated}; + Attribute::StructRet, Attribute::ByVal, Attribute::InAlloca, + Attribute::InReg, Attribute::SwiftSelf, Attribute::SwiftError, + Attribute::Preallocated, Attribute::ByRef}; AttrBuilder Copy; for (auto AK : ABIAttrs) { if (Attrs.hasParamAttribute(I, AK)) Copy.addAttribute(AK); } - // `align` is ABI-affecting only in combination with `byval`. + + // `align` is ABI-affecting only in combination with `byval` or `byref`. if (Attrs.hasParamAttribute(I, Attribute::Alignment) && - Attrs.hasParamAttribute(I, Attribute::ByVal)) + (Attrs.hasParamAttribute(I, Attribute::ByVal) || + Attrs.hasParamAttribute(I, Attribute::ByRef))) Copy.addAlignmentAttr(Attrs.getParamAlignment(I)); return Copy; } diff --git a/lib/Transforms/Utils/CodeExtractor.cpp b/lib/Transforms/Utils/CodeExtractor.cpp index 8cdbb9d3565..91036c510e4 100644 --- a/lib/Transforms/Utils/CodeExtractor.cpp +++ b/lib/Transforms/Utils/CodeExtractor.cpp @@ -895,6 +895,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs, case Attribute::WriteOnly: case Attribute::ZExt: case Attribute::ImmArg: + case Attribute::ByRef: case Attribute::EndAttrKinds: case Attribute::EmptyKey: case Attribute::TombstoneKey: diff --git a/test/Assembler/byref-parse-error-0.ll b/test/Assembler/byref-parse-error-0.ll new file mode 100644 index 00000000000..287cd8453a6 --- /dev/null +++ b/test/Assembler/byref-parse-error-0.ll @@ -0,0 +1,6 @@ +; RUN: not llvm-as < %s 2>&1 | FileCheck %s + +; CHECK: :[[@LINE+1]]:34: error: expected '('{{$}} +define void @test_byref(i8* byref) { + ret void +} diff --git a/test/Assembler/byref-parse-error-1.ll b/test/Assembler/byref-parse-error-1.ll new file mode 100644 index 00000000000..3f189d340c9 --- /dev/null +++ b/test/Assembler/byref-parse-error-1.ll @@ -0,0 +1,6 @@ +; RUN: not llvm-as < %s 2>&1 | FileCheck %s + +; CHECK: :[[@LINE+1]]:35: error: expected type{{$}} +define void @test_byref(i8* byref() { + ret void +} diff --git a/test/Assembler/byref-parse-error-10.ll b/test/Assembler/byref-parse-error-10.ll new file mode 100644 index 00000000000..55997e5ffe5 --- /dev/null +++ b/test/Assembler/byref-parse-error-10.ll @@ -0,0 +1,6 @@ +; RUN: not llvm-as < %s 2>&1 | FileCheck %s + +; CHECK: :[[@LINE+1]]:27: error: invalid use of parameter-only attribute on a function{{$}} +define void @test_byref() byref(4) { + ret void +} diff --git a/test/Assembler/byref-parse-error-2.ll b/test/Assembler/byref-parse-error-2.ll new file mode 100644 index 00000000000..6037c3ac3c7 --- /dev/null +++ b/test/Assembler/byref-parse-error-2.ll @@ -0,0 +1,7 @@ +; RUN: not llvm-as < %s 2>&1 | FileCheck %s + +; CHECK: :[[@LINE+1]]:35: error: expected type{{$}} +define void @test_byref(i8* byref()) { + ret void +} + diff --git a/test/Assembler/byref-parse-error-3.ll b/test/Assembler/byref-parse-error-3.ll new file mode 100644 index 00000000000..b86eab4e69c --- /dev/null +++ b/test/Assembler/byref-parse-error-3.ll @@ -0,0 +1,6 @@ +; RUN: not llvm-as < %s 2>&1 | FileCheck %s + +; CHECK: :[[@LINE+1]]:35: error: expected type{{$}} +define void @test_byref(i8* byref(-1)) { + ret void +} diff --git a/test/Assembler/byref-parse-error-4.ll b/test/Assembler/byref-parse-error-4.ll new file mode 100644 index 00000000000..6b6a6c25715 --- /dev/null +++ b/test/Assembler/byref-parse-error-4.ll @@ -0,0 +1,6 @@ +; RUN: not llvm-as < %s 2>&1 | FileCheck %s + +; CHECK: :[[@LINE+1]]:35: error: expected type{{$}} +define void @test_byref(i8* byref(0)) { + ret void +} diff --git a/test/Assembler/byref-parse-error-5.ll b/test/Assembler/byref-parse-error-5.ll new file mode 100644 index 00000000000..c1a154805f4 --- /dev/null +++ b/test/Assembler/byref-parse-error-5.ll @@ -0,0 +1,6 @@ +; RUN: not llvm-as < %s 2>&1 | FileCheck %s + +; CHECK: :[[@LINE+1]]:8: error: invalid use of parameter-only attribute{{$}} +define byref i8* @test_byref() { + ret void +} diff --git a/test/Assembler/byref-parse-error-6.ll b/test/Assembler/byref-parse-error-6.ll new file mode 100644 index 00000000000..c5ecf83596e --- /dev/null +++ b/test/Assembler/byref-parse-error-6.ll @@ -0,0 +1,6 @@ +; RUN: not llvm-as < %s 2>&1 | FileCheck %s + +; CHECK: :[[@LINE+1]]:8: error: invalid use of parameter-only attribute{{$}} +define byref 8 i8* @test_byref() { + ret void +} diff --git a/test/Assembler/byref-parse-error-7.ll b/test/Assembler/byref-parse-error-7.ll new file mode 100644 index 00000000000..9ac60af5348 --- /dev/null +++ b/test/Assembler/byref-parse-error-7.ll @@ -0,0 +1,6 @@ +; RUN: not llvm-as < %s 2>&1 | FileCheck %s + +; CHECK: :[[@LINE+1]]:8: error: invalid use of parameter-only attribute{{$}} +define byref(8) i8* @test_byref() { + ret void +} diff --git a/test/Assembler/byref-parse-error-8.ll b/test/Assembler/byref-parse-error-8.ll new file mode 100644 index 00000000000..8f86b9f8afc --- /dev/null +++ b/test/Assembler/byref-parse-error-8.ll @@ -0,0 +1,6 @@ +; RUN: not llvm-as < %s 2>&1 | FileCheck %s + +; CHECK: :[[@LINE+1]]:27: error: invalid use of parameter-only attribute on a function{{$}} +define void @test_byref() byref { + ret void +} diff --git a/test/Assembler/byref-parse-error-9.ll b/test/Assembler/byref-parse-error-9.ll new file mode 100644 index 00000000000..bc707062a2e --- /dev/null +++ b/test/Assembler/byref-parse-error-9.ll @@ -0,0 +1,6 @@ +; RUN: not llvm-as < %s 2>&1 | FileCheck %s + +; CHECK: :[[@LINE+1]]:27: error: invalid use of parameter-only attribute on a function{{$}} +define void @test_byref() byref=4 { + ret void +} diff --git a/test/Bitcode/attributes.ll b/test/Bitcode/attributes.ll index fbbe4a80f31..409528230a7 100644 --- a/test/Bitcode/attributes.ll +++ b/test/Bitcode/attributes.ll @@ -392,6 +392,12 @@ define noundef i32 @f66(i32 noundef %a) ret i32 %a } +; CHECK: define void @f67(i32* byref(i32) %a) +define void @f67(i32* byref(i32) %a) +{ + ret void +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } diff --git a/test/CodeGen/X86/byref.ll b/test/CodeGen/X86/byref.ll new file mode 100644 index 00000000000..90d5e9901b6 --- /dev/null +++ b/test/CodeGen/X86/byref.ll @@ -0,0 +1,20 @@ +; RUN: llc < %s -mtriple=i686-pc-win32 | FileCheck %s + +%Foo = type { i32, i32 } + +declare x86_stdcallcc void @foo_byref_stdcall_p(%Foo* byref(%Foo)) +declare x86_stdcallcc void @i(i32) + +; byref does not imply a stack copy, so this should append 4 bytes, +; not 8. +define void @stdcall(%Foo* %value) { +; CHECK-LABEL: _stdcall: +; CHECK: pushl 4(%esp) +; CHECK: calll _foo_byref_stdcall_p@4 + call x86_stdcallcc void @foo_byref_stdcall_p(%Foo* byref(%Foo) %value) +; CHECK-NOT: %esp +; CHECK: pushl +; CHECK: calll _i@4 + call x86_stdcallcc void @i(i32 0) + ret void +} diff --git a/test/Instrumentation/AddressSanitizer/byref-args.ll b/test/Instrumentation/AddressSanitizer/byref-args.ll new file mode 100644 index 00000000000..df2eeb4c184 --- /dev/null +++ b/test/Instrumentation/AddressSanitizer/byref-args.ll @@ -0,0 +1,20 @@ +; RUN: opt < %s -asan -S | FileCheck %s + +; Test that for call instructions, the byref arguments are not +; instrumented, as no copy is implied. + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.bar = type { %struct.foo } +%struct.foo = type { i8*, i8*, i8* } + +; CHECK-LABEL: @func2 +; CHECK-NEXT: tail call void @func1( +; CHECK-NEXT: ret void +define dso_local void @func2(%struct.foo* %foo) sanitize_address { + tail call void @func1(%struct.foo* byref(%struct.foo) align 8 %foo) #2 + ret void +} + +declare dso_local void @func1(%struct.foo* byref(%struct.foo) align 8) diff --git a/test/Transforms/DeadArgElim/byref.ll b/test/Transforms/DeadArgElim/byref.ll new file mode 100644 index 00000000000..95e839c04aa --- /dev/null +++ b/test/Transforms/DeadArgElim/byref.ll @@ -0,0 +1,22 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -deadargelim -S | FileCheck %s + +declare void @sideeffect() + +define void @unused_byref_arg(i32* byref(i32) %dead_arg) { +; CHECK-LABEL: @unused_byref_arg( +; CHECK-NEXT: tail call void @sideeffect() +; CHECK-NEXT: ret void +; + tail call void @sideeffect() + ret void +} + +define void @dont_replace_by_undef(i32* %ptr) { +; CHECK-LABEL: @dont_replace_by_undef( +; CHECK-NEXT: call void @unused_byref_arg(i32* undef) +; CHECK-NEXT: ret void +; + call void @unused_byref_arg(i32* %ptr) + ret void +} diff --git a/test/Transforms/Inline/byref-align.ll b/test/Transforms/Inline/byref-align.ll new file mode 100644 index 00000000000..fb70db2af44 --- /dev/null +++ b/test/Transforms/Inline/byref-align.ll @@ -0,0 +1,52 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature +; RUN: opt -inline -preserve-alignment-assumptions-during-inlining -S < %s | FileCheck %s +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Test behavior of inserted alignment assumptions with byref. There is +; no implied copy to a higher alignment, so an alignment assume call +; should be inserted. +define void @byref_callee(float* align(128) byref(float) nocapture %a, float* %b) #0 { +; CHECK-LABEL: define {{[^@]+}}@byref_callee +; CHECK-SAME: (float* nocapture byref(float) align 128 [[A:%.*]], float* [[B:%.*]]) #0 +; CHECK-NEXT: entry: +; CHECK-NEXT: [[LOAD:%.*]] = load float, float* [[A]], align 4 +; CHECK-NEXT: [[B_IDX:%.*]] = getelementptr inbounds float, float* [[B]], i64 8 +; CHECK-NEXT: [[ADD:%.*]] = fadd float [[LOAD]], 2.000000e+00 +; CHECK-NEXT: store float [[ADD]], float* [[B_IDX]], align 4 +; CHECK-NEXT: ret void +; +entry: + %load = load float, float* %a, align 4 + %b.idx = getelementptr inbounds float, float* %b, i64 8 + %add = fadd float %load, 2.0 + store float %add, float* %b.idx, align 4 + ret void +} + +define void @byref_caller(float* nocapture align 64 %a, float* %b) #0 { +; CHECK-LABEL: define {{[^@]+}}@byref_caller +; CHECK-SAME: (float* nocapture align 64 [[A:%.*]], float* [[B:%.*]]) #0 +; CHECK-NEXT: entry: +; CHECK-NEXT: [[PTRINT:%.*]] = ptrtoint float* [[A]] to i64 +; CHECK-NEXT: [[MASKEDPTR:%.*]] = and i64 [[PTRINT]], 127 +; CHECK-NEXT: [[MASKCOND:%.*]] = icmp eq i64 [[MASKEDPTR]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[MASKCOND]]) +; CHECK-NEXT: [[LOAD_I:%.*]] = load float, float* [[A]], align 4 +; CHECK-NEXT: [[B_IDX_I:%.*]] = getelementptr inbounds float, float* [[B]], i64 8 +; CHECK-NEXT: [[ADD_I:%.*]] = fadd float [[LOAD_I]], 2.000000e+00 +; CHECK-NEXT: store float [[ADD_I]], float* [[B_IDX_I]], align 4 +; CHECK-NEXT: [[CALLER_LOAD:%.*]] = load float, float* [[B]], align 4 +; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[A]], i64 7 +; CHECK-NEXT: store float [[CALLER_LOAD]], float* [[ARRAYIDX]], align 4 +; CHECK-NEXT: ret void +; +entry: + call void @byref_callee(float* align(128) byref(float) %a, float* %b) + %caller.load = load float, float* %b, align 4 + %arrayidx = getelementptr inbounds float, float* %a, i64 7 + store float %caller.load, float* %arrayidx, align 4 + ret void +} + +attributes #0 = { nounwind uwtable } diff --git a/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll b/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll index ee5d792d532..ab59b8950d7 100644 --- a/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll +++ b/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll @@ -89,3 +89,11 @@ define i64 @test_objectsize_byval_arg([42 x i8]* byval([42 x i8]) %ptr) { %size = tail call i64 @llvm.objectsize.i64(i8* %cast, i1 true, i1 false, i1 false) ret i64 %size } + +; CHECK-LABEL: @test_objectsize_byref_arg( +; CHECK: ret i64 42 +define i64 @test_objectsize_byref_arg([42 x i8]* byref([42 x i8]) %ptr) { + %cast = bitcast [42 x i8]* %ptr to i8* + %size = tail call i64 @llvm.objectsize.i64(i8* %cast, i1 true, i1 false, i1 false) + ret i64 %size +} diff --git a/test/Verifier/byref.ll b/test/Verifier/byref.ll new file mode 100644 index 00000000000..5e7d5873a8d --- /dev/null +++ b/test/Verifier/byref.ll @@ -0,0 +1,100 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s + +; CHECK: Attribute 'byref' type does not match parameter! +; CHECK-NEXT: void (i32*)* @byref_mismatched_pointee_type0 +define void @byref_mismatched_pointee_type0(i32* byref(i8)) { + ret void +} + +; CHECK: Attribute 'byref' type does not match parameter! +; CHECK-NEXT: void (i8*)* @byref_mismatched_pointee_type1 +define void @byref_mismatched_pointee_type1(i8* byref(i32)) { + ret void +} + +%opaque.ty = type opaque + +; CHECK: Attributes 'byval', 'byref', 'inalloca', and 'preallocated' do not support unsized types! +; CHECK-NEXT: void (%opaque.ty*)* @byref_unsized +define void @byref_unsized(%opaque.ty* byref(%opaque.ty)) { + ret void +} + +; CHECK: Attributes 'byval', 'inalloca', 'preallocated', 'inreg', 'nest', 'byref', and 'sret' are incompatible! +; CHECK-NEXT: void (i32*)* @byref_byval +define void @byref_byval(i32* byref(i32) byval(i32)) { + ret void +} + +; CHECK: Attributes 'byval', 'inalloca', 'preallocated', 'inreg', 'nest', 'byref', and 'sret' are incompatible! +; CHECK-NEXT: void (i32*)* @byref_inalloca +define void @byref_inalloca(i32* byref(i32) inalloca) { + ret void +} + +; CHECK: Attributes 'byval', 'inalloca', 'preallocated', 'inreg', 'nest', 'byref', and 'sret' are incompatible! +; CHECK-NEXT: void (i32*)* @byref_preallocated +define void @byref_preallocated(i32* byref(i32) preallocated(i32)) { + ret void +} + +; CHECK: Attributes 'byval', 'inalloca', 'preallocated', 'inreg', 'nest', 'byref', and 'sret' are incompatible! +; CHECK-NEXT: void (i32*)* @byref_sret +define void @byref_sret(i32* byref(i32) sret) { + ret void +} + +; CHECK: Attributes 'byval', 'inalloca', 'preallocated', 'inreg', 'nest', 'byref', and 'sret' are incompatible! +; CHECK-NEXT: void (i32*)* @byref_inreg +define void @byref_inreg(i32* byref(i32) inreg) { + ret void +} + +; CHECK: Attributes 'byval', 'inalloca', 'preallocated', 'inreg', 'nest', 'byref', and 'sret' are incompatible! +; CHECK-NEXT: void (i32*)* @byref_nest +define void @byref_nest(i32* byref(i32) nest) { + ret void +} + +; CHECK: Wrong types for attribute: inalloca nest noalias nocapture nonnull readnone readonly sret byref(i32) byval(i32) preallocated(i32) dereferenceable(1) dereferenceable_or_null(1) +; CHECK-NEXT: void (i32)* @byref_non_pointer +define void @byref_non_pointer(i32 byref(i32)) { + ret void +} + +define void @byref_callee([64 x i8]* byref([64 x i8])) { + ret void +} + +define void @no_byref_callee(i8*) { + ret void +} + +; CHECK: cannot guarantee tail call due to mismatched ABI impacting function attributes +; CHECK-NEXT: musttail call void @byref_callee([64 x i8]* byref([64 x i8]) %cast) +; CHECK-NEXT: i8* %ptr +define void @musttail_byref_caller(i8* %ptr) { + %cast = bitcast i8* %ptr to [64 x i8]* + musttail call void @byref_callee([64 x i8]* byref([64 x i8]) %cast) + ret void +} + +; CHECK: cannot guarantee tail call due to mismatched ABI impacting function attributes +; CHECK-NEXT: musttail call void @byref_callee([64 x i8]* %ptr) +; CHECK-NEXT: [64 x i8]* %ptr +define void @musttail_byref_callee([64 x i8]* byref([64 x i8]) %ptr) { + musttail call void @byref_callee([64 x i8]* %ptr) + ret void +} + +define void @byref_callee_align32(i8* byref([64 x i8]) align 32) { + ret void +} + +; CHECK: cannot guarantee tail call due to mismatched ABI impacting function attributes +; CHECK-NEXT: musttail call void @byref_callee_align32(i8* byref([64 x i8]) align 32 %ptr) +; CHECK-NEXT: i8* %ptr +define void @musttail_byref_caller_mismatched_align(i8* byref([64 x i8]) align 16 %ptr) { + musttail call void @byref_callee_align32(i8* byref([64 x i8]) align 32 %ptr) + ret void +}