Add WebGPU->Vulkan and Vulkan->WebGPU flags in spirv-opt (#2496)

Renames the existing flag '--webgpu-mode' to '--vulkan-to-webgpu' for
the Vulkan->WebGPU operation, and adds a new flag '--webgpu-to-vulkan'
for the WebGPU->Vulkan operation.

Currently '--webgpu-to-vulkan' doesn't have any passes associated with
it yet, but further patches will implement them.

Fixes #2495
This commit is contained in:
Ryan Harrison
2019-04-05 15:12:26 -04:00
committed by GitHub
parent a91c7c12b4
commit 331b223fb6
4 changed files with 221 additions and 96 deletions
+9 -4
View File
@@ -101,10 +101,15 @@ class Optimizer {
// from time to time.
Optimizer& RegisterSizePasses();
// Registers passes that have been prescribed for WebGPU environments.
// This sequence of passes is subject to constant review and will change
// from time to time.
Optimizer& RegisterWebGPUPasses();
// Registers passes that have been prescribed for converting from Vulkan to
// WebGPU. This sequence of passes is subject to constant review and will
// change from time to time.
Optimizer& RegisterVulkanToWebGPUPasses();
// Registers passes that have been prescribed for converting from WebGPU to
// Vulkan. This sequence of passes is subject to constant review and will
// change from time to time.
Optimizer& RegisterWebGPUToVulkanPasses();
// Registers passes that attempt to legalize the generated code.
//
+3 -1
View File
@@ -220,7 +220,7 @@ Optimizer& Optimizer::RegisterSizePasses() {
.RegisterPass(CreateAggressiveDCEPass());
}
Optimizer& Optimizer::RegisterWebGPUPasses() {
Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() {
return RegisterPass(CreateStripDebugInfoPass())
.RegisterPass(CreateStripAtomicCounterMemoryPass())
.RegisterPass(CreateGenerateWebGPUInitializersPass())
@@ -230,6 +230,8 @@ Optimizer& Optimizer::RegisterWebGPUPasses() {
.RegisterPass(CreateDeadBranchElimPass());
}
Optimizer& Optimizer::RegisterWebGPUToVulkanPasses() { return *this; }
bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) {
for (const auto& flag : flags) {
if (!RegisterPassFromFlag(flag)) {
+9 -8
View File
@@ -222,9 +222,9 @@ TEST(Optimizer, CanRegisterPassesFromFlags) {
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
}
TEST(Optimizer, WebGPUModeSetsCorrectPasses) {
TEST(Optimizer, VulkanToWebGPUModeSetsCorrectPasses) {
Optimizer opt(SPV_ENV_WEBGPU_0);
opt.RegisterWebGPUPasses();
opt.RegisterVulkanToWebGPUPasses();
std::vector<const char*> pass_names = opt.GetPassNames();
std::vector<std::string> registered_passes;
@@ -246,7 +246,7 @@ TEST(Optimizer, WebGPUModeSetsCorrectPasses) {
EXPECT_EQ(registered_passes[i], expected_passes[i]);
}
struct WebGPUPassCase {
struct VulkanToWebGPUPassCase {
// Input SPIR-V
std::string input;
// Expected result SPIR-V
@@ -255,15 +255,16 @@ struct WebGPUPassCase {
std::string pass;
};
using WebGPUPassTest = PassTest<::testing::TestWithParam<WebGPUPassCase>>;
using VulkanToWebGPUPassTest =
PassTest<::testing::TestWithParam<VulkanToWebGPUPassCase>>;
TEST_P(WebGPUPassTest, Ran) {
TEST_P(VulkanToWebGPUPassTest, Ran) {
SpirvTools tools(SPV_ENV_WEBGPU_0);
std::vector<uint32_t> binary;
tools.Assemble(GetParam().input, &binary);
Optimizer opt(SPV_ENV_WEBGPU_0);
opt.RegisterWebGPUPasses();
opt.RegisterVulkanToWebGPUPasses();
std::vector<uint32_t> optimized;
class ValidatorOptions validator_options;
@@ -277,8 +278,8 @@ TEST_P(WebGPUPassTest, Ran) {
}
INSTANTIATE_TEST_SUITE_P(
Optimizer, WebGPUPassTest,
::testing::ValuesIn(std::vector<WebGPUPassCase>{
Optimizer, VulkanToWebGPUPassTest,
::testing::ValuesIn(std::vector<VulkanToWebGPUPassCase>{
// FlattenDecorations
{// input
"OpCapability Shader\n"
+200 -83
View File
@@ -79,9 +79,15 @@ std::string GetSizePasses() {
return GetListOfPassesAsString(optimizer);
}
std::string GetWebGPUPasses() {
std::string GetVulkanToWebGPUPasses() {
spvtools::Optimizer optimizer(SPV_ENV_WEBGPU_0);
optimizer.RegisterWebGPUPasses();
optimizer.RegisterVulkanToWebGPUPasses();
return GetListOfPassesAsString(optimizer);
}
std::string GetWebGPUToVulkanPasses() {
spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_1);
optimizer.RegisterWebGPUToVulkanPasses();
return GetListOfPassesAsString(optimizer);
}
@@ -99,94 +105,119 @@ standard output.
NOTE: The optimizer is a work in progress.
Options (in lexicographical order):
Options (in lexicographical order):)",
program, program);
printf(R"(
--ccp
Apply the conditional constant propagation transform. This will
propagate constant values throughout the program, and simplify
expressions and conditional jumps with known predicate
values. Performed on entry point call tree functions and
exported functions.
exported functions.)");
printf(R"(
--cfg-cleanup
Cleanup the control flow graph. This will remove any unnecessary
code from the CFG like unreachable code. Performed on entry
point call tree functions and exported functions.
point call tree functions and exported functions.)");
printf(R"(
--combine-access-chains
Combines chained access chains to produce a single instruction
where possible.
where possible.)");
printf(R"(
--compact-ids
Remap result ids to a compact range starting from %%1 and without
any gaps.
any gaps.)");
printf(R"(
--convert-local-access-chains
Convert constant index access chain loads/stores into
equivalent load/stores with inserts and extracts. Performed
on function scope variables referenced only with load, store,
and constant index access chains in entry point call tree
functions.
functions.)");
printf(R"(
--copy-propagate-arrays
Does propagation of memory references when an array is a copy of
another. It will only propagate an array if the source is never
written to, and the only store to the target is the copy.
written to, and the only store to the target is the copy.)");
printf(R"(
--eliminate-common-uniform
Perform load/load elimination for duplicate uniform values.
Converts any constant index access chain uniform loads into
its equivalent load and extract. Some loads will be moved
to facilitate sharing. Performed only on entry point
call tree functions.
call tree functions.)");
printf(R"(
--eliminate-dead-branches
Convert conditional branches with constant condition to the
indicated unconditional brranch. Delete all resulting dead
code. Performed only on entry point call tree functions.
code. Performed only on entry point call tree functions.)");
printf(R"(
--eliminate-dead-code-aggressive
Delete instructions which do not contribute to a function's
output. Performed only on entry point call tree functions.
output. Performed only on entry point call tree functions.)");
printf(R"(
--eliminate-dead-const
Eliminate dead constants.
Eliminate dead constants.)");
printf(R"(
--eliminate-dead-functions
Deletes functions that cannot be reached from entry points or
exported functions.
exported functions.)");
printf(R"(
--eliminate-dead-inserts
Deletes unreferenced inserts into composites, most notably
unused stores to vector components, that are not removed by
aggressive dead code elimination.
aggressive dead code elimination.)");
printf(R"(
--eliminate-dead-variables
Deletes module scope variables that are not referenced.
Deletes module scope variables that are not referenced.)");
printf(R"(
--eliminate-insert-extract
DEPRECATED. This pass has been replaced by the simplification
pass, and that pass will be run instead.
See --simplify-instructions.
See --simplify-instructions.)");
printf(R"(
--eliminate-local-multi-store
Replace stores and loads of function scope variables that are
stored multiple times. Performed on variables referenceed only
with loads and stores. Performed only on entry point call tree
functions.
functions.)");
printf(R"(
--eliminate-local-single-block
Perform single-block store/load and load/load elimination.
Performed only on function scope variables in entry point
call tree functions.
call tree functions.)");
printf(R"(
--eliminate-local-single-store
Replace stores and loads of function scope variables that are
only stored once. Performed on variables referenceed only with
loads and stores. Performed only on entry point call tree
functions.
functions.)");
printf(R"(
--flatten-decorations
Replace decoration groups with repeated OpDecorate and
OpMemberDecorate instructions.
OpMemberDecorate instructions.)");
printf(R"(
--fold-spec-const-op-composite
Fold the spec constants defined by OpSpecConstantOp or
OpSpecConstantComposite instructions to front-end constants
when possible.
when possible.)");
printf(R"(
--freeze-spec-const
Freeze the values of specialization constants to their default
values.
values.)");
printf(R"(
--generate-webgpu-initializers
Adds initial values to OpVariable instructions that are missing
them, due to their storage type requiring them for WebGPU.
them, due to their storage type requiring them for WebGPU.)");
printf(R"(
--if-conversion
Convert if-then-else like assignments into OpSelect.
Convert if-then-else like assignments into OpSelect.)");
printf(R"(
--inline-entry-points-exhaustive
Exhaustively inline all function calls in entry point call tree
functions. Currently does not inline calls to functions with
early return in a loop.
early return in a loop.)");
printf(R"(
--legalize-hlsl
Runs a series of optimizations that attempts to take SPIR-V
generated by an HLSL front-end and generates legal Vulkan SPIR-V.
@@ -194,46 +225,58 @@ Options (in lexicographical order):
%s
Note this does not guarantee legal code. This option passes the
option --relax-logical-pointer to the validator.
option --relax-logical-pointer to the validator.)",
GetLegalizationPasses().c_str());
printf(R"(
--local-redundancy-elimination
Looks for instructions in the same basic block that compute the
same value, and deletes the redundant ones.
same value, and deletes the redundant ones.)");
printf(R"(
--loop-fission
Splits any top level loops in which the register pressure has
exceeded a given threshold. The threshold must follow the use of
this flag and must be a positive integer value.
this flag and must be a positive integer value.)");
printf(R"(
--loop-fusion
Identifies adjacent loops with the same lower and upper bound.
If this is legal, then merge the loops into a single loop.
Includes heuristics to ensure it does not increase number of
registers too much, while reducing the number of loads from
memory. Takes an additional positive integer argument to set
the maximum number of registers.
the maximum number of registers.)");
printf(R"(
--loop-invariant-code-motion
Identifies code in loops that has the same value for every
iteration of the loop, and move it to the loop pre-header.
iteration of the loop, and move it to the loop pre-header.)");
printf(R"(
--loop-unroll
Fully unrolls loops marked with the Unroll flag
Fully unrolls loops marked with the Unroll flag)");
printf(R"(
--loop-unroll-partial
Partially unrolls loops marked with the Unroll flag. Takes an
additional non-0 integer argument to set the unroll factor, or
how many times a loop body should be duplicated
how many times a loop body should be duplicated)");
printf(R"(
--loop-peeling
Execute few first (respectively last) iterations before
(respectively after) the loop if it can elide some branches.
(respectively after) the loop if it can elide some branches.)");
printf(R"(
--loop-peeling-threshold
Takes a non-0 integer argument to set the loop peeling code size
growth threshold. The threshold prevents the loop peeling
from happening if the code size increase created by
the optimization is above the threshold.
the optimization is above the threshold.)");
printf(R"(
--max-id-bound=<n>
Sets the maximum value for the id bound for the moudle. The
default is the minimum value for this limit, 0x3FFFFF. See
section 2.17 of the Spir-V specification.
section 2.17 of the Spir-V specification.)");
printf(R"(
--merge-blocks
Join two blocks into a single block if the second has the
first as its only predecessor. Performed only on entry point
call tree functions.
call tree functions.)");
printf(R"(
--merge-return
Changes functions that have multiple return statements so they
have a single return statement.
@@ -247,17 +290,21 @@ Options (in lexicographical order):
label and an OpBranch to the header, nothing else.
These conditions are guaranteed to be met after running
dead-branch elimination.
dead-branch elimination.)");
printf(R"(
--loop-unswitch
Hoists loop-invariant conditionals out of loops by duplicating
the loop on each branch of the conditional and adjusting each
copy of the loop.
copy of the loop.)");
printf(R"(
-O
Optimize for performance. Apply a sequence of transformations
in an attempt to improve the performance of the generated
code. For this version of the optimizer, this flag is equivalent
to specifying the following optimization code names:
%s
%s)",
GetOptimizationPasses().c_str());
printf(R"(
-Os
Optimize for size. Apply a sequence of transformations in an
attempt to minimize the size of the generated code. For this
@@ -266,7 +313,9 @@ Options (in lexicographical order):
%s
NOTE: The specific transformations done by -O and -Os change
from release to release.
from release to release.)",
GetSizePasses().c_str());
printf(R"(
-Oconfig=<file>
Apply the sequence of transformations indicated in <file>.
This file contains a sequence of strings separated by whitespace
@@ -294,109 +343,148 @@ Options (in lexicographical order):
that position in the command line. For example, the invocation
'spirv-opt --merge-blocks -O ...' applies the transformation
--merge-blocks followed by all the transformations implied by
-O.
-O.)");
printf(R"(
--print-all
Print SPIR-V assembly to standard error output before each pass
and after the last pass.
and after the last pass.)");
printf(R"(
--private-to-local
Change the scope of private variables that are used in a single
function to that function.
function to that function.)");
printf(R"(
--reduce-load-size
Replaces loads of composite objects where not every component is
used by loads of just the elements that are used.
used by loads of just the elements that are used.)");
printf(R"(
--redundancy-elimination
Looks for instructions in the same function that compute the
same value, and deletes the redundant ones.
same value, and deletes the redundant ones.)");
printf(R"(
--relax-struct-store
Allow store from one struct type to a different type with
compatible layout and members. This option is forwarded to the
validator.
validator.)");
printf(R"(
--remove-duplicates
Removes duplicate types, decorations, capabilities and extension
instructions.
instructions.)");
printf(R"(
--replace-invalid-opcode
Replaces instructions whose opcode is valid for shader modules,
but not for the current shader stage. To have an effect, all
entry points must have the same execution model.
entry points must have the same execution model.)");
printf(R"(
--ssa-rewrite
Replace loads and stores to function local variables with
operations on SSA IDs.
operations on SSA IDs.)");
printf(R"(
--scalar-replacement[=<n>]
Replace aggregate function scope variables that are only accessed
via their elements with new function variables representing each
element. <n> is a limit on the size of the aggragates that will
be replaced. 0 means there is no limit. The default value is
100.
100.)");
printf(R"(
--set-spec-const-default-value "<spec id>:<default value> ..."
Set the default values of the specialization constants with
<spec id>:<default value> pairs specified in a double-quoted
string. <spec id>:<default value> pairs must be separated by
blank spaces, and in each pair, spec id and default value must
be separated with colon ':' without any blank spaces in between.
e.g.: --set-spec-const-default-value "1:100 2:400"
e.g.: --set-spec-const-default-value "1:100 2:400")");
printf(R"(
--simplify-instructions
Will simplify all instructions in the function as much as
possible.
possible.)");
printf(R"(
--skip-validation
Will not validate the SPIR-V before optimizing. If the SPIR-V
is invalid, the optimizer may fail or generate incorrect code.
This options should be used rarely, and with caution.
This options should be used rarely, and with caution.)");
printf(R"(
--strength-reduction
Replaces instructions with equivalent and less expensive ones.
Replaces instructions with equivalent and less expensive ones.)");
printf(R"(
--strip-atomic-counter-memory
Removes AtomicCountMemory bit from memory semantics values.
Removes AtomicCountMemory bit from memory semantics values.)");
printf(R"(
--strip-debug
Remove all debug instructions.
Remove all debug instructions.)");
printf(R"(
--strip-reflect
Remove all reflection information. For now, this covers
reflection information defined by SPV_GOOGLE_hlsl_functionality1.
reflection information defined by SPV_GOOGLE_hlsl_functionality1.)");
printf(R"(
--target-env=<env>
Set the target environment. Without this flag the target
enviroment defaults to spv1.3.
<env> must be one of vulkan1.0, vulkan1.1, opencl2.2, spv1.0,
spv1.1, spv1.2, spv1.3, or webgpu0.
spv1.1, spv1.2, spv1.3, or webgpu0.)");
printf(R"(
--time-report
Print the resource utilization of each pass (e.g., CPU time,
RSS) to standard error output. Currently it supports only Unix
systems. This option is the same as -ftime-report in GCC. It
prints CPU/WALL/USR/SYS time (and RSS if possible), but note that
USR/SYS time are returned by getrusage() and can have a small
error.
error.)");
printf(R"(
--upgrade-memory-model
Upgrades the Logical GLSL450 memory model to Logical VulkanKHR.
Transforms memory, image, atomic and barrier operations to conform
to that model's requirements.
to that model's requirements.)");
printf(R"(
--vector-dce
This pass looks for components of vectors that are unused, and
removes them from the vector. Note this would still leave around
lots of dead code that a pass of ADCE will be able to remove.
--webgpu-mode
Turns on the prescribed passes for WebGPU and sets the target
environmet to webgpu0. Other passes may be turned on via
additional flags, but such combinations are not tested.
lots of dead code that a pass of ADCE will be able to remove.)");
printf(R"(
--vulkan-to-webgpu
Turns on the prescribed passes for converting from Vulkan to
WebGPU and sets the target environment to webgpu0. Other passes
may be turned on via additional flags, but such combinations are
not tested.
Using --target-env with this flag is not allowed.
This flag is the equivalent of passing in --target-env=webgpu0
and specifying the following optimization code names:
%s
NOTE: This flag is a WIP and its behaviour is subject to change.
NOTE: This flag is a WIP and its behaviour is subject to change.)",
GetVulkanToWebGPUPasses().c_str());
printf(R"(
--webgpu-to-vulkan
Turns on the prescribed passes for converting from WebGPU to
Vulkan and sets the target environment to vulkan1.1. Other passes
may be turned on via additional flags, but such combinations are
not tested.
Using --target-env with this flag is not allowed.
This flag is the equivalent of passing in --target-env=vulkan1.1
and specifying the following optimization code names:
%s
NOTE: This flag is a WIP and its behaviour is subject to change.)",
GetWebGPUToVulkanPasses().c_str());
printf(R"(
--workaround-1209
Rewrites instructions for which there are known driver bugs to
avoid triggering those bugs.
Current workarounds: Avoid OpUnreachable in loops.
Current workarounds: Avoid OpUnreachable in loops.)");
printf(R"(
--unify-const
Remove the duplicated constants.
Remove the duplicated constants.)");
printf(R"(
--validate-after-all
Validate the module after each pass is performed.
Validate the module after each pass is performed.)");
printf(R"(
-h, --help
Print this help.
Print this help.)");
printf(R"(
--version
Display optimizer version information.
)",
program, program, GetLegalizationPasses().c_str(),
GetOptimizationPasses().c_str(), GetSizePasses().c_str(),
GetWebGPUPasses().c_str());
)");
}
// Reads command-line flags the file specified in |oconfig_flag|. This string
@@ -547,7 +635,8 @@ OptStatus ParseFlags(int argc, const char** argv,
spvtools::OptimizerOptions* optimizer_options) {
std::vector<std::string> pass_flags;
bool target_env_set = false;
bool webgpu_mode_set = false;
bool vulkan_to_webgpu_set = false;
bool webgpu_to_vulkan_set = false;
for (int argi = 1; argi < argc; ++argi) {
const char* cur_arg = argv[argi];
if ('-' == cur_arg[0]) {
@@ -608,10 +697,16 @@ OptStatus ParseFlags(int argc, const char** argv,
max_id_bound);
} else if (0 == strncmp(cur_arg,
"--target-env=", sizeof("--target-env=") - 1)) {
if (webgpu_mode_set) {
if (vulkan_to_webgpu_set) {
spvtools::Error(opt_diagnostic, nullptr, {},
"Cannot use both --webgpu-mode and --target-env at "
"the same time");
"Cannot use both --vulkan-to-webgpu and --target-env "
"at the same time");
return {OPT_STOP, 1};
}
if (webgpu_to_vulkan_set) {
spvtools::Error(opt_diagnostic, nullptr, {},
"Cannot use both --webgpu-to-vulkan and --target-env "
"at the same time");
return {OPT_STOP, 1};
}
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
@@ -623,16 +718,38 @@ OptStatus ParseFlags(int argc, const char** argv,
return {OPT_STOP, 1};
}
optimizer->SetTargetEnv(target_env);
} else if (0 == strcmp(cur_arg, "--webgpu-mode")) {
} else if (0 == strcmp(cur_arg, "--vulkan-to-webgpu")) {
if (target_env_set) {
spvtools::Error(opt_diagnostic, nullptr, {},
"Cannot use both --webgpu-mode and --target-env at "
"the same time");
"Cannot use both --vulkan-to-webgpu and --target-env "
"at the same time");
return {OPT_STOP, 1};
}
if (webgpu_to_vulkan_set) {
spvtools::Error(opt_diagnostic, nullptr, {},
"Cannot use both --vulkan-to-webgpu and "
"--webgpu-to-vulkan at the same time");
return {OPT_STOP, 1};
}
optimizer->SetTargetEnv(SPV_ENV_WEBGPU_0);
optimizer->RegisterWebGPUPasses();
optimizer->RegisterVulkanToWebGPUPasses();
} else if (0 == strcmp(cur_arg, "--webgpu-to-vulkan")) {
if (target_env_set) {
spvtools::Error(opt_diagnostic, nullptr, {},
"Cannot use both --webgpu-to-vulkan and --target-env "
"at the same time");
return {OPT_STOP, 1};
}
if (vulkan_to_webgpu_set) {
spvtools::Error(opt_diagnostic, nullptr, {},
"Cannot use both --webgpu-to-vulkan and "
"--vulkan-to-webgpu at the same time");
return {OPT_STOP, 1};
}
optimizer->SetTargetEnv(SPV_ENV_VULKAN_1_1);
optimizer->RegisterWebGPUToVulkanPasses();
} else if (0 == strcmp(cur_arg, "--validate-after-all")) {
optimizer->SetValidateAfterAll(true);
} else {