mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-26 05:10:05 +00:00
b31baff4ee
This pass allows to re-assign offset layout decorations to tightly pack a struct according to its packing rules.
1302 lines
49 KiB
C++
1302 lines
49 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 "spirv-tools/optimizer.hpp"
|
|
|
|
#include <cassert>
|
|
#include <charconv>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "source/opt/build_module.h"
|
|
#include "source/opt/graphics_robust_access_pass.h"
|
|
#include "source/opt/log.h"
|
|
#include "source/opt/pass_manager.h"
|
|
#include "source/opt/passes.h"
|
|
#include "source/spirv_optimizer_options.h"
|
|
#include "source/util/make_unique.h"
|
|
#include "source/util/string_utils.h"
|
|
|
|
namespace spvtools {
|
|
|
|
std::vector<std::string> GetVectorOfStrings(const char** strings,
|
|
const size_t string_count) {
|
|
std::vector<std::string> result;
|
|
for (uint32_t i = 0; i < string_count; i++) {
|
|
result.emplace_back(strings[i]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
struct Optimizer::PassToken::Impl {
|
|
Impl(std::unique_ptr<opt::Pass> p) : pass(std::move(p)) {}
|
|
|
|
std::unique_ptr<opt::Pass> pass; // Internal implementation pass.
|
|
};
|
|
|
|
Optimizer::PassToken::PassToken(
|
|
std::unique_ptr<Optimizer::PassToken::Impl> impl)
|
|
: impl_(std::move(impl)) {}
|
|
|
|
Optimizer::PassToken::PassToken(std::unique_ptr<opt::Pass>&& pass)
|
|
: impl_(MakeUnique<Optimizer::PassToken::Impl>(std::move(pass))) {}
|
|
|
|
Optimizer::PassToken::PassToken(PassToken&& that)
|
|
: impl_(std::move(that.impl_)) {}
|
|
|
|
Optimizer::PassToken& Optimizer::PassToken::operator=(PassToken&& that) {
|
|
impl_ = std::move(that.impl_);
|
|
return *this;
|
|
}
|
|
|
|
Optimizer::PassToken::~PassToken() {}
|
|
|
|
struct Optimizer::Impl {
|
|
explicit Impl(spv_target_env env) : target_env(env), pass_manager() {}
|
|
|
|
spv_target_env target_env; // Target environment.
|
|
opt::PassManager pass_manager; // Internal implementation pass manager.
|
|
std::unordered_set<uint32_t> live_locs; // Arg to debug dead output passes
|
|
};
|
|
|
|
Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {
|
|
assert(env != SPV_ENV_WEBGPU_0);
|
|
}
|
|
|
|
Optimizer::~Optimizer() {}
|
|
|
|
void Optimizer::SetMessageConsumer(MessageConsumer c) {
|
|
// All passes' message consumer needs to be updated.
|
|
for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); ++i) {
|
|
impl_->pass_manager.GetPass(i)->SetMessageConsumer(c);
|
|
}
|
|
impl_->pass_manager.SetMessageConsumer(std::move(c));
|
|
}
|
|
|
|
const MessageConsumer& Optimizer::consumer() const {
|
|
return impl_->pass_manager.consumer();
|
|
}
|
|
|
|
Optimizer& Optimizer::RegisterPass(PassToken&& p) {
|
|
// Change to use the pass manager's consumer.
|
|
p.impl_->pass->SetMessageConsumer(consumer());
|
|
impl_->pass_manager.AddPass(std::move(p.impl_->pass));
|
|
return *this;
|
|
}
|
|
|
|
// The legalization passes take a spir-v shader generated by an HLSL front-end
|
|
// and turn it into a valid vulkan spir-v shader. There are two ways in which
|
|
// the code will be invalid at the start:
|
|
//
|
|
// 1) There will be opaque objects, like images, which will be passed around
|
|
// in intermediate objects. Valid spir-v will have to replace the use of
|
|
// the opaque object with an intermediate object that is the result of the
|
|
// load of the global opaque object.
|
|
//
|
|
// 2) There will be variables that contain pointers to structured or uniform
|
|
// buffers. It be legal, the variables must be eliminated, and the
|
|
// references to the structured buffers must use the result of OpVariable
|
|
// in the Uniform storage class.
|
|
//
|
|
// Optimization in this list must accept shaders with these relaxation of the
|
|
// rules. There is not guarantee that this list of optimizations is able to
|
|
// legalize all inputs, but it is on a best effort basis.
|
|
//
|
|
// The legalization problem is essentially a very general copy propagation
|
|
// problem. The optimization we use are all used to either do copy propagation
|
|
// or enable more copy propagation.
|
|
Optimizer& Optimizer::RegisterLegalizationPasses(bool preserve_interface) {
|
|
return
|
|
// Wrap OpKill instructions so all other code can be inlined.
|
|
RegisterPass(CreateWrapOpKillPass())
|
|
// Remove unreachable block so that merge return works.
|
|
.RegisterPass(CreateDeadBranchElimPass())
|
|
// Merge the returns so we can inline.
|
|
.RegisterPass(CreateMergeReturnPass())
|
|
// Make sure uses and definitions are in the same function.
|
|
.RegisterPass(CreateInlineExhaustivePass())
|
|
// Make private variable function scope
|
|
.RegisterPass(CreateEliminateDeadFunctionsPass())
|
|
.RegisterPass(CreatePrivateToLocalPass())
|
|
// Fix up the storage classes that DXC may have purposely generated
|
|
// incorrectly. All functions are inlined, and a lot of dead code has
|
|
// been removed.
|
|
.RegisterPass(CreateFixStorageClassPass())
|
|
// Propagate the value stored to the loads in very simple cases.
|
|
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
|
|
.RegisterPass(CreateLocalSingleStoreElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
// Split up aggregates so they are easier to deal with.
|
|
.RegisterPass(CreateScalarReplacementPass(0))
|
|
// Remove loads and stores so everything is in intermediate values.
|
|
// Takes care of copy propagation of non-members.
|
|
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
|
|
.RegisterPass(CreateLocalSingleStoreElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
.RegisterPass(CreateLocalMultiStoreElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
// Propagate constants to get as many constant conditions on branches
|
|
// as possible.
|
|
.RegisterPass(CreateCCPPass())
|
|
.RegisterPass(CreateLoopUnrollPass(true))
|
|
.RegisterPass(CreateDeadBranchElimPass())
|
|
// Copy propagate members. Cleans up code sequences generated by
|
|
// scalar replacement. Also important for removing OpPhi nodes.
|
|
.RegisterPass(CreateSimplificationPass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
.RegisterPass(CreateCopyPropagateArraysPass())
|
|
// May need loop unrolling here see
|
|
// https://github.com/Microsoft/DirectXShaderCompiler/pull/930
|
|
// Get rid of unused code that contain traces of illegal code
|
|
// or unused references to unbound external objects
|
|
.RegisterPass(CreateVectorDCEPass())
|
|
.RegisterPass(CreateDeadInsertElimPass())
|
|
.RegisterPass(CreateReduceLoadSizePass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
.RegisterPass(CreateRemoveUnusedInterfaceVariablesPass())
|
|
.RegisterPass(CreateInterpolateFixupPass())
|
|
.RegisterPass(CreateInvocationInterlockPlacementPass())
|
|
.RegisterPass(CreateOpExtInstWithForwardReferenceFixupPass());
|
|
}
|
|
|
|
Optimizer& Optimizer::RegisterLegalizationPasses() {
|
|
return RegisterLegalizationPasses(false);
|
|
}
|
|
|
|
Optimizer& Optimizer::RegisterPerformancePasses(bool preserve_interface) {
|
|
return RegisterPass(CreateWrapOpKillPass())
|
|
.RegisterPass(CreateDeadBranchElimPass())
|
|
.RegisterPass(CreateMergeReturnPass())
|
|
.RegisterPass(CreateInlineExhaustivePass())
|
|
.RegisterPass(CreateEliminateDeadFunctionsPass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
.RegisterPass(CreatePrivateToLocalPass())
|
|
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
|
|
.RegisterPass(CreateLocalSingleStoreElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
.RegisterPass(CreateScalarReplacementPass())
|
|
.RegisterPass(CreateLocalAccessChainConvertPass())
|
|
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
|
|
.RegisterPass(CreateLocalSingleStoreElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
.RegisterPass(CreateLocalMultiStoreElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
.RegisterPass(CreateCCPPass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
.RegisterPass(CreateLoopUnrollPass(true))
|
|
.RegisterPass(CreateDeadBranchElimPass())
|
|
.RegisterPass(CreateRedundancyEliminationPass())
|
|
.RegisterPass(CreateCombineAccessChainsPass())
|
|
.RegisterPass(CreateSimplificationPass())
|
|
.RegisterPass(CreateScalarReplacementPass())
|
|
.RegisterPass(CreateLocalAccessChainConvertPass())
|
|
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
|
|
.RegisterPass(CreateLocalSingleStoreElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
.RegisterPass(CreateSSARewritePass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
.RegisterPass(CreateVectorDCEPass())
|
|
.RegisterPass(CreateDeadInsertElimPass())
|
|
.RegisterPass(CreateDeadBranchElimPass())
|
|
.RegisterPass(CreateSimplificationPass())
|
|
.RegisterPass(CreateIfConversionPass())
|
|
.RegisterPass(CreateCopyPropagateArraysPass())
|
|
.RegisterPass(CreateReduceLoadSizePass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
.RegisterPass(CreateBlockMergePass())
|
|
.RegisterPass(CreateRedundancyEliminationPass())
|
|
.RegisterPass(CreateDeadBranchElimPass())
|
|
.RegisterPass(CreateBlockMergePass())
|
|
.RegisterPass(CreateSimplificationPass());
|
|
}
|
|
|
|
Optimizer& Optimizer::RegisterPerformancePasses() {
|
|
return RegisterPerformancePasses(false);
|
|
}
|
|
|
|
Optimizer& Optimizer::RegisterSizePasses(bool preserve_interface) {
|
|
return RegisterPass(CreateWrapOpKillPass())
|
|
.RegisterPass(CreateDeadBranchElimPass())
|
|
.RegisterPass(CreateMergeReturnPass())
|
|
.RegisterPass(CreateInlineExhaustivePass())
|
|
.RegisterPass(CreateEliminateDeadFunctionsPass())
|
|
.RegisterPass(CreatePrivateToLocalPass())
|
|
.RegisterPass(CreateScalarReplacementPass(0))
|
|
.RegisterPass(CreateLocalMultiStoreElimPass())
|
|
.RegisterPass(CreateCCPPass())
|
|
.RegisterPass(CreateLoopUnrollPass(true))
|
|
.RegisterPass(CreateDeadBranchElimPass())
|
|
.RegisterPass(CreateSimplificationPass())
|
|
.RegisterPass(CreateScalarReplacementPass(0))
|
|
.RegisterPass(CreateLocalSingleStoreElimPass())
|
|
.RegisterPass(CreateIfConversionPass())
|
|
.RegisterPass(CreateSimplificationPass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
.RegisterPass(CreateDeadBranchElimPass())
|
|
.RegisterPass(CreateBlockMergePass())
|
|
.RegisterPass(CreateLocalAccessChainConvertPass())
|
|
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
.RegisterPass(CreateCopyPropagateArraysPass())
|
|
.RegisterPass(CreateVectorDCEPass())
|
|
.RegisterPass(CreateDeadInsertElimPass())
|
|
.RegisterPass(CreateEliminateDeadMembersPass())
|
|
.RegisterPass(CreateLocalSingleStoreElimPass())
|
|
.RegisterPass(CreateBlockMergePass())
|
|
.RegisterPass(CreateLocalMultiStoreElimPass())
|
|
.RegisterPass(CreateRedundancyEliminationPass())
|
|
.RegisterPass(CreateSimplificationPass())
|
|
.RegisterPass(CreateAggressiveDCEPass(preserve_interface))
|
|
.RegisterPass(CreateCFGCleanupPass());
|
|
}
|
|
|
|
Optimizer& Optimizer::RegisterSizePasses() { return RegisterSizePasses(false); }
|
|
|
|
bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) {
|
|
return RegisterPassesFromFlags(flags, false);
|
|
}
|
|
|
|
bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags,
|
|
bool preserve_interface) {
|
|
for (const auto& flag : flags) {
|
|
if (!RegisterPassFromFlag(flag, preserve_interface)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Optimizer::FlagHasValidForm(const std::string& flag) const {
|
|
if (flag == "-O" || flag == "-Os") {
|
|
return true;
|
|
} else if (flag.size() > 2 && flag.substr(0, 2) == "--") {
|
|
return true;
|
|
}
|
|
|
|
Errorf(consumer(), nullptr, {},
|
|
"%s is not a valid flag. Flag passes should have the form "
|
|
"'--pass_name[=pass_args]'. Special flag names also accepted: -O "
|
|
"and -Os.",
|
|
flag.c_str());
|
|
return false;
|
|
}
|
|
|
|
bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
|
|
return RegisterPassFromFlag(flag, false);
|
|
}
|
|
|
|
bool Optimizer::RegisterPassFromFlag(const std::string& flag,
|
|
bool preserve_interface) {
|
|
if (!FlagHasValidForm(flag)) {
|
|
return false;
|
|
}
|
|
|
|
// Split flags of the form --pass_name=pass_args.
|
|
auto p = utils::SplitFlagArgs(flag);
|
|
std::string pass_name = p.first;
|
|
std::string pass_args = p.second;
|
|
|
|
// FIXME(dnovillo): This should be re-factored so that pass names can be
|
|
// automatically checked against Pass::name() and PassToken instances created
|
|
// via a template function. Additionally, class Pass should have a desc()
|
|
// method that describes the pass (so it can be used in --help).
|
|
//
|
|
// Both Pass::name() and Pass::desc() should be static class members so they
|
|
// can be invoked without creating a pass instance.
|
|
if (pass_name == "strip-debug") {
|
|
RegisterPass(CreateStripDebugInfoPass());
|
|
} else if (pass_name == "strip-reflect") {
|
|
RegisterPass(CreateStripReflectInfoPass());
|
|
} else if (pass_name == "strip-nonsemantic") {
|
|
RegisterPass(CreateStripNonSemanticInfoPass());
|
|
} else if (pass_name == "fix-opextinst-opcodes") {
|
|
RegisterPass(CreateOpExtInstWithForwardReferenceFixupPass());
|
|
} else if (pass_name == "set-spec-const-default-value") {
|
|
if (pass_args.size() > 0) {
|
|
auto spec_ids_vals =
|
|
opt::SetSpecConstantDefaultValuePass::ParseDefaultValuesString(
|
|
pass_args.c_str());
|
|
if (!spec_ids_vals) {
|
|
Errorf(consumer(), nullptr, {},
|
|
"Invalid argument for --set-spec-const-default-value: %s",
|
|
pass_args.c_str());
|
|
return false;
|
|
}
|
|
RegisterPass(
|
|
CreateSetSpecConstantDefaultValuePass(std::move(*spec_ids_vals)));
|
|
} else {
|
|
Errorf(consumer(), nullptr, {},
|
|
"Invalid spec constant value string '%s'. Expected a string of "
|
|
"<spec id>:<default value> pairs.",
|
|
pass_args.c_str());
|
|
return false;
|
|
}
|
|
} else if (pass_name == "if-conversion") {
|
|
RegisterPass(CreateIfConversionPass());
|
|
} else if (pass_name == "freeze-spec-const") {
|
|
RegisterPass(CreateFreezeSpecConstantValuePass());
|
|
} else if (pass_name == "inline-entry-points-exhaustive") {
|
|
RegisterPass(CreateInlineExhaustivePass());
|
|
} else if (pass_name == "inline-entry-points-opaque") {
|
|
RegisterPass(CreateInlineOpaquePass());
|
|
} else if (pass_name == "combine-access-chains") {
|
|
RegisterPass(CreateCombineAccessChainsPass());
|
|
} else if (pass_name == "convert-local-access-chains") {
|
|
RegisterPass(CreateLocalAccessChainConvertPass());
|
|
} else if (pass_name == "replace-desc-array-access-using-var-index") {
|
|
RegisterPass(CreateReplaceDescArrayAccessUsingVarIndexPass());
|
|
} else if (pass_name == "spread-volatile-semantics") {
|
|
RegisterPass(CreateSpreadVolatileSemanticsPass());
|
|
} else if (pass_name == "descriptor-scalar-replacement") {
|
|
RegisterPass(CreateDescriptorScalarReplacementPass());
|
|
} else if (pass_name == "descriptor-composite-scalar-replacement") {
|
|
RegisterPass(CreateDescriptorCompositeScalarReplacementPass());
|
|
} else if (pass_name == "descriptor-array-scalar-replacement") {
|
|
RegisterPass(CreateDescriptorArrayScalarReplacementPass());
|
|
} else if (pass_name == "eliminate-dead-code-aggressive") {
|
|
RegisterPass(CreateAggressiveDCEPass(preserve_interface));
|
|
} else if (pass_name == "eliminate-insert-extract") {
|
|
RegisterPass(CreateInsertExtractElimPass());
|
|
} else if (pass_name == "eliminate-local-single-block") {
|
|
RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
|
|
} else if (pass_name == "eliminate-local-single-store") {
|
|
RegisterPass(CreateLocalSingleStoreElimPass());
|
|
} else if (pass_name == "merge-blocks") {
|
|
RegisterPass(CreateBlockMergePass());
|
|
} else if (pass_name == "merge-return") {
|
|
RegisterPass(CreateMergeReturnPass());
|
|
} else if (pass_name == "eliminate-dead-branches") {
|
|
RegisterPass(CreateDeadBranchElimPass());
|
|
} else if (pass_name == "eliminate-dead-functions") {
|
|
RegisterPass(CreateEliminateDeadFunctionsPass());
|
|
} else if (pass_name == "eliminate-local-multi-store") {
|
|
RegisterPass(CreateLocalMultiStoreElimPass());
|
|
} else if (pass_name == "eliminate-dead-const") {
|
|
RegisterPass(CreateEliminateDeadConstantPass());
|
|
} else if (pass_name == "eliminate-dead-inserts") {
|
|
RegisterPass(CreateDeadInsertElimPass());
|
|
} else if (pass_name == "eliminate-dead-variables") {
|
|
RegisterPass(CreateDeadVariableEliminationPass());
|
|
} else if (pass_name == "eliminate-dead-members") {
|
|
RegisterPass(CreateEliminateDeadMembersPass());
|
|
} else if (pass_name == "fold-spec-const-op-composite") {
|
|
RegisterPass(CreateFoldSpecConstantOpAndCompositePass());
|
|
} else if (pass_name == "loop-unswitch") {
|
|
RegisterPass(CreateLoopUnswitchPass());
|
|
} else if (pass_name == "scalar-replacement") {
|
|
if (pass_args.size() == 0) {
|
|
RegisterPass(CreateScalarReplacementPass());
|
|
} else {
|
|
int limit = -1;
|
|
if (pass_args.find_first_not_of("0123456789") == std::string::npos) {
|
|
limit = atoi(pass_args.c_str());
|
|
}
|
|
|
|
if (limit >= 0) {
|
|
RegisterPass(CreateScalarReplacementPass(limit));
|
|
} else {
|
|
Error(consumer(), nullptr, {},
|
|
"--scalar-replacement must have no arguments or a non-negative "
|
|
"integer argument");
|
|
return false;
|
|
}
|
|
}
|
|
} else if (pass_name == "strength-reduction") {
|
|
RegisterPass(CreateStrengthReductionPass());
|
|
} else if (pass_name == "unify-const") {
|
|
RegisterPass(CreateUnifyConstantPass());
|
|
} else if (pass_name == "flatten-decorations") {
|
|
RegisterPass(CreateFlattenDecorationPass());
|
|
} else if (pass_name == "compact-ids") {
|
|
RegisterPass(CreateCompactIdsPass());
|
|
} else if (pass_name == "cfg-cleanup") {
|
|
RegisterPass(CreateCFGCleanupPass());
|
|
} else if (pass_name == "local-redundancy-elimination") {
|
|
RegisterPass(CreateLocalRedundancyEliminationPass());
|
|
} else if (pass_name == "loop-invariant-code-motion") {
|
|
RegisterPass(CreateLoopInvariantCodeMotionPass());
|
|
} else if (pass_name == "reduce-load-size") {
|
|
if (pass_args.size() == 0) {
|
|
RegisterPass(CreateReduceLoadSizePass());
|
|
} else {
|
|
double load_replacement_threshold = 0.9;
|
|
if (pass_args.find_first_not_of(".0123456789") == std::string::npos) {
|
|
load_replacement_threshold = atof(pass_args.c_str());
|
|
}
|
|
|
|
if (load_replacement_threshold >= 0) {
|
|
RegisterPass(CreateReduceLoadSizePass(load_replacement_threshold));
|
|
} else {
|
|
Error(consumer(), nullptr, {},
|
|
"--reduce-load-size must have no arguments or a non-negative "
|
|
"double argument");
|
|
return false;
|
|
}
|
|
}
|
|
} else if (pass_name == "redundancy-elimination") {
|
|
RegisterPass(CreateRedundancyEliminationPass());
|
|
} else if (pass_name == "private-to-local") {
|
|
RegisterPass(CreatePrivateToLocalPass());
|
|
} else if (pass_name == "remove-duplicates") {
|
|
RegisterPass(CreateRemoveDuplicatesPass());
|
|
} else if (pass_name == "workaround-1209") {
|
|
RegisterPass(CreateWorkaround1209Pass());
|
|
} else if (pass_name == "replace-invalid-opcode") {
|
|
RegisterPass(CreateReplaceInvalidOpcodePass());
|
|
} else if (pass_name == "convert-relaxed-to-half") {
|
|
RegisterPass(CreateConvertRelaxedToHalfPass());
|
|
} else if (pass_name == "relax-float-ops") {
|
|
RegisterPass(CreateRelaxFloatOpsPass());
|
|
} else if (pass_name == "inst-debug-printf") {
|
|
// This private option is not for user consumption.
|
|
// It is here to assist in debugging and fixing the debug printf
|
|
// instrumentation pass.
|
|
// For users who wish to utilize debug printf, see the white paper at
|
|
// https://www.lunarg.com/wp-content/uploads/2021/08/Using-Debug-Printf-02August2021.pdf
|
|
RegisterPass(CreateInstDebugPrintfPass(7, 23));
|
|
} else if (pass_name == "simplify-instructions") {
|
|
RegisterPass(CreateSimplificationPass());
|
|
} else if (pass_name == "ssa-rewrite") {
|
|
RegisterPass(CreateSSARewritePass());
|
|
} else if (pass_name == "copy-propagate-arrays") {
|
|
RegisterPass(CreateCopyPropagateArraysPass());
|
|
} else if (pass_name == "loop-fission") {
|
|
int register_threshold_to_split =
|
|
(pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
|
|
if (register_threshold_to_split > 0) {
|
|
RegisterPass(CreateLoopFissionPass(
|
|
static_cast<size_t>(register_threshold_to_split)));
|
|
} else {
|
|
Error(consumer(), nullptr, {},
|
|
"--loop-fission must have a positive integer argument");
|
|
return false;
|
|
}
|
|
} else if (pass_name == "loop-fusion") {
|
|
int max_registers_per_loop =
|
|
(pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
|
|
if (max_registers_per_loop > 0) {
|
|
RegisterPass(
|
|
CreateLoopFusionPass(static_cast<size_t>(max_registers_per_loop)));
|
|
} else {
|
|
Error(consumer(), nullptr, {},
|
|
"--loop-fusion must have a positive integer argument");
|
|
return false;
|
|
}
|
|
} else if (pass_name == "loop-unroll") {
|
|
RegisterPass(CreateLoopUnrollPass(true));
|
|
} else if (pass_name == "upgrade-memory-model") {
|
|
RegisterPass(CreateUpgradeMemoryModelPass());
|
|
} else if (pass_name == "vector-dce") {
|
|
RegisterPass(CreateVectorDCEPass());
|
|
} else if (pass_name == "loop-unroll-partial") {
|
|
int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
|
|
if (factor > 0) {
|
|
RegisterPass(CreateLoopUnrollPass(false, factor));
|
|
} else {
|
|
Error(consumer(), nullptr, {},
|
|
"--loop-unroll-partial must have a positive integer argument");
|
|
return false;
|
|
}
|
|
} else if (pass_name == "loop-peeling") {
|
|
RegisterPass(CreateLoopPeelingPass());
|
|
} else if (pass_name == "loop-peeling-threshold") {
|
|
int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
|
|
if (factor > 0) {
|
|
opt::LoopPeelingPass::SetLoopPeelingThreshold(factor);
|
|
} else {
|
|
Error(consumer(), nullptr, {},
|
|
"--loop-peeling-threshold must have a positive integer argument");
|
|
return false;
|
|
}
|
|
} else if (pass_name == "ccp") {
|
|
RegisterPass(CreateCCPPass());
|
|
} else if (pass_name == "code-sink") {
|
|
RegisterPass(CreateCodeSinkingPass());
|
|
} else if (pass_name == "fix-storage-class") {
|
|
RegisterPass(CreateFixStorageClassPass());
|
|
} else if (pass_name == "O") {
|
|
RegisterPerformancePasses(preserve_interface);
|
|
} else if (pass_name == "Os") {
|
|
RegisterSizePasses(preserve_interface);
|
|
} else if (pass_name == "legalize-hlsl") {
|
|
RegisterLegalizationPasses(preserve_interface);
|
|
} else if (pass_name == "remove-unused-interface-variables") {
|
|
RegisterPass(CreateRemoveUnusedInterfaceVariablesPass());
|
|
} else if (pass_name == "graphics-robust-access") {
|
|
RegisterPass(CreateGraphicsRobustAccessPass());
|
|
} else if (pass_name == "wrap-opkill") {
|
|
RegisterPass(CreateWrapOpKillPass());
|
|
} else if (pass_name == "amd-ext-to-khr") {
|
|
RegisterPass(CreateAmdExtToKhrPass());
|
|
} else if (pass_name == "interpolate-fixup") {
|
|
RegisterPass(CreateInterpolateFixupPass());
|
|
} else if (pass_name == "remove-dont-inline") {
|
|
RegisterPass(CreateRemoveDontInlinePass());
|
|
} else if (pass_name == "eliminate-dead-input-components") {
|
|
RegisterPass(CreateEliminateDeadInputComponentsSafePass());
|
|
} else if (pass_name == "fix-func-call-param") {
|
|
RegisterPass(CreateFixFuncCallArgumentsPass());
|
|
} else if (pass_name == "convert-to-sampled-image") {
|
|
if (pass_args.size() > 0) {
|
|
auto descriptor_set_binding_pairs =
|
|
opt::ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString(
|
|
pass_args.c_str());
|
|
if (!descriptor_set_binding_pairs) {
|
|
Errorf(consumer(), nullptr, {},
|
|
"Invalid argument for --convert-to-sampled-image: %s",
|
|
pass_args.c_str());
|
|
return false;
|
|
}
|
|
RegisterPass(CreateConvertToSampledImagePass(
|
|
std::move(*descriptor_set_binding_pairs)));
|
|
} else {
|
|
Errorf(consumer(), nullptr, {},
|
|
"Invalid pairs of descriptor set and binding '%s'. Expected a "
|
|
"string of <descriptor set>:<binding> pairs.",
|
|
pass_args.c_str());
|
|
return false;
|
|
}
|
|
} else if (pass_name == "struct-packing") {
|
|
if (pass_args.size() == 0) {
|
|
Error(consumer(), nullptr, {},
|
|
"--struct-packing requires a name:rule argument.");
|
|
return false;
|
|
}
|
|
|
|
auto separator_pos = pass_args.find(':');
|
|
if (separator_pos == std::string::npos || separator_pos == 0 ||
|
|
separator_pos + 1 == pass_args.size()) {
|
|
Errorf(consumer(), nullptr, {},
|
|
"Invalid argument for --struct-packing: %s", pass_args.c_str());
|
|
return false;
|
|
}
|
|
|
|
const std::string struct_name = pass_args.substr(0, separator_pos);
|
|
const std::string rule_name = pass_args.substr(separator_pos + 1);
|
|
|
|
RegisterPass(
|
|
CreateStructPackingPass(struct_name.c_str(), rule_name.c_str()));
|
|
} else if (pass_name == "switch-descriptorset") {
|
|
if (pass_args.size() == 0) {
|
|
Error(consumer(), nullptr, {},
|
|
"--switch-descriptorset requires a from:to argument.");
|
|
return false;
|
|
}
|
|
uint32_t from_set = 0, to_set = 0;
|
|
const char* start = pass_args.data();
|
|
const char* end = pass_args.data() + pass_args.size();
|
|
|
|
auto result = std::from_chars(start, end, from_set);
|
|
if (result.ec != std::errc()) {
|
|
Errorf(consumer(), nullptr, {},
|
|
"Invalid argument for --switch-descriptorset: %s",
|
|
pass_args.c_str());
|
|
return false;
|
|
}
|
|
start = result.ptr;
|
|
if (start[0] != ':') {
|
|
Errorf(consumer(), nullptr, {},
|
|
"Invalid argument for --switch-descriptorset: %s",
|
|
pass_args.c_str());
|
|
return false;
|
|
}
|
|
start++;
|
|
result = std::from_chars(start, end, to_set);
|
|
if (result.ec != std::errc() || result.ptr != end) {
|
|
Errorf(consumer(), nullptr, {},
|
|
"Invalid argument for --switch-descriptorset: %s",
|
|
pass_args.c_str());
|
|
return false;
|
|
}
|
|
RegisterPass(CreateSwitchDescriptorSetPass(from_set, to_set));
|
|
} else if (pass_name == "modify-maximal-reconvergence") {
|
|
if (pass_args.size() == 0) {
|
|
Error(consumer(), nullptr, {},
|
|
"--modify-maximal-reconvergence requires an argument");
|
|
return false;
|
|
}
|
|
if (pass_args == "add") {
|
|
RegisterPass(CreateModifyMaximalReconvergencePass(true));
|
|
} else if (pass_args == "remove") {
|
|
RegisterPass(CreateModifyMaximalReconvergencePass(false));
|
|
} else {
|
|
Errorf(consumer(), nullptr, {},
|
|
"Invalid argument for --modify-maximal-reconvergence: %s (must be "
|
|
"'add' or 'remove')",
|
|
pass_args.c_str());
|
|
return false;
|
|
}
|
|
} else if (pass_name == "trim-capabilities") {
|
|
RegisterPass(CreateTrimCapabilitiesPass());
|
|
} else {
|
|
Errorf(consumer(), nullptr, {},
|
|
"Unknown flag '--%s'. Use --help for a list of valid flags",
|
|
pass_name.c_str());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Optimizer::SetTargetEnv(const spv_target_env env) {
|
|
impl_->target_env = env;
|
|
}
|
|
|
|
bool Optimizer::Run(const uint32_t* original_binary,
|
|
const size_t original_binary_size,
|
|
std::vector<uint32_t>* optimized_binary) const {
|
|
return Run(original_binary, original_binary_size, optimized_binary,
|
|
OptimizerOptions());
|
|
}
|
|
|
|
bool Optimizer::Run(const uint32_t* original_binary,
|
|
const size_t original_binary_size,
|
|
std::vector<uint32_t>* optimized_binary,
|
|
const ValidatorOptions& validator_options,
|
|
bool skip_validation) const {
|
|
OptimizerOptions opt_options;
|
|
opt_options.set_run_validator(!skip_validation);
|
|
opt_options.set_validator_options(validator_options);
|
|
return Run(original_binary, original_binary_size, optimized_binary,
|
|
opt_options);
|
|
}
|
|
|
|
bool Optimizer::Run(const uint32_t* original_binary,
|
|
const size_t original_binary_size,
|
|
std::vector<uint32_t>* optimized_binary,
|
|
const spv_optimizer_options opt_options) const {
|
|
spvtools::SpirvTools tools(impl_->target_env);
|
|
tools.SetMessageConsumer(impl_->pass_manager.consumer());
|
|
if (opt_options->run_validator_ &&
|
|
!tools.Validate(original_binary, original_binary_size,
|
|
&opt_options->val_options_)) {
|
|
return false;
|
|
}
|
|
|
|
std::unique_ptr<opt::IRContext> context = BuildModule(
|
|
impl_->target_env, consumer(), original_binary, original_binary_size);
|
|
if (context == nullptr) return false;
|
|
|
|
context->set_max_id_bound(opt_options->max_id_bound_);
|
|
context->set_preserve_bindings(opt_options->preserve_bindings_);
|
|
context->set_preserve_spec_constants(opt_options->preserve_spec_constants_);
|
|
|
|
impl_->pass_manager.SetValidatorOptions(&opt_options->val_options_);
|
|
impl_->pass_manager.SetTargetEnv(impl_->target_env);
|
|
auto status = impl_->pass_manager.Run(context.get());
|
|
|
|
if (status == opt::Pass::Status::Failure) {
|
|
return false;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
// We do not keep the result id of DebugScope in struct DebugScope.
|
|
// Instead, we assign random ids for them, which results in integrity
|
|
// check failures. In addition, propagating the OpLine/OpNoLine to preserve
|
|
// the debug information through transformations results in integrity
|
|
// check failures. We want to skip the integrity check when the module
|
|
// contains DebugScope or OpLine/OpNoLine instructions.
|
|
if (status == opt::Pass::Status::SuccessWithoutChange &&
|
|
!context->module()->ContainsDebugInfo()) {
|
|
std::vector<uint32_t> optimized_binary_with_nop;
|
|
context->module()->ToBinary(&optimized_binary_with_nop,
|
|
/* skip_nop = */ false);
|
|
assert(optimized_binary_with_nop.size() == original_binary_size &&
|
|
"Binary size unexpectedly changed despite the optimizer saying "
|
|
"there was no change");
|
|
|
|
// Compare the magic number to make sure the binaries were encoded in the
|
|
// endianness. If not, the contents of the binaries will be different, so
|
|
// do not check the contents.
|
|
if (optimized_binary_with_nop[0] == original_binary[0]) {
|
|
assert(memcmp(optimized_binary_with_nop.data(), original_binary,
|
|
original_binary_size) == 0 &&
|
|
"Binary content unexpectedly changed despite the optimizer saying "
|
|
"there was no change");
|
|
}
|
|
}
|
|
#endif // !NDEBUG
|
|
|
|
// Note that |original_binary| and |optimized_binary| may share the same
|
|
// buffer and the below will invalidate |original_binary|.
|
|
optimized_binary->clear();
|
|
context->module()->ToBinary(optimized_binary, /* skip_nop = */ true);
|
|
|
|
return true;
|
|
}
|
|
|
|
Optimizer& Optimizer::SetPrintAll(std::ostream* out) {
|
|
impl_->pass_manager.SetPrintAll(out);
|
|
return *this;
|
|
}
|
|
|
|
Optimizer& Optimizer::SetTimeReport(std::ostream* out) {
|
|
impl_->pass_manager.SetTimeReport(out);
|
|
return *this;
|
|
}
|
|
|
|
Optimizer& Optimizer::SetValidateAfterAll(bool validate) {
|
|
impl_->pass_manager.SetValidateAfterAll(validate);
|
|
return *this;
|
|
}
|
|
|
|
Optimizer::PassToken CreateNullPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateStripDebugInfoPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::StripDebugInfoPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateStripReflectInfoPass() {
|
|
return CreateStripNonSemanticInfoPass();
|
|
}
|
|
|
|
Optimizer::PassToken CreateStripNonSemanticInfoPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::StripNonSemanticInfoPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateEliminateDeadFunctionsPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::EliminateDeadFunctionsPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateEliminateDeadMembersPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::EliminateDeadMembersPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
|
|
const std::unordered_map<uint32_t, std::string>& id_value_map) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
|
|
}
|
|
|
|
Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
|
|
const std::unordered_map<uint32_t, std::vector<uint32_t>>& id_value_map) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
|
|
}
|
|
|
|
Optimizer::PassToken CreateFlattenDecorationPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::FlattenDecorationPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateFreezeSpecConstantValuePass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::FreezeSpecConstantValuePass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::FoldSpecConstantOpAndCompositePass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateUnifyConstantPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::UnifyConstantPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateEliminateDeadConstantPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::EliminateDeadConstantPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateDeadVariableEliminationPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::DeadVariableElimination>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateStrengthReductionPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::StrengthReductionPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateBlockMergePass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::BlockMergePass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateInlineExhaustivePass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::InlineExhaustivePass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateInlineOpaquePass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::InlineOpaquePass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateLocalAccessChainConvertPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::LocalAccessChainConvertPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::LocalSingleBlockLoadStoreElimPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateLocalSingleStoreElimPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::LocalSingleStoreElimPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateInsertExtractElimPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::SimplificationPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateDeadInsertElimPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::DeadInsertElimPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateDeadBranchElimPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::DeadBranchElimPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateLocalMultiStoreElimPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::SSARewritePass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateAggressiveDCEPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::AggressiveDCEPass>(false, false));
|
|
}
|
|
|
|
Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::AggressiveDCEPass>(preserve_interface, false));
|
|
}
|
|
|
|
Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface,
|
|
bool remove_outputs) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::AggressiveDCEPass>(preserve_interface, remove_outputs));
|
|
}
|
|
|
|
Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::RemoveUnusedInterfaceVariablesPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreatePropagateLineInfoPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::EmptyPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateRedundantLineInfoElimPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::EmptyPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateCompactIdsPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::CompactIdsPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateMergeReturnPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::MergeReturnPass>());
|
|
}
|
|
|
|
std::vector<const char*> Optimizer::GetPassNames() const {
|
|
std::vector<const char*> v;
|
|
for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) {
|
|
v.push_back(impl_->pass_manager.GetPass(i)->name());
|
|
}
|
|
return v;
|
|
}
|
|
|
|
Optimizer::PassToken CreateCFGCleanupPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::CFGCleanupPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateLocalRedundancyEliminationPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::LocalRedundancyEliminationPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateLoopFissionPass(size_t threshold) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::LoopFissionPass>(threshold));
|
|
}
|
|
|
|
Optimizer::PassToken CreateLoopFusionPass(size_t max_registers_per_loop) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::LoopFusionPass>(max_registers_per_loop));
|
|
}
|
|
|
|
Optimizer::PassToken CreateLoopInvariantCodeMotionPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::LICMPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateLoopPeelingPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::LoopPeelingPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateLoopUnswitchPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::LoopUnswitchPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateRedundancyEliminationPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::RedundancyEliminationPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateRemoveDuplicatesPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::RemoveDuplicatesPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::ScalarReplacementPass>(size_limit));
|
|
}
|
|
|
|
Optimizer::PassToken CreatePrivateToLocalPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::PrivateToLocalPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateCCPPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::CCPPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateWorkaround1209Pass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::Workaround1209>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateIfConversionPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::IfConversion>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateReplaceInvalidOpcodePass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::ReplaceInvalidOpcodePass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateSimplificationPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::SimplificationPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateLoopUnrollPass(bool fully_unroll, int factor) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::LoopUnroller>(fully_unroll, factor));
|
|
}
|
|
|
|
Optimizer::PassToken CreateSSARewritePass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::SSARewritePass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateCopyPropagateArraysPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::CopyPropagateArrays>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateVectorDCEPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::VectorDCE>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateReduceLoadSizePass(
|
|
double load_replacement_threshold) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::ReduceLoadSize>(load_replacement_threshold));
|
|
}
|
|
|
|
Optimizer::PassToken CreateCombineAccessChainsPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::CombineAccessChains>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateUpgradeMemoryModelPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::UpgradeMemoryModel>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
|
|
uint32_t shader_id) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::InstDebugPrintfPass>(desc_set, shader_id));
|
|
}
|
|
|
|
Optimizer::PassToken CreateConvertRelaxedToHalfPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::ConvertToHalfPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateRelaxFloatOpsPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::RelaxFloatOpsPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateCodeSinkingPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::CodeSinkingPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateFixStorageClassPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::FixStorageClass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateGraphicsRobustAccessPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::GraphicsRobustAccessPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::ReplaceDescArrayAccessUsingVarIndex>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateSpreadVolatileSemanticsPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::SpreadVolatileSemantics>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::DescriptorScalarReplacement>(
|
|
/* flatten_composites= */ true, /* flatten_arrays= */ true));
|
|
}
|
|
|
|
Optimizer::PassToken CreateDescriptorCompositeScalarReplacementPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::DescriptorScalarReplacement>(
|
|
/* flatten_composites= */ true, /* flatten_arrays= */ false));
|
|
}
|
|
|
|
Optimizer::PassToken CreateDescriptorArrayScalarReplacementPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::DescriptorScalarReplacement>(
|
|
/* flatten_composites= */ false, /* flatten_arrays= */ true));
|
|
}
|
|
|
|
Optimizer::PassToken CreateWrapOpKillPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::WrapOpKill>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateAmdExtToKhrPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::AmdExtensionToKhrPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateInterpolateFixupPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::InterpFixupPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateEliminateDeadInputComponentsPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Input,
|
|
/* safe_mode */ false));
|
|
}
|
|
|
|
Optimizer::PassToken CreateEliminateDeadOutputComponentsPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Output,
|
|
/* safe_mode */ false));
|
|
}
|
|
|
|
Optimizer::PassToken CreateEliminateDeadInputComponentsSafePass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Input,
|
|
/* safe_mode */ true));
|
|
}
|
|
|
|
Optimizer::PassToken CreateAnalyzeLiveInputPass(
|
|
std::unordered_set<uint32_t>* live_locs,
|
|
std::unordered_set<uint32_t>* live_builtins) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::AnalyzeLiveInputPass>(live_locs, live_builtins));
|
|
}
|
|
|
|
Optimizer::PassToken CreateEliminateDeadOutputStoresPass(
|
|
std::unordered_set<uint32_t>* live_locs,
|
|
std::unordered_set<uint32_t>* live_builtins) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::EliminateDeadOutputStoresPass>(live_locs, live_builtins));
|
|
}
|
|
|
|
Optimizer::PassToken CreateConvertToSampledImagePass(
|
|
const std::vector<opt::DescriptorSetAndBinding>&
|
|
descriptor_set_binding_pairs) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::ConvertToSampledImagePass>(descriptor_set_binding_pairs));
|
|
}
|
|
|
|
Optimizer::PassToken CreateInterfaceVariableScalarReplacementPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::InterfaceVariableScalarReplacement>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateRemoveDontInlinePass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::RemoveDontInline>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateFixFuncCallArgumentsPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::FixFuncCallArgumentsPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateTrimCapabilitiesPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::TrimCapabilitiesPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateStructPackingPass(const char* structToPack,
|
|
const char* packingRule) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::StructPackingPass>(
|
|
structToPack,
|
|
opt::StructPackingPass::ParsePackingRuleFromString(packingRule)));
|
|
}
|
|
|
|
Optimizer::PassToken CreateSwitchDescriptorSetPass(uint32_t from, uint32_t to) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::SwitchDescriptorSetPass>(from, to));
|
|
}
|
|
|
|
Optimizer::PassToken CreateInvocationInterlockPlacementPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::InvocationInterlockPlacementPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateModifyMaximalReconvergencePass(bool add) {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::ModifyMaximalReconvergence>(add));
|
|
}
|
|
|
|
Optimizer::PassToken CreateOpExtInstWithForwardReferenceFixupPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::OpExtInstWithForwardReferenceFixupPass>());
|
|
}
|
|
|
|
} // namespace spvtools
|
|
|
|
extern "C" {
|
|
|
|
SPIRV_TOOLS_EXPORT spv_optimizer_t* spvOptimizerCreate(spv_target_env env) {
|
|
return reinterpret_cast<spv_optimizer_t*>(new spvtools::Optimizer(env));
|
|
}
|
|
|
|
SPIRV_TOOLS_EXPORT void spvOptimizerDestroy(spv_optimizer_t* optimizer) {
|
|
delete reinterpret_cast<spvtools::Optimizer*>(optimizer);
|
|
}
|
|
|
|
SPIRV_TOOLS_EXPORT void spvOptimizerSetMessageConsumer(
|
|
spv_optimizer_t* optimizer, spv_message_consumer consumer) {
|
|
reinterpret_cast<spvtools::Optimizer*>(optimizer)->
|
|
SetMessageConsumer(
|
|
[consumer](spv_message_level_t level, const char* source,
|
|
const spv_position_t& position, const char* message) {
|
|
return consumer(level, source, &position, message);
|
|
});
|
|
}
|
|
|
|
SPIRV_TOOLS_EXPORT void spvOptimizerRegisterLegalizationPasses(
|
|
spv_optimizer_t* optimizer) {
|
|
reinterpret_cast<spvtools::Optimizer*>(optimizer)->
|
|
RegisterLegalizationPasses();
|
|
}
|
|
|
|
SPIRV_TOOLS_EXPORT void spvOptimizerRegisterPerformancePasses(
|
|
spv_optimizer_t* optimizer) {
|
|
reinterpret_cast<spvtools::Optimizer*>(optimizer)->
|
|
RegisterPerformancePasses();
|
|
}
|
|
|
|
SPIRV_TOOLS_EXPORT void spvOptimizerRegisterSizePasses(
|
|
spv_optimizer_t* optimizer) {
|
|
reinterpret_cast<spvtools::Optimizer*>(optimizer)->RegisterSizePasses();
|
|
}
|
|
|
|
SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassFromFlag(
|
|
spv_optimizer_t* optimizer, const char* flag)
|
|
{
|
|
return reinterpret_cast<spvtools::Optimizer*>(optimizer)->
|
|
RegisterPassFromFlag(flag);
|
|
}
|
|
|
|
SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassesFromFlags(
|
|
spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) {
|
|
std::vector<std::string> opt_flags =
|
|
spvtools::GetVectorOfStrings(flags, flag_count);
|
|
return reinterpret_cast<spvtools::Optimizer*>(optimizer)
|
|
->RegisterPassesFromFlags(opt_flags, false);
|
|
}
|
|
|
|
SPIRV_TOOLS_EXPORT bool
|
|
spvOptimizerRegisterPassesFromFlagsWhilePreservingTheInterface(
|
|
spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) {
|
|
std::vector<std::string> opt_flags =
|
|
spvtools::GetVectorOfStrings(flags, flag_count);
|
|
return reinterpret_cast<spvtools::Optimizer*>(optimizer)
|
|
->RegisterPassesFromFlags(opt_flags, true);
|
|
}
|
|
|
|
SPIRV_TOOLS_EXPORT
|
|
spv_result_t spvOptimizerRun(spv_optimizer_t* optimizer,
|
|
const uint32_t* binary,
|
|
const size_t word_count,
|
|
spv_binary* optimized_binary,
|
|
const spv_optimizer_options options) {
|
|
std::vector<uint32_t> optimized;
|
|
|
|
if (!reinterpret_cast<spvtools::Optimizer*>(optimizer)->
|
|
Run(binary, word_count, &optimized, options)) {
|
|
return SPV_ERROR_INTERNAL;
|
|
}
|
|
|
|
auto result_binary = new spv_binary_t();
|
|
if (!result_binary) {
|
|
*optimized_binary = nullptr;
|
|
return SPV_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result_binary->code = new uint32_t[optimized.size()];
|
|
if (!result_binary->code) {
|
|
delete result_binary;
|
|
*optimized_binary = nullptr;
|
|
return SPV_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
result_binary->wordCount = optimized.size();
|
|
|
|
memcpy(result_binary->code, optimized.data(),
|
|
optimized.size() * sizeof(uint32_t));
|
|
|
|
*optimized_binary = result_binary;
|
|
|
|
return SPV_SUCCESS;
|
|
}
|
|
|
|
} // extern "C"
|