InsertExtractElim: Split out DeadInsertElim as separate pass
authorGregF <greg@LunarG.com>
Sat, 27 Jan 2018 00:05:33 +0000 (17:05 -0700)
committerSteven Perron <stevenperron@google.com>
Tue, 30 Jan 2018 13:52:14 +0000 (08:52 -0500)
15 files changed:
Android.mk
include/spirv-tools/optimizer.hpp
source/opt/CMakeLists.txt
source/opt/composite.cpp [new file with mode: 0644]
source/opt/composite.h [new file with mode: 0644]
source/opt/dead_insert_elim_pass.cpp [new file with mode: 0644]
source/opt/dead_insert_elim_pass.h [new file with mode: 0644]
source/opt/insert_extract_elim.cpp
source/opt/insert_extract_elim.h
source/opt/optimizer.cpp
source/opt/passes.h
test/opt/CMakeLists.txt
test/opt/dead_insert_elim_test.cpp [new file with mode: 0644]
test/opt/insert_extract_elim_test.cpp
tools/opt/opt.cpp

index e28144c3650510784fc67d222497b6128a3a1cec..902c6825dffc59328e601196168e551ea617b610 100644 (file)
@@ -62,9 +62,11 @@ SPVTOOLS_OPT_SRC_FILES := \
                source/opt/cfg_cleanup_pass.cpp \
                source/opt/ccp_pass.cpp \
                source/opt/compact_ids_pass.cpp \
+               source/opt/composite.cpp \
                source/opt/common_uniform_elim_pass.cpp \
                source/opt/constants.cpp \
                source/opt/dead_branch_elim_pass.cpp \
+               source/opt/dead_insert_elim_pass.cpp \
                source/opt/dead_variable_elimination.cpp \
                source/opt/decoration_manager.cpp \
                source/opt/def_use_manager.cpp \
index b1e2cfe06fd78ad7aa386fcebc92e3c8fb3c5a16..a8d8da0cc0dafdb22d5a02336f2bca092047b256 100644 (file)
@@ -370,6 +370,21 @@ Optimizer::PassToken CreateLocalSingleStoreElimPass();
 // converted to inserts and extracts and local loads and stores are eliminated.
 Optimizer::PassToken CreateInsertExtractElimPass();
 
+// Creates a dead insert elimination pass.
+// This pass processes each entry point function in the module, searching for
+// unreferenced inserts into composite types. These are most often unused
+// stores to vector components. They are unused because they are never
+// referenced, or because there is another insert to the same component between
+// the insert and the reference. After removing the inserts, dead code
+// elimination is attempted on the inserted values.
+//
+// This pass performs best after access chains are converted to inserts and
+// extracts and local loads and stores are eliminated. While executing this
+// pass can be advantageous on its own, it is also advantageous to execute
+// this pass after CreateInsertExtractPass() as it will remove any unused
+// inserts created by that pass.
+Optimizer::PassToken CreateDeadInsertElimPass();
+
 // Creates a pass to consolidate uniform references.
 // For each entry point function in the module, first change all constant index
 // access chain loads into equivalent composite extracts. Then consolidate
index 8dfe229a102708d7526e8a901bbdaba04418b0c6..321d3f8a1903c0a44963134c79ab05a4b4842e39 100644 (file)
@@ -21,8 +21,10 @@ add_library(SPIRV-Tools-opt
   cfg.h
   common_uniform_elim_pass.h
   compact_ids_pass.h
+  composite.h
   constants.h
   dead_branch_elim_pass.h
+  dead_insert_elim_pass.h
   dead_variable_elimination.h
   decoration_manager.h
   def_use_manager.h
@@ -83,8 +85,10 @@ add_library(SPIRV-Tools-opt
   cfg.cpp
   common_uniform_elim_pass.cpp
   compact_ids_pass.cpp
+  composite.cpp
   constants.cpp
   dead_branch_elim_pass.cpp
+  dead_insert_elim_pass.cpp
   dead_variable_elimination.cpp
   decoration_manager.cpp
   def_use_manager.cpp
diff --git a/source/opt/composite.cpp b/source/opt/composite.cpp
new file mode 100644 (file)
index 0000000..1fbb71f
--- /dev/null
@@ -0,0 +1,52 @@
+// Copyright (c) 2018 The Khronos Group Inc.
+// Copyright (c) 2018 Valve Corporation
+// Copyright (c) 2018 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 "composite.h"
+
+#include "ir_context.h"
+#include "iterator.h"
+#include "spirv/1.2/GLSL.std.450.h"
+
+#include <vector>
+
+namespace spvtools {
+namespace opt {
+
+bool ExtInsMatch(const std::vector<uint32_t>& extIndices,
+                 const ir::Instruction* insInst, const uint32_t extOffset) {
+  uint32_t numIndices = static_cast<uint32_t>(extIndices.size()) - extOffset;
+  if (numIndices != insInst->NumInOperands() - 2) return false;
+  for (uint32_t i = 0; i < numIndices; ++i)
+    if (extIndices[i + extOffset] != insInst->GetSingleWordInOperand(i + 2))
+      return false;
+  return true;
+}
+
+bool ExtInsConflict(const std::vector<uint32_t>& extIndices,
+                    const ir::Instruction* insInst, const uint32_t extOffset) {
+  if (extIndices.size() - extOffset == insInst->NumInOperands() - 2)
+    return false;
+  uint32_t extNumIndices = static_cast<uint32_t>(extIndices.size()) - extOffset;
+  uint32_t insNumIndices = insInst->NumInOperands() - 2;
+  uint32_t numIndices = std::min(extNumIndices, insNumIndices);
+  for (uint32_t i = 0; i < numIndices; ++i)
+    if (extIndices[i + extOffset] != insInst->GetSingleWordInOperand(i + 2))
+      return false;
+  return true;
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/composite.h b/source/opt/composite.h
new file mode 100644 (file)
index 0000000..2153c62
--- /dev/null
@@ -0,0 +1,50 @@
+// Copyright (c) 2018 The Khronos Group Inc.
+// Copyright (c) 2018 Valve Corporation
+// Copyright (c) 2018 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.
+
+#ifndef LIBSPIRV_UTIL_COMPOSITE_PASS_H_
+#define LIBSPIRV_UTIL_COMPOSITE_PASS_H_
+
+#include <algorithm>
+#include <map>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+
+#include "basic_block.h"
+#include "def_use_manager.h"
+#include "ir_context.h"
+#include "module.h"
+
+namespace spvtools {
+namespace opt {
+
+// Return true if the extract indices in |extIndices| starting at |extOffset|
+// match indices of insert |insInst|.
+bool ExtInsMatch(const std::vector<uint32_t>& extIndices,
+                 const ir::Instruction* insInst, const uint32_t extOffset);
+
+// Return true if indices in |extIndices| starting at |extOffset| and
+// indices of insert |insInst| conflict, specifically, if the insert
+// changes bits specified by the extract, but changes either more bits
+// or less bits than the extract specifies, meaning the exact value being
+// inserted cannot be used to replace the extract.
+bool ExtInsConflict(const std::vector<uint32_t>& extIndices,
+                    const ir::Instruction* insInst, const uint32_t extOffset);
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_UTIL_COMPOSITE_PASS_H_
diff --git a/source/opt/dead_insert_elim_pass.cpp b/source/opt/dead_insert_elim_pass.cpp
new file mode 100644 (file)
index 0000000..b99a3fe
--- /dev/null
@@ -0,0 +1,310 @@
+// Copyright (c) 2018 The Khronos Group Inc.
+// Copyright (c) 2018 Valve Corporation
+// Copyright (c) 2018 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 "dead_insert_elim_pass.h"
+
+#include "composite.h"
+#include "ir_context.h"
+#include "iterator.h"
+#include "spirv/1.2/GLSL.std.450.h"
+
+#include <vector>
+
+namespace spvtools {
+namespace opt {
+
+namespace {
+
+const uint32_t kTypeVectorCountInIdx = 1;
+const uint32_t kTypeMatrixCountInIdx = 1;
+const uint32_t kTypeArrayLengthIdInIdx = 1;
+const uint32_t kTypeIntWidthInIdx = 0;
+const uint32_t kConstantValueInIdx = 0;
+const uint32_t kInsertObjectIdInIdx = 0;
+const uint32_t kInsertCompositeIdInIdx = 1;
+
+}  // anonymous namespace
+
+uint32_t DeadInsertElimPass::NumComponents(ir::Instruction* typeInst) {
+  switch (typeInst->opcode()) {
+    case SpvOpTypeVector: {
+      return typeInst->GetSingleWordInOperand(kTypeVectorCountInIdx);
+    } break;
+    case SpvOpTypeMatrix: {
+      return typeInst->GetSingleWordInOperand(kTypeMatrixCountInIdx);
+    } break;
+    case SpvOpTypeArray: {
+      uint32_t lenId =
+          typeInst->GetSingleWordInOperand(kTypeArrayLengthIdInIdx);
+      ir::Instruction* lenInst = get_def_use_mgr()->GetDef(lenId);
+      if (lenInst->opcode() != SpvOpConstant) return 0;
+      uint32_t lenTypeId = lenInst->type_id();
+      ir::Instruction* lenTypeInst = get_def_use_mgr()->GetDef(lenTypeId);
+      // TODO(greg-lunarg): Support non-32-bit array length
+      if (lenTypeInst->GetSingleWordInOperand(kTypeIntWidthInIdx) != 32)
+        return 0;
+      return lenInst->GetSingleWordInOperand(kConstantValueInIdx);
+    } break;
+    case SpvOpTypeStruct: {
+      return typeInst->NumInOperands();
+    } break;
+    default: { return 0; } break;
+  }
+}
+
+void DeadInsertElimPass::MarkInsertChain(ir::Instruction* insertChain,
+                                         std::vector<uint32_t>* pExtIndices,
+                                         uint32_t extOffset) {
+  // Not currently optimizing array inserts.
+  ir::Instruction* typeInst = get_def_use_mgr()->GetDef(insertChain->type_id());
+  if (typeInst->opcode() == SpvOpTypeArray) return;
+  // Insert chains are only composed of inserts and phis
+  if (insertChain->opcode() != SpvOpCompositeInsert &&
+      insertChain->opcode() != SpvOpPhi)
+    return;
+  // If extract indices are empty, mark all subcomponents if type
+  // is constant length.
+  if (pExtIndices == nullptr) {
+    uint32_t cnum = NumComponents(typeInst);
+    if (cnum > 0) {
+      std::vector<uint32_t> extIndices;
+      for (uint32_t i = 0; i < cnum; i++) {
+        extIndices.clear();
+        extIndices.push_back(i);
+        MarkInsertChain(insertChain, &extIndices, 0);
+      }
+      return;
+    }
+  }
+  ir::Instruction* insInst = insertChain;
+  while (insInst->opcode() == SpvOpCompositeInsert) {
+    // If no extract indices, mark insert and inserted object (which might
+    // also be an insert chain) and continue up the chain though the input
+    // composite.
+    //
+    // Note: We mark inserted objects in this function (rather than in
+    // EliminateDeadInsertsOnePass) because in some cases, we can do it
+    // more accurately here.
+    if (pExtIndices == nullptr) {
+      liveInserts_.insert(insInst->result_id());
+      uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
+      MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0);
+    }
+    // If extract indices match insert, we are done. Mark insert and
+    // inserted object.
+    else if (ExtInsMatch(*pExtIndices, insInst, extOffset)) {
+      liveInserts_.insert(insInst->result_id());
+      uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
+      MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0);
+      break;
+    }
+    // If non-matching intersection, mark insert
+    else if (ExtInsConflict(*pExtIndices, insInst, extOffset)) {
+      liveInserts_.insert(insInst->result_id());
+      // If more extract indices than insert, we are done. Use remaining
+      // extract indices to mark inserted object.
+      uint32_t numInsertIndices = insInst->NumInOperands() - 2;
+      if (pExtIndices->size() - extOffset > numInsertIndices) {
+        uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
+        MarkInsertChain(get_def_use_mgr()->GetDef(objId), pExtIndices,
+                        extOffset + numInsertIndices);
+        break;
+      }
+      // If fewer extract indices than insert, also mark inserted object and
+      // continue up chain.
+      else {
+        uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
+        MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0);
+      }
+    }
+    // Get next insert in chain
+    const uint32_t compId =
+        insInst->GetSingleWordInOperand(kInsertCompositeIdInIdx);
+    insInst = get_def_use_mgr()->GetDef(compId);
+  }
+  // If insert chain ended with phi, do recursive call on each operand
+  if (insInst->opcode() != SpvOpPhi) return;
+  // Mark phi visited to prevent potential infinite loop. If phi is already
+  // visited, return to avoid infinite loop
+  if (!visitedPhis_.insert(insInst->result_id()).second) return;
+  uint32_t icnt = 0;
+  insInst->ForEachInId([&icnt, &pExtIndices, &extOffset, this](uint32_t* idp) {
+    if (icnt % 2 == 0) {
+      ir::Instruction* pi = get_def_use_mgr()->GetDef(*idp);
+      MarkInsertChain(pi, pExtIndices, extOffset);
+    }
+    ++icnt;
+  });
+  // Unmark phi when done visiting
+  visitedPhis_.erase(insInst->result_id());
+}
+
+bool DeadInsertElimPass::EliminateDeadInserts(ir::Function* func) {
+  bool modified = false;
+  bool lastmodified = true;
+  // Each pass can delete dead instructions, thus potentially revealing
+  // new dead insertions ie insertions with no uses.
+  while (lastmodified) {
+    lastmodified = EliminateDeadInsertsOnePass(func);
+    modified |= lastmodified;
+  }
+  return modified;
+}
+
+bool DeadInsertElimPass::EliminateDeadInsertsOnePass(ir::Function* func) {
+  bool modified = false;
+  liveInserts_.clear();
+  visitedPhis_.clear();
+  // Mark all live inserts
+  for (auto bi = func->begin(); bi != func->end(); ++bi) {
+    for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
+      // Only process Inserts and composite Phis
+      SpvOp op = ii->opcode();
+      ir::Instruction* typeInst = get_def_use_mgr()->GetDef(ii->type_id());
+      if (op != SpvOpCompositeInsert &&
+          (op != SpvOpPhi || !spvOpcodeIsComposite(typeInst->opcode())))
+        continue;
+      // The marking algorithm can be expensive for large arrays and the
+      // efficacy of eliminating dead inserts into arrays is questionable.
+      // Skip optimizing array inserts for now. Just mark them live.
+      // TODO(greg-lunarg): Eliminate dead array inserts
+      if (op == SpvOpCompositeInsert) {
+        if (typeInst->opcode() == SpvOpTypeArray) {
+          liveInserts_.insert(ii->result_id());
+          continue;
+        }
+      }
+      const uint32_t id = ii->result_id();
+      get_def_use_mgr()->ForEachUser(id, [&ii, this](ir::Instruction* user) {
+        switch (user->opcode()) {
+          case SpvOpCompositeInsert:
+          case SpvOpPhi:
+            // Use by insert or phi does not initiate marking
+            break;
+          case SpvOpCompositeExtract: {
+            // Capture extract indices
+            std::vector<uint32_t> extIndices;
+            uint32_t icnt = 0;
+            user->ForEachInOperand([&icnt, &extIndices](const uint32_t* idp) {
+              if (icnt > 0) extIndices.push_back(*idp);
+              ++icnt;
+            });
+            // Mark all inserts in chain that intersect with extract
+            MarkInsertChain(&*ii, &extIndices, 0);
+          } break;
+          default: {
+            // Mark inserts in chain for all components
+            MarkInsertChain(&*ii, nullptr, 0);
+          } break;
+        }
+      });
+    }
+  }
+  // Find and disconnect dead inserts
+  std::vector<ir::Instruction*> dead_instructions;
+  for (auto bi = func->begin(); bi != func->end(); ++bi) {
+    for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
+      if (ii->opcode() != SpvOpCompositeInsert) continue;
+      const uint32_t id = ii->result_id();
+      if (liveInserts_.find(id) != liveInserts_.end()) continue;
+      const uint32_t replId =
+          ii->GetSingleWordInOperand(kInsertCompositeIdInIdx);
+      (void)context()->ReplaceAllUsesWith(id, replId);
+      dead_instructions.push_back(&*ii);
+      modified = true;
+    }
+  }
+  // DCE dead inserts
+  while (!dead_instructions.empty()) {
+    ir::Instruction* inst = dead_instructions.back();
+    dead_instructions.pop_back();
+    DCEInst(inst, [&dead_instructions](ir::Instruction* other_inst) {
+      auto i = std::find(dead_instructions.begin(), dead_instructions.end(),
+                         other_inst);
+      if (i != dead_instructions.end()) {
+        dead_instructions.erase(i);
+      }
+    });
+  }
+  return modified;
+}
+
+void DeadInsertElimPass::Initialize(ir::IRContext* c) {
+  InitializeProcessing(c);
+
+  // Initialize extension whitelist
+  InitExtensions();
+};
+
+bool DeadInsertElimPass::AllExtensionsSupported() const {
+  // If any extension not in whitelist, return false
+  for (auto& ei : get_module()->extensions()) {
+    const char* extName =
+        reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
+    if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
+      return false;
+  }
+  return true;
+}
+
+Pass::Status DeadInsertElimPass::ProcessImpl() {
+  // Do not process if any disallowed extensions are enabled
+  if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
+  // Process all entry point functions.
+  ProcessFunction pfn = [this](ir::Function* fp) {
+    return EliminateDeadInserts(fp);
+  };
+  bool modified = ProcessEntryPointCallTree(pfn, get_module());
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+}
+
+DeadInsertElimPass::DeadInsertElimPass() {}
+
+Pass::Status DeadInsertElimPass::Process(ir::IRContext* c) {
+  Initialize(c);
+  return ProcessImpl();
+}
+
+void DeadInsertElimPass::InitExtensions() {
+  extensions_whitelist_.clear();
+  extensions_whitelist_.insert({
+      "SPV_AMD_shader_explicit_vertex_parameter",
+      "SPV_AMD_shader_trinary_minmax",
+      "SPV_AMD_gcn_shader",
+      "SPV_KHR_shader_ballot",
+      "SPV_AMD_shader_ballot",
+      "SPV_AMD_gpu_shader_half_float",
+      "SPV_KHR_shader_draw_parameters",
+      "SPV_KHR_subgroup_vote",
+      "SPV_KHR_16bit_storage",
+      "SPV_KHR_device_group",
+      "SPV_KHR_multiview",
+      "SPV_NVX_multiview_per_view_attributes",
+      "SPV_NV_viewport_array2",
+      "SPV_NV_stereo_view_rendering",
+      "SPV_NV_sample_mask_override_coverage",
+      "SPV_NV_geometry_shader_passthrough",
+      "SPV_AMD_texture_gather_bias_lod",
+      "SPV_KHR_storage_buffer_storage_class",
+      "SPV_KHR_variable_pointers",
+      "SPV_AMD_gpu_shader_int16",
+      "SPV_KHR_post_depth_coverage",
+      "SPV_KHR_shader_atomic_counter_ops",
+  });
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/dead_insert_elim_pass.h b/source/opt/dead_insert_elim_pass.h
new file mode 100644 (file)
index 0000000..df13c0c
--- /dev/null
@@ -0,0 +1,88 @@
+// Copyright (c) 2018 The Khronos Group Inc.
+// Copyright (c) 2018 Valve Corporation
+// Copyright (c) 2018 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.
+
+#ifndef LIBSPIRV_OPT_DEAD_INSERT_ELIM_PASS_H_
+#define LIBSPIRV_OPT_DEAD_INSERT_ELIM_PASS_H_
+
+#include <algorithm>
+#include <map>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+
+#include "basic_block.h"
+#include "def_use_manager.h"
+#include "ir_context.h"
+#include "mem_pass.h"
+#include "module.h"
+
+namespace spvtools {
+namespace opt {
+
+// See optimizer.hpp for documentation.
+class DeadInsertElimPass : public MemPass {
+ public:
+  DeadInsertElimPass();
+  const char* name() const override { return "eliminate-dead-insert"; }
+  Status Process(ir::IRContext*) override;
+
+ private:
+  // Return the number of subcomponents in the composite type |typeId|.
+  // Return 0 if not a composite type or number of components is not a
+  // 32-bit constant.
+  uint32_t NumComponents(ir::Instruction* typeInst);
+
+  // Mark all inserts in instruction chain ending at |insertChain| with
+  // indices that intersect with extract indices |extIndices| starting with
+  // index at |extOffset|. Chains are composed solely of Inserts and Phis.
+  // Mark all inserts in chain if |extIndices| is nullptr.
+  void MarkInsertChain(ir::Instruction* insertChain,
+                       std::vector<uint32_t>* extIndices, uint32_t extOffset);
+
+  // Perform EliminateDeadInsertsOnePass(|func|) until no modification is
+  // made. Return true if modified.
+  bool EliminateDeadInserts(ir::Function* func);
+
+  // DCE all dead struct, matrix and vector inserts in |func|. An insert is
+  // dead if the value it inserts is never used. Replace any reference to the
+  // insert with its original composite. Return true if modified. Dead inserts
+  // in dependence cycles are not currently eliminated. Dead inserts into
+  // arrays are not currently eliminated.
+  bool EliminateDeadInsertsOnePass(ir::Function* func);
+
+  // Initialize extensions whitelist
+  void InitExtensions();
+
+  // Return true if all extensions in this module are allowed by this pass.
+  bool AllExtensionsSupported() const;
+
+  void Initialize(ir::IRContext* c);
+  Pass::Status ProcessImpl();
+
+  // Live inserts
+  std::unordered_set<uint32_t> liveInserts_;
+
+  // Visited phis as insert chain is traversed; used to avoid infinite loop
+  std::unordered_set<uint32_t> visitedPhis_;
+
+  // Extensions supported by this pass.
+  std::unordered_set<std::string> extensions_whitelist_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_OPT_DEAD_INSERT_ELIM_PASS_H_
index 4940fe61666c331282b722c8a1b85256abf0e974..152c07aa86c8924a64ab14f92d95a5198d9ba793 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "insert_extract_elim.h"
 
+#include "composite.h"
 #include "ir_context.h"
 #include "iterator.h"
 #include "spirv/1.2/GLSL.std.450.h"
@@ -27,14 +28,10 @@ namespace opt {
 
 namespace {
 
+const uint32_t kConstantValueInIdx = 0;
 const uint32_t kExtractCompositeIdInIdx = 0;
 const uint32_t kInsertObjectIdInIdx = 0;
 const uint32_t kInsertCompositeIdInIdx = 1;
-const uint32_t kTypeVectorCountInIdx = 1;
-const uint32_t kTypeMatrixCountInIdx = 1;
-const uint32_t kTypeArrayLengthIdInIdx = 1;
-const uint32_t kTypeIntWidthInIdx = 0;
-const uint32_t kConstantValueInIdx = 0;
 const uint32_t kVectorShuffleVec1IdInIdx = 0;
 const uint32_t kVectorShuffleVec2IdInIdx = 1;
 const uint32_t kVectorShuffleCompsInIdx = 2;
@@ -49,246 +46,6 @@ const uint32_t kFMixAIdInIdx = 4;
 
 }  // anonymous namespace
 
-bool InsertExtractElimPass::ExtInsMatch(const std::vector<uint32_t>& extIndices,
-                                        const ir::Instruction* insInst,
-                                        const uint32_t extOffset) const {
-  uint32_t numIndices = static_cast<uint32_t>(extIndices.size()) - extOffset;
-  if (numIndices != insInst->NumInOperands() - 2) return false;
-  for (uint32_t i = 0; i < numIndices; ++i)
-    if (extIndices[i + extOffset] != insInst->GetSingleWordInOperand(i + 2))
-      return false;
-  return true;
-}
-
-bool InsertExtractElimPass::ExtInsConflict(
-    const std::vector<uint32_t>& extIndices, const ir::Instruction* insInst,
-    const uint32_t extOffset) const {
-  if (extIndices.size() - extOffset == insInst->NumInOperands() - 2)
-    return false;
-  uint32_t extNumIndices = static_cast<uint32_t>(extIndices.size()) - extOffset;
-  uint32_t insNumIndices = insInst->NumInOperands() - 2;
-  uint32_t numIndices = std::min(extNumIndices, insNumIndices);
-  for (uint32_t i = 0; i < numIndices; ++i)
-    if (extIndices[i + extOffset] != insInst->GetSingleWordInOperand(i + 2))
-      return false;
-  return true;
-}
-
-bool InsertExtractElimPass::IsVectorType(uint32_t typeId) {
-  ir::Instruction* typeInst = get_def_use_mgr()->GetDef(typeId);
-  return typeInst->opcode() == SpvOpTypeVector;
-}
-
-bool InsertExtractElimPass::IsComposite(uint32_t typeId) {
-  ir::Instruction* typeInst = get_def_use_mgr()->GetDef(typeId);
-  return spvOpcodeIsComposite(typeInst->opcode());
-}
-
-uint32_t InsertExtractElimPass::NumComponents(uint32_t typeId) {
-  ir::Instruction* typeInst = get_def_use_mgr()->GetDef(typeId);
-  switch (typeInst->opcode()) {
-    case SpvOpTypeVector: {
-      return typeInst->GetSingleWordInOperand(kTypeVectorCountInIdx);
-    } break;
-    case SpvOpTypeMatrix: {
-      return typeInst->GetSingleWordInOperand(kTypeMatrixCountInIdx);
-    } break;
-    case SpvOpTypeArray: {
-      uint32_t lenId =
-          typeInst->GetSingleWordInOperand(kTypeArrayLengthIdInIdx);
-      ir::Instruction* lenInst = get_def_use_mgr()->GetDef(lenId);
-      if (lenInst->opcode() != SpvOpConstant) return 0;
-      uint32_t lenTypeId = lenInst->type_id();
-      ir::Instruction* lenTypeInst = get_def_use_mgr()->GetDef(lenTypeId);
-      // TODO(greg-lunarg): Support non-32-bit array length
-      if (lenTypeInst->GetSingleWordInOperand(kTypeIntWidthInIdx) != 32)
-        return 0;
-      return lenInst->GetSingleWordInOperand(kConstantValueInIdx);
-    } break;
-    case SpvOpTypeStruct: {
-      return typeInst->NumInOperands();
-    } break;
-    default: { return 0; } break;
-  }
-}
-
-void InsertExtractElimPass::MarkInsertChain(ir::Instruction* insertChain,
-                                            std::vector<uint32_t>* pExtIndices,
-                                            uint32_t extOffset) {
-  // Not currently optimizing array inserts.
-  ir::Instruction* typeInst = get_def_use_mgr()->GetDef(insertChain->type_id());
-  if (typeInst->opcode() == SpvOpTypeArray) return;
-  // Insert chains are only composed of inserts and phis
-  if (insertChain->opcode() != SpvOpCompositeInsert &&
-      insertChain->opcode() != SpvOpPhi)
-    return;
-  // If extract indices are empty, mark all subcomponents if type
-  // is constant length.
-  if (pExtIndices == nullptr) {
-    uint32_t cnum = NumComponents(insertChain->type_id());
-    if (cnum > 0) {
-      std::vector<uint32_t> extIndices;
-      for (uint32_t i = 0; i < cnum; i++) {
-        extIndices.clear();
-        extIndices.push_back(i);
-        MarkInsertChain(insertChain, &extIndices, 0);
-      }
-      return;
-    }
-  }
-  ir::Instruction* insInst = insertChain;
-  while (insInst->opcode() == SpvOpCompositeInsert) {
-    // If no extract indices, mark insert and inserted object (which might
-    // also be an insert chain) and continue up the chain though the input
-    // composite.
-    //
-    // Note: We mark inserted objects in this function (rather than in
-    // EliminateDeadInsertsOnePass) because in some cases, we can do it
-    // more accurately here.
-    if (pExtIndices == nullptr) {
-      liveInserts_.insert(insInst->result_id());
-      uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
-      MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0);
-    }
-    // If extract indices match insert, we are done. Mark insert and
-    // inserted object.
-    else if (ExtInsMatch(*pExtIndices, insInst, extOffset)) {
-      liveInserts_.insert(insInst->result_id());
-      uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
-      MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0);
-      break;
-    }
-    // If non-matching intersection, mark insert
-    else if (ExtInsConflict(*pExtIndices, insInst, extOffset)) {
-      liveInserts_.insert(insInst->result_id());
-      // If more extract indices than insert, we are done. Use remaining
-      // extract indices to mark inserted object.
-      uint32_t numInsertIndices = insInst->NumInOperands() - 2;
-      if (pExtIndices->size() - extOffset > numInsertIndices) {
-        uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
-        MarkInsertChain(get_def_use_mgr()->GetDef(objId), pExtIndices,
-                        extOffset + numInsertIndices);
-        break;
-      }
-      // If fewer extract indices than insert, also mark inserted object and
-      // continue up chain.
-      else {
-        uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
-        MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0);
-      }
-    }
-    // Get next insert in chain
-    const uint32_t compId =
-        insInst->GetSingleWordInOperand(kInsertCompositeIdInIdx);
-    insInst = get_def_use_mgr()->GetDef(compId);
-  }
-  // If insert chain ended with phi, do recursive call on each operand
-  if (insInst->opcode() != SpvOpPhi) return;
-  // Mark phi visited to prevent potential infinite loop. If phi is already
-  // visited, return to avoid infinite loop
-  if (!visitedPhis_.insert(insInst->result_id()).second) return;
-  uint32_t icnt = 0;
-  insInst->ForEachInId([&icnt, &pExtIndices, &extOffset, this](uint32_t* idp) {
-    if (icnt % 2 == 0) {
-      ir::Instruction* pi = get_def_use_mgr()->GetDef(*idp);
-      MarkInsertChain(pi, pExtIndices, extOffset);
-    }
-    ++icnt;
-  });
-  // Unmark phi when done visiting
-  visitedPhis_.erase(insInst->result_id());
-}
-
-bool InsertExtractElimPass::EliminateDeadInserts(ir::Function* func) {
-  bool modified = false;
-  bool lastmodified = true;
-  // Each pass can delete dead instructions, thus potentially revealing
-  // new dead insertions ie insertions with no uses.
-  while (lastmodified) {
-    lastmodified = EliminateDeadInsertsOnePass(func);
-    modified |= lastmodified;
-  }
-  return modified;
-}
-
-bool InsertExtractElimPass::EliminateDeadInsertsOnePass(ir::Function* func) {
-  bool modified = false;
-  liveInserts_.clear();
-  visitedPhis_.clear();
-  // Mark all live inserts
-  for (auto bi = func->begin(); bi != func->end(); ++bi) {
-    for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
-      // Only process Inserts and composite Phis
-      SpvOp op = ii->opcode();
-      if (op != SpvOpCompositeInsert &&
-          (op != SpvOpPhi || !IsComposite(ii->type_id())))
-        continue;
-      // The marking algorithm can be expensive for large arrays and the
-      // efficacy of eliminating dead inserts into arrays is questionable.
-      // Skip optimizing array inserts for now. Just mark them live.
-      // TODO(greg-lunarg): Eliminate dead array inserts
-      if (op == SpvOpCompositeInsert) {
-        ir::Instruction* typeInst = get_def_use_mgr()->GetDef(ii->type_id());
-        if (typeInst->opcode() == SpvOpTypeArray) {
-          liveInserts_.insert(ii->result_id());
-          continue;
-        }
-      }
-      const uint32_t id = ii->result_id();
-      get_def_use_mgr()->ForEachUser(id, [&ii, this](ir::Instruction* user) {
-        switch (user->opcode()) {
-          case SpvOpCompositeInsert:
-          case SpvOpPhi:
-            // Use by insert or phi does not initiate marking
-            break;
-          case SpvOpCompositeExtract: {
-            // Capture extract indices
-            std::vector<uint32_t> extIndices;
-            uint32_t icnt = 0;
-            user->ForEachInOperand([&icnt, &extIndices](const uint32_t* idp) {
-              if (icnt > 0) extIndices.push_back(*idp);
-              ++icnt;
-            });
-            // Mark all inserts in chain that intersect with extract
-            MarkInsertChain(&*ii, &extIndices, 0);
-          } break;
-          default: {
-            // Mark inserts in chain for all components
-            MarkInsertChain(&*ii, nullptr, 0);
-          } break;
-        }
-      });
-    }
-  }
-  // Find and disconnect dead inserts
-  std::vector<ir::Instruction*> dead_instructions;
-  for (auto bi = func->begin(); bi != func->end(); ++bi) {
-    for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
-      if (ii->opcode() != SpvOpCompositeInsert) continue;
-      const uint32_t id = ii->result_id();
-      if (liveInserts_.find(id) != liveInserts_.end()) continue;
-      const uint32_t replId =
-          ii->GetSingleWordInOperand(kInsertCompositeIdInIdx);
-      (void)context()->ReplaceAllUsesWith(id, replId);
-      dead_instructions.push_back(&*ii);
-      modified = true;
-    }
-  }
-  // DCE dead inserts
-  while (!dead_instructions.empty()) {
-    ir::Instruction* inst = dead_instructions.back();
-    dead_instructions.pop_back();
-    DCEInst(inst, [&dead_instructions](ir::Instruction* other_inst) {
-      auto i = std::find(dead_instructions.begin(), dead_instructions.end(),
-                         other_inst);
-      if (i != dead_instructions.end()) {
-        dead_instructions.erase(i);
-      }
-    });
-  }
-  return modified;
-}
-
 uint32_t InsertExtractElimPass::DoExtract(ir::Instruction* compInst,
                                           std::vector<uint32_t>* pExtIndices,
                                           uint32_t extOffset) {
@@ -437,7 +194,6 @@ bool InsertExtractElimPass::EliminateInsertExtract(ir::Function* func) {
       }
     }
   }
-  modified |= EliminateDeadInserts(func);
   return modified;
 }
 
index 5e585fecec9c3728dc279647504904feba7f6286..ed793b96c6de6efc3984e6f3615595480bf4c6d7 100644 (file)
@@ -40,50 +40,6 @@ class InsertExtractElimPass : public MemPass {
   Status Process(ir::IRContext*) override;
 
  private:
-  // Return true if the extract indices in |extIndices| starting at |extOffset|
-  // match indices of insert |insInst|.
-  bool ExtInsMatch(const std::vector<uint32_t>& extIndices,
-                   const ir::Instruction* insInst,
-                   const uint32_t extOffset) const;
-
-  // Return true if indices in |extIndices| starting at |extOffset| and
-  // indices of insert |insInst| conflict, specifically, if the insert
-  // changes bits specified by the extract, but changes either more bits
-  // or less bits than the extract specifies, meaning the exact value being
-  // inserted cannot be used to replace the extract.
-  bool ExtInsConflict(const std::vector<uint32_t>& extIndices,
-                      const ir::Instruction* insInst,
-                      const uint32_t extOffset) const;
-
-  // Return true if |typeId| is a vector type
-  bool IsVectorType(uint32_t typeId);
-
-  // Return true if |typeId| is composite.
-  bool IsComposite(uint32_t typeId);
-
-  // Return the number of subcomponents in the composite type |typeId|.
-  // Return 0 if not a composite type or number of components is not a
-  // 32-bit constant.
-  uint32_t NumComponents(uint32_t typeId);
-
-  // Mark all inserts in instruction chain ending at |insertChain| with
-  // indices that intersect with extract indices |extIndices| starting with
-  // index at |extOffset|. Chains are composed solely of Inserts and Phis.
-  // Mark all inserts in chain if |extIndices| is nullptr.
-  void MarkInsertChain(ir::Instruction* insertChain,
-                       std::vector<uint32_t>* extIndices, uint32_t extOffset);
-
-  // Perform EliminateDeadInsertsOnePass(|func|) until no modification is
-  // made. Return true if modified.
-  bool EliminateDeadInserts(ir::Function* func);
-
-  // DCE all dead struct, matrix and vector inserts in |func|. An insert is
-  // dead if the value it inserts is never used. Replace any reference to the
-  // insert with its original composite. Return true if modified. Dead inserts
-  // in dependence cycles are not currently eliminated. Dead inserts into
-  // arrays are not currently eliminated.
-  bool EliminateDeadInsertsOnePass(ir::Function* func);
-
   // Return id of component of |cinst| specified by |extIndices| starting with
   // index at |extOffset|. Return 0 if indices cannot be matched exactly.
   uint32_t DoExtract(ir::Instruction* cinst, std::vector<uint32_t>* extIndices,
@@ -104,12 +60,6 @@ class InsertExtractElimPass : public MemPass {
   void Initialize(ir::IRContext* c);
   Pass::Status ProcessImpl();
 
-  // Live inserts
-  std::unordered_set<uint32_t> liveInserts_;
-
-  // Visited phis as insert chain is traversed; used to avoid infinite loop
-  std::unordered_set<uint32_t> visitedPhis_;
-
   // Extensions supported by this pass.
   std::unordered_set<std::string> extensions_whitelist_;
 };
index 47ac3fdbd2608322bafec13ff96826a1131f0a7b..2fe82764053ecca3455acdebb0086c3a3651e202 100644 (file)
@@ -107,7 +107,9 @@ Optimizer& Optimizer::RegisterLegalizationPasses() {
           // May need loop unrolling here see
           // https://github.com/Microsoft/DirectXShaderCompiler/pull/930
           .RegisterPass(CreateDeadBranchElimPass())
-          // Get rid of unused code that leave traces of the illegal code.
+          // Get rid of unused code that contain traces of illegal code
+          // or unused references to unbound external objects
+          .RegisterPass(CreateDeadInsertElimPass())
           .RegisterPass(CreateAggressiveDCEPass());
 }
 
@@ -121,6 +123,7 @@ Optimizer& Optimizer::RegisterPerformancePasses() {
       .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
       .RegisterPass(CreateLocalSingleStoreElimPass())
       .RegisterPass(CreateInsertExtractElimPass())
+      .RegisterPass(CreateDeadInsertElimPass())
       .RegisterPass(CreateLocalMultiStoreElimPass())
       .RegisterPass(CreateCCPPass())
       .RegisterPass(CreateAggressiveDCEPass())
@@ -129,6 +132,7 @@ Optimizer& Optimizer::RegisterPerformancePasses() {
       .RegisterPass(CreateAggressiveDCEPass())
       .RegisterPass(CreateBlockMergePass())
       .RegisterPass(CreateInsertExtractElimPass())
+      .RegisterPass(CreateDeadInsertElimPass())
       .RegisterPass(CreateRedundancyEliminationPass())
       .RegisterPass(CreateCFGCleanupPass())
       // Currently exposing driver bugs resulting in crashes (#946)
@@ -145,6 +149,7 @@ Optimizer& Optimizer::RegisterSizePasses() {
       .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
       .RegisterPass(CreateLocalSingleStoreElimPass())
       .RegisterPass(CreateInsertExtractElimPass())
+      .RegisterPass(CreateDeadInsertElimPass())
       .RegisterPass(CreateLocalMultiStoreElimPass())
       .RegisterPass(CreateCCPPass())
       .RegisterPass(CreateAggressiveDCEPass())
@@ -153,6 +158,7 @@ Optimizer& Optimizer::RegisterSizePasses() {
       .RegisterPass(CreateAggressiveDCEPass())
       .RegisterPass(CreateBlockMergePass())
       .RegisterPass(CreateInsertExtractElimPass())
+      .RegisterPass(CreateDeadInsertElimPass())
       .RegisterPass(CreateRedundancyEliminationPass())
       .RegisterPass(CreateCFGCleanupPass())
       // Currently exposing driver bugs resulting in crashes (#946)
@@ -281,6 +287,11 @@ Optimizer::PassToken CreateInsertExtractElimPass() {
       MakeUnique<opt::InsertExtractElimPass>());
 }
 
+Optimizer::PassToken CreateDeadInsertElimPass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::DeadInsertElimPass>());
+}
+
 Optimizer::PassToken CreateDeadBranchElimPass() {
   return MakeUnique<Optimizer::PassToken::Impl>(
       MakeUnique<opt::DeadBranchElimPass>());
index fd34131d38329276577edeb72282fee8cff9d5f9..a2d4f0915f747f1906613693d4a043d4d27ed1ee 100644 (file)
@@ -24,6 +24,7 @@
 #include "common_uniform_elim_pass.h"
 #include "compact_ids_pass.h"
 #include "dead_branch_elim_pass.h"
+#include "dead_insert_elim_pass.h"
 #include "dead_variable_elimination.h"
 #include "eliminate_dead_constant_pass.h"
 #include "eliminate_dead_functions_pass.h"
index b7a7955810528384084bb285215edd1f66820dd8..0f53897968e2ce9b0771e72e466beb679126932b 100644 (file)
@@ -81,6 +81,11 @@ add_spvtools_unittest(TARGET pass_insert_extract_elim
   LIBS SPIRV-Tools-opt
 )
 
+add_spvtools_unittest(TARGET pass_dead_insert_elim
+  SRCS dead_insert_elim_test.cpp pass_utils.cpp
+  LIBS SPIRV-Tools-opt
+)
+
 add_spvtools_unittest(TARGET pass_local_ssa_elim
   SRCS local_ssa_elim_test.cpp pass_utils.cpp
   LIBS SPIRV-Tools-opt
diff --git a/test/opt/dead_insert_elim_test.cpp b/test/opt/dead_insert_elim_test.cpp
new file mode 100644 (file)
index 0000000..882ee2e
--- /dev/null
@@ -0,0 +1,685 @@
+// 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 "pass_fixture.h"
+#include "pass_utils.h"
+
+namespace {
+
+using namespace spvtools;
+
+using DeadInsertElimTest = PassTest<::testing::Test>;
+
+TEST_F(DeadInsertElimTest, InsertAfterInsertElim) {
+  // With two insertions to the same offset, the first is dead.
+  //
+  // Note: The SPIR-V assembly has had store/load elimination
+  // performed to allow the inserts and extracts to directly
+  // reference each other.
+  //
+  // #version 450
+  //
+  // layout (location=0) in float In0;
+  // layout (location=1) in float In1;
+  // layout (location=2) in vec2 In2;
+  // layout (location=0) out vec4 OutColor;
+  //
+  // void main()
+  // {
+  //     vec2 v = In2;
+  //     v.x = In0 + In1; // dead
+  //     v.x = 0.0;
+  //     OutColor = v.xyxy;
+  // }
+
+  const std::string before_predefs =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+OpName %In2 "In2"
+OpName %In0 "In0"
+OpName %In1 "In1"
+OpName %OutColor "OutColor"
+OpName %_Globals_ "_Globals_"
+OpMemberName %_Globals_ 0 "g_b"
+OpMemberName %_Globals_ 1 "g_n"
+OpName %_ ""
+OpDecorate %In2 Location 2
+OpDecorate %In0 Location 0
+OpDecorate %In1 Location 1
+OpDecorate %OutColor Location 0
+OpMemberDecorate %_Globals_ 0 Offset 0
+OpMemberDecorate %_Globals_ 1 Offset 4
+OpDecorate %_Globals_ Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 0
+%void = OpTypeVoid
+%11 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+%In2 = OpVariable %_ptr_Input_v2float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%In0 = OpVariable %_ptr_Input_float Input
+%In1 = OpVariable %_ptr_Input_float Input
+%uint = OpTypeInt 32 0
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%OutColor = OpVariable %_ptr_Output_v4float Output
+%int = OpTypeInt 32 1
+%_Globals_ = OpTypeStruct %uint %int
+%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
+%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
+)";
+
+  const std::string after_predefs =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+OpName %In2 "In2"
+OpName %In0 "In0"
+OpName %In1 "In1"
+OpName %OutColor "OutColor"
+OpName %_Globals_ "_Globals_"
+OpMemberName %_Globals_ 0 "g_b"
+OpMemberName %_Globals_ 1 "g_n"
+OpName %_ ""
+OpDecorate %In2 Location 2
+OpDecorate %In0 Location 0
+OpDecorate %In1 Location 1
+OpDecorate %OutColor Location 0
+OpMemberDecorate %_Globals_ 0 Offset 0
+OpMemberDecorate %_Globals_ 1 Offset 4
+OpDecorate %_Globals_ Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 0
+%void = OpTypeVoid
+%10 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+%In2 = OpVariable %_ptr_Input_v2float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%In0 = OpVariable %_ptr_Input_float Input
+%In1 = OpVariable %_ptr_Input_float Input
+%uint = OpTypeInt 32 0
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%OutColor = OpVariable %_ptr_Output_v4float Output
+%int = OpTypeInt 32 1
+%_Globals_ = OpTypeStruct %uint %int
+%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
+%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
+)";
+
+  const std::string before =
+      R"(%main = OpFunction %void None %11
+%25 = OpLabel
+%26 = OpLoad %v2float %In2
+%27 = OpLoad %float %In0
+%28 = OpLoad %float %In1
+%29 = OpFAdd %float %27 %28
+%35 = OpCompositeInsert %v2float %29 %26 0
+%37 = OpCompositeInsert %v2float %float_0 %35 0
+%33 = OpVectorShuffle %v4float %37 %37 0 1 0 1
+OpStore %OutColor %33
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(%main = OpFunction %void None %10
+%23 = OpLabel
+%24 = OpLoad %v2float %In2
+%29 = OpCompositeInsert %v2float %float_0 %24 0
+%30 = OpVectorShuffle %v4float %29 %29 0 1 0 1
+OpStore %OutColor %30
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndCheck<opt::DeadInsertElimPass>(
+      before_predefs + before, after_predefs + after, true, true);
+}
+
+TEST_F(DeadInsertElimTest, DeadInsertInChainWithPhi) {
+  // Dead insert eliminated with phi in insertion chain.
+  //
+  // Note: The SPIR-V assembly has had store/load elimination
+  // performed to allow the inserts and extracts to directly
+  // reference each other.
+  //
+  // #version 450
+  //
+  // layout (location=0) in vec4 In0;
+  // layout (location=1) in float In1;
+  // layout (location=2) in float In2;
+  // layout (location=0) out vec4 OutColor;
+  //
+  // layout(std140, binding = 0 ) uniform _Globals_
+  // {
+  //     bool g_b;
+  // };
+  //
+  // void main()
+  // {
+  //     vec4 v = In0;
+  //     v.z = In1 + In2;
+  //     if (g_b) v.w = 1.0;
+  //     OutColor = vec4(v.x,v.y,0.0,v.w);
+  // }
+
+  const std::string before_predefs =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+OpName %In0 "In0"
+OpName %In1 "In1"
+OpName %In2 "In2"
+OpName %_Globals_ "_Globals_"
+OpMemberName %_Globals_ 0 "g_b"
+OpName %_ ""
+OpName %OutColor "OutColor"
+OpDecorate %In0 Location 0
+OpDecorate %In1 Location 1
+OpDecorate %In2 Location 2
+OpMemberDecorate %_Globals_ 0 Offset 0
+OpDecorate %_Globals_ Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 0
+OpDecorate %OutColor Location 0
+%void = OpTypeVoid
+%11 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%In0 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%In1 = OpVariable %_ptr_Input_float Input
+%In2 = OpVariable %_ptr_Input_float Input
+%uint = OpTypeInt 32 0
+%_ptr_Function_float = OpTypePointer Function %float
+%_Globals_ = OpTypeStruct %uint
+%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
+%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%bool = OpTypeBool
+%uint_0 = OpConstant %uint 0
+%float_1 = OpConstant %float 1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%OutColor = OpVariable %_ptr_Output_v4float Output
+%float_0 = OpConstant %float 0
+)";
+
+  const std::string after_predefs =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+OpName %In0 "In0"
+OpName %In1 "In1"
+OpName %In2 "In2"
+OpName %_Globals_ "_Globals_"
+OpMemberName %_Globals_ 0 "g_b"
+OpName %_ ""
+OpName %OutColor "OutColor"
+OpDecorate %In0 Location 0
+OpDecorate %In1 Location 1
+OpDecorate %In2 Location 2
+OpMemberDecorate %_Globals_ 0 Offset 0
+OpDecorate %_Globals_ Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 0
+OpDecorate %OutColor Location 0
+%void = OpTypeVoid
+%10 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%In0 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%In1 = OpVariable %_ptr_Input_float Input
+%In2 = OpVariable %_ptr_Input_float Input
+%uint = OpTypeInt 32 0
+%_ptr_Function_float = OpTypePointer Function %float
+%_Globals_ = OpTypeStruct %uint
+%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
+%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%bool = OpTypeBool
+%uint_0 = OpConstant %uint 0
+%float_1 = OpConstant %float 1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%OutColor = OpVariable %_ptr_Output_v4float Output
+%float_0 = OpConstant %float 0
+)";
+
+  const std::string before =
+      R"(%main = OpFunction %void None %11
+%31 = OpLabel
+%32 = OpLoad %v4float %In0
+%33 = OpLoad %float %In1
+%34 = OpLoad %float %In2
+%35 = OpFAdd %float %33 %34
+%51 = OpCompositeInsert %v4float %35 %32 2
+%37 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
+%38 = OpLoad %uint %37
+%39 = OpINotEqual %bool %38 %uint_0
+OpSelectionMerge %40 None
+OpBranchConditional %39 %41 %40
+%41 = OpLabel
+%53 = OpCompositeInsert %v4float %float_1 %51 3
+OpBranch %40
+%40 = OpLabel
+%60 = OpPhi %v4float %51 %31 %53 %41
+%55 = OpCompositeExtract %float %60 0
+%57 = OpCompositeExtract %float %60 1
+%59 = OpCompositeExtract %float %60 3
+%49 = OpCompositeConstruct %v4float %55 %57 %float_0 %59
+OpStore %OutColor %49
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(%main = OpFunction %void None %10
+%27 = OpLabel
+%28 = OpLoad %v4float %In0
+%33 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
+%34 = OpLoad %uint %33
+%35 = OpINotEqual %bool %34 %uint_0
+OpSelectionMerge %36 None
+OpBranchConditional %35 %37 %36
+%37 = OpLabel
+%38 = OpCompositeInsert %v4float %float_1 %28 3
+OpBranch %36
+%36 = OpLabel
+%39 = OpPhi %v4float %28 %27 %38 %37
+%40 = OpCompositeExtract %float %39 0
+%41 = OpCompositeExtract %float %39 1
+%42 = OpCompositeExtract %float %39 3
+%43 = OpCompositeConstruct %v4float %40 %41 %float_0 %42
+OpStore %OutColor %43
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndCheck<opt::DeadInsertElimPass>(
+      before_predefs + before, after_predefs + after, true, true);
+}
+
+TEST_F(DeadInsertElimTest, DeadInsertTwoPasses) {
+  // Dead insert which requires two passes to eliminate
+  //
+  // Note: The SPIR-V assembly has had store/load elimination
+  // performed to allow the inserts and extracts to directly
+  // reference each other.
+  //
+  // #version 450
+  //
+  // layout (location=0) in vec4 In0;
+  // layout (location=1) in float In1;
+  // layout (location=2) in float In2;
+  // layout (location=0) out vec4 OutColor;
+  //
+  // layout(std140, binding = 0 ) uniform _Globals_
+  // {
+  //     bool g_b;
+  //     bool g_b2;
+  // };
+  //
+  // void main()
+  // {
+  //     vec4 v1, v2;
+  //     v1 = In0;
+  //     v1.y = In1 + In2; // dead, second pass
+  //     if (g_b) v1.x = 1.0;
+  //     v2.x = v1.x;
+  //     v2.y = v1.y; // dead, first pass
+  //     if (g_b2) v2.x = 0.0;
+  //     OutColor = vec4(v2.x,v2.x,0.0,1.0);
+  // }
+
+  const std::string before_predefs =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+OpName %In0 "In0"
+OpName %In1 "In1"
+OpName %In2 "In2"
+OpName %_Globals_ "_Globals_"
+OpMemberName %_Globals_ 0 "g_b"
+OpMemberName %_Globals_ 1 "g_b2"
+OpName %_ ""
+OpName %OutColor "OutColor"
+OpDecorate %In0 Location 0
+OpDecorate %In1 Location 1
+OpDecorate %In2 Location 2
+OpMemberDecorate %_Globals_ 0 Offset 0
+OpMemberDecorate %_Globals_ 1 Offset 4
+OpDecorate %_Globals_ Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 0
+OpDecorate %OutColor Location 0
+%void = OpTypeVoid
+%10 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%In0 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%In1 = OpVariable %_ptr_Input_float Input
+%In2 = OpVariable %_ptr_Input_float Input
+%uint = OpTypeInt 32 0
+%_Globals_ = OpTypeStruct %uint %uint
+%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
+%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%bool = OpTypeBool
+%uint_0 = OpConstant %uint 0
+%float_1 = OpConstant %float 1
+%int_1 = OpConstant %int 1
+%float_0 = OpConstant %float 0
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%OutColor = OpVariable %_ptr_Output_v4float Output
+%27 = OpUndef %v4float
+)";
+
+  const std::string after_predefs =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+OpName %In0 "In0"
+OpName %In1 "In1"
+OpName %In2 "In2"
+OpName %_Globals_ "_Globals_"
+OpMemberName %_Globals_ 0 "g_b"
+OpMemberName %_Globals_ 1 "g_b2"
+OpName %_ ""
+OpName %OutColor "OutColor"
+OpDecorate %In0 Location 0
+OpDecorate %In1 Location 1
+OpDecorate %In2 Location 2
+OpMemberDecorate %_Globals_ 0 Offset 0
+OpMemberDecorate %_Globals_ 1 Offset 4
+OpDecorate %_Globals_ Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 0
+OpDecorate %OutColor Location 0
+%void = OpTypeVoid
+%10 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%In0 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%In1 = OpVariable %_ptr_Input_float Input
+%In2 = OpVariable %_ptr_Input_float Input
+%uint = OpTypeInt 32 0
+%_Globals_ = OpTypeStruct %uint %uint
+%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
+%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%bool = OpTypeBool
+%uint_0 = OpConstant %uint 0
+%float_1 = OpConstant %float 1
+%int_1 = OpConstant %int 1
+%float_0 = OpConstant %float 0
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%OutColor = OpVariable %_ptr_Output_v4float Output
+%27 = OpUndef %v4float
+)";
+
+  const std::string before =
+      R"(%main = OpFunction %void None %10
+%28 = OpLabel
+%29 = OpLoad %v4float %In0
+%30 = OpLoad %float %In1
+%31 = OpLoad %float %In2
+%32 = OpFAdd %float %30 %31
+%33 = OpCompositeInsert %v4float %32 %29 1
+%34 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
+%35 = OpLoad %uint %34
+%36 = OpINotEqual %bool %35 %uint_0
+OpSelectionMerge %37 None
+OpBranchConditional %36 %38 %37
+%38 = OpLabel
+%39 = OpCompositeInsert %v4float %float_1 %33 0
+OpBranch %37
+%37 = OpLabel
+%40 = OpPhi %v4float %33 %28 %39 %38
+%41 = OpCompositeExtract %float %40 0
+%42 = OpCompositeInsert %v4float %41 %27 0
+%43 = OpCompositeExtract %float %40 1
+%44 = OpCompositeInsert %v4float %43 %42 1
+%45 = OpAccessChain %_ptr_Uniform_uint %_ %int_1
+%46 = OpLoad %uint %45
+%47 = OpINotEqual %bool %46 %uint_0
+OpSelectionMerge %48 None
+OpBranchConditional %47 %49 %48
+%49 = OpLabel
+%50 = OpCompositeInsert %v4float %float_0 %44 0
+OpBranch %48
+%48 = OpLabel
+%51 = OpPhi %v4float %44 %37 %50 %49
+%52 = OpCompositeExtract %float %51 0
+%53 = OpCompositeExtract %float %51 0
+%54 = OpCompositeConstruct %v4float %52 %53 %float_0 %float_1
+OpStore %OutColor %54
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(%main = OpFunction %void None %10
+%28 = OpLabel
+%29 = OpLoad %v4float %In0
+%34 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
+%35 = OpLoad %uint %34
+%36 = OpINotEqual %bool %35 %uint_0
+OpSelectionMerge %37 None
+OpBranchConditional %36 %38 %37
+%38 = OpLabel
+%39 = OpCompositeInsert %v4float %float_1 %29 0
+OpBranch %37
+%37 = OpLabel
+%40 = OpPhi %v4float %29 %28 %39 %38
+%41 = OpCompositeExtract %float %40 0
+%42 = OpCompositeInsert %v4float %41 %27 0
+%45 = OpAccessChain %_ptr_Uniform_uint %_ %int_1
+%46 = OpLoad %uint %45
+%47 = OpINotEqual %bool %46 %uint_0
+OpSelectionMerge %48 None
+OpBranchConditional %47 %49 %48
+%49 = OpLabel
+%50 = OpCompositeInsert %v4float %float_0 %42 0
+OpBranch %48
+%48 = OpLabel
+%51 = OpPhi %v4float %42 %37 %50 %49
+%52 = OpCompositeExtract %float %51 0
+%53 = OpCompositeExtract %float %51 0
+%54 = OpCompositeConstruct %v4float %52 %53 %float_0 %float_1
+OpStore %OutColor %54
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndCheck<opt::DeadInsertElimPass>(
+      before_predefs + before, after_predefs + after, true, true);
+}
+
+TEST_F(DeadInsertElimTest, DeadInsertInCycleToDo) {
+  // Dead insert in chain with cycle. Demonstrates analysis can handle
+  // cycles in chains.
+  //
+  // TODO(greg-lunarg): Improve algorithm to remove dead insert into v.y. Will
+  // likely require similar logic to ADCE.
+  //
+  // Note: The SPIR-V assembly has had store/load elimination
+  // performed to allow the inserts and extracts to directly
+  // reference each other.
+  //
+  // #version 450
+  //
+  // layout (location=0) in vec4 In0;
+  // layout (location=1) in float In1;
+  // layout (location=2) in float In2;
+  // layout (location=0) out vec4 OutColor;
+  //
+  // layout(std140, binding = 0 ) uniform _Globals_
+  // {
+  //     int g_n  ;
+  // };
+  //
+  // void main()
+  // {
+  //     vec2 v = vec2(0.0, 1.0);
+  //     for (int i = 0; i < g_n; i++) {
+  //       v.x = v.x + 1;
+  //       v.y = v.y * 0.9; // dead
+  //     }
+  //     OutColor = vec4(v.x);
+  // }
+
+  const std::string assembly =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %OutColor %In0 %In1 %In2
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpName %main "main"
+OpName %_Globals_ "_Globals_"
+OpMemberName %_Globals_ 0 "g_n"
+OpName %_ ""
+OpName %OutColor "OutColor"
+OpName %In0 "In0"
+OpName %In1 "In1"
+OpName %In2 "In2"
+OpMemberDecorate %_Globals_ 0 Offset 0
+OpDecorate %_Globals_ Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 0
+OpDecorate %OutColor Location 0
+OpDecorate %In0 Location 0
+OpDecorate %In1 Location 1
+OpDecorate %In2 Location 2
+%void = OpTypeVoid
+%10 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%float_0 = OpConstant %float 0
+%float_1 = OpConstant %float 1
+%16 = OpConstantComposite %v2float %float_0 %float_1
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%_Globals_ = OpTypeStruct %int
+%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
+%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+%bool = OpTypeBool
+%float_0_9 = OpConstant %float 0.9
+%int_1 = OpConstant %int 1
+%v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%OutColor = OpVariable %_ptr_Output_v4float Output
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%In0 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%In1 = OpVariable %_ptr_Input_float Input
+%In2 = OpVariable %_ptr_Input_float Input
+%main = OpFunction %void None %10
+%29 = OpLabel
+OpBranch %30
+%30 = OpLabel
+%31 = OpPhi %v2float %16 %29 %32 %33
+%34 = OpPhi %int %int_0 %29 %35 %33
+OpLoopMerge %36 %33 None
+OpBranch %37
+%37 = OpLabel
+%38 = OpAccessChain %_ptr_Uniform_int %_ %int_0
+%39 = OpLoad %int %38
+%40 = OpSLessThan %bool %34 %39
+OpBranchConditional %40 %41 %36
+%41 = OpLabel
+%42 = OpCompositeExtract %float %31 0
+%43 = OpFAdd %float %42 %float_1
+%44 = OpCompositeInsert %v2float %43 %31 0
+%45 = OpCompositeExtract %float %44 1
+%46 = OpFMul %float %45 %float_0_9
+%32 = OpCompositeInsert %v2float %46 %44 1
+OpBranch %33
+%33 = OpLabel
+%35 = OpIAdd %int %34 %int_1
+OpBranch %30
+%36 = OpLabel
+%47 = OpCompositeExtract %float %31 0
+%48 = OpCompositeConstruct %v4float %47 %47 %47 %47
+OpStore %OutColor %48
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndCheck<opt::DeadInsertElimPass>(assembly, assembly, true,
+                                                 true);
+}
+
+// TODO(greg-lunarg): Add tests to verify handling of these cases:
+//
+
+}  // anonymous namespace
index b3b9440de5f23860de233582b9f0d907c5cc74ef..afb0036f71e766252836c5e7d68fa2559b7bd2fa 100644 (file)
@@ -545,6 +545,7 @@ OpName %gl_FragColor "gl_FragColor"
 %_ptr_Function_S_t = OpTypePointer Function %S_t
 %int = OpTypeInt 32 1
 %int_1 = OpConstant %int 1
+%float_1 = OpConstant %float 1
 %uint = OpTypeInt 32 0
 %uint_1 = OpConstant %uint 1
 %_ptr_Function_float = OpTypePointer Function %float
@@ -576,8 +577,9 @@ OpFunctionEnd
 %22 = OpLabel
 %s0 = OpVariable %_ptr_Function_S_t Function
 %23 = OpLoad %S_t %s0
+%24 = OpCompositeInsert %S_t %float_1 %23 1 1
 %25 = OpLoad %v4float %BaseColor
-%26 = OpCompositeInsert %S_t %25 %23 1
+%26 = OpCompositeInsert %S_t %25 %24 1
 %27 = OpCompositeExtract %float %26 1 1
 %28 = OpCompositeConstruct %v4float %27 %float_0 %float_0 %float_0
 OpStore %gl_FragColor %28
@@ -855,663 +857,6 @@ OpFunctionEnd
       predefs + before, predefs + after, true, true);
 }
 
-TEST_F(InsertExtractElimTest, InsertAfterInsertElim) {
-  // With two insertions to the same offset, the first is dead.
-  //
-  // Note: The SPIR-V assembly has had store/load elimination
-  // performed to allow the inserts and extracts to directly
-  // reference each other.
-  //
-  // #version 450
-  //
-  // layout (location=0) in float In0;
-  // layout (location=1) in float In1;
-  // layout (location=2) in vec2 In2;
-  // layout (location=0) out vec4 OutColor;
-  //
-  // void main()
-  // {
-  //     vec2 v = In2;
-  //     v.x = In0 + In1; // dead
-  //     v.x = 0.0;
-  //     OutColor = v.xyxy;
-  // }
-
-  const std::string before_predefs =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpName %main "main"
-OpName %In2 "In2"
-OpName %In0 "In0"
-OpName %In1 "In1"
-OpName %OutColor "OutColor"
-OpName %_Globals_ "_Globals_"
-OpMemberName %_Globals_ 0 "g_b"
-OpMemberName %_Globals_ 1 "g_n"
-OpName %_ ""
-OpDecorate %In2 Location 2
-OpDecorate %In0 Location 0
-OpDecorate %In1 Location 1
-OpDecorate %OutColor Location 0
-OpMemberDecorate %_Globals_ 0 Offset 0
-OpMemberDecorate %_Globals_ 1 Offset 4
-OpDecorate %_Globals_ Block
-OpDecorate %_ DescriptorSet 0
-OpDecorate %_ Binding 0
-%void = OpTypeVoid
-%11 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%_ptr_Function_v2float = OpTypePointer Function %v2float
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%In2 = OpVariable %_ptr_Input_v2float Input
-%_ptr_Input_float = OpTypePointer Input %float
-%In0 = OpVariable %_ptr_Input_float Input
-%In1 = OpVariable %_ptr_Input_float Input
-%uint = OpTypeInt 32 0
-%_ptr_Function_float = OpTypePointer Function %float
-%float_0 = OpConstant %float 0
-%v4float = OpTypeVector %float 4
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%OutColor = OpVariable %_ptr_Output_v4float Output
-%int = OpTypeInt 32 1
-%_Globals_ = OpTypeStruct %uint %int
-%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
-%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
-)";
-
-  const std::string after_predefs =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpName %main "main"
-OpName %In2 "In2"
-OpName %In0 "In0"
-OpName %In1 "In1"
-OpName %OutColor "OutColor"
-OpName %_Globals_ "_Globals_"
-OpMemberName %_Globals_ 0 "g_b"
-OpMemberName %_Globals_ 1 "g_n"
-OpName %_ ""
-OpDecorate %In2 Location 2
-OpDecorate %In0 Location 0
-OpDecorate %In1 Location 1
-OpDecorate %OutColor Location 0
-OpMemberDecorate %_Globals_ 0 Offset 0
-OpMemberDecorate %_Globals_ 1 Offset 4
-OpDecorate %_Globals_ Block
-OpDecorate %_ DescriptorSet 0
-OpDecorate %_ Binding 0
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%_ptr_Function_v2float = OpTypePointer Function %v2float
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%In2 = OpVariable %_ptr_Input_v2float Input
-%_ptr_Input_float = OpTypePointer Input %float
-%In0 = OpVariable %_ptr_Input_float Input
-%In1 = OpVariable %_ptr_Input_float Input
-%uint = OpTypeInt 32 0
-%_ptr_Function_float = OpTypePointer Function %float
-%float_0 = OpConstant %float 0
-%v4float = OpTypeVector %float 4
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%OutColor = OpVariable %_ptr_Output_v4float Output
-%int = OpTypeInt 32 1
-%_Globals_ = OpTypeStruct %uint %int
-%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
-%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
-)";
-
-  const std::string before =
-      R"(%main = OpFunction %void None %11
-%25 = OpLabel
-%26 = OpLoad %v2float %In2
-%27 = OpLoad %float %In0
-%28 = OpLoad %float %In1
-%29 = OpFAdd %float %27 %28
-%35 = OpCompositeInsert %v2float %29 %26 0
-%37 = OpCompositeInsert %v2float %float_0 %35 0
-%33 = OpVectorShuffle %v4float %37 %37 0 1 0 1
-OpStore %OutColor %33
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string after =
-      R"(%main = OpFunction %void None %10
-%23 = OpLabel
-%24 = OpLoad %v2float %In2
-%29 = OpCompositeInsert %v2float %float_0 %24 0
-%30 = OpVectorShuffle %v4float %29 %29 0 1 0 1
-OpStore %OutColor %30
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<opt::InsertExtractElimPass>(
-      before_predefs + before, after_predefs + after, true, true);
-}
-
-TEST_F(InsertExtractElimTest, DeadInsertInChainWithPhi) {
-  // Dead insert eliminated with phi in insertion chain.
-  //
-  // Note: The SPIR-V assembly has had store/load elimination
-  // performed to allow the inserts and extracts to directly
-  // reference each other.
-  //
-  // #version 450
-  //
-  // layout (location=0) in vec4 In0;
-  // layout (location=1) in float In1;
-  // layout (location=2) in float In2;
-  // layout (location=0) out vec4 OutColor;
-  //
-  // layout(std140, binding = 0 ) uniform _Globals_
-  // {
-  //     bool g_b;
-  // };
-  //
-  // void main()
-  // {
-  //     vec4 v = In0;
-  //     v.z = In1 + In2;
-  //     if (g_b) v.w = 1.0;
-  //     OutColor = vec4(v.x,v.y,0.0,v.w);
-  // }
-
-  const std::string before_predefs =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpName %main "main"
-OpName %In0 "In0"
-OpName %In1 "In1"
-OpName %In2 "In2"
-OpName %_Globals_ "_Globals_"
-OpMemberName %_Globals_ 0 "g_b"
-OpName %_ ""
-OpName %OutColor "OutColor"
-OpDecorate %In0 Location 0
-OpDecorate %In1 Location 1
-OpDecorate %In2 Location 2
-OpMemberDecorate %_Globals_ 0 Offset 0
-OpDecorate %_Globals_ Block
-OpDecorate %_ DescriptorSet 0
-OpDecorate %_ Binding 0
-OpDecorate %OutColor Location 0
-%void = OpTypeVoid
-%11 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%In0 = OpVariable %_ptr_Input_v4float Input
-%_ptr_Input_float = OpTypePointer Input %float
-%In1 = OpVariable %_ptr_Input_float Input
-%In2 = OpVariable %_ptr_Input_float Input
-%uint = OpTypeInt 32 0
-%_ptr_Function_float = OpTypePointer Function %float
-%_Globals_ = OpTypeStruct %uint
-%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
-%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_uint = OpTypePointer Uniform %uint
-%bool = OpTypeBool
-%uint_0 = OpConstant %uint 0
-%float_1 = OpConstant %float 1
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%OutColor = OpVariable %_ptr_Output_v4float Output
-%float_0 = OpConstant %float 0
-)";
-
-  const std::string after_predefs =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpName %main "main"
-OpName %In0 "In0"
-OpName %In1 "In1"
-OpName %In2 "In2"
-OpName %_Globals_ "_Globals_"
-OpMemberName %_Globals_ 0 "g_b"
-OpName %_ ""
-OpName %OutColor "OutColor"
-OpDecorate %In0 Location 0
-OpDecorate %In1 Location 1
-OpDecorate %In2 Location 2
-OpMemberDecorate %_Globals_ 0 Offset 0
-OpDecorate %_Globals_ Block
-OpDecorate %_ DescriptorSet 0
-OpDecorate %_ Binding 0
-OpDecorate %OutColor Location 0
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%In0 = OpVariable %_ptr_Input_v4float Input
-%_ptr_Input_float = OpTypePointer Input %float
-%In1 = OpVariable %_ptr_Input_float Input
-%In2 = OpVariable %_ptr_Input_float Input
-%uint = OpTypeInt 32 0
-%_ptr_Function_float = OpTypePointer Function %float
-%_Globals_ = OpTypeStruct %uint
-%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
-%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_uint = OpTypePointer Uniform %uint
-%bool = OpTypeBool
-%uint_0 = OpConstant %uint 0
-%float_1 = OpConstant %float 1
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%OutColor = OpVariable %_ptr_Output_v4float Output
-%float_0 = OpConstant %float 0
-)";
-
-  const std::string before =
-      R"(%main = OpFunction %void None %11
-%31 = OpLabel
-%32 = OpLoad %v4float %In0
-%33 = OpLoad %float %In1
-%34 = OpLoad %float %In2
-%35 = OpFAdd %float %33 %34
-%51 = OpCompositeInsert %v4float %35 %32 2
-%37 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
-%38 = OpLoad %uint %37
-%39 = OpINotEqual %bool %38 %uint_0
-OpSelectionMerge %40 None
-OpBranchConditional %39 %41 %40
-%41 = OpLabel
-%53 = OpCompositeInsert %v4float %float_1 %51 3
-OpBranch %40
-%40 = OpLabel
-%60 = OpPhi %v4float %51 %31 %53 %41
-%55 = OpCompositeExtract %float %60 0
-%57 = OpCompositeExtract %float %60 1
-%59 = OpCompositeExtract %float %60 3
-%49 = OpCompositeConstruct %v4float %55 %57 %float_0 %59
-OpStore %OutColor %49
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string after =
-      R"(%main = OpFunction %void None %10
-%27 = OpLabel
-%28 = OpLoad %v4float %In0
-%33 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
-%34 = OpLoad %uint %33
-%35 = OpINotEqual %bool %34 %uint_0
-OpSelectionMerge %36 None
-OpBranchConditional %35 %37 %36
-%37 = OpLabel
-%38 = OpCompositeInsert %v4float %float_1 %28 3
-OpBranch %36
-%36 = OpLabel
-%39 = OpPhi %v4float %28 %27 %38 %37
-%40 = OpCompositeExtract %float %39 0
-%41 = OpCompositeExtract %float %39 1
-%42 = OpCompositeExtract %float %39 3
-%43 = OpCompositeConstruct %v4float %40 %41 %float_0 %42
-OpStore %OutColor %43
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<opt::InsertExtractElimPass>(
-      before_predefs + before, after_predefs + after, true, true);
-}
-
-TEST_F(InsertExtractElimTest, DeadInsertTwoPasses) {
-  // Dead insert which requires two passes to eliminate
-  //
-  // Note: The SPIR-V assembly has had store/load elimination
-  // performed to allow the inserts and extracts to directly
-  // reference each other.
-  //
-  // #version 450
-  //
-  // layout (location=0) in vec4 In0;
-  // layout (location=1) in float In1;
-  // layout (location=2) in float In2;
-  // layout (location=0) out vec4 OutColor;
-  //
-  // layout(std140, binding = 0 ) uniform _Globals_
-  // {
-  //     bool g_b;
-  //     bool g_b2;
-  // };
-  //
-  // void main()
-  // {
-  //     vec4 v1, v2;
-  //     v1 = In0;
-  //     v1.y = In1 + In2; // dead, second pass
-  //     if (g_b) v1.x = 1.0;
-  //     v2.x = v1.x;
-  //     v2.y = v1.y; // dead, first pass
-  //     if (g_b2) v2.x = 0.0;
-  //     OutColor = vec4(v2.x,v2.x,0.0,1.0);
-  // }
-
-  const std::string before_predefs =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpName %main "main"
-OpName %In0 "In0"
-OpName %In1 "In1"
-OpName %In2 "In2"
-OpName %_Globals_ "_Globals_"
-OpMemberName %_Globals_ 0 "g_b"
-OpMemberName %_Globals_ 1 "g_b2"
-OpName %_ ""
-OpName %OutColor "OutColor"
-OpDecorate %In0 Location 0
-OpDecorate %In1 Location 1
-OpDecorate %In2 Location 2
-OpMemberDecorate %_Globals_ 0 Offset 0
-OpMemberDecorate %_Globals_ 1 Offset 4
-OpDecorate %_Globals_ Block
-OpDecorate %_ DescriptorSet 0
-OpDecorate %_ Binding 0
-OpDecorate %OutColor Location 0
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%In0 = OpVariable %_ptr_Input_v4float Input
-%_ptr_Input_float = OpTypePointer Input %float
-%In1 = OpVariable %_ptr_Input_float Input
-%In2 = OpVariable %_ptr_Input_float Input
-%uint = OpTypeInt 32 0
-%_Globals_ = OpTypeStruct %uint %uint
-%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
-%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_uint = OpTypePointer Uniform %uint
-%bool = OpTypeBool
-%uint_0 = OpConstant %uint 0
-%float_1 = OpConstant %float 1
-%int_1 = OpConstant %int 1
-%float_0 = OpConstant %float 0
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%OutColor = OpVariable %_ptr_Output_v4float Output
-%27 = OpUndef %v4float
-)";
-
-  const std::string after_predefs =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpName %main "main"
-OpName %In0 "In0"
-OpName %In1 "In1"
-OpName %In2 "In2"
-OpName %_Globals_ "_Globals_"
-OpMemberName %_Globals_ 0 "g_b"
-OpMemberName %_Globals_ 1 "g_b2"
-OpName %_ ""
-OpName %OutColor "OutColor"
-OpDecorate %In0 Location 0
-OpDecorate %In1 Location 1
-OpDecorate %In2 Location 2
-OpMemberDecorate %_Globals_ 0 Offset 0
-OpMemberDecorate %_Globals_ 1 Offset 4
-OpDecorate %_Globals_ Block
-OpDecorate %_ DescriptorSet 0
-OpDecorate %_ Binding 0
-OpDecorate %OutColor Location 0
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%In0 = OpVariable %_ptr_Input_v4float Input
-%_ptr_Input_float = OpTypePointer Input %float
-%In1 = OpVariable %_ptr_Input_float Input
-%In2 = OpVariable %_ptr_Input_float Input
-%uint = OpTypeInt 32 0
-%_Globals_ = OpTypeStruct %uint %uint
-%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
-%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_uint = OpTypePointer Uniform %uint
-%bool = OpTypeBool
-%uint_0 = OpConstant %uint 0
-%float_1 = OpConstant %float 1
-%int_1 = OpConstant %int 1
-%float_0 = OpConstant %float 0
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%OutColor = OpVariable %_ptr_Output_v4float Output
-%27 = OpUndef %v4float
-)";
-
-  const std::string before =
-      R"(%main = OpFunction %void None %10
-%28 = OpLabel
-%29 = OpLoad %v4float %In0
-%30 = OpLoad %float %In1
-%31 = OpLoad %float %In2
-%32 = OpFAdd %float %30 %31
-%33 = OpCompositeInsert %v4float %32 %29 1
-%34 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
-%35 = OpLoad %uint %34
-%36 = OpINotEqual %bool %35 %uint_0
-OpSelectionMerge %37 None
-OpBranchConditional %36 %38 %37
-%38 = OpLabel
-%39 = OpCompositeInsert %v4float %float_1 %33 0
-OpBranch %37
-%37 = OpLabel
-%40 = OpPhi %v4float %33 %28 %39 %38
-%41 = OpCompositeExtract %float %40 0
-%42 = OpCompositeInsert %v4float %41 %27 0
-%43 = OpCompositeExtract %float %40 1
-%44 = OpCompositeInsert %v4float %43 %42 1
-%45 = OpAccessChain %_ptr_Uniform_uint %_ %int_1
-%46 = OpLoad %uint %45
-%47 = OpINotEqual %bool %46 %uint_0
-OpSelectionMerge %48 None
-OpBranchConditional %47 %49 %48
-%49 = OpLabel
-%50 = OpCompositeInsert %v4float %float_0 %44 0
-OpBranch %48
-%48 = OpLabel
-%51 = OpPhi %v4float %44 %37 %50 %49
-%52 = OpCompositeExtract %float %51 0
-%53 = OpCompositeExtract %float %51 0
-%54 = OpCompositeConstruct %v4float %52 %53 %float_0 %float_1
-OpStore %OutColor %54
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string after =
-      R"(%main = OpFunction %void None %10
-%28 = OpLabel
-%29 = OpLoad %v4float %In0
-%34 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
-%35 = OpLoad %uint %34
-%36 = OpINotEqual %bool %35 %uint_0
-OpSelectionMerge %37 None
-OpBranchConditional %36 %38 %37
-%38 = OpLabel
-%39 = OpCompositeInsert %v4float %float_1 %29 0
-OpBranch %37
-%37 = OpLabel
-%40 = OpPhi %v4float %29 %28 %39 %38
-%41 = OpCompositeExtract %float %40 0
-%42 = OpCompositeInsert %v4float %41 %27 0
-%45 = OpAccessChain %_ptr_Uniform_uint %_ %int_1
-%46 = OpLoad %uint %45
-%47 = OpINotEqual %bool %46 %uint_0
-OpSelectionMerge %48 None
-OpBranchConditional %47 %49 %48
-%49 = OpLabel
-%50 = OpCompositeInsert %v4float %float_0 %42 0
-OpBranch %48
-%48 = OpLabel
-%51 = OpPhi %v4float %42 %37 %50 %49
-%52 = OpCompositeExtract %float %51 0
-%53 = OpCompositeExtract %float %51 0
-%54 = OpCompositeConstruct %v4float %52 %53 %float_0 %float_1
-OpStore %OutColor %54
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<opt::InsertExtractElimPass>(
-      before_predefs + before, after_predefs + after, true, true);
-}
-
-TEST_F(InsertExtractElimTest, DeadInsertInCycleToDo) {
-  // Dead insert in chain with cycle. Demonstrates analysis can handle
-  // cycles in chains.
-  //
-  // TODO(greg-lunarg): Improve algorithm to remove dead insert into v.y. Will
-  // likely require similar logic to ADCE.
-  //
-  // Note: The SPIR-V assembly has had store/load elimination
-  // performed to allow the inserts and extracts to directly
-  // reference each other.
-  //
-  // #version 450
-  //
-  // layout (location=0) in vec4 In0;
-  // layout (location=1) in float In1;
-  // layout (location=2) in float In2;
-  // layout (location=0) out vec4 OutColor;
-  //
-  // layout(std140, binding = 0 ) uniform _Globals_
-  // {
-  //     int g_n  ;
-  // };
-  //
-  // void main()
-  // {
-  //     vec2 v = vec2(0.0, 1.0);
-  //     for (int i = 0; i < g_n; i++) {
-  //       v.x = v.x + 1;
-  //       v.y = v.y * 0.9; // dead
-  //     }
-  //     OutColor = vec4(v.x);
-  // }
-
-  const std::string assembly =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %OutColor %In0 %In1 %In2
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpName %main "main"
-OpName %_Globals_ "_Globals_"
-OpMemberName %_Globals_ 0 "g_n"
-OpName %_ ""
-OpName %OutColor "OutColor"
-OpName %In0 "In0"
-OpName %In1 "In1"
-OpName %In2 "In2"
-OpMemberDecorate %_Globals_ 0 Offset 0
-OpDecorate %_Globals_ Block
-OpDecorate %_ DescriptorSet 0
-OpDecorate %_ Binding 0
-OpDecorate %OutColor Location 0
-OpDecorate %In0 Location 0
-OpDecorate %In1 Location 1
-OpDecorate %In2 Location 2
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%_ptr_Function_v2float = OpTypePointer Function %v2float
-%float_0 = OpConstant %float 0
-%float_1 = OpConstant %float 1
-%16 = OpConstantComposite %v2float %float_0 %float_1
-%int = OpTypeInt 32 1
-%_ptr_Function_int = OpTypePointer Function %int
-%int_0 = OpConstant %int 0
-%_Globals_ = OpTypeStruct %int
-%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
-%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
-%_ptr_Uniform_int = OpTypePointer Uniform %int
-%bool = OpTypeBool
-%float_0_9 = OpConstant %float 0.9
-%int_1 = OpConstant %int 1
-%v4float = OpTypeVector %float 4
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%OutColor = OpVariable %_ptr_Output_v4float Output
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%In0 = OpVariable %_ptr_Input_v4float Input
-%_ptr_Input_float = OpTypePointer Input %float
-%In1 = OpVariable %_ptr_Input_float Input
-%In2 = OpVariable %_ptr_Input_float Input
-%main = OpFunction %void None %10
-%29 = OpLabel
-OpBranch %30
-%30 = OpLabel
-%31 = OpPhi %v2float %16 %29 %32 %33
-%34 = OpPhi %int %int_0 %29 %35 %33
-OpLoopMerge %36 %33 None
-OpBranch %37
-%37 = OpLabel
-%38 = OpAccessChain %_ptr_Uniform_int %_ %int_0
-%39 = OpLoad %int %38
-%40 = OpSLessThan %bool %34 %39
-OpBranchConditional %40 %41 %36
-%41 = OpLabel
-%42 = OpCompositeExtract %float %31 0
-%43 = OpFAdd %float %42 %float_1
-%44 = OpCompositeInsert %v2float %43 %31 0
-%45 = OpCompositeExtract %float %44 1
-%46 = OpFMul %float %45 %float_0_9
-%32 = OpCompositeInsert %v2float %46 %44 1
-OpBranch %33
-%33 = OpLabel
-%35 = OpIAdd %int %34 %int_1
-OpBranch %30
-%36 = OpLabel
-%47 = OpCompositeExtract %float %31 0
-%48 = OpCompositeConstruct %v4float %47 %47 %47 %47
-OpStore %OutColor %48
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<opt::InsertExtractElimPass>(assembly, assembly, true,
-                                                    true);
-}
-
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 
index c007b0aea457160d907bed317f0c747461dbedff..e072084e710d04f1e2c487c14be507f6cc7f5447 100644 (file)
@@ -118,6 +118,10 @@ Options (in lexicographical order):
   --eliminate-dead-functions
                Deletes functions that cannot be reached from entry points or
                exported functions.
+  --eliminate-dead-insert
+               Deletes unreferenced inserts into composites, most notably
+               unused stores to vector components, that are not removed by
+               aggressive dead code elimination.
   --eliminate-dead-variables
                Deletes module scope variables that are not referenced.
   --eliminate-insert-extract
@@ -425,6 +429,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-inserts")) {
+        optimizer->RegisterPass(CreateDeadInsertElimPass());
       } else if (0 == strcmp(cur_arg, "--eliminate-dead-variables")) {
         optimizer->RegisterPass(CreateDeadVariableEliminationPass());
       } else if (0 == strcmp(cur_arg, "--fold-spec-const-op-composite")) {