1 // Copyright (c) 2016 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/instruction.h"
16
17 #include <initializer_list>
18
19 #include "OpenCLDebugInfo100.h"
20 #include "source/disassemble.h"
21 #include "source/opt/fold.h"
22 #include "source/opt/ir_context.h"
23 #include "source/opt/reflect.h"
24
25 namespace spvtools {
26 namespace opt {
27
28 namespace {
29 // Indices used to get particular operands out of instructions using InOperand.
30 const uint32_t kTypeImageDimIndex = 1;
31 const uint32_t kLoadBaseIndex = 0;
32 const uint32_t kPointerTypeStorageClassIndex = 0;
33 const uint32_t kVariableStorageClassIndex = 0;
34 const uint32_t kTypeImageSampledIndex = 5;
35
36 // Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100
37 // extension instructions.
38 const uint32_t kExtInstSetIdInIdx = 0;
39 const uint32_t kExtInstInstructionInIdx = 1;
40 const uint32_t kDebugScopeNumWords = 7;
41 const uint32_t kDebugScopeNumWordsWithoutInlinedAt = 6;
42 const uint32_t kDebugNoScopeNumWords = 5;
43
44 // Number of operands of an OpBranchConditional instruction
45 // with weights.
46 const uint32_t kOpBranchConditionalWithWeightsNumOperands = 5;
47 } // namespace
48
Instruction(IRContext * c)49 Instruction::Instruction(IRContext* c)
50 : utils::IntrusiveNodeBase<Instruction>(),
51 context_(c),
52 opcode_(SpvOpNop),
53 has_type_id_(false),
54 has_result_id_(false),
55 unique_id_(c->TakeNextUniqueId()),
56 dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
57
Instruction(IRContext * c,SpvOp op)58 Instruction::Instruction(IRContext* c, SpvOp op)
59 : utils::IntrusiveNodeBase<Instruction>(),
60 context_(c),
61 opcode_(op),
62 has_type_id_(false),
63 has_result_id_(false),
64 unique_id_(c->TakeNextUniqueId()),
65 dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
66
Instruction(IRContext * c,const spv_parsed_instruction_t & inst,std::vector<Instruction> && dbg_line)67 Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
68 std::vector<Instruction>&& dbg_line)
69 : utils::IntrusiveNodeBase<Instruction>(),
70 context_(c),
71 opcode_(static_cast<SpvOp>(inst.opcode)),
72 has_type_id_(inst.type_id != 0),
73 has_result_id_(inst.result_id != 0),
74 unique_id_(c->TakeNextUniqueId()),
75 dbg_line_insts_(std::move(dbg_line)),
76 dbg_scope_(kNoDebugScope, kNoInlinedAt) {
77 for (uint32_t i = 0; i < inst.num_operands; ++i) {
78 const auto& current_payload = inst.operands[i];
79 operands_.emplace_back(
80 current_payload.type, inst.words + current_payload.offset,
81 inst.words + current_payload.offset + current_payload.num_words);
82 }
83 assert((!IsLineInst() || dbg_line.empty()) &&
84 "Op(No)Line attaching to Op(No)Line found");
85 }
86
Instruction(IRContext * c,const spv_parsed_instruction_t & inst,const DebugScope & dbg_scope)87 Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
88 const DebugScope& dbg_scope)
89 : utils::IntrusiveNodeBase<Instruction>(),
90 context_(c),
91 opcode_(static_cast<SpvOp>(inst.opcode)),
92 has_type_id_(inst.type_id != 0),
93 has_result_id_(inst.result_id != 0),
94 unique_id_(c->TakeNextUniqueId()),
95 dbg_scope_(dbg_scope) {
96 for (uint32_t i = 0; i < inst.num_operands; ++i) {
97 const auto& current_payload = inst.operands[i];
98 operands_.emplace_back(
99 current_payload.type, inst.words + current_payload.offset,
100 inst.words + current_payload.offset + current_payload.num_words);
101 }
102 }
103
Instruction(IRContext * c,SpvOp op,uint32_t ty_id,uint32_t res_id,const OperandList & in_operands)104 Instruction::Instruction(IRContext* c, SpvOp op, uint32_t ty_id,
105 uint32_t res_id, const OperandList& in_operands)
106 : utils::IntrusiveNodeBase<Instruction>(),
107 context_(c),
108 opcode_(op),
109 has_type_id_(ty_id != 0),
110 has_result_id_(res_id != 0),
111 unique_id_(c->TakeNextUniqueId()),
112 operands_(),
113 dbg_scope_(kNoDebugScope, kNoInlinedAt) {
114 if (has_type_id_) {
115 operands_.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_TYPE_ID,
116 std::initializer_list<uint32_t>{ty_id});
117 }
118 if (has_result_id_) {
119 operands_.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_RESULT_ID,
120 std::initializer_list<uint32_t>{res_id});
121 }
122 operands_.insert(operands_.end(), in_operands.begin(), in_operands.end());
123 }
124
Instruction(Instruction && that)125 Instruction::Instruction(Instruction&& that)
126 : utils::IntrusiveNodeBase<Instruction>(),
127 context_(that.context_),
128 opcode_(that.opcode_),
129 has_type_id_(that.has_type_id_),
130 has_result_id_(that.has_result_id_),
131 unique_id_(that.unique_id_),
132 operands_(std::move(that.operands_)),
133 dbg_line_insts_(std::move(that.dbg_line_insts_)),
134 dbg_scope_(that.dbg_scope_) {
135 for (auto& i : dbg_line_insts_) {
136 i.dbg_scope_ = that.dbg_scope_;
137 }
138 }
139
operator =(Instruction && that)140 Instruction& Instruction::operator=(Instruction&& that) {
141 context_ = that.context_;
142 opcode_ = that.opcode_;
143 has_type_id_ = that.has_type_id_;
144 has_result_id_ = that.has_result_id_;
145 unique_id_ = that.unique_id_;
146 operands_ = std::move(that.operands_);
147 dbg_line_insts_ = std::move(that.dbg_line_insts_);
148 dbg_scope_ = that.dbg_scope_;
149 return *this;
150 }
151
Clone(IRContext * c) const152 Instruction* Instruction::Clone(IRContext* c) const {
153 Instruction* clone = new Instruction(c);
154 clone->opcode_ = opcode_;
155 clone->has_type_id_ = has_type_id_;
156 clone->has_result_id_ = has_result_id_;
157 clone->unique_id_ = c->TakeNextUniqueId();
158 clone->operands_ = operands_;
159 clone->dbg_line_insts_ = dbg_line_insts_;
160 for (auto& i : clone->dbg_line_insts_) {
161 i.unique_id_ = c->TakeNextUniqueId();
162 if (i.IsDebugLineInst()) i.SetResultId(c->TakeNextId());
163 }
164 clone->dbg_scope_ = dbg_scope_;
165 return clone;
166 }
167
GetSingleWordOperand(uint32_t index) const168 uint32_t Instruction::GetSingleWordOperand(uint32_t index) const {
169 const auto& words = GetOperand(index).words;
170 assert(words.size() == 1 && "expected the operand only taking one word");
171 return words.front();
172 }
173
NumInOperandWords() const174 uint32_t Instruction::NumInOperandWords() const {
175 uint32_t size = 0;
176 for (uint32_t i = TypeResultIdCount(); i < operands_.size(); ++i)
177 size += static_cast<uint32_t>(operands_[i].words.size());
178 return size;
179 }
180
HasBranchWeights() const181 bool Instruction::HasBranchWeights() const {
182 if (opcode_ == SpvOpBranchConditional &&
183 NumOperands() == kOpBranchConditionalWithWeightsNumOperands) {
184 return true;
185 }
186
187 return false;
188 }
189
ToBinaryWithoutAttachedDebugInsts(std::vector<uint32_t> * binary) const190 void Instruction::ToBinaryWithoutAttachedDebugInsts(
191 std::vector<uint32_t>* binary) const {
192 const uint32_t num_words = 1 + NumOperandWords();
193 binary->push_back((num_words << 16) | static_cast<uint16_t>(opcode_));
194 for (const auto& operand : operands_) {
195 binary->insert(binary->end(), operand.words.begin(), operand.words.end());
196 }
197 }
198
ReplaceOperands(const OperandList & new_operands)199 void Instruction::ReplaceOperands(const OperandList& new_operands) {
200 operands_.clear();
201 operands_.insert(operands_.begin(), new_operands.begin(), new_operands.end());
202 }
203
IsReadOnlyLoad() const204 bool Instruction::IsReadOnlyLoad() const {
205 if (IsLoad()) {
206 Instruction* address_def = GetBaseAddress();
207 if (!address_def) {
208 return false;
209 }
210
211 if (address_def->opcode() == SpvOpVariable) {
212 if (address_def->IsReadOnlyPointer()) {
213 return true;
214 }
215 }
216
217 if (address_def->opcode() == SpvOpLoad) {
218 const analysis::Type* address_type =
219 context()->get_type_mgr()->GetType(address_def->type_id());
220 if (address_type->AsSampledImage() != nullptr) {
221 const auto* image_type =
222 address_type->AsSampledImage()->image_type()->AsImage();
223 if (image_type->sampled() == 1) {
224 return true;
225 }
226 }
227 }
228 }
229 return false;
230 }
231
GetBaseAddress() const232 Instruction* Instruction::GetBaseAddress() const {
233 uint32_t base = GetSingleWordInOperand(kLoadBaseIndex);
234 Instruction* base_inst = context()->get_def_use_mgr()->GetDef(base);
235 bool done = false;
236 while (!done) {
237 switch (base_inst->opcode()) {
238 case SpvOpAccessChain:
239 case SpvOpInBoundsAccessChain:
240 case SpvOpPtrAccessChain:
241 case SpvOpInBoundsPtrAccessChain:
242 case SpvOpImageTexelPointer:
243 case SpvOpCopyObject:
244 // All of these instructions have the base pointer use a base pointer
245 // in in-operand 0.
246 base = base_inst->GetSingleWordInOperand(0);
247 base_inst = context()->get_def_use_mgr()->GetDef(base);
248 break;
249 default:
250 done = true;
251 break;
252 }
253 }
254 return base_inst;
255 }
256
IsReadOnlyPointer() const257 bool Instruction::IsReadOnlyPointer() const {
258 if (context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
259 return IsReadOnlyPointerShaders();
260 else
261 return IsReadOnlyPointerKernel();
262 }
263
IsVulkanStorageImage() const264 bool Instruction::IsVulkanStorageImage() const {
265 if (opcode() != SpvOpTypePointer) {
266 return false;
267 }
268
269 uint32_t storage_class =
270 GetSingleWordInOperand(kPointerTypeStorageClassIndex);
271 if (storage_class != SpvStorageClassUniformConstant) {
272 return false;
273 }
274
275 Instruction* base_type =
276 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
277
278 // Unpack the optional layer of arraying.
279 if (base_type->opcode() == SpvOpTypeArray ||
280 base_type->opcode() == SpvOpTypeRuntimeArray) {
281 base_type = context()->get_def_use_mgr()->GetDef(
282 base_type->GetSingleWordInOperand(0));
283 }
284
285 if (base_type->opcode() != SpvOpTypeImage) {
286 return false;
287 }
288
289 if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) == SpvDimBuffer) {
290 return false;
291 }
292
293 // Check if the image is sampled. If we do not know for sure that it is,
294 // then assume it is a storage image.
295 return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) != 1;
296 }
297
IsVulkanSampledImage() const298 bool Instruction::IsVulkanSampledImage() const {
299 if (opcode() != SpvOpTypePointer) {
300 return false;
301 }
302
303 uint32_t storage_class =
304 GetSingleWordInOperand(kPointerTypeStorageClassIndex);
305 if (storage_class != SpvStorageClassUniformConstant) {
306 return false;
307 }
308
309 Instruction* base_type =
310 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
311
312 // Unpack the optional layer of arraying.
313 if (base_type->opcode() == SpvOpTypeArray ||
314 base_type->opcode() == SpvOpTypeRuntimeArray) {
315 base_type = context()->get_def_use_mgr()->GetDef(
316 base_type->GetSingleWordInOperand(0));
317 }
318
319 if (base_type->opcode() != SpvOpTypeImage) {
320 return false;
321 }
322
323 if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) == SpvDimBuffer) {
324 return false;
325 }
326
327 // Check if the image is sampled. If we know for sure that it is,
328 // then return true.
329 return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) == 1;
330 }
331
IsVulkanStorageTexelBuffer() const332 bool Instruction::IsVulkanStorageTexelBuffer() const {
333 if (opcode() != SpvOpTypePointer) {
334 return false;
335 }
336
337 uint32_t storage_class =
338 GetSingleWordInOperand(kPointerTypeStorageClassIndex);
339 if (storage_class != SpvStorageClassUniformConstant) {
340 return false;
341 }
342
343 Instruction* base_type =
344 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
345
346 // Unpack the optional layer of arraying.
347 if (base_type->opcode() == SpvOpTypeArray ||
348 base_type->opcode() == SpvOpTypeRuntimeArray) {
349 base_type = context()->get_def_use_mgr()->GetDef(
350 base_type->GetSingleWordInOperand(0));
351 }
352
353 if (base_type->opcode() != SpvOpTypeImage) {
354 return false;
355 }
356
357 if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) != SpvDimBuffer) {
358 return false;
359 }
360
361 // Check if the image is sampled. If we do not know for sure that it is,
362 // then assume it is a storage texel buffer.
363 return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) != 1;
364 }
365
IsVulkanStorageBuffer() const366 bool Instruction::IsVulkanStorageBuffer() const {
367 // Is there a difference between a "Storage buffer" and a "dynamic storage
368 // buffer" in SPIR-V and do we care about the difference?
369 if (opcode() != SpvOpTypePointer) {
370 return false;
371 }
372
373 Instruction* base_type =
374 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
375
376 // Unpack the optional layer of arraying.
377 if (base_type->opcode() == SpvOpTypeArray ||
378 base_type->opcode() == SpvOpTypeRuntimeArray) {
379 base_type = context()->get_def_use_mgr()->GetDef(
380 base_type->GetSingleWordInOperand(0));
381 }
382
383 if (base_type->opcode() != SpvOpTypeStruct) {
384 return false;
385 }
386
387 uint32_t storage_class =
388 GetSingleWordInOperand(kPointerTypeStorageClassIndex);
389 if (storage_class == SpvStorageClassUniform) {
390 bool is_buffer_block = false;
391 context()->get_decoration_mgr()->ForEachDecoration(
392 base_type->result_id(), SpvDecorationBufferBlock,
393 [&is_buffer_block](const Instruction&) { is_buffer_block = true; });
394 return is_buffer_block;
395 } else if (storage_class == SpvStorageClassStorageBuffer) {
396 bool is_block = false;
397 context()->get_decoration_mgr()->ForEachDecoration(
398 base_type->result_id(), SpvDecorationBlock,
399 [&is_block](const Instruction&) { is_block = true; });
400 return is_block;
401 }
402 return false;
403 }
404
IsVulkanStorageBufferVariable() const405 bool Instruction::IsVulkanStorageBufferVariable() const {
406 if (opcode() != SpvOpVariable) {
407 return false;
408 }
409
410 uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
411 if (storage_class == SpvStorageClassStorageBuffer ||
412 storage_class == SpvStorageClassUniform) {
413 Instruction* var_type = context()->get_def_use_mgr()->GetDef(type_id());
414 return var_type != nullptr && var_type->IsVulkanStorageBuffer();
415 }
416
417 return false;
418 }
419
IsVulkanUniformBuffer() const420 bool Instruction::IsVulkanUniformBuffer() const {
421 if (opcode() != SpvOpTypePointer) {
422 return false;
423 }
424
425 uint32_t storage_class =
426 GetSingleWordInOperand(kPointerTypeStorageClassIndex);
427 if (storage_class != SpvStorageClassUniform) {
428 return false;
429 }
430
431 Instruction* base_type =
432 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
433
434 // Unpack the optional layer of arraying.
435 if (base_type->opcode() == SpvOpTypeArray ||
436 base_type->opcode() == SpvOpTypeRuntimeArray) {
437 base_type = context()->get_def_use_mgr()->GetDef(
438 base_type->GetSingleWordInOperand(0));
439 }
440
441 if (base_type->opcode() != SpvOpTypeStruct) {
442 return false;
443 }
444
445 bool is_block = false;
446 context()->get_decoration_mgr()->ForEachDecoration(
447 base_type->result_id(), SpvDecorationBlock,
448 [&is_block](const Instruction&) { is_block = true; });
449 return is_block;
450 }
451
IsReadOnlyPointerShaders() const452 bool Instruction::IsReadOnlyPointerShaders() const {
453 if (type_id() == 0) {
454 return false;
455 }
456
457 Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id());
458 if (type_def->opcode() != SpvOpTypePointer) {
459 return false;
460 }
461
462 uint32_t storage_class =
463 type_def->GetSingleWordInOperand(kPointerTypeStorageClassIndex);
464
465 switch (storage_class) {
466 case SpvStorageClassUniformConstant:
467 if (!type_def->IsVulkanStorageImage() &&
468 !type_def->IsVulkanStorageTexelBuffer()) {
469 return true;
470 }
471 break;
472 case SpvStorageClassUniform:
473 if (!type_def->IsVulkanStorageBuffer()) {
474 return true;
475 }
476 break;
477 case SpvStorageClassPushConstant:
478 case SpvStorageClassInput:
479 return true;
480 default:
481 break;
482 }
483
484 bool is_nonwritable = false;
485 context()->get_decoration_mgr()->ForEachDecoration(
486 result_id(), SpvDecorationNonWritable,
487 [&is_nonwritable](const Instruction&) { is_nonwritable = true; });
488 return is_nonwritable;
489 }
490
IsReadOnlyPointerKernel() const491 bool Instruction::IsReadOnlyPointerKernel() const {
492 if (type_id() == 0) {
493 return false;
494 }
495
496 Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id());
497 if (type_def->opcode() != SpvOpTypePointer) {
498 return false;
499 }
500
501 uint32_t storage_class =
502 type_def->GetSingleWordInOperand(kPointerTypeStorageClassIndex);
503
504 return storage_class == SpvStorageClassUniformConstant;
505 }
506
UpdateLexicalScope(uint32_t scope)507 void Instruction::UpdateLexicalScope(uint32_t scope) {
508 dbg_scope_.SetLexicalScope(scope);
509 for (auto& i : dbg_line_insts_) {
510 i.dbg_scope_.SetLexicalScope(scope);
511 }
512 if (!IsLineInst() &&
513 context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
514 context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
515 }
516 }
517
UpdateDebugInlinedAt(uint32_t new_inlined_at)518 void Instruction::UpdateDebugInlinedAt(uint32_t new_inlined_at) {
519 dbg_scope_.SetInlinedAt(new_inlined_at);
520 for (auto& i : dbg_line_insts_) {
521 i.dbg_scope_.SetInlinedAt(new_inlined_at);
522 }
523 if (!IsLineInst() &&
524 context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
525 context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
526 }
527 }
528
ClearDbgLineInsts()529 void Instruction::ClearDbgLineInsts() {
530 if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) {
531 auto def_use_mgr = context()->get_def_use_mgr();
532 for (auto& l_inst : dbg_line_insts_) def_use_mgr->ClearInst(&l_inst);
533 }
534 clear_dbg_line_insts();
535 }
536
UpdateDebugInfoFrom(const Instruction * from)537 void Instruction::UpdateDebugInfoFrom(const Instruction* from) {
538 if (from == nullptr) return;
539 ClearDbgLineInsts();
540 if (!from->dbg_line_insts().empty())
541 AddDebugLine(&from->dbg_line_insts().back());
542 SetDebugScope(from->GetDebugScope());
543 if (!IsLineInst() &&
544 context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
545 context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
546 }
547 }
548
AddDebugLine(const Instruction * inst)549 void Instruction::AddDebugLine(const Instruction* inst) {
550 dbg_line_insts_.push_back(*inst);
551 dbg_line_insts_.back().unique_id_ = context()->TakeNextUniqueId();
552 if (inst->IsDebugLineInst())
553 dbg_line_insts_.back().SetResultId(context_->TakeNextId());
554 if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse))
555 context()->get_def_use_mgr()->AnalyzeInstDefUse(&dbg_line_insts_.back());
556 }
557
IsDebugLineInst() const558 bool Instruction::IsDebugLineInst() const {
559 NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode();
560 return ((ext_opt == NonSemanticShaderDebugInfo100DebugLine) ||
561 (ext_opt == NonSemanticShaderDebugInfo100DebugNoLine));
562 }
563
IsLineInst() const564 bool Instruction::IsLineInst() const { return IsLine() || IsNoLine(); }
565
IsLine() const566 bool Instruction::IsLine() const {
567 if (opcode() == SpvOpLine) return true;
568 NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode();
569 return ext_opt == NonSemanticShaderDebugInfo100DebugLine;
570 }
571
IsNoLine() const572 bool Instruction::IsNoLine() const {
573 if (opcode() == SpvOpNoLine) return true;
574 NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode();
575 return ext_opt == NonSemanticShaderDebugInfo100DebugNoLine;
576 }
577
InsertBefore(std::unique_ptr<Instruction> && inst)578 Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& inst) {
579 inst.get()->InsertBefore(this);
580 return inst.release();
581 }
582
InsertBefore(std::vector<std::unique_ptr<Instruction>> && list)583 Instruction* Instruction::InsertBefore(
584 std::vector<std::unique_ptr<Instruction>>&& list) {
585 Instruction* first_node = list.front().get();
586 for (auto& inst : list) {
587 inst.release()->InsertBefore(this);
588 }
589 list.clear();
590 return first_node;
591 }
592
IsValidBasePointer() const593 bool Instruction::IsValidBasePointer() const {
594 uint32_t tid = type_id();
595 if (tid == 0) {
596 return false;
597 }
598
599 Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
600 if (type->opcode() != SpvOpTypePointer) {
601 return false;
602 }
603
604 auto feature_mgr = context()->get_feature_mgr();
605 if (feature_mgr->HasCapability(SpvCapabilityAddresses)) {
606 // TODO: The rules here could be more restrictive.
607 return true;
608 }
609
610 if (opcode() == SpvOpVariable || opcode() == SpvOpFunctionParameter) {
611 return true;
612 }
613
614 // With variable pointers, there are more valid base pointer objects.
615 // Variable pointers implicitly declares Variable pointers storage buffer.
616 SpvStorageClass storage_class =
617 static_cast<SpvStorageClass>(type->GetSingleWordInOperand(0));
618 if ((feature_mgr->HasCapability(SpvCapabilityVariablePointersStorageBuffer) &&
619 storage_class == SpvStorageClassStorageBuffer) ||
620 (feature_mgr->HasCapability(SpvCapabilityVariablePointers) &&
621 storage_class == SpvStorageClassWorkgroup)) {
622 switch (opcode()) {
623 case SpvOpPhi:
624 case SpvOpSelect:
625 case SpvOpFunctionCall:
626 case SpvOpConstantNull:
627 return true;
628 default:
629 break;
630 }
631 }
632
633 uint32_t pointee_type_id = type->GetSingleWordInOperand(1);
634 Instruction* pointee_type_inst =
635 context()->get_def_use_mgr()->GetDef(pointee_type_id);
636
637 if (pointee_type_inst->IsOpaqueType()) {
638 return true;
639 }
640 return false;
641 }
642
GetOpenCL100DebugOpcode() const643 OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const {
644 if (opcode() != SpvOpExtInst) {
645 return OpenCLDebugInfo100InstructionsMax;
646 }
647
648 if (!context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) {
649 return OpenCLDebugInfo100InstructionsMax;
650 }
651
652 if (GetSingleWordInOperand(kExtInstSetIdInIdx) !=
653 context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) {
654 return OpenCLDebugInfo100InstructionsMax;
655 }
656
657 return OpenCLDebugInfo100Instructions(
658 GetSingleWordInOperand(kExtInstInstructionInIdx));
659 }
660
GetShader100DebugOpcode() const661 NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode()
662 const {
663 if (opcode() != SpvOpExtInst) {
664 return NonSemanticShaderDebugInfo100InstructionsMax;
665 }
666
667 if (!context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) {
668 return NonSemanticShaderDebugInfo100InstructionsMax;
669 }
670
671 if (GetSingleWordInOperand(kExtInstSetIdInIdx) !=
672 context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) {
673 return NonSemanticShaderDebugInfo100InstructionsMax;
674 }
675
676 uint32_t opcode = GetSingleWordInOperand(kExtInstInstructionInIdx);
677 if (opcode >= NonSemanticShaderDebugInfo100InstructionsMax) {
678 return NonSemanticShaderDebugInfo100InstructionsMax;
679 }
680
681 return NonSemanticShaderDebugInfo100Instructions(opcode);
682 }
683
GetCommonDebugOpcode() const684 CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const {
685 if (opcode() != SpvOpExtInst) {
686 return CommonDebugInfoInstructionsMax;
687 }
688
689 const uint32_t opencl_set_id =
690 context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo();
691 const uint32_t shader_set_id =
692 context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo();
693
694 if (!opencl_set_id && !shader_set_id) {
695 return CommonDebugInfoInstructionsMax;
696 }
697
698 const uint32_t used_set_id = GetSingleWordInOperand(kExtInstSetIdInIdx);
699
700 if (used_set_id != opencl_set_id && used_set_id != shader_set_id) {
701 return CommonDebugInfoInstructionsMax;
702 }
703
704 return CommonDebugInfoInstructions(
705 GetSingleWordInOperand(kExtInstInstructionInIdx));
706 }
707
IsValidBaseImage() const708 bool Instruction::IsValidBaseImage() const {
709 uint32_t tid = type_id();
710 if (tid == 0) {
711 return false;
712 }
713
714 Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
715 return (type->opcode() == SpvOpTypeImage ||
716 type->opcode() == SpvOpTypeSampledImage);
717 }
718
IsOpaqueType() const719 bool Instruction::IsOpaqueType() const {
720 if (opcode() == SpvOpTypeStruct) {
721 bool is_opaque = false;
722 ForEachInOperand([&is_opaque, this](const uint32_t* op_id) {
723 Instruction* type_inst = context()->get_def_use_mgr()->GetDef(*op_id);
724 is_opaque |= type_inst->IsOpaqueType();
725 });
726 return is_opaque;
727 } else if (opcode() == SpvOpTypeArray) {
728 uint32_t sub_type_id = GetSingleWordInOperand(0);
729 Instruction* sub_type_inst =
730 context()->get_def_use_mgr()->GetDef(sub_type_id);
731 return sub_type_inst->IsOpaqueType();
732 } else {
733 return opcode() == SpvOpTypeRuntimeArray ||
734 spvOpcodeIsBaseOpaqueType(opcode());
735 }
736 }
737
IsFoldable() const738 bool Instruction::IsFoldable() const {
739 return IsFoldableByFoldScalar() ||
740 context()->get_instruction_folder().HasConstFoldingRule(this);
741 }
742
IsFoldableByFoldScalar() const743 bool Instruction::IsFoldableByFoldScalar() const {
744 const InstructionFolder& folder = context()->get_instruction_folder();
745 if (!folder.IsFoldableOpcode(opcode())) {
746 return false;
747 }
748
749 Instruction* type = context()->get_def_use_mgr()->GetDef(type_id());
750 if (!folder.IsFoldableType(type)) {
751 return false;
752 }
753
754 // Even if the type of the instruction is foldable, its operands may not be
755 // foldable (e.g., comparisons of 64bit types). Check that all operand types
756 // are foldable before accepting the instruction.
757 return WhileEachInOperand([&folder, this](const uint32_t* op_id) {
758 Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id);
759 Instruction* def_inst_type =
760 context()->get_def_use_mgr()->GetDef(def_inst->type_id());
761 return folder.IsFoldableType(def_inst_type);
762 });
763 }
764
IsFloatingPointFoldingAllowed() const765 bool Instruction::IsFloatingPointFoldingAllowed() const {
766 // TODO: Add the rules for kernels. For now it will be pessimistic.
767 // For now, do not support capabilities introduced by SPV_KHR_float_controls.
768 if (!context_->get_feature_mgr()->HasCapability(SpvCapabilityShader) ||
769 context_->get_feature_mgr()->HasCapability(SpvCapabilityDenormPreserve) ||
770 context_->get_feature_mgr()->HasCapability(
771 SpvCapabilityDenormFlushToZero) ||
772 context_->get_feature_mgr()->HasCapability(
773 SpvCapabilitySignedZeroInfNanPreserve) ||
774 context_->get_feature_mgr()->HasCapability(
775 SpvCapabilityRoundingModeRTZ) ||
776 context_->get_feature_mgr()->HasCapability(
777 SpvCapabilityRoundingModeRTE)) {
778 return false;
779 }
780
781 bool is_nocontract = false;
782 context_->get_decoration_mgr()->WhileEachDecoration(
783 result_id(), SpvDecorationNoContraction,
784 [&is_nocontract](const Instruction&) {
785 is_nocontract = true;
786 return false;
787 });
788 return !is_nocontract;
789 }
790
PrettyPrint(uint32_t options) const791 std::string Instruction::PrettyPrint(uint32_t options) const {
792 // Convert the module to binary.
793 std::vector<uint32_t> module_binary;
794 context()->module()->ToBinary(&module_binary, /* skip_nop = */ false);
795
796 // Convert the instruction to binary. This is used to identify the correct
797 // stream of words to output from the module.
798 std::vector<uint32_t> inst_binary;
799 ToBinaryWithoutAttachedDebugInsts(&inst_binary);
800
801 // Do not generate a header.
802 return spvInstructionBinaryToText(
803 context()->grammar().target_env(), inst_binary.data(), inst_binary.size(),
804 module_binary.data(), module_binary.size(),
805 options | SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
806 }
807
operator <<(std::ostream & str,const Instruction & inst)808 std::ostream& operator<<(std::ostream& str, const Instruction& inst) {
809 str << inst.PrettyPrint();
810 return str;
811 }
812
Dump() const813 void Instruction::Dump() const {
814 std::cerr << "Instruction #" << unique_id() << "\n" << *this << "\n";
815 }
816
IsOpcodeCodeMotionSafe() const817 bool Instruction::IsOpcodeCodeMotionSafe() const {
818 switch (opcode_) {
819 case SpvOpNop:
820 case SpvOpUndef:
821 case SpvOpLoad:
822 case SpvOpAccessChain:
823 case SpvOpInBoundsAccessChain:
824 case SpvOpArrayLength:
825 case SpvOpVectorExtractDynamic:
826 case SpvOpVectorInsertDynamic:
827 case SpvOpVectorShuffle:
828 case SpvOpCompositeConstruct:
829 case SpvOpCompositeExtract:
830 case SpvOpCompositeInsert:
831 case SpvOpCopyObject:
832 case SpvOpTranspose:
833 case SpvOpConvertFToU:
834 case SpvOpConvertFToS:
835 case SpvOpConvertSToF:
836 case SpvOpConvertUToF:
837 case SpvOpUConvert:
838 case SpvOpSConvert:
839 case SpvOpFConvert:
840 case SpvOpQuantizeToF16:
841 case SpvOpBitcast:
842 case SpvOpSNegate:
843 case SpvOpFNegate:
844 case SpvOpIAdd:
845 case SpvOpFAdd:
846 case SpvOpISub:
847 case SpvOpFSub:
848 case SpvOpIMul:
849 case SpvOpFMul:
850 case SpvOpUDiv:
851 case SpvOpSDiv:
852 case SpvOpFDiv:
853 case SpvOpUMod:
854 case SpvOpSRem:
855 case SpvOpSMod:
856 case SpvOpFRem:
857 case SpvOpFMod:
858 case SpvOpVectorTimesScalar:
859 case SpvOpMatrixTimesScalar:
860 case SpvOpVectorTimesMatrix:
861 case SpvOpMatrixTimesVector:
862 case SpvOpMatrixTimesMatrix:
863 case SpvOpOuterProduct:
864 case SpvOpDot:
865 case SpvOpIAddCarry:
866 case SpvOpISubBorrow:
867 case SpvOpUMulExtended:
868 case SpvOpSMulExtended:
869 case SpvOpAny:
870 case SpvOpAll:
871 case SpvOpIsNan:
872 case SpvOpIsInf:
873 case SpvOpLogicalEqual:
874 case SpvOpLogicalNotEqual:
875 case SpvOpLogicalOr:
876 case SpvOpLogicalAnd:
877 case SpvOpLogicalNot:
878 case SpvOpSelect:
879 case SpvOpIEqual:
880 case SpvOpINotEqual:
881 case SpvOpUGreaterThan:
882 case SpvOpSGreaterThan:
883 case SpvOpUGreaterThanEqual:
884 case SpvOpSGreaterThanEqual:
885 case SpvOpULessThan:
886 case SpvOpSLessThan:
887 case SpvOpULessThanEqual:
888 case SpvOpSLessThanEqual:
889 case SpvOpFOrdEqual:
890 case SpvOpFUnordEqual:
891 case SpvOpFOrdNotEqual:
892 case SpvOpFUnordNotEqual:
893 case SpvOpFOrdLessThan:
894 case SpvOpFUnordLessThan:
895 case SpvOpFOrdGreaterThan:
896 case SpvOpFUnordGreaterThan:
897 case SpvOpFOrdLessThanEqual:
898 case SpvOpFUnordLessThanEqual:
899 case SpvOpFOrdGreaterThanEqual:
900 case SpvOpFUnordGreaterThanEqual:
901 case SpvOpShiftRightLogical:
902 case SpvOpShiftRightArithmetic:
903 case SpvOpShiftLeftLogical:
904 case SpvOpBitwiseOr:
905 case SpvOpBitwiseXor:
906 case SpvOpBitwiseAnd:
907 case SpvOpNot:
908 case SpvOpBitFieldInsert:
909 case SpvOpBitFieldSExtract:
910 case SpvOpBitFieldUExtract:
911 case SpvOpBitReverse:
912 case SpvOpBitCount:
913 case SpvOpSizeOf:
914 return true;
915 default:
916 return false;
917 }
918 }
919
IsScalarizable() const920 bool Instruction::IsScalarizable() const {
921 if (spvOpcodeIsScalarizable(opcode())) {
922 return true;
923 }
924
925 if (opcode() == SpvOpExtInst) {
926 uint32_t instSetId =
927 context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
928
929 if (GetSingleWordInOperand(kExtInstSetIdInIdx) == instSetId) {
930 switch (GetSingleWordInOperand(kExtInstInstructionInIdx)) {
931 case GLSLstd450Round:
932 case GLSLstd450RoundEven:
933 case GLSLstd450Trunc:
934 case GLSLstd450FAbs:
935 case GLSLstd450SAbs:
936 case GLSLstd450FSign:
937 case GLSLstd450SSign:
938 case GLSLstd450Floor:
939 case GLSLstd450Ceil:
940 case GLSLstd450Fract:
941 case GLSLstd450Radians:
942 case GLSLstd450Degrees:
943 case GLSLstd450Sin:
944 case GLSLstd450Cos:
945 case GLSLstd450Tan:
946 case GLSLstd450Asin:
947 case GLSLstd450Acos:
948 case GLSLstd450Atan:
949 case GLSLstd450Sinh:
950 case GLSLstd450Cosh:
951 case GLSLstd450Tanh:
952 case GLSLstd450Asinh:
953 case GLSLstd450Acosh:
954 case GLSLstd450Atanh:
955 case GLSLstd450Atan2:
956 case GLSLstd450Pow:
957 case GLSLstd450Exp:
958 case GLSLstd450Log:
959 case GLSLstd450Exp2:
960 case GLSLstd450Log2:
961 case GLSLstd450Sqrt:
962 case GLSLstd450InverseSqrt:
963 case GLSLstd450Modf:
964 case GLSLstd450FMin:
965 case GLSLstd450UMin:
966 case GLSLstd450SMin:
967 case GLSLstd450FMax:
968 case GLSLstd450UMax:
969 case GLSLstd450SMax:
970 case GLSLstd450FClamp:
971 case GLSLstd450UClamp:
972 case GLSLstd450SClamp:
973 case GLSLstd450FMix:
974 case GLSLstd450Step:
975 case GLSLstd450SmoothStep:
976 case GLSLstd450Fma:
977 case GLSLstd450Frexp:
978 case GLSLstd450Ldexp:
979 case GLSLstd450FindILsb:
980 case GLSLstd450FindSMsb:
981 case GLSLstd450FindUMsb:
982 case GLSLstd450NMin:
983 case GLSLstd450NMax:
984 case GLSLstd450NClamp:
985 return true;
986 default:
987 return false;
988 }
989 }
990 }
991 return false;
992 }
993
IsOpcodeSafeToDelete() const994 bool Instruction::IsOpcodeSafeToDelete() const {
995 if (context()->IsCombinatorInstruction(this)) {
996 return true;
997 }
998
999 switch (opcode()) {
1000 case SpvOpDPdx:
1001 case SpvOpDPdy:
1002 case SpvOpFwidth:
1003 case SpvOpDPdxFine:
1004 case SpvOpDPdyFine:
1005 case SpvOpFwidthFine:
1006 case SpvOpDPdxCoarse:
1007 case SpvOpDPdyCoarse:
1008 case SpvOpFwidthCoarse:
1009 case SpvOpImageQueryLod:
1010 return true;
1011 default:
1012 return false;
1013 }
1014 }
1015
IsNonSemanticInstruction() const1016 bool Instruction::IsNonSemanticInstruction() const {
1017 if (!HasResultId()) return false;
1018 if (opcode() != SpvOpExtInst) return false;
1019
1020 auto import_inst =
1021 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(0));
1022 std::string import_name = import_inst->GetInOperand(0).AsString();
1023 return import_name.find("NonSemantic.") == 0;
1024 }
1025
ToBinary(uint32_t type_id,uint32_t result_id,uint32_t ext_set,std::vector<uint32_t> * binary) const1026 void DebugScope::ToBinary(uint32_t type_id, uint32_t result_id,
1027 uint32_t ext_set,
1028 std::vector<uint32_t>* binary) const {
1029 uint32_t num_words = kDebugScopeNumWords;
1030 CommonDebugInfoInstructions dbg_opcode = CommonDebugInfoDebugScope;
1031 if (GetLexicalScope() == kNoDebugScope) {
1032 num_words = kDebugNoScopeNumWords;
1033 dbg_opcode = CommonDebugInfoDebugNoScope;
1034 } else if (GetInlinedAt() == kNoInlinedAt) {
1035 num_words = kDebugScopeNumWordsWithoutInlinedAt;
1036 }
1037 std::vector<uint32_t> operands = {
1038 (num_words << 16) | static_cast<uint16_t>(SpvOpExtInst),
1039 type_id,
1040 result_id,
1041 ext_set,
1042 static_cast<uint32_t>(dbg_opcode),
1043 };
1044 binary->insert(binary->end(), operands.begin(), operands.end());
1045 if (GetLexicalScope() != kNoDebugScope) {
1046 binary->push_back(GetLexicalScope());
1047 if (GetInlinedAt() != kNoInlinedAt) binary->push_back(GetInlinedAt());
1048 }
1049 }
1050
1051 } // namespace opt
1052 } // namespace spvtools
1053