spirv-fuzz: adds TransformationReplaceCopyMemoryWithLoadStore (#3575)
Adds a transformation that replaces instruction OpCopyMemory with
loading the source variable to an intermediate value and storing this
value into the target variable of the original OpCopyMemory instruction.
Fixes #3352
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index 2d76bb2..f1309f3 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -73,6 +73,7 @@
fuzzer_pass_permute_function_parameters.h
fuzzer_pass_permute_phi_operands.h
fuzzer_pass_push_ids_through_variables.h
+ fuzzer_pass_replace_copy_memories_with_loads_stores.h
fuzzer_pass_replace_copy_objects_with_stores_loads.h
fuzzer_pass_replace_linear_algebra_instructions.h
fuzzer_pass_replace_parameter_with_global.h
@@ -137,6 +138,7 @@
transformation_record_synonymous_constants.h
transformation_replace_boolean_constant_with_constant_binary.h
transformation_replace_constant_with_uniform.h
+ transformation_replace_copy_memory_with_load_store.h
transformation_replace_copy_object_with_store_load.h
transformation_replace_id_with_synonym.h
transformation_replace_linear_algebra_instruction.h
@@ -198,6 +200,7 @@
fuzzer_pass_permute_function_parameters.cpp
fuzzer_pass_permute_phi_operands.cpp
fuzzer_pass_push_ids_through_variables.cpp
+ fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
fuzzer_pass_replace_linear_algebra_instructions.cpp
fuzzer_pass_replace_parameter_with_global.cpp
@@ -261,6 +264,7 @@
transformation_record_synonymous_constants.cpp
transformation_replace_boolean_constant_with_constant_binary.cpp
transformation_replace_constant_with_uniform.cpp
+ transformation_replace_copy_memory_with_load_store.cpp
transformation_replace_copy_object_with_store_load.cpp
transformation_replace_id_with_synonym.cpp
transformation_replace_linear_algebra_instruction.cpp
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index 01f54ed..205e190 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -77,6 +77,8 @@
const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
const std::pair<uint32_t, uint32_t> kChanceOfPermutingPhiOperands = {30, 90};
const std::pair<uint32_t, uint32_t> kChanceOfPushingIdThroughVariable = {5, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyMemoryWithLoadStore =
+ {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyObjectWithStoreLoad =
{20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
@@ -212,6 +214,8 @@
ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands);
chance_of_pushing_id_through_variable_ =
ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable);
+ chance_of_replacing_copy_memory_with_load_store_ =
+ ChooseBetweenMinAndMax(kChanceOfReplacingCopyMemoryWithLoadStore);
chance_of_replacing_copyobject_with_store_load_ =
ChooseBetweenMinAndMax(kChanceOfReplacingCopyObjectWithStoreLoad);
chance_of_replacing_id_with_synonym_ =
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index acc0ac4..8fc6c15 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -212,6 +212,9 @@
uint32_t GetChanceOfPushingIdThroughVariable() {
return chance_of_pushing_id_through_variable_;
}
+ uint32_t GetChanceOfReplacingCopyMemoryWithLoadStore() {
+ return chance_of_replacing_copy_memory_with_load_store_;
+ }
uint32_t GetChanceOfReplacingCopyObjectWithStoreLoad() {
return chance_of_replacing_copyobject_with_store_load_;
}
@@ -357,6 +360,7 @@
uint32_t chance_of_permuting_parameters_;
uint32_t chance_of_permuting_phi_operands_;
uint32_t chance_of_pushing_id_through_variable_;
+ uint32_t chance_of_replacing_copy_memory_with_load_store_;
uint32_t chance_of_replacing_copyobject_with_store_load_;
uint32_t chance_of_replacing_id_with_synonym_;
uint32_t chance_of_replacing_linear_algebra_instructions_;
diff --git a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
new file mode 100644
index 0000000..6847146
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
@@ -0,0 +1,58 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceCopyMemoriesWithLoadsStores::
+ FuzzerPassReplaceCopyMemoriesWithLoadsStores(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassReplaceCopyMemoriesWithLoadsStores::
+ ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() = default;
+
+void FuzzerPassReplaceCopyMemoriesWithLoadsStores::Apply() {
+ GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
+ // Randomly decide whether to replace the OpCopyMemory.
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()
+ ->GetChanceOfReplacingCopyMemoryWithLoadStore())) {
+ return;
+ }
+
+ // The instruction must be OpCopyMemory.
+ if (instruction->opcode() != SpvOpCopyMemory) {
+ return;
+ }
+
+ // Apply the transformation replacing OpCopyMemory with OpLoad and OpStore.
+ ApplyTransformation(TransformationReplaceCopyMemoryWithLoadStore(
+ GetFuzzerContext()->GetFreshId(),
+ MakeInstructionDescriptor(GetIRContext(), instruction)));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h
new file mode 100644
index 0000000..2a89006
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H
+#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Replaces instructions OpCopyMemory with loading the source variable to
+// an intermediate value and storing this value into the target variable of
+// the original OpCopyMemory instruction.
+class FuzzerPassReplaceCopyMemoriesWithLoadsStores : public FuzzerPass {
+ public:
+ FuzzerPassReplaceCopyMemoriesWithLoadsStores(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index 9a01a17..968e084 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -405,6 +405,7 @@
TransformationAddRelaxedDecoration add_relaxed_decoration = 58;
TransformationReplaceParamsWithStruct replace_params_with_struct = 59;
TransformationReplaceCopyObjectWithStoreLoad replace_copy_object_with_store_load = 60;
+ TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61;
// Add additional option using the next available number.
}
}
@@ -1275,6 +1276,20 @@
}
+message TransformationReplaceCopyMemoryWithLoadStore {
+
+ // A transformation that replaces instructions OpCopyMemory with loading
+ // the source variable to an intermediate value and storing this value into the
+ // target variable of the original OpCopyMemory instruction.
+
+ // The intermediate value.
+ uint32 fresh_id = 1;
+
+ // The instruction descriptor to OpCopyMemory. It is necessary, because
+ // OpCopyMemory doesn't have a result id.
+ InstructionDescriptor copy_memory_instruction_descriptor = 2;
+}
+
message TransformationReplaceCopyObjectWithStoreLoad {
// A transformation that replaces instruction OpCopyObject with
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index 53b0f0c..a9fa611 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -62,6 +62,7 @@
#include "source/fuzz/transformation_record_synonymous_constants.h"
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
+#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
#include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
#include "source/fuzz/transformation_replace_id_with_synonym.h"
#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
@@ -218,6 +219,10 @@
return MakeUnique<TransformationReplaceConstantWithUniform>(
message.replace_constant_with_uniform());
case protobufs::Transformation::TransformationCase::
+ kReplaceCopyMemoryWithLoadStore:
+ return MakeUnique<TransformationReplaceCopyMemoryWithLoadStore>(
+ message.replace_copy_memory_with_load_store());
+ case protobufs::Transformation::TransformationCase::
kReplaceCopyObjectWithStoreLoad:
return MakeUnique<TransformationReplaceCopyObjectWithStoreLoad>(
message.replace_copy_object_with_store_load());
diff --git a/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp b/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp
new file mode 100644
index 0000000..bf6996a
--- /dev/null
+++ b/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp
@@ -0,0 +1,127 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationReplaceCopyMemoryWithLoadStore::
+ TransformationReplaceCopyMemoryWithLoadStore(
+ const spvtools::fuzz::protobufs::
+ TransformationReplaceCopyMemoryWithLoadStore& message)
+ : message_(message) {}
+
+TransformationReplaceCopyMemoryWithLoadStore::
+ TransformationReplaceCopyMemoryWithLoadStore(
+ uint32_t fresh_id, const protobufs::InstructionDescriptor&
+ copy_memory_instruction_descriptor) {
+ message_.set_fresh_id(fresh_id);
+ *message_.mutable_copy_memory_instruction_descriptor() =
+ copy_memory_instruction_descriptor;
+}
+
+bool TransformationReplaceCopyMemoryWithLoadStore::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ // |message_.fresh_id| must be fresh.
+ if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
+ return false;
+ }
+ // The instruction to be replaced must be defined and have opcode
+ // OpCopyMemory.
+ auto copy_memory_instruction = FindInstruction(
+ message_.copy_memory_instruction_descriptor(), ir_context);
+ if (!copy_memory_instruction ||
+ copy_memory_instruction->opcode() != SpvOpCopyMemory) {
+ return false;
+ }
+ return true;
+}
+
+void TransformationReplaceCopyMemoryWithLoadStore::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ auto copy_memory_instruction = FindInstruction(
+ message_.copy_memory_instruction_descriptor(), ir_context);
+ // |copy_memory_instruction| must be defined.
+ assert(copy_memory_instruction &&
+ copy_memory_instruction->opcode() == SpvOpCopyMemory &&
+ "The required OpCopyMemory instruction must be defined.");
+
+ // Coherence check: Both operands must be pointers.
+
+ // Get types of ids used as a source and target of |copy_memory_instruction|.
+ auto target = ir_context->get_def_use_mgr()->GetDef(
+ copy_memory_instruction->GetSingleWordInOperand(0));
+ auto source = ir_context->get_def_use_mgr()->GetDef(
+ copy_memory_instruction->GetSingleWordInOperand(1));
+ auto target_type_opcode =
+ ir_context->get_def_use_mgr()->GetDef(target->type_id())->opcode();
+ auto source_type_opcode =
+ ir_context->get_def_use_mgr()->GetDef(source->type_id())->opcode();
+
+ // Keep release-mode compilers happy. (No unused variables.)
+ (void)target;
+ (void)source;
+ (void)target_type_opcode;
+ (void)source_type_opcode;
+
+ assert(target_type_opcode == SpvOpTypePointer &&
+ source_type_opcode == SpvOpTypePointer &&
+ "Operands must be of type OpTypePointer");
+
+ // Coherence check: |source| and |target| must point to the same type.
+ uint32_t target_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
+ ir_context, target->type_id());
+ uint32_t source_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
+ ir_context, source->type_id());
+
+ // Keep release-mode compilers happy. (No unused variables.)
+ (void)target_pointee_type;
+ (void)source_pointee_type;
+
+ assert(target_pointee_type == source_pointee_type &&
+ "Operands must have the same type to which they point to.");
+
+ // First, insert the OpStore instruction before the OpCopyMemory instruction
+ // and then insert the OpLoad instruction before the OpStore instruction.
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+ FindInstruction(message_.copy_memory_instruction_descriptor(), ir_context)
+ ->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpStore, 0, 0,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {target->result_id()}},
+ {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}})))
+ ->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpLoad, target_pointee_type, message_.fresh_id(),
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {source->result_id()}}})));
+
+ // Remove the OpCopyMemory instruction.
+ ir_context->KillInst(copy_memory_instruction);
+
+ ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation
+TransformationReplaceCopyMemoryWithLoadStore::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_replace_copy_memory_with_load_store() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_replace_copy_memory_with_load_store.h b/source/fuzz/transformation_replace_copy_memory_with_load_store.h
new file mode 100644
index 0000000..70120f8
--- /dev/null
+++ b/source/fuzz/transformation_replace_copy_memory_with_load_store.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H
+#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_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 TransformationReplaceCopyMemoryWithLoadStore : public Transformation {
+ public:
+ explicit TransformationReplaceCopyMemoryWithLoadStore(
+ const protobufs::TransformationReplaceCopyMemoryWithLoadStore& message);
+
+ TransformationReplaceCopyMemoryWithLoadStore(
+ uint32_t fresh_id, const protobufs::InstructionDescriptor&
+ copy_memory_instruction_descriptor);
+
+ // - |message_.fresh_id| must be fresh.
+ // - |message_.copy_memory_instruction_descriptor| must refer to an
+ // OpCopyMemory instruction.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Replaces instruction OpCopyMemory with loading the source variable to an
+ // intermediate value and storing this value into the target variable of the
+ // original OpCopyMemory instruction.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationReplaceCopyMemoryWithLoadStore message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 34afd7f..2d0404e 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -70,6 +70,7 @@
transformation_replace_boolean_constant_with_constant_binary_test.cpp
transformation_replace_copy_object_with_store_load_test.cpp
transformation_replace_constant_with_uniform_test.cpp
+ transformation_replace_copy_memory_with_load_store_test.cpp
transformation_replace_id_with_synonym_test.cpp
transformation_replace_linear_algebra_instruction_test.cpp
transformation_replace_params_with_struct_test.cpp
diff --git a/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp b/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp
new file mode 100644
index 0000000..2bbe605
--- /dev/null
+++ b/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp
@@ -0,0 +1,151 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationReplaceCopyMemoryWithLoadStoreTest, BasicScenarios) {
+ // This is a simple transformation and this test handles the main cases.
+
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %10 "b"
+ OpName %14 "c"
+ OpName %16 "d"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 2
+ %11 = OpConstant %6 3
+ %12 = OpTypeFloat 32
+ %13 = OpTypePointer Function %12
+ %15 = OpConstant %12 2
+ %17 = OpConstant %12 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %14 = OpVariable %13 Function
+ %16 = OpVariable %13 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ OpStore %14 %15
+ OpStore %16 %17
+ OpCopyMemory %8 %10
+ OpCopyMemory %16 %14
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto instruction_descriptor_invalid_1 =
+ MakeInstructionDescriptor(5, SpvOpStore, 0);
+ auto instruction_descriptor_valid_1 =
+ MakeInstructionDescriptor(5, SpvOpCopyMemory, 0);
+ auto instruction_descriptor_valid_2 =
+ MakeInstructionDescriptor(5, SpvOpCopyMemory, 0);
+
+ // Invalid: |source_id| is not a fresh id.
+ auto transformation_invalid_1 = TransformationReplaceCopyMemoryWithLoadStore(
+ 15, instruction_descriptor_valid_1);
+ ASSERT_FALSE(transformation_invalid_1.IsApplicable(context.get(),
+ transformation_context));
+
+ // Invalid: |instruction_descriptor_invalid| refers to an instruction OpStore.
+ auto transformation_invalid_2 = TransformationReplaceCopyMemoryWithLoadStore(
+ 20, instruction_descriptor_invalid_1);
+ ASSERT_FALSE(transformation_invalid_2.IsApplicable(context.get(),
+ transformation_context));
+
+ auto transformation_valid_1 = TransformationReplaceCopyMemoryWithLoadStore(
+ 20, instruction_descriptor_valid_1);
+ ASSERT_TRUE(transformation_valid_1.IsApplicable(context.get(),
+ transformation_context));
+ transformation_valid_1.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation_valid_2 = TransformationReplaceCopyMemoryWithLoadStore(
+ 21, instruction_descriptor_valid_2);
+ ASSERT_TRUE(transformation_valid_2.IsApplicable(context.get(),
+ transformation_context));
+ transformation_valid_2.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %10 "b"
+ OpName %14 "c"
+ OpName %16 "d"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 2
+ %11 = OpConstant %6 3
+ %12 = OpTypeFloat 32
+ %13 = OpTypePointer Function %12
+ %15 = OpConstant %12 2
+ %17 = OpConstant %12 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %14 = OpVariable %13 Function
+ %16 = OpVariable %13 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ OpStore %14 %15
+ OpStore %16 %17
+ %20 = OpLoad %6 %10
+ OpStore %8 %20
+ %21 = OpLoad %12 %14
+ OpStore %16 %21
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools