• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   uint32_t base = GetSingleWordInOperand(kLoadBaseIndex);
159   Instruction* base_inst = context()->get_def_use_mgr()->GetDef(base);
160   bool done = false;
161   while (!done) {
162     switch (base_inst->opcode()) {
163       case SpvOpAccessChain:
164       case SpvOpInBoundsAccessChain:
165       case SpvOpPtrAccessChain:
166       case SpvOpInBoundsPtrAccessChain:
167       case SpvOpImageTexelPointer:
168       case SpvOpCopyObject:
169         // All of these instructions have the base pointer use a base pointer
170         // in in-operand 0.
171         base = base_inst->GetSingleWordInOperand(0);
172         base_inst = context()->get_def_use_mgr()->GetDef(base);
173         break;
174       default:
175         done = true;
176         break;
177     }
178   }
179   return base_inst;
180 }
181 
IsReadOnlyVariable() const182 bool Instruction::IsReadOnlyVariable() const {
183   if (context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
184     return IsReadOnlyVariableShaders();
185   else
186     return IsReadOnlyVariableKernel();
187 }
188 
IsVulkanStorageImage() const189 bool Instruction::IsVulkanStorageImage() const {
190   if (opcode() != SpvOpTypePointer) {
191     return false;
192   }
193 
194   uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
195   if (storage_class != SpvStorageClassUniformConstant) {
196     return false;
197   }
198 
199   Instruction* base_type =
200       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
201   if (base_type->opcode() != SpvOpTypeImage) {
202     return false;
203   }
204 
205   if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) == SpvDimBuffer) {
206     return false;
207   }
208 
209   // Check if the image is sampled.  If we do not know for sure that it is,
210   // then assume it is a storage image.
211   auto s = base_type->GetSingleWordInOperand(kTypeImageSampledIndex);
212   return s != 1;
213 }
214 
IsVulkanSampledImage() const215 bool Instruction::IsVulkanSampledImage() const {
216   if (opcode() != SpvOpTypePointer) {
217     return false;
218   }
219 
220   uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
221   if (storage_class != SpvStorageClassUniformConstant) {
222     return false;
223   }
224 
225   Instruction* base_type =
226       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
227   if (base_type->opcode() != SpvOpTypeImage) {
228     return false;
229   }
230 
231   if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) == SpvDimBuffer) {
232     return false;
233   }
234 
235   // Check if the image is sampled.  If we know for sure that it is,
236   // then return true.
237   auto s = base_type->GetSingleWordInOperand(kTypeImageSampledIndex);
238   return s == 1;
239 }
240 
IsVulkanStorageTexelBuffer() const241 bool Instruction::IsVulkanStorageTexelBuffer() const {
242   if (opcode() != SpvOpTypePointer) {
243     return false;
244   }
245 
246   uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
247   if (storage_class != SpvStorageClassUniformConstant) {
248     return false;
249   }
250 
251   Instruction* base_type =
252       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
253   if (base_type->opcode() != SpvOpTypeImage) {
254     return false;
255   }
256 
257   if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) != SpvDimBuffer) {
258     return false;
259   }
260 
261   // Check if the image is sampled.  If we do not know for sure that it is,
262   // then assume it is a storage texel buffer.
263   return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) != 1;
264 }
265 
IsVulkanStorageBuffer() const266 bool Instruction::IsVulkanStorageBuffer() const {
267   // Is there a difference between a "Storage buffer" and a "dynamic storage
268   // buffer" in SPIR-V and do we care about the difference?
269   if (opcode() != SpvOpTypePointer) {
270     return false;
271   }
272 
273   Instruction* base_type =
274       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
275 
276   if (base_type->opcode() != SpvOpTypeStruct) {
277     return false;
278   }
279 
280   uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
281   if (storage_class == SpvStorageClassUniform) {
282     bool is_buffer_block = false;
283     context()->get_decoration_mgr()->ForEachDecoration(
284         base_type->result_id(), SpvDecorationBufferBlock,
285         [&is_buffer_block](const Instruction&) { is_buffer_block = true; });
286     return is_buffer_block;
287   } else if (storage_class == SpvStorageClassStorageBuffer) {
288     bool is_block = false;
289     context()->get_decoration_mgr()->ForEachDecoration(
290         base_type->result_id(), SpvDecorationBlock,
291         [&is_block](const Instruction&) { is_block = true; });
292     return is_block;
293   }
294   return false;
295 }
296 
IsVulkanUniformBuffer() const297 bool Instruction::IsVulkanUniformBuffer() const {
298   if (opcode() != SpvOpTypePointer) {
299     return false;
300   }
301 
302   uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
303   if (storage_class != SpvStorageClassUniform) {
304     return false;
305   }
306 
307   Instruction* base_type =
308       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
309   if (base_type->opcode() != SpvOpTypeStruct) {
310     return false;
311   }
312 
313   bool is_block = false;
314   context()->get_decoration_mgr()->ForEachDecoration(
315       base_type->result_id(), SpvDecorationBlock,
316       [&is_block](const Instruction&) { is_block = true; });
317   return is_block;
318 }
319 
IsReadOnlyVariableShaders() const320 bool Instruction::IsReadOnlyVariableShaders() const {
321   uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
322   Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id());
323 
324   switch (storage_class) {
325     case SpvStorageClassUniformConstant:
326       if (!type_def->IsVulkanStorageImage() &&
327           !type_def->IsVulkanStorageTexelBuffer()) {
328         return true;
329       }
330       break;
331     case SpvStorageClassUniform:
332       if (!type_def->IsVulkanStorageBuffer()) {
333         return true;
334       }
335       break;
336     case SpvStorageClassPushConstant:
337     case SpvStorageClassInput:
338       return true;
339     default:
340       break;
341   }
342 
343   bool is_nonwritable = false;
344   context()->get_decoration_mgr()->ForEachDecoration(
345       result_id(), SpvDecorationNonWritable,
346       [&is_nonwritable](const Instruction&) { is_nonwritable = true; });
347   return is_nonwritable;
348 }
349 
IsReadOnlyVariableKernel() const350 bool Instruction::IsReadOnlyVariableKernel() const {
351   uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
352   return storage_class == SpvStorageClassUniformConstant;
353 }
354 
GetTypeComponent(uint32_t element) const355 uint32_t Instruction::GetTypeComponent(uint32_t element) const {
356   uint32_t subtype = 0;
357   switch (opcode()) {
358     case SpvOpTypeStruct:
359       subtype = GetSingleWordInOperand(element);
360       break;
361     case SpvOpTypeArray:
362     case SpvOpTypeRuntimeArray:
363     case SpvOpTypeVector:
364     case SpvOpTypeMatrix:
365       // These types all have uniform subtypes.
366       subtype = GetSingleWordInOperand(0u);
367       break;
368     default:
369       break;
370   }
371 
372   return subtype;
373 }
374 
InsertBefore(std::unique_ptr<Instruction> && i)375 Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& i) {
376   i.get()->InsertBefore(this);
377   return i.release();
378 }
379 
InsertBefore(std::vector<std::unique_ptr<Instruction>> && list)380 Instruction* Instruction::InsertBefore(
381     std::vector<std::unique_ptr<Instruction>>&& list) {
382   Instruction* first_node = list.front().get();
383   for (auto& i : list) {
384     i.release()->InsertBefore(this);
385   }
386   list.clear();
387   return first_node;
388 }
389 
IsValidBasePointer() const390 bool Instruction::IsValidBasePointer() const {
391   uint32_t tid = type_id();
392   if (tid == 0) {
393     return false;
394   }
395 
396   Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
397   if (type->opcode() != SpvOpTypePointer) {
398     return false;
399   }
400 
401   auto feature_mgr = context()->get_feature_mgr();
402   if (feature_mgr->HasCapability(SpvCapabilityAddresses)) {
403     // TODO: The rules here could be more restrictive.
404     return true;
405   }
406 
407   if (opcode() == SpvOpVariable || opcode() == SpvOpFunctionParameter) {
408     return true;
409   }
410 
411   // With variable pointers, there are more valid base pointer objects.
412   // Variable pointers implicitly declares Variable pointers storage buffer.
413   SpvStorageClass storage_class =
414       static_cast<SpvStorageClass>(type->GetSingleWordInOperand(0));
415   if ((feature_mgr->HasCapability(SpvCapabilityVariablePointersStorageBuffer) &&
416        storage_class == SpvStorageClassStorageBuffer) ||
417       (feature_mgr->HasCapability(SpvCapabilityVariablePointers) &&
418        storage_class == SpvStorageClassWorkgroup)) {
419     switch (opcode()) {
420       case SpvOpPhi:
421       case SpvOpSelect:
422       case SpvOpFunctionCall:
423       case SpvOpConstantNull:
424         return true;
425       default:
426         break;
427     }
428   }
429 
430   uint32_t pointee_type_id = type->GetSingleWordInOperand(1);
431   Instruction* pointee_type_inst =
432       context()->get_def_use_mgr()->GetDef(pointee_type_id);
433 
434   if (pointee_type_inst->IsOpaqueType()) {
435     return true;
436   }
437   return false;
438 }
439 
IsValidBaseImage() const440 bool Instruction::IsValidBaseImage() const {
441   uint32_t tid = type_id();
442   if (tid == 0) {
443     return false;
444   }
445 
446   Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
447   return (type->opcode() == SpvOpTypeImage ||
448           type->opcode() == SpvOpTypeSampledImage);
449 }
450 
IsOpaqueType() const451 bool Instruction::IsOpaqueType() const {
452   if (opcode() == SpvOpTypeStruct) {
453     bool is_opaque = false;
454     ForEachInOperand([&is_opaque, this](const uint32_t* op_id) {
455       Instruction* type_inst = context()->get_def_use_mgr()->GetDef(*op_id);
456       is_opaque |= type_inst->IsOpaqueType();
457     });
458     return is_opaque;
459   } else if (opcode() == SpvOpTypeArray) {
460     uint32_t sub_type_id = GetSingleWordInOperand(0);
461     Instruction* sub_type_inst =
462         context()->get_def_use_mgr()->GetDef(sub_type_id);
463     return sub_type_inst->IsOpaqueType();
464   } else {
465     return opcode() == SpvOpTypeRuntimeArray ||
466            spvOpcodeIsBaseOpaqueType(opcode());
467   }
468 }
469 
IsFoldable() const470 bool Instruction::IsFoldable() const {
471   return IsFoldableByFoldScalar() ||
472          context()->get_instruction_folder().HasConstFoldingRule(this);
473 }
474 
IsFoldableByFoldScalar() const475 bool Instruction::IsFoldableByFoldScalar() const {
476   const InstructionFolder& folder = context()->get_instruction_folder();
477   if (!folder.IsFoldableOpcode(opcode())) {
478     return false;
479   }
480   Instruction* type = context()->get_def_use_mgr()->GetDef(type_id());
481   return folder.IsFoldableType(type);
482 }
483 
IsFloatingPointFoldingAllowed() const484 bool Instruction::IsFloatingPointFoldingAllowed() const {
485   // TODO: Add the rules for kernels.  For now it will be pessimistic.
486   // For now, do not support capabilities introduced by SPV_KHR_float_controls.
487   if (!context_->get_feature_mgr()->HasCapability(SpvCapabilityShader) ||
488       context_->get_feature_mgr()->HasCapability(SpvCapabilityDenormPreserve) ||
489       context_->get_feature_mgr()->HasCapability(
490           SpvCapabilityDenormFlushToZero) ||
491       context_->get_feature_mgr()->HasCapability(
492           SpvCapabilitySignedZeroInfNanPreserve) ||
493       context_->get_feature_mgr()->HasCapability(
494           SpvCapabilityRoundingModeRTZ) ||
495       context_->get_feature_mgr()->HasCapability(
496           SpvCapabilityRoundingModeRTE)) {
497     return false;
498   }
499 
500   bool is_nocontract = false;
501   context_->get_decoration_mgr()->WhileEachDecoration(
502       result_id(), SpvDecorationNoContraction,
503       [&is_nocontract](const Instruction&) {
504         is_nocontract = true;
505         return false;
506       });
507   return !is_nocontract;
508 }
509 
PrettyPrint(uint32_t options) const510 std::string Instruction::PrettyPrint(uint32_t options) const {
511   // Convert the module to binary.
512   std::vector<uint32_t> module_binary;
513   context()->module()->ToBinary(&module_binary, /* skip_nop = */ false);
514 
515   // Convert the instruction to binary. This is used to identify the correct
516   // stream of words to output from the module.
517   std::vector<uint32_t> inst_binary;
518   ToBinaryWithoutAttachedDebugInsts(&inst_binary);
519 
520   // Do not generate a header.
521   return spvInstructionBinaryToText(
522       context()->grammar().target_env(), inst_binary.data(), inst_binary.size(),
523       module_binary.data(), module_binary.size(),
524       options | SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
525 }
526 
operator <<(std::ostream & str,const Instruction & inst)527 std::ostream& operator<<(std::ostream& str, const Instruction& inst) {
528   str << inst.PrettyPrint();
529   return str;
530 }
531 
Dump() const532 void Instruction::Dump() const {
533   std::cerr << "Instruction #" << unique_id() << "\n" << *this << "\n";
534 }
535 
IsOpcodeCodeMotionSafe() const536 bool Instruction::IsOpcodeCodeMotionSafe() const {
537   switch (opcode_) {
538     case SpvOpNop:
539     case SpvOpUndef:
540     case SpvOpLoad:
541     case SpvOpAccessChain:
542     case SpvOpInBoundsAccessChain:
543     case SpvOpArrayLength:
544     case SpvOpVectorExtractDynamic:
545     case SpvOpVectorInsertDynamic:
546     case SpvOpVectorShuffle:
547     case SpvOpCompositeConstruct:
548     case SpvOpCompositeExtract:
549     case SpvOpCompositeInsert:
550     case SpvOpCopyObject:
551     case SpvOpTranspose:
552     case SpvOpConvertFToU:
553     case SpvOpConvertFToS:
554     case SpvOpConvertSToF:
555     case SpvOpConvertUToF:
556     case SpvOpUConvert:
557     case SpvOpSConvert:
558     case SpvOpFConvert:
559     case SpvOpQuantizeToF16:
560     case SpvOpBitcast:
561     case SpvOpSNegate:
562     case SpvOpFNegate:
563     case SpvOpIAdd:
564     case SpvOpFAdd:
565     case SpvOpISub:
566     case SpvOpFSub:
567     case SpvOpIMul:
568     case SpvOpFMul:
569     case SpvOpUDiv:
570     case SpvOpSDiv:
571     case SpvOpFDiv:
572     case SpvOpUMod:
573     case SpvOpSRem:
574     case SpvOpSMod:
575     case SpvOpFRem:
576     case SpvOpFMod:
577     case SpvOpVectorTimesScalar:
578     case SpvOpMatrixTimesScalar:
579     case SpvOpVectorTimesMatrix:
580     case SpvOpMatrixTimesVector:
581     case SpvOpMatrixTimesMatrix:
582     case SpvOpOuterProduct:
583     case SpvOpDot:
584     case SpvOpIAddCarry:
585     case SpvOpISubBorrow:
586     case SpvOpUMulExtended:
587     case SpvOpSMulExtended:
588     case SpvOpAny:
589     case SpvOpAll:
590     case SpvOpIsNan:
591     case SpvOpIsInf:
592     case SpvOpLogicalEqual:
593     case SpvOpLogicalNotEqual:
594     case SpvOpLogicalOr:
595     case SpvOpLogicalAnd:
596     case SpvOpLogicalNot:
597     case SpvOpSelect:
598     case SpvOpIEqual:
599     case SpvOpINotEqual:
600     case SpvOpUGreaterThan:
601     case SpvOpSGreaterThan:
602     case SpvOpUGreaterThanEqual:
603     case SpvOpSGreaterThanEqual:
604     case SpvOpULessThan:
605     case SpvOpSLessThan:
606     case SpvOpULessThanEqual:
607     case SpvOpSLessThanEqual:
608     case SpvOpFOrdEqual:
609     case SpvOpFUnordEqual:
610     case SpvOpFOrdNotEqual:
611     case SpvOpFUnordNotEqual:
612     case SpvOpFOrdLessThan:
613     case SpvOpFUnordLessThan:
614     case SpvOpFOrdGreaterThan:
615     case SpvOpFUnordGreaterThan:
616     case SpvOpFOrdLessThanEqual:
617     case SpvOpFUnordLessThanEqual:
618     case SpvOpFOrdGreaterThanEqual:
619     case SpvOpFUnordGreaterThanEqual:
620     case SpvOpShiftRightLogical:
621     case SpvOpShiftRightArithmetic:
622     case SpvOpShiftLeftLogical:
623     case SpvOpBitwiseOr:
624     case SpvOpBitwiseXor:
625     case SpvOpBitwiseAnd:
626     case SpvOpNot:
627     case SpvOpBitFieldInsert:
628     case SpvOpBitFieldSExtract:
629     case SpvOpBitFieldUExtract:
630     case SpvOpBitReverse:
631     case SpvOpBitCount:
632     case SpvOpSizeOf:
633       return true;
634     default:
635       return false;
636   }
637 }
638 
IsScalarizable() const639 bool Instruction::IsScalarizable() const {
640   if (spvOpcodeIsScalarizable(opcode())) {
641     return true;
642   }
643 
644   const uint32_t kExtInstSetIdInIdx = 0;
645   const uint32_t kExtInstInstructionInIdx = 1;
646 
647   if (opcode() == SpvOpExtInst) {
648     uint32_t instSetId =
649         context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
650 
651     if (GetSingleWordInOperand(kExtInstSetIdInIdx) == instSetId) {
652       switch (GetSingleWordInOperand(kExtInstInstructionInIdx)) {
653         case GLSLstd450Round:
654         case GLSLstd450RoundEven:
655         case GLSLstd450Trunc:
656         case GLSLstd450FAbs:
657         case GLSLstd450SAbs:
658         case GLSLstd450FSign:
659         case GLSLstd450SSign:
660         case GLSLstd450Floor:
661         case GLSLstd450Ceil:
662         case GLSLstd450Fract:
663         case GLSLstd450Radians:
664         case GLSLstd450Degrees:
665         case GLSLstd450Sin:
666         case GLSLstd450Cos:
667         case GLSLstd450Tan:
668         case GLSLstd450Asin:
669         case GLSLstd450Acos:
670         case GLSLstd450Atan:
671         case GLSLstd450Sinh:
672         case GLSLstd450Cosh:
673         case GLSLstd450Tanh:
674         case GLSLstd450Asinh:
675         case GLSLstd450Acosh:
676         case GLSLstd450Atanh:
677         case GLSLstd450Atan2:
678         case GLSLstd450Pow:
679         case GLSLstd450Exp:
680         case GLSLstd450Log:
681         case GLSLstd450Exp2:
682         case GLSLstd450Log2:
683         case GLSLstd450Sqrt:
684         case GLSLstd450InverseSqrt:
685         case GLSLstd450Modf:
686         case GLSLstd450FMin:
687         case GLSLstd450UMin:
688         case GLSLstd450SMin:
689         case GLSLstd450FMax:
690         case GLSLstd450UMax:
691         case GLSLstd450SMax:
692         case GLSLstd450FClamp:
693         case GLSLstd450UClamp:
694         case GLSLstd450SClamp:
695         case GLSLstd450FMix:
696         case GLSLstd450Step:
697         case GLSLstd450SmoothStep:
698         case GLSLstd450Fma:
699         case GLSLstd450Frexp:
700         case GLSLstd450Ldexp:
701         case GLSLstd450FindILsb:
702         case GLSLstd450FindSMsb:
703         case GLSLstd450FindUMsb:
704         case GLSLstd450NMin:
705         case GLSLstd450NMax:
706         case GLSLstd450NClamp:
707           return true;
708         default:
709           return false;
710       }
711     }
712   }
713   return false;
714 }
715 
IsOpcodeSafeToDelete() const716 bool Instruction::IsOpcodeSafeToDelete() const {
717   if (context()->IsCombinatorInstruction(this)) {
718     return true;
719   }
720 
721   switch (opcode()) {
722     case SpvOpDPdx:
723     case SpvOpDPdy:
724     case SpvOpFwidth:
725     case SpvOpDPdxFine:
726     case SpvOpDPdyFine:
727     case SpvOpFwidthFine:
728     case SpvOpDPdxCoarse:
729     case SpvOpDPdyCoarse:
730     case SpvOpFwidthCoarse:
731     case SpvOpImageQueryLod:
732       return true;
733     default:
734       return false;
735   }
736 }
737 
738 }  // namespace opt
739 }  // namespace spvtools
740