• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017 Pierre Moreau
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/remove_duplicates_pass.h"
16 
17 #include <algorithm>
18 #include <string>
19 #include <unordered_map>
20 #include <unordered_set>
21 #include <vector>
22 
23 #include "source/opcode.h"
24 #include "source/opt/decoration_manager.h"
25 #include "source/opt/ir_context.h"
26 
27 namespace spvtools {
28 namespace opt {
29 
Process()30 Pass::Status RemoveDuplicatesPass::Process() {
31   bool modified = RemoveDuplicateCapabilities();
32   modified |= RemoveDuplicatesExtInstImports();
33   modified |= RemoveDuplicateTypes();
34   modified |= RemoveDuplicateDecorations();
35 
36   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
37 }
38 
RemoveDuplicateCapabilities() const39 bool RemoveDuplicatesPass::RemoveDuplicateCapabilities() const {
40   bool modified = false;
41 
42   if (context()->capabilities().empty()) {
43     return modified;
44   }
45 
46   std::unordered_set<uint32_t> capabilities;
47   for (auto* i = &*context()->capability_begin(); i;) {
48     auto res = capabilities.insert(i->GetSingleWordOperand(0u));
49 
50     if (res.second) {
51       // Never seen before, keep it.
52       i = i->NextNode();
53     } else {
54       // It's a duplicate, remove it.
55       i = context()->KillInst(i);
56       modified = true;
57     }
58   }
59 
60   return modified;
61 }
62 
RemoveDuplicatesExtInstImports() const63 bool RemoveDuplicatesPass::RemoveDuplicatesExtInstImports() const {
64   bool modified = false;
65 
66   if (context()->ext_inst_imports().empty()) {
67     return modified;
68   }
69 
70   std::unordered_map<std::string, spv::Id> ext_inst_imports;
71   for (auto* i = &*context()->ext_inst_import_begin(); i;) {
72     auto res = ext_inst_imports.emplace(i->GetInOperand(0u).AsString(),
73                                         i->result_id());
74     if (res.second) {
75       // Never seen before, keep it.
76       i = i->NextNode();
77     } else {
78       // It's a duplicate, remove it.
79       context()->ReplaceAllUsesWith(i->result_id(), res.first->second);
80       i = context()->KillInst(i);
81       modified = true;
82     }
83   }
84 
85   return modified;
86 }
87 
RemoveDuplicateTypes() const88 bool RemoveDuplicatesPass::RemoveDuplicateTypes() const {
89   bool modified = false;
90 
91   if (context()->types_values().empty()) {
92     return modified;
93   }
94 
95   analysis::TypeManager type_manager(context()->consumer(), context());
96 
97   std::vector<Instruction*> visited_types;
98   std::vector<analysis::ForwardPointer> visited_forward_pointers;
99   std::vector<Instruction*> to_delete;
100   for (auto* i = &*context()->types_values_begin(); i; i = i->NextNode()) {
101     const bool is_i_forward_pointer =
102         i->opcode() == spv::Op::OpTypeForwardPointer;
103 
104     // We only care about types.
105     if (!spvOpcodeGeneratesType(i->opcode()) && !is_i_forward_pointer) {
106       continue;
107     }
108 
109     if (!is_i_forward_pointer) {
110       // Is the current type equal to one of the types we have already visited?
111       spv::Id id_to_keep = 0u;
112       analysis::Type* i_type = type_manager.GetType(i->result_id());
113       assert(i_type);
114       // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the
115       // ResultIdTrie from unify_const_pass.cpp for this.
116       for (auto j : visited_types) {
117         analysis::Type* j_type = type_manager.GetType(j->result_id());
118         assert(j_type);
119         if (*i_type == *j_type) {
120           id_to_keep = j->result_id();
121           break;
122         }
123       }
124 
125       if (id_to_keep == 0u) {
126         // This is a never seen before type, keep it around.
127         visited_types.emplace_back(i);
128       } else {
129         // The same type has already been seen before, remove this one.
130         context()->KillNamesAndDecorates(i->result_id());
131         context()->ReplaceAllUsesWith(i->result_id(), id_to_keep);
132         modified = true;
133         to_delete.emplace_back(i);
134       }
135     } else {
136       analysis::ForwardPointer i_type(
137           i->GetSingleWordInOperand(0u),
138           (spv::StorageClass)i->GetSingleWordInOperand(1u));
139       i_type.SetTargetPointer(
140           type_manager.GetType(i_type.target_id())->AsPointer());
141 
142       // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the
143       // ResultIdTrie from unify_const_pass.cpp for this.
144       const bool found_a_match =
145           std::find(std::begin(visited_forward_pointers),
146                     std::end(visited_forward_pointers),
147                     i_type) != std::end(visited_forward_pointers);
148 
149       if (!found_a_match) {
150         // This is a never seen before type, keep it around.
151         visited_forward_pointers.emplace_back(i_type);
152       } else {
153         // The same type has already been seen before, remove this one.
154         modified = true;
155         to_delete.emplace_back(i);
156       }
157     }
158   }
159 
160   for (auto i : to_delete) {
161     context()->KillInst(i);
162   }
163 
164   return modified;
165 }
166 
167 // TODO(pierremoreau): Duplicate decoration groups should be removed. For
168 // example, in
169 //     OpDecorate %1 Constant
170 //     %1 = OpDecorationGroup
171 //     OpDecorate %2 Constant
172 //     %2 = OpDecorationGroup
173 //     OpGroupDecorate %1 %3
174 //     OpGroupDecorate %2 %4
175 // group %2 could be removed.
RemoveDuplicateDecorations() const176 bool RemoveDuplicatesPass::RemoveDuplicateDecorations() const {
177   bool modified = false;
178 
179   std::vector<const Instruction*> visited_decorations;
180 
181   analysis::DecorationManager decoration_manager(context()->module());
182   for (auto* i = &*context()->annotation_begin(); i;) {
183     // Is the current decoration equal to one of the decorations we have
184     // already visited?
185     bool already_visited = false;
186     // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the
187     // ResultIdTrie from unify_const_pass.cpp for this.
188     for (const Instruction* j : visited_decorations) {
189       if (decoration_manager.AreDecorationsTheSame(&*i, j, false)) {
190         already_visited = true;
191         break;
192       }
193     }
194 
195     if (!already_visited) {
196       // This is a never seen before decoration, keep it around.
197       visited_decorations.emplace_back(&*i);
198       i = i->NextNode();
199     } else {
200       // The same decoration has already been seen before, remove this one.
201       modified = true;
202       i = context()->KillInst(i);
203     }
204   }
205 
206   return modified;
207 }
208 
209 }  // namespace opt
210 }  // namespace spvtools
211