2016-06-29 20:16:03 +00:00
|
|
|
// Copyright (c) 2016 Google Inc.
|
|
|
|
//
|
2016-09-01 19:33:59 +00:00
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
2016-06-29 20:16:03 +00:00
|
|
|
//
|
2016-09-01 19:33:59 +00:00
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
2016-06-29 20:16:03 +00:00
|
|
|
//
|
2016-09-01 19:33:59 +00:00
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
2016-06-29 20:16:03 +00:00
|
|
|
|
|
|
|
#include <cstring>
|
2017-04-02 06:36:35 +00:00
|
|
|
#include <algorithm>
|
2016-06-29 20:16:03 +00:00
|
|
|
#include <iostream>
|
2016-09-22 15:50:12 +00:00
|
|
|
#include <memory>
|
|
|
|
#include <sstream>
|
2016-06-29 20:16:03 +00:00
|
|
|
#include <vector>
|
|
|
|
|
2017-04-02 06:36:35 +00:00
|
|
|
#include "opt/set_spec_constant_default_value_pass.h"
|
|
|
|
#include "spirv-tools/optimizer.hpp"
|
|
|
|
|
2016-08-20 03:17:44 +00:00
|
|
|
#include "message.h"
|
2016-06-30 18:24:04 +00:00
|
|
|
#include "tools/io.h"
|
2016-06-29 20:16:03 +00:00
|
|
|
|
|
|
|
using namespace spvtools;
|
|
|
|
|
|
|
|
void PrintUsage(const char* program) {
|
|
|
|
printf(
|
|
|
|
R"(%s - Optimize a SPIR-V binary file.
|
|
|
|
|
|
|
|
USAGE: %s [options] [<input>] -o <output>
|
|
|
|
|
|
|
|
The SPIR-V binary is read from <input>. If no file is specified,
|
|
|
|
or if <input> is "-", then the binary is read from standard input.
|
|
|
|
if <output> is "-", then the optimized output is written to
|
|
|
|
standard output.
|
|
|
|
|
|
|
|
NOTE: The optimizer is a work in progress.
|
|
|
|
|
|
|
|
Options:
|
|
|
|
--strip-debug
|
|
|
|
Remove all debug instructions.
|
2016-07-26 16:11:03 +00:00
|
|
|
--freeze-spec-const
|
|
|
|
Freeze the values of specialization constants to their default
|
|
|
|
values.
|
2016-08-26 20:25:40 +00:00
|
|
|
--eliminate-dead-const
|
2016-08-10 20:02:18 +00:00
|
|
|
Eliminate dead constants.
|
2016-08-04 17:24:08 +00:00
|
|
|
--fold-spec-const-op-composite
|
|
|
|
Fold the spec constants defined by OpSpecConstantOp or
|
|
|
|
OpSpecConstantComposite instructions to front-end constants
|
|
|
|
when possible.
|
2016-09-22 15:50:12 +00:00
|
|
|
--set-spec-const-default-value "<spec id>:<default value> ..."
|
|
|
|
Set the default values of the specialization constants with
|
2016-09-22 21:09:34 +00:00
|
|
|
<spec id>:<default value> pairs specified in a double-quoted
|
|
|
|
string. <spec id>:<default value> pairs must be separated by
|
2016-09-22 15:50:12 +00:00
|
|
|
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"
|
2016-08-17 21:19:48 +00:00
|
|
|
--unify-const
|
|
|
|
Remove the duplicated constants.
|
2017-04-01 20:10:16 +00:00
|
|
|
--flatten-decorations
|
|
|
|
Replace decoration groups with repeated OpDecorate and
|
|
|
|
OpMemberDecorate instructions.
|
2017-04-11 19:11:04 +00:00
|
|
|
--compact-ids
|
|
|
|
Remap result ids to a compact range starting from %%1 and without
|
|
|
|
any gaps.
|
2017-08-01 18:55:29 +00:00
|
|
|
--inline-entry-points-exhaustive
|
2017-08-15 23:58:28 +00:00
|
|
|
Exhaustively inline all function calls in entry point call tree
|
|
|
|
functions. Currently does not inline calls to functions with
|
|
|
|
early return in a loop.
|
2017-08-01 18:55:29 +00:00
|
|
|
--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,
|
2017-08-15 23:58:28 +00:00
|
|
|
and constant index access chains in entry point call tree
|
|
|
|
functions.
|
2017-07-03 23:23:04 +00:00
|
|
|
--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
|
2017-08-15 23:58:28 +00:00
|
|
|
call tree functions.
|
2017-08-01 18:55:29 +00:00
|
|
|
--eliminate-local-single-block
|
|
|
|
Perform single-block store/load and load/load elimination.
|
|
|
|
Performed only on function scope variables in entry point
|
2017-08-15 23:58:28 +00:00
|
|
|
call tree functions.
|
2017-08-01 18:55:29 +00:00
|
|
|
--eliminate-local-single-store
|
|
|
|
Replace stores and loads of function scope variables that are
|
|
|
|
only stored once. Performed on variables referenceed only with
|
2017-08-15 23:58:28 +00:00
|
|
|
loads and stores. Performed only on entry point call tree
|
|
|
|
functions.
|
2017-08-01 18:55:29 +00:00
|
|
|
--eliminate-local-multi-store
|
|
|
|
Replace stores and loads of function scope variables that are
|
|
|
|
stored multiple times. Performed on variables referenceed only
|
2017-08-15 23:58:28 +00:00
|
|
|
with loads and stores. Performed only on entry point call tree
|
|
|
|
functions.
|
2017-08-01 18:55:29 +00:00
|
|
|
--eliminate-insert-extract
|
|
|
|
Replace extract from a sequence of inserts with the
|
2017-08-15 23:58:28 +00:00
|
|
|
corresponding value. Performed only on entry point call tree
|
|
|
|
functions.
|
2017-08-01 18:55:29 +00:00
|
|
|
--eliminate-dead-code-aggressive
|
|
|
|
Delete instructions which do not contribute to a function's
|
2017-08-15 23:58:28 +00:00
|
|
|
output. Performed only on entry point call tree functions.
|
2017-08-01 18:55:29 +00:00
|
|
|
--eliminate-dead-branches
|
|
|
|
Convert conditional branches with constant condition to the
|
|
|
|
indicated unconditional brranch. Delete all resulting dead
|
2017-08-15 23:58:28 +00:00
|
|
|
code. Performed only on entry point call tree functions.
|
2017-08-01 18:55:29 +00:00
|
|
|
--merge-blocks
|
|
|
|
Join two blocks into a single block if the second has the
|
|
|
|
first as its only predecessor. Performed only on entry point
|
2017-08-15 23:58:28 +00:00
|
|
|
call tree functions.
|
2017-09-08 16:08:03 +00:00
|
|
|
--strength-reduction
|
|
|
|
Replaces instructions with equivalent and less expensive ones.
|
2017-08-01 18:55:29 +00:00
|
|
|
-h, --help
|
|
|
|
Print this help.
|
|
|
|
--version
|
|
|
|
Display optimizer version information.
|
2016-06-29 20:16:03 +00:00
|
|
|
)",
|
|
|
|
program, program);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
const char* in_file = nullptr;
|
|
|
|
const char* out_file = nullptr;
|
|
|
|
|
2017-03-14 16:43:41 +00:00
|
|
|
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2;
|
2016-06-29 20:16:03 +00:00
|
|
|
|
2017-04-02 06:36:35 +00:00
|
|
|
spvtools::Optimizer optimizer(target_env);
|
|
|
|
optimizer.SetMessageConsumer([](spv_message_level_t level, const char* source,
|
|
|
|
const spv_position_t& position,
|
|
|
|
const char* message) {
|
|
|
|
std::cerr << StringifyMessage(level, source, position, message)
|
|
|
|
<< std::endl;
|
|
|
|
});
|
2016-06-29 20:16:03 +00:00
|
|
|
|
|
|
|
for (int argi = 1; argi < argc; ++argi) {
|
|
|
|
const char* cur_arg = argv[argi];
|
|
|
|
if ('-' == cur_arg[0]) {
|
|
|
|
if (0 == strcmp(cur_arg, "--version")) {
|
|
|
|
printf("%s\n", spvSoftwareVersionDetailsString());
|
|
|
|
return 0;
|
|
|
|
} else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
|
|
|
|
PrintUsage(argv[0]);
|
|
|
|
return 0;
|
|
|
|
} else if (0 == strcmp(cur_arg, "-o")) {
|
|
|
|
if (!out_file && argi + 1 < argc) {
|
|
|
|
out_file = argv[++argi];
|
|
|
|
} else {
|
|
|
|
PrintUsage(argv[0]);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else if (0 == strcmp(cur_arg, "--strip-debug")) {
|
2017-04-02 06:36:35 +00:00
|
|
|
optimizer.RegisterPass(CreateStripDebugInfoPass());
|
2016-09-22 15:50:12 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--set-spec-const-default-value")) {
|
|
|
|
if (++argi < argc) {
|
|
|
|
auto spec_ids_vals =
|
|
|
|
opt::SetSpecConstantDefaultValuePass::ParseDefaultValuesString(
|
|
|
|
argv[argi]);
|
2016-09-22 21:09:34 +00:00
|
|
|
if (!spec_ids_vals) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"error: Invalid argument for "
|
|
|
|
"--set-spec-const-default-value: %s\n",
|
|
|
|
argv[argi]);
|
|
|
|
return 1;
|
|
|
|
}
|
2017-04-02 06:36:35 +00:00
|
|
|
optimizer.RegisterPass(
|
|
|
|
CreateSetSpecConstantDefaultValuePass(std::move(*spec_ids_vals)));
|
2016-09-22 15:50:12 +00:00
|
|
|
} else {
|
2016-09-23 19:13:24 +00:00
|
|
|
fprintf(
|
|
|
|
stderr,
|
|
|
|
"error: Expected a string of <spec id>:<default value> pairs.");
|
2016-09-22 15:50:12 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2016-07-26 16:11:03 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--freeze-spec-const")) {
|
2017-04-02 06:36:35 +00:00
|
|
|
optimizer.RegisterPass(CreateFreezeSpecConstantValuePass());
|
2017-04-26 00:07:35 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--inline-entry-points-exhaustive")) {
|
2017-08-01 23:20:13 +00:00
|
|
|
optimizer.RegisterPass(CreateInlineExhaustivePass());
|
2017-08-15 23:58:28 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--inline-entry-points-opaque")) {
|
|
|
|
optimizer.RegisterPass(CreateInlineOpaquePass());
|
2017-05-12 23:27:21 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--convert-local-access-chains")) {
|
|
|
|
optimizer.RegisterPass(CreateLocalAccessChainConvertPass());
|
2017-06-08 16:37:21 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--eliminate-dead-code-aggressive")) {
|
|
|
|
optimizer.RegisterPass(CreateAggressiveDCEPass());
|
2017-05-26 16:33:11 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--eliminate-insert-extract")) {
|
|
|
|
optimizer.RegisterPass(CreateInsertExtractElimPass());
|
2017-05-18 20:51:55 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--eliminate-local-single-block")) {
|
|
|
|
optimizer.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
|
2017-05-19 23:31:28 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--eliminate-local-single-store")) {
|
|
|
|
optimizer.RegisterPass(CreateLocalSingleStoreElimPass());
|
2017-06-07 21:28:53 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--merge-blocks")) {
|
|
|
|
optimizer.RegisterPass(CreateBlockMergePass());
|
2017-06-02 19:23:20 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--eliminate-dead-branches")) {
|
|
|
|
optimizer.RegisterPass(CreateDeadBranchElimPass());
|
2017-06-16 21:37:31 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--eliminate-local-multi-store")) {
|
|
|
|
optimizer.RegisterPass(CreateLocalMultiStoreElimPass());
|
2017-07-03 23:23:04 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--eliminate-common-uniform")) {
|
|
|
|
optimizer.RegisterPass(CreateCommonUniformElimPass());
|
2016-07-29 15:35:58 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--eliminate-dead-const")) {
|
2017-04-02 06:36:35 +00:00
|
|
|
optimizer.RegisterPass(CreateEliminateDeadConstantPass());
|
2016-08-04 17:24:08 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--fold-spec-const-op-composite")) {
|
2017-04-02 06:36:35 +00:00
|
|
|
optimizer.RegisterPass(CreateFoldSpecConstantOpAndCompositePass());
|
2017-09-08 16:08:03 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--strength-reduction")) {
|
|
|
|
optimizer.RegisterPass(CreateStrengthReductionPass());
|
2016-08-17 21:19:48 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--unify-const")) {
|
2017-04-02 06:36:35 +00:00
|
|
|
optimizer.RegisterPass(CreateUnifyConstantPass());
|
2017-04-01 20:10:16 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--flatten-decorations")) {
|
|
|
|
optimizer.RegisterPass(CreateFlattenDecorationPass());
|
2017-04-11 19:11:04 +00:00
|
|
|
} else if (0 == strcmp(cur_arg, "--compact-ids")) {
|
|
|
|
optimizer.RegisterPass(CreateCompactIdsPass());
|
2016-06-29 20:16:03 +00:00
|
|
|
} else if ('\0' == cur_arg[1]) {
|
|
|
|
// Setting a filename of "-" to indicate stdin.
|
|
|
|
if (!in_file) {
|
|
|
|
in_file = cur_arg;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "error: More than one input file specified\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
PrintUsage(argv[0]);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!in_file) {
|
|
|
|
in_file = cur_arg;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "error: More than one input file specified\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (out_file == nullptr) {
|
|
|
|
fprintf(stderr, "error: -o required\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-04-02 06:36:35 +00:00
|
|
|
std::vector<uint32_t> binary;
|
|
|
|
if (!ReadFile<uint32_t>(in_file, "rb", &binary)) return 1;
|
2016-06-29 20:16:03 +00:00
|
|
|
|
|
|
|
// Let's do validation first.
|
|
|
|
spv_context context = spvContextCreate(target_env);
|
|
|
|
spv_diagnostic diagnostic = nullptr;
|
2017-04-02 06:36:35 +00:00
|
|
|
spv_const_binary_t binary_struct = {binary.data(), binary.size()};
|
|
|
|
spv_result_t error = spvValidate(context, &binary_struct, &diagnostic);
|
2016-06-29 20:16:03 +00:00
|
|
|
if (error) {
|
|
|
|
spvDiagnosticPrint(diagnostic);
|
|
|
|
spvDiagnosticDestroy(diagnostic);
|
|
|
|
spvContextDestroy(context);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
spvDiagnosticDestroy(diagnostic);
|
|
|
|
spvContextDestroy(context);
|
|
|
|
|
2017-04-02 06:36:35 +00:00
|
|
|
// By using the same vector as input and output, we save time in the case
|
|
|
|
// that there was no change.
|
|
|
|
bool ok = optimizer.Run(binary.data(), binary.size(), &binary);
|
2016-06-29 20:16:03 +00:00
|
|
|
|
2017-04-02 06:36:35 +00:00
|
|
|
if (!WriteFile<uint32_t>(out_file, "wb", binary.data(), binary.size())) {
|
2016-06-29 20:16:03 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-04-02 06:36:35 +00:00
|
|
|
return ok ? 0 : 1;
|
2016-06-29 20:16:03 +00:00
|
|
|
}
|