// 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 #include #include #include #include #include #include #include #include #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*(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; } 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& 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 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 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 header2nextHeaderBranch_; // Maps basic block to their index in the structured order traversal. std::unordered_map structured_order_index_; // Map from branch to its associated merge instruction, if any std::unordered_map branch2merge_; // Store instructions to variables of private storage std::vector private_stores_; // Live Instructions utils::BitVector live_insts_; // Live Local Variables std::unordered_set live_local_vars_; // List of instructions to delete. Deletion is delayed until debug and // annotation instructions are processed. std::vector to_kill_; // Extensions supported by this pass. std::unordered_set extensions_whitelist_; }; } // namespace opt } // namespace spvtools #endif // SOURCE_OPT_AGGRESSIVE_DEAD_CODE_ELIM_PASS_H_