[clang] [llvm] [HLSL][SPRIV] Handle signed RWBuffer correctly (PR #144774)
Steven Perron via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 19 08:52:04 PDT 2025
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 <stevenperron at google.com>
Date: Wed, 18 Jun 2025 09:19:45 -0400
Subject: [PATCH 1/2] [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 %}
+
+ at .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 %}
+
+ at .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 <stevenperron at google.com>
Date: Thu, 19 Jun 2025 11:50:38 -0400
Subject: [PATCH 2/2] 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
More information about the llvm-commits
mailing list