[llvm] 5e999cb - IR: Define byref parameter attribute

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 20 07:23:52 PDT 2020


Author: Matt Arsenault
Date: 2020-07-20T10:23:09-04:00
New Revision: 5e999cbe8db0b50dc9828a1c062b4ffe84c5b137

URL: https://github.com/llvm/llvm-project/commit/5e999cbe8db0b50dc9828a1c062b4ffe84c5b137
DIFF: https://github.com/llvm/llvm-project/commit/5e999cbe8db0b50dc9828a1c062b4ffe84c5b137.diff

LOG: IR: Define byref parameter attribute

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.

Added: 
    llvm/test/Assembler/byref-parse-error-0.ll
    llvm/test/Assembler/byref-parse-error-1.ll
    llvm/test/Assembler/byref-parse-error-10.ll
    llvm/test/Assembler/byref-parse-error-2.ll
    llvm/test/Assembler/byref-parse-error-3.ll
    llvm/test/Assembler/byref-parse-error-4.ll
    llvm/test/Assembler/byref-parse-error-5.ll
    llvm/test/Assembler/byref-parse-error-6.ll
    llvm/test/Assembler/byref-parse-error-7.ll
    llvm/test/Assembler/byref-parse-error-8.ll
    llvm/test/Assembler/byref-parse-error-9.ll
    llvm/test/CodeGen/X86/byref.ll
    llvm/test/Instrumentation/AddressSanitizer/byref-args.ll
    llvm/test/Transforms/DeadArgElim/byref.ll
    llvm/test/Transforms/Inline/byref-align.ll
    llvm/test/Verifier/byref.ll

Modified: 
    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/Bitcode/attributes.ll
    llvm/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll

Removed: 
    


################################################################################
diff  --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 86d8c62af2b7..0c1039f60c0b 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/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(<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>)``

diff  --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst
index 6293c82e3538..42867a89da63 100644
--- a/llvm/docs/ReleaseNotes.rst
+++ b/llvm/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/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index de4fe6630324..452918cde64b 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/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/llvm/include/llvm/IR/Argument.h b/llvm/include/llvm/IR/Argument.h
index 2ca18c610349..2dd7c6cbc8b8 100644
--- a/llvm/include/llvm/IR/Argument.h
+++ b/llvm/include/llvm/IR/Argument.h
@@ -65,6 +65,9 @@ class Argument final : public Value {
   /// 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 @@ class Argument final : public Value {
   /// 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 @@ class Argument final : public Value {
   /// 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/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h
index 58365aa2b764..03f2fc123cba 100644
--- a/llvm/include/llvm/IR/Attributes.h
+++ b/llvm/include/llvm/IR/Attributes.h
@@ -108,6 +108,7 @@ class Attribute {
                                         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 @@ class AttributeSet {
   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 @@ class AttributeList {
   /// 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 @@ class AttrBuilder {
   /// 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 @@ class AttrBuilder {
   /// 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/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index 395f9dbfb176..214eb84b6a47 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/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/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h
index bb4ec13c7610..9bbd0cb6b20f 100644
--- a/llvm/include/llvm/IR/Function.h
+++ b/llvm/include/llvm/IR/Function.h
@@ -467,6 +467,11 @@ class Function : public GlobalObject, public ilist_node<Function> {
     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/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp
index 204f855d28b3..402a2bcadf5f 100644
--- a/llvm/lib/Analysis/MemoryBuiltins.cpp
+++ b/llvm/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<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);
 }
 

diff  --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index 777ce3abdddd..591c6c3be2f1 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/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/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index c9f21ee83826..1a1c1d8eec99 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/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(<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 ]* ']'

diff  --git a/llvm/lib/AsmParser/LLParser.h b/llvm/lib/AsmParser/LLParser.h
index ebd8655dc35e..333b8a35cfce 100644
--- a/llvm/lib/AsmParser/LLParser.h
+++ b/llvm/lib/AsmParser/LLParser.h
@@ -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();

diff  --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h
index 0fb3bae77dd3..8bdeb57999c6 100644
--- a/llvm/lib/AsmParser/LLToken.h
+++ b/llvm/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/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 659e26c2bd25..86b8f5ca5b84 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/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/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 9c15a5f9f193..4cab4e06bc6a 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/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/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index fd08310316b3..83ad4e2b2739 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/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/llvm/lib/IR/AttributeImpl.h b/llvm/lib/IR/AttributeImpl.h
index 5c334348cde3..b013474d9278 100644
--- a/llvm/lib/IR/AttributeImpl.h
+++ b/llvm/lib/IR/AttributeImpl.h
@@ -251,6 +251,7 @@ class AttributeSetNode final
   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 *;

diff  --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index 8bf4e82357c6..43074e8193b6 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/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/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp
index 8db2389ef542..35472f7473b4 100644
--- a/llvm/lib/IR/Function.cpp
+++ b/llvm/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<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");

diff  --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index c518ae87ea9b..ea562f64da23 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/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<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;
 }

diff  --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
index 8cdbb9d35652..91036c510e4d 100644
--- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp
+++ b/llvm/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/llvm/test/Assembler/byref-parse-error-0.ll b/llvm/test/Assembler/byref-parse-error-0.ll
new file mode 100644
index 000000000000..287cd8453a65
--- /dev/null
+++ b/llvm/test/Assembler/byref-parse-error-0.ll
@@ -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
index 000000000000..3f189d340c9e
--- /dev/null
+++ b/llvm/test/Assembler/byref-parse-error-1.ll
@@ -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
index 000000000000..55997e5ffe54
--- /dev/null
+++ b/llvm/test/Assembler/byref-parse-error-10.ll
@@ -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
index 000000000000..6037c3ac3c73
--- /dev/null
+++ b/llvm/test/Assembler/byref-parse-error-2.ll
@@ -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
index 000000000000..b86eab4e69c3
--- /dev/null
+++ b/llvm/test/Assembler/byref-parse-error-3.ll
@@ -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
index 000000000000..6b6a6c25715c
--- /dev/null
+++ b/llvm/test/Assembler/byref-parse-error-4.ll
@@ -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
index 000000000000..c1a154805f41
--- /dev/null
+++ b/llvm/test/Assembler/byref-parse-error-5.ll
@@ -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
index 000000000000..c5ecf83596e2
--- /dev/null
+++ b/llvm/test/Assembler/byref-parse-error-6.ll
@@ -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
index 000000000000..9ac60af5348d
--- /dev/null
+++ b/llvm/test/Assembler/byref-parse-error-7.ll
@@ -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
index 000000000000..8f86b9f8afc4
--- /dev/null
+++ b/llvm/test/Assembler/byref-parse-error-8.ll
@@ -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
index 000000000000..bc707062a2e8
--- /dev/null
+++ b/llvm/test/Assembler/byref-parse-error-9.ll
@@ -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/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll
index fbbe4a80f31e..409528230a7d 100644
--- a/llvm/test/Bitcode/attributes.ll
+++ b/llvm/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/llvm/test/CodeGen/X86/byref.ll b/llvm/test/CodeGen/X86/byref.ll
new file mode 100644
index 000000000000..90d5e9901b67
--- /dev/null
+++ b/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 at 4
+  call x86_stdcallcc void @foo_byref_stdcall_p(%Foo* byref(%Foo) %value)
+; CHECK-NOT: %esp
+; CHECK: pushl
+; CHECK: calll _i at 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
index 000000000000..df2eeb4c1847
--- /dev/null
+++ b/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)

diff  --git a/llvm/test/Transforms/DeadArgElim/byref.ll b/llvm/test/Transforms/DeadArgElim/byref.ll
new file mode 100644
index 000000000000..95e839c04aa2
--- /dev/null
+++ b/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
+}

diff  --git a/llvm/test/Transforms/Inline/byref-align.ll b/llvm/test/Transforms/Inline/byref-align.ll
new file mode 100644
index 000000000000..fb70db2af449
--- /dev/null
+++ b/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 }

diff  --git a/llvm/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll b/llvm/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll
index ee5d792d5329..ab59b8950d70 100644
--- a/llvm/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll
+++ b/llvm/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/llvm/test/Verifier/byref.ll b/llvm/test/Verifier/byref.ll
new file mode 100644
index 000000000000..5e7d5873a8db
--- /dev/null
+++ b/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
+}


        


More information about the llvm-commits mailing list