diff options
author | GregF <greg@LunarG.com> | 2017-07-27 13:30:12 -0600 |
---|---|---|
committer | David Neto <dneto@google.com> | 2017-08-02 14:24:02 -0400 |
commit | c1b46eedbd8a33370d4bb97e553f8893f10a54f8 (patch) | |
tree | d8361ca4302334498848473f3173eb8dd5ba270f | |
parent | 30bee67439c0b2fd0be0780c135908b993d9410e (diff) | |
download | SPIRV-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.txt | 2 | ||||
-rw-r--r-- | source/opt/aggressive_dead_code_elim_pass.cpp | 36 | ||||
-rw-r--r-- | source/opt/aggressive_dead_code_elim_pass.h | 17 | ||||
-rw-r--r-- | source/opt/dead_branch_elim_pass.cpp | 37 | ||||
-rw-r--r-- | source/opt/dead_branch_elim_pass.h | 27 | ||||
-rw-r--r-- | source/opt/local_access_chain_convert_pass.cpp | 160 | ||||
-rw-r--r-- | source/opt/local_access_chain_convert_pass.h | 68 | ||||
-rw-r--r-- | source/opt/local_single_block_elim_pass.cpp | 243 | ||||
-rw-r--r-- | source/opt/local_single_block_elim_pass.h | 80 | ||||
-rw-r--r-- | source/opt/local_single_store_elim_pass.cpp | 239 | ||||
-rw-r--r-- | source/opt/local_single_store_elim_pass.h | 81 | ||||
-rw-r--r-- | source/opt/local_ssa_elim_pass.cpp | 224 | ||||
-rw-r--r-- | source/opt/local_ssa_elim_pass.h | 71 | ||||
-rw-r--r-- | source/opt/mem_pass.cpp | 269 | ||||
-rw-r--r-- | source/opt/mem_pass.h | 128 | ||||
-rw-r--r-- | test/opt/local_access_chain_convert_test.cpp | 96 |
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, |