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