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>(CGT)) {} 43 CommonSPIRTargetCodeGenInfo(std::unique_ptr<ABIInfo> ABIInfo) 44 : TargetCodeGenInfo(std::move(ABIInfo)) {} 45 46 LangAS getASTAllocaAddressSpace() const override { 47 return getLangASFromTargetAS( 48 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>(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(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); 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(Ty)) 88 return getNaturalAlignIndirect(Ty, /* byval */ true); 89 } 90 return classifyArgumentType(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(FI.getReturnType()); 100 101 for (auto &I : FI.arguments()) { 102 if (CC == llvm::CallingConv::SPIR_KERNEL) { 103 I.info = classifyKernelArgumentType(I.type); 104 } else { 105 I.info = classifyArgumentType(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 FT, FT->getExtInfo().withCallingConv(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("image2d")) 150 IntParams[0] = 1; // 1D 151 else if (OpenCLName.starts_with("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("_depth")) 162 IntParams[1] = 1; 163 if (OpenCLName.contains("_array")) 164 IntParams[2] = 1; 165 if (OpenCLName.contains("_msaa")) 166 IntParams[3] = 1; 167 168 // Access qualifier 169 IntParams.push_back(AccessQualifier); 170 171 return llvm::TargetExtType::get(Ctx, BaseType, {llvm::Type::getVoidTy(Ctx)}, 172 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>(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" 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>(CGM.getTypes()); 213 } 214 215 std::unique_ptr<TargetCodeGenInfo> 216 CodeGen::createSPIRVTargetCodeGenInfo(CodeGenModule &CGM) { 217 return std::make_unique<SPIRVTargetCodeGenInfo>(CGM.getTypes()); 218 } 219