mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-20 03:50:05 +00:00
9de4e69856
Create aggressive dead code elimination pass This pass eliminates unused code from functions. In addition, it detects and eliminates code which may have spurious uses but which do not contribute to the output of the function. The most common cause of such code sequences is summations in loops whose result is no longer used due to dead code elimination. This optimization has additional compile time cost over standard dead code elimination. This pass only processes entry point functions. It also only processes shaders with logical addressing. It currently will not process functions with function calls. It currently only supports the GLSL.std.450 extended instruction set. It currently does not support any extensions. This pass will be made more effective by first running passes that remove dead control flow and inlines function calls. This pass can be especially useful after running Local Access Chain Conversion, which tends to cause cycles of dead code to be left after Store/Load elimination passes are completed. These cycles cannot be eliminated with standard dead code elimination. Additionally: This transform uses a whitelist of instructions that it knows do have side effects, (a.k.a. combinators). It assumes other instructions have side effects: it will not remove them, and assumes they have side effects via their ID operands.
216 lines
8.1 KiB
C++
216 lines
8.1 KiB
C++
// Copyright (c) 2016 Google Inc.
|
|
//
|
|
// 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
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// 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.
|
|
|
|
#include <cstring>
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <vector>
|
|
|
|
#include "opt/set_spec_constant_default_value_pass.h"
|
|
#include "spirv-tools/optimizer.hpp"
|
|
|
|
#include "message.h"
|
|
#include "tools/io.h"
|
|
|
|
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.
|
|
--freeze-spec-const
|
|
Freeze the values of specialization constants to their default
|
|
values.
|
|
--eliminate-dead-const
|
|
Eliminate dead constants.
|
|
--fold-spec-const-op-composite
|
|
Fold the spec constants defined by OpSpecConstantOp or
|
|
OpSpecConstantComposite instructions to front-end constants
|
|
when possible.
|
|
--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"
|
|
--unify-const
|
|
Remove the duplicated constants.
|
|
--inline-entry-points-exhaustive
|
|
Exhaustively inline all function calls in entry point functions.
|
|
Currently does not inline calls to functions with multiple
|
|
returns.
|
|
--flatten-decorations
|
|
Replace decoration groups with repeated OpDecorate and
|
|
OpMemberDecorate instructions.
|
|
--compact-ids
|
|
Remap result ids to a compact range starting from %%1 and without
|
|
any gaps.
|
|
-h, --help Print this help.
|
|
--version Display optimizer version information.
|
|
)",
|
|
program, program);
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
const char* in_file = nullptr;
|
|
const char* out_file = nullptr;
|
|
|
|
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2;
|
|
|
|
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;
|
|
});
|
|
|
|
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")) {
|
|
optimizer.RegisterPass(CreateStripDebugInfoPass());
|
|
} else if (0 == strcmp(cur_arg, "--set-spec-const-default-value")) {
|
|
if (++argi < argc) {
|
|
auto spec_ids_vals =
|
|
opt::SetSpecConstantDefaultValuePass::ParseDefaultValuesString(
|
|
argv[argi]);
|
|
if (!spec_ids_vals) {
|
|
fprintf(stderr,
|
|
"error: Invalid argument for "
|
|
"--set-spec-const-default-value: %s\n",
|
|
argv[argi]);
|
|
return 1;
|
|
}
|
|
optimizer.RegisterPass(
|
|
CreateSetSpecConstantDefaultValuePass(std::move(*spec_ids_vals)));
|
|
} else {
|
|
fprintf(
|
|
stderr,
|
|
"error: Expected a string of <spec id>:<default value> pairs.");
|
|
return 1;
|
|
}
|
|
} else if (0 == strcmp(cur_arg, "--freeze-spec-const")) {
|
|
optimizer.RegisterPass(CreateFreezeSpecConstantValuePass());
|
|
} else if (0 == strcmp(cur_arg, "--inline-entry-points-exhaustive")) {
|
|
optimizer.RegisterPass(CreateInlinePass());
|
|
} else if (0 == strcmp(cur_arg, "--convert-local-access-chains")) {
|
|
optimizer.RegisterPass(CreateLocalAccessChainConvertPass());
|
|
} else if (0 == strcmp(cur_arg, "--eliminate-dead-code-aggressive")) {
|
|
optimizer.RegisterPass(CreateAggressiveDCEPass());
|
|
} else if (0 == strcmp(cur_arg, "--eliminate-insert-extract")) {
|
|
optimizer.RegisterPass(CreateInsertExtractElimPass());
|
|
} else if (0 == strcmp(cur_arg, "--eliminate-local-single-block")) {
|
|
optimizer.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
|
|
} else if (0 == strcmp(cur_arg, "--eliminate-local-single-store")) {
|
|
optimizer.RegisterPass(CreateLocalSingleStoreElimPass());
|
|
} else if (0 == strcmp(cur_arg, "--merge-blocks")) {
|
|
optimizer.RegisterPass(CreateBlockMergePass());
|
|
} else if (0 == strcmp(cur_arg, "--eliminate-dead-branches")) {
|
|
optimizer.RegisterPass(CreateDeadBranchElimPass());
|
|
} else if (0 == strcmp(cur_arg, "--eliminate-local-multi-store")) {
|
|
optimizer.RegisterPass(CreateLocalMultiStoreElimPass());
|
|
} else if (0 == strcmp(cur_arg, "--eliminate-dead-const")) {
|
|
optimizer.RegisterPass(CreateEliminateDeadConstantPass());
|
|
} else if (0 == strcmp(cur_arg, "--fold-spec-const-op-composite")) {
|
|
optimizer.RegisterPass(CreateFoldSpecConstantOpAndCompositePass());
|
|
} else if (0 == strcmp(cur_arg, "--unify-const")) {
|
|
optimizer.RegisterPass(CreateUnifyConstantPass());
|
|
} else if (0 == strcmp(cur_arg, "--flatten-decorations")) {
|
|
optimizer.RegisterPass(CreateFlattenDecorationPass());
|
|
} else if (0 == strcmp(cur_arg, "--compact-ids")) {
|
|
optimizer.RegisterPass(CreateCompactIdsPass());
|
|
} 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;
|
|
}
|
|
|
|
std::vector<uint32_t> binary;
|
|
if (!ReadFile<uint32_t>(in_file, "rb", &binary)) return 1;
|
|
|
|
// Let's do validation first.
|
|
spv_context context = spvContextCreate(target_env);
|
|
spv_diagnostic diagnostic = nullptr;
|
|
spv_const_binary_t binary_struct = {binary.data(), binary.size()};
|
|
spv_result_t error = spvValidate(context, &binary_struct, &diagnostic);
|
|
if (error) {
|
|
spvDiagnosticPrint(diagnostic);
|
|
spvDiagnosticDestroy(diagnostic);
|
|
spvContextDestroy(context);
|
|
return error;
|
|
}
|
|
spvDiagnosticDestroy(diagnostic);
|
|
spvContextDestroy(context);
|
|
|
|
// 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);
|
|
|
|
if (!WriteFile<uint32_t>(out_file, "wb", binary.data(), binary.size())) {
|
|
return 1;
|
|
}
|
|
|
|
return ok ? 0 : 1;
|
|
}
|