Add continue construct analysis to struct cfg analysis (#2922)

* Add continue construct analysis to struct cfg analysis

Add the ability to identify which blocks are in the continue construct for a
loop, and to get functions that are called from those blocks, directly or
indirectly.

Part of https://github.com/KhronosGroup/SPIRV-Tools/issues/2912.
This commit is contained in:
Steven Perron 2019-10-01 10:27:09 -04:00 коммит произвёл GitHub
Родитель 85c67b5e08
Коммит 9eb1c9a4c4
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 632 добавлений и 8 удалений

Просмотреть файл

@ -60,8 +60,9 @@ class CFG {
// Compute structured block order into |order| for |func| starting at |root|.
// This order has the property that dominators come before all blocks they
// dominate and merge blocks come after all blocks that are in the control
// constructs of their header.
// dominate, merge blocks come after all blocks that are in the control
// constructs of their header, and continue blocks come after all of the
// blocks in the body of their loop.
void ComputeStructuredOrder(Function* func, BasicBlock* root,
std::list<BasicBlock*>* order);

Просмотреть файл

@ -540,7 +540,7 @@ class IRContext {
return GetFunction(inst->result_id());
}
// Add to |todo| all ids of functions called in |func|.
// Add to |todo| all ids of functions called directly from |func|.
void AddCalls(const Function* func, std::queue<uint32_t>* todo);
// Applies |pfn| to every function in the call trees that are rooted at the

Просмотреть файл

@ -45,6 +45,7 @@ void StructuredCFGAnalysis::AddBlocksInFunction(Function* func) {
struct TraversalInfo {
ConstructInfo cinfo;
uint32_t merge_node;
uint32_t continue_node;
};
// Set up a stack to keep track of currently active constructs.
@ -53,7 +54,9 @@ void StructuredCFGAnalysis::AddBlocksInFunction(Function* func) {
state[0].cinfo.containing_construct = 0;
state[0].cinfo.containing_loop = 0;
state[0].cinfo.containing_switch = 0;
state[0].cinfo.in_continue = false;
state[0].merge_node = 0;
state[0].continue_node = 0;
for (BasicBlock* block : order) {
if (context_->cfg()->IsPseudoEntryBlock(block) ||
@ -65,6 +68,12 @@ void StructuredCFGAnalysis::AddBlocksInFunction(Function* func) {
state.pop_back();
}
// This works because the structured order is designed to keep the blocks in
// the continue construct between the continue header and the merge node.
if (block->id() == state.back().continue_node) {
state.back().cinfo.in_continue = true;
}
bb_to_construct_.emplace(std::make_pair(block->id(), state.back().cinfo));
if (Instruction* merge_inst = block->GetMergeInst()) {
@ -76,8 +85,14 @@ void StructuredCFGAnalysis::AddBlocksInFunction(Function* func) {
if (merge_inst->opcode() == SpvOpLoopMerge) {
new_state.cinfo.containing_loop = block->id();
new_state.cinfo.containing_switch = 0;
new_state.cinfo.in_continue = false;
new_state.continue_node =
merge_inst->GetSingleWordInOperand(kContinueNodeIndex);
} else {
new_state.cinfo.containing_loop = state.back().cinfo.containing_loop;
new_state.cinfo.in_continue = state.back().cinfo.in_continue;
new_state.continue_node = state.back().continue_node;
if (merge_inst->NextNode()->opcode() == SpvOpSwitch) {
new_state.cinfo.containing_switch = block->id();
} else {
@ -146,9 +161,59 @@ bool StructuredCFGAnalysis::IsContinueBlock(uint32_t bb_id) {
return LoopContinueBlock(bb_id) == bb_id;
}
bool StructuredCFGAnalysis::IsInContainingLoopsContinueConstruct(
uint32_t bb_id) {
auto it = bb_to_construct_.find(bb_id);
if (it == bb_to_construct_.end()) {
return false;
}
return it->second.in_continue;
}
bool StructuredCFGAnalysis::IsInContinueConstruct(uint32_t bb_id) {
while (bb_id != 0) {
if (IsInContainingLoopsContinueConstruct(bb_id)) {
return true;
}
bb_id = ContainingLoop(bb_id);
}
return false;
}
bool StructuredCFGAnalysis::IsMergeBlock(uint32_t bb_id) {
return merge_blocks_.Get(bb_id);
}
std::unordered_set<uint32_t>
StructuredCFGAnalysis::FindFuncsCalledFromContinue() {
std::unordered_set<uint32_t> called_from_continue;
std::queue<uint32_t> funcs_to_process;
// First collect the functions that are called directly from a continue
// construct.
for (Function& func : *context_->module()) {
for (auto& bb : func) {
if (IsInContainingLoopsContinueConstruct(bb.id())) {
for (const Instruction& inst : bb) {
if (inst.opcode() == SpvOpFunctionCall) {
funcs_to_process.push(inst.GetSingleWordInOperand(0));
}
}
}
}
}
// Now collect all of the functions that are indirectly called as well.
while (!funcs_to_process.empty()) {
uint32_t func_id = funcs_to_process.front();
funcs_to_process.pop();
Function* func = context_->GetFunction(func_id);
if (called_from_continue.insert(func_id).second) {
context_->AddCalls(func, &funcs_to_process);
}
}
return called_from_continue;
}
} // namespace opt
} // namespace spvtools

Просмотреть файл

@ -16,6 +16,7 @@
#define SOURCE_OPT_STRUCT_CFG_ANALYSIS_H_
#include <unordered_map>
#include <unordered_set>
#include "source/opt/function.h"
#include "source/util/bit_vector.h"
@ -88,21 +89,46 @@ class StructuredCFGAnalysis {
// if no such block exists.
uint32_t SwitchMergeBlock(uint32_t bb_id);
// Returns true if |bb_id| is the continue block for a loop.
bool IsContinueBlock(uint32_t bb_id);
// Returns true if |bb_id| is in the continue construct for its inner most
// containing loop.
bool IsInContainingLoopsContinueConstruct(uint32_t bb_id);
// Returns true if |bb_id| is in the continue construct for any loop in its
// function.
bool IsInContinueConstruct(uint32_t bb_id);
// Return true if |bb_id| is the merge block for a construct.
bool IsMergeBlock(uint32_t bb_id);
// Returns the set of function ids that are called directly or indirectly from
// a continue construct.
std::unordered_set<uint32_t> FindFuncsCalledFromContinue();
private:
// Struct used to hold the information for a basic block.
// |containing_construct| is the header for the innermost containing
// construct, or 0 if no such construct exists. It could be a selection
// construct or a loop construct. |containing_loop| is the innermost
// containing loop construct, or 0 if the basic bloc is not in a loop. If the
// basic block is in a selection construct that is contained in a loop
// construct, then these two values will not be the same.
// construct or a loop construct.
//
// |containing_loop| is the innermost containing loop construct, or 0 if the
// basic bloc is not in a loop. If the basic block is in a selection
// construct that is contained in a loop construct, then these two values will
// not be the same.
//
// |containing_switch| is the innermost contain selection construct with an
// |OpSwitch| for the branch, as long as there is not intervening loop. This
// is used to identify the selection construct from which it can break.
//
// |in_continue| is true of the block is in the continue construct for its
// innermost containing loop.
struct ConstructInfo {
uint32_t containing_construct;
uint32_t containing_loop;
uint32_t containing_switch;
bool in_continue;
};
// Populates |bb_to_construct_| with the innermost containing merge and loop

Просмотреть файл

@ -26,6 +26,7 @@ namespace opt {
namespace {
using StructCFGAnalysisTest = PassTest<::testing::Test>;
using ::testing::UnorderedElementsAre;
TEST_F(StructCFGAnalysisTest, BBInSelection) {
const std::string text = R"(
@ -62,6 +63,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
EXPECT_FALSE(analysis.IsContinueBlock(1));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
EXPECT_FALSE(analysis.IsInContinueConstruct(1));
EXPECT_FALSE(analysis.IsMergeBlock(1));
// BB2 is in the construct.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@ -70,6 +75,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
EXPECT_FALSE(analysis.IsContinueBlock(2));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
EXPECT_FALSE(analysis.IsInContinueConstruct(2));
EXPECT_FALSE(analysis.IsMergeBlock(2));
// The merge node is not in the construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@ -78,6 +87,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
EXPECT_FALSE(analysis.IsContinueBlock(3));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
EXPECT_FALSE(analysis.IsInContinueConstruct(3));
EXPECT_TRUE(analysis.IsMergeBlock(3));
}
TEST_F(StructCFGAnalysisTest, BBInLoop) {
@ -119,6 +132,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
EXPECT_FALSE(analysis.IsContinueBlock(1));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
EXPECT_FALSE(analysis.IsInContinueConstruct(1));
EXPECT_FALSE(analysis.IsMergeBlock(1));
// BB2 is in the construct.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@ -127,6 +144,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
EXPECT_FALSE(analysis.IsContinueBlock(2));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
EXPECT_FALSE(analysis.IsInContinueConstruct(2));
EXPECT_FALSE(analysis.IsMergeBlock(2));
// The merge node is not in the construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@ -135,6 +156,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
EXPECT_FALSE(analysis.IsContinueBlock(3));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
EXPECT_FALSE(analysis.IsInContinueConstruct(3));
EXPECT_TRUE(analysis.IsMergeBlock(3));
// The continue block is in the construct.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@ -143,6 +168,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
EXPECT_TRUE(analysis.IsContinueBlock(4));
EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
EXPECT_TRUE(analysis.IsInContinueConstruct(4));
EXPECT_FALSE(analysis.IsMergeBlock(4));
}
TEST_F(StructCFGAnalysisTest, SelectionInLoop) {
@ -189,6 +218,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
EXPECT_FALSE(analysis.IsContinueBlock(1));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
EXPECT_FALSE(analysis.IsInContinueConstruct(1));
EXPECT_FALSE(analysis.IsMergeBlock(1));
// Selection header is in the loop only.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@ -197,6 +230,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
EXPECT_FALSE(analysis.IsContinueBlock(2));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
EXPECT_FALSE(analysis.IsInContinueConstruct(2));
EXPECT_FALSE(analysis.IsMergeBlock(2));
// The loop merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@ -205,6 +242,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
EXPECT_FALSE(analysis.IsContinueBlock(3));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
EXPECT_FALSE(analysis.IsInContinueConstruct(3));
EXPECT_TRUE(analysis.IsMergeBlock(3));
// The continue block is in the loop only.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@ -213,14 +254,22 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
EXPECT_TRUE(analysis.IsContinueBlock(4));
EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
EXPECT_TRUE(analysis.IsInContinueConstruct(4));
EXPECT_FALSE(analysis.IsMergeBlock(4));
// BB5 is in the selection fist and the loop.
// BB5 is in the selection and the loop.
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
EXPECT_EQ(analysis.ContainingLoop(5), 1);
EXPECT_EQ(analysis.MergeBlock(5), 6);
EXPECT_EQ(analysis.LoopMergeBlock(5), 3);
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
EXPECT_FALSE(analysis.IsContinueBlock(5));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
EXPECT_FALSE(analysis.IsInContinueConstruct(5));
EXPECT_FALSE(analysis.IsMergeBlock(5));
// The selection merge is in the loop only.
EXPECT_EQ(analysis.ContainingConstruct(6), 1);
@ -229,6 +278,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(6), 3);
EXPECT_EQ(analysis.ContainingSwitch(6), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
EXPECT_FALSE(analysis.IsContinueBlock(6));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
EXPECT_FALSE(analysis.IsInContinueConstruct(6));
EXPECT_TRUE(analysis.IsMergeBlock(6));
}
TEST_F(StructCFGAnalysisTest, LoopInSelection) {
@ -275,6 +328,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
EXPECT_FALSE(analysis.IsContinueBlock(1));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
EXPECT_FALSE(analysis.IsInContinueConstruct(1));
EXPECT_FALSE(analysis.IsMergeBlock(1));
// Loop header is in the selection only.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@ -283,6 +340,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
EXPECT_FALSE(analysis.IsContinueBlock(2));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
EXPECT_FALSE(analysis.IsInContinueConstruct(2));
EXPECT_FALSE(analysis.IsMergeBlock(2));
// The selection merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@ -291,6 +352,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
EXPECT_FALSE(analysis.IsContinueBlock(3));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
EXPECT_FALSE(analysis.IsInContinueConstruct(3));
EXPECT_TRUE(analysis.IsMergeBlock(3));
// The loop merge is in the selection only.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@ -299,6 +364,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
EXPECT_FALSE(analysis.IsContinueBlock(4));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
EXPECT_FALSE(analysis.IsInContinueConstruct(4));
EXPECT_TRUE(analysis.IsMergeBlock(4));
// The loop continue target is in the loop.
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@ -307,6 +376,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
EXPECT_TRUE(analysis.IsContinueBlock(5));
EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
EXPECT_TRUE(analysis.IsInContinueConstruct(5));
EXPECT_FALSE(analysis.IsMergeBlock(5));
// BB6 is in the loop.
EXPECT_EQ(analysis.ContainingConstruct(6), 2);
@ -315,6 +388,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
EXPECT_EQ(analysis.ContainingSwitch(6), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
EXPECT_FALSE(analysis.IsContinueBlock(6));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
EXPECT_FALSE(analysis.IsInContinueConstruct(6));
EXPECT_FALSE(analysis.IsMergeBlock(6));
}
TEST_F(StructCFGAnalysisTest, SelectionInSelection) {
@ -359,6 +436,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
EXPECT_FALSE(analysis.IsContinueBlock(1));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
EXPECT_FALSE(analysis.IsInContinueConstruct(1));
EXPECT_FALSE(analysis.IsMergeBlock(1));
// The inner header is in the outer selection.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@ -367,6 +448,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
EXPECT_FALSE(analysis.IsContinueBlock(2));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
EXPECT_FALSE(analysis.IsInContinueConstruct(2));
EXPECT_FALSE(analysis.IsMergeBlock(2));
// The outer merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@ -375,6 +460,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
EXPECT_FALSE(analysis.IsContinueBlock(3));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
EXPECT_FALSE(analysis.IsInContinueConstruct(3));
EXPECT_TRUE(analysis.IsMergeBlock(3));
// The inner merge is in the outer selection.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@ -383,6 +472,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
EXPECT_FALSE(analysis.IsContinueBlock(4));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
EXPECT_FALSE(analysis.IsInContinueConstruct(4));
EXPECT_TRUE(analysis.IsMergeBlock(4));
// BB5 is in the inner selection.
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@ -391,6 +484,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(5), 0);
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
EXPECT_FALSE(analysis.IsContinueBlock(5));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
EXPECT_FALSE(analysis.IsInContinueConstruct(5));
EXPECT_FALSE(analysis.IsMergeBlock(5));
}
TEST_F(StructCFGAnalysisTest, LoopInLoop) {
@ -439,6 +536,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
EXPECT_FALSE(analysis.IsContinueBlock(1));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
EXPECT_FALSE(analysis.IsInContinueConstruct(1));
EXPECT_FALSE(analysis.IsMergeBlock(1));
// The inner loop header is in the outer loop.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@ -447,6 +548,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
EXPECT_FALSE(analysis.IsContinueBlock(2));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
EXPECT_FALSE(analysis.IsInContinueConstruct(2));
EXPECT_FALSE(analysis.IsMergeBlock(2));
// The outer merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@ -455,6 +560,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
EXPECT_FALSE(analysis.IsContinueBlock(3));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
EXPECT_FALSE(analysis.IsInContinueConstruct(3));
EXPECT_TRUE(analysis.IsMergeBlock(3));
// The inner merge is in the outer loop.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@ -463,6 +572,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
EXPECT_FALSE(analysis.IsContinueBlock(4));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
EXPECT_FALSE(analysis.IsInContinueConstruct(4));
EXPECT_TRUE(analysis.IsMergeBlock(4));
// The inner continue target is in the inner loop.
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@ -471,6 +584,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
EXPECT_TRUE(analysis.IsContinueBlock(5));
EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
EXPECT_TRUE(analysis.IsInContinueConstruct(5));
EXPECT_FALSE(analysis.IsMergeBlock(5));
// BB6 is in the loop.
EXPECT_EQ(analysis.ContainingConstruct(6), 2);
@ -479,6 +596,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
EXPECT_EQ(analysis.ContainingSwitch(6), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
EXPECT_FALSE(analysis.IsContinueBlock(6));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
EXPECT_FALSE(analysis.IsInContinueConstruct(6));
EXPECT_FALSE(analysis.IsMergeBlock(6));
// The outer continue target is in the outer loop.
EXPECT_EQ(analysis.ContainingConstruct(7), 1);
@ -487,6 +608,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(7), 3);
EXPECT_EQ(analysis.ContainingSwitch(7), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(7), 0);
EXPECT_TRUE(analysis.IsContinueBlock(7));
EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(7));
EXPECT_TRUE(analysis.IsInContinueConstruct(7));
EXPECT_FALSE(analysis.IsMergeBlock(7));
}
TEST_F(StructCFGAnalysisTest, KernelTest) {
@ -523,6 +648,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(i), 0);
EXPECT_EQ(analysis.ContainingSwitch(i), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(i), 0);
EXPECT_FALSE(analysis.IsContinueBlock(i));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(i));
EXPECT_FALSE(analysis.IsInContinueConstruct(i));
EXPECT_FALSE(analysis.IsMergeBlock(i));
}
}
@ -581,6 +710,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
EXPECT_FALSE(analysis.IsContinueBlock(1));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
EXPECT_FALSE(analysis.IsInContinueConstruct(1));
EXPECT_FALSE(analysis.IsMergeBlock(1));
// BB2 is in the construct.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@ -589,6 +722,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
EXPECT_EQ(analysis.ContainingSwitch(2), 1);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 3);
EXPECT_FALSE(analysis.IsContinueBlock(2));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
EXPECT_FALSE(analysis.IsInContinueConstruct(2));
EXPECT_FALSE(analysis.IsMergeBlock(2));
// The merge node is not in the construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@ -597,6 +734,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
EXPECT_FALSE(analysis.IsContinueBlock(3));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
EXPECT_FALSE(analysis.IsInContinueConstruct(3));
EXPECT_TRUE(analysis.IsMergeBlock(3));
}
TEST_F(StructCFGAnalysisTest, LoopInSwitch) {
@ -643,6 +784,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
EXPECT_FALSE(analysis.IsContinueBlock(1));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
EXPECT_FALSE(analysis.IsInContinueConstruct(1));
EXPECT_FALSE(analysis.IsMergeBlock(1));
// Loop header is in the selection only.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@ -651,6 +796,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
EXPECT_EQ(analysis.ContainingSwitch(2), 1);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 3);
EXPECT_FALSE(analysis.IsContinueBlock(2));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
EXPECT_FALSE(analysis.IsInContinueConstruct(2));
EXPECT_FALSE(analysis.IsMergeBlock(2));
// The selection merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@ -659,6 +808,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
EXPECT_FALSE(analysis.IsContinueBlock(3));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
EXPECT_FALSE(analysis.IsInContinueConstruct(3));
EXPECT_TRUE(analysis.IsMergeBlock(3));
// The loop merge is in the selection only.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@ -667,6 +820,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
EXPECT_EQ(analysis.ContainingSwitch(4), 1);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 3);
EXPECT_FALSE(analysis.IsContinueBlock(4));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
EXPECT_FALSE(analysis.IsInContinueConstruct(4));
EXPECT_TRUE(analysis.IsMergeBlock(4));
// The loop continue target is in the loop.
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@ -675,6 +832,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
EXPECT_TRUE(analysis.IsContinueBlock(5));
EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
EXPECT_TRUE(analysis.IsInContinueConstruct(5));
EXPECT_FALSE(analysis.IsMergeBlock(5));
// BB6 is in the loop.
EXPECT_EQ(analysis.ContainingConstruct(6), 2);
@ -683,6 +844,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
EXPECT_EQ(analysis.ContainingSwitch(6), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
EXPECT_FALSE(analysis.IsContinueBlock(6));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
EXPECT_FALSE(analysis.IsInContinueConstruct(6));
EXPECT_FALSE(analysis.IsMergeBlock(6));
}
TEST_F(StructCFGAnalysisTest, SelectionInSwitch) {
@ -727,6 +892,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
EXPECT_FALSE(analysis.IsContinueBlock(1));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
EXPECT_FALSE(analysis.IsInContinueConstruct(1));
EXPECT_FALSE(analysis.IsMergeBlock(1));
// The inner header is in the outer selection.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@ -735,6 +904,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
EXPECT_EQ(analysis.ContainingSwitch(2), 1);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 3);
EXPECT_FALSE(analysis.IsContinueBlock(2));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
EXPECT_FALSE(analysis.IsInContinueConstruct(2));
EXPECT_FALSE(analysis.IsMergeBlock(2));
// The outer merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@ -743,6 +916,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
EXPECT_FALSE(analysis.IsContinueBlock(3));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
EXPECT_FALSE(analysis.IsInContinueConstruct(3));
EXPECT_TRUE(analysis.IsMergeBlock(3));
// The inner merge is in the outer selection.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@ -751,6 +928,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
EXPECT_EQ(analysis.ContainingSwitch(4), 1);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 3);
EXPECT_FALSE(analysis.IsContinueBlock(4));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
EXPECT_FALSE(analysis.IsInContinueConstruct(4));
EXPECT_TRUE(analysis.IsMergeBlock(4));
// BB5 is in the inner selection.
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@ -759,6 +940,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(5), 0);
EXPECT_EQ(analysis.ContainingSwitch(5), 1);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 3);
EXPECT_FALSE(analysis.IsContinueBlock(5));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
EXPECT_FALSE(analysis.IsInContinueConstruct(5));
EXPECT_FALSE(analysis.IsMergeBlock(5));
}
TEST_F(StructCFGAnalysisTest, SwitchInSelection) {
@ -803,6 +988,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
EXPECT_FALSE(analysis.IsContinueBlock(1));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
EXPECT_FALSE(analysis.IsInContinueConstruct(1));
EXPECT_FALSE(analysis.IsMergeBlock(1));
// The inner header is in the outer selection.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
@ -811,6 +1000,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
EXPECT_FALSE(analysis.IsContinueBlock(2));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
EXPECT_FALSE(analysis.IsInContinueConstruct(2));
EXPECT_FALSE(analysis.IsMergeBlock(2));
// The outer merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
@ -819,6 +1012,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
EXPECT_FALSE(analysis.IsContinueBlock(3));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
EXPECT_FALSE(analysis.IsInContinueConstruct(3));
EXPECT_TRUE(analysis.IsMergeBlock(3));
// The inner merge is in the outer selection.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
@ -827,6 +1024,10 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
EXPECT_FALSE(analysis.IsContinueBlock(4));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
EXPECT_FALSE(analysis.IsInContinueConstruct(4));
EXPECT_TRUE(analysis.IsMergeBlock(4));
// BB5 is in the inner selection.
EXPECT_EQ(analysis.ContainingConstruct(5), 2);
@ -835,8 +1036,339 @@ OpFunctionEnd
EXPECT_EQ(analysis.LoopMergeBlock(5), 0);
EXPECT_EQ(analysis.ContainingSwitch(5), 2);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 4);
EXPECT_FALSE(analysis.IsContinueBlock(5));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
EXPECT_FALSE(analysis.IsInContinueConstruct(5));
EXPECT_FALSE(analysis.IsMergeBlock(5));
}
TEST_F(StructCFGAnalysisTest, SelectionInContinue) {
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%bool = OpTypeBool
%bool_undef = OpUndef %bool
%uint = OpTypeInt 32 0
%uint_undef = OpUndef %uint
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%entry_lab = OpLabel
OpBranch %1
%1 = OpLabel
OpLoopMerge %3 %4 None
OpBranchConditional %undef_bool %2 %3
%2 = OpLabel
OpBranch %3
%4 = OpLabel
OpSelectionMerge %6 None
OpBranchConditional %undef_bool %5 %6
%5 = OpLabel
OpBranch %6
%6 = OpLabel
OpBranch %1
%3 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
StructuredCFGAnalysis analysis(context.get());
// The loop header is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(1), 0);
EXPECT_EQ(analysis.ContainingLoop(1), 0);
EXPECT_EQ(analysis.MergeBlock(1), 0);
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
EXPECT_FALSE(analysis.IsContinueBlock(1));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
EXPECT_FALSE(analysis.IsInContinueConstruct(1));
EXPECT_FALSE(analysis.IsMergeBlock(1));
// Selection header is in the loop only.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
EXPECT_EQ(analysis.ContainingLoop(2), 1);
EXPECT_EQ(analysis.MergeBlock(2), 3);
EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
EXPECT_FALSE(analysis.IsContinueBlock(2));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
EXPECT_FALSE(analysis.IsInContinueConstruct(2));
EXPECT_FALSE(analysis.IsMergeBlock(2));
// The loop merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
EXPECT_EQ(analysis.ContainingLoop(3), 0);
EXPECT_EQ(analysis.MergeBlock(3), 0);
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
EXPECT_FALSE(analysis.IsContinueBlock(3));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
EXPECT_FALSE(analysis.IsInContinueConstruct(3));
EXPECT_TRUE(analysis.IsMergeBlock(3));
// The continue block is in the loop only.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
EXPECT_EQ(analysis.ContainingLoop(4), 1);
EXPECT_EQ(analysis.MergeBlock(4), 3);
EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
EXPECT_TRUE(analysis.IsContinueBlock(4));
EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
EXPECT_TRUE(analysis.IsInContinueConstruct(4));
EXPECT_FALSE(analysis.IsMergeBlock(4));
// BB5 is in the selection and the continue for the loop.
EXPECT_EQ(analysis.ContainingConstruct(5), 4);
EXPECT_EQ(analysis.ContainingLoop(5), 1);
EXPECT_EQ(analysis.MergeBlock(5), 6);
EXPECT_EQ(analysis.LoopMergeBlock(5), 3);
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
EXPECT_FALSE(analysis.IsContinueBlock(5));
EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
EXPECT_TRUE(analysis.IsInContinueConstruct(5));
EXPECT_FALSE(analysis.IsMergeBlock(5));
// BB5 is in the continue for the loop.
EXPECT_EQ(analysis.ContainingConstruct(6), 1);
EXPECT_EQ(analysis.ContainingLoop(6), 1);
EXPECT_EQ(analysis.MergeBlock(6), 3);
EXPECT_EQ(analysis.LoopMergeBlock(6), 3);
EXPECT_EQ(analysis.ContainingSwitch(6), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
EXPECT_FALSE(analysis.IsContinueBlock(6));
EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(6));
EXPECT_TRUE(analysis.IsInContinueConstruct(6));
EXPECT_TRUE(analysis.IsMergeBlock(6));
}
TEST_F(StructCFGAnalysisTest, LoopInContinue) {
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%bool = OpTypeBool
%bool_undef = OpUndef %bool
%uint = OpTypeInt 32 0
%uint_undef = OpUndef %uint
%void_func = OpTypeFunction %void
%main = OpFunction %void None %void_func
%entry_lab = OpLabel
OpBranch %1
%1 = OpLabel
OpLoopMerge %3 %7 None
OpBranchConditional %undef_bool %2 %3
%2 = OpLabel
OpBranchConditional %undef_bool %3 %7
%7 = OpLabel
OpLoopMerge %4 %5 None
OpBranchConditional %undef_bool %4 %6
%5 = OpLabel
OpBranch %7
%6 = OpLabel
OpBranch %4
%4 = OpLabel
OpBranch %1
%3 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
StructuredCFGAnalysis analysis(context.get());
// The outer loop header is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(1), 0);
EXPECT_EQ(analysis.ContainingLoop(1), 0);
EXPECT_EQ(analysis.MergeBlock(1), 0);
EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
EXPECT_EQ(analysis.ContainingSwitch(1), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
EXPECT_FALSE(analysis.IsContinueBlock(1));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
EXPECT_FALSE(analysis.IsInContinueConstruct(1));
EXPECT_FALSE(analysis.IsMergeBlock(1));
// BB2 is a regular block in the inner loop.
EXPECT_EQ(analysis.ContainingConstruct(2), 1);
EXPECT_EQ(analysis.ContainingLoop(2), 1);
EXPECT_EQ(analysis.MergeBlock(2), 3);
EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
EXPECT_EQ(analysis.ContainingSwitch(2), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
EXPECT_FALSE(analysis.IsContinueBlock(2));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
EXPECT_FALSE(analysis.IsInContinueConstruct(2));
EXPECT_FALSE(analysis.IsMergeBlock(2));
// The outer merge node is not in either construct.
EXPECT_EQ(analysis.ContainingConstruct(3), 0);
EXPECT_EQ(analysis.ContainingLoop(3), 0);
EXPECT_EQ(analysis.MergeBlock(3), 0);
EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
EXPECT_EQ(analysis.ContainingSwitch(3), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
EXPECT_FALSE(analysis.IsContinueBlock(3));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
EXPECT_FALSE(analysis.IsInContinueConstruct(3));
EXPECT_TRUE(analysis.IsMergeBlock(3));
// The inner merge is in the continue of the outer loop.
EXPECT_EQ(analysis.ContainingConstruct(4), 1);
EXPECT_EQ(analysis.ContainingLoop(4), 1);
EXPECT_EQ(analysis.MergeBlock(4), 3);
EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
EXPECT_EQ(analysis.ContainingSwitch(4), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
EXPECT_FALSE(analysis.IsContinueBlock(4));
EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
EXPECT_TRUE(analysis.IsInContinueConstruct(4));
EXPECT_TRUE(analysis.IsMergeBlock(4));
// The inner continue target is in the inner loop.
EXPECT_EQ(analysis.ContainingConstruct(5), 7);
EXPECT_EQ(analysis.ContainingLoop(5), 7);
EXPECT_EQ(analysis.MergeBlock(5), 4);
EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
EXPECT_EQ(analysis.ContainingSwitch(5), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
EXPECT_TRUE(analysis.IsContinueBlock(5));
EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
EXPECT_TRUE(analysis.IsInContinueConstruct(5));
EXPECT_FALSE(analysis.IsMergeBlock(5));
// BB6 is a regular block in the inner loop.
EXPECT_EQ(analysis.ContainingConstruct(6), 7);
EXPECT_EQ(analysis.ContainingLoop(6), 7);
EXPECT_EQ(analysis.MergeBlock(6), 4);
EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
EXPECT_EQ(analysis.ContainingSwitch(6), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
EXPECT_FALSE(analysis.IsContinueBlock(6));
EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
EXPECT_TRUE(analysis.IsInContinueConstruct(6));
EXPECT_FALSE(analysis.IsMergeBlock(6));
// The outer continue target is in the outer loop.
EXPECT_EQ(analysis.ContainingConstruct(7), 1);
EXPECT_EQ(analysis.ContainingLoop(7), 1);
EXPECT_EQ(analysis.MergeBlock(7), 3);
EXPECT_EQ(analysis.LoopMergeBlock(7), 3);
EXPECT_EQ(analysis.ContainingSwitch(7), 0);
EXPECT_EQ(analysis.SwitchMergeBlock(7), 0);
EXPECT_TRUE(analysis.IsContinueBlock(7));
EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(7));
EXPECT_TRUE(analysis.IsInContinueConstruct(7));
EXPECT_FALSE(analysis.IsMergeBlock(7));
}
TEST_F(StructCFGAnalysisTest, FuncCallInContinueDirect) {
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
%void = OpTypeVoid
%bool = OpTypeBool
%4 = OpUndef %bool
%uint = OpTypeInt 32 0
%6 = OpUndef %uint
%7 = OpTypeFunction %void
%1 = OpFunction %void None %7
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpLoopMerge %10 %11 None
OpBranchConditional %12 %10 %11
%11 = OpLabel
%13 = OpFunctionCall %void %14
OpBranch %9
%10 = OpLabel
%15 = OpFunctionCall %void %16
OpReturn
OpFunctionEnd
%14 = OpFunction %void None %7
%17 = OpLabel
OpReturn
OpFunctionEnd
%16 = OpFunction %void None %7
%18 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
StructuredCFGAnalysis analysis(context.get());
auto c = analysis.FindFuncsCalledFromContinue();
EXPECT_THAT(c, UnorderedElementsAre(14u));
}
TEST_F(StructCFGAnalysisTest, FuncCallInContinueIndirect) {
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
%void = OpTypeVoid
%bool = OpTypeBool
%4 = OpUndef %bool
%uint = OpTypeInt 32 0
%6 = OpUndef %uint
%7 = OpTypeFunction %void
%1 = OpFunction %void None %7
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpLoopMerge %10 %11 None
OpBranchConditional %12 %10 %11
%11 = OpLabel
%13 = OpFunctionCall %void %14
OpBranch %9
%10 = OpLabel
%15 = OpFunctionCall %void %16
OpReturn
OpFunctionEnd
%14 = OpFunction %void None %7
%17 = OpLabel
%19 = OpFunctionCall %void %16
OpReturn
OpFunctionEnd
%16 = OpFunction %void None %7
%18 = OpLabel
%20 = OpFunctionCall %void %21
OpReturn
OpFunctionEnd
%21 = OpFunction %void None %7
%22 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
StructuredCFGAnalysis analysis(context.get());
auto c = analysis.FindFuncsCalledFromContinue();
EXPECT_THAT(c, UnorderedElementsAre(14u, 16u, 21u));
}
} // namespace
} // namespace opt
} // namespace spvtools