| // 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. |
| |
| #include "source/opt/interface_var_sroa.h" |
| |
| #include <iostream> |
| |
| #include "source/opt/decoration_manager.h" |
| #include "source/opt/def_use_manager.h" |
| #include "source/opt/function.h" |
| #include "source/opt/log.h" |
| #include "source/opt/type_manager.h" |
| #include "source/util/make_unique.h" |
| |
| namespace spvtools { |
| namespace opt { |
| namespace { |
| constexpr uint32_t kOpDecorateDecorationInOperandIndex = 1; |
| constexpr uint32_t kOpDecorateLiteralInOperandIndex = 2; |
| constexpr uint32_t kOpEntryPointInOperandInterface = 3; |
| constexpr uint32_t kOpVariableStorageClassInOperandIndex = 0; |
| constexpr uint32_t kOpTypeArrayElemTypeInOperandIndex = 0; |
| constexpr uint32_t kOpTypeArrayLengthInOperandIndex = 1; |
| constexpr uint32_t kOpTypeMatrixColCountInOperandIndex = 1; |
| constexpr uint32_t kOpTypeMatrixColTypeInOperandIndex = 0; |
| constexpr uint32_t kOpTypePtrTypeInOperandIndex = 1; |
| constexpr uint32_t kOpConstantValueInOperandIndex = 0; |
| |
| // Get the length of the OpTypeArray |array_type|. |
| uint32_t GetArrayLength(analysis::DefUseManager* def_use_mgr, |
| Instruction* array_type) { |
| assert(array_type->opcode() == spv::Op::OpTypeArray); |
| uint32_t const_int_id = |
| array_type->GetSingleWordInOperand(kOpTypeArrayLengthInOperandIndex); |
| Instruction* array_length_inst = def_use_mgr->GetDef(const_int_id); |
| assert(array_length_inst->opcode() == spv::Op::OpConstant); |
| return array_length_inst->GetSingleWordInOperand( |
| kOpConstantValueInOperandIndex); |
| } |
| |
| // Get the element type instruction of the OpTypeArray |array_type|. |
| Instruction* GetArrayElementType(analysis::DefUseManager* def_use_mgr, |
| Instruction* array_type) { |
| assert(array_type->opcode() == spv::Op::OpTypeArray); |
| uint32_t elem_type_id = |
| array_type->GetSingleWordInOperand(kOpTypeArrayElemTypeInOperandIndex); |
| return def_use_mgr->GetDef(elem_type_id); |
| } |
| |
| // Get the column type instruction of the OpTypeMatrix |matrix_type|. |
| Instruction* GetMatrixColumnType(analysis::DefUseManager* def_use_mgr, |
| Instruction* matrix_type) { |
| assert(matrix_type->opcode() == spv::Op::OpTypeMatrix); |
| uint32_t column_type_id = |
| matrix_type->GetSingleWordInOperand(kOpTypeMatrixColTypeInOperandIndex); |
| return def_use_mgr->GetDef(column_type_id); |
| } |
| |
| // Traverses the component type of OpTypeArray or OpTypeMatrix. Repeats it |
| // |depth_to_component| times recursively and returns the component type. |
| // |type_id| is the result id of the OpTypeArray or OpTypeMatrix instruction. |
| uint32_t GetComponentTypeOfArrayMatrix(analysis::DefUseManager* def_use_mgr, |
| uint32_t type_id, |
| uint32_t depth_to_component) { |
| if (depth_to_component == 0) return type_id; |
| |
| Instruction* type_inst = def_use_mgr->GetDef(type_id); |
| if (type_inst->opcode() == spv::Op::OpTypeArray) { |
| uint32_t elem_type_id = |
| type_inst->GetSingleWordInOperand(kOpTypeArrayElemTypeInOperandIndex); |
| return GetComponentTypeOfArrayMatrix(def_use_mgr, elem_type_id, |
| depth_to_component - 1); |
| } |
| |
| assert(type_inst->opcode() == spv::Op::OpTypeMatrix); |
| uint32_t column_type_id = |
| type_inst->GetSingleWordInOperand(kOpTypeMatrixColTypeInOperandIndex); |
| return GetComponentTypeOfArrayMatrix(def_use_mgr, column_type_id, |
| depth_to_component - 1); |
| } |
| |
| // Creates an OpDecorate instruction whose Target is |var_id| and Decoration is |
| // |decoration|. Adds |literal| as an extra operand of the instruction. |
| void CreateDecoration(analysis::DecorationManager* decoration_mgr, |
| uint32_t var_id, spv::Decoration decoration, |
| uint32_t literal) { |
| std::vector<Operand> operands({ |
| {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}}, |
| {spv_operand_type_t::SPV_OPERAND_TYPE_DECORATION, |
| {static_cast<uint32_t>(decoration)}}, |
| {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {literal}}, |
| }); |
| decoration_mgr->AddDecoration(spv::Op::OpDecorate, std::move(operands)); |
| } |
| |
| // Replaces load instructions with composite construct instructions in all the |
| // users of the loads. |loads_to_composites| is the mapping from each load to |
| // its corresponding OpCompositeConstruct. |
| void ReplaceLoadWithCompositeConstruct( |
| IRContext* context, |
| const std::unordered_map<Instruction*, Instruction*>& loads_to_composites) { |
| for (const auto& load_and_composite : loads_to_composites) { |
| Instruction* load = load_and_composite.first; |
| Instruction* composite_construct = load_and_composite.second; |
| |
| std::vector<Instruction*> users; |
| context->get_def_use_mgr()->ForEachUse( |
| load, [&users, composite_construct](Instruction* user, uint32_t index) { |
| user->GetOperand(index).words[0] = composite_construct->result_id(); |
| users.push_back(user); |
| }); |
| |
| for (Instruction* user : users) |
| context->get_def_use_mgr()->AnalyzeInstUse(user); |
| } |
| } |
| |
| // Returns the storage class of the instruction |var|. |
| spv::StorageClass GetStorageClass(Instruction* var) { |
| return static_cast<spv::StorageClass>( |
| var->GetSingleWordInOperand(kOpVariableStorageClassInOperandIndex)); |
| } |
| |
| } // namespace |
| |
| bool InterfaceVariableScalarReplacement::HasExtraArrayness( |
| Instruction& entry_point, Instruction* var) { |
| spv::ExecutionModel execution_model = |
| static_cast<spv::ExecutionModel>(entry_point.GetSingleWordInOperand(0)); |
| if (execution_model != spv::ExecutionModel::TessellationEvaluation && |
| execution_model != spv::ExecutionModel::TessellationControl) { |
| return false; |
| } |
| if (!context()->get_decoration_mgr()->HasDecoration( |
| var->result_id(), uint32_t(spv::Decoration::Patch))) { |
| if (execution_model == spv::ExecutionModel::TessellationControl) |
| return true; |
| return GetStorageClass(var) != spv::StorageClass::Output; |
| } |
| return false; |
| } |
| |
| bool InterfaceVariableScalarReplacement:: |
| CheckExtraArraynessConflictBetweenEntries(Instruction* interface_var, |
| bool has_extra_arrayness) { |
| if (has_extra_arrayness) { |
| return !ReportErrorIfHasNoExtraArraynessForOtherEntry(interface_var); |
| } |
| return !ReportErrorIfHasExtraArraynessForOtherEntry(interface_var); |
| } |
| |
| bool InterfaceVariableScalarReplacement::GetVariableLocation( |
| Instruction* var, uint32_t* location) { |
| return !context()->get_decoration_mgr()->WhileEachDecoration( |
| var->result_id(), uint32_t(spv::Decoration::Location), |
| [location](const Instruction& inst) { |
| *location = |
| inst.GetSingleWordInOperand(kOpDecorateLiteralInOperandIndex); |
| return false; |
| }); |
| } |
| |
| bool InterfaceVariableScalarReplacement::GetVariableComponent( |
| Instruction* var, uint32_t* component) { |
| return !context()->get_decoration_mgr()->WhileEachDecoration( |
| var->result_id(), uint32_t(spv::Decoration::Component), |
| [component](const Instruction& inst) { |
| *component = |
| inst.GetSingleWordInOperand(kOpDecorateLiteralInOperandIndex); |
| return false; |
| }); |
| } |
| |
| std::vector<Instruction*> |
| InterfaceVariableScalarReplacement::CollectInterfaceVariables( |
| Instruction& entry_point) { |
| std::vector<Instruction*> interface_vars; |
| for (uint32_t i = kOpEntryPointInOperandInterface; |
| i < entry_point.NumInOperands(); ++i) { |
| Instruction* interface_var = context()->get_def_use_mgr()->GetDef( |
| entry_point.GetSingleWordInOperand(i)); |
| assert(interface_var->opcode() == spv::Op::OpVariable); |
| |
| spv::StorageClass storage_class = GetStorageClass(interface_var); |
| if (storage_class != spv::StorageClass::Input && |
| storage_class != spv::StorageClass::Output) { |
| continue; |
| } |
| |
| interface_vars.push_back(interface_var); |
| } |
| return interface_vars; |
| } |
| |
| void InterfaceVariableScalarReplacement::KillInstructionAndUsers( |
| Instruction* inst) { |
| if (inst->opcode() == spv::Op::OpEntryPoint) { |
| return; |
| } |
| if (inst->opcode() != spv::Op::OpAccessChain) { |
| context()->KillInst(inst); |
| return; |
| } |
| std::vector<Instruction*> users; |
| context()->get_def_use_mgr()->ForEachUser( |
| inst, [&users](Instruction* user) { users.push_back(user); }); |
| for (auto user : users) { |
| context()->KillInst(user); |
| } |
| context()->KillInst(inst); |
| } |
| |
| void InterfaceVariableScalarReplacement::KillInstructionsAndUsers( |
| const std::vector<Instruction*>& insts) { |
| for (Instruction* inst : insts) { |
| KillInstructionAndUsers(inst); |
| } |
| } |
| |
| void InterfaceVariableScalarReplacement::KillLocationAndComponentDecorations( |
| uint32_t var_id) { |
| context()->get_decoration_mgr()->RemoveDecorationsFrom( |
| var_id, [](const Instruction& inst) { |
| spv::Decoration decoration = spv::Decoration( |
| inst.GetSingleWordInOperand(kOpDecorateDecorationInOperandIndex)); |
| return decoration == spv::Decoration::Location || |
| decoration == spv::Decoration::Component; |
| }); |
| } |
| |
| bool InterfaceVariableScalarReplacement::ReplaceInterfaceVariableWithScalars( |
| Instruction* interface_var, Instruction* interface_var_type, |
| uint32_t location, uint32_t component, uint32_t extra_array_length) { |
| NestedCompositeComponents scalar_interface_vars = |
| CreateScalarInterfaceVarsForReplacement(interface_var_type, |
| GetStorageClass(interface_var), |
| extra_array_length); |
| |
| AddLocationAndComponentDecorations(scalar_interface_vars, &location, |
| component); |
| KillLocationAndComponentDecorations(interface_var->result_id()); |
| |
| if (!ReplaceInterfaceVarWith(interface_var, extra_array_length, |
| scalar_interface_vars)) { |
| return false; |
| } |
| |
| context()->KillInst(interface_var); |
| return true; |
| } |
| |
| bool InterfaceVariableScalarReplacement::ReplaceInterfaceVarWith( |
| Instruction* interface_var, uint32_t extra_array_length, |
| const NestedCompositeComponents& scalar_interface_vars) { |
| std::vector<Instruction*> users; |
| context()->get_def_use_mgr()->ForEachUser( |
| interface_var, [&users](Instruction* user) { users.push_back(user); }); |
| |
| std::vector<uint32_t> interface_var_component_indices; |
| std::unordered_map<Instruction*, Instruction*> loads_to_composites; |
| std::unordered_map<Instruction*, Instruction*> |
| loads_for_access_chain_to_composites; |
| if (extra_array_length != 0) { |
| // Note that the extra arrayness is the first dimension of the array |
| // interface variable. |
| for (uint32_t index = 0; index < extra_array_length; ++index) { |
| std::unordered_map<Instruction*, Instruction*> loads_to_component_values; |
| if (!ReplaceComponentsOfInterfaceVarWith( |
| interface_var, users, scalar_interface_vars, |
| interface_var_component_indices, &index, |
| &loads_to_component_values, |
| &loads_for_access_chain_to_composites)) { |
| return false; |
| } |
| AddComponentsToCompositesForLoads(loads_to_component_values, |
| &loads_to_composites, 0); |
| } |
| } else if (!ReplaceComponentsOfInterfaceVarWith( |
| interface_var, users, scalar_interface_vars, |
| interface_var_component_indices, nullptr, &loads_to_composites, |
| &loads_for_access_chain_to_composites)) { |
| return false; |
| } |
| |
| ReplaceLoadWithCompositeConstruct(context(), loads_to_composites); |
| ReplaceLoadWithCompositeConstruct(context(), |
| loads_for_access_chain_to_composites); |
| |
| KillInstructionsAndUsers(users); |
| return true; |
| } |
| |
| void InterfaceVariableScalarReplacement::AddLocationAndComponentDecorations( |
| const NestedCompositeComponents& vars, uint32_t* location, |
| uint32_t component) { |
| if (!vars.HasMultipleComponents()) { |
| uint32_t var_id = vars.GetComponentVariable()->result_id(); |
| CreateDecoration(context()->get_decoration_mgr(), var_id, |
| spv::Decoration::Location, *location); |
| CreateDecoration(context()->get_decoration_mgr(), var_id, |
| spv::Decoration::Component, component); |
| ++(*location); |
| return; |
| } |
| for (const auto& var : vars.GetComponents()) { |
| AddLocationAndComponentDecorations(var, location, component); |
| } |
| } |
| |
| bool InterfaceVariableScalarReplacement::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) { |
| if (!scalar_interface_vars.HasMultipleComponents()) { |
| for (Instruction* interface_var_user : interface_var_users) { |
| if (!ReplaceComponentOfInterfaceVarWith( |
| interface_var, interface_var_user, |
| scalar_interface_vars.GetComponentVariable(), |
| interface_var_component_indices, extra_array_index, |
| loads_to_composites, loads_for_access_chain_to_composites)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| return ReplaceMultipleComponentsOfInterfaceVarWith( |
| interface_var, interface_var_users, scalar_interface_vars.GetComponents(), |
| interface_var_component_indices, extra_array_index, loads_to_composites, |
| loads_for_access_chain_to_composites); |
| } |
| |
| bool InterfaceVariableScalarReplacement:: |
| 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) { |
| for (uint32_t i = 0; i < components.size(); ++i) { |
| interface_var_component_indices.push_back(i); |
| std::unordered_map<Instruction*, Instruction*> loads_to_component_values; |
| std::unordered_map<Instruction*, Instruction*> |
| loads_for_access_chain_to_component_values; |
| if (!ReplaceComponentsOfInterfaceVarWith( |
| interface_var, interface_var_users, components[i], |
| interface_var_component_indices, extra_array_index, |
| &loads_to_component_values, |
| &loads_for_access_chain_to_component_values)) { |
| return false; |
| } |
| interface_var_component_indices.pop_back(); |
| |
| uint32_t depth_to_component = |
| static_cast<uint32_t>(interface_var_component_indices.size()); |
| AddComponentsToCompositesForLoads( |
| loads_for_access_chain_to_component_values, |
| loads_for_access_chain_to_composites, depth_to_component); |
| if (extra_array_index) ++depth_to_component; |
| AddComponentsToCompositesForLoads(loads_to_component_values, |
| loads_to_composites, depth_to_component); |
| } |
| return true; |
| } |
| |
| bool InterfaceVariableScalarReplacement::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) { |
| spv::Op opcode = interface_var_user->opcode(); |
| if (opcode == spv::Op::OpStore) { |
| uint32_t value_id = interface_var_user->GetSingleWordInOperand(1); |
| StoreComponentOfValueToScalarVar(value_id, interface_var_component_indices, |
| scalar_var, extra_array_index, |
| interface_var_user); |
| return true; |
| } |
| if (opcode == spv::Op::OpLoad) { |
| Instruction* scalar_load = |
| LoadScalarVar(scalar_var, extra_array_index, interface_var_user); |
| loads_to_component_values->insert({interface_var_user, scalar_load}); |
| return true; |
| } |
| |
| // Copy OpName and annotation instructions only once. Therefore, we create |
| // them only for the first element of the extra array. |
| if (extra_array_index && *extra_array_index != 0) return true; |
| |
| if (opcode == spv::Op::OpDecorateId || opcode == spv::Op::OpDecorateString || |
| opcode == spv::Op::OpDecorate) { |
| CloneAnnotationForVariable(interface_var_user, scalar_var->result_id()); |
| return true; |
| } |
| |
| if (opcode == spv::Op::OpName) { |
| std::unique_ptr<Instruction> new_inst(interface_var_user->Clone(context())); |
| new_inst->SetInOperand(0, {scalar_var->result_id()}); |
| context()->AddDebug2Inst(std::move(new_inst)); |
| return true; |
| } |
| |
| if (opcode == spv::Op::OpEntryPoint) { |
| return ReplaceInterfaceVarInEntryPoint(interface_var, interface_var_user, |
| scalar_var->result_id()); |
| } |
| |
| if (opcode == spv::Op::OpAccessChain) { |
| ReplaceAccessChainWith(interface_var_user, interface_var_component_indices, |
| scalar_var, |
| loads_for_access_chain_to_component_values); |
| return true; |
| } |
| |
| std::string message("Unhandled instruction"); |
| message += "\n " + interface_var_user->PrettyPrint( |
| SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); |
| message += |
| "\nfor interface variable scalar replacement\n " + |
| interface_var->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); |
| context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); |
| return false; |
| } |
| |
| void InterfaceVariableScalarReplacement::UseBaseAccessChainForAccessChain( |
| Instruction* access_chain, Instruction* base_access_chain) { |
| assert(base_access_chain->opcode() == spv::Op::OpAccessChain && |
| access_chain->opcode() == spv::Op::OpAccessChain && |
| access_chain->GetSingleWordInOperand(0) == |
| base_access_chain->result_id()); |
| Instruction::OperandList new_operands; |
| for (uint32_t i = 0; i < base_access_chain->NumInOperands(); ++i) { |
| new_operands.emplace_back(base_access_chain->GetInOperand(i)); |
| } |
| for (uint32_t i = 1; i < access_chain->NumInOperands(); ++i) { |
| new_operands.emplace_back(access_chain->GetInOperand(i)); |
| } |
| access_chain->SetInOperands(std::move(new_operands)); |
| } |
| |
| Instruction* InterfaceVariableScalarReplacement::CreateAccessChainToVar( |
| uint32_t var_type_id, Instruction* var, |
| const std::vector<uint32_t>& index_ids, Instruction* insert_before, |
| uint32_t* component_type_id) { |
| analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); |
| *component_type_id = GetComponentTypeOfArrayMatrix( |
| def_use_mgr, var_type_id, static_cast<uint32_t>(index_ids.size())); |
| |
| uint32_t ptr_type_id = |
| GetPointerType(*component_type_id, GetStorageClass(var)); |
| |
| std::unique_ptr<Instruction> new_access_chain(new Instruction( |
| context(), spv::Op::OpAccessChain, ptr_type_id, TakeNextId(), |
| std::initializer_list<Operand>{ |
| {SPV_OPERAND_TYPE_ID, {var->result_id()}}})); |
| for (uint32_t index_id : index_ids) { |
| new_access_chain->AddOperand({SPV_OPERAND_TYPE_ID, {index_id}}); |
| } |
| |
| Instruction* inst = new_access_chain.get(); |
| def_use_mgr->AnalyzeInstDefUse(inst); |
| insert_before->InsertBefore(std::move(new_access_chain)); |
| return inst; |
| } |
| |
| Instruction* InterfaceVariableScalarReplacement::CreateAccessChainWithIndex( |
| uint32_t component_type_id, Instruction* var, uint32_t index, |
| Instruction* insert_before) { |
| uint32_t ptr_type_id = |
| GetPointerType(component_type_id, GetStorageClass(var)); |
| uint32_t index_id = context()->get_constant_mgr()->GetUIntConstId(index); |
| std::unique_ptr<Instruction> new_access_chain(new Instruction( |
| context(), spv::Op::OpAccessChain, ptr_type_id, TakeNextId(), |
| std::initializer_list<Operand>{ |
| {SPV_OPERAND_TYPE_ID, {var->result_id()}}, |
| {SPV_OPERAND_TYPE_ID, {index_id}}, |
| })); |
| Instruction* inst = new_access_chain.get(); |
| context()->get_def_use_mgr()->AnalyzeInstDefUse(inst); |
| insert_before->InsertBefore(std::move(new_access_chain)); |
| return inst; |
| } |
| |
| void InterfaceVariableScalarReplacement::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) { |
| std::vector<uint32_t> indexes; |
| for (uint32_t i = 1; i < access_chain->NumInOperands(); ++i) { |
| indexes.push_back(access_chain->GetSingleWordInOperand(i)); |
| } |
| |
| // Note that we have a strong assumption that |access_chain| has only a single |
| // index that is for the extra arrayness. |
| context()->get_def_use_mgr()->ForEachUser( |
| access_chain, |
| [this, access_chain, &indexes, &interface_var_component_indices, |
| scalar_var, loads_to_component_values](Instruction* user) { |
| switch (user->opcode()) { |
| case spv::Op::OpAccessChain: { |
| UseBaseAccessChainForAccessChain(user, access_chain); |
| ReplaceAccessChainWith(user, interface_var_component_indices, |
| scalar_var, loads_to_component_values); |
| return; |
| } |
| case spv::Op::OpStore: { |
| uint32_t value_id = user->GetSingleWordInOperand(1); |
| StoreComponentOfValueToAccessChainToScalarVar( |
| value_id, interface_var_component_indices, scalar_var, indexes, |
| user); |
| return; |
| } |
| case spv::Op::OpLoad: { |
| Instruction* value = |
| LoadAccessChainToVar(scalar_var, indexes, user); |
| loads_to_component_values->insert({user, value}); |
| return; |
| } |
| default: |
| break; |
| } |
| }); |
| } |
| |
| void InterfaceVariableScalarReplacement::CloneAnnotationForVariable( |
| Instruction* annotation_inst, uint32_t var_id) { |
| assert(annotation_inst->opcode() == spv::Op::OpDecorate || |
| annotation_inst->opcode() == spv::Op::OpDecorateId || |
| annotation_inst->opcode() == spv::Op::OpDecorateString); |
| std::unique_ptr<Instruction> new_inst(annotation_inst->Clone(context())); |
| new_inst->SetInOperand(0, {var_id}); |
| context()->AddAnnotationInst(std::move(new_inst)); |
| } |
| |
| bool InterfaceVariableScalarReplacement::ReplaceInterfaceVarInEntryPoint( |
| Instruction* interface_var, Instruction* entry_point, |
| uint32_t scalar_var_id) { |
| analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); |
| uint32_t interface_var_id = interface_var->result_id(); |
| if (interface_vars_removed_from_entry_point_operands_.find( |
| interface_var_id) != |
| interface_vars_removed_from_entry_point_operands_.end()) { |
| entry_point->AddOperand({SPV_OPERAND_TYPE_ID, {scalar_var_id}}); |
| def_use_mgr->AnalyzeInstUse(entry_point); |
| return true; |
| } |
| |
| bool success = !entry_point->WhileEachInId( |
| [&interface_var_id, &scalar_var_id](uint32_t* id) { |
| if (*id == interface_var_id) { |
| *id = scalar_var_id; |
| return false; |
| } |
| return true; |
| }); |
| if (!success) { |
| std::string message( |
| "interface variable is not an operand of the entry point"); |
| message += "\n " + interface_var->PrettyPrint( |
| SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); |
| message += "\n " + entry_point->PrettyPrint( |
| SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); |
| context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); |
| return false; |
| } |
| |
| def_use_mgr->AnalyzeInstUse(entry_point); |
| interface_vars_removed_from_entry_point_operands_.insert(interface_var_id); |
| return true; |
| } |
| |
| uint32_t InterfaceVariableScalarReplacement::GetPointeeTypeIdOfVar( |
| Instruction* var) { |
| assert(var->opcode() == spv::Op::OpVariable); |
| |
| uint32_t ptr_type_id = var->type_id(); |
| analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); |
| Instruction* ptr_type_inst = def_use_mgr->GetDef(ptr_type_id); |
| |
| assert(ptr_type_inst->opcode() == spv::Op::OpTypePointer && |
| "Variable must have a pointer type."); |
| return ptr_type_inst->GetSingleWordInOperand(kOpTypePtrTypeInOperandIndex); |
| } |
| |
| void InterfaceVariableScalarReplacement::StoreComponentOfValueToScalarVar( |
| uint32_t value_id, const std::vector<uint32_t>& component_indices, |
| Instruction* scalar_var, const uint32_t* extra_array_index, |
| Instruction* insert_before) { |
| uint32_t component_type_id = GetPointeeTypeIdOfVar(scalar_var); |
| Instruction* ptr = scalar_var; |
| if (extra_array_index) { |
| auto* ty_mgr = context()->get_type_mgr(); |
| analysis::Array* array_type = ty_mgr->GetType(component_type_id)->AsArray(); |
| assert(array_type != nullptr); |
| component_type_id = ty_mgr->GetTypeInstruction(array_type->element_type()); |
| ptr = CreateAccessChainWithIndex(component_type_id, scalar_var, |
| *extra_array_index, insert_before); |
| } |
| |
| StoreComponentOfValueTo(component_type_id, value_id, component_indices, ptr, |
| extra_array_index, insert_before); |
| } |
| |
| Instruction* InterfaceVariableScalarReplacement::LoadScalarVar( |
| Instruction* scalar_var, const uint32_t* extra_array_index, |
| Instruction* insert_before) { |
| uint32_t component_type_id = GetPointeeTypeIdOfVar(scalar_var); |
| Instruction* ptr = scalar_var; |
| if (extra_array_index) { |
| auto* ty_mgr = context()->get_type_mgr(); |
| analysis::Array* array_type = ty_mgr->GetType(component_type_id)->AsArray(); |
| assert(array_type != nullptr); |
| component_type_id = ty_mgr->GetTypeInstruction(array_type->element_type()); |
| ptr = CreateAccessChainWithIndex(component_type_id, scalar_var, |
| *extra_array_index, insert_before); |
| } |
| |
| return CreateLoad(component_type_id, ptr, insert_before); |
| } |
| |
| Instruction* InterfaceVariableScalarReplacement::CreateLoad( |
| uint32_t type_id, Instruction* ptr, Instruction* insert_before) { |
| std::unique_ptr<Instruction> load( |
| new Instruction(context(), spv::Op::OpLoad, type_id, TakeNextId(), |
| std::initializer_list<Operand>{ |
| {SPV_OPERAND_TYPE_ID, {ptr->result_id()}}})); |
| Instruction* load_inst = load.get(); |
| context()->get_def_use_mgr()->AnalyzeInstDefUse(load_inst); |
| insert_before->InsertBefore(std::move(load)); |
| return load_inst; |
| } |
| |
| void InterfaceVariableScalarReplacement::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) { |
| std::unique_ptr<Instruction> composite_extract(CreateCompositeExtract( |
| component_type_id, value_id, component_indices, extra_array_index)); |
| |
| std::unique_ptr<Instruction> new_store( |
| new Instruction(context(), spv::Op::OpStore)); |
| new_store->AddOperand({SPV_OPERAND_TYPE_ID, {ptr->result_id()}}); |
| new_store->AddOperand( |
| {SPV_OPERAND_TYPE_ID, {composite_extract->result_id()}}); |
| |
| analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); |
| def_use_mgr->AnalyzeInstDefUse(composite_extract.get()); |
| def_use_mgr->AnalyzeInstDefUse(new_store.get()); |
| |
| insert_before->InsertBefore(std::move(composite_extract)); |
| insert_before->InsertBefore(std::move(new_store)); |
| } |
| |
| Instruction* InterfaceVariableScalarReplacement::CreateCompositeExtract( |
| uint32_t type_id, uint32_t composite_id, |
| const std::vector<uint32_t>& indexes, const uint32_t* extra_first_index) { |
| uint32_t component_id = TakeNextId(); |
| Instruction* composite_extract = new Instruction( |
| context(), spv::Op::OpCompositeExtract, type_id, component_id, |
| std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {composite_id}}}); |
| if (extra_first_index) { |
| composite_extract->AddOperand( |
| {SPV_OPERAND_TYPE_LITERAL_INTEGER, {*extra_first_index}}); |
| } |
| for (uint32_t index : indexes) { |
| composite_extract->AddOperand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}); |
| } |
| return composite_extract; |
| } |
| |
| void InterfaceVariableScalarReplacement:: |
| 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) { |
| uint32_t component_type_id = GetPointeeTypeIdOfVar(scalar_var); |
| Instruction* ptr = scalar_var; |
| if (!access_chain_indices.empty()) { |
| ptr = CreateAccessChainToVar(component_type_id, scalar_var, |
| access_chain_indices, insert_before, |
| &component_type_id); |
| } |
| |
| StoreComponentOfValueTo(component_type_id, value_id, component_indices, ptr, |
| nullptr, insert_before); |
| } |
| |
| Instruction* InterfaceVariableScalarReplacement::LoadAccessChainToVar( |
| Instruction* var, const std::vector<uint32_t>& indexes, |
| Instruction* insert_before) { |
| uint32_t component_type_id = GetPointeeTypeIdOfVar(var); |
| Instruction* ptr = var; |
| if (!indexes.empty()) { |
| ptr = CreateAccessChainToVar(component_type_id, var, indexes, insert_before, |
| &component_type_id); |
| } |
| |
| return CreateLoad(component_type_id, ptr, insert_before); |
| } |
| |
| Instruction* |
| InterfaceVariableScalarReplacement::CreateCompositeConstructForComponentOfLoad( |
| Instruction* load, uint32_t depth_to_component) { |
| analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); |
| uint32_t type_id = load->type_id(); |
| if (depth_to_component != 0) { |
| type_id = GetComponentTypeOfArrayMatrix(def_use_mgr, load->type_id(), |
| depth_to_component); |
| } |
| uint32_t new_id = context()->TakeNextId(); |
| std::unique_ptr<Instruction> new_composite_construct(new Instruction( |
| context(), spv::Op::OpCompositeConstruct, type_id, new_id, {})); |
| Instruction* composite_construct = new_composite_construct.get(); |
| def_use_mgr->AnalyzeInstDefUse(composite_construct); |
| |
| // Insert |new_composite_construct| after |load|. When there are multiple |
| // recursive composite construct instructions for a load, we have to place the |
| // composite construct with a lower depth later because it constructs the |
| // composite that contains other composites with lower depths. |
| auto* insert_before = load->NextNode(); |
| while (true) { |
| auto itr = |
| composite_ids_to_component_depths.find(insert_before->result_id()); |
| if (itr == composite_ids_to_component_depths.end()) break; |
| if (itr->second <= depth_to_component) break; |
| insert_before = insert_before->NextNode(); |
| } |
| insert_before->InsertBefore(std::move(new_composite_construct)); |
| composite_ids_to_component_depths.insert({new_id, depth_to_component}); |
| return composite_construct; |
| } |
| |
| void InterfaceVariableScalarReplacement::AddComponentsToCompositesForLoads( |
| const std::unordered_map<Instruction*, Instruction*>& |
| loads_to_component_values, |
| std::unordered_map<Instruction*, Instruction*>* loads_to_composites, |
| uint32_t depth_to_component) { |
| analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); |
| for (auto& load_and_component_vale : loads_to_component_values) { |
| Instruction* load = load_and_component_vale.first; |
| Instruction* component_value = load_and_component_vale.second; |
| Instruction* composite_construct = nullptr; |
| auto itr = loads_to_composites->find(load); |
| if (itr == loads_to_composites->end()) { |
| composite_construct = |
| CreateCompositeConstructForComponentOfLoad(load, depth_to_component); |
| loads_to_composites->insert({load, composite_construct}); |
| } else { |
| composite_construct = itr->second; |
| } |
| composite_construct->AddOperand( |
| {SPV_OPERAND_TYPE_ID, {component_value->result_id()}}); |
| def_use_mgr->AnalyzeInstDefUse(composite_construct); |
| } |
| } |
| |
| uint32_t InterfaceVariableScalarReplacement::GetArrayType( |
| uint32_t elem_type_id, uint32_t array_length) { |
| analysis::Type* elem_type = context()->get_type_mgr()->GetType(elem_type_id); |
| uint32_t array_length_id = |
| context()->get_constant_mgr()->GetUIntConstId(array_length); |
| analysis::Array array_type( |
| elem_type, |
| analysis::Array::LengthInfo{array_length_id, {0, array_length}}); |
| return context()->get_type_mgr()->GetTypeInstruction(&array_type); |
| } |
| |
| uint32_t InterfaceVariableScalarReplacement::GetPointerType( |
| uint32_t type_id, spv::StorageClass storage_class) { |
| analysis::Type* type = context()->get_type_mgr()->GetType(type_id); |
| analysis::Pointer ptr_type(type, storage_class); |
| return context()->get_type_mgr()->GetTypeInstruction(&ptr_type); |
| } |
| |
| InterfaceVariableScalarReplacement::NestedCompositeComponents |
| InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForArray( |
| Instruction* interface_var_type, spv::StorageClass storage_class, |
| uint32_t extra_array_length) { |
| assert(interface_var_type->opcode() == spv::Op::OpTypeArray); |
| |
| analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); |
| uint32_t array_length = GetArrayLength(def_use_mgr, interface_var_type); |
| Instruction* elem_type = GetArrayElementType(def_use_mgr, interface_var_type); |
| |
| NestedCompositeComponents scalar_vars; |
| while (array_length > 0) { |
| NestedCompositeComponents scalar_vars_for_element = |
| CreateScalarInterfaceVarsForReplacement(elem_type, storage_class, |
| extra_array_length); |
| scalar_vars.AddComponent(scalar_vars_for_element); |
| --array_length; |
| } |
| return scalar_vars; |
| } |
| |
| InterfaceVariableScalarReplacement::NestedCompositeComponents |
| InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForMatrix( |
| Instruction* interface_var_type, spv::StorageClass storage_class, |
| uint32_t extra_array_length) { |
| assert(interface_var_type->opcode() == spv::Op::OpTypeMatrix); |
| |
| analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); |
| uint32_t column_count = interface_var_type->GetSingleWordInOperand( |
| kOpTypeMatrixColCountInOperandIndex); |
| Instruction* column_type = |
| GetMatrixColumnType(def_use_mgr, interface_var_type); |
| |
| NestedCompositeComponents scalar_vars; |
| while (column_count > 0) { |
| NestedCompositeComponents scalar_vars_for_column = |
| CreateScalarInterfaceVarsForReplacement(column_type, storage_class, |
| extra_array_length); |
| scalar_vars.AddComponent(scalar_vars_for_column); |
| --column_count; |
| } |
| return scalar_vars; |
| } |
| |
| InterfaceVariableScalarReplacement::NestedCompositeComponents |
| InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForReplacement( |
| Instruction* interface_var_type, spv::StorageClass storage_class, |
| uint32_t extra_array_length) { |
| // Handle array case. |
| if (interface_var_type->opcode() == spv::Op::OpTypeArray) { |
| return CreateScalarInterfaceVarsForArray(interface_var_type, storage_class, |
| extra_array_length); |
| } |
| |
| // Handle matrix case. |
| if (interface_var_type->opcode() == spv::Op::OpTypeMatrix) { |
| return CreateScalarInterfaceVarsForMatrix(interface_var_type, storage_class, |
| extra_array_length); |
| } |
| |
| // Handle scalar or vector case. |
| NestedCompositeComponents scalar_var; |
| uint32_t type_id = interface_var_type->result_id(); |
| if (extra_array_length != 0) { |
| type_id = GetArrayType(type_id, extra_array_length); |
| } |
| uint32_t ptr_type_id = |
| context()->get_type_mgr()->FindPointerToType(type_id, storage_class); |
| uint32_t id = TakeNextId(); |
| std::unique_ptr<Instruction> variable( |
| new Instruction(context(), spv::Op::OpVariable, ptr_type_id, id, |
| std::initializer_list<Operand>{ |
| {SPV_OPERAND_TYPE_STORAGE_CLASS, |
| {static_cast<uint32_t>(storage_class)}}})); |
| scalar_var.SetSingleComponentVariable(variable.get()); |
| context()->AddGlobalValue(std::move(variable)); |
| return scalar_var; |
| } |
| |
| Instruction* InterfaceVariableScalarReplacement::GetTypeOfVariable( |
| Instruction* var) { |
| uint32_t pointee_type_id = GetPointeeTypeIdOfVar(var); |
| analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); |
| return def_use_mgr->GetDef(pointee_type_id); |
| } |
| |
| Pass::Status InterfaceVariableScalarReplacement::Process() { |
| Pass::Status status = Status::SuccessWithoutChange; |
| for (Instruction& entry_point : get_module()->entry_points()) { |
| status = |
| CombineStatus(status, ReplaceInterfaceVarsWithScalars(entry_point)); |
| } |
| return status; |
| } |
| |
| bool InterfaceVariableScalarReplacement:: |
| ReportErrorIfHasExtraArraynessForOtherEntry(Instruction* var) { |
| if (vars_with_extra_arrayness.find(var) == vars_with_extra_arrayness.end()) |
| return false; |
| |
| std::string message( |
| "A variable is arrayed for an entry point but it is not " |
| "arrayed for another entry point"); |
| message += |
| "\n " + var->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); |
| context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); |
| return true; |
| } |
| |
| bool InterfaceVariableScalarReplacement:: |
| ReportErrorIfHasNoExtraArraynessForOtherEntry(Instruction* var) { |
| if (vars_without_extra_arrayness.find(var) == |
| vars_without_extra_arrayness.end()) |
| return false; |
| |
| std::string message( |
| "A variable is not arrayed for an entry point but it is " |
| "arrayed for another entry point"); |
| message += |
| "\n " + var->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); |
| context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); |
| return true; |
| } |
| |
| Pass::Status |
| InterfaceVariableScalarReplacement::ReplaceInterfaceVarsWithScalars( |
| Instruction& entry_point) { |
| std::vector<Instruction*> interface_vars = |
| CollectInterfaceVariables(entry_point); |
| |
| Pass::Status status = Status::SuccessWithoutChange; |
| for (Instruction* interface_var : interface_vars) { |
| uint32_t location, component; |
| if (!GetVariableLocation(interface_var, &location)) continue; |
| if (!GetVariableComponent(interface_var, &component)) component = 0; |
| |
| Instruction* interface_var_type = GetTypeOfVariable(interface_var); |
| uint32_t extra_array_length = 0; |
| if (HasExtraArrayness(entry_point, interface_var)) { |
| extra_array_length = |
| GetArrayLength(context()->get_def_use_mgr(), interface_var_type); |
| interface_var_type = |
| GetArrayElementType(context()->get_def_use_mgr(), interface_var_type); |
| vars_with_extra_arrayness.insert(interface_var); |
| } else { |
| vars_without_extra_arrayness.insert(interface_var); |
| } |
| |
| if (!CheckExtraArraynessConflictBetweenEntries(interface_var, |
| extra_array_length != 0)) { |
| return Pass::Status::Failure; |
| } |
| |
| if (interface_var_type->opcode() != spv::Op::OpTypeArray && |
| interface_var_type->opcode() != spv::Op::OpTypeMatrix) { |
| continue; |
| } |
| |
| if (!ReplaceInterfaceVariableWithScalars(interface_var, interface_var_type, |
| location, component, |
| extra_array_length)) { |
| return Pass::Status::Failure; |
| } |
| status = Pass::Status::SuccessWithChange; |
| } |
| |
| return status; |
| } |
| |
| } // namespace opt |
| } // namespace spvtools |