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 | private: |
26 | void setCCs(); |
27 | }; |
28 | |
29 | class SPIRVABIInfo : public CommonSPIRABIInfo { |
30 | public: |
31 | 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 | : TargetCodeGenInfo(std::make_unique<CommonSPIRABIInfo>(args&: CGT)) {} |
43 | CommonSPIRTargetCodeGenInfo(std::unique_ptr<ABIInfo> ABIInfo) |
44 | : TargetCodeGenInfo(std::move(ABIInfo)) {} |
45 | |
46 | LangAS getASTAllocaAddressSpace() const override { |
47 | return getLangASFromTargetAS( |
48 | TargetAS: getABIInfo().getDataLayout().getAllocaAddrSpace()); |
49 | } |
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 | : CommonSPIRTargetCodeGenInfo(std::make_unique<SPIRVABIInfo>(args&: CGT)) {} |
58 | void setCUDAKernelCallingConvention(const FunctionType *&FT) const override; |
59 | }; |
60 | } // End anonymous namespace. |
61 | |
62 | void CommonSPIRABIInfo::setCCs() { |
63 | assert(getRuntimeCC() == llvm::CallingConv::C); |
64 | RuntimeCC = llvm::CallingConv::SPIR_FUNC; |
65 | } |
66 | |
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(T: Ty); |
73 | auto DefaultAS = getContext().getTargetAddressSpace(AS: LangAS::Default); |
74 | auto GlobalAS = getContext().getTargetAddressSpace(AS: LangAS::cuda_device); |
75 | auto *PtrTy = llvm::dyn_cast<llvm::PointerType>(Val: LTy); |
76 | if (PtrTy && PtrTy->getAddressSpace() == DefaultAS) { |
77 | LTy = llvm::PointerType::get(C&: PtrTy->getContext(), AddressSpace: GlobalAS); |
78 | return ABIArgInfo::getDirect(T: LTy, Offset: 0, Padding: nullptr, CanBeFlattened: false); |
79 | } |
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 | if (isAggregateTypeForABI(T: Ty)) |
88 | return getNaturalAlignIndirect(Ty, /* byval */ ByVal: true); |
89 | } |
90 | return classifyArgumentType(RetTy: Ty); |
91 | } |
92 | |
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(); |
97 | |
98 | if (!getCXXABI().classifyReturnType(FI)) |
99 | FI.getReturnInfo() = classifyReturnType(RetTy: FI.getReturnType()); |
100 | |
101 | for (auto &I : FI.arguments()) { |
102 | if (CC == llvm::CallingConv::SPIR_KERNEL) { |
103 | I.info = classifyKernelArgumentType(Ty: I.type); |
104 | } else { |
105 | I.info = classifyArgumentType(RetTy: I.type); |
106 | } |
107 | } |
108 | } |
109 | |
110 | namespace clang { |
111 | namespace CodeGen { |
112 | void computeSPIRKernelABIInfo(CodeGenModule &CGM, CGFunctionInfo &FI) { |
113 | if (CGM.getTarget().getTriple().isSPIRV()) |
114 | SPIRVABIInfo(CGM.getTypes()).computeInfo(FI); |
115 | else |
116 | CommonSPIRABIInfo(CGM.getTypes()).computeInfo(FI); |
117 | } |
118 | } |
119 | } |
120 | |
121 | unsigned CommonSPIRTargetCodeGenInfo::getOpenCLKernelCallingConv() const { |
122 | return llvm::CallingConv::SPIR_KERNEL; |
123 | } |
124 | |
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 | Fn: FT, EInfo: FT->getExtInfo().withCallingConv(cc: CC_OpenCLKernel)); |
131 | return; |
132 | } |
133 | } |
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 | 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 | 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 | if (OpenCLName.starts_with(Prefix: "image2d" )) |
150 | IntParams[0] = 1; // 1D |
151 | else if (OpenCLName.starts_with(Prefix: "image3d" )) |
152 | IntParams[0] = 2; // 2D |
153 | else if (OpenCLName == "image1d_buffer" ) |
154 | IntParams[0] = 5; // Buffer |
155 | else |
156 | 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 | if (OpenCLName.contains(Other: "_depth" )) |
162 | IntParams[1] = 1; |
163 | if (OpenCLName.contains(Other: "_array" )) |
164 | IntParams[2] = 1; |
165 | if (OpenCLName.contains(Other: "_msaa" )) |
166 | IntParams[3] = 1; |
167 | |
168 | // Access qualifier |
169 | IntParams.push_back(Elt: AccessQualifier); |
170 | |
171 | return llvm::TargetExtType::get(Context&: Ctx, Name: BaseType, Types: {llvm::Type::getVoidTy(C&: Ctx)}, |
172 | Ints: IntParams); |
173 | } |
174 | |
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>(Val: Ty)) |
179 | return llvm::TargetExtType::get(Context&: Ctx, Name: "spirv.Pipe" , Types: {}, |
180 | Ints: {!PipeTy->isReadOnly()}); |
181 | if (auto *BuiltinTy = dyn_cast<BuiltinType>(Val: 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(Context&: Ctx, Name: "spirv.Sampler" ); |
190 | case BuiltinType::OCLEvent: |
191 | return llvm::TargetExtType::get(Context&: Ctx, Name: "spirv.Event" ); |
192 | case BuiltinType::OCLClkEvent: |
193 | return llvm::TargetExtType::get(Context&: Ctx, Name: "spirv.DeviceEvent" ); |
194 | case BuiltinType::OCLQueue: |
195 | return llvm::TargetExtType::get(Context&: Ctx, Name: "spirv.Queue" ); |
196 | case BuiltinType::OCLReserveID: |
197 | return llvm::TargetExtType::get(Context&: Ctx, Name: "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" |
202 | default: |
203 | return nullptr; |
204 | } |
205 | } |
206 | |
207 | return nullptr; |
208 | } |
209 | |
210 | std::unique_ptr<TargetCodeGenInfo> |
211 | CodeGen::createCommonSPIRTargetCodeGenInfo(CodeGenModule &CGM) { |
212 | return std::make_unique<CommonSPIRTargetCodeGenInfo>(args&: CGM.getTypes()); |
213 | } |
214 | |
215 | std::unique_ptr<TargetCodeGenInfo> |
216 | CodeGen::createSPIRVTargetCodeGenInfo(CodeGenModule &CGM) { |
217 | return std::make_unique<SPIRVTargetCodeGenInfo>(args&: CGM.getTypes()); |
218 | } |
219 | |