From b42f0f61c453c95af2efd4c8216a4fd4160aa07e Mon Sep 17 00:00:00 2001 From: Farzon Lotfi Date: Mon, 24 Aug 2026 14:22:14 -0400 Subject: [PATCH 1/4] [202x][unroll] Only run the default LLVM loop unroll for 202x onward resolves #8789 This change maintains the old loop unroll behavior for language modes below 202x. The 202x unroll behavior is for this to become a hint that is mostly used for O3 optimizations. We will do this by passing a language-version-derived flag through PassManagerBuilder to DxilLoopUnroll. For HLSL 202x and later, DxilLoopUnroll will leave loops carrying llvm.loop.unroll.count unchanged. The standard unroller may then consume the metadata at -O3. For older language versions, DxilLoopUnroll will continue using the count as an execution limit. The custom pass will continue handling llvm.loop.unroll.full in every language version. This keeps bare [unroll] behavior and its diagnostics unchanged. --- docs/ReleaseNotes.md | 3 ++ .../llvm/Transforms/IPO/PassManagerBuilder.h | 1 + include/llvm/Transforms/Scalar.h | 4 +- lib/Transforms/IPO/PassManagerBuilder.cpp | 4 +- lib/Transforms/Scalar/DxilLoopUnroll.cpp | 16 +++++-- tools/clang/lib/CodeGen/BackendUtil.cpp | 2 + .../attributes/unroll/count_cbuff.hlsl | 2 +- .../unroll/count_greater_than_i.hlsl | 2 +- .../attributes/unroll/count_hint_202x.hlsl | 21 ++++++++++ .../attributes/unroll/count_less_than_i.hlsl | 2 +- .../unroll/count_runtime_hint_202x.hlsl | 42 +++++++++++++++++++ .../attributes/unroll/full_hint_202x.hlsl | 16 +++++++ 12 files changed, 106 insertions(+), 9 deletions(-) create mode 100644 tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_hint_202x.hlsl create mode 100644 tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_runtime_hint_202x.hlsl create mode 100644 tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/full_hint_202x.hlsl diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 19c0d75174..8202895de8 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -33,6 +33,9 @@ line upon naming the release. Refer to previous for appropriate section names. #### HLSL Language +- Starting with HLSL 202x, the count in `[unroll(N)]` is a partial-unroll hint + and no longer limits the number of loop iterations + [#8789](https://github.com/microsoft/DirectXShaderCompiler/issues/8789). - Casting a scalar to a struct or array containing a resource is now an error instead of crashing [#6661](https://github.com/microsoft/DirectXShaderCompiler/issues/6661). diff --git a/include/llvm/Transforms/IPO/PassManagerBuilder.h b/include/llvm/Transforms/IPO/PassManagerBuilder.h index 2f342d9412..b5266fce7f 100644 --- a/include/llvm/Transforms/IPO/PassManagerBuilder.h +++ b/include/llvm/Transforms/IPO/PassManagerBuilder.h @@ -129,6 +129,7 @@ class PassManagerBuilder { bool HLSLHighLevel = false; // HLSL Change bool HLSLAllowPreserveValues = false; // HLSL Change bool HLSLOnlyWarnOnUnrollFail = false; // HLSL Change + bool HLSLUnrollCountIsHint = false; // HLSL Change hlsl::HLSLExtensionsCodegenHelper *HLSLExtensionsCodeGen = nullptr; // HLSL Change bool HLSLResMayAlias = false; // HLSL Change unsigned ScanLimit = 0; // HLSL Change diff --git a/include/llvm/Transforms/Scalar.h b/include/llvm/Transforms/Scalar.h index 02edcbddaf..bc7bc31cbc 100644 --- a/include/llvm/Transforms/Scalar.h +++ b/include/llvm/Transforms/Scalar.h @@ -129,7 +129,9 @@ void initializeDxilFixConstArrayInitializerPass(PassRegistry&); Pass *createDxilConditionalMem2RegPass(bool NoOpt); void initializeDxilConditionalMem2RegPass(PassRegistry&); -Pass *createDxilLoopUnrollPass(unsigned MaxIterationAttempt, bool OnlyWarnOnFail, bool StructurizeLoopExits); +Pass *createDxilLoopUnrollPass(unsigned MaxIterationAttempt, + bool OnlyWarnOnFail, bool StructurizeLoopExits, + bool UnrollCountIsHint); void initializeDxilLoopUnrollPass(PassRegistry&); Pass *createDxilEraseDeadRegionPass(); diff --git a/lib/Transforms/IPO/PassManagerBuilder.cpp b/lib/Transforms/IPO/PassManagerBuilder.cpp index 46dc5508b9..89a2f4b9d5 100644 --- a/lib/Transforms/IPO/PassManagerBuilder.cpp +++ b/lib/Transforms/IPO/PassManagerBuilder.cpp @@ -308,7 +308,9 @@ void PassManagerBuilder::addHLSLPasses(legacy::PassManagerBase &MPM) { // struct members. // Needs to happen before resources are lowered and before HL // module is gone. - MPM.add(createDxilLoopUnrollPass(1024, HLSLOnlyWarnOnUnrollFail, StructurizeLoopExitsForUnroll)); + MPM.add(createDxilLoopUnrollPass(1024, HLSLOnlyWarnOnUnrollFail, + StructurizeLoopExitsForUnroll, + HLSLUnrollCountIsHint)); // Default unroll pass. This is purely for optimizing loops without // attributes. diff --git a/lib/Transforms/Scalar/DxilLoopUnroll.cpp b/lib/Transforms/Scalar/DxilLoopUnroll.cpp index a48896f4a7..c59e827456 100644 --- a/lib/Transforms/Scalar/DxilLoopUnroll.cpp +++ b/lib/Transforms/Scalar/DxilLoopUnroll.cpp @@ -109,12 +109,15 @@ class DxilLoopUnroll : public LoopPass { unsigned MaxIterationAttempt = 0; bool OnlyWarnOnFail = false; bool StructurizeLoopExits = false; + bool UnrollCountIsHint = false; DxilLoopUnroll(unsigned MaxIterationAttempt = 1024, - bool OnlyWarnOnFail = false, bool StructurizeLoopExits = false) + bool OnlyWarnOnFail = false, bool StructurizeLoopExits = false, + bool UnrollCountIsHint = false) : LoopPass(ID), MaxIterationAttempt(MaxIterationAttempt), OnlyWarnOnFail(OnlyWarnOnFail), - StructurizeLoopExits(StructurizeLoopExits) { + StructurizeLoopExits(StructurizeLoopExits), + UnrollCountIsHint(UnrollCountIsHint) { initializeDxilLoopUnrollPass(*PassRegistry::getPassRegistry()); } StringRef getPassName() const override { return "Dxil Loop Unroll"; } @@ -138,12 +141,14 @@ class DxilLoopUnroll : public LoopPass { false); GetPassOptionBool(O, "OnlyWarnOnFail", &OnlyWarnOnFail, false); GetPassOptionBool(O, "StructurizeLoopExits", &StructurizeLoopExits, false); + GetPassOptionBool(O, "UnrollCountIsHint", &UnrollCountIsHint, false); } void dumpConfig(raw_ostream &OS) override { LoopPass::dumpConfig(OS); OS << ",MaxIterationAttempt=" << MaxIterationAttempt; OS << ",OnlyWarnOnFail=" << OnlyWarnOnFail; OS << ",StructurizeLoopExits=" << StructurizeLoopExits; + OS << ",UnrollCountIsHint=" << UnrollCountIsHint; } void RecursivelyRemoveLoopOnSuccess(LPPassManager &LPM, Loop *L); void RecursivelyRecreateSubLoopForIteration(LPPassManager &LPM, LoopInfo *LI, @@ -790,6 +795,8 @@ bool DxilLoopUnroll::runOnLoop(Loop *L, LPPassManager &LPM) { return false; } ExplicitUnrollCount = (unsigned)ExplicitUnrollCountSigned; + if (UnrollCountIsHint) + return false; } if (!IsLoopSafeToClone(L)) @@ -1256,9 +1263,10 @@ bool DxilLoopUnroll::doFinalization() { Pass *llvm::createDxilLoopUnrollPass(unsigned MaxIterationAttempt, bool OnlyWarnOnFail, - bool StructurizeLoopExits) { + bool StructurizeLoopExits, + bool UnrollCountIsHint) { return new DxilLoopUnroll(MaxIterationAttempt, OnlyWarnOnFail, - StructurizeLoopExits); + StructurizeLoopExits, UnrollCountIsHint); } INITIALIZE_PASS_BEGIN(DxilLoopUnroll, "dxil-loop-unroll", "Dxil Unroll loops", diff --git a/tools/clang/lib/CodeGen/BackendUtil.cpp b/tools/clang/lib/CodeGen/BackendUtil.cpp index 52d77bf115..88e0f4639e 100644 --- a/tools/clang/lib/CodeGen/BackendUtil.cpp +++ b/tools/clang/lib/CodeGen/BackendUtil.cpp @@ -342,6 +342,8 @@ void EmitAssemblyHelper::CreatePasses() { PMBuilder.HLSLHighLevel = CodeGenOpts.HLSLHighLevel; PMBuilder.HLSLAllowPreserveValues = CodeGenOpts.HLSLAllowPreserveValues; PMBuilder.HLSLOnlyWarnOnUnrollFail = CodeGenOpts.HLSLOnlyWarnOnUnrollFail; + PMBuilder.HLSLUnrollCountIsHint = + LangOpts.HLSLVersion >= hlsl::LangStd::v202x; PMBuilder.HLSLExtensionsCodeGen = CodeGenOpts.HLSLExtensionsCodegen.get(); PMBuilder.HLSLResMayAlias = CodeGenOpts.HLSLResMayAlias; PMBuilder.ScanLimit = CodeGenOpts.ScanLimit; diff --git a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_cbuff.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_cbuff.hlsl index 8bb0b0fc3a..42aeba751d 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_cbuff.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_cbuff.hlsl @@ -1,4 +1,4 @@ -// RUN: %dxc -E main -T ps_6_0 %s | FileCheck %s +// RUN: %dxc -E main -T ps_6_0 -HV 2021 %s | FileCheck %s // CHECK: call float @dx.op.dot3 // CHECK: call float @dx.op.dot3 diff --git a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_greater_than_i.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_greater_than_i.hlsl index d86da03dca..b0d6c787cb 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_greater_than_i.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_greater_than_i.hlsl @@ -1,4 +1,4 @@ -// RUN: %dxc -E main -T ps_6_0 %s | FileCheck %s +// RUN: %dxc -E main -T ps_6_0 -HV 2021 %s | FileCheck %s // CHECK: call float @dx.op.dot3 // CHECK: call float @dx.op.dot3 diff --git a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_hint_202x.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_hint_202x.hlsl new file mode 100644 index 0000000000..94e3a335ea --- /dev/null +++ b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_hint_202x.hlsl @@ -0,0 +1,21 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x -O2 %s | FileCheck %s --check-prefix=O2 +// RUN: %dxc -E main -T ps_6_0 -HV 202x -O3 %s | FileCheck %s --check-prefix=O3 + +// O2: call float @dx.op.dot3 +// O2-NOT: call float @dx.op.dot3 +// O2: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop +// O2: !{!"llvm.loop.unroll.count", i32 3} + +// O3-COUNT-4: call float @dx.op.dot3 +// O3-NOT: call float @dx.op.dot3 +// O3: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop +// O3: !{!"llvm.loop.unroll.disable"} + +float main(float3 a : A, float3 b : B) : SV_Target { + float result = 0; + [unroll(3)] + for (int i = 0; i < 10; i++) { + result += dot(a * i, b); + } + return result; +} diff --git a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_less_than_i.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_less_than_i.hlsl index a2fc63157d..e85291a644 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_less_than_i.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_less_than_i.hlsl @@ -1,4 +1,4 @@ -// RUN: %dxc -E main -T ps_6_0 %s | FileCheck %s +// RUN: %dxc -E main -T ps_6_0 -HV 2021 %s | FileCheck %s // CHECK: call float @dx.op.dot3 // CHECK: call float @dx.op.dot3 diff --git a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_runtime_hint_202x.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_runtime_hint_202x.hlsl new file mode 100644 index 0000000000..a2602b2373 --- /dev/null +++ b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_runtime_hint_202x.hlsl @@ -0,0 +1,42 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x -O2 %s | FileCheck %s --check-prefixes=COMMON,O2 +// RUN: %dxc -E main -T ps_6_0 -HV 202x -O3 %s | FileCheck %s --check-prefixes=COMMON,O3 +// RUN: %dxc -E mainPowerOfTwo -T ps_6_0 -HV 202x -O2 %s | FileCheck %s --check-prefix=COMMON,POWER-O2 +// RUN: %dxc -E mainPowerOfTwo -T ps_6_0 -HV 202x -O3 %s | FileCheck %s --check-prefix=POWER-O3 + +// O2 Doesn't unroll and O3 Runtime unrolling only supports +// power-of-two factors, so the count hint is +// consumed without cloning the loop body. +// COMMON: call float @dx.op.dot3 +// COMMON-NOT: call float @dx.op.dot3 +// COMMON: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop +// O2: !{!"llvm.loop.unroll.count", i32 3} +// O3: !{!"llvm.loop.unroll.disable"} +// POWER-O2: !{!"llvm.loop.unroll.count", i32 4} + +// The O3 output has one remainder-loop body and four unrolled main-loop bodies. +// POWER-O3: and i32 {{.*}}, 3 +// POWER-O3-COUNT-5: call float @dx.op.dot3 +// POWER-O3-NOT: call float @dx.op.dot3 +// POWER-O3: add i32 {{.*}}, 4 +// POWER-O3: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop +// POWER-O3: !{!"llvm.loop.unroll.disable"} + +float main(float3 a : A, float3 b : B, + uint iterationCount : COUNT) : SV_Target { + float result = 0; + [unroll(3)] + for (uint i = 0; i < iterationCount; i++) { + result += dot(a * i, b); + } + return result; +} + +float mainPowerOfTwo(float3 a : A, float3 b : B, + uint iterationCount : COUNT) : SV_Target { + float result = 0; + [unroll(4)] + for (uint i = 0; i < iterationCount; i++) { + result += dot(a * i, b); + } + return result; +} diff --git a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/full_hint_202x.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/full_hint_202x.hlsl new file mode 100644 index 0000000000..b2ad622829 --- /dev/null +++ b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/full_hint_202x.hlsl @@ -0,0 +1,16 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x -O2 %s | FileCheck %s --check-prefix=CHECK +// RUN: %dxc -E main -T ps_6_0 -HV 202x -O3 %s | FileCheck %s --check-prefix=CHECK + +// CHECK-COUNT-10: call float @dx.op.dot3 +// CHECK-NOT: call float @dx.op.dot3 +// CHECK-NOT: br i1 +// CHECK-NOT: !llvm.loop + +float main(float3 a : A, float3 b : B) : SV_Target { + float result = 0; + [unroll] + for (int i = 0; i < 10; i++) { + result += dot(a * i, b); + } + return result; +} From 9d4b85b3e384cdbf132b7659276da3ead254ffc8 Mon Sep 17 00:00:00 2001 From: Farzon Lotfi Date: Mon, 24 Aug 2026 16:55:54 -0400 Subject: [PATCH 2/4] add co-pilot comment, fix failing test caused by typo --- lib/Transforms/IPO/PassManagerBuilder.cpp | 4 ++-- .../attributes/unroll/count_runtime_hint_202x.hlsl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Transforms/IPO/PassManagerBuilder.cpp b/lib/Transforms/IPO/PassManagerBuilder.cpp index 89a2f4b9d5..38d7adb309 100644 --- a/lib/Transforms/IPO/PassManagerBuilder.cpp +++ b/lib/Transforms/IPO/PassManagerBuilder.cpp @@ -312,8 +312,8 @@ void PassManagerBuilder::addHLSLPasses(legacy::PassManagerBase &MPM) { StructurizeLoopExitsForUnroll, HLSLUnrollCountIsHint)); - // Default unroll pass. This is purely for optimizing loops without - // attributes. + // Default unroll pass. In HLSL 202x, this consumes [unroll(N)] as a + // partial-unroll hint; it otherwise optimizes loops without attributes. if (OptLevel > 2) { MPM.add(createLoopUnrollPass(-1, -1, -1, -1, StructurizeLoopExitsForUnroll)); } diff --git a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_runtime_hint_202x.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_runtime_hint_202x.hlsl index a2602b2373..a4c1cc6250 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_runtime_hint_202x.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_runtime_hint_202x.hlsl @@ -1,6 +1,6 @@ // RUN: %dxc -E main -T ps_6_0 -HV 202x -O2 %s | FileCheck %s --check-prefixes=COMMON,O2 // RUN: %dxc -E main -T ps_6_0 -HV 202x -O3 %s | FileCheck %s --check-prefixes=COMMON,O3 -// RUN: %dxc -E mainPowerOfTwo -T ps_6_0 -HV 202x -O2 %s | FileCheck %s --check-prefix=COMMON,POWER-O2 +// RUN: %dxc -E mainPowerOfTwo -T ps_6_0 -HV 202x -O2 %s | FileCheck %s --check-prefixes=COMMON,POWER-O2 // RUN: %dxc -E mainPowerOfTwo -T ps_6_0 -HV 202x -O3 %s | FileCheck %s --check-prefix=POWER-O3 // O2 Doesn't unroll and O3 Runtime unrolling only supports From c1edf4d1171058169024967a00ffb45f6160dd35 Mon Sep 17 00:00:00 2001 From: Farzon Lotfi Date: Mon, 24 Aug 2026 20:21:56 -0400 Subject: [PATCH 3/4] remove check count as it breaks the tests also make check-prefix one - instead of two (--) --- .../attributes/unroll/count_hint_202x.hlsl | 9 ++++++--- .../unroll/count_runtime_hint_202x.hlsl | 14 +++++++++----- .../attributes/unroll/full_hint_202x.hlsl | 15 ++++++++++++--- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_hint_202x.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_hint_202x.hlsl index 94e3a335ea..8fec049021 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_hint_202x.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_hint_202x.hlsl @@ -1,12 +1,15 @@ -// RUN: %dxc -E main -T ps_6_0 -HV 202x -O2 %s | FileCheck %s --check-prefix=O2 -// RUN: %dxc -E main -T ps_6_0 -HV 202x -O3 %s | FileCheck %s --check-prefix=O3 +// RUN: %dxc -E main -T ps_6_0 -HV 202x -O2 %s | FileCheck %s -check-prefix=O2 +// RUN: %dxc -E main -T ps_6_0 -HV 202x -O3 %s | FileCheck %s -check-prefix=O3 // O2: call float @dx.op.dot3 // O2-NOT: call float @dx.op.dot3 // O2: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop // O2: !{!"llvm.loop.unroll.count", i32 3} -// O3-COUNT-4: call float @dx.op.dot3 +// O3: call float @dx.op.dot3 +// O3: call float @dx.op.dot3 +// O3: call float @dx.op.dot3 +// O3: call float @dx.op.dot3 // O3-NOT: call float @dx.op.dot3 // O3: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop // O3: !{!"llvm.loop.unroll.disable"} diff --git a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_runtime_hint_202x.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_runtime_hint_202x.hlsl index a4c1cc6250..943d6e8a9f 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_runtime_hint_202x.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/count_runtime_hint_202x.hlsl @@ -1,7 +1,7 @@ -// RUN: %dxc -E main -T ps_6_0 -HV 202x -O2 %s | FileCheck %s --check-prefixes=COMMON,O2 -// RUN: %dxc -E main -T ps_6_0 -HV 202x -O3 %s | FileCheck %s --check-prefixes=COMMON,O3 -// RUN: %dxc -E mainPowerOfTwo -T ps_6_0 -HV 202x -O2 %s | FileCheck %s --check-prefixes=COMMON,POWER-O2 -// RUN: %dxc -E mainPowerOfTwo -T ps_6_0 -HV 202x -O3 %s | FileCheck %s --check-prefix=POWER-O3 +// RUN: %dxc -E main -T ps_6_0 -HV 202x -O2 %s | FileCheck %s -check-prefixes=COMMON,O2 +// RUN: %dxc -E main -T ps_6_0 -HV 202x -O3 %s | FileCheck %s -check-prefixes=COMMON,O3 +// RUN: %dxc -E mainPowerOfTwo -T ps_6_0 -HV 202x -O2 %s | FileCheck %s -check-prefixes=COMMON,POWER-O2 +// RUN: %dxc -E mainPowerOfTwo -T ps_6_0 -HV 202x -O3 %s | FileCheck %s -check-prefix=POWER-O3 // O2 Doesn't unroll and O3 Runtime unrolling only supports // power-of-two factors, so the count hint is @@ -15,7 +15,11 @@ // The O3 output has one remainder-loop body and four unrolled main-loop bodies. // POWER-O3: and i32 {{.*}}, 3 -// POWER-O3-COUNT-5: call float @dx.op.dot3 +// POWER-O3: call float @dx.op.dot3 +// POWER-O3: call float @dx.op.dot3 +// POWER-O3: call float @dx.op.dot3 +// POWER-O3: call float @dx.op.dot3 +// POWER-O3: call float @dx.op.dot3 // POWER-O3-NOT: call float @dx.op.dot3 // POWER-O3: add i32 {{.*}}, 4 // POWER-O3: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop diff --git a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/full_hint_202x.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/full_hint_202x.hlsl index b2ad622829..38be4d8135 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/full_hint_202x.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/full_hint_202x.hlsl @@ -1,7 +1,16 @@ -// RUN: %dxc -E main -T ps_6_0 -HV 202x -O2 %s | FileCheck %s --check-prefix=CHECK -// RUN: %dxc -E main -T ps_6_0 -HV 202x -O3 %s | FileCheck %s --check-prefix=CHECK +// RUN: %dxc -E main -T ps_6_0 -HV 202x -O2 %s | FileCheck %s -check-prefix=CHECK +// RUN: %dxc -E main -T ps_6_0 -HV 202x -O3 %s | FileCheck %s -check-prefix=CHECK -// CHECK-COUNT-10: call float @dx.op.dot3 +// CHECK: call float @dx.op.dot3 +// CHECK: call float @dx.op.dot3 +// CHECK: call float @dx.op.dot3 +// CHECK: call float @dx.op.dot3 +// CHECK: call float @dx.op.dot3 +// CHECK: call float @dx.op.dot3 +// CHECK: call float @dx.op.dot3 +// CHECK: call float @dx.op.dot3 +// CHECK: call float @dx.op.dot3 +// CHECK: call float @dx.op.dot3 // CHECK-NOT: call float @dx.op.dot3 // CHECK-NOT: br i1 // CHECK-NOT: !llvm.loop From de4c1a6b3ba8d248ba4802fe07be07eb80595a3e Mon Sep 17 00:00:00 2001 From: Farzon Lotfi Date: Mon, 24 Aug 2026 20:22:27 -0400 Subject: [PATCH 4/4] expose UnrollCountIsHint via python hctdb.py file --- utils/hct/hctdb.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index db9b7b6ffe..fa23370576 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -7580,6 +7580,12 @@ def add_pass(name, type_name, doc, opts): "c": 1, "d": "Whether the unroller should try to structurize loop exits first.", }, + { + "n": "UnrollCountIsHint", + "t": "bool", + "c": 1, + "d": "Whether an explicit unroll count should be treated as a hint.", + }, ], ) add_pass(