-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[HLSL][SPIRV] Add -fspv-use-unknown-image-format option #155664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
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 llvm#148270
@llvm/pr-subscribers-hlsl @llvm/pr-subscribers-clang Author: Steven Perron (s-perron) ChangesThis option allows users to control the image format used for HLSL resources Fixes #148270 Full diff: https://github.com/llvm/llvm-project/pull/155664.diff 8 Files Affected:
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>>;
|
Enable vulkan tests that are now able to pass using clang. Some of the tests require small updates in the options used. FYI: Requires llvm/llvm-project#155664 to avoid validation problems on Intel.
Enable vulkan tests that are now able to pass using clang. Some of the tests require small updates in the options used. FYI: Requires llvm/llvm-project#155664 to avoid validation problems on Intel.
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 #148270