• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Process()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