• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, SpvStorageClass 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   // Map from function's result id to function.
154   std::unordered_map<uint32_t, Function*> id2function_;
155 
156   // Map from block's label id to block. TODO(dnovillo): This is superfluous wrt
157   // CFG. It has functionality not present in CFG. Consolidate.
158   std::unordered_map<uint32_t, BasicBlock*> id2block_;
159 
160   // Set of ids of functions with early return.
161   std::set<uint32_t> early_return_funcs_;
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
167   std::set<uint32_t> inlinable_;
168 
169   // result id for OpConstantFalse
170   uint32_t false_id_;
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_;
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,
194       analysis::DebugInlinedAtContext* inlined_at_ctx,
195       std::unique_ptr<BasicBlock>* new_blk_ptr,
196       UptrVectorIterator<BasicBlock> callee_block_itr);
197 
198   // Inlines a single instruction of the callee function.
199   bool InlineSingleInstruction(
200       const std::unordered_map<uint32_t, uint32_t>& callee2caller,
201       BasicBlock* new_blk_ptr, const Instruction* inst,
202       uint32_t dbg_inlined_at);
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,
208       std::unique_ptr<BasicBlock> new_blk_ptr,
209       analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn,
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,
216       UptrVectorIterator<BasicBlock> callee_first_block,
217       analysis::DebugInlinedAtContext* inlined_at_ctx);
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,
224       std::unique_ptr<BasicBlock> new_blk_ptr,
225       analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn);
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);
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);
244 };
245 
246 }  // namespace opt
247 }  // namespace spvtools
248 
249 #endif  // SOURCE_OPT_INLINE_PASS_H_
250