[llvm] [SPIRV] Add StorageImageExtendedFormats to available list for Vulkan (PR #192512)

Steven Perron via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 16 11:47:23 PDT 2026


https://github.com/s-perron created https://github.com/llvm/llvm-project/pull/192512

Some image formats used in OpTypeImage require the
StorageImageExtendedFormats capability. When it is not in the available
list, it is not emitted, causing invalid SPIR-V.

The solution is to add it to the list. It is available for all versions
of Vulkan.

Fixes #192486


>From 34a8fb40f447cb9ed46b776c2b83943defc8f3b9 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Thu, 16 Apr 2026 14:38:30 -0400
Subject: [PATCH] [SPIRV] Add StorageImageExtendedFormats to available list for
 Vulkan

Some image formats used in OpTypeImage require the
StorageImageExtendedFormats capability. When it is not in the available
list, it is not emitted, causing invalid SPIR-V.

The solution is to add it to the list. It is available for all versions
of Vulkan.

Fixes #192486
---
 llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp |  3 ++-
 .../hlsl-resources/ImageFormatR11fG11fB10f.ll | 23 +++++++++++++++++
 .../SPIRV/hlsl-resources/ImageFormatRg32f.ll  | 23 +++++++++++++++++
 .../SPIRV/hlsl-resources/ImageFormatRg32i.ll  | 23 +++++++++++++++++
 .../SPIRV/hlsl-resources/ImageFormatRg32ui.ll | 23 +++++++++++++++++
 .../hlsl-resources/ImageFormatRgba32f.ll      | 25 +++++++++++++++++++
 .../hlsl-resources/ImageFormatRgba32ui.ll     | 24 ++++++++++++++++++
 7 files changed, 143 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatR11fG11fB10f.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRg32f.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRg32i.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRg32ui.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRgba32f.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRgba32ui.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index 2eb84dec2cf29..c5d5af3bad238 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -965,7 +965,8 @@ void RequirementHandler::initAvailableCapabilitiesForVulkan(
                     Capability::StorageImageArrayDynamicIndexing,
                     Capability::DerivativeControl, Capability::MinLod,
                     Capability::ImageQuery, Capability::ImageGatherExtended,
-                    Capability::Addresses, Capability::VulkanMemoryModelKHR});
+                    Capability::Addresses, Capability::VulkanMemoryModelKHR,
+                    Capability::StorageImageExtendedFormats});
 
   // Became core in Vulkan 1.2
   if (ST.isAtLeastSPIRVVer(VersionTuple(1, 5))) {
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatR11fG11fB10f.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatR11fG11fB10f.ll
new file mode 100644
index 0000000000000..261097256c8f9
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatR11fG11fB10f.ll
@@ -0,0 +1,23 @@
+; 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 %}
+
+; CHECK-DAG: OpCapability StorageImageExtendedFormats
+; CHECK-DAG: %[[#float:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#image:]] = OpTypeImage %[[#float]] Buffer 2 0 0 2 R11fG11fB10f
+; CHECK-DAG: %[[#ptr:]] = OpTypePointer UniformConstant %[[#image]]
+; CHECK-DAG: %[[#var:]] = OpVariable %[[#ptr]] UniformConstant
+
+ at .str = private unnamed_addr constant [2 x i8] c"B\00", align 1
+
+define void @main() #0 {
+  %buffer = call target("spirv.Image", float, 5, 2, 0, 0, 2, 8)
+      @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_8(
+          i32 16, i32 7, i32 1, i32 0, ptr nonnull @.str)
+  %data = call <4 x float> @llvm.spv.resource.load.typedbuffer(
+      target("spirv.Image", float, 5, 2, 0, 0, 2, 8) %buffer, i32 0)
+  ret void
+}
+
+declare <4 x float> @llvm.spv.resource.load.typedbuffer(target("spirv.Image", float, 5, 2, 0, 0, 2, 8), i32)
+
+attributes #0 = { convergent noinline norecurse "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRg32f.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRg32f.ll
new file mode 100644
index 0000000000000..94e53e1f53844
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRg32f.ll
@@ -0,0 +1,23 @@
+; 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 %}
+
+; CHECK-DAG: OpCapability StorageImageExtendedFormats
+; CHECK-DAG: %[[#float:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#image:]] = OpTypeImage %[[#float]] Buffer 2 0 0 2 Rg32f
+; CHECK-DAG: %[[#ptr:]] = OpTypePointer UniformConstant %[[#image]]
+; CHECK-DAG: %[[#var:]] = OpVariable %[[#ptr]] UniformConstant
+
+ at .str = private unnamed_addr constant [2 x i8] c"B\00", align 1
+
+define void @main() #0 {
+  %buffer = call target("spirv.Image", float, 5, 2, 0, 0, 2, 6)
+      @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_6(
+          i32 16, i32 7, i32 1, i32 0, ptr nonnull @.str)
+  %data = call <4 x float> @llvm.spv.resource.load.typedbuffer(
+      target("spirv.Image", float, 5, 2, 0, 0, 2, 6) %buffer, i32 0)
+  ret void
+}
+
+declare <4 x float> @llvm.spv.resource.load.typedbuffer(target("spirv.Image", float, 5, 2, 0, 0, 2, 6), i32)
+
+attributes #0 = { convergent noinline norecurse "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRg32i.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRg32i.ll
new file mode 100644
index 0000000000000..60b19062c09e8
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRg32i.ll
@@ -0,0 +1,23 @@
+; 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 %}
+
+; CHECK-DAG: OpCapability StorageImageExtendedFormats
+; CHECK-DAG: %[[#int:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#image:]] = OpTypeImage %[[#int]] Buffer 2 0 0 2 Rg32i
+; CHECK-DAG: %[[#ptr:]] = OpTypePointer UniformConstant %[[#image]]
+; CHECK-DAG: %[[#var:]] = OpVariable %[[#ptr]] UniformConstant
+
+ at .str = private unnamed_addr constant [2 x i8] c"B\00", align 1
+
+define void @main() #0 {
+  %buffer = call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 25)
+      @llvm.spv.resource.handlefrombinding.tspirv.SignedImage_i32_5_2_0_0_2_25(
+          i32 16, i32 7, i32 1, i32 0, ptr nonnull @.str)
+  %data = call <4 x i32> @llvm.spv.resource.load.typedbuffer(
+      target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 25) %buffer, i32 0)
+  ret void
+}
+
+declare <4 x i32> @llvm.spv.resource.load.typedbuffer(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 25), i32)
+
+attributes #0 = { convergent noinline norecurse "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRg32ui.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRg32ui.ll
new file mode 100644
index 0000000000000..7255992230c40
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRg32ui.ll
@@ -0,0 +1,23 @@
+; 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 %}
+
+; CHECK-DAG: OpCapability StorageImageExtendedFormats
+; CHECK-DAG: %[[#uint:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#image:]] = OpTypeImage %[[#uint]] Buffer 2 0 0 2 Rg32ui
+; CHECK-DAG: %[[#ptr:]] = OpTypePointer UniformConstant %[[#image]]
+; CHECK-DAG: %[[#var:]] = OpVariable %[[#ptr]] UniformConstant
+
+ at .str = private unnamed_addr constant [2 x i8] c"B\00", align 1
+
+define void @main() #0 {
+  %buffer = call target("spirv.Image", i32, 5, 2, 0, 0, 2, 35)
+      @llvm.spv.resource.handlefrombinding.tspirv.Image_i32_5_2_0_0_2_35(
+          i32 16, i32 7, i32 1, i32 0, ptr nonnull @.str)
+  %data = call <4 x i32> @llvm.spv.resource.load.typedbuffer(
+      target("spirv.Image", i32, 5, 2, 0, 0, 2, 35) %buffer, i32 0)
+  ret void
+}
+
+declare <4 x i32> @llvm.spv.resource.load.typedbuffer(target("spirv.Image", i32, 5, 2, 0, 0, 2, 35), i32)
+
+attributes #0 = { convergent noinline norecurse "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRgba32f.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRgba32f.ll
new file mode 100644
index 0000000000000..4ad877f450f78
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRgba32f.ll
@@ -0,0 +1,25 @@
+; 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 %}
+
+; CHECK-DAG: OpCapability Shader
+; CHECK-NOT: OpCapability StorageImageExtendedFormats
+; CHECK-DAG: %[[#float:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#image:]] = OpTypeImage %[[#float]] Buffer 2 0 0 2 Rgba32f
+; CHECK-DAG: %[[#ptr:]] = OpTypePointer UniformConstant %[[#image]]
+; CHECK-DAG: %[[#var:]] = OpVariable %[[#ptr]] UniformConstant
+
+ at .str = private unnamed_addr constant [2 x i8] c"B\00", align 1
+
+define void @main() #0 {
+  %buffer = call target("spirv.Image", float, 5, 2, 0, 0, 2, 1)
+      @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1(
+          i32 16, i32 7, i32 1, i32 0, ptr nonnull @.str)
+  %data = call <4 x float> @llvm.spv.resource.load.typedbuffer(
+      target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %buffer, i32 0)
+  ret void
+}
+
+declare <4 x float> @llvm.spv.resource.load.typedbuffer(target("spirv.Image", float, 5, 2, 0, 0, 2, 1), i32)
+
+
+attributes #0 = { convergent noinline norecurse "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRgba32ui.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRgba32ui.ll
new file mode 100644
index 0000000000000..24c6a58a49197
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImageFormatRgba32ui.ll
@@ -0,0 +1,24 @@
+; 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 %}
+
+; CHECK-DAG: OpCapability Shader
+; CHECK-NOT: OpCapability StorageImageExtendedFormats
+; CHECK-DAG: %[[#uint:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#image:]] = OpTypeImage %[[#uint]] Buffer 2 0 0 2 Rgba32ui
+; CHECK-DAG: %[[#ptr:]] = OpTypePointer UniformConstant %[[#image]]
+; CHECK-DAG: %[[#var:]] = OpVariable %[[#ptr]] UniformConstant
+
+ at .str = private unnamed_addr constant [2 x i8] c"B\00", align 1
+
+define void @main() #0 {
+  %buffer = call target("spirv.Image", i32, 5, 2, 0, 0, 2, 30)
+      @llvm.spv.resource.handlefrombinding.tspirv.Image_i32_5_2_0_0_2_30(
+          i32 16, i32 7, i32 1, i32 0, ptr nonnull @.str)
+  %data = call <4 x i32> @llvm.spv.resource.load.typedbuffer(
+      target("spirv.Image", i32, 5, 2, 0, 0, 2, 30) %buffer, i32 0)
+  ret void
+}
+
+declare <4 x i32> @llvm.spv.resource.load.typedbuffer(target("spirv.Image", i32, 5, 2, 0, 0, 2, 30), i32)
+
+attributes #0 = { convergent noinline norecurse "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }



More information about the llvm-commits mailing list