source/opt/compact_ids_pass.cpp \
source/opt/common_uniform_elim_pass.cpp \
source/opt/dead_branch_elim_pass.cpp \
+ source/opt/dead_variable_elimination.cpp \
source/opt/decoration_manager.cpp \
source/opt/def_use_manager.cpp \
source/opt/eliminate_dead_constant_pass.cpp \
// Returns a vector of strings with all the pass names added to this
// optimizer's pass manager. These strings are valid until the associated
// pass manager is destroyed.
- std::vector<const char *> GetPassNames() const;
+ std::vector<const char*> GetPassNames() const;
private:
struct Impl; // Opaque struct for holding internal data.
Optimizer::PassToken CreateStripDebugInfoPass();
// Creates an eliminate-dead-functions pass.
-// An eliminate-dead-functions pass will remove all functions that are not in the
-// call trees rooted at entry points and exported functions. These functions
-// are not needed because they will never be called.
+// An eliminate-dead-functions pass will remove all functions that are not in
+// the call trees rooted at entry points and exported functions. These
+// functions are not needed because they will never be called.
Optimizer::PassToken CreateEliminateDeadFunctionsPass();
// Creates a set-spec-constant-default-value pass from a mapping from spec-ids
// - Removal of unreachable basic blocks.
Optimizer::PassToken CreateCFGCleanupPass();
+// Create dead variable elimination pass.
+// This pass will delete module scope variables, along with their decorations,
+// that are not referenced.
+Optimizer::PassToken CreateDeadVariableEliminationPass();
+
} // namespace spvtools
#endif // SPIRV_TOOLS_OPTIMIZER_HPP_
compact_ids_pass.h
constants.h
dead_branch_elim_pass.h
+ dead_variable_elimination.h
decoration_manager.h
def_use_manager.h
eliminate_dead_constant_pass.h
decoration_manager.cpp
def_use_manager.cpp
dead_branch_elim_pass.cpp
+ dead_variable_elimination.cpp
eliminate_dead_constant_pass.cpp
flatten_decoration_pass.cpp
fold.cpp
--- /dev/null
+// Copyright (c) 2017 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 "dead_variable_elimination.h"
+
+#include "reflect.h"
+
+namespace spvtools {
+namespace opt {
+
+// This optimization removes global variables that are not needed because they
+// are definitely not accessed.
+Pass::Status DeadVariableElimination::Process(spvtools::ir::Module* module) {
+ // The algorithm will compute the reference count for every global variable.
+ // Anything with a reference count of 0 will then be deleted. For variables
+ // that might have references that are not explicit in this module, we use the
+ // value kMustKeep as the reference count.
+
+ bool modified = false;
+ module_ = module;
+ def_use_mgr_.reset(new analysis::DefUseManager(consumer(), module));
+ FindNamedOrDecoratedIds();
+
+ // Decoration manager to help organize decorations.
+ analysis::DecorationManager decoration_manager(module);
+
+ std::vector<uint32_t> ids_to_remove;
+
+ // Get the reference count for all of the global OpVariable instructions.
+ for (auto& inst : module->types_values()) {
+ if (inst.opcode() != SpvOp::SpvOpVariable) {
+ continue;
+ }
+
+ size_t count = 0;
+ uint32_t result_id = inst.result_id();
+
+ // Check the linkage. If it is exported, it could be reference somewhere
+ // else, so we must keep the variable around.
+ decoration_manager.ForEachDecoration(
+ result_id, SpvDecorationLinkageAttributes,
+ [&count](const ir::Instruction& linkage_instruction) {
+ uint32_t last_operand = linkage_instruction.NumOperands() - 1;
+ if (linkage_instruction.GetSingleWordOperand(last_operand) ==
+ SpvLinkageTypeExport) {
+ count = kMustKeep;
+ }
+ });
+
+ if (count != kMustKeep) {
+ // If we don't have to keep the instruction for other reasons, then look
+ // at the uses and count the number of real references.
+ if (analysis::UseList* uses = def_use_mgr_->GetUses(result_id)) {
+ count = std::count_if(
+ uses->begin(), uses->end(), [](const analysis::Use& u) {
+ return (!ir::IsAnnotationInst(u.inst->opcode()) &&
+ u.inst->opcode() != SpvOpName);
+ });
+ }
+ }
+ reference_count_[result_id] = count;
+ if (count == 0) {
+ ids_to_remove.push_back(result_id);
+ }
+ }
+
+ // Remove all of the variables that have a reference count of 0.
+ if (!ids_to_remove.empty()) {
+ modified = true;
+ for (auto result_id : ids_to_remove) {
+ DeleteVariable(result_id);
+ }
+ }
+ return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
+}
+
+void DeadVariableElimination::DeleteVariable(uint32_t result_id) {
+ ir::Instruction* inst = def_use_mgr_->GetDef(result_id);
+ assert(inst->opcode() == SpvOpVariable &&
+ "Should not be trying to delete anything other than an OpVariable.");
+
+ // Look for an initializer that references another variable. We need to know
+ // if that variable can be deleted after the reference is removed.
+ if (inst->NumOperands() == 4) {
+ ir::Instruction* initializer =
+ def_use_mgr_->GetDef(inst->GetSingleWordOperand(3));
+
+ // TODO: Handle OpSpecConstantOP which might be defined in terms of other
+ // variables. Will probably require a unified dead code pass that does all
+ // instruction types. (Issue 906)
+ if (initializer->opcode() == SpvOpVariable) {
+ uint32_t initializer_id = initializer->result_id();
+ size_t& count = reference_count_[initializer_id];
+ if (count != kMustKeep) {
+ --count;
+ }
+
+ if (count == 0) {
+ DeleteVariable(initializer_id);
+ }
+ }
+ }
+ this->KillNamesAndDecorates(result_id);
+ def_use_mgr_->KillDef(result_id);
+}
+} // namespace opt
+} // namespace spvtools
--- /dev/null
+// Copyright (c) 2017 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.
+
+#ifndef SPIRV_TOOLS_DEAD_VARIABLE_ELIMINATION_H
+#define SPIRV_TOOLS_DEAD_VARIABLE_ELIMINATION_H
+
+#include <unordered_map>
+#include <climits>
+
+#include "decoration_manager.h"
+#include "mem_pass.h"
+
+namespace spvtools {
+namespace opt {
+
+class DeadVariableElimination : public MemPass {
+ public:
+ const char* name() const override { return "dead-variable-elimination"; }
+ Status Process(ir::Module*) override;
+
+ private:
+ // Deletes the OpVariable instruction who result id is |result_id|.
+ void DeleteVariable(uint32_t result_id);
+
+ // Keeps track of the number of references of an id. Once that value is 0, it
+ // is safe to remove the corresponding instruction.
+ //
+ // Note that the special value kMustKeep is used to indicate that the
+ // instruction cannot be deleted for reasons other that is being explicitly
+ // referenced.
+ std::unordered_map<uint32_t, size_t> reference_count_;
+
+ // Special value used to indicate that an id cannot be safely deleted.
+ enum { kMustKeep = INT_MAX };
+};
+
+} // namespace opt
+} // namespace spvtools
+
+#endif // SPIRV_TOOLS_DEAD_VARIABLE_ELIMINATION_H
std::vector<const ir::Instruction*> DecorationManager::GetDecorationsFor(
uint32_t id, bool include_linkage) const {
- return const_cast<DecorationManager*>(this)->InternalGetDecorationsFor<const ir::Instruction*>(id, include_linkage);
+ return const_cast<DecorationManager*>(this)
+ ->InternalGetDecorationsFor<const ir::Instruction*>(id, include_linkage);
}
// TODO(pierremoreau): The code will return true for { deco1, deco1 }, { deco1,
}
template <typename T>
-std::vector<T> DecorationManager::InternalGetDecorationsFor(uint32_t id,
- bool include_linkage) {
+std::vector<T> DecorationManager::InternalGetDecorationsFor(
+ uint32_t id, bool include_linkage) {
std::vector<T> decorations;
std::stack<uint32_t> ids_to_process;
- const auto process = [&ids_to_process,
- &decorations](T inst) {
+ const auto process = [&ids_to_process, &decorations](T inst) {
if (inst->opcode() == SpvOpGroupDecorate ||
inst->opcode() == SpvOpGroupMemberDecorate)
ids_to_process.push(inst->GetSingleWordInOperand(0u));
return decorations;
}
+void DecorationManager::ForEachDecoration(uint32_t id,
+ uint32_t decoration,
+ std::function<void(const ir::Instruction&)> f) const {
+ auto decoration_list = id_to_decoration_insts_.find(id);
+ if (decoration_list != id_to_decoration_insts_.end()) {
+ for (const ir::Instruction* inst : decoration_list->second) {
+ switch (inst->opcode()) {
+ case SpvOpDecorate:
+ if (inst->GetSingleWordInOperand(1) == decoration) {
+ f(*inst);
+ }
+ break;
+ case SpvOpMemberDecorate:
+ if (inst->GetSingleWordInOperand(2) == decoration) {
+ f(*inst);
+ }
+ break;
+ case SpvOpDecorateId:
+ if (inst->GetSingleWordInOperand(1) == decoration) {
+ f(*inst);
+ }
+ break;
+ default:
+ assert(false && "Unexpected decoration instruction");
+ }
+ }
+ }
+}
+
} // namespace analysis
} // namespace opt
} // namespace spvtools
#ifndef LIBSPIRV_OPT_DECORATION_MANAGER_H_
#define LIBSPIRV_OPT_DECORATION_MANAGER_H_
+#include <functional>
#include <unordered_map>
#include <vector>
bool AreDecorationsTheSame(const ir::Instruction* inst1,
const ir::Instruction* inst2) const;
+ // |f| is run on each decoration instruction for |id| with decoration
+ // |decoration|.
+ void ForEachDecoration(uint32_t id, uint32_t decoration,
+ std::function<void(const ir::Instruction& f)>) const;
+
private:
using IdToDecorationInstsMap =
std::unordered_map<uint32_t, std::vector<ir::Instruction*>>;
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>());
#include "common_uniform_elim_pass.h"
#include "compact_ids_pass.h"
#include "dead_branch_elim_pass.h"
+#include "dead_variable_elimination.h"
#include "eliminate_dead_constant_pass.h"
#include "flatten_decoration_pass.h"
#include "fold_spec_constant_op_and_composite_pass.h"
LIBS SPIRV-Tools-opt
)
+add_spvtools_unittest(TARGET pass_dead_variable_elim
+ SRCS dead_variable_elim_test.cpp pass_utils.cpp
+ LIBS SPIRV-Tools-opt
+ )
+
add_spvtools_unittest(TARGET pass_aggressive_dce
SRCS aggressive_dead_code_elim_test.cpp pass_utils.cpp
LIBS SPIRV-Tools-opt
--- /dev/null
+// Copyright (c) 2017 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 "pass_fixture.h"
+#include "pass_utils.h"
+
+namespace {
+
+using namespace spvtools;
+
+using DeadVariableElimTest = PassTest<::testing::Test>;
+
+// %dead is unused. Make sure we remove it along with its name.
+TEST_F(DeadVariableElimTest, RemoveUnreferenced) {
+ const std::string before =
+ R"(OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 150
+OpName %main "main"
+OpName %dead "dead"
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Private_float = OpTypePointer Private %float
+%dead = OpVariable %_ptr_Private_float Private
+%main = OpFunction %void None %5
+%8 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 150
+OpName %main "main"
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Private_float = OpTypePointer Private %float
+%main = OpFunction %void None %5
+%8 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndCheck<opt::DeadVariableElimination>(before, after, true,
+ true);
+}
+
+// Since %dead is exported, make sure we keep it. It could be referenced
+// somewhere else.
+TEST_F(DeadVariableElimTest, KeepExported) {
+ const std::string before =
+ R"(OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 150
+OpName %main "main"
+OpName %dead "dead"
+OpDecorate %dead LinkageAttributes "dead" Export
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Private_float = OpTypePointer Private %float
+%dead = OpVariable %_ptr_Private_float Private
+%main = OpFunction %void None %5
+%8 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndCheck<opt::DeadVariableElimination>(before, before, true,
+ true);
+}
+
+// Delete %dead because it is unreferenced. Then %initializer becomes
+// unreferenced, so remove it as well.
+TEST_F(DeadVariableElimTest, RemoveUnreferencedWithInit1) {
+ const std::string before =
+ R"(OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 150
+OpName %main "main"
+OpName %dead "dead"
+OpName %initializer "initializer"
+%void = OpTypeVoid
+%6 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Private_float = OpTypePointer Private %float
+%initializer = OpVariable %_ptr_Private_float Private
+%dead = OpVariable %_ptr_Private_float Private %initializer
+%main = OpFunction %void None %6
+%9 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 150
+OpName %main "main"
+%void = OpTypeVoid
+%6 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Private_float = OpTypePointer Private %float
+%main = OpFunction %void None %6
+%9 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndCheck<opt::DeadVariableElimination>(before, after, true,
+ true);
+}
+
+// Delete %dead because it is unreferenced. In this case, the initialized has
+// another reference, and should not be removed.
+TEST_F(DeadVariableElimTest, RemoveUnreferencedWithInit2) {
+ const std::string before =
+ R"(OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 150
+OpName %main "main"
+OpName %dead "dead"
+OpName %initializer "initializer"
+%void = OpTypeVoid
+%6 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Private_float = OpTypePointer Private %float
+%initializer = OpVariable %_ptr_Private_float Private
+%dead = OpVariable %_ptr_Private_float Private %initializer
+%main = OpFunction %void None %6
+%9 = OpLabel
+%10 = OpLoad %float %initializer
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 150
+OpName %main "main"
+OpName %initializer "initializer"
+%void = OpTypeVoid
+%6 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Private_float = OpTypePointer Private %float
+%initializer = OpVariable %_ptr_Private_float Private
+%main = OpFunction %void None %6
+%9 = OpLabel
+%10 = OpLoad %float %initializer
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndCheck<opt::DeadVariableElimination>(before, after, true,
+ true);
+}
+
+// Keep %live because it is used, and its initializer.
+TEST_F(DeadVariableElimTest, KeepReferenced) {
+ const std::string before =
+ R"(OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 150
+OpName %main "main"
+OpName %live "live"
+OpName %initializer "initializer"
+%void = OpTypeVoid
+%6 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Private_float = OpTypePointer Private %float
+%initializer = OpVariable %_ptr_Private_float Private
+%live = OpVariable %_ptr_Private_float Private %initializer
+%main = OpFunction %void None %6
+%9 = OpLabel
+%10 = OpLoad %float %live
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndCheck<opt::DeadVariableElimination>(before, before, true,
+ true);
+}
+
+// This test that the decoration associated with a variable are removed when the
+// variable is removed.
+TEST_F(DeadVariableElimTest, RemoveVariableAndDecorations) {
+ const std::string before =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+OpSource GLSL 450
+OpName %main "main"
+OpName %B "B"
+OpMemberName %B 0 "a"
+OpName %Bdat "Bdat"
+OpMemberDecorate %B 0 Offset 0
+OpDecorate %B BufferBlock
+OpDecorate %Bdat DescriptorSet 0
+OpDecorate %Bdat Binding 0
+%void = OpTypeVoid
+%6 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%B = OpTypeStruct %uint
+%_ptr_Uniform_B = OpTypePointer Uniform %B
+%Bdat = OpVariable %_ptr_Uniform_B Uniform
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%uint_1 = OpConstant %uint 1
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%main = OpFunction %void None %6
+%13 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+OpSource GLSL 450
+OpName %main "main"
+OpName %B "B"
+OpMemberName %B 0 "a"
+OpMemberDecorate %B 0 Offset 0
+OpDecorate %B BufferBlock
+%void = OpTypeVoid
+%6 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%B = OpTypeStruct %uint
+%_ptr_Uniform_B = OpTypePointer Uniform %B
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%uint_1 = OpConstant %uint 1
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%main = OpFunction %void None %6
+%13 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndCheck<opt::DeadVariableElimination>(before, after, true,
+ true);
+}
+} // namespace
call tree functions.
--strength-reduction
Replaces instructions with equivalent and less expensive ones.
+ --eliminate-dead-variables
+ Deletes module scope variables that are not referenced.
-O
Optimize for performance. Apply a sequence of transformations
in an attempt to improve the performance of the generated
optimizer->RegisterPass(CreateCommonUniformElimPass());
} else if (0 == strcmp(cur_arg, "--eliminate-dead-const")) {
optimizer->RegisterPass(CreateEliminateDeadConstantPass());
+ } else if (0 == strcmp(cur_arg, "--eliminate-dead-variables")) {
+ optimizer->RegisterPass(CreateDeadVariableEliminationPass());
} else if (0 == strcmp(cur_arg, "--fold-spec-const-op-composite")) {
optimizer->RegisterPass(CreateFoldSpecConstantOpAndCompositePass());
} else if (0 == strcmp(cur_arg, "--strength-reduction")) {