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