Coverage Report

Created: 2026-06-30 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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_