From 66e975acbfa1e30d9010cee1a1bde9c29daa3604 Mon Sep 17 00:00:00 2001 From: Arkadiusz Lachowicz Date: Sun, 22 Mar 2026 08:12:29 +0100 Subject: [PATCH 1/2] Signal when SPIR-V legalization needs targeted cleanup --- external/SPIRV-Headers | 2 +- external/SPIRV-Tools | 2 +- .../lib/Frontend/Rewrite/RewriteObjC.cpp | 2 +- tools/clang/lib/SPIRV/SpirvEmitter.cpp | 22 ++++++++++++--- tools/clang/lib/SPIRV/SpirvEmitter.h | 2 ++ .../cast.flat-conversion.matrix.hlsl | 27 ++++++++++--------- .../legal-examples/15-loop-var-unroll-ok.hlsl | 16 +++++------ tools/clang/test/CodeGenSPIRV/max_id.hlsl | 4 +-- ...uctured-buffer.array.counter.indirect.hlsl | 6 +++-- ...ctured-buffer.array.counter.indirect2.hlsl | 6 +++-- ....global-struct-of-resources.optimized.hlsl | 24 ++++++++--------- .../CodeGenSPIRV/vk.spec-constant.reuse.hlsl | 2 +- 12 files changed, 68 insertions(+), 47 deletions(-) diff --git a/external/SPIRV-Headers b/external/SPIRV-Headers index f31ca173ef..465055f6c9 160000 --- a/external/SPIRV-Headers +++ b/external/SPIRV-Headers @@ -1 +1 @@ -Subproject commit f31ca173eff866369e54d35e53375fadbabd58f4 +Subproject commit 465055f6c9128772e20082e893d974146acf7a02 diff --git a/external/SPIRV-Tools b/external/SPIRV-Tools index 64f5770f59..7134be5024 160000 --- a/external/SPIRV-Tools +++ b/external/SPIRV-Tools @@ -1 +1 @@ -Subproject commit 64f5770f59db933d46b9cad6edc42b4186409ef4 +Subproject commit 7134be5024ffe0b48de1f8206f0fe69e34497bbe diff --git a/tools/clang/lib/Frontend/Rewrite/RewriteObjC.cpp b/tools/clang/lib/Frontend/Rewrite/RewriteObjC.cpp index 204820b304..e55d2acd99 100644 --- a/tools/clang/lib/Frontend/Rewrite/RewriteObjC.cpp +++ b/tools/clang/lib/Frontend/Rewrite/RewriteObjC.cpp @@ -483,7 +483,7 @@ namespace { result = Context->getObjCIdType(); FunctionProtoType::ExtProtoInfo fpi; fpi.Variadic = variadic; - return Context->getFunctionType(result, args, fpi); + return Context->getFunctionType(result, args, fpi, {}); } // Helper function: create a CStyleCastExpr with trivial type source info. diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.cpp b/tools/clang/lib/SPIRV/SpirvEmitter.cpp index 930532191a..4a616a00e8 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.cpp +++ b/tools/clang/lib/SPIRV/SpirvEmitter.cpp @@ -593,6 +593,8 @@ SpirvEmitter::SpirvEmitter(CompilerInstance &ci) constEvaluator(astContext, spvBuilder), entryFunction(nullptr), curFunction(nullptr), curThis(nullptr), seenPushConstantAt(), isSpecConstantMode(false), needsLegalization(false), + needsLegalizationLoopUnroll(false), + needsLegalizationSsaRewrite(false), beforeHlslLegalization(false), mainSourceFile(nullptr) { // Get ShaderModel from command line hlsl profile option. @@ -954,6 +956,9 @@ void SpirvEmitter::HandleTranslationUnit(ASTContext &context) { declIdMapper.requiresFlatteningCompositeResources() || !dsetbindingsToCombineImageSampler.empty() || spirvOptions.signaturePacking; + needsLegalizationSsaRewrite = + needsLegalizationSsaRewrite || + !dsetbindingsToCombineImageSampler.empty(); // Run legalization passes if (spirvOptions.codeGenHighLevel) { @@ -5823,8 +5828,11 @@ SpirvInstruction *SpirvEmitter::createImageSample( SpirvInstruction *minLod, SpirvInstruction *residencyCodeId, SourceLocation loc, SourceRange range) { - if (varOffset) + if (varOffset) { needsLegalization = true; + needsLegalizationLoopUnroll = true; + needsLegalizationSsaRewrite = true; + } // SampleDref* instructions in SPIR-V always return a scalar. // They also have the correct type in HLSL. @@ -8045,7 +8053,7 @@ SpirvInstruction *SpirvEmitter::createVectorSplat(const Expr *scalarExpr, // Should find a more meaningful one. if (auto *constVal = dyn_cast(scalarVal)) { llvm::SmallVector elements(size_t(size), constVal); - const bool isSpecConst = constVal->getopcode() == spv::Op::OpSpecConstant; + const bool isSpecConst = constVal->isSpecConstant(); auto *value = spvBuilder.getConstantComposite(vecType, elements, isSpecConst); if (!value) @@ -16665,7 +16673,15 @@ bool SpirvEmitter::spirvToolsLegalize(std::vector *mod, optimizer.RegisterPass( spvtools::CreateInterfaceVariableScalarReplacementPass()); } - optimizer.RegisterLegalizationPasses(spirvOptions.preserveInterface); + auto legalizationSsaRewriteMode = spvtools::SSARewriteMode::None; + if (needsLegalizationLoopUnroll) { + legalizationSsaRewriteMode = spvtools::SSARewriteMode::All; + } else if (needsLegalizationSsaRewrite) { + legalizationSsaRewriteMode = spvtools::SSARewriteMode::OpaqueOnly; + } + optimizer.RegisterLegalizationPasses( + spirvOptions.preserveInterface, needsLegalizationLoopUnroll, + legalizationSsaRewriteMode); // Add flattening of resources if needed. if (spirvOptions.flattenResourceArrays) { optimizer.RegisterPass( diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.h b/tools/clang/lib/SPIRV/SpirvEmitter.h index 9b890d3af4..e6f5f5e57a 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.h +++ b/tools/clang/lib/SPIRV/SpirvEmitter.h @@ -1578,6 +1578,8 @@ class SpirvEmitter : public ASTConsumer { /// /// Note: legalization specific code bool needsLegalization; + bool needsLegalizationLoopUnroll; + bool needsLegalizationSsaRewrite; /// Whether the translated SPIR-V binary passes --before-hlsl-legalization /// option to spirv-val because of illegal function parameter scope. diff --git a/tools/clang/test/CodeGenSPIRV/cast.flat-conversion.matrix.hlsl b/tools/clang/test/CodeGenSPIRV/cast.flat-conversion.matrix.hlsl index f0b9d547e9..0ba8084f38 100644 --- a/tools/clang/test/CodeGenSPIRV/cast.flat-conversion.matrix.hlsl +++ b/tools/clang/test/CodeGenSPIRV/cast.flat-conversion.matrix.hlsl @@ -16,20 +16,18 @@ RWStructuredBuffer t_output; // COL: OpMemberDecorate %S 0 RowMajor // ROW: OpMemberDecorate %S 0 ColMajor -// The DXIL generated for the two cases seem to produce the same results, -// and this matches that behaviour. -// CHECK: [[array_const:%[0-9]+]] = OpConstantComposite %_arr_float_uint_6 %float_0 %float_1 %float_2 %float_3 %float_4 %float_5 -// CHECK: [[t:%[0-9]+]] = OpConstantComposite %T [[array_const]] - // The DXIL that is generates different order for the values depending on // whether the matrix is column or row major. However, for SPIR-V, the value // stored in both cases is the same because the decoration, which is checked // above, is what determines the layout in memory for the value. -// CHECK: [[row0:%[0-9]+]] = OpConstantComposite %v3float %float_0 %float_1 %float_2 -// CHECK: [[row1:%[0-9]+]] = OpConstantComposite %v3float %float_3 %float_4 %float_5 -// CHECK: [[mat:%[0-9]+]] = OpConstantComposite %mat2v3float %33 %34 -// CHECK: [[s:%[0-9]+]] = OpConstantComposite %S %35 +// CHECK: [[mat:%[0-9]+]] = OpLoad %mat2v3float +// CHECK: [[e00:%[0-9]+]] = OpCompositeExtract %float [[mat]] 0 0 +// CHECK: [[e01:%[0-9]+]] = OpCompositeExtract %float [[mat]] 0 1 +// CHECK: [[e02:%[0-9]+]] = OpCompositeExtract %float [[mat]] 0 2 +// CHECK: [[e10:%[0-9]+]] = OpCompositeExtract %float [[mat]] 1 0 +// CHECK: [[e11:%[0-9]+]] = OpCompositeExtract %float [[mat]] 1 1 +// CHECK: [[e12:%[0-9]+]] = OpCompositeExtract %float [[mat]] 1 2 void main() { S s; @@ -40,13 +38,16 @@ void main() { s.a[i][j] = i*3+j; } } -// CHECK: [[ac:%[0-9]+]] = OpAccessChain %_ptr_Uniform_T %t_output %int_0 %uint_0 -// CHECK: OpStore [[ac]] [[t]] +// CHECK: [[tptr:%[0-9]+]] = OpAccessChain %_ptr_Uniform_T %t_output %int_0 %uint_0 +// CHECK: [[tarr:%[0-9]+]] = OpCompositeConstruct %_arr_float_uint_6 [[e00]] [[e01]] [[e02]] [[e10]] [[e11]] [[e12]] +// CHECK: [[tval:%[0-9]+]] = OpCompositeConstruct %T [[tarr]] +// CHECK: OpStore [[tptr]] [[tval]] T t = (T)(s); t_output[0] = t; -// CHECK: [[ac:%[0-9]+]] = OpAccessChain %_ptr_Uniform_S %s_output %int_0 %uint_0 -// CHECK: OpStore [[ac]] [[s]] +// CHECK: [[sptr:%[0-9]+]] = OpAccessChain %_ptr_Uniform_S %s_output %int_0 %uint_0 +// CHECK: [[sval:%[0-9]+]] = OpCompositeConstruct %S [[mat]] +// CHECK: OpStore [[sptr]] [[sval]] s = (S)t; s_output[0] = s; } diff --git a/tools/clang/test/CodeGenSPIRV/legal-examples/15-loop-var-unroll-ok.hlsl b/tools/clang/test/CodeGenSPIRV/legal-examples/15-loop-var-unroll-ok.hlsl index be80b6c5a8..334eeb676a 100644 --- a/tools/clang/test/CodeGenSPIRV/legal-examples/15-loop-var-unroll-ok.hlsl +++ b/tools/clang/test/CodeGenSPIRV/legal-examples/15-loop-var-unroll-ok.hlsl @@ -1,14 +1,12 @@ // RUN: %dxc -T cs_6_0 -E main -O3 -Vd %s -spirv | FileCheck %s -// CHECK: [[ptr:%[0-9]+]] = OpAccessChain %_ptr_Uniform_S %gSBuffer2 -// CHECK-NEXT: [[val:%[0-9]+]] = OpLoad %S [[ptr]] -// CHECK-NEXT: [[ptr:%[0-9]+]] = OpAccessChain %_ptr_Uniform_S %gRWSBuffer -// CHECK-NEXT: OpStore [[ptr]] [[val]] - -// CHECK: [[ptr:%[0-9]+]] = OpAccessChain %_ptr_Uniform_S %gSBuffer2 -// CHECK-NEXT: [[val:%[0-9]+]] = OpLoad %S [[ptr]] -// CHECK-NEXT: [[ptr:%[0-9]+]] = OpAccessChain %_ptr_Uniform_S %gRWSBuffer -// CHECK-NEXT: OpStore [[ptr]] [[val]] +// CHECK: OpLoopMerge {{%[0-9]+}} {{%[0-9]+}} Unroll +// CHECK: [[which:%[0-9]+]] = OpSelect %_ptr_Uniform_type_StructuredBuffer_S {{%[0-9]+}} %gSBuffer1 %gSBuffer2 +// CHECK: [[idx:%[0-9]+]] = OpBitcast %uint {{%[0-9]+}} +// CHECK: [[src:%[0-9]+]] = OpAccessChain %_ptr_Uniform_S [[which]] %int_0 [[idx]] +// CHECK: [[val:%[0-9]+]] = OpLoad %S [[src]] +// CHECK: [[dst:%[0-9]+]] = OpAccessChain %_ptr_Uniform_S %gRWSBuffer %int_0 [[idx]] +// CHECK: OpStore [[dst]] [[val]] struct S { float4 f; diff --git a/tools/clang/test/CodeGenSPIRV/max_id.hlsl b/tools/clang/test/CodeGenSPIRV/max_id.hlsl index d57267b399..9e1c32c024 100644 --- a/tools/clang/test/CodeGenSPIRV/max_id.hlsl +++ b/tools/clang/test/CodeGenSPIRV/max_id.hlsl @@ -8,7 +8,7 @@ // CHECK-30: fatal error: failed to optimize SPIR-V: ID overflow. Try running compact-ids. // With a larger limit, the test case can compile successfully. -// CHECK-400: Bound: 204 +// CHECK-400: Bound: RWStructuredBuffer data; @@ -20,4 +20,4 @@ void main(uint3 id : SV_DispatchThreadID) for( int i = 0; i < 64; i++ ) { data[i] = i; } -} \ No newline at end of file +} diff --git a/tools/clang/test/CodeGenSPIRV/type.rwstructured-buffer.array.counter.indirect.hlsl b/tools/clang/test/CodeGenSPIRV/type.rwstructured-buffer.array.counter.indirect.hlsl index 76d8165107..3d3a633abb 100644 --- a/tools/clang/test/CodeGenSPIRV/type.rwstructured-buffer.array.counter.indirect.hlsl +++ b/tools/clang/test/CodeGenSPIRV/type.rwstructured-buffer.array.counter.indirect.hlsl @@ -20,11 +20,13 @@ void func(RWStructuredBuffer local) { float4 main(PSInput input) : SV_TARGET { -// CHECK: [[ac2:%[0-9]+]] = OpAccessChain %_ptr_Uniform_int %counter_var_g_rwbuffer {{%[0-9]+}} %uint_0 +// CHECK: [[counter_struct:%[0-9]+]] = OpAccessChain %_ptr_Uniform_type_ACSBuffer_counter %counter_var_g_rwbuffer {{%[0-9]+}} +// CHECK: [[ac2:%[0-9]+]] = OpAccessChain %_ptr_Uniform_int [[counter_struct]] %uint_0 // CHECK: OpAtomicIAdd %int [[ac2]] %uint_1 %uint_0 %int_1 func(g_rwbuffer[input.idx]); -// CHECK: [[ac2_0:%[0-9]+]] = OpAccessChain %_ptr_Uniform_uint %g_rwbuffer {{%[0-9]+}} %int_0 %uint_0 +// CHECK: [[rwbuffer_struct:%[0-9]+]] = OpAccessChain %_ptr_Uniform_type_RWStructuredBuffer_uint %g_rwbuffer {{%[0-9]+}} +// CHECK: [[ac2_0:%[0-9]+]] = OpAccessChain %_ptr_Uniform_uint [[rwbuffer_struct]] %int_0 %uint_0 // CHECK: OpLoad %uint [[ac2_0]] return g_rwbuffer[input.idx][0]; } diff --git a/tools/clang/test/CodeGenSPIRV/type.rwstructured-buffer.array.counter.indirect2.hlsl b/tools/clang/test/CodeGenSPIRV/type.rwstructured-buffer.array.counter.indirect2.hlsl index 0ea52864a2..9ad0fac8f6 100644 --- a/tools/clang/test/CodeGenSPIRV/type.rwstructured-buffer.array.counter.indirect2.hlsl +++ b/tools/clang/test/CodeGenSPIRV/type.rwstructured-buffer.array.counter.indirect2.hlsl @@ -18,11 +18,13 @@ float4 main(PSInput input) : SV_TARGET { RWStructuredBuffer l_rwbuffer[5] = g_rwbuffer; -// CHECK: [[ac2:%[0-9]+]] = OpAccessChain %_ptr_Uniform_int %counter_var_g_rwbuffer %int_0 %uint_0 +// CHECK: [[counter_struct:%[0-9]+]] = OpAccessChain %_ptr_Uniform_type_ACSBuffer_counter %counter_var_g_rwbuffer %int_0 +// CHECK: [[ac2:%[0-9]+]] = OpAccessChain %_ptr_Uniform_int [[counter_struct]] %uint_0 // CHECK: OpAtomicIAdd %int [[ac2]] %uint_1 %uint_0 %int_1 l_rwbuffer[0].IncrementCounter(); -// CHECK: [[ac2_0:%[0-9]+]] = OpAccessChain %_ptr_Uniform_uint %g_rwbuffer {{%[0-9]+}} %int_0 %uint_0 +// CHECK: [[rwbuffer_struct:%[0-9]+]] = OpAccessChain %_ptr_Uniform_type_RWStructuredBuffer_uint %g_rwbuffer {{%[0-9]+}} +// CHECK: [[ac2_0:%[0-9]+]] = OpAccessChain %_ptr_Uniform_uint [[rwbuffer_struct]] %int_0 %uint_0 // CHECK: OpLoad %uint [[ac2_0]] return l_rwbuffer[input.idx][0]; } diff --git a/tools/clang/test/CodeGenSPIRV/vk.binding.global-struct-of-resources.optimized.hlsl b/tools/clang/test/CodeGenSPIRV/vk.binding.global-struct-of-resources.optimized.hlsl index 1656e7f50f..5f668532d1 100644 --- a/tools/clang/test/CodeGenSPIRV/vk.binding.global-struct-of-resources.optimized.hlsl +++ b/tools/clang/test/CodeGenSPIRV/vk.binding.global-struct-of-resources.optimized.hlsl @@ -3,20 +3,20 @@ // Check the names // // CHECK: OpName %secondGlobal_t "secondGlobal.t" -// CHECK: OpName %[[fg0:firstGlobal_[0-9]+__t]] "firstGlobal{{.*}}.t" -// CHECK: OpName %[[fg1:firstGlobal_[0-9]+__t]] "firstGlobal{{.*}}.t" -// CHECK: OpName %[[fg2:firstGlobal_[0-9]+__t]] "firstGlobal{{.*}}.t" -// CHECK: OpName %[[fg3:firstGlobal_[0-9]+__t]] "firstGlobal{{.*}}.t" +// CHECK: OpName %[[fg0:[0-9A-Za-z_]+]] "firstGlobal{{.*}}.t" +// CHECK: OpName %[[fg1:[0-9A-Za-z_]+]] "firstGlobal{{.*}}.t" +// CHECK: OpName %[[fg2:[0-9A-Za-z_]+]] "firstGlobal{{.*}}.t" +// CHECK: OpName %[[fg3:[0-9A-Za-z_]+]] "firstGlobal{{.*}}.t" // CHECK: OpName %secondGlobal_tt_0__s "secondGlobal.tt[0].s" // CHECK: OpName %secondGlobal_tt_1__s "secondGlobal.tt[1].s" -// CHECK: OpName %[[fgtt0_0:firstGlobal_[0-9]+__tt_0__s]] "firstGlobal{{.*}}.tt[0].s" -// CHECK: OpName %[[fgtt0_1:firstGlobal_[0-9]+__tt_1__s]] "firstGlobal{{.*}}.tt[1].s" -// CHECK: OpName %[[fgtt1_0:firstGlobal_[0-9]+__tt_0__s]] "firstGlobal{{.*}}.tt[0].s" -// CHECK: OpName %[[fgtt1_1:firstGlobal_[0-9]+__tt_1__s]] "firstGlobal{{.*}}.tt[1].s" -// CHECK: OpName %[[fgtt2_0:firstGlobal_[0-9]+__tt_0__s]] "firstGlobal{{.*}}.tt[0].s" -// CHECK: OpName %[[fgtt2_1:firstGlobal_[0-9]+__tt_1__s]] "firstGlobal{{.*}}.tt[1].s" -// CHECK: OpName %[[fgtt3_0:firstGlobal_[0-9]+__tt_0__s]] "firstGlobal{{.*}}.tt[0].s" -// CHECK: OpName %[[fgtt3_1:firstGlobal_[0-9]+__tt_1__s]] "firstGlobal{{.*}}.tt[1].s" +// CHECK: OpName %[[fgtt0_0:[0-9A-Za-z_]+]] "firstGlobal{{.*}}.tt[0].s" +// CHECK: OpName %[[fgtt0_1:[0-9A-Za-z_]+]] "firstGlobal{{.*}}.tt[1].s" +// CHECK: OpName %[[fgtt1_0:[0-9A-Za-z_]+]] "firstGlobal{{.*}}.tt[0].s" +// CHECK: OpName %[[fgtt1_1:[0-9A-Za-z_]+]] "firstGlobal{{.*}}.tt[1].s" +// CHECK: OpName %[[fgtt2_0:[0-9A-Za-z_]+]] "firstGlobal{{.*}}.tt[0].s" +// CHECK: OpName %[[fgtt2_1:[0-9A-Za-z_]+]] "firstGlobal{{.*}}.tt[1].s" +// CHECK: OpName %[[fgtt3_0:[0-9A-Za-z_]+]] "firstGlobal{{.*}}.tt[0].s" +// CHECK: OpName %[[fgtt3_1:[0-9A-Za-z_]+]] "firstGlobal{{.*}}.tt[1].s" // Check flattening of bindings // Explanation: Only the resources that are used will have a binding assignment diff --git a/tools/clang/test/CodeGenSPIRV/vk.spec-constant.reuse.hlsl b/tools/clang/test/CodeGenSPIRV/vk.spec-constant.reuse.hlsl index 95fb0502d7..3a1aeab246 100644 --- a/tools/clang/test/CodeGenSPIRV/vk.spec-constant.reuse.hlsl +++ b/tools/clang/test/CodeGenSPIRV/vk.spec-constant.reuse.hlsl @@ -5,6 +5,6 @@ [shader("pixel")] float4 main(float4 position : SV_Position) : SV_Target0 { -// CHECK: OpConstantComposite %v4bool %y %y %y %y +// CHECK: OpSpecConstantComposite %v4bool %y %y %y %y return y ? position : 1.0; } From 9ac57982725c4264348b20ad3fb271acc0993754 Mon Sep 17 00:00:00 2001 From: Arkadiusz Lachowicz Date: Mon, 23 Mar 2026 10:41:25 +0100 Subject: [PATCH 2/2] Update SPIR-V submodule pointers --- external/SPIRV-Headers | 2 +- external/SPIRV-Tools | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/external/SPIRV-Headers b/external/SPIRV-Headers index 465055f6c9..c141151dd5 160000 --- a/external/SPIRV-Headers +++ b/external/SPIRV-Headers @@ -1 +1 @@ -Subproject commit 465055f6c9128772e20082e893d974146acf7a02 +Subproject commit c141151dd53cbd5b1ced0665ad95ae3e91e8f916 diff --git a/external/SPIRV-Tools b/external/SPIRV-Tools index 7134be5024..2a730e127a 160000 --- a/external/SPIRV-Tools +++ b/external/SPIRV-Tools @@ -1 +1 @@ -Subproject commit 7134be5024ffe0b48de1f8206f0fe69e34497bbe +Subproject commit 2a730e127a32ac8b0713f5e1490d7b9be9d1cc9a