Home | History | Annotate | Download | only in opt
      1 // Copyright (c) 2017 Google Inc.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #include "source/opt/flatten_decoration_pass.h"
     16 
     17 #include <cassert>
     18 #include <memory>
     19 #include <unordered_map>
     20 #include <unordered_set>
     21 #include <utility>
     22 #include <vector>
     23 
     24 #include "source/opt/ir_context.h"
     25 
     26 namespace spvtools {
     27 namespace opt {
     28 
     29 using Words = std::vector<uint32_t>;
     30 using OrderedUsesMap = std::unordered_map<uint32_t, Words>;
     31 
     32 Pass::Status FlattenDecorationPass::Process() {
     33   bool modified = false;
     34 
     35   // The target Id of OpDecorationGroup instructions.
     36   // We have to track this separately from its uses, in case it
     37   // has no uses.
     38   std::unordered_set<uint32_t> group_ids;
     39   // Maps a decoration group Id to its GroupDecorate targets, in order
     40   // of appearance.
     41   OrderedUsesMap normal_uses;
     42   // Maps a decoration group Id to its GroupMemberDecorate targets and
     43   // their indices, in of appearance.
     44   OrderedUsesMap member_uses;
     45 
     46   auto annotations = context()->annotations();
     47 
     48   // On the first pass, record each OpDecorationGroup with its ordered uses.
     49   // Rely on unordered_map::operator[] to create its entries on first access.
     50   for (const auto& inst : annotations) {
     51     switch (inst.opcode()) {
     52       case SpvOp::SpvOpDecorationGroup:
     53         group_ids.insert(inst.result_id());
     54         break;
     55       case SpvOp::SpvOpGroupDecorate: {
     56         Words& words = normal_uses[inst.GetSingleWordInOperand(0)];
     57         for (uint32_t i = 1; i < inst.NumInOperandWords(); i++) {
     58           words.push_back(inst.GetSingleWordInOperand(i));
     59         }
     60       } break;
     61       case SpvOp::SpvOpGroupMemberDecorate: {
     62         Words& words = member_uses[inst.GetSingleWordInOperand(0)];
     63         for (uint32_t i = 1; i < inst.NumInOperandWords(); i++) {
     64           words.push_back(inst.GetSingleWordInOperand(i));
     65         }
     66       } break;
     67       default:
     68         break;
     69     }
     70   }
     71 
     72   // On the second pass, replace OpDecorationGroup and its uses with
     73   // equivalent normal and struct member uses.
     74   auto inst_iter = annotations.begin();
     75   // We have to re-evaluate the end pointer
     76   while (inst_iter != context()->annotations().end()) {
     77     // Should we replace this instruction?
     78     bool replace = false;
     79     switch (inst_iter->opcode()) {
     80       case SpvOp::SpvOpDecorationGroup:
     81       case SpvOp::SpvOpGroupDecorate:
     82       case SpvOp::SpvOpGroupMemberDecorate:
     83         replace = true;
     84         break;
     85       case SpvOp::SpvOpDecorate: {
     86         // If this decoration targets a group, then replace it
     87         // by sets of normal and member decorations.
     88         const uint32_t group = inst_iter->GetSingleWordOperand(0);
     89         const auto normal_uses_iter = normal_uses.find(group);
     90         if (normal_uses_iter != normal_uses.end()) {
     91           for (auto target : normal_uses[group]) {
     92             std::unique_ptr<Instruction> new_inst(inst_iter->Clone(context()));
     93             new_inst->SetInOperand(0, Words{target});
     94             inst_iter = inst_iter.InsertBefore(std::move(new_inst));
     95             ++inst_iter;
     96             replace = true;
     97           }
     98         }
     99         const auto member_uses_iter = member_uses.find(group);
    100         if (member_uses_iter != member_uses.end()) {
    101           const Words& member_id_pairs = (*member_uses_iter).second;
    102           // The collection is a sequence of pairs.
    103           assert((member_id_pairs.size() % 2) == 0);
    104           for (size_t i = 0; i < member_id_pairs.size(); i += 2) {
    105             // Make an OpMemberDecorate instruction for each (target, member)
    106             // pair.
    107             const uint32_t target = member_id_pairs[i];
    108             const uint32_t member = member_id_pairs[i + 1];
    109             std::vector<Operand> operands;
    110             operands.push_back(Operand(SPV_OPERAND_TYPE_ID, {target}));
    111             operands.push_back(
    112                 Operand(SPV_OPERAND_TYPE_LITERAL_INTEGER, {member}));
    113             auto decoration_operands_iter = inst_iter->begin();
    114             decoration_operands_iter++;  // Skip the group target.
    115             operands.insert(operands.end(), decoration_operands_iter,
    116                             inst_iter->end());
    117             std::unique_ptr<Instruction> new_inst(new Instruction(
    118                 context(), SpvOp::SpvOpMemberDecorate, 0, 0, operands));
    119             inst_iter = inst_iter.InsertBefore(std::move(new_inst));
    120             ++inst_iter;
    121             replace = true;
    122           }
    123         }
    124         // If this is an OpDecorate targeting the OpDecorationGroup itself,
    125         // remove it even if that decoration group itself is not the target of
    126         // any OpGroupDecorate or OpGroupMemberDecorate.
    127         if (!replace && group_ids.count(group)) {
    128           replace = true;
    129         }
    130       } break;
    131       default:
    132         break;
    133     }
    134     if (replace) {
    135       inst_iter = inst_iter.Erase();
    136       modified = true;
    137     } else {
    138       // Handle the case of decorations unrelated to decoration groups.
    139       ++inst_iter;
    140     }
    141   }
    142 
    143   // Remove OpName instructions which reference the removed group decorations.
    144   // An OpDecorationGroup instruction might not have been used by an
    145   // OpGroupDecorate or OpGroupMemberDecorate instruction.
    146   if (!group_ids.empty()) {
    147     for (auto debug_inst_iter = context()->debug2_begin();
    148          debug_inst_iter != context()->debug2_end();) {
    149       if (debug_inst_iter->opcode() == SpvOp::SpvOpName) {
    150         const uint32_t target = debug_inst_iter->GetSingleWordOperand(0);
    151         if (group_ids.count(target)) {
    152           debug_inst_iter = debug_inst_iter.Erase();
    153           modified = true;
    154         } else {
    155           ++debug_inst_iter;
    156         }
    157       }
    158     }
    159   }
    160 
    161   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
    162 }
    163 
    164 }  // namespace opt
    165 }  // namespace spvtools
    166