diff options
author | Diego Novillo <dnovillo@google.com> | 2017-10-30 17:42:26 -0400 |
---|---|---|
committer | Diego Novillo <dnovillo@google.com> | 2017-11-02 10:37:03 -0400 |
commit | fef669f30f8d81f21fbb9191046ec6c6b90c7f5a (patch) | |
tree | 3f1d58efabcb8d728465ff5e9158343b426c7053 /source | |
parent | 476cae6f7d415d03d607265cd1e8267e66be9f35 (diff) |
Add a new class opt::CFG to represent the CFG for the module.
This class moves some of the CFG-related functionality into a new
class opt::CFG. There is some other code related to the CFG in the
inliner and in opt::LocalSingleStoreElimPass that should also be moved,
but that require more changes than this pure restructuring.
I will move those bits in a follow-up PR.
Currently, the CFG is computed every time a pass is instantiated, but
this should be later moved to the new IRContext class that @s-perron is
working on.
Other re-factoring:
- Add BasicBlock::ContinueBlockIdIfAny. Re-factored out of MergeBlockIdIfAny
- Rewrite IsLoopHeader in terms of GetLoopMergeInst.
- Run clang-format on some files.
Diffstat (limited to 'source')
-rw-r--r-- | source/cfa.h | 1 | ||||
-rw-r--r-- | source/opt/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/opt/aggressive_dead_code_elim_pass.cpp | 2 | ||||
-rw-r--r-- | source/opt/basic_block.cpp | 50 | ||||
-rw-r--r-- | source/opt/basic_block.h | 20 | ||||
-rw-r--r-- | source/opt/block_merge_pass.cpp | 2 | ||||
-rw-r--r-- | source/opt/cfg.cpp | 94 | ||||
-rw-r--r-- | source/opt/cfg.h | 110 | ||||
-rw-r--r-- | source/opt/common_uniform_elim_pass.cpp | 312 | ||||
-rw-r--r-- | source/opt/common_uniform_elim_pass.h | 5 | ||||
-rw-r--r-- | source/opt/dead_branch_elim_pass.cpp | 15 | ||||
-rw-r--r-- | source/opt/inline_pass.cpp | 9 | ||||
-rw-r--r-- | source/opt/inline_pass.h | 10 | ||||
-rw-r--r-- | source/opt/local_single_store_elim_pass.cpp | 8 | ||||
-rw-r--r-- | source/opt/local_single_store_elim_pass.h | 1 | ||||
-rw-r--r-- | source/opt/mem_pass.cpp | 63 | ||||
-rw-r--r-- | source/opt/mem_pass.h | 12 | ||||
-rw-r--r-- | source/opt/pass.cpp | 86 | ||||
-rw-r--r-- | source/opt/pass.h | 69 |
19 files changed, 479 insertions, 392 deletions
diff --git a/source/cfa.h b/source/cfa.h index 70c241a0..6cba06ab 100644 --- a/source/cfa.h +++ b/source/cfa.h @@ -19,6 +19,7 @@ #include <cassert> #include <functional> #include <map> +#include <cstdint> #include <unordered_map> #include <unordered_set> #include <utility> diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index e72f49cc..ff8583e7 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -17,6 +17,7 @@ add_library(SPIRV-Tools-opt block_merge_pass.h build_module.h cfg_cleanup_pass.h + cfg.h common_uniform_elim_pass.h compact_ids_pass.h constants.h @@ -63,6 +64,7 @@ add_library(SPIRV-Tools-opt block_merge_pass.cpp build_module.cpp cfg_cleanup_pass.cpp + cfg.cpp common_uniform_elim_pass.cpp compact_ids_pass.cpp decoration_manager.cpp diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index e4dc5deb..6be613a6 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -199,7 +199,7 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) { ComputeInst2BlockMap(func); // Compute map from block to controlling conditional branch std::list<ir::BasicBlock*> structuredOrder; - ComputeStructuredOrder(func, &*func->begin(), &structuredOrder); + cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder); ComputeBlock2HeaderMaps(structuredOrder); bool modified = false; // Add instructions with external side effects to worklist. Also add branches diff --git a/source/opt/basic_block.cpp b/source/opt/basic_block.cpp index 5eef51e1..7e0f421f 100644 --- a/source/opt/basic_block.cpp +++ b/source/opt/basic_block.cpp @@ -19,6 +19,14 @@ namespace spvtools { namespace ir { +namespace { + +const uint32_t kLoopMergeContinueBlockIdInIdx = 1; +const uint32_t kLoopMergeMergeBlockIdInIdx = 0; +const uint32_t kSelectionMergeMergeBlockIdInIdx = 0; + +} // namespace + BasicBlock::BasicBlock(const BasicBlock& bb) : function_(nullptr), label_(MakeUnique<Instruction>(bb.GetLabelInst())), @@ -35,7 +43,7 @@ const Instruction* BasicBlock::GetMergeInst() const { if (iter != cbegin()) { --iter; const auto opcode = iter->opcode(); - if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) { + if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) { result = &*iter; } } @@ -50,7 +58,7 @@ Instruction* BasicBlock::GetMergeInst() { if (iter != begin()) { --iter; const auto opcode = iter->opcode(); - if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) { + if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) { result = &*iter; } } @@ -101,13 +109,39 @@ void BasicBlock::ForMergeAndContinueLabel( --ii; if (ii == insts_.begin()) return; --ii; - if (ii->opcode() == SpvOpSelectionMerge || - ii->opcode() == SpvOpLoopMerge) - ii->ForEachInId([&f](const uint32_t* idp) { - f(*idp); - }); + if (ii->opcode() == SpvOpSelectionMerge || ii->opcode() == SpvOpLoopMerge) { + ii->ForEachInId([&f](const uint32_t* idp) { f(*idp); }); + } +} + +uint32_t BasicBlock::MergeBlockIdIfAny() const { + auto merge_ii = cend(); + --merge_ii; + uint32_t mbid = 0; + if (merge_ii != cbegin()) { + --merge_ii; + if (merge_ii->opcode() == SpvOpLoopMerge) { + mbid = merge_ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx); + } else if (merge_ii->opcode() == SpvOpSelectionMerge) { + mbid = merge_ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); + } + } + + return mbid; +} + +uint32_t BasicBlock::ContinueBlockIdIfAny() const { + auto merge_ii = cend(); + --merge_ii; + uint32_t cbid = 0; + if (merge_ii != cbegin()) { + --merge_ii; + if (merge_ii->opcode() == SpvOpLoopMerge) { + cbid = merge_ii->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx); + } + } + return cbid; } } // namespace ir } // namespace spvtools - diff --git a/source/opt/basic_block.h b/source/opt/basic_block.h index 90e04aba..32550e73 100644 --- a/source/opt/basic_block.h +++ b/source/opt/basic_block.h @@ -64,6 +64,7 @@ class BasicBlock { // Otherwise return null. May be used whenever tail() can be used. const Instruction* GetMergeInst() const; Instruction* GetMergeInst(); + // Returns the OpLoopMerge instruciton in this basic block, if it exists. // Otherwise return null. May be used whenever tail() can be used. const Instruction* GetLoopMergeInst() const; @@ -84,6 +85,7 @@ class BasicBlock { assert(!insts_.empty()); return --end(); } + // Returns a const iterator, but othewrise similar to tail(). const_iterator ctail() const { assert(!insts_.empty()); @@ -118,6 +120,18 @@ class BasicBlock { return count > 0; } + // Return true if this block is a loop header block. + bool IsLoopHeader() const { return GetLoopMergeInst() != nullptr; } + + // Returns the ID of the merge block declared by a merge instruction in this + // block, if any. If none, returns zero. If |cbid| is not nullptr, the ID of + // the continue block in the merge instruction is set in |*cbid|. + uint32_t MergeBlockIdIfAny() const; + + // Returns the ID of the continue block declared by a merge instruction in + // this block, if any. If none, returns zero. + uint32_t ContinueBlockIdIfAny() const; + private: // The enclosing function. Function* function_; @@ -136,7 +150,7 @@ inline void BasicBlock::AddInstruction(std::unique_ptr<Instruction> i) { inline void BasicBlock::AddInstructions(BasicBlock* bp) { auto bEnd = end(); - (void) bEnd.MoveBefore(&bp->insts_); + (void)bEnd.MoveBefore(&bp->insts_); } inline void BasicBlock::ForEachInst(const std::function<void(Instruction*)>& f, @@ -152,8 +166,8 @@ inline void BasicBlock::ForEachInst( static_cast<const Instruction*>(label_.get()) ->ForEachInst(f, run_on_debug_line_insts); for (const auto& inst : insts_) - static_cast<const Instruction*>(&inst) - ->ForEachInst(f, run_on_debug_line_insts); + static_cast<const Instruction*>(&inst)->ForEachInst( + f, run_on_debug_line_insts); } inline void BasicBlock::ForEachPhiInst( diff --git a/source/opt/block_merge_pass.cpp b/source/opt/block_merge_pass.cpp index 470bb2a7..cf5f0626 100644 --- a/source/opt/block_merge_pass.cpp +++ b/source/opt/block_merge_pass.cpp @@ -54,7 +54,7 @@ bool BlockMergePass::MergeBlocks(ir::Function* func) { bool modified = false; for (auto bi = func->begin(); bi != func->end(); ) { // Do not merge loop header blocks, at least for now. - if (IsLoopHeader(&*bi)) { + if (bi->IsLoopHeader()) { ++bi; continue; } diff --git a/source/opt/cfg.cpp b/source/opt/cfg.cpp new file mode 100644 index 00000000..3883a047 --- /dev/null +++ b/source/opt/cfg.cpp @@ -0,0 +1,94 @@ +// Copyright (c) 2017 Google 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 "cfa.h" +#include "cfg.h" +#include "module.h" + +namespace spvtools { +namespace opt { + +namespace { + +// Universal Limit of ResultID + 1 +const int kInvalidId = 0x400000; + +} // namespace + +CFG::CFG(ir::Module* module) + : module_(module), + 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, {}))) { + for (auto& fn : *module) { + for (auto& blk : fn) { + uint32_t blkId = blk.id(); + id2block_[blkId] = &blk; + blk.ForEachSuccessorLabel([&blkId, this](uint32_t sbid) { + label2preds_[sbid].push_back(blkId); + }); + } + } +} + +void CFG::ComputeStructuredOrder(ir::Function* func, ir::BasicBlock* root, + std::list<ir::BasicBlock*>* order) { + assert(module_->HasCapability(SpvCapabilityShader) && + "This only works on structured control flow"); + + // Compute structured successors and do DFS. + ComputeStructuredSuccessors(func); + auto ignore_block = [](cbb_ptr) {}; + auto ignore_edge = [](cbb_ptr, cbb_ptr) {}; + auto get_structured_successors = [this](const ir::BasicBlock* b) { + return &(block2structured_succs_[b]); + }; + + // TODO(greg-lunarg): Get rid of const_cast by making moving const + // out of the cfa.h prototypes and into the invoking code. + auto post_order = [&](cbb_ptr b) { + order->push_front(const_cast<ir::BasicBlock*>(b)); + }; + spvtools::CFA<ir::BasicBlock>::DepthFirstTraversal( + root, get_structured_successors, ignore_block, post_order, + ignore_edge); +} + +void CFG::ComputeStructuredSuccessors(ir::Function *func) { + block2structured_succs_.clear(); + for (auto& blk : *func) { + // If no predecessors in function, make successor to pseudo entry. + if (label2preds_[blk.id()].size() == 0) + block2structured_succs_[&pseudo_entry_block_].push_back(&blk); + + // If header, make merge block first successor and continue block second + // successor if there is one. + uint32_t mbid = blk.MergeBlockIdIfAny(); + if (mbid != 0) { + block2structured_succs_[&blk].push_back(id2block_[mbid]); + uint32_t cbid = blk.ContinueBlockIdIfAny(); + if (cbid != 0) + block2structured_succs_[&blk].push_back(id2block_[cbid]); + } + + // Add true successors. + blk.ForEachSuccessorLabel([&blk, this](uint32_t sbid) { + block2structured_succs_[&blk].push_back(id2block_[sbid]); + }); + } +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/cfg.h b/source/opt/cfg.h new file mode 100644 index 00000000..e51c43c6 --- /dev/null +++ b/source/opt/cfg.h @@ -0,0 +1,110 @@ +// Copyright (c) 2017 Google 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_CFG_H_ +#define LIBSPIRV_OPT_CFG_H_ + +#include "basic_block.h" + +#include <list> +#include <unordered_map> + +namespace spvtools { +namespace opt { + +class CFG { + public: + CFG(ir::Module *module); + + // Return the module described by this CFG. + ir::Module* get_module() const { return module_; } + + // Return the list of predecesors for basic block with label |blkid|. + // TODO(dnovillo): Move this to ir::BasicBlock. + const std::vector<uint32_t>& preds(uint32_t blk_id) const { + return label2preds_.at(blk_id); + } + + // Return a pointer to the basic block instance corresponding to the label + // |blk_id|. + ir::BasicBlock* block(uint32_t blk_id) const { return id2block_.at(blk_id); } + + // Return the pseudo entry and exit blocks. TODO(dnovillo): Remove when + // LocalSingleStoreElimPass::CalculateImmediateDominators() is moved into this + // class. + const ir::BasicBlock* pseudo_entry_block() const { + return &pseudo_entry_block_; + } + ir::BasicBlock* pseudo_entry_block() { return &pseudo_entry_block_; } + + const ir::BasicBlock* pseudo_exit_block() const { + return &pseudo_exit_block_; + } + ir::BasicBlock* pseudo_exit_block() { return &pseudo_exit_block_; } + + // Return true if |block_ptr| is the pseudo-entry block. + bool IsPseudoEntryBlock(ir::BasicBlock* block_ptr) const { + return block_ptr == &pseudo_entry_block_; + } + + // Return true if |block_ptr| is the pseudo-exit block. + bool IsPseudoExitBlock(ir::BasicBlock* block_ptr) const { + return block_ptr == &pseudo_exit_block_; + } + + // Compute structured block order into |structuredOrder| for |func| starting + // at |root|. This order has the property that dominators come before all + // blocks they dominate and merge blocks come after all blocks that are in + // the control constructs of their header. + void ComputeStructuredOrder(ir::Function* func, ir::BasicBlock* root, + std::list<ir::BasicBlock*>* order); + + private: + using cbb_ptr = const ir::BasicBlock*; + + // Compute structured successors for function |func|. A block's structured + // successors are the blocks it branches to together with its declared merge + // block and continue block if it has them. When order matters, the merge + // block and continue block always appear first. This assures correct depth + // first search in the presence of early returns and kills. If the successor + // vector contain duplicates of the merge or continue blocks, they are safely + // ignored by DFS. + void ComputeStructuredSuccessors(ir::Function* func); + + // Module for this CFG. + ir::Module *module_; + + // Map from block to its structured successor blocks. See + // ComputeStructuredSuccessors() for definition. + std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>> + block2structured_succs_; + + // Extra block whose successors are all blocks with no predecessors + // in function. TODO(dnovillo): Needed? + ir::BasicBlock pseudo_entry_block_; + + // Augmented CFG Exit Block. TODO(dnovillo): Needed? + ir::BasicBlock pseudo_exit_block_; + + // Map from block's label id to its predecessor blocks ids + std::unordered_map<uint32_t, std::vector<uint32_t>> label2preds_; + + // Map from block's label id to block. + std::unordered_map<uint32_t, ir::BasicBlock*> id2block_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_CFG_H_ diff --git a/source/opt/common_uniform_elim_pass.cpp b/source/opt/common_uniform_elim_pass.cpp index 752ef938..3e2ac0cf 100644 --- a/source/opt/common_uniform_elim_pass.cpp +++ b/source/opt/common_uniform_elim_pass.cpp @@ -34,14 +34,14 @@ const uint32_t kLoadPtrIdInIdx = 0; const uint32_t kCopyObjectOperandInIdx = 0; const uint32_t kTypeIntWidthInIdx = 0; -} // anonymous namespace +} // anonymous namespace bool CommonUniformElimPass::IsNonPtrAccessChain(const SpvOp opcode) const { return opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain; } bool CommonUniformElimPass::IsSamplerOrImageType( - const ir::Instruction* typeInst) const { + const ir::Instruction* typeInst) const { switch (typeInst->opcode()) { case SpvOpTypeSampler: case SpvOpTypeImage: @@ -50,8 +50,7 @@ bool CommonUniformElimPass::IsSamplerOrImageType( default: break; } - if (typeInst->opcode() != SpvOpTypeStruct) - return false; + if (typeInst->opcode() != SpvOpTypeStruct) return false; // Return true if any member is a sampler or image int samplerOrImageCnt = 0; typeInst->ForEachInId([&samplerOrImageCnt, this](const uint32_t* tid) { @@ -61,24 +60,23 @@ bool CommonUniformElimPass::IsSamplerOrImageType( return samplerOrImageCnt > 0; } -bool CommonUniformElimPass::IsSamplerOrImageVar( - uint32_t varId) const { +bool CommonUniformElimPass::IsSamplerOrImageVar(uint32_t varId) const { const ir::Instruction* varInst = get_def_use_mgr()->GetDef(varId); assert(varInst->opcode() == SpvOpVariable); const uint32_t varTypeId = varInst->type_id(); const ir::Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId); const uint32_t varPteTypeId = - varTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx); + varTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx); ir::Instruction* varPteTypeInst = get_def_use_mgr()->GetDef(varPteTypeId); return IsSamplerOrImageType(varPteTypeInst); } -ir::Instruction* CommonUniformElimPass::GetPtr( - ir::Instruction* ip, uint32_t* objId) { +ir::Instruction* CommonUniformElimPass::GetPtr(ir::Instruction* ip, + uint32_t* objId) { const SpvOp op = ip->opcode(); assert(op == SpvOpStore || op == SpvOpLoad); - *objId = ip->GetSingleWordInOperand( - op == SpvOpStore ? kStorePtrIdInIdx : kLoadPtrIdInIdx); + *objId = ip->GetSingleWordInOperand(op == SpvOpStore ? kStorePtrIdInIdx + : kLoadPtrIdInIdx); ir::Instruction* ptrInst = get_def_use_mgr()->GetDef(*objId); while (ptrInst->opcode() == SpvOpCopyObject) { *objId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx); @@ -86,11 +84,10 @@ ir::Instruction* CommonUniformElimPass::GetPtr( } ir::Instruction* objInst = ptrInst; while (objInst->opcode() != SpvOpVariable && - objInst->opcode() != SpvOpFunctionParameter) { + objInst->opcode() != SpvOpFunctionParameter) { if (IsNonPtrAccessChain(objInst->opcode())) { *objId = objInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx); - } - else { + } else { assert(objInst->opcode() == SpvOpCopyObject); *objId = objInst->GetSingleWordInOperand(kCopyObjectOperandInIdx); } @@ -103,11 +100,14 @@ bool CommonUniformElimPass::IsVolatileStruct(uint32_t type_id) { assert(get_def_use_mgr()->GetDef(type_id)->opcode() == SpvOpTypeStruct); bool has_volatile_deco = false; dec_mgr_->ForEachDecoration(type_id, SpvDecorationVolatile, - [&has_volatile_deco](const ir::Instruction&){ has_volatile_deco = true;}); + [&has_volatile_deco](const ir::Instruction&) { + has_volatile_deco = true; + }); return has_volatile_deco; } -bool CommonUniformElimPass::IsAccessChainToVolatileStructType(const ir::Instruction &AccessChainInst) { +bool CommonUniformElimPass::IsAccessChainToVolatileStructType( + const ir::Instruction& AccessChainInst) { assert(AccessChainInst.opcode() == SpvOpAccessChain); uint32_t ptr_id = AccessChainInst.GetSingleWordInOperand(0); @@ -132,8 +132,10 @@ bool CommonUniformElimPass::IsAccessChainToVolatileStructType(const ir::Instruct if (idx < num_operands - 1) { const uint32_t index_id = AccessChainInst.GetSingleWordOperand(idx); - const ir::Instruction* index_inst = get_def_use_mgr()->GetDef(index_id); - uint32_t index_value = index_inst->GetSingleWordOperand(2); // TODO: replace with GetUintValueFromConstant() + const ir::Instruction* index_inst = + get_def_use_mgr()->GetDef(index_id); + uint32_t index_value = index_inst->GetSingleWordOperand( + 2); // TODO: replace with GetUintValueFromConstant() pointee_type_id = pointee_type->GetSingleWordInOperand(index_value); } break; @@ -149,8 +151,7 @@ bool CommonUniformElimPass::IsVolatileLoad(const ir::Instruction& loadInst) { // Check if this Load instruction has Volatile Memory Access flag if (loadInst.NumOperands() == 4) { uint32_t memory_access_mask = loadInst.GetSingleWordOperand(3); - if (memory_access_mask & SpvMemoryAccessVolatileMask) - return true; + if (memory_access_mask & SpvMemoryAccessVolatileMask) return true; } // If we load a struct directly (result type is struct), // check if the struct is decorated volatile @@ -163,65 +164,56 @@ bool CommonUniformElimPass::IsVolatileLoad(const ir::Instruction& loadInst) { bool CommonUniformElimPass::IsUniformVar(uint32_t varId) { const ir::Instruction* varInst = - get_def_use_mgr()->id_to_defs().find(varId)->second; - if (varInst->opcode() != SpvOpVariable) - return false; + get_def_use_mgr()->id_to_defs().find(varId)->second; + if (varInst->opcode() != SpvOpVariable) return false; const uint32_t varTypeId = varInst->type_id(); const ir::Instruction* varTypeInst = - get_def_use_mgr()->id_to_defs().find(varTypeId)->second; + get_def_use_mgr()->id_to_defs().find(varTypeId)->second; return varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) == - SpvStorageClassUniform || - varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) == - SpvStorageClassUniformConstant; + SpvStorageClassUniform || + varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) == + SpvStorageClassUniformConstant; } bool CommonUniformElimPass::HasUnsupportedDecorates(uint32_t id) const { analysis::UseList* uses = get_def_use_mgr()->GetUses(id); - if (uses == nullptr) - return false; + if (uses == nullptr) return false; for (auto u : *uses) { const SpvOp op = u.inst->opcode(); - if (IsNonTypeDecorate(op)) - return true; + if (IsNonTypeDecorate(op)) return true; } return false; } bool CommonUniformElimPass::HasOnlyNamesAndDecorates(uint32_t id) const { analysis::UseList* uses = get_def_use_mgr()->GetUses(id); - if (uses == nullptr) - return true; + if (uses == nullptr) return true; for (auto u : *uses) { const SpvOp op = u.inst->opcode(); - if (op != SpvOpName && !IsNonTypeDecorate(op)) - return false; + if (op != SpvOpName && !IsNonTypeDecorate(op)) return false; } return true; } void CommonUniformElimPass::KillNamesAndDecorates(uint32_t id) { - // TODO(greg-lunarg): Remove id from any OpGroupDecorate and + // TODO(greg-lunarg): Remove id from any OpGroupDecorate and // kill if no other operands. analysis::UseList* uses = get_def_use_mgr()->GetUses(id); - if (uses == nullptr) - return; + if (uses == nullptr) return; std::list<ir::Instruction*> killList; for (auto u : *uses) { const SpvOp op = u.inst->opcode(); - if (op != SpvOpName && !IsNonTypeDecorate(op)) - continue; + if (op != SpvOpName && !IsNonTypeDecorate(op)) continue; killList.push_back(u.inst); } - for (auto kip : killList) - get_def_use_mgr()->KillInst(kip); + for (auto kip : killList) get_def_use_mgr()->KillInst(kip); } void CommonUniformElimPass::KillNamesAndDecorates(ir::Instruction* inst) { - // TODO(greg-lunarg): Remove inst from any OpGroupDecorate and + // TODO(greg-lunarg): Remove inst from any OpGroupDecorate and // kill if not other operands. const uint32_t rId = inst->result_id(); - if (rId == 0) - return; + if (rId == 0) return; KillNamesAndDecorates(rId); } @@ -235,35 +227,34 @@ void CommonUniformElimPass::DeleteIfUseless(ir::Instruction* inst) { } void CommonUniformElimPass::ReplaceAndDeleteLoad(ir::Instruction* loadInst, - uint32_t replId, - ir::Instruction* ptrInst) { + uint32_t replId, + ir::Instruction* ptrInst) { const uint32_t loadId = loadInst->result_id(); KillNamesAndDecorates(loadId); - (void) get_def_use_mgr()->ReplaceAllUsesWith(loadId, replId); + (void)get_def_use_mgr()->ReplaceAllUsesWith(loadId, replId); // remove load instruction get_def_use_mgr()->KillInst(loadInst); // if access chain, see if it can be removed as well - if (IsNonPtrAccessChain(ptrInst->opcode())) - DeleteIfUseless(ptrInst); + if (IsNonPtrAccessChain(ptrInst->opcode())) DeleteIfUseless(ptrInst); } -void CommonUniformElimPass::GenACLoadRepl(const ir::Instruction* ptrInst, - std::vector<std::unique_ptr<ir::Instruction>>* newInsts, - uint32_t* resultId) { - +void CommonUniformElimPass::GenACLoadRepl( + const ir::Instruction* ptrInst, + std::vector<std::unique_ptr<ir::Instruction>>* newInsts, + uint32_t* resultId) { // Build and append Load const uint32_t ldResultId = TakeNextId(); const uint32_t varId = - ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx); + ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx); const ir::Instruction* varInst = get_def_use_mgr()->GetDef(varId); assert(varInst->opcode() == SpvOpVariable); const uint32_t varPteTypeId = GetPointeeTypeId(varInst); std::vector<ir::Operand> load_in_operands; load_in_operands.push_back( - ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, - std::initializer_list<uint32_t>{varId})); - std::unique_ptr<ir::Instruction> newLoad(new ir::Instruction(SpvOpLoad, - varPteTypeId, ldResultId, load_in_operands)); + ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, + std::initializer_list<uint32_t>{varId})); + std::unique_ptr<ir::Instruction> newLoad(new ir::Instruction( + SpvOpLoad, varPteTypeId, ldResultId, load_in_operands)); get_def_use_mgr()->AnalyzeInstDefUse(&*newLoad); newInsts->emplace_back(std::move(newLoad)); @@ -272,21 +263,21 @@ void CommonUniformElimPass::GenACLoadRepl(const ir::Instruction* ptrInst, const uint32_t ptrPteTypeId = GetPointeeTypeId(ptrInst); std::vector<ir::Operand> ext_in_opnds; ext_in_opnds.push_back( - ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, - std::initializer_list<uint32_t>{ldResultId})); + ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, + std::initializer_list<uint32_t>{ldResultId})); uint32_t iidIdx = 0; - ptrInst->ForEachInId([&iidIdx, &ext_in_opnds, this](const uint32_t *iid) { + ptrInst->ForEachInId([&iidIdx, &ext_in_opnds, this](const uint32_t* iid) { if (iidIdx > 0) { const ir::Instruction* cInst = get_def_use_mgr()->GetDef(*iid); uint32_t val = cInst->GetSingleWordInOperand(kConstantValueInIdx); ext_in_opnds.push_back( - ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, - std::initializer_list<uint32_t>{val})); + ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + std::initializer_list<uint32_t>{val})); } ++iidIdx; }); std::unique_ptr<ir::Instruction> newExt(new ir::Instruction( - SpvOpCompositeExtract, ptrPteTypeId, extResultId, ext_in_opnds)); + SpvOpCompositeExtract, ptrPteTypeId, extResultId, ext_in_opnds)); get_def_use_mgr()->AnalyzeInstDefUse(&*newExt); newInsts->emplace_back(std::move(newExt)); *resultId = extResultId; @@ -309,27 +300,19 @@ bool CommonUniformElimPass::UniformAccessChainConvert(ir::Function* func) { bool modified = false; for (auto bi = func->begin(); bi != func->end(); ++bi) { for (auto ii = bi->begin(); ii != bi->end(); ++ii) { - if (ii->opcode() != SpvOpLoad) - continue; + if (ii->opcode() != SpvOpLoad) continue; uint32_t varId; ir::Instruction* ptrInst = GetPtr(&*ii, &varId); - if (!IsNonPtrAccessChain(ptrInst->opcode())) - continue; + if (!IsNonPtrAccessChain(ptrInst->opcode())) continue; // Do not convert nested access chains if (ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx) != varId) continue; - if (!IsUniformVar(varId)) - continue; - if (!IsConstantIndexAccessChain(ptrInst)) - continue; - if (HasUnsupportedDecorates(ii->result_id())) - continue; - if (HasUnsupportedDecorates(ptrInst->result_id())) - continue; - if (IsVolatileLoad(*ii)) - continue; - if (IsAccessChainToVolatileStructType(*ptrInst)) - continue; + if (!IsUniformVar(varId)) continue; + if (!IsConstantIndexAccessChain(ptrInst)) continue; + if (HasUnsupportedDecorates(ii->result_id())) continue; + if (HasUnsupportedDecorates(ptrInst->result_id())) continue; + if (IsVolatileLoad(*ii)) continue; + if (IsAccessChainToVolatileStructType(*ptrInst)) continue; std::vector<std::unique_ptr<ir::Instruction>> newInsts; uint32_t replId; GenACLoadRepl(ptrInst, &newInsts, &replId); @@ -344,18 +327,20 @@ bool CommonUniformElimPass::UniformAccessChainConvert(ir::Function* func) { } void CommonUniformElimPass::ComputeStructuredSuccessors(ir::Function* func) { + block2structured_succs_.clear(); for (auto& blk : *func) { // If header, make merge block first successor. - uint32_t cbid; - const uint32_t mbid = MergeBlockIdIfAny(blk, &cbid); + uint32_t mbid = blk.MergeBlockIdIfAny(); if (mbid != 0) { - block2structured_succs_[&blk].push_back(id2block_[mbid]); - if (cbid != 0) - block2structured_succs_[&blk].push_back(id2block_[cbid]); + block2structured_succs_[&blk].push_back(cfg()->block(mbid)); + uint32_t cbid = blk.ContinueBlockIdIfAny(); + if (cbid != 0) { + block2structured_succs_[&blk].push_back(cfg()->block(mbid)); + } } // add true successors blk.ForEachSuccessorLabel([&blk, this](uint32_t sbid) { - block2structured_succs_[&blk].push_back(id2block_[sbid]); + block2structured_succs_[&blk].push_back(cfg()->block(sbid)); }); } } @@ -367,16 +352,18 @@ void CommonUniformElimPass::ComputeStructuredOrder( auto ignore_block = [](cbb_ptr) {}; auto ignore_edge = [](cbb_ptr, cbb_ptr) {}; auto get_structured_successors = [this](const ir::BasicBlock* block) { - return &(block2structured_succs_[block]); }; + return &(block2structured_succs_[block]); + }; // TODO(greg-lunarg): Get rid of const_cast by making moving const // out of the cfa.h prototypes and into the invoking code. auto post_order = [&](cbb_ptr b) { - order->push_front(const_cast<ir::BasicBlock*>(b)); }; - + order->push_front(const_cast<ir::BasicBlock*>(b)); + }; + order->clear(); spvtools::CFA<ir::BasicBlock>::DepthFirstTraversal( - &*func->begin(), get_structured_successors, ignore_block, - post_order, ignore_edge); + &*func->begin(), get_structured_successors, ignore_block, post_order, + ignore_edge); } bool CommonUniformElimPass::CommonUniformLoadElimination(ir::Function* func) { @@ -391,7 +378,7 @@ bool CommonUniformElimPass::CommonUniformLoadElimination(ir::Function* func) { // Find insertion point in first block to copy non-dominating loads. auto insertItr = func->begin()->begin(); while (insertItr->opcode() == SpvOpVariable || - insertItr->opcode() == SpvOpNop) + insertItr->opcode() == SpvOpNop) ++insertItr; uint32_t mergeBlockId = 0; for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) { @@ -403,36 +390,29 @@ bool CommonUniformElimPass::CommonUniformLoadElimination(ir::Function* func) { insertItr = bp->begin(); } for (auto ii = bp->begin(); ii != bp->end(); ++ii) { - if (ii->opcode() != SpvOpLoad) - continue; + if (ii->opcode() != SpvOpLoad) continue; uint32_t varId; ir::Instruction* ptrInst = GetPtr(&*ii, &varId); - if (ptrInst->opcode() != SpvOpVariable) - continue; - if (!IsUniformVar(varId)) - continue; - if (IsSamplerOrImageVar(varId)) - continue; - if (HasUnsupportedDecorates(ii->result_id())) - continue; - if (IsVolatileLoad(*ii)) - continue; + if (ptrInst->opcode() != SpvOpVariable) continue; + if (!IsUniformVar(varId)) continue; + if (IsSamplerOrImageVar(varId)) continue; + if (HasUnsupportedDecorates(ii->result_id())) continue; + if (IsVolatileLoad(*ii)) continue; uint32_t replId; const auto uItr = uniform2load_id_.find(varId); if (uItr != uniform2load_id_.end()) { replId = uItr->second; - } - else { + } else { if (mergeBlockId == 0) { // Load is in dominating block; just remember it uniform2load_id_[varId] = ii->result_id(); continue; - } - else { + } else { // Copy load into most recent dominating block and remember it replId = TakeNextId(); - std::unique_ptr<ir::Instruction> newLoad(new ir::Instruction(SpvOpLoad, - ii->type_id(), replId, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {varId}}})); + std::unique_ptr<ir::Instruction> newLoad(new ir::Instruction( + SpvOpLoad, ii->type_id(), replId, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {varId}}})); get_def_use_mgr()->AnalyzeInstDefUse(&*newLoad); insertItr = insertItr.InsertBefore(std::move(newLoad)); ++insertItr; @@ -445,8 +425,7 @@ bool CommonUniformElimPass::CommonUniformLoadElimination(ir::Function* func) { // If we are outside of any control construct and entering one, remember // the id of the merge block if (mergeBlockId == 0) { - uint32_t dummy; - mergeBlockId = MergeBlockIdIfAny(*bp, &dummy); + mergeBlockId = bp->MergeBlockIdIfAny(); } } return modified; @@ -457,26 +436,19 @@ bool CommonUniformElimPass::CommonUniformLoadElimBlock(ir::Function* func) { for (auto& blk : *func) { uniform2load_id_.clear(); for (auto ii = blk.begin(); ii != blk.end(); ++ii) { - if (ii->opcode() != SpvOpLoad) - continue; + if (ii->opcode() != SpvOpLoad) continue; uint32_t varId; ir::Instruction* ptrInst = GetPtr(&*ii, &varId); - if (ptrInst->opcode() != SpvOpVariable) - continue; - if (!IsUniformVar(varId)) - continue; - if (!IsSamplerOrImageVar(varId)) - continue; - if (HasUnsupportedDecorates(ii->result_id())) - continue; - if (IsVolatileLoad(*ii)) - continue; + if (ptrInst->opcode() != SpvOpVariable) continue; + if (!IsUniformVar(varId)) continue; + if (!IsSamplerOrImageVar(varId)) continue; + if (HasUnsupportedDecorates(ii->result_id())) continue; + if (IsVolatileLoad(*ii)) continue; uint32_t replId; const auto uItr = uniform2load_id_.find(varId); if (uItr != uniform2load_id_.end()) { replId = uItr->second; - } - else { + } else { uniform2load_id_[varId] = ii->result_id(); continue; } @@ -491,13 +463,10 @@ bool CommonUniformElimPass::CommonExtractElimination(ir::Function* func) { // Find all composite ids with duplicate extracts. for (auto bi = func->begin(); bi != func->end(); ++bi) { for (auto ii = bi->begin(); ii != bi->end(); ++ii) { - if (ii->opcode() != SpvOpCompositeExtract) - continue; + if (ii->opcode() != SpvOpCompositeExtract) continue; // TODO(greg-lunarg): Support multiple indices - if (ii->NumInOperands() > 2) - continue; - if (HasUnsupportedDecorates(ii->result_id())) - continue; + if (ii->NumInOperands() > 2) continue; + if (HasUnsupportedDecorates(ii->result_id())) continue; uint32_t compId = ii->GetSingleWordInOperand(kExtractCompositeIdInIdx); uint32_t idx = ii->GetSingleWordInOperand(kExtractIdx0InIdx); comp2idx2inst_[compId][idx].push_back(&*ii); @@ -509,13 +478,12 @@ bool CommonUniformElimPass::CommonExtractElimination(ir::Function* func) { for (auto bi = func->begin(); bi != func->end(); ++bi) { for (auto ii = bi->begin(); ii != bi->end(); ++ii) { const auto cItr = comp2idx2inst_.find(ii->result_id()); - if (cItr == comp2idx2inst_.end()) - continue; + if (cItr == comp2idx2inst_.end()) continue; for (auto idxItr : cItr->second) { - if (idxItr.second.size() < 2) - continue; + if (idxItr.second.size() < 2) continue; uint32_t replId = TakeNextId(); - std::unique_ptr<ir::Instruction> newExtract(new ir::Instruction(*idxItr.second.front())); + std::unique_ptr<ir::Instruction> newExtract( + new ir::Instruction(*idxItr.second.front())); newExtract->SetResultId(replId); get_def_use_mgr()->AnalyzeInstDefUse(&*newExtract); ++ii; @@ -534,13 +502,13 @@ bool CommonUniformElimPass::CommonExtractElimination(ir::Function* func) { } bool CommonUniformElimPass::EliminateCommonUniform(ir::Function* func) { - bool modified = false; - modified |= UniformAccessChainConvert(func); - modified |= CommonUniformLoadElimination(func); - modified |= CommonExtractElimination(func); + bool modified = false; + modified |= UniformAccessChainConvert(func); + modified |= CommonUniformLoadElimination(func); + modified |= CommonExtractElimination(func); - modified |= CommonUniformLoadElimBlock(func); - return modified; + modified |= CommonUniformLoadElimBlock(func); + return modified; } void CommonUniformElimPass::Initialize(ir::IRContext* c) { @@ -557,8 +525,8 @@ void CommonUniformElimPass::Initialize(ir::IRContext* c) { bool CommonUniformElimPass::AllExtensionsSupported() const { // If any extension not in whitelist, return false for (auto& ei : get_module()->extensions()) { - const char* extName = reinterpret_cast<const char*>( - &ei.GetInOperand(0).words[0]); + const char* extName = + reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]); if (extensions_whitelist_.find(extName) == extensions_whitelist_.end()) return false; } @@ -575,14 +543,12 @@ Pass::Status CommonUniformElimPass::ProcessImpl() { if (get_module()->HasCapability(SpvCapabilityAddresses)) return Status::SuccessWithoutChange; // Do not process if any disallowed extensions are enabled - if (!AllExtensionsSupported()) - return Status::SuccessWithoutChange; + if (!AllExtensionsSupported()) return Status::SuccessWithoutChange; // Do not process if module contains OpGroupDecorate. Additional // support required in KillNamesAndDecorates(). // TODO(greg-lunarg): Add support for OpGroupDecorate for (auto& ai : get_module()->annotations()) - if (ai.opcode() == SpvOpGroupDecorate) - return Status::SuccessWithoutChange; + if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange; // If non-32-bit integer type in module, terminate processing // TODO(): Handle non-32-bit integer constants in access chains for (const ir::Instruction& inst : get_module()->types_values()) @@ -607,29 +573,29 @@ Pass::Status CommonUniformElimPass::Process(ir::IRContext* c) { void CommonUniformElimPass::InitExtensions() { extensions_whitelist_.clear(); extensions_whitelist_.insert({ - "SPV_AMD_shader_explicit_vertex_parameter", - "SPV_AMD_shader_trinary_minmax", - "SPV_AMD_gcn_shader", - "SPV_KHR_shader_ballot", - "SPV_AMD_shader_ballot", - "SPV_AMD_gpu_shader_half_float", - "SPV_KHR_shader_draw_parameters", - "SPV_KHR_subgroup_vote", - "SPV_KHR_16bit_storage", - "SPV_KHR_device_group", - "SPV_KHR_multiview", - "SPV_NVX_multiview_per_view_attributes", - "SPV_NV_viewport_array2", - "SPV_NV_stereo_view_rendering", - "SPV_NV_sample_mask_override_coverage", - "SPV_NV_geometry_shader_passthrough", - "SPV_AMD_texture_gather_bias_lod", - "SPV_KHR_storage_buffer_storage_class", - // SPV_KHR_variable_pointers - // Currently do not support extended pointer expressions - "SPV_AMD_gpu_shader_int16", - "SPV_KHR_post_depth_coverage", - "SPV_KHR_shader_atomic_counter_ops", + "SPV_AMD_shader_explicit_vertex_parameter", + "SPV_AMD_shader_trinary_minmax", + "SPV_AMD_gcn_shader", + "SPV_KHR_shader_ballot", + "SPV_AMD_shader_ballot", + "SPV_AMD_gpu_shader_half_float", + "SPV_KHR_shader_draw_parameters", + "SPV_KHR_subgroup_vote", + "SPV_KHR_16bit_storage", + "SPV_KHR_device_group", + "SPV_KHR_multiview", + "SPV_NVX_multiview_per_view_attributes", + "SPV_NV_viewport_array2", + "SPV_NV_stereo_view_rendering", + "SPV_NV_sample_mask_override_coverage", + "SPV_NV_geometry_shader_passthrough", + "SPV_AMD_texture_gather_bias_lod", + "SPV_KHR_storage_buffer_storage_class", + // SPV_KHR_variable_pointers + // Currently do not support extended pointer expressions + "SPV_AMD_gpu_shader_int16", + "SPV_KHR_post_depth_coverage", + "SPV_KHR_shader_atomic_counter_ops", }); } diff --git a/source/opt/common_uniform_elim_pass.h b/source/opt/common_uniform_elim_pass.h index 5aa7f8b3..ccafd313 100644 --- a/source/opt/common_uniform_elim_pass.h +++ b/source/opt/common_uniform_elim_pass.h @@ -190,6 +190,11 @@ class CommonUniformElimPass : public Pass { // Extensions supported by this pass. std::unordered_set<std::string> extensions_whitelist_; + + // Map from block to its structured successor blocks. See + // ComputeStructuredSuccessors() for definition. + std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>> + block2structured_succs_; }; } // namespace opt diff --git a/source/opt/dead_branch_elim_pass.cpp b/source/opt/dead_branch_elim_pass.cpp index 82317f41..2558c816 100644 --- a/source/opt/dead_branch_elim_pass.cpp +++ b/source/opt/dead_branch_elim_pass.cpp @@ -175,7 +175,7 @@ void DeadBranchElimPass::ComputeBackEdges( bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) { // Traverse blocks in structured order std::list<ir::BasicBlock*> structuredOrder; - ComputeStructuredOrder(func, &*func->begin(), &structuredOrder); + cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder); ComputeBackEdges(structuredOrder); std::unordered_set<ir::BasicBlock*> elimBlocks; bool modified = false; @@ -289,7 +289,7 @@ bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) { pii->ForEachInId( [&deadPreds,&icnt,&lcnt,&lidx,this](uint32_t* idp) { if (icnt % 2 == 1) { - if (deadPreds.find(id2block_[*idp]) == deadPreds.end()) { + if (deadPreds.find(cfg()->block(*idp)) == deadPreds.end()) { ++lcnt; lidx = icnt - 1; } @@ -311,7 +311,7 @@ bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) { pii->ForEachInId( [&deadPreds,&icnt,&phi_in_opnds,&lastId,this](uint32_t* idp) { if (icnt % 2 == 1) { - if (deadPreds.find(id2block_[*idp]) == deadPreds.end()) { + if (deadPreds.find(cfg()->block(*idp)) == deadPreds.end()) { phi_in_opnds.push_back( {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {lastId}}); phi_in_opnds.push_back( @@ -348,15 +348,6 @@ bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) { void DeadBranchElimPass::Initialize(ir::IRContext* c) { InitializeProcessing(c); - // Initialize function and block maps - id2block_.clear(); - block2structured_succs_.clear(); - - // Initialize block map - for (auto& fn : *get_module()) - for (auto& blk : fn) - id2block_[blk.id()] = &blk; - // Initialize extension whitelist InitExtensions(); }; diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp index 7627355e..83d365cd 100644 --- a/source/opt/inline_pass.cpp +++ b/source/opt/inline_pass.cpp @@ -563,15 +563,18 @@ bool InlinePass::HasMultipleReturns(ir::Function* func) { void InlinePass::ComputeStructuredSuccessors(ir::Function* func) { // If header, make merge block first successor. for (auto& blk : *func) { - uint32_t mbid = MergeBlockIdIfAny(blk, nullptr); - if (mbid != 0) + uint32_t mbid = blk.MergeBlockIdIfAny(); + if (mbid != 0) { block2structured_succs_[&blk].push_back(id2block_[mbid]); - // add true successors + } + + // Add true successors. blk.ForEachSuccessorLabel([&blk, this](uint32_t sbid) { block2structured_succs_[&blk].push_back(id2block_[sbid]); }); } } + InlinePass::GetBlocksFunction InlinePass::StructuredSuccessorsFunction() { return [this](const ir::BasicBlock* block) { return &(block2structured_succs_[block]); diff --git a/source/opt/inline_pass.h b/source/opt/inline_pass.h index 2451ae90..1eae3f07 100644 --- a/source/opt/inline_pass.h +++ b/source/opt/inline_pass.h @@ -166,7 +166,8 @@ class InlinePass : public Pass { // Map from function's result id to function. std::unordered_map<uint32_t, ir::Function*> id2function_; - // Map from block's label id to block. + // Map from block's label id to block. TODO(dnovillo): This is superfluous wrt + // opt::CFG. It has functionality not present in opt::CFG. Consolidate. std::unordered_map<uint32_t, ir::BasicBlock*> id2block_; // Set of ids of functions with multiple returns. @@ -180,6 +181,13 @@ class InlinePass : public Pass { // result id for OpConstantFalse uint32_t false_id_; + + // Map from block to its structured successor blocks. See + // ComputeStructuredSuccessors() for definition. TODO(dnovillo): This is + // superfluous wrt opt::CFG, but it seems to be computed in a slightly + // different way in the inliner. Can these be consolidated? + std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>> + block2structured_succs_; }; } // namespace opt diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp index f50f70b5..83f6f3a8 100644 --- a/source/opt/local_single_store_elim_pass.cpp +++ b/source/opt/local_single_store_elim_pass.cpp @@ -132,16 +132,16 @@ void LocalSingleStoreElimPass::CalculateImmediateDominators( // Compute Augmented CFG augmented_successors_map_.clear(); augmented_predecessors_map_.clear(); - successors_map_[&pseudo_exit_block_] = {}; - predecessors_map_[&pseudo_entry_block_] = {}; + successors_map_[cfg()->pseudo_exit_block()] = {}; + predecessors_map_[cfg()->pseudo_entry_block()] = {}; auto succ_func = [this](const ir::BasicBlock* b) { return &successors_map_[b]; }; auto pred_func = [this](const ir::BasicBlock* b) { return &predecessors_map_[b]; }; CFA<ir::BasicBlock>::ComputeAugmentedCFG( ordered_blocks, - &pseudo_entry_block_, - &pseudo_exit_block_, + cfg()->pseudo_entry_block(), + cfg()->pseudo_exit_block(), &augmented_successors_map_, &augmented_predecessors_map_, succ_func, diff --git a/source/opt/local_single_store_elim_pass.h b/source/opt/local_single_store_elim_pass.h index 4a7dacb9..e3aff0d1 100644 --- a/source/opt/local_single_store_elim_pass.h +++ b/source/opt/local_single_store_elim_pass.h @@ -68,6 +68,7 @@ class LocalSingleStoreElimPass : public MemPass { // Calculate immediate dominators for |func|'s CFG. Leaves result // in idom_. Entries for augmented CFG (pseudo blocks) are not created. + // TODO(dnovillo): Move to new CFG class. void CalculateImmediateDominators(ir::Function* func); // Return true if instruction in |blk0| at ordinal position |idx0| diff --git a/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp index 9c4b0a49..02a3cc3e 100644 --- a/source/opt/mem_pass.cpp +++ b/source/opt/mem_pass.cpp @@ -16,6 +16,7 @@ #include "mem_pass.h" +#include "basic_block.h" #include "cfa.h" #include "iterator.h" @@ -272,19 +273,9 @@ void MemPass::InitSSARewrite(ir::Function* func) { visitedBlocks_.clear(); type2undefs_.clear(); supported_ref_vars_.clear(); - block2structured_succs_.clear(); - label2preds_.clear(); label2ssa_map_.clear(); phis_to_patch_.clear(); - // Init predecessors - label2preds_.clear(); - for (auto& blk : *func) { - uint32_t blkId = blk.id(); - blk.ForEachSuccessorLabel( - [&blkId, this](uint32_t sbid) { label2preds_[sbid].push_back(blkId); }); - } - // Collect target (and non-) variable sets. Remove variables with // non-load/store refs from target variable set for (auto& blk : *func) { @@ -319,7 +310,7 @@ bool MemPass::IsLiveAfter(uint32_t var_id, uint32_t label) const { void MemPass::SSABlockInitSinglePred(ir::BasicBlock* block_ptr) { // Copy map entry from single predecessor const uint32_t label = block_ptr->id(); - const uint32_t predLabel = label2preds_[label].front(); + const uint32_t predLabel = cfg()->preds(label).front(); assert(visitedBlocks_.find(predLabel) != visitedBlocks_.end()); label2ssa_map_[label] = label2ssa_map_[predLabel]; } @@ -342,7 +333,7 @@ void MemPass::SSABlockInitLoopHeader( // Determine the back-edge label. uint32_t backLabel = 0; - for (uint32_t predLabel : label2preds_[label]) + for (uint32_t predLabel : cfg()->preds(label)) if (visitedBlocks_.find(predLabel) == visitedBlocks_.end()) { assert(backLabel == 0); backLabel = predLabel; @@ -362,7 +353,7 @@ void MemPass::SSABlockInitLoopHeader( // generated based on order and test results will otherwise vary across // platforms. std::map<uint32_t, uint32_t> liveVars; - for (uint32_t predLabel : label2preds_[label]) { + for (uint32_t predLabel : cfg()->preds(label)) { for (auto var_val : label2ssa_map_[predLabel]) { uint32_t varId = var_val.first; liveVars[varId] = var_val.second; @@ -395,7 +386,7 @@ void MemPass::SSABlockInitLoopHeader( const uint32_t val0Id = var_val.second; bool needsPhi = false; if (val0Id != 0) { - for (uint32_t predLabel : label2preds_[label]) { + for (uint32_t predLabel : cfg()->preds(label)) { // Skip back edge predecessor. if (predLabel == backLabel) continue; const auto var_val_itr = label2ssa_map_[predLabel].find(varId); @@ -426,7 +417,7 @@ void MemPass::SSABlockInitLoopHeader( // use undef. std::vector<ir::Operand> phi_in_operands; uint32_t typeId = GetPointeeTypeId(get_def_use_mgr()->GetDef(varId)); - for (uint32_t predLabel : label2preds_[label]) { + for (uint32_t predLabel : cfg()->preds(label)) { uint32_t valId; if (predLabel == backLabel) { valId = varId; @@ -462,7 +453,7 @@ void MemPass::SSABlockInitMultiPred(ir::BasicBlock* block_ptr) { // predecesors. Must be ordered map because phis are generated based on // order and test results will otherwise vary across platforms. std::map<uint32_t, uint32_t> liveVars; - for (uint32_t predLabel : label2preds_[label]) { + for (uint32_t predLabel : cfg()->preds(label)) { assert(visitedBlocks_.find(predLabel) != visitedBlocks_.end()); for (auto var_val : label2ssa_map_[predLabel]) { const uint32_t varId = var_val.first; @@ -477,7 +468,7 @@ void MemPass::SSABlockInitMultiPred(ir::BasicBlock* block_ptr) { if (!IsLiveAfter(varId, label)) continue; const uint32_t val0Id = var_val.second; bool differs = false; - for (uint32_t predLabel : label2preds_[label]) { + for (uint32_t predLabel : cfg()->preds(label)) { const auto var_val_itr = label2ssa_map_[predLabel].find(varId); // Missing values cause a difference because we'll need to create an // undef for that predecessor. @@ -499,7 +490,7 @@ void MemPass::SSABlockInitMultiPred(ir::BasicBlock* block_ptr) { // id to the map. std::vector<ir::Operand> phi_in_operands; const uint32_t typeId = GetPointeeTypeId(get_def_use_mgr()->GetDef(varId)); - for (uint32_t predLabel : label2preds_[label]) { + for (uint32_t predLabel : cfg()->preds(label)) { const auto var_val_itr = label2ssa_map_[predLabel].find(varId); // If variable not defined on this path, use undef const uint32_t valId = (var_val_itr != label2ssa_map_[predLabel].end()) @@ -521,11 +512,11 @@ void MemPass::SSABlockInitMultiPred(ir::BasicBlock* block_ptr) { } void MemPass::SSABlockInit(std::list<ir::BasicBlock*>::iterator block_itr) { - const size_t numPreds = label2preds_[(*block_itr)->id()].size(); + const size_t numPreds = cfg()->preds((*block_itr)->id()).size(); if (numPreds == 0) return; if (numPreds == 1) SSABlockInitSinglePred(*block_itr); - else if (IsLoopHeader(*block_itr)) + else if ((*block_itr)->IsLoopHeader()) SSABlockInitLoopHeader(block_itr); else SSABlockInitMultiPred(*block_itr); @@ -557,7 +548,7 @@ bool MemPass::IsTargetVar(uint32_t varId) { } void MemPass::PatchPhis(uint32_t header_id, uint32_t back_id) { - ir::BasicBlock* header = id2block_[header_id]; + ir::BasicBlock* header = cfg()->block(header_id); auto phiItr = header->begin(); for (; phiItr->opcode() == SpvOpPhi; ++phiItr) { // Only patch phis that we created in a loop header. @@ -590,8 +581,8 @@ Pass::Status MemPass::InsertPhiInstructions(ir::Function* func) { // TODO(dnovillo) the current Phi placement mechanism assumes structured // control-flow. This should be generalized // (https://github.com/KhronosGroup/SPIRV-Tools/issues/893). - if (!get_module()->HasCapability(SpvCapabilityShader)) - return Status::SuccessWithoutChange; + assert(get_module()->HasCapability(SpvCapabilityShader) && + "This only works on structured control flow"); // Initialize the data structures used to insert Phi instructions. InitSSARewrite(func); @@ -600,10 +591,14 @@ Pass::Status MemPass::InsertPhiInstructions(ir::Function* func) { // simplest?) to make sure all predecessors blocks are processed before // a block itself. std::list<ir::BasicBlock*> structuredOrder; - ComputeStructuredOrder(func, &pseudo_entry_block_, &structuredOrder); + cfg()->ComputeStructuredOrder(func, cfg()->pseudo_entry_block(), + &structuredOrder); for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) { // Skip pseudo entry block - if (*bi == &pseudo_entry_block_) continue; + if (cfg()->IsPseudoEntryBlock(*bi)) { + continue; + } + // Initialize this block's label2ssa_map_ entry using predecessor maps. // Then process all stores and loads of targeted variables. SSABlockInit(bi); @@ -725,7 +720,7 @@ void MemPass::RemovePhiOperands( assert(i % 2 == 0 && i < phi->NumOperands() - 1 && "malformed Phi arguments"); - ir::BasicBlock *in_block = label2block_[phi->GetSingleWordOperand(i + 1)]; + ir::BasicBlock *in_block = cfg()->block(phi->GetSingleWordOperand(i + 1)); if (reachable_blocks.find(in_block) == reachable_blocks.end()) { // If the incoming block is unreachable, remove both operands as this // means that the |phi| has lost an incoming edge. @@ -798,7 +793,7 @@ bool MemPass::RemoveUnreachableBlocks(ir::Function* func) { auto mark_reachable = [&reachable_blocks, &visited_blocks, &worklist, this](uint32_t label_id) { - auto successor = label2block_[label_id]; + auto successor = cfg()->block(label_id); if (visited_blocks.count(successor) == 0) { reachable_blocks.insert(successor); worklist.push(successor); @@ -855,16 +850,13 @@ bool MemPass::CFGCleanup(ir::Function* func) { } void MemPass::InitializeCFGCleanup(ir::IRContext* c) { - // Initialize block lookup map. - label2block_.clear(); + // Build a map between SSA names to the block they are defined in. + // + // TODO(dnovillo): This is expensive and unnecessary if ir::Instruction + // instances could figure out what basic block they belong to. Remove this + // once this is possible. for (auto& fn : *c->module()) { for (auto& block : fn) { - label2block_[block.id()] = █ - - // Build a map between SSA names to the block they are defined in. - // TODO(dnovillo): This is expensive and unnecessary if ir::Instruction - // instances could figure out what basic block they belong to. Remove this - // once this is possible. block.ForEachInst([this, &block](ir::Instruction* inst) { uint32_t result_id = inst->result_id(); if (result_id > 0) { @@ -875,7 +867,6 @@ void MemPass::InitializeCFGCleanup(ir::IRContext* c) { } } - } // namespace opt } // namespace spvtools diff --git a/source/opt/mem_pass.h b/source/opt/mem_pass.h index 6a18aebb..a9fd36fa 100644 --- a/source/opt/mem_pass.h +++ b/source/opt/mem_pass.h @@ -132,10 +132,6 @@ class MemPass : public Pass { // non-target variables. bool IsTargetVar(uint32_t varId); - // Initialize data structures used by EliminateLocalMultiStore for - // function |func|, specifically block predecessors and target variables. - void InitSSARewrite(ir::Function* func); - // Return undef in function for type. Create and insert an undef after the // first non-variable in the function if it doesn't already exist. Add // undef to function undef map. @@ -168,6 +164,10 @@ class MemPass : public Pass { // predecessor map. void PatchPhis(uint32_t header_id, uint32_t back_id); + // Initialize data structures used by EliminateLocalMultiStore for + // function |func|, specifically block predecessors and target variables. + void InitSSARewrite(ir::Function* func); + // Initialize label2ssa_map_ entry for block |block_ptr| with single // predecessor. void SSABlockInitSinglePred(ir::BasicBlock* block_ptr); @@ -232,10 +232,6 @@ class MemPass : public Pass { // patching of the value for the loop back-edge. std::unordered_set<uint32_t> phis_to_patch_; - // Map from block's label id to block. TODO(dnovillo): Basic blocks ought to - // have basic blocks in their pred/succ list. - std::unordered_map<uint32_t, ir::BasicBlock*> label2block_; - // Map from an instruction result ID to the block that holds it. // TODO(dnovillo): This would be unnecessary if ir::Instruction instances // knew what basic block they belong to. diff --git a/source/opt/pass.cpp b/source/opt/pass.cpp index 5f7fba54..d0af4af6 100644 --- a/source/opt/pass.cpp +++ b/source/opt/pass.cpp @@ -16,7 +16,6 @@ #include "pass.h" -#include "cfa.h" #include "iterator.h" namespace spvtools { @@ -25,22 +24,12 @@ namespace opt { namespace { const uint32_t kEntryPointFunctionIdInIdx = 1; -const uint32_t kLoopMergeContinueBlockIdInIdx = 1; -const uint32_t kLoopMergeMergeBlockIdInIdx = 0; -const uint32_t kSelectionMergeMergeBlockIdInIdx = 0; const uint32_t kTypePointerTypeIdInIdx = 1; -// Universal Limit of ResultID + 1 -const int kInvalidId = 0x400000; - } // namespace Pass::Pass() - : 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, {}))), - consumer_(nullptr), + : consumer_(nullptr), def_use_mgr_(nullptr), next_id_(0), context_(nullptr) {} @@ -117,85 +106,12 @@ bool Pass::ProcessCallTreeFromRoots( return modified; } -bool Pass::IsLoopHeader(ir::BasicBlock* block_ptr) const { - auto iItr = block_ptr->end(); - --iItr; - if (iItr == block_ptr->begin()) - return false; - --iItr; - return iItr->opcode() == SpvOpLoopMerge; -} - uint32_t Pass::GetPointeeTypeId(const ir::Instruction* ptrInst) const { const uint32_t ptrTypeId = ptrInst->type_id(); const ir::Instruction* ptrTypeInst = get_def_use_mgr()->GetDef(ptrTypeId); return ptrTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx); } -void Pass::ComputeStructuredOrder(ir::Function* func, ir::BasicBlock* root, - std::list<ir::BasicBlock*>* order) { - // Compute structured successors and do DFS - ComputeStructuredSuccessors(func); - auto ignore_block = [](cbb_ptr) {}; - auto ignore_edge = [](cbb_ptr, cbb_ptr) {}; - auto get_structured_successors = [this](const ir::BasicBlock* block) { - return &(block2structured_succs_[block]); - }; - - // TODO(greg-lunarg): Get rid of const_cast by making moving const - // out of the cfa.h prototypes and into the invoking code. - auto post_order = [&](cbb_ptr b) { - order->push_front(const_cast<ir::BasicBlock*>(b)); - }; - spvtools::CFA<ir::BasicBlock>::DepthFirstTraversal( - root, get_structured_successors, ignore_block, post_order, - ignore_edge); -} - -void Pass::ComputeStructuredSuccessors(ir::Function *func) { - block2structured_succs_.clear(); - for (auto& blk : *func) { - // If no predecessors in function, make successor to pseudo entry - if (label2preds_[blk.id()].size() == 0) - block2structured_succs_[&pseudo_entry_block_].push_back(&blk); - // If header, make merge block first successor and continue block second - // successor if there is one. - uint32_t cbid; - const uint32_t mbid = MergeBlockIdIfAny(blk, &cbid); - if (mbid != 0) { - block2structured_succs_[&blk].push_back(id2block_[mbid]); - if (cbid != 0) - block2structured_succs_[&blk].push_back(id2block_[cbid]); - } - // add true successors - blk.ForEachSuccessorLabel([&blk, this](uint32_t sbid) { - block2structured_succs_[&blk].push_back(id2block_[sbid]); - }); - } -} - - -uint32_t Pass::MergeBlockIdIfAny(const ir::BasicBlock& blk, uint32_t* cbid) { - auto merge_ii = blk.cend(); - --merge_ii; - if (cbid != nullptr) { - *cbid = 0; - } - uint32_t mbid = 0; - if (merge_ii != blk.cbegin()) { - --merge_ii; - if (merge_ii->opcode() == SpvOpLoopMerge) { - mbid = merge_ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx); - if (cbid != nullptr) { - *cbid = - merge_ii->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx); - } - } else if (merge_ii->opcode() == SpvOpSelectionMerge) { - mbid = merge_ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); - } - } - return mbid; -} } // namespace opt } // namespace spvtools diff --git a/source/opt/pass.h b/source/opt/pass.h index ed85a3e4..f1be7e0e 100644 --- a/source/opt/pass.h +++ b/source/opt/pass.h @@ -16,18 +16,18 @@ #define LIBSPIRV_OPT_PASS_H_ #include <algorithm> -#include <list> #include <map> #include <queue> #include <unordered_map> #include <unordered_set> #include <utility> +#include "basic_block.h" +#include "cfg.h" #include "def_use_manager.h" +#include "ir_context.h" #include "module.h" #include "spirv-tools/libspirv.hpp" -#include "basic_block.h" -#include "ir_context.h" namespace spvtools { namespace opt { @@ -85,6 +85,10 @@ class Pass { // Returns a pointer to the current context for this pass. ir::IRContext* context() const { return context_; } + // Returns a pointer to the CFG for current module. TODO(dnovillo): This + // should belong in IRContext. + CFG *cfg() const { return cfg_.get(); } + // Add to |todo| all ids of functions called in |func|. void AddCalls(ir::Function* func, std::queue<uint32_t>* todo); @@ -121,37 +125,9 @@ class Pass { context_ = c; next_id_ = context_->IdBound(); def_use_mgr_.reset(new analysis::DefUseManager(consumer(), get_module())); - block2structured_succs_.clear(); - label2preds_.clear(); - id2block_.clear(); - for (auto& fn : *context_->module()) { - for (auto& blk : fn) { - id2block_[blk.id()] = &blk; - } - } + cfg_.reset(new CFG(get_module())); } - // Return true if |block_ptr| points to a loop header block. TODO(dnovillo) - // This belongs in a CFG class. - bool IsLoopHeader(ir::BasicBlock* block_ptr) const; - - // Compute structured successors for function |func|. A block's structured - // successors are the blocks it branches to together with its declared merge - // block and continue block if it has them. When order matters, the merge - // block and continue block always appear first. This assures correct depth - // first search in the presence of early returns and kills. If the successor - // vector contain duplicates of the merge or continue blocks, they are safely - // ignored by DFS. TODO(dnovillo): This belongs in a CFG class. - void ComputeStructuredSuccessors(ir::Function* func); - - // Compute structured block order into |structuredOrder| for |func| starting - // at |root|. This order has the property that dominators come before all - // blocks they dominate and merge blocks come after all blocks that are in - // the control constructs of their header. TODO(dnovillo): This belongs in - // a CFG class. - void ComputeStructuredOrder(ir::Function* func, ir::BasicBlock* root, - std::list<ir::BasicBlock*>* order); - // Return type id for |ptrInst|'s pointee uint32_t GetPointeeTypeId(const ir::Instruction* ptrInst) const; @@ -163,31 +139,7 @@ class Pass { return retval; } - // Returns the id of the merge block declared by a merge instruction in this - // block, if any. If none, returns zero. - uint32_t MergeBlockIdIfAny(const ir::BasicBlock& blk, uint32_t* cbid); - - // Map from block to its structured successor blocks. See - // ComputeStructuredSuccessors() for definition. - std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>> - block2structured_succs_; - - // Extra block whose successors are all blocks with no predecessors - // in function. - ir::BasicBlock pseudo_entry_block_; - - // Augmented CFG Exit Block. - ir::BasicBlock pseudo_exit_block_; - - // Map from block's label id to its predecessor blocks ids - std::unordered_map<uint32_t, std::vector<uint32_t>> label2preds_; - - // Map from block's label id to block. - std::unordered_map<uint32_t, ir::BasicBlock*> id2block_; - private: - using cbb_ptr = const ir::BasicBlock*; - MessageConsumer consumer_; // Message consumer. // Def-Uses for the module we are processing @@ -196,8 +148,11 @@ class Pass { // Next unused ID uint32_t next_id_; - // The module that the pass is being applied to. + // The context that this pass belongs to. ir::IRContext* context_; + + // The CFG for all the functions in this module. + std::unique_ptr<CFG> cfg_; }; } // namespace opt |