Allows breaks selection breaks to switches (#2605)

Fixes #2604

* Allow selection constructs to branch to the nearest selection merge
whose header is terminated by an OpSwitch
  * Cleanup break and continue checks generally
  * add tests
This commit is contained in:
alan-baker 2019-05-22 01:49:37 -04:00 committed by David Neto
parent 0982f0212e
commit 60aaafbc70
3 changed files with 165 additions and 5 deletions

View File

@ -133,6 +133,7 @@ bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const {
// - Selection: // - Selection:
// - branch to its merge // - branch to its merge
// - branch to nearest enclosing loop merge or continue // - branch to nearest enclosing loop merge or continue
// - branch to nearest enclosing switch selection merge
// - Loop: // - Loop:
// - branch to its merge // - branch to its merge
// - branch to its continue // - branch to its continue
@ -168,13 +169,17 @@ bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const {
return true; return true;
} }
bool seen_switch = false;
auto header = entry_block(); auto header = entry_block();
auto block = header; auto block = header->immediate_dominator();
while (block) { while (block) {
auto terminator = block->terminator(); auto terminator = block->terminator();
auto index = terminator - &_.ordered_instructions()[0]; auto index = terminator - &_.ordered_instructions()[0];
auto merge_inst = &_.ordered_instructions()[index - 1]; auto merge_inst = &_.ordered_instructions()[index - 1];
if (merge_inst->opcode() == SpvOpLoopMerge) { if (merge_inst->opcode() == SpvOpLoopMerge ||
(header->terminator()->opcode() != SpvOpSwitch &&
merge_inst->opcode() == SpvOpSelectionMerge &&
terminator->opcode() == SpvOpSwitch)) {
auto merge_target = merge_inst->GetOperandAs<uint32_t>(0u); auto merge_target = merge_inst->GetOperandAs<uint32_t>(0u);
auto merge_block = merge_inst->function()->GetBlock(merge_target).first; auto merge_block = merge_inst->function()->GetBlock(merge_target).first;
if (merge_block->dominates(*header)) { if (merge_block->dominates(*header)) {
@ -182,10 +187,20 @@ bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const {
continue; continue;
} }
auto continue_target = merge_inst->GetOperandAs<uint32_t>(1u); if ((!seen_switch || merge_inst->opcode() == SpvOpLoopMerge) &&
if (dest->id() == merge_target || dest->id() == continue_target) { dest->id() == merge_target) {
return true; return true;
} else if (merge_inst->opcode() == SpvOpLoopMerge) {
auto continue_target = merge_inst->GetOperandAs<uint32_t>(1u);
if (dest->id() == continue_target) {
return true;
}
} }
if (terminator->opcode() == SpvOpSwitch) seen_switch = true;
// Hit an enclosing loop and didn't break or continue.
if (merge_inst->opcode() == SpvOpLoopMerge) return false;
} }
block = block->immediate_dominator(); block = block->immediate_dominator();

View File

@ -116,7 +116,9 @@ class Construct {
// * branch to the associated merge // * branch to the associated merge
// * branch to the merge or continue of the innermost loop containing the // * branch to the merge or continue of the innermost loop containing the
// selection // selection
// Loop: // * branch to the merge block of the innermost switch containing the
// selection
// Loop:
// * branch to the associated merge or continue // * branch to the associated merge or continue
// Continue: // Continue:
// * back-edge to the associated loop header // * back-edge to the associated loop header

View File

@ -3403,6 +3403,149 @@ OpFunctionEnd
"9[%9], but not via a structured exit")); "9[%9], but not via a structured exit"));
} }
TEST_F(ValidateCFG, BreakFromSwitch) {
const std::string text = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%1 = OpTypeVoid
%2 = OpTypeBool
%3 = OpTypeInt 32 0
%4 = OpUndef %2
%5 = OpUndef %3
%6 = OpTypeFunction %1
%7 = OpFunction %1 None %6
%8 = OpLabel
OpSelectionMerge %9 None
OpSwitch %5 %9 0 %10
%10 = OpLabel
OpSelectionMerge %11 None
OpBranchConditional %4 %11 %12
%12 = OpLabel
OpBranch %9
%11 = OpLabel
OpBranch %9
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateCFG, InvalidBreakFromSwitch) {
const std::string text = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%1 = OpTypeVoid
%2 = OpTypeBool
%3 = OpTypeInt 32 0
%4 = OpUndef %2
%5 = OpUndef %3
%6 = OpTypeFunction %1
%7 = OpFunction %1 None %6
%8 = OpLabel
OpSelectionMerge %9 None
OpSwitch %5 %9 0 %10
%10 = OpLabel
OpSelectionMerge %11 None
OpSwitch %5 %11 0 %12
%12 = OpLabel
OpBranch %9
%11 = OpLabel
OpBranch %9
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("block <ID> 12[%12] exits the selection headed by <ID> "
"10[%10], but not via a structured exit"));
}
TEST_F(ValidateCFG, BreakToOuterSwitch) {
const std::string text = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%1 = OpTypeVoid
%2 = OpTypeBool
%3 = OpTypeInt 32 0
%4 = OpUndef %2
%5 = OpUndef %3
%6 = OpTypeFunction %1
%7 = OpFunction %1 None %6
%8 = OpLabel
OpSelectionMerge %9 None
OpSwitch %5 %9 0 %10
%10 = OpLabel
OpSelectionMerge %11 None
OpSwitch %5 %11 0 %12
%12 = OpLabel
OpSelectionMerge %13 None
OpBranchConditional %4 %13 %14
%14 = OpLabel
OpBranch %9
%13 = OpLabel
OpBranch %11
%11 = OpLabel
OpBranch %9
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("block <ID> 14[%14] exits the selection headed by <ID> "
"10[%10], but not via a structured exit"));
}
TEST_F(ValidateCFG, BreakToOuterLoop) {
const std::string text = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%1 = OpTypeVoid
%2 = OpTypeBool
%3 = OpUndef %2
%4 = OpTypeFunction %1
%5 = OpFunction %1 None %4
%6 = OpLabel
OpBranch %7
%7 = OpLabel
OpLoopMerge %8 %9 None
OpBranch %10
%10 = OpLabel
OpLoopMerge %9 %11 None
OpBranch %12
%12 = OpLabel
OpSelectionMerge %11 None
OpBranchConditional %3 %11 %13
%13 = OpLabel
OpBranch %8
%11 = OpLabel
OpBranchConditional %3 %9 %10
%9 = OpLabel
OpBranchConditional %3 %8 %7
%8 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("block <ID> 13[%13] exits the loop headed by <ID> "
"10[%10], but not via a structured exit"));
}
/// TODO(umar): Nested CFG constructs /// TODO(umar): Nested CFG constructs
} // namespace } // namespace