SPIRV-Tools/source/fuzz/instruction_descriptor.cpp
Alastair Donaldson 041f0a0249
spirv-fuzz: simplify transformation for replacing an id with a synonym (#3020)
Prior to this change, TransformationReplaceIdWithSynonym was designed
to be able to replace an id with some synonymous data descriptor,
possibly necessitating extracting from a composite into a fresh id in
order to get at the synonymous data.  This change simplifies things so
that TransformationReplaceIdWithSynonym just allows one id to be
replaced by another id.  It is the responsibility of the associated
fuzzer pass - FuzzerPassApplyIdSynonyms - to perform the extraction
operations, using e.g. TransformationCompositeExtract.
2019-11-07 16:19:06 +00:00

128 lines
4.7 KiB
C++

// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/instruction_descriptor.h"
namespace spvtools {
namespace fuzz {
opt::Instruction* FindInstruction(
const protobufs::InstructionDescriptor& instruction_descriptor,
spvtools::opt::IRContext* context) {
for (auto& function : *context->module()) {
for (auto& block : function) {
bool found_base =
block.id() == instruction_descriptor.base_instruction_result_id();
uint32_t num_ignored = 0;
for (auto& instruction : block) {
if (instruction.HasResultId() &&
instruction.result_id() ==
instruction_descriptor.base_instruction_result_id()) {
assert(!found_base &&
"It should not be possible to find the base instruction "
"multiple times.");
found_base = true;
assert(num_ignored == 0 &&
"The skipped instruction count should only be incremented "
"after the instruction base has been found.");
}
if (found_base &&
instruction.opcode() ==
instruction_descriptor.target_instruction_opcode()) {
if (num_ignored == instruction_descriptor.num_opcodes_to_ignore()) {
return &instruction;
}
num_ignored++;
}
}
if (found_base) {
// We found the base instruction, but did not find the target
// instruction in the same block.
return nullptr;
}
}
}
return nullptr;
}
protobufs::InstructionDescriptor MakeInstructionDescriptor(
uint32_t base_instruction_result_id, SpvOp target_instruction_opcode,
uint32_t num_opcodes_to_ignore) {
protobufs::InstructionDescriptor result;
result.set_base_instruction_result_id(base_instruction_result_id);
result.set_target_instruction_opcode(target_instruction_opcode);
result.set_num_opcodes_to_ignore(num_opcodes_to_ignore);
return result;
}
protobufs::InstructionDescriptor MakeInstructionDescriptor(
const opt::BasicBlock& block,
const opt::BasicBlock::const_iterator& inst_it) {
const SpvOp opcode =
inst_it->opcode(); // The opcode of the instruction being described.
uint32_t skip_count = 0; // The number of these opcodes we have skipped when
// searching backwards.
// Consider instructions in the block in reverse order, starting from
// |inst_it|.
for (opt::BasicBlock::const_iterator backwards_iterator = inst_it;;
--backwards_iterator) {
if (backwards_iterator->HasResultId()) {
// As soon as we find an instruction with a result id, we can return a
// descriptor for |inst_it|.
return MakeInstructionDescriptor(backwards_iterator->result_id(), opcode,
skip_count);
}
if (backwards_iterator != inst_it &&
backwards_iterator->opcode() == opcode) {
// We are skipping over an instruction with the same opcode as |inst_it|;
// we increase our skip count to reflect this.
skip_count++;
}
if (backwards_iterator == block.begin()) {
// We exit the loop when we reach the start of the block, but only after
// we have processed the first instruction in the block.
break;
}
}
// We did not find an instruction inside the block with a result id, so we use
// the block's label's id.
return MakeInstructionDescriptor(block.id(), opcode, skip_count);
}
protobufs::InstructionDescriptor MakeInstructionDescriptor(
opt::IRContext* context, opt::Instruction* inst) {
auto block = context->get_instr_block(inst);
uint32_t base_instruction_result_id = block->id();
uint32_t num_opcodes_to_ignore = 0;
for (auto& inst_in_block : *block) {
if (inst_in_block.HasResultId()) {
base_instruction_result_id = inst_in_block.result_id();
num_opcodes_to_ignore = 0;
}
if (&inst_in_block == inst) {
return MakeInstructionDescriptor(base_instruction_result_id,
inst->opcode(), num_opcodes_to_ignore);
}
if (inst_in_block.opcode() == inst->opcode()) {
num_opcodes_to_ignore++;
}
}
assert(false && "No matching instruction was found.");
return protobufs::InstructionDescriptor();
}
} // namespace fuzz
} // namespace spvtools