From a8dc7469f4c330174490113ea518578293716e70 Mon Sep 17 00:00:00 2001 From: Dmitry Sidorov Date: Thu, 25 Apr 2024 16:37:10 +0200 Subject: [PATCH] Add SPV_INTEL_bindless_images preview extension (#2535) Specification: https://github.com/intel/llvm/blob/sycl/sycl/doc/design/spirv-extensions/SPV_INTEL_bindless_images.asciidoc Co-author: He, Wenju wenju.he@intel.com --- include/LLVMSPIRVExtensions.inc | 1 + lib/SPIRV/SPIRVInternal.h | 2 + lib/SPIRV/SPIRVReader.cpp | 1 + lib/SPIRV/SPIRVUtil.cpp | 15 ++++- lib/SPIRV/libSPIRV/SPIRVInstruction.h | 61 +++++++++++++++++++ lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h | 1 + lib/SPIRV/libSPIRV/SPIRVOpCodeEnumInternal.h | 4 ++ lib/SPIRV/libSPIRV/spirv_internal.hpp | 9 ++- .../bindless_images_generic.ll | 49 +++++++++++++++ .../negative/i32-in-physical64.ll | 31 ++++++++++ 10 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 test/extensions/INTEL/SPV_INTEL_bindless_images/bindless_images_generic.ll create mode 100644 test/extensions/INTEL/SPV_INTEL_bindless_images/negative/i32-in-physical64.ll diff --git a/include/LLVMSPIRVExtensions.inc b/include/LLVMSPIRVExtensions.inc index fb79cccf0a..b98007aa65 100644 --- a/include/LLVMSPIRVExtensions.inc +++ b/include/LLVMSPIRVExtensions.inc @@ -67,3 +67,4 @@ EXT(SPV_EXT_relaxed_printf_string_address_space) EXT(SPV_INTEL_fp_max_error) EXT(SPV_INTEL_cache_controls) EXT(SPV_INTEL_maximum_registers) +EXT(SPV_INTEL_bindless_images) diff --git a/lib/SPIRV/SPIRVInternal.h b/lib/SPIRV/SPIRVInternal.h index e2298e964b..52488d5ba0 100644 --- a/lib/SPIRV/SPIRVInternal.h +++ b/lib/SPIRV/SPIRVInternal.h @@ -313,6 +313,8 @@ const static char Float[] = "float"; const static char Half[] = "half"; const static char Int[] = "int"; const static char UInt[] = "uint"; +const static char Long[] = "long"; +const static char ULong[] = "ulong"; const static char Void[] = "void"; } // namespace kSPIRVImageSampledTypeName diff --git a/lib/SPIRV/SPIRVReader.cpp b/lib/SPIRV/SPIRVReader.cpp index 27ba98e5c5..352429a1ed 100644 --- a/lib/SPIRV/SPIRVReader.cpp +++ b/lib/SPIRV/SPIRVReader.cpp @@ -3373,6 +3373,7 @@ Instruction *SPIRVToLLVM::transSPIRVBuiltinFromInst(SPIRVInstruction *BI, case internal::OpJointMatrixLoadINTEL: case OpCooperativeMatrixLoadKHR: case internal::OpCooperativeMatrixLoadCheckedINTEL: + case internal::OpConvertHandleToImageINTEL: AddRetTypePostfix = true; break; default: { diff --git a/lib/SPIRV/SPIRVUtil.cpp b/lib/SPIRV/SPIRVUtil.cpp index 99d9622b57..763d42f0b3 100644 --- a/lib/SPIRV/SPIRVUtil.cpp +++ b/lib/SPIRV/SPIRVUtil.cpp @@ -1497,6 +1497,12 @@ std::string getSPIRVImageSampledTypeName(SPIRVType *Ty) { else return kSPIRVImageSampledTypeName::UInt; } + if (Ty->getIntegerBitWidth() == 64) { + if (static_cast(Ty)->isSigned()) + return kSPIRVImageSampledTypeName::Long; + else + return kSPIRVImageSampledTypeName::ULong; + } break; case OpTypeFloat: switch (Ty->getFloatBitWidth()) { @@ -1528,6 +1534,9 @@ Type *getLLVMTypeForSPIRVImageSampledTypePostfix(StringRef Postfix, if (Postfix == kSPIRVImageSampledTypeName::Int || Postfix == kSPIRVImageSampledTypeName::UInt) return Type::getInt32Ty(Ctx); + if (Postfix == kSPIRVImageSampledTypeName::Long || + Postfix == kSPIRVImageSampledTypeName::ULong) + return Type::getInt64Ty(Ctx); llvm_unreachable("Invalid sampled type postfix"); return nullptr; } @@ -2219,7 +2228,7 @@ class SPIRVFriendlyIRMangleInfo : public BuiltinFuncMangleInfo { void init(StringRef UniqUnmangledName) override { UnmangledName = UniqUnmangledName.str(); - switch (OC) { + switch (static_cast(OC)) { case OpConvertUToF: case OpUConvert: case OpSatConvertUToS: @@ -2384,6 +2393,10 @@ class SPIRVFriendlyIRMangleInfo : public BuiltinFuncMangleInfo { } break; } + case internal::OpConvertHandleToImageINTEL: + case internal::OpConvertHandleToSamplerINTEL: + addUnsignedArg(0); + break; default:; // No special handling is needed } diff --git a/lib/SPIRV/libSPIRV/SPIRVInstruction.h b/lib/SPIRV/libSPIRV/SPIRVInstruction.h index 3877ebe5c2..21fcc77a3d 100644 --- a/lib/SPIRV/libSPIRV/SPIRVInstruction.h +++ b/lib/SPIRV/libSPIRV/SPIRVInstruction.h @@ -3779,5 +3779,66 @@ template class SPIRVReadClockKHRInstBase : public SPIRVUnaryInst { _SPIRV_OP(ReadClockKHR) #undef _SPIRV_OP +template class SPIRVBindlessImagesInstBase : public SPIRVUnaryInst { +protected: + SPIRVCapVec getRequiredCapability() const override { + return getVec(internal::CapabilityBindlessImagesINTEL); + } + + llvm::Optional getRequiredExtension() const override { + return ExtensionID::SPV_INTEL_bindless_images; + } + + void validate() const override { + SPIRVUnary::validate(); + + // validate is a const method, whilst getOperand is non-const method + // because it may call a method of class Module that may modify LiteralMap + // of Module field. That modification is not impacting validate method for + // these instructions, so const_cast is safe here. + using SPVBindlessImagesInst = SPIRVBindlessImagesInstBase; + SPIRVValue *Input = + const_cast(this)->getOperand(0); + SPIRVType *InCompTy = Input->getType(); + + auto StringAddrMod = [](SPIRVAddressingModelKind Kind) -> std::string { + if (Kind == AddressingModelPhysical32) + return std::string("Physical32"); + if (Kind == AddressingModelPhysical64) + return std::string("Physical64"); + return std::string("AddressingModel: ") + std::to_string(Kind); + }; + + auto InstName = OpCodeNameMap::map(OC); + auto AddrMod = this->getModule()->getAddressingModel(); + SPIRVErrorLog &SPVErrLog = this->getModule()->getErrorLog(); + SPVErrLog.checkError( + (InCompTy->isTypeInt(32) && AddrMod == AddressingModelPhysical32) || + (InCompTy->isTypeInt(64) && AddrMod == AddressingModelPhysical64), + SPIRVEC_InvalidInstruction, + InstName + + "\nParameter value must be a 32-bit scalar in case of " + "Physical32 addressing model or a 64-bit scalar in case of " + "Physical64 addressing model\n" + "Type size: " + + std::to_string(InCompTy->getBitWidth()) + + "\nAddressing model: " + StringAddrMod(AddrMod) + "\n"); + + SPIRVType *ResTy = this->getType(); + SPVErrLog.checkError( + (ResTy->isTypeImage() && OC == internal::OpConvertHandleToImageINTEL) || + (ResTy->isTypeSampler() && + OC == internal::OpConvertHandleToSamplerINTEL), + SPIRVEC_InvalidInstruction, + InstName + "\nIncorrect return type of the instruction must be " + "image/sampler\n"); + } +}; +#define _SPIRV_OP(x) \ + typedef SPIRVBindlessImagesInstBase SPIRV##x; +_SPIRV_OP(ConvertHandleToImageINTEL) +_SPIRV_OP(ConvertHandleToSamplerINTEL) +#undef _SPIRV_OP + } // namespace SPIRV #endif // SPIRV_LIBSPIRV_SPIRVINSTRUCTION_H diff --git a/lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h b/lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h index 6355c02bd2..41c3a7a7d2 100644 --- a/lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h +++ b/lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h @@ -630,6 +630,7 @@ template <> inline void SPIRVMap::init() { add(internal::CapabilityRegisterLimitsINTEL, "RegisterLimitsINTEL"); add(internal::CapabilityCooperativeMatrixCheckedInstructionsINTEL, "CooperativeMatrixCheckedInstructionsINTEL"); + add(internal::CapabilityBindlessImagesINTEL, "BindlessImagesINTEL"); } SPIRV_DEF_NAMEMAP(Capability, SPIRVCapabilityNameMap) diff --git a/lib/SPIRV/libSPIRV/SPIRVOpCodeEnumInternal.h b/lib/SPIRV/libSPIRV/SPIRVOpCodeEnumInternal.h index 4cf17802fe..91b7a85c46 100644 --- a/lib/SPIRV/libSPIRV/SPIRVOpCodeEnumInternal.h +++ b/lib/SPIRV/libSPIRV/SPIRVOpCodeEnumInternal.h @@ -27,3 +27,7 @@ _SPIRV_OP_INTERNAL(ComplexFDivINTEL, internal::ComplexFDivINTEL) _SPIRV_OP_INTERNAL(MaskedGatherINTEL, internal::OpMaskedGatherINTEL) _SPIRV_OP_INTERNAL(MaskedScatterINTEL, internal::OpMaskedScatterINTEL) _SPIRV_OP_INTERNAL(RoundFToTF32INTEL, internal::RoundFToTF32INTEL) +_SPIRV_OP_INTERNAL(ConvertHandleToImageINTEL, + internal::ConvertHandleToImageINTEL) +_SPIRV_OP_INTERNAL(ConvertHandleToSamplerINTEL, + internal::ConvertHandleToSamplerINTEL) diff --git a/lib/SPIRV/libSPIRV/spirv_internal.hpp b/lib/SPIRV/libSPIRV/spirv_internal.hpp index 207a75ad63..5ad8caaac5 100644 --- a/lib/SPIRV/libSPIRV/spirv_internal.hpp +++ b/lib/SPIRV/libSPIRV/spirv_internal.hpp @@ -79,6 +79,8 @@ enum InternalOp { IOpMaskedGatherINTEL = 6428, IOpMaskedScatterINTEL = 6429, IOpJointMatrixGetElementCoordINTEL = 6440, + IOpConvertHandleToImageINTEL = 6529, + IOpConvertHandleToSamplerINTEL = 6530, IOpPrev = OpMax - 2, IOpForward }; @@ -113,7 +115,8 @@ enum InternalCapability { ICapabilityMaskedGatherScatterINTEL = 6427, ICapabilityJointMatrixWIInstructionsINTEL = 6435, ICapabilityCacheControlsINTEL = 6441, - ICapRegisterLimitsINTEL = 6460 + ICapRegisterLimitsINTEL = 6460, + ICapabilityBindlessImagesINTEL = 6528 }; enum InternalFunctionControlMask { IFunctionControlOptNoneINTELMask = 0x10000 }; @@ -198,6 +201,10 @@ _SPIRV_OP(Capability, TensorFloat32RoundingINTEL) _SPIRV_OP(Op, RoundFToTF32INTEL) _SPIRV_OP(Capability, CacheControlsINTEL) + +_SPIRV_OP(Capability, BindlessImagesINTEL) +_SPIRV_OP(Op, ConvertHandleToImageINTEL) +_SPIRV_OP(Op, ConvertHandleToSamplerINTEL) #undef _SPIRV_OP constexpr SourceLanguage SourceLanguagePython = diff --git a/test/extensions/INTEL/SPV_INTEL_bindless_images/bindless_images_generic.ll b/test/extensions/INTEL/SPV_INTEL_bindless_images/bindless_images_generic.ll new file mode 100644 index 0000000000..460fe1bc15 --- /dev/null +++ b/test/extensions/INTEL/SPV_INTEL_bindless_images/bindless_images_generic.ll @@ -0,0 +1,49 @@ +; RUN: llvm-as %s -o %t.bc +; RUN: llvm-spirv %t.bc -o %t.spv --spirv-ext=+SPV_INTEL_bindless_images +; RUN: llvm-spirv %t.spv -o %t.spt --to-text +; RUN: FileCheck < %t.spt %s --check-prefix=CHECK-SPIRV +; RUN: llvm-spirv %t.spv -o %t.rev.bc -r --spirv-target-env=SPV-IR +; RUN: llvm-dis %t.rev.bc -o %t.rev.ll +; RUN: FileCheck < %t.rev.ll %s --check-prefix=CHECK-LLVM + +; RUN: not llvm-spirv %t.bc 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR +; CHECK-ERROR: RequiresExtension: Feature requires the following SPIR-V extension: +; CHECK-ERROR-NEXT: SPV_INTEL_bindless_images + +target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64" +target triple = "spir64-unknown-unknown" + +; CHECK-SPIRV: Capability BindlessImagesINTEL +; CHECK-SPIRV: Extension "SPV_INTEL_bindless_images" +; CHECK-SPIRV-DAG: TypeVoid [[#VoidTy:]] +; CHECK-SPIRV-DAG: TypeInt [[#Int64Ty:]] 64 +; CHECK-SPIRV-DAG: Constant [[#Int64Ty]] [[#Const42:]] 42 0 +; CHECK-SPIRV-DAG: TypeImage [[#IntImgTy:]] [[#Int64Ty]] +; CHECK-SPIRV-DAG: TypeSampler [[#SamplerTy:]] +; CHECK-SPIRV: FunctionParameter [[#Int64Ty]] [[#Input:]] +; CHECK-SPIRV: ConvertHandleToImageINTEL [[#IntImgTy]] [[#]] [[#Input]] +; CHECK-SPIRV: ConvertHandleToSamplerINTEL [[#SamplerTy]] [[#]] [[#Const42]] + +; CHECK-LLVM: call spir_func %spirv.Image._ulong_2_0_0_0_0_0_0 addrspace(1)* @_Z77__spirv_ConvertHandleToImageINTEL_RPU3AS134__spirv_Image__ulong_2_0_0_0_0_0_0m(i64 %{{.*}}) +; CHECK-LLVM: call spir_func %spirv.Sampler addrspace(2)* @_Z35__spirv_ConvertHandleToSamplerINTELm(i64 42) + +%spirv.Image._long_2_0_0_0_0_0_0 = type opaque +%spirv.Sampler = type opaque + +define spir_func void @foo(i64 %in) { + %img = call spir_func %spirv.Image._long_2_0_0_0_0_0_0 addrspace(1)* @_Z33__spirv_ConvertHandleToImageINTELl(i64 %in) + %samp = call spir_func %spirv.Sampler addrspace(2)* @_Z35__spirv_ConvertHandleToSamplerINTELl(i64 42) + ret void +} + +declare spir_func %spirv.Image._long_2_0_0_0_0_0_0 addrspace(1)* @_Z33__spirv_ConvertHandleToImageINTELl(i64) + +declare spir_func %spirv.Sampler addrspace(2)* @_Z35__spirv_ConvertHandleToSamplerINTELl(i64) + +!opencl.spir.version = !{!0} +!spirv.Source = !{!1} +!llvm.ident = !{!2} + +!0 = !{i32 1, i32 2} +!1 = !{i32 4, i32 100000} +!2 = !{!"clang version 17.0.0"} diff --git a/test/extensions/INTEL/SPV_INTEL_bindless_images/negative/i32-in-physical64.ll b/test/extensions/INTEL/SPV_INTEL_bindless_images/negative/i32-in-physical64.ll new file mode 100644 index 0000000000..515ed293ad --- /dev/null +++ b/test/extensions/INTEL/SPV_INTEL_bindless_images/negative/i32-in-physical64.ll @@ -0,0 +1,31 @@ +; RUN: llvm-as %s -o %t.bc +; RUN: not llvm-spirv %t.bc --spirv-ext=+SPV_INTEL_bindless_images 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR-1 +; CHECK-ERROR-1: InvalidInstruction: Can't translate llvm instruction: +; CHECK-ERROR-1-NEXT: ConvertHandleToImageINTEL +; CHECK-ERROR-1-NEXT: Parameter value must be a 32-bit scalar in case of Physical32 addressing model or a 64-bit scalar in case of Physical64 addressing model +; CHECK-ERROR-1-NEXT: Type size: 32 +; CHECK-ERROR-1-NEXT: Addressing model: Physical64 + +target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64" +target triple = "spir64-unknown-unknown" + +%spirv.Image._long_2_0_0_0_0_0_0 = type opaque +%spirv.Sampler = type opaque + +define spir_func void @foo(i32 %in) { + %img = call spir_func %spirv.Image._long_2_0_0_0_0_0_0 addrspace(1)* @_Z33__spirv_ConvertHandleToImageINTELi(i32 %in) + %samp = call spir_func %spirv.Sampler addrspace(1)* @_Z35__spirv_ConvertHandleToSamplerINTELl(i64 42) + ret void +} + +declare spir_func %spirv.Image._long_2_0_0_0_0_0_0 addrspace(1)* @_Z33__spirv_ConvertHandleToImageINTELi(i32) + +declare spir_func %spirv.Sampler addrspace(1)* @_Z35__spirv_ConvertHandleToSamplerINTELl(i64) + +!opencl.spir.version = !{!0} +!spirv.Source = !{!1} +!llvm.ident = !{!2} + +!0 = !{i32 1, i32 2} +!1 = !{i32 4, i32 100000} +!2 = !{!"clang version 17.0.0"}