blob: 31fb9a2f1aee639aa42c5f7c9e4b77203af1f977 [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/transformation_duplicate_region_with_selection.h"
#include "gtest/gtest.h"
#include "source/fuzz/counter_overflow_id_source.h"
#include "source/fuzz/fuzzer_util.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationDuplicateRegionWithSelectionTest, BasicUseTest) {
// This test handles a case where the ids from the original region are used in
// subsequent block.
std::string shader = R"(
OpCapability Shader
OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %10 "fun(i1;"
OpName %9 "a"
OpName %12 "b"
OpName %18 "c"
OpName %20 "param"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %2 %7
%14 = OpConstant %6 2
%16 = OpTypeBool
%17 = OpTypePointer Function %16
%19 = OpConstantTrue %16
%4 = OpFunction %2 None %3
%5 = OpLabel
%18 = OpVariable %17 Function
%20 = OpVariable %7 Function
OpStore %18 %19
OpStore %20 %14
%21 = OpFunctionCall %2 %10 %20
OpReturn
OpFunctionEnd
%10 = OpFunction %2 None %8
%9 = OpFunctionParameter %7
%11 = OpLabel
%12 = OpVariable %7 Function
OpBranch %800
%800 = OpLabel
%13 = OpLoad %6 %9
%15 = OpIAdd %6 %13 %14
OpStore %12 %15
OpBranch %900
%900 = OpLabel
%901 = OpIAdd %6 %15 %13
%902 = OpISub %6 %13 %15
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
TransformationDuplicateRegionWithSelection transformation_good_1 =
TransformationDuplicateRegionWithSelection(
500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
{{13, 301}, {15, 302}});
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
ApplyAndCheckFreshIds(transformation_good_1, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected_shader = R"(
OpCapability Shader
OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %10 "fun(i1;"
OpName %9 "a"
OpName %12 "b"
OpName %18 "c"
OpName %20 "param"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %2 %7
%14 = OpConstant %6 2
%16 = OpTypeBool
%17 = OpTypePointer Function %16
%19 = OpConstantTrue %16
%4 = OpFunction %2 None %3
%5 = OpLabel
%18 = OpVariable %17 Function
%20 = OpVariable %7 Function
OpStore %18 %19
OpStore %20 %14
%21 = OpFunctionCall %2 %10 %20
OpReturn
OpFunctionEnd
%10 = OpFunction %2 None %8
%9 = OpFunctionParameter %7
%11 = OpLabel
%12 = OpVariable %7 Function
OpBranch %500
%500 = OpLabel
OpSelectionMerge %501 None
OpBranchConditional %19 %800 %100
%800 = OpLabel
%13 = OpLoad %6 %9
%15 = OpIAdd %6 %13 %14
OpStore %12 %15
OpBranch %501
%100 = OpLabel
%201 = OpLoad %6 %9
%202 = OpIAdd %6 %201 %14
OpStore %12 %202
OpBranch %501
%501 = OpLabel
%301 = OpPhi %6 %13 %800 %201 %100
%302 = OpPhi %6 %15 %800 %202 %100
OpBranch %900
%900 = OpLabel
%901 = OpIAdd %6 %302 %301
%902 = OpISub %6 %301 %302
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
TEST(TransformationDuplicateRegionWithSelectionTest, BasicExitBlockTest) {
// This test handles a case where the exit block of the region is the exit
// block of the containing function.
std::string shader = R"(
OpCapability Shader
OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %10 "fun(i1;"
OpName %9 "a"
OpName %12 "b"
OpName %18 "c"
OpName %20 "param"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %2 %7
%14 = OpConstant %6 2
%16 = OpTypeBool
%17 = OpTypePointer Function %16
%19 = OpConstantTrue %16
%4 = OpFunction %2 None %3
%5 = OpLabel
%18 = OpVariable %17 Function
%20 = OpVariable %7 Function
OpStore %18 %19
OpStore %20 %14
%21 = OpFunctionCall %2 %10 %20
OpReturn
OpFunctionEnd
%10 = OpFunction %2 None %8
%9 = OpFunctionParameter %7
%11 = OpLabel
%12 = OpVariable %7 Function
OpBranch %800
%800 = OpLabel
%13 = OpLoad %6 %9
%15 = OpIAdd %6 %13 %14
OpStore %12 %15
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
TransformationDuplicateRegionWithSelection transformation_good_1 =
TransformationDuplicateRegionWithSelection(
500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
{{13, 301}, {15, 302}});
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
ApplyAndCheckFreshIds(transformation_good_1, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected_shader = R"(
OpCapability Shader
OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %10 "fun(i1;"
OpName %9 "a"
OpName %12 "b"
OpName %18 "c"
OpName %20 "param"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %2 %7
%14 = OpConstant %6 2
%16 = OpTypeBool
%17 = OpTypePointer Function %16
%19 = OpConstantTrue %16
%4 = OpFunction %2 None %3
%5 = OpLabel
%18 = OpVariable %17 Function
%20 = OpVariable %7 Function
OpStore %18 %19
OpStore %20 %14
%21 = OpFunctionCall %2 %10 %20
OpReturn
OpFunctionEnd
%10 = OpFunction %2 None %8
%9 = OpFunctionParameter %7
%11 = OpLabel
%12 = OpVariable %7 Function
OpBranch %500
%500 = OpLabel
OpSelectionMerge %501 None
OpBranchConditional %19 %800 %100
%800 = OpLabel
%13 = OpLoad %6 %9
%15 = OpIAdd %6 %13 %14
OpStore %12 %15
OpBranch %501
%100 = OpLabel
%201 = OpLoad %6 %9
%202 = OpIAdd %6 %201 %14
OpStore %12 %202
OpBranch %501
%501 = OpLabel
%301 = OpPhi %6 %13 %800 %201 %100
%302 = OpPhi %6 %15 %800 %202 %100
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
TEST(TransformationDuplicateRegionWithSelectionTest, NotApplicableCFGTest) {
// This test handles few cases where the transformation is not applicable
// because of the control flow graph or layout of the blocks.
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 %10 "fun(i1;"
OpName %9 "a"
OpName %18 "b"
OpName %25 "c"
OpName %27 "param"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %2 %7
%13 = OpConstant %6 2
%14 = OpTypeBool
%24 = OpTypePointer Function %14
%26 = OpConstantTrue %14
%4 = OpFunction %2 None %3
%5 = OpLabel
%25 = OpVariable %24 Function
%27 = OpVariable %7 Function
OpStore %25 %26
OpStore %27 %13
%28 = OpFunctionCall %2 %10 %27
OpReturn
OpFunctionEnd
%10 = OpFunction %2 None %8
%9 = OpFunctionParameter %7
%11 = OpLabel
%18 = OpVariable %7 Function
%12 = OpLoad %6 %9
%15 = OpSLessThan %14 %12 %13
OpSelectionMerge %17 None
OpBranchConditional %15 %16 %21
%16 = OpLabel
%19 = OpLoad %6 %9
%20 = OpIAdd %6 %19 %13
OpStore %18 %20
OpBranch %17
%21 = OpLabel
%22 = OpLoad %6 %9
%23 = OpISub %6 %22 %13
OpStore %18 %23
OpBranch %17
%17 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
// Bad: |entry_block_id| refers to the entry block of the function (this
// transformation currently avoids such cases).
TransformationDuplicateRegionWithSelection transformation_bad_1 =
TransformationDuplicateRegionWithSelection(
500, 26, 501, 11, 11, {{11, 100}}, {{18, 201}, {12, 202}, {15, 203}},
{{18, 301}, {12, 302}, {15, 303}});
ASSERT_FALSE(
transformation_bad_1.IsApplicable(context.get(), transformation_context));
// Bad: The block with id 16 does not dominate the block with id 21.
TransformationDuplicateRegionWithSelection transformation_bad_2 =
TransformationDuplicateRegionWithSelection(
500, 26, 501, 16, 21, {{16, 100}, {21, 101}},
{{19, 201}, {20, 202}, {22, 203}, {23, 204}},
{{19, 301}, {20, 302}, {22, 303}, {23, 304}});
ASSERT_FALSE(
transformation_bad_2.IsApplicable(context.get(), transformation_context));
// Bad: The block with id 21 does not post-dominate the block with id 11.
TransformationDuplicateRegionWithSelection transformation_bad_3 =
TransformationDuplicateRegionWithSelection(
500, 26, 501, 11, 21, {{11, 100}, {21, 101}},
{{18, 201}, {12, 202}, {15, 203}, {22, 204}, {23, 205}},
{{18, 301}, {12, 302}, {15, 303}, {22, 304}, {23, 305}});
ASSERT_FALSE(
transformation_bad_3.IsApplicable(context.get(), transformation_context));
// Bad: The block with id 5 is contained in a different function than the
// block with id 11.
TransformationDuplicateRegionWithSelection transformation_bad_4 =
TransformationDuplicateRegionWithSelection(
500, 26, 501, 5, 11, {{5, 100}, {11, 101}},
{{25, 201}, {27, 202}, {28, 203}, {18, 204}, {12, 205}, {15, 206}},
{{25, 301}, {27, 302}, {28, 303}, {18, 304}, {12, 305}, {15, 306}});
ASSERT_FALSE(
transformation_bad_4.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationDuplicateRegionWithSelectionTest, NotApplicableIdTest) {
// This test handles a case where the supplied ids are either not fresh, not
// distinct, not valid in their context or do not refer to the existing
// instructions.
std::string shader = R"(
OpCapability Shader
OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %10 "fun(i1;"
OpName %9 "a"
OpName %12 "b"
OpName %18 "c"
OpName %20 "param"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %2 %7
%14 = OpConstant %6 2
%16 = OpTypeBool
%17 = OpTypePointer Function %16
%19 = OpConstantTrue %16
%4 = OpFunction %2 None %3
%5 = OpLabel
%18 = OpVariable %17 Function
%20 = OpVariable %7 Function
OpStore %18 %19
OpStore %20 %14
%21 = OpFunctionCall %2 %10 %20
OpReturn
OpFunctionEnd
%10 = OpFunction %2 None %8
%9 = OpFunctionParameter %7
%11 = OpLabel
%12 = OpVariable %7 Function
OpBranch %800
%800 = OpLabel
%13 = OpLoad %6 %9
%15 = OpIAdd %6 %13 %14
OpStore %12 %15
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
// Bad: A value in the |original_label_to_duplicate_label| is not a fresh id.
TransformationDuplicateRegionWithSelection transformation_bad_1 =
TransformationDuplicateRegionWithSelection(
500, 19, 501, 800, 800, {{800, 21}}, {{13, 201}, {15, 202}},
{{13, 301}, {15, 302}});
ASSERT_FALSE(
transformation_bad_1.IsApplicable(context.get(), transformation_context));
// Bad: Values in the |original_id_to_duplicate_id| are not distinct.
TransformationDuplicateRegionWithSelection transformation_bad_2 =
TransformationDuplicateRegionWithSelection(
500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 201}},
{{13, 301}, {15, 302}});
ASSERT_FALSE(
transformation_bad_2.IsApplicable(context.get(), transformation_context));
// Bad: Values in the |original_id_to_phi_id| are not fresh and are not
// distinct with previous values.
TransformationDuplicateRegionWithSelection transformation_bad_3 =
TransformationDuplicateRegionWithSelection(
500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
{{13, 18}, {15, 202}});
ASSERT_FALSE(
transformation_bad_3.IsApplicable(context.get(), transformation_context));
// Bad: |entry_block_id| does not refer to an existing instruction.
TransformationDuplicateRegionWithSelection transformation_bad_4 =
TransformationDuplicateRegionWithSelection(
500, 19, 501, 802, 800, {{800, 100}}, {{13, 201}, {15, 202}},
{{13, 301}, {15, 302}});
ASSERT_FALSE(
transformation_bad_4.IsApplicable(context.get(), transformation_context));
// Bad: |exit_block_id| does not refer to a block.
TransformationDuplicateRegionWithSelection transformation_bad_5 =
TransformationDuplicateRegionWithSelection(
500, 19, 501, 800, 9, {{800, 100}}, {{13, 201}, {15, 202}},
{{13, 301}, {15, 302}});
ASSERT_FALSE(
transformation_bad_5.IsApplicable(context.get(), transformation_context));
// Bad: |new_entry_fresh_id| is not fresh.
TransformationDuplicateRegionWithSelection transformation_bad_6 =
TransformationDuplicateRegionWithSelection(
20, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
{{13, 301}, {15, 302}});
ASSERT_FALSE(
transformation_bad_6.IsApplicable(context.get(), transformation_context));
// Bad: |merge_label_fresh_id| is not fresh.
TransformationDuplicateRegionWithSelection transformation_bad_7 =
TransformationDuplicateRegionWithSelection(
500, 19, 20, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
{{13, 301}, {15, 302}});
ASSERT_FALSE(
transformation_bad_7.IsApplicable(context.get(), transformation_context));
#ifndef NDEBUG
// Bad: Instruction with id 15 is from the original region and is available
// at the end of the region but it is not present in the
// |original_id_to_phi_id|.
TransformationDuplicateRegionWithSelection transformation_bad_8 =
TransformationDuplicateRegionWithSelection(
500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
{{13, 301}});
ASSERT_DEATH(
transformation_bad_8.IsApplicable(context.get(), transformation_context),
"Bad attempt to query whether overflow ids are available.");
// Bad: Instruction with id 15 is from the original region but it is
// not present in the |original_id_to_duplicate_id|.
TransformationDuplicateRegionWithSelection transformation_bad_9 =
TransformationDuplicateRegionWithSelection(500, 19, 501, 800, 800,
{{800, 100}}, {{13, 201}},
{{13, 301}, {15, 302}});
ASSERT_DEATH(
transformation_bad_9.IsApplicable(context.get(), transformation_context),
"Bad attempt to query whether overflow ids are available.");
#endif
// Bad: |condition_id| does not refer to the valid instruction.
TransformationDuplicateRegionWithSelection transformation_bad_10 =
TransformationDuplicateRegionWithSelection(
500, 200, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
{{13, 301}, {15, 302}});
ASSERT_FALSE(transformation_bad_10.IsApplicable(context.get(),
transformation_context));
// Bad: |condition_id| does not refer to the instruction of type OpTypeBool
TransformationDuplicateRegionWithSelection transformation_bad_11 =
TransformationDuplicateRegionWithSelection(
500, 14, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
{{13, 301}, {15, 302}});
ASSERT_FALSE(transformation_bad_11.IsApplicable(context.get(),
transformation_context));
}
TEST(TransformationDuplicateRegionWithSelectionTest, NotApplicableCFGTest2) {
// This test handles few cases where the transformation is not applicable
// because of the control flow graph or the layout of the blocks.
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 %6 "fun("
OpName %10 "s"
OpName %12 "i"
OpName %29 "b"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%8 = OpTypeInt 32 1
%9 = OpTypePointer Function %8
%11 = OpConstant %8 0
%19 = OpConstant %8 10
%20 = OpTypeBool
%26 = OpConstant %8 1
%28 = OpTypePointer Function %20
%30 = OpConstantTrue %20
%4 = OpFunction %2 None %3
%5 = OpLabel
%29 = OpVariable %28 Function
OpStore %29 %30
%31 = OpFunctionCall %2 %6
OpReturn
OpFunctionEnd
%6 = OpFunction %2 None %3
%7 = OpLabel
%10 = OpVariable %9 Function
%12 = OpVariable %9 Function
OpStore %10 %11
OpStore %12 %11
OpBranch %13
%13 = OpLabel
OpLoopMerge %15 %16 None
OpBranch %17
%17 = OpLabel
%18 = OpLoad %8 %12
%21 = OpSLessThan %20 %18 %19
OpBranchConditional %21 %14 %15
%14 = OpLabel
%22 = OpLoad %8 %10
%23 = OpLoad %8 %12
%24 = OpIAdd %8 %22 %23
OpStore %10 %24
OpBranch %16
%16 = OpLabel
OpBranch %50
%50 = OpLabel
%25 = OpLoad %8 %12
%27 = OpIAdd %8 %25 %26
OpStore %12 %27
OpBranch %13
%15 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
// Bad: The exit block cannot be a header of a loop, because the region won't
// be a single-entry, single-exit region.
TransformationDuplicateRegionWithSelection transformation_bad_1 =
TransformationDuplicateRegionWithSelection(500, 30, 501, 13, 13,
{{13, 100}}, {{}}, {{}});
ASSERT_FALSE(
transformation_bad_1.IsApplicable(context.get(), transformation_context));
// Bad: The block with id 13, the loop header, is in the region. The block
// with id 15, the loop merge block, is not in the region.
TransformationDuplicateRegionWithSelection transformation_bad_2 =
TransformationDuplicateRegionWithSelection(
500, 30, 501, 13, 17, {{13, 100}, {17, 101}}, {{18, 201}, {21, 202}},
{{18, 301}, {21, 302}});
ASSERT_FALSE(
transformation_bad_2.IsApplicable(context.get(), transformation_context));
// Bad: The block with id 13, the loop header, is not in the region. The block
// with id 16, the loop continue target, is in the region.
TransformationDuplicateRegionWithSelection transformation_bad_3 =
TransformationDuplicateRegionWithSelection(
500, 30, 501, 16, 50, {{16, 100}, {50, 101}}, {{25, 201}, {27, 202}},
{{25, 301}, {27, 302}});
ASSERT_FALSE(
transformation_bad_3.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationDuplicateRegionWithSelectionTest, NotApplicableCFGTest3) {
// This test handles a case where for the block which is not the exit block,
// not all successors are in the region.
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 %6 "fun("
OpName %14 "a"
OpName %19 "b"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%8 = OpTypeBool
%9 = OpConstantTrue %8
%12 = OpTypeInt 32 1
%13 = OpTypePointer Function %12
%15 = OpConstant %12 2
%17 = OpConstant %12 3
%18 = OpTypePointer Function %8
%4 = OpFunction %2 None %3
%5 = OpLabel
%19 = OpVariable %18 Function
OpStore %19 %9
%20 = OpFunctionCall %2 %6
OpReturn
OpFunctionEnd
%6 = OpFunction %2 None %3
%7 = OpLabel
%14 = OpVariable %13 Function
OpSelectionMerge %11 None
OpBranchConditional %9 %10 %16
%10 = OpLabel
OpStore %14 %15
OpBranch %11
%16 = OpLabel
OpStore %14 %17
OpBranch %11
%11 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
// Bad: The block with id 7, which is not an exit block, has two successors:
// the block with id 10 and the block with id 16. The block with id 16 is not
// in the region.
TransformationDuplicateRegionWithSelection transformation_bad_1 =
TransformationDuplicateRegionWithSelection(
500, 30, 501, 7, 10, {{13, 100}}, {{14, 201}}, {{14, 301}});
ASSERT_FALSE(
transformation_bad_1.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationDuplicateRegionWithSelectionTest, MultipleBlocksLoopTest) {
// This test handles a case where the region consists of multiple blocks
// (they form a loop). The transformation is applicable and the region is
// duplicated.
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 %6 "fun("
OpName %10 "s"
OpName %12 "i"
OpName %29 "b"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%8 = OpTypeInt 32 1
%9 = OpTypePointer Function %8
%11 = OpConstant %8 0
%19 = OpConstant %8 10
%20 = OpTypeBool
%26 = OpConstant %8 1
%28 = OpTypePointer Function %20
%30 = OpConstantTrue %20
%4 = OpFunction %2 None %3
%5 = OpLabel
%29 = OpVariable %28 Function
OpStore %29 %30
%31 = OpFunctionCall %2 %6
OpReturn
OpFunctionEnd
%6 = OpFunction %2 None %3
%7 = OpLabel
%10 = OpVariable %9 Function
%12 = OpVariable %9 Function
OpStore %10 %11
OpStore %12 %11
OpBranch %50
%50 = OpLabel
OpBranch %13
%13 = OpLabel
OpLoopMerge %15 %16 None
OpBranch %17
%17 = OpLabel
%18 = OpLoad %8 %12
%21 = OpSLessThan %20 %18 %19
OpBranchConditional %21 %14 %15
%14 = OpLabel
%22 = OpLoad %8 %10
%23 = OpLoad %8 %12
%24 = OpIAdd %8 %22 %23
OpStore %10 %24
OpBranch %16
%16 = OpLabel
%25 = OpLoad %8 %12
%27 = OpIAdd %8 %25 %26
OpStore %12 %27
OpBranch %13
%15 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
TransformationDuplicateRegionWithSelection transformation_good_1 =
TransformationDuplicateRegionWithSelection(
500, 30, 501, 50, 15,
{{50, 100}, {13, 101}, {14, 102}, {15, 103}, {16, 104}, {17, 105}},
{{22, 201},
{23, 202},
{24, 203},
{25, 204},
{27, 205},
{18, 206},
{21, 207}},
{{22, 301},
{23, 302},
{24, 303},
{25, 304},
{27, 305},
{18, 306},
{21, 307}});
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
ApplyAndCheckFreshIds(transformation_good_1, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected_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 %6 "fun("
OpName %10 "s"
OpName %12 "i"
OpName %29 "b"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%8 = OpTypeInt 32 1
%9 = OpTypePointer Function %8
%11 = OpConstant %8 0
%19 = OpConstant %8 10
%20 = OpTypeBool
%26 = OpConstant %8 1
%28 = OpTypePointer Function %20
%30 = OpConstantTrue %20
%4 = OpFunction %2 None %3
%5 = OpLabel
%29 = OpVariable %28 Function
OpStore %29 %30
%31 = OpFunctionCall %2 %6
OpReturn
OpFunctionEnd
%6 = OpFunction %2 None %3
%7 = OpLabel
%10 = OpVariable %9 Function
%12 = OpVariable %9 Function
OpStore %10 %11
OpStore %12 %11
OpBranch %500
%500 = OpLabel
OpSelectionMerge %501 None
OpBranchConditional %30 %50 %100
%50 = OpLabel
OpBranch %13
%13 = OpLabel
OpLoopMerge %15 %16 None
OpBranch %17
%17 = OpLabel
%18 = OpLoad %8 %12
%21 = OpSLessThan %20 %18 %19
OpBranchConditional %21 %14 %15
%14 = OpLabel
%22 = OpLoad %8 %10
%23 = OpLoad %8 %12
%24 = OpIAdd %8 %22 %23
OpStore %10 %24
OpBranch %16
%16 = OpLabel
%25 = OpLoad %8 %12
%27 = OpIAdd %8 %25 %26
OpStore %12 %27
OpBranch %13
%15 = OpLabel
OpBranch %501
%100 = OpLabel
OpBranch %101
%101 = OpLabel
OpLoopMerge %103 %104 None
OpBranch %105
%105 = OpLabel
%206 = OpLoad %8 %12
%207 = OpSLessThan %20 %206 %19
OpBranchConditional %207 %102 %103
%102 = OpLabel
%201 = OpLoad %8 %10
%202 = OpLoad %8 %12
%203 = OpIAdd %8 %201 %202
OpStore %10 %203
OpBranch %104
%104 = OpLabel
%204 = OpLoad %8 %12
%205 = OpIAdd %8 %204 %26
OpStore %12 %205
OpBranch %101
%103 = OpLabel
OpBranch %501
%501 = OpLabel
%306 = OpPhi %8 %18 %15 %206 %103
%307 = OpPhi %20 %21 %15 %207 %103
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
TEST(TransformationDuplicateRegionWithSelectionTest,
ResolvingOpPhiExitBlockTest) {
// This test handles a case where the region under the transformation is
// referenced in OpPhi instructions. Since the new merge block becomes the
// exit of the region, these OpPhi instructions need to be updated.
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 %10 "fun(i1;"
OpName %9 "a"
OpName %12 "s"
OpName %26 "b"
OpName %29 "param"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %2 %7
%13 = OpConstant %6 0
%15 = OpConstant %6 2
%16 = OpTypeBool
%25 = OpTypePointer Function %16
%27 = OpConstantTrue %16
%28 = OpConstant %6 3
%4 = OpFunction %2 None %3
%5 = OpLabel
%26 = OpVariable %25 Function
%29 = OpVariable %7 Function
OpStore %26 %27
OpStore %29 %28
%30 = OpFunctionCall %2 %10 %29
OpReturn
OpFunctionEnd
%10 = OpFunction %2 None %8
%9 = OpFunctionParameter %7
%11 = OpLabel
%12 = OpVariable %7 Function
OpStore %12 %13
%14 = OpLoad %6 %9
%17 = OpSLessThan %16 %14 %15
OpSelectionMerge %19 None
OpBranchConditional %17 %18 %22
%18 = OpLabel
%20 = OpLoad %6 %9
%21 = OpIAdd %6 %20 %15
OpStore %12 %21
OpBranch %19
%22 = OpLabel
%23 = OpLoad %6 %9
%24 = OpIMul %6 %23 %15
OpStore %12 %24
OpBranch %19
%19 = OpLabel
%40 = OpPhi %6 %21 %18 %24 %22
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
TransformationDuplicateRegionWithSelection transformation_good_1 =
TransformationDuplicateRegionWithSelection(
500, 27, 501, 22, 22, {{22, 100}}, {{23, 201}, {24, 202}},
{{23, 301}, {24, 302}});
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
ApplyAndCheckFreshIds(transformation_good_1, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected_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 %10 "fun(i1;"
OpName %9 "a"
OpName %12 "s"
OpName %26 "b"
OpName %29 "param"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %2 %7
%13 = OpConstant %6 0
%15 = OpConstant %6 2
%16 = OpTypeBool
%25 = OpTypePointer Function %16
%27 = OpConstantTrue %16
%28 = OpConstant %6 3
%4 = OpFunction %2 None %3
%5 = OpLabel
%26 = OpVariable %25 Function
%29 = OpVariable %7 Function
OpStore %26 %27
OpStore %29 %28
%30 = OpFunctionCall %2 %10 %29
OpReturn
OpFunctionEnd
%10 = OpFunction %2 None %8
%9 = OpFunctionParameter %7
%11 = OpLabel
%12 = OpVariable %7 Function
OpStore %12 %13
%14 = OpLoad %6 %9
%17 = OpSLessThan %16 %14 %15
OpSelectionMerge %19 None
OpBranchConditional %17 %18 %500
%18 = OpLabel
%20 = OpLoad %6 %9
%21 = OpIAdd %6 %20 %15
OpStore %12 %21
OpBranch %19
%500 = OpLabel
OpSelectionMerge %501 None
OpBranchConditional %27 %22 %100
%22 = OpLabel
%23 = OpLoad %6 %9
%24 = OpIMul %6 %23 %15
OpStore %12 %24
OpBranch %501
%100 = OpLabel
%201 = OpLoad %6 %9
%202 = OpIMul %6 %201 %15
OpStore %12 %202
OpBranch %501
%501 = OpLabel
%301 = OpPhi %6 %23 %22 %201 %100
%302 = OpPhi %6 %24 %22 %202 %100
OpBranch %19
%19 = OpLabel
%40 = OpPhi %6 %21 %18 %302 %501
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
TEST(TransformationDuplicateRegionWithSelectionTest, NotApplicableEarlyReturn) {
// This test handles a case where one of the blocks has successor outside of
// the region, which has an early return from the function, so that the
// transformation is not applicable.
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 %10 "fun(i1;"
OpName %9 "a"
OpName %12 "s"
OpName %27 "b"
OpName %30 "param"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %2 %7
%13 = OpConstant %6 0
%15 = OpConstant %6 2
%16 = OpTypeBool
%26 = OpTypePointer Function %16
%28 = OpConstantTrue %16
%29 = OpConstant %6 3
%4 = OpFunction %2 None %3
%5 = OpLabel
%27 = OpVariable %26 Function
%30 = OpVariable %7 Function
OpStore %27 %28
OpStore %30 %29
%31 = OpFunctionCall %2 %10 %30
OpReturn
OpFunctionEnd
%10 = OpFunction %2 None %8
%9 = OpFunctionParameter %7
%11 = OpLabel
%12 = OpVariable %7 Function
OpBranch %50
%50 = OpLabel
OpStore %12 %13
%14 = OpLoad %6 %9
%17 = OpSLessThan %16 %14 %15
OpSelectionMerge %19 None
OpBranchConditional %17 %18 %22
%18 = OpLabel
%20 = OpLoad %6 %9
%21 = OpIAdd %6 %20 %15
OpStore %12 %21
OpBranch %19
%22 = OpLabel
OpReturn
%19 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
// Bad: The block with id 50, which is the entry block, has two successors:
// the block with id 18 and the block with id 22. The block 22 has an early
// return from the function, so that the entry block is not post-dominated by
// the exit block.
TransformationDuplicateRegionWithSelection transformation_bad_1 =
TransformationDuplicateRegionWithSelection(
500, 28, 501, 50, 19, {{50, 100}, {18, 101}, {22, 102}, {19, 103}},
{{14, 202}, {17, 203}, {20, 204}, {21, 205}},
{{14, 302}, {17, 303}, {20, 304}, {21, 305}});
ASSERT_FALSE(
transformation_bad_1.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationDuplicateRegionWithSelectionTest,
ResolvingOpPhiEntryBlockOnePredecessor) {
// This test handles a case where the entry block has an OpPhi instruction
// referring to its predecessor. After transformation, this instruction needs
// to be updated.
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 %10 "fun(i1;"
OpName %9 "a"
OpName %12 "s"
OpName %14 "t"
OpName %20 "b"
OpName %23 "param"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %2 %7
%13 = OpConstant %6 0
%15 = OpConstant %6 2
%18 = OpTypeBool
%19 = OpTypePointer Function %18
%21 = OpConstantTrue %18
%22 = OpConstant %6 3
%4 = OpFunction %2 None %3
%5 = OpLabel
%20 = OpVariable %19 Function
%23 = OpVariable %7 Function
OpStore %20 %21
OpStore %23 %22
%24 = OpFunctionCall %2 %10 %23
OpReturn
OpFunctionEnd
%10 = OpFunction %2 None %8
%9 = OpFunctionParameter %7
%11 = OpLabel
%12 = OpVariable %7 Function
%14 = OpVariable %7 Function
OpStore %12 %13
%16 = OpLoad %6 %12
%17 = OpIMul %6 %15 %16
OpStore %14 %17
OpBranch %50
%50 = OpLabel
%51 = OpPhi %6 %17 %11
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
TransformationDuplicateRegionWithSelection transformation_good_1 =
TransformationDuplicateRegionWithSelection(
500, 21, 501, 50, 50, {{50, 100}}, {{51, 201}}, {{51, 301}});
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
ApplyAndCheckFreshIds(transformation_good_1, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected_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 %10 "fun(i1;"
OpName %9 "a"
OpName %12 "s"
OpName %14 "t"
OpName %20 "b"
OpName %23 "param"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %2 %7
%13 = OpConstant %6 0
%15 = OpConstant %6 2
%18 = OpTypeBool
%19 = OpTypePointer Function %18
%21 = OpConstantTrue %18
%22 = OpConstant %6 3
%4 = OpFunction %2 None %3
%5 = OpLabel
%20 = OpVariable %19 Function
%23 = OpVariable %7 Function
OpStore %20 %21
OpStore %23 %22
%24 = OpFunctionCall %2 %10 %23
OpReturn
OpFunctionEnd
%10 = OpFunction %2 None %8
%9 = OpFunctionParameter %7
%11 = OpLabel
%12 = OpVariable %7 Function
%14 = OpVariable %7 Function
OpStore %12 %13
%16 = OpLoad %6 %12
%17 = OpIMul %6 %15 %16
OpStore %14 %17
OpBranch %500
%500 = OpLabel
OpSelectionMerge %501 None
OpBranchConditional %21 %50 %100
%50 = OpLabel
%51 = OpPhi %6 %17 %500
OpBranch %501
%100 = OpLabel
%201 = OpPhi %6 %17 %500
OpBranch %501
%501 = OpLabel
%301 = OpPhi %6 %51 %50 %201 %100
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
TEST(TransformationDuplicateRegionWithSelectionTest,
NotApplicableNoVariablePointerCapability) {
// This test handles a case where the transformation would create an OpPhi
// instruction with pointer operands, however there is no cab
// CapabilityVariablePointers. Hence, the transformation is not applicable.
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 %10 "fun(i1;"
OpName %9 "a"
OpName %12 "s"
OpName %14 "t"
OpName %20 "b"
OpName %23 "param"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %2 %7
%13 = OpConstant %6 0
%15 = OpConstant %6 2
%18 = OpTypeBool
%19 = OpTypePointer Function %18
%21 = OpConstantTrue %18
%22 = OpConstant %6 3
%4 = OpFunction %2 None %3
%5 = OpLabel
%20 = OpVariable %19 Function
%23 = OpVariable %7 Function
OpStore %20 %21
OpStore %23 %22
%24 = OpFunctionCall %2 %10 %23
OpReturn
OpFunctionEnd
%10 = OpFunction %2 None %8
%9 = OpFunctionParameter %7
%11 = OpLabel
%12 = OpVariable %7 Function
%14 = OpVariable %7 Function
OpStore %12 %13
%16 = OpLoad %6 %12
%17 = OpIMul %6 %15 %16
OpStore %14 %17
OpBranch %50
%50 = OpLabel
%51 = OpCopyObject %7 %12
OpBranch %52
%52 = OpLabel
%53 = OpCopyObject %7 %51
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
// Bad: There is no required capability CapabilityVariablePointers
TransformationDuplicateRegionWithSelection transformation_bad_1 =
TransformationDuplicateRegionWithSelection(
500, 21, 501, 50, 50, {{50, 100}}, {{51, 201}}, {{51, 301}});
ASSERT_FALSE(
transformation_bad_1.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationDuplicateRegionWithSelectionTest,
ExitBlockTerminatorOpUnreachable) {
// This test handles a case where the exit block ends with OpUnreachable.
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 %6 "fun("
OpName %10 "s"
OpName %17 "b"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%8 = OpTypeInt 32 1
%9 = OpTypePointer Function %8
%11 = OpConstant %8 0
%13 = OpConstant %8 2
%15 = OpTypeBool
%16 = OpTypePointer Function %15
%18 = OpConstantTrue %15
%4 = OpFunction %2 None %3
%5 = OpLabel
%17 = OpVariable %16 Function
OpStore %17 %18
%19 = OpFunctionCall %2 %6
OpReturn
OpFunctionEnd
%6 = OpFunction %2 None %3
%7 = OpLabel
%10 = OpVariable %9 Function
OpBranch %50
%50 = OpLabel
OpStore %10 %11
%12 = OpLoad %8 %10
%14 = OpIAdd %8 %12 %13
OpStore %10 %14
OpUnreachable
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
TransformationDuplicateRegionWithSelection transformation_good_1 =
TransformationDuplicateRegionWithSelection(
500, 18, 501, 50, 50, {{50, 100}}, {{12, 201}, {14, 202}},
{{12, 301}, {14, 302}});
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
ApplyAndCheckFreshIds(transformation_good_1, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected_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 %6 "fun("
OpName %10 "s"
OpName %17 "b"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%8 = OpTypeInt 32 1
%9 = OpTypePointer Function %8
%11 = OpConstant %8 0
%13 = OpConstant %8 2
%15 = OpTypeBool
%16 = OpTypePointer Function %15
%18 = OpConstantTrue %15
%4 = OpFunction %2 None %3
%5 = OpLabel
%17 = OpVariable %16 Function
OpStore %17 %18
%19 = OpFunctionCall %2 %6
OpReturn
OpFunctionEnd
%6 = OpFunction %2 None %3
%7 = OpLabel
%10 = OpVariable %9 Function
OpBranch %500
%500 = OpLabel
OpSelectionMerge %501 None
OpBranchConditional %18 %50 %100
%50 = OpLabel
OpStore %10 %11
%12 = OpLoad %8 %10
%14 = OpIAdd %8 %12 %13
OpStore %10 %14
OpBranch %501
%100 = OpLabel
OpStore %10 %11
%201 = OpLoad %8 %10
%202 = OpIAdd %8 %201 %13
OpStore %10 %202
OpBranch %501
%501 = OpLabel
%301 = OpPhi %8 %12 %50 %201 %100
%302 = OpPhi %8 %14 %50 %202 %100
OpUnreachable
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
TEST(TransformationDuplicateRegionWithSelectionTest,
ExitBlockTerminatorOpKill) {
// This test handles a case where the exit block ends with OpKill.
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 %6 "fun("
OpName %10 "s"
OpName %17 "b"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%8 = OpTypeInt 32 1
%9 = OpTypePointer Function %8
%11 = OpConstant %8 0
%13 = OpConstant %8 2
%15 = OpTypeBool
%16 = OpTypePointer Function %15
%18 = OpConstantTrue %15
%4 = OpFunction %2 None %3
%5 = OpLabel
%17 = OpVariable %16 Function
OpStore %17 %18
%19 = OpFunctionCall %2 %6
OpReturn
OpFunctionEnd
%6 = OpFunction %2 None %3
%7 = OpLabel
%10 = OpVariable %9 Function
OpBranch %50
%50 = OpLabel
OpStore %10 %11
%12 = OpLoad %8 %10
%14 = OpIAdd %8 %12 %13
OpStore %10 %14
OpKill
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
TransformationDuplicateRegionWithSelection transformation_good_1 =
TransformationDuplicateRegionWithSelection(
500, 18, 501, 50, 50, {{50, 100}}, {{12, 201}, {14, 202}},
{{12, 301}, {14, 302}});
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
ApplyAndCheckFreshIds(transformation_good_1, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected_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 %6 "fun("
OpName %10 "s"
OpName %17 "b"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%8 = OpTypeInt 32 1
%9 = OpTypePointer Function %8
%11 = OpConstant %8 0
%13 = OpConstant %8 2
%15 = OpTypeBool
%16 = OpTypePointer Function %15
%18 = OpConstantTrue %15
%4 = OpFunction %2 None %3
%5 = OpLabel
%17 = OpVariable %16 Function
OpStore %17 %18
%19 = OpFunctionCall %2 %6
OpReturn
OpFunctionEnd
%6 = OpFunction %2 None %3
%7 = OpLabel
%10 = OpVariable %9 Function
OpBranch %500
%500 = OpLabel
OpSelectionMerge %501 None
OpBranchConditional %18 %50 %100
%50 = OpLabel
OpStore %10 %11
%12 = OpLoad %8 %10
%14 = OpIAdd %8 %12 %13
OpStore %10 %14
OpBranch %501
%100 = OpLabel
OpStore %10 %11
%201 = OpLoad %8 %10
%202 = OpIAdd %8 %201 %13
OpStore %10 %202
OpBranch %501
%501 = OpLabel
%301 = OpPhi %8 %12 %50 %201 %100
%302 = OpPhi %8 %14 %50 %202 %100
OpKill
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
TEST(TransformationDuplicateRegionWithSelectionTest,
ContinueExitBlockNotApplicable) {
// This test handles a case where the exit block is the continue target and
// the transformation is not applicable.
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 "s"
OpName %10 "i"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%17 = OpConstant %6 10
%18 = OpTypeBool
%24 = OpConstant %6 5
%30 = OpConstant %6 1
%50 = OpConstantTrue %18
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpStore %8 %9
OpStore %10 %9
OpBranch %11
%11 = OpLabel
OpLoopMerge %13 %14 None
OpBranch %15
%15 = OpLabel
%16 = OpLoad %6 %10
%19 = OpSLessThan %18 %16 %17
OpBranchConditional %19 %12 %13
%12 = OpLabel
%20 = OpLoad %6 %10
%21 = OpLoad %6 %8
%22 = OpIAdd %6 %21 %20
OpStore %8 %22
%23 = OpLoad %6 %10
%25 = OpIEqual %18 %23 %24
OpSelectionMerge %27 None
OpBranchConditional %25 %26 %27
%26 = OpLabel
OpBranch %13
%27 = OpLabel
OpBranch %14
%14 = OpLabel
%29 = OpLoad %6 %10
%31 = OpIAdd %6 %29 %30
OpStore %10 %31
OpBranch %11
%13 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
TransformationDuplicateRegionWithSelection transformation_bad =
TransformationDuplicateRegionWithSelection(
500, 50, 501, 27, 14, {{27, 101}, {14, 102}}, {{29, 201}, {31, 202}},
{{29, 301}, {31, 302}});
ASSERT_FALSE(
transformation_bad.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationDuplicateRegionWithSelectionTest,
MultiplePredecessorsNotApplicableTest) {
// This test handles a case where the entry block has multiple predecessors
// and the transformation is not applicable.
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 %10 "fun1(i1;"
OpName %9 "a"
OpName %18 "b"
OpName %24 "b"
OpName %27 "param"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %2 %7
%13 = OpConstant %6 2
%14 = OpTypeBool
%23 = OpTypePointer Function %14
%25 = OpConstantTrue %14
%26 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%24 = OpVariable %23 Function
%27 = OpVariable %7 Function
OpStore %24 %25
OpStore %27 %26
%28 = OpFunctionCall %2 %10 %27
OpReturn
OpFunctionEnd
%10 = OpFunction %2 None %8
%9 = OpFunctionParameter %7
%11 = OpLabel
%18 = OpVariable %7 Function
%12 = OpLoad %6 %9
%15 = OpSLessThan %14 %12 %13
OpSelectionMerge %17 None
OpBranchConditional %15 %16 %20
%16 = OpLabel
%19 = OpLoad %6 %9
OpStore %18 %19
OpBranch %60
%20 = OpLabel
%21 = OpLoad %6 %9
%22 = OpIAdd %6 %21 %13
OpStore %18 %22
OpBranch %60
%60 = OpLabel
OpBranch %17
%17 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
TransformationDuplicateRegionWithSelection transformation_bad =
TransformationDuplicateRegionWithSelection(500, 25, 501, 60, 60,
{{60, 101}}, {{}}, {{}});
ASSERT_FALSE(
transformation_bad.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationDuplicateRegionWithSelectionTest, OverflowIds) {
// This test checks that the transformation correctly uses overflow ids, when
// they are both needed and provided.
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 %6 "fun("
OpName %10 "s"
OpName %17 "b"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%8 = OpTypeInt 32 1
%9 = OpTypePointer Function %8
%11 = OpConstant %8 0
%13 = OpConstant %8 2
%15 = OpTypeBool
%16 = OpTypePointer Function %15
%18 = OpConstantTrue %15
%4 = OpFunction %2 None %3
%5 = OpLabel
%17 = OpVariable %16 Function
OpStore %17 %18
%19 = OpFunctionCall %2 %6
OpReturn
OpFunctionEnd
%6 = OpFunction %2 None %3
%7 = OpLabel
%10 = OpVariable %9 Function
OpBranch %50
%50 = OpLabel
OpStore %10 %11
%12 = OpLoad %8 %10
%14 = OpIAdd %8 %12 %13
OpStore %10 %14
OpKill
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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));
auto overflow_ids_unique_ptr = MakeUnique<CounterOverflowIdSource>(1000);
auto overflow_ids_ptr = overflow_ids_unique_ptr.get();
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options,
std::move(overflow_ids_unique_ptr));
// The mappings do not provide sufficient ids, thus overflow ids are required.
TransformationDuplicateRegionWithSelection transformation_good_1 =
TransformationDuplicateRegionWithSelection(500, 18, 501, 50, 50, {},
{{12, 201}}, {{14, 302}});
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
ApplyAndCheckFreshIds(transformation_good_1, context.get(),
&transformation_context,
overflow_ids_ptr->GetIssuedOverflowIds());
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected_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 %6 "fun("
OpName %10 "s"
OpName %17 "b"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%8 = OpTypeInt 32 1
%9 = OpTypePointer Function %8
%11 = OpConstant %8 0
%13 = OpConstant %8 2
%15 = OpTypeBool
%16 = OpTypePointer Function %15
%18 = OpConstantTrue %15
%4 = OpFunction %2 None %3
%5 = OpLabel
%17 = OpVariable %16 Function
OpStore %17 %18
%19 = OpFunctionCall %2 %6
OpReturn
OpFunctionEnd
%6 = OpFunction %2 None %3
%7 = OpLabel
%10 = OpVariable %9 Function
OpBranch %500
%500 = OpLabel
OpSelectionMerge %501 None
OpBranchConditional %18 %50 %1000
%50 = OpLabel
OpStore %10 %11
%12 = OpLoad %8 %10
%14 = OpIAdd %8 %12 %13
OpStore %10 %14
OpBranch %501
%1000 = OpLabel
OpStore %10 %11
%201 = OpLoad %8 %10
%1002 = OpIAdd %8 %201 %13
OpStore %10 %1002
OpBranch %501
%501 = OpLabel
%1001 = OpPhi %8 %12 %50 %201 %1000
%302 = OpPhi %8 %14 %50 %1002 %1000
OpKill
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
TEST(TransformationDuplicateRegionWithSelectionTest,
RegionExitIsOpBranchConditional) {
// Checks the case where the exit block of a region ends with
// OpBranchConditional (but is not a header).
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
OpName %4 "main"
OpName %8 "i"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%50 = OpConstantTrue %17
%20 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%19 = OpLoad %6 %8
%21 = OpIAdd %6 %19 %20
OpStore %8 %21
OpBranchConditional %50 %13 %12
%13 = OpLabel
%22 = OpLoad %6 %8
%23 = OpIAdd %6 %22 %20
OpStore %8 %23
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
TransformationDuplicateRegionWithSelection transformation_good_1 =
TransformationDuplicateRegionWithSelection(
600, 50, 601, 11, 11, {{11, 602}}, {{19, 603}, {21, 604}},
{{19, 605}, {21, 606}});
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
ApplyAndCheckFreshIds(transformation_good_1, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
OpName %4 "main"
OpName %8 "i"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%50 = OpConstantTrue %17
%20 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %600 %12
%600 = OpLabel
OpSelectionMerge %601 None
OpBranchConditional %50 %11 %602
%11 = OpLabel
%19 = OpLoad %6 %8
%21 = OpIAdd %6 %19 %20
OpStore %8 %21
OpBranch %601
%602 = OpLabel
%603 = OpLoad %6 %8
%604 = OpIAdd %6 %603 %20
OpStore %8 %604
OpBranch %601
%601 = OpLabel
%605 = OpPhi %6 %19 %11 %603 %602
%606 = OpPhi %6 %21 %11 %604 %602
OpBranchConditional %50 %13 %12
%13 = OpLabel
%22 = OpLoad %6 %8
%23 = OpIAdd %6 %22 %20
OpStore %8 %23
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
TEST(TransformationDuplicateRegionWithSelectionTest,
RegionExitIsOpBranchConditionalUsingBooleanDefinedInBlock) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
OpName %4 "main"
OpName %8 "i"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%50 = OpConstantTrue %17
%20 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%19 = OpLoad %6 %8
%21 = OpIAdd %6 %19 %20
%70 = OpCopyObject %17 %50
OpStore %8 %21
OpBranchConditional %70 %13 %12
%13 = OpLabel
%22 = OpLoad %6 %8
%23 = OpIAdd %6 %22 %20
OpStore %8 %23
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
TransformationDuplicateRegionWithSelection transformation_good_1 =
TransformationDuplicateRegionWithSelection(
600, 50, 601, 11, 11, {{11, 602}}, {{19, 603}, {21, 604}, {70, 608}},
{{19, 605}, {21, 606}, {70, 607}});
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
ApplyAndCheckFreshIds(transformation_good_1, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
OpName %4 "main"
OpName %8 "i"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%50 = OpConstantTrue %17
%20 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %600 %12
%600 = OpLabel
OpSelectionMerge %601 None
OpBranchConditional %50 %11 %602
%11 = OpLabel
%19 = OpLoad %6 %8
%21 = OpIAdd %6 %19 %20
%70 = OpCopyObject %17 %50
OpStore %8 %21
OpBranch %601
%602 = OpLabel
%603 = OpLoad %6 %8
%604 = OpIAdd %6 %603 %20
%608 = OpCopyObject %17 %50
OpStore %8 %604
OpBranch %601
%601 = OpLabel
%605 = OpPhi %6 %19 %11 %603 %602
%606 = OpPhi %6 %21 %11 %604 %602
%607 = OpPhi %17 %70 %11 %608 %602
OpBranchConditional %607 %13 %12
%13 = OpLabel
%22 = OpLoad %6 %8
%23 = OpIAdd %6 %22 %20
OpStore %8 %23
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
TEST(TransformationDuplicateRegionWithSelectionTest,
RegionExitUsesOpReturnValueWithIdDefinedInRegion) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeFunction %6
%10 = OpConstant %6 2
%30 = OpTypeBool
%31 = OpConstantTrue %30
%4 = OpFunction %2 None %3
%5 = OpLabel
%13 = OpFunctionCall %6 %8
OpReturn
OpFunctionEnd
%8 = OpFunction %6 None %7
%9 = OpLabel
OpBranch %20
%20 = OpLabel
%21 = OpCopyObject %6 %10
OpReturnValue %21
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
TransformationDuplicateRegionWithSelection transformation_good_1 =
TransformationDuplicateRegionWithSelection(
600, 31, 601, 20, 20, {{20, 602}}, {{21, 603}}, {{21, 605}});
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
ApplyAndCheckFreshIds(transformation_good_1, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeFunction %6
%10 = OpConstant %6 2
%30 = OpTypeBool
%31 = OpConstantTrue %30
%4 = OpFunction %2 None %3
%5 = OpLabel
%13 = OpFunctionCall %6 %8
OpReturn
OpFunctionEnd
%8 = OpFunction %6 None %7
%9 = OpLabel
OpBranch %600
%600 = OpLabel
OpSelectionMerge %601 None
OpBranchConditional %31 %20 %602
%20 = OpLabel
%21 = OpCopyObject %6 %10
OpBranch %601
%602 = OpLabel
%603 = OpCopyObject %6 %10
OpBranch %601
%601 = OpLabel
%605 = OpPhi %6 %21 %20 %603 %602
OpReturnValue %605
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
TEST(TransformationDuplicateRegionWithSelectionTest,
InapplicableDueToOpTypeSampledImage) {
std::string reference_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %10
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
OpDecorate %10 RelaxedPrecision
OpDecorate %10 DescriptorSet 0
OpDecorate %10 Binding 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeImage %6 2D 0 0 0 1 Unknown
%8 = OpTypeSampledImage %7
%9 = OpTypePointer UniformConstant %8
%10 = OpVariable %9 UniformConstant
%12 = OpTypeVector %6 2
%13 = OpConstant %6 0
%14 = OpConstantComposite %12 %13 %13
%15 = OpTypeVector %6 4
%30 = OpTypeBool
%31 = OpConstantTrue %30
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %20
%20 = OpLabel
%11 = OpLoad %8 %10
OpBranch %21
%21 = OpLabel
%16 = OpImageSampleImplicitLod %15 %11 %14
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
ASSERT_FALSE(TransformationDuplicateRegionWithSelection(
600, 31, 601, 20, 20, {{20, 602}}, {{11, 603}}, {{11, 605}})
.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationDuplicateRegionWithSelectionTest,
DoNotProduceOpPhiWithVoidType) {
std::string reference_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%10 = OpTypeBool
%11 = OpConstantTrue %10
%4 = OpFunction %2 None %3
%12 = OpLabel
OpBranch %5
%5 = OpLabel
%8 = OpFunctionCall %2 %6
OpReturn
OpFunctionEnd
%6 = OpFunction %2 None %3
%7 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
TransformationDuplicateRegionWithSelection transformation(
100, 11, 101, 5, 5, {{5, 102}}, {{8, 103}}, {});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%10 = OpTypeBool
%11 = OpConstantTrue %10
%4 = OpFunction %2 None %3
%12 = OpLabel
OpBranch %100
%100 = OpLabel
OpSelectionMerge %101 None
OpBranchConditional %11 %5 %102
%5 = OpLabel
%8 = OpFunctionCall %2 %6
OpBranch %101
%102 = OpLabel
%103 = OpFunctionCall %2 %6
OpBranch %101
%101 = OpLabel
OpReturn
OpFunctionEnd
%6 = OpFunction %2 None %3
%7 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
TEST(TransformationDuplicateRegionWithSelectionTest,
DoNotProduceOpPhiWithDisallowedType) {
std::string reference_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
OpDecorate %13 DescriptorSet 0
OpDecorate %13 Binding 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 2
%8 = OpTypePointer Function %7
%10 = OpTypeImage %6 2D 0 0 0 1 Unknown
%11 = OpTypeSampledImage %10
%12 = OpTypePointer UniformConstant %11
%13 = OpVariable %12 UniformConstant
%15 = OpConstant %6 1
%16 = OpConstantComposite %7 %15 %15
%17 = OpTypeVector %6 4
%19 = OpTypeInt 32 0
%20 = OpConstant %19 0
%22 = OpTypePointer Function %6
%90 = OpTypeBool
%91 = OpConstantTrue %90
%4 = OpFunction %2 None %3
%5 = OpLabel
%9 = OpVariable %8 Function
OpBranch %81
%81 = OpLabel
%14 = OpLoad %11 %13
%18 = OpImageSampleImplicitLod %17 %14 %16
%21 = OpCompositeExtract %6 %18 0
%23 = OpAccessChain %22 %9 %20
OpStore %23 %21
OpBranch %80
%80 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
TransformationDuplicateRegionWithSelection transformation(
100, 91, 101, 81, 81, {{81, 102}},
{{14, 103}, {18, 104}, {21, 105}, {23, 106}}, {{18, 107}, {21, 108}});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
OpDecorate %13 DescriptorSet 0
OpDecorate %13 Binding 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 2
%8 = OpTypePointer Function %7
%10 = OpTypeImage %6 2D 0 0 0 1 Unknown
%11 = OpTypeSampledImage %10
%12 = OpTypePointer UniformConstant %11
%13 = OpVariable %12 UniformConstant
%15 = OpConstant %6 1
%16 = OpConstantComposite %7 %15 %15
%17 = OpTypeVector %6 4
%19 = OpTypeInt 32 0
%20 = OpConstant %19 0
%22 = OpTypePointer Function %6
%90 = OpTypeBool
%91 = OpConstantTrue %90
%4 = OpFunction %2 None %3
%5 = OpLabel
%9 = OpVariable %8 Function
OpBranch %100
%100 = OpLabel
OpSelectionMerge %101 None
OpBranchConditional %91 %81 %102
%81 = OpLabel
%14 = OpLoad %11 %13
%18 = OpImageSampleImplicitLod %17 %14 %16
%21 = OpCompositeExtract %6 %18 0
%23 = OpAccessChain %22 %9 %20
OpStore %23 %21
OpBranch %101
%102 = OpLabel
%103 = OpLoad %11 %13
%104 = OpImageSampleImplicitLod %17 %103 %16
%105 = OpCompositeExtract %6 %104 0
%106 = OpAccessChain %22 %9 %20
OpStore %106 %105
OpBranch %101
%101 = OpLabel
%107 = OpPhi %17 %18 %81 %104 %102
%108 = OpPhi %6 %21 %81 %105 %102
OpBranch %80
%80 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools