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