spirv-fuzz: Add FuzzerPassAddCopyMemoryInstructions (#3391)
Fixes #3382.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index d147408..c6beeb1 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -39,6 +39,7 @@
fuzzer_pass.h
fuzzer_pass_add_access_chains.h
fuzzer_pass_add_composite_types.h
+ fuzzer_pass_add_copy_memory.h
fuzzer_pass_add_dead_blocks.h
fuzzer_pass_add_dead_breaks.h
fuzzer_pass_add_dead_continues.h
@@ -87,6 +88,7 @@
transformation_add_constant_composite.h
transformation_add_constant_null.h
transformation_add_constant_scalar.h
+ transformation_add_copy_memory.h
transformation_add_dead_block.h
transformation_add_dead_break.h
transformation_add_dead_continue.h
@@ -147,6 +149,7 @@
fuzzer_pass.cpp
fuzzer_pass_add_access_chains.cpp
fuzzer_pass_add_composite_types.cpp
+ fuzzer_pass_add_copy_memory.cpp
fuzzer_pass_add_dead_blocks.cpp
fuzzer_pass_add_dead_breaks.cpp
fuzzer_pass_add_dead_continues.cpp
@@ -194,6 +197,7 @@
transformation_add_constant_composite.cpp
transformation_add_constant_null.cpp
transformation_add_constant_scalar.cpp
+ transformation_add_copy_memory.cpp
transformation_add_dead_block.cpp
transformation_add_dead_break.cpp
transformation_add_dead_continue.cpp
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index 36e7f90..b7b035e 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -23,6 +23,7 @@
#include "source/fuzz/fuzzer_context.h"
#include "source/fuzz/fuzzer_pass_add_access_chains.h"
#include "source/fuzz/fuzzer_pass_add_composite_types.h"
+#include "source/fuzz/fuzzer_pass_add_copy_memory.h"
#include "source/fuzz/fuzzer_pass_add_dead_blocks.h"
#include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
#include "source/fuzz/fuzzer_pass_add_dead_continues.h"
@@ -201,6 +202,9 @@
MaybeAddPass<FuzzerPassAddCompositeTypes>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassAddCopyMemory>(
+ &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
MaybeAddPass<FuzzerPassAddDeadBlocks>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index 97e145b..ec34528 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -27,6 +27,7 @@
const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherStructField = {20,
90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingArrayOrStructType = {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingCopyMemory = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBlock = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
@@ -119,6 +120,8 @@
ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField);
chance_of_adding_array_or_struct_type_ =
ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType);
+ chance_of_adding_copy_memory_ =
+ ChooseBetweenMinAndMax(kChanceOfAddingCopyMemory);
chance_of_adding_dead_block_ =
ChooseBetweenMinAndMax(kChanceOfAddingDeadBlock);
chance_of_adding_dead_break_ =
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index 4598124..a81e287 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -114,6 +114,9 @@
uint32_t GetChanceOfAddingArrayOrStructType() {
return chance_of_adding_array_or_struct_type_;
}
+ uint32_t GetChanceOfAddingCopyMemory() {
+ return chance_of_adding_copy_memory_;
+ }
uint32_t GetChanceOfAddingDeadBlock() { return chance_of_adding_dead_block_; }
uint32_t GetChanceOfAddingDeadBreak() { return chance_of_adding_dead_break_; }
uint32_t GetChanceOfAddingDeadContinue() {
@@ -274,6 +277,7 @@
uint32_t chance_of_adding_access_chain_;
uint32_t chance_of_adding_another_struct_field_;
uint32_t chance_of_adding_array_or_struct_type_;
+ uint32_t chance_of_adding_copy_memory_;
uint32_t chance_of_adding_dead_block_;
uint32_t chance_of_adding_dead_break_;
uint32_t chance_of_adding_dead_continue_;
diff --git a/source/fuzz/fuzzer_pass_add_copy_memory.cpp b/source/fuzz/fuzzer_pass_add_copy_memory.cpp
new file mode 100644
index 0000000..ed375a1
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_add_copy_memory.cpp
@@ -0,0 +1,82 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_copy_memory.h"
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_add_copy_memory.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddCopyMemory::FuzzerPassAddCopyMemory(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassAddCopyMemory::~FuzzerPassAddCopyMemory() = default;
+
+void FuzzerPassAddCopyMemory::Apply() {
+ ForEachInstructionWithInstructionDescriptor(
+ [this](opt::Function* function, opt::BasicBlock* block,
+ opt::BasicBlock::iterator inst_it,
+ const protobufs::InstructionDescriptor& instruction_descriptor) {
+ // Check that we can insert an OpCopyMemory before this instruction.
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyMemory,
+ inst_it)) {
+ return;
+ }
+
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfAddingCopyMemory())) {
+ return;
+ }
+
+ // Get all instructions available before |inst_it| according to the
+ // domination rules.
+ auto instructions = FindAvailableInstructions(
+ function, block, inst_it,
+ TransformationAddCopyMemory::IsInstructionSupported);
+
+ if (instructions.empty()) {
+ return;
+ }
+
+ const auto* inst =
+ instructions[GetFuzzerContext()->RandomIndex(instructions)];
+
+ // Decide whether to create global or local variable.
+ auto storage_class = GetFuzzerContext()->ChooseEven()
+ ? SpvStorageClassPrivate
+ : SpvStorageClassFunction;
+
+ auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
+ GetIRContext(), inst->type_id());
+
+ // Create a pointer type with |storage_class| if needed.
+ FindOrCreatePointerType(pointee_type_id, storage_class);
+
+ ApplyTransformation(TransformationAddCopyMemory(
+ instruction_descriptor, GetFuzzerContext()->GetFreshId(),
+ inst->result_id(), storage_class,
+ FindOrCreateZeroConstant(pointee_type_id)));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_add_copy_memory.h b/source/fuzz/fuzzer_pass_add_copy_memory.h
new file mode 100644
index 0000000..321e4a1
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_add_copy_memory.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Randomly decides whether to add OpCopyMemory before some instruction in the
+// module.
+class FuzzerPassAddCopyMemory : public FuzzerPass {
+ public:
+ FuzzerPassAddCopyMemory(opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassAddCopyMemory() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index 7e0db3e..068c6d8 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -381,6 +381,7 @@
TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 50;
TransformationPermutePhiOperands permute_phi_operands = 51;
TransformationAddParameter add_parameter = 52;
+ TransformationAddCopyMemory add_copy_memory = 53;
// Add additional option using the next available number.
}
}
@@ -458,6 +459,30 @@
}
+message TransformationAddCopyMemory {
+
+ // Adds an OpCopyMemory instruction into the module.
+ // Creates either a global or a local variable (based on
+ // |storage_class| field) to copy the target into.
+
+ // OpCopyMemory will be inserted before this instruction.
+ InstructionDescriptor instruction_descriptor = 1;
+
+ // Fresh id to copy memory into.
+ uint32 fresh_id = 2;
+
+ // Source to copy memory from.
+ uint32 source_id = 3;
+
+ // Storage class for the target variable. Can be either Function or Private.
+ uint32 storage_class = 4;
+
+ // Result id for the variable's initializer operand. Its type must be equal to
+ // variable's pointee type.
+ uint32 initializer_id = 5;
+
+}
+
message TransformationAddDeadBlock {
// Adds a new block to the module that is statically reachable from an
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index ac16a9f..252c750 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -22,6 +22,7 @@
#include "source/fuzz/transformation_add_constant_composite.h"
#include "source/fuzz/transformation_add_constant_null.h"
#include "source/fuzz/transformation_add_constant_scalar.h"
+#include "source/fuzz/transformation_add_copy_memory.h"
#include "source/fuzz/transformation_add_dead_block.h"
#include "source/fuzz/transformation_add_dead_break.h"
#include "source/fuzz/transformation_add_dead_continue.h"
@@ -93,6 +94,8 @@
case protobufs::Transformation::TransformationCase::kAddConstantScalar:
return MakeUnique<TransformationAddConstantScalar>(
message.add_constant_scalar());
+ case protobufs::Transformation::TransformationCase::kAddCopyMemory:
+ return MakeUnique<TransformationAddCopyMemory>(message.add_copy_memory());
case protobufs::Transformation::TransformationCase::kAddDeadBlock:
return MakeUnique<TransformationAddDeadBlock>(message.add_dead_block());
case protobufs::Transformation::TransformationCase::kAddDeadBreak:
diff --git a/source/fuzz/transformation_add_copy_memory.cpp b/source/fuzz/transformation_add_copy_memory.cpp
new file mode 100644
index 0000000..e9c401d
--- /dev/null
+++ b/source/fuzz/transformation_add_copy_memory.cpp
@@ -0,0 +1,193 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_copy_memory.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddCopyMemory::TransformationAddCopyMemory(
+ const protobufs::TransformationAddCopyMemory& message)
+ : message_(message) {}
+
+TransformationAddCopyMemory::TransformationAddCopyMemory(
+ const protobufs::InstructionDescriptor& instruction_descriptor,
+ uint32_t fresh_id, uint32_t source_id, SpvStorageClass storage_class,
+ uint32_t initializer_id) {
+ *message_.mutable_instruction_descriptor() = instruction_descriptor;
+ message_.set_fresh_id(fresh_id);
+ message_.set_source_id(source_id);
+ message_.set_storage_class(storage_class);
+ message_.set_initializer_id(initializer_id);
+}
+
+bool TransformationAddCopyMemory::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ // Check that target id is fresh.
+ if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
+ return false;
+ }
+
+ // Check that instruction descriptor is valid. This also checks that
+ // |message_.instruction_descriptor| is not a global instruction.
+ auto* inst = FindInstruction(message_.instruction_descriptor(), ir_context);
+ if (!inst) {
+ return false;
+ }
+
+ // Check that we can insert OpCopyMemory before |instruction_descriptor|.
+ auto iter = fuzzerutil::GetIteratorForInstruction(
+ ir_context->get_instr_block(inst), inst);
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyMemory, iter)) {
+ return false;
+ }
+
+ // Check that source instruction exists and is valid.
+ auto* source_inst =
+ ir_context->get_def_use_mgr()->GetDef(message_.source_id());
+ if (!source_inst || !IsInstructionSupported(ir_context, source_inst)) {
+ return false;
+ }
+
+ // |storage_class| is either Function or Private.
+ if (message_.storage_class() != SpvStorageClassFunction &&
+ message_.storage_class() != SpvStorageClassPrivate) {
+ return false;
+ }
+
+ auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
+ ir_context, source_inst->type_id());
+
+ // OpTypePointer with |message_.storage_class| exists.
+ if (!fuzzerutil::MaybeGetPointerType(
+ ir_context, pointee_type_id,
+ static_cast<SpvStorageClass>(message_.storage_class()))) {
+ return false;
+ }
+
+ // Check that |initializer_id| exists and has valid type.
+ const auto* initializer_inst =
+ ir_context->get_def_use_mgr()->GetDef(message_.initializer_id());
+ if (!initializer_inst || initializer_inst->type_id() != pointee_type_id) {
+ return false;
+ }
+
+ // Check that domination rules are satisfied.
+ return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, inst,
+ message_.source_id());
+}
+
+void TransformationAddCopyMemory::Apply(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
+ // Insert OpCopyMemory before |instruction_descriptor|.
+ auto* insert_before_inst =
+ FindInstruction(message_.instruction_descriptor(), ir_context);
+ assert(insert_before_inst);
+
+ auto insert_before_iter = fuzzerutil::GetIteratorForInstruction(
+ ir_context->get_instr_block(insert_before_inst), insert_before_inst);
+
+ insert_before_iter.InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCopyMemory, 0, 0,
+ opt::Instruction::OperandList{
+ {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}},
+ {SPV_OPERAND_TYPE_ID, {message_.source_id()}}}));
+
+ // Add global or local variable to copy memory into.
+ auto storage_class = static_cast<SpvStorageClass>(message_.storage_class());
+ auto type_id = fuzzerutil::MaybeGetPointerType(
+ ir_context,
+ fuzzerutil::GetPointeeTypeIdFromPointerType(
+ ir_context, fuzzerutil::GetTypeId(ir_context, message_.source_id())),
+ storage_class);
+
+ if (storage_class == SpvStorageClassPrivate) {
+ fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_id(), type_id,
+ storage_class, message_.initializer_id());
+ } else {
+ assert(storage_class == SpvStorageClassFunction &&
+ "Storage class can be either Private or Function");
+ fuzzerutil::AddLocalVariable(ir_context, message_.fresh_id(), type_id,
+ ir_context->get_instr_block(insert_before_inst)
+ ->GetParent()
+ ->result_id(),
+ message_.initializer_id());
+ }
+
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+ // Even though the copy memory instruction will - at least temporarily - lead
+ // to the destination and source pointers referring to identical values, this
+ // fact is not guaranteed to hold throughout execution of the SPIR-V code
+ // since the source pointer could be over-written. We thus assume nothing
+ // about the destination pointer, and record this fact so that the destination
+ // pointer can be used freely by other fuzzer passes.
+ transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+ message_.fresh_id());
+
+ // Make sure our changes are analyzed
+ ir_context->InvalidateAnalysesExceptFor(
+ opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddCopyMemory::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_copy_memory() = message_;
+ return result;
+}
+
+bool TransformationAddCopyMemory::IsInstructionSupported(
+ opt::IRContext* ir_context, opt::Instruction* inst) {
+ if (!inst->result_id() || !inst->type_id() ||
+ inst->opcode() == SpvOpConstantNull || inst->opcode() == SpvOpUndef) {
+ return false;
+ }
+
+ const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
+ assert(type && "Instruction must have a valid type");
+
+ return type->AsPointer() &&
+ CanUsePointeeWithCopyMemory(*type->AsPointer()->pointee_type());
+}
+
+bool TransformationAddCopyMemory::CanUsePointeeWithCopyMemory(
+ const opt::analysis::Type& type) {
+ switch (type.kind()) {
+ case opt::analysis::Type::kBool:
+ case opt::analysis::Type::kInteger:
+ case opt::analysis::Type::kFloat:
+ case opt::analysis::Type::kArray:
+ return true;
+ case opt::analysis::Type::kVector:
+ return CanUsePointeeWithCopyMemory(*type.AsVector()->element_type());
+ case opt::analysis::Type::kMatrix:
+ return CanUsePointeeWithCopyMemory(*type.AsMatrix()->element_type());
+ case opt::analysis::Type::kStruct:
+ return std::all_of(type.AsStruct()->element_types().begin(),
+ type.AsStruct()->element_types().end(),
+ [](const opt::analysis::Type* element) {
+ return CanUsePointeeWithCopyMemory(*element);
+ });
+ default:
+ return false;
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_add_copy_memory.h b/source/fuzz/transformation_add_copy_memory.h
new file mode 100644
index 0000000..138d992
--- /dev/null
+++ b/source/fuzz/transformation_add_copy_memory.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddCopyMemory : public Transformation {
+ public:
+ explicit TransformationAddCopyMemory(
+ const protobufs::TransformationAddCopyMemory& message);
+
+ TransformationAddCopyMemory(
+ const protobufs::InstructionDescriptor& instruction_descriptor,
+ uint32_t fresh_id, uint32_t source_id, SpvStorageClass storage_class,
+ uint32_t initializer_id);
+
+ // - |instruction_descriptor| must point to a valid instruction in the module.
+ // - it should be possible to insert OpCopyMemory before
+ // |instruction_descriptor| (i.e. the module remains valid after the
+ // insertion).
+ // - |source_id| must be a result id for some valid instruction in the module.
+ // - |fresh_id| must be a fresh id to copy memory into.
+ // - type of |source_id| must be OpTypePointer where pointee can be used with
+ // OpCopyMemory.
+ // - |storage_class| must be either Private or Function.
+ // - type ids of instructions with result ids |source_id| and |initialize_id|
+ // must be the same.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // A global or local variable with id |target_id| and |storage_class| class is
+ // created. An 'OpCopyMemory %fresh_id %source_id' instruction is inserted
+ // before the |instruction_descriptor|.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ // Returns true if we can copy memory from |instruction| using OpCopyMemory.
+ static bool IsInstructionSupported(opt::IRContext* ir_context,
+ opt::Instruction* inst);
+
+ private:
+ // Returns whether the type, pointed to by some OpTypePointer, can be used
+ // with OpCopyMemory instruction.
+ static bool CanUsePointeeWithCopyMemory(const opt::analysis::Type& type);
+
+ protobufs::TransformationAddCopyMemory message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_H_
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 57d770d..3b710e5 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -29,6 +29,7 @@
transformation_add_constant_composite_test.cpp
transformation_add_constant_null_test.cpp
transformation_add_constant_scalar_test.cpp
+ transformation_add_copy_memory_test.cpp
transformation_add_dead_block_test.cpp
transformation_add_dead_break_test.cpp
transformation_add_dead_continue_test.cpp
diff --git a/test/fuzz/transformation_add_copy_memory_test.cpp b/test/fuzz/transformation_add_copy_memory_test.cpp
new file mode 100644
index 0000000..66a15f4
--- /dev/null
+++ b/test/fuzz/transformation_add_copy_memory_test.cpp
@@ -0,0 +1,384 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_copy_memory.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddCopyMemoryTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %19 RelaxedPrecision
+ OpMemberDecorate %66 0 RelaxedPrecision
+ OpDecorate %69 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpTypePointer Function %6
+ %78 = OpTypePointer Private %6
+ %8 = OpTypeFunction %6 %7
+ %17 = OpTypeInt 32 1
+ %18 = OpTypePointer Function %17
+ %79 = OpTypePointer Private %17
+ %20 = OpConstant %17 0
+ %21 = OpTypeFloat 32
+ %22 = OpTypePointer Function %21
+ %80 = OpTypePointer Private %21
+ %24 = OpConstant %21 0
+ %25 = OpConstantFalse %6
+ %32 = OpConstantTrue %6
+ %33 = OpTypeVector %21 4
+ %34 = OpTypePointer Function %33
+ %81 = OpTypePointer Private %33
+ %36 = OpConstantComposite %33 %24 %24 %24 %24
+ %37 = OpTypeMatrix %33 4
+ %84 = OpConstantComposite %37 %36 %36 %36 %36
+ %38 = OpTypePointer Function %37
+ %82 = OpTypePointer Private %37
+ %44 = OpConstant %21 1
+ %66 = OpTypeStruct %17 %21 %6 %33 %37
+ %85 = OpConstantComposite %66 %20 %24 %25 %36 %84
+ %67 = OpTypePointer Function %66
+ %83 = OpTypePointer Private %66
+ %86 = OpVariable %79 Private %20
+ %87 = OpUndef %79
+ %88 = OpConstantNull %79
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %19 = OpVariable %18 Function
+ %23 = OpVariable %22 Function
+ %26 = OpVariable %7 Function
+ %30 = OpVariable %7 Function
+ %35 = OpVariable %34 Function
+ %39 = OpVariable %38 Function
+ %68 = OpVariable %67 Function
+ OpStore %19 %20
+ OpStore %23 %24
+ OpStore %26 %25
+ %27 = OpFunctionCall %6 %10 %26
+ OpSelectionMerge %29 None
+ OpBranchConditional %27 %28 %31
+ %28 = OpLabel
+ OpBranch %29
+ %31 = OpLabel
+ OpBranch %29
+ %76 = OpLabel
+ %77 = OpLogicalEqual %6 %25 %32
+ OpBranch %29
+ %29 = OpLabel
+ %75 = OpPhi %6 %25 %31 %32 %28 %77 %76
+ OpStore %30 %75
+ %40 = OpLoad %33 %35
+ %41 = OpLoad %33 %35
+ %42 = OpLoad %33 %35
+ %43 = OpLoad %33 %35
+ %45 = OpCompositeExtract %21 %40 0
+ %46 = OpCompositeExtract %21 %40 1
+ %47 = OpCompositeExtract %21 %40 2
+ %48 = OpCompositeExtract %21 %40 3
+ %49 = OpCompositeExtract %21 %41 0
+ %50 = OpCompositeExtract %21 %41 1
+ %51 = OpCompositeExtract %21 %41 2
+ %52 = OpCompositeExtract %21 %41 3
+ %53 = OpCompositeExtract %21 %42 0
+ %54 = OpCompositeExtract %21 %42 1
+ %55 = OpCompositeExtract %21 %42 2
+ %56 = OpCompositeExtract %21 %42 3
+ %57 = OpCompositeExtract %21 %43 0
+ %58 = OpCompositeExtract %21 %43 1
+ %59 = OpCompositeExtract %21 %43 2
+ %60 = OpCompositeExtract %21 %43 3
+ %61 = OpCompositeConstruct %33 %45 %46 %47 %48
+ %62 = OpCompositeConstruct %33 %49 %50 %51 %52
+ %63 = OpCompositeConstruct %33 %53 %54 %55 %56
+ %64 = OpCompositeConstruct %33 %57 %58 %59 %60
+ %65 = OpCompositeConstruct %37 %61 %62 %63 %64
+ OpStore %39 %65
+ %69 = OpLoad %17 %19
+ %70 = OpLoad %21 %23
+ %71 = OpLoad %6 %30
+ %72 = OpLoad %33 %35
+ %73 = OpLoad %37 %39
+ %74 = OpCompositeConstruct %66 %69 %70 %71 %72 %73
+ OpStore %68 %74
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %6 None %8
+ %9 = OpFunctionParameter %7
+ %11 = OpLabel
+ %12 = OpVariable %7 Function
+ %13 = OpLoad %6 %9
+ OpStore %12 %13
+ %14 = OpLoad %6 %12
+ OpReturnValue %14
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Target id is not fresh (59).
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 59, 19,
+ SpvStorageClassPrivate, 20)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Instruction descriptor is invalid (id 89 is undefined).
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(89, SpvOpVariable, 0), 89, 19,
+ SpvStorageClassPrivate, 20)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Cannot insert OpCopyMemory before OpPhi.
+ ASSERT_FALSE(
+ TransformationAddCopyMemory(MakeInstructionDescriptor(75, SpvOpPhi, 0),
+ 89, 19, SpvStorageClassPrivate, 20)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source instruction is invalid.
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 89, 76,
+ SpvStorageClassPrivate, 0)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source instruction's type doesn't exist.
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 89, 5,
+ SpvStorageClassPrivate, 0)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source instruction's type is invalid.
+ ASSERT_FALSE(
+ TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0),
+ 89, 40, SpvStorageClassPrivate, 0)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source instruction is OpUndef.
+ ASSERT_FALSE(
+ TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0),
+ 89, 87, SpvStorageClassPrivate, 0)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source instruction is OpConstantNull.
+ ASSERT_FALSE(
+ TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0),
+ 89, 88, SpvStorageClassPrivate, 0)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Storage class is invalid.
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 89, 19,
+ SpvStorageClassWorkgroup, 20)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Initializer is 0.
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 89, 19,
+ SpvStorageClassPrivate, 0)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Initializer has wrong type.
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 89, 19,
+ SpvStorageClassPrivate, 25)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source and target instructions are in different functions.
+ ASSERT_FALSE(
+ TransformationAddCopyMemory(MakeInstructionDescriptor(13, SpvOpLoad, 0),
+ 89, 19, SpvStorageClassPrivate, 20)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source instruction doesn't dominate the target instruction.
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(77, SpvOpLogicalEqual, 0), 89, 19,
+ SpvStorageClassPrivate, 20)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source and target instructions are the same.
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(19, SpvOpVariable, 0), 89, 19,
+ SpvStorageClassPrivate, 20)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Correct transformations.
+ uint32_t fresh_id = 89;
+ auto descriptor = MakeInstructionDescriptor(27, SpvOpFunctionCall, 0);
+ std::vector<uint32_t> source_ids = {19, 23, 26, 30, 35, 39, 68, 86};
+ std::vector<uint32_t> initializers = {20, 24, 25, 25, 36, 84, 85, 20};
+ std::vector<SpvStorageClass> storage_classes = {SpvStorageClassPrivate,
+ SpvStorageClassFunction};
+ for (size_t i = 0, n = source_ids.size(); i < n; ++i) {
+ TransformationAddCopyMemory transformation(
+ descriptor, fresh_id, source_ids[i],
+ storage_classes[i % storage_classes.size()], initializers[i]);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(fresh_id));
+ fresh_id++;
+ }
+
+ std::string expected = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %19 RelaxedPrecision
+ OpMemberDecorate %66 0 RelaxedPrecision
+ OpDecorate %69 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpTypePointer Function %6
+ %78 = OpTypePointer Private %6
+ %8 = OpTypeFunction %6 %7
+ %17 = OpTypeInt 32 1
+ %18 = OpTypePointer Function %17
+ %79 = OpTypePointer Private %17
+ %20 = OpConstant %17 0
+ %21 = OpTypeFloat 32
+ %22 = OpTypePointer Function %21
+ %80 = OpTypePointer Private %21
+ %24 = OpConstant %21 0
+ %25 = OpConstantFalse %6
+ %32 = OpConstantTrue %6
+ %33 = OpTypeVector %21 4
+ %34 = OpTypePointer Function %33
+ %81 = OpTypePointer Private %33
+ %36 = OpConstantComposite %33 %24 %24 %24 %24
+ %37 = OpTypeMatrix %33 4
+ %84 = OpConstantComposite %37 %36 %36 %36 %36
+ %38 = OpTypePointer Function %37
+ %82 = OpTypePointer Private %37
+ %44 = OpConstant %21 1
+ %66 = OpTypeStruct %17 %21 %6 %33 %37
+ %85 = OpConstantComposite %66 %20 %24 %25 %36 %84
+ %67 = OpTypePointer Function %66
+ %83 = OpTypePointer Private %66
+ %86 = OpVariable %79 Private %20
+ %87 = OpUndef %79
+ %88 = OpConstantNull %79
+ %89 = OpVariable %79 Private %20
+ %91 = OpVariable %78 Private %25
+ %93 = OpVariable %81 Private %36
+ %95 = OpVariable %83 Private %85
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %96 = OpVariable %18 Function %20
+ %94 = OpVariable %38 Function %84
+ %92 = OpVariable %7 Function %25
+ %90 = OpVariable %22 Function %24
+ %19 = OpVariable %18 Function
+ %23 = OpVariable %22 Function
+ %26 = OpVariable %7 Function
+ %30 = OpVariable %7 Function
+ %35 = OpVariable %34 Function
+ %39 = OpVariable %38 Function
+ %68 = OpVariable %67 Function
+ OpStore %19 %20
+ OpStore %23 %24
+ OpStore %26 %25
+ OpCopyMemory %89 %19
+ OpCopyMemory %90 %23
+ OpCopyMemory %91 %26
+ OpCopyMemory %92 %30
+ OpCopyMemory %93 %35
+ OpCopyMemory %94 %39
+ OpCopyMemory %95 %68
+ OpCopyMemory %96 %86
+ %27 = OpFunctionCall %6 %10 %26
+ OpSelectionMerge %29 None
+ OpBranchConditional %27 %28 %31
+ %28 = OpLabel
+ OpBranch %29
+ %31 = OpLabel
+ OpBranch %29
+ %76 = OpLabel
+ %77 = OpLogicalEqual %6 %25 %32
+ OpBranch %29
+ %29 = OpLabel
+ %75 = OpPhi %6 %25 %31 %32 %28 %77 %76
+ OpStore %30 %75
+ %40 = OpLoad %33 %35
+ %41 = OpLoad %33 %35
+ %42 = OpLoad %33 %35
+ %43 = OpLoad %33 %35
+ %45 = OpCompositeExtract %21 %40 0
+ %46 = OpCompositeExtract %21 %40 1
+ %47 = OpCompositeExtract %21 %40 2
+ %48 = OpCompositeExtract %21 %40 3
+ %49 = OpCompositeExtract %21 %41 0
+ %50 = OpCompositeExtract %21 %41 1
+ %51 = OpCompositeExtract %21 %41 2
+ %52 = OpCompositeExtract %21 %41 3
+ %53 = OpCompositeExtract %21 %42 0
+ %54 = OpCompositeExtract %21 %42 1
+ %55 = OpCompositeExtract %21 %42 2
+ %56 = OpCompositeExtract %21 %42 3
+ %57 = OpCompositeExtract %21 %43 0
+ %58 = OpCompositeExtract %21 %43 1
+ %59 = OpCompositeExtract %21 %43 2
+ %60 = OpCompositeExtract %21 %43 3
+ %61 = OpCompositeConstruct %33 %45 %46 %47 %48
+ %62 = OpCompositeConstruct %33 %49 %50 %51 %52
+ %63 = OpCompositeConstruct %33 %53 %54 %55 %56
+ %64 = OpCompositeConstruct %33 %57 %58 %59 %60
+ %65 = OpCompositeConstruct %37 %61 %62 %63 %64
+ OpStore %39 %65
+ %69 = OpLoad %17 %19
+ %70 = OpLoad %21 %23
+ %71 = OpLoad %6 %30
+ %72 = OpLoad %33 %35
+ %73 = OpLoad %37 %39
+ %74 = OpCompositeConstruct %66 %69 %70 %71 %72 %73
+ OpStore %68 %74
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %6 None %8
+ %9 = OpFunctionParameter %7
+ %11 = OpLabel
+ %12 = OpVariable %7 Function
+ %13 = OpLoad %6 %9
+ OpStore %12 %13
+ %14 = OpLoad %6 %12
+ OpReturnValue %14
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
\ No newline at end of file