SPIRV-Tools/tools/opt/opt.cpp
GregF cc8bad3a5b Add LocalMultiStoreElim pass
A SSA local variable load/store elimination pass.
For every entry point function, eliminate all loads and stores of function
scope variables only referenced with non-access-chain loads and stores.
Eliminate the variables as well.

The presence of access chain references and function calls can inhibit
the above optimization.

Only shader modules with logical addressing are currently processed.
Currently modules with any extensions enabled are not processed. This
is left for future work.

This pass is most effective if preceeded by Inlining and
LocalAccessChainConvert. LocalSingleStoreElim and LocalSingleBlockElim
will reduce the work that this pass has to do.
2017-07-07 17:54:21 -04:00

214 lines
8.0 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-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;
}