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