Opt: Delete names and decorations of dead instructions
authorGregF <greg@LunarG.com>
Wed, 26 Jul 2017 22:34:41 +0000 (18:34 -0400)
committerLei Zhang <antiagainst@gmail.com>
Wed, 26 Jul 2017 22:36:41 +0000 (18:36 -0400)
14 files changed:
source/opt/aggressive_dead_code_elim_pass.cpp
source/opt/dead_branch_elim_pass.cpp
source/opt/dead_branch_elim_pass.h
source/opt/local_access_chain_convert_pass.cpp
source/opt/local_access_chain_convert_pass.h
source/opt/local_single_block_elim_pass.cpp
source/opt/local_single_block_elim_pass.h
source/opt/local_single_store_elim_pass.cpp
source/opt/local_single_store_elim_pass.h
source/opt/local_ssa_elim_pass.cpp
source/opt/local_ssa_elim_pass.h
test/opt/dead_branch_elim_test.cpp
test/opt/local_single_block_elim.cpp
test/opt/local_single_store_elim_test.cpp

index fe9c9f2..0b1fd3b 100644 (file)
@@ -17,7 +17,6 @@
 #include "aggressive_dead_code_elim_pass.h"
 
 #include "iterator.h"
-
 #include "spirv/1.0/GLSL.std.450.h"
 
 namespace spvtools {
index 720d574..0c57307 100644 (file)
@@ -147,8 +147,34 @@ void DeadBranchElimPass::AddBranchConditional(uint32_t condId,
   bp->AddInstruction(std::move(newBranchCond));
 }
 
+void DeadBranchElimPass::KillNamesAndDecorates(uint32_t id) {
+  // TODO(greg-lunarg): Remove id from any OpGroupDecorate and 
+  // kill if no other operands.
+  if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
+    return;
+  analysis::UseList* uses = def_use_mgr_->GetUses(id);
+  if (uses == nullptr)
+    return;
+  std::list<ir::Instruction*> killList;
+  for (auto u : *uses) {
+    const SpvOp op = u.inst->opcode();
+    if (op == SpvOpName || IsDecorate(op))
+      killList.push_back(u.inst);
+  }
+  for (auto kip : killList)
+    def_use_mgr_->KillInst(kip);
+}
+
+void DeadBranchElimPass::KillNamesAndDecorates(ir::Instruction* inst) {
+  const uint32_t rId = inst->result_id();
+  if (rId == 0)
+    return;
+  KillNamesAndDecorates(rId);
+}
+
 void DeadBranchElimPass::KillAllInsts(ir::BasicBlock* bp) {
   bp->ForEachInst([this](ir::Instruction* ip) {
+    KillNamesAndDecorates(ip);
     def_use_mgr_->KillInst(ip);
   });
 }
@@ -249,6 +275,7 @@ bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) {
           useFirst ? kPhiVal0IdInIdx : kPhiVal1IdInIdx;
       const uint32_t replId = phiInst->GetSingleWordInOperand(phiValIdx);
       const uint32_t phiId = phiInst->result_id();
+      KillNamesAndDecorates(phiId);
       (void)def_use_mgr_->ReplaceAllUsesWith(phiId, replId);
       def_use_mgr_->KillInst(phiInst);
     });
@@ -304,6 +331,15 @@ void DeadBranchElimPass::Initialize(ir::Module* module) {
   InitExtensions();
 };
 
+void DeadBranchElimPass::FindNamedOrDecoratedIds() {
+  for (auto& di : module_->debugs())
+    if (di.opcode() == SpvOpName)
+      named_or_decorated_ids_.insert(di.GetSingleWordInOperand(0));
+  for (auto& ai : module_->annotations())
+    if (ai.opcode() == SpvOpDecorate || ai.opcode() == SpvOpDecorateId)
+      named_or_decorated_ids_.insert(ai.GetSingleWordInOperand(0));
+}
+
 bool DeadBranchElimPass::AllExtensionsSupported() const {
   // If any extension not in whitelist, return false
   for (auto& ei : module_->extensions()) {
@@ -320,9 +356,17 @@ Pass::Status DeadBranchElimPass::ProcessImpl() {
   // TODO(greg-lunarg): Handle non-structured control-flow.
   if (!module_->HasCapability(SpvCapabilityShader))
     return Status::SuccessWithoutChange;
+  // Do not process if module contains OpGroupDecorate. Additional
+  // support required in KillNamesAndDecorates().
+  // TODO(greg-lunarg): Add support for OpGroupDecorate
+  for (auto& ai : module_->annotations())
+    if (ai.opcode() == SpvOpGroupDecorate)
+      return Status::SuccessWithoutChange;
   // Do not process if any disallowed extensions are enabled
   if (!AllExtensionsSupported())
     return Status::SuccessWithoutChange;
+  // Collect all named and decorated ids
+  FindNamedOrDecoratedIds();
   // Process all entry point functions
   bool modified = false;
   for (const auto& e : module_->entry_points()) {
index f507f4e..912149e 100644 (file)
@@ -97,6 +97,20 @@ class DeadBranchElimPass : public Pass {
   // Return true if |labelId| has any non-phi references
   bool HasNonPhiRef(uint32_t labelId);
 
+  // Return true if |op| is supported decorate.
+  inline bool IsDecorate(uint32_t op) const {
+    return (op == SpvOpDecorate || op == SpvOpDecorateId);
+  }
+
+  // Kill all name and decorate ops using |inst|
+  void KillNamesAndDecorates(ir::Instruction* inst);
+
+  // Kill all name and decorate ops using |id|
+  void KillNamesAndDecorates(uint32_t id);
+
+  // Collect all named or decorated ids in module
+  void FindNamedOrDecoratedIds();
+
   // For function |func|, look for BranchConditionals with constant condition
   // and convert to a Branch to the indicated label. Delete resulting dead
   // blocks. Assumes only structured control flow in shader. Note some such
@@ -130,6 +144,9 @@ class DeadBranchElimPass : public Pass {
   // ComputeStructuredSuccessors() for definition.
   std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
       block2structured_succs_;
+  
+  // named or decorated ids
+  std::unordered_set<uint32_t> named_or_decorated_ids_;
 
   // Extensions supported by this pass.
   std::unordered_set<std::string> extensions_whitelist_;
index f0fb55d..1093bd8 100644 (file)
@@ -124,12 +124,27 @@ bool LocalAccessChainConvertPass::IsTargetVar(uint32_t varId) {
   return true;
 }
 
+bool LocalAccessChainConvertPass::HasOnlyNamesAndDecorates(uint32_t id) const {
+  analysis::UseList* uses = def_use_mgr_->GetUses(id);
+  if (uses == nullptr)
+    return true;
+  if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
+    return false;
+  for (auto u : *uses) {
+    const SpvOp op = u.inst->opcode();
+    if (op != SpvOpName && !IsDecorate(op))
+      return false;
+  }
+  return true;
+}
+
 void LocalAccessChainConvertPass::DeleteIfUseless(ir::Instruction* inst) {
   const uint32_t resId = inst->result_id();
   assert(resId != 0);
-  analysis::UseList* uses = def_use_mgr_->GetUses(resId);
-  if (uses == nullptr)
+  if (HasOnlyNamesAndDecorates(resId)) {
+    KillNamesAndDecorates(resId);
     def_use_mgr_->KillInst(inst);
+  }
 }
 
 void LocalAccessChainConvertPass::ReplaceAndDeleteLoad(
@@ -137,6 +152,7 @@ void LocalAccessChainConvertPass::ReplaceAndDeleteLoad(
     uint32_t replId,
     ir::Instruction* ptrInst) {
   const uint32_t loadId = loadInst->result_id();
+  KillNamesAndDecorates(loadId);
   (void) def_use_mgr_->ReplaceAllUsesWith(loadId, replId);
   // remove load instruction
   def_use_mgr_->KillInst(loadInst);
@@ -146,6 +162,31 @@ void LocalAccessChainConvertPass::ReplaceAndDeleteLoad(
   }
 }
 
+void LocalAccessChainConvertPass::KillNamesAndDecorates(uint32_t id) {
+  // TODO(greg-lunarg): Remove id from any OpGroupDecorate and 
+  // kill if no other operands.
+  if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
+    return;
+  analysis::UseList* uses = def_use_mgr_->GetUses(id);
+  if (uses == nullptr)
+    return;
+  std::list<ir::Instruction*> killList;
+  for (auto u : *uses) {
+    const SpvOp op = u.inst->opcode();
+    if (op == SpvOpName || IsDecorate(op))
+      killList.push_back(u.inst);
+  }
+  for (auto kip : killList)
+    def_use_mgr_->KillInst(kip);
+}
+
+void LocalAccessChainConvertPass::KillNamesAndDecorates(ir::Instruction* inst) {
+  const uint32_t rId = inst->result_id();
+  if (rId == 0)
+    return;
+  KillNamesAndDecorates(rId);
+}
+
 uint32_t LocalAccessChainConvertPass::GetPointeeTypeId(
     const ir::Instruction* ptrInst) const {
   const uint32_t ptrTypeId = ptrInst->type_id();
@@ -368,6 +409,15 @@ void LocalAccessChainConvertPass::Initialize(ir::Module* module) {
   InitExtensions();
 };
 
+void LocalAccessChainConvertPass::FindNamedOrDecoratedIds() {
+  for (auto& di : module_->debugs())
+    if (di.opcode() == SpvOpName)
+      named_or_decorated_ids_.insert(di.GetSingleWordInOperand(0));
+  for (auto& ai : module_->annotations())
+    if (ai.opcode() == SpvOpDecorate || ai.opcode() == SpvOpDecorateId)
+      named_or_decorated_ids_.insert(ai.GetSingleWordInOperand(0));
+}
+  
 bool LocalAccessChainConvertPass::AllExtensionsSupported() const {
   // If any extension not in whitelist, return false
   for (auto& ei : module_->extensions()) {
@@ -386,9 +436,18 @@ Pass::Status LocalAccessChainConvertPass::ProcessImpl() {
     if (inst.opcode() == SpvOpTypeInt &&
         inst.GetSingleWordInOperand(kTypeIntWidthInIdx) != 32)
       return Status::SuccessWithoutChange;
+
+  // Do not process if module contains OpGroupDecorate. Additional
+  // support required in KillNamesAndDecorates().
+  // TODO(greg-lunarg): Add support for OpGroupDecorate
+  for (auto& ai : module_->annotations())
+    if (ai.opcode() == SpvOpGroupDecorate)
+      return Status::SuccessWithoutChange;
   // Do not process if any disallowed extensions are enabled
   if (!AllExtensionsSupported())
     return Status::SuccessWithoutChange;
+  // Collect all named and decorated ids
+  FindNamedOrDecoratedIds();
   // Process all entry point functions.
   bool modified = false;
   for (auto& e : module_->entry_points()) {
index bdd7410..c56fc19 100644 (file)
@@ -72,6 +72,23 @@ class LocalAccessChainConvertPass : public Pass {
   // variables.
   bool IsTargetVar(uint32_t varId);
 
+  // Return true if |op| is supported decorate.
+  inline bool IsDecorate(uint32_t op) const {
+    return (op == SpvOpDecorate || op == SpvOpDecorateId);
+  }
+
+  // Return true if all uses of |id| are only name or decorate ops.
+  bool HasOnlyNamesAndDecorates(uint32_t id) const;
+
+  // Kill all name and decorate ops using |inst|
+  void KillNamesAndDecorates(ir::Instruction* inst);
+
+  // Kill all name and decorate ops using |id|
+  void KillNamesAndDecorates(uint32_t id);
+
+  // Collect all named or decorated ids in module
+  void FindNamedOrDecoratedIds();
+
   // Delete |inst| if it has no uses. Assumes |inst| has a non-zero resultId.
   void DeleteIfUseless(ir::Instruction* inst);
 
@@ -162,6 +179,9 @@ class LocalAccessChainConvertPass : public Pass {
   // Cache of verified non-target vars
   std::unordered_set<uint32_t> seen_non_target_vars_;
 
+  // named or decorated ids
+  std::unordered_set<uint32_t> named_or_decorated_ids_;
+
   // Extensions supported by this pass.
   std::unordered_set<std::string> extensions_whitelist_;
 
index a54ba9b..54c19ca 100644 (file)
@@ -124,6 +124,7 @@ bool LocalSingleBlockLoadStoreElimPass::IsTargetVar(uint32_t varId) {
 void LocalSingleBlockLoadStoreElimPass::ReplaceAndDeleteLoad(
     ir::Instruction* loadInst, uint32_t replId) {
   const uint32_t loadId = loadInst->result_id();
+  KillNamesAndDecorates(loadId);
   (void) def_use_mgr_->ReplaceAllUsesWith(loadId, replId);
   // TODO(greg-lunarg): Consider moving DCE into separate pass
   DCEInst(loadInst);
@@ -183,6 +184,47 @@ void LocalSingleBlockLoadStoreElimPass::AddStores(
   }
 }
 
+bool LocalSingleBlockLoadStoreElimPass::HasOnlyNamesAndDecorates(
+    uint32_t id) const {
+  analysis::UseList* uses = def_use_mgr_->GetUses(id);
+  if (uses == nullptr)
+    return true;
+  if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
+    return false;
+  for (auto u : *uses) {
+    const SpvOp op = u.inst->opcode();
+    if (op != SpvOpName && !IsDecorate(op))
+      return false;
+  }
+  return true;
+}
+
+void LocalSingleBlockLoadStoreElimPass::KillNamesAndDecorates(uint32_t id) {
+  // TODO(greg-lunarg): Remove id from any OpGroupDecorate and 
+  // kill if no other operands.
+  if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
+    return;
+  analysis::UseList* uses = def_use_mgr_->GetUses(id);
+  if (uses == nullptr)
+    return;
+  std::list<ir::Instruction*> killList;
+  for (auto u : *uses) {
+    const SpvOp op = u.inst->opcode();
+    if (op == SpvOpName || IsDecorate(op))
+      killList.push_back(u.inst);
+  }
+  for (auto kip : killList)
+    def_use_mgr_->KillInst(kip);
+}
+
+void LocalSingleBlockLoadStoreElimPass::KillNamesAndDecorates(
+    ir::Instruction* inst) {
+  const uint32_t rId = inst->result_id();
+  if (rId == 0)
+    return;
+  KillNamesAndDecorates(rId);
+}
+
 void LocalSingleBlockLoadStoreElimPass::DCEInst(ir::Instruction* inst) {
   std::queue<ir::Instruction*> deadInsts;
   deadInsts.push(inst);
@@ -202,12 +244,12 @@ void LocalSingleBlockLoadStoreElimPass::DCEInst(ir::Instruction* inst) {
     // Remember variable if dead load
     if (di->opcode() == SpvOpLoad)
       (void) GetPtr(di, &varId);
+    KillNamesAndDecorates(di);
     def_use_mgr_->KillInst(di);
     // For all operands with no remaining uses, add their instruction
     // to the dead instruction queue.
     for (auto id : ids) {
-      analysis::UseList* uses = def_use_mgr_->GetUses(id);
-      if (uses == nullptr)
+      if (HasOnlyNamesAndDecorates(id))
         deadInsts.push(def_use_mgr_->GetDef(id));
     }
     // if a load was deleted and it was the variable's
@@ -353,6 +395,15 @@ void LocalSingleBlockLoadStoreElimPass::Initialize(ir::Module* module) {
   InitExtensions();
 };
 
+void LocalSingleBlockLoadStoreElimPass::FindNamedOrDecoratedIds() {
+  for (auto& di : module_->debugs())
+    if (di.opcode() == SpvOpName)
+      named_or_decorated_ids_.insert(di.GetSingleWordInOperand(0));
+  for (auto& ai : module_->annotations())
+    if (ai.opcode() == SpvOpDecorate || ai.opcode() == SpvOpDecorateId)
+      named_or_decorated_ids_.insert(ai.GetSingleWordInOperand(0));
+}
+  
 bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const {
   // If any extension not in whitelist, return false
   for (auto& ei : module_->extensions()) {
@@ -368,10 +419,18 @@ Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() {
   // Assumes logical addressing only
   if (module_->HasCapability(SpvCapabilityAddresses))
     return Status::SuccessWithoutChange;
+  // Do not process if module contains OpGroupDecorate. Additional
+  // support required in KillNamesAndDecorates().
+  // TODO(greg-lunarg): Add support for OpGroupDecorate
+  for (auto& ai : module_->annotations())
+    if (ai.opcode() == SpvOpGroupDecorate)
+      return Status::SuccessWithoutChange;
   // If any extensions in the module are not explicitly supported,
   // return unmodified. 
   if (!AllExtensionsSupported())
     return Status::SuccessWithoutChange;
+  // Collect all named and decorated ids
+  FindNamedOrDecoratedIds();
   // Process all entry point functions
   bool modified = false;
   for (auto& e : module_->entry_points()) {
index 77c3dd8..674b699 100644 (file)
@@ -81,6 +81,23 @@ class LocalSingleBlockLoadStoreElimPass : public Pass {
   // Add stores using |ptr_id| to |insts|
   void AddStores(uint32_t ptr_id, std::queue<ir::Instruction*>* insts);
 
+  // Return true if |op| is supported decorate.
+  inline bool IsDecorate(uint32_t op) const {
+    return (op == SpvOpDecorate || op == SpvOpDecorateId);
+  }
+
+  // Return true if all uses of |id| are only name or decorate ops.
+  bool HasOnlyNamesAndDecorates(uint32_t id) const;
+
+  // Kill all name and decorate ops using |inst|
+  void KillNamesAndDecorates(ir::Instruction* inst);
+
+  // Kill all name and decorate ops using |id|
+  void KillNamesAndDecorates(uint32_t id);
+
+  // Collect all named or decorated ids in module
+  void FindNamedOrDecoratedIds();
+
   // Delete |inst| and iterate DCE on all its operands. Won't delete
   // labels. 
   void DCEInst(ir::Instruction* inst);
@@ -152,6 +169,9 @@ class LocalSingleBlockLoadStoreElimPass : public Pass {
   // from this set each time a new store of that variable is encountered.
   std::unordered_set<uint32_t> pinned_vars_;
 
+  // named or decorated ids
+  std::unordered_set<uint32_t> named_or_decorated_ids_;
+
   // Extensions supported by this pass.
   std::unordered_set<std::string> extensions_whitelist_;
 
index a083ab9..ed11c44 100644 (file)
@@ -191,7 +191,9 @@ void LocalSingleStoreElimPass::SingleStoreAnalyze(ir::Function* func) {
 
 void LocalSingleStoreElimPass::ReplaceAndDeleteLoad(
     ir::Instruction* loadInst, uint32_t replId) {
-  (void) def_use_mgr_->ReplaceAllUsesWith(loadInst->result_id(), replId);
+  const uint32_t loadId = loadInst->result_id();
+  KillNamesAndDecorates(loadId);
+  (void) def_use_mgr_->ReplaceAllUsesWith(loadId, replId);
   DCEInst(loadInst);
 }
 
@@ -358,6 +360,47 @@ void LocalSingleStoreElimPass::AddStores(
   }
 }
 
+bool LocalSingleStoreElimPass::HasOnlyNamesAndDecorates(
+    uint32_t id) const {
+  analysis::UseList* uses = def_use_mgr_->GetUses(id);
+  if (uses == nullptr)
+    return true;
+  if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
+    return false;
+  for (auto u : *uses) {
+    const SpvOp op = u.inst->opcode();
+    if (op != SpvOpName && !IsDecorate(op))
+      return false;
+  }
+  return true;
+}
+
+void LocalSingleStoreElimPass::KillNamesAndDecorates(uint32_t id) {
+  // TODO(greg-lunarg): Remove id from any OpGroupDecorate and 
+  // kill if no other operands.
+  if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
+    return;
+  analysis::UseList* uses = def_use_mgr_->GetUses(id);
+  if (uses == nullptr)
+    return;
+  std::list<ir::Instruction*> killList;
+  for (auto u : *uses) {
+    const SpvOp op = u.inst->opcode();
+    if (op == SpvOpName || IsDecorate(op))
+      killList.push_back(u.inst);
+  }
+  for (auto kip : killList)
+    def_use_mgr_->KillInst(kip);
+}
+
+void LocalSingleStoreElimPass::KillNamesAndDecorates(
+    ir::Instruction* inst) {
+  const uint32_t rId = inst->result_id();
+  if (rId == 0)
+    return;
+  KillNamesAndDecorates(rId);
+}
+
 void LocalSingleStoreElimPass::DCEInst(ir::Instruction* inst) {
   std::queue<ir::Instruction*> deadInsts;
   deadInsts.push(inst);
@@ -369,27 +412,25 @@ void LocalSingleStoreElimPass::DCEInst(ir::Instruction* inst) {
       continue;
     }
     // Remember operands
-    std::queue<uint32_t> ids;
+    std::vector<uint32_t> ids;
     di->ForEachInId([&ids](uint32_t* iid) {
-      ids.push(*iid);
+      ids.push_back(*iid);
     });
     uint32_t varId = 0;
     // Remember variable if dead load
     if (di->opcode() == SpvOpLoad)
       (void) GetPtr(di, &varId);
+    KillNamesAndDecorates(di);
     def_use_mgr_->KillInst(di);
     // For all operands with no remaining uses, add their instruction
     // to the dead instruction queue.
-    while (!ids.empty()) {
-      uint32_t id = ids.front();
-      analysis::UseList* uses = def_use_mgr_->GetUses(id);
-      if (uses == nullptr)
+    for (auto id : ids) {
+      if (HasOnlyNamesAndDecorates(id))
         deadInsts.push(def_use_mgr_->GetDef(id));
-      ids.pop();
     }
     // if a load was deleted and it was the variable's
     // last load, add all its stores to dead queue
-    if (varId != 0 && !IsLiveVar(varId)) 
+    if (varId != 0 && !IsLiveVar(varId))
       AddStores(varId, &deadInsts);
     deadInsts.pop();
   }
@@ -452,6 +493,15 @@ void LocalSingleStoreElimPass::Initialize(ir::Module* module) {
   InitExtensions();
 };
 
+void LocalSingleStoreElimPass::FindNamedOrDecoratedIds() {
+  for (auto& di : module_->debugs())
+    if (di.opcode() == SpvOpName)
+      named_or_decorated_ids_.insert(di.GetSingleWordInOperand(0));
+  for (auto& ai : module_->annotations())
+    if (ai.opcode() == SpvOpDecorate || ai.opcode() == SpvOpDecorateId)
+      named_or_decorated_ids_.insert(ai.GetSingleWordInOperand(0));
+}
+  
 bool LocalSingleStoreElimPass::AllExtensionsSupported() const {
   // If any extension not in whitelist, return false
   for (auto& ei : module_->extensions()) {
@@ -467,9 +517,17 @@ Pass::Status LocalSingleStoreElimPass::ProcessImpl() {
   // Assumes logical addressing only
   if (module_->HasCapability(SpvCapabilityAddresses))
     return Status::SuccessWithoutChange;
+  // Do not process if module contains OpGroupDecorate. Additional
+  // support required in KillNamesAndDecorates().
+  // TODO(greg-lunarg): Add support for OpGroupDecorate
+  for (auto& ai : module_->annotations())
+    if (ai.opcode() == SpvOpGroupDecorate)
+      return Status::SuccessWithoutChange;
   // Do not process if any disallowed extensions are enabled
   if (!AllExtensionsSupported())
     return Status::SuccessWithoutChange;
+  // Collect all named and decorated ids
+  FindNamedOrDecoratedIds();
   // Process all entry point functions
   bool modified = false;
   for (auto& e : module_->entry_points()) {
index 158a4c6..84b9ea6 100644 (file)
@@ -119,6 +119,23 @@ class LocalSingleStoreElimPass : public Pass {
   // Add stores using |ptr_id| to |insts|
   void AddStores(uint32_t ptr_id, std::queue<ir::Instruction*>* insts);
 
+  // Return true if |op| is supported decorate.
+  inline bool IsDecorate(uint32_t op) const {
+    return (op == SpvOpDecorate || op == SpvOpDecorateId);
+  }
+
+  // Return true if all uses of |id| are only name or decorate ops.
+  bool HasOnlyNamesAndDecorates(uint32_t id) const;
+
+  // Kill all name and decorate ops using |inst|
+  void KillNamesAndDecorates(ir::Instruction* inst);
+
+  // Kill all name and decorate ops using |id|
+  void KillNamesAndDecorates(uint32_t id);
+
+  // Collect all named or decorated ids in module
+  void FindNamedOrDecoratedIds();
+
   // Delete |inst| and iterate DCE on all its operands if they are now
   // useless. If a load is deleted and its variable has no other loads,
   // delete all its variable's stores.
@@ -214,12 +231,14 @@ class LocalSingleStoreElimPass : public Pass {
   // If block has no idom it points to itself.
   std::unordered_map<ir::BasicBlock*, ir::BasicBlock*> idom_;
 
+  // named or decorated ids
+  std::unordered_set<uint32_t> named_or_decorated_ids_;
+
   // Extensions supported by this pass.
   std::unordered_set<std::string> extensions_whitelist_;
 
   // Next unused ID
   uint32_t next_id_;
-
 };
 
 }  // namespace opt
index 43bbac7..60cb5c9 100644 (file)
@@ -175,6 +175,8 @@ bool LocalMultiStoreElimPass::HasOnlyNamesAndDecorates(uint32_t id) const {
   analysis::UseList* uses = def_use_mgr_->GetUses(id);
   if (uses == nullptr)
     return true;
+  if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
+    return false;
   for (auto u : *uses) {
     const SpvOp op = u.inst->opcode();
     if (op != SpvOpName && !IsDecorate(op))
@@ -186,6 +188,8 @@ bool LocalMultiStoreElimPass::HasOnlyNamesAndDecorates(uint32_t id) const {
 void LocalMultiStoreElimPass::KillNamesAndDecorates(uint32_t id) {
   // TODO(greg-lunarg): Remove id from any OpGroupDecorate and 
   // kill if no other operands.
+  if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
+    return;
   analysis::UseList* uses = def_use_mgr_->GetUses(id);
   if (uses == nullptr)
     return;
@@ -201,8 +205,6 @@ void LocalMultiStoreElimPass::KillNamesAndDecorates(uint32_t id) {
 }
 
 void LocalMultiStoreElimPass::KillNamesAndDecorates(ir::Instruction* inst) {
-  // TODO(greg-lunarg): Remove inst from any OpGroupDecorate and 
-  // kill if not other operands.
   const uint32_t rId = inst->result_id();
   if (rId == 0)
     return;
@@ -738,6 +740,15 @@ bool LocalMultiStoreElimPass::AllExtensionsSupported() const {
   return true;
 }
 
+void LocalMultiStoreElimPass::FindNamedOrDecoratedIds() {
+  for (auto& di : module_->debugs())
+    if (di.opcode() == SpvOpName)
+      named_or_decorated_ids_.insert(di.GetSingleWordInOperand(0));
+  for (auto& ai : module_->annotations())
+    if (ai.opcode() == SpvOpDecorate || ai.opcode() == SpvOpDecorateId)
+      named_or_decorated_ids_.insert(ai.GetSingleWordInOperand(0));
+}
+
 Pass::Status LocalMultiStoreElimPass::ProcessImpl() {
   // Assumes all control flow structured.
   // TODO(greg-lunarg): Do SSA rewrite for non-structured control flow
@@ -756,6 +767,8 @@ Pass::Status LocalMultiStoreElimPass::ProcessImpl() {
   // Do not process if any disallowed extensions are enabled
   if (!AllExtensionsSupported())
     return Status::SuccessWithoutChange;
+  // Collect all named and decorated ids
+  FindNamedOrDecoratedIds();
   // Process functions
   bool modified = false;
   for (auto& e : module_->entry_points()) {
index 3beaeea..8237c30 100644 (file)
@@ -178,6 +178,9 @@ class LocalMultiStoreElimPass : public Pass {
   // Return true if all extensions in this module are allowed by this pass.
   bool AllExtensionsSupported() const;
 
+  // Collect all named or decorated ids in module
+  void FindNamedOrDecoratedIds();
+
   // Remove remaining loads and stores of function scope variables only
   // referenced with non-access-chain loads and stores from function |func|.
   // Insert Phi functions where necessary. Running LocalAccessChainRemoval,
@@ -231,6 +234,9 @@ class LocalMultiStoreElimPass : public Pass {
   // pass ie. loads and stores.
   std::unordered_set<uint32_t> supported_ref_vars_;
 
+  // named or decorated ids
+  std::unordered_set<uint32_t> named_or_decorated_ids_;
+
   // Map from block to its structured successor blocks. See 
   // ComputeStructuredSuccessors() for definition.
   std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
index b5aaf0d..46958fb 100644 (file)
@@ -792,6 +792,111 @@ OpFunctionEnd
       predefs + before, predefs + after, true, true);
 }
 
+TEST_F(DeadBranchElimTest, DecorateDeleted) {
+  // Note: SPIR-V hand-edited to add decoration
+  // #version 140
+  // 
+  // in vec4 BaseColor;
+  // 
+  // void main()
+  // {
+  //     vec4 v = BaseColor;
+  //     if (false)
+  //       v = v * vec4(0.5,0.5,0.5,0.5);
+  //     gl_FragColor = v;
+  // }
+
+  const std::string predefs_before =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %v "v"
+OpName %BaseColor "BaseColor"
+OpName %gl_FragColor "gl_FragColor"
+OpDecorate %22 RelaxedPrecision
+%void = OpTypeVoid
+%7 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%bool = OpTypeBool
+%false = OpConstantFalse %bool
+%float_0_5 = OpConstant %float 0.5
+%15 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+)";
+
+  const std::string predefs_after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %v "v"
+OpName %BaseColor "BaseColor"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%bool = OpTypeBool
+%false = OpConstantFalse %bool
+%float_0_5 = OpConstant %float 0.5
+%16 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+)";
+
+  const std::string before =
+      R"(%main = OpFunction %void None %7
+%17 = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+%18 = OpLoad %v4float %BaseColor
+OpStore %v %18 
+OpSelectionMerge %19 None 
+OpBranchConditional %false %20 %19
+%20 = OpLabel
+%21 = OpLoad %v4float %v
+%22 = OpFMul %v4float %21 %15
+OpStore %v %22 
+OpBranch %19
+%19 = OpLabel
+%23 = OpLoad %v4float %v
+OpStore %gl_FragColor %23
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(%main = OpFunction %void None %8
+%18 = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+%19 = OpLoad %v4float %BaseColor
+OpStore %v %19
+OpBranch %20
+%20 = OpLabel
+%23 = OpLoad %v4float %v
+OpStore %gl_FragColor %23
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndCheck<opt::DeadBranchElimPass>(
+      predefs_before + before, predefs_after + after, true, true);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    More complex control flow
index 9596471..68c7d5c 100644 (file)
@@ -41,7 +41,7 @@ TEST_F(LocalSingleBlockLoadStoreElimTest, SimpleStoreLoadElim) {
   //     gl_FragColor = v;
   // }
 
-  const std::string predefs =
+  const std::string predefs_before =
       R"(OpCapability Shader
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -63,6 +63,27 @@ OpName %gl_FragColor "gl_FragColor"
 %gl_FragColor = OpVariable %_ptr_Output_v4float Output
 )";
 
+  const std::string predefs_after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %BaseColor "BaseColor"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%7 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+)";
+
   const std::string before =
       R"(%main = OpFunction %void None %7
 %13 = OpLabel
@@ -78,7 +99,6 @@ OpFunctionEnd
   const std::string after =
       R"(%main = OpFunction %void None %7
 %13 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
 %14 = OpLoad %v4float %BaseColor
 OpStore %gl_FragColor %14
 OpReturn
@@ -86,7 +106,7 @@ OpFunctionEnd
 )";
 
   SinglePassRunAndCheck<opt::LocalSingleBlockLoadStoreElimPass>(
-      predefs + before, predefs + after, true, true);
+      predefs_before + before, predefs_after + after, true, true);
 }
 
 TEST_F(LocalSingleBlockLoadStoreElimTest, SimpleLoadLoadElim) {
@@ -208,7 +228,7 @@ TEST_F(LocalSingleBlockLoadStoreElimTest,
   //     gl_FragColor = v/f;
   // }
 
-  const std::string predefs =
+  const std::string predefs_before =
       R"(OpCapability Shader
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -237,6 +257,34 @@ OpDecorate %Idx Flat
 %gl_FragColor = OpVariable %_ptr_Output_v4float Output
 )";
 
+  const std::string predefs_after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %Idx %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %v "v"
+OpName %BaseColor "BaseColor"
+OpName %Idx "Idx"
+OpName %gl_FragColor "gl_FragColor"
+OpDecorate %Idx Flat
+%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%_ptr_Function_float = OpTypePointer Function %float
+%int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%Idx = OpVariable %_ptr_Input_int Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+)";
+
   const std::string before =
       R"(%main = OpFunction %void None %9
 %18 = OpLabel
@@ -261,7 +309,6 @@ OpFunctionEnd
       R"(%main = OpFunction %void None %9
 %18 = OpLabel
 %v = OpVariable %_ptr_Function_v4float Function
-%f = OpVariable %_ptr_Function_float Function
 %19 = OpLoad %v4float %BaseColor
 OpStore %v %19
 %20 = OpLoad %int %Idx
@@ -275,7 +322,7 @@ OpFunctionEnd
 )";
 
   SinglePassRunAndCheck<opt::LocalSingleBlockLoadStoreElimPass>(
-      predefs + before, predefs + after, true, true);
+      predefs_before + before, predefs_after + after, true, true);
 }
 
 TEST_F(LocalSingleBlockLoadStoreElimTest, NoElimIfInterveningAccessChainStore) {
@@ -407,7 +454,7 @@ TEST_F(LocalSingleBlockLoadStoreElimTest, ElimIfCopyObjectInFunction) {
   //   gl_FragData[1] = v2;
   // }
 
-  const std::string predefs =
+  const std::string predefs_before =
       R"(OpCapability Shader
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -438,6 +485,35 @@ OpName %v2 "v2"
 %int_1 = OpConstant %int 1
 )";
 
+  const std::string predefs_after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %gl_FragData
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %BaseColor "BaseColor"
+OpName %gl_FragData "gl_FragData"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_arr_v4float_uint_32 = OpTypeArray %v4float %uint_32
+%_ptr_Output__arr_v4float_uint_32 = OpTypePointer Output %_arr_v4float_uint_32
+%gl_FragData = OpVariable %_ptr_Output__arr_v4float_uint_32 Output
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%float_0_5 = OpConstant %float 0.5
+%int_1 = OpConstant %int 1
+)";
+
   const std::string before =
       R"(%main = OpFunction %void None %8
 %22 = OpLabel
@@ -462,8 +538,6 @@ OpFunctionEnd
   const std::string after =
       R"(%main = OpFunction %void None %8
 %22 = OpLabel
-%v1 = OpVariable %_ptr_Function_v4float Function
-%v2 = OpVariable %_ptr_Function_v4float Function
 %23 = OpLoad %v4float %BaseColor
 %25 = OpAccessChain %_ptr_Output_v4float %gl_FragData %int_0
 OpStore %25 %23
@@ -476,7 +550,7 @@ OpFunctionEnd
 )";
 
   SinglePassRunAndCheck<opt::LocalSingleBlockLoadStoreElimPass>(
-      predefs + before, predefs + after, true, true);
+      predefs_before + before, predefs_after + after, true, true);
 }
 
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
index a05952f..cc74dab 100644 (file)
@@ -40,7 +40,7 @@ TEST_F(LocalSingleStoreElimTest, PositiveAndNegative) {
   //     gl_FragColor = v + f;
   // }
 
-  const std::string predefs =
+  const std::string predefs_before =
       R"(OpCapability Shader
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -69,6 +69,34 @@ OpName %gl_FragColor "gl_FragColor"
 %gl_FragColor = OpVariable %_ptr_Output_v4float Output
 )";
 
+  const std::string predefs_after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %fi %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %BaseColor "BaseColor"
+OpName %f "f"
+OpName %fi "fi"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%_ptr_Function_float = OpTypePointer Function %float
+%_ptr_Input_float = OpTypePointer Input %float
+%fi = OpVariable %_ptr_Input_float Input
+%float_0 = OpConstant %float 0
+%bool = OpTypeBool
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+)";
+
   const std::string before =
       R"(%main = OpFunction %void None %9
 %19 = OpLabel
@@ -98,7 +126,6 @@ OpFunctionEnd
   const std::string after =
       R"(%main = OpFunction %void None %9
 %19 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
 %f = OpVariable %_ptr_Function_float Function
 %20 = OpLoad %v4float %BaseColor
 %21 = OpLoad %float %fi
@@ -120,7 +147,8 @@ OpFunctionEnd
 )";
 
   SinglePassRunAndCheck<opt::LocalSingleStoreElimPass>(
-      predefs + before, predefs + after, true, true);
+      predefs_before + before, 
+      predefs_after + after, true, true);
 }
 
 TEST_F(LocalSingleStoreElimTest, MultipleLoads) {
@@ -140,7 +168,7 @@ TEST_F(LocalSingleStoreElimTest, MultipleLoads) {
   //     gl_FragColor = v + f;
   // }
 
-  const std::string predefs =
+  const std::string predefs_before =
       R"(OpCapability Shader
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -169,6 +197,34 @@ OpName %gl_FragColor "gl_FragColor"
 %gl_FragColor = OpVariable %_ptr_Output_v4float Output
 )";
 
+  const std::string predefs_after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %fi %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %BaseColor "BaseColor"
+OpName %fi "fi"
+OpName %r "r"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%fi = OpVariable %_ptr_Input_float Input
+%float_0 = OpConstant %float 0
+%bool = OpTypeBool
+%float_1 = OpConstant %float 1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+)";
+
   const std::string before =
       R"(%main = OpFunction %void None %9
 %19 = OpLabel
@@ -200,7 +256,6 @@ OpFunctionEnd
   const std::string after =
       R"(%main = OpFunction %void None %9
 %19 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
 %r = OpVariable %_ptr_Function_v4float Function
 %20 = OpLoad %v4float %BaseColor
 %21 = OpLoad %float %fi
@@ -223,7 +278,8 @@ OpFunctionEnd
 )";
 
   SinglePassRunAndCheck<opt::LocalSingleStoreElimPass>(
-      predefs + before, predefs + after, true, true);
+      predefs_before + before, 
+      predefs_after + after, true, true);
 }
 
 TEST_F(LocalSingleStoreElimTest, NoStoreElimWithInterveningAccessChainLoad) {
@@ -240,7 +296,7 @@ TEST_F(LocalSingleStoreElimTest, NoStoreElimWithInterveningAccessChainLoad) {
   //     gl_FragColor = v * f;
   // }
 
-  const std::string predefs =
+  const std::string predefs_before =
       R"(OpCapability Shader
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -266,6 +322,31 @@ OpName %gl_FragColor "gl_FragColor"
 %gl_FragColor = OpVariable %_ptr_Output_v4float Output
 )";
 
+  const std::string predefs_after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %v "v"
+OpName %BaseColor "BaseColor"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%_ptr_Function_float = OpTypePointer Function %float
+%uint = OpTypeInt 32 0
+%uint_3 = OpConstant %uint 3
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+)";
+
   const std::string before =
       R"(%main = OpFunction %void None %8
 %17 = OpLabel
@@ -288,7 +369,6 @@ OpFunctionEnd
       R"(%main = OpFunction %void None %8
 %17 = OpLabel
 %v = OpVariable %_ptr_Function_v4float Function
-%f = OpVariable %_ptr_Function_float Function
 %18 = OpLoad %v4float %BaseColor
 OpStore %v %18
 %19 = OpAccessChain %_ptr_Function_float %v %uint_3
@@ -300,7 +380,8 @@ OpFunctionEnd
 )";
 
   SinglePassRunAndCheck<opt::LocalSingleStoreElimPass>(
-      predefs + before, predefs + after, true, true);
+      predefs_before + before, 
+      predefs_after + after, true, true);
 }
 
 TEST_F(LocalSingleStoreElimTest, NoReplaceOfDominatingPartialStore) {
@@ -375,7 +456,7 @@ TEST_F(LocalSingleStoreElimTest, ElimIfCopyObjectInFunction) {
   //     gl_FragColor = v + f;
   // }
 
-  const std::string predefs =
+  const std::string predefs_before =
       R"(OpCapability Shader
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -404,6 +485,34 @@ OpName %gl_FragColor "gl_FragColor"
 %gl_FragColor = OpVariable %_ptr_Output_v4float Output
 )";
 
+  const std::string predefs_after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %fi %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %BaseColor "BaseColor"
+OpName %f "f"
+OpName %fi "fi"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%_ptr_Function_float = OpTypePointer Function %float
+%_ptr_Input_float = OpTypePointer Input %float
+%fi = OpVariable %_ptr_Input_float Input
+%float_0 = OpConstant %float 0
+%bool = OpTypeBool
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+)";
+
   const std::string before =
       R"(%main = OpFunction %void None %9
 %19 = OpLabel
@@ -434,7 +543,6 @@ OpFunctionEnd
   const std::string after =
       R"(%main = OpFunction %void None %9
 %19 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
 %f = OpVariable %_ptr_Function_float Function
 %20 = OpLoad %v4float %BaseColor
 %21 = OpLoad %float %fi
@@ -456,7 +564,7 @@ OpFunctionEnd
 )";
 
   SinglePassRunAndCheck<opt::LocalSingleStoreElimPass>(
-      predefs + before, predefs + after, true, true);
+      predefs_before + before, predefs_after + after, true, true);
 }
 
 TEST_F(LocalSingleStoreElimTest, NoOptIfStoreNotDominating) {