| // Copyright (c) 2022 Google LLC | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #ifndef SOURCE_OPT_INTERFACE_VAR_SROA_H_ | |
| #define SOURCE_OPT_INTERFACE_VAR_SROA_H_ | |
| #include <unordered_set> | |
| #include "source/opt/pass.h" | |
| namespace spvtools { | |
| namespace opt { | |
| // See optimizer.hpp for documentation. | |
| // | |
| // Note that the current implementation of this pass covers only store, load, | |
| // access chain instructions for the interface variables. Supporting other types | |
| // of instructions is a future work. | |
| class InterfaceVariableScalarReplacement : public Pass { | |
| public: | |
| InterfaceVariableScalarReplacement() {} | |
| const char* name() const override { | |
| return "interface-variable-scalar-replacement"; | |
| } | |
| Status Process() override; | |
| IRContext::Analysis GetPreservedAnalyses() override { | |
| return IRContext::kAnalysisDecorations | IRContext::kAnalysisDefUse | | |
| IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; | |
| } | |
| private: | |
| // A struct containing components of a composite variable. If the composite | |
| // consists of multiple or recursive components, |component_variable| is | |
| // nullptr and |nested_composite_components| keeps the components. If it has a | |
| // single component, |nested_composite_components| is empty and | |
| // |component_variable| is the component. Note that each element of | |
| // |nested_composite_components| has the NestedCompositeComponents struct as | |
| // its type that can recursively keep the components. | |
| struct NestedCompositeComponents { | |
| NestedCompositeComponents() : component_variable(nullptr) {} | |
| bool HasMultipleComponents() const { | |
| return !nested_composite_components.empty(); | |
| } | |
| const std::vector<NestedCompositeComponents>& GetComponents() const { | |
| return nested_composite_components; | |
| } | |
| void AddComponent(const NestedCompositeComponents& component) { | |
| nested_composite_components.push_back(component); | |
| } | |
| Instruction* GetComponentVariable() const { return component_variable; } | |
| void SetSingleComponentVariable(Instruction* var) { | |
| component_variable = var; | |
| } | |
| private: | |
| std::vector<NestedCompositeComponents> nested_composite_components; | |
| Instruction* component_variable; | |
| }; | |
| // Collects all interface variables used by the |entry_point|. | |
| std::vector<Instruction*> CollectInterfaceVariables(Instruction& entry_point); | |
| // Returns whether |var| has the extra arrayness for the entry point | |
| // |entry_point| or not. | |
| bool HasExtraArrayness(Instruction& entry_point, Instruction* var); | |
| // Finds a Location BuiltIn decoration of |var| and returns it via | |
| // |location|. Returns true whether the location exists or not. | |
| bool GetVariableLocation(Instruction* var, uint32_t* location); | |
| // Finds a Component BuiltIn decoration of |var| and returns it via | |
| // |component|. Returns true whether the component exists or not. | |
| bool GetVariableComponent(Instruction* var, uint32_t* component); | |
| // Returns the type of |var| as an instruction. | |
| Instruction* GetTypeOfVariable(Instruction* var); | |
| // Replaces an interface variable |interface_var| whose type is | |
| // |interface_var_type| with scalars and returns whether it succeeds or not. | |
| // |location| is the value of Location Decoration for |interface_var|. | |
| // |component| is the value of Component Decoration for |interface_var|. | |
| // If |extra_array_length| is 0, it means |interface_var| has a Patch | |
| // decoration. Otherwise, |extra_array_length| denotes the length of the extra | |
| // array of |interface_var|. | |
| bool ReplaceInterfaceVariableWithScalars(Instruction* interface_var, | |
| Instruction* interface_var_type, | |
| uint32_t location, | |
| uint32_t component, | |
| uint32_t extra_array_length); | |
| // Creates scalar variables with the storage classe |storage_class| to replace | |
| // an interface variable whose type is |interface_var_type|. If | |
| // |extra_array_length| is not zero, adds the extra arrayness to the created | |
| // scalar variables. | |
| NestedCompositeComponents CreateScalarInterfaceVarsForReplacement( | |
| Instruction* interface_var_type, spv::StorageClass storage_class, | |
| uint32_t extra_array_length); | |
| // Creates scalar variables with the storage classe |storage_class| to replace | |
| // the interface variable whose type is OpTypeArray |interface_var_type| with. | |
| // If |extra_array_length| is not zero, adds the extra arrayness to all the | |
| // scalar variables. | |
| NestedCompositeComponents CreateScalarInterfaceVarsForArray( | |
| Instruction* interface_var_type, spv::StorageClass storage_class, | |
| uint32_t extra_array_length); | |
| // Creates scalar variables with the storage classe |storage_class| to replace | |
| // the interface variable whose type is OpTypeMatrix |interface_var_type| | |
| // with. If |extra_array_length| is not zero, adds the extra arrayness to all | |
| // the scalar variables. | |
| NestedCompositeComponents CreateScalarInterfaceVarsForMatrix( | |
| Instruction* interface_var_type, spv::StorageClass storage_class, | |
| uint32_t extra_array_length); | |
| // Recursively adds Location and Component decorations to variables in | |
| // |vars| with |location| and |component|. Increases |location| by one after | |
| // it actually adds Location and Component decorations for a variable. | |
| void AddLocationAndComponentDecorations(const NestedCompositeComponents& vars, | |
| uint32_t* location, | |
| uint32_t component); | |
| // Replaces the interface variable |interface_var| with | |
| // |scalar_interface_vars| and returns whether it succeeds or not. | |
| // |extra_arrayness| is the extra arrayness of the interface variable. | |
| // |scalar_interface_vars| contains the nested variables to replace the | |
| // interface variable with. | |
| bool ReplaceInterfaceVarWith( | |
| Instruction* interface_var, uint32_t extra_arrayness, | |
| const NestedCompositeComponents& scalar_interface_vars); | |
| // Replaces |interface_var| in the operands of instructions | |
| // |interface_var_users| with |scalar_interface_vars|. This is a recursive | |
| // method and |interface_var_component_indices| is used to specify which | |
| // recursive component of |interface_var| is replaced. Returns composite | |
| // construct instructions to be replaced with load instructions of | |
| // |interface_var_users| via |loads_to_composites|. Returns composite | |
| // construct instructions to be replaced with load instructions of access | |
| // chain instructions in |interface_var_users| via | |
| // |loads_for_access_chain_to_composites|. | |
| bool ReplaceComponentsOfInterfaceVarWith( | |
| Instruction* interface_var, | |
| const std::vector<Instruction*>& interface_var_users, | |
| const NestedCompositeComponents& scalar_interface_vars, | |
| std::vector<uint32_t>& interface_var_component_indices, | |
| const uint32_t* extra_array_index, | |
| std::unordered_map<Instruction*, Instruction*>* loads_to_composites, | |
| std::unordered_map<Instruction*, Instruction*>* | |
| loads_for_access_chain_to_composites); | |
| // Replaces |interface_var| in the operands of instructions | |
| // |interface_var_users| with |components| that is a vector of components for | |
| // the interface variable |interface_var|. This is a recursive method and | |
| // |interface_var_component_indices| is used to specify which recursive | |
| // component of |interface_var| is replaced. Returns composite construct | |
| // instructions to be replaced with load instructions of |interface_var_users| | |
| // via |loads_to_composites|. Returns composite construct instructions to be | |
| // replaced with load instructions of access chain instructions in | |
| // |interface_var_users| via |loads_for_access_chain_to_composites|. | |
| bool ReplaceMultipleComponentsOfInterfaceVarWith( | |
| Instruction* interface_var, | |
| const std::vector<Instruction*>& interface_var_users, | |
| const std::vector<NestedCompositeComponents>& components, | |
| std::vector<uint32_t>& interface_var_component_indices, | |
| const uint32_t* extra_array_index, | |
| std::unordered_map<Instruction*, Instruction*>* loads_to_composites, | |
| std::unordered_map<Instruction*, Instruction*>* | |
| loads_for_access_chain_to_composites); | |
| // Replaces a component of |interface_var| that is used as an operand of | |
| // instruction |interface_var_user| with |scalar_var|. | |
| // |interface_var_component_indices| is a vector of recursive indices for | |
| // which recursive component of |interface_var| is replaced. If | |
| // |interface_var_user| is a load, returns the component value via | |
| // |loads_to_component_values|. If |interface_var_user| is an access chain, | |
| // returns the component value for loads of |interface_var_user| via | |
| // |loads_for_access_chain_to_component_values|. | |
| bool ReplaceComponentOfInterfaceVarWith( | |
| Instruction* interface_var, Instruction* interface_var_user, | |
| Instruction* scalar_var, | |
| const std::vector<uint32_t>& interface_var_component_indices, | |
| const uint32_t* extra_array_index, | |
| std::unordered_map<Instruction*, Instruction*>* loads_to_component_values, | |
| std::unordered_map<Instruction*, Instruction*>* | |
| loads_for_access_chain_to_component_values); | |
| // Creates instructions to load |scalar_var| and inserts them before | |
| // |insert_before|. If |extra_array_index| is not null, they load | |
| // |extra_array_index| th component of |scalar_var| instead of |scalar_var| | |
| // itself. | |
| Instruction* LoadScalarVar(Instruction* scalar_var, | |
| const uint32_t* extra_array_index, | |
| Instruction* insert_before); | |
| // Creates instructions to load an access chain to |var| and inserts them | |
| // before |insert_before|. |Indexes| will be Indexes operand of the access | |
| // chain. | |
| Instruction* LoadAccessChainToVar(Instruction* var, | |
| const std::vector<uint32_t>& indexes, | |
| Instruction* insert_before); | |
| // Creates instructions to store a component of an aggregate whose id is | |
| // |value_id| to an access chain to |scalar_var| and inserts the created | |
| // instructions before |insert_before|. To get the component, recursively | |
| // traverses the aggregate with |component_indices| as indexes. | |
| // Numbers in |access_chain_indices| are the Indexes operand of the access | |
| // chain to |scalar_var| | |
| void StoreComponentOfValueToAccessChainToScalarVar( | |
| uint32_t value_id, const std::vector<uint32_t>& component_indices, | |
| Instruction* scalar_var, | |
| const std::vector<uint32_t>& access_chain_indices, | |
| Instruction* insert_before); | |
| // Creates instructions to store a component of an aggregate whose id is | |
| // |value_id| to |scalar_var| and inserts the created instructions before | |
| // |insert_before|. To get the component, recursively traverses the aggregate | |
| // using |extra_array_index| and |component_indices| as indexes. | |
| void StoreComponentOfValueToScalarVar( | |
| uint32_t value_id, const std::vector<uint32_t>& component_indices, | |
| Instruction* scalar_var, const uint32_t* extra_array_index, | |
| Instruction* insert_before); | |
| // Creates instructions to store a component of an aggregate whose id is | |
| // |value_id| to |ptr| and inserts the created instructions before | |
| // |insert_before|. To get the component, recursively traverses the aggregate | |
| // using |extra_array_index| and |component_indices| as indexes. | |
| // |component_type_id| is the id of the type instruction of the component. | |
| void StoreComponentOfValueTo(uint32_t component_type_id, uint32_t value_id, | |
| const std::vector<uint32_t>& component_indices, | |
| Instruction* ptr, | |
| const uint32_t* extra_array_index, | |
| Instruction* insert_before); | |
| // Creates new OpCompositeExtract with |type_id| for Result Type, | |
| // |composite_id| for Composite operand, and |indexes| for Indexes operands. | |
| // If |extra_first_index| is not nullptr, uses it as the first Indexes | |
| // operand. | |
| Instruction* CreateCompositeExtract(uint32_t type_id, uint32_t composite_id, | |
| const std::vector<uint32_t>& indexes, | |
| const uint32_t* extra_first_index); | |
| // Creates a new OpLoad whose Result Type is |type_id| and Pointer operand is | |
| // |ptr|. Inserts the new instruction before |insert_before|. | |
| Instruction* CreateLoad(uint32_t type_id, Instruction* ptr, | |
| Instruction* insert_before); | |
| // Clones an annotation instruction |annotation_inst| and sets the target | |
| // operand of the new annotation instruction as |var_id|. | |
| void CloneAnnotationForVariable(Instruction* annotation_inst, | |
| uint32_t var_id); | |
| // Replaces the interface variable |interface_var| in the operands of the | |
| // entry point |entry_point| with |scalar_var_id|. If it cannot find | |
| // |interface_var| from the operands of the entry point |entry_point|, adds | |
| // |scalar_var_id| as an operand of the entry point |entry_point|. | |
| bool ReplaceInterfaceVarInEntryPoint(Instruction* interface_var, | |
| Instruction* entry_point, | |
| uint32_t scalar_var_id); | |
| // Creates an access chain instruction whose Base operand is |var| and Indexes | |
| // operand is |index|. |component_type_id| is the id of the type instruction | |
| // that is the type of component. Inserts the new access chain before | |
| // |insert_before|. | |
| Instruction* CreateAccessChainWithIndex(uint32_t component_type_id, | |
| Instruction* var, uint32_t index, | |
| Instruction* insert_before); | |
| // Returns the pointee type of the type of variable |var|. | |
| uint32_t GetPointeeTypeIdOfVar(Instruction* var); | |
| // Replaces the access chain |access_chain| and its users with a new access | |
| // chain that points |scalar_var| as the Base operand having | |
| // |interface_var_component_indices| as Indexes operands and users of the new | |
| // access chain. When some of the users are load instructions, returns the | |
| // original load instruction to the new instruction that loads a component of | |
| // the original load value via |loads_to_component_values|. | |
| void ReplaceAccessChainWith( | |
| Instruction* access_chain, | |
| const std::vector<uint32_t>& interface_var_component_indices, | |
| Instruction* scalar_var, | |
| std::unordered_map<Instruction*, Instruction*>* | |
| loads_to_component_values); | |
| // Assuming that |access_chain| is an access chain instruction whose Base | |
| // operand is |base_access_chain|, replaces the operands of |access_chain| | |
| // with operands of |base_access_chain| and Indexes operands of | |
| // |access_chain|. | |
| void UseBaseAccessChainForAccessChain(Instruction* access_chain, | |
| Instruction* base_access_chain); | |
| // Creates composite construct instructions for load instructions that are the | |
| // keys of |loads_to_component_values| if no such composite construct | |
| // instructions exist. Adds a component of the composite as an operand of the | |
| // created composite construct instruction. Each value of | |
| // |loads_to_component_values| is the component. Returns the created composite | |
| // construct instructions using |loads_to_composites|. |depth_to_component| is | |
| // the number of recursive access steps to get the component from the | |
| // composite. | |
| void AddComponentsToCompositesForLoads( | |
| const std::unordered_map<Instruction*, Instruction*>& | |
| loads_to_component_values, | |
| std::unordered_map<Instruction*, Instruction*>* loads_to_composites, | |
| uint32_t depth_to_component); | |
| // Creates a composite construct instruction for a component of the value of | |
| // instruction |load| in |depth_to_component| th recursive depth and inserts | |
| // it after |load|. | |
| Instruction* CreateCompositeConstructForComponentOfLoad( | |
| Instruction* load, uint32_t depth_to_component); | |
| // Creates a new access chain instruction that points to variable |var| whose | |
| // type is the instruction with |var_type_id| and inserts it before | |
| // |insert_before|. The new access chain will have |index_ids| for Indexes | |
| // operands. Returns the type id of the component that is pointed by the new | |
| // access chain via |component_type_id|. | |
| Instruction* CreateAccessChainToVar(uint32_t var_type_id, Instruction* var, | |
| const std::vector<uint32_t>& index_ids, | |
| Instruction* insert_before, | |
| uint32_t* component_type_id); | |
| // Returns the result id of OpTypeArray instrunction whose Element Type | |
| // operand is |elem_type_id| and Length operand is |array_length|. | |
| uint32_t GetArrayType(uint32_t elem_type_id, uint32_t array_length); | |
| // Returns the result id of OpTypePointer instrunction whose Type | |
| // operand is |type_id| and Storage Class operand is |storage_class|. | |
| uint32_t GetPointerType(uint32_t type_id, spv::StorageClass storage_class); | |
| // Kills an instrunction |inst| and its users. | |
| void KillInstructionAndUsers(Instruction* inst); | |
| // Kills a vector of instrunctions |insts| and their users. | |
| void KillInstructionsAndUsers(const std::vector<Instruction*>& insts); | |
| // Kills all OpDecorate instructions for Location and Component of the | |
| // variable whose id is |var_id|. | |
| void KillLocationAndComponentDecorations(uint32_t var_id); | |
| // If |var| has the extra arrayness for an entry point, reports an error and | |
| // returns true. Otherwise, returns false. | |
| bool ReportErrorIfHasExtraArraynessForOtherEntry(Instruction* var); | |
| // If |var| does not have the extra arrayness for an entry point, reports an | |
| // error and returns true. Otherwise, returns false. | |
| bool ReportErrorIfHasNoExtraArraynessForOtherEntry(Instruction* var); | |
| // If |interface_var| has the extra arrayness for an entry point but it does | |
| // not have one for another entry point, reports an error and returns false. | |
| // Otherwise, returns true. |has_extra_arrayness| denotes whether it has an | |
| // extra arrayness for an entry point or not. | |
| bool CheckExtraArraynessConflictBetweenEntries(Instruction* interface_var, | |
| bool has_extra_arrayness); | |
| // Conducts the scalar replacement for the interface variables used by the | |
| // |entry_point|. | |
| Pass::Status ReplaceInterfaceVarsWithScalars(Instruction& entry_point); | |
| // A set of interface variable ids that were already removed from operands of | |
| // the entry point. | |
| std::unordered_set<uint32_t> | |
| interface_vars_removed_from_entry_point_operands_; | |
| // A mapping from ids of new composite construct instructions that load | |
| // instructions are replaced with to the recursive depth of the component of | |
| // load that the new component construct instruction is used for. | |
| std::unordered_map<uint32_t, uint32_t> composite_ids_to_component_depths; | |
| // A set of interface variables with the extra arrayness for any of the entry | |
| // points. | |
| std::unordered_set<Instruction*> vars_with_extra_arrayness; | |
| // A set of interface variables without the extra arrayness for any of the | |
| // entry points. | |
| std::unordered_set<Instruction*> vars_without_extra_arrayness; | |
| }; | |
| } // namespace opt | |
| } // namespace spvtools | |
| #endif // SOURCE_OPT_INTERFACE_VAR_SROA_H_ |