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 \
// 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
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
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
--- /dev/null
+// 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
--- /dev/null
+// 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_
--- /dev/null
+// 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
--- /dev/null
+// 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_
#include "insert_extract_elim.h"
+#include "composite.h"
#include "ir_context.h"
#include "iterator.h"
#include "spirv/1.2/GLSL.std.450.h"
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;
} // 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) {
}
}
}
- modified |= EliminateDeadInserts(func);
return modified;
}
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,
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_;
};
// 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());
}
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
.RegisterPass(CreateLocalSingleStoreElimPass())
.RegisterPass(CreateInsertExtractElimPass())
+ .RegisterPass(CreateDeadInsertElimPass())
.RegisterPass(CreateLocalMultiStoreElimPass())
.RegisterPass(CreateCCPPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateBlockMergePass())
.RegisterPass(CreateInsertExtractElimPass())
+ .RegisterPass(CreateDeadInsertElimPass())
.RegisterPass(CreateRedundancyEliminationPass())
.RegisterPass(CreateCFGCleanupPass())
// Currently exposing driver bugs resulting in crashes (#946)
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
.RegisterPass(CreateLocalSingleStoreElimPass())
.RegisterPass(CreateInsertExtractElimPass())
+ .RegisterPass(CreateDeadInsertElimPass())
.RegisterPass(CreateLocalMultiStoreElimPass())
.RegisterPass(CreateCCPPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateBlockMergePass())
.RegisterPass(CreateInsertExtractElimPass())
+ .RegisterPass(CreateDeadInsertElimPass())
.RegisterPass(CreateRedundancyEliminationPass())
.RegisterPass(CreateCFGCleanupPass())
// Currently exposing driver bugs resulting in crashes (#946)
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>());
#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"
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
--- /dev/null
+// 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
%_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
%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
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:
//
--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
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")) {