mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-11 19:30:05 +00:00
e6b953361d
This CL moves the files in opt/ to consistenly be under the opt:: namespace. This frees up the ir:: namespace so it can be used to make a shared ir represenation.
480 lines
17 KiB
C++
480 lines
17 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 "build_module.h"
|
|
#include "make_unique.h"
|
|
#include "pass_manager.h"
|
|
#include "passes.h"
|
|
#include "reduce_load_size.h"
|
|
#include "simplification_pass.h"
|
|
|
|
namespace spvtools {
|
|
|
|
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() {}
|
|
|
|
const spv_target_env target_env; // Target environment.
|
|
opt::PassManager pass_manager; // Internal implementation pass manager.
|
|
};
|
|
|
|
Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {}
|
|
|
|
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));
|
|
}
|
|
|
|
Optimizer& Optimizer::RegisterPass(PassToken&& p) {
|
|
// Change to use the pass manager's consumer.
|
|
p.impl_->pass->SetMessageConsumer(impl_->pass_manager.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() {
|
|
return
|
|
// 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())
|
|
// Propagate the value stored to the loads in very simple cases.
|
|
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
|
|
.RegisterPass(CreateLocalSingleStoreElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass())
|
|
// Split up aggragates 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())
|
|
.RegisterPass(CreateLocalMultiStoreElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass())
|
|
// Propagate constants to get as many constant conditions on branches
|
|
// as possible.
|
|
.RegisterPass(CreateCCPPass())
|
|
.RegisterPass(CreateDeadBranchElimPass())
|
|
// Copy propagate members. Cleans up code sequences generated by
|
|
// scalar replacement. Also important for removing OpPhi nodes.
|
|
.RegisterPass(CreateSimplificationPass())
|
|
.RegisterPass(CreateAggressiveDCEPass())
|
|
.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());
|
|
}
|
|
|
|
Optimizer& Optimizer::RegisterPerformancePasses() {
|
|
return RegisterPass(CreateMergeReturnPass())
|
|
.RegisterPass(CreateInlineExhaustivePass())
|
|
.RegisterPass(CreateAggressiveDCEPass())
|
|
.RegisterPass(CreatePrivateToLocalPass())
|
|
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
|
|
.RegisterPass(CreateLocalSingleStoreElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass())
|
|
.RegisterPass(CreateScalarReplacementPass())
|
|
.RegisterPass(CreateLocalAccessChainConvertPass())
|
|
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
|
|
.RegisterPass(CreateLocalSingleStoreElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass())
|
|
.RegisterPass(CreateLocalMultiStoreElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass())
|
|
.RegisterPass(CreateCCPPass())
|
|
.RegisterPass(CreateAggressiveDCEPass())
|
|
.RegisterPass(CreateRedundancyEliminationPass())
|
|
.RegisterPass(CreateSimplificationPass())
|
|
.RegisterPass(CreateVectorDCEPass())
|
|
.RegisterPass(CreateDeadInsertElimPass())
|
|
.RegisterPass(CreateDeadBranchElimPass())
|
|
.RegisterPass(CreateSimplificationPass())
|
|
.RegisterPass(CreateIfConversionPass())
|
|
.RegisterPass(CreateCopyPropagateArraysPass())
|
|
.RegisterPass(CreateReduceLoadSizePass())
|
|
.RegisterPass(CreateAggressiveDCEPass())
|
|
.RegisterPass(CreateBlockMergePass())
|
|
.RegisterPass(CreateRedundancyEliminationPass())
|
|
.RegisterPass(CreateDeadBranchElimPass())
|
|
.RegisterPass(CreateBlockMergePass())
|
|
.RegisterPass(CreateSimplificationPass());
|
|
// Currently exposing driver bugs resulting in crashes (#946)
|
|
// .RegisterPass(CreateCommonUniformElimPass())
|
|
}
|
|
|
|
Optimizer& Optimizer::RegisterSizePasses() {
|
|
return RegisterPass(CreateMergeReturnPass())
|
|
.RegisterPass(CreateInlineExhaustivePass())
|
|
.RegisterPass(CreateAggressiveDCEPass())
|
|
.RegisterPass(CreatePrivateToLocalPass())
|
|
.RegisterPass(CreateScalarReplacementPass())
|
|
.RegisterPass(CreateLocalAccessChainConvertPass())
|
|
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
|
|
.RegisterPass(CreateLocalSingleStoreElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass())
|
|
.RegisterPass(CreateSimplificationPass())
|
|
.RegisterPass(CreateDeadInsertElimPass())
|
|
.RegisterPass(CreateLocalMultiStoreElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass())
|
|
.RegisterPass(CreateCCPPass())
|
|
.RegisterPass(CreateAggressiveDCEPass())
|
|
.RegisterPass(CreateDeadBranchElimPass())
|
|
.RegisterPass(CreateIfConversionPass())
|
|
.RegisterPass(CreateAggressiveDCEPass())
|
|
.RegisterPass(CreateBlockMergePass())
|
|
.RegisterPass(CreateSimplificationPass())
|
|
.RegisterPass(CreateDeadInsertElimPass())
|
|
.RegisterPass(CreateRedundancyEliminationPass())
|
|
.RegisterPass(CreateCFGCleanupPass())
|
|
// Currently exposing driver bugs resulting in crashes (#946)
|
|
// .RegisterPass(CreateCommonUniformElimPass())
|
|
.RegisterPass(CreateAggressiveDCEPass());
|
|
}
|
|
|
|
bool Optimizer::Run(const uint32_t* original_binary,
|
|
const size_t original_binary_size,
|
|
std::vector<uint32_t>* optimized_binary) const {
|
|
std::unique_ptr<opt::IRContext> context =
|
|
BuildModule(impl_->target_env, impl_->pass_manager.consumer(),
|
|
original_binary, original_binary_size);
|
|
if (context == nullptr) return false;
|
|
|
|
auto status = impl_->pass_manager.Run(context.get());
|
|
if (status == opt::Pass::Status::SuccessWithChange ||
|
|
(status == opt::Pass::Status::SuccessWithoutChange &&
|
|
(optimized_binary->data() != original_binary ||
|
|
optimized_binary->size() != original_binary_size))) {
|
|
optimized_binary->clear();
|
|
context->module()->ToBinary(optimized_binary, /* skip_nop = */ true);
|
|
}
|
|
|
|
return status != opt::Pass::Status::Failure;
|
|
}
|
|
|
|
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::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 MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::StripReflectInfoPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateEliminateDeadFunctionsPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::EliminateDeadFunctionsPass>());
|
|
}
|
|
|
|
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::LocalMultiStoreElimPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateAggressiveDCEPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::AggressiveDCEPass>());
|
|
}
|
|
|
|
Optimizer::PassToken CreateCommonUniformElimPass() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::CommonUniformElimPass>());
|
|
}
|
|
|
|
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() {
|
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
|
MakeUnique<opt::ReduceLoadSize>());
|
|
}
|
|
} // namespace spvtools
|