spirv-fuzz: Refactor 'copy object' and 'construct composite' transformations (#2966)

Rework these transformations to identify instructions via (base,
opcode, skip-count) triples, rather than (base, offset) pairs.
This commit is contained in:
Alastair Donaldson 2019-10-15 20:00:17 +01:00 committed by GitHub
parent 964dc52df5
commit 00170cc5e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 299 additions and 291 deletions

View File

@ -14,6 +14,8 @@
#include "source/fuzz/fuzzer_pass.h" #include "source/fuzz/fuzzer_pass.h"
#include "source/fuzz/instruction_descriptor.h"
namespace spvtools { namespace spvtools {
namespace fuzz { namespace fuzz {
@ -69,9 +71,10 @@ std::vector<opt::Instruction*> FuzzerPass::FindAvailableInstructions(
} }
void FuzzerPass::MaybeAddTransformationBeforeEachInstruction( void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
std::function<uint32_t( std::function<
const opt::Function& function, opt::BasicBlock* block, void(const opt::Function& function, opt::BasicBlock* block,
opt::BasicBlock::iterator inst_it, uint32_t base, uint32_t offset)> opt::BasicBlock::iterator inst_it,
const protobufs::InstructionDescriptor& instruction_descriptor)>
maybe_apply_transformation) { maybe_apply_transformation) {
// Consider every block in every function. // Consider every block in every function.
for (auto& function : *GetIRContext()->module()) { for (auto& function : *GetIRContext()->module()) {
@ -80,46 +83,45 @@ void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
// whether to apply a transformation before it. // whether to apply a transformation before it.
// In order for transformations to insert new instructions, they need to // In order for transformations to insert new instructions, they need to
// be able to identify the instruction to insert before. We enable this // be able to identify the instruction to insert before. We describe an
// by tracking a base instruction, which must generate a result id, and // instruction via its opcode, 'opc', a base instruction 'base' that has a
// an offset (to allow us to identify instructions that do not generate // result id, and the number of instructions with opcode 'opc' that we
// result ids). // should skip when searching from 'base' for the desired instruction.
// (An instruction that has a result id is represented by its own opcode,
// itself as 'base', and a skip-count of 0.)
std::vector<std::tuple<uint32_t, SpvOp, uint32_t>>
base_opcode_skip_triples;
// The initial base instruction is the block label. // The initial base instruction is the block label.
uint32_t base = block.id(); uint32_t base = block.id();
uint32_t offset = 0;
// Consider every instruction in the block. // Counts the number of times we have seen each opcode since we reset the
// base instruction.
std::map<SpvOp, uint32_t> skip_count;
// Consider every instruction in the block. The label is excluded: it is
// only necessary to consider it as a base in case the first instruction
// in the block does not have a result id.
for (auto inst_it = block.begin(); inst_it != block.end(); ++inst_it) { for (auto inst_it = block.begin(); inst_it != block.end(); ++inst_it) {
if (inst_it->HasResultId()) { if (inst_it->HasResultId()) {
// In the case that the instruction has a result id, we use the // In the case that the instruction has a result id, we use the
// instruction as its own base, with zero offset. // instruction as its own base, and clear the skip counts we have
// collected.
base = inst_it->result_id(); base = inst_it->result_id();
offset = 0; skip_count.clear();
} else {
// The instruction does not have a result id, so we need to identify
// it via the latest instruction that did have a result id (base), and
// an incremented offset.
offset++;
} }
const SpvOp opcode = inst_it->opcode();
// Invoke the provided function, which might apply a transformation. // Invoke the provided function, which might apply a transformation.
// Its return value informs us of how many instructions it inserted. maybe_apply_transformation(
// (This will be 0 if no transformation was applied.) function, &block, inst_it,
uint32_t num_instructions_inserted = MakeInstructionDescriptor(
maybe_apply_transformation(function, &block, inst_it, base, offset); base, opcode,
skip_count.count(opcode) ? skip_count.at(opcode) : 0));
if (!inst_it->HasResultId()) { if (!inst_it->HasResultId()) {
// We are tracking the current id-less instruction via an offset, skip_count[opcode] =
// |offset|, from a previous instruction, |base|, that has an id. We skip_count.count(opcode) ? skip_count.at(opcode) + 1 : 1;
// increment |offset| to reflect any newly-inserted instructions.
//
// An alternative would be to reset |base| to be an id generated by
// a newly-inserted instruction, but that would be more complex, and
// sticking to a |base| that already existed before this
// transformation was applied makes the applicability of future
// transformations less tightly coupled with the presence of the just-
// applied transformation.
offset += num_instructions_inserted;
} }
} }
} }

View File

@ -67,27 +67,26 @@ class FuzzerPass {
instruction_is_relevant); instruction_is_relevant);
// A helper method that iterates through each instruction in each block, at // A helper method that iterates through each instruction in each block, at
// all times tracking a base instruction and offset that allows that latest // all times tracking an instruction descriptor that allows the latest
// instruction to be located even if it has no result id. // instruction to be located even if it has no result id.
// //
// The code to manipulate the base and offset is a bit fiddly, and the point // The code to manipulate the instruction descriptor is a bit fiddly, and the
// of this method is to avoiding having to duplicate it in multiple // point of this method is to avoiding having to duplicate it in multiple
// transformation passes. // transformation passes.
// //
// The function |maybe_apply_transformation| is invoked for each instruction // The function |maybe_apply_transformation| is invoked for each instruction
// |inst_it| in block |block| of function |function| that is encountered. The // |inst_it| in block |block| of function |function| that is encountered. The
// |base| and |offset| parameters to the function object allow |inst_it| to be // |instruction_descriptor| parameter to the function object allows |inst_it|
// identified. // to be identified.
// //
// The job of |maybe_apply_transformation| is to randomly decide whether to // The job of |maybe_apply_transformation| is to randomly decide whether to
// try to apply some transformation, and then - if selected - to attempt to // try to apply some transformation, and then - if selected - to attempt to
// apply it. The function returns the number of instructions that were // apply it.
// inserted before |inst_it|, so that |offset| can be updated.
//
void MaybeAddTransformationBeforeEachInstruction( void MaybeAddTransformationBeforeEachInstruction(
std::function<uint32_t( std::function<
const opt::Function& function, opt::BasicBlock* block, void(const opt::Function& function, opt::BasicBlock* block,
opt::BasicBlock::iterator inst_it, uint32_t base, uint32_t offset)> opt::BasicBlock::iterator inst_it,
const protobufs::InstructionDescriptor& instruction_descriptor)>
maybe_apply_transformation); maybe_apply_transformation);
private: private:

View File

@ -43,21 +43,22 @@ void FuzzerPassConstructComposites::Apply() {
} }
MaybeAddTransformationBeforeEachInstruction( MaybeAddTransformationBeforeEachInstruction(
[this, &composite_type_ids](const opt::Function& function, [this, &composite_type_ids](
opt::BasicBlock* block, const opt::Function& function, opt::BasicBlock* block,
opt::BasicBlock::iterator inst_it, opt::BasicBlock::iterator inst_it,
uint32_t base, uint32_t offset) -> uint32_t { const protobufs::InstructionDescriptor& instruction_descriptor)
-> void {
// Check whether it is legitimate to insert a composite construction // Check whether it is legitimate to insert a composite construction
// before the instruction. // before the instruction.
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
SpvOpCompositeConstruct, inst_it)) { SpvOpCompositeConstruct, inst_it)) {
return 0; return;
} }
// Randomly decide whether to try inserting an object copy here. // Randomly decide whether to try inserting an object copy here.
if (!GetFuzzerContext()->ChoosePercentage( if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfConstructingComposite())) { GetFuzzerContext()->GetChanceOfConstructingComposite())) {
return 0; return;
} }
// For each instruction that is available at this program point (i.e. an // For each instruction that is available at this program point (i.e. an
@ -134,21 +135,20 @@ void FuzzerPassConstructComposites::Apply() {
// We did not manage to make a composite; return 0 to indicate that no // We did not manage to make a composite; return 0 to indicate that no
// instructions were added. // instructions were added.
assert(constructor_arguments == nullptr); assert(constructor_arguments == nullptr);
return 0; return;
} }
assert(constructor_arguments != nullptr); assert(constructor_arguments != nullptr);
// Make and apply a transformation. // Make and apply a transformation.
TransformationConstructComposite transformation( TransformationConstructComposite transformation(
chosen_composite_type, *constructor_arguments, base, offset, chosen_composite_type, *constructor_arguments,
GetFuzzerContext()->GetFreshId()); instruction_descriptor, GetFuzzerContext()->GetFreshId());
assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) && assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
"This transformation should be applicable by construction."); "This transformation should be applicable by construction.");
transformation.Apply(GetIRContext(), GetFactManager()); transformation.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() = *GetTransformations()->add_transformation() =
transformation.ToMessage(); transformation.ToMessage();
// Indicate that one instruction was added. // Indicate that one instruction was added.
return 1;
}); });
} }

View File

@ -31,19 +31,25 @@ FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default;
void FuzzerPassCopyObjects::Apply() { void FuzzerPassCopyObjects::Apply() {
MaybeAddTransformationBeforeEachInstruction( MaybeAddTransformationBeforeEachInstruction(
[this](const opt::Function& function, opt::BasicBlock* block, [this](const opt::Function& function, opt::BasicBlock* block,
opt::BasicBlock::iterator inst_it, uint32_t base, opt::BasicBlock::iterator inst_it,
uint32_t offset) -> uint32_t { const protobufs::InstructionDescriptor& instruction_descriptor)
-> void {
assert(inst_it->opcode() ==
instruction_descriptor.target_instruction_opcode() &&
"The opcode of the instruction we might insert before must be "
"the same as the opcode in the descriptor for the instruction");
// Check whether it is legitimate to insert a copy before this // Check whether it is legitimate to insert a copy before this
// instruction. // instruction.
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject, if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject,
inst_it)) { inst_it)) {
return 0; return;
} }
// Randomly decide whether to try inserting an object copy here. // Randomly decide whether to try inserting an object copy here.
if (!GetFuzzerContext()->ChoosePercentage( if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfCopyingObject())) { GetFuzzerContext()->GetChanceOfCopyingObject())) {
return 0; return;
} }
std::vector<opt::Instruction*> relevant_instructions = std::vector<opt::Instruction*> relevant_instructions =
@ -53,21 +59,20 @@ void FuzzerPassCopyObjects::Apply() {
// At this point, |relevant_instructions| contains all the instructions // At this point, |relevant_instructions| contains all the instructions
// we might think of copying. // we might think of copying.
if (relevant_instructions.empty()) { if (relevant_instructions.empty()) {
return 0; return;
} }
// Choose a copyable instruction at random, and create and apply an // Choose a copyable instruction at random, and create and apply an
// object copying transformation based on it. // object copying transformation based on it.
uint32_t index = GetFuzzerContext()->RandomIndex(relevant_instructions); uint32_t index = GetFuzzerContext()->RandomIndex(relevant_instructions);
TransformationCopyObject transformation( TransformationCopyObject transformation(
relevant_instructions[index]->result_id(), base, offset, relevant_instructions[index]->result_id(), instruction_descriptor,
GetFuzzerContext()->GetFreshId()); GetFuzzerContext()->GetFreshId());
assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) && assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
"This transformation should be applicable by construction."); "This transformation should be applicable by construction.");
transformation.Apply(GetIRContext(), GetFactManager()); transformation.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() = *GetTransformations()->add_transformation() =
transformation.ToMessage(); transformation.ToMessage();
return 1;
}); });
} }

View File

@ -180,41 +180,6 @@ opt::BasicBlock::iterator GetIteratorForInstruction(
return block->end(); return block->end();
} }
opt::BasicBlock::iterator GetIteratorForBaseInstructionAndOffset(
opt::BasicBlock* block, const opt::Instruction* base_inst,
uint32_t offset) {
// The cases where |base_inst| is the block's label, vs. inside the block,
// are dealt with separately.
if (base_inst == block->GetLabelInst()) {
// |base_inst| is the block's label.
if (offset == 0) {
// We cannot return an iterator to the block's label.
return block->end();
}
// Conceptually, the first instruction in the block is [label + 1].
// We thus start from 1 when applying the offset.
auto inst_it = block->begin();
for (uint32_t i = 1; i < offset && inst_it != block->end(); i++) {
++inst_it;
}
// This is either the desired instruction, or the end of the block.
return inst_it;
}
// |base_inst| is inside the block.
for (auto inst_it = block->begin(); inst_it != block->end(); ++inst_it) {
if (base_inst == &*inst_it) {
// We have found the base instruction; we now apply the offset.
for (uint32_t i = 0; i < offset && inst_it != block->end(); i++) {
++inst_it;
}
// This is either the desired instruction, or the end of the block.
return inst_it;
}
}
assert(false && "The base instruction was not found.");
return nullptr;
}
bool NewEdgeRespectsUseDefDominance(opt::IRContext* context, bool NewEdgeRespectsUseDefDominance(opt::IRContext* context,
opt::BasicBlock* bb_from, opt::BasicBlock* bb_from,
opt::BasicBlock* bb_to) { opt::BasicBlock* bb_to) {

View File

@ -69,16 +69,6 @@ bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
opt::BasicBlock::iterator GetIteratorForInstruction( opt::BasicBlock::iterator GetIteratorForInstruction(
opt::BasicBlock* block, const opt::Instruction* inst); opt::BasicBlock* block, const opt::Instruction* inst);
// Requires that |base_inst| is either the label instruction of |block| or an
// instruction inside |block|.
//
// If the block contains a (non-label, non-terminator) instruction |offset|
// instructions after |base_inst|, an iterator to this instruction is returned.
//
// Otherwise |block|->end() is returned.
opt::BasicBlock::iterator GetIteratorForBaseInstructionAndOffset(
opt::BasicBlock* block, const opt::Instruction* base_inst, uint32_t offset);
// The function determines whether adding an edge from |bb_from| to |bb_to| - // The function determines whether adding an edge from |bb_from| to |bb_to| -
// is legitimate with respect to the SPIR-V rule that a definition must // is legitimate with respect to the SPIR-V rule that a definition must
// dominate all of its uses. This is because adding such an edge can change // dominate all of its uses. This is because adding such an edge can change

View File

@ -331,15 +331,12 @@ message TransformationCopyObject {
// Id of the object to be copied // Id of the object to be copied
uint32 object = 1; uint32 object = 1;
// The id of an instruction in a block // A descriptor for an instruction in a block before which the new
uint32 base_instruction_id = 2; // OpCopyObject instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 2;
// An offset, such that OpCopyObject instruction should be inserted right
// before the instruction |offset| instructions after |base_instruction_id|
uint32 offset = 3;
// A fresh id for the copied object // A fresh id for the copied object
uint32 fresh_id = 4; uint32 fresh_id = 3;
} }
@ -354,16 +351,12 @@ message TransformationConstructComposite {
// Ids of the objects that will form the components of the composite // Ids of the objects that will form the components of the composite
repeated uint32 component = 2; repeated uint32 component = 2;
// The id of an instruction in a block // A descriptor for an instruction in a block before which the new
uint32 base_instruction_id = 3; // OpCompositeConstruct instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 3;
// An offset, such that OpCompositeConstruct instruction should be inserted
// right before the instruction |offset| instructions after
// |base_instruction_id|
uint32 offset = 4;
// A fresh id for the composite object // A fresh id for the composite object
uint32 fresh_id = 5; uint32 fresh_id = 4;
} }

View File

@ -15,6 +15,8 @@
#include "source/fuzz/transformation_construct_composite.h" #include "source/fuzz/transformation_construct_composite.h"
#include "source/fuzz/fuzzer_util.h" #include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/opt/instruction.h"
namespace spvtools { namespace spvtools {
namespace fuzz { namespace fuzz {
@ -25,13 +27,14 @@ TransformationConstructComposite::TransformationConstructComposite(
TransformationConstructComposite::TransformationConstructComposite( TransformationConstructComposite::TransformationConstructComposite(
uint32_t composite_type_id, std::vector<uint32_t> component, uint32_t composite_type_id, std::vector<uint32_t> component,
uint32_t base_instruction_id, uint32_t offset, uint32_t fresh_id) { const protobufs::InstructionDescriptor& instruction_to_insert_before,
uint32_t fresh_id) {
message_.set_composite_type_id(composite_type_id); message_.set_composite_type_id(composite_type_id);
for (auto a_component : component) { for (auto a_component : component) {
message_.add_component(a_component); message_.add_component(a_component);
} }
message_.set_base_instruction_id(base_instruction_id); *message_.mutable_instruction_to_insert_before() =
message_.set_offset(offset); instruction_to_insert_before;
message_.set_fresh_id(fresh_id); message_.set_fresh_id(fresh_id);
} }
@ -42,24 +45,11 @@ bool TransformationConstructComposite::IsApplicable(
return false; return false;
} }
auto base_instruction = auto insert_before =
context->get_def_use_mgr()->GetDef(message_.base_instruction_id()); FindInstruction(message_.instruction_to_insert_before(), context);
if (!base_instruction) { if (!insert_before) {
// The given id to insert after is not defined. // The instruction before which the composite should be inserted was not
return false; // found.
}
auto destination_block = context->get_instr_block(base_instruction);
if (!destination_block) {
// The given id to insert after is not in a block.
return false;
}
auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
destination_block, base_instruction, message_.offset());
if (insert_before == destination_block->end()) {
// The offset was inappropriate.
return false; return false;
} }
@ -125,11 +115,11 @@ void TransformationConstructComposite::Apply(opt::IRContext* context,
FactManager* fact_manager) const { FactManager* fact_manager) const {
// Use the base and offset information from the transformation to determine // Use the base and offset information from the transformation to determine
// where in the module a new instruction should be inserted. // where in the module a new instruction should be inserted.
auto base_instruction = auto insert_before_inst =
context->get_def_use_mgr()->GetDef(message_.base_instruction_id()); FindInstruction(message_.instruction_to_insert_before(), context);
auto destination_block = context->get_instr_block(base_instruction); auto destination_block = context->get_instr_block(insert_before_inst);
auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset( auto insert_before = fuzzerutil::GetIteratorForInstruction(
destination_block, base_instruction, message_.offset()); destination_block, insert_before_inst);
// Prepare the input operands for an OpCompositeConstruct instruction. // Prepare the input operands for an OpCompositeConstruct instruction.
opt::Instruction::OperandList in_operands; opt::Instruction::OperandList in_operands;

View File

@ -28,10 +28,10 @@ class TransformationConstructComposite : public Transformation {
explicit TransformationConstructComposite( explicit TransformationConstructComposite(
const protobufs::TransformationConstructComposite& message); const protobufs::TransformationConstructComposite& message);
TransformationConstructComposite(uint32_t composite_type_id, TransformationConstructComposite(
std::vector<uint32_t> component, uint32_t composite_type_id, std::vector<uint32_t> component,
uint32_t base_instruction_id, const protobufs::InstructionDescriptor& instruction_to_insert_before,
uint32_t offset, uint32_t fresh_id); uint32_t fresh_id);
// - |message_.fresh_id| must not be used by the module. // - |message_.fresh_id| must not be used by the module.
// - |message_.composite_type_id| must be the id of a composite type // - |message_.composite_type_id| must be the id of a composite type

View File

@ -15,6 +15,7 @@
#include "source/fuzz/transformation_copy_object.h" #include "source/fuzz/transformation_copy_object.h"
#include "source/fuzz/fuzzer_util.h" #include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/opt/instruction.h" #include "source/opt/instruction.h"
#include "source/util/make_unique.h" #include "source/util/make_unique.h"
@ -25,13 +26,13 @@ TransformationCopyObject::TransformationCopyObject(
const protobufs::TransformationCopyObject& message) const protobufs::TransformationCopyObject& message)
: message_(message) {} : message_(message) {}
TransformationCopyObject::TransformationCopyObject(uint32_t object, TransformationCopyObject::TransformationCopyObject(
uint32_t base_instruction_id, uint32_t object,
uint32_t offset, const protobufs::InstructionDescriptor& instruction_to_insert_before,
uint32_t fresh_id) { uint32_t fresh_id) {
message_.set_object(object); message_.set_object(object);
message_.set_base_instruction_id(base_instruction_id); *message_.mutable_instruction_to_insert_before() =
message_.set_offset(offset); instruction_to_insert_before;
message_.set_fresh_id(fresh_id); message_.set_fresh_id(fresh_id);
} }
@ -50,24 +51,10 @@ bool TransformationCopyObject::IsApplicable(
return false; return false;
} }
auto base_instruction = auto insert_before =
context->get_def_use_mgr()->GetDef(message_.base_instruction_id()); FindInstruction(message_.instruction_to_insert_before(), context);
if (!base_instruction) { if (!insert_before) {
// The given id to insert after is not defined. // The instruction before which the copy should be inserted was not found.
return false;
}
auto destination_block = context->get_instr_block(base_instruction);
if (!destination_block) {
// The given id to insert after is not in a block.
return false;
}
auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
destination_block, base_instruction, message_.offset());
if (insert_before == destination_block->end()) {
// The offset was inappropriate.
return false; return false;
} }
@ -86,7 +73,9 @@ bool TransformationCopyObject::IsApplicable(
// insert it before the object's defining instruction. // insert it before the object's defining instruction.
return !context->get_instr_block(object_inst) || return !context->get_instr_block(object_inst) ||
(object_inst != &*insert_before && (object_inst != &*insert_before &&
context->GetDominatorAnalysis(destination_block->GetParent()) context
->GetDominatorAnalysis(
context->get_instr_block(insert_before)->GetParent())
->Dominates(object_inst, &*insert_before)); ->Dominates(object_inst, &*insert_before));
} }
@ -94,13 +83,12 @@ void TransformationCopyObject::Apply(opt::IRContext* context,
FactManager* fact_manager) const { FactManager* fact_manager) const {
auto object_inst = context->get_def_use_mgr()->GetDef(message_.object()); auto object_inst = context->get_def_use_mgr()->GetDef(message_.object());
assert(object_inst && "The object to be copied must exist."); assert(object_inst && "The object to be copied must exist.");
auto base_instruction = auto insert_before_inst =
context->get_def_use_mgr()->GetDef(message_.base_instruction_id()); FindInstruction(message_.instruction_to_insert_before(), context);
assert(base_instruction && "The base instruction must exist."); auto destination_block = context->get_instr_block(insert_before_inst);
auto destination_block = context->get_instr_block(base_instruction);
assert(destination_block && "The base instruction must be in a block."); assert(destination_block && "The base instruction must be in a block.");
auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset( auto insert_before = fuzzerutil::GetIteratorForInstruction(
destination_block, base_instruction, message_.offset()); destination_block, insert_before_inst);
assert(insert_before != destination_block->end() && assert(insert_before != destination_block->end() &&
"There must be an instruction before which the copy can be inserted."); "There must be an instruction before which the copy can be inserted.");

View File

@ -28,8 +28,10 @@ class TransformationCopyObject : public Transformation {
explicit TransformationCopyObject( explicit TransformationCopyObject(
const protobufs::TransformationCopyObject& message); const protobufs::TransformationCopyObject& message);
TransformationCopyObject(uint32_t object, uint32_t base_instruction_id, TransformationCopyObject(
uint32_t offset, uint32_t fresh_id); uint32_t object,
const protobufs::InstructionDescriptor& instruction_to_insert_before,
uint32_t fresh_id);
// - |message_.fresh_id| must not be used by the module. // - |message_.fresh_id| must not be used by the module.
// - |message_.object| must be a result id that is a legitimate operand for // - |message_.object| must be a result id that is a legitimate operand for

View File

@ -14,6 +14,7 @@
#include "source/fuzz/transformation_construct_composite.h" #include "source/fuzz/transformation_construct_composite.h"
#include "source/fuzz/data_descriptor.h" #include "source/fuzz/data_descriptor.h"
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h" #include "test/fuzz/fuzz_test_util.h"
namespace spvtools { namespace spvtools {
@ -144,11 +145,13 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
FactManager fact_manager; FactManager fact_manager;
// Make a vec2[3] // Make a vec2[3]
TransformationConstructComposite make_vec2_array_length_3(37, {41, 45, 27}, TransformationConstructComposite make_vec2_array_length_3(
46, 0, 200); 37, {41, 45, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0),
200);
// Bad: there are too many components // Bad: there are too many components
TransformationConstructComposite make_vec2_array_length_3_bad( TransformationConstructComposite make_vec2_array_length_3_bad(
37, {41, 45, 27, 27}, 46, 0, 200); 37, {41, 45, 27, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0),
200);
ASSERT_TRUE( ASSERT_TRUE(
make_vec2_array_length_3.IsApplicable(context.get(), fact_manager)); make_vec2_array_length_3.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE( ASSERT_FALSE(
@ -160,11 +163,11 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 27, 200, {2})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 27, 200, {2}));
// Make a float[2] // Make a float[2]
TransformationConstructComposite make_float_array_length_2(9, {24, 40}, 71, 1, TransformationConstructComposite make_float_array_length_2(
201); 9, {24, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
// Bad: %41 does not have type float // Bad: %41 does not have type float
TransformationConstructComposite make_float_array_length_2_bad(9, {41, 40}, TransformationConstructComposite make_float_array_length_2_bad(
71, 1, 201); 9, {41, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
ASSERT_TRUE( ASSERT_TRUE(
make_float_array_length_2.IsApplicable(context.get(), fact_manager)); make_float_array_length_2.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE( ASSERT_FALSE(
@ -175,11 +178,13 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 40, 201, {1})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 40, 201, {1}));
// Make a bool[3] // Make a bool[3]
TransformationConstructComposite make_bool_array_length_3(47, {33, 50, 50}, TransformationConstructComposite make_bool_array_length_3(
33, 1, 202); 47, {33, 50, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0),
202);
// Bad: %54 is not available at the desired program point. // Bad: %54 is not available at the desired program point.
TransformationConstructComposite make_bool_array_length_3_bad( TransformationConstructComposite make_bool_array_length_3_bad(
47, {33, 54, 50}, 33, 1, 202); 47, {33, 54, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0),
202);
ASSERT_TRUE( ASSERT_TRUE(
make_bool_array_length_3.IsApplicable(context.get(), fact_manager)); make_bool_array_length_3.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE( ASSERT_FALSE(
@ -191,11 +196,11 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 50, 202, {2})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 50, 202, {2}));
// make a uvec3[2][2] // make a uvec3[2][2]
TransformationConstructComposite make_uvec3_array_length_2_2(58, {69, 100}, TransformationConstructComposite make_uvec3_array_length_2_2(
64, 1, 203); 58, {69, 100}, MakeInstructionDescriptor(64, SpvOpStore, 0), 203);
// Bad: Offset 100 is too large. // Bad: Skip count 100 is too large.
TransformationConstructComposite make_uvec3_array_length_2_2_bad( TransformationConstructComposite make_uvec3_array_length_2_2_bad(
58, {33, 54}, 64, 100, 203); 58, {33, 54}, MakeInstructionDescriptor(64, SpvOpStore, 100), 203);
ASSERT_TRUE( ASSERT_TRUE(
make_uvec3_array_length_2_2.IsApplicable(context.get(), fact_manager)); make_uvec3_array_length_2_2.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable(context.get(), ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable(context.get(),
@ -393,9 +398,11 @@ TEST(TransformationConstructCompositeTest, ConstructMatrices) {
FactManager fact_manager; FactManager fact_manager;
// make a mat3x4 // make a mat3x4
TransformationConstructComposite make_mat34(32, {25, 28, 31}, 31, 2, 200); TransformationConstructComposite make_mat34(
32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
// Bad: %35 is mat4x3, not mat3x4. // Bad: %35 is mat4x3, not mat3x4.
TransformationConstructComposite make_mat34_bad(35, {25, 28, 31}, 31, 2, 200); TransformationConstructComposite make_mat34_bad(
35, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
ASSERT_TRUE(make_mat34.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_mat34.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_mat34_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_mat34_bad.IsApplicable(context.get(), fact_manager));
make_mat34.Apply(context.get(), &fact_manager); make_mat34.Apply(context.get(), &fact_manager);
@ -405,11 +412,11 @@ TEST(TransformationConstructCompositeTest, ConstructMatrices) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 31, 200, {2})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 31, 200, {2}));
// make a mat4x3 // make a mat4x3
TransformationConstructComposite make_mat43(35, {11, 13, 16, 100}, 31, 1, TransformationConstructComposite make_mat43(
201); 35, {11, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201);
// Bad: %25 does not match the matrix's column type. // Bad: %25 does not match the matrix's column type.
TransformationConstructComposite make_mat43_bad(35, {25, 13, 16, 100}, 31, 1, TransformationConstructComposite make_mat43_bad(
201); 35, {25, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201);
ASSERT_TRUE(make_mat43.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_mat43.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_mat43_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_mat43_bad.IsApplicable(context.get(), fact_manager));
make_mat43.Apply(context.get(), &fact_manager); make_mat43.Apply(context.get(), &fact_manager);
@ -592,9 +599,11 @@ TEST(TransformationConstructCompositeTest, ConstructStructs) {
FactManager fact_manager; FactManager fact_manager;
// make an Inner // make an Inner
TransformationConstructComposite make_inner(9, {25, 19}, 57, 0, 200); TransformationConstructComposite make_inner(
9, {25, 19}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
// Bad: Too few fields to make the struct. // Bad: Too few fields to make the struct.
TransformationConstructComposite make_inner_bad(9, {25}, 57, 0, 200); TransformationConstructComposite make_inner_bad(
9, {25}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
ASSERT_TRUE(make_inner.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_inner.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_inner_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_inner_bad.IsApplicable(context.get(), fact_manager));
make_inner.Apply(context.get(), &fact_manager); make_inner.Apply(context.get(), &fact_manager);
@ -603,10 +612,13 @@ TEST(TransformationConstructCompositeTest, ConstructStructs) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 19, 200, {1})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 19, 200, {1}));
// make an Outer // make an Outer
TransformationConstructComposite make_outer(33, {46, 200, 56}, 200, 1, 201); TransformationConstructComposite make_outer(
33, {46, 200, 56}, MakeInstructionDescriptor(200, SpvOpAccessChain, 0),
201);
// Bad: %200 is not available at the desired program point. // Bad: %200 is not available at the desired program point.
TransformationConstructComposite make_outer_bad(33, {46, 200, 56}, 200, 0, TransformationConstructComposite make_outer_bad(
201); 33, {46, 200, 56},
MakeInstructionDescriptor(200, SpvOpCompositeConstruct, 0), 201);
ASSERT_TRUE(make_outer.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_outer.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_outer_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_outer_bad.IsApplicable(context.get(), fact_manager));
make_outer.Apply(context.get(), &fact_manager); make_outer.Apply(context.get(), &fact_manager);
@ -900,9 +912,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
FactManager fact_manager; FactManager fact_manager;
TransformationConstructComposite make_vec2(7, {17, 11}, 100, 1, 200); TransformationConstructComposite make_vec2(
7, {17, 11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
// Bad: not enough data for a vec2 // Bad: not enough data for a vec2
TransformationConstructComposite make_vec2_bad(7, {11}, 100, 1, 200); TransformationConstructComposite make_vec2_bad(
7, {11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
ASSERT_TRUE(make_vec2.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_vec2.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_vec2_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_vec2_bad.IsApplicable(context.get(), fact_manager));
make_vec2.Apply(context.get(), &fact_manager); make_vec2.Apply(context.get(), &fact_manager);
@ -910,9 +924,13 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 17, 200, {0})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 17, 200, {0}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 11, 200, {1})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 11, 200, {1}));
TransformationConstructComposite make_vec3(25, {12, 32}, 35, 0, 201); TransformationConstructComposite make_vec3(
25, {12, 32}, MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0),
201);
// Bad: too much data for a vec3 // Bad: too much data for a vec3
TransformationConstructComposite make_vec3_bad(25, {12, 32, 32}, 35, 0, 201); TransformationConstructComposite make_vec3_bad(
25, {12, 32, 32},
MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0), 201);
ASSERT_TRUE(make_vec3.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_vec3.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_vec3_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_vec3_bad.IsApplicable(context.get(), fact_manager));
make_vec3.Apply(context.get(), &fact_manager); make_vec3.Apply(context.get(), &fact_manager);
@ -920,10 +938,13 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 12, 201, {0})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 12, 201, {0}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 32, 201, {2})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 32, 201, {2}));
TransformationConstructComposite make_vec4(44, {32, 32, 10, 11}, 75, 0, 202); TransformationConstructComposite make_vec4(
44, {32, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0),
202);
// Bad: id 48 is not available at the insertion points // Bad: id 48 is not available at the insertion points
TransformationConstructComposite make_vec4_bad(44, {48, 32, 10, 11}, 75, 0, TransformationConstructComposite make_vec4_bad(
202); 44, {48, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0),
202);
ASSERT_TRUE(make_vec4.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_vec4.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_vec4_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_vec4_bad.IsApplicable(context.get(), fact_manager));
make_vec4.Apply(context.get(), &fact_manager); make_vec4.Apply(context.get(), &fact_manager);
@ -933,9 +954,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 10, 202, {2})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 10, 202, {2}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 11, 202, {3})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 11, 202, {3}));
TransformationConstructComposite make_ivec2(51, {126, 120}, 128, 0, 203); TransformationConstructComposite make_ivec2(
51, {126, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203);
// Bad: if 128 is not available at the instruction that defines 128 // Bad: if 128 is not available at the instruction that defines 128
TransformationConstructComposite make_ivec2_bad(51, {128, 120}, 128, 0, 203); TransformationConstructComposite make_ivec2_bad(
51, {128, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203);
ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_ivec2_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_ivec2_bad.IsApplicable(context.get(), fact_manager));
make_ivec2.Apply(context.get(), &fact_manager); make_ivec2.Apply(context.get(), &fact_manager);
@ -943,10 +966,13 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 126, 203, {0})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 126, 203, {0}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 120, 203, {1})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 120, 203, {1}));
TransformationConstructComposite make_ivec3(114, {56, 117, 56}, 66, 1, 204); TransformationConstructComposite make_ivec3(
114, {56, 117, 56}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
204);
// Bad because 1300 is not an id // Bad because 1300 is not an id
TransformationConstructComposite make_ivec3_bad(114, {56, 117, 1300}, 66, 1, TransformationConstructComposite make_ivec3_bad(
204); 114, {56, 117, 1300}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
204);
ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_ivec3_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_ivec3_bad.IsApplicable(context.get(), fact_manager));
make_ivec3.Apply(context.get(), &fact_manager); make_ivec3.Apply(context.get(), &fact_manager);
@ -955,11 +981,13 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 204, {1})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 204, {1}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 56, 204, {2})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 56, 204, {2}));
TransformationConstructComposite make_ivec4(122, {56, 117, 117, 117}, 66, 0, TransformationConstructComposite make_ivec4(
205); 122, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
205);
// Bad because 86 is the wrong type. // Bad because 86 is the wrong type.
TransformationConstructComposite make_ivec4_bad(86, {56, 117, 117, 117}, 66, TransformationConstructComposite make_ivec4_bad(
0, 205); 86, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
205);
ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_ivec4_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_ivec4_bad.IsApplicable(context.get(), fact_manager));
make_ivec4.Apply(context.get(), &fact_manager); make_ivec4.Apply(context.get(), &fact_manager);
@ -969,8 +997,10 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 205, {2})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 205, {2}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 205, {3})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 205, {3}));
TransformationConstructComposite make_uvec2(86, {18, 38}, 133, 2, 206); TransformationConstructComposite make_uvec2(
TransformationConstructComposite make_uvec2_bad(86, {18, 38}, 133, 200, 206); 86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 0), 206);
TransformationConstructComposite make_uvec2_bad(
86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 200), 206);
ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_uvec2_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_uvec2_bad.IsApplicable(context.get(), fact_manager));
make_uvec2.Apply(context.get(), &fact_manager); make_uvec2.Apply(context.get(), &fact_manager);
@ -978,10 +1008,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 18, 206, {0})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 18, 206, {0}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 38, 206, {1})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 38, 206, {1}));
TransformationConstructComposite make_uvec3(59, {14, 18, 136}, 137, 2, 207); TransformationConstructComposite make_uvec3(
59, {14, 18, 136}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
// Bad because 1300 is not an id // Bad because 1300 is not an id
TransformationConstructComposite make_uvec3_bad(59, {14, 18, 1300}, 137, 2, TransformationConstructComposite make_uvec3_bad(
207); 59, {14, 18, 1300}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_uvec3_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_uvec3_bad.IsApplicable(context.get(), fact_manager));
make_uvec3.Apply(context.get(), &fact_manager); make_uvec3.Apply(context.get(), &fact_manager);
@ -990,11 +1021,13 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 18, 207, {1})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 18, 207, {1}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 207, {2})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 207, {2}));
TransformationConstructComposite make_uvec4(131, {14, 18, 136, 136}, 137, 0, TransformationConstructComposite make_uvec4(
208); 131, {14, 18, 136, 136},
MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
// Bad because 86 is the wrong type. // Bad because 86 is the wrong type.
TransformationConstructComposite make_uvec4_bad(86, {14, 18, 136, 136}, 137, TransformationConstructComposite make_uvec4_bad(
0, 208); 86, {14, 18, 136, 136},
MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_uvec4_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_uvec4_bad.IsApplicable(context.get(), fact_manager));
make_uvec4.Apply(context.get(), &fact_manager); make_uvec4.Apply(context.get(), &fact_manager);
@ -1004,19 +1037,21 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 208, {2})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 208, {2}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 208, {3})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 208, {3}));
TransformationConstructComposite make_bvec2(102, TransformationConstructComposite make_bvec2(
{ 102,
111, {
41, 111,
}, 41,
75, 0, 209); },
MakeInstructionDescriptor(75, SpvOpAccessChain, 0), 209);
// Bad because 0 is not a valid base instruction id // Bad because 0 is not a valid base instruction id
TransformationConstructComposite make_bvec2_bad(102, TransformationConstructComposite make_bvec2_bad(
{ 102,
111, {
41, 111,
}, 41,
0, 0, 209); },
MakeInstructionDescriptor(0, SpvOpExtInstImport, 0), 209);
ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_bvec2_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_bvec2_bad.IsApplicable(context.get(), fact_manager));
make_bvec2.Apply(context.get(), &fact_manager); make_bvec2.Apply(context.get(), &fact_manager);
@ -1024,9 +1059,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 111, 209, {0})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 111, 209, {0}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 41, 209, {1})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 41, 209, {1}));
TransformationConstructComposite make_bvec3(93, {108, 73}, 108, 1, 210); TransformationConstructComposite make_bvec3(
93, {108, 73}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
// Bad because there are too many components for a bvec3 // Bad because there are too many components for a bvec3
TransformationConstructComposite make_bvec3_bad(93, {108, 108}, 108, 1, 210); TransformationConstructComposite make_bvec3_bad(
93, {108, 108}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_bvec3_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_bvec3_bad.IsApplicable(context.get(), fact_manager));
make_bvec3.Apply(context.get(), &fact_manager); make_bvec3.Apply(context.get(), &fact_manager);
@ -1034,9 +1071,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 108, 210, {0})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 108, 210, {0}));
ASSERT_TRUE(SynonymFactHolds(fact_manager, 73, 210, {2})); ASSERT_TRUE(SynonymFactHolds(fact_manager, 73, 210, {2}));
TransformationConstructComposite make_bvec4(70, {108, 108}, 108, 3, 211); TransformationConstructComposite make_bvec4(
70, {108, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
// Bad because 21 is a type, not a result id // Bad because 21 is a type, not a result id
TransformationConstructComposite make_bvec4_bad(70, {21, 108}, 108, 3, 211); TransformationConstructComposite make_bvec4_bad(
70, {21, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_bvec4_bad.IsApplicable(context.get(), fact_manager)); ASSERT_FALSE(make_bvec4_bad.IsApplicable(context.get(), fact_manager));
make_bvec4.Apply(context.get(), &fact_manager); make_bvec4.Apply(context.get(), &fact_manager);

View File

@ -14,6 +14,7 @@
#include "source/fuzz/transformation_copy_object.h" #include "source/fuzz/transformation_copy_object.h"
#include "source/fuzz/data_descriptor.h" #include "source/fuzz/data_descriptor.h"
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h" #include "test/fuzz/fuzz_test_util.h"
namespace spvtools { namespace spvtools {
@ -49,7 +50,8 @@ TEST(TransformationCopyObjectTest, CopyBooleanConstants) {
ASSERT_EQ(0, fact_manager.GetIdsForWhichSynonymsAreKnown().size()); ASSERT_EQ(0, fact_manager.GetIdsForWhichSynonymsAreKnown().size());
TransformationCopyObject copy_true(7, 5, 1, 100); TransformationCopyObject copy_true(
7, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100);
ASSERT_TRUE(copy_true.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(copy_true.IsApplicable(context.get(), fact_manager));
copy_true.Apply(context.get(), &fact_manager); copy_true.Apply(context.get(), &fact_manager);
@ -63,7 +65,8 @@ TEST(TransformationCopyObjectTest, CopyBooleanConstants) {
ASSERT_TRUE(DataDescriptorEquals()(&descriptor_100, ASSERT_TRUE(DataDescriptorEquals()(&descriptor_100,
&fact_manager.GetSynonymsForId(7)[0])); &fact_manager.GetSynonymsForId(7)[0]));
TransformationCopyObject copy_false(8, 100, 1, 101); TransformationCopyObject copy_false(
8, MakeInstructionDescriptor(100, SpvOpReturn, 0), 101);
ASSERT_TRUE(copy_false.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(copy_false.IsApplicable(context.get(), fact_manager));
copy_false.Apply(context.get(), &fact_manager); copy_false.Apply(context.get(), &fact_manager);
ASSERT_EQ(2, ids_for_which_synonyms_are_known.size()); ASSERT_EQ(2, ids_for_which_synonyms_are_known.size());
@ -74,7 +77,8 @@ TEST(TransformationCopyObjectTest, CopyBooleanConstants) {
ASSERT_TRUE(DataDescriptorEquals()(&descriptor_101, ASSERT_TRUE(DataDescriptorEquals()(&descriptor_101,
&fact_manager.GetSynonymsForId(8)[0])); &fact_manager.GetSynonymsForId(8)[0]));
TransformationCopyObject copy_false_again(101, 5, 3, 102); TransformationCopyObject copy_false_again(
101, MakeInstructionDescriptor(5, SpvOpReturn, 0), 102);
ASSERT_TRUE(copy_false_again.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(copy_false_again.IsApplicable(context.get(), fact_manager));
copy_false_again.Apply(context.get(), &fact_manager); copy_false_again.Apply(context.get(), &fact_manager);
ASSERT_EQ(3, ids_for_which_synonyms_are_known.size()); ASSERT_EQ(3, ids_for_which_synonyms_are_known.size());
@ -85,7 +89,8 @@ TEST(TransformationCopyObjectTest, CopyBooleanConstants) {
ASSERT_TRUE(DataDescriptorEquals()(&descriptor_102, ASSERT_TRUE(DataDescriptorEquals()(&descriptor_102,
&fact_manager.GetSynonymsForId(101)[0])); &fact_manager.GetSynonymsForId(101)[0]));
TransformationCopyObject copy_true_again(7, 102, 1, 103); TransformationCopyObject copy_true_again(
7, MakeInstructionDescriptor(102, SpvOpReturn, 0), 103);
ASSERT_TRUE(copy_true_again.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(copy_true_again.IsApplicable(context.get(), fact_manager));
copy_true_again.Apply(context.get(), &fact_manager); copy_true_again.Apply(context.get(), &fact_manager);
// This does re-uses an id for which synonyms are already known, so the count // This does re-uses an id for which synonyms are already known, so the count
@ -318,90 +323,113 @@ TEST(TransformationCopyObjectTest, CheckIllegalCases) {
FactManager fact_manager; FactManager fact_manager;
// Inapplicable because %18 is decorated. // Inapplicable because %18 is decorated.
ASSERT_FALSE(TransformationCopyObject(18, 21, 0, 200) ASSERT_FALSE(TransformationCopyObject(
18, MakeInstructionDescriptor(21, SpvOpAccessChain, 0), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
// Inapplicable because %77 is decorated. // Inapplicable because %77 is decorated.
ASSERT_FALSE(TransformationCopyObject(17, 17, 1, 200) ASSERT_FALSE(TransformationCopyObject(
77, MakeInstructionDescriptor(77, SpvOpBranch, 0), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
// Inapplicable because %80 is decorated. // Inapplicable because %80 is decorated.
ASSERT_FALSE(TransformationCopyObject(80, 77, 0, 200) ASSERT_FALSE(TransformationCopyObject(
80, MakeInstructionDescriptor(77, SpvOpIAdd, 0), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
// Inapplicable because %84 is not available at the requested point // Inapplicable because %84 is not available at the requested point
ASSERT_FALSE(TransformationCopyObject(84, 32, 1, 200) ASSERT_FALSE(
.IsApplicable(context.get(), fact_manager)); TransformationCopyObject(
84, MakeInstructionDescriptor(32, SpvOpCompositeExtract, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Fine because %84 is available at the requested point // Fine because %84 is available at the requested point
ASSERT_TRUE(TransformationCopyObject(84, 32, 2, 200) ASSERT_TRUE(
.IsApplicable(context.get(), fact_manager)); TransformationCopyObject(
84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because id %9 is already in use // Inapplicable because id %9 is already in use
ASSERT_FALSE(TransformationCopyObject(84, 32, 2, 9) ASSERT_FALSE(
.IsApplicable(context.get(), fact_manager)); TransformationCopyObject(
84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 9)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because the requested point is not in a block // Inapplicable because the requested point does not exist
ASSERT_FALSE(TransformationCopyObject(84, 86, 3, 200) ASSERT_FALSE(TransformationCopyObject(
84, MakeInstructionDescriptor(86, SpvOpReturn, 2), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
// Inapplicable because %9 is not in a function // Inapplicable because %9 is not in a function
ASSERT_FALSE(TransformationCopyObject(9, 9, 1, 200) ASSERT_FALSE(TransformationCopyObject(
.IsApplicable(context.get(), fact_manager)); 9, MakeInstructionDescriptor(9, SpvOpTypeInt, 0), 200)
// Inapplicable because %9 is not in a function
ASSERT_FALSE(TransformationCopyObject(9, 9, 1, 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
// Inapplicable because the insert point is right before, or inside, a chunk // Inapplicable because the insert point is right before, or inside, a chunk
// of OpPhis // of OpPhis
ASSERT_FALSE(TransformationCopyObject(9, 30, 1, 200) ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(30, SpvOpPhi, 0), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationCopyObject(9, 99, 1, 200) ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(99, SpvOpPhi, 1), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
// OK, because the insert point is just after a chunk of OpPhis. // OK, because the insert point is just after a chunk of OpPhis.
ASSERT_TRUE(TransformationCopyObject(9, 96, 1, 200) ASSERT_TRUE(TransformationCopyObject(
9, MakeInstructionDescriptor(96, SpvOpAccessChain, 0), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
// Inapplicable because the insert point is right after an OpSelectionMerge // Inapplicable because the insert point is right after an OpSelectionMerge
ASSERT_FALSE(TransformationCopyObject(9, 58, 2, 200) ASSERT_FALSE(
.IsApplicable(context.get(), fact_manager)); TransformationCopyObject(
9, MakeInstructionDescriptor(58, SpvOpBranchConditional, 0), 200)
.IsApplicable(context.get(), fact_manager));
// OK, because the insert point is right before the OpSelectionMerge // OK, because the insert point is right before the OpSelectionMerge
ASSERT_TRUE(TransformationCopyObject(9, 58, 1, 200) ASSERT_TRUE(TransformationCopyObject(
9, MakeInstructionDescriptor(58, SpvOpSelectionMerge, 0), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
// Inapplicable because the insert point is right after an OpSelectionMerge // Inapplicable because the insert point is right after an OpSelectionMerge
ASSERT_FALSE(TransformationCopyObject(9, 43, 2, 200) ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(43, SpvOpSwitch, 0), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
// OK, because the insert point is right before the OpSelectionMerge // OK, because the insert point is right before the OpSelectionMerge
ASSERT_TRUE(TransformationCopyObject(9, 43, 1, 200) ASSERT_TRUE(TransformationCopyObject(
9, MakeInstructionDescriptor(43, SpvOpSelectionMerge, 0), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
// Inapplicable because the insert point is right after an OpLoopMerge // Inapplicable because the insert point is right after an OpLoopMerge
ASSERT_FALSE(TransformationCopyObject(9, 40, 2, 200) ASSERT_FALSE(
.IsApplicable(context.get(), fact_manager)); TransformationCopyObject(
9, MakeInstructionDescriptor(40, SpvOpBranchConditional, 0), 200)
.IsApplicable(context.get(), fact_manager));
// OK, because the insert point is right before the OpLoopMerge // OK, because the insert point is right before the OpLoopMerge
ASSERT_TRUE(TransformationCopyObject(9, 40, 1, 200) ASSERT_TRUE(TransformationCopyObject(
9, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
// Inapplicable because id %300 does not exist // Inapplicable because id %300 does not exist
ASSERT_FALSE(TransformationCopyObject(300, 40, 1, 200) ASSERT_FALSE(TransformationCopyObject(
300, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
// Inapplicable because the following instruction is OpVariable // Inapplicable because the following instruction is OpVariable
ASSERT_FALSE(TransformationCopyObject(9, 180, 0, 200) ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(180, SpvOpVariable, 0), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationCopyObject(9, 181, 0, 200) ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(181, SpvOpVariable, 0), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationCopyObject(9, 182, 0, 200) ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(182, SpvOpVariable, 0), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
// OK, because this is just past the group of OpVariable instructions. // OK, because this is just past the group of OpVariable instructions.
ASSERT_TRUE(TransformationCopyObject(9, 182, 1, 200) ASSERT_TRUE(TransformationCopyObject(
9, MakeInstructionDescriptor(182, SpvOpAccessChain, 0), 200)
.IsApplicable(context.get(), fact_manager)); .IsApplicable(context.get(), fact_manager));
} }
@ -470,13 +498,20 @@ TEST(TransformationCopyObjectTest, MiscellaneousCopies) {
FactManager fact_manager; FactManager fact_manager;
std::vector<TransformationCopyObject> transformations = { std::vector<TransformationCopyObject> transformations = {
TransformationCopyObject(19, 22, 1, 100), TransformationCopyObject(19, MakeInstructionDescriptor(22, SpvOpStore, 0),
TransformationCopyObject(22, 22, 1, 101), 100),
TransformationCopyObject(12, 22, 1, 102), TransformationCopyObject(
TransformationCopyObject(11, 22, 1, 103), 22, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 101),
TransformationCopyObject(16, 22, 1, 104), TransformationCopyObject(
TransformationCopyObject(8, 22, 1, 105), 12, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 102),
TransformationCopyObject(17, 22, 1, 106)}; TransformationCopyObject(
11, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 103),
TransformationCopyObject(
16, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 104),
TransformationCopyObject(
8, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 105),
TransformationCopyObject(
17, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 106)};
for (auto& transformation : transformations) { for (auto& transformation : transformations) {
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));