/src/spirv-tools/source/opt/interface_var_sroa.h
Line | Count | Source |
1 | | // Copyright (c) 2022 Google LLC |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #ifndef SOURCE_OPT_INTERFACE_VAR_SROA_H_ |
16 | | #define SOURCE_OPT_INTERFACE_VAR_SROA_H_ |
17 | | |
18 | | #include <optional> |
19 | | #include <unordered_set> |
20 | | |
21 | | #include "source/opt/pass.h" |
22 | | |
23 | | namespace spvtools { |
24 | | namespace opt { |
25 | | |
26 | | // See optimizer.hpp for documentation. |
27 | | // |
28 | | // Note that the current implementation of this pass covers only store, load, |
29 | | // access chain instructions for the interface variables. Supporting other types |
30 | | // of instructions is a future work. |
31 | | class InterfaceVariableScalarReplacement : public Pass { |
32 | | public: |
33 | 0 | InterfaceVariableScalarReplacement() {} |
34 | | |
35 | 0 | const char* name() const override { |
36 | 0 | return "interface-variable-scalar-replacement"; |
37 | 0 | } |
38 | | Status Process() override; |
39 | | |
40 | 0 | IRContext::Analysis GetPreservedAnalyses() override { |
41 | 0 | return IRContext::kAnalysisDecorations | IRContext::kAnalysisDefUse | |
42 | 0 | IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; |
43 | 0 | } |
44 | | |
45 | | private: |
46 | | // A struct containing components of a composite variable. If the composite |
47 | | // consists of multiple or recursive components, |component_variable| is |
48 | | // nullptr and |nested_composite_components| keeps the components. If it has a |
49 | | // single component, |nested_composite_components| is empty and |
50 | | // |component_variable| is the component. Note that each element of |
51 | | // |nested_composite_components| has the NestedCompositeComponents struct as |
52 | | // its type that can recursively keep the components. |
53 | | struct NestedCompositeComponents { |
54 | 0 | NestedCompositeComponents() : component_variable(nullptr) {} |
55 | | |
56 | 0 | bool HasMultipleComponents() const { |
57 | 0 | return !nested_composite_components.empty(); |
58 | 0 | } |
59 | | |
60 | 0 | const std::vector<NestedCompositeComponents>& GetComponents() const { |
61 | 0 | return nested_composite_components; |
62 | 0 | } |
63 | | |
64 | 0 | void AddComponent(const NestedCompositeComponents& component) { |
65 | 0 | nested_composite_components.push_back(component); |
66 | 0 | } |
67 | | |
68 | 0 | Instruction* GetComponentVariable() const { return component_variable; } |
69 | | |
70 | 0 | void SetSingleComponentVariable(Instruction* var) { |
71 | 0 | component_variable = var; |
72 | 0 | } |
73 | | |
74 | | private: |
75 | | std::vector<NestedCompositeComponents> nested_composite_components; |
76 | | Instruction* component_variable; |
77 | | }; |
78 | | |
79 | | // Collects all interface variables used by the |entry_point|. |
80 | | std::vector<Instruction*> CollectInterfaceVariables(Instruction& entry_point); |
81 | | |
82 | | // Returns whether |var| has the extra arrayness for the entry point |
83 | | // |entry_point| or not. |
84 | | bool HasExtraArrayness(Instruction& entry_point, Instruction* var); |
85 | | |
86 | | // Finds a Location BuiltIn decoration of |var| and returns it via |
87 | | // |location|. Returns true whether the location exists or not. |
88 | | bool GetVariableLocation(Instruction* var, uint32_t* location); |
89 | | |
90 | | // Finds a Component BuiltIn decoration of |var| and returns it via |
91 | | // |component|. Returns true whether the component exists or not. |
92 | | bool GetVariableComponent(Instruction* var, uint32_t* component); |
93 | | |
94 | | // Returns the type of |var| as an instruction. |
95 | | Instruction* GetTypeOfVariable(Instruction* var); |
96 | | |
97 | | // Replaces an interface variable |interface_var| whose type is |
98 | | // |interface_var_type| with scalars and returns whether it succeeds or not. |
99 | | // |location| is the value of Location Decoration for |interface_var|. |
100 | | // |component| is the value of Component Decoration for |interface_var|. |
101 | | // If |extra_array_length| is 0, it means |interface_var| has a Patch |
102 | | // decoration. Otherwise, |extra_array_length| denotes the length of the extra |
103 | | // array of |interface_var|. |
104 | | Status ReplaceInterfaceVariableWithScalars(Instruction* interface_var, |
105 | | Instruction* interface_var_type, |
106 | | uint32_t location, |
107 | | uint32_t component, |
108 | | uint32_t extra_array_length); |
109 | | |
110 | | // Creates scalar variables with the storage classe |storage_class| to replace |
111 | | // an interface variable whose type is |interface_var_type|. If |
112 | | // |extra_array_length| is not zero, adds the extra arrayness to the created |
113 | | // scalar variables. |
114 | | std::optional<NestedCompositeComponents> |
115 | | CreateScalarInterfaceVarsForReplacement(Instruction* interface_var_type, |
116 | | spv::StorageClass storage_class, |
117 | | uint32_t extra_array_length); |
118 | | |
119 | | // Creates scalar variables with the storage classe |storage_class| to replace |
120 | | // the interface variable whose type is OpTypeArray |interface_var_type| with. |
121 | | // If |extra_array_length| is not zero, adds the extra arrayness to all the |
122 | | // scalar variables. |
123 | | std::optional<NestedCompositeComponents> CreateScalarInterfaceVarsForArray( |
124 | | Instruction* interface_var_type, spv::StorageClass storage_class, |
125 | | uint32_t extra_array_length); |
126 | | |
127 | | // Creates scalar variables with the storage classe |storage_class| to replace |
128 | | // the interface variable whose type is OpTypeMatrix |interface_var_type| |
129 | | // with. If |extra_array_length| is not zero, adds the extra arrayness to all |
130 | | // the scalar variables. |
131 | | std::optional<NestedCompositeComponents> CreateScalarInterfaceVarsForMatrix( |
132 | | Instruction* interface_var_type, spv::StorageClass storage_class, |
133 | | uint32_t extra_array_length); |
134 | | |
135 | | // Recursively adds Location and Component decorations to variables in |
136 | | // |vars| with |location| and |component|. Increases |location| by one after |
137 | | // it actually adds Location and Component decorations for a variable. |
138 | | void AddLocationAndComponentDecorations(const NestedCompositeComponents& vars, |
139 | | uint32_t* location, |
140 | | uint32_t component); |
141 | | |
142 | | // Replaces the interface variable |interface_var| with |
143 | | // |scalar_interface_vars| and returns whether it succeeds or not. |
144 | | // |extra_arrayness| is the extra arrayness of the interface variable. |
145 | | // |scalar_interface_vars| contains the nested variables to replace the |
146 | | // interface variable with. |
147 | | Status ReplaceInterfaceVarWith( |
148 | | Instruction* interface_var, uint32_t extra_arrayness, |
149 | | const NestedCompositeComponents& scalar_interface_vars); |
150 | | |
151 | | // Replaces |interface_var| in the operands of instructions |
152 | | // |interface_var_users| with |scalar_interface_vars|. This is a recursive |
153 | | // method and |interface_var_component_indices| is used to specify which |
154 | | // recursive component of |interface_var| is replaced. Returns composite |
155 | | // construct instructions to be replaced with load instructions of |
156 | | // |interface_var_users| via |loads_to_composites|. Returns composite |
157 | | // construct instructions to be replaced with load instructions of access |
158 | | // chain instructions in |interface_var_users| via |
159 | | // |loads_for_access_chain_to_composites|. |
160 | | Status ReplaceComponentsOfInterfaceVarWith( |
161 | | Instruction* interface_var, |
162 | | const std::vector<Instruction*>& interface_var_users, |
163 | | const NestedCompositeComponents& scalar_interface_vars, |
164 | | std::vector<uint32_t>& interface_var_component_indices, |
165 | | const uint32_t* extra_array_index, |
166 | | std::unordered_map<Instruction*, Instruction*>* loads_to_composites, |
167 | | std::unordered_map<Instruction*, Instruction*>* |
168 | | loads_for_access_chain_to_composites); |
169 | | |
170 | | // Replaces |interface_var| in the operands of instructions |
171 | | // |interface_var_users| with |components| that is a vector of components for |
172 | | // the interface variable |interface_var|. This is a recursive method and |
173 | | // |interface_var_component_indices| is used to specify which recursive |
174 | | // component of |interface_var| is replaced. Returns composite construct |
175 | | // instructions to be replaced with load instructions of |interface_var_users| |
176 | | // via |loads_to_composites|. Returns composite construct instructions to be |
177 | | // replaced with load instructions of access chain instructions in |
178 | | // |interface_var_users| via |loads_for_access_chain_to_composites|. |
179 | | Status ReplaceMultipleComponentsOfInterfaceVarWith( |
180 | | Instruction* interface_var, |
181 | | const std::vector<Instruction*>& interface_var_users, |
182 | | const std::vector<NestedCompositeComponents>& components, |
183 | | std::vector<uint32_t>& interface_var_component_indices, |
184 | | const uint32_t* extra_array_index, |
185 | | std::unordered_map<Instruction*, Instruction*>* loads_to_composites, |
186 | | std::unordered_map<Instruction*, Instruction*>* |
187 | | loads_for_access_chain_to_composites); |
188 | | |
189 | | // Replaces a component of |interface_var| that is used as an operand of |
190 | | // instruction |interface_var_user| with |scalar_var|. |
191 | | // |interface_var_component_indices| is a vector of recursive indices for |
192 | | // which recursive component of |interface_var| is replaced. If |
193 | | // |interface_var_user| is a load, returns the component value via |
194 | | // |loads_to_component_values|. If |interface_var_user| is an access chain, |
195 | | // returns the component value for loads of |interface_var_user| via |
196 | | // |loads_for_access_chain_to_component_values|. |
197 | | Status ReplaceComponentOfInterfaceVarWith( |
198 | | Instruction* interface_var, Instruction* interface_var_user, |
199 | | Instruction* scalar_var, |
200 | | const std::vector<uint32_t>& interface_var_component_indices, |
201 | | const uint32_t* extra_array_index, |
202 | | std::unordered_map<Instruction*, Instruction*>* loads_to_component_values, |
203 | | std::unordered_map<Instruction*, Instruction*>* |
204 | | loads_for_access_chain_to_component_values); |
205 | | |
206 | | // Creates instructions to load |scalar_var| and inserts them before |
207 | | // |insert_before|. If |extra_array_index| is not null, they load |
208 | | // |extra_array_index| th component of |scalar_var| instead of |scalar_var| |
209 | | // itself. |
210 | | Instruction* LoadScalarVar(Instruction* scalar_var, |
211 | | const uint32_t* extra_array_index, |
212 | | Instruction* insert_before); |
213 | | |
214 | | // Creates instructions to load an access chain to |var| and inserts them |
215 | | // before |insert_before|. |Indexes| will be Indexes operand of the access |
216 | | // chain. |
217 | | Instruction* LoadAccessChainToVar(Instruction* var, |
218 | | const std::vector<uint32_t>& indexes, |
219 | | Instruction* insert_before); |
220 | | |
221 | | // Creates instructions to store a component of an aggregate whose id is |
222 | | // |value_id| to an access chain to |scalar_var| and inserts the created |
223 | | // instructions before |insert_before|. To get the component, recursively |
224 | | // traverses the aggregate with |component_indices| as indexes. |
225 | | // Numbers in |access_chain_indices| are the Indexes operand of the access |
226 | | // chain to |scalar_var| |
227 | | void StoreComponentOfValueToAccessChainToScalarVar( |
228 | | uint32_t value_id, const std::vector<uint32_t>& component_indices, |
229 | | Instruction* scalar_var, |
230 | | const std::vector<uint32_t>& access_chain_indices, |
231 | | Instruction* insert_before); |
232 | | |
233 | | // Creates instructions to store a component of an aggregate whose id is |
234 | | // |value_id| to |scalar_var| and inserts the created instructions before |
235 | | // |insert_before|. To get the component, recursively traverses the aggregate |
236 | | // using |extra_array_index| and |component_indices| as indexes. |
237 | | void StoreComponentOfValueToScalarVar( |
238 | | uint32_t value_id, const std::vector<uint32_t>& component_indices, |
239 | | Instruction* scalar_var, const uint32_t* extra_array_index, |
240 | | Instruction* insert_before); |
241 | | |
242 | | // Creates instructions to store a component of an aggregate whose id is |
243 | | // |value_id| to |ptr| and inserts the created instructions before |
244 | | // |insert_before|. To get the component, recursively traverses the aggregate |
245 | | // using |extra_array_index| and |component_indices| as indexes. |
246 | | // |component_type_id| is the id of the type instruction of the component. |
247 | | void StoreComponentOfValueTo(uint32_t component_type_id, uint32_t value_id, |
248 | | const std::vector<uint32_t>& component_indices, |
249 | | Instruction* ptr, |
250 | | const uint32_t* extra_array_index, |
251 | | Instruction* insert_before); |
252 | | |
253 | | // Creates new OpCompositeExtract with |type_id| for Result Type, |
254 | | // |composite_id| for Composite operand, and |indexes| for Indexes operands. |
255 | | // If |extra_first_index| is not nullptr, uses it as the first Indexes |
256 | | // operand. |
257 | | Instruction* CreateCompositeExtract(uint32_t type_id, uint32_t composite_id, |
258 | | const std::vector<uint32_t>& indexes, |
259 | | const uint32_t* extra_first_index); |
260 | | |
261 | | // Creates a new OpLoad whose Result Type is |type_id| and Pointer operand is |
262 | | // |ptr|. Inserts the new instruction before |insert_before|. |
263 | | Instruction* CreateLoad(uint32_t type_id, Instruction* ptr, |
264 | | Instruction* insert_before); |
265 | | |
266 | | // Clones an annotation instruction |annotation_inst| and sets the target |
267 | | // operand of the new annotation instruction as |var_id|. |
268 | | void CloneAnnotationForVariable(Instruction* annotation_inst, |
269 | | uint32_t var_id); |
270 | | |
271 | | // Replaces the interface variable |interface_var| in the operands of the |
272 | | // entry point |entry_point| with |scalar_var_id|. If it cannot find |
273 | | // |interface_var| from the operands of the entry point |entry_point|, adds |
274 | | // |scalar_var_id| as an operand of the entry point |entry_point|. |
275 | | bool ReplaceInterfaceVarInEntryPoint(Instruction* interface_var, |
276 | | Instruction* entry_point, |
277 | | uint32_t scalar_var_id); |
278 | | |
279 | | // Creates an access chain instruction whose Base operand is |var| and Indexes |
280 | | // operand is |index|. |component_type_id| is the id of the type instruction |
281 | | // that is the type of component. Inserts the new access chain before |
282 | | // |insert_before|. |
283 | | Instruction* CreateAccessChainWithIndex(uint32_t component_type_id, |
284 | | Instruction* var, uint32_t index, |
285 | | Instruction* insert_before); |
286 | | |
287 | | // Returns the pointee type of the type of variable |var|. |
288 | | uint32_t GetPointeeTypeIdOfVar(Instruction* var); |
289 | | |
290 | | // Replaces the access chain |access_chain| and its users with a new access |
291 | | // chain that points |scalar_var| as the Base operand having |
292 | | // |interface_var_component_indices| as Indexes operands and users of the new |
293 | | // access chain. When some of the users are load instructions, returns the |
294 | | // original load instruction to the new instruction that loads a component of |
295 | | // the original load value via |loads_to_component_values|. |
296 | | void ReplaceAccessChainWith( |
297 | | Instruction* access_chain, |
298 | | const std::vector<uint32_t>& interface_var_component_indices, |
299 | | Instruction* scalar_var, |
300 | | std::unordered_map<Instruction*, Instruction*>* |
301 | | loads_to_component_values); |
302 | | |
303 | | // Assuming that |access_chain| is an access chain instruction whose Base |
304 | | // operand is |base_access_chain|, replaces the operands of |access_chain| |
305 | | // with operands of |base_access_chain| and Indexes operands of |
306 | | // |access_chain|. |
307 | | void UseBaseAccessChainForAccessChain(Instruction* access_chain, |
308 | | Instruction* base_access_chain); |
309 | | |
310 | | // Creates composite construct instructions for load instructions that are the |
311 | | // keys of |loads_to_component_values| if no such composite construct |
312 | | // instructions exist. Adds a component of the composite as an operand of the |
313 | | // created composite construct instruction. Each value of |
314 | | // |loads_to_component_values| is the component. Returns the created composite |
315 | | // construct instructions using |loads_to_composites|. |depth_to_component| is |
316 | | // the number of recursive access steps to get the component from the |
317 | | // composite. |
318 | | void AddComponentsToCompositesForLoads( |
319 | | const std::unordered_map<Instruction*, Instruction*>& |
320 | | loads_to_component_values, |
321 | | std::unordered_map<Instruction*, Instruction*>* loads_to_composites, |
322 | | uint32_t depth_to_component); |
323 | | |
324 | | // Creates a composite construct instruction for a component of the value of |
325 | | // instruction |load| in |depth_to_component| th recursive depth and inserts |
326 | | // it after |load|. |
327 | | Instruction* CreateCompositeConstructForComponentOfLoad( |
328 | | Instruction* load, uint32_t depth_to_component); |
329 | | |
330 | | // Creates a new access chain instruction that points to variable |var| whose |
331 | | // type is the instruction with |var_type_id| and inserts it before |
332 | | // |insert_before|. The new access chain will have |index_ids| for Indexes |
333 | | // operands. Returns the type id of the component that is pointed by the new |
334 | | // access chain via |component_type_id|. |
335 | | Instruction* CreateAccessChainToVar(uint32_t var_type_id, Instruction* var, |
336 | | const std::vector<uint32_t>& index_ids, |
337 | | Instruction* insert_before, |
338 | | uint32_t* component_type_id); |
339 | | |
340 | | // Returns the result id of OpTypeArray instrunction whose Element Type |
341 | | // operand is |elem_type_id| and Length operand is |array_length|. |
342 | | uint32_t GetArrayType(uint32_t elem_type_id, uint32_t array_length); |
343 | | |
344 | | // Returns the result id of OpTypePointer instrunction whose Type |
345 | | // operand is |type_id| and Storage Class operand is |storage_class|. |
346 | | uint32_t GetPointerType(uint32_t type_id, spv::StorageClass storage_class); |
347 | | |
348 | | // Kills an instrunction |inst| and its users. |
349 | | void KillInstructionAndUsers(Instruction* inst); |
350 | | |
351 | | // Kills a vector of instrunctions |insts| and their users. |
352 | | void KillInstructionsAndUsers(const std::vector<Instruction*>& insts); |
353 | | |
354 | | // Kills all OpDecorate instructions for Location and Component of the |
355 | | // variable whose id is |var_id|. |
356 | | void KillLocationAndComponentDecorations(uint32_t var_id); |
357 | | |
358 | | // If |var| has the extra arrayness for an entry point, reports an error and |
359 | | // returns true. Otherwise, returns false. |
360 | | bool ReportErrorIfHasExtraArraynessForOtherEntry(Instruction* var); |
361 | | |
362 | | // If |var| does not have the extra arrayness for an entry point, reports an |
363 | | // error and returns true. Otherwise, returns false. |
364 | | bool ReportErrorIfHasNoExtraArraynessForOtherEntry(Instruction* var); |
365 | | |
366 | | // If |interface_var| has the extra arrayness for an entry point but it does |
367 | | // not have one for another entry point, reports an error and returns false. |
368 | | // Otherwise, returns true. |has_extra_arrayness| denotes whether it has an |
369 | | // extra arrayness for an entry point or not. |
370 | | bool CheckExtraArraynessConflictBetweenEntries(Instruction* interface_var, |
371 | | bool has_extra_arrayness); |
372 | | |
373 | | // Conducts the scalar replacement for the interface variables used by the |
374 | | // |entry_point|. |
375 | | Pass::Status ReplaceInterfaceVarsWithScalars(Instruction& entry_point); |
376 | | |
377 | | // A set of interface variable ids that were already removed from operands of |
378 | | // the entry point. |
379 | | std::unordered_set<uint32_t> |
380 | | interface_vars_removed_from_entry_point_operands_; |
381 | | |
382 | | // A mapping from ids of new composite construct instructions that load |
383 | | // instructions are replaced with to the recursive depth of the component of |
384 | | // load that the new component construct instruction is used for. |
385 | | std::unordered_map<uint32_t, uint32_t> composite_ids_to_component_depths; |
386 | | |
387 | | // A set of interface variables with the extra arrayness for any of the entry |
388 | | // points. |
389 | | std::unordered_set<Instruction*> vars_with_extra_arrayness; |
390 | | |
391 | | // A set of interface variables without the extra arrayness for any of the |
392 | | // entry points. |
393 | | std::unordered_set<Instruction*> vars_without_extra_arrayness; |
394 | | |
395 | | // Returns the next available id, or 0 if the id overflows. |
396 | 0 | uint32_t TakeNextId() { return context()->TakeNextId(); } |
397 | | }; |
398 | | |
399 | | } // namespace opt |
400 | | } // namespace spvtools |
401 | | |
402 | | #endif // SOURCE_OPT_INTERFACE_VAR_SROA_H_ |