brintos

brintos / llvm-project-archived public Read only

0
0
Text · 26.7 KiB · 1a8c85d Raw
705 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "ABIInfoImpl.h"10#include "HLSLBufferLayoutBuilder.h"11#include "TargetInfo.h"12 13using namespace clang;14using namespace clang::CodeGen;15 16//===----------------------------------------------------------------------===//17// Base ABI and target codegen info implementation common between SPIR and18// SPIR-V.19//===----------------------------------------------------------------------===//20 21namespace {22class CommonSPIRABIInfo : public DefaultABIInfo {23public:24  CommonSPIRABIInfo(CodeGenTypes &CGT) : DefaultABIInfo(CGT) { setCCs(); }25 26private:27  void setCCs();28};29 30class SPIRVABIInfo : public CommonSPIRABIInfo {31public:32  SPIRVABIInfo(CodeGenTypes &CGT) : CommonSPIRABIInfo(CGT) {}33  void computeInfo(CGFunctionInfo &FI) const override;34 35private:36  ABIArgInfo classifyReturnType(QualType RetTy) const;37  ABIArgInfo classifyKernelArgumentType(QualType Ty) const;38  ABIArgInfo classifyArgumentType(QualType Ty) const;39};40} // end anonymous namespace41namespace {42class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo {43public:44  CommonSPIRTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT)45      : TargetCodeGenInfo(std::make_unique<CommonSPIRABIInfo>(CGT)) {}46  CommonSPIRTargetCodeGenInfo(std::unique_ptr<ABIInfo> ABIInfo)47      : TargetCodeGenInfo(std::move(ABIInfo)) {}48 49  LangAS getASTAllocaAddressSpace() const override {50    return getLangASFromTargetAS(51        getABIInfo().getDataLayout().getAllocaAddrSpace());52  }53 54  unsigned getDeviceKernelCallingConv() const override;55  llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override;56  llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *Ty,57                          const CGHLSLOffsetInfo &OffsetInfo) const override;58 59  llvm::Type *getHLSLPadding(CodeGenModule &CGM,60                             CharUnits NumBytes) const override {61    unsigned Size = NumBytes.getQuantity();62    return llvm::TargetExtType::get(CGM.getLLVMContext(), "spirv.Padding", {},63                                    {Size});64  }65 66  bool isHLSLPadding(llvm::Type *Ty) const override {67    if (auto *TET = dyn_cast<llvm::TargetExtType>(Ty))68      return TET->getName() == "spirv.Padding";69    return false;70  }71 72  llvm::Type *getSPIRVImageTypeFromHLSLResource(73      const HLSLAttributedResourceType::Attributes &attributes,74      QualType SampledType, CodeGenModule &CGM) const;75  void76  setOCLKernelStubCallingConvention(const FunctionType *&FT) const override;77  llvm::Constant *getNullPointer(const CodeGen::CodeGenModule &CGM,78                                 llvm::PointerType *T,79                                 QualType QT) const override;80  void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,81                           CodeGen::CodeGenModule &M) const override;82};83class SPIRVTargetCodeGenInfo : public CommonSPIRTargetCodeGenInfo {84public:85  SPIRVTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT)86      : CommonSPIRTargetCodeGenInfo(std::make_unique<SPIRVABIInfo>(CGT)) {}87  void setCUDAKernelCallingConvention(const FunctionType *&FT) const override;88  LangAS getGlobalVarAddressSpace(CodeGenModule &CGM,89                                  const VarDecl *D) const override;90  void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,91                           CodeGen::CodeGenModule &M) const override;92  llvm::SyncScope::ID getLLVMSyncScopeID(const LangOptions &LangOpts,93                                         SyncScope Scope,94                                         llvm::AtomicOrdering Ordering,95                                         llvm::LLVMContext &Ctx) const override;96  bool supportsLibCall() const override {97    return getABIInfo().getTarget().getTriple().getVendor() !=98           llvm::Triple::AMD;99  }100};101 102inline StringRef mapClangSyncScopeToLLVM(SyncScope Scope) {103  switch (Scope) {104  case SyncScope::HIPSingleThread:105  case SyncScope::SingleScope:106    return "singlethread";107  case SyncScope::HIPWavefront:108  case SyncScope::OpenCLSubGroup:109  case SyncScope::WavefrontScope:110    return "subgroup";111  case SyncScope::HIPCluster:112  case SyncScope::ClusterScope:113  case SyncScope::HIPWorkgroup:114  case SyncScope::OpenCLWorkGroup:115  case SyncScope::WorkgroupScope:116    return "workgroup";117  case SyncScope::HIPAgent:118  case SyncScope::OpenCLDevice:119  case SyncScope::DeviceScope:120    return "device";121  case SyncScope::SystemScope:122  case SyncScope::HIPSystem:123  case SyncScope::OpenCLAllSVMDevices:124    return "";125  }126  return "";127}128} // End anonymous namespace.129 130void CommonSPIRABIInfo::setCCs() {131  assert(getRuntimeCC() == llvm::CallingConv::C);132  RuntimeCC = llvm::CallingConv::SPIR_FUNC;133}134 135ABIArgInfo SPIRVABIInfo::classifyReturnType(QualType RetTy) const {136  if (getTarget().getTriple().getVendor() != llvm::Triple::AMD)137    return DefaultABIInfo::classifyReturnType(RetTy);138  if (!isAggregateTypeForABI(RetTy) || getRecordArgABI(RetTy, getCXXABI()))139    return DefaultABIInfo::classifyReturnType(RetTy);140 141  if (const auto *RD = RetTy->getAsRecordDecl();142      RD && RD->hasFlexibleArrayMember())143    return DefaultABIInfo::classifyReturnType(RetTy);144 145  // TODO: The AMDGPU ABI is non-trivial to represent in SPIR-V; in order to146  // avoid encoding various architecture specific bits here we return everything147  // as direct to retain type info for things like aggregates, for later perusal148  // when translating back to LLVM/lowering in the BE. This is also why we149  // disable flattening as the outcomes can mismatch between SPIR-V and AMDGPU.150  // This will be revisited / optimised in the future.151  return ABIArgInfo::getDirect(CGT.ConvertType(RetTy), 0u, nullptr, false);152}153 154ABIArgInfo SPIRVABIInfo::classifyKernelArgumentType(QualType Ty) const {155  if (getContext().getLangOpts().isTargetDevice()) {156    // Coerce pointer arguments with default address space to CrossWorkGroup157    // pointers for target devices as default address space kernel arguments158    // are not allowed. We use the opencl_global language address space which159    // always maps to CrossWorkGroup.160    llvm::Type *LTy = CGT.ConvertType(Ty);161    auto DefaultAS = getContext().getTargetAddressSpace(LangAS::Default);162    auto GlobalAS = getContext().getTargetAddressSpace(LangAS::opencl_global);163    auto *PtrTy = llvm::dyn_cast<llvm::PointerType>(LTy);164    if (PtrTy && PtrTy->getAddressSpace() == DefaultAS) {165      LTy = llvm::PointerType::get(PtrTy->getContext(), GlobalAS);166      return ABIArgInfo::getDirect(LTy, 0, nullptr, false);167    }168 169    if (isAggregateTypeForABI(Ty)) {170      if (getTarget().getTriple().getVendor() == llvm::Triple::AMD)171        // TODO: The AMDGPU kernel ABI passes aggregates byref, which is not172        // currently expressible in SPIR-V; SPIR-V passes aggregates byval,173        // which the AMDGPU kernel ABI does not allow. Passing aggregates as174        // direct works around this impedance mismatch, as it retains type info175        // and can be correctly handled, post reverse-translation, by the AMDGPU176        // BE, which has to support this CC for legacy OpenCL purposes. It can177        // be brittle and does lead to performance degradation in certain178        // pathological cases. This will be revisited / optimised in the future,179        // once a way to deal with the byref/byval impedance mismatch is180        // identified.181        return ABIArgInfo::getDirect(LTy, 0, nullptr, false);182      // Force copying aggregate type in kernel arguments by value when183      // compiling CUDA targeting SPIR-V. This is required for the object184      // copied to be valid on the device.185      // This behavior follows the CUDA spec186      // https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#global-function-argument-processing,187      // and matches the NVPTX implementation. TODO: hardcoding to 0 should be188      // revisited if HIPSPV / byval starts making use of the AS of an indirect189      // arg.190      return getNaturalAlignIndirect(Ty, /*AddrSpace=*/0, /*byval=*/true);191    }192  }193  return classifyArgumentType(Ty);194}195 196ABIArgInfo SPIRVABIInfo::classifyArgumentType(QualType Ty) const {197  if (getTarget().getTriple().getVendor() != llvm::Triple::AMD)198    return DefaultABIInfo::classifyArgumentType(Ty);199  if (!isAggregateTypeForABI(Ty))200    return DefaultABIInfo::classifyArgumentType(Ty);201 202  // Records with non-trivial destructors/copy-constructors should not be203  // passed by value.204  if (auto RAA = getRecordArgABI(Ty, getCXXABI()))205    return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),206                                   RAA == CGCXXABI::RAA_DirectInMemory);207 208  if (const auto *RD = Ty->getAsRecordDecl();209      RD && RD->hasFlexibleArrayMember())210    return DefaultABIInfo::classifyArgumentType(Ty);211 212  return ABIArgInfo::getDirect(CGT.ConvertType(Ty), 0u, nullptr, false);213}214 215void SPIRVABIInfo::computeInfo(CGFunctionInfo &FI) const {216  // The logic is same as in DefaultABIInfo with an exception on the kernel217  // arguments handling.218  llvm::CallingConv::ID CC = FI.getCallingConvention();219 220  if (!getCXXABI().classifyReturnType(FI))221    FI.getReturnInfo() = classifyReturnType(FI.getReturnType());222 223  for (auto &I : FI.arguments()) {224    if (CC == llvm::CallingConv::SPIR_KERNEL) {225      I.info = classifyKernelArgumentType(I.type);226    } else {227      I.info = classifyArgumentType(I.type);228    }229  }230}231 232namespace clang {233namespace CodeGen {234void computeSPIRKernelABIInfo(CodeGenModule &CGM, CGFunctionInfo &FI) {235  if (CGM.getTarget().getTriple().isSPIRV())236    SPIRVABIInfo(CGM.getTypes()).computeInfo(FI);237  else238    CommonSPIRABIInfo(CGM.getTypes()).computeInfo(FI);239}240}241}242 243unsigned CommonSPIRTargetCodeGenInfo::getDeviceKernelCallingConv() const {244  return llvm::CallingConv::SPIR_KERNEL;245}246 247void SPIRVTargetCodeGenInfo::setCUDAKernelCallingConvention(248    const FunctionType *&FT) const {249  // Convert HIP kernels to SPIR-V kernels.250  if (getABIInfo().getContext().getLangOpts().HIP) {251    FT = getABIInfo().getContext().adjustFunctionType(252        FT, FT->getExtInfo().withCallingConv(CC_DeviceKernel));253    return;254  }255}256 257void CommonSPIRTargetCodeGenInfo::setOCLKernelStubCallingConvention(258    const FunctionType *&FT) const {259  FT = getABIInfo().getContext().adjustFunctionType(260      FT, FT->getExtInfo().withCallingConv(CC_SpirFunction));261}262 263// LLVM currently assumes a null pointer has the bit pattern 0, but some GPU264// targets use a non-zero encoding for null in certain address spaces.265// Because SPIR(-V) is a generic target and the bit pattern of null in266// non-generic AS is unspecified, materialize null in non-generic AS via an267// addrspacecast from null in generic AS. This allows later lowering to268// substitute the target's real sentinel value.269llvm::Constant *270CommonSPIRTargetCodeGenInfo::getNullPointer(const CodeGen::CodeGenModule &CGM,271                                            llvm::PointerType *PT,272                                            QualType QT) const {273  LangAS AS = QT->getUnqualifiedDesugaredType()->isNullPtrType()274                  ? LangAS::Default275                  : QT->getPointeeType().getAddressSpace();276  unsigned ASAsInt = static_cast<unsigned>(AS);277  unsigned FirstTargetASAsInt =278      static_cast<unsigned>(LangAS::FirstTargetAddressSpace);279  unsigned CodeSectionINTELAS = FirstTargetASAsInt + 9;280  // As per SPV_INTEL_function_pointers, it is illegal to addrspacecast281  // function pointers to/from the generic AS.282  bool IsFunctionPtrAS =283      CGM.getTriple().isSPIRV() && ASAsInt == CodeSectionINTELAS;284  if (AS == LangAS::Default || AS == LangAS::opencl_generic ||285      AS == LangAS::opencl_constant || IsFunctionPtrAS)286    return llvm::ConstantPointerNull::get(PT);287 288  auto &Ctx = CGM.getContext();289  auto NPT = llvm::PointerType::get(290      PT->getContext(), Ctx.getTargetAddressSpace(LangAS::opencl_generic));291  return llvm::ConstantExpr::getAddrSpaceCast(292      llvm::ConstantPointerNull::get(NPT), PT);293}294 295void CommonSPIRTargetCodeGenInfo::setTargetAttributes(296    const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &M) const {297  if (M.getLangOpts().OpenCL || GV->isDeclaration())298    return;299 300  const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);301  if (!FD)302    return;303 304  llvm::Function *F = dyn_cast<llvm::Function>(GV);305  assert(F && "Expected GlobalValue to be a Function");306 307  if (FD->hasAttr<DeviceKernelAttr>())308    F->setCallingConv(getDeviceKernelCallingConv());309}310 311LangAS312SPIRVTargetCodeGenInfo::getGlobalVarAddressSpace(CodeGenModule &CGM,313                                                 const VarDecl *D) const {314  assert(!CGM.getLangOpts().OpenCL &&315         !(CGM.getLangOpts().CUDA && CGM.getLangOpts().CUDAIsDevice) &&316         "Address space agnostic languages only");317  // If we're here it means that we're using the SPIRDefIsGen ASMap, hence for318  // the global AS we can rely on either cuda_device or sycl_global to be319  // correct; however, since this is not a CUDA Device context, we use320  // sycl_global to prevent confusion with the assertion.321  LangAS DefaultGlobalAS = getLangASFromTargetAS(322      CGM.getContext().getTargetAddressSpace(LangAS::sycl_global));323  if (!D)324    return DefaultGlobalAS;325 326  LangAS AddrSpace = D->getType().getAddressSpace();327  if (AddrSpace != LangAS::Default)328    return AddrSpace;329 330  return DefaultGlobalAS;331}332 333void SPIRVTargetCodeGenInfo::setTargetAttributes(334    const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &M) const {335  if (GV->isDeclaration())336    return;337 338  const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);339  if (!FD)340    return;341 342  llvm::Function *F = dyn_cast<llvm::Function>(GV);343  assert(F && "Expected GlobalValue to be a Function");344 345  if (FD->hasAttr<DeviceKernelAttr>())346    F->setCallingConv(getDeviceKernelCallingConv());347 348  if (!M.getLangOpts().HIP ||349      M.getTarget().getTriple().getVendor() != llvm::Triple::AMD)350    return;351 352  if (!FD->hasAttr<CUDAGlobalAttr>())353    return;354 355  unsigned N = M.getLangOpts().GPUMaxThreadsPerBlock;356  if (auto FlatWGS = FD->getAttr<AMDGPUFlatWorkGroupSizeAttr>())357    N = FlatWGS->getMax()->EvaluateKnownConstInt(M.getContext()).getExtValue();358 359  // We encode the maximum flat WG size in the first component of the 3D360  // max_work_group_size attribute, which will get reverse translated into the361  // original AMDGPU attribute when targeting AMDGPU.362  auto Int32Ty = llvm::IntegerType::getInt32Ty(M.getLLVMContext());363  llvm::Metadata *AttrMDArgs[] = {364      llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(Int32Ty, N)),365      llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(Int32Ty, 1)),366      llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(Int32Ty, 1))};367 368  F->setMetadata("max_work_group_size",369                 llvm::MDNode::get(M.getLLVMContext(), AttrMDArgs));370}371 372llvm::SyncScope::ID373SPIRVTargetCodeGenInfo::getLLVMSyncScopeID(const LangOptions &, SyncScope Scope,374                                           llvm::AtomicOrdering,375                                           llvm::LLVMContext &Ctx) const {376  return Ctx.getOrInsertSyncScopeID(mapClangSyncScopeToLLVM(Scope));377}378 379/// Construct a SPIR-V target extension type for the given OpenCL image type.380static llvm::Type *getSPIRVImageType(llvm::LLVMContext &Ctx, StringRef BaseType,381                                     StringRef OpenCLName,382                                     unsigned AccessQualifier) {383  // These parameters compare to the operands of OpTypeImage (see384  // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpTypeImage385  // for more details). The first 6 integer parameters all default to 0, and386  // will be changed to 1 only for the image type(s) that set the parameter to387  // one. The 7th integer parameter is the access qualifier, which is tacked on388  // at the end.389  SmallVector<unsigned, 7> IntParams = {0, 0, 0, 0, 0, 0};390 391  // Choose the dimension of the image--this corresponds to the Dim enum in392  // SPIR-V (first integer parameter of OpTypeImage).393  if (OpenCLName.starts_with("image2d"))394    IntParams[0] = 1;395  else if (OpenCLName.starts_with("image3d"))396    IntParams[0] = 2;397  else if (OpenCLName == "image1d_buffer")398    IntParams[0] = 5; // Buffer399  else400    assert(OpenCLName.starts_with("image1d") && "Unknown image type");401 402  // Set the other integer parameters of OpTypeImage if necessary. Note that the403  // OpenCL image types don't provide any information for the Sampled or404  // Image Format parameters.405  if (OpenCLName.contains("_depth"))406    IntParams[1] = 1;407  if (OpenCLName.contains("_array"))408    IntParams[2] = 1;409  if (OpenCLName.contains("_msaa"))410    IntParams[3] = 1;411 412  // Access qualifier413  IntParams.push_back(AccessQualifier);414 415  return llvm::TargetExtType::get(Ctx, BaseType, {llvm::Type::getVoidTy(Ctx)},416                                  IntParams);417}418 419llvm::Type *CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM,420                                                       const Type *Ty) const {421  llvm::LLVMContext &Ctx = CGM.getLLVMContext();422  if (auto *PipeTy = dyn_cast<PipeType>(Ty))423    return llvm::TargetExtType::get(Ctx, "spirv.Pipe", {},424                                    {!PipeTy->isReadOnly()});425  if (auto *BuiltinTy = dyn_cast<BuiltinType>(Ty)) {426    enum AccessQualifier : unsigned { AQ_ro = 0, AQ_wo = 1, AQ_rw = 2 };427    switch (BuiltinTy->getKind()) {428#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix)                   \429    case BuiltinType::Id:                                                      \430      return getSPIRVImageType(Ctx, "spirv.Image", #ImgType, AQ_##Suffix);431#include "clang/Basic/OpenCLImageTypes.def"432    case BuiltinType::OCLSampler:433      return llvm::TargetExtType::get(Ctx, "spirv.Sampler");434    case BuiltinType::OCLEvent:435      return llvm::TargetExtType::get(Ctx, "spirv.Event");436    case BuiltinType::OCLClkEvent:437      return llvm::TargetExtType::get(Ctx, "spirv.DeviceEvent");438    case BuiltinType::OCLQueue:439      return llvm::TargetExtType::get(Ctx, "spirv.Queue");440    case BuiltinType::OCLReserveID:441      return llvm::TargetExtType::get(Ctx, "spirv.ReserveId");442#define INTEL_SUBGROUP_AVC_TYPE(Name, Id)                                      \443    case BuiltinType::OCLIntelSubgroupAVC##Id:                                 \444      return llvm::TargetExtType::get(Ctx, "spirv.Avc" #Id "INTEL");445#include "clang/Basic/OpenCLExtensionTypes.def"446    default:447      return nullptr;448    }449  }450 451  return nullptr;452}453 454// Gets a spirv.IntegralConstant or spirv.Literal. If IntegralType is present,455// returns an IntegralConstant, otherwise returns a Literal.456static llvm::Type *getInlineSpirvConstant(CodeGenModule &CGM,457                                          llvm::Type *IntegralType,458                                          llvm::APInt Value) {459  llvm::LLVMContext &Ctx = CGM.getLLVMContext();460 461  // Convert the APInt value to an array of uint32_t words462  llvm::SmallVector<uint32_t> Words;463 464  while (Value.ugt(0)) {465    uint32_t Word = Value.trunc(32).getZExtValue();466    Value.lshrInPlace(32);467 468    Words.push_back(Word);469  }470  if (Words.size() == 0)471    Words.push_back(0);472 473  if (IntegralType)474    return llvm::TargetExtType::get(Ctx, "spirv.IntegralConstant",475                                    {IntegralType}, Words);476  return llvm::TargetExtType::get(Ctx, "spirv.Literal", {}, Words);477}478 479static llvm::Type *getInlineSpirvType(CodeGenModule &CGM,480                                      const HLSLInlineSpirvType *SpirvType) {481  llvm::LLVMContext &Ctx = CGM.getLLVMContext();482 483  llvm::SmallVector<llvm::Type *> Operands;484 485  for (auto &Operand : SpirvType->getOperands()) {486    using SpirvOperandKind = SpirvOperand::SpirvOperandKind;487 488    llvm::Type *Result = nullptr;489    switch (Operand.getKind()) {490    case SpirvOperandKind::ConstantId: {491      llvm::Type *IntegralType =492          CGM.getTypes().ConvertType(Operand.getResultType());493 494      Result = getInlineSpirvConstant(CGM, IntegralType, Operand.getValue());495      break;496    }497    case SpirvOperandKind::Literal: {498      Result = getInlineSpirvConstant(CGM, nullptr, Operand.getValue());499      break;500    }501    case SpirvOperandKind::TypeId: {502      QualType TypeOperand = Operand.getResultType();503      if (const auto *RD = TypeOperand->getAsRecordDecl()) {504        assert(RD->isCompleteDefinition() &&505               "Type completion should have been required in Sema");506 507        const FieldDecl *HandleField = RD->findFirstNamedDataMember();508        if (HandleField) {509          QualType ResourceType = HandleField->getType();510          if (ResourceType->getAs<HLSLAttributedResourceType>()) {511            TypeOperand = ResourceType;512          }513        }514      }515      Result = CGM.getTypes().ConvertType(TypeOperand);516      break;517    }518    default:519      llvm_unreachable("HLSLInlineSpirvType had invalid operand!");520      break;521    }522 523    assert(Result);524    Operands.push_back(Result);525  }526 527  return llvm::TargetExtType::get(Ctx, "spirv.Type", Operands,528                                  {SpirvType->getOpcode(), SpirvType->getSize(),529                                   SpirvType->getAlignment()});530}531 532llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(533    CodeGenModule &CGM, const Type *Ty,534    const CGHLSLOffsetInfo &OffsetInfo) const {535  llvm::LLVMContext &Ctx = CGM.getLLVMContext();536 537  if (auto *SpirvType = dyn_cast<HLSLInlineSpirvType>(Ty))538    return getInlineSpirvType(CGM, SpirvType);539 540  auto *ResType = dyn_cast<HLSLAttributedResourceType>(Ty);541  if (!ResType)542    return nullptr;543 544  const HLSLAttributedResourceType::Attributes &ResAttrs = ResType->getAttrs();545  switch (ResAttrs.ResourceClass) {546  case llvm::dxil::ResourceClass::UAV:547  case llvm::dxil::ResourceClass::SRV: {548    // TypedBuffer and RawBuffer both need element type549    QualType ContainedTy = ResType->getContainedType();550    if (ContainedTy.isNull())551      return nullptr;552 553    assert(!ResAttrs.IsROV &&554           "Rasterizer order views not implemented for SPIR-V yet");555 556    if (!ResAttrs.RawBuffer) {557      // convert element type558      return getSPIRVImageTypeFromHLSLResource(ResAttrs, ContainedTy, CGM);559    }560 561    if (ResAttrs.IsCounter) {562      llvm::Type *ElemType = llvm::Type::getInt32Ty(Ctx);563      uint32_t StorageClass = /* StorageBuffer storage class */ 12;564      return llvm::TargetExtType::get(Ctx, "spirv.VulkanBuffer", {ElemType},565                                      {StorageClass, true});566    }567    llvm::Type *ElemType = CGM.getTypes().ConvertTypeForMem(ContainedTy);568    llvm::ArrayType *RuntimeArrayType = llvm::ArrayType::get(ElemType, 0);569    uint32_t StorageClass = /* StorageBuffer storage class */ 12;570    bool IsWritable = ResAttrs.ResourceClass == llvm::dxil::ResourceClass::UAV;571    return llvm::TargetExtType::get(Ctx, "spirv.VulkanBuffer",572                                    {RuntimeArrayType},573                                    {StorageClass, IsWritable});574  }575  case llvm::dxil::ResourceClass::CBuffer: {576    QualType ContainedTy = ResType->getContainedType();577    if (ContainedTy.isNull() || !ContainedTy->isStructureType())578      return nullptr;579 580    llvm::StructType *BufferLayoutTy =581        HLSLBufferLayoutBuilder(CGM).layOutStruct(582            ContainedTy->getAsCanonical<RecordType>(), OffsetInfo);583    uint32_t StorageClass = /* Uniform storage class */ 2;584    return llvm::TargetExtType::get(Ctx, "spirv.VulkanBuffer", {BufferLayoutTy},585                                    {StorageClass, false});586    break;587  }588  case llvm::dxil::ResourceClass::Sampler:589    return llvm::TargetExtType::get(Ctx, "spirv.Sampler");590  }591  return nullptr;592}593 594static unsigned595getImageFormat(const LangOptions &LangOpts,596               const HLSLAttributedResourceType::Attributes &attributes,597               llvm::Type *SampledType, QualType Ty, unsigned NumChannels) {598  // For images with `Sampled` operand equal to 2, there are restrictions on599  // using the Unknown image format. To avoid these restrictions in common600  // cases, we guess an image format for them based on the sampled type and the601  // number of channels. This is intended to match the behaviour of DXC.602  if (LangOpts.HLSLSpvUseUnknownImageFormat ||603      attributes.ResourceClass != llvm::dxil::ResourceClass::UAV) {604    return 0; // Unknown605  }606 607  if (SampledType->isIntegerTy(32)) {608    if (Ty->isSignedIntegerType()) {609      if (NumChannels == 1)610        return 24; // R32i611      if (NumChannels == 2)612        return 25; // Rg32i613      if (NumChannels == 4)614        return 21; // Rgba32i615    } else {616      if (NumChannels == 1)617        return 33; // R32ui618      if (NumChannels == 2)619        return 35; // Rg32ui620      if (NumChannels == 4)621        return 30; // Rgba32ui622    }623  } else if (SampledType->isIntegerTy(64)) {624    if (NumChannels == 1) {625      if (Ty->isSignedIntegerType()) {626        return 41; // R64i627      }628      return 40; // R64ui629    }630  } else if (SampledType->isFloatTy()) {631    if (NumChannels == 1)632      return 3; // R32f633    if (NumChannels == 2)634      return 6; // Rg32f635    if (NumChannels == 4)636      return 1; // Rgba32f637  }638 639  return 0; // Unknown640}641 642llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource(643    const HLSLAttributedResourceType::Attributes &attributes, QualType Ty,644    CodeGenModule &CGM) const {645  llvm::LLVMContext &Ctx = CGM.getLLVMContext();646 647  unsigned NumChannels = 1;648  Ty = Ty->getCanonicalTypeUnqualified();649  if (const VectorType *V = dyn_cast<VectorType>(Ty)) {650    NumChannels = V->getNumElements();651    Ty = V->getElementType();652  }653  assert(!Ty->isVectorType() && "We still have a vector type.");654 655  llvm::Type *SampledType = CGM.getTypes().ConvertTypeForMem(Ty);656 657  assert((SampledType->isIntegerTy() || SampledType->isFloatingPointTy()) &&658         "The element type for a SPIR-V resource must be a scalar integer or "659         "floating point type.");660 661  // These parameters correspond to the operands to the OpTypeImage SPIR-V662  // instruction. See663  // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpTypeImage.664  SmallVector<unsigned, 6> IntParams(6, 0);665 666  const char *Name =667      Ty->isSignedIntegerType() ? "spirv.SignedImage" : "spirv.Image";668 669  // Dim670  // For now we assume everything is a buffer.671  IntParams[0] = 5;672 673  // Depth674  // HLSL does not indicate if it is a depth texture or not, so we use unknown.675  IntParams[1] = 2;676 677  // Arrayed678  IntParams[2] = 0;679 680  // MS681  IntParams[3] = 0;682 683  // Sampled684  IntParams[4] =685      attributes.ResourceClass == llvm::dxil::ResourceClass::UAV ? 2 : 1;686 687  // Image format.688  IntParams[5] = getImageFormat(CGM.getLangOpts(), attributes, SampledType, Ty,689                                NumChannels);690 691  llvm::TargetExtType *ImageType =692      llvm::TargetExtType::get(Ctx, Name, {SampledType}, IntParams);693  return ImageType;694}695 696std::unique_ptr<TargetCodeGenInfo>697CodeGen::createCommonSPIRTargetCodeGenInfo(CodeGenModule &CGM) {698  return std::make_unique<CommonSPIRTargetCodeGenInfo>(CGM.getTypes());699}700 701std::unique_ptr<TargetCodeGenInfo>702CodeGen::createSPIRVTargetCodeGenInfo(CodeGenModule &CGM) {703  return std::make_unique<SPIRVTargetCodeGenInfo>(CGM.getTypes());704}705