| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- // Copyright (c) 2018 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/combine_access_chains.h"
- #include <utility>
- #include "source/opt/constants.h"
- #include "source/opt/ir_builder.h"
- #include "source/opt/ir_context.h"
- namespace spvtools {
- namespace opt {
- Pass::Status CombineAccessChains::Process() {
- bool modified = false;
- for (auto& function : *get_module()) {
- modified |= ProcessFunction(function);
- }
- return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
- }
- bool CombineAccessChains::ProcessFunction(Function& function) {
- if (function.IsDeclaration()) {
- return false;
- }
- bool modified = false;
- cfg()->ForEachBlockInReversePostOrder(
- function.entry().get(), [&modified, this](BasicBlock* block) {
- block->ForEachInst([&modified, this](Instruction* inst) {
- switch (inst->opcode()) {
- case spv::Op::OpAccessChain:
- case spv::Op::OpInBoundsAccessChain:
- case spv::Op::OpPtrAccessChain:
- case spv::Op::OpInBoundsPtrAccessChain:
- modified |= CombineAccessChain(inst);
- break;
- default:
- break;
- }
- });
- });
- return modified;
- }
- uint32_t CombineAccessChains::GetConstantValue(
- const analysis::Constant* constant_inst) {
- if (constant_inst->type()->AsInteger()->width() <= 32) {
- if (constant_inst->type()->AsInteger()->IsSigned()) {
- return static_cast<uint32_t>(constant_inst->GetS32());
- } else {
- return constant_inst->GetU32();
- }
- } else {
- assert(false);
- return 0u;
- }
- }
- uint32_t CombineAccessChains::GetArrayStride(const Instruction* inst) {
- uint32_t array_stride = 0;
- context()->get_decoration_mgr()->WhileEachDecoration(
- inst->type_id(), uint32_t(spv::Decoration::ArrayStride),
- [&array_stride](const Instruction& decoration) {
- assert(decoration.opcode() != spv::Op::OpDecorateId);
- if (decoration.opcode() == spv::Op::OpDecorate) {
- array_stride = decoration.GetSingleWordInOperand(1);
- } else {
- array_stride = decoration.GetSingleWordInOperand(2);
- }
- return false;
- });
- return array_stride;
- }
- const analysis::Type* CombineAccessChains::GetIndexedType(Instruction* inst) {
- analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
- analysis::TypeManager* type_mgr = context()->get_type_mgr();
- Instruction* base_ptr = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
- const analysis::Type* type = type_mgr->GetType(base_ptr->type_id());
- assert(type->AsPointer());
- type = type->AsPointer()->pointee_type();
- std::vector<uint32_t> element_indices;
- uint32_t starting_index = 1;
- if (IsPtrAccessChain(inst->opcode())) {
- // Skip the first index of OpPtrAccessChain as it does not affect type
- // resolution.
- starting_index = 2;
- }
- for (uint32_t i = starting_index; i < inst->NumInOperands(); ++i) {
- Instruction* index_inst =
- def_use_mgr->GetDef(inst->GetSingleWordInOperand(i));
- const analysis::Constant* index_constant =
- context()->get_constant_mgr()->GetConstantFromInst(index_inst);
- if (index_constant) {
- uint32_t index_value = GetConstantValue(index_constant);
- element_indices.push_back(index_value);
- } else {
- // This index must not matter to resolve the type in valid SPIR-V.
- element_indices.push_back(0);
- }
- }
- type = type_mgr->GetMemberType(type, element_indices);
- return type;
- }
- bool CombineAccessChains::CombineIndices(Instruction* ptr_input,
- Instruction* inst,
- std::vector<Operand>* new_operands) {
- analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
- analysis::ConstantManager* constant_mgr = context()->get_constant_mgr();
- Instruction* last_index_inst = def_use_mgr->GetDef(
- ptr_input->GetSingleWordInOperand(ptr_input->NumInOperands() - 1));
- const analysis::Constant* last_index_constant =
- constant_mgr->GetConstantFromInst(last_index_inst);
- Instruction* element_inst =
- def_use_mgr->GetDef(inst->GetSingleWordInOperand(1));
- const analysis::Constant* element_constant =
- constant_mgr->GetConstantFromInst(element_inst);
- // Combine the last index of the AccessChain (|ptr_inst|) with the element
- // operand of the PtrAccessChain (|inst|).
- const bool combining_element_operands =
- IsPtrAccessChain(inst->opcode()) &&
- IsPtrAccessChain(ptr_input->opcode()) && ptr_input->NumInOperands() == 2;
- uint32_t new_value_id = 0;
- const analysis::Type* type = GetIndexedType(ptr_input);
- if (last_index_constant && element_constant) {
- // Combine the constants.
- uint32_t new_value = GetConstantValue(last_index_constant) +
- GetConstantValue(element_constant);
- const analysis::Constant* new_value_constant =
- constant_mgr->GetConstant(last_index_constant->type(), {new_value});
- Instruction* new_value_inst =
- constant_mgr->GetDefiningInstruction(new_value_constant);
- new_value_id = new_value_inst->result_id();
- } else if (!type->AsStruct() || combining_element_operands) {
- // Generate an addition of the two indices.
- InstructionBuilder builder(
- context(), inst,
- IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
- Instruction* addition = builder.AddIAdd(last_index_inst->type_id(),
- last_index_inst->result_id(),
- element_inst->result_id());
- new_value_id = addition->result_id();
- } else {
- // Indexing into structs must be constant, so bail out here.
- return false;
- }
- new_operands->push_back({SPV_OPERAND_TYPE_ID, {new_value_id}});
- return true;
- }
- bool CombineAccessChains::CreateNewInputOperands(
- Instruction* ptr_input, Instruction* inst,
- std::vector<Operand>* new_operands) {
- // Start by copying all the input operands of the feeder access chain.
- for (uint32_t i = 0; i != ptr_input->NumInOperands() - 1; ++i) {
- new_operands->push_back(ptr_input->GetInOperand(i));
- }
- // Deal with the last index of the feeder access chain.
- if (IsPtrAccessChain(inst->opcode())) {
- // The last index of the feeder should be combined with the element operand
- // of |inst|.
- if (!CombineIndices(ptr_input, inst, new_operands)) return false;
- } else {
- // The indices aren't being combined so now add the last index operand of
- // |ptr_input|.
- new_operands->push_back(
- ptr_input->GetInOperand(ptr_input->NumInOperands() - 1));
- }
- // Copy the remaining index operands.
- uint32_t starting_index = IsPtrAccessChain(inst->opcode()) ? 2 : 1;
- for (uint32_t i = starting_index; i < inst->NumInOperands(); ++i) {
- new_operands->push_back(inst->GetInOperand(i));
- }
- return true;
- }
- bool CombineAccessChains::CombineAccessChain(Instruction* inst) {
- assert((inst->opcode() == spv::Op::OpPtrAccessChain ||
- inst->opcode() == spv::Op::OpAccessChain ||
- inst->opcode() == spv::Op::OpInBoundsAccessChain ||
- inst->opcode() == spv::Op::OpInBoundsPtrAccessChain) &&
- "Wrong opcode. Expected an access chain.");
- Instruction* ptr_input =
- context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0));
- if (ptr_input->opcode() != spv::Op::OpAccessChain &&
- ptr_input->opcode() != spv::Op::OpInBoundsAccessChain &&
- ptr_input->opcode() != spv::Op::OpPtrAccessChain &&
- ptr_input->opcode() != spv::Op::OpInBoundsPtrAccessChain) {
- return false;
- }
- if (Has64BitIndices(inst) || Has64BitIndices(ptr_input)) return false;
- // Handles the following cases:
- // 1. |ptr_input| is an index-less access chain. Replace the pointer
- // in |inst| with |ptr_input|'s pointer.
- // 2. |inst| is a index-less access chain. Change |inst| to an
- // OpCopyObject.
- // 3. |inst| is not a pointer access chain.
- // |inst|'s indices are appended to |ptr_input|'s indices.
- // 4. |ptr_input| is not pointer access chain.
- // |inst| is a pointer access chain.
- // |inst|'s element operand is combined with the last index in
- // |ptr_input| to form a new operand.
- // 5. |ptr_input| is a pointer access chain.
- // Like the above scenario, |inst|'s element operand is combined
- // with |ptr_input|'s last index. This results is either a
- // combined element operand or combined regular index.
- // TODO(alan-baker): Support this properly. Requires analyzing the
- // size/alignment of the type and converting the stride into an element
- // index.
- uint32_t array_stride = GetArrayStride(ptr_input);
- if (array_stride != 0) return false;
- if (ptr_input->NumInOperands() == 1) {
- // The input is effectively a no-op.
- inst->SetInOperand(0, {ptr_input->GetSingleWordInOperand(0)});
- context()->AnalyzeUses(inst);
- } else if (inst->NumInOperands() == 1) {
- // |inst| is a no-op, change it to a copy. Instruction simplification will
- // clean it up.
- inst->SetOpcode(spv::Op::OpCopyObject);
- } else {
- std::vector<Operand> new_operands;
- if (!CreateNewInputOperands(ptr_input, inst, &new_operands)) return false;
- // Update the instruction.
- inst->SetOpcode(UpdateOpcode(inst->opcode(), ptr_input->opcode()));
- inst->SetInOperands(std::move(new_operands));
- context()->AnalyzeUses(inst);
- }
- return true;
- }
- spv::Op CombineAccessChains::UpdateOpcode(spv::Op base_opcode,
- spv::Op input_opcode) {
- auto IsInBounds = [](spv::Op opcode) {
- return opcode == spv::Op::OpInBoundsPtrAccessChain ||
- opcode == spv::Op::OpInBoundsAccessChain;
- };
- if (input_opcode == spv::Op::OpInBoundsPtrAccessChain) {
- if (!IsInBounds(base_opcode)) return spv::Op::OpPtrAccessChain;
- } else if (input_opcode == spv::Op::OpInBoundsAccessChain) {
- if (!IsInBounds(base_opcode)) return spv::Op::OpAccessChain;
- }
- return input_opcode;
- }
- bool CombineAccessChains::IsPtrAccessChain(spv::Op opcode) {
- return opcode == spv::Op::OpPtrAccessChain ||
- opcode == spv::Op::OpInBoundsPtrAccessChain;
- }
- bool CombineAccessChains::Has64BitIndices(Instruction* inst) {
- for (uint32_t i = 1; i < inst->NumInOperands(); ++i) {
- Instruction* index_inst =
- context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(i));
- const analysis::Type* index_type =
- context()->get_type_mgr()->GetType(index_inst->type_id());
- if (!index_type->AsInteger() || index_type->AsInteger()->width() != 32)
- return true;
- }
- return false;
- }
- } // namespace opt
- } // namespace spvtools
|