| // 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 SOURCE_OPT_AGGRESSIVE_DEAD_CODE_ELIM_PASS_H_ |
| #define SOURCE_OPT_AGGRESSIVE_DEAD_CODE_ELIM_PASS_H_ |
| |
| #include <algorithm> |
| #include <list> |
| #include <map> |
| #include <queue> |
| #include <string> |
| #include <unordered_map> |
| #include <unordered_set> |
| #include <utility> |
| #include <vector> |
| |
| #include "source/opt/basic_block.h" |
| #include "source/opt/def_use_manager.h" |
| #include "source/opt/mem_pass.h" |
| #include "source/opt/module.h" |
| #include "source/util/bit_vector.h" |
| |
| namespace spvtools { |
| namespace opt { |
| |
| // See optimizer.hpp for documentation. |
| class AggressiveDCEPass : public MemPass { |
| using cbb_ptr = const BasicBlock*; |
| |
| public: |
| using GetBlocksFunction = |
| std::function<std::vector<BasicBlock*>*(const BasicBlock*)>; |
| |
| AggressiveDCEPass(); |
| const char* name() const override { return "eliminate-dead-code-aggressive"; } |
| Status Process() override; |
| |
| IRContext::Analysis GetPreservedAnalyses() override { |
| return IRContext::kAnalysisDefUse | |
| IRContext::kAnalysisInstrToBlockMapping | |
| IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; |
| } |
| |
| private: |
| // Return true if |varId| is a variable of |storageClass|. |varId| must either |
| // be 0 or the result of an instruction. |
| bool IsVarOfStorage(uint32_t varId, uint32_t storageClass); |
| |
| // Return true if |varId| is variable of function storage class or is |
| // private variable and privates can be optimized like locals (see |
| // privates_like_local_). |
| bool IsLocalVar(uint32_t varId); |
| |
| // Return true if |inst| is marked live. |
| bool IsLive(const Instruction* inst) const { |
| return live_insts_.Get(inst->unique_id()); |
| } |
| |
| // Returns true if |inst| is dead. |
| bool IsDead(Instruction* inst); |
| |
| // Adds entry points, execution modes and workgroup size decorations to the |
| // worklist for processing with the first function. |
| void InitializeModuleScopeLiveInstructions(); |
| |
| // Add |inst| to worklist_ and live_insts_. |
| void AddToWorklist(Instruction* inst) { |
| if (!live_insts_.Set(inst->unique_id())) { |
| worklist_.push(inst); |
| } |
| } |
| |
| // Add all store instruction which use |ptrId|, directly or indirectly, |
| // to the live instruction worklist. |
| void AddStores(uint32_t ptrId); |
| |
| // Initialize extensions whitelist |
| void InitExtensions(); |
| |
| // Return true if all extensions in this module are supported by this pass. |
| bool AllExtensionsSupported() const; |
| |
| // Returns true if the target of |inst| is dead. An instruction is dead if |
| // its result id is used in decoration or debug instructions only. |inst| is |
| // assumed to be OpName, OpMemberName or an annotation instruction. |
| bool IsTargetDead(Instruction* inst); |
| |
| // If |varId| is local, mark all stores of varId as live. |
| void ProcessLoad(uint32_t varId); |
| |
| // If |bp| is structured header block, returns true and sets |mergeInst| to |
| // the merge instruction, |branchInst| to the branch and |mergeBlockId| to the |
| // merge block if they are not nullptr. Any of |mergeInst|, |branchInst| or |
| // |mergeBlockId| may be a null pointer. Returns false if |bp| is a null |
| // pointer. |
| bool IsStructuredHeader(BasicBlock* bp, Instruction** mergeInst, |
| Instruction** branchInst, uint32_t* mergeBlockId); |
| |
| // Initialize block2headerBranch_, header2nextHeaderBranch_, and |
| // branch2merge_ using |structuredOrder| to order blocks. |
| void ComputeBlock2HeaderMaps(std::list<BasicBlock*>& structuredOrder); |
| |
| // Add branch to |labelId| to end of block |bp|. |
| void AddBranch(uint32_t labelId, BasicBlock* bp); |
| |
| // Add all break and continue branches in the construct associated with |
| // |mergeInst| to worklist if not already live |
| void AddBreaksAndContinuesToWorklist(Instruction* mergeInst); |
| |
| // Eliminates dead debug2 and annotation instructions. Marks dead globals for |
| // removal (e.g. types, constants and variables). |
| bool ProcessGlobalValues(); |
| |
| // Erases functions that are unreachable from the entry points of the module. |
| bool EliminateDeadFunctions(); |
| |
| // Removes |func| from the module and deletes all its instructions. |
| void EliminateFunction(Function* func); |
| |
| // For function |func|, mark all Stores to non-function-scope variables |
| // and block terminating instructions as live. Recursively mark the values |
| // they use. When complete, mark any non-live instructions to be deleted. |
| // Returns true if the function has been modified. |
| // |
| // Note: This function does not delete useless control structures. All |
| // existing control structures will remain. This can leave not-insignificant |
| // sequences of ultimately useless code. |
| // TODO(): Remove useless control constructs. |
| bool AggressiveDCE(Function* func); |
| |
| Pass::Status ProcessImpl(); |
| |
| // True if current function has a call instruction contained in it |
| bool call_in_func_; |
| |
| // True if current function is an entry point |
| bool func_is_entry_point_; |
| |
| // True if current function is entry point and has no function calls. |
| bool private_like_local_; |
| |
| // Live Instruction Worklist. An instruction is added to this list |
| // if it might have a side effect, either directly or indirectly. |
| // If we don't know, then add it to this list. Instructions are |
| // removed from this list as the algorithm traces side effects, |
| // building up the live instructions set |live_insts_|. |
| std::queue<Instruction*> worklist_; |
| |
| // Map from block to the branch instruction in the header of the most |
| // immediate controlling structured if or loop. A loop header block points |
| // to its own branch instruction. An if-selection block points to the branch |
| // of an enclosing construct's header, if one exists. |
| std::unordered_map<BasicBlock*, Instruction*> block2headerBranch_; |
| |
| // Map from header block to the branch instruction in the header of the |
| // structured construct enclosing it. |
| // The liveness algorithm is designed to iteratively mark as live all |
| // structured constructs enclosing a live instruction. |
| std::unordered_map<BasicBlock*, Instruction*> header2nextHeaderBranch_; |
| |
| // Maps basic block to their index in the structured order traversal. |
| std::unordered_map<BasicBlock*, uint32_t> structured_order_index_; |
| |
| // Map from branch to its associated merge instruction, if any |
| std::unordered_map<Instruction*, Instruction*> branch2merge_; |
| |
| // Store instructions to variables of private storage |
| std::vector<Instruction*> private_stores_; |
| |
| // Live Instructions |
| utils::BitVector live_insts_; |
| |
| // Live Local Variables |
| std::unordered_set<uint32_t> live_local_vars_; |
| |
| // List of instructions to delete. Deletion is delayed until debug and |
| // annotation instructions are processed. |
| std::vector<Instruction*> to_kill_; |
| |
| // Extensions supported by this pass. |
| std::unordered_set<std::string> extensions_whitelist_; |
| }; |
| |
| } // namespace opt |
| } // namespace spvtools |
| |
| #endif // SOURCE_OPT_AGGRESSIVE_DEAD_CODE_ELIM_PASS_H_ |