1 // Copyright (c) 2019 Google LLC
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/fuzz/instruction_descriptor.h"
16 
17 namespace spvtools {
18 namespace fuzz {
19 
FindInstruction(const protobufs::InstructionDescriptor & instruction_descriptor,spvtools::opt::IRContext * context)20 opt::Instruction* FindInstruction(
21     const protobufs::InstructionDescriptor& instruction_descriptor,
22     spvtools::opt::IRContext* context) {
23   auto block = context->get_instr_block(
24       instruction_descriptor.base_instruction_result_id());
25   if (block == nullptr) {
26     return nullptr;
27   }
28   bool found_base =
29       block->id() == instruction_descriptor.base_instruction_result_id();
30   uint32_t num_ignored = 0;
31   for (auto& instruction : *block) {
32     if (instruction.HasResultId() &&
33         instruction.result_id() ==
34             instruction_descriptor.base_instruction_result_id()) {
35       assert(!found_base &&
36              "It should not be possible to find the base instruction "
37              "multiple times.");
38       found_base = true;
39       assert(num_ignored == 0 &&
40              "The skipped instruction count should only be incremented "
41              "after the instruction base has been found.");
42     }
43     if (found_base &&
44         instruction.opcode() ==
45             spv::Op(instruction_descriptor.target_instruction_opcode())) {
46       if (num_ignored == instruction_descriptor.num_opcodes_to_ignore()) {
47         return &instruction;
48       }
49       num_ignored++;
50     }
51   }
52   return nullptr;
53 }
54 
MakeInstructionDescriptor(uint32_t base_instruction_result_id,spv::Op target_instruction_opcode,uint32_t num_opcodes_to_ignore)55 protobufs::InstructionDescriptor MakeInstructionDescriptor(
56     uint32_t base_instruction_result_id, spv::Op target_instruction_opcode,
57     uint32_t num_opcodes_to_ignore) {
58   protobufs::InstructionDescriptor result;
59   result.set_base_instruction_result_id(base_instruction_result_id);
60   result.set_target_instruction_opcode(uint32_t(target_instruction_opcode));
61   result.set_num_opcodes_to_ignore(num_opcodes_to_ignore);
62   return result;
63 }
64 
MakeInstructionDescriptor(const opt::BasicBlock & block,const opt::BasicBlock::const_iterator & inst_it)65 protobufs::InstructionDescriptor MakeInstructionDescriptor(
66     const opt::BasicBlock& block,
67     const opt::BasicBlock::const_iterator& inst_it) {
68   const spv::Op opcode =
69       inst_it->opcode();    // The opcode of the instruction being described.
70   uint32_t skip_count = 0;  // The number of these opcodes we have skipped when
71   // searching backwards.
72 
73   // Consider instructions in the block in reverse order, starting from
74   // |inst_it|.
75   for (opt::BasicBlock::const_iterator backwards_iterator = inst_it;;
76        --backwards_iterator) {
77     if (backwards_iterator->HasResultId()) {
78       // As soon as we find an instruction with a result id, we can return a
79       // descriptor for |inst_it|.
80       return MakeInstructionDescriptor(backwards_iterator->result_id(), opcode,
81                                        skip_count);
82     }
83     if (backwards_iterator != inst_it &&
84         backwards_iterator->opcode() == opcode) {
85       // We are skipping over an instruction with the same opcode as |inst_it|;
86       // we increase our skip count to reflect this.
87       skip_count++;
88     }
89     if (backwards_iterator == block.begin()) {
90       // We exit the loop when we reach the start of the block, but only after
91       // we have processed the first instruction in the block.
92       break;
93     }
94   }
95   // We did not find an instruction inside the block with a result id, so we use
96   // the block's label's id.
97   return MakeInstructionDescriptor(block.id(), opcode, skip_count);
98 }
99 
MakeInstructionDescriptor(opt::IRContext * context,opt::Instruction * inst)100 protobufs::InstructionDescriptor MakeInstructionDescriptor(
101     opt::IRContext* context, opt::Instruction* inst) {
102   auto block = context->get_instr_block(inst);
103   uint32_t base_instruction_result_id = block->id();
104   uint32_t num_opcodes_to_ignore = 0;
105   for (auto& inst_in_block : *block) {
106     if (inst_in_block.HasResultId()) {
107       base_instruction_result_id = inst_in_block.result_id();
108       num_opcodes_to_ignore = 0;
109     }
110     if (&inst_in_block == inst) {
111       return MakeInstructionDescriptor(base_instruction_result_id,
112                                        inst->opcode(), num_opcodes_to_ignore);
113     }
114     if (inst_in_block.opcode() == inst->opcode()) {
115       num_opcodes_to_ignore++;
116     }
117   }
118   assert(false && "No matching instruction was found.");
119   return protobufs::InstructionDescriptor();
120 }
121 
122 }  // namespace fuzz
123 }  // namespace spvtools
124