/src/spirv-tools/source/opt/remove_duplicates_pass.cpp
Line | Count | Source |
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 | | |
30 | 0 | Pass::Status RemoveDuplicatesPass::Process() { |
31 | 0 | bool modified = RemoveDuplicateCapabilities(); |
32 | 0 | modified |= RemoveDuplicateExtensions(); |
33 | 0 | modified |= RemoveDuplicatesExtInstImports(); |
34 | 0 | modified |= RemoveDuplicateTypes(); |
35 | 0 | modified |= RemoveDuplicateDecorations(); |
36 | |
|
37 | 0 | return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; |
38 | 0 | } |
39 | | |
40 | 0 | bool RemoveDuplicatesPass::RemoveDuplicateExtensions() const { |
41 | 0 | bool modified = false; |
42 | |
|
43 | 0 | if (context()->extensions().empty()) { |
44 | 0 | return modified; |
45 | 0 | } |
46 | | |
47 | | // set of {condition ID, extension name} |
48 | | // ID 0 means unconditional extension, ie., OpExtension, otherwise the ID is |
49 | | // the condition operand of OpConditionalExtensionINTEL. |
50 | 0 | std::set<std::pair<uint32_t, std::string>> extensions; |
51 | 0 | for (auto* inst = &*context()->extension_begin(); inst;) { |
52 | 0 | uint32_t cond_id = 0; |
53 | 0 | uint32_t i_name = 0; |
54 | 0 | if (inst->opcode() == spv::Op::OpConditionalExtensionINTEL) { |
55 | 0 | cond_id = inst->GetOperand(0).AsId(); |
56 | 0 | i_name = 1; |
57 | 0 | } |
58 | |
|
59 | 0 | auto res = |
60 | 0 | extensions.insert({cond_id, inst->GetOperand(i_name).AsString()}); |
61 | |
|
62 | 0 | if (res.second) { |
63 | | // Never seen before, keep it. |
64 | 0 | inst = inst->NextNode(); |
65 | 0 | } else { |
66 | | // It's a duplicate, remove it. |
67 | 0 | inst = context()->KillInst(inst); |
68 | 0 | modified = true; |
69 | 0 | } |
70 | 0 | } |
71 | |
|
72 | 0 | return modified; |
73 | 0 | } |
74 | | |
75 | 0 | bool RemoveDuplicatesPass::RemoveDuplicateCapabilities() const { |
76 | 0 | bool modified = false; |
77 | |
|
78 | 0 | if (context()->capabilities().empty()) { |
79 | 0 | return modified; |
80 | 0 | } |
81 | | |
82 | | // set of {condition ID, capability} |
83 | | // ID 0 means unconditional capability, ie., OpCapability, otherwise the ID is |
84 | | // the condition operand of OpConditionalCapabilityINTEL. |
85 | 0 | std::set<std::pair<uint32_t, uint32_t>> capabilities; |
86 | 0 | for (auto* inst = &*context()->capability_begin(); inst;) { |
87 | 0 | uint32_t cond_id = 0; |
88 | 0 | uint32_t i_cap = 0; |
89 | 0 | if (inst->opcode() == spv::Op::OpConditionalCapabilityINTEL) { |
90 | 0 | cond_id = inst->GetOperand(0).AsId(); |
91 | 0 | i_cap = 1; |
92 | 0 | } |
93 | |
|
94 | 0 | auto res = |
95 | 0 | capabilities.insert({cond_id, inst->GetSingleWordOperand(i_cap)}); |
96 | |
|
97 | 0 | if (res.second) { |
98 | | // Never seen before, keep it. |
99 | 0 | inst = inst->NextNode(); |
100 | 0 | } else { |
101 | | // It's a duplicate, remove it. |
102 | 0 | inst = context()->KillInst(inst); |
103 | 0 | modified = true; |
104 | 0 | } |
105 | 0 | } |
106 | |
|
107 | 0 | return modified; |
108 | 0 | } |
109 | | |
110 | 0 | bool RemoveDuplicatesPass::RemoveDuplicatesExtInstImports() const { |
111 | 0 | bool modified = false; |
112 | |
|
113 | 0 | if (context()->ext_inst_imports().empty()) { |
114 | 0 | return modified; |
115 | 0 | } |
116 | | |
117 | 0 | std::unordered_map<std::string, spv::Id> ext_inst_imports; |
118 | 0 | for (auto* i = &*context()->ext_inst_import_begin(); i;) { |
119 | 0 | auto res = ext_inst_imports.emplace(i->GetInOperand(0u).AsString(), |
120 | 0 | i->result_id()); |
121 | 0 | if (res.second) { |
122 | | // Never seen before, keep it. |
123 | 0 | i = i->NextNode(); |
124 | 0 | } else { |
125 | | // It's a duplicate, remove it. |
126 | 0 | context()->ReplaceAllUsesWith(i->result_id(), res.first->second); |
127 | 0 | i = context()->KillInst(i); |
128 | 0 | modified = true; |
129 | 0 | } |
130 | 0 | } |
131 | |
|
132 | 0 | return modified; |
133 | 0 | } |
134 | | |
135 | 0 | bool RemoveDuplicatesPass::RemoveDuplicateTypes() const { |
136 | 0 | bool modified = false; |
137 | |
|
138 | 0 | if (context()->types_values().empty()) { |
139 | 0 | return modified; |
140 | 0 | } |
141 | | |
142 | 0 | analysis::TypeManager type_manager(context()->consumer(), context()); |
143 | |
|
144 | 0 | std::vector<Instruction*> visited_types; |
145 | 0 | std::vector<analysis::ForwardPointer> visited_forward_pointers; |
146 | 0 | std::vector<Instruction*> to_delete; |
147 | 0 | for (auto* i = &*context()->types_values_begin(); i; i = i->NextNode()) { |
148 | 0 | const bool is_i_forward_pointer = |
149 | 0 | i->opcode() == spv::Op::OpTypeForwardPointer; |
150 | | |
151 | | // We only care about types. |
152 | 0 | if (!spvOpcodeGeneratesType(i->opcode()) && !is_i_forward_pointer) { |
153 | 0 | continue; |
154 | 0 | } |
155 | | |
156 | 0 | if (!is_i_forward_pointer) { |
157 | | // Is the current type equal to one of the types we have already visited? |
158 | 0 | spv::Id id_to_keep = 0u; |
159 | 0 | analysis::Type* i_type = type_manager.GetType(i->result_id()); |
160 | 0 | assert(i_type); |
161 | | // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the |
162 | | // ResultIdTrie from unify_const_pass.cpp for this. |
163 | 0 | for (auto j : visited_types) { |
164 | 0 | analysis::Type* j_type = type_manager.GetType(j->result_id()); |
165 | 0 | assert(j_type); |
166 | 0 | if (*i_type == *j_type) { |
167 | 0 | id_to_keep = j->result_id(); |
168 | 0 | break; |
169 | 0 | } |
170 | 0 | } |
171 | | |
172 | 0 | if (id_to_keep == 0u) { |
173 | | // This is a never seen before type, keep it around. |
174 | 0 | visited_types.emplace_back(i); |
175 | 0 | } else { |
176 | | // The same type has already been seen before, remove this one. |
177 | 0 | context()->KillNamesAndDecorates(i->result_id()); |
178 | 0 | context()->ReplaceAllUsesWith(i->result_id(), id_to_keep); |
179 | 0 | modified = true; |
180 | 0 | to_delete.emplace_back(i); |
181 | 0 | } |
182 | 0 | } else { |
183 | 0 | analysis::ForwardPointer i_type( |
184 | 0 | i->GetSingleWordInOperand(0u), |
185 | 0 | (spv::StorageClass)i->GetSingleWordInOperand(1u)); |
186 | 0 | i_type.SetTargetPointer( |
187 | 0 | type_manager.GetType(i_type.target_id())->AsPointer()); |
188 | | |
189 | | // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the |
190 | | // ResultIdTrie from unify_const_pass.cpp for this. |
191 | 0 | const bool found_a_match = |
192 | 0 | std::find(std::begin(visited_forward_pointers), |
193 | 0 | std::end(visited_forward_pointers), |
194 | 0 | i_type) != std::end(visited_forward_pointers); |
195 | |
|
196 | 0 | if (!found_a_match) { |
197 | | // This is a never seen before type, keep it around. |
198 | 0 | visited_forward_pointers.emplace_back(i_type); |
199 | 0 | } else { |
200 | | // The same type has already been seen before, remove this one. |
201 | 0 | modified = true; |
202 | 0 | to_delete.emplace_back(i); |
203 | 0 | } |
204 | 0 | } |
205 | 0 | } |
206 | | |
207 | 0 | for (auto i : to_delete) { |
208 | 0 | context()->KillInst(i); |
209 | 0 | } |
210 | |
|
211 | 0 | return modified; |
212 | 0 | } |
213 | | |
214 | | // TODO(pierremoreau): Duplicate decoration groups should be removed. For |
215 | | // example, in |
216 | | // OpDecorate %1 Constant |
217 | | // %1 = OpDecorationGroup |
218 | | // OpDecorate %2 Constant |
219 | | // %2 = OpDecorationGroup |
220 | | // OpGroupDecorate %1 %3 |
221 | | // OpGroupDecorate %2 %4 |
222 | | // group %2 could be removed. |
223 | 0 | bool RemoveDuplicatesPass::RemoveDuplicateDecorations() const { |
224 | 0 | bool modified = false; |
225 | |
|
226 | 0 | std::vector<const Instruction*> visited_decorations; |
227 | |
|
228 | 0 | analysis::DecorationManager decoration_manager(context()->module()); |
229 | 0 | for (auto* i = &*context()->annotation_begin(); i;) { |
230 | | // Is the current decoration equal to one of the decorations we have |
231 | | // already visited? |
232 | 0 | bool already_visited = false; |
233 | | // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the |
234 | | // ResultIdTrie from unify_const_pass.cpp for this. |
235 | 0 | for (const Instruction* j : visited_decorations) { |
236 | 0 | if (decoration_manager.AreDecorationsTheSame(&*i, j, false)) { |
237 | 0 | already_visited = true; |
238 | 0 | break; |
239 | 0 | } |
240 | 0 | } |
241 | |
|
242 | 0 | if (!already_visited) { |
243 | | // This is a never seen before decoration, keep it around. |
244 | 0 | visited_decorations.emplace_back(&*i); |
245 | 0 | i = i->NextNode(); |
246 | 0 | } else { |
247 | | // The same decoration has already been seen before, remove this one. |
248 | 0 | modified = true; |
249 | 0 | i = context()->KillInst(i); |
250 | 0 | } |
251 | 0 | } |
252 | |
|
253 | 0 | return modified; |
254 | 0 | } |
255 | | |
256 | | } // namespace opt |
257 | | } // namespace spvtools |