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
25namespace spvtools {
26namespace opt {
27
28namespace {
29// Indices used to get particular operands out of instructions using InOperand.
30const uint32_t kTypeImageDimIndex = 1;
31const uint32_t kLoadBaseIndex = 0;
32const uint32_t kVariableStorageClassIndex = 0;
33const uint32_t kTypeImageSampledIndex = 5;
34
35// Constants for OpenCL.DebugInfo.100 extension instructions.
36const uint32_t kDebugScopeNumWords = 7;
37const uint32_t kDebugScopeNumWordsWithoutInlinedAt = 6;
38const uint32_t kDebugNoScopeNumWords = 5;
39} // namespace
40
41Instruction::Instruction(IRContext* c)
42 : utils::IntrusiveNodeBase<Instruction>(),
43 context_(c),
44 opcode_(SpvOpNop),
45 has_type_id_(false),
46 has_result_id_(false),
47 unique_id_(c->TakeNextUniqueId()),
48 dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
49
50Instruction::Instruction(IRContext* c, SpvOp op)
51 : utils::IntrusiveNodeBase<Instruction>(),
52 context_(c),
53 opcode_(op),
54 has_type_id_(false),
55 has_result_id_(false),
56 unique_id_(c->TakeNextUniqueId()),
57 dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
58
59Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
60 std::vector<Instruction>&& dbg_line)
61 : context_(c),
62 opcode_(static_cast<SpvOp>(inst.opcode)),
63 has_type_id_(inst.type_id != 0),
64 has_result_id_(inst.result_id != 0),
65 unique_id_(c->TakeNextUniqueId()),
66 dbg_line_insts_(std::move(dbg_line)),
67 dbg_scope_(kNoDebugScope, kNoInlinedAt) {
68 assert((!IsDebugLineInst(opcode_) || dbg_line.empty()) &&
69 "Op(No)Line attaching to Op(No)Line found");
70 for (uint32_t i = 0; i < inst.num_operands; ++i) {
71 const auto& current_payload = inst.operands[i];
72 std::vector<uint32_t> words(
73 inst.words + current_payload.offset,
74 inst.words + current_payload.offset + current_payload.num_words);
75 operands_.emplace_back(current_payload.type, std::move(words));
76 }
77}
78
79Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
80 const DebugScope& dbg_scope)
81 : context_(c),
82 opcode_(static_cast<SpvOp>(inst.opcode)),
83 has_type_id_(inst.type_id != 0),
84 has_result_id_(inst.result_id != 0),
85 unique_id_(c->TakeNextUniqueId()),
86 dbg_scope_(dbg_scope) {
87 for (uint32_t i = 0; i < inst.num_operands; ++i) {
88 const auto& current_payload = inst.operands[i];
89 std::vector<uint32_t> words(
90 inst.words + current_payload.offset,
91 inst.words + current_payload.offset + current_payload.num_words);
92 operands_.emplace_back(current_payload.type, std::move(words));
93 }
94}
95
96Instruction::Instruction(IRContext* c, SpvOp op, uint32_t ty_id,
97 uint32_t res_id, const OperandList& in_operands)
98 : utils::IntrusiveNodeBase<Instruction>(),
99 context_(c),
100 opcode_(op),
101 has_type_id_(ty_id != 0),
102 has_result_id_(res_id != 0),
103 unique_id_(c->TakeNextUniqueId()),
104 operands_(),
105 dbg_scope_(kNoDebugScope, kNoInlinedAt) {
106 if (has_type_id_) {
107 operands_.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_TYPE_ID,
108 std::initializer_list<uint32_t>{ty_id});
109 }
110 if (has_result_id_) {
111 operands_.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_RESULT_ID,
112 std::initializer_list<uint32_t>{res_id});
113 }
114 operands_.insert(operands_.end(), in_operands.begin(), in_operands.end());
115}
116
117Instruction::Instruction(Instruction&& that)
118 : utils::IntrusiveNodeBase<Instruction>(),
119 opcode_(that.opcode_),
120 has_type_id_(that.has_type_id_),
121 has_result_id_(that.has_result_id_),
122 unique_id_(that.unique_id_),
123 operands_(std::move(that.operands_)),
124 dbg_line_insts_(std::move(that.dbg_line_insts_)),
125 dbg_scope_(that.dbg_scope_) {
126 for (auto& i : dbg_line_insts_) {
127 i.dbg_scope_ = that.dbg_scope_;
128 }
129}
130
131Instruction& Instruction::operator=(Instruction&& that) {
132 opcode_ = that.opcode_;
133 has_type_id_ = that.has_type_id_;
134 has_result_id_ = that.has_result_id_;
135 unique_id_ = that.unique_id_;
136 operands_ = std::move(that.operands_);
137 dbg_line_insts_ = std::move(that.dbg_line_insts_);
138 dbg_scope_ = that.dbg_scope_;
139 return *this;
140}
141
142Instruction* Instruction::Clone(IRContext* c) const {
143 Instruction* clone = new Instruction(c);
144 clone->opcode_ = opcode_;
145 clone->has_type_id_ = has_type_id_;
146 clone->has_result_id_ = has_result_id_;
147 clone->unique_id_ = c->TakeNextUniqueId();
148 clone->operands_ = operands_;
149 clone->dbg_line_insts_ = dbg_line_insts_;
150 clone->dbg_scope_ = dbg_scope_;
151 return clone;
152}
153
154uint32_t Instruction::GetSingleWordOperand(uint32_t index) const {
155 const auto& words = GetOperand(index).words;
156 assert(words.size() == 1 && "expected the operand only taking one word");
157 return words.front();
158}
159
160uint32_t Instruction::NumInOperandWords() const {
161 uint32_t size = 0;
162 for (uint32_t i = TypeResultIdCount(); i < operands_.size(); ++i)
163 size += static_cast<uint32_t>(operands_[i].words.size());
164 return size;
165}
166
167void Instruction::ToBinaryWithoutAttachedDebugInsts(
168 std::vector<uint32_t>* binary) const {
169 const uint32_t num_words = 1 + NumOperandWords();
170 binary->push_back((num_words << 16) | static_cast<uint16_t>(opcode_));
171 for (const auto& operand : operands_)
172 binary->insert(binary->end(), operand.words.begin(), operand.words.end());
173}
174
175void Instruction::ReplaceOperands(const OperandList& new_operands) {
176 operands_.clear();
177 operands_.insert(operands_.begin(), new_operands.begin(), new_operands.end());
178}
179
180bool Instruction::IsReadOnlyLoad() const {
181 if (IsLoad()) {
182 Instruction* address_def = GetBaseAddress();
183 if (!address_def || address_def->opcode() != SpvOpVariable) {
184 return false;
185 }
186 return address_def->IsReadOnlyVariable();
187 }
188 return false;
189}
190
191Instruction* Instruction::GetBaseAddress() const {
192 uint32_t base = GetSingleWordInOperand(kLoadBaseIndex);
193 Instruction* base_inst = context()->get_def_use_mgr()->GetDef(base);
194 bool done = false;
195 while (!done) {
196 switch (base_inst->opcode()) {
197 case SpvOpAccessChain:
198 case SpvOpInBoundsAccessChain:
199 case SpvOpPtrAccessChain:
200 case SpvOpInBoundsPtrAccessChain:
201 case SpvOpImageTexelPointer:
202 case SpvOpCopyObject:
203 // All of these instructions have the base pointer use a base pointer
204 // in in-operand 0.
205 base = base_inst->GetSingleWordInOperand(0);
206 base_inst = context()->get_def_use_mgr()->GetDef(base);
207 break;
208 default:
209 done = true;
210 break;
211 }
212 }
213 return base_inst;
214}
215
216bool Instruction::IsReadOnlyVariable() const {
217 if (context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
218 return IsReadOnlyVariableShaders();
219 else
220 return IsReadOnlyVariableKernel();
221}
222
223bool Instruction::IsVulkanStorageImage() const {
224 if (opcode() != SpvOpTypePointer) {
225 return false;
226 }
227
228 uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
229 if (storage_class != SpvStorageClassUniformConstant) {
230 return false;
231 }
232
233 Instruction* base_type =
234 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
235
236 // Unpack the optional layer of arraying.
237 if (base_type->opcode() == SpvOpTypeArray ||
238 base_type->opcode() == SpvOpTypeRuntimeArray) {
239 base_type = context()->get_def_use_mgr()->GetDef(
240 base_type->GetSingleWordInOperand(0));
241 }
242
243 if (base_type->opcode() != SpvOpTypeImage) {
244 return false;
245 }
246
247 if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) == SpvDimBuffer) {
248 return false;
249 }
250
251 // Check if the image is sampled. If we do not know for sure that it is,
252 // then assume it is a storage image.
253 auto s = base_type->GetSingleWordInOperand(kTypeImageSampledIndex);
254 return s != 1;
255}
256
257bool Instruction::IsVulkanSampledImage() const {
258 if (opcode() != SpvOpTypePointer) {
259 return false;
260 }
261
262 uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
263 if (storage_class != SpvStorageClassUniformConstant) {
264 return false;
265 }
266
267 Instruction* base_type =
268 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
269
270 // Unpack the optional layer of arraying.
271 if (base_type->opcode() == SpvOpTypeArray ||
272 base_type->opcode() == SpvOpTypeRuntimeArray) {
273 base_type = context()->get_def_use_mgr()->GetDef(
274 base_type->GetSingleWordInOperand(0));
275 }
276
277 if (base_type->opcode() != SpvOpTypeImage) {
278 return false;
279 }
280
281 if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) == SpvDimBuffer) {
282 return false;
283 }
284
285 // Check if the image is sampled. If we know for sure that it is,
286 // then return true.
287 auto s = base_type->GetSingleWordInOperand(kTypeImageSampledIndex);
288 return s == 1;
289}
290
291bool Instruction::IsVulkanStorageTexelBuffer() const {
292 if (opcode() != SpvOpTypePointer) {
293 return false;
294 }
295
296 uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
297 if (storage_class != SpvStorageClassUniformConstant) {
298 return false;
299 }
300
301 Instruction* base_type =
302 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
303
304 // Unpack the optional layer of arraying.
305 if (base_type->opcode() == SpvOpTypeArray ||
306 base_type->opcode() == SpvOpTypeRuntimeArray) {
307 base_type = context()->get_def_use_mgr()->GetDef(
308 base_type->GetSingleWordInOperand(0));
309 }
310
311 if (base_type->opcode() != SpvOpTypeImage) {
312 return false;
313 }
314
315 if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) != SpvDimBuffer) {
316 return false;
317 }
318
319 // Check if the image is sampled. If we do not know for sure that it is,
320 // then assume it is a storage texel buffer.
321 return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) != 1;
322}
323
324bool Instruction::IsVulkanStorageBuffer() const {
325 // Is there a difference between a "Storage buffer" and a "dynamic storage
326 // buffer" in SPIR-V and do we care about the difference?
327 if (opcode() != SpvOpTypePointer) {
328 return false;
329 }
330
331 Instruction* base_type =
332 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
333
334 // Unpack the optional layer of arraying.
335 if (base_type->opcode() == SpvOpTypeArray ||
336 base_type->opcode() == SpvOpTypeRuntimeArray) {
337 base_type = context()->get_def_use_mgr()->GetDef(
338 base_type->GetSingleWordInOperand(0));
339 }
340
341 if (base_type->opcode() != SpvOpTypeStruct) {
342 return false;
343 }
344
345 uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
346 if (storage_class == SpvStorageClassUniform) {
347 bool is_buffer_block = false;
348 context()->get_decoration_mgr()->ForEachDecoration(
349 base_type->result_id(), SpvDecorationBufferBlock,
350 [&is_buffer_block](const Instruction&) { is_buffer_block = true; });
351 return is_buffer_block;
352 } else if (storage_class == SpvStorageClassStorageBuffer) {
353 bool is_block = false;
354 context()->get_decoration_mgr()->ForEachDecoration(
355 base_type->result_id(), SpvDecorationBlock,
356 [&is_block](const Instruction&) { is_block = true; });
357 return is_block;
358 }
359 return false;
360}
361
362bool Instruction::IsVulkanUniformBuffer() const {
363 if (opcode() != SpvOpTypePointer) {
364 return false;
365 }
366
367 uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
368 if (storage_class != SpvStorageClassUniform) {
369 return false;
370 }
371
372 Instruction* base_type =
373 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
374
375 // Unpack the optional layer of arraying.
376 if (base_type->opcode() == SpvOpTypeArray ||
377 base_type->opcode() == SpvOpTypeRuntimeArray) {
378 base_type = context()->get_def_use_mgr()->GetDef(
379 base_type->GetSingleWordInOperand(0));
380 }
381
382 if (base_type->opcode() != SpvOpTypeStruct) {
383 return false;
384 }
385
386 bool is_block = false;
387 context()->get_decoration_mgr()->ForEachDecoration(
388 base_type->result_id(), SpvDecorationBlock,
389 [&is_block](const Instruction&) { is_block = true; });
390 return is_block;
391}
392
393bool Instruction::IsReadOnlyVariableShaders() const {
394 uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
395 Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id());
396
397 switch (storage_class) {
398 case SpvStorageClassUniformConstant:
399 if (!type_def->IsVulkanStorageImage() &&
400 !type_def->IsVulkanStorageTexelBuffer()) {
401 return true;
402 }
403 break;
404 case SpvStorageClassUniform:
405 if (!type_def->IsVulkanStorageBuffer()) {
406 return true;
407 }
408 break;
409 case SpvStorageClassPushConstant:
410 case SpvStorageClassInput:
411 return true;
412 default:
413 break;
414 }
415
416 bool is_nonwritable = false;
417 context()->get_decoration_mgr()->ForEachDecoration(
418 result_id(), SpvDecorationNonWritable,
419 [&is_nonwritable](const Instruction&) { is_nonwritable = true; });
420 return is_nonwritable;
421}
422
423bool Instruction::IsReadOnlyVariableKernel() const {
424 uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
425 return storage_class == SpvStorageClassUniformConstant;
426}
427
428uint32_t Instruction::GetTypeComponent(uint32_t element) const {
429 uint32_t subtype = 0;
430 switch (opcode()) {
431 case SpvOpTypeStruct:
432 subtype = GetSingleWordInOperand(element);
433 break;
434 case SpvOpTypeArray:
435 case SpvOpTypeRuntimeArray:
436 case SpvOpTypeVector:
437 case SpvOpTypeMatrix:
438 // These types all have uniform subtypes.
439 subtype = GetSingleWordInOperand(0u);
440 break;
441 default:
442 break;
443 }
444
445 return subtype;
446}
447
448Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& i) {
449 i.get()->InsertBefore(this);
450 return i.release();
451}
452
453Instruction* Instruction::InsertBefore(
454 std::vector<std::unique_ptr<Instruction>>&& list) {
455 Instruction* first_node = list.front().get();
456 for (auto& i : list) {
457 i.release()->InsertBefore(this);
458 }
459 list.clear();
460 return first_node;
461}
462
463bool Instruction::IsValidBasePointer() const {
464 uint32_t tid = type_id();
465 if (tid == 0) {
466 return false;
467 }
468
469 Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
470 if (type->opcode() != SpvOpTypePointer) {
471 return false;
472 }
473
474 auto feature_mgr = context()->get_feature_mgr();
475 if (feature_mgr->HasCapability(SpvCapabilityAddresses)) {
476 // TODO: The rules here could be more restrictive.
477 return true;
478 }
479
480 if (opcode() == SpvOpVariable || opcode() == SpvOpFunctionParameter) {
481 return true;
482 }
483
484 // With variable pointers, there are more valid base pointer objects.
485 // Variable pointers implicitly declares Variable pointers storage buffer.
486 SpvStorageClass storage_class =
487 static_cast<SpvStorageClass>(type->GetSingleWordInOperand(0));
488 if ((feature_mgr->HasCapability(SpvCapabilityVariablePointersStorageBuffer) &&
489 storage_class == SpvStorageClassStorageBuffer) ||
490 (feature_mgr->HasCapability(SpvCapabilityVariablePointers) &&
491 storage_class == SpvStorageClassWorkgroup)) {
492 switch (opcode()) {
493 case SpvOpPhi:
494 case SpvOpSelect:
495 case SpvOpFunctionCall:
496 case SpvOpConstantNull:
497 return true;
498 default:
499 break;
500 }
501 }
502
503 uint32_t pointee_type_id = type->GetSingleWordInOperand(1);
504 Instruction* pointee_type_inst =
505 context()->get_def_use_mgr()->GetDef(pointee_type_id);
506
507 if (pointee_type_inst->IsOpaqueType()) {
508 return true;
509 }
510 return false;
511}
512
513bool Instruction::IsValidBaseImage() const {
514 uint32_t tid = type_id();
515 if (tid == 0) {
516 return false;
517 }
518
519 Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
520 return (type->opcode() == SpvOpTypeImage ||
521 type->opcode() == SpvOpTypeSampledImage);
522}
523
524bool Instruction::IsOpaqueType() const {
525 if (opcode() == SpvOpTypeStruct) {
526 bool is_opaque = false;
527 ForEachInOperand([&is_opaque, this](const uint32_t* op_id) {
528 Instruction* type_inst = context()->get_def_use_mgr()->GetDef(*op_id);
529 is_opaque |= type_inst->IsOpaqueType();
530 });
531 return is_opaque;
532 } else if (opcode() == SpvOpTypeArray) {
533 uint32_t sub_type_id = GetSingleWordInOperand(0);
534 Instruction* sub_type_inst =
535 context()->get_def_use_mgr()->GetDef(sub_type_id);
536 return sub_type_inst->IsOpaqueType();
537 } else {
538 return opcode() == SpvOpTypeRuntimeArray ||
539 spvOpcodeIsBaseOpaqueType(opcode());
540 }
541}
542
543bool Instruction::IsFoldable() const {
544 return IsFoldableByFoldScalar() ||
545 context()->get_instruction_folder().HasConstFoldingRule(this);
546}
547
548bool Instruction::IsFoldableByFoldScalar() const {
549 const InstructionFolder& folder = context()->get_instruction_folder();
550 if (!folder.IsFoldableOpcode(opcode())) {
551 return false;
552 }
553 Instruction* type = context()->get_def_use_mgr()->GetDef(type_id());
554 return folder.IsFoldableType(type);
555}
556
557bool Instruction::IsFloatingPointFoldingAllowed() const {
558 // TODO: Add the rules for kernels. For now it will be pessimistic.
559 // For now, do not support capabilities introduced by SPV_KHR_float_controls.
560 if (!context_->get_feature_mgr()->HasCapability(SpvCapabilityShader) ||
561 context_->get_feature_mgr()->HasCapability(SpvCapabilityDenormPreserve) ||
562 context_->get_feature_mgr()->HasCapability(
563 SpvCapabilityDenormFlushToZero) ||
564 context_->get_feature_mgr()->HasCapability(
565 SpvCapabilitySignedZeroInfNanPreserve) ||
566 context_->get_feature_mgr()->HasCapability(
567 SpvCapabilityRoundingModeRTZ) ||
568 context_->get_feature_mgr()->HasCapability(
569 SpvCapabilityRoundingModeRTE)) {
570 return false;
571 }
572
573 bool is_nocontract = false;
574 context_->get_decoration_mgr()->WhileEachDecoration(
575 result_id(), SpvDecorationNoContraction,
576 [&is_nocontract](const Instruction&) {
577 is_nocontract = true;
578 return false;
579 });
580 return !is_nocontract;
581}
582
583std::string Instruction::PrettyPrint(uint32_t options) const {
584 // Convert the module to binary.
585 std::vector<uint32_t> module_binary;
586 context()->module()->ToBinary(&module_binary, /* skip_nop = */ false);
587
588 // Convert the instruction to binary. This is used to identify the correct
589 // stream of words to output from the module.
590 std::vector<uint32_t> inst_binary;
591 ToBinaryWithoutAttachedDebugInsts(&inst_binary);
592
593 // Do not generate a header.
594 return spvInstructionBinaryToText(
595 context()->grammar().target_env(), inst_binary.data(), inst_binary.size(),
596 module_binary.data(), module_binary.size(),
597 options | SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
598}
599
600std::ostream& operator<<(std::ostream& str, const Instruction& inst) {
601 str << inst.PrettyPrint();
602 return str;
603}
604
605void Instruction::Dump() const {
606 std::cerr << "Instruction #" << unique_id() << "\n" << *this << "\n";
607}
608
609bool Instruction::IsOpcodeCodeMotionSafe() const {
610 switch (opcode_) {
611 case SpvOpNop:
612 case SpvOpUndef:
613 case SpvOpLoad:
614 case SpvOpAccessChain:
615 case SpvOpInBoundsAccessChain:
616 case SpvOpArrayLength:
617 case SpvOpVectorExtractDynamic:
618 case SpvOpVectorInsertDynamic:
619 case SpvOpVectorShuffle:
620 case SpvOpCompositeConstruct:
621 case SpvOpCompositeExtract:
622 case SpvOpCompositeInsert:
623 case SpvOpCopyObject:
624 case SpvOpTranspose:
625 case SpvOpConvertFToU:
626 case SpvOpConvertFToS:
627 case SpvOpConvertSToF:
628 case SpvOpConvertUToF:
629 case SpvOpUConvert:
630 case SpvOpSConvert:
631 case SpvOpFConvert:
632 case SpvOpQuantizeToF16:
633 case SpvOpBitcast:
634 case SpvOpSNegate:
635 case SpvOpFNegate:
636 case SpvOpIAdd:
637 case SpvOpFAdd:
638 case SpvOpISub:
639 case SpvOpFSub:
640 case SpvOpIMul:
641 case SpvOpFMul:
642 case SpvOpUDiv:
643 case SpvOpSDiv:
644 case SpvOpFDiv:
645 case SpvOpUMod:
646 case SpvOpSRem:
647 case SpvOpSMod:
648 case SpvOpFRem:
649 case SpvOpFMod:
650 case SpvOpVectorTimesScalar:
651 case SpvOpMatrixTimesScalar:
652 case SpvOpVectorTimesMatrix:
653 case SpvOpMatrixTimesVector:
654 case SpvOpMatrixTimesMatrix:
655 case SpvOpOuterProduct:
656 case SpvOpDot:
657 case SpvOpIAddCarry:
658 case SpvOpISubBorrow:
659 case SpvOpUMulExtended:
660 case SpvOpSMulExtended:
661 case SpvOpAny:
662 case SpvOpAll:
663 case SpvOpIsNan:
664 case SpvOpIsInf:
665 case SpvOpLogicalEqual:
666 case SpvOpLogicalNotEqual:
667 case SpvOpLogicalOr:
668 case SpvOpLogicalAnd:
669 case SpvOpLogicalNot:
670 case SpvOpSelect:
671 case SpvOpIEqual:
672 case SpvOpINotEqual:
673 case SpvOpUGreaterThan:
674 case SpvOpSGreaterThan:
675 case SpvOpUGreaterThanEqual:
676 case SpvOpSGreaterThanEqual:
677 case SpvOpULessThan:
678 case SpvOpSLessThan:
679 case SpvOpULessThanEqual:
680 case SpvOpSLessThanEqual:
681 case SpvOpFOrdEqual:
682 case SpvOpFUnordEqual:
683 case SpvOpFOrdNotEqual:
684 case SpvOpFUnordNotEqual:
685 case SpvOpFOrdLessThan:
686 case SpvOpFUnordLessThan:
687 case SpvOpFOrdGreaterThan:
688 case SpvOpFUnordGreaterThan:
689 case SpvOpFOrdLessThanEqual:
690 case SpvOpFUnordLessThanEqual:
691 case SpvOpFOrdGreaterThanEqual:
692 case SpvOpFUnordGreaterThanEqual:
693 case SpvOpShiftRightLogical:
694 case SpvOpShiftRightArithmetic:
695 case SpvOpShiftLeftLogical:
696 case SpvOpBitwiseOr:
697 case SpvOpBitwiseXor:
698 case SpvOpBitwiseAnd:
699 case SpvOpNot:
700 case SpvOpBitFieldInsert:
701 case SpvOpBitFieldSExtract:
702 case SpvOpBitFieldUExtract:
703 case SpvOpBitReverse:
704 case SpvOpBitCount:
705 case SpvOpSizeOf:
706 return true;
707 default:
708 return false;
709 }
710}
711
712bool Instruction::IsScalarizable() const {
713 if (spvOpcodeIsScalarizable(opcode())) {
714 return true;
715 }
716
717 const uint32_t kExtInstSetIdInIdx = 0;
718 const uint32_t kExtInstInstructionInIdx = 1;
719
720 if (opcode() == SpvOpExtInst) {
721 uint32_t instSetId =
722 context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
723
724 if (GetSingleWordInOperand(kExtInstSetIdInIdx) == instSetId) {
725 switch (GetSingleWordInOperand(kExtInstInstructionInIdx)) {
726 case GLSLstd450Round:
727 case GLSLstd450RoundEven:
728 case GLSLstd450Trunc:
729 case GLSLstd450FAbs:
730 case GLSLstd450SAbs:
731 case GLSLstd450FSign:
732 case GLSLstd450SSign:
733 case GLSLstd450Floor:
734 case GLSLstd450Ceil:
735 case GLSLstd450Fract:
736 case GLSLstd450Radians:
737 case GLSLstd450Degrees:
738 case GLSLstd450Sin:
739 case GLSLstd450Cos:
740 case GLSLstd450Tan:
741 case GLSLstd450Asin:
742 case GLSLstd450Acos:
743 case GLSLstd450Atan:
744 case GLSLstd450Sinh:
745 case GLSLstd450Cosh:
746 case GLSLstd450Tanh:
747 case GLSLstd450Asinh:
748 case GLSLstd450Acosh:
749 case GLSLstd450Atanh:
750 case GLSLstd450Atan2:
751 case GLSLstd450Pow:
752 case GLSLstd450Exp:
753 case GLSLstd450Log:
754 case GLSLstd450Exp2:
755 case GLSLstd450Log2:
756 case GLSLstd450Sqrt:
757 case GLSLstd450InverseSqrt:
758 case GLSLstd450Modf:
759 case GLSLstd450FMin:
760 case GLSLstd450UMin:
761 case GLSLstd450SMin:
762 case GLSLstd450FMax:
763 case GLSLstd450UMax:
764 case GLSLstd450SMax:
765 case GLSLstd450FClamp:
766 case GLSLstd450UClamp:
767 case GLSLstd450SClamp:
768 case GLSLstd450FMix:
769 case GLSLstd450Step:
770 case GLSLstd450SmoothStep:
771 case GLSLstd450Fma:
772 case GLSLstd450Frexp:
773 case GLSLstd450Ldexp:
774 case GLSLstd450FindILsb:
775 case GLSLstd450FindSMsb:
776 case GLSLstd450FindUMsb:
777 case GLSLstd450NMin:
778 case GLSLstd450NMax:
779 case GLSLstd450NClamp:
780 return true;
781 default:
782 return false;
783 }
784 }
785 }
786 return false;
787}
788
789bool Instruction::IsOpcodeSafeToDelete() const {
790 if (context()->IsCombinatorInstruction(this)) {
791 return true;
792 }
793
794 switch (opcode()) {
795 case SpvOpDPdx:
796 case SpvOpDPdy:
797 case SpvOpFwidth:
798 case SpvOpDPdxFine:
799 case SpvOpDPdyFine:
800 case SpvOpFwidthFine:
801 case SpvOpDPdxCoarse:
802 case SpvOpDPdyCoarse:
803 case SpvOpFwidthCoarse:
804 case SpvOpImageQueryLod:
805 return true;
806 default:
807 return false;
808 }
809}
810
811void DebugScope::ToBinary(uint32_t type_id, uint32_t result_id,
812 uint32_t ext_set,
813 std::vector<uint32_t>* binary) const {
814 uint32_t num_words = kDebugScopeNumWords;
815 OpenCLDebugInfo100Instructions dbg_opcode = OpenCLDebugInfo100DebugScope;
816 if (GetLexicalScope() == kNoDebugScope) {
817 num_words = kDebugNoScopeNumWords;
818 dbg_opcode = OpenCLDebugInfo100DebugNoScope;
819 } else if (GetInlinedAt() == kNoInlinedAt) {
820 num_words = kDebugScopeNumWordsWithoutInlinedAt;
821 }
822 std::vector<uint32_t> operands = {
823 (num_words << 16) | static_cast<uint16_t>(SpvOpExtInst),
824 type_id,
825 result_id,
826 ext_set,
827 static_cast<uint32_t>(dbg_opcode),
828 };
829 binary->insert(binary->end(), operands.begin(), operands.end());
830 if (GetLexicalScope() != kNoDebugScope) binary->push_back(GetLexicalScope());
831 if (GetInlinedAt() != kNoInlinedAt) binary->push_back(GetInlinedAt());
832}
833
834} // namespace opt
835} // namespace spvtools
836