• 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 LIBSPIRV_OPT_LOCAL_SSA_ELIM_PASS_H_
18 #define LIBSPIRV_OPT_LOCAL_SSA_ELIM_PASS_H_
19 
20 
21 #include <algorithm>
22 #include <map>
23 #include <queue>
24 #include <utility>
25 #include <unordered_map>
26 #include <unordered_set>
27 
28 #include "basic_block.h"
29 #include "def_use_manager.h"
30 #include "module.h"
31 #include "pass.h"
32 
33 namespace spvtools {
34 namespace opt {
35 
36 // See optimizer.hpp for documentation.
37 class LocalMultiStoreElimPass : public Pass {
38   using cbb_ptr = const ir::BasicBlock*;
39 
40  public:
41    using GetBlocksFunction =
42      std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
43 
44   LocalMultiStoreElimPass();
name()45   const char* name() const override { return "eliminate-local-multi-store"; }
46   Status Process(ir::Module*) override;
47 
48  private:
49   // Returns true if |opcode| is a non-ptr access chain op
50   bool IsNonPtrAccessChain(const SpvOp opcode) const;
51 
52   // Returns true if |typeInst| is a scalar type
53   // or a vector or matrix
54   bool IsMathType(const ir::Instruction* typeInst) const;
55 
56   // Returns true if |typeInst| is a math type or a struct or array
57   // of a math type.
58   bool IsTargetType(const ir::Instruction* typeInst) const;
59 
60   // Given a load or store |ip|, return the pointer instruction.
61   // Also return the base variable's id in |varId|.
62   ir::Instruction* GetPtr(ir::Instruction* ip, uint32_t* varId);
63 
64   // Return true if |varId| is a previously identified target variable.
65   // Return false if |varId| is a previously identified non-target variable.
66   // If variable is not cached, return true if variable is a function scope
67   // variable of target type, false otherwise. Updates caches of target
68   // and non-target variables.
69   bool IsTargetVar(uint32_t varId);
70 
71   // Return type id for |ptrInst|'s pointee
72   uint32_t GetPointeeTypeId(const ir::Instruction* ptrInst) const;
73 
74   // Replace all instances of |loadInst|'s id with |replId| and delete
75   // |loadInst|.
76   void ReplaceAndDeleteLoad(ir::Instruction* loadInst, uint32_t replId);
77 
78   // Return true if any instruction loads from |ptrId|
79   bool HasLoads(uint32_t ptrId) const;
80 
81   // Return true if |varId| is not a function variable or if it has
82   // a load
83   bool IsLiveVar(uint32_t varId) const;
84 
85   // Add stores using |ptr_id| to |insts|
86   void AddStores(uint32_t ptr_id, std::queue<ir::Instruction*>* insts);
87 
88   // Delete |inst| and iterate DCE on all its operands. Won't delete
89   // labels.
90   void DCEInst(ir::Instruction* inst);
91 
92   // Return true if all uses of |varId| are only through supported reference
93   // operations ie. loads and store. Also cache in supported_ref_vars_;
94   bool HasOnlySupportedRefs(uint32_t varId);
95 
96   // Return true if all uses of |id| are only name or decorate ops.
97   bool HasOnlyNamesAndDecorates(uint32_t id) const;
98 
99   // Kill all name and decorate ops using |inst|
100   void KillNamesAndDecorates(ir::Instruction* inst);
101 
102   // Kill all name and decorate ops using |id|
103   void KillNamesAndDecorates(uint32_t id);
104 
105   // Initialize data structures used by EliminateLocalMultiStore for
106   // function |func|, specifically block predecessors and target variables.
107   void InitSSARewrite(ir::Function& func);
108 
109   // Returns the id of the merge block declared by a merge instruction in
110   // this block, if any.  If none, returns zero.
111   uint32_t MergeBlockIdIfAny(const ir::BasicBlock& blk, uint32_t* cbid);
112 
113   // Compute structured successors for function |func|.
114   // A block's structured successors are the blocks it branches to
115   // together with its declared merge block if it has one.
116   // When order matters, the merge block always appears first.
117   // This assures correct depth first search in the presence of early
118   // returns and kills. If the successor vector contain duplicates
119   // if the merge block, they are safely ignored by DFS.
120   void ComputeStructuredSuccessors(ir::Function* func);
121 
122   // Compute structured block order for |func| into |structuredOrder|. This
123   // order has the property that dominators come before all blocks they
124   // dominate and merge blocks come after all blocks that are in the control
125   // constructs of their header.
126   void ComputeStructuredOrder(ir::Function* func,
127       std::list<ir::BasicBlock*>* order);
128 
129   // Return true if loop header block
130   bool IsLoopHeader(ir::BasicBlock* block_ptr) const;
131 
132   // Initialize label2ssa_map_ entry for block |block_ptr| with single
133   // predecessor.
134   void SSABlockInitSinglePred(ir::BasicBlock* block_ptr);
135 
136   // Return true if variable is loaded in block with |label| or in
137   // any succeeding block in structured order.
138   bool IsLiveAfter(uint32_t var_id, uint32_t label) const;
139 
140   // Initialize label2ssa_map_ entry for loop header block pointed to
141   // |block_itr| by merging entries from all predecessors. If any value
142   // ids differ for any variable across predecessors, create a phi function
143   // in the block and use that value id for the variable in the new map.
144   // Assumes all predecessors have been visited by EliminateLocalMultiStore
145   // except the back edge. Use a dummy value in the phi for the back edge
146   // until the back edge block is visited and patch the phi value then.
147   void SSABlockInitLoopHeader(std::list<ir::BasicBlock*>::iterator block_itr);
148 
149   // Initialize label2ssa_map_ entry for multiple predecessor block
150   // |block_ptr| by merging label2ssa_map_ entries for all predecessors.
151   // If any value ids differ for any variable across predecessors, create
152   // a phi function in the block and use that value id for the variable in
153   // the new map. Assumes all predecessors have been visited by
154   // EliminateLocalMultiStore.
155   void SSABlockInitMultiPred(ir::BasicBlock* block_ptr);
156 
157   // Initialize the label2ssa_map entry for a block pointed to by |block_itr|.
158   // Insert phi instructions into block when necessary. All predecessor
159   // blocks must have been visited by EliminateLocalMultiStore except for
160   // backedges.
161   void SSABlockInit(std::list<ir::BasicBlock*>::iterator block_itr);
162 
163   // Return undef in function for type. Create and insert an undef after the
164   // first non-variable in the function if it doesn't already exist. Add
165   // undef to function undef map.
166   uint32_t Type2Undef(uint32_t type_id);
167 
168   // Patch phis in loop header block now that the map is complete for the
169   // backedge predecessor. Specifically, for each phi, find the value
170   // corresponding to the backedge predecessor. That contains the variable id
171   // that this phi corresponds to. Change this phi operand to the the value
172   // which corresponds to that variable in the predecessor map.
173   void PatchPhis(uint32_t header_id, uint32_t back_id);
174 
175   // Return true if all extensions in this module are allowed by this pass.
176   // Currently, no extensions are supported.
177   // TODO(greg-lunarg): Add extensions to supported list.
178   bool AllExtensionsSupported() const;
179 
180   // Remove remaining loads and stores of function scope variables only
181   // referenced with non-access-chain loads and stores from function |func|.
182   // Insert Phi functions where necessary. Running LocalAccessChainRemoval,
183   // SingleBlockLocalElim and SingleStoreLocalElim beforehand will improve
184   // the runtime and effectiveness of this function.
185   bool EliminateMultiStoreLocal(ir::Function* func);
186 
187   // Return true if all uses of varId are only through supported reference
188   // operations ie. loads and store. Also cache in supported_ref_vars_;
IsDecorate(uint32_t op)189   inline bool IsDecorate(uint32_t op) const {
190     return (op == SpvOpDecorate || op == SpvOpDecorateId);
191   }
192 
193   // Save next available id into |module|.
FinalizeNextId(ir::Module * module)194   inline void FinalizeNextId(ir::Module* module) {
195     module->SetIdBound(next_id_);
196   }
197 
198   // Return next available id and calculate next.
TakeNextId()199   inline uint32_t TakeNextId() {
200     return next_id_++;
201   }
202 
203   void Initialize(ir::Module* module);
204   Pass::Status ProcessImpl();
205 
206   // Module this pass is processing
207   ir::Module* module_;
208 
209   // Def-Uses for the module we are processing
210   std::unique_ptr<analysis::DefUseManager> def_use_mgr_;
211 
212   // Map from function's result id to function
213   std::unordered_map<uint32_t, ir::Function*> id2function_;
214 
215   // Map from block's label id to block.
216   std::unordered_map<uint32_t, ir::BasicBlock*> id2block_;
217 
218   // Cache of previously seen target types
219   std::unordered_set<uint32_t> seen_target_vars_;
220 
221   // Cache of previously seen non-target types
222   std::unordered_set<uint32_t> seen_non_target_vars_;
223 
224   // Set of label ids of visited blocks
225   std::unordered_set<uint32_t> visitedBlocks_;
226 
227   // Map from type to undef
228   std::unordered_map<uint32_t, uint32_t> type2undefs_;
229 
230   // Variables that are only referenced by supported operations for this
231   // pass ie. loads and stores.
232   std::unordered_set<uint32_t> supported_ref_vars_;
233 
234   // Map from block to its structured successor blocks. See
235   // ComputeStructuredSuccessors() for definition.
236   std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
237       block2structured_succs_;
238 
239   // Map from block's label id to its predecessor blocks ids
240   std::unordered_map<uint32_t, std::vector<uint32_t>> label2preds_;
241 
242   // Map from block's label id to a map of a variable to its value at the
243   // end of the block.
244   std::unordered_map<uint32_t, std::unordered_map<uint32_t, uint32_t>>
245       label2ssa_map_;
246 
247   // Extra block whose successors are all blocks with no predecessors
248   // in function.
249   ir::BasicBlock pseudo_entry_block_;
250 
251   // Next unused ID
252   uint32_t next_id_;
253 };
254 
255 }  // namespace opt
256 }  // namespace spvtools
257 
258 #endif  // LIBSPIRV_OPT_LOCAL_SSA_ELIM_PASS_H_
259 
260