| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- // Copyright (c) 2017 Google Inc.
- //
- // 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/flatten_decoration_pass.h"
- #include <cassert>
- #include <memory>
- #include <unordered_map>
- #include <unordered_set>
- #include <utility>
- #include <vector>
- #include "source/opt/ir_context.h"
- namespace spvtools {
- namespace opt {
- using Words = std::vector<uint32_t>;
- using OrderedUsesMap = std::unordered_map<uint32_t, Words>;
- Pass::Status FlattenDecorationPass::Process() {
- bool modified = false;
- // The target Id of OpDecorationGroup instructions.
- // We have to track this separately from its uses, in case it
- // has no uses.
- std::unordered_set<uint32_t> group_ids;
- // Maps a decoration group Id to its GroupDecorate targets, in order
- // of appearance.
- OrderedUsesMap normal_uses;
- // Maps a decoration group Id to its GroupMemberDecorate targets and
- // their indices, in of appearance.
- OrderedUsesMap member_uses;
- auto annotations = context()->annotations();
- // On the first pass, record each OpDecorationGroup with its ordered uses.
- // Rely on unordered_map::operator[] to create its entries on first access.
- for (const auto& inst : annotations) {
- switch (inst.opcode()) {
- case spv::Op::OpDecorationGroup:
- group_ids.insert(inst.result_id());
- break;
- case spv::Op::OpGroupDecorate: {
- Words& words = normal_uses[inst.GetSingleWordInOperand(0)];
- for (uint32_t i = 1; i < inst.NumInOperandWords(); i++) {
- words.push_back(inst.GetSingleWordInOperand(i));
- }
- } break;
- case spv::Op::OpGroupMemberDecorate: {
- Words& words = member_uses[inst.GetSingleWordInOperand(0)];
- for (uint32_t i = 1; i < inst.NumInOperandWords(); i++) {
- words.push_back(inst.GetSingleWordInOperand(i));
- }
- } break;
- default:
- break;
- }
- }
- // On the second pass, replace OpDecorationGroup and its uses with
- // equivalent normal and struct member uses.
- auto inst_iter = annotations.begin();
- // We have to re-evaluate the end pointer
- while (inst_iter != context()->annotations().end()) {
- // Should we replace this instruction?
- bool replace = false;
- switch (inst_iter->opcode()) {
- case spv::Op::OpDecorationGroup:
- case spv::Op::OpGroupDecorate:
- case spv::Op::OpGroupMemberDecorate:
- replace = true;
- break;
- case spv::Op::OpDecorate: {
- // If this decoration targets a group, then replace it
- // by sets of normal and member decorations.
- const uint32_t group = inst_iter->GetSingleWordOperand(0);
- const auto normal_uses_iter = normal_uses.find(group);
- if (normal_uses_iter != normal_uses.end()) {
- for (auto target : normal_uses[group]) {
- std::unique_ptr<Instruction> new_inst(inst_iter->Clone(context()));
- new_inst->SetInOperand(0, Words{target});
- inst_iter = inst_iter.InsertBefore(std::move(new_inst));
- ++inst_iter;
- replace = true;
- }
- }
- const auto member_uses_iter = member_uses.find(group);
- if (member_uses_iter != member_uses.end()) {
- const Words& member_id_pairs = (*member_uses_iter).second;
- // The collection is a sequence of pairs.
- assert((member_id_pairs.size() % 2) == 0);
- for (size_t i = 0; i < member_id_pairs.size(); i += 2) {
- // Make an OpMemberDecorate instruction for each (target, member)
- // pair.
- const uint32_t target = member_id_pairs[i];
- const uint32_t member = member_id_pairs[i + 1];
- std::vector<Operand> operands;
- operands.push_back(Operand(SPV_OPERAND_TYPE_ID, {target}));
- operands.push_back(
- Operand(SPV_OPERAND_TYPE_LITERAL_INTEGER, {member}));
- auto decoration_operands_iter = inst_iter->begin();
- decoration_operands_iter++; // Skip the group target.
- operands.insert(operands.end(), decoration_operands_iter,
- inst_iter->end());
- std::unique_ptr<Instruction> new_inst(new Instruction(
- context(), spv::Op::OpMemberDecorate, 0, 0, operands));
- inst_iter = inst_iter.InsertBefore(std::move(new_inst));
- ++inst_iter;
- replace = true;
- }
- }
- // If this is an OpDecorate targeting the OpDecorationGroup itself,
- // remove it even if that decoration group itself is not the target of
- // any OpGroupDecorate or OpGroupMemberDecorate.
- if (!replace && group_ids.count(group)) {
- replace = true;
- }
- } break;
- default:
- break;
- }
- if (replace) {
- inst_iter = inst_iter.Erase();
- modified = true;
- } else {
- // Handle the case of decorations unrelated to decoration groups.
- ++inst_iter;
- }
- }
- // Remove OpName instructions which reference the removed group decorations.
- // An OpDecorationGroup instruction might not have been used by an
- // OpGroupDecorate or OpGroupMemberDecorate instruction.
- if (!group_ids.empty()) {
- for (auto debug_inst_iter = context()->debug2_begin();
- debug_inst_iter != context()->debug2_end();) {
- if (debug_inst_iter->opcode() == spv::Op::OpName) {
- const uint32_t target = debug_inst_iter->GetSingleWordOperand(0);
- if (group_ids.count(target)) {
- debug_inst_iter = debug_inst_iter.Erase();
- modified = true;
- } else {
- ++debug_inst_iter;
- }
- }
- }
- }
- return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
- }
- } // namespace opt
- } // namespace spvtools
|