| // Copyright (c) 2021 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/desc_sroa_util.h" | |
| namespace spvtools { | |
| namespace opt { | |
| namespace { | |
| constexpr uint32_t kOpAccessChainInOperandIndexes = 1; | |
| // Returns the length of array type |type|. | |
| uint32_t GetLengthOfArrayType(IRContext* context, Instruction* type) { | |
| assert(type->opcode() == spv::Op::OpTypeArray && "type must be array"); | |
| uint32_t length_id = type->GetSingleWordInOperand(1); | |
| const analysis::Constant* length_const = | |
| context->get_constant_mgr()->FindDeclaredConstant(length_id); | |
| assert(length_const != nullptr); | |
| return length_const->GetU32(); | |
| } | |
| bool HasDescriptorDecorations(IRContext* context, Instruction* var) { | |
| const auto& decoration_mgr = context->get_decoration_mgr(); | |
| return decoration_mgr->HasDecoration( | |
| var->result_id(), uint32_t(spv::Decoration::DescriptorSet)) && | |
| decoration_mgr->HasDecoration(var->result_id(), | |
| uint32_t(spv::Decoration::Binding)); | |
| } | |
| Instruction* GetVariableType(IRContext* context, Instruction* var) { | |
| if (var->opcode() != spv::Op::OpVariable) { | |
| return nullptr; | |
| } | |
| uint32_t ptr_type_id = var->type_id(); | |
| Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id); | |
| if (ptr_type_inst->opcode() != spv::Op::OpTypePointer) { | |
| return nullptr; | |
| } | |
| uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1); | |
| return context->get_def_use_mgr()->GetDef(var_type_id); | |
| } | |
| } // namespace | |
| namespace descsroautil { | |
| bool IsDescriptorArray(IRContext* context, Instruction* var) { | |
| Instruction* var_type_inst = GetVariableType(context, var); | |
| if (var_type_inst == nullptr) return false; | |
| return var_type_inst->opcode() == spv::Op::OpTypeArray && | |
| HasDescriptorDecorations(context, var); | |
| } | |
| bool IsDescriptorStruct(IRContext* context, Instruction* var) { | |
| Instruction* var_type_inst = GetVariableType(context, var); | |
| if (var_type_inst == nullptr) return false; | |
| while (var_type_inst->opcode() == spv::Op::OpTypeArray) { | |
| var_type_inst = context->get_def_use_mgr()->GetDef( | |
| var_type_inst->GetInOperand(0).AsId()); | |
| } | |
| if (var_type_inst->opcode() != spv::Op::OpTypeStruct) return false; | |
| // All structures with descriptor assignments must be replaced by variables, | |
| // one for each of their members - with the exceptions of buffers. | |
| if (IsTypeOfStructuredBuffer(context, var_type_inst)) { | |
| return false; | |
| } | |
| return HasDescriptorDecorations(context, var); | |
| } | |
| bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type) { | |
| if (type->opcode() != spv::Op::OpTypeStruct) { | |
| return false; | |
| } | |
| // All buffers have offset decorations for members of their structure types. | |
| // This is how we distinguish it from a structure of descriptors. | |
| return context->get_decoration_mgr()->HasDecoration( | |
| type->result_id(), uint32_t(spv::Decoration::Offset)); | |
| } | |
| const analysis::Constant* GetAccessChainIndexAsConst( | |
| IRContext* context, Instruction* access_chain) { | |
| if (access_chain->NumInOperands() <= 1) { | |
| return nullptr; | |
| } | |
| uint32_t idx_id = GetFirstIndexOfAccessChain(access_chain); | |
| const analysis::Constant* idx_const = | |
| context->get_constant_mgr()->FindDeclaredConstant(idx_id); | |
| return idx_const; | |
| } | |
| uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) { | |
| assert(access_chain->NumInOperands() > 1 && | |
| "OpAccessChain does not have Indexes operand"); | |
| return access_chain->GetSingleWordInOperand(kOpAccessChainInOperandIndexes); | |
| } | |
| uint32_t GetNumberOfElementsForArrayOrStruct(IRContext* context, | |
| Instruction* var) { | |
| uint32_t ptr_type_id = var->type_id(); | |
| Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id); | |
| assert(ptr_type_inst->opcode() == spv::Op::OpTypePointer && | |
| "Variable should be a pointer to an array or structure."); | |
| uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1); | |
| Instruction* pointee_type_inst = | |
| context->get_def_use_mgr()->GetDef(pointee_type_id); | |
| if (pointee_type_inst->opcode() == spv::Op::OpTypeArray) { | |
| return GetLengthOfArrayType(context, pointee_type_inst); | |
| } | |
| assert(pointee_type_inst->opcode() == spv::Op::OpTypeStruct && | |
| "Variable should be a pointer to an array or structure."); | |
| return pointee_type_inst->NumInOperands(); | |
| } | |
| } // namespace descsroautil | |
| } // namespace opt | |
| } // namespace spvtools |