https://github.com/s-perron updated https://github.com/llvm/llvm-project/pull/144774
>From 7d3d8bb30863dd860183f7b9635aa34b72a9c3ae Mon Sep 17 00:00:00 2001 From: Steven Perron <stevenper...@google.com> Date: Wed, 18 Jun 2025 09:19:45 -0400 Subject: [PATCH 1/3] [HLSL][SPRIV] Handle sign RWBuffer correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In Vulkan, the signedness of the accesses to images has to match the signedness of the backing image. See https://docs.vulkan.org/spec/latest/chapters/textures.html#textures-input, where it says the behaviour is undefined if > the signedness of any read or sample operation does not match the signedness > of the image’s format. Users who define say an `RWBuffer<int>` will create a Vulkan image with a signed integer format. So the HLSL that is generated must match that expecation. The solution we use is to generate a `spirv.SignedImage` target type for signed integer instead of `spirv.Image`. The two types are otherwise the same. The backend will add the `signExtend` image operand to access to the image to ensure the image is access as a signed image. Fixes #144580  --- clang/lib/CodeGen/Targets/SPIR.cpp | 28 ++-- .../builtins/RWBuffer-elementtype.hlsl | 10 +- .../builtins/RWBuffer-subscript.hlsl | 8 +- llvm/docs/SPIRVUsage.rst | 7 + llvm/lib/IR/Type.cpp | 2 +- llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp | 38 +---- llvm/lib/Target/SPIRV/SPIRVBuiltins.td | 1 + llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 3 +- llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 34 +++++ llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h | 15 +- .../Target/SPIRV/SPIRVInstructionSelector.cpp | 61 +++++--- .../hlsl-resources/SignedBufferLoadStore.ll | 137 ++++++++++++++++++ .../hlsl-resources/UnsignedBufferLoadStore.ll | 137 ++++++++++++++++++ offload-test-suite | 1 + 14 files changed, 403 insertions(+), 79 deletions(-) create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/SignedBufferLoadStore.ll create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/UnsignedBufferLoadStore.ll create mode 160000 offload-test-suite diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp b/clang/lib/CodeGen/Targets/SPIR.cpp index 2f1e43cdc8cc3..ebbf8ac0a6752 100644 --- a/clang/lib/CodeGen/Targets/SPIR.cpp +++ b/clang/lib/CodeGen/Targets/SPIR.cpp @@ -58,7 +58,7 @@ class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo { const SmallVector<int32_t> *Packoffsets = nullptr) const override; llvm::Type *getSPIRVImageTypeFromHLSLResource( const HLSLAttributedResourceType::Attributes &attributes, - llvm::Type *ElementType, llvm::LLVMContext &Ctx) const; + QualType SampledType, CodeGenModule &CGM) const; void setOCLKernelStubCallingConvention(const FunctionType *&FT) const override; }; @@ -483,12 +483,12 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType( assert(!ResAttrs.IsROV && "Rasterizer order views not implemented for SPIR-V yet"); - llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy); if (!ResAttrs.RawBuffer) { // convert element type - return getSPIRVImageTypeFromHLSLResource(ResAttrs, ElemType, Ctx); + return getSPIRVImageTypeFromHLSLResource(ResAttrs, ContainedTy, CGM); } + llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy); llvm::ArrayType *RuntimeArrayType = llvm::ArrayType::get(ElemType, 0); uint32_t StorageClass = /* StorageBuffer storage class */ 12; bool IsWritable = ResAttrs.ResourceClass == llvm::dxil::ResourceClass::UAV; @@ -516,13 +516,18 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType( } llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource( - const HLSLAttributedResourceType::Attributes &attributes, - llvm::Type *ElementType, llvm::LLVMContext &Ctx) const { + const HLSLAttributedResourceType::Attributes &attributes, QualType Ty, + CodeGenModule &CGM) const { + llvm::LLVMContext &Ctx = CGM.getLLVMContext(); - if (ElementType->isVectorTy()) - ElementType = ElementType->getScalarType(); + Ty = Ty->getCanonicalTypeUnqualified(); + if (const VectorType *V = dyn_cast<VectorType>(Ty)) + Ty = V->getElementType(); + assert(!Ty->isVectorType() && "We still have a vector type."); - assert((ElementType->isIntegerTy() || ElementType->isFloatingPointTy()) && + llvm::Type *SampledType = CGM.getTypes().ConvertType(Ty); + + assert((SampledType->isIntegerTy() || SampledType->isFloatingPointTy()) && "The element type for a SPIR-V resource must be a scalar integer or " "floating point type."); @@ -531,6 +536,9 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource( // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpTypeImage. SmallVector<unsigned, 6> IntParams(6, 0); + const char *Name = + Ty->isSignedIntegerType() ? "spirv.SignedImage" : "spirv.Image"; + // Dim // For now we assume everything is a buffer. IntParams[0] = 5; @@ -553,7 +561,9 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource( // Setting to unknown for now. IntParams[5] = 0; - return llvm::TargetExtType::get(Ctx, "spirv.Image", {ElementType}, IntParams); + llvm::TargetExtType *ImageType = + llvm::TargetExtType::get(Ctx, Name, {SampledType}, IntParams); + return ImageType; } std::unique_ptr<TargetCodeGenInfo> diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl index 0944ad59d5fb5..5512a657bc5f0 100644 --- a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl +++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl @@ -16,20 +16,20 @@ // DXIL: %"class.hlsl::RWBuffer.11" = type { target("dx.TypedBuffer", <3 x float>, 1, 0, 0) } // DXIL: %"class.hlsl::RWBuffer.12" = type { target("dx.TypedBuffer", <4 x i32>, 1, 0, 1) } -// SPIRV: %"class.hlsl::RWBuffer" = type { target("spirv.Image", i16, 5, 2, 0, 0, 2, 0) } +// SPIRV: %"class.hlsl::RWBuffer" = type { target("spirv.SignedImage", i16, 5, 2, 0, 0, 2, 0) } // SPIRV: %"class.hlsl::RWBuffer.0" = type { target("spirv.Image", i16, 5, 2, 0, 0, 2, 0) } -// SPIRV: %"class.hlsl::RWBuffer.1" = type { target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) } +// SPIRV: %"class.hlsl::RWBuffer.1" = type { target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) } // SPIRV: %"class.hlsl::RWBuffer.2" = type { target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) } -// SPIRV: %"class.hlsl::RWBuffer.3" = type { target("spirv.Image", i64, 5, 2, 0, 0, 2, 0) } +// SPIRV: %"class.hlsl::RWBuffer.3" = type { target("spirv.SignedImage", i64, 5, 2, 0, 0, 2, 0) } // SPIRV: %"class.hlsl::RWBuffer.4" = type { target("spirv.Image", i64, 5, 2, 0, 0, 2, 0) } // SPIRV: %"class.hlsl::RWBuffer.5" = type { target("spirv.Image", half, 5, 2, 0, 0, 2, 0) } // SPIRV: %"class.hlsl::RWBuffer.6" = type { target("spirv.Image", float, 5, 2, 0, 0, 2, 0) } // SPIRV: %"class.hlsl::RWBuffer.7" = type { target("spirv.Image", double, 5, 2, 0, 0, 2, 0) } -// SPIRV: %"class.hlsl::RWBuffer.8" = type { target("spirv.Image", i16, 5, 2, 0, 0, 2, 0) } +// SPIRV: %"class.hlsl::RWBuffer.8" = type { target("spirv.SignedImage", i16, 5, 2, 0, 0, 2, 0) } // SPIRV: %"class.hlsl::RWBuffer.9" = type { target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) } // SPIRV: %"class.hlsl::RWBuffer.10" = type { target("spirv.Image", half, 5, 2, 0, 0, 2, 0) } // SPIRV: %"class.hlsl::RWBuffer.11" = type { target("spirv.Image", float, 5, 2, 0, 0, 2, 0) } -// SPIRV: %"class.hlsl::RWBuffer.12" = type { target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) } +// SPIRV: %"class.hlsl::RWBuffer.12" = type { target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) } RWBuffer<int16_t> BufI16; RWBuffer<uint16_t> BufU16; diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-subscript.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-subscript.hlsl index cf810ed909eb7..63e3552b680b6 100644 --- a/clang/test/CodeGenHLSL/builtins/RWBuffer-subscript.hlsl +++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-subscript.hlsl @@ -9,18 +9,18 @@ void main(unsigned GI : SV_GroupIndex) { // CHECK: define void @main() // DXC: %[[INPTR:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}}) - // SPIRV: %[[INPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_i32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) + // SPIRV: %[[INPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) // CHECK: %[[LOAD:.*]] = load i32, ptr {{.*}}%[[INPTR]] // DXC: %[[OUTPTR:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}}) - // SPIRV: %[[OUTPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_i32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) + // SPIRV: %[[OUTPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) // CHECK: store i32 %[[LOAD]], ptr {{.*}}%[[OUTPTR]] Out[GI] = In[GI]; // DXC: %[[INPTR:.*]] = call ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}}) - // SPIRV: %[[INPTR:.*]] = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_i32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) + // SPIRV: %[[INPTR:.*]] = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) // CHECK: %[[LOAD:.*]] = load i32, ptr {{.*}}%[[INPTR]] // DXC: %[[OUTPTR:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}}) - // SPIRV: %[[OUTPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_i32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) + // SPIRV: %[[OUTPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) // CHECK: store i32 %[[LOAD]], ptr {{.*}}%[[OUTPTR]] Out[GI] = In.Load(GI); } diff --git a/llvm/docs/SPIRVUsage.rst b/llvm/docs/SPIRVUsage.rst index 1858bda6160d4..579c262e16477 100644 --- a/llvm/docs/SPIRVUsage.rst +++ b/llvm/docs/SPIRVUsage.rst @@ -255,6 +255,7 @@ using target extension types and are represented as follows: SPIR-V Type LLVM type name LLVM type arguments ================== ======================= =========================================================================================== OpTypeImage ``spirv.Image`` sampled type, dimensionality, depth, arrayed, MS, sampled, image format, [access qualifier] + OpTypeImage ``spirv.SignedImage`` sampled type, dimensionality, depth, arrayed, MS, sampled, image format, [access qualifier] OpTypeSampler ``spirv.Sampler`` (none) OpTypeSampledImage ``spirv.SampledImage`` sampled type, dimensionality, depth, arrayed, MS, sampled, image format, [access qualifier] OpTypeEvent ``spirv.Event`` (none) @@ -275,6 +276,12 @@ parameters of its underlying image type, so that a sampled image for the previous type has the representation ``target("spirv.SampledImage, void, 1, 1, 0, 0, 0, 0, 0)``. +The differences between ``spirv.Image`` and ``spirv.SignedImage`` is that the +backend will generate code assuming that the format of the image is a signed +integer instead of unsigned. This is required because llvm-ir will create the +same sampled type for signed and unsigned integers. If the image format is +unknown, the backend cannot distinguish the two case. + See `wg-hlsl proposal 0018 <https://github.com/llvm/wg-hlsl/blob/main/proposals/0018-spirv-resource-representation.md>`_ for details on ``spirv.VulkanBuffer``. diff --git a/llvm/lib/IR/Type.cpp b/llvm/lib/IR/Type.cpp index 7e64992c2dfe2..5e1bf2863191c 100644 --- a/llvm/lib/IR/Type.cpp +++ b/llvm/lib/IR/Type.cpp @@ -984,7 +984,7 @@ struct TargetTypeInfo { static TargetTypeInfo getTargetTypeInfo(const TargetExtType *Ty) { LLVMContext &C = Ty->getContext(); StringRef Name = Ty->getName(); - if (Name == "spirv.Image") + if (Name == "spirv.Image" || Name == "spirv.SignedImage") return TargetTypeInfo(PointerType::get(C, 0), TargetExtType::CanBeGlobal, TargetExtType::CanBeLocal); if (Name == "spirv.Type") { diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp index f73a39c6ee9da..6ec7544767c52 100644 --- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp @@ -3086,43 +3086,11 @@ static SPIRVType *getCoopMatrType(const TargetExtType *ExtensionType, ExtensionType->getIntParameter(3), true); } -static SPIRVType * -getImageType(const TargetExtType *ExtensionType, - const SPIRV::AccessQualifier::AccessQualifier Qualifier, - MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) { - assert(ExtensionType->getNumTypeParameters() == 1 && - "SPIR-V image builtin type must have sampled type parameter!"); - const SPIRVType *SampledType = - GR->getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder, - SPIRV::AccessQualifier::ReadWrite, true); - assert((ExtensionType->getNumIntParameters() == 7 || - ExtensionType->getNumIntParameters() == 6) && - "Invalid number of parameters for SPIR-V image builtin!"); - - SPIRV::AccessQualifier::AccessQualifier accessQualifier = - SPIRV::AccessQualifier::None; - if (ExtensionType->getNumIntParameters() == 7) { - accessQualifier = Qualifier == SPIRV::AccessQualifier::WriteOnly - ? SPIRV::AccessQualifier::WriteOnly - : SPIRV::AccessQualifier::AccessQualifier( - ExtensionType->getIntParameter(6)); - } - - // Create or get an existing type from GlobalRegistry. - return GR->getOrCreateOpTypeImage( - MIRBuilder, SampledType, - SPIRV::Dim::Dim(ExtensionType->getIntParameter(0)), - ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2), - ExtensionType->getIntParameter(3), ExtensionType->getIntParameter(4), - SPIRV::ImageFormat::ImageFormat(ExtensionType->getIntParameter(5)), - accessQualifier); -} - static SPIRVType *getSampledImageType(const TargetExtType *OpaqueType, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) { - SPIRVType *OpaqueImageType = getImageType( - OpaqueType, SPIRV::AccessQualifier::ReadOnly, MIRBuilder, GR); + SPIRVType *OpaqueImageType = GR->getImageType( + OpaqueType, SPIRV::AccessQualifier::ReadOnly, MIRBuilder); // Create or get an existing type from GlobalRegistry. return GR->getOrCreateOpTypeSampledImage(OpaqueImageType, MIRBuilder); } @@ -3293,7 +3261,7 @@ SPIRVType *lowerBuiltinType(const Type *OpaqueType, switch (TypeRecord->Opcode) { case SPIRV::OpTypeImage: - TargetType = getImageType(BuiltinType, AccessQual, MIRBuilder, GR); + TargetType = GR->getImageType(BuiltinType, AccessQual, MIRBuilder); break; case SPIRV::OpTypePipe: TargetType = getPipeType(BuiltinType, MIRBuilder, GR); diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td index 6842e5ff067cf..6b65defcf54c8 100644 --- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td +++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td @@ -1632,6 +1632,7 @@ def : BuiltinType<"spirv.Event", OpTypeEvent>; def : BuiltinType<"spirv.Sampler", OpTypeSampler>; def : BuiltinType<"spirv.DeviceEvent", OpTypeDeviceEvent>; def : BuiltinType<"spirv.Image", OpTypeImage>; +def : BuiltinType<"spirv.SignedImage", OpTypeImage>; def : BuiltinType<"spirv.SampledImage", OpTypeSampledImage>; def : BuiltinType<"spirv.Pipe", OpTypePipe>; def : BuiltinType<"spirv.CooperativeMatrixKHR", OpTypeCooperativeMatrixKHR>; diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp index cc95fde6a516d..b90e1aadbb5a1 100644 --- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp @@ -663,7 +663,8 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper( auto *II = dyn_cast<IntrinsicInst>(I); if (II && II->getIntrinsicID() == Intrinsic::spv_resource_getpointer) { auto *HandleType = cast<TargetExtType>(II->getOperand(0)->getType()); - if (HandleType->getTargetExtName() == "spirv.Image") { + if (HandleType->getTargetExtName() == "spirv.Image" || + HandleType->getTargetExtName() == "spirv.SignedImage") { if (II->hasOneUse()) { auto *U = *II->users().begin(); Ty = cast<Instruction>(U)->getAccessType(); diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp index 292b83e05b56d..83fccdc2bdba3 100644 --- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp @@ -1406,6 +1406,40 @@ SPIRVType *SPIRVGlobalRegistry::getOrCreateLayoutType( return SPIRVStructType; } +SPIRVType *SPIRVGlobalRegistry::getImageType( + const TargetExtType *ExtensionType, + const SPIRV::AccessQualifier::AccessQualifier Qualifier, + MachineIRBuilder &MIRBuilder) { + assert(ExtensionType->getNumTypeParameters() == 1 && + "SPIR-V image builtin type must have sampled type parameter!"); + const SPIRVType *SampledType = + getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder, + SPIRV::AccessQualifier::ReadWrite, true); + assert((ExtensionType->getNumIntParameters() == 7 || + ExtensionType->getNumIntParameters() == 6) && + "Invalid number of parameters for SPIR-V image builtin!"); + + SPIRV::AccessQualifier::AccessQualifier accessQualifier = + SPIRV::AccessQualifier::None; + if (ExtensionType->getNumIntParameters() == 7) { + accessQualifier = Qualifier == SPIRV::AccessQualifier::WriteOnly + ? SPIRV::AccessQualifier::WriteOnly + : SPIRV::AccessQualifier::AccessQualifier( + ExtensionType->getIntParameter(6)); + } + + // Create or get an existing type from GlobalRegistry. + SPIRVType *R = getOrCreateOpTypeImage( + MIRBuilder, SampledType, + SPIRV::Dim::Dim(ExtensionType->getIntParameter(0)), + ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2), + ExtensionType->getIntParameter(3), ExtensionType->getIntParameter(4), + SPIRV::ImageFormat::ImageFormat(ExtensionType->getIntParameter(5)), + accessQualifier); + SPIRVToLLVMType[R] = ExtensionType; + return R; +} + SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeImage( MachineIRBuilder &MIRBuilder, SPIRVType *SampledType, SPIRV::Dim::Dim Dim, uint32_t Depth, uint32_t Arrayed, uint32_t Multisampled, uint32_t Sampled, diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h index 35f616a1981d2..7ef812828b7cc 100644 --- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h +++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h @@ -501,6 +501,13 @@ class SPIRVGlobalRegistry : public SPIRVIRMapping { MachineIRBuilder &MIRBuilder); bool hasBlockDecoration(SPIRVType *Type) const; + SPIRVType * + getOrCreateOpTypeImage(MachineIRBuilder &MIRBuilder, SPIRVType *SampledType, + SPIRV::Dim::Dim Dim, uint32_t Depth, uint32_t Arrayed, + uint32_t Multisampled, uint32_t Sampled, + SPIRV::ImageFormat::ImageFormat ImageFormat, + SPIRV::AccessQualifier::AccessQualifier AccQual); + public: Register buildConstantInt(uint64_t Val, MachineIRBuilder &MIRBuilder, SPIRVType *SpvType, bool EmitIR, @@ -607,11 +614,9 @@ class SPIRVGlobalRegistry : public SPIRVIRMapping { const TargetExtType *T, bool EmitIr = false); SPIRVType * - getOrCreateOpTypeImage(MachineIRBuilder &MIRBuilder, SPIRVType *SampledType, - SPIRV::Dim::Dim Dim, uint32_t Depth, uint32_t Arrayed, - uint32_t Multisampled, uint32_t Sampled, - SPIRV::ImageFormat::ImageFormat ImageFormat, - SPIRV::AccessQualifier::AccessQualifier AccQual); + getImageType(const TargetExtType *ExtensionType, + const SPIRV::AccessQualifier::AccessQualifier Qualifier, + MachineIRBuilder &MIRBuilder); SPIRVType *getOrCreateOpTypeSampler(MachineIRBuilder &MIRBuilder); diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp index 851e0c6b81fcf..7104e5a226c82 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -341,6 +341,13 @@ class SPIRVInstructionSelector : public InstructionSelector { GIntrinsic &HandleDef, MachineInstr &Pos) const; }; +bool sampledTypeIsSignedInteger(const llvm::Type *HandleType) { + const TargetExtType *TET = cast<TargetExtType>(HandleType); + if (TET->getTargetExtName() == "spirv.Image") { + return false; + } + return TET->getTypeParameter(0)->isIntegerTy(); +} } // end anonymous namespace #define GET_GLOBALISEL_IMPL @@ -1195,12 +1202,17 @@ bool SPIRVInstructionSelector::selectStore(MachineInstr &I) const { Register IdxReg = IntPtrDef->getOperand(3).getReg(); if (HandleType->getOpcode() == SPIRV::OpTypeImage) { - return BuildMI(*I.getParent(), I, I.getDebugLoc(), - TII.get(SPIRV::OpImageWrite)) - .addUse(NewHandleReg) - .addUse(IdxReg) - .addUse(StoreVal) - .constrainAllUses(TII, TRI, RBI); + auto BMI = BuildMI(*I.getParent(), I, I.getDebugLoc(), + TII.get(SPIRV::OpImageWrite)) + .addUse(NewHandleReg) + .addUse(IdxReg) + .addUse(StoreVal); + + const llvm::Type *LLVMHandleType = GR.getTypeForSPIRVType(HandleType); + if (sampledTypeIsSignedInteger(LLVMHandleType)) + BMI.addImm(0x1000); // SignExtend + + return BMI.constrainAllUses(TII, TRI, RBI); } } @@ -3246,25 +3258,35 @@ bool SPIRVInstructionSelector::generateImageRead(Register &ResVReg, Register ImageReg, Register IdxReg, DebugLoc Loc, MachineInstr &Pos) const { + SPIRVType *ImageType = GR.getSPIRVTypeForVReg(ImageReg); + assert(ImageType && ImageType->getOpcode() == SPIRV::OpTypeImage && + "ImageReg is not an image type."); + bool IsSignedInteger = + sampledTypeIsSignedInteger(GR.getTypeForSPIRVType(ImageType)); + uint64_t ResultSize = GR.getScalarOrVectorComponentCount(ResType); if (ResultSize == 4) { - return BuildMI(*Pos.getParent(), Pos, Loc, TII.get(SPIRV::OpImageRead)) - .addDef(ResVReg) - .addUse(GR.getSPIRVTypeID(ResType)) - .addUse(ImageReg) - .addUse(IdxReg) - .constrainAllUses(TII, TRI, RBI); + auto BMI = BuildMI(*Pos.getParent(), Pos, Loc, TII.get(SPIRV::OpImageRead)) + .addDef(ResVReg) + .addUse(GR.getSPIRVTypeID(ResType)) + .addUse(ImageReg) + .addUse(IdxReg); + + if (IsSignedInteger) + BMI.addImm(0x1000); // SignExtend + return BMI.constrainAllUses(TII, TRI, RBI); } SPIRVType *ReadType = widenTypeToVec4(ResType, Pos); Register ReadReg = MRI->createVirtualRegister(GR.getRegClass(ReadType)); - bool Succeed = - BuildMI(*Pos.getParent(), Pos, Loc, TII.get(SPIRV::OpImageRead)) - .addDef(ReadReg) - .addUse(GR.getSPIRVTypeID(ReadType)) - .addUse(ImageReg) - .addUse(IdxReg) - .constrainAllUses(TII, TRI, RBI); + auto BMI = BuildMI(*Pos.getParent(), Pos, Loc, TII.get(SPIRV::OpImageRead)) + .addDef(ReadReg) + .addUse(GR.getSPIRVTypeID(ReadType)) + .addUse(ImageReg) + .addUse(IdxReg); + if (IsSignedInteger) + BMI.addImm(0x1000); // SignExtend + bool Succeed = BMI.constrainAllUses(TII, TRI, RBI); if (!Succeed) return false; @@ -4106,6 +4128,7 @@ bool SPIRVInstructionSelector::loadHandleBeforePosition( // handle is the image object. So images get an extra load. uint32_t LoadOpcode = IsStructuredBuffer ? SPIRV::OpCopyObject : SPIRV::OpLoad; + GR.assignSPIRVTypeToVReg(ResType, HandleReg, *Pos.getMF()); return BuildMI(*Pos.getParent(), Pos, HandleDef.getDebugLoc(), TII.get(LoadOpcode)) .addDef(HandleReg) diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/SignedBufferLoadStore.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/SignedBufferLoadStore.ll new file mode 100644 index 0000000000000..ecf7256ef90f4 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/SignedBufferLoadStore.ll @@ -0,0 +1,137 @@ +; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv1.6-vulkan1.3-library %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-vulkan1.3-library %s -o - -filetype=obj | spirv-val --target-env vulkan1.3 %} + +@.str.b0 = private unnamed_addr constant [3 x i8] c"B0\00", align 1 + +; CHECK-DAG: [[uint:%[0-9]+]] = OpTypeInt 32 0 +; CHECK-DAG: [[v2int:%[0-9]+]] = OpTypeVector [[uint]] 2 +; CHECK-DAG: [[v4int:%[0-9]+]] = OpTypeVector [[uint]] 4 +; CHECK-DAG: [[zero:%[0-9]+]] = OpConstant [[uint]] 0 +; CHECK-DAG: [[one:%[0-9]+]] = OpConstant [[uint]] 1 +; CHECK-DAG: [[twenty:%[0-9]+]] = OpConstant [[uint]] 20 +; CHECK-DAG: [[twenty_three:%[0-9]+]] = OpConstant [[uint]] 23 +; CHECK-DAG: [[ImageType:%[0-9]+]] = OpTypeImage [[uint]] Buffer 2 0 0 2 Unknown +; CHECK-DAG: [[ImagePtr:%[0-9]+]] = OpTypePointer UniformConstant [[ImageType]] +; CHECK-DAG: [[Var:%[0-9]+]] = OpVariable [[ImagePtr]] UniformConstant + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +; CHECK: OpFunction +define void @main_scalar() local_unnamed_addr #0 { +entry: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] + %s_h.i = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.SignedImage_f32_5_2_0_0_2_0t(i32 3, i32 5, i32 1, i32 0, i1 false, ptr nonnull @.str.b0) + +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4int]] [[H]] [[one]] SignExtend +; CHECK: [[V:%[0-9]+]] = OpCompositeExtract [[uint]] [[R]] 0 + %0 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.SignedImage_f32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 1) + %1 = load i32, ptr %0, align 4 +; CHECK: OpBranch [[bb_store:%[0-9]+]] + br label %bb_store + +; CHECK: [[bb_store]] = OpLabel +bb_store: + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[zero]] [[V]] SignExtend + %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.SignedImage_f32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 0) + store i32 %1, ptr %2, align 4 +; CHECK: OpBranch [[bb_both:%[0-9]+]] + br label %bb_both + +; CHECK: [[bb_both]] = OpLabel +bb_both: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4int]] [[H]] [[twenty_three]] SignExtend +; CHECK: [[V:%[0-9]+]] = OpCompositeExtract [[uint]] [[R]] 0 + %3 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.SignedImage_f32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 23) + %4 = load i32, ptr %3, align 4 + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[twenty]] [[V]] SignExtend + %5 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.SignedImage_f32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 20) + store i32 %4, ptr %5, align 4 + ret void +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +; CHECK: OpFunction +define void @main_vector2() local_unnamed_addr #0 { +entry: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] + %s_h.i = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.SignedImage_f32_5_2_0_0_2_0t(i32 3, i32 5, i32 1, i32 0, i1 false, ptr nonnull @.str.b0) + +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4int]] [[H]] [[one]] SignExtend +; CHECK: [[E0:%[0-9]+]] = OpCompositeExtract [[uint]] [[R]] 0 +; CHECK: [[E1:%[0-9]+]] = OpCompositeExtract [[uint]] [[R]] 1 +; CHECK: [[V:%[0-9]+]] = OpCompositeConstruct [[v2int]] [[E0]] [[E1]] + %0 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.SignedImage_f32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 1) + %1 = load <2 x i32>, ptr %0, align 4 +; CHECK: OpBranch [[bb_store:%[0-9]+]] + br label %bb_store + +; CHECK: [[bb_store]] = OpLabel +bb_store: + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[zero]] [[V]] SignExtend + %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.SignedImage_f32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 0) + store <2 x i32> %1, ptr %2, align 4 +; CHECK: OpBranch [[bb_both:%[0-9]+]] + br label %bb_both + +; CHECK: [[bb_both]] = OpLabel +bb_both: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4int]] [[H]] [[twenty_three]] SignExtend +; CHECK: [[E0:%[0-9]+]] = OpCompositeExtract [[uint]] [[R]] 0 +; CHECK: [[E1:%[0-9]+]] = OpCompositeExtract [[uint]] [[R]] 1 +; CHECK: [[V:%[0-9]+]] = OpCompositeConstruct [[v2int]] [[E0]] [[E1]] + %3 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.SignedImage_f32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 23) + %4 = load <2 x i32>, ptr %3, align 4 + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[twenty]] [[V]] SignExtend + %5 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.SignedImage_f32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 20) + store <2 x i32> %4, ptr %5, align 4 + ret void +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +; CHECK: OpFunction +define void @main_vector4() local_unnamed_addr #0 { +entry: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] + %s_h.i = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.SignedImage_f32_5_2_0_0_2_0t(i32 3, i32 5, i32 1, i32 0, i1 false, ptr nonnull @.str.b0) + +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4int]] [[H]] [[one]] SignExtend + %0 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.SignedImage_f32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 1) + %1 = load <4 x i32>, ptr %0, align 4 +; CHECK: OpBranch [[bb_store:%[0-9]+]] + br label %bb_store + +; CHECK: [[bb_store]] = OpLabel +bb_store: + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[zero]] [[R]] SignExtend + %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.SignedImage_f32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 0) + store <4 x i32> %1, ptr %2, align 4 +; CHECK: OpBranch [[bb_both:%[0-9]+]] + br label %bb_both + +; CHECK: [[bb_both]] = OpLabel +bb_both: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4int]] [[H]] [[twenty_three]] SignExtend + %3 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.SignedImage_f32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 23) + %4 = load <4 x i32>, ptr %3, align 4 + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[twenty]] [[R]] SignExtend + %5 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.SignedImage_f32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 20) + store <4 x i32> %4, ptr %5, align 4 + ret void +} + +attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #1 = { mustprogress nocallback nofree nosync nounwind willreturn memory(none) } diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/UnsignedBufferLoadStore.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/UnsignedBufferLoadStore.ll new file mode 100644 index 0000000000000..55227f025e6ad --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/UnsignedBufferLoadStore.ll @@ -0,0 +1,137 @@ +; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv1.6-vulkan1.3-library %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-vulkan1.3-library %s -o - -filetype=obj | spirv-val --target-env vulkan1.3 %} + +@.str.b0 = private unnamed_addr constant [3 x i8] c"B0\00", align 1 + +; CHECK-DAG: [[uint:%[0-9]+]] = OpTypeInt 32 0 +; CHECK-DAG: [[v2int:%[0-9]+]] = OpTypeVector [[uint]] 2 +; CHECK-DAG: [[v4int:%[0-9]+]] = OpTypeVector [[uint]] 4 +; CHECK-DAG: [[zero:%[0-9]+]] = OpConstant [[uint]] 0 +; CHECK-DAG: [[one:%[0-9]+]] = OpConstant [[uint]] 1 +; CHECK-DAG: [[twenty:%[0-9]+]] = OpConstant [[uint]] 20 +; CHECK-DAG: [[twenty_three:%[0-9]+]] = OpConstant [[uint]] 23 +; CHECK-DAG: [[ImageType:%[0-9]+]] = OpTypeImage [[uint]] Buffer 2 0 0 2 Unknown +; CHECK-DAG: [[ImagePtr:%[0-9]+]] = OpTypePointer UniformConstant [[ImageType]] +; CHECK-DAG: [[Var:%[0-9]+]] = OpVariable [[ImagePtr]] UniformConstant + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +; CHECK: OpFunction +define void @main_scalar() local_unnamed_addr #0 { +entry: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] + %s_h.i = tail call target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_0t(i32 3, i32 5, i32 1, i32 0, i1 false, ptr nonnull @.str.b0) + +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4int]] [[H]] [[one]] +; CHECK: [[V:%[0-9]+]] = OpCompositeExtract [[uint]] [[R]] 0 + %0 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 1) + %1 = load i32, ptr %0, align 4 +; CHECK: OpBranch [[bb_store:%[0-9]+]] + br label %bb_store + +; CHECK: [[bb_store]] = OpLabel +bb_store: + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[zero]] [[V]] + %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 0) + store i32 %1, ptr %2, align 4 +; CHECK: OpBranch [[bb_both:%[0-9]+]] + br label %bb_both + +; CHECK: [[bb_both]] = OpLabel +bb_both: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4int]] [[H]] [[twenty_three]] +; CHECK: [[V:%[0-9]+]] = OpCompositeExtract [[uint]] [[R]] 0 + %3 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 23) + %4 = load i32, ptr %3, align 4 + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[twenty]] [[V]] + %5 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 20) + store i32 %4, ptr %5, align 4 + ret void +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +; CHECK: OpFunction +define void @main_vector2() local_unnamed_addr #0 { +entry: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] + %s_h.i = tail call target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_0t(i32 3, i32 5, i32 1, i32 0, i1 false, ptr nonnull @.str.b0) + +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4int]] [[H]] [[one]] +; CHECK: [[E0:%[0-9]+]] = OpCompositeExtract [[uint]] [[R]] 0 +; CHECK: [[E1:%[0-9]+]] = OpCompositeExtract [[uint]] [[R]] 1 +; CHECK: [[V:%[0-9]+]] = OpCompositeConstruct [[v2int]] [[E0]] [[E1]] + %0 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 1) + %1 = load <2 x i32>, ptr %0, align 4 +; CHECK: OpBranch [[bb_store:%[0-9]+]] + br label %bb_store + +; CHECK: [[bb_store]] = OpLabel +bb_store: + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[zero]] [[V]] + %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 0) + store <2 x i32> %1, ptr %2, align 4 +; CHECK: OpBranch [[bb_both:%[0-9]+]] + br label %bb_both + +; CHECK: [[bb_both]] = OpLabel +bb_both: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4int]] [[H]] [[twenty_three]] +; CHECK: [[E0:%[0-9]+]] = OpCompositeExtract [[uint]] [[R]] 0 +; CHECK: [[E1:%[0-9]+]] = OpCompositeExtract [[uint]] [[R]] 1 +; CHECK: [[V:%[0-9]+]] = OpCompositeConstruct [[v2int]] [[E0]] [[E1]] + %3 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 23) + %4 = load <2 x i32>, ptr %3, align 4 + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[twenty]] [[V]] + %5 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 20) + store <2 x i32> %4, ptr %5, align 4 + ret void +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +; CHECK: OpFunction +define void @main_vector4() local_unnamed_addr #0 { +entry: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] + %s_h.i = tail call target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_0t(i32 3, i32 5, i32 1, i32 0, i1 false, ptr nonnull @.str.b0) + +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4int]] [[H]] [[one]] + %0 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 1) + %1 = load <4 x i32>, ptr %0, align 4 +; CHECK: OpBranch [[bb_store:%[0-9]+]] + br label %bb_store + +; CHECK: [[bb_store]] = OpLabel +bb_store: + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[zero]] [[R]] + %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 0) + store <4 x i32> %1, ptr %2, align 4 +; CHECK: OpBranch [[bb_both:%[0-9]+]] + br label %bb_both + +; CHECK: [[bb_both]] = OpLabel +bb_both: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4int]] [[H]] [[twenty_three]] + %3 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 23) + %4 = load <4 x i32>, ptr %3, align 4 + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[twenty]] [[R]] + %5 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 20) + store <4 x i32> %4, ptr %5, align 4 + ret void +} + +attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #1 = { mustprogress nocallback nofree nosync nounwind willreturn memory(none) } diff --git a/offload-test-suite b/offload-test-suite new file mode 160000 index 0000000000000..ef40d70010f26 --- /dev/null +++ b/offload-test-suite @@ -0,0 +1 @@ +Subproject commit ef40d70010f26bc3f385e948736e75a470c9aec8 >From 8a82c0d4836b3d253dc5945b4d2d1dc572fbaad5 Mon Sep 17 00:00:00 2001 From: Steven Perron <stevenper...@google.com> Date: Thu, 19 Jun 2025 11:50:38 -0400 Subject: [PATCH 2/3] Fixes from code review. --- llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp | 5 +++-- offload-test-suite | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) delete mode 160000 offload-test-suite diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp index 7104e5a226c82..563f5f5445481 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -346,8 +346,9 @@ bool sampledTypeIsSignedInteger(const llvm::Type *HandleType) { if (TET->getTargetExtName() == "spirv.Image") { return false; } - return TET->getTypeParameter(0)->isIntegerTy(); -} + assert(TET->getTargetExtName() == "spirv.SignedImage") { + return TET->getTypeParameter(0)->isIntegerTy(); + } } // end anonymous namespace #define GET_GLOBALISEL_IMPL diff --git a/offload-test-suite b/offload-test-suite deleted file mode 160000 index ef40d70010f26..0000000000000 --- a/offload-test-suite +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ef40d70010f26bc3f385e948736e75a470c9aec8 >From 742bb69088cb4e758c05c423343a3eb5f625162f Mon Sep 17 00:00:00 2001 From: Steven Perron <stevenper...@google.com> Date: Thu, 19 Jun 2025 12:22:32 -0400 Subject: [PATCH 3/3] Add tests using both signed and unsigned together with same image type. --- .../Target/SPIRV/SPIRVInstructionSelector.cpp | 6 ++-- .../hlsl-resources/MixedBufferLoadStore.ll | 36 +++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/MixedBufferLoadStore.ll diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp index 563f5f5445481..40a0bd97adaf9 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -346,9 +346,9 @@ bool sampledTypeIsSignedInteger(const llvm::Type *HandleType) { if (TET->getTargetExtName() == "spirv.Image") { return false; } - assert(TET->getTargetExtName() == "spirv.SignedImage") { - return TET->getTypeParameter(0)->isIntegerTy(); - } + assert(TET->getTargetExtName() == "spirv.SignedImage"); + return TET->getTypeParameter(0)->isIntegerTy(); +} } // end anonymous namespace #define GET_GLOBALISEL_IMPL diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/MixedBufferLoadStore.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/MixedBufferLoadStore.ll new file mode 100644 index 0000000000000..6305b04c0147a --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/MixedBufferLoadStore.ll @@ -0,0 +1,36 @@ +; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv1.6-vulkan1.3-library %s -o - | FileCheck %s --match-full-lines +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-vulkan1.3-library %s -o - -filetype=obj | spirv-val --target-env vulkan1.3 %} + +@.str.signed = private unnamed_addr constant [7 x i8] c"signed\00", align 1 +@.str.unsigned = private unnamed_addr constant [9 x i8] c"unsigned\00", align 1 + +; CHECK: {{.*}} OpFunction {{.*}} +define void @main_unsigned() local_unnamed_addr #0 { +entry: + %s_h.i = tail call target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_0t(i32 3, i32 5, i32 1, i32 0, i1 false, ptr nonnull @.str.unsigned) + +; CHECK: {{%[0-9]+}} = OpImageRead {{%[0-9]+}} {{%[0-9]+}} {{%[0-9]+}} + %0 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 1) + %1 = load i32, ptr %0, align 4 + %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 0) +; CHECK: OpImageWrite {{%[0-9]+}} {{%[0-9]+}} {{%[0-9]+}} + store i32 %1, ptr %2, align 4 + ret void +} + +; CHECK: {{.*}} OpFunction {{.*}} +define void @main_signed() local_unnamed_addr #0 { +entry: + %s_h.i = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.SignedImage_f32_5_2_0_0_2_0t(i32 3, i32 5, i32 1, i32 0, i1 false, ptr nonnull @.str.signed) + +; CHECK: {{%[0-9]+}} = OpImageRead {{%[0-9]+}} {{%[0-9]+}} {{%[0-9]+}} SignExtend + %0 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.SignedImage_f32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 1) + %1 = load i32, ptr %0, align 4 + %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.SignedImage_f32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %s_h.i, i32 0) +; CHECK: OpImageWrite {{%[0-9]+}} {{%[0-9]+}} {{%[0-9]+}} SignExtend + store i32 %1, ptr %2, align 4 + ret void +} + + +attributes #0 = { "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits