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