diff --git a/Android.mk b/Android.mk index f868a623..ed68c406 100644 --- a/Android.mk +++ b/Android.mk @@ -55,6 +55,7 @@ SPVTOOLS_OPT_SRC_FILES := \ 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 \ diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 32800e62..8d9f94d5 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -101,7 +101,7 @@ class Optimizer { // 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 GetPassNames() const; + std::vector GetPassNames() const; private: struct Impl; // Opaque struct for holding internal data. @@ -118,9 +118,9 @@ Optimizer::PassToken CreateNullPass(); 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 @@ -401,6 +401,11 @@ Optimizer::PassToken CreateRemoveDuplicatesPass(); // - 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_ diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index a7dfa57c..bf3dcb15 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -21,6 +21,7 @@ add_library(SPIRV-Tools-opt 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 @@ -66,6 +67,7 @@ add_library(SPIRV-Tools-opt 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 diff --git a/source/opt/dead_variable_elimination.cpp b/source/opt/dead_variable_elimination.cpp new file mode 100644 index 00000000..0e160816 --- /dev/null +++ b/source/opt/dead_variable_elimination.cpp @@ -0,0 +1,118 @@ +// 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 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 diff --git a/source/opt/dead_variable_elimination.h b/source/opt/dead_variable_elimination.h new file mode 100644 index 00000000..a0992cca --- /dev/null +++ b/source/opt/dead_variable_elimination.h @@ -0,0 +1,51 @@ +// 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 +#include + +#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 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 diff --git a/source/opt/decoration_manager.cpp b/source/opt/decoration_manager.cpp index 52a9bca2..b12670bf 100644 --- a/source/opt/decoration_manager.cpp +++ b/source/opt/decoration_manager.cpp @@ -64,7 +64,8 @@ std::vector DecorationManager::GetDecorationsFor( std::vector DecorationManager::GetDecorationsFor( uint32_t id, bool include_linkage) const { - return const_cast(this)->InternalGetDecorationsFor(id, include_linkage); + return const_cast(this) + ->InternalGetDecorationsFor(id, include_linkage); } // TODO(pierremoreau): The code will return true for { deco1, deco1 }, { deco1, @@ -180,13 +181,12 @@ void DecorationManager::AnalyzeDecorations(ir::Module* module) { } template -std::vector DecorationManager::InternalGetDecorationsFor(uint32_t id, - bool include_linkage) { +std::vector DecorationManager::InternalGetDecorationsFor( + uint32_t id, bool include_linkage) { std::vector decorations; std::stack 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)); @@ -229,6 +229,35 @@ std::vector DecorationManager::InternalGetDecorationsFor(uint32_t id, return decorations; } +void DecorationManager::ForEachDecoration(uint32_t id, + uint32_t decoration, + std::function 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 diff --git a/source/opt/decoration_manager.h b/source/opt/decoration_manager.h index acd8aa8f..30894120 100644 --- a/source/opt/decoration_manager.h +++ b/source/opt/decoration_manager.h @@ -15,6 +15,7 @@ #ifndef LIBSPIRV_OPT_DECORATION_MANAGER_H_ #define LIBSPIRV_OPT_DECORATION_MANAGER_H_ +#include #include #include @@ -50,6 +51,11 @@ class DecorationManager { 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) const; + private: using IdToDecorationInstsMap = std::unordered_map>; diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 2022f9cb..a0ea1ebf 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -165,6 +165,11 @@ Optimizer::PassToken CreateEliminateDeadConstantPass() { MakeUnique()); } +Optimizer::PassToken CreateDeadVariableEliminationPass() { + return MakeUnique( + MakeUnique()); +} + Optimizer::PassToken CreateStrengthReductionPass() { return MakeUnique( MakeUnique()); diff --git a/source/opt/passes.h b/source/opt/passes.h index 98b9424f..657f4945 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h @@ -22,6 +22,7 @@ #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" diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index a3d3ff85..f19ff7f5 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt @@ -103,6 +103,11 @@ add_spvtools_unittest(TARGET pass_dead_branch_elim 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 diff --git a/test/opt/dead_variable_elim_test.cpp b/test/opt/dead_variable_elim_test.cpp new file mode 100644 index 00000000..676719cf --- /dev/null +++ b/test/opt/dead_variable_elim_test.cpp @@ -0,0 +1,299 @@ +// 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(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(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(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(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(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(before, after, true, + true); +} +} // namespace diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 2a187f04..dd44d248 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -153,6 +153,8 @@ Options: 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 @@ -353,6 +355,8 @@ OptStatus ParseFlags(int argc, const char** argv, Optimizer* optimizer, 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")) {