diff options
-rw-r--r-- | docs/InAlloca.rst | 140 | ||||
-rw-r--r-- | docs/LangRef.rst | 37 | ||||
-rw-r--r-- | include/llvm-c/Core.h | 3 | ||||
-rw-r--r-- | include/llvm/Bitcode/LLVMBitCodes.h | 3 | ||||
-rw-r--r-- | include/llvm/IR/Argument.h | 5 | ||||
-rw-r--r-- | include/llvm/IR/Attributes.h | 1 | ||||
-rw-r--r-- | include/llvm/Support/CallSite.h | 16 | ||||
-rw-r--r-- | lib/AsmParser/LLLexer.cpp | 1 | ||||
-rw-r--r-- | lib/AsmParser/LLParser.cpp | 3 | ||||
-rw-r--r-- | lib/AsmParser/LLToken.h | 1 | ||||
-rw-r--r-- | lib/Bitcode/Reader/BitcodeReader.cpp | 2 | ||||
-rw-r--r-- | lib/Bitcode/Writer/BitcodeWriter.cpp | 2 | ||||
-rw-r--r-- | lib/IR/Attributes.cpp | 6 | ||||
-rw-r--r-- | lib/IR/Function.cpp | 8 | ||||
-rw-r--r-- | lib/IR/Verifier.cpp | 78 | ||||
-rw-r--r-- | lib/Target/CppBackend/CPPBackend.cpp | 1 | ||||
-rw-r--r-- | test/Bitcode/attributes.ll | 5 | ||||
-rw-r--r-- | test/CodeGen/CPP/attributes.ll | 7 | ||||
-rw-r--r-- | test/Verifier/inalloca1.ll | 19 | ||||
-rw-r--r-- | test/Verifier/inalloca2.ll | 21 |
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 +} |