spirv-fuzz: Permute OpPhi instruction operands (#3421)

Fixes #3415.
This commit is contained in:
Vasyl Teliman 2020-06-23 17:00:28 +03:00 committed by GitHub
parent 36b5bb701d
commit 8e586e46a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 495 additions and 15 deletions

View File

@ -64,6 +64,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_outline_functions.h
fuzzer_pass_permute_blocks.h
fuzzer_pass_permute_function_parameters.h
fuzzer_pass_permute_phi_operands.h
fuzzer_pass_push_ids_through_variables.h
fuzzer_pass_replace_linear_algebra_instructions.h
fuzzer_pass_split_blocks.h
@ -116,6 +117,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_move_block_down.h
transformation_outline_function.h
transformation_permute_function_parameters.h
transformation_permute_phi_operands.h
transformation_push_id_through_variable.h
transformation_replace_boolean_constant_with_constant_binary.h
transformation_replace_constant_with_uniform.h
@ -168,6 +170,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_outline_functions.cpp
fuzzer_pass_permute_blocks.cpp
fuzzer_pass_permute_function_parameters.cpp
fuzzer_pass_permute_phi_operands.cpp
fuzzer_pass_push_ids_through_variables.cpp
fuzzer_pass_replace_linear_algebra_instructions.cpp
fuzzer_pass_split_blocks.cpp
@ -219,6 +222,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_move_block_down.cpp
transformation_outline_function.cpp
transformation_permute_function_parameters.cpp
transformation_permute_phi_operands.cpp
transformation_push_id_through_variable.cpp
transformation_replace_boolean_constant_with_constant_binary.cpp
transformation_replace_constant_with_uniform.cpp

View File

@ -47,6 +47,7 @@
#include "source/fuzz/fuzzer_pass_outline_functions.h"
#include "source/fuzz/fuzzer_pass_permute_blocks.h"
#include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
#include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h"
#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
#include "source/fuzz/fuzzer_pass_split_blocks.h"
@ -305,6 +306,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassPermutePhiOperands>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassSwapCommutableOperands>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);

View File

@ -63,6 +63,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
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> kChanceOfReplacingIdWithSynonym = {10, 90};
const std::pair<uint32_t, uint32_t>
@ -165,6 +166,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
chance_of_permuting_parameters_ =
ChooseBetweenMinAndMax(kChanceOfPermutingParameters);
chance_of_permuting_phi_operands_ =
ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands);
chance_of_pushing_id_through_variable_ =
ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable);
chance_of_replacing_id_with_synonym_ =

View File

@ -185,6 +185,9 @@ class FuzzerContext {
uint32_t GetChanceOfPermutingParameters() {
return chance_of_permuting_parameters_;
}
uint32_t GetChanceOfPermutingPhiOperands() {
return chance_of_permuting_phi_operands_;
}
uint32_t GetChanceOfPushingIdThroughVariable() {
return chance_of_pushing_id_through_variable_;
}
@ -290,6 +293,7 @@ class FuzzerContext {
uint32_t chance_of_obfuscating_constant_;
uint32_t chance_of_outlining_function_;
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_id_with_synonym_;
uint32_t chance_of_replacing_linear_algebra_instructions_;

View File

@ -0,0 +1,64 @@
// 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 <numeric>
#include <vector>
#include "source/fuzz/fuzzer_context.h"
#include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_permute_phi_operands.h"
namespace spvtools {
namespace fuzz {
FuzzerPassPermutePhiOperands::FuzzerPassPermutePhiOperands(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
transformations) {}
FuzzerPassPermutePhiOperands::~FuzzerPassPermutePhiOperands() = default;
void FuzzerPassPermutePhiOperands::Apply() {
ForEachInstructionWithInstructionDescriptor(
[this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
opt::BasicBlock::iterator inst_it,
const protobufs::InstructionDescriptor& /*unused*/) {
const auto& inst = *inst_it;
if (inst.opcode() != SpvOpPhi) {
return;
}
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfPermutingPhiOperands())) {
return;
}
// Create a vector of indices for each pair of operands in the |inst|.
// OpPhi always has an even number of operands.
std::vector<uint32_t> permutation(inst.NumInOperands() / 2);
std::iota(permutation.begin(), permutation.end(), 0);
GetFuzzerContext()->Shuffle(&permutation);
ApplyTransformation(TransformationPermutePhiOperands(
inst.result_id(), std::move(permutation)));
});
}
} // namespace fuzz
} // namespace spvtools

View File

@ -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_PERMUTE_PHI_OPERANDS_H_
#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// Iterates over all instructions in the module and randomly decides for each
// OpPhi instruction whether to permute its operands.
class FuzzerPassPermutePhiOperands : public FuzzerPass {
public:
FuzzerPassPermutePhiOperands(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassPermutePhiOperands() override;
void Apply() override;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_

View File

@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <unordered_set>
#include "source/fuzz/fuzzer_util.h"
#include "source/opt/build_module.h"
@ -652,6 +655,26 @@ void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
{SPV_OPERAND_TYPE_ID, {initializer_id}}}));
}
bool HasDuplicates(const std::vector<uint32_t>& arr) {
return std::unordered_set<uint32_t>(arr.begin(), arr.end()).size() !=
arr.size();
}
bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
uint32_t hi) {
if (arr.empty()) {
return lo > hi;
}
if (HasDuplicates(arr)) {
return false;
}
auto min_max = std::minmax_element(arr.begin(), arr.end());
return arr.size() == hi - lo + 1 && *min_max.first == lo &&
*min_max.second == hi;
}
} // namespace fuzzerutil
} // namespace fuzz

View File

@ -252,6 +252,15 @@ void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
uint32_t type_id, uint32_t function_id,
uint32_t initializer_id);
// Returns true if the vector |arr| has duplicates.
bool HasDuplicates(const std::vector<uint32_t>& arr);
// Checks that the given vector |arr| contains a permutation of a range
// [lo, hi]. That being said, all elements in the range are present without
// duplicates. If |arr| is empty, returns true iff |lo > hi|.
bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
uint32_t hi);
} // namespace fuzzerutil
} // namespace fuzz

View File

@ -379,6 +379,7 @@ message Transformation {
TransformationAddSpecConstantOp add_spec_constant_op = 48;
TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 49;
TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 50;
TransformationPermutePhiOperands permute_phi_operands = 51;
// Add additional option using the next available number.
}
}
@ -1005,6 +1006,19 @@ message TransformationPermuteFunctionParameters {
}
message TransformationPermutePhiOperands {
// Permutes operands of some OpPhi instruction.
// Result id of the instruction to apply the transformation to.
uint32 result_id = 1;
// A sequence of numbers in the range [0, n/2 - 1] where |n| is the number
// of operands of the OpPhi instruction with |result_id|.
repeated uint32 permutation = 2;
}
message TransformationPushIdThroughVariable {
// A transformation that makes |value_synonym_id| and |value_id| to be

View File

@ -52,6 +52,7 @@
#include "source/fuzz/transformation_move_block_down.h"
#include "source/fuzz/transformation_outline_function.h"
#include "source/fuzz/transformation_permute_function_parameters.h"
#include "source/fuzz/transformation_permute_phi_operands.h"
#include "source/fuzz/transformation_push_id_through_variable.h"
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
@ -170,6 +171,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
kPermuteFunctionParameters:
return MakeUnique<TransformationPermuteFunctionParameters>(
message.permute_function_parameters());
case protobufs::Transformation::TransformationCase::kPermutePhiOperands:
return MakeUnique<TransformationPermutePhiOperands>(
message.permute_phi_operands());
case protobufs::Transformation::TransformationCase::kPushIdThroughVariable:
return MakeUnique<TransformationPushIdThroughVariable>(
message.push_id_through_variable());

View File

@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <unordered_set>
#include <vector>
#include "source/fuzz/fuzzer_util.h"
@ -53,7 +52,8 @@ bool TransformationPermuteFunctionParameters::IsApplicable(
const auto* function_type = fuzzerutil::GetFunctionType(ir_context, function);
assert(function_type && "Function type is null");
const auto& permutation = message_.permutation();
std::vector<uint32_t> permutation(message_.permutation().begin(),
message_.permutation().end());
// Don't take return type into account
auto arg_size = function_type->NumInOperands() - 1;
@ -63,21 +63,20 @@ bool TransformationPermuteFunctionParameters::IsApplicable(
return false;
}
// Check that all indices are valid
// and unique integers from the [0, n-1] set
std::unordered_set<uint32_t> unique_indices;
for (auto index : permutation) {
// We don't compare |index| with 0 since it's an unsigned integer
if (index >= arg_size) {
return false;
}
// Check that permutation doesn't have duplicated values.
assert(!fuzzerutil::HasDuplicates(permutation) &&
"Permutation has duplicates");
unique_indices.insert(index);
// Check that elements in permutation are in range [0, arg_size - 1].
//
// We must check whether the permutation is empty first because in that case
// |arg_size - 1| will produce |std::numeric_limits<uint32_t>::max()| since
// it's an unsigned integer.
if (!permutation.empty() &&
!fuzzerutil::IsPermutationOfRange(permutation, 0, arg_size - 1)) {
return false;
}
// Check that permutation doesn't have duplicated values
assert(unique_indices.size() == arg_size && "Permutation has duplicates");
// Check that new function's type is valid:
// - Has the same number of operands
// - Has the same result type as the old one

View File

@ -0,0 +1,94 @@
// 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 <vector>
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/transformation_permute_phi_operands.h"
namespace spvtools {
namespace fuzz {
TransformationPermutePhiOperands::TransformationPermutePhiOperands(
const spvtools::fuzz::protobufs::TransformationPermutePhiOperands& message)
: message_(message) {}
TransformationPermutePhiOperands::TransformationPermutePhiOperands(
uint32_t result_id, const std::vector<uint32_t>& permutation) {
message_.set_result_id(result_id);
for (auto index : permutation) {
message_.add_permutation(index);
}
}
bool TransformationPermutePhiOperands::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
// Check that |message_.result_id| is valid.
const auto* inst =
ir_context->get_def_use_mgr()->GetDef(message_.result_id());
if (!inst || inst->opcode() != SpvOpPhi) {
return false;
}
// Check that |message_.permutation| has expected size.
auto expected_permutation_size = inst->NumInOperands() / 2;
if (static_cast<uint32_t>(message_.permutation().size()) !=
expected_permutation_size) {
return false;
}
// Check that |message_.permutation| has elements in range
// [0, expected_permutation_size - 1].
std::vector<uint32_t> permutation(message_.permutation().begin(),
message_.permutation().end());
assert(!fuzzerutil::HasDuplicates(permutation) &&
"Permutation has duplicates");
// We must check whether the permutation is empty first because in that case
// |expected_permutation_size - 1| will produce
// |std::numeric_limits<uint32_t>::max()| since it's an unsigned integer.
return permutation.empty() ||
fuzzerutil::IsPermutationOfRange(permutation, 0,
expected_permutation_size - 1);
}
void TransformationPermutePhiOperands::Apply(
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
assert(inst);
opt::Instruction::OperandList permuted_operands;
permuted_operands.reserve(inst->NumInOperands());
for (auto index : message_.permutation()) {
permuted_operands.push_back(std::move(inst->GetInOperand(2 * index)));
permuted_operands.push_back(std::move(inst->GetInOperand(2 * index + 1)));
}
inst->SetInOperands(std::move(permuted_operands));
// Make sure our changes are analyzed
ir_context->InvalidateAnalysesExceptFor(
opt::IRContext::Analysis::kAnalysisNone);
}
protobufs::Transformation TransformationPermutePhiOperands::ToMessage() const {
protobufs::Transformation result;
*result.mutable_permute_phi_operands() = message_;
return result;
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,56 @@
// 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_PERMUTE_PHI_OPERANDS_H_
#define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_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 TransformationPermutePhiOperands : public Transformation {
public:
explicit TransformationPermutePhiOperands(
const protobufs::TransformationPermutePhiOperands& message);
TransformationPermutePhiOperands(uint32_t result_id,
const std::vector<uint32_t>& permutation);
// - |result_id| must be a valid id of some OpPhi instruction in the module.
// - |permutation| must contain elements in the range [0, n/2 - 1] where |n|
// is a number of operands to the instruction with |result_id|. All elements
// must be unique (i.e. |permutation.size() == n / 2|).
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// Permutes operands of the OpPhi instruction with |result_id| according to
// the elements in |permutation|.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationPermutePhiOperands message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_

View File

@ -58,6 +58,7 @@ if (${SPIRV_BUILD_FUZZER})
transformation_move_block_down_test.cpp
transformation_outline_function_test.cpp
transformation_permute_function_parameters_test.cpp
transformation_permute_phi_operands_test.cpp
transformation_push_id_through_variable_test.cpp
transformation_replace_boolean_constant_with_constant_binary_test.cpp
transformation_replace_constant_with_uniform_test.cpp

View File

@ -220,10 +220,17 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) {
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {0, 1})
.IsApplicable(context.get(), transformation_context));
// Permutation has invalid values
// Permutation has invalid values 1
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {3, 1, 0})
.IsApplicable(context.get(), transformation_context));
#ifndef NDEBUG
// Permutation has invalid values 2
ASSERT_DEATH(TransformationPermuteFunctionParameters(22, 0, {2, 2, 1})
.IsApplicable(context.get(), transformation_context),
"Permutation has duplicates");
#endif
// Type id is not an OpTypeFunction instruction
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 42, {2, 1, 0})
.IsApplicable(context.get(), transformation_context));

View File

@ -0,0 +1,154 @@
// 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_permute_phi_operands.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationPermutePhiOperandsTest, 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
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%11 = OpConstant %6 1
%14 = OpTypeBool
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpStore %8 %9
OpStore %10 %11
%12 = OpLoad %6 %8
%13 = OpLoad %6 %10
%15 = OpSLessThan %14 %12 %13
OpSelectionMerge %17 None
OpBranchConditional %15 %16 %21
%16 = OpLabel
%18 = OpLoad %6 %10
%19 = OpLoad %6 %8
%20 = OpIAdd %6 %19 %18
OpBranch %17
%21 = OpLabel
%22 = OpLoad %6 %10
%23 = OpLoad %6 %8
%24 = OpISub %6 %23 %22
OpBranch %17
%17 = OpLabel
%25 = OpPhi %6 %20 %16 %24 %21
OpStore %8 %25
OpReturn
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);
// Result id is invalid.
ASSERT_FALSE(TransformationPermutePhiOperands(26, {}).IsApplicable(
context.get(), transformation_context));
// Result id is not of an OpPhi instruction.
ASSERT_FALSE(TransformationPermutePhiOperands(24, {}).IsApplicable(
context.get(), transformation_context));
// Result id is not of an OpPhi instruction.
ASSERT_FALSE(TransformationPermutePhiOperands(24, {}).IsApplicable(
context.get(), transformation_context));
// Permutation has invalid size.
ASSERT_FALSE(TransformationPermutePhiOperands(25, {0, 1, 2})
.IsApplicable(context.get(), transformation_context));
#ifndef NDEBUG
// Permutation has duplicates.
ASSERT_DEATH(TransformationPermutePhiOperands(25, {0, 0})
.IsApplicable(context.get(), transformation_context),
"Permutation has duplicates");
#endif
// Permutation's values are not in range.
ASSERT_FALSE(TransformationPermutePhiOperands(25, {1, 2})
.IsApplicable(context.get(), transformation_context));
TransformationPermutePhiOperands transformation(25, {1, 0});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
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
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%11 = OpConstant %6 1
%14 = OpTypeBool
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpStore %8 %9
OpStore %10 %11
%12 = OpLoad %6 %8
%13 = OpLoad %6 %10
%15 = OpSLessThan %14 %12 %13
OpSelectionMerge %17 None
OpBranchConditional %15 %16 %21
%16 = OpLabel
%18 = OpLoad %6 %10
%19 = OpLoad %6 %8
%20 = OpIAdd %6 %19 %18
OpBranch %17
%21 = OpLabel
%22 = OpLoad %6 %10
%23 = OpLoad %6 %8
%24 = OpISub %6 %23 %22
OpBranch %17
%17 = OpLabel
%25 = OpPhi %6 %24 %21 %20 %16
OpStore %8 %25
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools