1 // Copyright (c) 2017 The Khronos Group Inc. 2 // Copyright (c) 2017 Valve Corporation 3 // Copyright (c) 2017 LunarG Inc. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 #ifndef SOURCE_OPT_AGGRESSIVE_DEAD_CODE_ELIM_PASS_H_ 18 #define SOURCE_OPT_AGGRESSIVE_DEAD_CODE_ELIM_PASS_H_ 19 20 #include <algorithm> 21 #include <list> 22 #include <map> 23 #include <queue> 24 #include <string> 25 #include <unordered_map> 26 #include <unordered_set> 27 #include <utility> 28 #include <vector> 29 30 #include "source/opt/basic_block.h" 31 #include "source/opt/def_use_manager.h" 32 #include "source/opt/mem_pass.h" 33 #include "source/opt/module.h" 34 #include "source/util/bit_vector.h" 35 36 namespace spvtools { 37 namespace opt { 38 39 // See optimizer.hpp for documentation. 40 class AggressiveDCEPass : public MemPass { 41 using cbb_ptr = const BasicBlock*; 42 43 public: 44 using GetBlocksFunction = 45 std::function<std::vector<BasicBlock*>*(const BasicBlock*)>; 46 47 AggressiveDCEPass(bool preserve_interface = false, 48 bool remove_outputs = false) preserve_interface_(preserve_interface)49 : preserve_interface_(preserve_interface), 50 remove_outputs_(remove_outputs) {} 51 name()52 const char* name() const override { return "eliminate-dead-code-aggressive"; } 53 Status Process() override; 54 GetPreservedAnalyses()55 IRContext::Analysis GetPreservedAnalyses() override { 56 return IRContext::kAnalysisDefUse | 57 IRContext::kAnalysisInstrToBlockMapping | 58 IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; 59 } 60 61 private: 62 // Preserve entry point interface if true. All variables in interface 63 // will be marked live and will not be eliminated. This mode is needed by 64 // GPU-Assisted Validation instrumentation where a change in the interface 65 // is not allowed. 66 bool preserve_interface_; 67 68 // Output variables can be removed from the interface if this is true. 69 // This is safe if the caller knows that the corresponding input variable 70 // in the following shader has been removed. It is false by default. 71 bool remove_outputs_; 72 73 // Return true if |varId| is a variable of |storageClass|. |varId| must either 74 // be 0 or the result of an instruction. 75 bool IsVarOfStorage(uint32_t varId, spv::StorageClass storageClass); 76 77 // Return true if the instance of the variable |varId| can only be access in 78 // |func|. For example, a function scope variable, or a private variable 79 // where |func| is an entry point with no function calls. 80 bool IsLocalVar(uint32_t varId, Function* func); 81 82 // Return true if |inst| is marked live. IsLive(const Instruction * inst)83 bool IsLive(const Instruction* inst) const { 84 return live_insts_.Get(inst->unique_id()); 85 } 86 87 // Adds entry points, execution modes and workgroup size decorations to the 88 // worklist for processing with the first function. 89 void InitializeModuleScopeLiveInstructions(); 90 91 // Add |inst| to worklist_ and live_insts_. AddToWorklist(Instruction * inst)92 void AddToWorklist(Instruction* inst) { 93 if (!live_insts_.Set(inst->unique_id())) { 94 worklist_.push(inst); 95 } 96 } 97 98 // Add all store instruction which use |ptrId|, directly or indirectly, 99 // to the live instruction worklist. 100 void AddStores(Function* func, uint32_t ptrId); 101 102 // Initialize extensions allowlist 103 void InitExtensions(); 104 105 // Return true if all extensions in this module are supported by this pass. 106 bool AllExtensionsSupported() const; 107 108 // Returns true if the target of |inst| is dead. An instruction is dead if 109 // its result id is used in decoration or debug instructions only. |inst| is 110 // assumed to be OpName, OpMemberName or an annotation instruction. 111 bool IsTargetDead(Instruction* inst); 112 113 // If |varId| is local, mark all stores of varId as live. 114 void ProcessLoad(Function* func, uint32_t varId); 115 116 // Add branch to |labelId| to end of block |bp|. 117 void AddBranch(uint32_t labelId, BasicBlock* bp); 118 119 // Add all break and continue branches in the construct associated with 120 // |mergeInst| to worklist if not already live 121 void AddBreaksAndContinuesToWorklist(Instruction* mergeInst); 122 123 // Eliminates dead debug2 and annotation instructions. Marks dead globals for 124 // removal (e.g. types, constants and variables). 125 bool ProcessGlobalValues(); 126 127 // Erases functions that are unreachable from the entry points of the module. 128 bool EliminateDeadFunctions(); 129 130 // For function |func|, mark all Stores to non-function-scope variables 131 // and block terminating instructions as live. Recursively mark the values 132 // they use. When complete, mark any non-live instructions to be deleted. 133 // Returns true if the function has been modified. 134 // 135 // Note: This function does not delete useless control structures. All 136 // existing control structures will remain. This can leave not-insignificant 137 // sequences of ultimately useless code. 138 // TODO(): Remove useless control constructs. 139 bool AggressiveDCE(Function* func); 140 141 Pass::Status ProcessImpl(); 142 143 // Adds instructions which must be kept because of they have side-effects 144 // that ADCE cannot model to the work list. 145 void InitializeWorkList(Function* func, 146 std::list<BasicBlock*>& structured_order); 147 148 // Process each instruction in the work list by marking any instruction that 149 // that it depends on as live, and adding it to the work list. The work list 150 // will be empty at the end. 151 void ProcessWorkList(Function* func); 152 153 // Kills any instructions in |func| that have not been marked as live. 154 bool KillDeadInstructions(const Function* func, 155 std::list<BasicBlock*>& structured_order); 156 157 // Adds the instructions that define the operands of |inst| to the work list. 158 void AddOperandsToWorkList(const Instruction* inst); 159 160 // Marks all of the labels and branch that inst requires as live. 161 void MarkBlockAsLive(Instruction* inst); 162 163 // Marks any variables from which |inst| may require data as live. 164 void MarkLoadedVariablesAsLive(Function* func, Instruction* inst); 165 166 // Returns the id of the variable that |ptr_id| point to. |ptr_id| must be a 167 // value whose type is a pointer. 168 uint32_t GetVariableId(uint32_t ptr_id); 169 170 // Returns all of the ids for the variables from which |inst| will load data. 171 std::vector<uint32_t> GetLoadedVariables(Instruction* inst); 172 173 // Returns all of the ids for the variables from which |inst| will load data. 174 // The opcode of |inst| must be OpFunctionCall. 175 std::vector<uint32_t> GetLoadedVariablesFromFunctionCall( 176 const Instruction* inst); 177 178 // Returns the id of the variable from which |inst| will load data. |inst| 179 // must not be an OpFunctionCall. Returns 0 if no data is read or the 180 // variable cannot be determined. Note that in logical addressing mode the 181 // latter is not possible for function and private storage class because there 182 // cannot be variable pointers pointing to those storage classes. 183 uint32_t GetLoadedVariableFromNonFunctionCalls(Instruction* inst); 184 185 // Adds all decorations of |inst| to the work list. 186 void AddDecorationsToWorkList(const Instruction* inst); 187 188 // Adds DebugScope instruction associated with |inst| to the work list. 189 void AddDebugScopeToWorkList(const Instruction* inst); 190 191 // Adds all debug instruction associated with |inst| to the work list. 192 void AddDebugInstructionsToWorkList(const Instruction* inst); 193 194 // Marks all of the OpFunctionParameter instructions in |func| as live. 195 void MarkFunctionParameterAsLive(const Function* func); 196 197 // Returns the terminator instruction in the header for the innermost 198 // construct that contains |blk|. Returns nullptr if no such header exists. 199 Instruction* GetHeaderBranch(BasicBlock* blk); 200 201 // Returns the header for the innermost construct that contains |blk|. A loop 202 // header will be its own header. Returns nullptr if no such header exists. 203 BasicBlock* GetHeaderBlock(BasicBlock* blk) const; 204 205 // Returns the same as |GetHeaderBlock| except if |blk| is a loop header it 206 // will return the header of the next enclosing construct. Returns nullptr if 207 // no such header exists. 208 Instruction* GetBranchForNextHeader(BasicBlock* blk); 209 210 // Returns the merge instruction in the same basic block as |inst|. Returns 211 // nullptr if one does not exist. 212 Instruction* GetMergeInstruction(Instruction* inst); 213 214 // Returns true if |bb| is in the construct with header |header_block|. 215 bool BlockIsInConstruct(BasicBlock* header_block, BasicBlock* bb); 216 217 // Returns true if |func| is an entry point that does not have any function 218 // calls. 219 bool IsEntryPointWithNoCalls(Function* func); 220 221 // Returns true if |func| is an entry point. 222 bool IsEntryPoint(Function* func); 223 224 // Returns true if |func| contains a function call. 225 bool HasCall(Function* func); 226 227 // Marks the first block, which is the entry block, in |func| as live. 228 void MarkFirstBlockAsLive(Function* func); 229 230 // Adds an OpUnreachable instruction at the end of |block|. 231 void AddUnreachable(BasicBlock*& block); 232 233 // Marks the OpLoopMerge and the terminator in |basic_block| as live if 234 // |basic_block| is a loop header. 235 void MarkLoopConstructAsLiveIfLoopHeader(BasicBlock* basic_block); 236 237 // The cached results for |IsEntryPointWithNoCalls|. It maps the function's 238 // result id to the return value. 239 std::unordered_map<uint32_t, bool> entry_point_with_no_calls_cache_; 240 241 // Live Instruction Worklist. An instruction is added to this list 242 // if it might have a side effect, either directly or indirectly. 243 // If we don't know, then add it to this list. Instructions are 244 // removed from this list as the algorithm traces side effects, 245 // building up the live instructions set |live_insts_|. 246 std::queue<Instruction*> worklist_; 247 248 // Live Instructions 249 utils::BitVector live_insts_; 250 251 // Live Local Variables 252 std::unordered_set<uint32_t> live_local_vars_; 253 254 // List of instructions to delete. Deletion is delayed until debug and 255 // annotation instructions are processed. 256 std::vector<Instruction*> to_kill_; 257 258 // Extensions supported by this pass. 259 std::unordered_set<std::string> extensions_allowlist_; 260 }; 261 262 } // namespace opt 263 } // namespace spvtools 264 265 #endif // SOURCE_OPT_AGGRESSIVE_DEAD_CODE_ELIM_PASS_H_ 266