spirv-fuzz: Add toggle access chain instruction transformation (#3211)
In this PR, the classes that represent the toggle access chain
instruction transformation and fuzzer pass were implemented. This
transformation toggles the instructions OpAccessChain and
OpInBoundsAccessChain between them.
Fixes #3193.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index 8fd0593..3a9d604 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -65,6 +65,7 @@
fuzzer_pass_permute_function_parameters.h
fuzzer_pass_split_blocks.h
fuzzer_pass_swap_commutable_operands.h
+ fuzzer_pass_toggle_access_chain_instruction.h
fuzzer_util.h
id_use_descriptor.h
instruction_descriptor.h
@@ -116,6 +117,7 @@
transformation_split_block.h
transformation_store.h
transformation_swap_commutable_operands.h
+ transformation_toggle_access_chain_instruction.h
transformation_vector_shuffle.h
uniform_buffer_element_descriptor.h
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
@@ -155,6 +157,7 @@
fuzzer_pass_permute_function_parameters.cpp
fuzzer_pass_split_blocks.cpp
fuzzer_pass_swap_commutable_operands.cpp
+ fuzzer_pass_toggle_access_chain_instruction.cpp
fuzzer_util.cpp
id_use_descriptor.cpp
instruction_descriptor.cpp
@@ -205,6 +208,7 @@
transformation_split_block.cpp
transformation_store.cpp
transformation_swap_commutable_operands.cpp
+ transformation_toggle_access_chain_instruction.cpp
transformation_vector_shuffle.cpp
uniform_buffer_element_descriptor.cpp
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index a89c361..119bd3c 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -48,6 +48,7 @@
#include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
#include "source/fuzz/fuzzer_pass_split_blocks.h"
#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
+#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/pseudo_random_generator.h"
#include "source/opt/build_module.h"
@@ -288,6 +289,9 @@
MaybeAddPass<FuzzerPassSwapCommutableOperands>(
&final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassToggleAccessChainInstruction>(
+ &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+ transformation_sequence_out);
for (auto& pass : final_passes) {
if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) {
return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index dd2d7c3..2f9fc5a 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -63,6 +63,8 @@
const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
+const std::pair<uint32_t, uint32_t> kChanceOfTogglingAccessChainInstruction = {
+ 20, 90};
// Default limits for various quantities that are chosen during fuzzing.
// Keep them in alphabetical order.
@@ -152,6 +154,8 @@
chance_of_replacing_id_with_synonym_ =
ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
+ chance_of_toggling_access_chain_instruction_ =
+ ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction);
}
FuzzerContext::~FuzzerContext() = default;
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index 813f232..1529705 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -180,6 +180,9 @@
return chance_of_replacing_id_with_synonym_;
}
uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
+ uint32_t GetChanceOfTogglingAccessChainInstruction() {
+ return chance_of_toggling_access_chain_instruction_;
+ }
uint32_t GetRandomLoopControlPeelCount() {
return random_generator_->RandomUint32(max_loop_control_peel_count_);
}
@@ -243,6 +246,7 @@
uint32_t chance_of_permuting_parameters_;
uint32_t chance_of_replacing_id_with_synonym_;
uint32_t chance_of_splitting_block_;
+ uint32_t chance_of_toggling_access_chain_instruction_;
// Limits associated with various quantities for which random values are
// chosen during fuzzing.
diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
new file mode 100644
index 0000000..9fb175b
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
@@ -0,0 +1,54 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_toggle_access_chain_instruction.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassToggleAccessChainInstruction::FuzzerPassToggleAccessChainInstruction(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassToggleAccessChainInstruction::
+ ~FuzzerPassToggleAccessChainInstruction() = default;
+
+void FuzzerPassToggleAccessChainInstruction::Apply() {
+ auto context = GetIRContext();
+ // Iterates over the module's instructions and checks whether it is
+ // OpAccessChain or OpInBoundsAccessChain. In this case, the transformation is
+ // probabilistically applied.
+ context->module()->ForEachInst([this,
+ context](opt::Instruction* instruction) {
+ SpvOp opcode = instruction->opcode();
+ if ((opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) &&
+ GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfTogglingAccessChainInstruction())) {
+ auto instructionDescriptor =
+ MakeInstructionDescriptor(context, instruction);
+ auto transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ApplyTransformation(transformation);
+ }
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
new file mode 100644
index 0000000..ec8c3f7
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
+#define SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// This fuzzer pass searches for all access chain instructions in the module,
+// probabilistically choosing which of these instructions will be toggled.
+class FuzzerPassToggleAccessChainInstruction : public FuzzerPass {
+ public:
+ FuzzerPassToggleAccessChainInstruction(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassToggleAccessChainInstruction();
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index 83dd2cf..b816e3b 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -371,6 +371,7 @@
TransformationEquationInstruction equation_instruction = 40;
TransformationSwapCommutableOperands swap_commutable_operands = 41;
TransformationPermuteFunctionParameters permute_function_parameters = 42;
+ TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 43;
// Add additional option using the next available number.
}
}
@@ -1109,6 +1110,15 @@
}
+message TransformationToggleAccessChainInstruction {
+
+ // A transformation that toggles an access chain instruction.
+
+ // A descriptor for an access chain instruction
+ InstructionDescriptor instruction_descriptor = 1;
+
+}
+
message TransformationVectorShuffle {
// A transformation that adds a vector shuffle instruction.
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index e06999c..f18c86b 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -58,6 +58,7 @@
#include "source/fuzz/transformation_split_block.h"
#include "source/fuzz/transformation_store.h"
#include "source/fuzz/transformation_swap_commutable_operands.h"
+#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
#include "source/fuzz/transformation_vector_shuffle.h"
#include "source/util/make_unique.h"
@@ -179,6 +180,10 @@
case protobufs::Transformation::TransformationCase::kSwapCommutableOperands:
return MakeUnique<TransformationSwapCommutableOperands>(
message.swap_commutable_operands());
+ case protobufs::Transformation::TransformationCase::
+ kToggleAccessChainInstruction:
+ return MakeUnique<TransformationToggleAccessChainInstruction>(
+ message.toggle_access_chain_instruction());
case protobufs::Transformation::TransformationCase::kVectorShuffle:
return MakeUnique<TransformationVectorShuffle>(message.vector_shuffle());
case protobufs::Transformation::TRANSFORMATION_NOT_SET:
diff --git a/source/fuzz/transformation_toggle_access_chain_instruction.cpp b/source/fuzz/transformation_toggle_access_chain_instruction.cpp
new file mode 100644
index 0000000..ace331a
--- /dev/null
+++ b/source/fuzz/transformation_toggle_access_chain_instruction.cpp
@@ -0,0 +1,83 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_toggle_access_chain_instruction.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationToggleAccessChainInstruction::
+ TransformationToggleAccessChainInstruction(
+ const spvtools::fuzz::protobufs::
+ TransformationToggleAccessChainInstruction& message)
+ : message_(message) {}
+
+TransformationToggleAccessChainInstruction::
+ TransformationToggleAccessChainInstruction(
+ const protobufs::InstructionDescriptor& instruction_descriptor) {
+ *message_.mutable_instruction_descriptor() = instruction_descriptor;
+}
+
+bool TransformationToggleAccessChainInstruction::IsApplicable(
+ opt::IRContext* context, const spvtools::fuzz::FactManager& /*unused*/
+ ) const {
+ auto instruction =
+ FindInstruction(message_.instruction_descriptor(), context);
+ if (instruction == nullptr) {
+ return false;
+ }
+
+ SpvOp opcode = static_cast<SpvOp>(
+ message_.instruction_descriptor().target_instruction_opcode());
+
+ assert(instruction->opcode() == opcode &&
+ "The located instruction must have the same opcode as in the "
+ "descriptor.");
+
+ if (opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) {
+ return true;
+ }
+
+ return false;
+}
+
+void TransformationToggleAccessChainInstruction::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/
+ ) const {
+ auto instruction =
+ FindInstruction(message_.instruction_descriptor(), context);
+ SpvOp opcode = instruction->opcode();
+
+ if (opcode == SpvOpAccessChain) {
+ instruction->SetOpcode(SpvOpInBoundsAccessChain);
+ } else {
+ assert(opcode == SpvOpInBoundsAccessChain &&
+ "The located instruction must be an OpInBoundsAccessChain "
+ "instruction.");
+ instruction->SetOpcode(SpvOpAccessChain);
+ }
+}
+
+protobufs::Transformation
+TransformationToggleAccessChainInstruction::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_toggle_access_chain_instruction() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_toggle_access_chain_instruction.h b/source/fuzz/transformation_toggle_access_chain_instruction.h
new file mode 100644
index 0000000..125e1ab
--- /dev/null
+++ b/source/fuzz/transformation_toggle_access_chain_instruction.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationToggleAccessChainInstruction : public Transformation {
+ public:
+ explicit TransformationToggleAccessChainInstruction(
+ const protobufs::TransformationToggleAccessChainInstruction& message);
+
+ TransformationToggleAccessChainInstruction(
+ const protobufs::InstructionDescriptor& instruction_descriptor);
+
+ // - |message_.instruction_descriptor| must identify an existing
+ // access chain instruction
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // Toggles the access chain instruction.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationToggleAccessChainInstruction message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 1e26e74..99a78fd 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -65,6 +65,7 @@
transformation_split_block_test.cpp
transformation_store_test.cpp
transformation_swap_commutable_operands_test.cpp
+ transformation_toggle_access_chain_instruction_test.cpp
transformation_vector_shuffle_test.cpp
uniform_buffer_element_descriptor_test.cpp)
diff --git a/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp b/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp
new file mode 100644
index 0000000..98e0a64
--- /dev/null
+++ b/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp
@@ -0,0 +1,413 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_toggle_access_chain_instruction.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationToggleAccessChainInstructionTest, IsApplicableTest) {
+ 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"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %8 = OpConstant %7 2
+ %9 = OpTypeArray %6 %8
+ %10 = OpTypePointer Function %9
+ %12 = OpConstant %6 1
+ %13 = OpConstant %6 2
+ %14 = OpConstantComposite %9 %12 %13
+ %15 = OpTypePointer Function %6
+ %17 = OpConstant %6 0
+ %29 = OpTypeFloat 32
+ %30 = OpTypeArray %29 %8
+ %31 = OpTypePointer Function %30
+ %33 = OpConstant %29 1
+ %34 = OpConstant %29 2
+ %35 = OpConstantComposite %30 %33 %34
+ %36 = OpTypePointer Function %29
+ %49 = OpTypeVector %29 3
+ %50 = OpTypeArray %49 %8
+ %51 = OpTypePointer Function %50
+ %53 = OpConstant %29 3
+ %54 = OpConstantComposite %49 %33 %34 %53
+ %55 = OpConstant %29 4
+ %56 = OpConstant %29 5
+ %57 = OpConstant %29 6
+ %58 = OpConstantComposite %49 %55 %56 %57
+ %59 = OpConstantComposite %50 %54 %58
+ %61 = OpTypePointer Function %49
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpVariable %10 Function
+ %16 = OpVariable %15 Function
+ %23 = OpVariable %15 Function
+ %32 = OpVariable %31 Function
+ %37 = OpVariable %36 Function
+ %43 = OpVariable %36 Function
+ %52 = OpVariable %51 Function
+ %60 = OpVariable %36 Function
+ OpStore %11 %14
+ %18 = OpAccessChain %15 %11 %17
+ %19 = OpLoad %6 %18
+ %20 = OpInBoundsAccessChain %15 %11 %12
+ %21 = OpLoad %6 %20
+ %22 = OpIAdd %6 %19 %21
+ OpStore %16 %22
+ %24 = OpAccessChain %15 %11 %17
+ %25 = OpLoad %6 %24
+ %26 = OpInBoundsAccessChain %15 %11 %12
+ %27 = OpLoad %6 %26
+ %28 = OpIMul %6 %25 %27
+ OpStore %23 %28
+ OpStore %32 %35
+ %38 = OpAccessChain %36 %32 %17
+ %39 = OpLoad %29 %38
+ %40 = OpAccessChain %36 %32 %12
+ %41 = OpLoad %29 %40
+ %42 = OpFAdd %29 %39 %41
+ OpStore %37 %42
+ %44 = OpAccessChain %36 %32 %17
+ %45 = OpLoad %29 %44
+ %46 = OpAccessChain %36 %32 %12
+ %47 = OpLoad %29 %46
+ %48 = OpFMul %29 %45 %47
+ OpStore %43 %48
+ OpStore %52 %59
+ %62 = OpAccessChain %61 %52 %17
+ %63 = OpLoad %49 %62
+ %64 = OpAccessChain %61 %52 %12
+ %65 = OpLoad %49 %64
+ %66 = OpDot %29 %63 %65
+ OpStore %60 %66
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager factManager;
+
+ // Tests existing access chain instructions
+ auto instructionDescriptor =
+ MakeInstructionDescriptor(18, SpvOpAccessChain, 0);
+ auto transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+ // Tests existing non-access chain instructions
+ instructionDescriptor = MakeInstructionDescriptor(1, SpvOpExtInstImport, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(5, SpvOpLabel, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(14, SpvOpConstantComposite, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ // Tests the base instruction id not existing
+ instructionDescriptor = MakeInstructionDescriptor(67, SpvOpAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(68, SpvOpAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(69, SpvOpInBoundsAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ // Tests there being no instruction with the desired opcode after the base
+ // instruction id
+ instructionDescriptor = MakeInstructionDescriptor(65, SpvOpAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(66, SpvOpInBoundsAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ // Tests there being an instruction with the desired opcode after the base
+ // instruction id, but the skip count associated with the instruction
+ // descriptor being so high.
+ instructionDescriptor = MakeInstructionDescriptor(11, SpvOpAccessChain, 100);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(16, SpvOpInBoundsAccessChain, 100);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+}
+
+TEST(TransformationToggleAccessChainInstructionTest, ApplyTest) {
+ 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"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %8 = OpConstant %7 2
+ %9 = OpTypeArray %6 %8
+ %10 = OpTypePointer Function %9
+ %12 = OpConstant %6 1
+ %13 = OpConstant %6 2
+ %14 = OpConstantComposite %9 %12 %13
+ %15 = OpTypePointer Function %6
+ %17 = OpConstant %6 0
+ %29 = OpTypeFloat 32
+ %30 = OpTypeArray %29 %8
+ %31 = OpTypePointer Function %30
+ %33 = OpConstant %29 1
+ %34 = OpConstant %29 2
+ %35 = OpConstantComposite %30 %33 %34
+ %36 = OpTypePointer Function %29
+ %49 = OpTypeVector %29 3
+ %50 = OpTypeArray %49 %8
+ %51 = OpTypePointer Function %50
+ %53 = OpConstant %29 3
+ %54 = OpConstantComposite %49 %33 %34 %53
+ %55 = OpConstant %29 4
+ %56 = OpConstant %29 5
+ %57 = OpConstant %29 6
+ %58 = OpConstantComposite %49 %55 %56 %57
+ %59 = OpConstantComposite %50 %54 %58
+ %61 = OpTypePointer Function %49
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpVariable %10 Function
+ %16 = OpVariable %15 Function
+ %23 = OpVariable %15 Function
+ %32 = OpVariable %31 Function
+ %37 = OpVariable %36 Function
+ %43 = OpVariable %36 Function
+ %52 = OpVariable %51 Function
+ %60 = OpVariable %36 Function
+ OpStore %11 %14
+ %18 = OpAccessChain %15 %11 %17
+ %19 = OpLoad %6 %18
+ %20 = OpInBoundsAccessChain %15 %11 %12
+ %21 = OpLoad %6 %20
+ %22 = OpIAdd %6 %19 %21
+ OpStore %16 %22
+ %24 = OpAccessChain %15 %11 %17
+ %25 = OpLoad %6 %24
+ %26 = OpInBoundsAccessChain %15 %11 %12
+ %27 = OpLoad %6 %26
+ %28 = OpIMul %6 %25 %27
+ OpStore %23 %28
+ OpStore %32 %35
+ %38 = OpAccessChain %36 %32 %17
+ %39 = OpLoad %29 %38
+ %40 = OpAccessChain %36 %32 %12
+ %41 = OpLoad %29 %40
+ %42 = OpFAdd %29 %39 %41
+ OpStore %37 %42
+ %44 = OpAccessChain %36 %32 %17
+ %45 = OpLoad %29 %44
+ %46 = OpAccessChain %36 %32 %12
+ %47 = OpLoad %29 %46
+ %48 = OpFMul %29 %45 %47
+ OpStore %43 %48
+ OpStore %52 %59
+ %62 = OpAccessChain %61 %52 %17
+ %63 = OpLoad %49 %62
+ %64 = OpAccessChain %61 %52 %12
+ %65 = OpLoad %49 %64
+ %66 = OpDot %29 %63 %65
+ OpStore %60 %66
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager factManager;
+
+ auto instructionDescriptor =
+ MakeInstructionDescriptor(18, SpvOpAccessChain, 0);
+ auto transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ transformation.Apply(context.get(), &factManager);
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ transformation.Apply(context.get(), &factManager);
+
+ instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ transformation.Apply(context.get(), &factManager);
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ transformation.Apply(context.get(), &factManager);
+
+ instructionDescriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ transformation.Apply(context.get(), &factManager);
+
+ std::string variantShader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %8 = OpConstant %7 2
+ %9 = OpTypeArray %6 %8
+ %10 = OpTypePointer Function %9
+ %12 = OpConstant %6 1
+ %13 = OpConstant %6 2
+ %14 = OpConstantComposite %9 %12 %13
+ %15 = OpTypePointer Function %6
+ %17 = OpConstant %6 0
+ %29 = OpTypeFloat 32
+ %30 = OpTypeArray %29 %8
+ %31 = OpTypePointer Function %30
+ %33 = OpConstant %29 1
+ %34 = OpConstant %29 2
+ %35 = OpConstantComposite %30 %33 %34
+ %36 = OpTypePointer Function %29
+ %49 = OpTypeVector %29 3
+ %50 = OpTypeArray %49 %8
+ %51 = OpTypePointer Function %50
+ %53 = OpConstant %29 3
+ %54 = OpConstantComposite %49 %33 %34 %53
+ %55 = OpConstant %29 4
+ %56 = OpConstant %29 5
+ %57 = OpConstant %29 6
+ %58 = OpConstantComposite %49 %55 %56 %57
+ %59 = OpConstantComposite %50 %54 %58
+ %61 = OpTypePointer Function %49
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpVariable %10 Function
+ %16 = OpVariable %15 Function
+ %23 = OpVariable %15 Function
+ %32 = OpVariable %31 Function
+ %37 = OpVariable %36 Function
+ %43 = OpVariable %36 Function
+ %52 = OpVariable %51 Function
+ %60 = OpVariable %36 Function
+ OpStore %11 %14
+ %18 = OpInBoundsAccessChain %15 %11 %17
+ %19 = OpLoad %6 %18
+ %20 = OpAccessChain %15 %11 %12
+ %21 = OpLoad %6 %20
+ %22 = OpIAdd %6 %19 %21
+ OpStore %16 %22
+ %24 = OpInBoundsAccessChain %15 %11 %17
+ %25 = OpLoad %6 %24
+ %26 = OpAccessChain %15 %11 %12
+ %27 = OpLoad %6 %26
+ %28 = OpIMul %6 %25 %27
+ OpStore %23 %28
+ OpStore %32 %35
+ %38 = OpInBoundsAccessChain %36 %32 %17
+ %39 = OpLoad %29 %38
+ %40 = OpAccessChain %36 %32 %12
+ %41 = OpLoad %29 %40
+ %42 = OpFAdd %29 %39 %41
+ OpStore %37 %42
+ %44 = OpAccessChain %36 %32 %17
+ %45 = OpLoad %29 %44
+ %46 = OpAccessChain %36 %32 %12
+ %47 = OpLoad %29 %46
+ %48 = OpFMul %29 %45 %47
+ OpStore %43 %48
+ OpStore %52 %59
+ %62 = OpAccessChain %61 %52 %17
+ %63 = OpLoad %49 %62
+ %64 = OpAccessChain %61 %52 %12
+ %65 = OpLoad %49 %64
+ %66 = OpDot %29 %63 %65
+ OpStore %60 %66
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, variantShader, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools