blob: a088e17b15ab3416b003539019803d53f1d84d8f [file] [log] [blame]
// 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_outline_functions.h"
#include "gtest/gtest.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/pseudo_random_generator.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
OpName %2 "main"
OpName %3 "a"
OpName %4 "b"
OpDecorate %3 RelaxedPrecision
OpDecorate %4 RelaxedPrecision
OpDecorate %5 RelaxedPrecision
OpDecorate %6 RelaxedPrecision
OpDecorate %7 RelaxedPrecision
OpDecorate %8 RelaxedPrecision
OpDecorate %9 RelaxedPrecision
%10 = OpTypeVoid
%11 = OpTypeFunction %10
%12 = OpTypeInt 32 1
%13 = OpTypePointer Function %12
%14 = OpConstant %12 8
%15 = OpConstant %12 23
%16 = OpTypeBool
%17 = OpConstantTrue %16
%18 = OpConstant %12 0
%19 = OpConstant %12 1
%2 = OpFunction %10 None %11
%20 = OpLabel
%3 = OpVariable %13 Function
%4 = OpVariable %13 Function
OpStore %3 %14
OpStore %4 %15
OpBranch %21
%21 = OpLabel
OpLoopMerge %22 %23 None
OpBranch %24
%24 = OpLabel
%25 = OpPhi %12 %19 %21 %18 %26
OpLoopMerge %27 %26 None
OpBranch %28
%28 = OpLabel
%5 = OpLoad %12 %3
%29 = OpSGreaterThan %16 %5 %18
OpBranchConditional %29 %30 %27
%30 = OpLabel
%6 = OpLoad %12 %4
%7 = OpISub %12 %6 %19
OpStore %4 %7
OpBranch %26
%26 = OpLabel
%8 = OpLoad %12 %3
%9 = OpISub %12 %8 %19
OpStore %3 %9
OpBranch %24
%27 = OpLabel
OpBranch %23
%23 = OpLabel
OpBranch %21
%22 = OpLabel
OpBranch %31
%31 = OpLabel
OpLoopMerge %32 %31 None
OpBranchConditional %17 %31 %32
%32 = OpLabel
OpSelectionMerge %33 None
OpBranchConditional %17 %34 %35
%34 = OpLabel
OpBranch %33
%35 = OpLabel
OpBranch %33
%33 = OpLabel
%42 = OpPhi %12 %19 %33 %18 %34 %18 %35
OpLoopMerge %36 %33 None
OpBranchConditional %17 %36 %33
%36 = OpLabel
%43 = OpPhi %12 %18 %33 %18 %41
OpReturn
%37 = OpLabel
OpLoopMerge %38 %39 None
OpBranch %40
%40 = OpLabel
OpBranchConditional %17 %41 %38
%41 = OpLabel
OpBranchConditional %17 %36 %39
%39 = OpLabel
OpBranch %37
%38 = OpLabel
OpReturn
OpFunctionEnd
)";
TEST(FuzzerPassOutlineFunctionsTest, EntryIsAlreadySuitable) {
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
false);
protobufs::TransformationSequence transformation_sequence;
FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
&fuzzer_context,
&transformation_sequence, false);
// Block 28
auto suitable_entry_block =
fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining(
context->get_instr_block(28));
ASSERT_TRUE(suitable_entry_block);
ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 28);
// Block 32
suitable_entry_block = fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining(
context->get_instr_block(32));
ASSERT_TRUE(suitable_entry_block);
ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 32);
// Block 41
suitable_entry_block = fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining(
context->get_instr_block(41));
ASSERT_TRUE(suitable_entry_block);
ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 41);
// The module should not have been changed.
ASSERT_TRUE(IsEqual(env, shader, context.get()));
}
TEST(FuzzerPassOutlineFunctionsTest, EntryHasOpVariable) {
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
false);
protobufs::TransformationSequence transformation_sequence;
FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
&fuzzer_context,
&transformation_sequence, false);
// Block 20
auto suitable_entry_block =
fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining(
context->get_instr_block(20));
// The block should have been split, the new entry block being the block
// generated by the splitting.
ASSERT_TRUE(suitable_entry_block);
ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 100);
std::string after_adjustment = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
OpName %2 "main"
OpName %3 "a"
OpName %4 "b"
OpDecorate %3 RelaxedPrecision
OpDecorate %4 RelaxedPrecision
OpDecorate %5 RelaxedPrecision
OpDecorate %6 RelaxedPrecision
OpDecorate %7 RelaxedPrecision
OpDecorate %8 RelaxedPrecision
OpDecorate %9 RelaxedPrecision
%10 = OpTypeVoid
%11 = OpTypeFunction %10
%12 = OpTypeInt 32 1
%13 = OpTypePointer Function %12
%14 = OpConstant %12 8
%15 = OpConstant %12 23
%16 = OpTypeBool
%17 = OpConstantTrue %16
%18 = OpConstant %12 0
%19 = OpConstant %12 1
%2 = OpFunction %10 None %11
%20 = OpLabel
%3 = OpVariable %13 Function
%4 = OpVariable %13 Function
OpBranch %100
%100 = OpLabel
OpStore %3 %14
OpStore %4 %15
OpBranch %21
%21 = OpLabel
OpLoopMerge %22 %23 None
OpBranch %24
%24 = OpLabel
%25 = OpPhi %12 %19 %21 %18 %26
OpLoopMerge %27 %26 None
OpBranch %28
%28 = OpLabel
%5 = OpLoad %12 %3
%29 = OpSGreaterThan %16 %5 %18
OpBranchConditional %29 %30 %27
%30 = OpLabel
%6 = OpLoad %12 %4
%7 = OpISub %12 %6 %19
OpStore %4 %7
OpBranch %26
%26 = OpLabel
%8 = OpLoad %12 %3
%9 = OpISub %12 %8 %19
OpStore %3 %9
OpBranch %24
%27 = OpLabel
OpBranch %23
%23 = OpLabel
OpBranch %21
%22 = OpLabel
OpBranch %31
%31 = OpLabel
OpLoopMerge %32 %31 None
OpBranchConditional %17 %31 %32
%32 = OpLabel
OpSelectionMerge %33 None
OpBranchConditional %17 %34 %35
%34 = OpLabel
OpBranch %33
%35 = OpLabel
OpBranch %33
%33 = OpLabel
%42 = OpPhi %12 %19 %33 %18 %34 %18 %35
OpLoopMerge %36 %33 None
OpBranchConditional %17 %36 %33
%36 = OpLabel
%43 = OpPhi %12 %18 %33 %18 %41
OpReturn
%37 = OpLabel
OpLoopMerge %38 %39 None
OpBranch %40
%40 = OpLabel
OpBranchConditional %17 %41 %38
%41 = OpLabel
OpBranchConditional %17 %36 %39
%39 = OpLabel
OpBranch %37
%38 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_adjustment, context.get()));
}
TEST(FuzzerPassOutlineFunctionsTest, EntryBlockIsHeader) {
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
false);
protobufs::TransformationSequence transformation_sequence;
FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
&fuzzer_context,
&transformation_sequence, false);
// Block 21
auto suitable_entry_block =
fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining(
context->get_instr_block(21));
// A suitable entry block should have been found by finding the preheader
// (%20) and then splitting it.
ASSERT_TRUE(suitable_entry_block);
ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 100);
// Block 24
suitable_entry_block = fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining(
context->get_instr_block(24));
// A preheader should have been created, because the current one is a loop
// header.
ASSERT_TRUE(suitable_entry_block);
ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 101);
// Block 31
suitable_entry_block = fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining(
context->get_instr_block(31));
// An existing suitable entry block should have been found by finding the
// preheader (%22), which is already suitable.
ASSERT_TRUE(suitable_entry_block);
ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 22);
// Block 33
suitable_entry_block = fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining(
context->get_instr_block(33));
// An existing suitable entry block should have been found by creating a new
// preheader (there is not one already), and then splitting it (as it contains
// OpPhi).
ASSERT_TRUE(suitable_entry_block);
ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 104);
// Block 37
suitable_entry_block = fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining(
context->get_instr_block(37));
// No suitable entry block can be found for block 37, since it is a loop
// header with only one predecessor (the back-edge block).
ASSERT_FALSE(suitable_entry_block);
std::string after_adjustments = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
OpName %2 "main"
OpName %3 "a"
OpName %4 "b"
OpDecorate %3 RelaxedPrecision
OpDecorate %4 RelaxedPrecision
OpDecorate %5 RelaxedPrecision
OpDecorate %6 RelaxedPrecision
OpDecorate %7 RelaxedPrecision
OpDecorate %8 RelaxedPrecision
OpDecorate %9 RelaxedPrecision
%10 = OpTypeVoid
%11 = OpTypeFunction %10
%12 = OpTypeInt 32 1
%13 = OpTypePointer Function %12
%14 = OpConstant %12 8
%15 = OpConstant %12 23
%16 = OpTypeBool
%17 = OpConstantTrue %16
%18 = OpConstant %12 0
%19 = OpConstant %12 1
%2 = OpFunction %10 None %11
%20 = OpLabel
%3 = OpVariable %13 Function
%4 = OpVariable %13 Function
OpBranch %100
%100 = OpLabel
OpStore %3 %14
OpStore %4 %15
OpBranch %21
%21 = OpLabel
OpLoopMerge %22 %23 None
OpBranch %101
%101 = OpLabel
OpBranch %24
%24 = OpLabel
%25 = OpPhi %12 %19 %101 %18 %26
OpLoopMerge %27 %26 None
OpBranch %28
%28 = OpLabel
%5 = OpLoad %12 %3
%29 = OpSGreaterThan %16 %5 %18
OpBranchConditional %29 %30 %27
%30 = OpLabel
%6 = OpLoad %12 %4
%7 = OpISub %12 %6 %19
OpStore %4 %7
OpBranch %26
%26 = OpLabel
%8 = OpLoad %12 %3
%9 = OpISub %12 %8 %19
OpStore %3 %9
OpBranch %24
%27 = OpLabel
OpBranch %23
%23 = OpLabel
OpBranch %21
%22 = OpLabel
OpBranch %31
%31 = OpLabel
OpLoopMerge %32 %31 None
OpBranchConditional %17 %31 %32
%32 = OpLabel
OpSelectionMerge %102 None
OpBranchConditional %17 %34 %35
%34 = OpLabel
OpBranch %102
%35 = OpLabel
OpBranch %102
%102 = OpLabel
%103 = OpPhi %12 %18 %34 %18 %35
OpBranch %104
%104 = OpLabel
OpBranch %33
%33 = OpLabel
%42 = OpPhi %12 %103 %104 %19 %33
OpLoopMerge %36 %33 None
OpBranchConditional %17 %36 %33
%36 = OpLabel
%43 = OpPhi %12 %18 %33 %18 %41
OpReturn
%37 = OpLabel
OpLoopMerge %38 %39 None
OpBranch %40
%40 = OpLabel
OpBranchConditional %17 %41 %38
%41 = OpLabel
OpBranchConditional %17 %36 %39
%39 = OpLabel
OpBranch %37
%38 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_adjustments, context.get()));
}
TEST(FuzzerPassOutlineFunctionsTest, ExitBlock) {
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100,
false);
protobufs::TransformationSequence transformation_sequence;
FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
&fuzzer_context,
&transformation_sequence, false);
// Block 39 is not a merge block, so it is already suitable.
auto suitable_exit_block = fuzzer_pass.MaybeGetExitBlockSuitableForOutlining(
context->get_instr_block(39));
ASSERT_TRUE(suitable_exit_block);
ASSERT_TRUE(suitable_exit_block->GetLabel()->result_id() == 39);
// The following are merge blocks and, thus, they will need to be split.
// Block 22
suitable_exit_block = fuzzer_pass.MaybeGetExitBlockSuitableForOutlining(
context->get_instr_block(22));
ASSERT_TRUE(suitable_exit_block);
ASSERT_TRUE(suitable_exit_block->GetLabel()->result_id() == 100);
// Block 27
suitable_exit_block = fuzzer_pass.MaybeGetExitBlockSuitableForOutlining(
context->get_instr_block(27));
ASSERT_TRUE(suitable_exit_block);
ASSERT_TRUE(suitable_exit_block->GetLabel()->result_id() == 101);
// Block 36
suitable_exit_block = fuzzer_pass.MaybeGetExitBlockSuitableForOutlining(
context->get_instr_block(36));
ASSERT_TRUE(suitable_exit_block);
ASSERT_TRUE(suitable_exit_block->GetLabel()->result_id() == 102);
std::string after_adjustments = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
OpName %2 "main"
OpName %3 "a"
OpName %4 "b"
OpDecorate %3 RelaxedPrecision
OpDecorate %4 RelaxedPrecision
OpDecorate %5 RelaxedPrecision
OpDecorate %6 RelaxedPrecision
OpDecorate %7 RelaxedPrecision
OpDecorate %8 RelaxedPrecision
OpDecorate %9 RelaxedPrecision
%10 = OpTypeVoid
%11 = OpTypeFunction %10
%12 = OpTypeInt 32 1
%13 = OpTypePointer Function %12
%14 = OpConstant %12 8
%15 = OpConstant %12 23
%16 = OpTypeBool
%17 = OpConstantTrue %16
%18 = OpConstant %12 0
%19 = OpConstant %12 1
%2 = OpFunction %10 None %11
%20 = OpLabel
%3 = OpVariable %13 Function
%4 = OpVariable %13 Function
OpStore %3 %14
OpStore %4 %15
OpBranch %21
%21 = OpLabel
OpLoopMerge %22 %23 None
OpBranch %24
%24 = OpLabel
%25 = OpPhi %12 %19 %21 %18 %26
OpLoopMerge %27 %26 None
OpBranch %28
%28 = OpLabel
%5 = OpLoad %12 %3
%29 = OpSGreaterThan %16 %5 %18
OpBranchConditional %29 %30 %27
%30 = OpLabel
%6 = OpLoad %12 %4
%7 = OpISub %12 %6 %19
OpStore %4 %7
OpBranch %26
%26 = OpLabel
%8 = OpLoad %12 %3
%9 = OpISub %12 %8 %19
OpStore %3 %9
OpBranch %24
%27 = OpLabel
OpBranch %101
%101 = OpLabel
OpBranch %23
%23 = OpLabel
OpBranch %21
%22 = OpLabel
OpBranch %100
%100 = OpLabel
OpBranch %31
%31 = OpLabel
OpLoopMerge %32 %31 None
OpBranchConditional %17 %31 %32
%32 = OpLabel
OpSelectionMerge %33 None
OpBranchConditional %17 %34 %35
%34 = OpLabel
OpBranch %33
%35 = OpLabel
OpBranch %33
%33 = OpLabel
%42 = OpPhi %12 %19 %33 %18 %34 %18 %35
OpLoopMerge %36 %33 None
OpBranchConditional %17 %36 %33
%36 = OpLabel
%43 = OpPhi %12 %18 %33 %18 %41
OpBranch %102
%102 = OpLabel
OpReturn
%37 = OpLabel
OpLoopMerge %38 %39 None
OpBranch %40
%40 = OpLabel
OpBranchConditional %17 %41 %38
%41 = OpLabel
OpBranchConditional %17 %36 %39
%39 = OpLabel
OpBranch %37
%38 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_adjustments, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools