[IR] Add @llvm.structured.alloca (#186811)
This instruction is an alternative for the `alloca` instruction when targeting logical targets like DXIL/SPIR-V. This instruction allocates some memory, but the exact size of the allocation is not known at the IR level. Only some equivalence can be determined. Commit adds docs, instruction declaration, and IR verifier testing. Related to: https://discourse.llvm.org/t/rfc-adding-logical-structured-alloca/
This commit is contained in:
@@ -1457,10 +1457,11 @@ Currently, only the following parameter attributes are defined:
|
||||
present and assign it particular semantics. This will be documented on
|
||||
individual intrinsics.
|
||||
|
||||
The attribute may only be applied to pointer typed arguments of intrinsic
|
||||
calls. It cannot be applied to non-intrinsic calls, and cannot be applied
|
||||
to parameters on function declarations. For non-opaque pointers, the type
|
||||
passed to ``elementtype`` must match the pointer element type.
|
||||
The attribute may only be applied to pointer typed arguments or return
|
||||
values of intrinsic calls. It cannot be applied to non-intrinsic calls,
|
||||
and cannot be applied to parameters on function declarations.
|
||||
For non-opaque pointers, the type passed to ``elementtype`` must match
|
||||
the pointer element type.
|
||||
|
||||
.. _attr_align:
|
||||
|
||||
@@ -15327,6 +15328,86 @@ This is, however, dependent on context that codegen has an insight on. The
|
||||
fact that `[ i32 x 4 ]` and `%S` are equivalent depends on the target.
|
||||
|
||||
|
||||
.. _i_structured_alloca:
|
||||
|
||||
'``llvm.structured.alloca``' Intrinsic
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Syntax:
|
||||
"""""""
|
||||
|
||||
::
|
||||
|
||||
declare elementtype(<allocated_type>) ptr
|
||||
@llvm.structured.alloca()
|
||||
|
||||
Overview:
|
||||
"""""""""
|
||||
|
||||
The '``llvm.structured.alloca``' intrinsic allocates uninitialized memory on
|
||||
the stack for a logical type, such as an aggregate, an array, or a scalar.
|
||||
|
||||
Unlike the standard :ref:`alloca <i_alloca>` instruction, the physical memory
|
||||
layout of a ``llvm.structured.alloca`` is completely opaque to the IR.
|
||||
Exact padding, size, alignment and subtype offsets is target-dependent and
|
||||
may differ from the standard ``DataLayout``.
|
||||
|
||||
Arguments:
|
||||
""""""""""
|
||||
|
||||
The intrinsic must be annotated with an :ref:`elementtype <attr_elementtype>`
|
||||
attribute at the call-site on the return value. This attribute specifies the
|
||||
type of the allocated element.
|
||||
|
||||
Semantics:
|
||||
""""""""""
|
||||
|
||||
The ``llvm.structured.alloca`` intrinsic allocates uninitialized memory for a
|
||||
logical type. Loading from uninitialized memory produces an undefined value.
|
||||
This intrinsic does not guarantee that the allocated memory will store a value
|
||||
of the given type, only that it allocates enough space for it on the destination
|
||||
target. While the type's size and layout are constant (independent of location),
|
||||
the exact padding and offsets between subtypes are opaque to the IR and are
|
||||
determined by the target's backend.
|
||||
|
||||
The resulting pointer is in the :ref:`alloca address space <alloca_addrspace>`
|
||||
defined in the :ref:`datalayout string <langref_datalayout>`.
|
||||
|
||||
Standard pointer arithmetic (``getelementptr``, ``ptradd``) and lifetime
|
||||
intrinsics (:ref:`llvm.lifetime.start <int_lifestart>`,
|
||||
:ref:`llvm.lifetime.end <int_lifeend>`) are permitted on the returned pointer.
|
||||
However, because the physical layout is opaque, using physical pointer
|
||||
arithmetic requires the frontend or emitting pass to have explicit knowledge of
|
||||
the backend's layout rules.
|
||||
|
||||
Example:
|
||||
""""""""
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
%S = type { i32, i32, i32, i32 }
|
||||
|
||||
; Allocate one instance of %S on the stack
|
||||
%ptr = call elementtype(%S) ptr @llvm.structured.alloca()
|
||||
|
||||
; Access the second field of the allocated struct
|
||||
%field_ptr = call ptr @llvm.structured.gep(ptr elementtype(%S) %ptr, i32 1)
|
||||
%val = load i32, ptr %field_ptr
|
||||
|
||||
; Allocate an array of 10 i32s on the stack
|
||||
%array_ptr = call elementtype([10 x i32]) ptr @llvm.structured.alloca()
|
||||
|
||||
; Allocate a single i32 on the stack
|
||||
%scalar_ptr = call elementtype(i32) ptr @llvm.structured.alloca()
|
||||
|
||||
; Although the exact size of 'i32' or '%S' is opaque, it is constant
|
||||
; for the duration of the module. This allows, for example, reusing
|
||||
; an allocation slot for two different values of the same type.
|
||||
%a = call elementtype(float) ptr @llvm.structured.alloca()
|
||||
%b = call elementtype(float) ptr @llvm.structured.alloca()
|
||||
; %a and %b are guaranteed to have the same allocation size.
|
||||
|
||||
|
||||
.. _int_get_dynamic_area_offset:
|
||||
|
||||
'``llvm.get.dynamic.area.offset``' Intrinsic
|
||||
@@ -27843,8 +27924,8 @@ object's lifetime.
|
||||
Arguments:
|
||||
""""""""""
|
||||
|
||||
The argument is either a pointer to an ``alloca`` instruction or a ``poison``
|
||||
value.
|
||||
The argument is either a pointer to an ``alloca`` instruction or an
|
||||
``llvm.structured.alloca`` intrinsic, or a ``poison`` value.
|
||||
|
||||
Semantics:
|
||||
""""""""""
|
||||
@@ -27855,8 +27936,8 @@ Otherwise, the stack-allocated object that ``ptr`` points to is initially
|
||||
marked as dead. After '``llvm.lifetime.start``', the stack object is marked as
|
||||
alive and has an uninitialized value.
|
||||
The stack object is marked as dead when either
|
||||
:ref:`llvm.lifetime.end <int_lifeend>` to the alloca is executed or the
|
||||
function returns.
|
||||
:ref:`llvm.lifetime.end <int_lifeend>` to the alloca/structured.alloca is
|
||||
executed or the function returns.
|
||||
|
||||
After :ref:`llvm.lifetime.end <int_lifeend>` is called,
|
||||
'``llvm.lifetime.start``' on the stack object can be called again.
|
||||
@@ -27884,8 +27965,8 @@ The '``llvm.lifetime.end``' intrinsic specifies the end of a
|
||||
Arguments:
|
||||
""""""""""
|
||||
|
||||
The argument is either a pointer to an ``alloca`` instruction or a ``poison``
|
||||
value.
|
||||
The argument is either a pointer to an ``alloca`` instruction or an
|
||||
``llvm.structured.alloca`` intrinsic, or a ``poison`` value.
|
||||
|
||||
Semantics:
|
||||
""""""""""
|
||||
@@ -27895,7 +27976,8 @@ If ``ptr`` is a ``poison`` value, the intrinsic has no effect.
|
||||
Otherwise, the stack-allocated object that ``ptr`` points to becomes dead after
|
||||
the call to this intrinsic.
|
||||
|
||||
Calling ``llvm.lifetime.end`` on an already dead alloca is no-op.
|
||||
Calling ``llvm.lifetime.end`` on an already dead alloca/structured.alloca is
|
||||
no-op.
|
||||
|
||||
'``llvm.invariant.start``' Intrinsic
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -132,7 +132,7 @@ def DereferenceableOrNull : IntAttr<"dereferenceable_or_null", IntersectMin,
|
||||
def DisableSanitizerInstrumentation: EnumAttr<"disable_sanitizer_instrumentation", IntersectPreserve, [FnAttr]>;
|
||||
|
||||
/// Provide pointer element type to intrinsic.
|
||||
def ElementType : TypeAttr<"elementtype", IntersectPreserve, [ParamAttr]>;
|
||||
def ElementType : TypeAttr<"elementtype", IntersectPreserve, [RetAttr, ParamAttr]>;
|
||||
|
||||
/// Flatten function by recursively inlining all calls.
|
||||
def Flatten : EnumAttr<"flatten", IntersectPreserve, [FnAttr]>;
|
||||
|
||||
@@ -1804,6 +1804,21 @@ public:
|
||||
CreateLoop(BasicBlock &BB, ConvergenceControlInst *Parent);
|
||||
};
|
||||
|
||||
class StructuredAllocaInst : public IntrinsicInst {
|
||||
public:
|
||||
static bool classof(const IntrinsicInst *I) {
|
||||
return I->getIntrinsicID() == Intrinsic::structured_alloca;
|
||||
}
|
||||
|
||||
static bool classof(const Value *V) {
|
||||
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
|
||||
}
|
||||
|
||||
Type *getAllocationType() const {
|
||||
return getRetAttr(Attribute::ElementType).getValueAsType();
|
||||
}
|
||||
};
|
||||
|
||||
class StructuredGEPInst : public IntrinsicInst {
|
||||
public:
|
||||
static bool classof(const IntrinsicInst *I) {
|
||||
|
||||
@@ -1034,6 +1034,8 @@ def int_structured_gep
|
||||
[LLVMMatchType<0>, llvm_vararg_ty],
|
||||
[IntrNoMem, IntrSpeculatable]>;
|
||||
|
||||
def int_structured_alloca : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [], [IntrInaccessibleMemOnly]>;
|
||||
|
||||
//===------------------- Standard C Library Intrinsics --------------------===//
|
||||
//
|
||||
|
||||
|
||||
@@ -6978,6 +6978,11 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Intrinsic::structured_alloca:
|
||||
Check(Call.hasRetAttr(Attribute::ElementType),
|
||||
"@llvm.structured.alloca calls require elementtype attribute.",
|
||||
&Call);
|
||||
break;
|
||||
case Intrinsic::amdgcn_cs_chain: {
|
||||
auto CallerCC = Call.getCaller()->getCallingConv();
|
||||
switch (CallerCC) {
|
||||
@@ -7255,7 +7260,9 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
|
||||
case Intrinsic::lifetime_start:
|
||||
case Intrinsic::lifetime_end: {
|
||||
Value *Ptr = Call.getArgOperand(0);
|
||||
Check(isa<AllocaInst>(Ptr) || isa<PoisonValue>(Ptr),
|
||||
IntrinsicInst *II = dyn_cast<IntrinsicInst>(Ptr);
|
||||
Check(isa<AllocaInst>(Ptr) || isa<PoisonValue>(Ptr) ||
|
||||
(II && II->getIntrinsicID() == Intrinsic::structured_alloca),
|
||||
"llvm.lifetime.start/end can only be used on alloca or poison",
|
||||
&Call);
|
||||
break;
|
||||
|
||||
17
llvm/test/Assembler/structured-alloca.ll
Normal file
17
llvm/test/Assembler/structured-alloca.ll
Normal file
@@ -0,0 +1,17 @@
|
||||
; RUN: llvm-as < %s | llvm-dis
|
||||
|
||||
%S = type { i32, i32 }
|
||||
|
||||
define void @simple_scalar_allocation() {
|
||||
entry:
|
||||
; CHECK: %ptr = call elementtype(i32) ptr @llvm.structured.alloca.p0()
|
||||
%ptr = call elementtype(i32) ptr @llvm.structured.alloca()
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @struct_allocation() {
|
||||
entry:
|
||||
; CHECK: %ptr = call elementtype(%S) ptr @llvm.structured.alloca.p0()
|
||||
%ptr = call elementtype(%S) ptr @llvm.structured.alloca()
|
||||
ret void
|
||||
}
|
||||
8
llvm/test/Verifier/structured-alloca-bad.ll
Normal file
8
llvm/test/Verifier/structured-alloca-bad.ll
Normal file
@@ -0,0 +1,8 @@
|
||||
; RUN: not llvm-as -disable-output %s 2>&1 | FileCheck %s
|
||||
|
||||
define void @missing_attribute() {
|
||||
entry:
|
||||
; CHECK: @llvm.structured.alloca calls require elementtype attribute.
|
||||
%ptr = call ptr @llvm.structured.alloca()
|
||||
ret void
|
||||
}
|
||||
Reference in New Issue
Block a user