mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-26 09:41:03 +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:
|
||||
// - branch to its merge
|
||||
// - branch to nearest enclosing loop merge or continue
|
||||
// - branch to nearest enclosing switch selection merge
|
||||
// - Loop:
|
||||
// - branch to its merge
|
||||
// - branch to its continue
|
||||
@ -168,13 +169,17 @@ bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool seen_switch = false;
|
||||
auto header = entry_block();
|
||||
auto block = header;
|
||||
auto block = header->immediate_dominator();
|
||||
while (block) {
|
||||
auto terminator = block->terminator();
|
||||
auto index = terminator - &_.ordered_instructions()[0];
|
||||
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_block = merge_inst->function()->GetBlock(merge_target).first;
|
||||
if (merge_block->dominates(*header)) {
|
||||
@ -182,10 +187,20 @@ bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto continue_target = merge_inst->GetOperandAs<uint32_t>(1u);
|
||||
if (dest->id() == merge_target || dest->id() == continue_target) {
|
||||
if ((!seen_switch || merge_inst->opcode() == SpvOpLoopMerge) &&
|
||||
dest->id() == merge_target) {
|
||||
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();
|
||||
|
@ -116,7 +116,9 @@ class Construct {
|
||||
// * branch to the associated merge
|
||||
// * branch to the merge or continue of the innermost loop containing the
|
||||
// selection
|
||||
// Loop:
|
||||
// * branch to the merge block of the innermost switch containing the
|
||||
// selection
|
||||
// Loop:
|
||||
// * branch to the associated merge or continue
|
||||
// Continue:
|
||||
// * back-edge to the associated loop header
|
||||
|
@ -3403,6 +3403,149 @@ OpFunctionEnd
|
||||
"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
|
||||
|
||||
} // namespace
|
||||
|
Loading…
Reference in New Issue
Block a user