Add FlattenDecoration transform
authorDavid Neto <dneto@google.com>
Sat, 1 Apr 2017 20:10:16 +0000 (16:10 -0400)
committerDavid Neto <dneto@google.com>
Thu, 6 Apr 2017 15:19:56 +0000 (11:19 -0400)
Add --flatten-decorations to spirv-opt

Flattens decoration groups.  That is, replace OpDecorationGroup
and its uses in OpGroupDecorate and OpGroupMemberDecorate with
ordinary OpDecorate and OpMemberDecorate instructions.

Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/602

CHANGES
include/spirv-tools/optimizer.hpp
source/opt/CMakeLists.txt
source/opt/flatten_decoration_pass.cpp [new file with mode: 0644]
source/opt/flatten_decoration_pass.h [new file with mode: 0644]
source/opt/optimizer.cpp
source/opt/passes.h
test/opt/CMakeLists.txt
test/opt/flatten_decoration_test.cpp [new file with mode: 0644]
tools/opt/opt.cpp

diff --git a/CHANGES b/CHANGES
index 8347371ddd63228c5a6acb0f778940b8955bd7d6..55e28ce375eb7b8062429daa776988e082e6e909 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,7 @@ Revision history for SPIRV-Tools
 
 v2016.7-dev 2017-01-06
  - Optimizer: Add inlining of all function calls in entry points
+ - Optimizer: Add flattening of decoration groups.  Fixes #602
  - Add build target spirv-tools-vimsyntax to generate spvasm.vim, a SPIR-V
    assembly syntax file for Vim.
  - Version string: Allow overriding of wall clock timestamp with contents
index 68bb6956001e2da564504c044804cbf55d1dece9..0b5af1950eae34c5c73af40a5ab6c18f7399c814 100644 (file)
@@ -109,6 +109,15 @@ Optimizer::PassToken CreateStripDebugInfoPass();
 Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
     const std::unordered_map<uint32_t, std::string>& id_value_map);
 
+// Creates a flatten-decoration pass.
+// A flatten-decoration pass replaces grouped decorations with equivalent
+// ungrouped decorations.  That is, it replaces each OpDecorationGroup
+// instruction and associated OpGroupDecorate and OpGroupMemberDecorate
+// instructions with equivalent OpDecorate and OpMemberDecorate instructions.
+// The pass does not attempt to preserve debug information for instructions
+// it removes.
+Optimizer::PassToken CreateFlattenDecorationPass();
+
 // Creates a freeze-spec-constant-value pass.
 // A freeze-spec-constant pass specializes the value of spec constants to
 // their default values. This pass only processes the spec constants that have
index 917086ce42fd14a92c68b225ed8b89d4799662bf..bd51fdfedfb017eaf97fb5cbfb8b76e0eb267992 100644 (file)
@@ -17,6 +17,7 @@ add_library(SPIRV-Tools-opt
   constants.h
   def_use_manager.h
   eliminate_dead_constant_pass.h
+  flatten_decoration_pass.h
   function.h
   fold_spec_constant_op_and_composite_pass.h
   freeze_spec_constant_value_pass.h
@@ -40,6 +41,7 @@ add_library(SPIRV-Tools-opt
   build_module.cpp
   def_use_manager.cpp
   eliminate_dead_constant_pass.cpp
+  flatten_decoration_pass.cpp
   function.cpp
   fold_spec_constant_op_and_composite_pass.cpp
   freeze_spec_constant_value_pass.cpp
diff --git a/source/opt/flatten_decoration_pass.cpp b/source/opt/flatten_decoration_pass.cpp
new file mode 100644 (file)
index 0000000..98bb69c
--- /dev/null
@@ -0,0 +1,164 @@
+// 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 "flatten_decoration_pass.h"
+
+#include <cassert>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+
+namespace spvtools {
+namespace opt {
+
+using ir::Instruction;
+using ir::Operand;
+
+using Words = std::vector<uint32_t>;
+using OrderedUsesMap = std::unordered_map<uint32_t, Words>;
+
+Pass::Status FlattenDecorationPass::Process(ir::Module* module) {
+  bool modified = false;
+
+  // The target Id of OpDecorationGroup instructions.
+  // We have to track this separately from its uses, in case it
+  // has no uses.
+  std::unordered_set<uint32_t> group_ids;
+  // Maps a decoration group Id to its GroupDecorate targets, in order
+  // of appearance.
+  OrderedUsesMap normal_uses;
+  // Maps a decoration group Id to its GroupMemberDecorate targets and
+  // their indices, in of appearance.
+  OrderedUsesMap member_uses;
+
+  auto annotations = module->annotations();
+
+  // On the first pass, record each OpDecorationGroup with its ordered uses.
+  // Rely on unordered_map::operator[] to create its entries on first access.
+  for (const auto& inst : annotations) {
+    switch (inst.opcode()) {
+      case SpvOp::SpvOpDecorationGroup:
+        group_ids.insert(inst.result_id());
+        break;
+      case SpvOp::SpvOpGroupDecorate: {
+        Words& words = normal_uses[inst.GetSingleWordInOperand(0)];
+        for (uint32_t i = 1; i < inst.NumInOperandWords(); i++) {
+          words.push_back(inst.GetSingleWordInOperand(i));
+        }
+      } break;
+      case SpvOp::SpvOpGroupMemberDecorate: {
+        Words& words = member_uses[inst.GetSingleWordInOperand(0)];
+        for (uint32_t i = 1; i < inst.NumInOperandWords(); i++) {
+          words.push_back(inst.GetSingleWordInOperand(i));
+        }
+      } break;
+      default:
+        break;
+    }
+  }
+
+  // On the second pass, replace OpDecorationGroup and its uses with
+  // equivalent normal and struct member uses.
+  auto inst_iter = annotations.begin();
+  // We have to re-evaluate the end pointer
+  while (inst_iter != module->annotations().end()) {
+    // Should we replace this instruction?
+    bool replace = false;
+    switch (inst_iter->opcode()) {
+      case SpvOp::SpvOpDecorationGroup:
+      case SpvOp::SpvOpGroupDecorate:
+      case SpvOp::SpvOpGroupMemberDecorate:
+        replace = true;
+        break;
+      case SpvOp::SpvOpDecorate: {
+        // If this decoration targets a group, then replace it
+        // by sets of normal and member decorations.
+        const uint32_t group = inst_iter->GetSingleWordOperand(0);
+        const auto normal_uses_iter = normal_uses.find(group);
+        if (normal_uses_iter != normal_uses.end()) {
+          for (auto target : normal_uses[group]) {
+            std::unique_ptr<Instruction> new_inst(new Instruction(*inst_iter));
+            new_inst->SetInOperand(0, Words{target});
+            inst_iter = inst_iter.InsertBefore(std::move(new_inst));
+            ++inst_iter;
+            replace = true;
+          }
+        }
+        const auto member_uses_iter = member_uses.find(group);
+        if (member_uses_iter != member_uses.end()) {
+          const Words& member_id_pairs = (*member_uses_iter).second;
+          // The collection is a sequence of pairs.
+          assert((member_id_pairs.size() % 2) == 0);
+          for (size_t i = 0; i < member_id_pairs.size(); i += 2) {
+            // Make an OpMemberDecorate instruction for each (target, member)
+            // pair.
+            const uint32_t target = member_id_pairs[i];
+            const uint32_t member = member_id_pairs[i + 1];
+            std::vector<Operand> operands;
+            operands.push_back(Operand(SPV_OPERAND_TYPE_ID, {target}));
+            operands.push_back(
+                Operand(SPV_OPERAND_TYPE_LITERAL_INTEGER, {member}));
+            auto decoration_operands_iter = inst_iter->begin();
+            decoration_operands_iter++;  // Skip the group target.
+            operands.insert(operands.end(), decoration_operands_iter,
+                            inst_iter->end());
+            std::unique_ptr<Instruction> new_inst(
+                new Instruction(SpvOp::SpvOpMemberDecorate, 0, 0, operands));
+            inst_iter = inst_iter.InsertBefore(std::move(new_inst));
+            ++inst_iter;
+            replace = true;
+          }
+        }
+        // If this is an OpDecorate targeting the OpDecorationGroup itself,
+        // remove it even if that decoration group itself is not the target of
+        // any OpGroupDecorate or OpGroupMemberDecorate.
+        if (!replace && group_ids.count(group)) {
+          replace = true;
+        }
+      } break;
+      default:
+        break;
+    }
+    if (replace) {
+      inst_iter = inst_iter.Erase();
+      modified = true;
+    } else {
+      // Handle the case of decorations unrelated to decoration groups.
+      ++inst_iter;
+    }
+  }
+
+  // Remove OpName instructions which reference the removed group decorations.
+  // An OpDecorationGroup instruction might not have been used by an
+  // OpGroupDecorate or OpGroupMemberDecorate instruction.
+  if (!group_ids.empty()) {
+    for (auto debug_inst_iter = module->debug_begin();
+         debug_inst_iter != module->debug_end();) {
+      if (debug_inst_iter->opcode() == SpvOp::SpvOpName) {
+        const uint32_t target = debug_inst_iter->GetSingleWordOperand(0);
+        if (group_ids.count(target)) {
+          debug_inst_iter = debug_inst_iter.Erase();
+          modified = true;
+        } else {
+          ++debug_inst_iter;
+        }
+      }
+    }
+  }
+
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/flatten_decoration_pass.h b/source/opt/flatten_decoration_pass.h
new file mode 100644 (file)
index 0000000..bcdfdc0
--- /dev/null
@@ -0,0 +1,34 @@
+// 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 LIBSPIRV_OPT_FLATTEN_DECORATION_PASS_H_
+#define LIBSPIRV_OPT_FLATTEN_DECORATION_PASS_H_
+
+#include "module.h"
+#include "pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// See optimizer.hpp for documentation.
+class FlattenDecorationPass : public Pass {
+ public:
+  const char* name() const override { return "flatten-decoration"; }
+  Status Process(ir::Module*) override;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_OPT_FLATTEN_DECORATION_PASS_H_
index b1d71973d46ebabd6739297cb9b29280b8a6346b..bdc0621f78968864e4fd86bcb7ee9b1b90743db1 100644 (file)
@@ -101,6 +101,11 @@ Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
       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>());
index 26c55f9b789ffa6bc7968276d13c976d95c9005e..30e3cd0be99905ef70b0660da6555aefe54f9688 100644 (file)
@@ -18,6 +18,7 @@
 // A single header to include all passes.
 
 #include "eliminate_dead_constant_pass.h"
+#include "flatten_decoration_pass.h"
 #include "fold_spec_constant_op_and_composite_pass.h"
 #include "inline_pass.h"
 #include "freeze_spec_constant_value_pass.h"
index d807d47c5fb22366c05d06c217fa5155f71e7f84..61809428ec7f35fe6608b61a530b7745c8194af2 100644 (file)
@@ -38,6 +38,11 @@ add_spvtools_unittest(TARGET pass_strip_debug_info
   LIBS SPIRV-Tools-opt
 )
 
+add_spvtools_unittest(TARGET pass_flatten_decoration
+  SRCS flatten_decoration_test.cpp pass_utils.cpp
+  LIBS SPIRV-Tools-opt
+)
+
 add_spvtools_unittest(TARGET pass_freeze_spec_const
   SRCS freeze_spec_const_test.cpp pass_utils.cpp
   LIBS SPIRV-Tools-opt
diff --git a/test/opt/flatten_decoration_test.cpp b/test/opt/flatten_decoration_test.cpp
new file mode 100644 (file)
index 0000000..8e6d979
--- /dev/null
@@ -0,0 +1,234 @@
+// Copyright (c) 2017 Valve Corporation
+// Copyright (c) 2017 LunarG 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 <gmock/gmock.h>
+
+#include "pass_fixture.h"
+#include "pass_utils.h"
+
+namespace {
+
+using namespace spvtools;
+
+// Returns the initial part of the assembly text for a valid
+// SPIR-V module, including instructions prior to decorations.
+std::string PreambleAssembly() {
+  return
+      R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %hue %saturation %value
+OpName %main "main"
+OpName %void_fn "void_fn"
+OpName %hue "hue"
+OpName %saturation "saturation"
+OpName %value "value"
+OpName %entry "entry"
+OpName %Point "Point"
+OpName %Camera "Camera"
+)";
+}
+
+// Retuns types
+std::string TypesAndFunctionsAssembly() {
+  return
+      R"(%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%Point = OpTypeStruct %float %float %float
+%Camera = OpTypeStruct %float %float
+%_ptr_Input_float = OpTypePointer Input %float
+%hue = OpVariable %_ptr_Input_float Input
+%saturation = OpVariable %_ptr_Input_float Input
+%value = OpVariable %_ptr_Input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+}
+
+struct FlattenDecorationCase {
+  // Names and decorations before the pass.
+  std::string input;
+  // Names and decorations after the pass.
+  std::string expected;
+};
+
+using FlattenDecorationTest =
+    PassTest<::testing::TestWithParam<FlattenDecorationCase>>;
+
+TEST_P(FlattenDecorationTest, TransformsDecorations) {
+  const auto before =
+      PreambleAssembly() + GetParam().input + TypesAndFunctionsAssembly();
+  const auto after =
+      PreambleAssembly() + GetParam().expected + TypesAndFunctionsAssembly();
+
+  SinglePassRunAndCheck<opt::FlattenDecorationPass>(before, after, false, true);
+}
+
+INSTANTIATE_TEST_CASE_P(NoUses, FlattenDecorationTest,
+                        ::testing::ValuesIn(std::vector<FlattenDecorationCase>{
+                            // No OpDecorationGroup
+                            {"", ""},
+
+                            // OpDecorationGroup without any uses, and
+                            // no OpName.
+                            {"%group = OpDecorationGroup\n", ""},
+
+                            // OpDecorationGroup without any uses, and
+                            // with OpName targeting it. Proves you must
+                            // remove the names as well.
+                            {"OpName %group \"group\"\n"
+                             "%group = OpDecorationGroup\n",
+                             ""},
+
+                            // OpDecorationGroup with decorations that
+                            // target it, but no uses in OpGroupDecorate
+                            // or OpGroupMemberDecorate instructions.
+                            {"OpDecorate %group Flat\n"
+                             "OpDecorate %group NoPerspective\n"
+                             "%group = OpDecorationGroup\n",
+                             ""},
+                        }), );
+
+INSTANTIATE_TEST_CASE_P(OpGroupDecorate, FlattenDecorationTest,
+                        ::testing::ValuesIn(std::vector<FlattenDecorationCase>{
+                            // One OpGroupDecorate
+                            {"OpName %group \"group\"\n"
+                             "OpDecorate %group Flat\n"
+                             "OpDecorate %group NoPerspective\n"
+                             "%group = OpDecorationGroup\n"
+                             "OpGroupDecorate %group %hue %saturation\n",
+                             "OpDecorate %hue Flat\n"
+                             "OpDecorate %saturation Flat\n"
+                             "OpDecorate %hue NoPerspective\n"
+                             "OpDecorate %saturation NoPerspective\n"},
+                            // Multiple OpGroupDecorate
+                            {"OpName %group \"group\"\n"
+                             "OpDecorate %group Flat\n"
+                             "OpDecorate %group NoPerspective\n"
+                             "%group = OpDecorationGroup\n"
+                             "OpGroupDecorate %group %hue %value\n"
+                             "OpGroupDecorate %group %saturation\n",
+                             "OpDecorate %hue Flat\n"
+                             "OpDecorate %value Flat\n"
+                             "OpDecorate %saturation Flat\n"
+                             "OpDecorate %hue NoPerspective\n"
+                             "OpDecorate %value NoPerspective\n"
+                             "OpDecorate %saturation NoPerspective\n"},
+                            // Two group decorations, interleaved
+                            {"OpName %group0 \"group0\"\n"
+                             "OpName %group1 \"group1\"\n"
+                             "OpDecorate %group0 Flat\n"
+                             "OpDecorate %group1 NoPerspective\n"
+                             "%group0 = OpDecorationGroup\n"
+                             "%group1 = OpDecorationGroup\n"
+                             "OpGroupDecorate %group0 %hue %value\n"
+                             "OpGroupDecorate %group1 %saturation\n",
+                             "OpDecorate %hue Flat\n"
+                             "OpDecorate %value Flat\n"
+                             "OpDecorate %saturation NoPerspective\n"},
+                            // Decoration with operands
+                            {"OpName %group \"group\"\n"
+                             "OpDecorate %group Location 42\n"
+                             "%group = OpDecorationGroup\n"
+                             "OpGroupDecorate %group %hue %saturation\n",
+                             "OpDecorate %hue Location 42\n"
+                             "OpDecorate %saturation Location 42\n"},
+                        }), );
+
+INSTANTIATE_TEST_CASE_P(OpGroupMemberDecorate, FlattenDecorationTest,
+                        ::testing::ValuesIn(std::vector<FlattenDecorationCase>{
+                            // One OpGroupMemberDecorate
+                            {"OpName %group \"group\"\n"
+                             "OpDecorate %group Flat\n"
+                             "OpDecorate %group Offset 16\n"
+                             "%group = OpDecorationGroup\n"
+                             "OpGroupMemberDecorate %group %Point 1\n",
+                             "OpMemberDecorate %Point 1 Flat\n"
+                             "OpMemberDecorate %Point 1 Offset 16\n"},
+                            // Multiple OpGroupMemberDecorate using the same
+                            // decoration group.
+                            {"OpName %group \"group\"\n"
+                             "OpDecorate %group Flat\n"
+                             "OpDecorate %group NoPerspective\n"
+                             "OpDecorate %group Offset 8\n"
+                             "%group = OpDecorationGroup\n"
+                             "OpGroupMemberDecorate %group %Point 2\n"
+                             "OpGroupMemberDecorate %group %Camera 1\n",
+                             "OpMemberDecorate %Point 2 Flat\n"
+                             "OpMemberDecorate %Camera 1 Flat\n"
+                             "OpMemberDecorate %Point 2 NoPerspective\n"
+                             "OpMemberDecorate %Camera 1 NoPerspective\n"
+                             "OpMemberDecorate %Point 2 Offset 8\n"
+                             "OpMemberDecorate %Camera 1 Offset 8\n"},
+                            // Two groups of member decorations, interleaved.
+                            // Decoration is with and without operands.
+                            {"OpName %group0 \"group0\"\n"
+                             "OpName %group1 \"group1\"\n"
+                             "OpDecorate %group0 Flat\n"
+                             "OpDecorate %group0 Offset 8\n"
+                             "OpDecorate %group1 NoPerspective\n"
+                             "OpDecorate %group1 Offset 16\n"
+                             "%group0 = OpDecorationGroup\n"
+                             "%group1 = OpDecorationGroup\n"
+                             "OpGroupMemberDecorate %group0 %Point 0\n"
+                             "OpGroupMemberDecorate %group1 %Point 2\n",
+                             "OpMemberDecorate %Point 0 Flat\n"
+                             "OpMemberDecorate %Point 0 Offset 8\n"
+                             "OpMemberDecorate %Point 2 NoPerspective\n"
+                             "OpMemberDecorate %Point 2 Offset 16\n"},
+                        }), );
+
+INSTANTIATE_TEST_CASE_P(UnrelatedDecorations, FlattenDecorationTest,
+                        ::testing::ValuesIn(std::vector<FlattenDecorationCase>{
+                            // A non-group non-member decoration is untouched.
+                            {"OpDecorate %hue Centroid\n"
+                             "OpDecorate %saturation Flat\n",
+                             "OpDecorate %hue Centroid\n"
+                             "OpDecorate %saturation Flat\n"},
+                            // A non-group member decoration is untouched.
+                            {"OpMemberDecorate %Point 0 Offset 0\n"
+                             "OpMemberDecorate %Point 1 Offset 4\n"
+                             "OpMemberDecorate %Point 1 Flat\n",
+                             "OpMemberDecorate %Point 0 Offset 0\n"
+                             "OpMemberDecorate %Point 1 Offset 4\n"
+                             "OpMemberDecorate %Point 1 Flat\n"},
+                            // A non-group non-member decoration survives any
+                            // replacement of group decorations.
+                            {"OpName %group \"group\"\n"
+                             "OpDecorate %group Flat\n"
+                             "OpDecorate %hue Centroid\n"
+                             "OpDecorate %group NoPerspective\n"
+                             "%group = OpDecorationGroup\n"
+                             "OpGroupDecorate %group %hue %saturation\n",
+                             "OpDecorate %hue Flat\n"
+                             "OpDecorate %saturation Flat\n"
+                             "OpDecorate %hue Centroid\n"
+                             "OpDecorate %hue NoPerspective\n"
+                             "OpDecorate %saturation NoPerspective\n"},
+                            // A non-group member decoration survives any
+                            // replacement of group decorations.
+                            {"OpDecorate %group Offset 0\n"
+                             "OpDecorate %group Flat\n"
+                             "OpMemberDecorate %Point 1 Offset 4\n"
+                             "%group = OpDecorationGroup\n"
+                             "OpGroupMemberDecorate %group %Point 0\n",
+                             "OpMemberDecorate %Point 0 Offset 0\n"
+                             "OpMemberDecorate %Point 0 Flat\n"
+                             "OpMemberDecorate %Point 1 Offset 4\n"},
+                        }), );
+
+}  // anonymous namespace
index 52918c983cc8ebc388688d15020cf7589bada71a..d5419e23f3d4e188d0a94ef4f134093c53148060 100644 (file)
@@ -63,6 +63,9 @@ Options:
                Remove the duplicated constants.
   --inline-entry-points-all
                Exhaustively inline all function calls in entry points
+  --flatten-decorations
+               Replace decoration groups with repeated OpDecorate and
+               OpMemberDecorate instructions.
   -h, --help   Print this help.
   --version    Display optimizer version information.
 )",
@@ -131,6 +134,8 @@ int main(int argc, char** argv) {
         optimizer.RegisterPass(CreateFoldSpecConstantOpAndCompositePass());
       } else if (0 == strcmp(cur_arg, "--unify-const")) {
         optimizer.RegisterPass(CreateUnifyConstantPass());
+      } else if (0 == strcmp(cur_arg, "--flatten-decorations")) {
+        optimizer.RegisterPass(CreateFlattenDecorationPass());
       } else if ('\0' == cur_arg[1]) {
         // Setting a filename of "-" to indicate stdin.
         if (!in_file) {