/src/llvm-project/clang/lib/CodeGen/Targets/SPIR.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 0 | CommonSPIRABIInfo(CodeGenTypes &CGT) : DefaultABIInfo(CGT) { setCCs(); } |
24 | | |
25 | | private: |
26 | | void setCCs(); |
27 | | }; |
28 | | |
29 | | class SPIRVABIInfo : public CommonSPIRABIInfo { |
30 | | public: |
31 | 0 | SPIRVABIInfo(CodeGenTypes &CGT) : CommonSPIRABIInfo(CGT) {} |
32 | | void computeInfo(CGFunctionInfo &FI) const override; |
33 | | |
34 | | private: |
35 | | ABIArgInfo classifyKernelArgumentType(QualType Ty) const; |
36 | | }; |
37 | | } // end anonymous namespace |
38 | | namespace { |
39 | | class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo { |
40 | | public: |
41 | | CommonSPIRTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT) |
42 | 0 | : TargetCodeGenInfo(std::make_unique<CommonSPIRABIInfo>(CGT)) {} |
43 | | CommonSPIRTargetCodeGenInfo(std::unique_ptr<ABIInfo> ABIInfo) |
44 | 0 | : TargetCodeGenInfo(std::move(ABIInfo)) {} |
45 | | |
46 | 0 | LangAS getASTAllocaAddressSpace() const override { |
47 | 0 | return getLangASFromTargetAS( |
48 | 0 | getABIInfo().getDataLayout().getAllocaAddrSpace()); |
49 | 0 | } |
50 | | |
51 | | unsigned getOpenCLKernelCallingConv() const override; |
52 | | llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override; |
53 | | }; |
54 | | class SPIRVTargetCodeGenInfo : public CommonSPIRTargetCodeGenInfo { |
55 | | public: |
56 | | SPIRVTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT) |
57 | 0 | : CommonSPIRTargetCodeGenInfo(std::make_unique<SPIRVABIInfo>(CGT)) {} |
58 | | void setCUDAKernelCallingConvention(const FunctionType *&FT) const override; |
59 | | }; |
60 | | } // End anonymous namespace. |
61 | | |
62 | 0 | void CommonSPIRABIInfo::setCCs() { |
63 | 0 | assert(getRuntimeCC() == llvm::CallingConv::C); |
64 | 0 | RuntimeCC = llvm::CallingConv::SPIR_FUNC; |
65 | 0 | } |
66 | | |
67 | 0 | ABIArgInfo SPIRVABIInfo::classifyKernelArgumentType(QualType Ty) const { |
68 | 0 | 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 | 0 | llvm::Type *LTy = CGT.ConvertType(Ty); |
73 | 0 | auto DefaultAS = getContext().getTargetAddressSpace(LangAS::Default); |
74 | 0 | auto GlobalAS = getContext().getTargetAddressSpace(LangAS::cuda_device); |
75 | 0 | auto *PtrTy = llvm::dyn_cast<llvm::PointerType>(LTy); |
76 | 0 | if (PtrTy && PtrTy->getAddressSpace() == DefaultAS) { |
77 | 0 | LTy = llvm::PointerType::get(PtrTy->getContext(), GlobalAS); |
78 | 0 | return ABIArgInfo::getDirect(LTy, 0, nullptr, false); |
79 | 0 | } |
80 | | |
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 | 0 | if (isAggregateTypeForABI(Ty)) |
88 | 0 | return getNaturalAlignIndirect(Ty, /* byval */ true); |
89 | 0 | } |
90 | 0 | return classifyArgumentType(Ty); |
91 | 0 | } |
92 | | |
93 | 0 | void SPIRVABIInfo::computeInfo(CGFunctionInfo &FI) const { |
94 | | // The logic is same as in DefaultABIInfo with an exception on the kernel |
95 | | // arguments handling. |
96 | 0 | llvm::CallingConv::ID CC = FI.getCallingConvention(); |
97 | |
|
98 | 0 | if (!getCXXABI().classifyReturnType(FI)) |
99 | 0 | FI.getReturnInfo() = classifyReturnType(FI.getReturnType()); |
100 | |
|
101 | 0 | for (auto &I : FI.arguments()) { |
102 | 0 | if (CC == llvm::CallingConv::SPIR_KERNEL) { |
103 | 0 | I.info = classifyKernelArgumentType(I.type); |
104 | 0 | } else { |
105 | 0 | I.info = classifyArgumentType(I.type); |
106 | 0 | } |
107 | 0 | } |
108 | 0 | } |
109 | | |
110 | | namespace clang { |
111 | | namespace CodeGen { |
112 | 0 | void computeSPIRKernelABIInfo(CodeGenModule &CGM, CGFunctionInfo &FI) { |
113 | 0 | if (CGM.getTarget().getTriple().isSPIRV()) |
114 | 0 | SPIRVABIInfo(CGM.getTypes()).computeInfo(FI); |
115 | 0 | else |
116 | 0 | CommonSPIRABIInfo(CGM.getTypes()).computeInfo(FI); |
117 | 0 | } |
118 | | } |
119 | | } |
120 | | |
121 | 0 | unsigned CommonSPIRTargetCodeGenInfo::getOpenCLKernelCallingConv() const { |
122 | 0 | return llvm::CallingConv::SPIR_KERNEL; |
123 | 0 | } |
124 | | |
125 | | void SPIRVTargetCodeGenInfo::setCUDAKernelCallingConvention( |
126 | 0 | const FunctionType *&FT) const { |
127 | | // Convert HIP kernels to SPIR-V kernels. |
128 | 0 | if (getABIInfo().getContext().getLangOpts().HIP) { |
129 | 0 | FT = getABIInfo().getContext().adjustFunctionType( |
130 | 0 | FT, FT->getExtInfo().withCallingConv(CC_OpenCLKernel)); |
131 | 0 | return; |
132 | 0 | } |
133 | 0 | } |
134 | | |
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 | 0 | 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 |
144 | | // at the end. |
145 | 0 | SmallVector<unsigned, 7> IntParams = {0, 0, 0, 0, 0, 0}; |
146 | | |
147 | | // Choose the dimension of the image--this corresponds to the Dim enum in |
148 | | // SPIR-V (first integer parameter of OpTypeImage). |
149 | 0 | if (OpenCLName.starts_with("image2d")) |
150 | 0 | IntParams[0] = 1; // 1D |
151 | 0 | else if (OpenCLName.starts_with("image3d")) |
152 | 0 | IntParams[0] = 2; // 2D |
153 | 0 | else if (OpenCLName == "image1d_buffer") |
154 | 0 | IntParams[0] = 5; // Buffer |
155 | 0 | else |
156 | 0 | assert(OpenCLName.starts_with("image1d") && "Unknown image type"); |
157 | | |
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 | 0 | if (OpenCLName.contains("_depth")) |
162 | 0 | IntParams[1] = 1; |
163 | 0 | if (OpenCLName.contains("_array")) |
164 | 0 | IntParams[2] = 1; |
165 | 0 | if (OpenCLName.contains("_msaa")) |
166 | 0 | IntParams[3] = 1; |
167 | | |
168 | | // Access qualifier |
169 | 0 | IntParams.push_back(AccessQualifier); |
170 | |
|
171 | 0 | return llvm::TargetExtType::get(Ctx, BaseType, {llvm::Type::getVoidTy(Ctx)}, |
172 | 0 | IntParams); |
173 | 0 | } |
174 | | |
175 | | llvm::Type *CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM, |
176 | 0 | const Type *Ty) const { |
177 | 0 | llvm::LLVMContext &Ctx = CGM.getLLVMContext(); |
178 | 0 | if (auto *PipeTy = dyn_cast<PipeType>(Ty)) |
179 | 0 | return llvm::TargetExtType::get(Ctx, "spirv.Pipe", {}, |
180 | 0 | {!PipeTy->isReadOnly()}); |
181 | 0 | if (auto *BuiltinTy = dyn_cast<BuiltinType>(Ty)) { |
182 | 0 | enum AccessQualifier : unsigned { AQ_ro = 0, AQ_wo = 1, AQ_rw = 2 }; |
183 | 0 | switch (BuiltinTy->getKind()) { |
184 | 0 | #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ |
185 | 0 | case BuiltinType::Id: \ |
186 | 0 | return getSPIRVImageType(Ctx, "spirv.Image", #ImgType, AQ_##Suffix); |
187 | 0 | #include "clang/Basic/OpenCLImageTypes.def" |
188 | 0 | case BuiltinType::OCLSampler: |
189 | 0 | return llvm::TargetExtType::get(Ctx, "spirv.Sampler"); |
190 | 0 | case BuiltinType::OCLEvent: |
191 | 0 | return llvm::TargetExtType::get(Ctx, "spirv.Event"); |
192 | 0 | case BuiltinType::OCLClkEvent: |
193 | 0 | return llvm::TargetExtType::get(Ctx, "spirv.DeviceEvent"); |
194 | 0 | case BuiltinType::OCLQueue: |
195 | 0 | return llvm::TargetExtType::get(Ctx, "spirv.Queue"); |
196 | 0 | case BuiltinType::OCLReserveID: |
197 | 0 | return llvm::TargetExtType::get(Ctx, "spirv.ReserveId"); |
198 | 0 | #define INTEL_SUBGROUP_AVC_TYPE(Name, Id) \ |
199 | 0 | case BuiltinType::OCLIntelSubgroupAVC##Id: \ |
200 | 0 | return llvm::TargetExtType::get(Ctx, "spirv.Avc" #Id "INTEL"); |
201 | 0 | #include "clang/Basic/OpenCLExtensionTypes.def" |
202 | 0 | default: |
203 | 0 | return nullptr; |
204 | 0 | } |
205 | 0 | } |
206 | | |
207 | 0 | return nullptr; |
208 | 0 | } |
209 | | |
210 | | std::unique_ptr<TargetCodeGenInfo> |
211 | 0 | CodeGen::createCommonSPIRTargetCodeGenInfo(CodeGenModule &CGM) { |
212 | 0 | return std::make_unique<CommonSPIRTargetCodeGenInfo>(CGM.getTypes()); |
213 | 0 | } |
214 | | |
215 | | std::unique_ptr<TargetCodeGenInfo> |
216 | 0 | CodeGen::createSPIRVTargetCodeGenInfo(CodeGenModule &CGM) { |
217 | 0 | return std::make_unique<SPIRVTargetCodeGenInfo>(CGM.getTypes()); |
218 | 0 | } |