OSDN Git Service

IR: Define byref parameter attribute
authorMatt Arsenault <Matthew.Arsenault@amd.com>
Fri, 5 Jun 2020 20:58:47 +0000 (16:58 -0400)
committerMatt Arsenault <Matthew.Arsenault@amd.com>
Mon, 20 Jul 2020 14:23:09 +0000 (10:23 -0400)
This allows tracking the in-memory type of a pointer argument to a
function for ABI purposes. This is essentially a stripped down version
of byval to remove some of the stack-copy implications in its
definition.

This includes the base IR changes, and some tests for places where it
should be treated similarly to byval. Codegen support will be in a
future patch.

My original attempt at solving some of these problems was to repurpose
byval with a different address space from the stack. However, it is
technically permitted for the callee to introduce a write to the
argument, although nothing does this in reality. There is also talk of
removing and replacing the byval attribute, so a new attribute would
need to take its place anyway.

This is intended avoid some optimization issues with the current
handling of aggregate arguments, as well as fixes inflexibilty in how
frontends can specify the kernel ABI. The most honest representation
of the amdgpu_kernel convention is to expose all kernel arguments as
loads from constant memory. Today, these are raw, SSA Argument values
and codegen is responsible for turning these into loads.

Background:

There currently isn't a satisfactory way to represent how arguments
for the amdgpu_kernel calling convention are passed. In reality,
arguments are passed in a single, flat, constant memory buffer
implicitly passed to the function. It is also illegal to call this
function in the IR, and this is only ever invoked by a driver of some
kind.

It does not make sense to have a stack passed parameter in this
context as is implied by byval. It is never valid to write to the
kernel arguments, as this would corrupt the inputs seen by other
dispatches of the kernel. These argumets are also not in the same
address space as the stack, so a copy is needed to an alloca. From a
source C-like language, the kernel parameters are invisible.
Semantically, a copy is always required from the constant argument
memory to a mutable variable.

The current clang calling convention lowering emits raw values,
including aggregates into the function argument list, since using
byval would not make sense. This has some unfortunate consequences for
the optimizer. In the aggregate case, we end up with an aggregate
store to alloca, which both SROA and instcombine turn into a store of
each aggregate field. The optimizer never pieces this back together to
see that this is really just a copy from constant memory, so we end up
stuck with expensive stack usage.

This also means the backend dictates the alignment of arguments, and
arbitrarily picks the LLVM IR ABI type alignment. By allowing an
explicit alignment, frontends can make better decisions. For example,
there's real no advantage to an aligment higher than 4, so a frontend
could choose to compact the argument layout. Similarly, there is a
high penalty to using an alignment lower than 4, so a frontend could
opt into more padding for small arguments.

Another design consideration is when it is appropriate to expose the
fact that these arguments are all really passed in adjacent
memory. Currently we have a late IR optimization pass in codegen to
rewrite the kernel argument values into explicit loads to enable
vectorization. In most programs, unrelated argument loads can be
merged together. However, exposing this property directly from the
frontend has some disadvantages. We still need a way to track the
original argument sizes and alignments to report to the driver. I find
using some side-channel, metadata mechanism to track this
unappealing. If the kernel arguments were exposed as a single buffer
to begin with, alias analysis would be unaware that the padding bits
betewen arguments are meaningless. Another family of problems is there
are still some gaps in replacing all of the available parameter
attributes with metadata equivalents once lowered to loads.

The immediate plan is to start using this new attribute to handle all
aggregate argumets for kernels. Long term, it makes sense to migrate
all kernel arguments, including scalars, to be passed indirectly in
the same manner.

Additional context is in D79744.

38 files changed:
llvm/docs/LangRef.rst
llvm/docs/ReleaseNotes.rst
llvm/include/llvm/Bitcode/LLVMBitCodes.h
llvm/include/llvm/IR/Argument.h
llvm/include/llvm/IR/Attributes.h
llvm/include/llvm/IR/Attributes.td
llvm/include/llvm/IR/Function.h
llvm/lib/Analysis/MemoryBuiltins.cpp
llvm/lib/AsmParser/LLLexer.cpp
llvm/lib/AsmParser/LLParser.cpp
llvm/lib/AsmParser/LLParser.h
llvm/lib/AsmParser/LLToken.h
llvm/lib/Bitcode/Reader/BitcodeReader.cpp
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
llvm/lib/IR/AsmWriter.cpp
llvm/lib/IR/AttributeImpl.h
llvm/lib/IR/Attributes.cpp
llvm/lib/IR/Function.cpp
llvm/lib/IR/Verifier.cpp
llvm/lib/Transforms/Utils/CodeExtractor.cpp
llvm/test/Assembler/byref-parse-error-0.ll [new file with mode: 0644]
llvm/test/Assembler/byref-parse-error-1.ll [new file with mode: 0644]
llvm/test/Assembler/byref-parse-error-10.ll [new file with mode: 0644]
llvm/test/Assembler/byref-parse-error-2.ll [new file with mode: 0644]
llvm/test/Assembler/byref-parse-error-3.ll [new file with mode: 0644]
llvm/test/Assembler/byref-parse-error-4.ll [new file with mode: 0644]
llvm/test/Assembler/byref-parse-error-5.ll [new file with mode: 0644]
llvm/test/Assembler/byref-parse-error-6.ll [new file with mode: 0644]
llvm/test/Assembler/byref-parse-error-7.ll [new file with mode: 0644]
llvm/test/Assembler/byref-parse-error-8.ll [new file with mode: 0644]
llvm/test/Assembler/byref-parse-error-9.ll [new file with mode: 0644]
llvm/test/Bitcode/attributes.ll
llvm/test/CodeGen/X86/byref.ll [new file with mode: 0644]
llvm/test/Instrumentation/AddressSanitizer/byref-args.ll [new file with mode: 0644]
llvm/test/Transforms/DeadArgElim/byref.ll [new file with mode: 0644]
llvm/test/Transforms/Inline/byref-align.ll [new file with mode: 0644]
llvm/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll
llvm/test/Verifier/byref.ll [new file with mode: 0644]

index 86d8c62..0c1039f 100644 (file)
@@ -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(<ty>)``
+
+    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(<ty>)``
index 6293c82..42867a8 100644 (file)
@@ -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
 -----------------------------
 
index de4fe66..452918c 100644 (file)
@@ -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 {
index 2ca18c6..2dd7c6c 100644 (file)
@@ -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;
 
index 58365aa..03f2fc1 100644 (file)
@@ -108,6 +108,7 @@ public:
                                         unsigned ElemSizeArg,
                                         const Optional<unsigned> &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<unsigned, Optional<unsigned>> 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);
 
index 395f9db..214eb84 100644 (file)
@@ -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">;
 
index bb4ec13..9bbd0cb 100644 (file)
@@ -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.
index 204f855..402a2bc 100644 (file)
@@ -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<PointerType>(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 777ce3a..591c6c3 100644 (file)
@@ -697,6 +697,7 @@ lltok::Kind LLLexer::LexIdentifier() {
   KEYWORD(writeonly);
   KEYWORD(zeroext);
   KEYWORD(immarg);
+  KEYWORD(byref);
 
   KEYWORD(type);
   KEYWORD(opaque);
index c9f21ee..1a1c1d8 100644 (file)
@@ -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(<ty>)
-bool LLParser::ParsePreallocated(Type *&Result) {
+/// ParseRequiredTypeAttr
+///   ::= attrname(<ty>)
+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(<ty>)
+bool LLParser::ParsePreallocated(Type *&Result) {
+  return ParseRequiredTypeAttr(Result, lltok::kw_preallocated);
+}
+
+/// ParseByRef
+///   ::= byref(<type>)
+bool LLParser::ParseByRef(Type *&Result) {
+  return ParseRequiredTypeAttr(Result, lltok::kw_byref);
+}
+
 /// ParseOptionalOperandBundles
 ///    ::= /*empty*/
 ///    ::= '[' OperandBundle [, OperandBundle ]* ']'
index ebd8655..333b8a3 100644 (file)
@@ -333,7 +333,10 @@ namespace llvm {
                                     std::vector<unsigned> &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 0fb3bae..8bdeb57 100644 (file)
@@ -240,6 +240,7 @@ enum Kind {
   kw_writeonly,
   kw_zeroext,
   kw_immarg,
+  kw_byref,
 
   kw_type,
   kw_opaque,
index 659e26c..86b8f5c 100644 (file)
@@ -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]));
           }
index 9c15a5f..4cab4e0 100644 (file)
@@ -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:
index fd08310..83ad4e2 100644 (file)
@@ -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";
   }
index 5c33434..b013474 100644 (file)
@@ -251,6 +251,7 @@ public:
   std::pair<unsigned, Optional<unsigned>> getAllocSizeArgs() const;
   std::string getAsString(bool InAttrGrp) const;
   Type *getByValType() const;
+  Type *getByRefType() const;
   Type *getPreallocatedType() const;
 
   using iterator = const Attribute *;
index 8bf4e82..43074e8 100644 (file)
@@ -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;
 }
index 8db2389..35472f7 100644 (file)
@@ -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<PointerType>(getType())->getElementType());
+    return cast<PointerType>(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");
index c518ae8..ea562f6 100644 (file)
@@ -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<Type*, 4> 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;
 }
index 8cdbb9d..91036c5 100644 (file)
@@ -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/llvm/test/Assembler/byref-parse-error-0.ll b/llvm/test/Assembler/byref-parse-error-0.ll
new file mode 100644 (file)
index 0000000..287cd84
--- /dev/null
@@ -0,0 +1,6 @@
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:34: error: expected '('{{$}}
+define void @test_byref(i8* byref) {
+  ret void
+}
diff --git a/llvm/test/Assembler/byref-parse-error-1.ll b/llvm/test/Assembler/byref-parse-error-1.ll
new file mode 100644 (file)
index 0000000..3f189d3
--- /dev/null
@@ -0,0 +1,6 @@
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:35: error: expected type{{$}}
+define void @test_byref(i8* byref() {
+  ret void
+}
diff --git a/llvm/test/Assembler/byref-parse-error-10.ll b/llvm/test/Assembler/byref-parse-error-10.ll
new file mode 100644 (file)
index 0000000..55997e5
--- /dev/null
@@ -0,0 +1,6 @@
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:27: error: invalid use of parameter-only attribute on a function{{$}}
+define void @test_byref() byref(4) {
+  ret void
+}
diff --git a/llvm/test/Assembler/byref-parse-error-2.ll b/llvm/test/Assembler/byref-parse-error-2.ll
new file mode 100644 (file)
index 0000000..6037c3a
--- /dev/null
@@ -0,0 +1,7 @@
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:35: error: expected type{{$}}
+define void @test_byref(i8* byref()) {
+  ret void
+}
+
diff --git a/llvm/test/Assembler/byref-parse-error-3.ll b/llvm/test/Assembler/byref-parse-error-3.ll
new file mode 100644 (file)
index 0000000..b86eab4
--- /dev/null
@@ -0,0 +1,6 @@
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:35: error: expected type{{$}}
+define void @test_byref(i8* byref(-1)) {
+  ret void
+}
diff --git a/llvm/test/Assembler/byref-parse-error-4.ll b/llvm/test/Assembler/byref-parse-error-4.ll
new file mode 100644 (file)
index 0000000..6b6a6c2
--- /dev/null
@@ -0,0 +1,6 @@
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:35: error: expected type{{$}}
+define void @test_byref(i8* byref(0)) {
+  ret void
+}
diff --git a/llvm/test/Assembler/byref-parse-error-5.ll b/llvm/test/Assembler/byref-parse-error-5.ll
new file mode 100644 (file)
index 0000000..c1a1548
--- /dev/null
@@ -0,0 +1,6 @@
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:8: error: invalid use of parameter-only attribute{{$}}
+define byref i8* @test_byref() {
+  ret void
+}
diff --git a/llvm/test/Assembler/byref-parse-error-6.ll b/llvm/test/Assembler/byref-parse-error-6.ll
new file mode 100644 (file)
index 0000000..c5ecf83
--- /dev/null
@@ -0,0 +1,6 @@
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:8: error: invalid use of parameter-only attribute{{$}}
+define byref 8 i8* @test_byref() {
+  ret void
+}
diff --git a/llvm/test/Assembler/byref-parse-error-7.ll b/llvm/test/Assembler/byref-parse-error-7.ll
new file mode 100644 (file)
index 0000000..9ac60af
--- /dev/null
@@ -0,0 +1,6 @@
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:8: error: invalid use of parameter-only attribute{{$}}
+define byref(8) i8* @test_byref() {
+  ret void
+}
diff --git a/llvm/test/Assembler/byref-parse-error-8.ll b/llvm/test/Assembler/byref-parse-error-8.ll
new file mode 100644 (file)
index 0000000..8f86b9f
--- /dev/null
@@ -0,0 +1,6 @@
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:27: error: invalid use of parameter-only attribute on a function{{$}}
+define void @test_byref() byref {
+  ret void
+}
diff --git a/llvm/test/Assembler/byref-parse-error-9.ll b/llvm/test/Assembler/byref-parse-error-9.ll
new file mode 100644 (file)
index 0000000..bc70706
--- /dev/null
@@ -0,0 +1,6 @@
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: <stdin>:[[@LINE+1]]:27: error: invalid use of parameter-only attribute on a function{{$}}
+define void @test_byref() byref=4 {
+  ret void
+}
index fbbe4a8..4095282 100644 (file)
@@ -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/llvm/test/CodeGen/X86/byref.ll b/llvm/test/CodeGen/X86/byref.ll
new file mode 100644 (file)
index 0000000..90d5e99
--- /dev/null
@@ -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/llvm/test/Instrumentation/AddressSanitizer/byref-args.ll b/llvm/test/Instrumentation/AddressSanitizer/byref-args.ll
new file mode 100644 (file)
index 0000000..df2eeb4
--- /dev/null
@@ -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/llvm/test/Transforms/DeadArgElim/byref.ll b/llvm/test/Transforms/DeadArgElim/byref.ll
new file mode 100644 (file)
index 0000000..95e839c
--- /dev/null
@@ -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/llvm/test/Transforms/Inline/byref-align.ll b/llvm/test/Transforms/Inline/byref-align.ll
new file mode 100644 (file)
index 0000000..fb70db2
--- /dev/null
@@ -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 ee5d792..ab59b89 100644 (file)
@@ -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/llvm/test/Verifier/byref.ll b/llvm/test/Verifier/byref.ll
new file mode 100644 (file)
index 0000000..5e7d587
--- /dev/null
@@ -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
+}