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