1 //===- SPIR.cpp -----------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "ABIInfoImpl.h"
10 #include "TargetInfo.h"
12 using namespace clang
;
13 using namespace clang::CodeGen
;
15 //===----------------------------------------------------------------------===//
16 // Base ABI and target codegen info implementation common between SPIR and
18 //===----------------------------------------------------------------------===//
21 class CommonSPIRABIInfo
: public DefaultABIInfo
{
23 CommonSPIRABIInfo(CodeGenTypes
&CGT
) : DefaultABIInfo(CGT
) { setCCs(); }
29 class SPIRVABIInfo
: public CommonSPIRABIInfo
{
31 SPIRVABIInfo(CodeGenTypes
&CGT
) : CommonSPIRABIInfo(CGT
) {}
32 void computeInfo(CGFunctionInfo
&FI
) const override
;
35 ABIArgInfo
classifyKernelArgumentType(QualType Ty
) const;
37 } // end anonymous namespace
39 class CommonSPIRTargetCodeGenInfo
: public TargetCodeGenInfo
{
41 CommonSPIRTargetCodeGenInfo(CodeGen::CodeGenTypes
&CGT
)
42 : TargetCodeGenInfo(std::make_unique
<CommonSPIRABIInfo
>(CGT
)) {}
43 CommonSPIRTargetCodeGenInfo(std::unique_ptr
<ABIInfo
> ABIInfo
)
44 : TargetCodeGenInfo(std::move(ABIInfo
)) {}
46 LangAS
getASTAllocaAddressSpace() const override
{
47 return getLangASFromTargetAS(
48 getABIInfo().getDataLayout().getAllocaAddrSpace());
51 unsigned getOpenCLKernelCallingConv() const override
;
52 llvm::Type
*getOpenCLType(CodeGenModule
&CGM
, const Type
*T
) const override
;
54 class SPIRVTargetCodeGenInfo
: public CommonSPIRTargetCodeGenInfo
{
56 SPIRVTargetCodeGenInfo(CodeGen::CodeGenTypes
&CGT
)
57 : CommonSPIRTargetCodeGenInfo(std::make_unique
<SPIRVABIInfo
>(CGT
)) {}
58 void setCUDAKernelCallingConvention(const FunctionType
*&FT
) const override
;
60 } // End anonymous namespace.
62 void CommonSPIRABIInfo::setCCs() {
63 assert(getRuntimeCC() == llvm::CallingConv::C
);
64 RuntimeCC
= llvm::CallingConv::SPIR_FUNC
;
67 ABIArgInfo
SPIRVABIInfo::classifyKernelArgumentType(QualType Ty
) const {
68 if (getContext().getLangOpts().CUDAIsDevice
) {
69 // Coerce pointer arguments with default address space to CrossWorkGroup
70 // pointers for HIPSPV/CUDASPV. When the language mode is HIP/CUDA, the
71 // SPIRTargetInfo maps cuda_device to SPIR-V's CrossWorkGroup address space.
72 llvm::Type
*LTy
= CGT
.ConvertType(Ty
);
73 auto DefaultAS
= getContext().getTargetAddressSpace(LangAS::Default
);
74 auto GlobalAS
= getContext().getTargetAddressSpace(LangAS::cuda_device
);
75 auto *PtrTy
= llvm::dyn_cast
<llvm::PointerType
>(LTy
);
76 if (PtrTy
&& PtrTy
->getAddressSpace() == DefaultAS
) {
77 LTy
= llvm::PointerType::get(PtrTy
->getContext(), GlobalAS
);
78 return ABIArgInfo::getDirect(LTy
, 0, nullptr, false);
81 // Force copying aggregate type in kernel arguments by value when
82 // compiling CUDA targeting SPIR-V. This is required for the object
83 // copied to be valid on the device.
84 // This behavior follows the CUDA spec
85 // https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#global-function-argument-processing,
86 // and matches the NVPTX implementation.
87 if (isAggregateTypeForABI(Ty
))
88 return getNaturalAlignIndirect(Ty
, /* byval */ true);
90 return classifyArgumentType(Ty
);
93 void SPIRVABIInfo::computeInfo(CGFunctionInfo
&FI
) const {
94 // The logic is same as in DefaultABIInfo with an exception on the kernel
95 // arguments handling.
96 llvm::CallingConv::ID CC
= FI
.getCallingConvention();
98 if (!getCXXABI().classifyReturnType(FI
))
99 FI
.getReturnInfo() = classifyReturnType(FI
.getReturnType());
101 for (auto &I
: FI
.arguments()) {
102 if (CC
== llvm::CallingConv::SPIR_KERNEL
) {
103 I
.info
= classifyKernelArgumentType(I
.type
);
105 I
.info
= classifyArgumentType(I
.type
);
112 void computeSPIRKernelABIInfo(CodeGenModule
&CGM
, CGFunctionInfo
&FI
) {
113 if (CGM
.getTarget().getTriple().isSPIRV())
114 SPIRVABIInfo(CGM
.getTypes()).computeInfo(FI
);
116 CommonSPIRABIInfo(CGM
.getTypes()).computeInfo(FI
);
121 unsigned CommonSPIRTargetCodeGenInfo::getOpenCLKernelCallingConv() const {
122 return llvm::CallingConv::SPIR_KERNEL
;
125 void SPIRVTargetCodeGenInfo::setCUDAKernelCallingConvention(
126 const FunctionType
*&FT
) const {
127 // Convert HIP kernels to SPIR-V kernels.
128 if (getABIInfo().getContext().getLangOpts().HIP
) {
129 FT
= getABIInfo().getContext().adjustFunctionType(
130 FT
, FT
->getExtInfo().withCallingConv(CC_OpenCLKernel
));
135 /// Construct a SPIR-V target extension type for the given OpenCL image type.
136 static llvm::Type
*getSPIRVImageType(llvm::LLVMContext
&Ctx
, StringRef BaseType
,
137 StringRef OpenCLName
,
138 unsigned AccessQualifier
) {
139 // These parameters compare to the operands of OpTypeImage (see
140 // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpTypeImage
141 // for more details). The first 6 integer parameters all default to 0, and
142 // will be changed to 1 only for the image type(s) that set the parameter to
143 // one. The 7th integer parameter is the access qualifier, which is tacked on
145 SmallVector
<unsigned, 7> IntParams
= {0, 0, 0, 0, 0, 0};
147 // Choose the dimension of the image--this corresponds to the Dim enum in
148 // SPIR-V (first integer parameter of OpTypeImage).
149 if (OpenCLName
.startswith("image2d"))
150 IntParams
[0] = 1; // 1D
151 else if (OpenCLName
.startswith("image3d"))
152 IntParams
[0] = 2; // 2D
153 else if (OpenCLName
== "image1d_buffer")
154 IntParams
[0] = 5; // Buffer
156 assert(OpenCLName
.startswith("image1d") && "Unknown image type");
158 // Set the other integer parameters of OpTypeImage if necessary. Note that the
159 // OpenCL image types don't provide any information for the Sampled or
160 // Image Format parameters.
161 if (OpenCLName
.contains("_depth"))
163 if (OpenCLName
.contains("_array"))
165 if (OpenCLName
.contains("_msaa"))
169 IntParams
.push_back(AccessQualifier
);
171 return llvm::TargetExtType::get(Ctx
, BaseType
, {llvm::Type::getVoidTy(Ctx
)},
175 llvm::Type
*CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule
&CGM
,
176 const Type
*Ty
) const {
177 llvm::LLVMContext
&Ctx
= CGM
.getLLVMContext();
178 if (auto *PipeTy
= dyn_cast
<PipeType
>(Ty
))
179 return llvm::TargetExtType::get(Ctx
, "spirv.Pipe", {},
180 {!PipeTy
->isReadOnly()});
181 if (auto *BuiltinTy
= dyn_cast
<BuiltinType
>(Ty
)) {
182 enum AccessQualifier
: unsigned { AQ_ro
= 0, AQ_wo
= 1, AQ_rw
= 2 };
183 switch (BuiltinTy
->getKind()) {
184 #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
185 case BuiltinType::Id: \
186 return getSPIRVImageType(Ctx, "spirv.Image", #ImgType, AQ_##Suffix);
187 #include "clang/Basic/OpenCLImageTypes.def"
188 case BuiltinType::OCLSampler
:
189 return llvm::TargetExtType::get(Ctx
, "spirv.Sampler");
190 case BuiltinType::OCLEvent
:
191 return llvm::TargetExtType::get(Ctx
, "spirv.Event");
192 case BuiltinType::OCLClkEvent
:
193 return llvm::TargetExtType::get(Ctx
, "spirv.DeviceEvent");
194 case BuiltinType::OCLQueue
:
195 return llvm::TargetExtType::get(Ctx
, "spirv.Queue");
196 case BuiltinType::OCLReserveID
:
197 return llvm::TargetExtType::get(Ctx
, "spirv.ReserveId");
198 #define INTEL_SUBGROUP_AVC_TYPE(Name, Id) \
199 case BuiltinType::OCLIntelSubgroupAVC##Id: \
200 return llvm::TargetExtType::get(Ctx, "spirv.Avc" #Id "INTEL");
201 #include "clang/Basic/OpenCLExtensionTypes.def"
210 std::unique_ptr
<TargetCodeGenInfo
>
211 CodeGen::createCommonSPIRTargetCodeGenInfo(CodeGenModule
&CGM
) {
212 return std::make_unique
<CommonSPIRTargetCodeGenInfo
>(CGM
.getTypes());
215 std::unique_ptr
<TargetCodeGenInfo
>
216 CodeGen::createSPIRVTargetCodeGenInfo(CodeGenModule
&CGM
) {
217 return std::make_unique
<SPIRVTargetCodeGenInfo
>(CGM
.getTypes());