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