Skip to content

Commit 5f9c364

Browse files
committed
Add O1experimental SPIR-V fast compile mode
1 parent 9ac5798 commit 5f9c364

8 files changed

Lines changed: 91 additions & 28 deletions

File tree

docs/SPIR-V.rst

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -518,12 +518,22 @@ generate valid SPIR-V for Vulkan.
518518
Optimization
519519
~~~~~~~~~~~~
520520

521-
Optimization is also delegated to SPIRV-Tools. Right now there are no difference
522-
between optimization levels greater than zero; they will all invoke the same
523-
optimization recipe. That is, the recipe behind ``spirv-opt -O``. If you want to
524-
run a custom optimization recipe, you can do so using the command line option
525-
``-Oconfig=`` and specifying a comma-separated list of your desired passes.
526-
The passes are invoked in the specified order.
521+
Optimization is also delegated to SPIRV-Tools. There are two built-in
522+
optimization recipes for SPIR-V code generation:
523+
524+
* ``-O1experimental``: a development-oriented recipe that favors lower
525+
optimizer wall time. This profile is opt-in, leaves the default ``-O0``,
526+
``-O1``, ``-O2``, ``-O3`` and ``-Oconfig`` behavior unchanged, and may emit
527+
``VariablePointers`` for modules that need the fast path. On Vulkan targets,
528+
use at least ``-fspv-target-env=vulkan1.1`` and ensure the target device
529+
supports the required variable-pointer features. Vulkan 1.4 guarantees that
530+
support.
531+
* ``-O1``, ``-O2``, and ``-O3``: the performance-oriented recipe behind
532+
``spirv-opt -O``.
533+
534+
If you want to run a custom optimization recipe, you can do so using the
535+
command line option ``-Oconfig=`` and specifying a comma-separated list of
536+
your desired passes. The passes are invoked in the specified order.
527537

528538
For example, you can specify ``-Oconfig=--loop-unroll,--scalar-replacement=300,--eliminate-dead-code-aggressive``
529539
to firstly invoke loop unrolling, then invoke scalar replacement of aggregates,

include/dxc/Support/HLSLOptions.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ def O0 : Flag<["-", "/"], "O0">, Group<hlsloptz_Group>, Flags<[CoreOption]>,
9696
HelpText<"Optimization Level 0">;
9797
def O1 : Flag<["-", "/"], "O1">, Group<hlsloptz_Group>, Flags<[CoreOption]>,
9898
HelpText<"Optimization Level 1">;
99+
def O1experimental : Flag<["-"], "O1experimental">, Group<spirv_Group>,
100+
Flags<[CoreOption, DriverOption]>,
101+
HelpText<"Enable the experimental SPIR-V fast-compile profile; may emit VariablePointers and requires target support for them when used">;
99102
def O2 : Flag<["-", "/"], "O2">, Group<hlsloptz_Group>, Flags<[CoreOption]>,
100103
HelpText<"Optimization Level 2">;
101104
def O3 : Flag<["-", "/"], "O3">, Group<hlsloptz_Group>, Flags<[CoreOption]>,

include/dxc/Support/SPIRVOptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ struct SpirvCodeGenOptions {
5959
bool noWarnIgnoredFeatures = false;
6060
bool preserveBindings = false;
6161
bool preserveInterface = false;
62+
bool o1ExperimentalFastCompile = false;
6263
bool useDxLayout = false;
6364
bool useGlLayout = false;
6465
bool useLegacyBufferMatrixOrder = false;

lib/DxcSupport/HLSLOptions.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -792,11 +792,15 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
792792
}
793793

794794
opts.DisableOptimizations = false;
795-
if (Arg *A = Args.getLastArg(OPT_O0, OPT_O1, OPT_O2, OPT_O3, OPT_Od)) {
795+
if (Arg *A =
796+
Args.getLastArg(OPT_O0, OPT_O1, OPT_O1experimental, OPT_O2, OPT_O3,
797+
OPT_Od)) {
796798
if (A->getOption().matches(OPT_O0))
797799
opts.OptLevel = 0;
798800
if (A->getOption().matches(OPT_O1))
799801
opts.OptLevel = 1;
802+
if (A->getOption().matches(OPT_O1experimental))
803+
opts.OptLevel = 1;
800804
if (A->getOption().matches(OPT_O2))
801805
opts.OptLevel = 2;
802806
if (A->getOption().matches(OPT_O3))
@@ -1096,6 +1100,8 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
10961100
// SPIRV Change Starts
10971101
#ifdef ENABLE_SPIRV_CODEGEN
10981102
opts.GenSPIRV = Args.hasFlag(OPT_spirv, OPT_INVALID, false);
1103+
opts.SpirvOptions.o1ExperimentalFastCompile =
1104+
Args.hasFlag(OPT_O1experimental, OPT_INVALID, false);
10991105
opts.SpirvOptions.invertY =
11001106
Args.hasFlag(OPT_fvk_invert_y, OPT_INVALID, false);
11011107
opts.SpirvOptions.invertW =
@@ -1261,7 +1267,8 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
12611267
errors << "-Oconfig should not be specified more than once";
12621268
return 1;
12631269
}
1264-
if (Args.getLastArg(OPT_O0, OPT_O1, OPT_O2, OPT_O3)) {
1270+
if (Args.getLastArg(OPT_O0, OPT_O1, OPT_O1experimental, OPT_O2,
1271+
OPT_O3)) {
12651272
errors << "-Oconfig should not be used together with -O";
12661273
return 1;
12671274
}
@@ -1273,6 +1280,11 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
12731280
opts.SpirvOptions.entrypointName =
12741281
Args.getLastArgValue(OPT_fspv_entrypoint_name_EQ);
12751282

1283+
if (opts.SpirvOptions.o1ExperimentalFastCompile && !opts.GenSPIRV) {
1284+
errors << "-O1experimental requires -spirv";
1285+
return 1;
1286+
}
1287+
12761288
// Check for use of options not implemented in the SPIR-V backend.
12771289
if (Args.hasFlag(OPT_spirv, OPT_INVALID, false) &&
12781290
hasUnsupportedSpirvOption(Args, errors))
@@ -1295,6 +1307,7 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
12951307
Args.hasFlag(OPT_fspv_reflect, OPT_INVALID, false) ||
12961308
Args.hasFlag(OPT_fspv_fix_func_call_arguments, OPT_INVALID, false) ||
12971309
Args.hasFlag(OPT_fspv_print_all, OPT_INVALID, false) ||
1310+
Args.hasFlag(OPT_O1experimental, OPT_INVALID, false) ||
12981311
Args.hasFlag(OPT_Wno_vk_ignored_features, OPT_INVALID, false) ||
12991312
Args.hasFlag(OPT_Wno_vk_emulated_features, OPT_INVALID, false) ||
13001313
Args.hasFlag(OPT_fvk_auto_shift_bindings, OPT_INVALID, false) ||

tools/clang/lib/SPIRV/SpirvEmitter.cpp

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,7 @@ SpirvEmitter::SpirvEmitter(CompilerInstance &ci)
595595
isSpecConstantMode(false), needsLegalization(false),
596596
needsLegalizationLoopUnroll(false),
597597
needsLegalizationSsaRewrite(false),
598+
sawExplicitUnrollHint(false),
598599
beforeHlslLegalization(false), mainSourceFile(nullptr) {
599600

600601
// Get ShaderModel from command line hlsl profile option.
@@ -920,6 +921,20 @@ void SpirvEmitter::HandleTranslationUnit(ASTContext &context) {
920921
}
921922
}
922923

924+
if (useSpirvFastCompileProfile() &&
925+
(needsLegalizationLoopUnroll || sawExplicitUnrollHint)) {
926+
if (featureManager.isTargetEnvVulkan() &&
927+
!featureManager.isTargetEnvVulkan1p1OrAbove()) {
928+
emitFatalError(
929+
"-O1experimental requires -fspv-target-env=vulkan1.1 or above "
930+
"when the generated module needs VariablePointers",
931+
{});
932+
return;
933+
}
934+
935+
spvBuilder.requireCapability(spv::Capability::VariablePointers);
936+
}
937+
923938
// Output the constructed module.
924939
std::vector<uint32_t> m = spvBuilder.takeModule();
925940
if (context.getDiagnostics().hasErrorOccurred())
@@ -2289,6 +2304,7 @@ spv::LoopControlMask SpirvEmitter::translateLoopAttribute(const Stmt *stmt,
22892304
case attr::HLSLFastOpt:
22902305
return spv::LoopControlMask::DontUnroll;
22912306
case attr::HLSLUnroll:
2307+
sawExplicitUnrollHint = true;
22922308
return spv::LoopControlMask::Unroll;
22932309
case attr::HLSLAllowUAVCondition:
22942310
if (!spirvOptions.noWarnIgnoredFeatures) {
@@ -16627,8 +16643,12 @@ bool SpirvEmitter::spirvToolsOptimize(std::vector<uint32_t> *mod,
1662716643
options.set_max_id_bound(spirvOptions.maxId);
1662816644

1662916645
if (spirvOptions.optConfig.empty()) {
16630-
// Add performance passes.
16631-
optimizer.RegisterPerformancePasses(spirvOptions.preserveInterface);
16646+
if (useSpirvFastCompileProfile()) {
16647+
optimizer.RegisterPerformancePassesFastCompile(
16648+
spirvOptions.preserveInterface);
16649+
} else {
16650+
optimizer.RegisterPerformancePasses(spirvOptions.preserveInterface);
16651+
}
1663216652

1663316653
// Add propagation of volatile semantics passes.
1663416654
optimizer.RegisterPass(spvtools::CreateSpreadVolatileSemanticsPass());
@@ -16648,6 +16668,11 @@ bool SpirvEmitter::spirvToolsOptimize(std::vector<uint32_t> *mod,
1664816668
return optimizer.Run(mod->data(), mod->size(), mod, options);
1664916669
}
1665016670

16671+
bool SpirvEmitter::useSpirvFastCompileProfile() const {
16672+
return spirvOptions.o1ExperimentalFastCompile &&
16673+
spirvOptions.optConfig.empty();
16674+
}
16675+
1665116676
bool SpirvEmitter::spirvToolsLegalize(std::vector<uint32_t> *mod,
1665216677
std::string *messages,
1665316678
const std::vector<DescriptorSetAndBinding>
@@ -16673,15 +16698,19 @@ bool SpirvEmitter::spirvToolsLegalize(std::vector<uint32_t> *mod,
1667316698
optimizer.RegisterPass(
1667416699
spvtools::CreateInterfaceVariableScalarReplacementPass());
1667516700
}
16676-
auto legalizationSsaRewriteMode = spvtools::SSARewriteMode::None;
16677-
if (needsLegalizationLoopUnroll) {
16678-
legalizationSsaRewriteMode = spvtools::SSARewriteMode::All;
16679-
} else if (needsLegalizationSsaRewrite) {
16680-
legalizationSsaRewriteMode = spvtools::SSARewriteMode::OpaqueOnly;
16701+
if (useSpirvFastCompileProfile()) {
16702+
auto legalizationSsaRewriteMode = spvtools::SSARewriteMode::None;
16703+
if (needsLegalizationLoopUnroll) {
16704+
legalizationSsaRewriteMode = spvtools::SSARewriteMode::All;
16705+
} else if (needsLegalizationSsaRewrite) {
16706+
legalizationSsaRewriteMode = spvtools::SSARewriteMode::OpaqueOnly;
16707+
}
16708+
optimizer.RegisterLegalizationPasses(
16709+
spirvOptions.preserveInterface, needsLegalizationLoopUnroll,
16710+
legalizationSsaRewriteMode);
16711+
} else {
16712+
optimizer.RegisterLegalizationPasses(spirvOptions.preserveInterface);
1668116713
}
16682-
optimizer.RegisterLegalizationPasses(
16683-
spirvOptions.preserveInterface, needsLegalizationLoopUnroll,
16684-
legalizationSsaRewriteMode);
1668516714
// Add flattening of resources if needed.
1668616715
if (spirvOptions.flattenResourceArrays) {
1668716716
optimizer.RegisterPass(

tools/clang/lib/SPIRV/SpirvEmitter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,6 +1339,7 @@ class SpirvEmitter : public ASTConsumer {
13391339
/// gets the info/warning/error messages via |messages|.
13401340
/// Returns true on success and false otherwise.
13411341
bool spirvToolsOptimize(std::vector<uint32_t> *mod, std::string *messages);
1342+
bool useSpirvFastCompileProfile() const;
13421343

13431344
// \brief Runs the pass represented by the given pass token on the module.
13441345
// Returns true if the pass was successfully run. Any messages from the
@@ -1580,6 +1581,7 @@ class SpirvEmitter : public ASTConsumer {
15801581
bool needsLegalization;
15811582
bool needsLegalizationLoopUnroll;
15821583
bool needsLegalizationSsaRewrite;
1584+
bool sawExplicitUnrollHint;
15831585

15841586
/// Whether the translated SPIR-V binary passes --before-hlsl-legalization
15851587
/// option to spirv-val because of illegal function parameter scope.

tools/clang/test/CodeGenSPIRV/legal-examples/15-loop-var-unroll-ok.hlsl

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
// RUN: %dxc -T cs_6_0 -E main -O3 -Vd %s -spirv | FileCheck %s
2-
3-
// CHECK: OpLoopMerge {{%[0-9]+}} {{%[0-9]+}} Unroll
4-
// CHECK: [[which:%[0-9]+]] = OpSelect %_ptr_Uniform_type_StructuredBuffer_S {{%[0-9]+}} %gSBuffer1 %gSBuffer2
5-
// CHECK: [[idx:%[0-9]+]] = OpBitcast %uint {{%[0-9]+}}
6-
// CHECK: [[src:%[0-9]+]] = OpAccessChain %_ptr_Uniform_S [[which]] %int_0 [[idx]]
7-
// CHECK: [[val:%[0-9]+]] = OpLoad %S [[src]]
8-
// CHECK: [[dst:%[0-9]+]] = OpAccessChain %_ptr_Uniform_S %gRWSBuffer %int_0 [[idx]]
9-
// CHECK: OpStore [[dst]] [[val]]
1+
// RUN: %dxc -T cs_6_0 -E main -O3 -Vd -fspv-target-env=vulkan1.3 %s -spirv | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-O3
2+
// RUN: %dxc -T cs_6_0 -E main -O1experimental -Vd -fspv-target-env=vulkan1.3 %s -spirv | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-O1EXP
3+
4+
// CHECK-O1EXP: OpCapability VariablePointers
5+
6+
// CHECK: [[ptr:%[0-9]+]] = OpAccessChain %_ptr_Uniform_S %gSBuffer2
7+
// CHECK-NEXT: [[val:%[0-9]+]] = OpLoad %S [[ptr]]
8+
// CHECK-NEXT: [[ptr:%[0-9]+]] = OpAccessChain %_ptr_Uniform_S %gRWSBuffer
9+
// CHECK-NEXT: OpStore [[ptr]] [[val]]
10+
11+
// CHECK: [[ptr:%[0-9]+]] = OpAccessChain %_ptr_Uniform_S %gSBuffer2
12+
// CHECK-NEXT: [[val:%[0-9]+]] = OpLoad %S [[ptr]]
13+
// CHECK-NEXT: [[ptr:%[0-9]+]] = OpAccessChain %_ptr_Uniform_S %gRWSBuffer
14+
// CHECK-NEXT: OpStore [[ptr]] [[val]]
1015

1116
struct S {
1217
float4 f;

0 commit comments

Comments
 (0)