summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/InAlloca.rst140
-rw-r--r--docs/LangRef.rst37
-rw-r--r--include/llvm-c/Core.h3
-rw-r--r--include/llvm/Bitcode/LLVMBitCodes.h3
-rw-r--r--include/llvm/IR/Argument.h5
-rw-r--r--include/llvm/IR/Attributes.h1
-rw-r--r--include/llvm/Support/CallSite.h16
-rw-r--r--lib/AsmParser/LLLexer.cpp1
-rw-r--r--lib/AsmParser/LLParser.cpp3
-rw-r--r--lib/AsmParser/LLToken.h1
-rw-r--r--lib/Bitcode/Reader/BitcodeReader.cpp2
-rw-r--r--lib/Bitcode/Writer/BitcodeWriter.cpp2
-rw-r--r--lib/IR/Attributes.cpp6
-rw-r--r--lib/IR/Function.cpp8
-rw-r--r--lib/IR/Verifier.cpp78
-rw-r--r--lib/Target/CppBackend/CPPBackend.cpp1
-rw-r--r--test/Bitcode/attributes.ll5
-rw-r--r--test/CodeGen/CPP/attributes.ll7
-rw-r--r--test/Verifier/inalloca1.ll19
-rw-r--r--test/Verifier/inalloca2.ll21
20 files changed, 330 insertions, 29 deletions
diff --git a/docs/InAlloca.rst b/docs/InAlloca.rst
new file mode 100644
index 0000000000..b1779874e0
--- /dev/null
+++ b/docs/InAlloca.rst
@@ -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.
diff --git a/docs/LangRef.rst b/docs/LangRef.rst
index 2faa15692b..86b5a15f25 100644
--- a/docs/LangRef.rst
+++ b/docs/LangRef.rst
@@ -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.
+.. _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
@@ -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.
+.. _int_lifestart:
+
'``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'``.
+.. _int_lifeend:
+
'``llvm.lifetime.end``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/include/llvm-c/Core.h b/include/llvm-c/Core.h
index c5707224e6..269869ef0d 100644
--- a/include/llvm-c/Core.h
+++ b/include/llvm-c/Core.h
@@ -167,7 +167,8 @@ typedef enum {
LLVMAddressSafety = 1ULL << 32,
LLVMStackProtectStrongAttribute = 1ULL<<33,
LLVMCold = 1ULL << 34,
- LLVMOptimizeNone = 1ULL << 35
+ LLVMOptimizeNone = 1ULL << 35,
+ LLVMInAllocaAttribute = 1ULL << 36
*/
} LLVMAttribute;
diff --git a/include/llvm/Bitcode/LLVMBitCodes.h b/include/llvm/Bitcode/LLVMBitCodes.h
index b3d24661d7..7e6831bb5f 100644
--- a/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/include/llvm/Bitcode/LLVMBitCodes.h
@@ -370,7 +370,8 @@ namespace bitc {
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
diff --git a/include/llvm/IR/Argument.h b/include/llvm/IR/Argument.h
index eb6ed46b47..9ba51bc213 100644
--- a/include/llvm/IR/Argument.h
+++ b/include/llvm/IR/Argument.h
@@ -59,7 +59,7 @@ public:
/// 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
@@ -86,6 +86,9 @@ public:
/// 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);
diff --git a/include/llvm/IR/Attributes.h b/include/llvm/IR/Attributes.h
index c23ba0f73c..bb62492db8 100644
--- a/include/llvm/IR/Attributes.h
+++ b/include/llvm/IR/Attributes.h
@@ -71,6 +71,7 @@ public:
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
diff --git a/include/llvm/Support/CallSite.h b/include/llvm/Support/CallSite.h
index 2a1c5ca4d4..d2c47b53ed 100644
--- a/include/llvm/Support/CallSite.h
+++ b/include/llvm/Support/CallSite.h
@@ -257,6 +257,22 @@ public:
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);
}
diff --git a/lib/AsmParser/LLLexer.cpp b/lib/AsmParser/LLLexer.cpp
index 3c384f5fcc..1b32047e4b 100644
--- a/lib/AsmParser/LLLexer.cpp
+++ b/lib/AsmParser/LLLexer.cpp
@@ -572,6 +572,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(alwaysinline);
KEYWORD(builtin);
KEYWORD(byval);
+ KEYWORD(inalloca);
KEYWORD(cold);
KEYWORD(inlinehint);
KEYWORD(inreg);
diff --git a/lib/AsmParser/LLParser.cpp b/lib/AsmParser/LLParser.cpp
index 3b903cdb09..a5b2aa586e 100644
--- a/lib/AsmParser/LLParser.cpp
+++ b/lib/AsmParser/LLParser.cpp
@@ -944,6 +944,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
"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:
@@ -1156,6 +1157,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
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;
@@ -1218,6 +1220,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
// 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:
diff --git a/lib/AsmParser/LLToken.h b/lib/AsmParser/LLToken.h
index 786d84d766..5a6866dd14 100644
--- a/lib/AsmParser/LLToken.h
+++ b/lib/AsmParser/LLToken.h
@@ -99,6 +99,7 @@ namespace lltok {
kw_sanitize_address,
kw_builtin,
kw_byval,
+ kw_inalloca,
kw_cold,
kw_inlinehint,
kw_inreg,
diff --git a/lib/Bitcode/Reader/BitcodeReader.cpp b/lib/Bitcode/Reader/BitcodeReader.cpp
index ce3b7d163e..37515eb6f2 100644
--- a/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -522,6 +522,8 @@ static Attribute::AttrKind GetAttrFromCode(uint64_t Code) {
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:
diff --git a/lib/Bitcode/Writer/BitcodeWriter.cpp b/lib/Bitcode/Writer/BitcodeWriter.cpp
index caa9e56263..be19b781d4 100644
--- a/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -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;
+ case Attribute::InAlloca:
+ return bitc::ATTR_KIND_IN_ALLOCA;
case Attribute::Cold:
return bitc::ATTR_KIND_COLD;
case Attribute::InlineHint:
diff --git a/lib/IR/Attributes.cpp b/lib/IR/Attributes.cpp
index 0f2b7a0ebb..9d9d948527 100644
--- a/lib/IR/Attributes.cpp
+++ b/lib/IR/Attributes.cpp
@@ -166,6 +166,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
return "builtin";
if (hasAttribute(Attribute::ByVal))
return "byval";
+ if (hasAttribute(Attribute::InAlloca))
+ return "inalloca";
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::InAlloca: return 1ULL << 43;
}
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::StructRet);
+ .addAttribute(Attribute::StructRet)
+ .addAttribute(Attribute::InAlloca);
return AttributeSet::get(Ty->getContext(), Index, Incompatible);
}
diff --git a/lib/IR/Function.cpp b/lib/IR/Function.cpp
index e8a2402b3d..970bbaeed8 100644
--- a/lib/IR/Function.cpp
+++ b/lib/IR/Function.cpp
@@ -84,6 +84,14 @@ bool Argument::hasByValAttr() const {
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);
diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp
index febd29f072..35b786ecf2 100644
--- a/lib/IR/Verifier.cpp
+++ b/lib/IR/Verifier.cpp
@@ -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::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 "
@@ -855,14 +854,18 @@ void Verifier::VerifyParameterAttrs(AttributeSet Attrs, unsigned Idx, Type *Ty,
"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);
+ }
}
// 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 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;
@@ -1870,6 +1882,22 @@ void Verifier::visitAllocaInst(AllocaInst &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);
}
diff --git a/lib/Target/CppBackend/CPPBackend.cpp b/lib/Target/CppBackend/CPPBackend.cpp
index 07a9db35a1..c290f70e9e 100644
--- a/lib/Target/CppBackend/CPPBackend.cpp
+++ b/lib/Target/CppBackend/CPPBackend.cpp
@@ -491,6 +491,7 @@ void CppWriter::printAttributes(const AttributeSet &PAL,
HANDLE_ATTR(NoUnwind);
HANDLE_ATTR(NoAlias);
HANDLE_ATTR(ByVal);
+ HANDLE_ATTR(InAlloca);
HANDLE_ATTR(Nest);
HANDLE_ATTR(ReadNone);
HANDLE_ATTR(ReadOnly);
diff --git a/test/Bitcode/attributes.ll b/test/Bitcode/attributes.ll
index 1789878e9f..545f1cbb28 100644
--- a/test/Bitcode/attributes.ll
+++ b/test/Bitcode/attributes.ll
@@ -213,6 +213,11 @@ define void @f35() optnone noinline
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 }
diff --git a/test/CodeGen/CPP/attributes.ll b/test/CodeGen/CPP/attributes.ll
new file mode 100644
index 0000000000..3dab617d80
--- /dev/null
+++ b/test/CodeGen/CPP/attributes.ll
@@ -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
index 0000000000..9408c95ee3
--- /dev/null
+++ b/test/Verifier/inalloca1.ll
@@ -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
index 0000000000..acdd9ae3a6
--- /dev/null
+++ b/test/Verifier/inalloca2.ll
@@ -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
+}