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_INLINE_PASS_H_ 18 #define SOURCE_OPT_INLINE_PASS_H_ 19 20 #include <algorithm> 21 #include <list> 22 #include <memory> 23 #include <set> 24 #include <unordered_map> 25 #include <vector> 26 27 #include "source/opt/debug_info_manager.h" 28 #include "source/opt/decoration_manager.h" 29 #include "source/opt/module.h" 30 #include "source/opt/pass.h" 31 32 namespace spvtools { 33 namespace opt { 34 35 // See optimizer.hpp for documentation. 36 class InlinePass : public Pass { 37 using cbb_ptr = const BasicBlock*; 38 39 public: 40 virtual ~InlinePass() override = default; 41 42 protected: 43 InlinePass(); 44 45 // Add pointer to type to module and return resultId. Returns 0 if the type 46 // could not be created. 47 uint32_t AddPointerToType(uint32_t type_id, spv::StorageClass storage_class); 48 49 // Add unconditional branch to labelId to end of block block_ptr. 50 void AddBranch(uint32_t labelId, std::unique_ptr<BasicBlock>* block_ptr); 51 52 // Add conditional branch to end of block |block_ptr|. 53 void AddBranchCond(uint32_t cond_id, uint32_t true_id, uint32_t false_id, 54 std::unique_ptr<BasicBlock>* block_ptr); 55 56 // Add unconditional branch to labelId to end of block block_ptr. 57 void AddLoopMerge(uint32_t merge_id, uint32_t continue_id, 58 std::unique_ptr<BasicBlock>* block_ptr); 59 60 // Add store of valId to ptrId to end of block block_ptr. 61 void AddStore(uint32_t ptrId, uint32_t valId, 62 std::unique_ptr<BasicBlock>* block_ptr, 63 const Instruction* line_inst, const DebugScope& dbg_scope); 64 65 // Add load of ptrId into resultId to end of block block_ptr. 66 void AddLoad(uint32_t typeId, uint32_t resultId, uint32_t ptrId, 67 std::unique_ptr<BasicBlock>* block_ptr, 68 const Instruction* line_inst, const DebugScope& dbg_scope); 69 70 // Return new label. 71 std::unique_ptr<Instruction> NewLabel(uint32_t label_id); 72 73 // Returns the id for the boolean false value. Looks in the module first 74 // and creates it if not found. Remembers it for future calls. Returns 0 if 75 // the value could not be created. 76 uint32_t GetFalseId(); 77 78 // Map callee params to caller args 79 void MapParams(Function* calleeFn, BasicBlock::iterator call_inst_itr, 80 std::unordered_map<uint32_t, uint32_t>* callee2caller); 81 82 // Clone and map callee locals. Return true if successful. 83 bool CloneAndMapLocals(Function* calleeFn, 84 std::vector<std::unique_ptr<Instruction>>* new_vars, 85 std::unordered_map<uint32_t, uint32_t>* callee2caller, 86 analysis::DebugInlinedAtContext* inlined_at_ctx); 87 88 // Create return variable for callee clone code. The return type of 89 // |calleeFn| must not be void. Returns the id of the return variable if 90 // created. Returns 0 if the return variable could not be created. 91 uint32_t CreateReturnVar(Function* calleeFn, 92 std::vector<std::unique_ptr<Instruction>>* new_vars); 93 94 // Return true if instruction must be in the same block that its result 95 // is used. 96 bool IsSameBlockOp(const Instruction* inst) const; 97 98 // Clone operands which must be in same block as consumer instructions. 99 // Look in preCallSB for instructions that need cloning. Look in 100 // postCallSB for instructions already cloned. Add cloned instruction 101 // to postCallSB. 102 bool CloneSameBlockOps(std::unique_ptr<Instruction>* inst, 103 std::unordered_map<uint32_t, uint32_t>* postCallSB, 104 std::unordered_map<uint32_t, Instruction*>* preCallSB, 105 std::unique_ptr<BasicBlock>* block_ptr); 106 107 // Return in new_blocks the result of inlining the call at call_inst_itr 108 // within its block at call_block_itr. The block at call_block_itr can 109 // just be replaced with the blocks in new_blocks. Any additional branches 110 // are avoided. Debug instructions are cloned along with their callee 111 // instructions. Early returns are replaced by a store to a local return 112 // variable and a branch to a (created) exit block where the local variable 113 // is returned. Formal parameters are trivially mapped to their actual 114 // parameters. Note that the first block in new_blocks retains the label 115 // of the original calling block. Also note that if an exit block is 116 // created, it is the last block of new_blocks. 117 // 118 // Also return in new_vars additional OpVariable instructions required by 119 // and to be inserted into the caller function after the block at 120 // call_block_itr is replaced with new_blocks. 121 // 122 // Returns true if successful. 123 bool GenInlineCode(std::vector<std::unique_ptr<BasicBlock>>* new_blocks, 124 std::vector<std::unique_ptr<Instruction>>* new_vars, 125 BasicBlock::iterator call_inst_itr, 126 UptrVectorIterator<BasicBlock> call_block_itr); 127 128 // Return true if |inst| is a function call that can be inlined. 129 bool IsInlinableFunctionCall(const Instruction* inst); 130 131 // Return true if |func| has no return in a loop. The current analysis 132 // requires structured control flow, so return false if control flow not 133 // structured ie. module is not a shader. 134 bool HasNoReturnInLoop(Function* func); 135 136 // Find all functions with multiple returns and no returns in loops 137 void AnalyzeReturns(Function* func); 138 139 // Return true if |func| is a function that can be inlined. 140 bool IsInlinableFunction(Function* func); 141 142 // Returns true if |func| contains an abort instruction that is not an 143 // `OpUnreachable` instruction. 144 bool ContainsAbortOtherThanUnreachable(Function* func) const; 145 146 // Update phis in succeeding blocks to point to new last block 147 void UpdateSucceedingPhis( 148 std::vector<std::unique_ptr<BasicBlock>>& new_blocks); 149 150 // Initialize state for optimization of |module| 151 void InitializeInline(); 152 153 // Fixes invalid debug declare functions in `func` that were caused by 154 // inlining. This function cannot be called while in the middle of inlining 155 // because it needs to be able to find the instructions that define an 156 // id. 157 void FixDebugDeclares(Function* func); 158 159 // Map from function's result id to function. 160 std::unordered_map<uint32_t, Function*> id2function_; 161 162 // Map from block's label id to block. TODO(dnovillo): This is superfluous wrt 163 // CFG. It has functionality not present in CFG. Consolidate. 164 std::unordered_map<uint32_t, BasicBlock*> id2block_; 165 166 // Set of ids of functions with early return. 167 std::set<uint32_t> early_return_funcs_; 168 169 // Set of ids of functions with no returns in loop 170 std::set<uint32_t> no_return_in_loop_; 171 172 // Set of ids of inlinable functions 173 std::set<uint32_t> inlinable_; 174 175 // result id for OpConstantFalse 176 uint32_t false_id_; 177 178 // Set of functions that are originally called directly or indirectly from a 179 // continue construct. 180 std::unordered_set<uint32_t> funcs_called_from_continue_; 181 182 private: 183 // Moves instructions of the caller function up to the call instruction 184 // to |new_blk_ptr|. 185 void MoveInstsBeforeEntryBlock( 186 std::unordered_map<uint32_t, Instruction*>* preCallSB, 187 BasicBlock* new_blk_ptr, BasicBlock::iterator call_inst_itr, 188 UptrVectorIterator<BasicBlock> call_block_itr); 189 190 // Returns a new guard block after adding a branch to the end of 191 // |new_blocks|. 192 std::unique_ptr<BasicBlock> AddGuardBlock( 193 std::vector<std::unique_ptr<BasicBlock>>* new_blocks, 194 std::unordered_map<uint32_t, uint32_t>* callee2caller, 195 std::unique_ptr<BasicBlock> new_blk_ptr, uint32_t entry_blk_label_id); 196 197 // Add store instructions for initializers of variables. 198 InstructionList::iterator AddStoresForVariableInitializers( 199 const std::unordered_map<uint32_t, uint32_t>& callee2caller, 200 analysis::DebugInlinedAtContext* inlined_at_ctx, 201 std::unique_ptr<BasicBlock>* new_blk_ptr, 202 UptrVectorIterator<BasicBlock> callee_block_itr); 203 204 // Inlines a single instruction of the callee function. 205 bool InlineSingleInstruction( 206 const std::unordered_map<uint32_t, uint32_t>& callee2caller, 207 BasicBlock* new_blk_ptr, const Instruction* inst, 208 uint32_t dbg_inlined_at); 209 210 // Inlines the return instruction of the callee function. 211 std::unique_ptr<BasicBlock> InlineReturn( 212 const std::unordered_map<uint32_t, uint32_t>& callee2caller, 213 std::vector<std::unique_ptr<BasicBlock>>* new_blocks, 214 std::unique_ptr<BasicBlock> new_blk_ptr, 215 analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn, 216 const Instruction* inst, uint32_t returnVarId); 217 218 // Inlines the entry block of the callee function. 219 bool InlineEntryBlock( 220 const std::unordered_map<uint32_t, uint32_t>& callee2caller, 221 std::unique_ptr<BasicBlock>* new_blk_ptr, 222 UptrVectorIterator<BasicBlock> callee_first_block, 223 analysis::DebugInlinedAtContext* inlined_at_ctx); 224 225 // Inlines basic blocks of the callee function other than the entry basic 226 // block. 227 std::unique_ptr<BasicBlock> InlineBasicBlocks( 228 std::vector<std::unique_ptr<BasicBlock>>* new_blocks, 229 const std::unordered_map<uint32_t, uint32_t>& callee2caller, 230 std::unique_ptr<BasicBlock> new_blk_ptr, 231 analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn); 232 233 // Moves instructions of the caller function after the call instruction 234 // to |new_blk_ptr|. 235 bool MoveCallerInstsAfterFunctionCall( 236 std::unordered_map<uint32_t, Instruction*>* preCallSB, 237 std::unordered_map<uint32_t, uint32_t>* postCallSB, 238 std::unique_ptr<BasicBlock>* new_blk_ptr, 239 BasicBlock::iterator call_inst_itr, bool multiBlocks); 240 241 // Move the OpLoopMerge from the last block back to the first. 242 void MoveLoopMergeInstToFirstBlock( 243 std::vector<std::unique_ptr<BasicBlock>>* new_blocks); 244 245 // Update the structure of single block loops so that the inlined code ends 246 // up in the loop construct and a new continue target is added to satisfy 247 // structural dominance. 248 void UpdateSingleBlockLoopContinueTarget( 249 uint32_t new_id, std::vector<std::unique_ptr<BasicBlock>>* new_blocks); 250 251 // Replaces the `var` operand of `dbg_declare_inst` and updates the indexes 252 // accordingly, if it is the id of an access chain in `access_chains`. 253 void FixDebugDeclare(Instruction* dbg_declare_inst, 254 const std::map<uint32_t, Instruction*>& access_chains); 255 }; 256 257 } // namespace opt 258 } // namespace spvtools 259 260 #endif // SOURCE_OPT_INLINE_PASS_H_ 261