Skip to content

Commit 96fd2bb

Browse files
committed
Add O1experimental SPIR-V fast compile mode
1 parent 07f06e9 commit 96fd2bb

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
@@ -60,6 +60,7 @@ struct SpirvCodeGenOptions {
6060
bool noWarnIgnoredFeatures = false;
6161
bool preserveBindings = false;
6262
bool preserveInterface = false;
63+
bool o1ExperimentalFastCompile = false;
6364
bool useDxLayout = false;
6465
bool useGlLayout = false;
6566
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))
@@ -1098,6 +1102,8 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
10981102
opts.SpirvOptions.disableHLSLIntrinsics =
10991103
Args.hasFlag(OPT_fvk_disable_hlsl_intrinsics, OPT_INVALID, false);
11001104
opts.GenSPIRV = Args.hasFlag(OPT_spirv, OPT_INVALID, false);
1105+
opts.SpirvOptions.o1ExperimentalFastCompile =
1106+
Args.hasFlag(OPT_O1experimental, OPT_INVALID, false);
11011107
opts.SpirvOptions.invertY =
11021108
Args.hasFlag(OPT_fvk_invert_y, OPT_INVALID, false);
11031109
opts.SpirvOptions.invertW =
@@ -1263,7 +1269,8 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
12631269
errors << "-Oconfig should not be specified more than once";
12641270
return 1;
12651271
}
1266-
if (Args.getLastArg(OPT_O0, OPT_O1, OPT_O2, OPT_O3)) {
1272+
if (Args.getLastArg(OPT_O0, OPT_O1, OPT_O1experimental, OPT_O2,
1273+
OPT_O3)) {
12671274
errors << "-Oconfig should not be used together with -O";
12681275
return 1;
12691276
}
@@ -1275,6 +1282,11 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
12751282
opts.SpirvOptions.entrypointName =
12761283
Args.getLastArgValue(OPT_fspv_entrypoint_name_EQ);
12771284

1285+
if (opts.SpirvOptions.o1ExperimentalFastCompile && !opts.GenSPIRV) {
1286+
errors << "-O1experimental requires -spirv";
1287+
return 1;
1288+
}
1289+
12781290
// Check for use of options not implemented in the SPIR-V backend.
12791291
if (Args.hasFlag(OPT_spirv, OPT_INVALID, false) &&
12801292
hasUnsupportedSpirvOption(Args, errors))
@@ -1297,6 +1309,7 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
12971309
Args.hasFlag(OPT_fspv_reflect, OPT_INVALID, false) ||
12981310
Args.hasFlag(OPT_fspv_fix_func_call_arguments, OPT_INVALID, false) ||
12991311
Args.hasFlag(OPT_fspv_print_all, OPT_INVALID, false) ||
1312+
Args.hasFlag(OPT_O1experimental, OPT_INVALID, false) ||
13001313
Args.hasFlag(OPT_Wno_vk_ignored_features, OPT_INVALID, false) ||
13011314
Args.hasFlag(OPT_Wno_vk_emulated_features, OPT_INVALID, false) ||
13021315
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) {
@@ -16651,8 +16667,12 @@ bool SpirvEmitter::spirvToolsOptimize(std::vector<uint32_t> *mod,
1665116667
options.set_max_id_bound(spirvOptions.maxId);
1665216668

1665316669
if (spirvOptions.optConfig.empty()) {
16654-
// Add performance passes.
16655-
optimizer.RegisterPerformancePasses(spirvOptions.preserveInterface);
16670+
if (useSpirvFastCompileProfile()) {
16671+
optimizer.RegisterPerformancePassesFastCompile(
16672+
spirvOptions.preserveInterface);
16673+
} else {
16674+
optimizer.RegisterPerformancePasses(spirvOptions.preserveInterface);
16675+
}
1665616676

1665716677
// Add propagation of volatile semantics passes.
1665816678
optimizer.RegisterPass(spvtools::CreateSpreadVolatileSemanticsPass());
@@ -16672,6 +16692,11 @@ bool SpirvEmitter::spirvToolsOptimize(std::vector<uint32_t> *mod,
1667216692
return optimizer.Run(mod->data(), mod->size(), mod, options);
1667316693
}
1667416694

16695+
bool SpirvEmitter::useSpirvFastCompileProfile() const {
16696+
return spirvOptions.o1ExperimentalFastCompile &&
16697+
spirvOptions.optConfig.empty();
16698+
}
16699+
1667516700
bool SpirvEmitter::spirvToolsLegalize(std::vector<uint32_t> *mod,
1667616701
std::string *messages,
1667716702
const std::vector<DescriptorSetAndBinding>
@@ -16697,15 +16722,19 @@ bool SpirvEmitter::spirvToolsLegalize(std::vector<uint32_t> *mod,
1669716722
optimizer.RegisterPass(
1669816723
spvtools::CreateInterfaceVariableScalarReplacementPass());
1669916724
}
16700-
auto legalizationSsaRewriteMode = spvtools::SSARewriteMode::None;
16701-
if (needsLegalizationLoopUnroll) {
16702-
legalizationSsaRewriteMode = spvtools::SSARewriteMode::All;
16703-
} else if (needsLegalizationSsaRewrite) {
16704-
legalizationSsaRewriteMode = spvtools::SSARewriteMode::OpaqueOnly;
16725+
if (useSpirvFastCompileProfile()) {
16726+
auto legalizationSsaRewriteMode = spvtools::SSARewriteMode::None;
16727+
if (needsLegalizationLoopUnroll) {
16728+
legalizationSsaRewriteMode = spvtools::SSARewriteMode::All;
16729+
} else if (needsLegalizationSsaRewrite) {
16730+
legalizationSsaRewriteMode = spvtools::SSARewriteMode::OpaqueOnly;
16731+
}
16732+
optimizer.RegisterLegalizationPasses(
16733+
spirvOptions.preserveInterface, needsLegalizationLoopUnroll,
16734+
legalizationSsaRewriteMode);
16735+
} else {
16736+
optimizer.RegisterLegalizationPasses(spirvOptions.preserveInterface);
1670516737
}
16706-
optimizer.RegisterLegalizationPasses(
16707-
spirvOptions.preserveInterface, needsLegalizationLoopUnroll,
16708-
legalizationSsaRewriteMode);
1670916738
// Add flattening of resources if needed.
1671016739
if (spirvOptions.flattenResourceArrays) {
1671116740
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)