// 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 #include #include #include #include #include #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] [] -o The SPIR-V binary is read from . If no file is specified, or if is "-", then the binary is read from standard input. if 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 ": ..." Set the default values of the specialization constants with : pairs specified in a double-quoted string. : 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 : 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-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 binary; if (!ReadFile(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(out_file, "wb", binary.data(), binary.size())) { return 1; } return ok ? 0 : 1; }