mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-08-17 17:31:47 +00:00
Bug: tint:3 Change-Id: I746b04b545da9dcd7c0b8805f264b67cb59c8a1c Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/20240 Reviewed-by: dan sinclair <dsinclair@google.com>
2557 lines
63 KiB
C++
2557 lines
63 KiB
C++
// Copyright 2020 The Tint Authors.
|
|
//
|
|
// 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 <string>
|
|
#include <vector>
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "src/reader/spirv/function.h"
|
|
#include "src/reader/spirv/parser_impl.h"
|
|
#include "src/reader/spirv/parser_impl_test_helper.h"
|
|
#include "src/reader/spirv/spirv_tools_helpers_test.h"
|
|
|
|
namespace tint {
|
|
namespace reader {
|
|
namespace spirv {
|
|
namespace {
|
|
|
|
using ::testing::ElementsAre;
|
|
using ::testing::Eq;
|
|
|
|
std::string CommonTypes() {
|
|
return R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical Simple
|
|
|
|
%void = OpTypeVoid
|
|
%voidfn = OpTypeFunction %void
|
|
|
|
%bool = OpTypeBool
|
|
%cond = OpUndef %bool
|
|
%cond2 = OpUndef %bool
|
|
%cond3 = OpUndef %bool
|
|
|
|
%uint = OpTypeInt 32 0
|
|
%selector = OpUndef %uint
|
|
|
|
%999 = OpConstant %uint 999
|
|
)";
|
|
}
|
|
|
|
TEST_F(SpvParserTest, TerminatorsAreSane_SingleBlock) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%42 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_TRUE(fe.TerminatorsAreSane());
|
|
}
|
|
|
|
TEST_F(SpvParserTest, TerminatorsAreSane_Sequence) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%20 = OpLabel
|
|
OpBranch %30
|
|
|
|
%30 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_TRUE(fe.TerminatorsAreSane()) << p->error();
|
|
}
|
|
|
|
TEST_F(SpvParserTest, TerminatorsAreSane_If) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%20 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpBranchConditional %cond %30 %40
|
|
|
|
%30 = OpLabel
|
|
OpBranch %99
|
|
|
|
%40 = OpLabel
|
|
OpBranch %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_TRUE(fe.TerminatorsAreSane()) << p->error();
|
|
}
|
|
|
|
TEST_F(SpvParserTest, TerminatorsAreSane_Switch) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpSwitch %selector %80 20 %20 30 %30
|
|
|
|
%20 = OpLabel
|
|
OpBranch %30 ; fall through
|
|
|
|
%30 = OpLabel
|
|
OpBranch %99
|
|
|
|
%80 = OpLabel
|
|
OpBranch %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_TRUE(fe.TerminatorsAreSane());
|
|
}
|
|
|
|
TEST_F(SpvParserTest, TerminatorsAreSane_Loop_SingleBlock) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %20 None
|
|
OpBranchConditional %cond %20 %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_TRUE(fe.TerminatorsAreSane());
|
|
}
|
|
|
|
TEST_F(SpvParserTest, TerminatorsAreSane_Loop_Simple) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %40 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %40
|
|
|
|
%40 = OpLabel
|
|
OpBranch %20 ; back edge
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_TRUE(fe.TerminatorsAreSane());
|
|
}
|
|
|
|
TEST_F(SpvParserTest, TerminatorsAreSane_Kill) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpKill
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_TRUE(fe.TerminatorsAreSane());
|
|
}
|
|
|
|
TEST_F(SpvParserTest, TerminatorsAreSane_Unreachable) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpUnreachable
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_TRUE(fe.TerminatorsAreSane());
|
|
}
|
|
|
|
TEST_F(SpvParserTest, TerminatorsAreSane_MissingTerminator) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
// The SPIRV-Tools internal representation rejects this case earlier.
|
|
EXPECT_FALSE(p->BuildAndParseInternalModuleExceptFunctions());
|
|
}
|
|
|
|
TEST_F(SpvParserTest, TerminatorsAreSane_DisallowLoopToEntryBlock) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpBranch %10 ; not allowed
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_FALSE(fe.TerminatorsAreSane());
|
|
EXPECT_THAT(p->error(), Eq("Block 20 branches to function entry block 10"));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, TerminatorsAreSane_DisallowNonBlock) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %999 ; definitely wrong
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_FALSE(fe.TerminatorsAreSane());
|
|
EXPECT_THAT(p->error(),
|
|
Eq("Block 10 in function 100 branches to 999 which is "
|
|
"not a block in the function"));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, TerminatorsAreSane_DisallowBlockInDifferentFunction) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %210
|
|
|
|
OpFunctionEnd
|
|
|
|
|
|
%200 = OpFunction %void None %voidfn
|
|
|
|
%210 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_FALSE(fe.TerminatorsAreSane());
|
|
EXPECT_THAT(p->error(), Eq("Block 10 in function 100 branches to 210 which "
|
|
"is not a block in the function"));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_NoMerges) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_TRUE(fe.RegisterMerges());
|
|
|
|
const auto* bi = fe.GetBlockInfo(10);
|
|
ASSERT_NE(bi, nullptr);
|
|
EXPECT_EQ(bi->merge_for_header, 0u);
|
|
EXPECT_EQ(bi->continue_for_header, 0u);
|
|
EXPECT_EQ(bi->header_for_merge, 0u);
|
|
EXPECT_EQ(bi->header_for_continue, 0u);
|
|
EXPECT_FALSE(bi->single_block_loop);
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_GoodSelectionMerge_BranchConditional) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpBranchConditional %cond %20 %99
|
|
|
|
%20 = OpLabel
|
|
OpBranch %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_TRUE(fe.RegisterMerges());
|
|
|
|
// Header points to the merge
|
|
const auto* bi10 = fe.GetBlockInfo(10);
|
|
ASSERT_NE(bi10, nullptr);
|
|
EXPECT_EQ(bi10->merge_for_header, 99u);
|
|
EXPECT_EQ(bi10->continue_for_header, 0u);
|
|
EXPECT_EQ(bi10->header_for_merge, 0u);
|
|
EXPECT_EQ(bi10->header_for_continue, 0u);
|
|
EXPECT_FALSE(bi10->single_block_loop);
|
|
|
|
// Middle block is neither header nor merge
|
|
const auto* bi20 = fe.GetBlockInfo(20);
|
|
ASSERT_NE(bi20, nullptr);
|
|
EXPECT_EQ(bi20->merge_for_header, 0u);
|
|
EXPECT_EQ(bi20->continue_for_header, 0u);
|
|
EXPECT_EQ(bi20->header_for_merge, 0u);
|
|
EXPECT_EQ(bi20->header_for_continue, 0u);
|
|
EXPECT_FALSE(bi20->single_block_loop);
|
|
|
|
// Merge block points to the header
|
|
const auto* bi99 = fe.GetBlockInfo(99);
|
|
ASSERT_NE(bi99, nullptr);
|
|
EXPECT_EQ(bi99->merge_for_header, 0u);
|
|
EXPECT_EQ(bi99->continue_for_header, 0u);
|
|
EXPECT_EQ(bi99->header_for_merge, 10u);
|
|
EXPECT_EQ(bi99->header_for_continue, 0u);
|
|
EXPECT_FALSE(bi99->single_block_loop);
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_GoodSelectionMerge_Switch) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpSwitch %selector %99 20 %20
|
|
|
|
%20 = OpLabel
|
|
OpBranch %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_TRUE(fe.RegisterMerges());
|
|
|
|
// Header points to the merge
|
|
const auto* bi10 = fe.GetBlockInfo(10);
|
|
ASSERT_NE(bi10, nullptr);
|
|
EXPECT_EQ(bi10->merge_for_header, 99u);
|
|
EXPECT_EQ(bi10->continue_for_header, 0u);
|
|
EXPECT_EQ(bi10->header_for_merge, 0u);
|
|
EXPECT_EQ(bi10->header_for_continue, 0u);
|
|
EXPECT_FALSE(bi10->single_block_loop);
|
|
|
|
// Middle block is neither header nor merge
|
|
const auto* bi20 = fe.GetBlockInfo(20);
|
|
ASSERT_NE(bi20, nullptr);
|
|
EXPECT_EQ(bi20->merge_for_header, 0u);
|
|
EXPECT_EQ(bi20->continue_for_header, 0u);
|
|
EXPECT_EQ(bi20->header_for_merge, 0u);
|
|
EXPECT_EQ(bi20->header_for_continue, 0u);
|
|
EXPECT_FALSE(bi20->single_block_loop);
|
|
|
|
// Merge block points to the header
|
|
const auto* bi99 = fe.GetBlockInfo(99);
|
|
ASSERT_NE(bi99, nullptr);
|
|
EXPECT_EQ(bi99->merge_for_header, 0u);
|
|
EXPECT_EQ(bi99->continue_for_header, 0u);
|
|
EXPECT_EQ(bi99->header_for_merge, 10u);
|
|
EXPECT_EQ(bi99->header_for_continue, 0u);
|
|
EXPECT_FALSE(bi99->single_block_loop);
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_GoodLoopMerge_SingleBlockLoop) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %20 None
|
|
OpBranchConditional %cond %20 %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_TRUE(fe.RegisterMerges());
|
|
|
|
// Entry block is not special
|
|
const auto* bi10 = fe.GetBlockInfo(10);
|
|
ASSERT_NE(bi10, nullptr);
|
|
EXPECT_EQ(bi10->merge_for_header, 0u);
|
|
EXPECT_EQ(bi10->continue_for_header, 0u);
|
|
EXPECT_EQ(bi10->header_for_merge, 0u);
|
|
EXPECT_EQ(bi10->header_for_continue, 0u);
|
|
EXPECT_FALSE(bi10->single_block_loop);
|
|
|
|
// Single block loop is its own continue, and marked as single block loop.
|
|
const auto* bi20 = fe.GetBlockInfo(20);
|
|
ASSERT_NE(bi20, nullptr);
|
|
EXPECT_EQ(bi20->merge_for_header, 99u);
|
|
EXPECT_EQ(bi20->continue_for_header, 20u);
|
|
EXPECT_EQ(bi20->header_for_merge, 0u);
|
|
EXPECT_EQ(bi20->header_for_continue, 20u);
|
|
EXPECT_TRUE(bi20->single_block_loop);
|
|
|
|
// Merge block points to the header
|
|
const auto* bi99 = fe.GetBlockInfo(99);
|
|
ASSERT_NE(bi99, nullptr);
|
|
EXPECT_EQ(bi99->merge_for_header, 0u);
|
|
EXPECT_EQ(bi99->continue_for_header, 0u);
|
|
EXPECT_EQ(bi99->header_for_merge, 20u);
|
|
EXPECT_EQ(bi99->header_for_continue, 0u);
|
|
EXPECT_FALSE(bi99->single_block_loop);
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_GoodLoopMerge_MultiBlockLoop_Branch) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %40 None
|
|
OpBranch %30
|
|
|
|
%30 = OpLabel
|
|
OpBranchConditional %cond %40 %99
|
|
|
|
%40 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_TRUE(fe.RegisterMerges());
|
|
|
|
// Loop header points to continue and merge
|
|
const auto* bi20 = fe.GetBlockInfo(20);
|
|
ASSERT_NE(bi20, nullptr);
|
|
EXPECT_EQ(bi20->merge_for_header, 99u);
|
|
EXPECT_EQ(bi20->continue_for_header, 40u);
|
|
EXPECT_EQ(bi20->header_for_merge, 0u);
|
|
EXPECT_EQ(bi20->header_for_continue, 0u);
|
|
EXPECT_FALSE(bi20->single_block_loop);
|
|
|
|
// Continue block points to header
|
|
const auto* bi40 = fe.GetBlockInfo(40);
|
|
ASSERT_NE(bi40, nullptr);
|
|
EXPECT_EQ(bi40->merge_for_header, 0u);
|
|
EXPECT_EQ(bi40->continue_for_header, 0u);
|
|
EXPECT_EQ(bi40->header_for_merge, 0u);
|
|
EXPECT_EQ(bi40->header_for_continue, 20u);
|
|
EXPECT_FALSE(bi40->single_block_loop);
|
|
|
|
// Merge block points to the header
|
|
const auto* bi99 = fe.GetBlockInfo(99);
|
|
ASSERT_NE(bi99, nullptr);
|
|
EXPECT_EQ(bi99->merge_for_header, 0u);
|
|
EXPECT_EQ(bi99->continue_for_header, 0u);
|
|
EXPECT_EQ(bi99->header_for_merge, 20u);
|
|
EXPECT_EQ(bi99->header_for_continue, 0u);
|
|
EXPECT_FALSE(bi99->single_block_loop);
|
|
}
|
|
|
|
TEST_F(SpvParserTest,
|
|
RegisterMerges_GoodLoopMerge_MultiBlockLoop_BranchConditional) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %40 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %40
|
|
|
|
%40 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_TRUE(fe.RegisterMerges());
|
|
|
|
// Loop header points to continue and merge
|
|
const auto* bi20 = fe.GetBlockInfo(20);
|
|
ASSERT_NE(bi20, nullptr);
|
|
EXPECT_EQ(bi20->merge_for_header, 99u);
|
|
EXPECT_EQ(bi20->continue_for_header, 40u);
|
|
EXPECT_EQ(bi20->header_for_merge, 0u);
|
|
EXPECT_EQ(bi20->header_for_continue, 0u);
|
|
EXPECT_FALSE(bi20->single_block_loop);
|
|
|
|
// Continue block points to header
|
|
const auto* bi40 = fe.GetBlockInfo(40);
|
|
ASSERT_NE(bi40, nullptr);
|
|
EXPECT_EQ(bi40->merge_for_header, 0u);
|
|
EXPECT_EQ(bi40->continue_for_header, 0u);
|
|
EXPECT_EQ(bi40->header_for_merge, 0u);
|
|
EXPECT_EQ(bi40->header_for_continue, 20u);
|
|
EXPECT_FALSE(bi40->single_block_loop);
|
|
|
|
// Merge block points to the header
|
|
const auto* bi99 = fe.GetBlockInfo(99);
|
|
ASSERT_NE(bi99, nullptr);
|
|
EXPECT_EQ(bi99->merge_for_header, 0u);
|
|
EXPECT_EQ(bi99->continue_for_header, 0u);
|
|
EXPECT_EQ(bi99->header_for_merge, 20u);
|
|
EXPECT_EQ(bi99->header_for_continue, 0u);
|
|
EXPECT_FALSE(bi99->single_block_loop);
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_SelectionMerge_BadTerminator) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpBranch %30
|
|
|
|
%20 = OpLabel
|
|
OpBranch %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_FALSE(fe.RegisterMerges());
|
|
EXPECT_THAT(p->error(), Eq("Selection header 10 does not end in an "
|
|
"OpBranchConditional or OpSwitch instruction"));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_LoopMerge_BadTerminator) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %40 None
|
|
OpSwitch %selector %99 30 %30
|
|
|
|
%30 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_FALSE(fe.RegisterMerges());
|
|
EXPECT_THAT(p->error(), Eq("Loop header 20 does not end in an OpBranch or "
|
|
"OpBranchConditional instruction"));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_BadMergeBlock) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %void None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_FALSE(fe.RegisterMerges());
|
|
EXPECT_THAT(p->error(),
|
|
Eq("Structured header block 10 declares invalid merge block 1"));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_HeaderIsItsOwnMerge) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %10 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_FALSE(fe.RegisterMerges());
|
|
EXPECT_THAT(p->error(),
|
|
Eq("Structured header block 10 cannot be its own merge block"));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_MergeReused) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %49 None
|
|
OpBranchConditional %cond %20 %49
|
|
|
|
%20 = OpLabel
|
|
OpBranch %49
|
|
|
|
%49 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpSelectionMerge %49 None ; can't reuse merge block
|
|
OpBranchConditional %cond %60 %99
|
|
|
|
%60 = OpLabel
|
|
OpBranch %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_FALSE(fe.RegisterMerges());
|
|
EXPECT_THAT(
|
|
p->error(),
|
|
Eq("Block 49 declared as merge block for more than one header: 10, 50"));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_EntryBlockIsLoopHeader) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpLoopMerge %99 %30 None
|
|
OpBranchConditional %cond %10 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %10
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_FALSE(fe.RegisterMerges());
|
|
EXPECT_THAT(p->error(),
|
|
Eq("Function entry block 10 cannot be a loop header"));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_BadContinueTarget) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %999 None
|
|
OpBranchConditional %cond %20 %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_FALSE(fe.RegisterMerges());
|
|
EXPECT_THAT(p->error(),
|
|
Eq("Structured header 20 declares invalid continue target 999"));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_MergeSameAsContinue) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %50 %50 None
|
|
OpBranchConditional %cond %20 %99
|
|
|
|
|
|
%50 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_FALSE(fe.RegisterMerges());
|
|
EXPECT_THAT(p->error(),
|
|
Eq("Invalid structured header block 20: declares block 50 as "
|
|
"both its merge block and continue target"));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_ContinueReused) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %49 %40 None
|
|
OpBranchConditional %cond %30 %49
|
|
|
|
%30 = OpLabel
|
|
OpBranch %40
|
|
|
|
%40 = OpLabel
|
|
OpBranch %20
|
|
|
|
%49 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpLoopMerge %99 %40 None
|
|
OpBranchConditional %cond %60 %99
|
|
|
|
%60 = OpLabel
|
|
OpBranch %70
|
|
|
|
%70 = OpLabel
|
|
OpBranch %50
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_FALSE(fe.RegisterMerges());
|
|
EXPECT_THAT(p->error(), Eq("Block 40 declared as continue target for more "
|
|
"than one header: 20, 50"));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_SingleBlockLoop_NotItsOwnContinue) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %30 None
|
|
OpBranchConditional %cond %20 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_FALSE(fe.RegisterMerges());
|
|
EXPECT_THAT(
|
|
p->error(),
|
|
Eq("Block 20 branches to itself but is not its own continue target"));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, RegisterMerges_NotSingleBlockLoop_IsItsOwnContinue) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %20 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
EXPECT_FALSE(fe.RegisterMerges());
|
|
EXPECT_THAT(p->error(), Eq("Loop header block 20 declares itself as its own "
|
|
"continue target, but does not branch to itself"));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_OneBlock) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%42 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(42));
|
|
|
|
const auto* bi = fe.GetBlockInfo(42);
|
|
ASSERT_NE(bi, nullptr);
|
|
EXPECT_EQ(bi->pos, 0u);
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_IgnoreStaticalyUnreachable) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%15 = OpLabel ; statically dead
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpReturn
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_KillIsDeadEnd) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%15 = OpLabel ; statically dead
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpKill ; Kill doesn't lead anywhere
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_UnreachableIsDeadEnd) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%15 = OpLabel ; statically dead
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpUnreachable ; Unreachable doesn't lead anywhere
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_ReorderSequence) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%30 = OpLabel
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpBranch %30 ; backtrack
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30));
|
|
|
|
const auto* bi10 = fe.GetBlockInfo(10);
|
|
ASSERT_NE(bi10, nullptr);
|
|
EXPECT_EQ(bi10->pos, 0u);
|
|
const auto* bi20 = fe.GetBlockInfo(20);
|
|
ASSERT_NE(bi20, nullptr);
|
|
EXPECT_EQ(bi20->pos, 1u);
|
|
const auto* bi30 = fe.GetBlockInfo(30);
|
|
ASSERT_NE(bi30, nullptr);
|
|
EXPECT_EQ(bi30->pos, 2u);
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_DupConditionalBranch) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpBranchConditional %cond %20 %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpBranch %99
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_RespectConditionalBranchOrder) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpBranchConditional %cond %20 %30
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%30 = OpLabel
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpBranch %99
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_TrueOnlyBranch) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpBranchConditional %cond %20 %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpBranch %99
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_FalseOnlyBranch) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpBranchConditional %cond %99 %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpBranch %99
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_SwitchOrderNaturallyReversed) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpSwitch %selector %99 20 %20 30 %30
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%30 = OpLabel
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpBranch %99
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 30, 20, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest,
|
|
ComputeBlockOrder_SwitchWithDefaultOrderNaturallyReversed) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpSwitch %selector %80 20 %20 30 %30
|
|
|
|
%80 = OpLabel ; the default case
|
|
OpBranch %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%30 = OpLabel
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpBranch %99
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 30, 20, 80, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest,
|
|
ComputeBlockOrder_Switch_DefaultSameAsACase) {
|
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpSwitch %selector %30 20 %20 30 %30 40 %40
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%30 = OpLabel
|
|
OpBranch %99
|
|
|
|
%20 = OpLabel
|
|
OpBranch %99
|
|
|
|
%40 = OpLabel
|
|
OpBranch %99
|
|
|
|
OpFunctionEnd
|
|
)"));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 40, 20, 30, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_RespectSwitchCaseFallthrough) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpSwitch %selector %99 20 %20 30 %30 40 %40 50 %50
|
|
|
|
%50 = OpLabel
|
|
OpBranch %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%40 = OpLabel
|
|
OpBranch %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %50 ; fallthrough
|
|
|
|
%20 = OpLabel
|
|
OpBranch %40 ; fallthrough
|
|
|
|
OpFunctionEnd
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 30, 50, 20, 40, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest,
|
|
ComputeBlockOrder_RespectSwitchCaseFallthrough_FromDefault) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpSwitch %selector %80 20 %20 30 %30 40 %40
|
|
|
|
%80 = OpLabel ; the default case
|
|
OpBranch %30 ; fallthrough to another case
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%40 = OpLabel
|
|
OpBranch %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %40
|
|
|
|
%20 = OpLabel
|
|
OpBranch %99
|
|
|
|
OpFunctionEnd
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 80, 30, 40, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest,
|
|
ComputeBlockOrder_RespectSwitchCaseFallthrough_FromCaseToDefaultToCase) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpSwitch %selector %80 20 %20 30 %30
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpBranch %80 ; fallthrough to default
|
|
|
|
%80 = OpLabel ; the default case
|
|
OpBranch %30 ; fallthrough to 30
|
|
|
|
%30 = OpLabel
|
|
OpBranch %99
|
|
|
|
OpFunctionEnd
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 80, 30, 99)) << assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest,
|
|
ComputeBlockOrder_SwitchCasesFallthrough_OppositeDirections) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpSwitch %selector %99 20 %20 30 %30 40 %40 50 %50
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpBranch %30 ; forward
|
|
|
|
%40 = OpLabel
|
|
OpBranch %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %99
|
|
|
|
; SPIR-V doesn't actually allow a fall-through that goes backward in the
|
|
; module. But the block ordering algorithm tolerates it.
|
|
%50 = OpLabel
|
|
OpBranch %40 ; backward
|
|
|
|
OpFunctionEnd
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 50, 40, 20, 30, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest,
|
|
ComputeBlockOrder_RespectSwitchCaseFallthrough_Interleaved) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpSwitch %selector %99 20 %20 30 %30 40 %40 50 %50
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpBranch %40
|
|
|
|
%30 = OpLabel
|
|
OpBranch %50
|
|
|
|
%40 = OpLabel
|
|
OpBranch %60
|
|
|
|
%50 = OpLabel
|
|
OpBranch %70
|
|
|
|
%60 = OpLabel
|
|
OpBranch %99
|
|
|
|
%70 = OpLabel
|
|
OpBranch %99
|
|
|
|
OpFunctionEnd
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 30, 50, 70, 20, 40, 60, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest,
|
|
ComputeBlockOrder_Nest_If_Contains_If) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpBranchConditional %cond %20 %50
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpSelectionMerge %49 None
|
|
OpBranchConditional %cond %30 %40
|
|
|
|
%49 = OpLabel
|
|
OpBranch %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %49
|
|
|
|
%40 = OpLabel
|
|
OpBranch %49
|
|
|
|
%50 = OpLabel
|
|
OpSelectionMerge %79 None
|
|
OpBranchConditional %cond %60 %70
|
|
|
|
%79 = OpLabel
|
|
OpBranch %99
|
|
|
|
%60 = OpLabel
|
|
OpBranch %79
|
|
|
|
%70 = OpLabel
|
|
OpBranch %79
|
|
|
|
OpFunctionEnd
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(),
|
|
ElementsAre(10, 20, 30, 40, 49, 50, 60, 70, 79, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest,
|
|
ComputeBlockOrder_Nest_If_In_SwitchCase) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpSwitch %selector %50 20 %20 50 %50
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpSelectionMerge %49 None
|
|
OpBranchConditional %cond %30 %40
|
|
|
|
%49 = OpLabel
|
|
OpBranch %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %49
|
|
|
|
%40 = OpLabel
|
|
OpBranch %49
|
|
|
|
%50 = OpLabel
|
|
OpSelectionMerge %79 None
|
|
OpBranchConditional %cond %60 %70
|
|
|
|
%79 = OpLabel
|
|
OpBranch %99
|
|
|
|
%60 = OpLabel
|
|
OpBranch %79
|
|
|
|
%70 = OpLabel
|
|
OpBranch %79
|
|
|
|
OpFunctionEnd
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(),
|
|
ElementsAre(10, 20, 30, 40, 49, 50, 60, 70, 79, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest,
|
|
ComputeBlockOrder_Nest_IfFallthrough_In_SwitchCase) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpSwitch %selector %50 20 %20 50 %50
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpSelectionMerge %49 None
|
|
OpBranchConditional %cond %30 %40
|
|
|
|
%49 = OpLabel
|
|
OpBranchConditional %cond %99 %50 ; fallthrough
|
|
|
|
%30 = OpLabel
|
|
OpBranch %49
|
|
|
|
%40 = OpLabel
|
|
OpBranch %49
|
|
|
|
%50 = OpLabel
|
|
OpSelectionMerge %79 None
|
|
OpBranchConditional %cond %60 %70
|
|
|
|
%79 = OpLabel
|
|
OpBranch %99
|
|
|
|
%60 = OpLabel
|
|
OpBranch %79
|
|
|
|
%70 = OpLabel
|
|
OpBranch %79
|
|
|
|
OpFunctionEnd
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(),
|
|
ElementsAre(10, 20, 30, 40, 49, 50, 60, 70, 79, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest,
|
|
ComputeBlockOrder_Nest_IfBreak_In_SwitchCase) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpSelectionMerge %99 None
|
|
OpSwitch %selector %50 20 %20 50 %50
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
|
|
%20 = OpLabel
|
|
OpSelectionMerge %49 None
|
|
OpBranchConditional %cond %99 %40 ; break-if
|
|
|
|
%49 = OpLabel
|
|
OpBranch %99
|
|
|
|
%40 = OpLabel
|
|
OpBranch %49
|
|
|
|
%50 = OpLabel
|
|
OpSelectionMerge %79 None
|
|
OpBranchConditional %cond %60 %99 ; break-unless
|
|
|
|
%79 = OpLabel
|
|
OpBranch %99
|
|
|
|
%60 = OpLabel
|
|
OpBranch %79
|
|
|
|
OpFunctionEnd
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 40, 49, 50, 60, 79, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_SingleBlock_Simple) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
; The entry block can't be the target of a branch
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %20 None
|
|
OpBranchConditional %cond %20 %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 99)) << assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_SingleBlock_Infinite) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
; The entry block can't be the target of a branch
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %20 None
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 99)) << assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_SingleBlock_DupInfinite) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
; The entry block can't be the target of a branch
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %20 None
|
|
OpBranchConditional %cond %20 %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 99)) << assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_HeaderHasBreakIf) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99 ; like While
|
|
|
|
%30 = OpLabel ; trivial body
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 99)) << assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_HeaderHasBreakUnless) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %99 %30 ; has break-unless
|
|
|
|
%30 = OpLabel ; trivial body
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 99)) << assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_BodyHasBreak) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %99 ; break
|
|
|
|
%50 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 99)) << assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_BodyHasBreakIf) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranchConditional %cond2 %99 %40 ; break-if
|
|
|
|
%40 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 50, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_BodyHasBreakUnless) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranchConditional %cond2 %40 %99 ; break-unless
|
|
|
|
%40 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 50, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Body_If) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpSelectionMerge %49 None
|
|
OpBranchConditional %cond2 %40 %45 ; nested if
|
|
|
|
%40 = OpLabel
|
|
OpBranch %49
|
|
|
|
%45 = OpLabel
|
|
OpBranch %49
|
|
|
|
%49 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 45, 49, 50, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Body_If_Break) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpSelectionMerge %49 None
|
|
OpBranchConditional %cond2 %40 %49 ; nested if
|
|
|
|
%40 = OpLabel
|
|
OpBranch %99 ; break from nested if
|
|
|
|
%49 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 49, 50, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_BodyHasContinueIf) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranchConditional %cond2 %50 %40 ; continue-if
|
|
|
|
%40 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 50, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_BodyHasContinueUnless) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranchConditional %cond2 %40 %50 ; continue-unless
|
|
|
|
%40 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 50, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Body_If_Continue) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpSelectionMerge %49 None
|
|
OpBranchConditional %cond2 %40 %49 ; nested if
|
|
|
|
%40 = OpLabel
|
|
OpBranch %50 ; continue from nested if
|
|
|
|
%49 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 49, 50, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Body_Switch) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpSelectionMerge %49 None
|
|
OpSwitch %selector %49 40 %40 45 %45 ; fully nested switch
|
|
|
|
%40 = OpLabel
|
|
OpBranch %49
|
|
|
|
%45 = OpLabel
|
|
OpBranch %49
|
|
|
|
%49 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 45, 40, 49, 50, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Body_Switch_CaseBreaks) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpSelectionMerge %49 None
|
|
OpSwitch %selector %49 40 %40 45 %45
|
|
|
|
%40 = OpLabel
|
|
; This case breaks out of the loop. This is not possible in C
|
|
; because "break" will escape the switch only.
|
|
OpBranch %99
|
|
|
|
%45 = OpLabel
|
|
OpBranch %49
|
|
|
|
%49 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 45, 40, 49, 50, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Body_Switch_CaseContinues) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpSelectionMerge %49 None
|
|
OpSwitch %selector %49 40 %40 45 %45
|
|
|
|
%40 = OpLabel
|
|
OpBranch %50 ; continue bypasses switch merge
|
|
|
|
%45 = OpLabel
|
|
OpBranch %49
|
|
|
|
%49 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 45, 40, 49, 50, 99))
|
|
<< assembly;
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_BodyHasSwitchContinueBreak) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpSwitch %selector %99 50 %50 ; default is break, 50 is continue
|
|
|
|
%40 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Continue_Sequence) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranch %60
|
|
|
|
%60 = OpLabel
|
|
OpBranch %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 60, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Continue_ContainsIf) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpSelectionMerge %89 None
|
|
OpBranchConditional %cond2 %60 %70
|
|
|
|
%89 = OpLabel
|
|
OpBranch %20 ; backedge
|
|
|
|
%60 = OpLabel
|
|
OpBranch %89
|
|
|
|
%70 = OpLabel
|
|
OpBranch %89
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 60, 70, 89, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Continue_HasBreakIf) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranchConditional %cond2 %99 %20
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Continue_HasBreakUnless) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpBranchConditional %cond2 %20 %99
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Continue_SwitchBreak) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel
|
|
OpSwitch %selector %20 99 %99 ; yes, this is obtuse but valid
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Loop) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpLoopMerge %49 %40 None
|
|
OpBranchConditional %cond2 %35 %49
|
|
|
|
%35 = OpLabel
|
|
OpBranch %37
|
|
|
|
%37 = OpLabel
|
|
OpBranch %40
|
|
|
|
%40 = OpLabel ; inner loop's continue
|
|
OpBranch %30 ; backedge
|
|
|
|
%49 = OpLabel ; inner loop's merge
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel ; outer loop's continue
|
|
OpBranch %20 ; outer loop's backege
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(),
|
|
ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Loop_InnerBreak) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpLoopMerge %49 %40 None
|
|
OpBranchConditional %cond2 %35 %49
|
|
|
|
%35 = OpLabel
|
|
OpBranchConditional %cond3 %49 %37 ; break to inner merge
|
|
|
|
%37 = OpLabel
|
|
OpBranch %40
|
|
|
|
%40 = OpLabel ; inner loop's continue
|
|
OpBranch %30 ; backedge
|
|
|
|
%49 = OpLabel ; inner loop's merge
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel ; outer loop's continue
|
|
OpBranch %20 ; outer loop's backege
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(),
|
|
ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Loop_InnerContinue) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpLoopMerge %49 %40 None
|
|
OpBranchConditional %cond2 %35 %49
|
|
|
|
%35 = OpLabel
|
|
OpBranchConditional %cond3 %37 %49 ; continue to inner continue target
|
|
|
|
%37 = OpLabel
|
|
OpBranch %40
|
|
|
|
%40 = OpLabel ; inner loop's continue
|
|
OpBranch %30 ; backedge
|
|
|
|
%49 = OpLabel ; inner loop's merge
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel ; outer loop's continue
|
|
OpBranch %20 ; outer loop's backege
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(),
|
|
ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Loop_InnerContinueBreaks) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpLoopMerge %49 %40 None
|
|
OpBranchConditional %cond2 %35 %49
|
|
|
|
%35 = OpLabel
|
|
OpBranch %37
|
|
|
|
%37 = OpLabel
|
|
OpBranch %40
|
|
|
|
%40 = OpLabel ; inner loop's continue
|
|
OpBranchConditional %cond3 %30 %49 ; backedge and inner break
|
|
|
|
%49 = OpLabel ; inner loop's merge
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel ; outer loop's continue
|
|
OpBranch %20 ; outer loop's backege
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(),
|
|
ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Loop_InnerContinueContinues) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpLoopMerge %49 %40 None
|
|
OpBranchConditional %cond2 %35 %49
|
|
|
|
%35 = OpLabel
|
|
OpBranch %37
|
|
|
|
%37 = OpLabel
|
|
OpBranch %40
|
|
|
|
%40 = OpLabel ; inner loop's continue
|
|
OpBranchConditional %cond3 %30 %50 ; backedge and continue to outer
|
|
|
|
%49 = OpLabel ; inner loop's merge
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel ; outer loop's continue
|
|
OpBranch %20 ; outer loop's backege
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(),
|
|
ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
|
|
}
|
|
|
|
TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Loop_SwitchBackedgeBreakContinue) {
|
|
auto assembly = CommonTypes() + R"(
|
|
%100 = OpFunction %void None %voidfn
|
|
|
|
%10 = OpLabel
|
|
OpBranch %20
|
|
|
|
%20 = OpLabel
|
|
OpLoopMerge %99 %50 None
|
|
OpBranchConditional %cond %30 %99
|
|
|
|
%30 = OpLabel
|
|
OpLoopMerge %49 %40 None
|
|
OpBranchConditional %cond2 %35 %49
|
|
|
|
%35 = OpLabel
|
|
OpBranch %37
|
|
|
|
%37 = OpLabel
|
|
OpBranch %40
|
|
|
|
%40 = OpLabel ; inner loop's continue
|
|
; This switch does triple duty:
|
|
; default -> backedge
|
|
; 49 -> loop break
|
|
; 49 -> inner loop break
|
|
; 50 -> outer loop continue
|
|
OpSwitch %selector %30 49 %49 50 %50
|
|
|
|
%49 = OpLabel ; inner loop's merge
|
|
OpBranch %50
|
|
|
|
%50 = OpLabel ; outer loop's continue
|
|
OpBranch %20 ; outer loop's backege
|
|
|
|
%99 = OpLabel
|
|
OpReturn
|
|
)";
|
|
auto* p = parser(test::Assemble(assembly));
|
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
FunctionEmitter fe(p, *spirv_function(100));
|
|
fe.RegisterBasicBlocks();
|
|
fe.ComputeBlockOrderAndPositions();
|
|
|
|
EXPECT_THAT(fe.block_order(),
|
|
ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace spirv
|
|
} // namespace reader
|
|
} // namespace tint
|