mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-01 15:30:06 +00:00
c6422cff33
Suggested by Jakub as 'DoesBlockExist' was confusing.
307 lines
9.8 KiB
C++
307 lines
9.8 KiB
C++
// Copyright (c) 2021 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/opt/control_dependence.h"
|
|
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
#include "gmock/gmock-matchers.h"
|
|
#include "gtest/gtest.h"
|
|
#include "source/opt/build_module.h"
|
|
#include "source/opt/cfg.h"
|
|
#include "test/opt/function_utils.h"
|
|
|
|
namespace spvtools {
|
|
namespace opt {
|
|
|
|
namespace {
|
|
void GatherEdges(const ControlDependenceAnalysis& cdg,
|
|
std::vector<ControlDependence>& ret) {
|
|
cdg.ForEachBlockLabel([&](uint32_t label) {
|
|
ret.reserve(ret.size() + cdg.GetDependenceTargets(label).size());
|
|
ret.insert(ret.end(), cdg.GetDependenceTargets(label).begin(),
|
|
cdg.GetDependenceTargets(label).end());
|
|
});
|
|
std::sort(ret.begin(), ret.end());
|
|
// Verify that reverse graph is the same.
|
|
std::vector<ControlDependence> reverse_edges;
|
|
reverse_edges.reserve(ret.size());
|
|
cdg.ForEachBlockLabel([&](uint32_t label) {
|
|
reverse_edges.insert(reverse_edges.end(),
|
|
cdg.GetDependenceSources(label).begin(),
|
|
cdg.GetDependenceSources(label).end());
|
|
});
|
|
std::sort(reverse_edges.begin(), reverse_edges.end());
|
|
ASSERT_THAT(reverse_edges, testing::ElementsAreArray(ret));
|
|
}
|
|
|
|
using ControlDependenceTest = ::testing::Test;
|
|
|
|
TEST(ControlDependenceTest, DependenceSimpleCFG) {
|
|
const std::string text = R"(
|
|
OpCapability Addresses
|
|
OpCapability Kernel
|
|
OpMemoryModel Physical64 OpenCL
|
|
OpEntryPoint Kernel %1 "main"
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%4 = OpTypeBool
|
|
%5 = OpTypeInt 32 0
|
|
%6 = OpConstant %5 0
|
|
%7 = OpConstantFalse %4
|
|
%8 = OpConstantTrue %4
|
|
%9 = OpConstant %5 1
|
|
%1 = OpFunction %2 None %3
|
|
%10 = OpLabel
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
OpSwitch %6 %12 1 %13
|
|
%12 = OpLabel
|
|
OpBranch %14
|
|
%13 = OpLabel
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
OpBranchConditional %8 %15 %16
|
|
%15 = OpLabel
|
|
OpBranch %19
|
|
%16 = OpLabel
|
|
OpBranchConditional %8 %17 %18
|
|
%17 = OpLabel
|
|
OpBranch %18
|
|
%18 = OpLabel
|
|
OpBranch %19
|
|
%19 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
// CFG: (all edges pointing downward)
|
|
// %10
|
|
// |
|
|
// %11
|
|
// / \ (R: %6 == 1, L: default)
|
|
// %12 %13
|
|
// \ /
|
|
// %14
|
|
// T/ \F
|
|
// %15 %16
|
|
// | T/ |F
|
|
// | %17|
|
|
// | \ |
|
|
// | %18
|
|
// | /
|
|
// %19
|
|
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
Module* module = context->module();
|
|
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
|
|
<< text << std::endl;
|
|
const Function* fn = spvtest::GetFunction(module, 1);
|
|
const BasicBlock* entry = spvtest::GetBasicBlock(fn, 10);
|
|
EXPECT_EQ(entry, fn->entry().get())
|
|
<< "The entry node is not the expected one";
|
|
|
|
{
|
|
PostDominatorAnalysis pdom;
|
|
const CFG& cfg = *context->cfg();
|
|
pdom.InitializeTree(cfg, fn);
|
|
ControlDependenceAnalysis cdg;
|
|
cdg.ComputeControlDependenceGraph(cfg, pdom);
|
|
|
|
// Test HasBlock.
|
|
for (uint32_t id = 10; id <= 19; id++) {
|
|
EXPECT_TRUE(cdg.HasBlock(id));
|
|
}
|
|
EXPECT_TRUE(cdg.HasBlock(ControlDependenceAnalysis::kPseudoEntryBlock));
|
|
// Check blocks before/after valid range.
|
|
EXPECT_FALSE(cdg.HasBlock(5));
|
|
EXPECT_FALSE(cdg.HasBlock(25));
|
|
EXPECT_FALSE(cdg.HasBlock(UINT32_MAX));
|
|
|
|
// Test ForEachBlockLabel.
|
|
std::set<uint32_t> block_labels;
|
|
cdg.ForEachBlockLabel([&block_labels](uint32_t id) {
|
|
bool inserted = block_labels.insert(id).second;
|
|
EXPECT_TRUE(inserted); // Should have no duplicates.
|
|
});
|
|
EXPECT_THAT(block_labels, testing::ElementsAre(0, 10, 11, 12, 13, 14, 15,
|
|
16, 17, 18, 19));
|
|
|
|
{
|
|
// Test WhileEachBlockLabel.
|
|
uint32_t iters = 0;
|
|
EXPECT_TRUE(cdg.WhileEachBlockLabel([&iters](uint32_t) {
|
|
++iters;
|
|
return true;
|
|
}));
|
|
EXPECT_EQ((uint32_t)block_labels.size(), iters);
|
|
iters = 0;
|
|
EXPECT_FALSE(cdg.WhileEachBlockLabel([&iters](uint32_t) {
|
|
++iters;
|
|
return false;
|
|
}));
|
|
EXPECT_EQ(1, iters);
|
|
}
|
|
|
|
// Test IsDependent.
|
|
EXPECT_TRUE(cdg.IsDependent(12, 11));
|
|
EXPECT_TRUE(cdg.IsDependent(13, 11));
|
|
EXPECT_TRUE(cdg.IsDependent(15, 14));
|
|
EXPECT_TRUE(cdg.IsDependent(16, 14));
|
|
EXPECT_TRUE(cdg.IsDependent(18, 14));
|
|
EXPECT_TRUE(cdg.IsDependent(17, 16));
|
|
EXPECT_TRUE(cdg.IsDependent(10, 0));
|
|
EXPECT_TRUE(cdg.IsDependent(11, 0));
|
|
EXPECT_TRUE(cdg.IsDependent(14, 0));
|
|
EXPECT_TRUE(cdg.IsDependent(19, 0));
|
|
EXPECT_FALSE(cdg.IsDependent(14, 11));
|
|
EXPECT_FALSE(cdg.IsDependent(17, 14));
|
|
EXPECT_FALSE(cdg.IsDependent(19, 14));
|
|
EXPECT_FALSE(cdg.IsDependent(12, 0));
|
|
|
|
// Test GetDependenceSources/Targets.
|
|
std::vector<ControlDependence> edges;
|
|
GatherEdges(cdg, edges);
|
|
EXPECT_THAT(edges,
|
|
testing::ElementsAre(
|
|
ControlDependence(0, 10), ControlDependence(0, 11, 10),
|
|
ControlDependence(0, 14, 10), ControlDependence(0, 19, 10),
|
|
ControlDependence(11, 12), ControlDependence(11, 13),
|
|
ControlDependence(14, 15), ControlDependence(14, 16),
|
|
ControlDependence(14, 18, 16), ControlDependence(16, 17)));
|
|
|
|
const uint32_t expected_condition_ids[] = {
|
|
0, 0, 0, 0, 6, 6, 8, 8, 8, 8,
|
|
};
|
|
|
|
for (uint32_t i = 0; i < edges.size(); i++) {
|
|
EXPECT_EQ(expected_condition_ids[i], edges[i].GetConditionID(cfg));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(ControlDependenceTest, DependencePaperCFG) {
|
|
const std::string text = R"(
|
|
OpCapability Addresses
|
|
OpCapability Kernel
|
|
OpMemoryModel Physical64 OpenCL
|
|
OpEntryPoint Kernel %101 "main"
|
|
%102 = OpTypeVoid
|
|
%103 = OpTypeFunction %102
|
|
%104 = OpTypeBool
|
|
%108 = OpConstantTrue %104
|
|
%101 = OpFunction %102 None %103
|
|
%1 = OpLabel
|
|
OpBranch %2
|
|
%2 = OpLabel
|
|
OpBranchConditional %108 %3 %7
|
|
%3 = OpLabel
|
|
OpBranchConditional %108 %4 %5
|
|
%4 = OpLabel
|
|
OpBranch %6
|
|
%5 = OpLabel
|
|
OpBranch %6
|
|
%6 = OpLabel
|
|
OpBranch %8
|
|
%7 = OpLabel
|
|
OpBranch %8
|
|
%8 = OpLabel
|
|
OpBranch %9
|
|
%9 = OpLabel
|
|
OpBranchConditional %108 %10 %11
|
|
%10 = OpLabel
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
OpBranchConditional %108 %12 %9
|
|
%12 = OpLabel
|
|
OpBranchConditional %108 %13 %2
|
|
%13 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
// CFG: (edges pointing downward if no arrow)
|
|
// %1
|
|
// |
|
|
// %2 <----+
|
|
// T/ \F |
|
|
// %3 \ |
|
|
// T/ \F \ |
|
|
// %4 %5 %7 |
|
|
// \ / / |
|
|
// %6 / |
|
|
// \ / |
|
|
// %8 |
|
|
// | |
|
|
// %9 <-+ |
|
|
// T/ | | |
|
|
// %10 | | |
|
|
// \ | | |
|
|
// %11-F+ |
|
|
// T| |
|
|
// %12-F---+
|
|
// T|
|
|
// %13
|
|
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
Module* module = context->module();
|
|
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
|
|
<< text << std::endl;
|
|
const Function* fn = spvtest::GetFunction(module, 101);
|
|
const BasicBlock* entry = spvtest::GetBasicBlock(fn, 1);
|
|
EXPECT_EQ(entry, fn->entry().get())
|
|
<< "The entry node is not the expected one";
|
|
|
|
{
|
|
PostDominatorAnalysis pdom;
|
|
const CFG& cfg = *context->cfg();
|
|
pdom.InitializeTree(cfg, fn);
|
|
ControlDependenceAnalysis cdg;
|
|
cdg.ComputeControlDependenceGraph(cfg, pdom);
|
|
|
|
std::vector<ControlDependence> edges;
|
|
GatherEdges(cdg, edges);
|
|
EXPECT_THAT(
|
|
edges, testing::ElementsAre(
|
|
ControlDependence(0, 1), ControlDependence(0, 2, 1),
|
|
ControlDependence(0, 8, 1), ControlDependence(0, 9, 1),
|
|
ControlDependence(0, 11, 1), ControlDependence(0, 12, 1),
|
|
ControlDependence(0, 13, 1), ControlDependence(2, 3),
|
|
ControlDependence(2, 6, 3), ControlDependence(2, 7),
|
|
ControlDependence(3, 4), ControlDependence(3, 5),
|
|
ControlDependence(9, 10), ControlDependence(11, 9),
|
|
ControlDependence(11, 11, 9), ControlDependence(12, 2),
|
|
ControlDependence(12, 8, 2), ControlDependence(12, 9, 2),
|
|
ControlDependence(12, 11, 2), ControlDependence(12, 12, 2)));
|
|
|
|
const uint32_t expected_condition_ids[] = {
|
|
0, 0, 0, 0, 0, 0, 0, 108, 108, 108,
|
|
108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
|
|
};
|
|
|
|
for (uint32_t i = 0; i < edges.size(); i++) {
|
|
EXPECT_EQ(expected_condition_ids[i], edges[i].GetConditionID(cfg));
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace opt
|
|
} // namespace spvtools
|