106c3fb27SDimitry Andric //===- SPIR.cpp -----------------------------------------------------------===// 206c3fb27SDimitry Andric // 306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 606c3fb27SDimitry Andric // 706c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 806c3fb27SDimitry Andric 906c3fb27SDimitry Andric #include "ABIInfoImpl.h" 1006c3fb27SDimitry Andric #include "TargetInfo.h" 1106c3fb27SDimitry Andric 1206c3fb27SDimitry Andric using namespace clang; 1306c3fb27SDimitry Andric using namespace clang::CodeGen; 1406c3fb27SDimitry Andric 1506c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 1606c3fb27SDimitry Andric // Base ABI and target codegen info implementation common between SPIR and 1706c3fb27SDimitry Andric // SPIR-V. 1806c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 1906c3fb27SDimitry Andric 2006c3fb27SDimitry Andric namespace { 2106c3fb27SDimitry Andric class CommonSPIRABIInfo : public DefaultABIInfo { 2206c3fb27SDimitry Andric public: 2306c3fb27SDimitry Andric CommonSPIRABIInfo(CodeGenTypes &CGT) : DefaultABIInfo(CGT) { setCCs(); } 2406c3fb27SDimitry Andric 2506c3fb27SDimitry Andric private: 2606c3fb27SDimitry Andric void setCCs(); 2706c3fb27SDimitry Andric }; 2806c3fb27SDimitry Andric 2906c3fb27SDimitry Andric class SPIRVABIInfo : public CommonSPIRABIInfo { 3006c3fb27SDimitry Andric public: 3106c3fb27SDimitry Andric SPIRVABIInfo(CodeGenTypes &CGT) : CommonSPIRABIInfo(CGT) {} 3206c3fb27SDimitry Andric void computeInfo(CGFunctionInfo &FI) const override; 3306c3fb27SDimitry Andric 3406c3fb27SDimitry Andric private: 3506c3fb27SDimitry Andric ABIArgInfo classifyKernelArgumentType(QualType Ty) const; 3606c3fb27SDimitry Andric }; 3706c3fb27SDimitry Andric } // end anonymous namespace 3806c3fb27SDimitry Andric namespace { 3906c3fb27SDimitry Andric class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo { 4006c3fb27SDimitry Andric public: 4106c3fb27SDimitry Andric CommonSPIRTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT) 4206c3fb27SDimitry Andric : TargetCodeGenInfo(std::make_unique<CommonSPIRABIInfo>(CGT)) {} 4306c3fb27SDimitry Andric CommonSPIRTargetCodeGenInfo(std::unique_ptr<ABIInfo> ABIInfo) 4406c3fb27SDimitry Andric : TargetCodeGenInfo(std::move(ABIInfo)) {} 4506c3fb27SDimitry Andric 4606c3fb27SDimitry Andric LangAS getASTAllocaAddressSpace() const override { 4706c3fb27SDimitry Andric return getLangASFromTargetAS( 4806c3fb27SDimitry Andric getABIInfo().getDataLayout().getAllocaAddrSpace()); 4906c3fb27SDimitry Andric } 5006c3fb27SDimitry Andric 5106c3fb27SDimitry Andric unsigned getOpenCLKernelCallingConv() const override; 5206c3fb27SDimitry Andric llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override; 5306c3fb27SDimitry Andric }; 5406c3fb27SDimitry Andric class SPIRVTargetCodeGenInfo : public CommonSPIRTargetCodeGenInfo { 5506c3fb27SDimitry Andric public: 5606c3fb27SDimitry Andric SPIRVTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT) 5706c3fb27SDimitry Andric : CommonSPIRTargetCodeGenInfo(std::make_unique<SPIRVABIInfo>(CGT)) {} 5806c3fb27SDimitry Andric void setCUDAKernelCallingConvention(const FunctionType *&FT) const override; 5906c3fb27SDimitry Andric }; 6006c3fb27SDimitry Andric } // End anonymous namespace. 6106c3fb27SDimitry Andric 6206c3fb27SDimitry Andric void CommonSPIRABIInfo::setCCs() { 6306c3fb27SDimitry Andric assert(getRuntimeCC() == llvm::CallingConv::C); 6406c3fb27SDimitry Andric RuntimeCC = llvm::CallingConv::SPIR_FUNC; 6506c3fb27SDimitry Andric } 6606c3fb27SDimitry Andric 6706c3fb27SDimitry Andric ABIArgInfo SPIRVABIInfo::classifyKernelArgumentType(QualType Ty) const { 6806c3fb27SDimitry Andric if (getContext().getLangOpts().CUDAIsDevice) { 6906c3fb27SDimitry Andric // Coerce pointer arguments with default address space to CrossWorkGroup 7006c3fb27SDimitry Andric // pointers for HIPSPV/CUDASPV. When the language mode is HIP/CUDA, the 7106c3fb27SDimitry Andric // SPIRTargetInfo maps cuda_device to SPIR-V's CrossWorkGroup address space. 7206c3fb27SDimitry Andric llvm::Type *LTy = CGT.ConvertType(Ty); 7306c3fb27SDimitry Andric auto DefaultAS = getContext().getTargetAddressSpace(LangAS::Default); 7406c3fb27SDimitry Andric auto GlobalAS = getContext().getTargetAddressSpace(LangAS::cuda_device); 7506c3fb27SDimitry Andric auto *PtrTy = llvm::dyn_cast<llvm::PointerType>(LTy); 7606c3fb27SDimitry Andric if (PtrTy && PtrTy->getAddressSpace() == DefaultAS) { 7706c3fb27SDimitry Andric LTy = llvm::PointerType::get(PtrTy->getContext(), GlobalAS); 7806c3fb27SDimitry Andric return ABIArgInfo::getDirect(LTy, 0, nullptr, false); 7906c3fb27SDimitry Andric } 8006c3fb27SDimitry Andric 8106c3fb27SDimitry Andric // Force copying aggregate type in kernel arguments by value when 8206c3fb27SDimitry Andric // compiling CUDA targeting SPIR-V. This is required for the object 8306c3fb27SDimitry Andric // copied to be valid on the device. 8406c3fb27SDimitry Andric // This behavior follows the CUDA spec 8506c3fb27SDimitry Andric // https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#global-function-argument-processing, 8606c3fb27SDimitry Andric // and matches the NVPTX implementation. 8706c3fb27SDimitry Andric if (isAggregateTypeForABI(Ty)) 8806c3fb27SDimitry Andric return getNaturalAlignIndirect(Ty, /* byval */ true); 8906c3fb27SDimitry Andric } 9006c3fb27SDimitry Andric return classifyArgumentType(Ty); 9106c3fb27SDimitry Andric } 9206c3fb27SDimitry Andric 9306c3fb27SDimitry Andric void SPIRVABIInfo::computeInfo(CGFunctionInfo &FI) const { 9406c3fb27SDimitry Andric // The logic is same as in DefaultABIInfo with an exception on the kernel 9506c3fb27SDimitry Andric // arguments handling. 9606c3fb27SDimitry Andric llvm::CallingConv::ID CC = FI.getCallingConvention(); 9706c3fb27SDimitry Andric 9806c3fb27SDimitry Andric if (!getCXXABI().classifyReturnType(FI)) 9906c3fb27SDimitry Andric FI.getReturnInfo() = classifyReturnType(FI.getReturnType()); 10006c3fb27SDimitry Andric 10106c3fb27SDimitry Andric for (auto &I : FI.arguments()) { 10206c3fb27SDimitry Andric if (CC == llvm::CallingConv::SPIR_KERNEL) { 10306c3fb27SDimitry Andric I.info = classifyKernelArgumentType(I.type); 10406c3fb27SDimitry Andric } else { 10506c3fb27SDimitry Andric I.info = classifyArgumentType(I.type); 10606c3fb27SDimitry Andric } 10706c3fb27SDimitry Andric } 10806c3fb27SDimitry Andric } 10906c3fb27SDimitry Andric 11006c3fb27SDimitry Andric namespace clang { 11106c3fb27SDimitry Andric namespace CodeGen { 11206c3fb27SDimitry Andric void computeSPIRKernelABIInfo(CodeGenModule &CGM, CGFunctionInfo &FI) { 11306c3fb27SDimitry Andric if (CGM.getTarget().getTriple().isSPIRV()) 11406c3fb27SDimitry Andric SPIRVABIInfo(CGM.getTypes()).computeInfo(FI); 11506c3fb27SDimitry Andric else 11606c3fb27SDimitry Andric CommonSPIRABIInfo(CGM.getTypes()).computeInfo(FI); 11706c3fb27SDimitry Andric } 11806c3fb27SDimitry Andric } 11906c3fb27SDimitry Andric } 12006c3fb27SDimitry Andric 12106c3fb27SDimitry Andric unsigned CommonSPIRTargetCodeGenInfo::getOpenCLKernelCallingConv() const { 12206c3fb27SDimitry Andric return llvm::CallingConv::SPIR_KERNEL; 12306c3fb27SDimitry Andric } 12406c3fb27SDimitry Andric 12506c3fb27SDimitry Andric void SPIRVTargetCodeGenInfo::setCUDAKernelCallingConvention( 12606c3fb27SDimitry Andric const FunctionType *&FT) const { 12706c3fb27SDimitry Andric // Convert HIP kernels to SPIR-V kernels. 12806c3fb27SDimitry Andric if (getABIInfo().getContext().getLangOpts().HIP) { 12906c3fb27SDimitry Andric FT = getABIInfo().getContext().adjustFunctionType( 13006c3fb27SDimitry Andric FT, FT->getExtInfo().withCallingConv(CC_OpenCLKernel)); 13106c3fb27SDimitry Andric return; 13206c3fb27SDimitry Andric } 13306c3fb27SDimitry Andric } 13406c3fb27SDimitry Andric 13506c3fb27SDimitry Andric /// Construct a SPIR-V target extension type for the given OpenCL image type. 13606c3fb27SDimitry Andric static llvm::Type *getSPIRVImageType(llvm::LLVMContext &Ctx, StringRef BaseType, 13706c3fb27SDimitry Andric StringRef OpenCLName, 13806c3fb27SDimitry Andric unsigned AccessQualifier) { 13906c3fb27SDimitry Andric // These parameters compare to the operands of OpTypeImage (see 14006c3fb27SDimitry Andric // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpTypeImage 14106c3fb27SDimitry Andric // for more details). The first 6 integer parameters all default to 0, and 14206c3fb27SDimitry Andric // will be changed to 1 only for the image type(s) that set the parameter to 14306c3fb27SDimitry Andric // one. The 7th integer parameter is the access qualifier, which is tacked on 14406c3fb27SDimitry Andric // at the end. 14506c3fb27SDimitry Andric SmallVector<unsigned, 7> IntParams = {0, 0, 0, 0, 0, 0}; 14606c3fb27SDimitry Andric 14706c3fb27SDimitry Andric // Choose the dimension of the image--this corresponds to the Dim enum in 14806c3fb27SDimitry Andric // SPIR-V (first integer parameter of OpTypeImage). 149*5f757f3fSDimitry Andric if (OpenCLName.starts_with("image2d")) 15006c3fb27SDimitry Andric IntParams[0] = 1; // 1D 151*5f757f3fSDimitry Andric else if (OpenCLName.starts_with("image3d")) 15206c3fb27SDimitry Andric IntParams[0] = 2; // 2D 15306c3fb27SDimitry Andric else if (OpenCLName == "image1d_buffer") 15406c3fb27SDimitry Andric IntParams[0] = 5; // Buffer 15506c3fb27SDimitry Andric else 156*5f757f3fSDimitry Andric assert(OpenCLName.starts_with("image1d") && "Unknown image type"); 15706c3fb27SDimitry Andric 15806c3fb27SDimitry Andric // Set the other integer parameters of OpTypeImage if necessary. Note that the 15906c3fb27SDimitry Andric // OpenCL image types don't provide any information for the Sampled or 16006c3fb27SDimitry Andric // Image Format parameters. 16106c3fb27SDimitry Andric if (OpenCLName.contains("_depth")) 16206c3fb27SDimitry Andric IntParams[1] = 1; 16306c3fb27SDimitry Andric if (OpenCLName.contains("_array")) 16406c3fb27SDimitry Andric IntParams[2] = 1; 16506c3fb27SDimitry Andric if (OpenCLName.contains("_msaa")) 16606c3fb27SDimitry Andric IntParams[3] = 1; 16706c3fb27SDimitry Andric 16806c3fb27SDimitry Andric // Access qualifier 16906c3fb27SDimitry Andric IntParams.push_back(AccessQualifier); 17006c3fb27SDimitry Andric 17106c3fb27SDimitry Andric return llvm::TargetExtType::get(Ctx, BaseType, {llvm::Type::getVoidTy(Ctx)}, 17206c3fb27SDimitry Andric IntParams); 17306c3fb27SDimitry Andric } 17406c3fb27SDimitry Andric 17506c3fb27SDimitry Andric llvm::Type *CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM, 17606c3fb27SDimitry Andric const Type *Ty) const { 17706c3fb27SDimitry Andric llvm::LLVMContext &Ctx = CGM.getLLVMContext(); 17806c3fb27SDimitry Andric if (auto *PipeTy = dyn_cast<PipeType>(Ty)) 17906c3fb27SDimitry Andric return llvm::TargetExtType::get(Ctx, "spirv.Pipe", {}, 18006c3fb27SDimitry Andric {!PipeTy->isReadOnly()}); 18106c3fb27SDimitry Andric if (auto *BuiltinTy = dyn_cast<BuiltinType>(Ty)) { 18206c3fb27SDimitry Andric enum AccessQualifier : unsigned { AQ_ro = 0, AQ_wo = 1, AQ_rw = 2 }; 18306c3fb27SDimitry Andric switch (BuiltinTy->getKind()) { 18406c3fb27SDimitry Andric #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ 18506c3fb27SDimitry Andric case BuiltinType::Id: \ 18606c3fb27SDimitry Andric return getSPIRVImageType(Ctx, "spirv.Image", #ImgType, AQ_##Suffix); 18706c3fb27SDimitry Andric #include "clang/Basic/OpenCLImageTypes.def" 18806c3fb27SDimitry Andric case BuiltinType::OCLSampler: 18906c3fb27SDimitry Andric return llvm::TargetExtType::get(Ctx, "spirv.Sampler"); 19006c3fb27SDimitry Andric case BuiltinType::OCLEvent: 19106c3fb27SDimitry Andric return llvm::TargetExtType::get(Ctx, "spirv.Event"); 19206c3fb27SDimitry Andric case BuiltinType::OCLClkEvent: 19306c3fb27SDimitry Andric return llvm::TargetExtType::get(Ctx, "spirv.DeviceEvent"); 19406c3fb27SDimitry Andric case BuiltinType::OCLQueue: 19506c3fb27SDimitry Andric return llvm::TargetExtType::get(Ctx, "spirv.Queue"); 19606c3fb27SDimitry Andric case BuiltinType::OCLReserveID: 19706c3fb27SDimitry Andric return llvm::TargetExtType::get(Ctx, "spirv.ReserveId"); 19806c3fb27SDimitry Andric #define INTEL_SUBGROUP_AVC_TYPE(Name, Id) \ 19906c3fb27SDimitry Andric case BuiltinType::OCLIntelSubgroupAVC##Id: \ 20006c3fb27SDimitry Andric return llvm::TargetExtType::get(Ctx, "spirv.Avc" #Id "INTEL"); 20106c3fb27SDimitry Andric #include "clang/Basic/OpenCLExtensionTypes.def" 20206c3fb27SDimitry Andric default: 20306c3fb27SDimitry Andric return nullptr; 20406c3fb27SDimitry Andric } 20506c3fb27SDimitry Andric } 20606c3fb27SDimitry Andric 20706c3fb27SDimitry Andric return nullptr; 20806c3fb27SDimitry Andric } 20906c3fb27SDimitry Andric 21006c3fb27SDimitry Andric std::unique_ptr<TargetCodeGenInfo> 21106c3fb27SDimitry Andric CodeGen::createCommonSPIRTargetCodeGenInfo(CodeGenModule &CGM) { 21206c3fb27SDimitry Andric return std::make_unique<CommonSPIRTargetCodeGenInfo>(CGM.getTypes()); 21306c3fb27SDimitry Andric } 21406c3fb27SDimitry Andric 21506c3fb27SDimitry Andric std::unique_ptr<TargetCodeGenInfo> 21606c3fb27SDimitry Andric CodeGen::createSPIRVTargetCodeGenInfo(CodeGenModule &CGM) { 21706c3fb27SDimitry Andric return std::make_unique<SPIRVTargetCodeGenInfo>(CGM.getTypes()); 21806c3fb27SDimitry Andric } 219