SPIRV-Tools/source/fuzz/transformation_composite_insert.cpp
Vasyl Teliman e2ac64bdf0
spirv-fuzz: Move ApplyTransformation to .cpp file (#4258)
Sometimes, you need to change these functions during debugging (e.g.,
figure out why the transformation is inapplicable). When that happens,
you need to recompile the whole fuzzer just because these functions
are in the header file. This PR fixes the situation.
2021-05-26 00:39:51 +01:00

247 lines
9.2 KiB
C++

// Copyright (c) 2020 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 "transformation_composite_insert.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
namespace spvtools {
namespace fuzz {
TransformationCompositeInsert::TransformationCompositeInsert(
protobufs::TransformationCompositeInsert message)
: message_(std::move(message)) {}
TransformationCompositeInsert::TransformationCompositeInsert(
const protobufs::InstructionDescriptor& instruction_to_insert_before,
uint32_t fresh_id, uint32_t composite_id, uint32_t object_id,
const std::vector<uint32_t>& index) {
*message_.mutable_instruction_to_insert_before() =
instruction_to_insert_before;
message_.set_fresh_id(fresh_id);
message_.set_composite_id(composite_id);
message_.set_object_id(object_id);
for (auto an_index : index) {
message_.add_index(an_index);
}
}
bool TransformationCompositeInsert::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
// |message_.fresh_id| must be fresh.
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
return false;
}
// |message_.composite_id| must refer to an existing composite value.
auto composite =
ir_context->get_def_use_mgr()->GetDef(message_.composite_id());
if (!IsCompositeInstructionSupported(ir_context, composite)) {
return false;
}
// The indices in |message_.index| must be suitable for indexing into
// |composite->type_id()|.
auto component_to_be_replaced_type_id = fuzzerutil::WalkCompositeTypeIndices(
ir_context, composite->type_id(), message_.index());
if (component_to_be_replaced_type_id == 0) {
return false;
}
// The instruction having the id of |message_.object_id| must be defined.
auto object_instruction =
ir_context->get_def_use_mgr()->GetDef(message_.object_id());
if (object_instruction == nullptr || object_instruction->type_id() == 0) {
return false;
}
// We ignore pointers for now.
auto object_instruction_type =
ir_context->get_type_mgr()->GetType(object_instruction->type_id());
if (object_instruction_type->AsPointer() != nullptr) {
return false;
}
// The type id of the object having |message_.object_id| and the type id of
// the component of the composite at index |message_.index| must be the same.
if (component_to_be_replaced_type_id != object_instruction->type_id()) {
return false;
}
// |message_.instruction_to_insert_before| must be a defined instruction.
auto instruction_to_insert_before =
FindInstruction(message_.instruction_to_insert_before(), ir_context);
if (instruction_to_insert_before == nullptr) {
return false;
}
// |message_.composite_id| and |message_.object_id| must be available before
// the |message_.instruction_to_insert_before|.
if (!fuzzerutil::IdIsAvailableBeforeInstruction(
ir_context, instruction_to_insert_before, message_.composite_id())) {
return false;
}
if (!fuzzerutil::IdIsAvailableBeforeInstruction(
ir_context, instruction_to_insert_before, message_.object_id())) {
return false;
}
// It must be possible to insert an OpCompositeInsert before this
// instruction.
return fuzzerutil::CanInsertOpcodeBeforeInstruction(
SpvOpCompositeInsert, instruction_to_insert_before);
}
void TransformationCompositeInsert::Apply(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
// |message_.struct_fresh_id| must be fresh.
assert(fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) &&
"|message_.fresh_id| must be fresh");
std::vector<uint32_t> index =
fuzzerutil::RepeatedFieldToVector(message_.index());
opt::Instruction::OperandList in_operands;
in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.object_id()}});
in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.composite_id()}});
for (auto i : index) {
in_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}});
}
auto composite_type_id =
fuzzerutil::GetTypeId(ir_context, message_.composite_id());
auto insert_before =
FindInstruction(message_.instruction_to_insert_before(), ir_context);
auto new_instruction = MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeInsert, composite_type_id, message_.fresh_id(),
std::move(in_operands));
auto new_instruction_ptr = new_instruction.get();
insert_before->InsertBefore(std::move(new_instruction));
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
// Inform the def-use manager about the new instruction and record its basic
// block.
ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
ir_context->set_instr_block(new_instruction_ptr,
ir_context->get_instr_block(insert_before));
// Add data synonym facts that arise from the insertion.
AddDataSynonymFacts(ir_context, transformation_context);
}
protobufs::Transformation TransformationCompositeInsert::ToMessage() const {
protobufs::Transformation result;
*result.mutable_composite_insert() = message_;
return result;
}
bool TransformationCompositeInsert::IsCompositeInstructionSupported(
opt::IRContext* ir_context, opt::Instruction* instruction) {
if (instruction == nullptr) {
return false;
}
if (instruction->result_id() == 0 || instruction->type_id() == 0) {
return false;
}
auto composite_type =
ir_context->get_type_mgr()->GetType(instruction->type_id());
if (!fuzzerutil::IsCompositeType(composite_type)) {
return false;
}
// Empty composites are not supported.
auto instruction_type_inst =
ir_context->get_def_use_mgr()->GetDef(instruction->type_id());
if (fuzzerutil::GetBoundForCompositeIndex(*instruction_type_inst,
ir_context) == 0) {
return false;
}
return true;
}
std::unordered_set<uint32_t> TransformationCompositeInsert::GetFreshIds()
const {
return {message_.fresh_id()};
}
void TransformationCompositeInsert::AddDataSynonymFacts(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
// If the result id arising from the insertion is irrelevant then do not add
// any data synonym facts. (The result id can be irrelevant if the insertion
// occurs in a dead block.)
if (transformation_context->GetFactManager()->IdIsIrrelevant(
message_.fresh_id())) {
return;
}
// So long as the |message_.composite_id| is suitable for participating in
// synonyms, every every element of the insertion result except for at the
// index being inserted into is synonymous with the corresponding element of
// |message_.composite_id|. In that case, for every index that is a prefix of
// |index|, the components different from the one that contains the inserted
// object are synonymous with corresponding elements in the original
// composite.
uint32_t current_node_type_id =
fuzzerutil::GetTypeId(ir_context, message_.composite_id());
std::vector<uint32_t> current_index;
std::vector<uint32_t> index =
fuzzerutil::RepeatedFieldToVector(message_.index());
for (uint32_t current_level : index) {
auto current_node_type_inst =
ir_context->get_def_use_mgr()->GetDef(current_node_type_id);
uint32_t index_to_skip = current_level;
uint32_t num_of_components = fuzzerutil::GetBoundForCompositeIndex(
*current_node_type_inst, ir_context);
// Update the current_node_type_id.
current_node_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
ir_context, current_node_type_id, index_to_skip);
for (uint32_t i = 0; i < num_of_components; i++) {
if (i == index_to_skip) {
continue;
}
current_index.push_back(i);
if (fuzzerutil::CanMakeSynonymOf(
ir_context, *transformation_context,
ir_context->get_def_use_mgr()->GetDef(message_.composite_id()))) {
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(message_.fresh_id(), current_index),
MakeDataDescriptor(message_.composite_id(), current_index));
}
current_index.pop_back();
}
// Store the prefix of the |index|.
current_index.push_back(current_level);
}
// If the object being inserted supports synonym creation then it is
// synonymous with the result of the insert instruction at the given index.
if (fuzzerutil::CanMakeSynonymOf(
ir_context, *transformation_context,
ir_context->get_def_use_mgr()->GetDef(message_.object_id()))) {
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(message_.object_id(), {}),
MakeDataDescriptor(message_.fresh_id(), index));
}
}
} // namespace fuzz
} // namespace spvtools