diff --git a/source/opt/cfg.h b/source/opt/cfg.h index 4c7e936c..f2806822 100644 --- a/source/opt/cfg.h +++ b/source/opt/cfg.h @@ -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* order); diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index 9572fee3..45bf1290 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h @@ -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* todo); // Applies |pfn| to every function in the call trees that are rooted at the diff --git a/source/opt/struct_cfg_analysis.cpp b/source/opt/struct_cfg_analysis.cpp index 152ded50..b16322c1 100644 --- a/source/opt/struct_cfg_analysis.cpp +++ b/source/opt/struct_cfg_analysis.cpp @@ -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 +StructuredCFGAnalysis::FindFuncsCalledFromContinue() { + std::unordered_set called_from_continue; + std::queue 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 diff --git a/source/opt/struct_cfg_analysis.h b/source/opt/struct_cfg_analysis.h index f25266a8..dfae6d4f 100644 --- a/source/opt/struct_cfg_analysis.h +++ b/source/opt/struct_cfg_analysis.h @@ -16,6 +16,7 @@ #define SOURCE_OPT_STRUCT_CFG_ANALYSIS_H_ #include +#include #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 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 diff --git a/test/opt/struct_cfg_analysis_test.cpp b/test/opt/struct_cfg_analysis_test.cpp index 7fb37843..0451a8b4 100644 --- a/test/opt/struct_cfg_analysis_test.cpp +++ b/test/opt/struct_cfg_analysis_test.cpp @@ -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 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 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 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 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