InsertExtractElim: Split out DeadInsertElim as separate pass

This commit is contained in:
GregF 2018-01-26 17:05:33 -07:00 committed by Steven Perron
parent 1b46f7ecad
commit f28b106173
15 changed files with 1235 additions and 955 deletions

View File

@ -62,9 +62,11 @@ SPVTOOLS_OPT_SRC_FILES := \
source/opt/cfg_cleanup_pass.cpp \
source/opt/ccp_pass.cpp \
source/opt/compact_ids_pass.cpp \
source/opt/composite.cpp \
source/opt/common_uniform_elim_pass.cpp \
source/opt/constants.cpp \
source/opt/dead_branch_elim_pass.cpp \
source/opt/dead_insert_elim_pass.cpp \
source/opt/dead_variable_elimination.cpp \
source/opt/decoration_manager.cpp \
source/opt/def_use_manager.cpp \

View File

@ -370,6 +370,21 @@ Optimizer::PassToken CreateLocalSingleStoreElimPass();
// converted to inserts and extracts and local loads and stores are eliminated.
Optimizer::PassToken CreateInsertExtractElimPass();
// Creates a dead insert elimination pass.
// This pass processes each entry point function in the module, searching for
// unreferenced inserts into composite types. These are most often unused
// stores to vector components. They are unused because they are never
// referenced, or because there is another insert to the same component between
// the insert and the reference. After removing the inserts, dead code
// elimination is attempted on the inserted values.
//
// This pass performs best after access chains are converted to inserts and
// extracts and local loads and stores are eliminated. While executing this
// pass can be advantageous on its own, it is also advantageous to execute
// this pass after CreateInsertExtractPass() as it will remove any unused
// inserts created by that pass.
Optimizer::PassToken CreateDeadInsertElimPass();
// Creates a pass to consolidate uniform references.
// For each entry point function in the module, first change all constant index
// access chain loads into equivalent composite extracts. Then consolidate

View File

@ -21,8 +21,10 @@ add_library(SPIRV-Tools-opt
cfg.h
common_uniform_elim_pass.h
compact_ids_pass.h
composite.h
constants.h
dead_branch_elim_pass.h
dead_insert_elim_pass.h
dead_variable_elimination.h
decoration_manager.h
def_use_manager.h
@ -83,8 +85,10 @@ add_library(SPIRV-Tools-opt
cfg.cpp
common_uniform_elim_pass.cpp
compact_ids_pass.cpp
composite.cpp
constants.cpp
dead_branch_elim_pass.cpp
dead_insert_elim_pass.cpp
dead_variable_elimination.cpp
decoration_manager.cpp
def_use_manager.cpp

52
source/opt/composite.cpp Normal file
View File

@ -0,0 +1,52 @@
// Copyright (c) 2018 The Khronos Group Inc.
// Copyright (c) 2018 Valve Corporation
// Copyright (c) 2018 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "composite.h"
#include "ir_context.h"
#include "iterator.h"
#include "spirv/1.2/GLSL.std.450.h"
#include <vector>
namespace spvtools {
namespace opt {
bool ExtInsMatch(const std::vector<uint32_t>& extIndices,
const ir::Instruction* insInst, const uint32_t extOffset) {
uint32_t numIndices = static_cast<uint32_t>(extIndices.size()) - extOffset;
if (numIndices != insInst->NumInOperands() - 2) return false;
for (uint32_t i = 0; i < numIndices; ++i)
if (extIndices[i + extOffset] != insInst->GetSingleWordInOperand(i + 2))
return false;
return true;
}
bool ExtInsConflict(const std::vector<uint32_t>& extIndices,
const ir::Instruction* insInst, const uint32_t extOffset) {
if (extIndices.size() - extOffset == insInst->NumInOperands() - 2)
return false;
uint32_t extNumIndices = static_cast<uint32_t>(extIndices.size()) - extOffset;
uint32_t insNumIndices = insInst->NumInOperands() - 2;
uint32_t numIndices = std::min(extNumIndices, insNumIndices);
for (uint32_t i = 0; i < numIndices; ++i)
if (extIndices[i + extOffset] != insInst->GetSingleWordInOperand(i + 2))
return false;
return true;
}
} // namespace opt
} // namespace spvtools

50
source/opt/composite.h Normal file
View File

@ -0,0 +1,50 @@
// Copyright (c) 2018 The Khronos Group Inc.
// Copyright (c) 2018 Valve Corporation
// Copyright (c) 2018 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef LIBSPIRV_UTIL_COMPOSITE_PASS_H_
#define LIBSPIRV_UTIL_COMPOSITE_PASS_H_
#include <algorithm>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include "basic_block.h"
#include "def_use_manager.h"
#include "ir_context.h"
#include "module.h"
namespace spvtools {
namespace opt {
// Return true if the extract indices in |extIndices| starting at |extOffset|
// match indices of insert |insInst|.
bool ExtInsMatch(const std::vector<uint32_t>& extIndices,
const ir::Instruction* insInst, const uint32_t extOffset);
// Return true if indices in |extIndices| starting at |extOffset| and
// indices of insert |insInst| conflict, specifically, if the insert
// changes bits specified by the extract, but changes either more bits
// or less bits than the extract specifies, meaning the exact value being
// inserted cannot be used to replace the extract.
bool ExtInsConflict(const std::vector<uint32_t>& extIndices,
const ir::Instruction* insInst, const uint32_t extOffset);
} // namespace opt
} // namespace spvtools
#endif // LIBSPIRV_UTIL_COMPOSITE_PASS_H_

View File

@ -0,0 +1,310 @@
// Copyright (c) 2018 The Khronos Group Inc.
// Copyright (c) 2018 Valve Corporation
// Copyright (c) 2018 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dead_insert_elim_pass.h"
#include "composite.h"
#include "ir_context.h"
#include "iterator.h"
#include "spirv/1.2/GLSL.std.450.h"
#include <vector>
namespace spvtools {
namespace opt {
namespace {
const uint32_t kTypeVectorCountInIdx = 1;
const uint32_t kTypeMatrixCountInIdx = 1;
const uint32_t kTypeArrayLengthIdInIdx = 1;
const uint32_t kTypeIntWidthInIdx = 0;
const uint32_t kConstantValueInIdx = 0;
const uint32_t kInsertObjectIdInIdx = 0;
const uint32_t kInsertCompositeIdInIdx = 1;
} // anonymous namespace
uint32_t DeadInsertElimPass::NumComponents(ir::Instruction* typeInst) {
switch (typeInst->opcode()) {
case SpvOpTypeVector: {
return typeInst->GetSingleWordInOperand(kTypeVectorCountInIdx);
} break;
case SpvOpTypeMatrix: {
return typeInst->GetSingleWordInOperand(kTypeMatrixCountInIdx);
} break;
case SpvOpTypeArray: {
uint32_t lenId =
typeInst->GetSingleWordInOperand(kTypeArrayLengthIdInIdx);
ir::Instruction* lenInst = get_def_use_mgr()->GetDef(lenId);
if (lenInst->opcode() != SpvOpConstant) return 0;
uint32_t lenTypeId = lenInst->type_id();
ir::Instruction* lenTypeInst = get_def_use_mgr()->GetDef(lenTypeId);
// TODO(greg-lunarg): Support non-32-bit array length
if (lenTypeInst->GetSingleWordInOperand(kTypeIntWidthInIdx) != 32)
return 0;
return lenInst->GetSingleWordInOperand(kConstantValueInIdx);
} break;
case SpvOpTypeStruct: {
return typeInst->NumInOperands();
} break;
default: { return 0; } break;
}
}
void DeadInsertElimPass::MarkInsertChain(ir::Instruction* insertChain,
std::vector<uint32_t>* pExtIndices,
uint32_t extOffset) {
// Not currently optimizing array inserts.
ir::Instruction* typeInst = get_def_use_mgr()->GetDef(insertChain->type_id());
if (typeInst->opcode() == SpvOpTypeArray) return;
// Insert chains are only composed of inserts and phis
if (insertChain->opcode() != SpvOpCompositeInsert &&
insertChain->opcode() != SpvOpPhi)
return;
// If extract indices are empty, mark all subcomponents if type
// is constant length.
if (pExtIndices == nullptr) {
uint32_t cnum = NumComponents(typeInst);
if (cnum > 0) {
std::vector<uint32_t> extIndices;
for (uint32_t i = 0; i < cnum; i++) {
extIndices.clear();
extIndices.push_back(i);
MarkInsertChain(insertChain, &extIndices, 0);
}
return;
}
}
ir::Instruction* insInst = insertChain;
while (insInst->opcode() == SpvOpCompositeInsert) {
// If no extract indices, mark insert and inserted object (which might
// also be an insert chain) and continue up the chain though the input
// composite.
//
// Note: We mark inserted objects in this function (rather than in
// EliminateDeadInsertsOnePass) because in some cases, we can do it
// more accurately here.
if (pExtIndices == nullptr) {
liveInserts_.insert(insInst->result_id());
uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0);
}
// If extract indices match insert, we are done. Mark insert and
// inserted object.
else if (ExtInsMatch(*pExtIndices, insInst, extOffset)) {
liveInserts_.insert(insInst->result_id());
uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0);
break;
}
// If non-matching intersection, mark insert
else if (ExtInsConflict(*pExtIndices, insInst, extOffset)) {
liveInserts_.insert(insInst->result_id());
// If more extract indices than insert, we are done. Use remaining
// extract indices to mark inserted object.
uint32_t numInsertIndices = insInst->NumInOperands() - 2;
if (pExtIndices->size() - extOffset > numInsertIndices) {
uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
MarkInsertChain(get_def_use_mgr()->GetDef(objId), pExtIndices,
extOffset + numInsertIndices);
break;
}
// If fewer extract indices than insert, also mark inserted object and
// continue up chain.
else {
uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0);
}
}
// Get next insert in chain
const uint32_t compId =
insInst->GetSingleWordInOperand(kInsertCompositeIdInIdx);
insInst = get_def_use_mgr()->GetDef(compId);
}
// If insert chain ended with phi, do recursive call on each operand
if (insInst->opcode() != SpvOpPhi) return;
// Mark phi visited to prevent potential infinite loop. If phi is already
// visited, return to avoid infinite loop
if (!visitedPhis_.insert(insInst->result_id()).second) return;
uint32_t icnt = 0;
insInst->ForEachInId([&icnt, &pExtIndices, &extOffset, this](uint32_t* idp) {
if (icnt % 2 == 0) {
ir::Instruction* pi = get_def_use_mgr()->GetDef(*idp);
MarkInsertChain(pi, pExtIndices, extOffset);
}
++icnt;
});
// Unmark phi when done visiting
visitedPhis_.erase(insInst->result_id());
}
bool DeadInsertElimPass::EliminateDeadInserts(ir::Function* func) {
bool modified = false;
bool lastmodified = true;
// Each pass can delete dead instructions, thus potentially revealing
// new dead insertions ie insertions with no uses.
while (lastmodified) {
lastmodified = EliminateDeadInsertsOnePass(func);
modified |= lastmodified;
}
return modified;
}
bool DeadInsertElimPass::EliminateDeadInsertsOnePass(ir::Function* func) {
bool modified = false;
liveInserts_.clear();
visitedPhis_.clear();
// Mark all live inserts
for (auto bi = func->begin(); bi != func->end(); ++bi) {
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
// Only process Inserts and composite Phis
SpvOp op = ii->opcode();
ir::Instruction* typeInst = get_def_use_mgr()->GetDef(ii->type_id());
if (op != SpvOpCompositeInsert &&
(op != SpvOpPhi || !spvOpcodeIsComposite(typeInst->opcode())))
continue;
// The marking algorithm can be expensive for large arrays and the
// efficacy of eliminating dead inserts into arrays is questionable.
// Skip optimizing array inserts for now. Just mark them live.
// TODO(greg-lunarg): Eliminate dead array inserts
if (op == SpvOpCompositeInsert) {
if (typeInst->opcode() == SpvOpTypeArray) {
liveInserts_.insert(ii->result_id());
continue;
}
}
const uint32_t id = ii->result_id();
get_def_use_mgr()->ForEachUser(id, [&ii, this](ir::Instruction* user) {
switch (user->opcode()) {
case SpvOpCompositeInsert:
case SpvOpPhi:
// Use by insert or phi does not initiate marking
break;
case SpvOpCompositeExtract: {
// Capture extract indices
std::vector<uint32_t> extIndices;
uint32_t icnt = 0;
user->ForEachInOperand([&icnt, &extIndices](const uint32_t* idp) {
if (icnt > 0) extIndices.push_back(*idp);
++icnt;
});
// Mark all inserts in chain that intersect with extract
MarkInsertChain(&*ii, &extIndices, 0);
} break;
default: {
// Mark inserts in chain for all components
MarkInsertChain(&*ii, nullptr, 0);
} break;
}
});
}
}
// Find and disconnect dead inserts
std::vector<ir::Instruction*> dead_instructions;
for (auto bi = func->begin(); bi != func->end(); ++bi) {
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
if (ii->opcode() != SpvOpCompositeInsert) continue;
const uint32_t id = ii->result_id();
if (liveInserts_.find(id) != liveInserts_.end()) continue;
const uint32_t replId =
ii->GetSingleWordInOperand(kInsertCompositeIdInIdx);
(void)context()->ReplaceAllUsesWith(id, replId);
dead_instructions.push_back(&*ii);
modified = true;
}
}
// DCE dead inserts
while (!dead_instructions.empty()) {
ir::Instruction* inst = dead_instructions.back();
dead_instructions.pop_back();
DCEInst(inst, [&dead_instructions](ir::Instruction* other_inst) {
auto i = std::find(dead_instructions.begin(), dead_instructions.end(),
other_inst);
if (i != dead_instructions.end()) {
dead_instructions.erase(i);
}
});
}
return modified;
}
void DeadInsertElimPass::Initialize(ir::IRContext* c) {
InitializeProcessing(c);
// Initialize extension whitelist
InitExtensions();
};
bool DeadInsertElimPass::AllExtensionsSupported() const {
// If any extension not in whitelist, return false
for (auto& ei : get_module()->extensions()) {
const char* extName =
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
return false;
}
return true;
}
Pass::Status DeadInsertElimPass::ProcessImpl() {
// Do not process if any disallowed extensions are enabled
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
// Process all entry point functions.
ProcessFunction pfn = [this](ir::Function* fp) {
return EliminateDeadInserts(fp);
};
bool modified = ProcessEntryPointCallTree(pfn, get_module());
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
DeadInsertElimPass::DeadInsertElimPass() {}
Pass::Status DeadInsertElimPass::Process(ir::IRContext* c) {
Initialize(c);
return ProcessImpl();
}
void DeadInsertElimPass::InitExtensions() {
extensions_whitelist_.clear();
extensions_whitelist_.insert({
"SPV_AMD_shader_explicit_vertex_parameter",
"SPV_AMD_shader_trinary_minmax",
"SPV_AMD_gcn_shader",
"SPV_KHR_shader_ballot",
"SPV_AMD_shader_ballot",
"SPV_AMD_gpu_shader_half_float",
"SPV_KHR_shader_draw_parameters",
"SPV_KHR_subgroup_vote",
"SPV_KHR_16bit_storage",
"SPV_KHR_device_group",
"SPV_KHR_multiview",
"SPV_NVX_multiview_per_view_attributes",
"SPV_NV_viewport_array2",
"SPV_NV_stereo_view_rendering",
"SPV_NV_sample_mask_override_coverage",
"SPV_NV_geometry_shader_passthrough",
"SPV_AMD_texture_gather_bias_lod",
"SPV_KHR_storage_buffer_storage_class",
"SPV_KHR_variable_pointers",
"SPV_AMD_gpu_shader_int16",
"SPV_KHR_post_depth_coverage",
"SPV_KHR_shader_atomic_counter_ops",
});
}
} // namespace opt
} // namespace spvtools

View File

@ -0,0 +1,88 @@
// Copyright (c) 2018 The Khronos Group Inc.
// Copyright (c) 2018 Valve Corporation
// Copyright (c) 2018 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef LIBSPIRV_OPT_DEAD_INSERT_ELIM_PASS_H_
#define LIBSPIRV_OPT_DEAD_INSERT_ELIM_PASS_H_
#include <algorithm>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include "basic_block.h"
#include "def_use_manager.h"
#include "ir_context.h"
#include "mem_pass.h"
#include "module.h"
namespace spvtools {
namespace opt {
// See optimizer.hpp for documentation.
class DeadInsertElimPass : public MemPass {
public:
DeadInsertElimPass();
const char* name() const override { return "eliminate-dead-insert"; }
Status Process(ir::IRContext*) override;
private:
// Return the number of subcomponents in the composite type |typeId|.
// Return 0 if not a composite type or number of components is not a
// 32-bit constant.
uint32_t NumComponents(ir::Instruction* typeInst);
// Mark all inserts in instruction chain ending at |insertChain| with
// indices that intersect with extract indices |extIndices| starting with
// index at |extOffset|. Chains are composed solely of Inserts and Phis.
// Mark all inserts in chain if |extIndices| is nullptr.
void MarkInsertChain(ir::Instruction* insertChain,
std::vector<uint32_t>* extIndices, uint32_t extOffset);
// Perform EliminateDeadInsertsOnePass(|func|) until no modification is
// made. Return true if modified.
bool EliminateDeadInserts(ir::Function* func);
// DCE all dead struct, matrix and vector inserts in |func|. An insert is
// dead if the value it inserts is never used. Replace any reference to the
// insert with its original composite. Return true if modified. Dead inserts
// in dependence cycles are not currently eliminated. Dead inserts into
// arrays are not currently eliminated.
bool EliminateDeadInsertsOnePass(ir::Function* func);
// Initialize extensions whitelist
void InitExtensions();
// Return true if all extensions in this module are allowed by this pass.
bool AllExtensionsSupported() const;
void Initialize(ir::IRContext* c);
Pass::Status ProcessImpl();
// Live inserts
std::unordered_set<uint32_t> liveInserts_;
// Visited phis as insert chain is traversed; used to avoid infinite loop
std::unordered_set<uint32_t> visitedPhis_;
// Extensions supported by this pass.
std::unordered_set<std::string> extensions_whitelist_;
};
} // namespace opt
} // namespace spvtools
#endif // LIBSPIRV_OPT_DEAD_INSERT_ELIM_PASS_H_

View File

@ -16,6 +16,7 @@
#include "insert_extract_elim.h"
#include "composite.h"
#include "ir_context.h"
#include "iterator.h"
#include "spirv/1.2/GLSL.std.450.h"
@ -27,14 +28,10 @@ namespace opt {
namespace {
const uint32_t kConstantValueInIdx = 0;
const uint32_t kExtractCompositeIdInIdx = 0;
const uint32_t kInsertObjectIdInIdx = 0;
const uint32_t kInsertCompositeIdInIdx = 1;
const uint32_t kTypeVectorCountInIdx = 1;
const uint32_t kTypeMatrixCountInIdx = 1;
const uint32_t kTypeArrayLengthIdInIdx = 1;
const uint32_t kTypeIntWidthInIdx = 0;
const uint32_t kConstantValueInIdx = 0;
const uint32_t kVectorShuffleVec1IdInIdx = 0;
const uint32_t kVectorShuffleVec2IdInIdx = 1;
const uint32_t kVectorShuffleCompsInIdx = 2;
@ -49,246 +46,6 @@ const uint32_t kFMixAIdInIdx = 4;
} // anonymous namespace
bool InsertExtractElimPass::ExtInsMatch(const std::vector<uint32_t>& extIndices,
const ir::Instruction* insInst,
const uint32_t extOffset) const {
uint32_t numIndices = static_cast<uint32_t>(extIndices.size()) - extOffset;
if (numIndices != insInst->NumInOperands() - 2) return false;
for (uint32_t i = 0; i < numIndices; ++i)
if (extIndices[i + extOffset] != insInst->GetSingleWordInOperand(i + 2))
return false;
return true;
}
bool InsertExtractElimPass::ExtInsConflict(
const std::vector<uint32_t>& extIndices, const ir::Instruction* insInst,
const uint32_t extOffset) const {
if (extIndices.size() - extOffset == insInst->NumInOperands() - 2)
return false;
uint32_t extNumIndices = static_cast<uint32_t>(extIndices.size()) - extOffset;
uint32_t insNumIndices = insInst->NumInOperands() - 2;
uint32_t numIndices = std::min(extNumIndices, insNumIndices);
for (uint32_t i = 0; i < numIndices; ++i)
if (extIndices[i + extOffset] != insInst->GetSingleWordInOperand(i + 2))
return false;
return true;
}
bool InsertExtractElimPass::IsVectorType(uint32_t typeId) {
ir::Instruction* typeInst = get_def_use_mgr()->GetDef(typeId);
return typeInst->opcode() == SpvOpTypeVector;
}
bool InsertExtractElimPass::IsComposite(uint32_t typeId) {
ir::Instruction* typeInst = get_def_use_mgr()->GetDef(typeId);
return spvOpcodeIsComposite(typeInst->opcode());
}
uint32_t InsertExtractElimPass::NumComponents(uint32_t typeId) {
ir::Instruction* typeInst = get_def_use_mgr()->GetDef(typeId);
switch (typeInst->opcode()) {
case SpvOpTypeVector: {
return typeInst->GetSingleWordInOperand(kTypeVectorCountInIdx);
} break;
case SpvOpTypeMatrix: {
return typeInst->GetSingleWordInOperand(kTypeMatrixCountInIdx);
} break;
case SpvOpTypeArray: {
uint32_t lenId =
typeInst->GetSingleWordInOperand(kTypeArrayLengthIdInIdx);
ir::Instruction* lenInst = get_def_use_mgr()->GetDef(lenId);
if (lenInst->opcode() != SpvOpConstant) return 0;
uint32_t lenTypeId = lenInst->type_id();
ir::Instruction* lenTypeInst = get_def_use_mgr()->GetDef(lenTypeId);
// TODO(greg-lunarg): Support non-32-bit array length
if (lenTypeInst->GetSingleWordInOperand(kTypeIntWidthInIdx) != 32)
return 0;
return lenInst->GetSingleWordInOperand(kConstantValueInIdx);
} break;
case SpvOpTypeStruct: {
return typeInst->NumInOperands();
} break;
default: { return 0; } break;
}
}
void InsertExtractElimPass::MarkInsertChain(ir::Instruction* insertChain,
std::vector<uint32_t>* pExtIndices,
uint32_t extOffset) {
// Not currently optimizing array inserts.
ir::Instruction* typeInst = get_def_use_mgr()->GetDef(insertChain->type_id());
if (typeInst->opcode() == SpvOpTypeArray) return;
// Insert chains are only composed of inserts and phis
if (insertChain->opcode() != SpvOpCompositeInsert &&
insertChain->opcode() != SpvOpPhi)
return;
// If extract indices are empty, mark all subcomponents if type
// is constant length.
if (pExtIndices == nullptr) {
uint32_t cnum = NumComponents(insertChain->type_id());
if (cnum > 0) {
std::vector<uint32_t> extIndices;
for (uint32_t i = 0; i < cnum; i++) {
extIndices.clear();
extIndices.push_back(i);
MarkInsertChain(insertChain, &extIndices, 0);
}
return;
}
}
ir::Instruction* insInst = insertChain;
while (insInst->opcode() == SpvOpCompositeInsert) {
// If no extract indices, mark insert and inserted object (which might
// also be an insert chain) and continue up the chain though the input
// composite.
//
// Note: We mark inserted objects in this function (rather than in
// EliminateDeadInsertsOnePass) because in some cases, we can do it
// more accurately here.
if (pExtIndices == nullptr) {
liveInserts_.insert(insInst->result_id());
uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0);
}
// If extract indices match insert, we are done. Mark insert and
// inserted object.
else if (ExtInsMatch(*pExtIndices, insInst, extOffset)) {
liveInserts_.insert(insInst->result_id());
uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0);
break;
}
// If non-matching intersection, mark insert
else if (ExtInsConflict(*pExtIndices, insInst, extOffset)) {
liveInserts_.insert(insInst->result_id());
// If more extract indices than insert, we are done. Use remaining
// extract indices to mark inserted object.
uint32_t numInsertIndices = insInst->NumInOperands() - 2;
if (pExtIndices->size() - extOffset > numInsertIndices) {
uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
MarkInsertChain(get_def_use_mgr()->GetDef(objId), pExtIndices,
extOffset + numInsertIndices);
break;
}
// If fewer extract indices than insert, also mark inserted object and
// continue up chain.
else {
uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx);
MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0);
}
}
// Get next insert in chain
const uint32_t compId =
insInst->GetSingleWordInOperand(kInsertCompositeIdInIdx);
insInst = get_def_use_mgr()->GetDef(compId);
}
// If insert chain ended with phi, do recursive call on each operand
if (insInst->opcode() != SpvOpPhi) return;
// Mark phi visited to prevent potential infinite loop. If phi is already
// visited, return to avoid infinite loop
if (!visitedPhis_.insert(insInst->result_id()).second) return;
uint32_t icnt = 0;
insInst->ForEachInId([&icnt, &pExtIndices, &extOffset, this](uint32_t* idp) {
if (icnt % 2 == 0) {
ir::Instruction* pi = get_def_use_mgr()->GetDef(*idp);
MarkInsertChain(pi, pExtIndices, extOffset);
}
++icnt;
});
// Unmark phi when done visiting
visitedPhis_.erase(insInst->result_id());
}
bool InsertExtractElimPass::EliminateDeadInserts(ir::Function* func) {
bool modified = false;
bool lastmodified = true;
// Each pass can delete dead instructions, thus potentially revealing
// new dead insertions ie insertions with no uses.
while (lastmodified) {
lastmodified = EliminateDeadInsertsOnePass(func);
modified |= lastmodified;
}
return modified;
}
bool InsertExtractElimPass::EliminateDeadInsertsOnePass(ir::Function* func) {
bool modified = false;
liveInserts_.clear();
visitedPhis_.clear();
// Mark all live inserts
for (auto bi = func->begin(); bi != func->end(); ++bi) {
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
// Only process Inserts and composite Phis
SpvOp op = ii->opcode();
if (op != SpvOpCompositeInsert &&
(op != SpvOpPhi || !IsComposite(ii->type_id())))
continue;
// The marking algorithm can be expensive for large arrays and the
// efficacy of eliminating dead inserts into arrays is questionable.
// Skip optimizing array inserts for now. Just mark them live.
// TODO(greg-lunarg): Eliminate dead array inserts
if (op == SpvOpCompositeInsert) {
ir::Instruction* typeInst = get_def_use_mgr()->GetDef(ii->type_id());
if (typeInst->opcode() == SpvOpTypeArray) {
liveInserts_.insert(ii->result_id());
continue;
}
}
const uint32_t id = ii->result_id();
get_def_use_mgr()->ForEachUser(id, [&ii, this](ir::Instruction* user) {
switch (user->opcode()) {
case SpvOpCompositeInsert:
case SpvOpPhi:
// Use by insert or phi does not initiate marking
break;
case SpvOpCompositeExtract: {
// Capture extract indices
std::vector<uint32_t> extIndices;
uint32_t icnt = 0;
user->ForEachInOperand([&icnt, &extIndices](const uint32_t* idp) {
if (icnt > 0) extIndices.push_back(*idp);
++icnt;
});
// Mark all inserts in chain that intersect with extract
MarkInsertChain(&*ii, &extIndices, 0);
} break;
default: {
// Mark inserts in chain for all components
MarkInsertChain(&*ii, nullptr, 0);
} break;
}
});
}
}
// Find and disconnect dead inserts
std::vector<ir::Instruction*> dead_instructions;
for (auto bi = func->begin(); bi != func->end(); ++bi) {
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
if (ii->opcode() != SpvOpCompositeInsert) continue;
const uint32_t id = ii->result_id();
if (liveInserts_.find(id) != liveInserts_.end()) continue;
const uint32_t replId =
ii->GetSingleWordInOperand(kInsertCompositeIdInIdx);
(void)context()->ReplaceAllUsesWith(id, replId);
dead_instructions.push_back(&*ii);
modified = true;
}
}
// DCE dead inserts
while (!dead_instructions.empty()) {
ir::Instruction* inst = dead_instructions.back();
dead_instructions.pop_back();
DCEInst(inst, [&dead_instructions](ir::Instruction* other_inst) {
auto i = std::find(dead_instructions.begin(), dead_instructions.end(),
other_inst);
if (i != dead_instructions.end()) {
dead_instructions.erase(i);
}
});
}
return modified;
}
uint32_t InsertExtractElimPass::DoExtract(ir::Instruction* compInst,
std::vector<uint32_t>* pExtIndices,
uint32_t extOffset) {
@ -437,7 +194,6 @@ bool InsertExtractElimPass::EliminateInsertExtract(ir::Function* func) {
}
}
}
modified |= EliminateDeadInserts(func);
return modified;
}

View File

@ -40,50 +40,6 @@ class InsertExtractElimPass : public MemPass {
Status Process(ir::IRContext*) override;
private:
// Return true if the extract indices in |extIndices| starting at |extOffset|
// match indices of insert |insInst|.
bool ExtInsMatch(const std::vector<uint32_t>& extIndices,
const ir::Instruction* insInst,
const uint32_t extOffset) const;
// Return true if indices in |extIndices| starting at |extOffset| and
// indices of insert |insInst| conflict, specifically, if the insert
// changes bits specified by the extract, but changes either more bits
// or less bits than the extract specifies, meaning the exact value being
// inserted cannot be used to replace the extract.
bool ExtInsConflict(const std::vector<uint32_t>& extIndices,
const ir::Instruction* insInst,
const uint32_t extOffset) const;
// Return true if |typeId| is a vector type
bool IsVectorType(uint32_t typeId);
// Return true if |typeId| is composite.
bool IsComposite(uint32_t typeId);
// Return the number of subcomponents in the composite type |typeId|.
// Return 0 if not a composite type or number of components is not a
// 32-bit constant.
uint32_t NumComponents(uint32_t typeId);
// Mark all inserts in instruction chain ending at |insertChain| with
// indices that intersect with extract indices |extIndices| starting with
// index at |extOffset|. Chains are composed solely of Inserts and Phis.
// Mark all inserts in chain if |extIndices| is nullptr.
void MarkInsertChain(ir::Instruction* insertChain,
std::vector<uint32_t>* extIndices, uint32_t extOffset);
// Perform EliminateDeadInsertsOnePass(|func|) until no modification is
// made. Return true if modified.
bool EliminateDeadInserts(ir::Function* func);
// DCE all dead struct, matrix and vector inserts in |func|. An insert is
// dead if the value it inserts is never used. Replace any reference to the
// insert with its original composite. Return true if modified. Dead inserts
// in dependence cycles are not currently eliminated. Dead inserts into
// arrays are not currently eliminated.
bool EliminateDeadInsertsOnePass(ir::Function* func);
// Return id of component of |cinst| specified by |extIndices| starting with
// index at |extOffset|. Return 0 if indices cannot be matched exactly.
uint32_t DoExtract(ir::Instruction* cinst, std::vector<uint32_t>* extIndices,
@ -104,12 +60,6 @@ class InsertExtractElimPass : public MemPass {
void Initialize(ir::IRContext* c);
Pass::Status ProcessImpl();
// Live inserts
std::unordered_set<uint32_t> liveInserts_;
// Visited phis as insert chain is traversed; used to avoid infinite loop
std::unordered_set<uint32_t> visitedPhis_;
// Extensions supported by this pass.
std::unordered_set<std::string> extensions_whitelist_;
};

View File

@ -107,7 +107,9 @@ Optimizer& Optimizer::RegisterLegalizationPasses() {
// May need loop unrolling here see
// https://github.com/Microsoft/DirectXShaderCompiler/pull/930
.RegisterPass(CreateDeadBranchElimPass())
// Get rid of unused code that leave traces of the illegal code.
// Get rid of unused code that contain traces of illegal code
// or unused references to unbound external objects
.RegisterPass(CreateDeadInsertElimPass())
.RegisterPass(CreateAggressiveDCEPass());
}
@ -121,6 +123,7 @@ Optimizer& Optimizer::RegisterPerformancePasses() {
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
.RegisterPass(CreateLocalSingleStoreElimPass())
.RegisterPass(CreateInsertExtractElimPass())
.RegisterPass(CreateDeadInsertElimPass())
.RegisterPass(CreateLocalMultiStoreElimPass())
.RegisterPass(CreateCCPPass())
.RegisterPass(CreateAggressiveDCEPass())
@ -129,6 +132,7 @@ Optimizer& Optimizer::RegisterPerformancePasses() {
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateBlockMergePass())
.RegisterPass(CreateInsertExtractElimPass())
.RegisterPass(CreateDeadInsertElimPass())
.RegisterPass(CreateRedundancyEliminationPass())
.RegisterPass(CreateCFGCleanupPass())
// Currently exposing driver bugs resulting in crashes (#946)
@ -145,6 +149,7 @@ Optimizer& Optimizer::RegisterSizePasses() {
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
.RegisterPass(CreateLocalSingleStoreElimPass())
.RegisterPass(CreateInsertExtractElimPass())
.RegisterPass(CreateDeadInsertElimPass())
.RegisterPass(CreateLocalMultiStoreElimPass())
.RegisterPass(CreateCCPPass())
.RegisterPass(CreateAggressiveDCEPass())
@ -153,6 +158,7 @@ Optimizer& Optimizer::RegisterSizePasses() {
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateBlockMergePass())
.RegisterPass(CreateInsertExtractElimPass())
.RegisterPass(CreateDeadInsertElimPass())
.RegisterPass(CreateRedundancyEliminationPass())
.RegisterPass(CreateCFGCleanupPass())
// Currently exposing driver bugs resulting in crashes (#946)
@ -281,6 +287,11 @@ Optimizer::PassToken CreateInsertExtractElimPass() {
MakeUnique<opt::InsertExtractElimPass>());
}
Optimizer::PassToken CreateDeadInsertElimPass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::DeadInsertElimPass>());
}
Optimizer::PassToken CreateDeadBranchElimPass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::DeadBranchElimPass>());

View File

@ -24,6 +24,7 @@
#include "common_uniform_elim_pass.h"
#include "compact_ids_pass.h"
#include "dead_branch_elim_pass.h"
#include "dead_insert_elim_pass.h"
#include "dead_variable_elimination.h"
#include "eliminate_dead_constant_pass.h"
#include "eliminate_dead_functions_pass.h"

View File

@ -81,6 +81,11 @@ add_spvtools_unittest(TARGET pass_insert_extract_elim
LIBS SPIRV-Tools-opt
)
add_spvtools_unittest(TARGET pass_dead_insert_elim
SRCS dead_insert_elim_test.cpp pass_utils.cpp
LIBS SPIRV-Tools-opt
)
add_spvtools_unittest(TARGET pass_local_ssa_elim
SRCS local_ssa_elim_test.cpp pass_utils.cpp
LIBS SPIRV-Tools-opt

View File

@ -0,0 +1,685 @@
// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "pass_fixture.h"
#include "pass_utils.h"
namespace {
using namespace spvtools;
using DeadInsertElimTest = PassTest<::testing::Test>;
TEST_F(DeadInsertElimTest, InsertAfterInsertElim) {
// With two insertions to the same offset, the first is dead.
//
// Note: The SPIR-V assembly has had store/load elimination
// performed to allow the inserts and extracts to directly
// reference each other.
//
// #version 450
//
// layout (location=0) in float In0;
// layout (location=1) in float In1;
// layout (location=2) in vec2 In2;
// layout (location=0) out vec4 OutColor;
//
// void main()
// {
// vec2 v = In2;
// v.x = In0 + In1; // dead
// v.x = 0.0;
// OutColor = v.xyxy;
// }
const std::string before_predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %In2 "In2"
OpName %In0 "In0"
OpName %In1 "In1"
OpName %OutColor "OutColor"
OpName %_Globals_ "_Globals_"
OpMemberName %_Globals_ 0 "g_b"
OpMemberName %_Globals_ 1 "g_n"
OpName %_ ""
OpDecorate %In2 Location 2
OpDecorate %In0 Location 0
OpDecorate %In1 Location 1
OpDecorate %OutColor Location 0
OpMemberDecorate %_Globals_ 0 Offset 0
OpMemberDecorate %_Globals_ 1 Offset 4
OpDecorate %_Globals_ Block
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
%void = OpTypeVoid
%11 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%_ptr_Function_v2float = OpTypePointer Function %v2float
%_ptr_Input_v2float = OpTypePointer Input %v2float
%In2 = OpVariable %_ptr_Input_v2float Input
%_ptr_Input_float = OpTypePointer Input %float
%In0 = OpVariable %_ptr_Input_float Input
%In1 = OpVariable %_ptr_Input_float Input
%uint = OpTypeInt 32 0
%_ptr_Function_float = OpTypePointer Function %float
%float_0 = OpConstant %float 0
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
%int = OpTypeInt 32 1
%_Globals_ = OpTypeStruct %uint %int
%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
)";
const std::string after_predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %In2 "In2"
OpName %In0 "In0"
OpName %In1 "In1"
OpName %OutColor "OutColor"
OpName %_Globals_ "_Globals_"
OpMemberName %_Globals_ 0 "g_b"
OpMemberName %_Globals_ 1 "g_n"
OpName %_ ""
OpDecorate %In2 Location 2
OpDecorate %In0 Location 0
OpDecorate %In1 Location 1
OpDecorate %OutColor Location 0
OpMemberDecorate %_Globals_ 0 Offset 0
OpMemberDecorate %_Globals_ 1 Offset 4
OpDecorate %_Globals_ Block
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%_ptr_Function_v2float = OpTypePointer Function %v2float
%_ptr_Input_v2float = OpTypePointer Input %v2float
%In2 = OpVariable %_ptr_Input_v2float Input
%_ptr_Input_float = OpTypePointer Input %float
%In0 = OpVariable %_ptr_Input_float Input
%In1 = OpVariable %_ptr_Input_float Input
%uint = OpTypeInt 32 0
%_ptr_Function_float = OpTypePointer Function %float
%float_0 = OpConstant %float 0
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
%int = OpTypeInt 32 1
%_Globals_ = OpTypeStruct %uint %int
%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
)";
const std::string before =
R"(%main = OpFunction %void None %11
%25 = OpLabel
%26 = OpLoad %v2float %In2
%27 = OpLoad %float %In0
%28 = OpLoad %float %In1
%29 = OpFAdd %float %27 %28
%35 = OpCompositeInsert %v2float %29 %26 0
%37 = OpCompositeInsert %v2float %float_0 %35 0
%33 = OpVectorShuffle %v4float %37 %37 0 1 0 1
OpStore %OutColor %33
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%main = OpFunction %void None %10
%23 = OpLabel
%24 = OpLoad %v2float %In2
%29 = OpCompositeInsert %v2float %float_0 %24 0
%30 = OpVectorShuffle %v4float %29 %29 0 1 0 1
OpStore %OutColor %30
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::DeadInsertElimPass>(
before_predefs + before, after_predefs + after, true, true);
}
TEST_F(DeadInsertElimTest, DeadInsertInChainWithPhi) {
// Dead insert eliminated with phi in insertion chain.
//
// Note: The SPIR-V assembly has had store/load elimination
// performed to allow the inserts and extracts to directly
// reference each other.
//
// #version 450
//
// layout (location=0) in vec4 In0;
// layout (location=1) in float In1;
// layout (location=2) in float In2;
// layout (location=0) out vec4 OutColor;
//
// layout(std140, binding = 0 ) uniform _Globals_
// {
// bool g_b;
// };
//
// void main()
// {
// vec4 v = In0;
// v.z = In1 + In2;
// if (g_b) v.w = 1.0;
// OutColor = vec4(v.x,v.y,0.0,v.w);
// }
const std::string before_predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %In0 "In0"
OpName %In1 "In1"
OpName %In2 "In2"
OpName %_Globals_ "_Globals_"
OpMemberName %_Globals_ 0 "g_b"
OpName %_ ""
OpName %OutColor "OutColor"
OpDecorate %In0 Location 0
OpDecorate %In1 Location 1
OpDecorate %In2 Location 2
OpMemberDecorate %_Globals_ 0 Offset 0
OpDecorate %_Globals_ Block
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
OpDecorate %OutColor Location 0
%void = OpTypeVoid
%11 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%In0 = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%In1 = OpVariable %_ptr_Input_float Input
%In2 = OpVariable %_ptr_Input_float Input
%uint = OpTypeInt 32 0
%_ptr_Function_float = OpTypePointer Function %float
%_Globals_ = OpTypeStruct %uint
%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%bool = OpTypeBool
%uint_0 = OpConstant %uint 0
%float_1 = OpConstant %float 1
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
%float_0 = OpConstant %float 0
)";
const std::string after_predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %In0 "In0"
OpName %In1 "In1"
OpName %In2 "In2"
OpName %_Globals_ "_Globals_"
OpMemberName %_Globals_ 0 "g_b"
OpName %_ ""
OpName %OutColor "OutColor"
OpDecorate %In0 Location 0
OpDecorate %In1 Location 1
OpDecorate %In2 Location 2
OpMemberDecorate %_Globals_ 0 Offset 0
OpDecorate %_Globals_ Block
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
OpDecorate %OutColor Location 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%In0 = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%In1 = OpVariable %_ptr_Input_float Input
%In2 = OpVariable %_ptr_Input_float Input
%uint = OpTypeInt 32 0
%_ptr_Function_float = OpTypePointer Function %float
%_Globals_ = OpTypeStruct %uint
%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%bool = OpTypeBool
%uint_0 = OpConstant %uint 0
%float_1 = OpConstant %float 1
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
%float_0 = OpConstant %float 0
)";
const std::string before =
R"(%main = OpFunction %void None %11
%31 = OpLabel
%32 = OpLoad %v4float %In0
%33 = OpLoad %float %In1
%34 = OpLoad %float %In2
%35 = OpFAdd %float %33 %34
%51 = OpCompositeInsert %v4float %35 %32 2
%37 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
%38 = OpLoad %uint %37
%39 = OpINotEqual %bool %38 %uint_0
OpSelectionMerge %40 None
OpBranchConditional %39 %41 %40
%41 = OpLabel
%53 = OpCompositeInsert %v4float %float_1 %51 3
OpBranch %40
%40 = OpLabel
%60 = OpPhi %v4float %51 %31 %53 %41
%55 = OpCompositeExtract %float %60 0
%57 = OpCompositeExtract %float %60 1
%59 = OpCompositeExtract %float %60 3
%49 = OpCompositeConstruct %v4float %55 %57 %float_0 %59
OpStore %OutColor %49
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%main = OpFunction %void None %10
%27 = OpLabel
%28 = OpLoad %v4float %In0
%33 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
%34 = OpLoad %uint %33
%35 = OpINotEqual %bool %34 %uint_0
OpSelectionMerge %36 None
OpBranchConditional %35 %37 %36
%37 = OpLabel
%38 = OpCompositeInsert %v4float %float_1 %28 3
OpBranch %36
%36 = OpLabel
%39 = OpPhi %v4float %28 %27 %38 %37
%40 = OpCompositeExtract %float %39 0
%41 = OpCompositeExtract %float %39 1
%42 = OpCompositeExtract %float %39 3
%43 = OpCompositeConstruct %v4float %40 %41 %float_0 %42
OpStore %OutColor %43
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::DeadInsertElimPass>(
before_predefs + before, after_predefs + after, true, true);
}
TEST_F(DeadInsertElimTest, DeadInsertTwoPasses) {
// Dead insert which requires two passes to eliminate
//
// Note: The SPIR-V assembly has had store/load elimination
// performed to allow the inserts and extracts to directly
// reference each other.
//
// #version 450
//
// layout (location=0) in vec4 In0;
// layout (location=1) in float In1;
// layout (location=2) in float In2;
// layout (location=0) out vec4 OutColor;
//
// layout(std140, binding = 0 ) uniform _Globals_
// {
// bool g_b;
// bool g_b2;
// };
//
// void main()
// {
// vec4 v1, v2;
// v1 = In0;
// v1.y = In1 + In2; // dead, second pass
// if (g_b) v1.x = 1.0;
// v2.x = v1.x;
// v2.y = v1.y; // dead, first pass
// if (g_b2) v2.x = 0.0;
// OutColor = vec4(v2.x,v2.x,0.0,1.0);
// }
const std::string before_predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %In0 "In0"
OpName %In1 "In1"
OpName %In2 "In2"
OpName %_Globals_ "_Globals_"
OpMemberName %_Globals_ 0 "g_b"
OpMemberName %_Globals_ 1 "g_b2"
OpName %_ ""
OpName %OutColor "OutColor"
OpDecorate %In0 Location 0
OpDecorate %In1 Location 1
OpDecorate %In2 Location 2
OpMemberDecorate %_Globals_ 0 Offset 0
OpMemberDecorate %_Globals_ 1 Offset 4
OpDecorate %_Globals_ Block
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
OpDecorate %OutColor Location 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%In0 = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%In1 = OpVariable %_ptr_Input_float Input
%In2 = OpVariable %_ptr_Input_float Input
%uint = OpTypeInt 32 0
%_Globals_ = OpTypeStruct %uint %uint
%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%bool = OpTypeBool
%uint_0 = OpConstant %uint 0
%float_1 = OpConstant %float 1
%int_1 = OpConstant %int 1
%float_0 = OpConstant %float 0
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
%27 = OpUndef %v4float
)";
const std::string after_predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %In0 "In0"
OpName %In1 "In1"
OpName %In2 "In2"
OpName %_Globals_ "_Globals_"
OpMemberName %_Globals_ 0 "g_b"
OpMemberName %_Globals_ 1 "g_b2"
OpName %_ ""
OpName %OutColor "OutColor"
OpDecorate %In0 Location 0
OpDecorate %In1 Location 1
OpDecorate %In2 Location 2
OpMemberDecorate %_Globals_ 0 Offset 0
OpMemberDecorate %_Globals_ 1 Offset 4
OpDecorate %_Globals_ Block
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
OpDecorate %OutColor Location 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%In0 = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%In1 = OpVariable %_ptr_Input_float Input
%In2 = OpVariable %_ptr_Input_float Input
%uint = OpTypeInt 32 0
%_Globals_ = OpTypeStruct %uint %uint
%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%bool = OpTypeBool
%uint_0 = OpConstant %uint 0
%float_1 = OpConstant %float 1
%int_1 = OpConstant %int 1
%float_0 = OpConstant %float 0
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
%27 = OpUndef %v4float
)";
const std::string before =
R"(%main = OpFunction %void None %10
%28 = OpLabel
%29 = OpLoad %v4float %In0
%30 = OpLoad %float %In1
%31 = OpLoad %float %In2
%32 = OpFAdd %float %30 %31
%33 = OpCompositeInsert %v4float %32 %29 1
%34 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
%35 = OpLoad %uint %34
%36 = OpINotEqual %bool %35 %uint_0
OpSelectionMerge %37 None
OpBranchConditional %36 %38 %37
%38 = OpLabel
%39 = OpCompositeInsert %v4float %float_1 %33 0
OpBranch %37
%37 = OpLabel
%40 = OpPhi %v4float %33 %28 %39 %38
%41 = OpCompositeExtract %float %40 0
%42 = OpCompositeInsert %v4float %41 %27 0
%43 = OpCompositeExtract %float %40 1
%44 = OpCompositeInsert %v4float %43 %42 1
%45 = OpAccessChain %_ptr_Uniform_uint %_ %int_1
%46 = OpLoad %uint %45
%47 = OpINotEqual %bool %46 %uint_0
OpSelectionMerge %48 None
OpBranchConditional %47 %49 %48
%49 = OpLabel
%50 = OpCompositeInsert %v4float %float_0 %44 0
OpBranch %48
%48 = OpLabel
%51 = OpPhi %v4float %44 %37 %50 %49
%52 = OpCompositeExtract %float %51 0
%53 = OpCompositeExtract %float %51 0
%54 = OpCompositeConstruct %v4float %52 %53 %float_0 %float_1
OpStore %OutColor %54
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%main = OpFunction %void None %10
%28 = OpLabel
%29 = OpLoad %v4float %In0
%34 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
%35 = OpLoad %uint %34
%36 = OpINotEqual %bool %35 %uint_0
OpSelectionMerge %37 None
OpBranchConditional %36 %38 %37
%38 = OpLabel
%39 = OpCompositeInsert %v4float %float_1 %29 0
OpBranch %37
%37 = OpLabel
%40 = OpPhi %v4float %29 %28 %39 %38
%41 = OpCompositeExtract %float %40 0
%42 = OpCompositeInsert %v4float %41 %27 0
%45 = OpAccessChain %_ptr_Uniform_uint %_ %int_1
%46 = OpLoad %uint %45
%47 = OpINotEqual %bool %46 %uint_0
OpSelectionMerge %48 None
OpBranchConditional %47 %49 %48
%49 = OpLabel
%50 = OpCompositeInsert %v4float %float_0 %42 0
OpBranch %48
%48 = OpLabel
%51 = OpPhi %v4float %42 %37 %50 %49
%52 = OpCompositeExtract %float %51 0
%53 = OpCompositeExtract %float %51 0
%54 = OpCompositeConstruct %v4float %52 %53 %float_0 %float_1
OpStore %OutColor %54
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::DeadInsertElimPass>(
before_predefs + before, after_predefs + after, true, true);
}
TEST_F(DeadInsertElimTest, DeadInsertInCycleToDo) {
// Dead insert in chain with cycle. Demonstrates analysis can handle
// cycles in chains.
//
// TODO(greg-lunarg): Improve algorithm to remove dead insert into v.y. Will
// likely require similar logic to ADCE.
//
// Note: The SPIR-V assembly has had store/load elimination
// performed to allow the inserts and extracts to directly
// reference each other.
//
// #version 450
//
// layout (location=0) in vec4 In0;
// layout (location=1) in float In1;
// layout (location=2) in float In2;
// layout (location=0) out vec4 OutColor;
//
// layout(std140, binding = 0 ) uniform _Globals_
// {
// int g_n ;
// };
//
// void main()
// {
// vec2 v = vec2(0.0, 1.0);
// for (int i = 0; i < g_n; i++) {
// v.x = v.x + 1;
// v.y = v.y * 0.9; // dead
// }
// OutColor = vec4(v.x);
// }
const std::string assembly =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %OutColor %In0 %In1 %In2
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %_Globals_ "_Globals_"
OpMemberName %_Globals_ 0 "g_n"
OpName %_ ""
OpName %OutColor "OutColor"
OpName %In0 "In0"
OpName %In1 "In1"
OpName %In2 "In2"
OpMemberDecorate %_Globals_ 0 Offset 0
OpDecorate %_Globals_ Block
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
OpDecorate %OutColor Location 0
OpDecorate %In0 Location 0
OpDecorate %In1 Location 1
OpDecorate %In2 Location 2
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%_ptr_Function_v2float = OpTypePointer Function %v2float
%float_0 = OpConstant %float 0
%float_1 = OpConstant %float 1
%16 = OpConstantComposite %v2float %float_0 %float_1
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%_Globals_ = OpTypeStruct %int
%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
%_ptr_Uniform_int = OpTypePointer Uniform %int
%bool = OpTypeBool
%float_0_9 = OpConstant %float 0.9
%int_1 = OpConstant %int 1
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
%_ptr_Input_v4float = OpTypePointer Input %v4float
%In0 = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%In1 = OpVariable %_ptr_Input_float Input
%In2 = OpVariable %_ptr_Input_float Input
%main = OpFunction %void None %10
%29 = OpLabel
OpBranch %30
%30 = OpLabel
%31 = OpPhi %v2float %16 %29 %32 %33
%34 = OpPhi %int %int_0 %29 %35 %33
OpLoopMerge %36 %33 None
OpBranch %37
%37 = OpLabel
%38 = OpAccessChain %_ptr_Uniform_int %_ %int_0
%39 = OpLoad %int %38
%40 = OpSLessThan %bool %34 %39
OpBranchConditional %40 %41 %36
%41 = OpLabel
%42 = OpCompositeExtract %float %31 0
%43 = OpFAdd %float %42 %float_1
%44 = OpCompositeInsert %v2float %43 %31 0
%45 = OpCompositeExtract %float %44 1
%46 = OpFMul %float %45 %float_0_9
%32 = OpCompositeInsert %v2float %46 %44 1
OpBranch %33
%33 = OpLabel
%35 = OpIAdd %int %34 %int_1
OpBranch %30
%36 = OpLabel
%47 = OpCompositeExtract %float %31 0
%48 = OpCompositeConstruct %v4float %47 %47 %47 %47
OpStore %OutColor %48
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::DeadInsertElimPass>(assembly, assembly, true,
true);
}
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
} // anonymous namespace

View File

@ -545,6 +545,7 @@ OpName %gl_FragColor "gl_FragColor"
%_ptr_Function_S_t = OpTypePointer Function %S_t
%int = OpTypeInt 32 1
%int_1 = OpConstant %int 1
%float_1 = OpConstant %float 1
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%_ptr_Function_float = OpTypePointer Function %float
@ -576,8 +577,9 @@ OpFunctionEnd
%22 = OpLabel
%s0 = OpVariable %_ptr_Function_S_t Function
%23 = OpLoad %S_t %s0
%24 = OpCompositeInsert %S_t %float_1 %23 1 1
%25 = OpLoad %v4float %BaseColor
%26 = OpCompositeInsert %S_t %25 %23 1
%26 = OpCompositeInsert %S_t %25 %24 1
%27 = OpCompositeExtract %float %26 1 1
%28 = OpCompositeConstruct %v4float %27 %float_0 %float_0 %float_0
OpStore %gl_FragColor %28
@ -855,663 +857,6 @@ OpFunctionEnd
predefs + before, predefs + after, true, true);
}
TEST_F(InsertExtractElimTest, InsertAfterInsertElim) {
// With two insertions to the same offset, the first is dead.
//
// Note: The SPIR-V assembly has had store/load elimination
// performed to allow the inserts and extracts to directly
// reference each other.
//
// #version 450
//
// layout (location=0) in float In0;
// layout (location=1) in float In1;
// layout (location=2) in vec2 In2;
// layout (location=0) out vec4 OutColor;
//
// void main()
// {
// vec2 v = In2;
// v.x = In0 + In1; // dead
// v.x = 0.0;
// OutColor = v.xyxy;
// }
const std::string before_predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %In2 "In2"
OpName %In0 "In0"
OpName %In1 "In1"
OpName %OutColor "OutColor"
OpName %_Globals_ "_Globals_"
OpMemberName %_Globals_ 0 "g_b"
OpMemberName %_Globals_ 1 "g_n"
OpName %_ ""
OpDecorate %In2 Location 2
OpDecorate %In0 Location 0
OpDecorate %In1 Location 1
OpDecorate %OutColor Location 0
OpMemberDecorate %_Globals_ 0 Offset 0
OpMemberDecorate %_Globals_ 1 Offset 4
OpDecorate %_Globals_ Block
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
%void = OpTypeVoid
%11 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%_ptr_Function_v2float = OpTypePointer Function %v2float
%_ptr_Input_v2float = OpTypePointer Input %v2float
%In2 = OpVariable %_ptr_Input_v2float Input
%_ptr_Input_float = OpTypePointer Input %float
%In0 = OpVariable %_ptr_Input_float Input
%In1 = OpVariable %_ptr_Input_float Input
%uint = OpTypeInt 32 0
%_ptr_Function_float = OpTypePointer Function %float
%float_0 = OpConstant %float 0
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
%int = OpTypeInt 32 1
%_Globals_ = OpTypeStruct %uint %int
%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
)";
const std::string after_predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %In2 "In2"
OpName %In0 "In0"
OpName %In1 "In1"
OpName %OutColor "OutColor"
OpName %_Globals_ "_Globals_"
OpMemberName %_Globals_ 0 "g_b"
OpMemberName %_Globals_ 1 "g_n"
OpName %_ ""
OpDecorate %In2 Location 2
OpDecorate %In0 Location 0
OpDecorate %In1 Location 1
OpDecorate %OutColor Location 0
OpMemberDecorate %_Globals_ 0 Offset 0
OpMemberDecorate %_Globals_ 1 Offset 4
OpDecorate %_Globals_ Block
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%_ptr_Function_v2float = OpTypePointer Function %v2float
%_ptr_Input_v2float = OpTypePointer Input %v2float
%In2 = OpVariable %_ptr_Input_v2float Input
%_ptr_Input_float = OpTypePointer Input %float
%In0 = OpVariable %_ptr_Input_float Input
%In1 = OpVariable %_ptr_Input_float Input
%uint = OpTypeInt 32 0
%_ptr_Function_float = OpTypePointer Function %float
%float_0 = OpConstant %float 0
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
%int = OpTypeInt 32 1
%_Globals_ = OpTypeStruct %uint %int
%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
)";
const std::string before =
R"(%main = OpFunction %void None %11
%25 = OpLabel
%26 = OpLoad %v2float %In2
%27 = OpLoad %float %In0
%28 = OpLoad %float %In1
%29 = OpFAdd %float %27 %28
%35 = OpCompositeInsert %v2float %29 %26 0
%37 = OpCompositeInsert %v2float %float_0 %35 0
%33 = OpVectorShuffle %v4float %37 %37 0 1 0 1
OpStore %OutColor %33
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%main = OpFunction %void None %10
%23 = OpLabel
%24 = OpLoad %v2float %In2
%29 = OpCompositeInsert %v2float %float_0 %24 0
%30 = OpVectorShuffle %v4float %29 %29 0 1 0 1
OpStore %OutColor %30
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InsertExtractElimPass>(
before_predefs + before, after_predefs + after, true, true);
}
TEST_F(InsertExtractElimTest, DeadInsertInChainWithPhi) {
// Dead insert eliminated with phi in insertion chain.
//
// Note: The SPIR-V assembly has had store/load elimination
// performed to allow the inserts and extracts to directly
// reference each other.
//
// #version 450
//
// layout (location=0) in vec4 In0;
// layout (location=1) in float In1;
// layout (location=2) in float In2;
// layout (location=0) out vec4 OutColor;
//
// layout(std140, binding = 0 ) uniform _Globals_
// {
// bool g_b;
// };
//
// void main()
// {
// vec4 v = In0;
// v.z = In1 + In2;
// if (g_b) v.w = 1.0;
// OutColor = vec4(v.x,v.y,0.0,v.w);
// }
const std::string before_predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %In0 "In0"
OpName %In1 "In1"
OpName %In2 "In2"
OpName %_Globals_ "_Globals_"
OpMemberName %_Globals_ 0 "g_b"
OpName %_ ""
OpName %OutColor "OutColor"
OpDecorate %In0 Location 0
OpDecorate %In1 Location 1
OpDecorate %In2 Location 2
OpMemberDecorate %_Globals_ 0 Offset 0
OpDecorate %_Globals_ Block
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
OpDecorate %OutColor Location 0
%void = OpTypeVoid
%11 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%In0 = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%In1 = OpVariable %_ptr_Input_float Input
%In2 = OpVariable %_ptr_Input_float Input
%uint = OpTypeInt 32 0
%_ptr_Function_float = OpTypePointer Function %float
%_Globals_ = OpTypeStruct %uint
%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%bool = OpTypeBool
%uint_0 = OpConstant %uint 0
%float_1 = OpConstant %float 1
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
%float_0 = OpConstant %float 0
)";
const std::string after_predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %In0 "In0"
OpName %In1 "In1"
OpName %In2 "In2"
OpName %_Globals_ "_Globals_"
OpMemberName %_Globals_ 0 "g_b"
OpName %_ ""
OpName %OutColor "OutColor"
OpDecorate %In0 Location 0
OpDecorate %In1 Location 1
OpDecorate %In2 Location 2
OpMemberDecorate %_Globals_ 0 Offset 0
OpDecorate %_Globals_ Block
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
OpDecorate %OutColor Location 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%In0 = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%In1 = OpVariable %_ptr_Input_float Input
%In2 = OpVariable %_ptr_Input_float Input
%uint = OpTypeInt 32 0
%_ptr_Function_float = OpTypePointer Function %float
%_Globals_ = OpTypeStruct %uint
%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%bool = OpTypeBool
%uint_0 = OpConstant %uint 0
%float_1 = OpConstant %float 1
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
%float_0 = OpConstant %float 0
)";
const std::string before =
R"(%main = OpFunction %void None %11
%31 = OpLabel
%32 = OpLoad %v4float %In0
%33 = OpLoad %float %In1
%34 = OpLoad %float %In2
%35 = OpFAdd %float %33 %34
%51 = OpCompositeInsert %v4float %35 %32 2
%37 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
%38 = OpLoad %uint %37
%39 = OpINotEqual %bool %38 %uint_0
OpSelectionMerge %40 None
OpBranchConditional %39 %41 %40
%41 = OpLabel
%53 = OpCompositeInsert %v4float %float_1 %51 3
OpBranch %40
%40 = OpLabel
%60 = OpPhi %v4float %51 %31 %53 %41
%55 = OpCompositeExtract %float %60 0
%57 = OpCompositeExtract %float %60 1
%59 = OpCompositeExtract %float %60 3
%49 = OpCompositeConstruct %v4float %55 %57 %float_0 %59
OpStore %OutColor %49
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%main = OpFunction %void None %10
%27 = OpLabel
%28 = OpLoad %v4float %In0
%33 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
%34 = OpLoad %uint %33
%35 = OpINotEqual %bool %34 %uint_0
OpSelectionMerge %36 None
OpBranchConditional %35 %37 %36
%37 = OpLabel
%38 = OpCompositeInsert %v4float %float_1 %28 3
OpBranch %36
%36 = OpLabel
%39 = OpPhi %v4float %28 %27 %38 %37
%40 = OpCompositeExtract %float %39 0
%41 = OpCompositeExtract %float %39 1
%42 = OpCompositeExtract %float %39 3
%43 = OpCompositeConstruct %v4float %40 %41 %float_0 %42
OpStore %OutColor %43
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InsertExtractElimPass>(
before_predefs + before, after_predefs + after, true, true);
}
TEST_F(InsertExtractElimTest, DeadInsertTwoPasses) {
// Dead insert which requires two passes to eliminate
//
// Note: The SPIR-V assembly has had store/load elimination
// performed to allow the inserts and extracts to directly
// reference each other.
//
// #version 450
//
// layout (location=0) in vec4 In0;
// layout (location=1) in float In1;
// layout (location=2) in float In2;
// layout (location=0) out vec4 OutColor;
//
// layout(std140, binding = 0 ) uniform _Globals_
// {
// bool g_b;
// bool g_b2;
// };
//
// void main()
// {
// vec4 v1, v2;
// v1 = In0;
// v1.y = In1 + In2; // dead, second pass
// if (g_b) v1.x = 1.0;
// v2.x = v1.x;
// v2.y = v1.y; // dead, first pass
// if (g_b2) v2.x = 0.0;
// OutColor = vec4(v2.x,v2.x,0.0,1.0);
// }
const std::string before_predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %In0 "In0"
OpName %In1 "In1"
OpName %In2 "In2"
OpName %_Globals_ "_Globals_"
OpMemberName %_Globals_ 0 "g_b"
OpMemberName %_Globals_ 1 "g_b2"
OpName %_ ""
OpName %OutColor "OutColor"
OpDecorate %In0 Location 0
OpDecorate %In1 Location 1
OpDecorate %In2 Location 2
OpMemberDecorate %_Globals_ 0 Offset 0
OpMemberDecorate %_Globals_ 1 Offset 4
OpDecorate %_Globals_ Block
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
OpDecorate %OutColor Location 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%In0 = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%In1 = OpVariable %_ptr_Input_float Input
%In2 = OpVariable %_ptr_Input_float Input
%uint = OpTypeInt 32 0
%_Globals_ = OpTypeStruct %uint %uint
%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%bool = OpTypeBool
%uint_0 = OpConstant %uint 0
%float_1 = OpConstant %float 1
%int_1 = OpConstant %int 1
%float_0 = OpConstant %float 0
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
%27 = OpUndef %v4float
)";
const std::string after_predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %In0 "In0"
OpName %In1 "In1"
OpName %In2 "In2"
OpName %_Globals_ "_Globals_"
OpMemberName %_Globals_ 0 "g_b"
OpMemberName %_Globals_ 1 "g_b2"
OpName %_ ""
OpName %OutColor "OutColor"
OpDecorate %In0 Location 0
OpDecorate %In1 Location 1
OpDecorate %In2 Location 2
OpMemberDecorate %_Globals_ 0 Offset 0
OpMemberDecorate %_Globals_ 1 Offset 4
OpDecorate %_Globals_ Block
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
OpDecorate %OutColor Location 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%In0 = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%In1 = OpVariable %_ptr_Input_float Input
%In2 = OpVariable %_ptr_Input_float Input
%uint = OpTypeInt 32 0
%_Globals_ = OpTypeStruct %uint %uint
%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%bool = OpTypeBool
%uint_0 = OpConstant %uint 0
%float_1 = OpConstant %float 1
%int_1 = OpConstant %int 1
%float_0 = OpConstant %float 0
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
%27 = OpUndef %v4float
)";
const std::string before =
R"(%main = OpFunction %void None %10
%28 = OpLabel
%29 = OpLoad %v4float %In0
%30 = OpLoad %float %In1
%31 = OpLoad %float %In2
%32 = OpFAdd %float %30 %31
%33 = OpCompositeInsert %v4float %32 %29 1
%34 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
%35 = OpLoad %uint %34
%36 = OpINotEqual %bool %35 %uint_0
OpSelectionMerge %37 None
OpBranchConditional %36 %38 %37
%38 = OpLabel
%39 = OpCompositeInsert %v4float %float_1 %33 0
OpBranch %37
%37 = OpLabel
%40 = OpPhi %v4float %33 %28 %39 %38
%41 = OpCompositeExtract %float %40 0
%42 = OpCompositeInsert %v4float %41 %27 0
%43 = OpCompositeExtract %float %40 1
%44 = OpCompositeInsert %v4float %43 %42 1
%45 = OpAccessChain %_ptr_Uniform_uint %_ %int_1
%46 = OpLoad %uint %45
%47 = OpINotEqual %bool %46 %uint_0
OpSelectionMerge %48 None
OpBranchConditional %47 %49 %48
%49 = OpLabel
%50 = OpCompositeInsert %v4float %float_0 %44 0
OpBranch %48
%48 = OpLabel
%51 = OpPhi %v4float %44 %37 %50 %49
%52 = OpCompositeExtract %float %51 0
%53 = OpCompositeExtract %float %51 0
%54 = OpCompositeConstruct %v4float %52 %53 %float_0 %float_1
OpStore %OutColor %54
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%main = OpFunction %void None %10
%28 = OpLabel
%29 = OpLoad %v4float %In0
%34 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
%35 = OpLoad %uint %34
%36 = OpINotEqual %bool %35 %uint_0
OpSelectionMerge %37 None
OpBranchConditional %36 %38 %37
%38 = OpLabel
%39 = OpCompositeInsert %v4float %float_1 %29 0
OpBranch %37
%37 = OpLabel
%40 = OpPhi %v4float %29 %28 %39 %38
%41 = OpCompositeExtract %float %40 0
%42 = OpCompositeInsert %v4float %41 %27 0
%45 = OpAccessChain %_ptr_Uniform_uint %_ %int_1
%46 = OpLoad %uint %45
%47 = OpINotEqual %bool %46 %uint_0
OpSelectionMerge %48 None
OpBranchConditional %47 %49 %48
%49 = OpLabel
%50 = OpCompositeInsert %v4float %float_0 %42 0
OpBranch %48
%48 = OpLabel
%51 = OpPhi %v4float %42 %37 %50 %49
%52 = OpCompositeExtract %float %51 0
%53 = OpCompositeExtract %float %51 0
%54 = OpCompositeConstruct %v4float %52 %53 %float_0 %float_1
OpStore %OutColor %54
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InsertExtractElimPass>(
before_predefs + before, after_predefs + after, true, true);
}
TEST_F(InsertExtractElimTest, DeadInsertInCycleToDo) {
// Dead insert in chain with cycle. Demonstrates analysis can handle
// cycles in chains.
//
// TODO(greg-lunarg): Improve algorithm to remove dead insert into v.y. Will
// likely require similar logic to ADCE.
//
// Note: The SPIR-V assembly has had store/load elimination
// performed to allow the inserts and extracts to directly
// reference each other.
//
// #version 450
//
// layout (location=0) in vec4 In0;
// layout (location=1) in float In1;
// layout (location=2) in float In2;
// layout (location=0) out vec4 OutColor;
//
// layout(std140, binding = 0 ) uniform _Globals_
// {
// int g_n ;
// };
//
// void main()
// {
// vec2 v = vec2(0.0, 1.0);
// for (int i = 0; i < g_n; i++) {
// v.x = v.x + 1;
// v.y = v.y * 0.9; // dead
// }
// OutColor = vec4(v.x);
// }
const std::string assembly =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %OutColor %In0 %In1 %In2
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %_Globals_ "_Globals_"
OpMemberName %_Globals_ 0 "g_n"
OpName %_ ""
OpName %OutColor "OutColor"
OpName %In0 "In0"
OpName %In1 "In1"
OpName %In2 "In2"
OpMemberDecorate %_Globals_ 0 Offset 0
OpDecorate %_Globals_ Block
OpDecorate %_ DescriptorSet 0
OpDecorate %_ Binding 0
OpDecorate %OutColor Location 0
OpDecorate %In0 Location 0
OpDecorate %In1 Location 1
OpDecorate %In2 Location 2
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%_ptr_Function_v2float = OpTypePointer Function %v2float
%float_0 = OpConstant %float 0
%float_1 = OpConstant %float 1
%16 = OpConstantComposite %v2float %float_0 %float_1
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%_Globals_ = OpTypeStruct %int
%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
%_ptr_Uniform_int = OpTypePointer Uniform %int
%bool = OpTypeBool
%float_0_9 = OpConstant %float 0.9
%int_1 = OpConstant %int 1
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
%_ptr_Input_v4float = OpTypePointer Input %v4float
%In0 = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%In1 = OpVariable %_ptr_Input_float Input
%In2 = OpVariable %_ptr_Input_float Input
%main = OpFunction %void None %10
%29 = OpLabel
OpBranch %30
%30 = OpLabel
%31 = OpPhi %v2float %16 %29 %32 %33
%34 = OpPhi %int %int_0 %29 %35 %33
OpLoopMerge %36 %33 None
OpBranch %37
%37 = OpLabel
%38 = OpAccessChain %_ptr_Uniform_int %_ %int_0
%39 = OpLoad %int %38
%40 = OpSLessThan %bool %34 %39
OpBranchConditional %40 %41 %36
%41 = OpLabel
%42 = OpCompositeExtract %float %31 0
%43 = OpFAdd %float %42 %float_1
%44 = OpCompositeInsert %v2float %43 %31 0
%45 = OpCompositeExtract %float %44 1
%46 = OpFMul %float %45 %float_0_9
%32 = OpCompositeInsert %v2float %46 %44 1
OpBranch %33
%33 = OpLabel
%35 = OpIAdd %int %34 %int_1
OpBranch %30
%36 = OpLabel
%47 = OpCompositeExtract %float %31 0
%48 = OpCompositeConstruct %v4float %47 %47 %47 %47
OpStore %OutColor %48
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<opt::InsertExtractElimPass>(assembly, assembly, true,
true);
}
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//

View File

@ -118,6 +118,10 @@ Options (in lexicographical order):
--eliminate-dead-functions
Deletes functions that cannot be reached from entry points or
exported functions.
--eliminate-dead-insert
Deletes unreferenced inserts into composites, most notably
unused stores to vector components, that are not removed by
aggressive dead code elimination.
--eliminate-dead-variables
Deletes module scope variables that are not referenced.
--eliminate-insert-extract
@ -425,6 +429,8 @@ OptStatus ParseFlags(int argc, const char** argv, Optimizer* optimizer,
optimizer->RegisterPass(CreateCommonUniformElimPass());
} else if (0 == strcmp(cur_arg, "--eliminate-dead-const")) {
optimizer->RegisterPass(CreateEliminateDeadConstantPass());
} else if (0 == strcmp(cur_arg, "--eliminate-dead-inserts")) {
optimizer->RegisterPass(CreateDeadInsertElimPass());
} else if (0 == strcmp(cur_arg, "--eliminate-dead-variables")) {
optimizer->RegisterPass(CreateDeadVariableEliminationPass());
} else if (0 == strcmp(cur_arg, "--fold-spec-const-op-composite")) {