Skip to content

Commit

Permalink
Add IR constructs for preallocated (inalloca replacement)
Browse files Browse the repository at this point in the history
Add llvm.call.preallocated.{setup,arg} instrinsics.
Add "preallocated" operand bundle which takes a token produced by llvm.call.preallocated.setup.
Add "preallocated" parameter attribute, which is like byval but without the copy.

Verifier changes for these IR constructs.

See https://github.com/rnk/llvm-project/blob/call-setup-docs/llvm/docs/CallSetup.md

Subscribers: hiraditya, jdoerfert, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D74651
  • Loading branch information
aeubanks committed Apr 27, 2020
1 parent 9ea5cc8 commit 3b0450a
Show file tree
Hide file tree
Showing 23 changed files with 564 additions and 37 deletions.
137 changes: 137 additions & 0 deletions llvm/docs/LangRef.rst
Expand Up @@ -1056,6 +1056,26 @@ Currently, only the following parameter attributes are defined:
form and the known alignment of the pointer specified to the call
site. If the alignment is not specified, then the code generator
makes a target-specific assumption.
``preallocated(<ty>)``
This indicates that the pointer parameter should really be passed by
value to the function, and that the pointer parameter's pointee has
already been initialized before the call instruction. This attribute
is only valid on LLVM pointer arguments. The argument must be the value
returned by the appropriate
:ref:`llvm.call.preallocated.arg<int_call_preallocated_arg>`, although is
ignored during codegen.

Any function call with a ``preallocated`` attribute in any parameter
must have a ``"preallocated"`` operand bundle.

The preallocated attribute requires a type argument, which must be
the same as the pointee type of the argument.

The preallocated attribute also supports specifying an alignment with the
align attribute. It indicates the alignment of the stack slot to
form and the known alignment of the pointer specified to the call
site. If the alignment is not specified, then the code generator
makes a target-specific assumption.

.. _attr_inalloca:

Expand Down Expand Up @@ -1953,6 +1973,12 @@ attributes are supported:

<vector_redirection>:= optional, custom name of the vector function

``preallocated(<ty>)``
This attribute is required on calls to ``llvm.call.preallocated.arg``
and cannot be used on any other call. See
:ref:`llvm.call.preallocated.arg<int_call_preallocated_arg>` for more
details.

.. _glattrs:

Global Attributes
Expand Down Expand Up @@ -2165,6 +2191,33 @@ benefits:
simplifies and improves heuristics, e.g., for use "use-sensitive"
optimizations.

.. _ob_preallocated:

Preallocated Operand Bundles
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Preallocated operand bundles are characterized by the ``"preallocated"``
operand bundle tag. These operand bundles allow separation of the allocation
of the call argument memory from the call site. This is necessary to pass
non-trivially copyable objects by value in a way that is compatible with MSVC
on some targets. There can be at most one ``"preallocated"`` operand bundle
attached to a call site and it must have exactly one bundle operand, which is
a token generated by ``@llvm.call.preallocated.setup``. A call with this
operand bundle should not adjust the stack before entering the function, as
that will have been done by one of the ``@llvm.call.preallocated.*`` intrinsics.

.. code-block:: llvm

%foo = type { i64, i32 }

...

%t = call token @llvm.call.preallocated.setup(i32 1)
%a = call i8* @llvm.call.preallocated.arg(token %t, i32 0) preallocated(%foo)
%b = bitcast i8* %a to %foo*
; initialize %b
call void @bar(i32 42, %foo* preallocated(%foo) %b) ["preallocated"(token %t)]

.. _moduleasm:

Module-Level Inline Assembly
Expand Down Expand Up @@ -11874,6 +11927,90 @@ call a helper function, read from an alternate memory space, or perform
other operations necessary to locate the TLS area. Not all targets support
this intrinsic.

'``llvm.call.preallocated.setup``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax:
"""""""

::

declare token @llvm.call.preallocated.setup(i32 %num_args)

Overview:
"""""""""

The '``llvm.call.preallocated.setup``' intrinsic returns a token which can
be used with a call's ``"preallocated"`` operand bundle to indicate that
certain arguments are allocated and initialized before the call.

Semantics:
""""""""""

The '``llvm.call.preallocated.setup``' intrinsic returns a token which is
associated with at most one call. The token can be passed to
'``@llvm.call.preallocated.arg``' to get a pointer to get that
corresponding argument. The token must be the parameter to a
``"preallocated"`` operand bundle for the corresponding call.

Nested calls to '``llvm.call.preallocated.setup``' are allowed, but must
be properly nested. e.g.

:: code-block:: llvm

%t1 = call token @llvm.call.preallocated.setup(i32 0)
%t2 = call token @llvm.call.preallocated.setup(i32 0)
call void foo() ["preallocated"(token %t2)]
call void foo() ["preallocated"(token %t1)]

is allowed, but not

:: code-block:: llvm

%t1 = call token @llvm.call.preallocated.setup(i32 0)
%t2 = call token @llvm.call.preallocated.setup(i32 0)
call void foo() ["preallocated"(token %t1)]
call void foo() ["preallocated"(token %t2)]

.. _int_call_preallocated_arg:

'``llvm.call.preallocated.arg``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax:
"""""""

::

declare i8* @llvm.call.preallocated.arg(token %setup_token, i32 %arg_index)

Overview:
"""""""""

The '``llvm.call.preallocated.arg``' intrinsic returns a pointer to the
corresponding preallocated argument for the preallocated call.

Semantics:
""""""""""

The '``llvm.call.preallocated.arg``' intrinsic returns a pointer to the
``%arg_index``th argument with the ``preallocated`` attribute for
the call associated with the ``%setup_token``, which must be from
'``llvm.call.preallocated.setup``'.

A call to '``llvm.call.preallocated.arg``' must have a call site
``preallocated`` attribute. The type of the ``preallocated`` attribute must
match the type used by the ``preallocated`` attribute of the corresponding
argument at the preallocated call. The type is used in the case that an
``llvm.call.preallocated.setup`` does not have a corresponding call (e.g. due
to DCE), where otherwise we cannot know how large the arguments are.

It is undefined behavior if this is called with a token from an
'``llvm.call.preallocated.setup``' if another
'``llvm.call.preallocated.setup``' has already been called or if the
preallocated call corresponding to the '``llvm.call.preallocated.setup``'
has already been called.

Standard C Library Intrinsics
-----------------------------

Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/Bitcode/LLVMBitCodes.h
Expand Up @@ -633,6 +633,7 @@ enum AttributeKindCodes {
ATTR_KIND_NOFREE = 62,
ATTR_KIND_NOSYNC = 63,
ATTR_KIND_SANITIZE_MEMTAG = 64,
ATTR_KIND_PREALLOCATED = 65,
};

enum ComdatSelectionKindCodes {
Expand Down
9 changes: 9 additions & 0 deletions llvm/include/llvm/IR/Attributes.h
Expand Up @@ -108,6 +108,7 @@ class Attribute {
unsigned ElemSizeArg,
const Optional<unsigned> &NumElemsArg);
static Attribute getWithByValType(LLVMContext &Context, Type *Ty);
static Attribute getWithPreallocatedType(LLVMContext &Context, Type *Ty);

static Attribute::AttrKind getAttrKindFromName(StringRef AttrName);

Expand Down Expand Up @@ -302,6 +303,7 @@ class AttributeSet {
uint64_t getDereferenceableBytes() const;
uint64_t getDereferenceableOrNullBytes() const;
Type *getByValType() const;
Type *getPreallocatedType() const;
std::pair<unsigned, Optional<unsigned>> getAllocSizeArgs() const;
std::string getAsString(bool InAttrGrp = false) const;

Expand Down Expand Up @@ -724,6 +726,7 @@ class AttrBuilder {
uint64_t DerefOrNullBytes = 0;
uint64_t AllocSizeArgs = 0;
Type *ByValType = nullptr;
Type *PreallocatedType = nullptr;

public:
AttrBuilder() = default;
Expand Down Expand Up @@ -802,6 +805,9 @@ class AttrBuilder {
/// Retrieve the byval type.
Type *getByValType() const { return ByValType; }

/// Retrieve the preallocated type.
Type *getPreallocatedType() const { return PreallocatedType; }

/// Retrieve the allocsize args, if the allocsize attribute exists. If it
/// doesn't exist, pair(0, 0) is returned.
std::pair<unsigned, Optional<unsigned>> getAllocSizeArgs() const;
Expand Down Expand Up @@ -845,6 +851,9 @@ class AttrBuilder {
/// This turns a byval type into the form used internally in Attribute.
AttrBuilder &addByValAttr(Type *Ty);

/// This turns a preallocated type into the form used internally in Attribute.
AttrBuilder &addPreallocatedAttr(Type *Ty);

/// Add an allocsize attribute, using the representation returned by
/// Attribute.getIntValue().
AttrBuilder &addAllocSizeAttrFromRawRepr(uint64_t RawAllocSizeRepr);
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/IR/Attributes.td
Expand Up @@ -133,6 +133,9 @@ def OptimizeForSize : EnumAttr<"optsize">;
/// Function must not be optimized.
def OptimizeNone : EnumAttr<"optnone">;

/// Similar to byval but without a copy.
def Preallocated : TypeAttr<"preallocated">;

/// Function does not access memory.
def ReadNone : EnumAttr<"readnone">;

Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/IR/Intrinsics.td
Expand Up @@ -508,6 +508,9 @@ def int_instrprof_value_profile : Intrinsic<[],
llvm_i32_ty],
[]>;

def int_call_preallocated_setup : Intrinsic<[llvm_token_ty], [llvm_i32_ty]>;
def int_call_preallocated_arg : Intrinsic<[llvm_ptr_ty], [llvm_token_ty, llvm_i32_ty]>;

//===------------------- Standard C Library Intrinsics --------------------===//
//

Expand Down
4 changes: 3 additions & 1 deletion llvm/include/llvm/IR/LLVMContext.h
Expand Up @@ -83,12 +83,14 @@ class LLVMContext {
/// Known operand bundle tag IDs, which always have the same value. All
/// operand bundle tags that LLVM has special knowledge of are listed here.
/// Additionally, this scheme allows LLVM to efficiently check for specific
/// operand bundle tags without comparing strings.
/// operand bundle tags without comparing strings. Keep this in sync with
/// LLVMContext::LLVMContext().
enum : unsigned {
OB_deopt = 0, // "deopt"
OB_funclet = 1, // "funclet"
OB_gc_transition = 2, // "gc-transition"
OB_cfguardtarget = 3, // "cfguardtarget"
OB_preallocated = 4, // "preallocated"
};

/// getMDKindID - Return a unique non-zero ID for the specified metadata kind.
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLLexer.cpp
Expand Up @@ -667,6 +667,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(optforfuzzing);
KEYWORD(optnone);
KEYWORD(optsize);
KEYWORD(preallocated);
KEYWORD(readnone);
KEYWORD(readonly);
KEYWORD(returned);
Expand Down
40 changes: 38 additions & 2 deletions llvm/lib/AsmParser/LLParser.cpp
Expand Up @@ -1345,6 +1345,13 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
case lltok::kw_uwtable: B.addAttribute(Attribute::UWTable); break;
case lltok::kw_willreturn: B.addAttribute(Attribute::WillReturn); break;
case lltok::kw_writeonly: B.addAttribute(Attribute::WriteOnly); break;
case lltok::kw_preallocated: {
Type *Ty;
if (ParsePreallocated(Ty))
return true;
B.addPreallocatedAttr(Ty);
break;
}

// Error handling.
case lltok::kw_inreg:
Expand Down Expand Up @@ -1373,7 +1380,9 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
break;
}

Lex.Lex();
// ParsePreallocated() consumes token
if (Token != lltok::kw_preallocated)
Lex.Lex();
}
}

Expand Down Expand Up @@ -1637,6 +1646,13 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
B.addByValAttr(Ty);
continue;
}
case lltok::kw_preallocated: {
Type *Ty;
if (ParsePreallocated(Ty))
return true;
B.addPreallocatedAttr(Ty);
continue;
}
case lltok::kw_dereferenceable: {
uint64_t Bytes;
if (ParseOptionalDerefAttrBytes(lltok::kw_dereferenceable, Bytes))
Expand Down Expand Up @@ -1804,10 +1820,15 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
case lltok::kw_uwtable:
HaveError |= Error(Lex.getLoc(), "invalid use of function-only attribute");
break;

case lltok::kw_readnone:
case lltok::kw_readonly:
HaveError |= Error(Lex.getLoc(), "invalid use of attribute on return type");
break;
case lltok::kw_preallocated:
HaveError |=
Error(Lex.getLoc(),
"invalid use of parameter-only/call site-only attribute");
break;
}

Lex.Lex();
Expand Down Expand Up @@ -2519,6 +2540,21 @@ bool LLParser::ParseByValWithOptionalType(Type *&Result) {
return false;
}

/// ParsePreallocated
/// ::= preallocated(<ty>)
bool LLParser::ParsePreallocated(Type *&Result) {
Result = nullptr;
if (!EatIfPresent(lltok::kw_preallocated))
return true;
if (!EatIfPresent(lltok::lparen))
return Error(Lex.getLoc(), "expected '('");
if (ParseType(Result))
return true;
if (!EatIfPresent(lltok::rparen))
return Error(Lex.getLoc(), "expected ')'");
return false;
}

/// ParseOptionalOperandBundles
/// ::= /*empty*/
/// ::= '[' OperandBundle [, OperandBundle ]* ']'
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLParser.h
Expand Up @@ -338,6 +338,7 @@ namespace llvm {
std::vector<unsigned> &FwdRefAttrGrps,
bool inAttrGrp, LocTy &BuiltinLoc);
bool ParseByValWithOptionalType(Type *&Result);
bool ParsePreallocated(Type *&Result);

// Module Summary Index Parsing.
bool SkipModuleSummaryEntry();
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLToken.h
Expand Up @@ -213,6 +213,7 @@ enum Kind {
kw_optforfuzzing,
kw_optnone,
kw_optsize,
kw_preallocated,
kw_readnone,
kw_readonly,
kw_returned,
Expand Down

0 comments on commit 3b0450a

Please sign in to comment.