summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregF <greg@LunarG.com>2017-07-27 13:30:12 -0600
committerDavid Neto <dneto@google.com>2017-08-02 14:24:02 -0400
commitc1b46eedbd8a33370d4bb97e553f8893f10a54f8 (patch)
treed8361ca4302334498848473f3173eb8dd5ba270f
parent30bee67439c0b2fd0be0780c135908b993d9410e (diff)
downloadSPIRV-Tools-c1b46eedbd8a33370d4bb97e553f8893f10a54f8.tar.gz
SPIRV-Tools-c1b46eedbd8a33370d4bb97e553f8893f10a54f8.tar.bz2
SPIRV-Tools-c1b46eedbd8a33370d4bb97e553f8893f10a54f8.zip
Add MemPass, move all shared functions to it.
-rw-r--r--source/opt/CMakeLists.txt2
-rw-r--r--source/opt/aggressive_dead_code_elim_pass.cpp36
-rw-r--r--source/opt/aggressive_dead_code_elim_pass.h17
-rw-r--r--source/opt/dead_branch_elim_pass.cpp37
-rw-r--r--source/opt/dead_branch_elim_pass.h27
-rw-r--r--source/opt/local_access_chain_convert_pass.cpp160
-rw-r--r--source/opt/local_access_chain_convert_pass.h68
-rw-r--r--source/opt/local_single_block_elim_pass.cpp243
-rw-r--r--source/opt/local_single_block_elim_pass.h80
-rw-r--r--source/opt/local_single_store_elim_pass.cpp239
-rw-r--r--source/opt/local_single_store_elim_pass.h81
-rw-r--r--source/opt/local_ssa_elim_pass.cpp224
-rw-r--r--source/opt/local_ssa_elim_pass.h71
-rw-r--r--source/opt/mem_pass.cpp269
-rw-r--r--source/opt/mem_pass.h128
-rw-r--r--test/opt/local_access_chain_convert_test.cpp96
16 files changed, 508 insertions, 1270 deletions
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt
index e7fd25f0..de58f1a5 100644
--- a/source/opt/CMakeLists.txt
+++ b/source/opt/CMakeLists.txt
@@ -37,6 +37,7 @@ add_library(SPIRV-Tools-opt
module.h
null_pass.h
reflect.h
+ mem_pass.h
pass.h
passes.h
pass_manager.h
@@ -69,6 +70,7 @@ add_library(SPIRV-Tools-opt
module.cpp
set_spec_constant_default_value_pass.cpp
optimizer.cpp
+ mem_pass.cpp
pass_manager.cpp
strip_debug_info_pass.cpp
types.cpp
diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp
index 0b1fd3b9..f30dc445 100644
--- a/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -25,45 +25,12 @@ namespace opt {
namespace {
const uint32_t kEntryPointFunctionIdInIdx = 1;
-const uint32_t kStorePtrIdInIdx = 0;
-const uint32_t kLoadPtrIdInIdx = 0;
-const uint32_t kAccessChainPtrIdInIdx = 0;
const uint32_t kTypePointerStorageClassInIdx = 0;
-const uint32_t kCopyObjectOperandInIdx = 0;
const uint32_t kExtInstSetIdInIndx = 0;
const uint32_t kExtInstInstructionInIndx = 1;
} // namespace anonymous
-bool AggressiveDCEPass::IsNonPtrAccessChain(const SpvOp opcode) const {
- return opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain;
-}
-
-ir::Instruction* AggressiveDCEPass::GetPtr(
- ir::Instruction* ip, uint32_t* varId) {
- const SpvOp op = ip->opcode();
- assert(op == SpvOpStore || op == SpvOpLoad);
- *varId = ip->GetSingleWordInOperand(
- op == SpvOpStore ? kStorePtrIdInIdx : kLoadPtrIdInIdx);
- ir::Instruction* ptrInst = def_use_mgr_->GetDef(*varId);
- while (ptrInst->opcode() == SpvOpCopyObject) {
- *varId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
- ptrInst = def_use_mgr_->GetDef(*varId);
- }
- ir::Instruction* varInst = ptrInst;
- while (varInst->opcode() != SpvOpVariable) {
- if (IsNonPtrAccessChain(varInst->opcode())) {
- *varId = varInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
- }
- else {
- assert(varInst->opcode() == SpvOpCopyObject);
- *varId = varInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
- }
- varInst = def_use_mgr_->GetDef(*varId);
- }
- return ptrInst;
-}
-
bool AggressiveDCEPass::IsLocalVar(uint32_t varId) {
const ir::Instruction* varInst = def_use_mgr_->GetDef(varId);
assert(varInst->opcode() == SpvOpVariable);
@@ -279,8 +246,7 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
-AggressiveDCEPass::AggressiveDCEPass()
- : module_(nullptr), def_use_mgr_(nullptr) {}
+AggressiveDCEPass::AggressiveDCEPass() {}
Pass::Status AggressiveDCEPass::Process(ir::Module* module) {
Initialize(module);
diff --git a/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h
index b386c85c..f9733461 100644
--- a/source/opt/aggressive_dead_code_elim_pass.h
+++ b/source/opt/aggressive_dead_code_elim_pass.h
@@ -27,13 +27,13 @@
#include "basic_block.h"
#include "def_use_manager.h"
#include "module.h"
-#include "pass.h"
+#include "mem_pass.h"
namespace spvtools {
namespace opt {
// See optimizer.hpp for documentation.
-class AggressiveDCEPass : public Pass {
+class AggressiveDCEPass : public MemPass {
using cbb_ptr = const ir::BasicBlock*;
@@ -46,13 +46,6 @@ class AggressiveDCEPass : public Pass {
Status Process(ir::Module*) override;
private:
- // Returns true if |opcode| is a non-ptr access chain op
- bool IsNonPtrAccessChain(const SpvOp opcode) const;
-
- // Given a load or store |ip|, return the pointer instruction.
- // Also return the base variable's id in |varId|.
- ir::Instruction* GetPtr(ir::Instruction* ip, uint32_t* varId);
-
// Add all store instruction which use |ptrId|, directly or indirectly,
// to the live instruction worklist.
void AddStores(uint32_t ptrId);
@@ -96,12 +89,6 @@ class AggressiveDCEPass : public Pass {
void Initialize(ir::Module* module);
Pass::Status ProcessImpl();
- // Module this pass is processing
- ir::Module* module_;
-
- // Def-Uses for the module we are processing
- std::unique_ptr<analysis::DefUseManager> def_use_mgr_;
-
// Map from function's result id to function
std::unordered_map<uint32_t, ir::Function*> id2function_;
diff --git a/source/opt/dead_branch_elim_pass.cpp b/source/opt/dead_branch_elim_pass.cpp
index 0c57307b..3be40f9b 100644
--- a/source/opt/dead_branch_elim_pass.cpp
+++ b/source/opt/dead_branch_elim_pass.cpp
@@ -147,31 +147,6 @@ void DeadBranchElimPass::AddBranchConditional(uint32_t condId,
bp->AddInstruction(std::move(newBranchCond));
}
-void DeadBranchElimPass::KillNamesAndDecorates(uint32_t id) {
- // TODO(greg-lunarg): Remove id from any OpGroupDecorate and
- // kill if no other operands.
- if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
- return;
- analysis::UseList* uses = def_use_mgr_->GetUses(id);
- if (uses == nullptr)
- return;
- std::list<ir::Instruction*> killList;
- for (auto u : *uses) {
- const SpvOp op = u.inst->opcode();
- if (op == SpvOpName || IsDecorate(op))
- killList.push_back(u.inst);
- }
- for (auto kip : killList)
- def_use_mgr_->KillInst(kip);
-}
-
-void DeadBranchElimPass::KillNamesAndDecorates(ir::Instruction* inst) {
- const uint32_t rId = inst->result_id();
- if (rId == 0)
- return;
- KillNamesAndDecorates(rId);
-}
-
void DeadBranchElimPass::KillAllInsts(ir::BasicBlock* bp) {
bp->ForEachInst([this](ir::Instruction* ip) {
KillNamesAndDecorates(ip);
@@ -331,15 +306,6 @@ void DeadBranchElimPass::Initialize(ir::Module* module) {
InitExtensions();
};
-void DeadBranchElimPass::FindNamedOrDecoratedIds() {
- for (auto& di : module_->debugs())
- if (di.opcode() == SpvOpName)
- named_or_decorated_ids_.insert(di.GetSingleWordInOperand(0));
- for (auto& ai : module_->annotations())
- if (ai.opcode() == SpvOpDecorate || ai.opcode() == SpvOpDecorateId)
- named_or_decorated_ids_.insert(ai.GetSingleWordInOperand(0));
-}
-
bool DeadBranchElimPass::AllExtensionsSupported() const {
// If any extension not in whitelist, return false
for (auto& ei : module_->extensions()) {
@@ -377,8 +343,7 @@ Pass::Status DeadBranchElimPass::ProcessImpl() {
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
-DeadBranchElimPass::DeadBranchElimPass()
- : module_(nullptr), def_use_mgr_(nullptr) {}
+DeadBranchElimPass::DeadBranchElimPass() {}
Pass::Status DeadBranchElimPass::Process(ir::Module* module) {
Initialize(module);
diff --git a/source/opt/dead_branch_elim_pass.h b/source/opt/dead_branch_elim_pass.h
index 912149ec..07709ddf 100644
--- a/source/opt/dead_branch_elim_pass.h
+++ b/source/opt/dead_branch_elim_pass.h
@@ -28,13 +28,13 @@
#include "basic_block.h"
#include "def_use_manager.h"
#include "module.h"
-#include "pass.h"
+#include "mem_pass.h"
namespace spvtools {
namespace opt {
// See optimizer.hpp for documentation.
-class DeadBranchElimPass : public Pass {
+class DeadBranchElimPass : public MemPass {
using cbb_ptr = const ir::BasicBlock*;
@@ -97,20 +97,6 @@ class DeadBranchElimPass : public Pass {
// Return true if |labelId| has any non-phi references
bool HasNonPhiRef(uint32_t labelId);
- // Return true if |op| is supported decorate.
- inline bool IsDecorate(uint32_t op) const {
- return (op == SpvOpDecorate || op == SpvOpDecorateId);
- }
-
- // Kill all name and decorate ops using |inst|
- void KillNamesAndDecorates(ir::Instruction* inst);
-
- // Kill all name and decorate ops using |id|
- void KillNamesAndDecorates(uint32_t id);
-
- // Collect all named or decorated ids in module
- void FindNamedOrDecoratedIds();
-
// For function |func|, look for BranchConditionals with constant condition
// and convert to a Branch to the indicated label. Delete resulting dead
// blocks. Assumes only structured control flow in shader. Note some such
@@ -128,12 +114,6 @@ class DeadBranchElimPass : public Pass {
void Initialize(ir::Module* module);
Pass::Status ProcessImpl();
- // Module this pass is processing
- ir::Module* module_;
-
- // Def-Uses for the module we are processing
- std::unique_ptr<analysis::DefUseManager> def_use_mgr_;
-
// Map from function's result id to function
std::unordered_map<uint32_t, ir::Function*> id2function_;
@@ -145,9 +125,6 @@ class DeadBranchElimPass : public Pass {
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
block2structured_succs_;
- // named or decorated ids
- std::unordered_set<uint32_t> named_or_decorated_ids_;
-
// Extensions supported by this pass.
std::unordered_set<std::string> extensions_whitelist_;
};
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp
index 1093bd8c..b22555cb 100644
--- a/source/opt/local_access_chain_convert_pass.cpp
+++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -24,120 +24,14 @@ namespace opt {
namespace {
const uint32_t kEntryPointFunctionIdInIdx = 1;
-const uint32_t kStorePtrIdInIdx = 0;
const uint32_t kStoreValIdInIdx = 1;
-const uint32_t kLoadPtrIdInIdx = 0;
const uint32_t kAccessChainPtrIdInIdx = 0;
-const uint32_t kTypePointerStorageClassInIdx = 0;
const uint32_t kTypePointerTypeIdInIdx = 1;
const uint32_t kConstantValueInIdx = 0;
const uint32_t kTypeIntWidthInIdx = 0;
-const uint32_t kCopyObjectOperandInIdx = 0;
} // anonymous namespace
-bool LocalAccessChainConvertPass::IsNonPtrAccessChain(
- const SpvOp opcode) const {
- return opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain;
-}
-
-bool LocalAccessChainConvertPass::IsMathType(
- const ir::Instruction* typeInst) const {
- switch (typeInst->opcode()) {
- case SpvOpTypeInt:
- case SpvOpTypeFloat:
- case SpvOpTypeBool:
- case SpvOpTypeVector:
- case SpvOpTypeMatrix:
- return true;
- default:
- break;
- }
- return false;
-}
-
-bool LocalAccessChainConvertPass::IsTargetType(
- const ir::Instruction* typeInst) const {
- if (IsMathType(typeInst))
- return true;
- if (typeInst->opcode() == SpvOpTypeArray)
- return IsMathType(def_use_mgr_->GetDef(typeInst->GetSingleWordOperand(1)));
- if (typeInst->opcode() != SpvOpTypeStruct)
- return false;
- // All struct members must be math type
- int nonMathComp = 0;
- typeInst->ForEachInId([&nonMathComp,this](const uint32_t* tid) {
- ir::Instruction* compTypeInst = def_use_mgr_->GetDef(*tid);
- if (!IsMathType(compTypeInst)) ++nonMathComp;
- });
- return nonMathComp == 0;
-}
-
-ir::Instruction* LocalAccessChainConvertPass::GetPtr(
- ir::Instruction* ip, uint32_t* varId) {
- const SpvOp op = ip->opcode();
- assert(op == SpvOpStore || op == SpvOpLoad);
- *varId = ip->GetSingleWordInOperand(
- op == SpvOpStore ? kStorePtrIdInIdx : kLoadPtrIdInIdx);
- ir::Instruction* ptrInst = def_use_mgr_->GetDef(*varId);
- while (ptrInst->opcode() == SpvOpCopyObject) {
- *varId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
- ptrInst = def_use_mgr_->GetDef(*varId);
- }
- ir::Instruction* varInst = ptrInst;
- while (varInst->opcode() != SpvOpVariable) {
- if (IsNonPtrAccessChain(varInst->opcode())) {
- *varId = varInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
- }
- else {
- assert(varInst->opcode() == SpvOpCopyObject);
- *varId = varInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
- }
- varInst = def_use_mgr_->GetDef(*varId);
- }
- return ptrInst;
-}
-
-bool LocalAccessChainConvertPass::IsTargetVar(uint32_t varId) {
- if (seen_non_target_vars_.find(varId) != seen_non_target_vars_.end())
- return false;
- if (seen_target_vars_.find(varId) != seen_target_vars_.end())
- return true;
- const ir::Instruction* varInst = def_use_mgr_->GetDef(varId);
- if (varInst->opcode() != SpvOpVariable)
- return false;;
- const uint32_t varTypeId = varInst->type_id();
- const ir::Instruction* varTypeInst = def_use_mgr_->GetDef(varTypeId);
- if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) !=
- SpvStorageClassFunction) {
- seen_non_target_vars_.insert(varId);
- return false;
- }
- const uint32_t varPteTypeId =
- varTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
- ir::Instruction* varPteTypeInst = def_use_mgr_->GetDef(varPteTypeId);
- if (!IsTargetType(varPteTypeInst)) {
- seen_non_target_vars_.insert(varId);
- return false;
- }
- seen_target_vars_.insert(varId);
- return true;
-}
-
-bool LocalAccessChainConvertPass::HasOnlyNamesAndDecorates(uint32_t id) const {
- analysis::UseList* uses = def_use_mgr_->GetUses(id);
- if (uses == nullptr)
- return true;
- if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
- return false;
- for (auto u : *uses) {
- const SpvOp op = u.inst->opcode();
- if (op != SpvOpName && !IsDecorate(op))
- return false;
- }
- return true;
-}
-
void LocalAccessChainConvertPass::DeleteIfUseless(ir::Instruction* inst) {
const uint32_t resId = inst->result_id();
assert(resId != 0);
@@ -147,46 +41,6 @@ void LocalAccessChainConvertPass::DeleteIfUseless(ir::Instruction* inst) {
}
}
-void LocalAccessChainConvertPass::ReplaceAndDeleteLoad(
- ir::Instruction* loadInst,
- uint32_t replId,
- ir::Instruction* ptrInst) {
- const uint32_t loadId = loadInst->result_id();
- KillNamesAndDecorates(loadId);
- (void) def_use_mgr_->ReplaceAllUsesWith(loadId, replId);
- // remove load instruction
- def_use_mgr_->KillInst(loadInst);
- // if access chain, see if it can be removed as well
- if (IsNonPtrAccessChain(ptrInst->opcode())) {
- DeleteIfUseless(ptrInst);
- }
-}
-
-void LocalAccessChainConvertPass::KillNamesAndDecorates(uint32_t id) {
- // TODO(greg-lunarg): Remove id from any OpGroupDecorate and
- // kill if no other operands.
- if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
- return;
- analysis::UseList* uses = def_use_mgr_->GetUses(id);
- if (uses == nullptr)
- return;
- std::list<ir::Instruction*> killList;
- for (auto u : *uses) {
- const SpvOp op = u.inst->opcode();
- if (op == SpvOpName || IsDecorate(op))
- killList.push_back(u.inst);
- }
- for (auto kip : killList)
- def_use_mgr_->KillInst(kip);
-}
-
-void LocalAccessChainConvertPass::KillNamesAndDecorates(ir::Instruction* inst) {
- const uint32_t rId = inst->result_id();
- if (rId == 0)
- return;
- KillNamesAndDecorates(rId);
-}
-
uint32_t LocalAccessChainConvertPass::GetPointeeTypeId(
const ir::Instruction* ptrInst) const {
const uint32_t ptrTypeId = ptrInst->type_id();
@@ -355,7 +209,7 @@ bool LocalAccessChainConvertPass::ConvertLocalAccessChains(ir::Function* func) {
std::vector<std::unique_ptr<ir::Instruction>> newInsts;
uint32_t replId =
GenAccessChainLoadReplacement(ptrInst, &newInsts);
- ReplaceAndDeleteLoad(&*ii, replId, ptrInst);
+ ReplaceAndDeleteLoad(&*ii, replId);
++ii;
ii = ii.InsertBefore(&newInsts);
++ii;
@@ -409,15 +263,6 @@ void LocalAccessChainConvertPass::Initialize(ir::Module* module) {
InitExtensions();
};
-void LocalAccessChainConvertPass::FindNamedOrDecoratedIds() {
- for (auto& di : module_->debugs())
- if (di.opcode() == SpvOpName)
- named_or_decorated_ids_.insert(di.GetSingleWordInOperand(0));
- for (auto& ai : module_->annotations())
- if (ai.opcode() == SpvOpDecorate || ai.opcode() == SpvOpDecorateId)
- named_or_decorated_ids_.insert(ai.GetSingleWordInOperand(0));
-}
-
bool LocalAccessChainConvertPass::AllExtensionsSupported() const {
// If any extension not in whitelist, return false
for (auto& ei : module_->extensions()) {
@@ -461,8 +306,7 @@ Pass::Status LocalAccessChainConvertPass::ProcessImpl() {
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
-LocalAccessChainConvertPass::LocalAccessChainConvertPass()
- : module_(nullptr), def_use_mgr_(nullptr), next_id_(0) {}
+LocalAccessChainConvertPass::LocalAccessChainConvertPass() : next_id_(0) {}
Pass::Status LocalAccessChainConvertPass::Process(ir::Module* module) {
Initialize(module);
diff --git a/source/opt/local_access_chain_convert_pass.h b/source/opt/local_access_chain_convert_pass.h
index c56fc190..c92268be 100644
--- a/source/opt/local_access_chain_convert_pass.h
+++ b/source/opt/local_access_chain_convert_pass.h
@@ -28,76 +28,27 @@
#include "basic_block.h"
#include "def_use_manager.h"
#include "module.h"
-#include "pass.h"
+#include "mem_pass.h"
namespace spvtools {
namespace opt {
// See optimizer.hpp for documentation.
-class LocalAccessChainConvertPass : public Pass {
+class LocalAccessChainConvertPass : public MemPass {
public:
LocalAccessChainConvertPass();
const char* name() const override { return "convert-local-access-chains"; }
Status Process(ir::Module*) override;
private:
- // Returns true if |opcode| is a non-pointer access chain op
- // TODO(): Support conversion of pointer access chains.
- bool IsNonPtrAccessChain(const SpvOp opcode) const;
-
- // Returns true if |typeInst| is a scalar type
- // or a vector or matrix
- bool IsMathType(const ir::Instruction* typeInst) const;
-
- // Returns true if |typeInst| is a math type or a struct or array
- // of a math type.
- // TODO(): Add more complex types to convert
- bool IsTargetType(const ir::Instruction* typeInst) const;
-
- // Given a load or store |ip|, return the pointer instruction.
- // If the pointer is an access chain, |*varId| is its base id.
- // Otherwise it is the id of the pointer of the load/store.
- ir::Instruction* GetPtr(ir::Instruction* ip, uint32_t* varId);
-
// Search |func| and cache function scope variables of target type that are
// not accessed with non-constant-index access chains. Also cache non-target
// variables.
void FindTargetVars(ir::Function* func);
- // Return true if |varId| is a previously identified target variable.
- // Return false if |varId| is a previously identified non-target variable.
- // See FindTargetVars() for definition of target variable. If variable is
- // not cached, return true if variable is a function scope variable of
- // target type, false otherwise. Updates caches of target and non-target
- // variables.
- bool IsTargetVar(uint32_t varId);
-
- // Return true if |op| is supported decorate.
- inline bool IsDecorate(uint32_t op) const {
- return (op == SpvOpDecorate || op == SpvOpDecorateId);
- }
-
- // Return true if all uses of |id| are only name or decorate ops.
- bool HasOnlyNamesAndDecorates(uint32_t id) const;
-
- // Kill all name and decorate ops using |inst|
- void KillNamesAndDecorates(ir::Instruction* inst);
-
- // Kill all name and decorate ops using |id|
- void KillNamesAndDecorates(uint32_t id);
-
- // Collect all named or decorated ids in module
- void FindNamedOrDecoratedIds();
-
// Delete |inst| if it has no uses. Assumes |inst| has a non-zero resultId.
void DeleteIfUseless(ir::Instruction* inst);
- // Replace all instances of |loadInst|'s id with |replId| and delete
- // |loadInst| and its pointer |ptrInst| if it is a useless access chain.
- void ReplaceAndDeleteLoad(ir::Instruction* loadInst,
- uint32_t replId,
- ir::Instruction* ptrInst);
-
// Return type id for |ptrInst|'s pointee
uint32_t GetPointeeTypeId(const ir::Instruction* ptrInst) const;
@@ -164,24 +115,9 @@ class LocalAccessChainConvertPass : public Pass {
void Initialize(ir::Module* module);
Pass::Status ProcessImpl();
- // Module this pass is processing
- ir::Module* module_;
-
- // Def-Uses for the module we are processing
- std::unique_ptr<analysis::DefUseManager> def_use_mgr_;
-
// Map from function's result id to function
std::unordered_map<uint32_t, ir::Function*> id2function_;
- // Cache of verified target vars
- std::unordered_set<uint32_t> seen_target_vars_;
-
- // Cache of verified non-target vars
- std::unordered_set<uint32_t> seen_non_target_vars_;
-
- // named or decorated ids
- std::unordered_set<uint32_t> named_or_decorated_ids_;
-
// Extensions supported by this pass.
std::unordered_set<std::string> extensions_whitelist_;
diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp
index 54c19ca3..1f0031c4 100644
--- a/source/opt/local_single_block_elim_pass.cpp
+++ b/source/opt/local_single_block_elim_pass.cpp
@@ -24,242 +24,10 @@ namespace opt {
namespace {
const uint32_t kEntryPointFunctionIdInIdx = 1;
-const uint32_t kStorePtrIdInIdx = 0;
const uint32_t kStoreValIdInIdx = 1;
-const uint32_t kLoadPtrIdInIdx = 0;
-const uint32_t kAccessChainPtrIdInIdx = 0;
-const uint32_t kTypePointerStorageClassInIdx = 0;
-const uint32_t kTypePointerTypeIdInIdx = 1;
-const uint32_t kCopyObjectOperandInIdx = 0;
} // anonymous namespace
-bool LocalSingleBlockLoadStoreElimPass::IsNonPtrAccessChain(
- const SpvOp opcode) const {
- return opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain;
-}
-
-bool LocalSingleBlockLoadStoreElimPass::IsMathType(
- const ir::Instruction* typeInst) const {
- switch (typeInst->opcode()) {
- case SpvOpTypeInt:
- case SpvOpTypeFloat:
- case SpvOpTypeBool:
- case SpvOpTypeVector:
- case SpvOpTypeMatrix:
- return true;
- default:
- break;
- }
- return false;
-}
-
-bool LocalSingleBlockLoadStoreElimPass::IsTargetType(
- const ir::Instruction* typeInst) const {
- if (IsMathType(typeInst))
- return true;
- if (typeInst->opcode() == SpvOpTypeArray)
- return IsMathType(def_use_mgr_->GetDef(typeInst->GetSingleWordOperand(1)));
- if (typeInst->opcode() != SpvOpTypeStruct)
- return false;
- // All struct members must be math type
- int nonMathComp = 0;
- typeInst->ForEachInId([&nonMathComp,this](const uint32_t* tid) {
- ir::Instruction* compTypeInst = def_use_mgr_->GetDef(*tid);
- if (!IsMathType(compTypeInst)) ++nonMathComp;
- });
- return nonMathComp == 0;
-}
-
-ir::Instruction* LocalSingleBlockLoadStoreElimPass::GetPtr(
- ir::Instruction* ip, uint32_t* varId) {
- const SpvOp op = ip->opcode();
- assert(op == SpvOpStore || op == SpvOpLoad);
- *varId = ip->GetSingleWordInOperand(
- op == SpvOpStore ? kStorePtrIdInIdx : kLoadPtrIdInIdx);
- ir::Instruction* ptrInst = def_use_mgr_->GetDef(*varId);
- while (ptrInst->opcode() == SpvOpCopyObject) {
- *varId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
- ptrInst = def_use_mgr_->GetDef(*varId);
- }
- ir::Instruction* varInst = ptrInst;
- while (varInst->opcode() != SpvOpVariable) {
- if (IsNonPtrAccessChain(varInst->opcode())) {
- *varId = varInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
- }
- else {
- assert(varInst->opcode() == SpvOpCopyObject);
- *varId = varInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
- }
- varInst = def_use_mgr_->GetDef(*varId);
- }
- return ptrInst;
-}
-
-bool LocalSingleBlockLoadStoreElimPass::IsTargetVar(uint32_t varId) {
- if (seen_non_target_vars_.find(varId) != seen_non_target_vars_.end())
- return false;
- if (seen_target_vars_.find(varId) != seen_target_vars_.end())
- return true;
- const ir::Instruction* varInst = def_use_mgr_->GetDef(varId);
- assert(varInst->opcode() == SpvOpVariable);
- const uint32_t varTypeId = varInst->type_id();
- const ir::Instruction* varTypeInst = def_use_mgr_->GetDef(varTypeId);
- if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) !=
- SpvStorageClassFunction) {
- seen_non_target_vars_.insert(varId);
- return false;
- }
- const uint32_t varPteTypeId =
- varTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
- ir::Instruction* varPteTypeInst = def_use_mgr_->GetDef(varPteTypeId);
- if (!IsTargetType(varPteTypeInst)) {
- seen_non_target_vars_.insert(varId);
- return false;
- }
- seen_target_vars_.insert(varId);
- return true;
-}
-
-void LocalSingleBlockLoadStoreElimPass::ReplaceAndDeleteLoad(
- ir::Instruction* loadInst, uint32_t replId) {
- const uint32_t loadId = loadInst->result_id();
- KillNamesAndDecorates(loadId);
- (void) def_use_mgr_->ReplaceAllUsesWith(loadId, replId);
- // TODO(greg-lunarg): Consider moving DCE into separate pass
- DCEInst(loadInst);
-}
-
-bool LocalSingleBlockLoadStoreElimPass::HasLoads(uint32_t ptrId) const {
- analysis::UseList* uses = def_use_mgr_->GetUses(ptrId);
- if (uses == nullptr)
- return false;
- for (auto u : *uses) {
- SpvOp op = u.inst->opcode();
- if (IsNonPtrAccessChain(op)) {
- if (HasLoads(u.inst->result_id()))
- return true;
- }
- else {
- // Conservatively assume that calls will do a load
- // TODO(): Improve analysis around function calls
- if (op == SpvOpLoad || op == SpvOpFunctionCall)
- return true;
- }
- }
- return false;
-}
-
-bool LocalSingleBlockLoadStoreElimPass::IsLiveVar(uint32_t varId) const {
- // non-function scope vars are live
- const ir::Instruction* varInst = def_use_mgr_->GetDef(varId);
- assert(varInst->opcode() == SpvOpVariable);
- const uint32_t varTypeId = varInst->type_id();
- const ir::Instruction* varTypeInst = def_use_mgr_->GetDef(varTypeId);
- if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) !=
- SpvStorageClassFunction)
- return true;
- // test if variable is loaded from
- return HasLoads(varId);
-}
-
-bool LocalSingleBlockLoadStoreElimPass::IsLiveStore(
- ir::Instruction* storeInst) {
- // get store's variable
- uint32_t varId;
- (void) GetPtr(storeInst, &varId);
- return IsLiveVar(varId);
-}
-
-void LocalSingleBlockLoadStoreElimPass::AddStores(
- uint32_t ptr_id, std::queue<ir::Instruction*>* insts) {
- analysis::UseList* uses = def_use_mgr_->GetUses(ptr_id);
- if (uses != nullptr) {
- for (auto u : *uses) {
- if (IsNonPtrAccessChain(u.inst->opcode()))
- AddStores(u.inst->result_id(), insts);
- else if (u.inst->opcode() == SpvOpStore)
- insts->push(u.inst);
- }
- }
-}
-
-bool LocalSingleBlockLoadStoreElimPass::HasOnlyNamesAndDecorates(
- uint32_t id) const {
- analysis::UseList* uses = def_use_mgr_->GetUses(id);
- if (uses == nullptr)
- return true;
- if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
- return false;
- for (auto u : *uses) {
- const SpvOp op = u.inst->opcode();
- if (op != SpvOpName && !IsDecorate(op))
- return false;
- }
- return true;
-}
-
-void LocalSingleBlockLoadStoreElimPass::KillNamesAndDecorates(uint32_t id) {
- // TODO(greg-lunarg): Remove id from any OpGroupDecorate and
- // kill if no other operands.
- if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
- return;
- analysis::UseList* uses = def_use_mgr_->GetUses(id);
- if (uses == nullptr)
- return;
- std::list<ir::Instruction*> killList;
- for (auto u : *uses) {
- const SpvOp op = u.inst->opcode();
- if (op == SpvOpName || IsDecorate(op))
- killList.push_back(u.inst);
- }
- for (auto kip : killList)
- def_use_mgr_->KillInst(kip);
-}
-
-void LocalSingleBlockLoadStoreElimPass::KillNamesAndDecorates(
- ir::Instruction* inst) {
- const uint32_t rId = inst->result_id();
- if (rId == 0)
- return;
- KillNamesAndDecorates(rId);
-}
-
-void LocalSingleBlockLoadStoreElimPass::DCEInst(ir::Instruction* inst) {
- std::queue<ir::Instruction*> deadInsts;
- deadInsts.push(inst);
- while (!deadInsts.empty()) {
- ir::Instruction* di = deadInsts.front();
- // Don't delete labels
- if (di->opcode() == SpvOpLabel) {
- deadInsts.pop();
- continue;
- }
- // Remember operands
- std::vector<uint32_t> ids;
- di->ForEachInId([&ids](uint32_t* iid) {
- ids.push_back(*iid);
- });
- uint32_t varId = 0;
- // Remember variable if dead load
- if (di->opcode() == SpvOpLoad)
- (void) GetPtr(di, &varId);
- KillNamesAndDecorates(di);
- def_use_mgr_->KillInst(di);
- // For all operands with no remaining uses, add their instruction
- // to the dead instruction queue.
- for (auto id : ids) {
- if (HasOnlyNamesAndDecorates(id))
- deadInsts.push(def_use_mgr_->GetDef(id));
- }
- // if a load was deleted and it was the variable's
- // last load, add all its stores to dead queue
- if (varId != 0 && !IsLiveVar(varId))
- AddStores(varId, &deadInsts);
- deadInsts.pop();
- }
-}
-
bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) {
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end())
return true;
@@ -395,15 +163,6 @@ void LocalSingleBlockLoadStoreElimPass::Initialize(ir::Module* module) {
InitExtensions();
};
-void LocalSingleBlockLoadStoreElimPass::FindNamedOrDecoratedIds() {
- for (auto& di : module_->debugs())
- if (di.opcode() == SpvOpName)
- named_or_decorated_ids_.insert(di.GetSingleWordInOperand(0));
- for (auto& ai : module_->annotations())
- if (ai.opcode() == SpvOpDecorate || ai.opcode() == SpvOpDecorateId)
- named_or_decorated_ids_.insert(ai.GetSingleWordInOperand(0));
-}
-
bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const {
// If any extension not in whitelist, return false
for (auto& ei : module_->extensions()) {
@@ -443,7 +202,7 @@ Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() {
}
LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElimPass()
- : module_(nullptr), def_use_mgr_(nullptr), next_id_(0) {}
+ : next_id_(0) {}
Pass::Status LocalSingleBlockLoadStoreElimPass::Process(ir::Module* module) {
Initialize(module);
diff --git a/source/opt/local_single_block_elim_pass.h b/source/opt/local_single_block_elim_pass.h
index 674b699f..f3a92d7c 100644
--- a/source/opt/local_single_block_elim_pass.h
+++ b/source/opt/local_single_block_elim_pass.h
@@ -28,80 +28,19 @@
#include "basic_block.h"
#include "def_use_manager.h"
#include "module.h"
-#include "pass.h"
+#include "mem_pass.h"
namespace spvtools {
namespace opt {
// See optimizer.hpp for documentation.
-class LocalSingleBlockLoadStoreElimPass : public Pass {
+class LocalSingleBlockLoadStoreElimPass : public MemPass {
public:
LocalSingleBlockLoadStoreElimPass();
const char* name() const override { return "eliminate-local-single-block"; }
Status Process(ir::Module*) override;
private:
- // Returns true if |opcode| is a non-ptr access chain op
- bool IsNonPtrAccessChain(const SpvOp opcode) const;
-
- // Returns true if |typeInst| is a scalar type
- // or a vector or matrix
- bool IsMathType(const ir::Instruction* typeInst) const;
-
- // Returns true if |typeInst| is a math type or a struct or array
- // of a math type.
- bool IsTargetType(const ir::Instruction* typeInst) const;
-
- // Given a load or store |ip|, return the pointer instruction.
- // Also return the base variable's id in |varId|.
- ir::Instruction* GetPtr(ir::Instruction* ip, uint32_t* varId);
-
- // Return true if |varId| is a previously identified target variable.
- // Return false if |varId| is a previously identified non-target variable.
- // If variable is not cached, return true if variable is a function scope
- // variable of target type, false otherwise. Updates caches of target
- // and non-target variables.
- bool IsTargetVar(uint32_t varId);
-
- // Replace all instances of |loadInst|'s id with |replId| and delete
- // |loadInst|.
- void ReplaceAndDeleteLoad(ir::Instruction* loadInst, uint32_t replId);
-
- // Return true if any instruction loads from |ptrId|
- bool HasLoads(uint32_t ptrId) const;
-
- // Return true if |varId| is not a function variable or if it has
- // a load
- bool IsLiveVar(uint32_t varId) const;
-
- // Return true if |storeInst| is not to function variable or if its
- // base variable has a load
- bool IsLiveStore(ir::Instruction* storeInst);
-
- // Add stores using |ptr_id| to |insts|
- void AddStores(uint32_t ptr_id, std::queue<ir::Instruction*>* insts);
-
- // Return true if |op| is supported decorate.
- inline bool IsDecorate(uint32_t op) const {
- return (op == SpvOpDecorate || op == SpvOpDecorateId);
- }
-
- // Return true if all uses of |id| are only name or decorate ops.
- bool HasOnlyNamesAndDecorates(uint32_t id) const;
-
- // Kill all name and decorate ops using |inst|
- void KillNamesAndDecorates(ir::Instruction* inst);
-
- // Kill all name and decorate ops using |id|
- void KillNamesAndDecorates(uint32_t id);
-
- // Collect all named or decorated ids in module
- void FindNamedOrDecoratedIds();
-
- // Delete |inst| and iterate DCE on all its operands. Won't delete
- // labels.
- void DCEInst(ir::Instruction* inst);
-
// Return true if all uses of |varId| are only through supported reference
// operations ie. loads and store. Also cache in supported_ref_ptrs_;
bool HasOnlySupportedRefs(uint32_t varId);
@@ -133,21 +72,9 @@ class LocalSingleBlockLoadStoreElimPass : public Pass {
void Initialize(ir::Module* module);
Pass::Status ProcessImpl();
- // Module this pass is processing
- ir::Module* module_;
-
- // Def-Uses for the module we are processing
- std::unique_ptr<analysis::DefUseManager> def_use_mgr_;
-
// Map from function's result id to function
std::unordered_map<uint32_t, ir::Function*> id2function_;
- // Cache of previously seen target types
- std::unordered_set<uint32_t> seen_target_vars_;
-
- // Cache of previously seen non-target types
- std::unordered_set<uint32_t> seen_non_target_vars_;
-
// Map from function scope variable to a store of that variable in the
// current block whose value is currently valid. This map is cleared
// at the start of each block and incrementally updated as the block
@@ -169,9 +96,6 @@ class LocalSingleBlockLoadStoreElimPass : public Pass {
// from this set each time a new store of that variable is encountered.
std::unordered_set<uint32_t> pinned_vars_;
- // named or decorated ids
- std::unordered_set<uint32_t> named_or_decorated_ids_;
-
// Extensions supported by this pass.
std::unordered_set<std::string> extensions_whitelist_;
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp
index ed11c441..9f9df3a3 100644
--- a/source/opt/local_single_store_elim_pass.cpp
+++ b/source/opt/local_single_store_elim_pass.cpp
@@ -29,102 +29,10 @@ namespace opt {
namespace {
const uint32_t kEntryPointFunctionIdInIdx = 1;
-const uint32_t kStorePtrIdInIdx = 0;
const uint32_t kStoreValIdInIdx = 1;
-const uint32_t kLoadPtrIdInIdx = 0;
-const uint32_t kAccessChainPtrIdInIdx = 0;
-const uint32_t kTypePointerStorageClassInIdx = 0;
-const uint32_t kTypePointerTypeIdInIdx = 1;
-const uint32_t kCopyObjectOperandInIdx = 0;
} // anonymous namespace
-bool LocalSingleStoreElimPass::IsNonPtrAccessChain(const SpvOp opcode) const {
- return opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain;
-}
-
-bool LocalSingleStoreElimPass::IsMathType(
- const ir::Instruction* typeInst) const {
- switch (typeInst->opcode()) {
- case SpvOpTypeInt:
- case SpvOpTypeFloat:
- case SpvOpTypeBool:
- case SpvOpTypeVector:
- case SpvOpTypeMatrix:
- return true;
- default:
- break;
- }
- return false;
-}
-
-bool LocalSingleStoreElimPass::IsTargetType(
- const ir::Instruction* typeInst) const {
- if (IsMathType(typeInst))
- return true;
- if (typeInst->opcode() == SpvOpTypeArray)
- return IsMathType(def_use_mgr_->GetDef(typeInst->GetSingleWordOperand(1)));
- if (typeInst->opcode() != SpvOpTypeStruct)
- return false;
- // All struct members must be math type
- int nonMathComp = 0;
- typeInst->ForEachInId([&nonMathComp,this](const uint32_t* tid) {
- ir::Instruction* compTypeInst = def_use_mgr_->GetDef(*tid);
- if (!IsMathType(compTypeInst)) ++nonMathComp;
- });
- return nonMathComp == 0;
-}
-
-ir::Instruction* LocalSingleStoreElimPass::GetPtr(
- ir::Instruction* ip, uint32_t* varId) {
- const SpvOp op = ip->opcode();
- assert(op == SpvOpStore || op == SpvOpLoad);
- *varId = ip->GetSingleWordInOperand(
- op == SpvOpStore ? kStorePtrIdInIdx : kLoadPtrIdInIdx);
- ir::Instruction* ptrInst = def_use_mgr_->GetDef(*varId);
- while (ptrInst->opcode() == SpvOpCopyObject) {
- *varId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
- ptrInst = def_use_mgr_->GetDef(*varId);
- }
- ir::Instruction* varInst = ptrInst;
- while (varInst->opcode() != SpvOpVariable) {
- if (IsNonPtrAccessChain(varInst->opcode())) {
- *varId = varInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
- }
- else {
- assert(varInst->opcode() == SpvOpCopyObject);
- *varId = varInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
- }
- varInst = def_use_mgr_->GetDef(*varId);
- }
- return ptrInst;
-}
-
-bool LocalSingleStoreElimPass::IsTargetVar(uint32_t varId) {
- if (seen_non_target_vars_.find(varId) != seen_non_target_vars_.end())
- return false;
- if (seen_target_vars_.find(varId) != seen_target_vars_.end())
- return true;
- const ir::Instruction* varInst = def_use_mgr_->GetDef(varId);
- assert(varInst->opcode() == SpvOpVariable);
- const uint32_t varTypeId = varInst->type_id();
- const ir::Instruction* varTypeInst = def_use_mgr_->GetDef(varTypeId);
- if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) !=
- SpvStorageClassFunction) {
- seen_non_target_vars_.insert(varId);
- return false;
- }
- const uint32_t varPteTypeId =
- varTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
- ir::Instruction* varPteTypeInst = def_use_mgr_->GetDef(varPteTypeId);
- if (!IsTargetType(varPteTypeInst)) {
- seen_non_target_vars_.insert(varId);
- return false;
- }
- seen_target_vars_.insert(varId);
- return true;
-}
-
bool LocalSingleStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) {
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end())
return true;
@@ -189,14 +97,6 @@ void LocalSingleStoreElimPass::SingleStoreAnalyze(ir::Function* func) {
}
}
-void LocalSingleStoreElimPass::ReplaceAndDeleteLoad(
- ir::Instruction* loadInst, uint32_t replId) {
- const uint32_t loadId = loadInst->result_id();
- KillNamesAndDecorates(loadId);
- (void) def_use_mgr_->ReplaceAllUsesWith(loadId, replId);
- DCEInst(loadInst);
-}
-
LocalSingleStoreElimPass::GetBlocksFunction
LocalSingleStoreElimPass::AugmentedCFGSuccessorsFunction() const {
return [this](const ir::BasicBlock* block) {
@@ -309,133 +209,6 @@ bool LocalSingleStoreElimPass::SingleStoreProcess(ir::Function* func) {
return modified;
}
-bool LocalSingleStoreElimPass::HasLoads(uint32_t varId) const {
- analysis::UseList* uses = def_use_mgr_->GetUses(varId);
- if (uses == nullptr)
- return false;
- for (auto u : *uses) {
- SpvOp op = u.inst->opcode();
- // TODO(): The following is slightly conservative. Could be
- // better handling of non-store/name.
- if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) {
- if (HasLoads(u.inst->result_id()))
- return true;
- }
- else if (op != SpvOpStore && op != SpvOpName)
- return true;
- }
- return false;
-}
-
-bool LocalSingleStoreElimPass::IsLiveVar(uint32_t varId) const {
- // non-function scope vars are live
- const ir::Instruction* varInst = def_use_mgr_->GetDef(varId);
- assert(varInst->opcode() == SpvOpVariable);
- const uint32_t varTypeId = varInst->type_id();
- const ir::Instruction* varTypeInst = def_use_mgr_->GetDef(varTypeId);
- if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) !=
- SpvStorageClassFunction)
- return true;
- // test if variable is loaded from
- return HasLoads(varId);
-}
-
-bool LocalSingleStoreElimPass::IsLiveStore(ir::Instruction* storeInst) {
- // get store's variable
- uint32_t varId;
- (void) GetPtr(storeInst, &varId);
- return IsLiveVar(varId);
-}
-
-void LocalSingleStoreElimPass::AddStores(
- uint32_t ptr_id, std::queue<ir::Instruction*>* insts) {
- analysis::UseList* uses = def_use_mgr_->GetUses(ptr_id);
- if (uses != nullptr) {
- for (auto u : *uses) {
- if (IsNonPtrAccessChain(u.inst->opcode()))
- AddStores(u.inst->result_id(), insts);
- else if (u.inst->opcode() == SpvOpStore)
- insts->push(u.inst);
- }
- }
-}
-
-bool LocalSingleStoreElimPass::HasOnlyNamesAndDecorates(
- uint32_t id) const {
- analysis::UseList* uses = def_use_mgr_->GetUses(id);
- if (uses == nullptr)
- return true;
- if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
- return false;
- for (auto u : *uses) {
- const SpvOp op = u.inst->opcode();
- if (op != SpvOpName && !IsDecorate(op))
- return false;
- }
- return true;
-}
-
-void LocalSingleStoreElimPass::KillNamesAndDecorates(uint32_t id) {
- // TODO(greg-lunarg): Remove id from any OpGroupDecorate and
- // kill if no other operands.
- if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
- return;
- analysis::UseList* uses = def_use_mgr_->GetUses(id);
- if (uses == nullptr)
- return;
- std::list<ir::Instruction*> killList;
- for (auto u : *uses) {
- const SpvOp op = u.inst->opcode();
- if (op == SpvOpName || IsDecorate(op))
- killList.push_back(u.inst);
- }
- for (auto kip : killList)
- def_use_mgr_->KillInst(kip);
-}
-
-void LocalSingleStoreElimPass::KillNamesAndDecorates(
- ir::Instruction* inst) {
- const uint32_t rId = inst->result_id();
- if (rId == 0)
- return;
- KillNamesAndDecorates(rId);
-}
-
-void LocalSingleStoreElimPass::DCEInst(ir::Instruction* inst) {
- std::queue<ir::Instruction*> deadInsts;
- deadInsts.push(inst);
- while (!deadInsts.empty()) {
- ir::Instruction* di = deadInsts.front();
- // Don't delete labels
- if (di->opcode() == SpvOpLabel) {
- deadInsts.pop();
- continue;
- }
- // Remember operands
- std::vector<uint32_t> ids;
- di->ForEachInId([&ids](uint32_t* iid) {
- ids.push_back(*iid);
- });
- uint32_t varId = 0;
- // Remember variable if dead load
- if (di->opcode() == SpvOpLoad)
- (void) GetPtr(di, &varId);
- KillNamesAndDecorates(di);
- def_use_mgr_->KillInst(di);
- // For all operands with no remaining uses, add their instruction
- // to the dead instruction queue.
- for (auto id : ids) {
- if (HasOnlyNamesAndDecorates(id))
- deadInsts.push(def_use_mgr_->GetDef(id));
- }
- // if a load was deleted and it was the variable's
- // last load, add all its stores to dead queue
- if (varId != 0 && !IsLiveVar(varId))
- AddStores(varId, &deadInsts);
- deadInsts.pop();
- }
-}
-
bool LocalSingleStoreElimPass::SingleStoreDCE() {
bool modified = false;
for (auto v : ssa_var2store_) {
@@ -493,15 +266,6 @@ void LocalSingleStoreElimPass::Initialize(ir::Module* module) {
InitExtensions();
};
-void LocalSingleStoreElimPass::FindNamedOrDecoratedIds() {
- for (auto& di : module_->debugs())
- if (di.opcode() == SpvOpName)
- named_or_decorated_ids_.insert(di.GetSingleWordInOperand(0));
- for (auto& ai : module_->annotations())
- if (ai.opcode() == SpvOpDecorate || ai.opcode() == SpvOpDecorateId)
- named_or_decorated_ids_.insert(ai.GetSingleWordInOperand(0));
-}
-
bool LocalSingleStoreElimPass::AllExtensionsSupported() const {
// If any extension not in whitelist, return false
for (auto& ei : module_->extensions()) {
@@ -540,8 +304,7 @@ Pass::Status LocalSingleStoreElimPass::ProcessImpl() {
}
LocalSingleStoreElimPass::LocalSingleStoreElimPass()
- : module_(nullptr), def_use_mgr_(nullptr),
- pseudo_entry_block_(std::unique_ptr<ir::Instruction>(
+ : pseudo_entry_block_(std::unique_ptr<ir::Instruction>(
new ir::Instruction(SpvOpLabel, 0, 0, {}))),
pseudo_exit_block_(std::unique_ptr<ir::Instruction>(
new ir::Instruction(SpvOpLabel, 0, kInvalidId, {}))),
diff --git a/source/opt/local_single_store_elim_pass.h b/source/opt/local_single_store_elim_pass.h
index 84b9ea6f..10234a3b 100644
--- a/source/opt/local_single_store_elim_pass.h
+++ b/source/opt/local_single_store_elim_pass.h
@@ -28,13 +28,13 @@
#include "basic_block.h"
#include "def_use_manager.h"
#include "module.h"
-#include "pass.h"
+#include "mem_pass.h"
namespace spvtools {
namespace opt {
// See optimizer.hpp for documentation.
-class LocalSingleStoreElimPass : public Pass {
+class LocalSingleStoreElimPass : public MemPass {
using cbb_ptr = const ir::BasicBlock*;
public:
@@ -43,28 +43,6 @@ class LocalSingleStoreElimPass : public Pass {
Status Process(ir::Module*) override;
private:
- // Returns true if |opcode| is a non-ptr access chain op
- bool IsNonPtrAccessChain(const SpvOp opcode) const;
-
- // Returns true if |typeInst| is a scalar type
- // or a vector or matrix
- bool IsMathType(const ir::Instruction* typeInst) const;
-
- // Returns true if |typeInst| is a math type or a struct or array
- // of a math type.
- bool IsTargetType(const ir::Instruction* typeInst) const;
-
- // Given a load or store |ip|, return the pointer instruction.
- // Also return the base variable's id in |varId|.
- ir::Instruction* GetPtr(ir::Instruction* ip, uint32_t* varId);
-
- // Return true if |varId| is a previously identified target variable.
- // Return false if |varId| is a previously identified non-target variable.
- // If variable is not cached, return true if variable is a function scope
- // variable of target type, false otherwise. Updates caches of target
- // and non-target variables.
- bool IsTargetVar(uint32_t varId);
-
// Return true if all refs through |ptrId| are only loads or stores and
// cache ptrId in supported_ref_ptrs_.
bool HasOnlySupportedRefs(uint32_t ptrId);
@@ -77,10 +55,6 @@ class LocalSingleStoreElimPass : public Pass {
// analysis in the presence of function calls.
void SingleStoreAnalyze(ir::Function* func);
- // Replace all instances of |loadInst|'s id with |replId| and delete
- // |loadInst|.
- void ReplaceAndDeleteLoad(ir::Instruction* loadInst, uint32_t replId);
-
using GetBlocksFunction =
std::function<const std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
@@ -105,42 +79,6 @@ class LocalSingleStoreElimPass : public Pass {
// if any instructions are modified.
bool SingleStoreProcess(ir::Function* func);
- // Return true if any instruction loads from |varId|
- bool HasLoads(uint32_t varId) const;
-
- // Return true if |varId| is not a function variable or if it has
- // a load
- bool IsLiveVar(uint32_t varId) const;
-
- // Return true if |storeInst| is not a function variable or if its
- // base variable has a load
- bool IsLiveStore(ir::Instruction* storeInst);
-
- // Add stores using |ptr_id| to |insts|
- void AddStores(uint32_t ptr_id, std::queue<ir::Instruction*>* insts);
-
- // Return true if |op| is supported decorate.
- inline bool IsDecorate(uint32_t op) const {
- return (op == SpvOpDecorate || op == SpvOpDecorateId);
- }
-
- // Return true if all uses of |id| are only name or decorate ops.
- bool HasOnlyNamesAndDecorates(uint32_t id) const;
-
- // Kill all name and decorate ops using |inst|
- void KillNamesAndDecorates(ir::Instruction* inst);
-
- // Kill all name and decorate ops using |id|
- void KillNamesAndDecorates(uint32_t id);
-
- // Collect all named or decorated ids in module
- void FindNamedOrDecoratedIds();
-
- // Delete |inst| and iterate DCE on all its operands if they are now
- // useless. If a load is deleted and its variable has no other loads,
- // delete all its variable's stores.
- void DCEInst(ir::Instruction* inst);
-
// Remove all stores to useless SSA variables. Remove useless
// access chains and variables as well. Assumes SingleStoreAnalyze
// and SingleStoreProcess has been run.
@@ -171,12 +109,6 @@ class LocalSingleStoreElimPass : public Pass {
void Initialize(ir::Module* module);
Pass::Status ProcessImpl();
- // Module this pass is processing
- ir::Module* module_;
-
- // Def-Uses for the module we are processing
- std::unique_ptr<analysis::DefUseManager> def_use_mgr_;
-
// Map from function's result id to function
std::unordered_map<uint32_t, ir::Function*> id2function_;
@@ -195,12 +127,6 @@ class LocalSingleStoreElimPass : public Pass {
// Set of non-SSA Variables
std::unordered_set<uint32_t> non_ssa_vars_;
- // Cache of previously seen target types
- std::unordered_set<uint32_t> seen_target_vars_;
-
- // Cache of previously seen non-target types
- std::unordered_set<uint32_t> seen_non_target_vars_;
-
// Variables with only supported references, ie. loads and stores using
// variable directly or through non-ptr access chains.
std::unordered_set<uint32_t> supported_ref_ptrs_;
@@ -231,9 +157,6 @@ class LocalSingleStoreElimPass : public Pass {
// If block has no idom it points to itself.
std::unordered_map<ir::BasicBlock*, ir::BasicBlock*> idom_;
- // named or decorated ids
- std::unordered_set<uint32_t> named_or_decorated_ids_;
-
// Extensions supported by this pass.
std::unordered_set<std::string> extensions_whitelist_;
diff --git a/source/opt/local_ssa_elim_pass.cpp b/source/opt/local_ssa_elim_pass.cpp
index 60cb5c97..e1363eb7 100644
--- a/source/opt/local_ssa_elim_pass.cpp
+++ b/source/opt/local_ssa_elim_pass.cpp
@@ -25,226 +25,14 @@ namespace opt {
namespace {
const uint32_t kEntryPointFunctionIdInIdx = 1;
-const uint32_t kStorePtrIdInIdx = 0;
const uint32_t kStoreValIdInIdx = 1;
-const uint32_t kLoadPtrIdInIdx = 0;
-const uint32_t kAccessChainPtrIdInIdx = 0;
-const uint32_t kTypePointerStorageClassInIdx = 0;
const uint32_t kTypePointerTypeIdInIdx = 1;
const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
const uint32_t kLoopMergeMergeBlockIdInIdx = 0;
const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
-const uint32_t kCopyObjectOperandInIdx = 0;
} // anonymous namespace
-bool LocalMultiStoreElimPass::IsNonPtrAccessChain(
- const SpvOp opcode) const {
- return opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain;
-}
-
-bool LocalMultiStoreElimPass::IsMathType(
- const ir::Instruction* typeInst) const {
- switch (typeInst->opcode()) {
- case SpvOpTypeInt:
- case SpvOpTypeFloat:
- case SpvOpTypeBool:
- case SpvOpTypeVector:
- case SpvOpTypeMatrix:
- return true;
- default:
- break;
- }
- return false;
-}
-
-bool LocalMultiStoreElimPass::IsTargetType(
- const ir::Instruction* typeInst) const {
- if (IsMathType(typeInst))
- return true;
- if (typeInst->opcode() == SpvOpTypeArray)
- return IsMathType(def_use_mgr_->GetDef(typeInst->GetSingleWordOperand(1)));
- if (typeInst->opcode() != SpvOpTypeStruct)
- return false;
- // All struct members must be math type
- int nonMathComp = 0;
- typeInst->ForEachInId([&nonMathComp,this](const uint32_t* tid) {
- const ir::Instruction* compTypeInst = def_use_mgr_->GetDef(*tid);
- if (!IsMathType(compTypeInst)) ++nonMathComp;
- });
- return nonMathComp == 0;
-}
-
-ir::Instruction* LocalMultiStoreElimPass::GetPtr(
- ir::Instruction* ip, uint32_t* varId) {
- const SpvOp op = ip->opcode();
- assert(op == SpvOpStore || op == SpvOpLoad);
- *varId = ip->GetSingleWordInOperand(
- op == SpvOpStore ? kStorePtrIdInIdx : kLoadPtrIdInIdx);
- ir::Instruction* ptrInst = def_use_mgr_->GetDef(*varId);
- while (ptrInst->opcode() == SpvOpCopyObject) {
- *varId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
- ptrInst = def_use_mgr_->GetDef(*varId);
- }
- ir::Instruction* varInst = ptrInst;
- while (varInst->opcode() != SpvOpVariable) {
- if (IsNonPtrAccessChain(varInst->opcode())) {
- *varId = varInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
- }
- else {
- assert(varInst->opcode() == SpvOpCopyObject);
- *varId = varInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
- }
- varInst = def_use_mgr_->GetDef(*varId);
- }
- return ptrInst;
-}
-
-bool LocalMultiStoreElimPass::IsTargetVar(uint32_t varId) {
- if (seen_non_target_vars_.find(varId) != seen_non_target_vars_.end())
- return false;
- if (seen_target_vars_.find(varId) != seen_target_vars_.end())
- return true;
- const ir::Instruction* varInst = def_use_mgr_->GetDef(varId);
- assert(varInst->opcode() == SpvOpVariable);
- const uint32_t varTypeId = varInst->type_id();
- const ir::Instruction* varTypeInst = def_use_mgr_->GetDef(varTypeId);
- if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) !=
- SpvStorageClassFunction) {
- seen_non_target_vars_.insert(varId);
- return false;
- }
- const uint32_t varPteTypeId =
- varTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
- ir::Instruction* varPteTypeInst = def_use_mgr_->GetDef(varPteTypeId);
- if (!IsTargetType(varPteTypeInst)) {
- seen_non_target_vars_.insert(varId);
- return false;
- }
- seen_target_vars_.insert(varId);
- return true;
-}
-
-bool LocalMultiStoreElimPass::HasLoads(uint32_t ptrId) const {
- analysis::UseList* uses = def_use_mgr_->GetUses(ptrId);
- if (uses == nullptr)
- return false;
- for (auto u : *uses) {
- const SpvOp op = u.inst->opcode();
- if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) {
- if (HasLoads(u.inst->result_id()))
- return true;
- }
- else {
- // Conservatively assume that any non-store use is a load
- // TODO(greg-lunarg): Improve analysis around function calls, etc
- if (op != SpvOpStore && op != SpvOpName && !IsDecorate(op))
- return true;
- }
- }
- return false;
-}
-
-bool LocalMultiStoreElimPass::IsLiveVar(uint32_t varId) const {
- // non-function scope vars are live
- const ir::Instruction* varInst = def_use_mgr_->GetDef(varId);
- assert(varInst->opcode() == SpvOpVariable);
- const uint32_t varTypeId = varInst->type_id();
- const ir::Instruction* varTypeInst = def_use_mgr_->GetDef(varTypeId);
- if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) !=
- SpvStorageClassFunction)
- return true;
- // test if variable is loaded from
- return HasLoads(varId);
-}
-
-void LocalMultiStoreElimPass::AddStores(
- uint32_t ptr_id, std::queue<ir::Instruction*>* insts) {
- analysis::UseList* uses = def_use_mgr_->GetUses(ptr_id);
- if (uses != nullptr) {
- for (auto u : *uses) {
- if (IsNonPtrAccessChain(u.inst->opcode()))
- AddStores(u.inst->result_id(), insts);
- else if (u.inst->opcode() == SpvOpStore)
- insts->push(u.inst);
- }
- }
-}
-
-bool LocalMultiStoreElimPass::HasOnlyNamesAndDecorates(uint32_t id) const {
- analysis::UseList* uses = def_use_mgr_->GetUses(id);
- if (uses == nullptr)
- return true;
- if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
- return false;
- for (auto u : *uses) {
- const SpvOp op = u.inst->opcode();
- if (op != SpvOpName && !IsDecorate(op))
- return false;
- }
- return true;
-}
-
-void LocalMultiStoreElimPass::KillNamesAndDecorates(uint32_t id) {
- // TODO(greg-lunarg): Remove id from any OpGroupDecorate and
- // kill if no other operands.
- if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
- return;
- analysis::UseList* uses = def_use_mgr_->GetUses(id);
- if (uses == nullptr)
- return;
- std::list<ir::Instruction*> killList;
- for (auto u : *uses) {
- const SpvOp op = u.inst->opcode();
- if (op != SpvOpName && !IsDecorate(op))
- continue;
- killList.push_back(u.inst);
- }
- for (auto kip : killList)
- def_use_mgr_->KillInst(kip);
-}
-
-void LocalMultiStoreElimPass::KillNamesAndDecorates(ir::Instruction* inst) {
- const uint32_t rId = inst->result_id();
- if (rId == 0)
- return;
- KillNamesAndDecorates(rId);
-}
-
-void LocalMultiStoreElimPass::DCEInst(ir::Instruction* inst) {
- std::queue<ir::Instruction*> deadInsts;
- deadInsts.push(inst);
- while (!deadInsts.empty()) {
- ir::Instruction* di = deadInsts.front();
- // Don't delete labels
- if (di->opcode() == SpvOpLabel) {
- deadInsts.pop();
- continue;
- }
- // Remember operands
- std::vector<uint32_t> ids;
- di->ForEachInId([&ids](uint32_t* iid) {
- ids.push_back(*iid);
- });
- uint32_t varId = 0;
- // Remember variable if dead load
- if (di->opcode() == SpvOpLoad)
- (void) GetPtr(di, &varId);
- KillNamesAndDecorates(di);
- def_use_mgr_->KillInst(di);
- // For all operands with no remaining uses, add their instruction
- // to the dead instruction queue.
- for (auto id : ids)
- if (HasOnlyNamesAndDecorates(id))
- deadInsts.push(def_use_mgr_->GetDef(id));
- // if a load was deleted and it was the variable's
- // last load, add all its stores to dead queue
- if (varId != 0 && !IsLiveVar(varId))
- AddStores(varId, &deadInsts);
- deadInsts.pop();
- }
-}
-
bool LocalMultiStoreElimPass::HasOnlySupportedRefs(uint32_t varId) {
if (supported_ref_vars_.find(varId) != supported_ref_vars_.end())
return true;
@@ -740,15 +528,6 @@ bool LocalMultiStoreElimPass::AllExtensionsSupported() const {
return true;
}
-void LocalMultiStoreElimPass::FindNamedOrDecoratedIds() {
- for (auto& di : module_->debugs())
- if (di.opcode() == SpvOpName)
- named_or_decorated_ids_.insert(di.GetSingleWordInOperand(0));
- for (auto& ai : module_->annotations())
- if (ai.opcode() == SpvOpDecorate || ai.opcode() == SpvOpDecorateId)
- named_or_decorated_ids_.insert(ai.GetSingleWordInOperand(0));
-}
-
Pass::Status LocalMultiStoreElimPass::ProcessImpl() {
// Assumes all control flow structured.
// TODO(greg-lunarg): Do SSA rewrite for non-structured control flow
@@ -781,8 +560,7 @@ Pass::Status LocalMultiStoreElimPass::ProcessImpl() {
}
LocalMultiStoreElimPass::LocalMultiStoreElimPass()
- : module_(nullptr), def_use_mgr_(nullptr),
- pseudo_entry_block_(std::unique_ptr<ir::Instruction>(
+ : pseudo_entry_block_(std::unique_ptr<ir::Instruction>(
new ir::Instruction(SpvOpLabel, 0, 0, {}))),
next_id_(0) {}
diff --git a/source/opt/local_ssa_elim_pass.h b/source/opt/local_ssa_elim_pass.h
index 8237c30e..7d8d2e1f 100644
--- a/source/opt/local_ssa_elim_pass.h
+++ b/source/opt/local_ssa_elim_pass.h
@@ -28,13 +28,13 @@
#include "basic_block.h"
#include "def_use_manager.h"
#include "module.h"
-#include "pass.h"
+#include "mem_pass.h"
namespace spvtools {
namespace opt {
// See optimizer.hpp for documentation.
-class LocalMultiStoreElimPass : public Pass {
+class LocalMultiStoreElimPass : public MemPass {
using cbb_ptr = const ir::BasicBlock*;
public:
@@ -46,62 +46,13 @@ class LocalMultiStoreElimPass : public Pass {
Status Process(ir::Module*) override;
private:
- // Returns true if |opcode| is a non-ptr access chain op
- bool IsNonPtrAccessChain(const SpvOp opcode) const;
-
- // Returns true if |typeInst| is a scalar type
- // or a vector or matrix
- bool IsMathType(const ir::Instruction* typeInst) const;
-
- // Returns true if |typeInst| is a math type or a struct or array
- // of a math type.
- bool IsTargetType(const ir::Instruction* typeInst) const;
-
- // Given a load or store |ip|, return the pointer instruction.
- // Also return the base variable's id in |varId|.
- ir::Instruction* GetPtr(ir::Instruction* ip, uint32_t* varId);
-
- // Return true if |varId| is a previously identified target variable.
- // Return false if |varId| is a previously identified non-target variable.
- // If variable is not cached, return true if variable is a function scope
- // variable of target type, false otherwise. Updates caches of target
- // and non-target variables.
- bool IsTargetVar(uint32_t varId);
-
// Return type id for |ptrInst|'s pointee
uint32_t GetPointeeTypeId(const ir::Instruction* ptrInst) const;
- // Replace all instances of |loadInst|'s id with |replId| and delete
- // |loadInst|.
- void ReplaceAndDeleteLoad(ir::Instruction* loadInst, uint32_t replId);
-
- // Return true if any instruction loads from |ptrId|
- bool HasLoads(uint32_t ptrId) const;
-
- // Return true if |varId| is not a function variable or if it has
- // a load
- bool IsLiveVar(uint32_t varId) const;
-
- // Add stores using |ptr_id| to |insts|
- void AddStores(uint32_t ptr_id, std::queue<ir::Instruction*>* insts);
-
- // Delete |inst| and iterate DCE on all its operands. Won't delete
- // labels.
- void DCEInst(ir::Instruction* inst);
-
// Return true if all uses of |varId| are only through supported reference
// operations ie. loads and store. Also cache in supported_ref_vars_;
bool HasOnlySupportedRefs(uint32_t varId);
- // Return true if all uses of |id| are only name or decorate ops.
- bool HasOnlyNamesAndDecorates(uint32_t id) const;
-
- // Kill all name and decorate ops using |inst|
- void KillNamesAndDecorates(ir::Instruction* inst);
-
- // Kill all name and decorate ops using |id|
- void KillNamesAndDecorates(uint32_t id);
-
// Initialize data structures used by EliminateLocalMultiStore for
// function |func|, specifically block predecessors and target variables.
void InitSSARewrite(ir::Function& func);
@@ -178,9 +129,6 @@ class LocalMultiStoreElimPass : public Pass {
// Return true if all extensions in this module are allowed by this pass.
bool AllExtensionsSupported() const;
- // Collect all named or decorated ids in module
- void FindNamedOrDecoratedIds();
-
// Remove remaining loads and stores of function scope variables only
// referenced with non-access-chain loads and stores from function |func|.
// Insert Phi functions where necessary. Running LocalAccessChainRemoval,
@@ -206,24 +154,12 @@ class LocalMultiStoreElimPass : public Pass {
void Initialize(ir::Module* module);
Pass::Status ProcessImpl();
- // Module this pass is processing
- ir::Module* module_;
-
- // Def-Uses for the module we are processing
- std::unique_ptr<analysis::DefUseManager> def_use_mgr_;
-
// Map from function's result id to function
std::unordered_map<uint32_t, ir::Function*> id2function_;
// Map from block's label id to block.
std::unordered_map<uint32_t, ir::BasicBlock*> id2block_;
- // Cache of previously seen target types
- std::unordered_set<uint32_t> seen_target_vars_;
-
- // Cache of previously seen non-target types
- std::unordered_set<uint32_t> seen_non_target_vars_;
-
// Set of label ids of visited blocks
std::unordered_set<uint32_t> visitedBlocks_;
@@ -234,9 +170,6 @@ class LocalMultiStoreElimPass : public Pass {
// pass ie. loads and stores.
std::unordered_set<uint32_t> supported_ref_vars_;
- // named or decorated ids
- std::unordered_set<uint32_t> named_or_decorated_ids_;
-
// Map from block to its structured successor blocks. See
// ComputeStructuredSuccessors() for definition.
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
diff --git a/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp
new file mode 100644
index 00000000..fb97a041
--- /dev/null
+++ b/source/opt/mem_pass.cpp
@@ -0,0 +1,269 @@
+// Copyright (c) 2017 The Khronos Group Inc.
+// 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 "mem_pass.h"
+
+#include "iterator.h"
+
+namespace spvtools {
+namespace opt {
+
+namespace {
+
+const uint32_t kStorePtrIdInIdx = 0;
+const uint32_t kLoadPtrIdInIdx = 0;
+const uint32_t kAccessChainPtrIdInIdx = 0;
+const uint32_t kCopyObjectOperandInIdx = 0;
+const uint32_t kTypePointerStorageClassInIdx = 0;
+const uint32_t kTypePointerTypeIdInIdx = 1;
+
+} // namespace anonymous
+
+
+bool MemPass::IsMathType(
+ const ir::Instruction* typeInst) const {
+ switch (typeInst->opcode()) {
+ case SpvOpTypeInt:
+ case SpvOpTypeFloat:
+ case SpvOpTypeBool:
+ case SpvOpTypeVector:
+ case SpvOpTypeMatrix:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool MemPass::IsTargetType(
+ const ir::Instruction* typeInst) const {
+ if (IsMathType(typeInst))
+ return true;
+ if (typeInst->opcode() == SpvOpTypeArray)
+ return IsMathType(def_use_mgr_->GetDef(typeInst->GetSingleWordOperand(1)));
+ if (typeInst->opcode() != SpvOpTypeStruct)
+ return false;
+ // All struct members must be math type
+ int nonMathComp = 0;
+ typeInst->ForEachInId([&nonMathComp,this](const uint32_t* tid) {
+ ir::Instruction* compTypeInst = def_use_mgr_->GetDef(*tid);
+ if (!IsMathType(compTypeInst)) ++nonMathComp;
+ });
+ return nonMathComp == 0;
+}
+
+bool MemPass::IsNonPtrAccessChain(const SpvOp opcode) const {
+ return opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain;
+}
+
+ir::Instruction* MemPass::GetPtr(
+ ir::Instruction* ip, uint32_t* varId) {
+ const SpvOp op = ip->opcode();
+ assert(op == SpvOpStore || op == SpvOpLoad);
+ *varId = ip->GetSingleWordInOperand(
+ op == SpvOpStore ? kStorePtrIdInIdx : kLoadPtrIdInIdx);
+ ir::Instruction* ptrInst = def_use_mgr_->GetDef(*varId);
+ while (ptrInst->opcode() == SpvOpCopyObject) {
+ *varId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
+ ptrInst = def_use_mgr_->GetDef(*varId);
+ }
+ ir::Instruction* varInst = ptrInst;
+ while (varInst->opcode() != SpvOpVariable) {
+ if (IsNonPtrAccessChain(varInst->opcode())) {
+ *varId = varInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
+ }
+ else {
+ assert(varInst->opcode() == SpvOpCopyObject);
+ *varId = varInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
+ }
+ varInst = def_use_mgr_->GetDef(*varId);
+ }
+ return ptrInst;
+}
+
+bool MemPass::IsTargetVar(uint32_t varId) {
+ if (seen_non_target_vars_.find(varId) != seen_non_target_vars_.end())
+ return false;
+ if (seen_target_vars_.find(varId) != seen_target_vars_.end())
+ return true;
+ const ir::Instruction* varInst = def_use_mgr_->GetDef(varId);
+ if (varInst->opcode() != SpvOpVariable)
+ return false;;
+ const uint32_t varTypeId = varInst->type_id();
+ const ir::Instruction* varTypeInst = def_use_mgr_->GetDef(varTypeId);
+ if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) !=
+ SpvStorageClassFunction) {
+ seen_non_target_vars_.insert(varId);
+ return false;
+ }
+ const uint32_t varPteTypeId =
+ varTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
+ ir::Instruction* varPteTypeInst = def_use_mgr_->GetDef(varPteTypeId);
+ if (!IsTargetType(varPteTypeInst)) {
+ seen_non_target_vars_.insert(varId);
+ return false;
+ }
+ seen_target_vars_.insert(varId);
+ return true;
+}
+
+void MemPass::FindNamedOrDecoratedIds() {
+ named_or_decorated_ids_.clear();
+ for (auto& di : module_->debugs())
+ if (di.opcode() == SpvOpName)
+ named_or_decorated_ids_.insert(di.GetSingleWordInOperand(0));
+ for (auto& ai : module_->annotations())
+ if (ai.opcode() == SpvOpDecorate || ai.opcode() == SpvOpDecorateId)
+ named_or_decorated_ids_.insert(ai.GetSingleWordInOperand(0));
+}
+
+bool MemPass::HasOnlyNamesAndDecorates(uint32_t id) const {
+ analysis::UseList* uses = def_use_mgr_->GetUses(id);
+ if (uses == nullptr)
+ return true;
+ if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
+ return false;
+ for (auto u : *uses) {
+ const SpvOp op = u.inst->opcode();
+ if (op != SpvOpName && !IsDecorate(op))
+ return false;
+ }
+ return true;
+}
+
+void MemPass::KillNamesAndDecorates(uint32_t id) {
+ // TODO(greg-lunarg): Remove id from any OpGroupDecorate and
+ // kill if no other operands.
+ if (named_or_decorated_ids_.find(id) == named_or_decorated_ids_.end())
+ return;
+ analysis::UseList* uses = def_use_mgr_->GetUses(id);
+ if (uses == nullptr)
+ return;
+ std::list<ir::Instruction*> killList;
+ for (auto u : *uses) {
+ const SpvOp op = u.inst->opcode();
+ if (op == SpvOpName || IsDecorate(op))
+ killList.push_back(u.inst);
+ }
+ for (auto kip : killList)
+ def_use_mgr_->KillInst(kip);
+}
+
+void MemPass::KillNamesAndDecorates(ir::Instruction* inst) {
+ const uint32_t rId = inst->result_id();
+ if (rId == 0)
+ return;
+ KillNamesAndDecorates(rId);
+}
+
+bool MemPass::HasLoads(uint32_t varId) const {
+ analysis::UseList* uses = def_use_mgr_->GetUses(varId);
+ if (uses == nullptr)
+ return false;
+ for (auto u : *uses) {
+ SpvOp op = u.inst->opcode();
+ // TODO(): The following is slightly conservative. Could be
+ // better handling of non-store/name.
+ if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) {
+ if (HasLoads(u.inst->result_id()))
+ return true;
+ }
+ else if (op != SpvOpStore && op != SpvOpName)
+ return true;
+ }
+ return false;
+}
+
+bool MemPass::IsLiveVar(uint32_t varId) const {
+ // non-function scope vars are live
+ const ir::Instruction* varInst = def_use_mgr_->GetDef(varId);
+ assert(varInst->opcode() == SpvOpVariable);
+ const uint32_t varTypeId = varInst->type_id();
+ const ir::Instruction* varTypeInst = def_use_mgr_->GetDef(varTypeId);
+ if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) !=
+ SpvStorageClassFunction)
+ return true;
+ // test if variable is loaded from
+ return HasLoads(varId);
+}
+
+bool MemPass::IsLiveStore(ir::Instruction* storeInst) {
+ // get store's variable
+ uint32_t varId;
+ (void) GetPtr(storeInst, &varId);
+ return IsLiveVar(varId);
+}
+
+void MemPass::AddStores(
+ uint32_t ptr_id, std::queue<ir::Instruction*>* insts) {
+ analysis::UseList* uses = def_use_mgr_->GetUses(ptr_id);
+ if (uses != nullptr) {
+ for (auto u : *uses) {
+ if (IsNonPtrAccessChain(u.inst->opcode()))
+ AddStores(u.inst->result_id(), insts);
+ else if (u.inst->opcode() == SpvOpStore)
+ insts->push(u.inst);
+ }
+ }
+}
+
+void MemPass::DCEInst(ir::Instruction* inst) {
+ std::queue<ir::Instruction*> deadInsts;
+ deadInsts.push(inst);
+ while (!deadInsts.empty()) {
+ ir::Instruction* di = deadInsts.front();
+ // Don't delete labels
+ if (di->opcode() == SpvOpLabel) {
+ deadInsts.pop();
+ continue;
+ }
+ // Remember operands
+ std::vector<uint32_t> ids;
+ di->ForEachInId([&ids](uint32_t* iid) {
+ ids.push_back(*iid);
+ });
+ uint32_t varId = 0;
+ // Remember variable if dead load
+ if (di->opcode() == SpvOpLoad)
+ (void) GetPtr(di, &varId);
+ KillNamesAndDecorates(di);
+ def_use_mgr_->KillInst(di);
+ // For all operands with no remaining uses, add their instruction
+ // to the dead instruction queue.
+ for (auto id : ids)
+ if (HasOnlyNamesAndDecorates(id))
+ deadInsts.push(def_use_mgr_->GetDef(id));
+ // if a load was deleted and it was the variable's
+ // last load, add all its stores to dead queue
+ if (varId != 0 && !IsLiveVar(varId))
+ AddStores(varId, &deadInsts);
+ deadInsts.pop();
+ }
+}
+
+void MemPass::ReplaceAndDeleteLoad(
+ ir::Instruction* loadInst, uint32_t replId) {
+ const uint32_t loadId = loadInst->result_id();
+ KillNamesAndDecorates(loadId);
+ (void) def_use_mgr_->ReplaceAllUsesWith(loadId, replId);
+ DCEInst(loadInst);
+}
+
+MemPass::MemPass() : module_(nullptr), def_use_mgr_(nullptr) {}
+
+} // namespace opt
+} // namespace spvtools
+
diff --git a/source/opt/mem_pass.h b/source/opt/mem_pass.h
new file mode 100644
index 00000000..f670289d
--- /dev/null
+++ b/source/opt/mem_pass.h
@@ -0,0 +1,128 @@
+// Copyright (c) 2017 The Khronos Group Inc.
+// 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.
+
+#ifndef LIBSPIRV_OPT_OPT_PASS_H_
+#define LIBSPIRV_OPT_OPT_PASS_H_
+
+
+#include <algorithm>
+#include <map>
+#include <queue>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+
+#include "basic_block.h"
+#include "def_use_manager.h"
+#include "module.h"
+#include "pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// A common base class for mem2reg-type passes. Provides common
+// utility functions and supporting state.
+class MemPass : public Pass {
+ public:
+ MemPass();
+ virtual ~MemPass() = default;
+
+ protected:
+ // Returns true if |typeInst| is a scalar type
+ // or a vector or matrix
+ bool IsMathType(const ir::Instruction* typeInst) const;
+
+ // Returns true if |typeInst| is a math type or a struct or array
+ // of a math type.
+ // TODO(): Add more complex types to convert
+ bool IsTargetType(const ir::Instruction* typeInst) const;
+
+ // Returns true if |opcode| is a non-ptr access chain op
+ bool IsNonPtrAccessChain(const SpvOp opcode) const;
+
+ // Given a load or store |ip|, return the pointer instruction.
+ // Also return the base variable's id in |varId|.
+ ir::Instruction* GetPtr(ir::Instruction* ip, uint32_t* varId);
+
+ // Return true if |varId| is a previously identified target variable.
+ // Return false if |varId| is a previously identified non-target variable.
+ // See FindTargetVars() for definition of target variable. If variable is
+ // not cached, return true if variable is a function scope variable of
+ // target type, false otherwise. Updates caches of target and non-target
+ // variables.
+ bool IsTargetVar(uint32_t varId);
+
+ // Return true if all uses of |id| are only name or decorate ops.
+ bool HasOnlyNamesAndDecorates(uint32_t id) const;
+
+ // Kill all name and decorate ops using |inst|
+ void KillNamesAndDecorates(ir::Instruction* inst);
+
+ // Kill all name and decorate ops using |id|
+ void KillNamesAndDecorates(uint32_t id);
+
+ // Collect all named or decorated ids in module
+ void FindNamedOrDecoratedIds();
+
+ // Return true if any instruction loads from |varId|
+ bool HasLoads(uint32_t varId) const;
+
+ // Return true if |varId| is not a function variable or if it has
+ // a load
+ bool IsLiveVar(uint32_t varId) const;
+
+ // Return true if |storeInst| is not a function variable or if its
+ // base variable has a load
+ bool IsLiveStore(ir::Instruction* storeInst);
+
+ // Add stores using |ptr_id| to |insts|
+ void AddStores(uint32_t ptr_id, std::queue<ir::Instruction*>* insts);
+
+ // Delete |inst| and iterate DCE on all its operands if they are now
+ // useless. If a load is deleted and its variable has no other loads,
+ // delete all its variable's stores.
+ void DCEInst(ir::Instruction* inst);
+
+ // Replace all instances of |loadInst|'s id with |replId| and delete
+ // |loadInst|.
+ void ReplaceAndDeleteLoad(ir::Instruction* loadInst, uint32_t replId);
+
+ // Return true if |op| is supported decorate.
+ inline bool IsDecorate(uint32_t op) const {
+ return (op == SpvOpDecorate || op == SpvOpDecorateId);
+ }
+
+ // Module this pass is processing
+ ir::Module* module_;
+
+ // Def-Uses for the module we are processing
+ std::unique_ptr<analysis::DefUseManager> def_use_mgr_;
+
+ // Cache of verified target vars
+ std::unordered_set<uint32_t> seen_target_vars_;
+
+ // Cache of verified non-target vars
+ std::unordered_set<uint32_t> seen_non_target_vars_;
+
+ // named or decorated ids
+ std::unordered_set<uint32_t> named_or_decorated_ids_;
+};
+
+} // namespace opt
+} // namespace spvtools
+
+#endif // LIBSPIRV_OPT_OPT_PASS_H_
+
diff --git a/test/opt/local_access_chain_convert_test.cpp b/test/opt/local_access_chain_convert_test.cpp
index ad37622d..27324c3e 100644
--- a/test/opt/local_access_chain_convert_test.cpp
+++ b/test/opt/local_access_chain_convert_test.cpp
@@ -40,7 +40,7 @@ TEST_F(LocalAccessChainConvertTest, StructOfVecsOfFloatConverted) {
// gl_FragColor = s0.v1;
// }
- const std::string predefs =
+ const std::string predefs_before =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
@@ -69,6 +69,34 @@ OpName %gl_FragColor "gl_FragColor"
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
)";
+ const std::string predefs_after =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %S_t "S_t"
+OpMemberName %S_t 0 "v0"
+OpMemberName %S_t 1 "v1"
+OpName %s0 "s0"
+OpName %BaseColor "BaseColor"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%S_t = OpTypeStruct %v4float %v4float
+%_ptr_Function_S_t = OpTypePointer Function %S_t
+%int = OpTypeInt 32 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+)";
+
const std::string before =
R"(%main = OpFunction %void None %8
%17 = OpLabel
@@ -99,7 +127,7 @@ OpFunctionEnd
)";
SinglePassRunAndCheck<opt::LocalAccessChainConvertPass>(
- predefs + before, predefs + after, true, true);
+ predefs_before + before, predefs_after + after, true, true);
}
TEST_F(LocalAccessChainConvertTest, InBoundsAccessChainsConverted) {
@@ -120,7 +148,7 @@ TEST_F(LocalAccessChainConvertTest, InBoundsAccessChainsConverted) {
// gl_FragColor = s0.v1;
// }
- const std::string predefs =
+ const std::string predefs_before =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
@@ -149,6 +177,34 @@ OpName %gl_FragColor "gl_FragColor"
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
)";
+ const std::string predefs_after =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %S_t "S_t"
+OpMemberName %S_t 0 "v0"
+OpMemberName %S_t 1 "v1"
+OpName %s0 "s0"
+OpName %BaseColor "BaseColor"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%S_t = OpTypeStruct %v4float %v4float
+%_ptr_Function_S_t = OpTypePointer Function %S_t
+%int = OpTypeInt 32 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+)";
+
const std::string before =
R"(%main = OpFunction %void None %8
%17 = OpLabel
@@ -179,7 +235,7 @@ OpFunctionEnd
)";
SinglePassRunAndCheck<opt::LocalAccessChainConvertPass>(
- predefs + before, predefs + after, true, true);
+ predefs_before + before, predefs_after + after, true, true);
}
TEST_F(LocalAccessChainConvertTest, TwoUsesofSingleChainConverted) {
@@ -200,7 +256,7 @@ TEST_F(LocalAccessChainConvertTest, TwoUsesofSingleChainConverted) {
// gl_FragColor = s0.v1;
// }
- const std::string predefs =
+ const std::string predefs_before =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
@@ -229,6 +285,34 @@ OpName %gl_FragColor "gl_FragColor"
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
)";
+ const std::string predefs_after =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %S_t "S_t"
+OpMemberName %S_t 0 "v0"
+OpMemberName %S_t 1 "v1"
+OpName %s0 "s0"
+OpName %BaseColor "BaseColor"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%S_t = OpTypeStruct %v4float %v4float
+%_ptr_Function_S_t = OpTypePointer Function %S_t
+%int = OpTypeInt 32 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+)";
+
const std::string before =
R"(%main = OpFunction %void None %8
%17 = OpLabel
@@ -258,7 +342,7 @@ OpFunctionEnd
)";
SinglePassRunAndCheck<opt::LocalAccessChainConvertPass>(
- predefs + before, predefs + after, true, true);
+ predefs_before + before, predefs_after + after, true, true);
}
TEST_F(LocalAccessChainConvertTest,