Begin adding docs and IR-level support for the inalloca attribute
authorReid Kleckner <reid@kleckner.net>
Thu, 19 Dec 2013 02:14:12 +0000 (02:14 +0000)
committerReid Kleckner <reid@kleckner.net>
Thu, 19 Dec 2013 02:14:12 +0000 (02:14 +0000)
The inalloca attribute is designed to support passing C++ objects by
value in the Microsoft C++ ABI.  It behaves the same as byval, except
that it always implies that the argument is in memory and that the bytes
are never copied.  This attribute allows the caller to take the address
of an outgoing argument's memory and execute arbitrary code to store
into it.

This patch adds basic IR support, docs, and verification.  It does not
attempt to implement any lowering or fix any possibly broken transforms.

When this patch lands, a complete description of this feature should
appear at http://llvm.org/docs/InAlloca.html .

Differential Revision: http://llvm-reviews.chandlerc.com/D2173

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@197645 91177308-0d34-0410-b5e6-96231b3b80d8

20 files changed:
docs/InAlloca.rst [new file with mode: 0644]
docs/LangRef.rst
include/llvm-c/Core.h
include/llvm/Bitcode/LLVMBitCodes.h
include/llvm/IR/Argument.h
include/llvm/IR/Attributes.h
include/llvm/Support/CallSite.h
lib/AsmParser/LLLexer.cpp
lib/AsmParser/LLParser.cpp
lib/AsmParser/LLToken.h
lib/Bitcode/Reader/BitcodeReader.cpp
lib/Bitcode/Writer/BitcodeWriter.cpp
lib/IR/Attributes.cpp
lib/IR/Function.cpp
lib/IR/Verifier.cpp
lib/Target/CppBackend/CPPBackend.cpp
test/Bitcode/attributes.ll
test/CodeGen/CPP/attributes.ll [new file with mode: 0644]
test/Verifier/inalloca1.ll [new file with mode: 0644]
test/Verifier/inalloca2.ll [new file with mode: 0644]

diff --git a/docs/InAlloca.rst b/docs/InAlloca.rst
new file mode 100644 (file)
index 0000000..b177987
--- /dev/null
@@ -0,0 +1,140 @@
+==========================================
+Design and Usage of the InAlloca Attribute
+==========================================
+
+Introduction
+============
+
+.. Warning:: This feature is unstable and not fully implemented.
+
+The :ref:`attr_inalloca` attribute is designed to allow taking the
+address of an aggregate argument that is being passed by value through
+memory.  Primarily, this feature is required for compatibility with the
+Microsoft C++ ABI.  Under that ABI, class instances that are passed by
+value are constructed directly into argument stack memory.  Prior to the
+addition of inalloca, calls in LLVM were indivisible instructions.
+There was no way to perform intermediate work, such as object
+construction, between the first stack adjustment and the final control
+transfer.  With inalloca, each argument is modelled as an alloca, which
+can be stored to independently of the call.  Unfortunately, this
+complicated feature comes with a large set of restrictions designed to
+bound the lifetime of the argument memory around the call, which are
+explained in this document.
+
+For now, it is recommended that frontends and optimizers avoid producing
+this construct, primarily because it forces the use of a base pointer.
+This feature may grow in the future to allow general mid-level
+optimization, but for now, it should be regarded as less efficient than
+passing by value with a copy.
+
+Intended Usage
+==============
+
+In the example below, ``f`` is attempting to pass a default-constructed
+``Foo`` object to ``g`` by value.
+
+.. code-block:: llvm
+
+    %Foo = type { i32, i32 }
+    declare void @Foo_ctor(%Foo* %this)
+    declare void @g(%Foo* inalloca %arg)
+
+    define void @f() {
+      ...
+
+    bb1:
+      %base = call i8* @llvm.stacksave()
+      %arg = alloca %Foo
+      invoke void @Foo_ctor(%Foo* %arg)
+          to label %invoke.cont unwind %invoke.unwind
+
+    invoke.cont:
+      call void @g(%Foo* inalloca %arg)
+      call void @llvm.stackrestore(i8* %base)
+      ...
+
+    invoke.unwind:
+      call void @llvm.stackrestore(i8* %base)
+      ...
+    }
+
+The alloca in this example is dynamic, meaning it is not in the entry
+block, and it can be executed more than once.  Due to the restrictions
+against allocas between an alloca used with inalloca and its associated
+call site, all allocas used with inalloca are considered dynamic.
+
+To avoid any stack leakage, the frontend saves the current stack pointer
+with a call to :ref:`llvm.stacksave <int_stacksave>`.  Then, it
+allocates the argument stack space with alloca and calls the default
+constructor.  One important consideration is that the default
+constructor could throw an exception, so the frontend has to create a
+landing pad.  At this point, if there were any other inalloca arguments,
+the frontend would have to destruct them before restoring the stack
+pointer.  If the constructor does not unwind, ``g`` is called, and then
+the stack is restored.
+
+Design Considerations
+=====================
+
+Lifetime
+--------
+
+The biggest design consideration for this feature is object lifetime.
+We cannot model the arguments as static allocas in the entry block,
+because all calls need to use the memory that is at the end of the call
+frame to pass arguments.  We cannot vend pointers to that memory at
+function entry because after code generation they will alias.  In the
+current design, the rule against allocas between the inalloca alloca
+values and the call site avoids this problem, but it creates a cleanup
+problem.  Cleanup and lifetime is handled explicitly with stack save and
+restore calls.  In the future, we may be able to avoid this by using
+:ref:`llvm.lifetime.start <int_lifestart>` and :ref:`llvm.lifetime.end
+<int_lifeend>` instead.
+
+Nested Calls and Copy Elision
+-----------------------------
+
+The next consideration is the ability for the frontend to perform copy
+elision in the face of nested calls.  Consider the evaluation of
+``foo(foo(Bar()))``, where ``foo`` takes and returns a ``Bar`` object by
+value and ``Bar`` has non-trivial constructors.  In this case, we want
+to be able to elide copies into ``foo``'s argument slots.  That means we
+need to have more than one set of argument frames active at the same
+time.  First, we need to allocate the frame for the outer call so we can
+pass it in as the hidden struct return pointer to the middle call.  Then
+we do the same for the middle call, allocating a frame and passing its
+address to ``Bar``'s default constructor.  By wrapping the evaluation of
+the inner ``foo`` with stack save and restore, we can have multiple
+overlapping active call frames.
+
+Callee-cleanup Calling Conventions
+----------------------------------
+
+Another wrinkle is the existence of callee-cleanup conventions.  On
+Windows, all methods and many other functions adjust the stack to clear
+the memory used to pass their arguments.  In some sense, this means that
+the allocas are automatically cleared by the call.  However, LLVM
+instead models this as a write of undef to all of the inalloca values
+passed to the call instead of a stack adjustment.  Frontends should
+still restore the stack pointer to avoid a stack leak.
+
+Exceptions
+----------
+
+There is also the possibility of an exception.  If argument evaluation
+or copy construction throws an exception, the landing pad must do
+cleanup, which includes adjusting the stack pointer to avoid a stack
+leak.  This means the cleanup of the stack memory cannot be tied to the
+call itself.  There needs to be a separate IR-level instruction that can
+perform independent cleanup of arguments.
+
+Efficiency
+----------
+
+Eventually, it should be possible to generate efficient code for this
+construct.  In particular, using inalloca should not require a base
+pointer.  If the backend can prove that all points in the CFG only have
+one possible stack level, then it can address the stack directly from
+the stack pointer.  While this is not yet implemented, the plan is that
+the inalloca attribute should not change much, but the frontend IR
+generation recommendations may change.
index 2faa15692bb0651b184d891b0cca5c4ac0618f85..86b5a15f254821a8d91e2aca790cbd13891d76fd 100644 (file)
@@ -697,6 +697,39 @@ Currently, only the following parameter attributes are defined:
     site. If the alignment is not specified, then the code generator
     makes a target-specific assumption.
 
     site. If the alignment is not specified, then the code generator
     makes a target-specific assumption.
 
+.. _attr_inalloca:
+
+``inalloca``
+
+.. Warning:: This feature is unstable and not fully implemented.
+
+    The ``inalloca`` argument attribute allows the caller to get the
+    address of an outgoing argument to a ``call`` or ``invoke`` before
+    it executes.  It is similar to ``byval`` in that it is used to pass
+    arguments by value, but it guarantees that the argument will not be
+    copied.
+
+    To be :ref:`well formed <wellformed>`, the caller must pass in an
+    alloca value into an ``inalloca`` parameter, and an alloca may be
+    used as an ``inalloca`` argument at most once.  The attribute can
+    only be applied to parameters that would be passed in memory and not
+    registers.  The ``inalloca`` attribute cannot be used in conjunction
+    with other attributes that affect argument storage, like ``inreg``,
+    ``nest``, ``sret``, or ``byval``.  The ``inalloca`` stack space is
+    considered to be clobbered by any call that uses it, so any
+    ``inalloca`` parameters cannot be marked ``readonly``.
+
+    Allocas passed with ``inalloca`` to a call must be in the opposite
+    order of the parameter list, meaning that the rightmost argument
+    must be allocated first.  If a call has inalloca arguments, no other
+    allocas can occur between the first alloca used by the call and the
+    call site, unless they are are cleared by calls to
+    :ref:`llvm.stackrestore <int_stackrestore>`.  Violating these rules
+    results in undefined behavior at runtime.
+
+    See :doc:`InAlloca` for more information on how to use this
+    attribute.
+
 ``sret``
     This indicates that the pointer parameter specifies the address of a
     structure that is the return value of the function in the source
 ``sret``
     This indicates that the pointer parameter specifies the address of a
     structure that is the return value of the function in the source
@@ -8419,6 +8452,8 @@ Memory Use Markers
 This class of intrinsics exists to information about the lifetime of
 memory objects and ranges where variables are immutable.
 
 This class of intrinsics exists to information about the lifetime of
 memory objects and ranges where variables are immutable.
 
+.. _int_lifestart:
+
 '``llvm.lifetime.start``' Intrinsic
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 '``llvm.lifetime.start``' Intrinsic
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -8450,6 +8485,8 @@ of the memory pointed to by ``ptr`` is dead. This means that it is known
 to never be used and has an undefined value. A load from the pointer
 that precedes this intrinsic can be replaced with ``'undef'``.
 
 to never be used and has an undefined value. A load from the pointer
 that precedes this intrinsic can be replaced with ``'undef'``.
 
+.. _int_lifeend:
+
 '``llvm.lifetime.end``' Intrinsic
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 '``llvm.lifetime.end``' Intrinsic
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
index c5707224e6496dd3853180a94ef4a733e15f7f82..269869ef0da755fd7c311707ca28cca8feeab44e 100644 (file)
@@ -167,7 +167,8 @@ typedef enum {
     LLVMAddressSafety = 1ULL << 32,
     LLVMStackProtectStrongAttribute = 1ULL<<33,
     LLVMCold = 1ULL << 34,
     LLVMAddressSafety = 1ULL << 32,
     LLVMStackProtectStrongAttribute = 1ULL<<33,
     LLVMCold = 1ULL << 34,
-    LLVMOptimizeNone = 1ULL << 35
+    LLVMOptimizeNone = 1ULL << 35,
+    LLVMInAllocaAttribute = 1ULL << 36
     */
 } LLVMAttribute;
 
     */
 } LLVMAttribute;
 
index b3d24661d70beca91e7002dc503bb512884c195b..7e6831bb5f800529c5064119de6ea52866c03eab 100644 (file)
@@ -370,7 +370,8 @@ namespace bitc {
     ATTR_KIND_Z_EXT = 34,
     ATTR_KIND_BUILTIN = 35,
     ATTR_KIND_COLD = 36,
     ATTR_KIND_Z_EXT = 34,
     ATTR_KIND_BUILTIN = 35,
     ATTR_KIND_COLD = 36,
-    ATTR_KIND_OPTIMIZE_NONE = 37
+    ATTR_KIND_OPTIMIZE_NONE = 37,
+    ATTR_KIND_IN_ALLOCA = 38
   };
 
 } // End bitc namespace
   };
 
 } // End bitc namespace
index eb6ed46b473e95ec3230385629ee34bc266832d5..9ba51bc2137467c8e6de8a6e0b13eba23d186a7a 100644 (file)
@@ -59,7 +59,7 @@ public:
   /// containing function.
   bool hasByValAttr() const;
 
   /// containing function.
   bool hasByValAttr() const;
 
-  /// \brief If this is a byval argument, return its alignment.
+  /// \brief If this is a byval or inalloca argument, return its alignment.
   unsigned getParamAlignment() const;
 
   /// \brief Return true if this argument has the nest attribute on it in its
   unsigned getParamAlignment() const;
 
   /// \brief Return true if this argument has the nest attribute on it in its
@@ -86,6 +86,9 @@ public:
   /// on it in its containing function.
   bool onlyReadsMemory() const;
 
   /// on it in its containing function.
   bool onlyReadsMemory() const;
 
+  /// \brief Return true if this argument has the inalloca attribute on it in
+  /// its containing function.
+  bool hasInAllocaAttr() const;
 
   /// \brief Add a Attribute to an argument.
   void addAttr(AttributeSet AS);
 
   /// \brief Add a Attribute to an argument.
   void addAttr(AttributeSet AS);
index c23ba0f73c58b2606f570b164c976d57767f2ea3..bb62492db8ed0ca830f2d78667d40ca7fa92a2ec 100644 (file)
@@ -71,6 +71,7 @@ public:
     Builtin,               ///< Callee is recognized as a builtin, despite
                            ///< nobuiltin attribute on its declaration.
     ByVal,                 ///< Pass structure by value
     Builtin,               ///< Callee is recognized as a builtin, despite
                            ///< nobuiltin attribute on its declaration.
     ByVal,                 ///< Pass structure by value
+    InAlloca,              ///< Pass structure in an alloca
     Cold,                  ///< Marks function as being in a cold path.
     InlineHint,            ///< Source said inlining was desirable
     InReg,                 ///< Force argument to be passed in register
     Cold,                  ///< Marks function as being in a cold path.
     InlineHint,            ///< Source said inlining was desirable
     InReg,                 ///< Force argument to be passed in register
index 2a1c5ca4d47b6ef78cce7e26a1a512071d01b973..d2c47b53ed3efa8e0da6b8f8695d20e3e7118909 100644 (file)
@@ -257,6 +257,22 @@ public:
     return paramHasAttr(ArgNo + 1, Attribute::ByVal);
   }
 
     return paramHasAttr(ArgNo + 1, Attribute::ByVal);
   }
 
+  /// @brief Determine whether this argument is passed in an alloca.
+  bool isInAllocaArgument(unsigned ArgNo) const {
+    return paramHasAttr(ArgNo + 1, Attribute::InAlloca);
+  }
+
+  /// @brief Determine whether this argument is passed by value or in an alloca.
+  bool isByValOrInAllocaArgument(unsigned ArgNo) const {
+    return paramHasAttr(ArgNo + 1, Attribute::ByVal) ||
+           paramHasAttr(ArgNo + 1, Attribute::InAlloca);
+  }
+
+  /// @brief Determine if there are any inalloca arguments.
+  bool hasInAllocaArgument() const {
+    return getAttributes().hasAttrSomewhere(Attribute::InAlloca);
+  }
+
   bool doesNotAccessMemory(unsigned ArgNo) const {
     return paramHasAttr(ArgNo + 1, Attribute::ReadNone);
   }
   bool doesNotAccessMemory(unsigned ArgNo) const {
     return paramHasAttr(ArgNo + 1, Attribute::ReadNone);
   }
index 3c384f5fcca07727ea707c9436d1234cc60c7a4c..1b32047e4bf60fd55cc9e7f10b27db62aa0fcdff 100644 (file)
@@ -572,6 +572,7 @@ lltok::Kind LLLexer::LexIdentifier() {
   KEYWORD(alwaysinline);
   KEYWORD(builtin);
   KEYWORD(byval);
   KEYWORD(alwaysinline);
   KEYWORD(builtin);
   KEYWORD(byval);
+  KEYWORD(inalloca);
   KEYWORD(cold);
   KEYWORD(inlinehint);
   KEYWORD(inreg);
   KEYWORD(cold);
   KEYWORD(inlinehint);
   KEYWORD(inreg);
index 3b903cdb0906b357aabddbd753012b6bd5e09f7a..a5b2aa586e46ce08e839d5b402dda40982182e29 100644 (file)
@@ -944,6 +944,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
               "invalid use of attribute on a function");
       break;
     case lltok::kw_byval:
               "invalid use of attribute on a function");
       break;
     case lltok::kw_byval:
+    case lltok::kw_inalloca:
     case lltok::kw_nest:
     case lltok::kw_noalias:
     case lltok::kw_nocapture:
     case lltok::kw_nest:
     case lltok::kw_noalias:
     case lltok::kw_nocapture:
@@ -1156,6 +1157,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
       continue;
     }
     case lltok::kw_byval:           B.addAttribute(Attribute::ByVal); break;
       continue;
     }
     case lltok::kw_byval:           B.addAttribute(Attribute::ByVal); break;
+    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;
     case lltok::kw_noalias:         B.addAttribute(Attribute::NoAlias); break;
     case lltok::kw_inreg:           B.addAttribute(Attribute::InReg); break;
     case lltok::kw_nest:            B.addAttribute(Attribute::Nest); break;
     case lltok::kw_noalias:         B.addAttribute(Attribute::NoAlias); break;
@@ -1218,6 +1220,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
     // Error handling.
     case lltok::kw_align:
     case lltok::kw_byval:
     // Error handling.
     case lltok::kw_align:
     case lltok::kw_byval:
+    case lltok::kw_inalloca:
     case lltok::kw_nest:
     case lltok::kw_nocapture:
     case lltok::kw_returned:
     case lltok::kw_nest:
     case lltok::kw_nocapture:
     case lltok::kw_returned:
index 786d84d766349298d1eb519b5457f0940a584cf8..5a6866dd14989cd94d0b993f9715c0605c4d7159 100644 (file)
@@ -99,6 +99,7 @@ namespace lltok {
     kw_sanitize_address,
     kw_builtin,
     kw_byval,
     kw_sanitize_address,
     kw_builtin,
     kw_byval,
+    kw_inalloca,
     kw_cold,
     kw_inlinehint,
     kw_inreg,
     kw_cold,
     kw_inlinehint,
     kw_inreg,
index ce3b7d163e51ad668baa3e3b61ed241c372e7dc4..37515eb6f285457d4f64f6c1515ca45ad672853f 100644 (file)
@@ -522,6 +522,8 @@ static Attribute::AttrKind GetAttrFromCode(uint64_t Code) {
     return Attribute::Builtin;
   case bitc::ATTR_KIND_BY_VAL:
     return Attribute::ByVal;
     return Attribute::Builtin;
   case bitc::ATTR_KIND_BY_VAL:
     return Attribute::ByVal;
+  case bitc::ATTR_KIND_IN_ALLOCA:
+    return Attribute::InAlloca;
   case bitc::ATTR_KIND_COLD:
     return Attribute::Cold;
   case bitc::ATTR_KIND_INLINE_HINT:
   case bitc::ATTR_KIND_COLD:
     return Attribute::Cold;
   case bitc::ATTR_KIND_INLINE_HINT:
index caa9e5626306aa7d1ec3b71b2ea428a19a2d0b99..be19b781d4aacb8a989fb319df95e878484839d8 100644 (file)
@@ -169,6 +169,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
     return bitc::ATTR_KIND_BUILTIN;
   case Attribute::ByVal:
     return bitc::ATTR_KIND_BY_VAL;
     return bitc::ATTR_KIND_BUILTIN;
   case Attribute::ByVal:
     return bitc::ATTR_KIND_BY_VAL;
+  case Attribute::InAlloca:
+    return bitc::ATTR_KIND_IN_ALLOCA;
   case Attribute::Cold:
     return bitc::ATTR_KIND_COLD;
   case Attribute::InlineHint:
   case Attribute::Cold:
     return bitc::ATTR_KIND_COLD;
   case Attribute::InlineHint:
index 0f2b7a0ebb4f7cbe875490a6de09d7feed393cee..9d9d948527cf0a47fa41d9d4eed7b603485d5472 100644 (file)
@@ -166,6 +166,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
     return "builtin";
   if (hasAttribute(Attribute::ByVal))
     return "byval";
     return "builtin";
   if (hasAttribute(Attribute::ByVal))
     return "byval";
+  if (hasAttribute(Attribute::InAlloca))
+    return "inalloca";
   if (hasAttribute(Attribute::InlineHint))
     return "inlinehint";
   if (hasAttribute(Attribute::InReg))
   if (hasAttribute(Attribute::InlineHint))
     return "inlinehint";
   if (hasAttribute(Attribute::InReg))
@@ -388,6 +390,7 @@ uint64_t AttributeImpl::getAttrMask(Attribute::AttrKind Val) {
   case Attribute::Cold:            return 1ULL << 40;
   case Attribute::Builtin:         return 1ULL << 41;
   case Attribute::OptimizeNone:    return 1ULL << 42;
   case Attribute::Cold:            return 1ULL << 40;
   case Attribute::Builtin:         return 1ULL << 41;
   case Attribute::OptimizeNone:    return 1ULL << 42;
+  case Attribute::InAlloca:        return 1ULL << 43;
   }
   llvm_unreachable("Unsupported attribute type");
 }
   }
   llvm_unreachable("Unsupported attribute type");
 }
@@ -1174,7 +1177,8 @@ AttributeSet AttributeFuncs::typeIncompatible(Type *Ty, uint64_t Index) {
       .addAttribute(Attribute::NoCapture)
       .addAttribute(Attribute::ReadNone)
       .addAttribute(Attribute::ReadOnly)
       .addAttribute(Attribute::NoCapture)
       .addAttribute(Attribute::ReadNone)
       .addAttribute(Attribute::ReadOnly)
-      .addAttribute(Attribute::StructRet);
+      .addAttribute(Attribute::StructRet)
+      .addAttribute(Attribute::InAlloca);
 
   return AttributeSet::get(Ty->getContext(), Index, Incompatible);
 }
 
   return AttributeSet::get(Ty->getContext(), Index, Incompatible);
 }
index e8a2402b3d95e519e8b311c5e64904e7d63ebe5f..970bbaeed8b524c46e852840f7cb137eec26f7af 100644 (file)
@@ -84,6 +84,14 @@ bool Argument::hasByValAttr() const {
     hasAttribute(getArgNo()+1, Attribute::ByVal);
 }
 
     hasAttribute(getArgNo()+1, Attribute::ByVal);
 }
 
+/// \brief Return true if this argument has the inalloca attribute on it in
+/// its containing function.
+bool Argument::hasInAllocaAttr() const {
+  if (!getType()->isPointerTy()) return false;
+  return getParent()->getAttributes().
+    hasAttribute(getArgNo()+1, Attribute::InAlloca);
+}
+
 unsigned Argument::getParamAlignment() const {
   assert(getType()->isPointerTy() && "Only pointers have alignments");
   return getParent()->getParamAlignment(getArgNo()+1);
 unsigned Argument::getParamAlignment() const {
   assert(getType()->isPointerTy() && "Only pointers have alignments");
   return getParent()->getParamAlignment(getArgNo()+1);
index febd29f0725ae90ddff4a680894a29edb6856929..35b786ecf271c5072a7107342f13eb6f3fcaf33b 100644 (file)
@@ -813,26 +813,25 @@ void Verifier::VerifyParameterAttrs(AttributeSet Attrs, unsigned Idx, Type *Ty,
             !Attrs.hasAttribute(Idx, Attribute::Nest) &&
             !Attrs.hasAttribute(Idx, Attribute::StructRet) &&
             !Attrs.hasAttribute(Idx, Attribute::NoCapture) &&
             !Attrs.hasAttribute(Idx, Attribute::Nest) &&
             !Attrs.hasAttribute(Idx, Attribute::StructRet) &&
             !Attrs.hasAttribute(Idx, Attribute::NoCapture) &&
-            !Attrs.hasAttribute(Idx, Attribute::Returned),
-            "Attribute 'byval', 'nest', 'sret', 'nocapture', and 'returned' "
-            "do not apply to return values!", V);
-
-  // Check for mutually incompatible attributes.
-  Assert1(!((Attrs.hasAttribute(Idx, Attribute::ByVal) &&
-             Attrs.hasAttribute(Idx, Attribute::Nest)) ||
-            (Attrs.hasAttribute(Idx, Attribute::ByVal) &&
-             Attrs.hasAttribute(Idx, Attribute::StructRet)) ||
-            (Attrs.hasAttribute(Idx, Attribute::Nest) &&
-             Attrs.hasAttribute(Idx, Attribute::StructRet))), "Attributes "
-          "'byval, nest, and sret' are incompatible!", V);
-
-  Assert1(!((Attrs.hasAttribute(Idx, Attribute::ByVal) &&
-             Attrs.hasAttribute(Idx, Attribute::Nest)) ||
-            (Attrs.hasAttribute(Idx, Attribute::ByVal) &&
-             Attrs.hasAttribute(Idx, Attribute::InReg)) ||
-            (Attrs.hasAttribute(Idx, Attribute::Nest) &&
-             Attrs.hasAttribute(Idx, Attribute::InReg))), "Attributes "
-          "'byval, nest, and inreg' are incompatible!", V);
+            !Attrs.hasAttribute(Idx, Attribute::Returned) &&
+            !Attrs.hasAttribute(Idx, Attribute::InAlloca),
+            "Attributes 'byval', 'inalloca', 'nest', 'sret', 'nocapture', and "
+            "'returned' do not apply to return values!", V);
+
+  // Check for mutually incompatible attributes.  Only inreg is compatible with
+  // sret.
+  unsigned AttrCount = 0;
+  AttrCount += Attrs.hasAttribute(Idx, Attribute::ByVal);
+  AttrCount += Attrs.hasAttribute(Idx, Attribute::InAlloca);
+  AttrCount += Attrs.hasAttribute(Idx, Attribute::StructRet) ||
+               Attrs.hasAttribute(Idx, Attribute::InReg);
+  AttrCount += Attrs.hasAttribute(Idx, Attribute::Nest);
+  Assert1(AttrCount <= 1, "Attributes 'byval', 'inalloca', 'inreg', 'nest', "
+                          "and 'sret' are incompatible!", V);
+
+  Assert1(!(Attrs.hasAttribute(Idx, Attribute::InAlloca) &&
+            Attrs.hasAttribute(Idx, Attribute::ReadOnly)), "Attributes "
+          "'inalloca and readonly' are incompatible!", V);
 
   Assert1(!(Attrs.hasAttribute(Idx, Attribute::StructRet) &&
             Attrs.hasAttribute(Idx, Attribute::Returned)), "Attributes "
 
   Assert1(!(Attrs.hasAttribute(Idx, Attribute::StructRet) &&
             Attrs.hasAttribute(Idx, Attribute::Returned)), "Attributes "
@@ -855,14 +854,18 @@ void Verifier::VerifyParameterAttrs(AttributeSet Attrs, unsigned Idx, Type *Ty,
           "Wrong types for attribute: " +
           AttributeFuncs::typeIncompatible(Ty, Idx).getAsString(Idx), V);
 
           "Wrong types for attribute: " +
           AttributeFuncs::typeIncompatible(Ty, Idx).getAsString(Idx), V);
 
-  if (PointerType *PTy = dyn_cast<PointerType>(Ty))
-    Assert1(!Attrs.hasAttribute(Idx, Attribute::ByVal) ||
-            PTy->getElementType()->isSized(),
-            "Attribute 'byval' does not support unsized types!", V);
-  else
+  if (PointerType *PTy = dyn_cast<PointerType>(Ty)) {
+    if (!PTy->getElementType()->isSized()) {
+      Assert1(!Attrs.hasAttribute(Idx, Attribute::ByVal) &&
+              !Attrs.hasAttribute(Idx, Attribute::InAlloca),
+              "Attributes 'byval' and 'inalloca' do not support unsized types!",
+              V);
+    }
+  } else {
     Assert1(!Attrs.hasAttribute(Idx, Attribute::ByVal),
             "Attribute 'byval' only applies to parameters with pointer type!",
             V);
     Assert1(!Attrs.hasAttribute(Idx, Attribute::ByVal),
             "Attribute 'byval' only applies to parameters with pointer type!",
             V);
+  }
 }
 
 // VerifyFunctionAttrs - Check parameter attributes against a function type.
 }
 
 // VerifyFunctionAttrs - Check parameter attributes against a function type.
@@ -1533,6 +1536,15 @@ void Verifier::VerifyCallSite(CallSite CS) {
   // Verify call attributes.
   VerifyFunctionAttrs(FTy, Attrs, I);
 
   // Verify call attributes.
   VerifyFunctionAttrs(FTy, Attrs, I);
 
+  // Verify that values used for inalloca parameters are in fact allocas.
+  for (unsigned i = 0, e = CS.arg_size(); i != e; ++i) {
+    if (!Attrs.hasAttribute(1 + i, Attribute::InAlloca))
+      continue;
+    Value *Arg = CS.getArgument(i);
+    Assert2(isa<AllocaInst>(Arg), "Inalloca argument is not an alloca!", I,
+            Arg);
+  }
+
   if (FTy->isVarArg()) {
     // FIXME? is 'nest' even legal here?
     bool SawNest = false;
   if (FTy->isVarArg()) {
     // FIXME? is 'nest' even legal here?
     bool SawNest = false;
@@ -1870,6 +1882,22 @@ void Verifier::visitAllocaInst(AllocaInst &AI) {
           &AI);
   Assert1(AI.getArraySize()->getType()->isIntegerTy(),
           "Alloca array size must have integer type", &AI);
           &AI);
   Assert1(AI.getArraySize()->getType()->isIntegerTy(),
           "Alloca array size must have integer type", &AI);
+
+  // Verify that an alloca instruction is not used with inalloca more than once.
+  unsigned InAllocaUses = 0;
+  for (User::use_iterator UI = AI.use_begin(), UE = AI.use_end(); UI != UE;
+       ++UI) {
+    CallSite CS(*UI);
+    if (!CS)
+      continue;
+    unsigned ArgNo = CS.getArgumentNo(UI);
+    if (CS.isInAllocaArgument(ArgNo)) {
+      InAllocaUses++;
+      Assert1(InAllocaUses <= 1,
+              "Allocas can be used at most once with inalloca!", &AI);
+    }
+  }
+
   visitInstruction(AI);
 }
 
   visitInstruction(AI);
 }
 
index 07a9db35a1f354c04294904bcea62afbec5c4844..c290f70e9e4ccc8c404004fefc48ab69e1e1c3f2 100644 (file)
@@ -491,6 +491,7 @@ void CppWriter::printAttributes(const AttributeSet &PAL,
       HANDLE_ATTR(NoUnwind);
       HANDLE_ATTR(NoAlias);
       HANDLE_ATTR(ByVal);
       HANDLE_ATTR(NoUnwind);
       HANDLE_ATTR(NoAlias);
       HANDLE_ATTR(ByVal);
+      HANDLE_ATTR(InAlloca);
       HANDLE_ATTR(Nest);
       HANDLE_ATTR(ReadNone);
       HANDLE_ATTR(ReadOnly);
       HANDLE_ATTR(Nest);
       HANDLE_ATTR(ReadNone);
       HANDLE_ATTR(ReadOnly);
index 1789878e9f50c088e9dc4a652823884bfcd95e17..545f1cbb28c5b214b6c1210d84b38cf70a332581 100644 (file)
@@ -213,6 +213,11 @@ define void @f35() optnone noinline
         ret void;
 }
 
         ret void;
 }
 
+define void @f36(i8* inalloca) {
+; CHECK: define void @f36(i8* inalloca) {
+        ret void
+}
+
 ; CHECK: attributes #0 = { noreturn }
 ; CHECK: attributes #1 = { nounwind }
 ; CHECK: attributes #2 = { readnone }
 ; CHECK: attributes #0 = { noreturn }
 ; CHECK: attributes #1 = { nounwind }
 ; CHECK: attributes #2 = { readnone }
diff --git a/test/CodeGen/CPP/attributes.ll b/test/CodeGen/CPP/attributes.ll
new file mode 100644 (file)
index 0000000..3dab617
--- /dev/null
@@ -0,0 +1,7 @@
+; RUN: llc < %s -march=cpp | FileCheck %s
+
+define void @f1(i8* byval, i8* inalloca) {
+; CHECK: ByVal
+; CHECK: InAlloca
+  ret void
+}
diff --git a/test/Verifier/inalloca1.ll b/test/Verifier/inalloca1.ll
new file mode 100644 (file)
index 0000000..9408c95
--- /dev/null
@@ -0,0 +1,19 @@
+; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
+
+declare void @a(i64* byval inalloca %p)
+; CHECK: Attributes {{.*}} are incompatible
+
+declare void @b(i64* inreg inalloca %p)
+; CHECK: Attributes {{.*}} are incompatible
+
+declare void @c(i64* sret inalloca %p)
+; CHECK: Attributes {{.*}} are incompatible
+
+declare void @d(i64* nest inalloca %p)
+; CHECK: Attributes {{.*}} are incompatible
+
+declare void @e(i64* readonly inalloca %p)
+; CHECK: Attributes {{.*}} are incompatible
+
+declare void @f(void ()* inalloca %p)
+; CHECK: do not support unsized types
diff --git a/test/Verifier/inalloca2.ll b/test/Verifier/inalloca2.ll
new file mode 100644 (file)
index 0000000..acdd9ae
--- /dev/null
@@ -0,0 +1,21 @@
+; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
+
+declare void @doit(i64* inalloca %a)
+
+define void @a() {
+entry:
+  %a = alloca [2 x i32]
+  %b = bitcast [2 x i32]* %a to i64*
+  call void @doit(i64* inalloca %b)
+; CHECK: Inalloca argument is not an alloca!
+  ret void
+}
+
+define void @b() {
+entry:
+  %a = alloca i64
+  call void @doit(i64* inalloca %a)
+  call void @doit(i64* inalloca %a)
+; CHECK: Allocas can be used at most once with inalloca!
+  ret void
+}