Index: llvm/include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -644,6 +644,7 @@ ATTR_KIND_NO_MERGE = 66, ATTR_KIND_NULL_POINTER_IS_VALID = 67, ATTR_KIND_NOUNDEF = 68, + ATTR_KIND_BYREF = 69, }; enum ComdatSelectionKindCodes { Index: llvm/include/llvm/IR/Argument.h =================================================================== --- llvm/include/llvm/IR/Argument.h +++ llvm/include/llvm/IR/Argument.h @@ -65,6 +65,9 @@ /// 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 @@ /// 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 this argument satisfies has hasPointeeInMemoryValueAttr, return the + /// in-memory ABI type. Otherwise, return 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 @@ /// 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; Index: llvm/include/llvm/IR/Attributes.h =================================================================== --- llvm/include/llvm/IR/Attributes.h +++ llvm/include/llvm/IR/Attributes.h @@ -108,6 +108,7 @@ 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 @@ 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 @@ /// 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 @@ uint64_t DerefOrNullBytes = 0; uint64_t AllocSizeArgs = 0; Type *ByValType = nullptr; + Type *ByRefType = nullptr; Type *PreallocatedType = nullptr; public: @@ -808,6 +814,9 @@ /// 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 @@ /// 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); Index: llvm/include/llvm/IR/Attributes.td =================================================================== --- llvm/include/llvm/IR/Attributes.td +++ llvm/include/llvm/IR/Attributes.td @@ -39,6 +39,9 @@ /// 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">; Index: llvm/include/llvm/IR/Function.h =================================================================== --- llvm/include/llvm/IR/Function.h +++ llvm/include/llvm/IR/Function.h @@ -467,6 +467,11 @@ 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. Index: llvm/lib/Analysis/MemoryBuiltins.cpp =================================================================== --- llvm/lib/Analysis/MemoryBuiltins.cpp +++ llvm/lib/Analysis/MemoryBuiltins.cpp @@ -676,13 +676,14 @@ } 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); } Index: llvm/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/lib/AsmParser/LLLexer.cpp +++ llvm/lib/AsmParser/LLLexer.cpp @@ -697,6 +697,7 @@ KEYWORD(writeonly); KEYWORD(zeroext); KEYWORD(immarg); + KEYWORD(byref); KEYWORD(type); KEYWORD(opaque); Index: llvm/lib/AsmParser/LLParser.h =================================================================== --- llvm/lib/AsmParser/LLParser.h +++ llvm/lib/AsmParser/LLParser.h @@ -333,7 +333,10 @@ 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(); Index: llvm/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/lib/AsmParser/LLParser.cpp +++ llvm/lib/AsmParser/LLParser.cpp @@ -1382,6 +1382,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 ]* ']' Index: llvm/lib/AsmParser/LLToken.h =================================================================== --- llvm/lib/AsmParser/LLToken.h +++ llvm/lib/AsmParser/LLToken.h @@ -240,6 +240,7 @@ kw_writeonly, kw_zeroext, kw_immarg, + kw_byref, kw_type, kw_opaque, Index: llvm/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1532,6 +1532,8 @@ return Attribute::Preallocated; case bitc::ATTR_KIND_NOUNDEF: return Attribute::NoUndef; + case bitc::ATTR_KIND_BYREF: + return Attribute::ByRef; } } @@ -1649,6 +1651,8 @@ 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])); } Index: llvm/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -733,6 +733,8 @@ 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: Index: llvm/lib/IR/AsmWriter.cpp =================================================================== --- llvm/lib/IR/AsmWriter.cpp +++ llvm/lib/IR/AsmWriter.cpp @@ -4267,11 +4267,14 @@ } 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"; } Index: llvm/lib/IR/AttributeImpl.h =================================================================== --- llvm/lib/IR/AttributeImpl.h +++ llvm/lib/IR/AttributeImpl.h @@ -251,6 +251,7 @@ std::pair> getAllocSizeArgs() const; std::string getAsString(bool InAttrGrp) const; Type *getByValType() const; + Type *getByRefType() const; Type *getPreallocatedType() const; using iterator = const Attribute *; Index: llvm/lib/IR/Attributes.cpp =================================================================== --- llvm/lib/IR/Attributes.cpp +++ llvm/lib/IR/Attributes.cpp @@ -172,6 +172,10 @@ 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 @@ 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 @@ 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 @@ 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 @@ return nullptr; } +Type *AttributeSetNode::getByRefType() const { + if (auto A = findEnumAttribute(Attribute::ByRef)) + return A->getValueAsType(); + return nullptr; +} + Type *AttributeSetNode::getPreallocatedType() const { for (const auto &I : *this) if (I.hasAttribute(Attribute::Preallocated)) @@ -1452,6 +1469,10 @@ 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(); } @@ -1537,6 +1558,7 @@ DerefBytes = DerefOrNullBytes = 0; AllocSizeArgs = 0; ByValType = nullptr; + ByRefType = nullptr; PreallocatedType = nullptr; } @@ -1563,6 +1585,8 @@ 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) @@ -1589,6 +1613,8 @@ 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) @@ -1679,6 +1705,12 @@ 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; @@ -1705,6 +1737,9 @@ if (!ByValType) ByValType = B.ByValType; + if (!ByRefType) + ByRefType = B.ByRefType; + if (!PreallocatedType) PreallocatedType = B.PreallocatedType; @@ -1736,6 +1771,9 @@ if (B.ByValType) ByValType = nullptr; + if (B.ByRefType) + ByRefType = nullptr; + if (B.PreallocatedType) PreallocatedType = nullptr; @@ -1799,7 +1837,7 @@ return Alignment == B.Alignment && StackAlignment == B.StackAlignment && DerefBytes == B.DerefBytes && ByValType == B.ByValType && - PreallocatedType == B.PreallocatedType; + ByRefType == B.ByRefType && PreallocatedType == B.PreallocatedType; } //===----------------------------------------------------------------------===// @@ -1828,7 +1866,8 @@ .addAttribute(Attribute::StructRet) .addAttribute(Attribute::InAlloca) .addPreallocatedAttr(Ty) - .addByValAttr(Ty); + .addByValAttr(Ty) + .addByRefAttr(Ty); return Incompatible; } Index: llvm/lib/IR/Function.cpp =================================================================== --- llvm/lib/IR/Function.cpp +++ llvm/lib/IR/Function.cpp @@ -101,6 +101,12 @@ 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); } @@ -128,27 +134,52 @@ 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()); @@ -164,6 +195,11 @@ 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"); Index: llvm/lib/IR/Verifier.cpp =================================================================== --- llvm/lib/IR/Verifier.cpp +++ llvm/lib/IR/Verifier.cpp @@ -1652,9 +1652,10 @@ 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 @@ 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 @@ "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 @@ !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 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; } Index: llvm/lib/Transforms/Utils/CodeExtractor.cpp =================================================================== --- llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -895,6 +895,7 @@ case Attribute::WriteOnly: case Attribute::ZExt: case Attribute::ImmArg: + case Attribute::ByRef: case Attribute::EndAttrKinds: case Attribute::EmptyKey: case Attribute::TombstoneKey: Index: llvm/test/Assembler/byref-parse-error-0.ll =================================================================== --- /dev/null +++ llvm/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 +} Index: llvm/test/Assembler/byref-parse-error-1.ll =================================================================== --- /dev/null +++ llvm/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 +} Index: llvm/test/Assembler/byref-parse-error-10.ll =================================================================== --- /dev/null +++ llvm/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 +} Index: llvm/test/Assembler/byref-parse-error-2.ll =================================================================== --- /dev/null +++ llvm/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 +} + Index: llvm/test/Assembler/byref-parse-error-3.ll =================================================================== --- /dev/null +++ llvm/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 +} Index: llvm/test/Assembler/byref-parse-error-4.ll =================================================================== --- /dev/null +++ llvm/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 +} Index: llvm/test/Assembler/byref-parse-error-5.ll =================================================================== --- /dev/null +++ llvm/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 +} Index: llvm/test/Assembler/byref-parse-error-6.ll =================================================================== --- /dev/null +++ llvm/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 +} Index: llvm/test/Assembler/byref-parse-error-7.ll =================================================================== --- /dev/null +++ llvm/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 +} Index: llvm/test/Assembler/byref-parse-error-8.ll =================================================================== --- /dev/null +++ llvm/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 +} Index: llvm/test/Assembler/byref-parse-error-9.ll =================================================================== --- /dev/null +++ llvm/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 +} Index: llvm/test/Bitcode/attributes.ll =================================================================== --- llvm/test/Bitcode/attributes.ll +++ llvm/test/Bitcode/attributes.ll @@ -392,6 +392,12 @@ 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 } Index: llvm/test/CodeGen/X86/byref.ll =================================================================== --- /dev/null +++ llvm/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 +} Index: llvm/test/Instrumentation/AddressSanitizer/byref-args.ll =================================================================== --- /dev/null +++ llvm/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) Index: llvm/test/Transforms/DeadArgElim/byref.ll =================================================================== --- /dev/null +++ llvm/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 +} Index: llvm/test/Transforms/Inline/byref-align.ll =================================================================== --- /dev/null +++ llvm/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 } Index: llvm/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll =================================================================== --- llvm/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll +++ llvm/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll @@ -89,3 +89,11 @@ %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 +} Index: llvm/test/Verifier/byref.ll =================================================================== --- /dev/null +++ llvm/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 +}