clang  19.0.0git
SPIR.cpp
Go to the documentation of this file.
1 //===- SPIR.cpp -----------------------------------------------------------===//
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 
9 #include "ABIInfoImpl.h"
10 #include "TargetInfo.h"
11 
12 using namespace clang;
13 using namespace clang::CodeGen;
14 
15 //===----------------------------------------------------------------------===//
16 // Base ABI and target codegen info implementation common between SPIR and
17 // SPIR-V.
18 //===----------------------------------------------------------------------===//
19 
20 namespace {
21 class CommonSPIRABIInfo : public DefaultABIInfo {
22 public:
23  CommonSPIRABIInfo(CodeGenTypes &CGT) : DefaultABIInfo(CGT) { setCCs(); }
24 
25  ABIArgInfo classifyKernelArgumentType(QualType Ty) const;
26 
27  // Add new functions rather than overload existing so that these public APIs
28  // can't be blindly misused with wrong calling convention.
29  ABIArgInfo classifyRegcallReturnType(QualType RetTy) const;
30  ABIArgInfo classifyRegcallArgumentType(QualType RetTy) const;
31 
32  void computeInfo(CGFunctionInfo &FI) const override;
33 
34 private:
35  void setCCs();
36 };
37 
38 ABIArgInfo CommonSPIRABIInfo::classifyKernelArgumentType(QualType Ty) const {
40 
41  if (getContext().getLangOpts().SYCLIsDevice && isAggregateTypeForABI(Ty)) {
42  // Pass all aggregate types allowed by Sema by value.
43  return getNaturalAlignIndirect(Ty);
44  }
45 
47 }
48 
49 void CommonSPIRABIInfo::computeInfo(CGFunctionInfo &FI) const {
51  bool IsRegCall = CC == llvm::CallingConv::X86_RegCall;
52 
53  if (!getCXXABI().classifyReturnType(FI)) {
54  CanQualType RetT = FI.getReturnType();
55  FI.getReturnInfo() =
56  IsRegCall ? classifyRegcallReturnType(RetT) : classifyReturnType(RetT);
57  }
58 
59  for (auto &Arg : FI.arguments()) {
60  if (CC == llvm::CallingConv::SPIR_KERNEL) {
61  Arg.info = classifyKernelArgumentType(Arg.type);
62  } else {
63  Arg.info = IsRegCall ? classifyRegcallArgumentType(Arg.type)
65  }
66  }
67 }
68 
69 // The two functions below are based on AMDGPUABIInfo, but without any
70 // restriction on the maximum number of arguments passed via registers.
71 // SPIRV BEs are expected to further adjust the calling convention as
72 // needed (use stack or byval-like passing) for some of the arguments.
73 
74 ABIArgInfo CommonSPIRABIInfo::classifyRegcallReturnType(QualType RetTy) const {
75  if (isAggregateTypeForABI(RetTy)) {
76  // Records with non-trivial destructors/copy-constructors should not be
77  // returned by value.
78  if (!getRecordArgABI(RetTy, getCXXABI())) {
79  // Ignore empty structs/unions.
80  if (isEmptyRecord(getContext(), RetTy, true))
81  return ABIArgInfo::getIgnore();
82 
83  // Lower single-element structs to just return a regular value.
84  if (const Type *SeltTy = isSingleElementStruct(RetTy, getContext()))
85  return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
86 
87  if (const RecordType *RT = RetTy->getAs<RecordType>()) {
88  const RecordDecl *RD = RT->getDecl();
89  if (RD->hasFlexibleArrayMember())
90  return classifyReturnType(RetTy);
91  }
92 
93  // Pack aggregates <= 8 bytes into a single vector register or pair.
94  // TODO make this parameterizeable/adjustable depending on spir target
95  // triple abi component.
96  uint64_t Size = getContext().getTypeSize(RetTy);
97  if (Size <= 16)
98  return ABIArgInfo::getDirect(llvm::Type::getInt16Ty(getVMContext()));
99 
100  if (Size <= 32)
101  return ABIArgInfo::getDirect(llvm::Type::getInt32Ty(getVMContext()));
102 
103  if (Size <= 64) {
104  llvm::Type *I32Ty = llvm::Type::getInt32Ty(getVMContext());
105  return ABIArgInfo::getDirect(llvm::ArrayType::get(I32Ty, 2));
106  }
107  return ABIArgInfo::getDirect();
108  }
109  }
110  // Otherwise just do the default thing.
111  return classifyReturnType(RetTy);
112 }
113 
114 ABIArgInfo CommonSPIRABIInfo::classifyRegcallArgumentType(QualType Ty) const {
116 
117  if (isAggregateTypeForABI(Ty)) {
118  // Records with non-trivial destructors/copy-constructors should not be
119  // passed by value.
120  if (auto RAA = getRecordArgABI(Ty, getCXXABI()))
121  return getNaturalAlignIndirect(Ty, RAA == CGCXXABI::RAA_DirectInMemory);
122 
123  // Ignore empty structs/unions.
124  if (isEmptyRecord(getContext(), Ty, true))
125  return ABIArgInfo::getIgnore();
126 
127  // Lower single-element structs to just pass a regular value. TODO: We
128  // could do reasonable-size multiple-element structs too, using getExpand(),
129  // though watch out for things like bitfields.
130  if (const Type *SeltTy = isSingleElementStruct(Ty, getContext()))
131  return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
132 
133  if (const RecordType *RT = Ty->getAs<RecordType>()) {
134  const RecordDecl *RD = RT->getDecl();
135  if (RD->hasFlexibleArrayMember())
136  return classifyArgumentType(Ty);
137  }
138 
139  // Pack aggregates <= 8 bytes into single vector register or pair.
140  // TODO make this parameterizeable/adjustable depending on spir target
141  // triple abi component.
142  uint64_t Size = getContext().getTypeSize(Ty);
143  if (Size <= 64) {
144  if (Size <= 16)
145  return ABIArgInfo::getDirect(llvm::Type::getInt16Ty(getVMContext()));
146 
147  if (Size <= 32)
148  return ABIArgInfo::getDirect(llvm::Type::getInt32Ty(getVMContext()));
149 
150  // XXX: Should this be i64 instead, and should the limit increase?
151  llvm::Type *I32Ty = llvm::Type::getInt32Ty(getVMContext());
152  return ABIArgInfo::getDirect(llvm::ArrayType::get(I32Ty, 2));
153  }
154  return ABIArgInfo::getDirect();
155  }
156 
157  // Otherwise just do the default thing.
158  return classifyArgumentType(Ty);
159 }
160 
161 
162 class SPIRVABIInfo : public CommonSPIRABIInfo {
163 public:
164  SPIRVABIInfo(CodeGenTypes &CGT) : CommonSPIRABIInfo(CGT) {}
165  void computeInfo(CGFunctionInfo &FI) const override;
166 
167 private:
168  ABIArgInfo classifyKernelArgumentType(QualType Ty) const;
169 };
170 } // end anonymous namespace
171 namespace {
172 class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo {
173 public:
174  CommonSPIRTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT)
175  : TargetCodeGenInfo(std::make_unique<CommonSPIRABIInfo>(CGT)) {}
176  CommonSPIRTargetCodeGenInfo(std::unique_ptr<ABIInfo> ABIInfo)
177  : TargetCodeGenInfo(std::move(ABIInfo)) {}
178 
179  LangAS getASTAllocaAddressSpace() const override {
180  return getLangASFromTargetAS(
181  getABIInfo().getDataLayout().getAllocaAddrSpace());
182  }
183 
184  unsigned getOpenCLKernelCallingConv() const override;
185  llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override;
186 
187  bool shouldEmitStaticExternCAliases() const override;
188 };
189 class SPIRVTargetCodeGenInfo : public CommonSPIRTargetCodeGenInfo {
190 public:
191  SPIRVTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT)
192  : CommonSPIRTargetCodeGenInfo(std::make_unique<SPIRVABIInfo>(CGT)) {}
193  void setCUDAKernelCallingConvention(const FunctionType *&FT) const override;
194 };
195 } // End anonymous namespace.
196 
197 void CommonSPIRABIInfo::setCCs() {
198  assert(getRuntimeCC() == llvm::CallingConv::C);
199  RuntimeCC = llvm::CallingConv::SPIR_FUNC;
200 }
201 
202 ABIArgInfo SPIRVABIInfo::classifyKernelArgumentType(QualType Ty) const {
203  if (getContext().getLangOpts().CUDAIsDevice) {
204  // Coerce pointer arguments with default address space to CrossWorkGroup
205  // pointers for HIPSPV/CUDASPV. When the language mode is HIP/CUDA, the
206  // SPIRTargetInfo maps cuda_device to SPIR-V's CrossWorkGroup address space.
207  llvm::Type *LTy = CGT.ConvertType(Ty);
208  auto DefaultAS = getContext().getTargetAddressSpace(LangAS::Default);
209  auto GlobalAS = getContext().getTargetAddressSpace(LangAS::cuda_device);
210  auto *PtrTy = llvm::dyn_cast<llvm::PointerType>(LTy);
211  if (PtrTy && PtrTy->getAddressSpace() == DefaultAS) {
212  LTy = llvm::PointerType::get(PtrTy->getContext(), GlobalAS);
213  return ABIArgInfo::getDirect(LTy, 0, nullptr, false);
214  }
215 
216  // Force copying aggregate type in kernel arguments by value when
217  // compiling CUDA targeting SPIR-V. This is required for the object
218  // copied to be valid on the device.
219  // This behavior follows the CUDA spec
220  // https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#global-function-argument-processing,
221  // and matches the NVPTX implementation.
222  if (isAggregateTypeForABI(Ty))
223  return getNaturalAlignIndirect(Ty, /* byval */ true);
224  }
225  return classifyArgumentType(Ty);
226 }
227 
228 void SPIRVABIInfo::computeInfo(CGFunctionInfo &FI) const {
229  // The logic is same as in DefaultABIInfo with an exception on the kernel
230  // arguments handling.
232 
233  if (!getCXXABI().classifyReturnType(FI))
235 
236  for (auto &I : FI.arguments()) {
237  if (CC == llvm::CallingConv::SPIR_KERNEL) {
238  I.info = classifyKernelArgumentType(I.type);
239  } else {
240  I.info = classifyArgumentType(I.type);
241  }
242  }
243 }
244 
245 namespace clang {
246 namespace CodeGen {
248  if (CGM.getTarget().getTriple().isSPIRV())
249  SPIRVABIInfo(CGM.getTypes()).computeInfo(FI);
250  else
251  CommonSPIRABIInfo(CGM.getTypes()).computeInfo(FI);
252 }
253 }
254 }
255 
256 unsigned CommonSPIRTargetCodeGenInfo::getOpenCLKernelCallingConv() const {
257  return llvm::CallingConv::SPIR_KERNEL;
258 }
259 
260 bool CommonSPIRTargetCodeGenInfo::shouldEmitStaticExternCAliases() const {
261  return false;
262 }
263 
265  const FunctionType *&FT) const {
266  // Convert HIP kernels to SPIR-V kernels.
267  if (getABIInfo().getContext().getLangOpts().HIP) {
268  FT = getABIInfo().getContext().adjustFunctionType(
270  return;
271  }
272 }
273 
274 /// Construct a SPIR-V target extension type for the given OpenCL image type.
275 static llvm::Type *getSPIRVImageType(llvm::LLVMContext &Ctx, StringRef BaseType,
276  StringRef OpenCLName,
277  unsigned AccessQualifier) {
278  // These parameters compare to the operands of OpTypeImage (see
279  // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpTypeImage
280  // for more details). The first 6 integer parameters all default to 0, and
281  // will be changed to 1 only for the image type(s) that set the parameter to
282  // one. The 7th integer parameter is the access qualifier, which is tacked on
283  // at the end.
284  SmallVector<unsigned, 7> IntParams = {0, 0, 0, 0, 0, 0};
285 
286  // Choose the dimension of the image--this corresponds to the Dim enum in
287  // SPIR-V (first integer parameter of OpTypeImage).
288  if (OpenCLName.starts_with("image2d"))
289  IntParams[0] = 1; // 1D
290  else if (OpenCLName.starts_with("image3d"))
291  IntParams[0] = 2; // 2D
292  else if (OpenCLName == "image1d_buffer")
293  IntParams[0] = 5; // Buffer
294  else
295  assert(OpenCLName.starts_with("image1d") && "Unknown image type");
296 
297  // Set the other integer parameters of OpTypeImage if necessary. Note that the
298  // OpenCL image types don't provide any information for the Sampled or
299  // Image Format parameters.
300  if (OpenCLName.contains("_depth"))
301  IntParams[1] = 1;
302  if (OpenCLName.contains("_array"))
303  IntParams[2] = 1;
304  if (OpenCLName.contains("_msaa"))
305  IntParams[3] = 1;
306 
307  // Access qualifier
308  IntParams.push_back(AccessQualifier);
309 
310  return llvm::TargetExtType::get(Ctx, BaseType, {llvm::Type::getVoidTy(Ctx)},
311  IntParams);
312 }
313 
314 llvm::Type *CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM,
315  const Type *Ty) const {
316  llvm::LLVMContext &Ctx = CGM.getLLVMContext();
317  if (Ctx.supportsTypedPointers())
318  return nullptr;
319  if (auto *PipeTy = dyn_cast<PipeType>(Ty))
320  return llvm::TargetExtType::get(Ctx, "spirv.Pipe", {},
321  {!PipeTy->isReadOnly()});
322  if (auto *BuiltinTy = dyn_cast<BuiltinType>(Ty)) {
323  enum AccessQualifier : unsigned { AQ_ro = 0, AQ_wo = 1, AQ_rw = 2 };
324  switch (BuiltinTy->getKind()) {
325 // clang-format off
326 #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
327  case BuiltinType::Id: \
328  return getSPIRVImageType(Ctx, "spirv.Image", #ImgType, AQ_##Suffix);
329 #include "clang/Basic/OpenCLImageTypes.def"
330 #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
331  case BuiltinType::Sampled##Id: \
332  return getSPIRVImageType(Ctx, "spirv.SampledImage", #ImgType, AQ_##Suffix);
333 // clang-format on
334 #define IMAGE_WRITE_TYPE(Type, Id, Ext)
335 #define IMAGE_READ_WRITE_TYPE(Type, Id, Ext)
336 #include "clang/Basic/OpenCLImageTypes.def"
337  case BuiltinType::OCLSampler:
338  return llvm::TargetExtType::get(Ctx, "spirv.Sampler");
339  case BuiltinType::OCLEvent:
340  return llvm::TargetExtType::get(Ctx, "spirv.Event");
341  case BuiltinType::OCLClkEvent:
342  return llvm::TargetExtType::get(Ctx, "spirv.DeviceEvent");
343  case BuiltinType::OCLQueue:
344  return llvm::TargetExtType::get(Ctx, "spirv.Queue");
345  case BuiltinType::OCLReserveID:
346  return llvm::TargetExtType::get(Ctx, "spirv.ReserveId");
347 #define INTEL_SUBGROUP_AVC_TYPE(Name, Id) \
348  case BuiltinType::OCLIntelSubgroupAVC##Id: \
349  return llvm::TargetExtType::get(Ctx, "spirv.Avc" #Id "INTEL");
350 #include "clang/Basic/OpenCLExtensionTypes.def"
351  default:
352  return nullptr;
353  }
354  }
355 
356  return nullptr;
357 }
358 
359 std::unique_ptr<TargetCodeGenInfo>
361  return std::make_unique<CommonSPIRTargetCodeGenInfo>(CGM.getTypes());
362 }
363 
364 std::unique_ptr<TargetCodeGenInfo>
366  return std::make_unique<SPIRVTargetCodeGenInfo>(CGM.getTypes());
367 }
static char ID
Definition: Arena.cpp:183
static void setCUDAKernelCallingConvention(CanQualType &FTy, CodeGenModule &CGM, const FunctionDecl *FD)
Set calling convention for CUDA/HIP kernel.
Definition: CGCall.cpp:295
static llvm::Type * getSPIRVImageType(llvm::LLVMContext &Ctx, StringRef BaseType, StringRef OpenCLName, unsigned AccessQualifier)
Construct a SPIR-V target extension type for the given OpenCL image type.
Definition: SPIR.cpp:275
ABIArgInfo - Helper class to encapsulate information about how a specific C type should be passed to ...
static ABIArgInfo getIgnore()
static ABIArgInfo getDirect(llvm::Type *T=nullptr, unsigned Offset=0, llvm::Type *Padding=nullptr, bool CanBeFlattened=true, unsigned Align=0)
ABIInfo - Target specific hooks for defining how a type should be passed or returned from functions.
Definition: ABIInfo.h:45
@ RAA_DirectInMemory
Pass it on the stack using its defined layout.
Definition: CGCXXABI.h:158
CGFunctionInfo - Class to encapsulate the information about a function definition.
unsigned getCallingConvention() const
getCallingConvention - Return the user specified calling convention, which has been translated into a...
CanQualType getReturnType() const
MutableArrayRef< ArgInfo > arguments()
This class organizes the cross-function state that is used while generating LLVM code.
const TargetInfo & getTarget() const
llvm::LLVMContext & getLLVMContext()
This class organizes the cross-module state that is used while lowering AST types to LLVM types.
Definition: CodeGenTypes.h:54
DefaultABIInfo - The default implementation for ABI specific details.
Definition: ABIInfoImpl.h:21
ABIArgInfo classifyArgumentType(QualType RetTy) const
Definition: ABIInfoImpl.cpp:17
TargetCodeGenInfo - This class organizes various target-specific codegeneration issues,...
Definition: TargetInfo.h:46
ExtInfo withCallingConv(CallingConv cc) const
Definition: Type.h:4494
FunctionType - C99 6.7.5.3 - Function Declarators.
Definition: Type.h:4268
ExtInfo getExtInfo() const
Definition: Type.h:4597
A (possibly-)qualified type.
Definition: Type.h:940
Represents a struct/union/class.
Definition: Decl.h:4171
bool hasFlexibleArrayMember() const
Definition: Decl.h:4204
A helper class that allows the use of isa/cast/dyncast to detect TagType objects of structs/unions/cl...
Definition: Type.h:5561
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
Definition: TargetInfo.h:1256
The base class of the type hierarchy.
Definition: Type.h:1813
const T * getAs() const
Member-template getAs<specific type>'.
Definition: Type.h:8160
ABIArgInfo classifyArgumentType(CodeGenModule &CGM, CanQualType type)
Classify the rules for how to pass a particular type.
CGCXXABI::RecordArgABI getRecordArgABI(const RecordType *RT, CGCXXABI &CXXABI)
bool classifyReturnType(const CGCXXABI &CXXABI, CGFunctionInfo &FI, const ABIInfo &Info)
void computeSPIRKernelABIInfo(CodeGenModule &CGM, CGFunctionInfo &FI)
Definition: SPIR.cpp:247
bool isAggregateTypeForABI(QualType T)
const Type * isSingleElementStruct(QualType T, ASTContext &Context)
isSingleElementStruct - Determine if a structure is a "single element struct", i.e.
std::unique_ptr< TargetCodeGenInfo > createSPIRVTargetCodeGenInfo(CodeGenModule &CGM)
Definition: SPIR.cpp:365
QualType useFirstFieldIfTransparentUnion(QualType Ty)
Pass transparent unions as if they were the type of the first element.
std::unique_ptr< TargetCodeGenInfo > createCommonSPIRTargetCodeGenInfo(CodeGenModule &CGM)
Definition: SPIR.cpp:360
bool isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays, bool AsIfNoUniqueAddr=false)
isEmptyRecord - Return true iff a structure contains only empty fields.
const internal::VariadicAllOfMatcher< Type > type
Matches Types in the clang AST.
The JSON file list parser is used to communicate input to InstallAPI.
LangAS
Defines the address space values used by the address space qualifier of QualType.
Definition: AddressSpaces.h:25
const FunctionProtoType * T
@ CC_OpenCLKernel
Definition: Specifiers.h:289
LangAS getLangASFromTargetAS(unsigned TargetAS)
Definition: AddressSpaces.h:86
unsigned long uint64_t
Definition: Format.h:5433