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/instruction_descriptor.h"
namespace spvtools {
namespace fuzz {
@ -69,9 +71,10 @@ std::vector<opt::Instruction*> FuzzerPass::FindAvailableInstructions(
}
void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
std::function<uint32_t(
const opt::Function& function, opt::BasicBlock* block,
opt::BasicBlock::iterator inst_it, uint32_t base, uint32_t offset)>
std::function<
void(const opt::Function& function, opt::BasicBlock* block,
opt::BasicBlock::iterator inst_it,
const protobufs::InstructionDescriptor& instruction_descriptor)>
maybe_apply_transformation) {
// Consider every block in every function.
for (auto& function : *GetIRContext()->module()) {
@ -80,46 +83,45 @@ void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
// whether to apply a transformation before it.
// In order for transformations to insert new instructions, they need to
// be able to identify the instruction to insert before. We enable this
// by tracking a base instruction, which must generate a result id, and
// an offset (to allow us to identify instructions that do not generate
// result ids).
// be able to identify the instruction to insert before. We describe an
// instruction via its opcode, 'opc', a base instruction 'base' that has a
// result id, and the number of instructions with opcode 'opc' that we
// 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.
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) {
if (inst_it->HasResultId()) {
// 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();
offset = 0;
} 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++;
skip_count.clear();
}
const SpvOp opcode = inst_it->opcode();
// Invoke the provided function, which might apply a transformation.
// Its return value informs us of how many instructions it inserted.
// (This will be 0 if no transformation was applied.)
uint32_t num_instructions_inserted =
maybe_apply_transformation(function, &block, inst_it, base, offset);
maybe_apply_transformation(
function, &block, inst_it,
MakeInstructionDescriptor(
base, opcode,
skip_count.count(opcode) ? skip_count.at(opcode) : 0));
if (!inst_it->HasResultId()) {
// We are tracking the current id-less instruction via an offset,
// |offset|, from a previous instruction, |base|, that has an id. We
// 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;
skip_count[opcode] =
skip_count.count(opcode) ? skip_count.at(opcode) + 1 : 1;
}
}
}

View File

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

View File

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

View File

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

View File

@ -180,41 +180,6 @@ opt::BasicBlock::iterator GetIteratorForInstruction(
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,
opt::BasicBlock* bb_from,
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* 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| -
// 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

View File

@ -331,15 +331,12 @@ message TransformationCopyObject {
// Id of the object to be copied
uint32 object = 1;
// The id of an instruction in a block
uint32 base_instruction_id = 2;
// An offset, such that OpCopyObject instruction should be inserted right
// before the instruction |offset| instructions after |base_instruction_id|
uint32 offset = 3;
// A descriptor for an instruction in a block before which the new
// OpCopyObject instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 2;
// 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
repeated uint32 component = 2;
// The id of an instruction in a block
uint32 base_instruction_id = 3;
// An offset, such that OpCompositeConstruct instruction should be inserted
// right before the instruction |offset| instructions after
// |base_instruction_id|
uint32 offset = 4;
// A descriptor for an instruction in a block before which the new
// OpCompositeConstruct instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 3;
// 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/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/opt/instruction.h"
namespace spvtools {
namespace fuzz {
@ -25,13 +27,14 @@ TransformationConstructComposite::TransformationConstructComposite(
TransformationConstructComposite::TransformationConstructComposite(
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);
for (auto a_component : component) {
message_.add_component(a_component);
}
message_.set_base_instruction_id(base_instruction_id);
message_.set_offset(offset);
*message_.mutable_instruction_to_insert_before() =
instruction_to_insert_before;
message_.set_fresh_id(fresh_id);
}
@ -42,24 +45,11 @@ bool TransformationConstructComposite::IsApplicable(
return false;
}
auto base_instruction =
context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
if (!base_instruction) {
// The given id to insert after is not defined.
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.
auto insert_before =
FindInstruction(message_.instruction_to_insert_before(), context);
if (!insert_before) {
// The instruction before which the composite should be inserted was not
// found.
return false;
}
@ -125,11 +115,11 @@ void TransformationConstructComposite::Apply(opt::IRContext* context,
FactManager* fact_manager) const {
// Use the base and offset information from the transformation to determine
// where in the module a new instruction should be inserted.
auto base_instruction =
context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
auto destination_block = context->get_instr_block(base_instruction);
auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
destination_block, base_instruction, message_.offset());
auto insert_before_inst =
FindInstruction(message_.instruction_to_insert_before(), context);
auto destination_block = context->get_instr_block(insert_before_inst);
auto insert_before = fuzzerutil::GetIteratorForInstruction(
destination_block, insert_before_inst);
// Prepare the input operands for an OpCompositeConstruct instruction.
opt::Instruction::OperandList in_operands;

View File

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

View File

@ -28,8 +28,10 @@ class TransformationCopyObject : public Transformation {
explicit TransformationCopyObject(
const protobufs::TransformationCopyObject& message);
TransformationCopyObject(uint32_t object, uint32_t base_instruction_id,
uint32_t offset, uint32_t fresh_id);
TransformationCopyObject(
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_.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/data_descriptor.h"
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
@ -144,11 +145,13 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
FactManager fact_manager;
// Make a vec2[3]
TransformationConstructComposite make_vec2_array_length_3(37, {41, 45, 27},
46, 0, 200);
TransformationConstructComposite make_vec2_array_length_3(
37, {41, 45, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0),
200);
// Bad: there are too many components
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(
make_vec2_array_length_3.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
@ -160,11 +163,11 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 27, 200, {2}));
// Make a float[2]
TransformationConstructComposite make_float_array_length_2(9, {24, 40}, 71, 1,
201);
TransformationConstructComposite make_float_array_length_2(
9, {24, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
// Bad: %41 does not have type float
TransformationConstructComposite make_float_array_length_2_bad(9, {41, 40},
71, 1, 201);
TransformationConstructComposite make_float_array_length_2_bad(
9, {41, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
ASSERT_TRUE(
make_float_array_length_2.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
@ -175,11 +178,13 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 40, 201, {1}));
// Make a bool[3]
TransformationConstructComposite make_bool_array_length_3(47, {33, 50, 50},
33, 1, 202);
TransformationConstructComposite make_bool_array_length_3(
47, {33, 50, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0),
202);
// Bad: %54 is not available at the desired program point.
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(
make_bool_array_length_3.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
@ -191,11 +196,11 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
ASSERT_TRUE(SynonymFactHolds(fact_manager, 50, 202, {2}));
// make a uvec3[2][2]
TransformationConstructComposite make_uvec3_array_length_2_2(58, {69, 100},
64, 1, 203);
// Bad: Offset 100 is too large.
TransformationConstructComposite make_uvec3_array_length_2_2(
58, {69, 100}, MakeInstructionDescriptor(64, SpvOpStore, 0), 203);
// Bad: Skip count 100 is too large.
TransformationConstructComposite make_uvec3_array_length_2_2_bad(
58, {33, 54}, 64, 100, 203);
58, {33, 54}, MakeInstructionDescriptor(64, SpvOpStore, 100), 203);
ASSERT_TRUE(
make_uvec3_array_length_2_2.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable(context.get(),
@ -393,9 +398,11 @@ TEST(TransformationConstructCompositeTest, ConstructMatrices) {
FactManager fact_manager;
// 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.
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_FALSE(make_mat34_bad.IsApplicable(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}));
// make a mat4x3
TransformationConstructComposite make_mat43(35, {11, 13, 16, 100}, 31, 1,
201);
TransformationConstructComposite make_mat43(
35, {11, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201);
// Bad: %25 does not match the matrix's column type.
TransformationConstructComposite make_mat43_bad(35, {25, 13, 16, 100}, 31, 1,
201);
TransformationConstructComposite make_mat43_bad(
35, {25, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201);
ASSERT_TRUE(make_mat43.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_mat43_bad.IsApplicable(context.get(), fact_manager));
make_mat43.Apply(context.get(), &fact_manager);
@ -592,9 +599,11 @@ TEST(TransformationConstructCompositeTest, ConstructStructs) {
FactManager fact_manager;
// 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.
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_FALSE(make_inner_bad.IsApplicable(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}));
// 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.
TransformationConstructComposite make_outer_bad(33, {46, 200, 56}, 200, 0,
201);
TransformationConstructComposite make_outer_bad(
33, {46, 200, 56},
MakeInstructionDescriptor(200, SpvOpCompositeConstruct, 0), 201);
ASSERT_TRUE(make_outer.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_outer_bad.IsApplicable(context.get(), fact_manager));
make_outer.Apply(context.get(), &fact_manager);
@ -900,9 +912,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
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
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_FALSE(make_vec2_bad.IsApplicable(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, 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
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_FALSE(make_vec3_bad.IsApplicable(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, 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
TransformationConstructComposite make_vec4_bad(44, {48, 32, 10, 11}, 75, 0,
202);
TransformationConstructComposite make_vec4_bad(
44, {48, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0),
202);
ASSERT_TRUE(make_vec4.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_vec4_bad.IsApplicable(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, 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
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_FALSE(make_ivec2_bad.IsApplicable(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, 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
TransformationConstructComposite make_ivec3_bad(114, {56, 117, 1300}, 66, 1,
204);
TransformationConstructComposite make_ivec3_bad(
114, {56, 117, 1300}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
204);
ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_ivec3_bad.IsApplicable(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, 56, 204, {2}));
TransformationConstructComposite make_ivec4(122, {56, 117, 117, 117}, 66, 0,
205);
TransformationConstructComposite make_ivec4(
122, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
205);
// Bad because 86 is the wrong type.
TransformationConstructComposite make_ivec4_bad(86, {56, 117, 117, 117}, 66,
0, 205);
TransformationConstructComposite make_ivec4_bad(
86, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
205);
ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_ivec4_bad.IsApplicable(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, {3}));
TransformationConstructComposite make_uvec2(86, {18, 38}, 133, 2, 206);
TransformationConstructComposite make_uvec2_bad(86, {18, 38}, 133, 200, 206);
TransformationConstructComposite make_uvec2(
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_FALSE(make_uvec2_bad.IsApplicable(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, 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
TransformationConstructComposite make_uvec3_bad(59, {14, 18, 1300}, 137, 2,
207);
TransformationConstructComposite make_uvec3_bad(
59, {14, 18, 1300}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_uvec3_bad.IsApplicable(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, 136, 207, {2}));
TransformationConstructComposite make_uvec4(131, {14, 18, 136, 136}, 137, 0,
208);
TransformationConstructComposite make_uvec4(
131, {14, 18, 136, 136},
MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
// Bad because 86 is the wrong type.
TransformationConstructComposite make_uvec4_bad(86, {14, 18, 136, 136}, 137,
0, 208);
TransformationConstructComposite make_uvec4_bad(
86, {14, 18, 136, 136},
MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_uvec4_bad.IsApplicable(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, {3}));
TransformationConstructComposite make_bvec2(102,
{
111,
41,
},
75, 0, 209);
TransformationConstructComposite make_bvec2(
102,
{
111,
41,
},
MakeInstructionDescriptor(75, SpvOpAccessChain, 0), 209);
// Bad because 0 is not a valid base instruction id
TransformationConstructComposite make_bvec2_bad(102,
{
111,
41,
},
0, 0, 209);
TransformationConstructComposite make_bvec2_bad(
102,
{
111,
41,
},
MakeInstructionDescriptor(0, SpvOpExtInstImport, 0), 209);
ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(make_bvec2_bad.IsApplicable(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, 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
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_FALSE(make_bvec3_bad.IsApplicable(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, 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
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_FALSE(make_bvec4_bad.IsApplicable(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/data_descriptor.h"
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
@ -49,7 +50,8 @@ TEST(TransformationCopyObjectTest, CopyBooleanConstants) {
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));
copy_true.Apply(context.get(), &fact_manager);
@ -63,7 +65,8 @@ TEST(TransformationCopyObjectTest, CopyBooleanConstants) {
ASSERT_TRUE(DataDescriptorEquals()(&descriptor_100,
&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));
copy_false.Apply(context.get(), &fact_manager);
ASSERT_EQ(2, ids_for_which_synonyms_are_known.size());
@ -74,7 +77,8 @@ TEST(TransformationCopyObjectTest, CopyBooleanConstants) {
ASSERT_TRUE(DataDescriptorEquals()(&descriptor_101,
&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));
copy_false_again.Apply(context.get(), &fact_manager);
ASSERT_EQ(3, ids_for_which_synonyms_are_known.size());
@ -85,7 +89,8 @@ TEST(TransformationCopyObjectTest, CopyBooleanConstants) {
ASSERT_TRUE(DataDescriptorEquals()(&descriptor_102,
&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));
copy_true_again.Apply(context.get(), &fact_manager);
// 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;
// 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));
// 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));
// 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));
// Inapplicable because %84 is not available at the requested point
ASSERT_FALSE(TransformationCopyObject(84, 32, 1, 200)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationCopyObject(
84, MakeInstructionDescriptor(32, SpvOpCompositeExtract, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Fine because %84 is available at the requested point
ASSERT_TRUE(TransformationCopyObject(84, 32, 2, 200)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationCopyObject(
84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because id %9 is already in use
ASSERT_FALSE(TransformationCopyObject(84, 32, 2, 9)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationCopyObject(
84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 9)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because the requested point is not in a block
ASSERT_FALSE(TransformationCopyObject(84, 86, 3, 200)
// Inapplicable because the requested point does not exist
ASSERT_FALSE(TransformationCopyObject(
84, MakeInstructionDescriptor(86, SpvOpReturn, 2), 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because %9 is not in a function
ASSERT_FALSE(TransformationCopyObject(9, 9, 1, 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because %9 is not in a function
ASSERT_FALSE(TransformationCopyObject(9, 9, 1, 200)
ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(9, SpvOpTypeInt, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because the insert point is right before, or inside, a chunk
// of OpPhis
ASSERT_FALSE(TransformationCopyObject(9, 30, 1, 200)
ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(30, SpvOpPhi, 0), 200)
.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));
// 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));
// Inapplicable because the insert point is right after an OpSelectionMerge
ASSERT_FALSE(TransformationCopyObject(9, 58, 2, 200)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationCopyObject(
9, MakeInstructionDescriptor(58, SpvOpBranchConditional, 0), 200)
.IsApplicable(context.get(), fact_manager));
// 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));
// 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));
// 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));
// Inapplicable because the insert point is right after an OpLoopMerge
ASSERT_FALSE(TransformationCopyObject(9, 40, 2, 200)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationCopyObject(
9, MakeInstructionDescriptor(40, SpvOpBranchConditional, 0), 200)
.IsApplicable(context.get(), fact_manager));
// 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));
// 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));
// 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));
ASSERT_FALSE(TransformationCopyObject(9, 181, 0, 200)
ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(181, SpvOpVariable, 0), 200)
.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));
// 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));
}
@ -470,13 +498,20 @@ TEST(TransformationCopyObjectTest, MiscellaneousCopies) {
FactManager fact_manager;
std::vector<TransformationCopyObject> transformations = {
TransformationCopyObject(19, 22, 1, 100),
TransformationCopyObject(22, 22, 1, 101),
TransformationCopyObject(12, 22, 1, 102),
TransformationCopyObject(11, 22, 1, 103),
TransformationCopyObject(16, 22, 1, 104),
TransformationCopyObject(8, 22, 1, 105),
TransformationCopyObject(17, 22, 1, 106)};
TransformationCopyObject(19, MakeInstructionDescriptor(22, SpvOpStore, 0),
100),
TransformationCopyObject(
22, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 101),
TransformationCopyObject(
12, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 102),
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) {
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));