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