Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 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 | |
dan sinclair | 58a6876 | 2018-08-03 08:05:33 -0400 | [diff] [blame] | 17 | #ifndef SOURCE_OPT_INLINE_PASS_H_ |
| 18 | #define SOURCE_OPT_INLINE_PASS_H_ |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 19 | |
| 20 | #include <algorithm> |
Diego Novillo | d2938e4 | 2017-11-08 12:40:02 -0500 | [diff] [blame] | 21 | #include <list> |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 22 | #include <memory> |
dan sinclair | eda2cfb | 2018-08-03 15:06:09 -0400 | [diff] [blame] | 23 | #include <set> |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 24 | #include <unordered_map> |
| 25 | #include <vector> |
| 26 | |
Jaebaek Seo | 50b1557 | 2020-05-21 13:09:43 -0400 | [diff] [blame] | 27 | #include "source/opt/debug_info_manager.h" |
dan sinclair | eda2cfb | 2018-08-03 15:06:09 -0400 | [diff] [blame] | 28 | #include "source/opt/decoration_manager.h" |
| 29 | #include "source/opt/module.h" |
| 30 | #include "source/opt/pass.h" |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 31 | |
| 32 | namespace spvtools { |
| 33 | namespace opt { |
| 34 | |
| 35 | // See optimizer.hpp for documentation. |
| 36 | class InlinePass : public Pass { |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 37 | using cbb_ptr = const BasicBlock*; |
Greg Fischer | bba812f | 2017-05-04 20:55:53 -0600 | [diff] [blame] | 38 | |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 39 | public: |
Corentin Wallez | 4a59fd4 | 2021-03-09 13:16:43 +0000 | [diff] [blame] | 40 | virtual ~InlinePass() override = default; |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 41 | |
GregF | e28bd39 | 2017-08-01 17:20:13 -0600 | [diff] [blame] | 42 | protected: |
dan sinclair | f96b7f1 | 2018-07-12 09:08:45 -0400 | [diff] [blame] | 43 | InlinePass(); |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 44 | |
Steven Perron | acd2781 | 2018-12-18 19:34:03 +0000 | [diff] [blame] | 45 | // Add pointer to type to module and return resultId. Returns 0 if the type |
| 46 | // could not be created. |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 47 | uint32_t AddPointerToType(uint32_t type_id, SpvStorageClass storage_class); |
| 48 | |
| 49 | // Add unconditional branch to labelId to end of block block_ptr. |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 50 | void AddBranch(uint32_t labelId, std::unique_ptr<BasicBlock>* block_ptr); |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 51 | |
Greg Fischer | bba812f | 2017-05-04 20:55:53 -0600 | [diff] [blame] | 52 | // Add conditional branch to end of block |block_ptr|. |
Diego Novillo | d2938e4 | 2017-11-08 12:40:02 -0500 | [diff] [blame] | 53 | void AddBranchCond(uint32_t cond_id, uint32_t true_id, uint32_t false_id, |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 54 | std::unique_ptr<BasicBlock>* block_ptr); |
Greg Fischer | bba812f | 2017-05-04 20:55:53 -0600 | [diff] [blame] | 55 | |
| 56 | // Add unconditional branch to labelId to end of block block_ptr. |
| 57 | void AddLoopMerge(uint32_t merge_id, uint32_t continue_id, |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 58 | std::unique_ptr<BasicBlock>* block_ptr); |
Greg Fischer | bba812f | 2017-05-04 20:55:53 -0600 | [diff] [blame] | 59 | |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 60 | // Add store of valId to ptrId to end of block block_ptr. |
| 61 | void AddStore(uint32_t ptrId, uint32_t valId, |
Jaebaek Seo | 50b1557 | 2020-05-21 13:09:43 -0400 | [diff] [blame] | 62 | std::unique_ptr<BasicBlock>* block_ptr, |
| 63 | const Instruction* line_inst, const DebugScope& dbg_scope); |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 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, |
Jaebaek Seo | 50b1557 | 2020-05-21 13:09:43 -0400 | [diff] [blame] | 67 | std::unique_ptr<BasicBlock>* block_ptr, |
| 68 | const Instruction* line_inst, const DebugScope& dbg_scope); |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 69 | |
| 70 | // Return new label. |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 71 | std::unique_ptr<Instruction> NewLabel(uint32_t label_id); |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 72 | |
Greg Fischer | bba812f | 2017-05-04 20:55:53 -0600 | [diff] [blame] | 73 | // Returns the id for the boolean false value. Looks in the module first |
Steven Perron | acd2781 | 2018-12-18 19:34:03 +0000 | [diff] [blame] | 74 | // and creates it if not found. Remembers it for future calls. Returns 0 if |
| 75 | // the value could not be created. |
Greg Fischer | bba812f | 2017-05-04 20:55:53 -0600 | [diff] [blame] | 76 | uint32_t GetFalseId(); |
| 77 | |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 78 | // Map callee params to caller args |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 79 | void MapParams(Function* calleeFn, BasicBlock::iterator call_inst_itr, |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 80 | std::unordered_map<uint32_t, uint32_t>* callee2caller); |
| 81 | |
Steven Perron | acd2781 | 2018-12-18 19:34:03 +0000 | [diff] [blame] | 82 | // Clone and map callee locals. Return true if successful. |
| 83 | bool CloneAndMapLocals(Function* calleeFn, |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 84 | std::vector<std::unique_ptr<Instruction>>* new_vars, |
Jaebaek Seo | 50b1557 | 2020-05-21 13:09:43 -0400 | [diff] [blame] | 85 | std::unordered_map<uint32_t, uint32_t>* callee2caller, |
| 86 | analysis::DebugInlinedAtContext* inlined_at_ctx); |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 87 | |
Steven Perron | acd2781 | 2018-12-18 19:34:03 +0000 | [diff] [blame] | 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. |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 91 | uint32_t CreateReturnVar(Function* calleeFn, |
| 92 | std::vector<std::unique_ptr<Instruction>>* new_vars); |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 93 | |
| 94 | // Return true if instruction must be in the same block that its result |
| 95 | // is used. |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 96 | bool IsSameBlockOp(const Instruction* inst) const; |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 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. |
Steven Perron | acd2781 | 2018-12-18 19:34:03 +0000 | [diff] [blame] | 102 | bool CloneSameBlockOps(std::unique_ptr<Instruction>* inst, |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 103 | std::unordered_map<uint32_t, uint32_t>* postCallSB, |
| 104 | std::unordered_map<uint32_t, Instruction*>* preCallSB, |
| 105 | std::unique_ptr<BasicBlock>* block_ptr); |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 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 |
David Neto | ceb1d4f | 2017-03-31 10:36:58 -0400 | [diff] [blame] | 116 | // created, it is the last block of new_blocks. |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 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. |
Steven Perron | acd2781 | 2018-12-18 19:34:03 +0000 | [diff] [blame] | 121 | // |
| 122 | // Returns true if successful. |
| 123 | bool GenInlineCode(std::vector<std::unique_ptr<BasicBlock>>* new_blocks, |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 124 | std::vector<std::unique_ptr<Instruction>>* new_vars, |
| 125 | BasicBlock::iterator call_inst_itr, |
| 126 | UptrVectorIterator<BasicBlock> call_block_itr); |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 127 | |
Greg Fischer | bba812f | 2017-05-04 20:55:53 -0600 | [diff] [blame] | 128 | // Return true if |inst| is a function call that can be inlined. |
Steven Perron | aa9e8f5 | 2019-07-17 14:59:05 -0400 | [diff] [blame] | 129 | bool IsInlinableFunctionCall(const Instruction* inst); |
David Neto | ceb1d4f | 2017-03-31 10:36:58 -0400 | [diff] [blame] | 130 | |
Greg Fischer | bba812f | 2017-05-04 20:55:53 -0600 | [diff] [blame] | 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. |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 134 | bool HasNoReturnInLoop(Function* func); |
Greg Fischer | bba812f | 2017-05-04 20:55:53 -0600 | [diff] [blame] | 135 | |
| 136 | // Find all functions with multiple returns and no returns in loops |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 137 | void AnalyzeReturns(Function* func); |
Greg Fischer | bba812f | 2017-05-04 20:55:53 -0600 | [diff] [blame] | 138 | |
| 139 | // Return true if |func| is a function that can be inlined. |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 140 | bool IsInlinableFunction(Function* func); |
GregF | a107d34 | 2017-04-25 13:57:20 -0600 | [diff] [blame] | 141 | |
Steven Perron | f98473c | 2022-09-21 16:10:58 -0400 | [diff] [blame^] | 142 | // Returns true if |func| contains an abort instruction that is not an |
| 143 | // `OpUnreachable` instruction. |
| 144 | bool ContainsAbortOtherThanUnreachable(Function* func) const; |
Steven Perron | c18c9ff | 2019-10-04 13:05:32 -0400 | [diff] [blame] | 145 | |
GregF | e28bd39 | 2017-08-01 17:20:13 -0600 | [diff] [blame] | 146 | // Update phis in succeeding blocks to point to new last block |
| 147 | void UpdateSucceedingPhis( |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 148 | std::vector<std::unique_ptr<BasicBlock>>& new_blocks); |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 149 | |
GregF | 429ca05 | 2017-08-15 17:58:28 -0600 | [diff] [blame] | 150 | // Initialize state for optimization of |module| |
dan sinclair | f96b7f1 | 2018-07-12 09:08:45 -0400 | [diff] [blame] | 151 | void InitializeInline(); |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 152 | |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 153 | // Map from function's result id to function. |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 154 | std::unordered_map<uint32_t, Function*> id2function_; |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 155 | |
Diego Novillo | fef669f | 2017-10-30 17:42:26 -0400 | [diff] [blame] | 156 | // Map from block's label id to block. TODO(dnovillo): This is superfluous wrt |
dan sinclair | c7da51a | 2018-07-12 15:14:43 -0400 | [diff] [blame] | 157 | // CFG. It has functionality not present in CFG. Consolidate. |
| 158 | std::unordered_map<uint32_t, BasicBlock*> id2block_; |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 159 | |
greg-lunarg | 6721478 | 2018-11-08 07:11:20 -0700 | [diff] [blame] | 160 | // Set of ids of functions with early return. |
| 161 | std::set<uint32_t> early_return_funcs_; |
Greg Fischer | bba812f | 2017-05-04 20:55:53 -0600 | [diff] [blame] | 162 | |
| 163 | // Set of ids of functions with no returns in loop |
| 164 | std::set<uint32_t> no_return_in_loop_; |
| 165 | |
| 166 | // Set of ids of inlinable functions |
GregF | a107d34 | 2017-04-25 13:57:20 -0600 | [diff] [blame] | 167 | std::set<uint32_t> inlinable_; |
| 168 | |
Greg Fischer | bba812f | 2017-05-04 20:55:53 -0600 | [diff] [blame] | 169 | // result id for OpConstantFalse |
| 170 | uint32_t false_id_; |
Steven Perron | c18c9ff | 2019-10-04 13:05:32 -0400 | [diff] [blame] | 171 | |
| 172 | // Set of functions that are originally called directly or indirectly from a |
| 173 | // continue construct. |
| 174 | std::unordered_set<uint32_t> funcs_called_from_continue_; |
Steven Perron | bd0a2da | 2020-05-14 10:55:47 -0400 | [diff] [blame] | 175 | |
| 176 | private: |
| 177 | // Moves instructions of the caller function up to the call instruction |
| 178 | // to |new_blk_ptr|. |
| 179 | void MoveInstsBeforeEntryBlock( |
| 180 | std::unordered_map<uint32_t, Instruction*>* preCallSB, |
| 181 | BasicBlock* new_blk_ptr, BasicBlock::iterator call_inst_itr, |
| 182 | UptrVectorIterator<BasicBlock> call_block_itr); |
| 183 | |
| 184 | // Returns a new guard block after adding a branch to the end of |
| 185 | // |new_blocks|. |
| 186 | std::unique_ptr<BasicBlock> AddGuardBlock( |
| 187 | std::vector<std::unique_ptr<BasicBlock>>* new_blocks, |
| 188 | std::unordered_map<uint32_t, uint32_t>* callee2caller, |
| 189 | std::unique_ptr<BasicBlock> new_blk_ptr, uint32_t entry_blk_label_id); |
| 190 | |
| 191 | // Add store instructions for initializers of variables. |
| 192 | InstructionList::iterator AddStoresForVariableInitializers( |
| 193 | const std::unordered_map<uint32_t, uint32_t>& callee2caller, |
Jaebaek Seo | 50b1557 | 2020-05-21 13:09:43 -0400 | [diff] [blame] | 194 | analysis::DebugInlinedAtContext* inlined_at_ctx, |
Steven Perron | bd0a2da | 2020-05-14 10:55:47 -0400 | [diff] [blame] | 195 | std::unique_ptr<BasicBlock>* new_blk_ptr, |
| 196 | UptrVectorIterator<BasicBlock> callee_block_itr); |
| 197 | |
| 198 | // Inlines a single instruction of the callee function. |
Jaebaek Seo | 50b1557 | 2020-05-21 13:09:43 -0400 | [diff] [blame] | 199 | bool InlineSingleInstruction( |
Steven Perron | bd0a2da | 2020-05-14 10:55:47 -0400 | [diff] [blame] | 200 | const std::unordered_map<uint32_t, uint32_t>& callee2caller, |
Jaebaek Seo | 50b1557 | 2020-05-21 13:09:43 -0400 | [diff] [blame] | 201 | BasicBlock* new_blk_ptr, const Instruction* inst, |
| 202 | uint32_t dbg_inlined_at); |
Steven Perron | bd0a2da | 2020-05-14 10:55:47 -0400 | [diff] [blame] | 203 | |
| 204 | // Inlines the return instruction of the callee function. |
| 205 | std::unique_ptr<BasicBlock> InlineReturn( |
| 206 | const std::unordered_map<uint32_t, uint32_t>& callee2caller, |
| 207 | std::vector<std::unique_ptr<BasicBlock>>* new_blocks, |
Jaebaek Seo | 50b1557 | 2020-05-21 13:09:43 -0400 | [diff] [blame] | 208 | std::unique_ptr<BasicBlock> new_blk_ptr, |
| 209 | analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn, |
Steven Perron | bd0a2da | 2020-05-14 10:55:47 -0400 | [diff] [blame] | 210 | const Instruction* inst, uint32_t returnVarId); |
| 211 | |
| 212 | // Inlines the entry block of the callee function. |
| 213 | bool InlineEntryBlock( |
| 214 | const std::unordered_map<uint32_t, uint32_t>& callee2caller, |
| 215 | std::unique_ptr<BasicBlock>* new_blk_ptr, |
Jaebaek Seo | 50b1557 | 2020-05-21 13:09:43 -0400 | [diff] [blame] | 216 | UptrVectorIterator<BasicBlock> callee_first_block, |
| 217 | analysis::DebugInlinedAtContext* inlined_at_ctx); |
Steven Perron | bd0a2da | 2020-05-14 10:55:47 -0400 | [diff] [blame] | 218 | |
| 219 | // Inlines basic blocks of the callee function other than the entry basic |
| 220 | // block. |
| 221 | std::unique_ptr<BasicBlock> InlineBasicBlocks( |
| 222 | std::vector<std::unique_ptr<BasicBlock>>* new_blocks, |
| 223 | const std::unordered_map<uint32_t, uint32_t>& callee2caller, |
Jaebaek Seo | 50b1557 | 2020-05-21 13:09:43 -0400 | [diff] [blame] | 224 | std::unique_ptr<BasicBlock> new_blk_ptr, |
| 225 | analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn); |
Steven Perron | bd0a2da | 2020-05-14 10:55:47 -0400 | [diff] [blame] | 226 | |
| 227 | // Moves instructions of the caller function after the call instruction |
| 228 | // to |new_blk_ptr|. |
| 229 | bool MoveCallerInstsAfterFunctionCall( |
| 230 | std::unordered_map<uint32_t, Instruction*>* preCallSB, |
| 231 | std::unordered_map<uint32_t, uint32_t>* postCallSB, |
| 232 | std::unique_ptr<BasicBlock>* new_blk_ptr, |
| 233 | BasicBlock::iterator call_inst_itr, bool multiBlocks); |
| 234 | |
| 235 | // Move the OpLoopMerge from the last block back to the first. |
| 236 | void MoveLoopMergeInstToFirstBlock( |
| 237 | std::vector<std::unique_ptr<BasicBlock>>* new_blocks); |
alan-baker | 286e9c1 | 2022-06-29 23:32:20 -0400 | [diff] [blame] | 238 | |
| 239 | // Update the structure of single block loops so that the inlined code ends |
| 240 | // up in the loop construct and a new continue target is added to satisfy |
| 241 | // structural dominance. |
| 242 | void UpdateSingleBlockLoopContinueTarget( |
| 243 | uint32_t new_id, std::vector<std::unique_ptr<BasicBlock>>* new_blocks); |
Greg Fischer | 04fcc66 | 2016-11-10 10:11:50 -0700 | [diff] [blame] | 244 | }; |
| 245 | |
| 246 | } // namespace opt |
| 247 | } // namespace spvtools |
| 248 | |
dan sinclair | 58a6876 | 2018-08-03 08:05:33 -0400 | [diff] [blame] | 249 | #endif // SOURCE_OPT_INLINE_PASS_H_ |