mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-22 11:40:05 +00:00
InsertExtractElim: Split out DeadInsertElim as separate pass
This commit is contained in:
parent
1b46f7ecad
commit
f28b106173
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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
52
source/opt/composite.cpp
Normal 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
50
source/opt/composite.h
Normal 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_
|
310
source/opt/dead_insert_elim_pass.cpp
Normal file
310
source/opt/dead_insert_elim_pass.cpp
Normal 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
|
88
source/opt/dead_insert_elim_pass.h
Normal file
88
source/opt/dead_insert_elim_pass.h
Normal 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_
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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>());
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
685
test/opt/dead_insert_elim_test.cpp
Normal file
685
test/opt/dead_insert_elim_test.cpp
Normal 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
|
@ -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:
|
||||
//
|
||||
|
||||
|
@ -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")) {
|
||||
|
Loading…
Reference in New Issue
Block a user