SROA: Do replacement on structs with no partial references.
[platform/upstream/SPIRV-Tools.git] / source / opt / instruction.cpp
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 <initializer_list>
16
17 #include "disassemble.h"
18 #include "fold.h"
19 #include "instruction.h"
20 #include "ir_context.h"
21 #include "reflect.h"
22
23 namespace spvtools {
24 namespace ir {
25
26 namespace {
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;
32 }  // namespace
33
34 Instruction::Instruction(IRContext* c)
35     : utils::IntrusiveNodeBase<Instruction>(),
36       context_(c),
37       opcode_(SpvOpNop),
38       type_id_(0),
39       result_id_(0),
40       unique_id_(c->TakeNextUniqueId()) {}
41
42 Instruction::Instruction(IRContext* c, SpvOp op)
43     : utils::IntrusiveNodeBase<Instruction>(),
44       context_(c),
45       opcode_(op),
46       type_id_(0),
47       result_id_(0),
48       unique_id_(c->TakeNextUniqueId()) {}
49
50 Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
51                          std::vector<Instruction>&& dbg_line)
52     : context_(c),
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));
66   }
67 }
68
69 Instruction::Instruction(IRContext* c, SpvOp op, uint32_t ty_id,
70                          uint32_t res_id,
71                          const std::vector<Operand>& in_operands)
72     : utils::IntrusiveNodeBase<Instruction>(),
73       context_(c),
74       opcode_(op),
75       type_id_(ty_id),
76       result_id_(res_id),
77       unique_id_(c->TakeNextUniqueId()),
78       operands_() {
79   if (type_id_ != 0) {
80     operands_.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_TYPE_ID,
81                            std::initializer_list<uint32_t>{type_id_});
82   }
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_});
86   }
87   operands_.insert(operands_.end(), in_operands.begin(), in_operands.end());
88 }
89
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_)) {}
98
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_);
106   return *this;
107 }
108
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_;
117   return clone;
118 }
119
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();
124 }
125
126 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
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());
139 }
140
141 void Instruction::ReplaceOperands(const std::vector<Operand>& new_operands) {
142   operands_.clear();
143   operands_.insert(operands_.begin(), new_operands.begin(), new_operands.end());
144   operands_.shrink_to_fit();
145 }
146
147 bool Instruction::IsReadOnlyLoad() const {
148   if (IsLoad()) {
149     ir::Instruction* address_def = GetBaseAddress();
150     if (!address_def || address_def->opcode() != SpvOpVariable) {
151       return false;
152     }
153     return address_def->IsReadOnlyVariable();
154   }
155   return false;
156 }
157
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);
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 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");
195       break;
196     default:
197       // A load or store of an image.
198       assert(base_inst->IsValidBaseImage() && "We are expecting an image.");
199       break;
200   }
201   return base_inst;
202 }
203
204 bool Instruction::IsReadOnlyVariable() const {
205   if (context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
206     return IsReadOnlyVariableShaders();
207   else
208     return IsReadOnlyVariableKernel();
209 }
210
211 bool Instruction::IsVulkanStorageImage() const {
212   if (opcode() != SpvOpTypePointer) {
213     return false;
214   }
215
216   uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
217   if (storage_class != SpvStorageClassUniformConstant) {
218     return false;
219   }
220
221   ir::Instruction* base_type =
222       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
223   if (base_type->opcode() != SpvOpTypeImage) {
224     return false;
225   }
226
227   if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) == SpvDimBuffer) {
228     return false;
229   }
230
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);
234   return s != 1;
235 }
236
237 bool Instruction::IsVulkanSampledImage() const {
238   if (opcode() != SpvOpTypePointer) {
239     return false;
240   }
241
242   uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
243   if (storage_class != SpvStorageClassUniformConstant) {
244     return false;
245   }
246
247   ir::Instruction* base_type =
248       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
249   if (base_type->opcode() != SpvOpTypeImage) {
250     return false;
251   }
252
253   if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) == SpvDimBuffer) {
254     return false;
255   }
256
257   // Check if the image is sampled.  If we know for sure that it is,
258   // then return true.
259   auto s = base_type->GetSingleWordInOperand(kTypeImageSampledIndex);
260   return s == 1;
261 }
262
263 bool Instruction::IsVulkanStorageTexelBuffer() const {
264   if (opcode() != SpvOpTypePointer) {
265     return false;
266   }
267
268   uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
269   if (storage_class != SpvStorageClassUniformConstant) {
270     return false;
271   }
272
273   ir::Instruction* base_type =
274       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
275   if (base_type->opcode() != SpvOpTypeImage) {
276     return false;
277   }
278
279   if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) != SpvDimBuffer) {
280     return false;
281   }
282
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;
286 }
287
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) {
292     return false;
293   }
294
295   ir::Instruction* base_type =
296       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
297
298   if (base_type->opcode() != SpvOpTypeStruct) {
299     return false;
300   }
301
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; });
314     return is_block;
315   }
316   return false;
317 }
318
319 bool Instruction::IsVulkanUniformBuffer() const {
320   if (opcode() != SpvOpTypePointer) {
321     return false;
322   }
323
324   uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
325   if (storage_class != SpvStorageClassUniform) {
326     return false;
327   }
328
329   ir::Instruction* base_type =
330       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
331   if (base_type->opcode() != SpvOpTypeStruct) {
332     return false;
333   }
334
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; });
339   return is_block;
340 }
341
342 bool Instruction::IsReadOnlyVariableShaders() const {
343   uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
344   Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id());
345
346   switch (storage_class) {
347     case SpvStorageClassUniformConstant:
348       if (!type_def->IsVulkanStorageImage() &&
349           !type_def->IsVulkanStorageTexelBuffer()) {
350         return true;
351       }
352       break;
353     case SpvStorageClassUniform:
354       if (!type_def->IsVulkanStorageBuffer()) {
355         return true;
356       }
357       break;
358     case SpvStorageClassPushConstant:
359     case SpvStorageClassInput:
360       return true;
361     default:
362       break;
363   }
364
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;
370 }
371
372 bool Instruction::IsReadOnlyVariableKernel() const {
373   uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
374   return storage_class == SpvStorageClassUniformConstant;
375 }
376
377 uint32_t Instruction::GetTypeComponent(uint32_t element) const {
378   uint32_t subtype = 0;
379   switch (opcode()) {
380     case SpvOpTypeStruct:
381       subtype = GetSingleWordInOperand(element);
382       break;
383     case SpvOpTypeArray:
384     case SpvOpTypeRuntimeArray:
385     case SpvOpTypeVector:
386     case SpvOpTypeMatrix:
387       // These types all have uniform subtypes.
388       subtype = GetSingleWordInOperand(0u);
389       break;
390     default:
391       break;
392   }
393
394   return subtype;
395 }
396
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);
402   }
403   list.clear();
404   return first_node;
405 }
406
407 Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& i) {
408   i.get()->InsertBefore(this);
409   return i.release();
410 }
411
412 bool Instruction::IsValidBasePointer() const {
413   uint32_t tid = type_id();
414   if (tid == 0) {
415     return false;
416   }
417
418   ir::Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
419   if (type->opcode() != SpvOpTypePointer) {
420     return false;
421   }
422
423   if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses)) {
424     // TODO: The rules here could be more restrictive.
425     return true;
426   }
427
428   if (opcode() == SpvOpVariable || opcode() == SpvOpFunctionParameter) {
429     return true;
430   }
431
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);
435
436   if (pointee_type_inst->IsOpaqueType()) {
437     return true;
438   }
439   return false;
440 }
441
442 bool Instruction::IsValidBaseImage() const {
443   uint32_t tid = type_id();
444   if (tid == 0) {
445     return false;
446   }
447
448   ir::Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
449   return (type->opcode() == SpvOpTypeImage ||
450           type->opcode() == SpvOpTypeSampledImage);
451 }
452
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();
459     });
460     return is_opaque;
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();
466   } else {
467     return opcode() == SpvOpTypeRuntimeArray ||
468            spvOpcodeIsBaseOpaqueType(opcode());
469   }
470 }
471
472 bool Instruction::IsFoldable() const {
473   if (!opt::IsFoldableOpcode(opcode())) {
474     return false;
475   }
476   Instruction* type = context()->get_def_use_mgr()->GetDef(type_id());
477   return opt::IsFoldableType(type);
478 }
479
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);
484
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);
489
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);
495 }
496
497 std::ostream& operator<<(std::ostream& str, const ir::Instruction& inst) {
498   str << inst.PrettyPrint();
499   return str;
500 }
501
502 }  // namespace ir
503 }  // namespace spvtools