mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-19 03:20:14 +00:00
Dead branch elim fix (#3160)
We must treat a branch to the merge node of a switch that is in the header of a construct as a nested construced. The original merge instruction is still needed in that case.
This commit is contained in:
parent
1b3441036a
commit
97f1d485b7
@ -167,7 +167,6 @@ bool DeadBranchElimPass::MarkLiveBlocks(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (simplify) {
|
if (simplify) {
|
||||||
modified = true;
|
|
||||||
conditions_to_simplify.push_back({block, live_lab_id});
|
conditions_to_simplify.push_back({block, live_lab_id});
|
||||||
stack.push_back(GetParentBlock(live_lab_id));
|
stack.push_back(GetParentBlock(live_lab_id));
|
||||||
} else {
|
} else {
|
||||||
@ -179,24 +178,29 @@ bool DeadBranchElimPass::MarkLiveBlocks(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Traverse |conditions_to_simplify in reverse order. This is done so that we
|
// Traverse |conditions_to_simplify| in reverse order. This is done so that
|
||||||
// simplify nested constructs before simplifying the constructs that contain
|
// we simplify nested constructs before simplifying the constructs that
|
||||||
// them.
|
// contain them.
|
||||||
for (auto b = conditions_to_simplify.rbegin();
|
for (auto b = conditions_to_simplify.rbegin();
|
||||||
b != conditions_to_simplify.rend(); ++b) {
|
b != conditions_to_simplify.rend(); ++b) {
|
||||||
SimplifyBranch(b->first, b->second);
|
modified |= SimplifyBranch(b->first, b->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
return modified;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeadBranchElimPass::SimplifyBranch(BasicBlock* block,
|
bool DeadBranchElimPass::SimplifyBranch(BasicBlock* block,
|
||||||
uint32_t live_lab_id) {
|
uint32_t live_lab_id) {
|
||||||
Instruction* merge_inst = block->GetMergeInst();
|
Instruction* merge_inst = block->GetMergeInst();
|
||||||
Instruction* terminator = block->terminator();
|
Instruction* terminator = block->terminator();
|
||||||
if (merge_inst && merge_inst->opcode() == SpvOpSelectionMerge) {
|
if (merge_inst && merge_inst->opcode() == SpvOpSelectionMerge) {
|
||||||
if (merge_inst->NextNode()->opcode() == SpvOpSwitch &&
|
if (merge_inst->NextNode()->opcode() == SpvOpSwitch &&
|
||||||
SwitchHasNestedBreak(block->id())) {
|
SwitchHasNestedBreak(block->id())) {
|
||||||
|
if (terminator->NumInOperands() == 2) {
|
||||||
|
// We cannot remove the branch, and it already has a single case, so no
|
||||||
|
// work to do.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// We have to keep the switch because it has a nest break, so we
|
// We have to keep the switch because it has a nest break, so we
|
||||||
// remove all cases except for the live one.
|
// remove all cases except for the live one.
|
||||||
Instruction::OperandList new_operands;
|
Instruction::OperandList new_operands;
|
||||||
@ -231,6 +235,7 @@ void DeadBranchElimPass::SimplifyBranch(BasicBlock* block,
|
|||||||
AddBranch(live_lab_id, block);
|
AddBranch(live_lab_id, block);
|
||||||
context()->KillInst(terminator);
|
context()->KillInst(terminator);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeadBranchElimPass::MarkUnreachableStructuredTargets(
|
void DeadBranchElimPass::MarkUnreachableStructuredTargets(
|
||||||
@ -643,7 +648,8 @@ bool DeadBranchElimPass::SwitchHasNestedBreak(uint32_t switch_header_id) {
|
|||||||
if (bb->id() == switch_header_id) {
|
if (bb->id() == switch_header_id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return (cfg_analysis->ContainingConstruct(inst) == switch_header_id);
|
return (cfg_analysis->ContainingConstruct(inst) == switch_header_id &&
|
||||||
|
bb->GetMergeInst() == nullptr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,14 +159,15 @@ class DeadBranchElimPass : public MemPass {
|
|||||||
std::unordered_set<BasicBlock*>* blocks_with_back_edges);
|
std::unordered_set<BasicBlock*>* blocks_with_back_edges);
|
||||||
|
|
||||||
// Returns true if there is a brach to the merge node of the selection
|
// Returns true if there is a brach to the merge node of the selection
|
||||||
// construct |switch_header_id| that is inside a nested selection construct.
|
// construct |switch_header_id| that is inside a nested selection construct or
|
||||||
|
// in the header of the nested selection construct.
|
||||||
bool SwitchHasNestedBreak(uint32_t switch_header_id);
|
bool SwitchHasNestedBreak(uint32_t switch_header_id);
|
||||||
|
|
||||||
// Replaces the terminator of |block| with a branch to |live_lab_id|. The
|
// Return true of the terminator of |block| is successfully replaced with a
|
||||||
// merge instruction is deleted or moved as needed to maintain structured
|
// branch to |live_lab_id|. The merge instruction is deleted or moved as
|
||||||
// control flow. Assumes that the StructuredCFGAnalysis is valid for the
|
// needed to maintain structured control flow. Assumes that the
|
||||||
// constructs containing |block|.
|
// StructuredCFGAnalysis is valid for the constructs containing |block|.
|
||||||
void SimplifyBranch(BasicBlock* block, uint32_t live_lab_id);
|
bool SimplifyBranch(BasicBlock* block, uint32_t live_lab_id);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace opt
|
} // namespace opt
|
||||||
|
@ -1378,6 +1378,7 @@ OpFunctionEnd
|
|||||||
|
|
||||||
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DeadBranchElimTest, LeaveContinueBackedgeExtraBlock) {
|
TEST_F(DeadBranchElimTest, LeaveContinueBackedgeExtraBlock) {
|
||||||
const std::string text = R"(
|
const std::string text = R"(
|
||||||
; CHECK: OpBranch [[header:%\w+]]
|
; CHECK: OpBranch [[header:%\w+]]
|
||||||
@ -3161,6 +3162,73 @@ OpFunctionEnd
|
|||||||
SinglePassRunAndCheck<DeadBranchElimPass>(before, after, true, true);
|
SinglePassRunAndCheck<DeadBranchElimPass>(before, after, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DeadBranchElimTest, BreakInNestedHeaderWithSingleCase) {
|
||||||
|
const std::string text = R"(OpCapability Shader
|
||||||
|
%1 = OpExtInstImport "GLSL.std.450"
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Fragment %main "main"
|
||||||
|
OpExecutionMode %main OriginUpperLeft
|
||||||
|
OpSource GLSL 450
|
||||||
|
OpName %main "main"
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%4 = OpTypeFunction %void
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%uint_0 = OpConstant %uint 0
|
||||||
|
%8 = OpUndef %bool
|
||||||
|
%main = OpFunction %void None %4
|
||||||
|
%9 = OpLabel
|
||||||
|
OpSelectionMerge %10 None
|
||||||
|
OpSwitch %uint_0 %11
|
||||||
|
%11 = OpLabel
|
||||||
|
OpSelectionMerge %12 None
|
||||||
|
OpBranchConditional %8 %10 %12
|
||||||
|
%12 = OpLabel
|
||||||
|
OpBranch %10
|
||||||
|
%10 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
SinglePassRunAndCheck<DeadBranchElimPass>(text, text, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeadBranchElimTest, BreakInNestedHeaderWithTwoCases) {
|
||||||
|
const std::string text = R"(
|
||||||
|
; CHECK: OpSelectionMerge [[merge:%\w+]] None
|
||||||
|
; CHECK-NEXT: OpSwitch %uint_0 [[bb:%\w+\n]]
|
||||||
|
OpCapability Shader
|
||||||
|
%1 = OpExtInstImport "GLSL.std.450"
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Fragment %main "main"
|
||||||
|
OpExecutionMode %main OriginUpperLeft
|
||||||
|
OpSource GLSL 450
|
||||||
|
OpName %main "main"
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%4 = OpTypeFunction %void
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%uint_0 = OpConstant %uint 0
|
||||||
|
%8 = OpUndef %bool
|
||||||
|
%main = OpFunction %void None %4
|
||||||
|
%9 = OpLabel
|
||||||
|
OpSelectionMerge %10 None
|
||||||
|
OpSwitch %uint_0 %11 1 %12
|
||||||
|
%11 = OpLabel
|
||||||
|
OpSelectionMerge %13 None
|
||||||
|
OpBranchConditional %8 %10 %13
|
||||||
|
%13 = OpLabel
|
||||||
|
OpBranch %10
|
||||||
|
%12 = OpLabel
|
||||||
|
OpBranch %10
|
||||||
|
%10 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(greg-lunarg): Add tests to verify handling of these cases:
|
// TODO(greg-lunarg): Add tests to verify handling of these cases:
|
||||||
//
|
//
|
||||||
// More complex control flow
|
// More complex control flow
|
||||||
|
Loading…
Reference in New Issue
Block a user