diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index a76770063657..8bb834b45ed3 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -778,19 +778,16 @@ Currently, only the following parameter attributes are defined: .. Warning:: This feature is unstable and not fully implemented. The ``inalloca`` argument attribute allows the caller to take the - address of all stack-allocated arguments 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. + address of outgoing stack arguments. An ``inalloca`` argument must + be a pointer to stack memory produced by an ``alloca`` instruction. + The alloca, or argument allocation, must also be tagged with the + inalloca keyword. Only the past argument may have the ``inalloca`` + attribute, and that argument is guaranteed to be passed in memory. - To be :ref:`well formed `, an alloca may be used as an - ``inalloca`` argument at most once. The attribute can only be - applied to the last parameter, and it guarantees that they are - passed in memory. 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``. + An argument allocation may be used by a call at most once because + the call may deallocate it. The ``inalloca`` attribute cannot be + used in conjunction with other attributes that affect argument + storage, like ``inreg``, ``nest``, ``sret``, or ``byval``. When the call site is reached, the argument allocation must have been the most recent stack allocation that is still live, or the @@ -4693,7 +4690,7 @@ Syntax: :: - = alloca [, ][, align ] ; yields {type*}:result + = alloca [, inalloca][, ][, align ] ; yields {type*}:result Overview: """"""""" diff --git a/llvm/include/llvm/IR/Argument.h b/llvm/include/llvm/IR/Argument.h index 9ba51bc21374..7c1ebf6dfffd 100644 --- a/llvm/include/llvm/IR/Argument.h +++ b/llvm/include/llvm/IR/Argument.h @@ -59,6 +59,11 @@ public: /// containing function. bool hasByValAttr() const; + /// \brief Return true if this argument has the byval attribute or inalloca + /// attribute on it in its containing function. These attributes both + /// represent arguments being passed by value. + bool hasByValOrInAllocaAttr() const; + /// \brief If this is a byval or inalloca argument, return its alignment. unsigned getParamAlignment() const; diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h index 0843d8fca3e2..eeea9945e8a4 100644 --- a/llvm/include/llvm/IR/Instructions.h +++ b/llvm/include/llvm/IR/Instructions.h @@ -101,7 +101,7 @@ public: /// by the instruction. /// unsigned getAlignment() const { - return (1u << getSubclassDataFromInstruction()) >> 1; + return (1u << (getSubclassDataFromInstruction() & 31)) >> 1; } void setAlignment(unsigned Align); @@ -110,6 +110,20 @@ public: /// into the prolog/epilog code, so it is basically free. bool isStaticAlloca() const; + /// \brief Return true if this alloca is used as an inalloca argument to a + /// call. Such allocas are never considered static even if they are in the + /// entry block. + bool isUsedWithInAlloca() const { + return getSubclassDataFromInstruction() & 32; + } + + /// \brief Specify whether this alloca is used to represent a the arguments to + /// a call. + void setUsedWithInAlloca(bool V) { + setInstructionSubclassData((getSubclassDataFromInstruction() & ~32) | + (V ? 32 : 0)); + } + // Methods for support type inquiry through isa, cast, and dyn_cast: static inline bool classof(const Instruction *I) { return (I->getOpcode() == Instruction::Alloca); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index a1b5f9946c96..ab4b48bf6eca 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -4069,31 +4069,42 @@ bool LLParser::ParseCall(Instruction *&Inst, PerFunctionState &PFS, //===----------------------------------------------------------------------===// /// ParseAlloc -/// ::= 'alloca' Type (',' TypeAndValue)? (',' OptionalInfo)? +/// ::= 'alloca' Type (',' 'inalloca')? (',' TypeAndValue)? (',' OptionalInfo)? int LLParser::ParseAlloc(Instruction *&Inst, PerFunctionState &PFS) { Value *Size = 0; LocTy SizeLoc; unsigned Alignment = 0; + bool IsInAlloca = false; Type *Ty = 0; if (ParseType(Ty)) return true; bool AteExtraComma = false; if (EatIfPresent(lltok::comma)) { - if (Lex.getKind() == lltok::kw_align) { - if (ParseOptionalAlignment(Alignment)) return true; - } else if (Lex.getKind() == lltok::MetadataVar) { - AteExtraComma = true; - } else { - if (ParseTypeAndValue(Size, SizeLoc, PFS) || - ParseOptionalCommaAlign(Alignment, AteExtraComma)) - return true; + bool HaveComma = true; + if (EatIfPresent(lltok::kw_inalloca)) { + IsInAlloca = true; + HaveComma = EatIfPresent(lltok::comma); + } + + if (HaveComma) { + if (Lex.getKind() == lltok::kw_align) { + if (ParseOptionalAlignment(Alignment)) return true; + } else if (Lex.getKind() == lltok::MetadataVar) { + AteExtraComma = true; + } else { + if (ParseTypeAndValue(Size, SizeLoc, PFS) || + ParseOptionalCommaAlign(Alignment, AteExtraComma)) + return true; + } } } if (Size && !Size->getType()->isIntegerTy()) return Error(SizeLoc, "element count must have integer type"); - Inst = new AllocaInst(Ty, Size, Alignment); + AllocaInst *AI = new AllocaInst(Ty, Size, Alignment); + AI->setUsedWithInAlloca(IsInAlloca); + Inst = AI; return AteExtraComma ? InstExtraComma : InstNormal; } diff --git a/llvm/lib/AsmParser/LLParser.h b/llvm/lib/AsmParser/LLParser.h index c62979e6f29e..d25374ff0531 100644 --- a/llvm/lib/AsmParser/LLParser.h +++ b/llvm/lib/AsmParser/LLParser.h @@ -211,6 +211,7 @@ namespace llvm { AtomicOrdering &Ordering); bool ParseOptionalStackAlignment(unsigned &Alignment); bool ParseOptionalCommaAlign(unsigned &Alignment, bool &AteExtraComma); + bool ParseOptionalCommaInAlloca(bool &IsInAlloca); bool ParseIndexList(SmallVectorImpl &Indices,bool &AteExtraComma); bool ParseIndexList(SmallVectorImpl &Indices) { bool AteExtraComma; diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp index bb6bef7bd026..c1fa3720e3f2 100644 --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -92,6 +92,13 @@ bool Argument::hasInAllocaAttr() const { hasAttribute(getArgNo()+1, Attribute::InAlloca); } +bool Argument::hasByValOrInAllocaAttr() const { + if (!getType()->isPointerTy()) return false; + AttributeSet Attrs = getParent()->getAttributes(); + return Attrs.hasAttribute(getArgNo() + 1, Attribute::ByVal) || + Attrs.hasAttribute(getArgNo() + 1, Attribute::InAlloca); +} + unsigned Argument::getParamAlignment() const { assert(getType()->isPointerTy() && "Only pointers have alignments"); return getParent()->getParamAlignment(getArgNo()+1); diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp index 761f60063fab..5a50b7365005 100644 --- a/llvm/lib/IR/Instructions.cpp +++ b/llvm/lib/IR/Instructions.cpp @@ -893,7 +893,8 @@ void AllocaInst::setAlignment(unsigned Align) { assert((Align & (Align-1)) == 0 && "Alignment is not a power of 2!"); assert(Align <= MaximumAlignment && "Alignment is greater than MaximumAlignment!"); - setInstructionSubclassData(Log2_32(Align) + 1); + setInstructionSubclassData((getSubclassDataFromInstruction() & ~31) | + (Log2_32(Align) + 1)); assert(getAlignment() == Align && "Alignment representation error!"); } @@ -916,7 +917,7 @@ bool AllocaInst::isStaticAlloca() const { // Must be in the entry block. const BasicBlock *Parent = getParent(); - return Parent == &Parent->getParent()->front(); + return Parent == &Parent->getParent()->front() && !isUsedWithInAlloca(); } //===----------------------------------------------------------------------===// diff --git a/llvm/test/Assembler/inalloca.ll b/llvm/test/Assembler/inalloca.ll new file mode 100644 index 000000000000..94fac26265c4 --- /dev/null +++ b/llvm/test/Assembler/inalloca.ll @@ -0,0 +1,16 @@ +; RUN: llvm-as %s -o /dev/null + +define void @a() { +entry: + %0 = alloca i32, inalloca + %1 = alloca [2 x i32], inalloca + %2 = alloca i32, inalloca, i32 2 + %3 = alloca i32, inalloca, i32 2, align 16 + %4 = alloca i32, inalloca, i32 2, align 16, !foo !0 + %5 = alloca i32, i32 2, align 16, !foo !0 + %6 = alloca i32, i32 2, align 16 + ret void +} + +!0 = metadata !{i32 662302, null} +!foo = !{ !0 } diff --git a/llvm/test/Verifier/inalloca-vararg.ll b/llvm/test/Verifier/inalloca-vararg.ll index 6729c4283046..8521ebce2d9c 100755 --- a/llvm/test/Verifier/inalloca-vararg.ll +++ b/llvm/test/Verifier/inalloca-vararg.ll @@ -2,7 +2,7 @@ declare void @h(i32, ...) define void @i() { - %args = alloca i32 + %args = alloca i32, inalloca call void (i32, ...)* @h(i32 1, i32* inalloca %args, i32 3) ; CHECK: inalloca isn't on the last argument! ret void diff --git a/llvm/test/Verifier/inalloca2.ll b/llvm/test/Verifier/inalloca2.ll index ed65667dc308..e4e81be38615 100644 --- a/llvm/test/Verifier/inalloca2.ll +++ b/llvm/test/Verifier/inalloca2.ll @@ -6,7 +6,7 @@ declare void @doit(i64* inalloca %a) define void @a() { entry: - %a = alloca [2 x i32] + %a = alloca [2 x i32], inalloca %b = bitcast [2 x i32]* %a to i64* call void @doit(i64* inalloca %b) ret void @@ -14,8 +14,26 @@ entry: define void @b() { entry: - %a = alloca i64 + %a = alloca i64, inalloca call void @doit(i64* inalloca %a) call void @doit(i64* inalloca %a) ret void } + +define void @c(i1 %cond) { +entry: + br i1 %cond, label %if, label %else + +if: + %a = alloca i64, inalloca + br label %call + +else: + %b = alloca i64, inalloca + br label %call + +call: + %args = phi i64* [ %a, %if ], [ %b, %else ] + call void @doit(i64* inalloca %args) + ret void +}