1 // Copyright (c) 2016 Google Inc.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 #include <initializer_list>
17 #include "disassemble.h"
19 #include "instruction.h"
20 #include "ir_context.h"
27 // Indices used to get particular operands out of instructions using InOperand.
28 const uint32_t kTypeImageDimIndex = 1;
29 const uint32_t kLoadBaseIndex = 0;
30 const uint32_t kVariableStorageClassIndex = 0;
31 const uint32_t kTypeImageSampledIndex = 5;
34 Instruction::Instruction(IRContext* c)
35 : utils::IntrusiveNodeBase<Instruction>(),
40 unique_id_(c->TakeNextUniqueId()) {}
42 Instruction::Instruction(IRContext* c, SpvOp op)
43 : utils::IntrusiveNodeBase<Instruction>(),
48 unique_id_(c->TakeNextUniqueId()) {}
50 Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
51 std::vector<Instruction>&& dbg_line)
53 opcode_(static_cast<SpvOp>(inst.opcode)),
54 type_id_(inst.type_id),
55 result_id_(inst.result_id),
56 unique_id_(c->TakeNextUniqueId()),
57 dbg_line_insts_(std::move(dbg_line)) {
58 assert((!IsDebugLineInst(opcode_) || dbg_line.empty()) &&
59 "Op(No)Line attaching to Op(No)Line found");
60 for (uint32_t i = 0; i < inst.num_operands; ++i) {
61 const auto& current_payload = inst.operands[i];
62 std::vector<uint32_t> words(
63 inst.words + current_payload.offset,
64 inst.words + current_payload.offset + current_payload.num_words);
65 operands_.emplace_back(current_payload.type, std::move(words));
69 Instruction::Instruction(IRContext* c, SpvOp op, uint32_t ty_id,
71 const std::vector<Operand>& in_operands)
72 : utils::IntrusiveNodeBase<Instruction>(),
77 unique_id_(c->TakeNextUniqueId()),
80 operands_.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_TYPE_ID,
81 std::initializer_list<uint32_t>{type_id_});
83 if (result_id_ != 0) {
84 operands_.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_RESULT_ID,
85 std::initializer_list<uint32_t>{result_id_});
87 operands_.insert(operands_.end(), in_operands.begin(), in_operands.end());
90 Instruction::Instruction(Instruction&& that)
91 : utils::IntrusiveNodeBase<Instruction>(),
92 opcode_(that.opcode_),
93 type_id_(that.type_id_),
94 result_id_(that.result_id_),
95 unique_id_(that.unique_id_),
96 operands_(std::move(that.operands_)),
97 dbg_line_insts_(std::move(that.dbg_line_insts_)) {}
99 Instruction& Instruction::operator=(Instruction&& that) {
100 opcode_ = that.opcode_;
101 type_id_ = that.type_id_;
102 result_id_ = that.result_id_;
103 unique_id_ = that.unique_id_;
104 operands_ = std::move(that.operands_);
105 dbg_line_insts_ = std::move(that.dbg_line_insts_);
109 Instruction* Instruction::Clone(IRContext* c) const {
110 Instruction* clone = new Instruction(c);
111 clone->opcode_ = opcode_;
112 clone->type_id_ = type_id_;
113 clone->result_id_ = result_id_;
114 clone->unique_id_ = c->TakeNextUniqueId();
115 clone->operands_ = operands_;
116 clone->dbg_line_insts_ = dbg_line_insts_;
120 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();
126 uint32_t Instruction::NumInOperandWords() const {
128 for (uint32_t i = TypeResultIdCount(); i < operands_.size(); ++i)
129 size += static_cast<uint32_t>(operands_[i].words.size());
133 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());
141 void Instruction::ReplaceOperands(const std::vector<Operand>& new_operands) {
143 operands_.insert(operands_.begin(), new_operands.begin(), new_operands.end());
144 operands_.shrink_to_fit();
147 bool Instruction::IsReadOnlyLoad() const {
149 ir::Instruction* address_def = GetBaseAddress();
150 if (!address_def || address_def->opcode() != SpvOpVariable) {
153 return address_def->IsReadOnlyVariable();
158 Instruction* Instruction::GetBaseAddress() const {
159 assert((IsLoad() || opcode() == SpvOpStore || opcode() == SpvOpAccessChain ||
160 opcode() == SpvOpInBoundsAccessChain ||
161 opcode() == SpvOpCopyObject) &&
162 "GetBaseAddress should only be called on instructions that take a "
163 "pointer or image.");
164 uint32_t base = GetSingleWordInOperand(kLoadBaseIndex);
165 ir::Instruction* base_inst = context()->get_def_use_mgr()->GetDef(base);
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
177 base = base_inst->GetSingleWordInOperand(0);
178 base_inst = context()->get_def_use_mgr()->GetDef(base);
189 case SpvOpAccessChain:
190 case SpvOpInBoundsAccessChain:
191 case SpvOpCopyObject:
192 // A load or store through a pointer.
193 assert(base_inst->IsValidBasePointer() &&
194 "We cannot have a base pointer come from this load");
197 // A load or store of an image.
198 assert(base_inst->IsValidBaseImage() && "We are expecting an image.");
204 bool Instruction::IsReadOnlyVariable() const {
205 if (context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
206 return IsReadOnlyVariableShaders();
208 return IsReadOnlyVariableKernel();
211 bool Instruction::IsVulkanStorageImage() const {
212 if (opcode() != SpvOpTypePointer) {
216 uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
217 if (storage_class != SpvStorageClassUniformConstant) {
221 ir::Instruction* base_type =
222 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
223 if (base_type->opcode() != SpvOpTypeImage) {
227 if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) == SpvDimBuffer) {
231 // Check if the image is sampled. If we do not know for sure that it is,
232 // then assume it is a storage image.
233 auto s = base_type->GetSingleWordInOperand(kTypeImageSampledIndex);
237 bool Instruction::IsVulkanSampledImage() const {
238 if (opcode() != SpvOpTypePointer) {
242 uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
243 if (storage_class != SpvStorageClassUniformConstant) {
247 ir::Instruction* base_type =
248 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
249 if (base_type->opcode() != SpvOpTypeImage) {
253 if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) == SpvDimBuffer) {
257 // Check if the image is sampled. If we know for sure that it is,
259 auto s = base_type->GetSingleWordInOperand(kTypeImageSampledIndex);
263 bool Instruction::IsVulkanStorageTexelBuffer() const {
264 if (opcode() != SpvOpTypePointer) {
268 uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
269 if (storage_class != SpvStorageClassUniformConstant) {
273 ir::Instruction* base_type =
274 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
275 if (base_type->opcode() != SpvOpTypeImage) {
279 if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) != SpvDimBuffer) {
283 // Check if the image is sampled. If we do not know for sure that it is,
284 // then assume it is a storage texel buffer.
285 return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) != 1;
288 bool Instruction::IsVulkanStorageBuffer() const {
289 // Is there a difference between a "Storage buffer" and a "dynamic storage
290 // buffer" in SPIR-V and do we care about the difference?
291 if (opcode() != SpvOpTypePointer) {
295 ir::Instruction* base_type =
296 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
298 if (base_type->opcode() != SpvOpTypeStruct) {
302 uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
303 if (storage_class == SpvStorageClassUniform) {
304 bool is_buffer_block = false;
305 context()->get_decoration_mgr()->ForEachDecoration(
306 base_type->result_id(), SpvDecorationBufferBlock,
307 [&is_buffer_block](const ir::Instruction&) { is_buffer_block = true; });
308 return is_buffer_block;
309 } else if (storage_class == SpvStorageClassStorageBuffer) {
310 bool is_block = false;
311 context()->get_decoration_mgr()->ForEachDecoration(
312 base_type->result_id(), SpvDecorationBlock,
313 [&is_block](const ir::Instruction&) { is_block = true; });
319 bool Instruction::IsVulkanUniformBuffer() const {
320 if (opcode() != SpvOpTypePointer) {
324 uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
325 if (storage_class != SpvStorageClassUniform) {
329 ir::Instruction* base_type =
330 context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
331 if (base_type->opcode() != SpvOpTypeStruct) {
335 bool is_block = false;
336 context()->get_decoration_mgr()->ForEachDecoration(
337 base_type->result_id(), SpvDecorationBlock,
338 [&is_block](const ir::Instruction&) { is_block = true; });
342 bool Instruction::IsReadOnlyVariableShaders() const {
343 uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
344 Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id());
346 switch (storage_class) {
347 case SpvStorageClassUniformConstant:
348 if (!type_def->IsVulkanStorageImage() &&
349 !type_def->IsVulkanStorageTexelBuffer()) {
353 case SpvStorageClassUniform:
354 if (!type_def->IsVulkanStorageBuffer()) {
358 case SpvStorageClassPushConstant:
359 case SpvStorageClassInput:
365 bool is_nonwritable = false;
366 context()->get_decoration_mgr()->ForEachDecoration(
367 result_id(), SpvDecorationNonWritable,
368 [&is_nonwritable](const Instruction&) { is_nonwritable = true; });
369 return is_nonwritable;
372 bool Instruction::IsReadOnlyVariableKernel() const {
373 uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
374 return storage_class == SpvStorageClassUniformConstant;
377 uint32_t Instruction::GetTypeComponent(uint32_t element) const {
378 uint32_t subtype = 0;
380 case SpvOpTypeStruct:
381 subtype = GetSingleWordInOperand(element);
384 case SpvOpTypeRuntimeArray:
385 case SpvOpTypeVector:
386 case SpvOpTypeMatrix:
387 // These types all have uniform subtypes.
388 subtype = GetSingleWordInOperand(0u);
397 Instruction* Instruction::InsertBefore(
398 std::vector<std::unique_ptr<Instruction>>&& list) {
399 Instruction* first_node = list.front().get();
400 for (auto& i : list) {
401 i.release()->InsertBefore(this);
407 Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& i) {
408 i.get()->InsertBefore(this);
412 bool Instruction::IsValidBasePointer() const {
413 uint32_t tid = type_id();
418 ir::Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
419 if (type->opcode() != SpvOpTypePointer) {
423 if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses)) {
424 // TODO: The rules here could be more restrictive.
428 if (opcode() == SpvOpVariable || opcode() == SpvOpFunctionParameter) {
432 uint32_t pointee_type_id = type->GetSingleWordInOperand(1);
433 ir::Instruction* pointee_type_inst =
434 context()->get_def_use_mgr()->GetDef(pointee_type_id);
436 if (pointee_type_inst->IsOpaqueType()) {
442 bool Instruction::IsValidBaseImage() const {
443 uint32_t tid = type_id();
448 ir::Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
449 return (type->opcode() == SpvOpTypeImage ||
450 type->opcode() == SpvOpTypeSampledImage);
453 bool Instruction::IsOpaqueType() const {
454 if (opcode() == SpvOpTypeStruct) {
455 bool is_opaque = false;
456 ForEachInOperand([&is_opaque, this](const uint32_t* op_id) {
457 ir::Instruction* type_inst = context()->get_def_use_mgr()->GetDef(*op_id);
458 is_opaque |= type_inst->IsOpaqueType();
461 } else if (opcode() == SpvOpTypeArray) {
462 uint32_t sub_type_id = GetSingleWordInOperand(0);
463 ir::Instruction* sub_type_inst =
464 context()->get_def_use_mgr()->GetDef(sub_type_id);
465 return sub_type_inst->IsOpaqueType();
467 return opcode() == SpvOpTypeRuntimeArray ||
468 spvOpcodeIsBaseOpaqueType(opcode());
472 bool Instruction::IsFoldable() const {
473 if (!opt::IsFoldableOpcode(opcode())) {
476 Instruction* type = context()->get_def_use_mgr()->GetDef(type_id());
477 return opt::IsFoldableType(type);
480 std::string Instruction::PrettyPrint(uint32_t options) const {
481 // Convert the module to binary.
482 std::vector<uint32_t> module_binary;
483 context()->module()->ToBinary(&module_binary, /* skip_nop = */ false);
485 // Convert the instruction to binary. This is used to identify the correct
486 // stream of words to output from the module.
487 std::vector<uint32_t> inst_binary;
488 ToBinaryWithoutAttachedDebugInsts(&inst_binary);
490 // Do not generate a header.
491 return spvInstructionBinaryToText(
492 context()->grammar().target_env(), inst_binary.data(), inst_binary.size(),
493 module_binary.data(), module_binary.size(),
494 options | SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
497 std::ostream& operator<<(std::ostream& str, const ir::Instruction& inst) {
498 str << inst.PrettyPrint();
503 } // namespace spvtools