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