/src/spirv-tools/source/opt/value_number_table.cpp
Line | Count | Source (jump to first uncovered line) |
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/value_number_table.h" |
16 | | |
17 | | #include <algorithm> |
18 | | |
19 | | #include "source/opt/cfg.h" |
20 | | #include "source/opt/ir_context.h" |
21 | | |
22 | | namespace spvtools { |
23 | | namespace opt { |
24 | | |
25 | 4.14M | uint32_t ValueNumberTable::GetValueNumber(Instruction* inst) const { |
26 | 4.14M | assert(inst->result_id() != 0 && |
27 | 4.14M | "inst must have a result id to get a value number."); |
28 | | |
29 | | // Check if this instruction already has a value. |
30 | 4.14M | auto result_id_to_val = id_to_value_.find(inst->result_id()); |
31 | 4.14M | if (result_id_to_val != id_to_value_.end()) { |
32 | 1.16M | return result_id_to_val->second; |
33 | 1.16M | } |
34 | 2.97M | return 0; |
35 | 4.14M | } |
36 | | |
37 | 259k | uint32_t ValueNumberTable::GetValueNumber(uint32_t id) const { |
38 | 259k | return GetValueNumber(context()->get_def_use_mgr()->GetDef(id)); |
39 | 259k | } |
40 | | |
41 | 2.67M | uint32_t ValueNumberTable::AssignValueNumber(Instruction* inst) { |
42 | | // If it already has a value return that. |
43 | 2.67M | uint32_t value = GetValueNumber(inst); |
44 | 2.67M | if (value != 0) { |
45 | 0 | return value; |
46 | 0 | } |
47 | | |
48 | 2.67M | auto assign_new_number = [this](Instruction* i) { |
49 | 557k | const auto new_value = TakeNextValueNumber(); |
50 | 557k | id_to_value_[i->result_id()] = new_value; |
51 | 557k | return new_value; |
52 | 557k | }; |
53 | | |
54 | | // If the instruction has other side effects, then it must |
55 | | // have its own value number. |
56 | 2.67M | if (!context()->IsCombinatorInstruction(inst) && |
57 | 2.67M | !inst->IsCommonDebugInstr()) { |
58 | 19.7k | return assign_new_number(inst); |
59 | 19.7k | } |
60 | | |
61 | | // OpSampledImage and OpImage must remain in the same basic block in which |
62 | | // they are used, because of this we will assign each one it own value number. |
63 | 2.65M | switch (inst->opcode()) { |
64 | 5 | case spv::Op::OpSampledImage: |
65 | 5 | case spv::Op::OpImage: |
66 | 130k | case spv::Op::OpVariable: |
67 | 130k | return assign_new_number(inst); |
68 | 2.52M | default: |
69 | 2.52M | break; |
70 | 2.65M | } |
71 | | |
72 | | // A load that yields an image, sampler, or sampled image must remain in |
73 | | // the same basic block. So assign it its own value number. |
74 | 2.52M | if (inst->IsLoad()) { |
75 | 532k | switch (context()->get_def_use_mgr()->GetDef(inst->type_id())->opcode()) { |
76 | 122k | case spv::Op::OpTypeSampledImage: |
77 | 122k | case spv::Op::OpTypeImage: |
78 | 122k | case spv::Op::OpTypeSampler: |
79 | 122k | return assign_new_number(inst); |
80 | 410k | default: |
81 | 410k | break; |
82 | 532k | } |
83 | 532k | } |
84 | | |
85 | | // If it is a load from memory that can be modified, we have to assume the |
86 | | // memory has been modified, so we give it a new value number. |
87 | | // |
88 | | // Note that this test will also handle volatile loads because they are not |
89 | | // read only. However, if this is ever relaxed because we analyze stores, we |
90 | | // will have to add a new case for volatile loads. |
91 | 2.40M | if (inst->IsLoad() && !inst->IsReadOnlyLoad()) { |
92 | 284k | return assign_new_number(inst); |
93 | 284k | } |
94 | | |
95 | 2.11M | analysis::DecorationManager* dec_mgr = context()->get_decoration_mgr(); |
96 | | |
97 | | // When we copy an object, the value numbers should be the same. |
98 | 2.11M | if (inst->opcode() == spv::Op::OpCopyObject && |
99 | 2.11M | dec_mgr->HaveTheSameDecorations(inst->result_id(), |
100 | 11 | inst->GetSingleWordInOperand(0))) { |
101 | 2 | value = GetValueNumber(inst->GetSingleWordInOperand(0)); |
102 | 2 | if (value != 0) { |
103 | 2 | id_to_value_[inst->result_id()] = value; |
104 | 2 | return value; |
105 | 2 | } |
106 | 2 | } |
107 | | |
108 | | // Phi nodes are a type of copy. If all of the inputs have the same value |
109 | | // number, then we can assign the result of the phi the same value number. |
110 | 2.11M | if (inst->opcode() == spv::Op::OpPhi && inst->NumInOperands() > 0 && |
111 | 2.11M | dec_mgr->HaveTheSameDecorations(inst->result_id(), |
112 | 144k | inst->GetSingleWordInOperand(0))) { |
113 | 127k | value = GetValueNumber(inst->GetSingleWordInOperand(0)); |
114 | 127k | if (value != 0) { |
115 | 135k | for (uint32_t op = 2; op < inst->NumInOperands(); op += 2) { |
116 | 131k | if (value != GetValueNumber(inst->GetSingleWordInOperand(op))) { |
117 | 123k | value = 0; |
118 | 123k | break; |
119 | 123k | } |
120 | 131k | } |
121 | 127k | if (value != 0) { |
122 | 4.23k | id_to_value_[inst->result_id()] = value; |
123 | 4.23k | return value; |
124 | 4.23k | } |
125 | 127k | } |
126 | 127k | } |
127 | | |
128 | | // Replace all of the operands by their value number. The sign bit will be |
129 | | // set to distinguish between an id and a value number. |
130 | 2.11M | Instruction value_ins(context(), inst->opcode(), inst->type_id(), |
131 | 2.11M | inst->result_id(), {}); |
132 | 6.53M | for (uint32_t o = 0; o < inst->NumInOperands(); ++o) { |
133 | 4.42M | const Operand& op = inst->GetInOperand(o); |
134 | 4.42M | if (spvIsIdType(op.type)) { |
135 | 3.79M | uint32_t id_value = op.words[0]; |
136 | 3.79M | auto use_id_to_val = id_to_value_.find(id_value); |
137 | 3.79M | if (use_id_to_val != id_to_value_.end()) { |
138 | 3.44M | id_value = (1 << 31) | use_id_to_val->second; |
139 | 3.44M | } |
140 | 3.79M | value_ins.AddOperand(Operand(op.type, {id_value})); |
141 | 3.79M | } else { |
142 | 626k | value_ins.AddOperand(Operand(op.type, op.words)); |
143 | 626k | } |
144 | 4.42M | } |
145 | | |
146 | | // TODO: Implement a normal form for opcodes that commute like integer |
147 | | // addition. This will let us know that a+b is the same value as b+a. |
148 | | |
149 | | // Otherwise, we check if this value has been computed before. |
150 | 2.11M | auto value_iterator = instruction_to_value_.find(value_ins); |
151 | 2.11M | if (value_iterator != instruction_to_value_.end()) { |
152 | 429k | value = id_to_value_[value_iterator->first.result_id()]; |
153 | 429k | id_to_value_[inst->result_id()] = value; |
154 | 429k | return value; |
155 | 429k | } |
156 | | |
157 | | // If not, assign it a new value number. |
158 | 1.68M | value = TakeNextValueNumber(); |
159 | 1.68M | id_to_value_[inst->result_id()] = value; |
160 | 1.68M | instruction_to_value_[value_ins] = value; |
161 | 1.68M | return value; |
162 | 2.11M | } |
163 | | |
164 | 17.6k | void ValueNumberTable::BuildDominatorTreeValueNumberTable() { |
165 | | // First value number the headers. |
166 | 1.86M | for (auto& inst : context()->annotations()) { |
167 | 1.86M | if (inst.result_id() != 0) { |
168 | 1.98k | AssignValueNumber(&inst); |
169 | 1.98k | } |
170 | 1.86M | } |
171 | | |
172 | 21.8k | for (auto& inst : context()->capabilities()) { |
173 | 21.8k | if (inst.result_id() != 0) { |
174 | 0 | AssignValueNumber(&inst); |
175 | 0 | } |
176 | 21.8k | } |
177 | | |
178 | 523k | for (auto& inst : context()->types_values()) { |
179 | 523k | if (inst.result_id() != 0) { |
180 | 523k | AssignValueNumber(&inst); |
181 | 523k | } |
182 | 523k | } |
183 | | |
184 | 17.6k | for (auto& inst : context()->module()->ext_inst_imports()) { |
185 | 9.51k | if (inst.result_id() != 0) { |
186 | 9.51k | AssignValueNumber(&inst); |
187 | 9.51k | } |
188 | 9.51k | } |
189 | | |
190 | 17.6k | for (auto& inst : context()->module()->ext_inst_debuginfo()) { |
191 | 0 | if (inst.result_id() != 0) { |
192 | 0 | AssignValueNumber(&inst); |
193 | 0 | } |
194 | 0 | } |
195 | | |
196 | 17.6k | for (Function& func : *context()->module()) { |
197 | | // For best results we want to traverse the code in reverse post order. |
198 | | // This happens naturally because of the forward referencing rules. |
199 | 911k | for (BasicBlock& block : func) { |
200 | 3.68M | for (Instruction& inst : block) { |
201 | 3.68M | if (inst.result_id() != 0) { |
202 | 2.14M | AssignValueNumber(&inst); |
203 | 2.14M | } |
204 | 3.68M | } |
205 | 911k | } |
206 | 17.3k | } |
207 | 17.6k | } |
208 | | |
209 | | bool ComputeSameValue::operator()(const Instruction& lhs, |
210 | 433k | const Instruction& rhs) const { |
211 | 433k | if (lhs.result_id() == 0 || rhs.result_id() == 0) { |
212 | 0 | return false; |
213 | 0 | } |
214 | | |
215 | 433k | if (lhs.opcode() != rhs.opcode()) { |
216 | 0 | return false; |
217 | 0 | } |
218 | | |
219 | 433k | if (lhs.type_id() != rhs.type_id()) { |
220 | 0 | return false; |
221 | 0 | } |
222 | | |
223 | 433k | if (lhs.NumInOperands() != rhs.NumInOperands()) { |
224 | 0 | return false; |
225 | 0 | } |
226 | | |
227 | 1.31M | for (uint32_t i = 0; i < lhs.NumInOperands(); ++i) { |
228 | 882k | if (lhs.GetInOperand(i) != rhs.GetInOperand(i)) { |
229 | 0 | return false; |
230 | 0 | } |
231 | 882k | } |
232 | | |
233 | 433k | return lhs.context()->get_decoration_mgr()->HaveTheSameDecorations( |
234 | 433k | lhs.result_id(), rhs.result_id()); |
235 | 433k | } |
236 | | |
237 | 3.79M | std::size_t ValueTableHash::operator()(const Instruction& inst) const { |
238 | | // We hash the opcode and in-operands, not the result, because we want |
239 | | // instructions that are the same except for the result to hash to the |
240 | | // same value. |
241 | 3.79M | std::u32string h; |
242 | 3.79M | h.push_back(uint32_t(inst.opcode())); |
243 | 3.79M | h.push_back(inst.type_id()); |
244 | 11.7M | for (uint32_t i = 0; i < inst.NumInOperands(); ++i) { |
245 | 7.96M | const auto& opnd = inst.GetInOperand(i); |
246 | 7.96M | for (uint32_t word : opnd.words) { |
247 | 7.96M | h.push_back(word); |
248 | 7.96M | } |
249 | 7.96M | } |
250 | 3.79M | return std::hash<std::u32string>()(h); |
251 | 3.79M | } |
252 | | } // namespace opt |
253 | | } // namespace spvtools |