blob: d33ccba5b2958277e5580f155c381d7f24b0eae1 [file] [log] [blame]
// Copyright (c) 2019 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_split_block.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationSplitBlockTest, NotApplicable) {
// The SPIR-V in this test came from the following fragment shader, with
// local store elimination applied to get some OpPhi instructions.
//
// void main() {
// int x;
// int i;
// for (i = 0; i < 100; i++) {
// x += i;
// }
// }
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 "i"
OpName %19 "x"
OpDecorate %8 RelaxedPrecision
OpDecorate %19 RelaxedPrecision
OpDecorate %22 RelaxedPrecision
OpDecorate %25 RelaxedPrecision
OpDecorate %26 RelaxedPrecision
OpDecorate %27 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%24 = OpConstant %6 1
%28 = OpUndef %6
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%19 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
%27 = OpPhi %6 %28 %5 %22 %13
%26 = OpPhi %6 %9 %5 %25 %13
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %26 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%22 = OpIAdd %6 %27 %26
OpStore %19 %22
OpBranch %13
%13 = OpLabel
%25 = OpIAdd %6 %26 %24
OpStore %8 %25
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
// No split before OpVariable
ASSERT_FALSE(TransformationSplitBlock(8, 0, 100).IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(8, 1, 100).IsApplicable(context.get(),
fact_manager));
// No split before OpLabel
ASSERT_FALSE(TransformationSplitBlock(14, 0, 100)
.IsApplicable(context.get(), fact_manager));
// No split if base instruction is outside a function
ASSERT_FALSE(TransformationSplitBlock(1, 0, 100).IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(1, 4, 100).IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(1, 35, 100)
.IsApplicable(context.get(), fact_manager));
// No split if block is loop header
ASSERT_FALSE(TransformationSplitBlock(27, 0, 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSplitBlock(27, 1, 100)
.IsApplicable(context.get(), fact_manager));
// No split if base instruction does not exist
ASSERT_FALSE(TransformationSplitBlock(88, 0, 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSplitBlock(88, 22, 100)
.IsApplicable(context.get(), fact_manager));
// No split if offset is too large (goes into another block)
ASSERT_FALSE(TransformationSplitBlock(18, 3, 100)
.IsApplicable(context.get(), fact_manager));
// No split if id in use
ASSERT_FALSE(TransformationSplitBlock(18, 0, 27).IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(18, 0, 14).IsApplicable(context.get(),
fact_manager));
}
TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) {
// The SPIR-V in this test came from the following fragment shader:
//
// void main() {
// int a;
// int b;
// a = 1;
// b = a;
// a = b;
// b = 2;
// b++;
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "a"
OpName %10 "b"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
OpDecorate %14 RelaxedPrecision
OpDecorate %15 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%13 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpStore %8 %9
%11 = OpLoad %6 %8
OpStore %10 %11
%12 = OpLoad %6 %10
OpStore %8 %12
OpStore %10 %13
%14 = OpLoad %6 %10
%15 = OpIAdd %6 %14 %9
OpStore %10 %15
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
auto split_1 = TransformationSplitBlock(5, 3, 100);
ASSERT_TRUE(split_1.IsApplicable(context.get(), fact_manager));
split_1.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split_1 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "a"
OpName %10 "b"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
OpDecorate %14 RelaxedPrecision
OpDecorate %15 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%13 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpBranch %100
%100 = OpLabel
OpStore %8 %9
%11 = OpLoad %6 %8
OpStore %10 %11
%12 = OpLoad %6 %10
OpStore %8 %12
OpStore %10 %13
%14 = OpLoad %6 %10
%15 = OpIAdd %6 %14 %9
OpStore %10 %15
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_split_1, context.get()));
auto split_2 = TransformationSplitBlock(11, 1, 101);
ASSERT_TRUE(split_2.IsApplicable(context.get(), fact_manager));
split_2.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split_2 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "a"
OpName %10 "b"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
OpDecorate %14 RelaxedPrecision
OpDecorate %15 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%13 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpBranch %100
%100 = OpLabel
OpStore %8 %9
%11 = OpLoad %6 %8
OpBranch %101
%101 = OpLabel
OpStore %10 %11
%12 = OpLoad %6 %10
OpStore %8 %12
OpStore %10 %13
%14 = OpLoad %6 %10
%15 = OpIAdd %6 %14 %9
OpStore %10 %15
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_split_2, context.get()));
auto split_3 = TransformationSplitBlock(14, 0, 102);
ASSERT_TRUE(split_3.IsApplicable(context.get(), fact_manager));
split_3.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split_3 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "a"
OpName %10 "b"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
OpDecorate %14 RelaxedPrecision
OpDecorate %15 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%13 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpBranch %100
%100 = OpLabel
OpStore %8 %9
%11 = OpLoad %6 %8
OpBranch %101
%101 = OpLabel
OpStore %10 %11
%12 = OpLoad %6 %10
OpStore %8 %12
OpStore %10 %13
OpBranch %102
%102 = OpLabel
%14 = OpLoad %6 %10
%15 = OpIAdd %6 %14 %9
OpStore %10 %15
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_split_3, context.get()));
}
TEST(TransformationSplitBlockTest, SplitBlockBeforeSelectBranch) {
// The SPIR-V in this test came from the following fragment shader:
//
// void main() {
// int x, y;
// x = 2;
// if (x < y) {
// y = 3;
// } else {
// y = 4;
// }
// }
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 "x"
OpName %11 "y"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 2
%13 = OpTypeBool
%17 = OpConstant %6 3
%19 = OpConstant %6 4
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%11 = OpVariable %7 Function
OpStore %8 %9
%10 = OpLoad %6 %8
%12 = OpLoad %6 %11
%14 = OpSLessThan %13 %10 %12
OpSelectionMerge %16 None
OpBranchConditional %14 %15 %18
%15 = OpLabel
OpStore %11 %17
OpBranch %16
%18 = OpLabel
OpStore %11 %19
OpBranch %16
%16 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
// Illegal to split between the merge and the conditional branch.
ASSERT_FALSE(TransformationSplitBlock(14, 2, 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSplitBlock(12, 3, 100)
.IsApplicable(context.get(), fact_manager));
auto split = TransformationSplitBlock(14, 1, 100);
ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
split.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split = 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 "x"
OpName %11 "y"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 2
%13 = OpTypeBool
%17 = OpConstant %6 3
%19 = OpConstant %6 4
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%11 = OpVariable %7 Function
OpStore %8 %9
%10 = OpLoad %6 %8
%12 = OpLoad %6 %11
%14 = OpSLessThan %13 %10 %12
OpBranch %100
%100 = OpLabel
OpSelectionMerge %16 None
OpBranchConditional %14 %15 %18
%15 = OpLabel
OpStore %11 %17
OpBranch %16
%18 = OpLabel
OpStore %11 %19
OpBranch %16
%16 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_split, context.get()));
}
TEST(TransformationSplitBlockTest, SplitBlockBeforeSwitchBranch) {
// The SPIR-V in this test came from the following fragment shader:
//
// void main() {
// int x, y;
// switch (y) {
// case 1:
// x = 2;
// case 2:
// break;
// case 3:
// x = 4;
// default:
// x = 6;
// }
// }
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 "y"
OpName %15 "x"
OpDecorate %8 RelaxedPrecision
OpDecorate %9 RelaxedPrecision
OpDecorate %15 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%16 = OpConstant %6 2
%18 = OpConstant %6 4
%19 = OpConstant %6 6
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%15 = OpVariable %7 Function
%9 = OpLoad %6 %8
OpSelectionMerge %14 None
OpSwitch %9 %13 1 %10 2 %11 3 %12
%13 = OpLabel
OpStore %15 %19
OpBranch %14
%10 = OpLabel
OpStore %15 %16
OpBranch %11
%11 = OpLabel
OpBranch %14
%12 = OpLabel
OpStore %15 %18
OpBranch %13
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
// Illegal to split between the merge and the conditional branch.
ASSERT_FALSE(TransformationSplitBlock(9, 2, 100).IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(15, 3, 100)
.IsApplicable(context.get(), fact_manager));
auto split = TransformationSplitBlock(9, 1, 100);
ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
split.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split = 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 "y"
OpName %15 "x"
OpDecorate %8 RelaxedPrecision
OpDecorate %9 RelaxedPrecision
OpDecorate %15 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%16 = OpConstant %6 2
%18 = OpConstant %6 4
%19 = OpConstant %6 6
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%15 = OpVariable %7 Function
%9 = OpLoad %6 %8
OpBranch %100
%100 = OpLabel
OpSelectionMerge %14 None
OpSwitch %9 %13 1 %10 2 %11 3 %12
%13 = OpLabel
OpStore %15 %19
OpBranch %14
%10 = OpLabel
OpStore %15 %16
OpBranch %11
%11 = OpLabel
OpBranch %14
%12 = OpLabel
OpStore %15 %18
OpBranch %13
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_split, context.get()));
}
TEST(TransformationSplitBlockTest, NoSplitDuringOpPhis) {
// The SPIR-V in this test came from the following fragment shader, with
// local store elimination applied to get some OpPhi instructions.
//
// void main() {
// int x;
// int i;
// for (i = 0; i < 100; i++) {
// x += i;
// }
// }
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 "i"
OpName %19 "x"
OpDecorate %8 RelaxedPrecision
OpDecorate %19 RelaxedPrecision
OpDecorate %22 RelaxedPrecision
OpDecorate %25 RelaxedPrecision
OpDecorate %26 RelaxedPrecision
OpDecorate %27 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%24 = OpConstant %6 1
%28 = OpUndef %6
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%19 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
%27 = OpPhi %6 %28 %5 %22 %13
%26 = OpPhi %6 %9 %5 %25 %13
OpBranch %50
%50 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %26 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%22 = OpIAdd %6 %27 %26
OpStore %19 %22
OpBranch %13
%13 = OpLabel
%25 = OpIAdd %6 %26 %24
OpStore %8 %25
OpBranch %50
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
// We cannot split before OpPhi instructions, since the number of incoming
// blocks may not appropriately match after splitting.
ASSERT_FALSE(TransformationSplitBlock(26, 0, 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSplitBlock(27, 0, 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSplitBlock(27, 1, 100)
.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) {
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 "x"
OpName %10 "y"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpStore %8 %9
%11 = OpLoad %6 %8
OpBranch %20
%20 = OpLabel
%21 = OpPhi %6 %11 %5
OpStore %10 %21
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
ASSERT_TRUE(TransformationSplitBlock(21, 0, 100)
.IsApplicable(context.get(), fact_manager));
auto split = TransformationSplitBlock(20, 1, 100);
ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
split.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split = 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 "x"
OpName %10 "y"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpStore %8 %9
%11 = OpLoad %6 %8
OpBranch %20
%20 = OpLabel
OpBranch %100
%100 = OpLabel
%21 = OpPhi %6 %11 %20
OpStore %10 %21
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_split, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools