LLVM 24.0.0git
AMDGPU.cpp
Go to the documentation of this file.
1//===- AMDGPU.cpp - AMDGPU ABI Implementation ----------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
11#include "llvm/ABI/TargetInfo.h"
12#include "llvm/ABI/Types.h"
17#include <algorithm>
18#include <cassert>
19#include <cstdint>
20
21namespace llvm {
22namespace abi {
23
24class AMDGPUTargetInfo final : public DefaultTargetInfo {
25private:
26 static const unsigned MaxNumRegsForArgsRet = 16;
27
28 ABICompatInfo CompatInfo;
29
30 /// HIP coerces a generic scalar-pointer kernel argument to the global
31 /// address space. Gated by the front end, which alone can see LangOpts.HIP.
32 bool CoerceGenericPtrArgToGlobal;
33
34 ArgInfo classifyReturnType(const Type *RetTy) const;
35 ArgInfo classifyKernelArgumentType(const Type *Ty) const;
36 ArgInfo classifyArgumentType(const Type *Ty, bool Variadic,
37 unsigned &NumRegsLeft) const;
38
39 /// Estimate number of registers the type will use when passed in registers.
40 uint64_t getNumRegsForType(const Type *Ty) const;
41
42public:
44 bool CoerceGenericPtrArgToGlobal)
45 : DefaultTargetInfo(TypeBuilder), CompatInfo(Compat),
46 CoerceGenericPtrArgToGlobal(CoerceGenericPtrArgToGlobal) {}
47
48 const ABICompatInfo &getABICompatInfo() const override { return CompatInfo; }
49
50 /// Indirect arguments live in the private (alloca) address space on AMDGPU.
51 unsigned getAllocaAddrSpace() const override {
53 }
54
55 void computeInfo(FunctionInfo &FI) const override;
56};
57
58uint64_t AMDGPUTargetInfo::getNumRegsForType(const Type *Ty) const {
59 uint64_t NumRegs = 0;
60
61 if (const auto *VT = dyn_cast<VectorType>(Ty)) {
62 // Compute from the number of elements. The reported size is based on the
63 // in-memory size, which includes the padding 4th element for 3-vectors.
64 const Type *EltTy = VT->getElementType();
65 uint64_t EltSize = EltTy->getSizeInBits().getFixedValue();
66 unsigned NumElts = VT->getNumElements().getFixedValue();
67
68 // 16-bit element vectors should be passed as packed.
69 if (EltSize == 16)
70 return (NumElts + 1) / 2;
71
72 uint64_t EltNumRegs = (EltSize + 31) / 32;
73 return EltNumRegs * NumElts;
74 }
75
76 if (const auto *RT = dyn_cast<RecordType>(Ty)) {
77 for (const FieldInfo &Field : RT->getFields())
78 NumRegs += getNumRegsForType(Field.FieldType);
79 return NumRegs;
80 }
81
82 return (Ty->getSizeInBits().getFixedValue() + 31) / 32;
83}
84
85ArgInfo AMDGPUTargetInfo::classifyReturnType(const Type *RetTy) const {
86 if (RetTy->isVoid())
87 return ArgInfo::getIgnore();
88
89 if (isAggregateTypeForABI(RetTy)) {
90 // Records with non-trivial destructors/copy-constructors should not be
91 // returned by value.
92 if (getRecordArgABI(RetTy) == RAA_Default) {
93 const auto *RT = dyn_cast<RecordType>(RetTy);
94
95 // Ignore empty structs/unions.
96 if (RT && RT->isEmpty())
97 return ArgInfo::getIgnore();
98
99 // Lower single-element structs to just return a regular value.
100 if (const Type *SeltTy = isSingleElementStruct(RetTy))
101 return ArgInfo::getDirect(SeltTy);
102
103 if (RT && RT->hasFlexibleArrayMember())
105
106 // Pack aggregates <= 4 bytes into single VGPR or pair.
107 uint64_t Size = RetTy->getSizeInBits().getFixedValue();
108 if (Size <= 16)
109 return ArgInfo::getDirect(TB.getIntegerType(16, Align(2), false));
110
111 if (Size <= 32)
112 return ArgInfo::getDirect(TB.getIntegerType(32, Align(4), false));
113
114 if (Size <= 64) {
115 const Type *I32Ty = TB.getIntegerType(32, Align(4), false);
116 return ArgInfo::getDirect(TB.getArrayType(I32Ty, 2, /*SizeInBits=*/64));
117 }
118
119 if (getNumRegsForType(RetTy) <= MaxNumRegsForArgsRet)
120 return ArgInfo::getDirect();
121 }
122 }
123
124 // Otherwise just do the default thing.
126}
127
128/// For kernels all parameters are really passed in a special buffer. It doesn't
129/// make sense to pass anything byval, so everything must be direct.
130ArgInfo AMDGPUTargetInfo::classifyKernelArgumentType(const Type *Ty) const {
132
133 if (const Type *SeltTy = isSingleElementStruct(Ty))
134 Ty = SeltTy;
135
136 // HIP passes a generic scalar pointer as a global pointer; a pointer is not
137 // an aggregate, so this stays on the direct path.
138 if (CoerceGenericPtrArgToGlobal) {
139 if (const auto *PtrTy = dyn_cast<PointerType>(Ty);
140 PtrTy && PtrTy->getAddrSpace() == AMDGPUAS::FLAT_ADDRESS) {
141 const Type *Coerced =
142 TB.getPointerType(PtrTy->getSizeInBits().getFixedValue(),
143 PtrTy->getAlignment(), AMDGPUAS::GLOBAL_ADDRESS,
144 /*HasAddressSpaceQualifier=*/true);
145 return ArgInfo::getDirect(Coerced, /*Offset=*/0, /*Align=*/std::nullopt,
146 /*CanBeFlattened=*/false);
147 }
148 }
149
150 // FIXME: This doesn't apply the optimization of coercing pointers in structs
151 // to global address space when using byref. This would require implementing a
152 // new kind of coercion of the in-memory type when for indirect arguments.
153 if (isAggregateTypeForABI(Ty))
155 Ty->getAlignment(),
156 /*AddrSpace=*/AMDGPUAS::CONSTANT_ADDRESS);
157
158 // CanBeFlattened=false keeps the struct intact.
159 return ArgInfo::getDirect(Ty, /*Offset=*/0, /*Align=*/std::nullopt,
160 /*CanBeFlattened=*/false);
161}
162
163ArgInfo AMDGPUTargetInfo::classifyArgumentType(const Type *Ty, bool Variadic,
164 unsigned &NumRegsLeft) const {
165 assert(NumRegsLeft <= MaxNumRegsForArgsRet && "register estimate underflow");
166
168
169 // Variadic aggregates are kept intact rather than flattened into fields.
170 if (Variadic)
171 return ArgInfo::getDirect(/*T=*/nullptr, /*Offset=*/0,
172 /*Align=*/std::nullopt, /*CanBeFlattened=*/false);
173
174 if (isAggregateTypeForABI(Ty)) {
175 // Records with non-trivial destructors/copy-constructors should not be
176 // passed by value.
177 if (RecordArgABI RAA = getRecordArgABI(Ty); RAA != RAA_Default)
178 return ArgInfo::getIndirect(Ty->getAlignment(),
179 /*ByVal=*/RAA == RAA_DirectInMemory,
180 /*AddrSpace=*/AMDGPUAS::PRIVATE_ADDRESS);
181
182 // Ignore empty structs/unions.
183 if (Ty->isEmptyRecord())
184 return ArgInfo::getIgnore();
185
186 // Lower single-element structs to just pass a regular value.
187 if (const Type *SeltTy = isSingleElementStruct(Ty))
188 return ArgInfo::getDirect(SeltTy);
189
190 if (const auto *RT = dyn_cast<RecordType>(Ty);
191 RT && RT->hasFlexibleArrayMember())
193
194 // Pack aggregates <= 8 bytes into single VGPR or pair.
195 uint64_t Size = Ty->getSizeInBits().getFixedValue();
196 if (Size <= 64) {
197 unsigned NumRegs = (Size + 31) / 32;
198 NumRegsLeft -= std::min(NumRegsLeft, NumRegs);
199
200 if (Size <= 16)
201 return ArgInfo::getDirect(TB.getIntegerType(16, Align(2), false));
202
203 if (Size <= 32)
204 return ArgInfo::getDirect(TB.getIntegerType(32, Align(4), false));
205
206 const Type *I32Ty = TB.getIntegerType(32, Align(4), false);
207 return ArgInfo::getDirect(TB.getArrayType(I32Ty, 2, /*SizeInBits=*/64));
208 }
209
210 if (NumRegsLeft > 0) {
211 uint64_t NumRegs = getNumRegsForType(Ty);
212 if (NumRegsLeft >= NumRegs) {
213 NumRegsLeft -= NumRegs;
214 return ArgInfo::getDirect();
215 }
216 }
217
218 // Pass a struct argument by reference rather than by value.
219 return ArgInfo::getIndirectAliased(Ty->getAlignment(),
220 /*AddrSpace=*/AMDGPUAS::PRIVATE_ADDRESS);
221 }
222
223 // Otherwise just do the default thing.
225 if (!AI.isIndirect()) {
226 uint64_t NumRegs = getNumRegsForType(Ty);
227 NumRegsLeft -= std::min(NumRegs, uint64_t{NumRegsLeft});
228 }
229
230 return AI;
231}
232
235
236 // Non-trivial C++ records are returned indirectly
237 // in the flat address space.
239 FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
240
241 unsigned ArgumentIndex = 0;
242 const unsigned NumFixedArguments = FI.getNumRequiredArgs();
243
244 unsigned NumRegsLeft = MaxNumRegsForArgsRet;
245 for (ArgEntry &Arg : FI.arguments()) {
246 if (CC == CallingConv::AMDGPU_KERNEL) {
247 Arg.Info = classifyKernelArgumentType(Arg.ABIType);
248 } else {
249 bool FixedArgument = ArgumentIndex++ < NumFixedArguments;
250 Arg.Info = classifyArgumentType(Arg.ABIType, !FixedArgument, NumRegsLeft);
251 }
252 }
253}
254
255std::unique_ptr<TargetInfo>
256createAMDGPUTargetInfo(TypeBuilder &TB, bool CoerceGenericPtrArgToGlobal) {
257 return std::make_unique<AMDGPUTargetInfo>(TB, ABICompatInfo(),
258 CoerceGenericPtrArgToGlobal);
259}
260
261} // namespace abi
262} // namespace llvm
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
AMDGPU address space definition.
uint64_t uint64_t
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
Default ABI classification shared by targets without special rules.
OptimizedStructLayoutField Field
Target-specific ABI information and factory functions.
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
void computeInfo(FunctionInfo &FI) const override
Populate FI with the target's ABI-lowering decisions for each argument and return value.
Definition AMDGPU.cpp:233
const ABICompatInfo & getABICompatInfo() const override
Return this target's ABI compatibility flags.
Definition AMDGPU.cpp:48
AMDGPUTargetInfo(TypeBuilder &TypeBuilder, const ABICompatInfo &Compat, bool CoerceGenericPtrArgToGlobal)
Definition AMDGPU.cpp:43
unsigned getAllocaAddrSpace() const override
Indirect arguments live in the private (alloca) address space on AMDGPU.
Definition AMDGPU.cpp:51
Helper class to encapsulate information about how a specific type should be passed to or returned fro...
static ArgInfo getIgnore()
static ArgInfo getIndirectAliased(Align Align, unsigned AddrSpace, bool Realign=false)
An aliased indirect argument.
static ArgInfo getIndirect(Align Align, bool ByVal, unsigned AddrSpace=0, bool Realign=false)
Realign: the caller couldn't guarantee sufficient alignment - the callee must copy the argument to a ...
static ArgInfo getDirect(const Type *T=nullptr, unsigned Offset=0, MaybeAlign Align=std::nullopt, bool CanBeFlattened=true)
Self-consistent classification that conforms to no particular ABI.
ArgInfo classifyArgumentType(const Type *Ty) const
ArgInfo classifyReturnType(const Type *RetTy) const
ArrayRef< ArgEntry > arguments() const
unsigned getNumRequiredArgs() const
CallingConv::ID getCallingConvention() const
const Type * getReturnType() const
LLVM_ABI const Type * isSingleElementStruct(const Type *Ty) const
Returns the scalar a single-element struct reduces to, else null.
LLVM_ABI bool maybeCommonClassifyReturnType(FunctionInfo &FI) const
Apply rules for classifying return types that are common to all targets.
LLVM_ABI bool isAggregateTypeForABI(const Type *Ty) const
TypeBuilder & TB
Definition TargetInfo.h:67
LLVM_ABI const Type * useFirstFieldIfTransparentUnion(const Type *Ty) const
If Ty is a transparent union, return its first field type; otherwise return Ty unchanged.
LLVM_ABI RecordArgABI getRecordArgABI(const RecordType *RT) const
TypeBuilder manages the lifecycle of ABI types using bump pointer allocation.
Definition Types.h:471
Represents the ABI-specific view of a type in LLVM.
Definition Types.h:47
This file defines the type system for the LLVMABI library, which mirrors ABI-relevant aspects of fron...
@ CONSTANT_ADDRESS
Address space for constant memory (VTX2).
@ FLAT_ADDRESS
Address space for flat memory.
@ GLOBAL_ADDRESS
Address space for global memory (RAT0, VTX0).
@ PRIVATE_ADDRESS
Address space for private memory.
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
@ AMDGPU_KERNEL
Used for AMDGPU code object kernels.
LLVM_ABI std::unique_ptr< TargetInfo > createAMDGPUTargetInfo(TypeBuilder &TB, bool CoerceGenericPtrArgToGlobal=false)
Definition AMDGPU.cpp:256
@ RAA_DirectInMemory
Pass it on the stack using its defined layout.
Definition TargetInfo.h:34
@ RAA_Default
Pass it using the normal C aggregate rules for the ABI, potentially introducing extra copies and pass...
Definition TargetInfo.h:29
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
Flags controlling ABI compatibility behaviour that applies to every target.
Definition TargetInfo.h:43