/src/spirv-tools/source/opt/scalar_replacement_pass.cpp
Line | Count | Source |
1 | | // Copyright (c) 2017 Google Inc. |
2 | | // Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights |
3 | | // reserved. |
4 | | // |
5 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
6 | | // you may not use this file except in compliance with the License. |
7 | | // You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, software |
12 | | // distributed under the License is distributed on an "AS IS" BASIS, |
13 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | // See the License for the specific language governing permissions and |
15 | | // limitations under the License. |
16 | | |
17 | | #include "source/opt/scalar_replacement_pass.h" |
18 | | |
19 | | #include <algorithm> |
20 | | #include <queue> |
21 | | #include <tuple> |
22 | | #include <utility> |
23 | | |
24 | | #include "source/extensions.h" |
25 | | #include "source/opt/reflect.h" |
26 | | #include "source/opt/types.h" |
27 | | #include "source/util/make_unique.h" |
28 | | |
29 | | namespace spvtools { |
30 | | namespace opt { |
31 | | namespace { |
32 | | constexpr uint32_t kDebugValueOperandValueIndex = 5; |
33 | | constexpr uint32_t kDebugValueOperandExpressionIndex = 6; |
34 | | constexpr uint32_t kDebugDeclareOperandVariableIndex = 5; |
35 | | } // namespace |
36 | | |
37 | 9.06k | Pass::Status ScalarReplacementPass::Process() { |
38 | 9.06k | Status status = Status::SuccessWithoutChange; |
39 | 10.8k | for (auto& f : *get_module()) { |
40 | 10.8k | if (f.IsDeclaration()) { |
41 | 0 | continue; |
42 | 0 | } |
43 | | |
44 | 10.8k | Status functionStatus = ProcessFunction(&f); |
45 | 10.8k | if (functionStatus == Status::Failure) |
46 | 0 | return functionStatus; |
47 | 10.8k | else if (functionStatus == Status::SuccessWithChange) |
48 | 2.63k | status = functionStatus; |
49 | 10.8k | } |
50 | | |
51 | 9.06k | if (context()->id_overflow()) { |
52 | 0 | return Status::Failure; |
53 | 0 | } |
54 | 9.06k | return status; |
55 | 9.06k | } |
56 | | |
57 | 10.8k | Pass::Status ScalarReplacementPass::ProcessFunction(Function* function) { |
58 | 10.8k | std::queue<Instruction*> worklist; |
59 | 10.8k | BasicBlock& entry = *function->begin(); |
60 | 237k | for (auto iter = entry.begin(); iter != entry.end(); ++iter) { |
61 | | // Function storage class OpVariables must appear as the first instructions |
62 | | // of the entry block. |
63 | 237k | if (iter->opcode() != spv::Op::OpVariable) break; |
64 | | |
65 | 226k | Instruction* varInst = &*iter; |
66 | 226k | if (CanReplaceVariable(varInst)) { |
67 | 15.8k | worklist.push(varInst); |
68 | 15.8k | } |
69 | 226k | } |
70 | | |
71 | 10.8k | Status status = Status::SuccessWithoutChange; |
72 | 26.8k | while (!worklist.empty()) { |
73 | 15.9k | Instruction* varInst = worklist.front(); |
74 | 15.9k | worklist.pop(); |
75 | | |
76 | 15.9k | Status var_status = ReplaceVariable(varInst, &worklist); |
77 | 15.9k | if (var_status == Status::Failure) |
78 | 0 | return var_status; |
79 | 15.9k | else if (var_status == Status::SuccessWithChange) |
80 | 15.9k | status = var_status; |
81 | 15.9k | } |
82 | | |
83 | 10.8k | return status; |
84 | 10.8k | } |
85 | | |
86 | | Pass::Status ScalarReplacementPass::ReplaceVariable( |
87 | 15.9k | Instruction* inst, std::queue<Instruction*>* worklist) { |
88 | 15.9k | std::vector<Instruction*> replacements; |
89 | 15.9k | if (!CreateReplacementVariables(inst, &replacements)) { |
90 | 0 | return Status::Failure; |
91 | 0 | } |
92 | | |
93 | 15.9k | std::vector<Instruction*> dead; |
94 | 15.9k | bool replaced_all_uses = get_def_use_mgr()->WhileEachUser( |
95 | 59.7k | inst, [this, &replacements, &dead](Instruction* user) { |
96 | 59.7k | if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { |
97 | 0 | if (ReplaceWholeDebugDeclare(user, replacements)) { |
98 | 0 | dead.push_back(user); |
99 | 0 | return true; |
100 | 0 | } |
101 | 0 | return false; |
102 | 0 | } |
103 | 59.7k | if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { |
104 | 0 | if (ReplaceWholeDebugValue(user, replacements)) { |
105 | 0 | dead.push_back(user); |
106 | 0 | return true; |
107 | 0 | } |
108 | 0 | return false; |
109 | 0 | } |
110 | 59.7k | if (!IsAnnotationInst(user->opcode())) { |
111 | 59.6k | switch (user->opcode()) { |
112 | 9.60k | case spv::Op::OpLoad: |
113 | 9.60k | if (ReplaceWholeLoad(user, replacements)) { |
114 | 9.60k | dead.push_back(user); |
115 | 9.60k | } else { |
116 | 0 | return false; |
117 | 0 | } |
118 | 9.60k | break; |
119 | 12.3k | case spv::Op::OpStore: |
120 | 12.3k | if (ReplaceWholeStore(user, replacements)) { |
121 | 12.3k | dead.push_back(user); |
122 | 12.3k | } else { |
123 | 0 | return false; |
124 | 0 | } |
125 | 12.3k | break; |
126 | 36.2k | case spv::Op::OpAccessChain: |
127 | 36.4k | case spv::Op::OpInBoundsAccessChain: |
128 | 36.4k | if (ReplaceAccessChain(user, replacements)) |
129 | 36.4k | dead.push_back(user); |
130 | 0 | else |
131 | 0 | return false; |
132 | 36.4k | break; |
133 | 36.4k | case spv::Op::OpName: |
134 | 1.21k | case spv::Op::OpMemberName: |
135 | 1.21k | break; |
136 | 0 | default: |
137 | 0 | assert(false && "Unexpected opcode"); |
138 | 0 | break; |
139 | 59.6k | } |
140 | 59.6k | } |
141 | 59.7k | return true; |
142 | 59.7k | }); |
143 | | |
144 | 15.9k | if (replaced_all_uses) { |
145 | 15.9k | dead.push_back(inst); |
146 | 15.9k | } else { |
147 | 0 | return Status::Failure; |
148 | 0 | } |
149 | | |
150 | | // If there are no dead instructions to clean up, return with no changes. |
151 | 15.9k | if (dead.empty()) return Status::SuccessWithoutChange; |
152 | | |
153 | | // Clean up some dead code. |
154 | 90.3k | while (!dead.empty()) { |
155 | 74.3k | Instruction* toKill = dead.back(); |
156 | 74.3k | dead.pop_back(); |
157 | 74.3k | context()->KillInst(toKill); |
158 | 74.3k | } |
159 | | |
160 | | // Attempt to further scalarize. |
161 | 134k | for (auto var : replacements) { |
162 | 134k | if (var->opcode() == spv::Op::OpVariable) { |
163 | 79.0k | if (get_def_use_mgr()->NumUsers(var) == 0) { |
164 | 6 | context()->KillInst(var); |
165 | 79.0k | } else if (CanReplaceVariable(var)) { |
166 | 116 | worklist->push(var); |
167 | 116 | } |
168 | 79.0k | } |
169 | 134k | } |
170 | | |
171 | 15.9k | return Status::SuccessWithChange; |
172 | 15.9k | } |
173 | | |
174 | | bool ScalarReplacementPass::ReplaceWholeDebugDeclare( |
175 | 0 | Instruction* dbg_decl, const std::vector<Instruction*>& replacements) { |
176 | | // Insert Deref operation to the front of the operation list of |dbg_decl|. |
177 | 0 | Instruction* dbg_expr = context()->get_def_use_mgr()->GetDef( |
178 | 0 | dbg_decl->GetSingleWordOperand(kDebugValueOperandExpressionIndex)); |
179 | 0 | auto* deref_expr = |
180 | 0 | context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr); |
181 | 0 | if (deref_expr == nullptr) { |
182 | 0 | return false; |
183 | 0 | } |
184 | | |
185 | | // Add DebugValue instruction with Indexes operand and Deref operation. |
186 | 0 | int32_t idx = 0; |
187 | 0 | for (const auto* var : replacements) { |
188 | 0 | Instruction* insert_before = var->NextNode(); |
189 | 0 | while (insert_before->opcode() == spv::Op::OpVariable) |
190 | 0 | insert_before = insert_before->NextNode(); |
191 | 0 | assert(insert_before != nullptr && "unexpected end of list"); |
192 | 0 | Instruction* added_dbg_value = |
193 | 0 | context()->get_debug_info_mgr()->AddDebugValueForDecl( |
194 | 0 | dbg_decl, /*value_id=*/var->result_id(), |
195 | 0 | /*insert_before=*/insert_before, /*line=*/dbg_decl); |
196 | |
|
197 | 0 | if (added_dbg_value == nullptr) return false; |
198 | 0 | uint32_t idx_id = context()->get_constant_mgr()->GetSIntConstId(idx); |
199 | 0 | if (idx_id == 0) { |
200 | 0 | return false; |
201 | 0 | } |
202 | 0 | added_dbg_value->AddOperand({SPV_OPERAND_TYPE_ID, {idx_id}}); |
203 | 0 | added_dbg_value->SetOperand(kDebugValueOperandExpressionIndex, |
204 | 0 | {deref_expr->result_id()}); |
205 | 0 | if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) { |
206 | 0 | context()->get_def_use_mgr()->AnalyzeInstUse(added_dbg_value); |
207 | 0 | } |
208 | 0 | ++idx; |
209 | 0 | } |
210 | 0 | return true; |
211 | 0 | } |
212 | | |
213 | | bool ScalarReplacementPass::ReplaceWholeDebugValue( |
214 | 0 | Instruction* dbg_value, const std::vector<Instruction*>& replacements) { |
215 | 0 | int32_t idx = 0; |
216 | 0 | BasicBlock* block = context()->get_instr_block(dbg_value); |
217 | 0 | for (auto var : replacements) { |
218 | | // Clone the DebugValue. |
219 | 0 | std::unique_ptr<Instruction> new_dbg_value(dbg_value->Clone(context())); |
220 | 0 | if (!new_dbg_value) { |
221 | 0 | return false; |
222 | 0 | } |
223 | 0 | uint32_t new_id = TakeNextId(); |
224 | 0 | if (new_id == 0) { |
225 | 0 | return false; |
226 | 0 | } |
227 | 0 | new_dbg_value->SetResultId(new_id); |
228 | | // Update 'Value' operand to the |replacements|. |
229 | 0 | new_dbg_value->SetOperand(kDebugValueOperandValueIndex, {var->result_id()}); |
230 | | // Append 'Indexes' operand. |
231 | 0 | uint32_t idx_id = context()->get_constant_mgr()->GetSIntConstId(idx); |
232 | 0 | if (idx_id == 0) { |
233 | 0 | return false; |
234 | 0 | } |
235 | 0 | new_dbg_value->AddOperand({SPV_OPERAND_TYPE_ID, {idx_id}}); |
236 | | // Insert the new DebugValue to the basic block. |
237 | 0 | auto* added_instr = dbg_value->InsertBefore(std::move(new_dbg_value)); |
238 | 0 | get_def_use_mgr()->AnalyzeInstDefUse(added_instr); |
239 | 0 | context()->set_instr_block(added_instr, block); |
240 | 0 | ++idx; |
241 | 0 | } |
242 | 0 | return true; |
243 | 0 | } |
244 | | |
245 | | bool ScalarReplacementPass::ReplaceWholeLoad( |
246 | 9.60k | Instruction* load, const std::vector<Instruction*>& replacements) { |
247 | | // Replaces the load of the entire composite with a load from each replacement |
248 | | // variable followed by a composite construction. |
249 | 9.60k | BasicBlock* block = context()->get_instr_block(load); |
250 | 9.60k | std::vector<Instruction*> loads; |
251 | 9.60k | loads.reserve(replacements.size()); |
252 | 9.60k | BasicBlock::iterator where(load); |
253 | 100k | for (auto var : replacements) { |
254 | | // Create a load of each replacement variable. |
255 | 100k | if (var->opcode() != spv::Op::OpVariable) { |
256 | 9.79k | loads.push_back(var); |
257 | 9.79k | continue; |
258 | 9.79k | } |
259 | | |
260 | 90.4k | Instruction* type = GetStorageType(var); |
261 | 90.4k | uint32_t loadId = TakeNextId(); |
262 | 90.4k | if (loadId == 0) { |
263 | 0 | return false; |
264 | 0 | } |
265 | 90.4k | std::unique_ptr<Instruction> newLoad( |
266 | 90.4k | new Instruction(context(), spv::Op::OpLoad, type->result_id(), loadId, |
267 | 90.4k | std::initializer_list<Operand>{ |
268 | 90.4k | {SPV_OPERAND_TYPE_ID, {var->result_id()}}})); |
269 | | // Copy memory access attributes which start at index 1. Index 0 is the |
270 | | // pointer to load. |
271 | 90.4k | for (uint32_t i = 1; i < load->NumInOperands(); ++i) { |
272 | 0 | Operand copy(load->GetInOperand(i)); |
273 | 0 | newLoad->AddOperand(std::move(copy)); |
274 | 0 | } |
275 | 90.4k | where = where.InsertBefore(std::move(newLoad)); |
276 | 90.4k | get_def_use_mgr()->AnalyzeInstDefUse(&*where); |
277 | 90.4k | context()->set_instr_block(&*where, block); |
278 | 90.4k | where->UpdateDebugInfoFrom(load); |
279 | 90.4k | loads.push_back(&*where); |
280 | 90.4k | } |
281 | | |
282 | | // Construct a new composite. |
283 | 9.60k | uint32_t compositeId = TakeNextId(); |
284 | 9.60k | if (compositeId == 0) { |
285 | 0 | return false; |
286 | 0 | } |
287 | 9.60k | where = load; |
288 | 9.60k | std::unique_ptr<Instruction> compositeConstruct( |
289 | 9.60k | new Instruction(context(), spv::Op::OpCompositeConstruct, load->type_id(), |
290 | 9.60k | compositeId, {})); |
291 | 100k | for (auto l : loads) { |
292 | 100k | Operand op(SPV_OPERAND_TYPE_ID, |
293 | 100k | std::initializer_list<uint32_t>{l->result_id()}); |
294 | 100k | compositeConstruct->AddOperand(std::move(op)); |
295 | 100k | } |
296 | 9.60k | where = where.InsertBefore(std::move(compositeConstruct)); |
297 | 9.60k | get_def_use_mgr()->AnalyzeInstDefUse(&*where); |
298 | 9.60k | where->UpdateDebugInfoFrom(load); |
299 | 9.60k | context()->set_instr_block(&*where, block); |
300 | 9.60k | context()->ReplaceAllUsesWith(load->result_id(), compositeId); |
301 | 9.60k | return true; |
302 | 9.60k | } |
303 | | |
304 | | bool ScalarReplacementPass::ReplaceWholeStore( |
305 | 12.3k | Instruction* store, const std::vector<Instruction*>& replacements) { |
306 | | // Replaces a store to the whole composite with a series of extract and stores |
307 | | // to each element. |
308 | 12.3k | uint32_t storeInput = store->GetSingleWordInOperand(1u); |
309 | 12.3k | BasicBlock* block = context()->get_instr_block(store); |
310 | 12.3k | BasicBlock::iterator where(store); |
311 | 12.3k | uint32_t elementIndex = 0; |
312 | 104k | for (auto var : replacements) { |
313 | | // Create the extract. |
314 | 104k | if (var->opcode() != spv::Op::OpVariable) { |
315 | 30.9k | elementIndex++; |
316 | 30.9k | continue; |
317 | 30.9k | } |
318 | | |
319 | 73.6k | Instruction* type = GetStorageType(var); |
320 | 73.6k | uint32_t extractId = TakeNextId(); |
321 | 73.6k | if (extractId == 0) { |
322 | 0 | return false; |
323 | 0 | } |
324 | 73.6k | std::unique_ptr<Instruction> extract(new Instruction( |
325 | 73.6k | context(), spv::Op::OpCompositeExtract, type->result_id(), extractId, |
326 | 73.6k | std::initializer_list<Operand>{ |
327 | 73.6k | {SPV_OPERAND_TYPE_ID, {storeInput}}, |
328 | 73.6k | {SPV_OPERAND_TYPE_LITERAL_INTEGER, {elementIndex++}}})); |
329 | 73.6k | auto iter = where.InsertBefore(std::move(extract)); |
330 | 73.6k | iter->UpdateDebugInfoFrom(store); |
331 | 73.6k | get_def_use_mgr()->AnalyzeInstDefUse(&*iter); |
332 | 73.6k | context()->set_instr_block(&*iter, block); |
333 | | |
334 | | // Create the store. |
335 | 73.6k | std::unique_ptr<Instruction> newStore( |
336 | 73.6k | new Instruction(context(), spv::Op::OpStore, 0, 0, |
337 | 73.6k | std::initializer_list<Operand>{ |
338 | 73.6k | {SPV_OPERAND_TYPE_ID, {var->result_id()}}, |
339 | 73.6k | {SPV_OPERAND_TYPE_ID, {extractId}}})); |
340 | | // Copy memory access attributes which start at index 2. Index 0 is the |
341 | | // pointer and index 1 is the data. |
342 | 73.6k | for (uint32_t i = 2; i < store->NumInOperands(); ++i) { |
343 | 2 | Operand copy(store->GetInOperand(i)); |
344 | 2 | newStore->AddOperand(std::move(copy)); |
345 | 2 | } |
346 | 73.6k | iter = where.InsertBefore(std::move(newStore)); |
347 | 73.6k | iter->UpdateDebugInfoFrom(store); |
348 | 73.6k | get_def_use_mgr()->AnalyzeInstDefUse(&*iter); |
349 | 73.6k | context()->set_instr_block(&*iter, block); |
350 | 73.6k | } |
351 | 12.3k | return true; |
352 | 12.3k | } |
353 | | |
354 | | bool ScalarReplacementPass::ReplaceAccessChain( |
355 | 36.4k | Instruction* chain, const std::vector<Instruction*>& replacements) { |
356 | | // Replaces the access chain with either another access chain (with one fewer |
357 | | // indexes) or a direct use of the replacement variable. |
358 | 36.4k | uint32_t indexId = chain->GetSingleWordInOperand(1u); |
359 | 36.4k | const Instruction* index = get_def_use_mgr()->GetDef(indexId); |
360 | 36.4k | int64_t indexValue = context() |
361 | 36.4k | ->get_constant_mgr() |
362 | 36.4k | ->GetConstantFromInst(index) |
363 | 36.4k | ->GetSignExtendedValue(); |
364 | 36.4k | if (indexValue < 0 || |
365 | 36.4k | indexValue >= static_cast<int64_t>(replacements.size())) { |
366 | | // Out of bounds access, this is illegal IR. Notice that OpAccessChain |
367 | | // indexing is 0-based, so we should also reject index == size-of-array. |
368 | 0 | return false; |
369 | 36.4k | } else { |
370 | 36.4k | const Instruction* var = replacements[static_cast<size_t>(indexValue)]; |
371 | 36.4k | if (chain->NumInOperands() > 2) { |
372 | | // Replace input access chain with another access chain. |
373 | 2.74k | BasicBlock::iterator chainIter(chain); |
374 | 2.74k | uint32_t replacementId = TakeNextId(); |
375 | 2.74k | if (replacementId == 0) { |
376 | 0 | return false; |
377 | 0 | } |
378 | 2.74k | std::unique_ptr<Instruction> replacementChain(new Instruction( |
379 | 2.74k | context(), chain->opcode(), chain->type_id(), replacementId, |
380 | 2.74k | std::initializer_list<Operand>{ |
381 | 2.74k | {SPV_OPERAND_TYPE_ID, {var->result_id()}}})); |
382 | | // Add the remaining indexes. |
383 | 5.48k | for (uint32_t i = 2; i < chain->NumInOperands(); ++i) { |
384 | 2.74k | Operand copy(chain->GetInOperand(i)); |
385 | 2.74k | replacementChain->AddOperand(std::move(copy)); |
386 | 2.74k | } |
387 | 2.74k | replacementChain->UpdateDebugInfoFrom(chain); |
388 | 2.74k | auto iter = chainIter.InsertBefore(std::move(replacementChain)); |
389 | 2.74k | get_def_use_mgr()->AnalyzeInstDefUse(&*iter); |
390 | 2.74k | context()->set_instr_block(&*iter, context()->get_instr_block(chain)); |
391 | 2.74k | context()->ReplaceAllUsesWith(chain->result_id(), replacementId); |
392 | 33.7k | } else { |
393 | | // Replace with a use of the variable. |
394 | 33.7k | context()->ReplaceAllUsesWith(chain->result_id(), var->result_id()); |
395 | 33.7k | } |
396 | 36.4k | } |
397 | | |
398 | 36.4k | return true; |
399 | 36.4k | } |
400 | | |
401 | | bool ScalarReplacementPass::CreateReplacementVariables( |
402 | 15.9k | Instruction* inst, std::vector<Instruction*>* replacements) { |
403 | 15.9k | Instruction* type = GetStorageType(inst); |
404 | | |
405 | 15.9k | std::unique_ptr<std::unordered_set<int64_t>> components_used = |
406 | 15.9k | GetUsedComponents(inst); |
407 | | |
408 | 15.9k | uint32_t elem = 0; |
409 | 15.9k | switch (type->opcode()) { |
410 | 14.0k | case spv::Op::OpTypeStruct: |
411 | 14.0k | type->ForEachInOperand( |
412 | 40.4k | [this, inst, &elem, replacements, &components_used](uint32_t* id) { |
413 | 40.4k | if (!components_used || components_used->count(elem)) { |
414 | 30.0k | CreateVariable(*id, inst, elem, replacements); |
415 | 30.0k | } else { |
416 | 10.3k | replacements->push_back(GetUndef(*id)); |
417 | 10.3k | } |
418 | 40.4k | elem++; |
419 | 40.4k | }); |
420 | 14.0k | break; |
421 | 1.93k | case spv::Op::OpTypeArray: |
422 | 95.6k | for (uint32_t i = 0; i != GetArrayLength(type); ++i) { |
423 | 93.7k | if (!components_used || components_used->count(i)) { |
424 | 48.9k | CreateVariable(type->GetSingleWordInOperand(0u), inst, i, |
425 | 48.9k | replacements); |
426 | 48.9k | } else { |
427 | 44.8k | uint32_t element_type_id = type->GetSingleWordInOperand(0); |
428 | 44.8k | replacements->push_back(GetUndef(element_type_id)); |
429 | 44.8k | } |
430 | 93.7k | } |
431 | 1.93k | break; |
432 | | |
433 | 0 | case spv::Op::OpTypeMatrix: |
434 | 0 | case spv::Op::OpTypeVector: |
435 | 0 | for (uint32_t i = 0; i != GetNumElements(type); ++i) { |
436 | 0 | CreateVariable(type->GetSingleWordInOperand(0u), inst, i, replacements); |
437 | 0 | } |
438 | 0 | break; |
439 | | |
440 | 0 | default: |
441 | 0 | assert(false && "Unexpected type."); |
442 | 0 | break; |
443 | 15.9k | } |
444 | | |
445 | 15.9k | TransferAnnotations(inst, replacements); |
446 | 15.9k | return std::find(replacements->begin(), replacements->end(), nullptr) == |
447 | 15.9k | replacements->end(); |
448 | 15.9k | } |
449 | | |
450 | 55.1k | Instruction* ScalarReplacementPass::GetUndef(uint32_t type_id) { |
451 | 55.1k | return get_def_use_mgr()->GetDef(Type2Undef(type_id)); |
452 | 55.1k | } |
453 | | |
454 | | void ScalarReplacementPass::TransferAnnotations( |
455 | 15.9k | const Instruction* source, std::vector<Instruction*>* replacements) { |
456 | | // Only transfer invariant and restrict decorations on the variable. There are |
457 | | // no type or member decorations that are necessary to transfer. |
458 | 15.9k | for (auto inst : |
459 | 15.9k | get_decoration_mgr()->GetDecorationsFor(source->result_id(), false)) { |
460 | 64 | assert(inst->opcode() == spv::Op::OpDecorate); |
461 | 64 | auto decoration = spv::Decoration(inst->GetSingleWordInOperand(1u)); |
462 | 64 | if (decoration == spv::Decoration::Invariant || |
463 | 64 | decoration == spv::Decoration::Restrict) { |
464 | 514 | for (auto var : *replacements) { |
465 | 514 | if (var == nullptr) { |
466 | 0 | continue; |
467 | 0 | } |
468 | | |
469 | 514 | std::unique_ptr<Instruction> annotation(new Instruction( |
470 | 514 | context(), spv::Op::OpDecorate, 0, 0, |
471 | 514 | std::initializer_list<Operand>{ |
472 | 514 | {SPV_OPERAND_TYPE_ID, {var->result_id()}}, |
473 | 514 | {SPV_OPERAND_TYPE_DECORATION, {uint32_t(decoration)}}})); |
474 | 514 | for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { |
475 | 0 | Operand copy(inst->GetInOperand(i)); |
476 | 0 | annotation->AddOperand(std::move(copy)); |
477 | 0 | } |
478 | 514 | context()->AddAnnotationInst(std::move(annotation)); |
479 | 514 | get_def_use_mgr()->AnalyzeInstUse(&*--context()->annotation_end()); |
480 | 514 | } |
481 | 64 | } |
482 | 64 | } |
483 | 15.9k | } |
484 | | |
485 | | void ScalarReplacementPass::CreateVariable( |
486 | | uint32_t type_id, Instruction* var_inst, uint32_t index, |
487 | 79.0k | std::vector<Instruction*>* replacements) { |
488 | 79.0k | uint32_t ptr_id = GetOrCreatePointerType(type_id); |
489 | 79.0k | uint32_t id = TakeNextId(); |
490 | | |
491 | 79.0k | if (id == 0) { |
492 | 0 | replacements->push_back(nullptr); |
493 | 0 | return; |
494 | 0 | } |
495 | | |
496 | 79.0k | std::unique_ptr<Instruction> variable( |
497 | 79.0k | new Instruction(context(), spv::Op::OpVariable, ptr_id, id, |
498 | 79.0k | std::initializer_list<Operand>{ |
499 | 79.0k | {SPV_OPERAND_TYPE_STORAGE_CLASS, |
500 | 79.0k | {uint32_t(spv::StorageClass::Function)}}})); |
501 | | |
502 | 79.0k | BasicBlock* block = context()->get_instr_block(var_inst); |
503 | 79.0k | block->begin().InsertBefore(std::move(variable)); |
504 | 79.0k | Instruction* inst = &*block->begin(); |
505 | | |
506 | | // If varInst was initialized, make sure to initialize its replacement. |
507 | 79.0k | if (!GetOrCreateInitialValue(var_inst, index, inst)) { |
508 | 0 | replacements->push_back(nullptr); |
509 | 0 | return; |
510 | 0 | } |
511 | 79.0k | get_def_use_mgr()->AnalyzeInstDefUse(inst); |
512 | 79.0k | context()->set_instr_block(inst, block); |
513 | | |
514 | 79.0k | CopyDecorationsToVariable(var_inst, inst, index); |
515 | 79.0k | inst->UpdateDebugInfoFrom(var_inst); |
516 | | |
517 | 79.0k | replacements->push_back(inst); |
518 | 79.0k | } |
519 | | |
520 | 79.0k | uint32_t ScalarReplacementPass::GetOrCreatePointerType(uint32_t id) { |
521 | 79.0k | auto iter = pointee_to_pointer_.find(id); |
522 | 79.0k | if (iter != pointee_to_pointer_.end()) return iter->second; |
523 | | |
524 | 2.00k | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
525 | 2.00k | uint32_t ptr_type_id = |
526 | 2.00k | type_mgr->FindPointerToType(id, spv::StorageClass::Function); |
527 | 2.00k | pointee_to_pointer_[id] = ptr_type_id; |
528 | 2.00k | return ptr_type_id; |
529 | 79.0k | } |
530 | | |
531 | | bool ScalarReplacementPass::GetOrCreateInitialValue(Instruction* source, |
532 | | uint32_t index, |
533 | 79.0k | Instruction* newVar) { |
534 | 79.0k | assert(source->opcode() == spv::Op::OpVariable); |
535 | 79.0k | if (source->NumInOperands() < 2) return true; |
536 | | |
537 | 0 | uint32_t initId = source->GetSingleWordInOperand(1u); |
538 | 0 | uint32_t storageId = GetStorageType(newVar)->result_id(); |
539 | 0 | Instruction* init = get_def_use_mgr()->GetDef(initId); |
540 | 0 | uint32_t newInitId = 0; |
541 | | // TODO(dnovillo): Refactor this with constant propagation. |
542 | 0 | if (init->opcode() == spv::Op::OpConstantNull) { |
543 | | // Initialize to appropriate NULL. |
544 | 0 | auto iter = type_to_null_.find(storageId); |
545 | 0 | if (iter == type_to_null_.end()) { |
546 | 0 | newInitId = TakeNextId(); |
547 | 0 | if (newInitId == 0) return false; |
548 | 0 | type_to_null_[storageId] = newInitId; |
549 | 0 | context()->AddGlobalValue( |
550 | 0 | MakeUnique<Instruction>(context(), spv::Op::OpConstantNull, storageId, |
551 | 0 | newInitId, std::initializer_list<Operand>{})); |
552 | 0 | Instruction* newNull = &*--context()->types_values_end(); |
553 | 0 | get_def_use_mgr()->AnalyzeInstDefUse(newNull); |
554 | 0 | } else { |
555 | 0 | newInitId = iter->second; |
556 | 0 | } |
557 | 0 | } else if (IsSpecConstantInst(init->opcode())) { |
558 | | // Create a new constant extract. |
559 | 0 | newInitId = TakeNextId(); |
560 | 0 | if (newInitId == 0) return false; |
561 | 0 | context()->AddGlobalValue(MakeUnique<Instruction>( |
562 | 0 | context(), spv::Op::OpSpecConstantOp, storageId, newInitId, |
563 | 0 | std::initializer_list<Operand>{ |
564 | 0 | {SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER, |
565 | 0 | {uint32_t(spv::Op::OpCompositeExtract)}}, |
566 | 0 | {SPV_OPERAND_TYPE_ID, {init->result_id()}}, |
567 | 0 | {SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}})); |
568 | 0 | Instruction* newSpecConst = &*--context()->types_values_end(); |
569 | 0 | get_def_use_mgr()->AnalyzeInstDefUse(newSpecConst); |
570 | 0 | } else if (init->opcode() == spv::Op::OpConstantComposite) { |
571 | | // Get the appropriate index constant. |
572 | 0 | newInitId = init->GetSingleWordInOperand(index); |
573 | 0 | Instruction* element = get_def_use_mgr()->GetDef(newInitId); |
574 | 0 | if (element->opcode() == spv::Op::OpUndef) { |
575 | | // Undef is not a valid initializer for a variable. |
576 | 0 | newInitId = 0; |
577 | 0 | } |
578 | 0 | } else { |
579 | 0 | assert(false); |
580 | 0 | } |
581 | | |
582 | 0 | if (newInitId != 0) { |
583 | 0 | newVar->AddOperand({SPV_OPERAND_TYPE_ID, {newInitId}}); |
584 | 0 | } |
585 | 0 | return true; |
586 | 0 | } |
587 | | |
588 | | uint64_t ScalarReplacementPass::GetArrayLength( |
589 | 112k | const Instruction* arrayType) const { |
590 | 112k | assert(arrayType->opcode() == spv::Op::OpTypeArray); |
591 | 112k | const Instruction* length = |
592 | 112k | get_def_use_mgr()->GetDef(arrayType->GetSingleWordInOperand(1u)); |
593 | 112k | return context() |
594 | 112k | ->get_constant_mgr() |
595 | 112k | ->GetConstantFromInst(length) |
596 | 112k | ->GetZeroExtendedValue(); |
597 | 112k | } |
598 | | |
599 | 0 | uint64_t ScalarReplacementPass::GetNumElements(const Instruction* type) const { |
600 | 0 | assert(type->opcode() == spv::Op::OpTypeVector || |
601 | 0 | type->opcode() == spv::Op::OpTypeMatrix); |
602 | 0 | const Operand& op = type->GetInOperand(1u); |
603 | 0 | assert(op.words.size() <= 2); |
604 | 0 | uint64_t len = 0; |
605 | 0 | for (size_t i = 0; i != op.words.size(); ++i) { |
606 | 0 | len |= (static_cast<uint64_t>(op.words[i]) << (32ull * i)); |
607 | 0 | } |
608 | 0 | return len; |
609 | 0 | } |
610 | | |
611 | 10.7k | bool ScalarReplacementPass::IsSpecConstant(uint32_t id) const { |
612 | 10.7k | const Instruction* inst = get_def_use_mgr()->GetDef(id); |
613 | 10.7k | assert(inst); |
614 | 10.7k | return spvOpcodeIsSpecConstant(inst->opcode()); |
615 | 10.7k | } |
616 | | |
617 | | Instruction* ScalarReplacementPass::GetStorageType( |
618 | 589k | const Instruction* inst) const { |
619 | 589k | assert(inst->opcode() == spv::Op::OpVariable); |
620 | | |
621 | 589k | uint32_t ptrTypeId = inst->type_id(); |
622 | 589k | uint32_t typeId = |
623 | 589k | get_def_use_mgr()->GetDef(ptrTypeId)->GetSingleWordInOperand(1u); |
624 | 589k | return get_def_use_mgr()->GetDef(typeId); |
625 | 589k | } |
626 | | |
627 | | bool ScalarReplacementPass::CanReplaceVariable( |
628 | 305k | const Instruction* varInst) const { |
629 | 305k | assert(varInst->opcode() == spv::Op::OpVariable); |
630 | | |
631 | | // Can only replace function scope variables. |
632 | 305k | if (spv::StorageClass(varInst->GetSingleWordInOperand(0u)) != |
633 | 305k | spv::StorageClass::Function) { |
634 | 0 | return false; |
635 | 0 | } |
636 | | |
637 | 305k | if (!CheckTypeAnnotations(get_def_use_mgr()->GetDef(varInst->type_id()))) { |
638 | 343 | return false; |
639 | 343 | } |
640 | | |
641 | 304k | const Instruction* typeInst = GetStorageType(varInst); |
642 | 304k | if (!CheckType(typeInst)) { |
643 | 276k | return false; |
644 | 276k | } |
645 | | |
646 | 28.1k | if (!CheckAnnotations(varInst)) { |
647 | 1.99k | return false; |
648 | 1.99k | } |
649 | | |
650 | 26.1k | if (!CheckUses(varInst)) { |
651 | 10.1k | return false; |
652 | 10.1k | } |
653 | | |
654 | 15.9k | return true; |
655 | 26.1k | } |
656 | | |
657 | 304k | bool ScalarReplacementPass::CheckType(const Instruction* typeInst) const { |
658 | 304k | if (!CheckTypeAnnotations(typeInst)) { |
659 | 1.33k | return false; |
660 | 1.33k | } |
661 | | |
662 | 303k | switch (typeInst->opcode()) { |
663 | 20.1k | case spv::Op::OpTypeStruct: |
664 | | // Don't bother with empty structs or very large structs. |
665 | 20.1k | if (typeInst->NumInOperands() == 0 || |
666 | 20.1k | IsLargerThanSizeLimit(typeInst->NumInOperands())) { |
667 | 0 | return false; |
668 | 0 | } |
669 | 20.1k | return true; |
670 | 10.7k | case spv::Op::OpTypeArray: |
671 | 10.7k | if (IsSpecConstant(typeInst->GetSingleWordInOperand(1u))) { |
672 | 229 | return false; |
673 | 229 | } |
674 | 10.5k | if (IsLargerThanSizeLimit(GetArrayLength(typeInst))) { |
675 | 2.53k | return false; |
676 | 2.53k | } |
677 | 7.98k | return true; |
678 | | // TODO(alanbaker): Develop some heuristics for when this should be |
679 | | // re-enabled. |
680 | | //// Specifically including matrix and vector in an attempt to reduce the |
681 | | //// number of vector registers required. |
682 | | // case spv::Op::OpTypeMatrix: |
683 | | // case spv::Op::OpTypeVector: |
684 | | // if (IsLargerThanSizeLimit(GetNumElements(typeInst))) return false; |
685 | | // return true; |
686 | | |
687 | 0 | case spv::Op::OpTypeRuntimeArray: |
688 | 272k | default: |
689 | 272k | return false; |
690 | 303k | } |
691 | 303k | } |
692 | | |
693 | | bool ScalarReplacementPass::CheckTypeAnnotations( |
694 | 609k | const Instruction* typeInst) const { |
695 | 609k | for (auto inst : |
696 | 609k | get_decoration_mgr()->GetDecorationsFor(typeInst->result_id(), false)) { |
697 | 13.7k | uint32_t decoration; |
698 | 13.7k | if (inst->opcode() == spv::Op::OpDecorate || |
699 | 12.7k | inst->opcode() == spv::Op::OpDecorateId) { |
700 | 975 | decoration = inst->GetSingleWordInOperand(1u); |
701 | 12.7k | } else { |
702 | 12.7k | assert(inst->opcode() == spv::Op::OpMemberDecorate); |
703 | 12.7k | decoration = inst->GetSingleWordInOperand(2u); |
704 | 12.7k | } |
705 | | |
706 | 13.7k | switch (spv::Decoration(decoration)) { |
707 | 451 | case spv::Decoration::RowMajor: |
708 | 573 | case spv::Decoration::ColMajor: |
709 | 573 | case spv::Decoration::ArrayStride: |
710 | 573 | case spv::Decoration::MatrixStride: |
711 | 573 | case spv::Decoration::CPacked: |
712 | 1.19k | case spv::Decoration::Invariant: |
713 | 1.68k | case spv::Decoration::Restrict: |
714 | 1.70k | case spv::Decoration::Offset: |
715 | 1.70k | case spv::Decoration::Alignment: |
716 | 1.70k | case spv::Decoration::AlignmentId: |
717 | 1.70k | case spv::Decoration::MaxByteOffset: |
718 | 12.0k | case spv::Decoration::RelaxedPrecision: |
719 | 12.0k | case spv::Decoration::AliasedPointer: |
720 | 12.0k | case spv::Decoration::RestrictPointer: |
721 | 12.0k | break; |
722 | 1.67k | default: |
723 | 1.67k | return false; |
724 | 13.7k | } |
725 | 13.7k | } |
726 | | |
727 | 608k | return true; |
728 | 609k | } |
729 | | |
730 | 28.1k | bool ScalarReplacementPass::CheckAnnotations(const Instruction* varInst) const { |
731 | 28.1k | for (auto inst : |
732 | 28.1k | get_decoration_mgr()->GetDecorationsFor(varInst->result_id(), false)) { |
733 | 2.14k | assert(inst->opcode() == spv::Op::OpDecorate); |
734 | 2.14k | auto decoration = spv::Decoration(inst->GetSingleWordInOperand(1u)); |
735 | 2.14k | switch (decoration) { |
736 | 37 | case spv::Decoration::Invariant: |
737 | 147 | case spv::Decoration::Restrict: |
738 | 147 | case spv::Decoration::Alignment: |
739 | 147 | case spv::Decoration::AlignmentId: |
740 | 147 | case spv::Decoration::MaxByteOffset: |
741 | 147 | case spv::Decoration::AliasedPointer: |
742 | 147 | case spv::Decoration::RestrictPointer: |
743 | 147 | break; |
744 | 1.99k | default: |
745 | 1.99k | return false; |
746 | 2.14k | } |
747 | 2.14k | } |
748 | | |
749 | 26.1k | return true; |
750 | 28.1k | } |
751 | | |
752 | 26.1k | bool ScalarReplacementPass::CheckUses(const Instruction* inst) const { |
753 | 26.1k | VariableStats stats = {0, 0}; |
754 | 26.1k | bool ok = CheckUses(inst, &stats); |
755 | | |
756 | | // TODO(alanbaker/greg-lunarg): Add some meaningful heuristics about when |
757 | | // SRoA is costly, such as when the structure has many (unaccessed?) |
758 | | // members. |
759 | | |
760 | 26.1k | return ok; |
761 | 26.1k | } |
762 | | |
763 | | bool ScalarReplacementPass::CheckUses(const Instruction* inst, |
764 | 26.1k | VariableStats* stats) const { |
765 | 26.1k | uint64_t max_legal_index = GetMaxLegalIndex(inst); |
766 | | |
767 | 26.1k | bool ok = true; |
768 | 26.1k | get_def_use_mgr()->ForEachUse(inst, [this, max_legal_index, stats, &ok]( |
769 | 26.1k | const Instruction* user, |
770 | 149k | uint32_t index) { |
771 | 149k | if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || |
772 | 149k | user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { |
773 | | // TODO: include num_partial_accesses if it uses Fragment operation or |
774 | | // DebugValue has Indexes operand. |
775 | 0 | stats->num_full_accesses++; |
776 | 0 | return; |
777 | 0 | } |
778 | | |
779 | | // Annotations are check as a group separately. |
780 | 149k | if (!IsAnnotationInst(user->opcode())) { |
781 | 149k | switch (user->opcode()) { |
782 | 105k | case spv::Op::OpAccessChain: |
783 | 106k | case spv::Op::OpInBoundsAccessChain: |
784 | 106k | if (index == 2u && user->NumInOperands() > 1) { |
785 | 106k | uint32_t id = user->GetSingleWordInOperand(1u); |
786 | 106k | const Instruction* opInst = get_def_use_mgr()->GetDef(id); |
787 | 106k | const auto* constant = |
788 | 106k | context()->get_constant_mgr()->GetConstantFromInst(opInst); |
789 | 106k | if (!constant) { |
790 | 47.9k | ok = false; |
791 | 58.0k | } else if (constant->GetZeroExtendedValue() >= max_legal_index) { |
792 | 3.14k | ok = false; |
793 | 54.9k | } else { |
794 | 54.9k | if (!CheckUsesRelaxed(user)) ok = false; |
795 | 54.9k | } |
796 | 106k | stats->num_partial_accesses++; |
797 | 106k | } else { |
798 | 0 | ok = false; |
799 | 0 | } |
800 | 106k | break; |
801 | 16.3k | case spv::Op::OpLoad: |
802 | 16.3k | if (!CheckLoad(user, index)) ok = false; |
803 | 16.3k | stats->num_full_accesses++; |
804 | 16.3k | break; |
805 | 19.6k | case spv::Op::OpStore: |
806 | 19.6k | if (!CheckStore(user, index)) ok = false; |
807 | 19.6k | stats->num_full_accesses++; |
808 | 19.6k | break; |
809 | 2.40k | case spv::Op::OpName: |
810 | 2.40k | case spv::Op::OpMemberName: |
811 | 2.40k | break; |
812 | 4.63k | default: |
813 | 4.63k | ok = false; |
814 | 4.63k | break; |
815 | 149k | } |
816 | 149k | } |
817 | 149k | }); |
818 | | |
819 | 26.1k | return ok; |
820 | 26.1k | } |
821 | | |
822 | 54.9k | bool ScalarReplacementPass::CheckUsesRelaxed(const Instruction* inst) const { |
823 | 54.9k | bool ok = true; |
824 | 54.9k | get_def_use_mgr()->ForEachUse( |
825 | 59.5k | inst, [this, &ok](const Instruction* user, uint32_t index) { |
826 | 59.5k | switch (user->opcode()) { |
827 | 0 | case spv::Op::OpAccessChain: |
828 | 0 | case spv::Op::OpInBoundsAccessChain: |
829 | 0 | if (index != 2u) { |
830 | 0 | ok = false; |
831 | 0 | } else { |
832 | 0 | if (!CheckUsesRelaxed(user)) ok = false; |
833 | 0 | } |
834 | 0 | break; |
835 | 21.0k | case spv::Op::OpLoad: |
836 | 21.0k | if (!CheckLoad(user, index)) ok = false; |
837 | 21.0k | break; |
838 | 36.7k | case spv::Op::OpStore: |
839 | 36.7k | if (!CheckStore(user, index)) ok = false; |
840 | 36.7k | break; |
841 | 0 | case spv::Op::OpImageTexelPointer: |
842 | 0 | if (!CheckImageTexelPointer(index)) ok = false; |
843 | 0 | break; |
844 | 0 | case spv::Op::OpExtInst: |
845 | 0 | if (user->GetCommonDebugOpcode() != CommonDebugInfoDebugDeclare || |
846 | 0 | !CheckDebugDeclare(index)) |
847 | 0 | ok = false; |
848 | 0 | break; |
849 | 1.63k | default: |
850 | 1.63k | ok = false; |
851 | 1.63k | break; |
852 | 59.5k | } |
853 | 59.5k | }); |
854 | | |
855 | 54.9k | return ok; |
856 | 54.9k | } |
857 | | |
858 | 0 | bool ScalarReplacementPass::CheckImageTexelPointer(uint32_t index) const { |
859 | 0 | return index == 2u; |
860 | 0 | } |
861 | | |
862 | | bool ScalarReplacementPass::CheckLoad(const Instruction* inst, |
863 | 37.4k | uint32_t index) const { |
864 | 37.4k | if (index != 2u) return false; |
865 | 37.4k | if (inst->NumInOperands() >= 2 && |
866 | 444 | inst->GetSingleWordInOperand(1u) & |
867 | 444 | uint32_t(spv::MemoryAccessMask::Volatile)) |
868 | 415 | return false; |
869 | 37.0k | return true; |
870 | 37.4k | } |
871 | | |
872 | | bool ScalarReplacementPass::CheckStore(const Instruction* inst, |
873 | 56.4k | uint32_t index) const { |
874 | 56.4k | if (index != 0u) return false; |
875 | 56.4k | if (inst->NumInOperands() >= 3 && |
876 | 559 | inst->GetSingleWordInOperand(2u) & |
877 | 559 | uint32_t(spv::MemoryAccessMask::Volatile)) |
878 | 391 | return false; |
879 | 56.0k | return true; |
880 | 56.4k | } |
881 | | |
882 | 0 | bool ScalarReplacementPass::CheckDebugDeclare(uint32_t index) const { |
883 | 0 | if (index != kDebugDeclareOperandVariableIndex) return false; |
884 | 0 | return true; |
885 | 0 | } |
886 | | |
887 | 30.6k | bool ScalarReplacementPass::IsLargerThanSizeLimit(uint64_t length) const { |
888 | 30.6k | if (max_num_elements_ == 0) { |
889 | 0 | return false; |
890 | 0 | } |
891 | 30.6k | return length > max_num_elements_; |
892 | 30.6k | } |
893 | | |
894 | | std::unique_ptr<std::unordered_set<int64_t>> |
895 | 15.9k | ScalarReplacementPass::GetUsedComponents(Instruction* inst) { |
896 | 15.9k | std::unique_ptr<std::unordered_set<int64_t>> result( |
897 | 15.9k | new std::unordered_set<int64_t>()); |
898 | | |
899 | 15.9k | analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); |
900 | | |
901 | 15.9k | def_use_mgr->WhileEachUser(inst, [&result, def_use_mgr, |
902 | 35.3k | this](Instruction* use) { |
903 | 35.3k | switch (use->opcode()) { |
904 | 9.02k | case spv::Op::OpLoad: { |
905 | | // Look for extract from the load. |
906 | 9.02k | std::vector<uint32_t> t; |
907 | 21.7k | if (def_use_mgr->WhileEachUser(use, [&t](Instruction* use2) { |
908 | 21.7k | if (use2->opcode() != spv::Op::OpCompositeExtract || |
909 | 13.4k | use2->NumInOperands() <= 1) { |
910 | 8.35k | return false; |
911 | 8.35k | } |
912 | 13.4k | t.push_back(use2->GetSingleWordInOperand(1)); |
913 | 13.4k | return true; |
914 | 21.7k | })) { |
915 | 673 | result->insert(t.begin(), t.end()); |
916 | 673 | return true; |
917 | 8.35k | } else { |
918 | 8.35k | result.reset(nullptr); |
919 | 8.35k | return false; |
920 | 8.35k | } |
921 | 9.02k | } |
922 | 1.21k | case spv::Op::OpName: |
923 | 1.21k | case spv::Op::OpMemberName: |
924 | 13.1k | case spv::Op::OpStore: |
925 | | // No components are used. |
926 | 13.1k | return true; |
927 | 13.0k | case spv::Op::OpAccessChain: |
928 | 13.1k | case spv::Op::OpInBoundsAccessChain: { |
929 | | // Add the first index it if is a constant. |
930 | | // TODO: Could be improved by checking if the address is used in a load. |
931 | 13.1k | analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); |
932 | 13.1k | uint32_t index_id = use->GetSingleWordInOperand(1); |
933 | 13.1k | const analysis::Constant* index_const = |
934 | 13.1k | const_mgr->FindDeclaredConstant(index_id); |
935 | 13.1k | if (index_const) { |
936 | 13.1k | result->insert(index_const->GetSignExtendedValue()); |
937 | 13.1k | return true; |
938 | 13.1k | } else { |
939 | | // Could be any element. Assuming all are used. |
940 | 0 | result.reset(nullptr); |
941 | 0 | return false; |
942 | 0 | } |
943 | 13.1k | } |
944 | 61 | default: |
945 | | // We do not know what is happening. Have to assume the worst. |
946 | 61 | result.reset(nullptr); |
947 | 61 | return false; |
948 | 35.3k | } |
949 | 35.3k | }); |
950 | | |
951 | 15.9k | return result; |
952 | 15.9k | } |
953 | | |
954 | | uint64_t ScalarReplacementPass::GetMaxLegalIndex( |
955 | 26.1k | const Instruction* var_inst) const { |
956 | 26.1k | assert(var_inst->opcode() == spv::Op::OpVariable && |
957 | 26.1k | "|var_inst| must be a variable instruction."); |
958 | 26.1k | Instruction* type = GetStorageType(var_inst); |
959 | 26.1k | switch (type->opcode()) { |
960 | 19.3k | case spv::Op::OpTypeStruct: |
961 | 19.3k | return type->NumInOperands(); |
962 | 6.77k | case spv::Op::OpTypeArray: |
963 | 6.77k | return GetArrayLength(type); |
964 | 0 | case spv::Op::OpTypeMatrix: |
965 | 0 | case spv::Op::OpTypeVector: |
966 | 0 | return GetNumElements(type); |
967 | 0 | default: |
968 | 0 | return 0; |
969 | 26.1k | } |
970 | 0 | return 0; |
971 | 26.1k | } |
972 | | |
973 | | void ScalarReplacementPass::CopyDecorationsToVariable(Instruction* from, |
974 | | Instruction* to, |
975 | 79.0k | uint32_t member_index) { |
976 | 79.0k | CopyPointerDecorationsToVariable(from, to); |
977 | 79.0k | CopyNecessaryMemberDecorationsToVariable(from, to, member_index); |
978 | 79.0k | } |
979 | | |
980 | | void ScalarReplacementPass::CopyPointerDecorationsToVariable(Instruction* from, |
981 | 79.0k | Instruction* to) { |
982 | | // The RestrictPointer and AliasedPointer decorations are copied to all |
983 | | // members even if the new variable does not contain a pointer. It does |
984 | | // not hurt to do so. |
985 | 79.0k | for (auto dec_inst : |
986 | 79.0k | get_decoration_mgr()->GetDecorationsFor(from->result_id(), false)) { |
987 | 514 | uint32_t decoration; |
988 | 514 | decoration = dec_inst->GetSingleWordInOperand(1u); |
989 | 514 | switch (spv::Decoration(decoration)) { |
990 | 0 | case spv::Decoration::AliasedPointer: |
991 | 0 | case spv::Decoration::RestrictPointer: { |
992 | 0 | std::unique_ptr<Instruction> new_dec_inst(dec_inst->Clone(context())); |
993 | 0 | new_dec_inst->SetInOperand(0, {to->result_id()}); |
994 | 0 | context()->AddAnnotationInst(std::move(new_dec_inst)); |
995 | 0 | } break; |
996 | 514 | default: |
997 | 514 | break; |
998 | 514 | } |
999 | 514 | } |
1000 | 79.0k | } |
1001 | | |
1002 | | void ScalarReplacementPass::CopyNecessaryMemberDecorationsToVariable( |
1003 | 79.0k | Instruction* from, Instruction* to, uint32_t member_index) { |
1004 | 79.0k | Instruction* type_inst = GetStorageType(from); |
1005 | 79.0k | for (auto dec_inst : |
1006 | 79.0k | get_decoration_mgr()->GetDecorationsFor(type_inst->result_id(), false)) { |
1007 | 20.7k | uint32_t decoration; |
1008 | 20.7k | if (dec_inst->opcode() == spv::Op::OpMemberDecorate) { |
1009 | 20.7k | if (dec_inst->GetSingleWordInOperand(1) != member_index) { |
1010 | 13.4k | continue; |
1011 | 13.4k | } |
1012 | | |
1013 | 7.29k | decoration = dec_inst->GetSingleWordInOperand(2u); |
1014 | 7.29k | switch (spv::Decoration(decoration)) { |
1015 | 0 | case spv::Decoration::ArrayStride: |
1016 | 0 | case spv::Decoration::Alignment: |
1017 | 0 | case spv::Decoration::AlignmentId: |
1018 | 0 | case spv::Decoration::MaxByteOffset: |
1019 | 0 | case spv::Decoration::MaxByteOffsetId: |
1020 | 6.47k | case spv::Decoration::RelaxedPrecision: { |
1021 | 6.47k | std::unique_ptr<Instruction> new_dec_inst( |
1022 | 6.47k | new Instruction(context(), spv::Op::OpDecorate, 0, 0, {})); |
1023 | 6.47k | new_dec_inst->AddOperand( |
1024 | 6.47k | Operand(SPV_OPERAND_TYPE_ID, {to->result_id()})); |
1025 | 12.9k | for (uint32_t i = 2; i < dec_inst->NumInOperandWords(); ++i) { |
1026 | 6.47k | new_dec_inst->AddOperand(Operand(dec_inst->GetInOperand(i))); |
1027 | 6.47k | } |
1028 | 6.47k | context()->AddAnnotationInst(std::move(new_dec_inst)); |
1029 | 6.47k | } break; |
1030 | 826 | default: |
1031 | 826 | break; |
1032 | 7.29k | } |
1033 | 7.29k | } |
1034 | 20.7k | } |
1035 | 79.0k | } |
1036 | | |
1037 | | } // namespace opt |
1038 | | } // namespace spvtools |