mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-25 21:10:04 +00:00
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:
parent
0982f0212e
commit
60aaafbc70
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user