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