https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/155664

>From 95b0e22f32c62e973749d25286079515ba94e6ab Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenper...@google.com>
Date: Tue, 26 Aug 2025 13:12:42 -0400
Subject: [PATCH] [HLSL][SPIRV] Add -fhlsl-spv-use-unknown-image-format option

This option allows users to control the image format used for HLSL resources
when targeting SPIR-V. When the option is enabled, the unknown image format
is used. Otherwise, the image format is guessed based on the input type.

Fixes https://github.com/llvm/llvm-project/issues/148270
---
 clang/include/clang/Basic/LangOptions.def     |  1 +
 clang/include/clang/Driver/Options.td         | 10 +++
 clang/lib/CodeGen/Targets/SPIR.cpp            | 63 ++++++++++++++--
 clang/lib/Driver/ToolChains/Clang.cpp         |  3 +-
 .../resources/RWBuffer-elementtype.hlsl       | 12 +--
 .../resources/RWBuffer-imageformat.hlsl       | 74 +++++++++++++++++++
 .../resources/RWBuffer-subscript.hlsl         |  2 +-
 .../CodeGenHLSL/vk-features/SpirvType.hlsl    |  2 +-
 8 files changed, 152 insertions(+), 15 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/resources/RWBuffer-imageformat.hlsl

diff --git a/clang/include/clang/Basic/LangOptions.def 
b/clang/include/clang/Basic/LangOptions.def
index f094ba112988f..e0a5351143dfd 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -241,6 +241,7 @@ LANGOPT(HLSL, 1, 0, NotCompatible, "HLSL")
 ENUM_LANGOPT(HLSLVersion, HLSLLangStd, 16, HLSL_Unset, NotCompatible, "HLSL 
Version")
 LANGOPT(HLSLStrictAvailability, 1, 0, NotCompatible,
         "Strict availability diagnostic mode for HLSL built-in functions.")
+LANGOPT(HLSLSpvUseUnknownImageFormat, 1, 0, NotCompatible, "For storage images 
and texel buffers, sets the default format to 'Unknown' when not specified via 
the `vk::image_format` attribute. If this option is not used, the format is 
inferred from the resource's data type.")
 
 LANGOPT(CUDAIsDevice      , 1, 0, NotCompatible, "compiling for CUDA device")
 LANGOPT(CUDAAllowVariadicFunctions, 1, 0, NotCompatible, "allowing variadic 
functions in CUDA device code")
diff --git a/clang/include/clang/Driver/Options.td 
b/clang/include/clang/Driver/Options.td
index 9cfb1bbcac5c3..4e7864bbc59f7 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -9480,6 +9480,16 @@ def fvk_use_scalar_layout
     : DXCFlag<"fvk-use-scalar-layout">,
       HelpText<"Use scalar memory layout for Vulkan resources.">;
 
+def fhlsl_spv_use_unknown_image_format
+    : Flag<["-"], "fspv-use-unknown-image-format">,
+      Group<dxc_Group>,
+      Visibility<[CC1Option, DXCOption]>,
+      HelpText<"For storage images and texel buffers, sets the default format "
+               "to 'Unknown' when not specified via the `vk::image_format` "
+               "attribute. If this option is not used, the format is inferred "
+               "fron the resource's data type.">,
+      MarshallingInfoFlag<LangOpts<"HLSLSpvUseUnknownImageFormat">>;
+
 def no_wasm_opt : Flag<["--"], "no-wasm-opt">,
   Group<m_Group>,
   HelpText<"Disable the wasm-opt optimizer">,
diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp 
b/clang/lib/CodeGen/Targets/SPIR.cpp
index 237aea755fa29..442d71555ee59 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -517,14 +517,65 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(
   return nullptr;
 }
 
+static unsigned
+getImageFormat(const LangOptions &LangOpts,
+               const HLSLAttributedResourceType::Attributes &attributes,
+               llvm::Type *SampledType, QualType Ty, unsigned NumChannels) {
+  // For images with `Sampled` operand equal to 2, there are restrictions on
+  // using the Unknown image format. To avoid these restrictions in common
+  // cases, we guess an image format for them based on the sampled type and the
+  // number of channels. This is intended to match the behaviour of DXC.
+  if (LangOpts.HLSLSpvUseUnknownImageFormat ||
+      attributes.ResourceClass != llvm::dxil::ResourceClass::UAV) {
+    return 0; // Unknown
+  }
+
+  if (SampledType->isIntegerTy(32)) {
+    if (Ty->isSignedIntegerType()) {
+      if (NumChannels == 1)
+        return 24; // R32i
+      if (NumChannels == 2)
+        return 25; // Rg32i
+      if (NumChannels == 4)
+        return 21; // Rgba32i
+    } else {
+      if (NumChannels == 1)
+        return 33; // R32ui
+      if (NumChannels == 2)
+        return 35; // Rg32ui
+      if (NumChannels == 4)
+        return 30; // Rgba32ui
+    }
+  } else if (SampledType->isIntegerTy(64)) {
+    if (NumChannels == 1) {
+      if (Ty->isSignedIntegerType()) {
+        return 41; // R64i
+      }
+      return 40; // R64ui
+    }
+  } else if (SampledType->isFloatTy()) {
+    if (NumChannels == 1)
+      return 3; // R32f
+    if (NumChannels == 2)
+      return 6; // Rg32f
+    if (NumChannels == 4)
+      return 1; // Rgba32f
+  }
+
+  return 0; // Unknown
+}
+
 llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource(
-    const HLSLAttributedResourceType::Attributes &attributes, QualType Ty,
-    CodeGenModule &CGM) const {
+    const HLSLAttributedResourceType::Attributes &attributes,
+    QualType OriginalTy, CodeGenModule &CGM) const {
   llvm::LLVMContext &Ctx = CGM.getLLVMContext();
 
-  Ty = Ty->getCanonicalTypeUnqualified();
-  if (const VectorType *V = dyn_cast<VectorType>(Ty))
+  unsigned NumChannels = 1;
+  QualType Ty = OriginalTy->getCanonicalTypeUnqualified();
+  if (const VectorType *V = dyn_cast<VectorType>(Ty)) {
+    NumChannels = V->getNumElements();
     Ty = V->getElementType();
+  }
   assert(!Ty->isVectorType() && "We still have a vector type.");
 
   llvm::Type *SampledType = CGM.getTypes().ConvertTypeForMem(Ty);
@@ -560,8 +611,8 @@ llvm::Type 
*CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource(
       attributes.ResourceClass == llvm::dxil::ResourceClass::UAV ? 2 : 1;
 
   // Image format.
-  // Setting to unknown for now.
-  IntParams[5] = 0;
+  IntParams[5] = getImageFormat(CGM.getLangOpts(), attributes, SampledType, Ty,
+                                NumChannels);
 
   llvm::TargetExtType *ImageType =
       llvm::TargetExtType::get(Ctx, Name, {SampledType}, IntParams);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 29b7180df5cb5..747633898da6e 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -3801,7 +3801,8 @@ static void RenderHLSLOptions(const ArgList &Args, 
ArgStringList &CmdArgs,
       options::OPT_disable_llvm_passes,
       options::OPT_fnative_half_type,
       options::OPT_hlsl_entrypoint,
-      options::OPT_fdx_rootsignature_version};
+      options::OPT_fdx_rootsignature_version,
+      options::OPT_fhlsl_spv_use_unknown_image_format};
   if (!types::isHLSL(InputType))
     return;
   for (const auto &Arg : ForwardedArguments)
diff --git a/clang/test/CodeGenHLSL/resources/RWBuffer-elementtype.hlsl 
b/clang/test/CodeGenHLSL/resources/RWBuffer-elementtype.hlsl
index 5512a657bc5f0..f48521b0f1764 100644
--- a/clang/test/CodeGenHLSL/resources/RWBuffer-elementtype.hlsl
+++ b/clang/test/CodeGenHLSL/resources/RWBuffer-elementtype.hlsl
@@ -18,18 +18,18 @@
 
 // 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.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.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.1" = type { target("spirv.SignedImage", i32, 
5, 2, 0, 0, 2, 24) }
+// SPIRV: %"class.hlsl::RWBuffer.2" = type { target("spirv.Image", i32, 5, 2, 
0, 0, 2, 33) }
+// SPIRV: %"class.hlsl::RWBuffer.3" = type { target("spirv.SignedImage", i64, 
5, 2, 0, 0, 2, 41) }
+// SPIRV: %"class.hlsl::RWBuffer.4" = type { target("spirv.Image", i64, 5, 2, 
0, 0, 2, 40) }
 // 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.6" = type { target("spirv.Image", float, 5, 
2, 0, 0, 2, 3) }
 // SPIRV: %"class.hlsl::RWBuffer.7" = type { target("spirv.Image", double, 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.SignedImage", i32, 
5, 2, 0, 0, 2, 0) }
+// SPIRV: %"class.hlsl::RWBuffer.12" = type { target("spirv.SignedImage", i32, 
5, 2, 0, 0, 2, 21) }
 
 RWBuffer<int16_t> BufI16;
 RWBuffer<uint16_t> BufU16;
diff --git a/clang/test/CodeGenHLSL/resources/RWBuffer-imageformat.hlsl 
b/clang/test/CodeGenHLSL/resources/RWBuffer-imageformat.hlsl
new file mode 100644
index 0000000000000..aebee894f79d5
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/RWBuffer-imageformat.hlsl
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -emit-llvm -o - %s | FileCheck %s
+
+// Signed integers
+// CHECK: %"class.hlsl::RWBuffer" = type { target("spirv.SignedImage", i32, 5, 
2, 0, 0, 2, 24) }
+RWBuffer<int> rwb_int;
+// CHECK: %"class.hlsl::RWBuffer.0" = type { target("spirv.SignedImage", i32, 
5, 2, 0, 0, 2, 25) }
+RWBuffer<int2> rwb_int2;
+// CHECK: %"class.hlsl::RWBuffer.1" = type { target("spirv.SignedImage", i32, 
5, 2, 0, 0, 2, 0) }
+RWBuffer<int3> rwb_int3;
+// CHECK: %"class.hlsl::RWBuffer.2" = type { target("spirv.SignedImage", i32, 
5, 2, 0, 0, 2, 21) }
+RWBuffer<int4> rwb_int4;
+
+// Unsigned integers
+// CHECK: %"class.hlsl::RWBuffer.3" = type { target("spirv.Image", i32, 5, 2, 
0, 0, 2, 33) }
+RWBuffer<uint> rwb_uint;
+// CHECK: %"class.hlsl::RWBuffer.4" = type { target("spirv.Image", i32, 5, 2, 
0, 0, 2, 35) }
+RWBuffer<uint2> rwb_uint2;
+// CHECK: %"class.hlsl::RWBuffer.5" = type { target("spirv.Image", i32, 5, 2, 
0, 0, 2, 0) }
+RWBuffer<uint3> rwb_uint3;
+// CHECK: %"class.hlsl::RWBuffer.6" = type { target("spirv.Image", i32, 5, 2, 
0, 0, 2, 30) }
+RWBuffer<uint4> rwb_uint4;
+
+// 64-bit integers
+// CHECK: %"class.hlsl::RWBuffer.7" = type { target("spirv.SignedImage", i64, 
5, 2, 0, 0, 2, 41) }
+RWBuffer<int64_t> rwb_i64;
+// CHECK: %"class.hlsl::RWBuffer.8" = type { target("spirv.SignedImage", i64, 
5, 2, 0, 0, 2, 0) }
+RWBuffer<int64_t2> rwb_i64_2;
+// CHECK: %"class.hlsl::RWBuffer.9" = type { target("spirv.Image", i64, 5, 2, 
0, 0, 2, 40) }
+RWBuffer<uint64_t> rwb_u64;
+// CHECK: %"class.hlsl::RWBuffer.10" = type { target("spirv.Image", i64, 5, 2, 
0, 0, 2, 0) }
+RWBuffer<uint64_t2> rwb_u64_2;
+
+// Floats
+// CHECK: %"class.hlsl::RWBuffer.11" = type { target("spirv.Image", float, 5, 
2, 0, 0, 2, 3) }
+RWBuffer<float> rwb_float;
+// CHECK: %"class.hlsl::RWBuffer.12" = type { target("spirv.Image", float, 5, 
2, 0, 0, 2, 6) }
+RWBuffer<float2> rwb_float2;
+// CHECK: %"class.hlsl::RWBuffer.13" = type { target("spirv.Image", float, 5, 
2, 0, 0, 2, 0) }
+RWBuffer<float3> rwb_float3;
+// CHECK: %"class.hlsl::RWBuffer.14" = type { target("spirv.Image", float, 5, 
2, 0, 0, 2, 1) }
+RWBuffer<float4> rwb_float4;
+
+// Other types that should get Unknown format
+// CHECK: %"class.hlsl::RWBuffer.15" = type { target("spirv.Image", half, 5, 
2, 0, 0, 2, 0) }
+RWBuffer<half> rwb_half;
+// CHECK: %"class.hlsl::RWBuffer.16" = type { target("spirv.Image", double, 5, 
2, 0, 0, 2, 0) }
+RWBuffer<double> rwb_double;
+
+// Non-UAV resource
+// CHECK: %"class.hlsl::Buffer" = type { target("spirv.SignedImage", i32, 5, 
2, 0, 0, 1, 0) }
+Buffer<int> b_int;
+
+[numthreads(1,1,1)]
+void main(int GI : SV_GroupIndex) {
+    rwb_int[GI] = 0;
+    rwb_int2[GI] = 0;
+    rwb_int3[GI] = 0;
+    rwb_int4[GI] = 0;
+    rwb_uint[GI] = 0;
+    rwb_uint2[GI] = 0;
+    rwb_uint3[GI] = 0;
+    rwb_uint4[GI] = 0;
+    rwb_i64[GI] = 0;
+    rwb_i64_2[GI] = 0;
+    rwb_u64[GI] = 0;
+    rwb_u64_2[GI] = 0;
+    rwb_float[GI] = 0;
+    rwb_float2[GI] = 0;
+    rwb_float3[GI] = 0;
+    rwb_float4[GI] = 0;
+    rwb_half[GI] = 0;
+    rwb_double[GI] = 0;
+    int val = b_int[GI];
+}
diff --git a/clang/test/CodeGenHLSL/resources/RWBuffer-subscript.hlsl 
b/clang/test/CodeGenHLSL/resources/RWBuffer-subscript.hlsl
index 63e3552b680b6..0de171cb452d8 100644
--- a/clang/test/CodeGenHLSL/resources/RWBuffer-subscript.hlsl
+++ b/clang/test/CodeGenHLSL/resources/RWBuffer-subscript.hlsl
@@ -1,5 +1,5 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -emit-llvm -o - -O0 
%s | FileCheck %s --check-prefixes=DXC,CHECK
-// RUN: %clang_cc1 -triple spirv1.6-pc-vulkan1.3-compute -emit-llvm -o - -O0 
%s | FileCheck %s --check-prefixes=SPIRV,CHECK
+// RUN: %clang_cc1 -triple spirv1.6-pc-vulkan1.3-compute 
-fspv-use-unknown-image-format -emit-llvm -o - -O0 %s | FileCheck %s 
--check-prefixes=SPIRV,CHECK
 
 RWBuffer<int> In;
 RWBuffer<int> Out;
diff --git a/clang/test/CodeGenHLSL/vk-features/SpirvType.hlsl 
b/clang/test/CodeGenHLSL/vk-features/SpirvType.hlsl
index 7149be0122f4d..0cebac1e9d864 100644
--- a/clang/test/CodeGenHLSL/vk-features/SpirvType.hlsl
+++ b/clang/test/CodeGenHLSL/vk-features/SpirvType.hlsl
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
 // RUN:   spirv-unknown-vulkan-compute %s -emit-llvm -disable-llvm-passes \
-// RUN:   -o - | FileCheck %s
+// RUN:   -fspv-use-unknown-image-format -o - | FileCheck %s
 
 template<class T, uint64_t Size>
 using Array = vk::SpirvOpaqueType</* OpTypeArray */ 28, T, 
vk::integral_constant<uint64_t, Size>>;

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to