| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
- // 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_
|