Handle cases where merge selects as also loop merge or continue blocks.

This commit is contained in:
Hans-Kristian Arntzen 2018-03-08 14:01:10 +01:00
Родитель 8c8a93f745
Коммит 8d557d4103
3 изменённых файлов: 98 добавлений и 58 удалений

Просмотреть файл

@ -585,6 +585,11 @@ protected:
multiselect_merge_targets.find(next) != end(multiselect_merge_targets); multiselect_merge_targets.find(next) != end(multiselect_merge_targets);
} }
inline bool is_loop_break(uint32_t next) const
{
return loop_merge_targets.find(next) != end(loop_merge_targets);
}
inline bool is_conditional(uint32_t next) const inline bool is_conditional(uint32_t next) const
{ {
return selection_merge_targets.find(next) != end(selection_merge_targets) && return selection_merge_targets.find(next) != end(selection_merge_targets) &&

Просмотреть файл

@ -8316,21 +8316,10 @@ void CompilerGLSL::flush_phi(uint32_t from, uint32_t to)
} }
} }
void CompilerGLSL::branch(uint32_t from, uint32_t to) void CompilerGLSL::branch_to_continue(uint32_t from, uint32_t to)
{ {
flush_phi(from, to); assert(is_continue(to));
flush_all_active_variables();
// This is only a continue if we branch to our loop dominator.
if (loop_blocks.find(to) != end(loop_blocks) && get<SPIRBlock>(from).loop_dominator == to)
{
// This can happen if we had a complex continue block which was emitted.
// Once the continue block tries to branch to the loop header, just emit continue;
// and end the chain here.
statement("continue;");
}
else if (is_continue(to))
{
auto &to_block = get<SPIRBlock>(to); auto &to_block = get<SPIRBlock>(to);
if (to_block.complex_continue) if (to_block.complex_continue)
{ {
@ -8382,11 +8371,32 @@ void CompilerGLSL::branch(uint32_t from, uint32_t to)
if (!outside_control_flow) if (!outside_control_flow)
statement("continue;"); statement("continue;");
} }
}
void CompilerGLSL::branch(uint32_t from, uint32_t to)
{
flush_phi(from, to);
flush_all_active_variables();
// This is only a continue if we branch to our loop dominator.
if (loop_blocks.find(to) != end(loop_blocks) && get<SPIRBlock>(from).loop_dominator == to)
{
// This can happen if we had a complex continue block which was emitted.
// Once the continue block tries to branch to the loop header, just emit continue;
// and end the chain here.
statement("continue;");
} }
else if (is_break(to)) else if (is_break(to))
statement("break;"); statement("break;");
else if (is_continue(to))
branch_to_continue(from, to);
else if (!is_conditional(to)) else if (!is_conditional(to))
emit_block_chain(get<SPIRBlock>(to)); emit_block_chain(get<SPIRBlock>(to));
// It is important that we check for break before continue.
// A block might serve two purposes, a break block for the inner scope, and
// a continue block in the outer scope.
// Inner scope always takes precedence.
} }
void CompilerGLSL::branch(uint32_t from, uint32_t cond, uint32_t true_block, uint32_t false_block) void CompilerGLSL::branch(uint32_t from, uint32_t cond, uint32_t true_block, uint32_t false_block)
@ -8395,6 +8405,9 @@ void CompilerGLSL::branch(uint32_t from, uint32_t cond, uint32_t true_block, uin
bool true_sub = !is_conditional(true_block); bool true_sub = !is_conditional(true_block);
bool false_sub = !is_conditional(false_block); bool false_sub = !is_conditional(false_block);
// It is possible that a selection merge target also serves as a break/continue block.
// We will not emit break or continue here, but defer that to the outer scope.
if (true_sub) if (true_sub)
{ {
statement("if (", to_expression(cond), ")"); statement("if (", to_expression(cond), ")");
@ -8420,7 +8433,7 @@ void CompilerGLSL::branch(uint32_t from, uint32_t cond, uint32_t true_block, uin
else if (false_sub && !true_sub) else if (false_sub && !true_sub)
{ {
// Only need false path, use negative conditional. // Only need false path, use negative conditional.
statement("if (!", to_expression(cond), ")"); statement("if (!", to_enclosed_expression(cond), ")");
begin_scope(); begin_scope();
branch(from, false_block); branch(from, false_block);
end_scope(); end_scope();
@ -8959,6 +8972,22 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
// that block after this. If we had selection merge, we already flushed phi variables. // that block after this. If we had selection merge, we already flushed phi variables.
if (block.merge != SPIRBlock::MergeSelection) if (block.merge != SPIRBlock::MergeSelection)
flush_phi(block.self, block.next_block); flush_phi(block.self, block.next_block);
// For merge selects we might have ignored the fact that a merge target
// could have been a break; or continue;
// We will need to deal with it here.
if (is_loop_break(block.next_block))
{
// Cannot check for just break, because switch statements will also use break.
assert(block.merge == SPIRBlock::MergeSelection);
statement("break;");
}
else if (is_continue(block.next_block))
{
assert(block.merge == SPIRBlock::MergeSelection);
branch_to_continue(block.self, block.next_block);
}
else
emit_block_chain(get<SPIRBlock>(block.next_block)); emit_block_chain(get<SPIRBlock>(block.next_block));
} }
@ -8982,7 +9011,12 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
else else
end_scope(); end_scope();
flush_phi(block.self, block.merge_block); // We cannot break out of two loops at once, so don't check for break; here.
// Using block.self as the "from" block isn't quite right, but it has the same scope
// and dominance structure, so it's fine.
if (is_continue(block.merge_block))
branch_to_continue(block.self, block.merge_block);
else
emit_block_chain(get<SPIRBlock>(block.merge_block)); emit_block_chain(get<SPIRBlock>(block.merge_block));
} }
} }

Просмотреть файл

@ -356,6 +356,7 @@ protected:
void propagate_loop_dominators(const SPIRBlock &block); void propagate_loop_dominators(const SPIRBlock &block);
void branch(uint32_t from, uint32_t to); void branch(uint32_t from, uint32_t to);
void branch_to_continue(uint32_t from, uint32_t to);
void branch(uint32_t from, uint32_t cond, uint32_t true_block, uint32_t false_block); void branch(uint32_t from, uint32_t cond, uint32_t true_block, uint32_t false_block);
void flush_phi(uint32_t from, uint32_t to); void flush_phi(uint32_t from, uint32_t to);
bool flush_phi_required(uint32_t from, uint32_t to); bool flush_phi_required(uint32_t from, uint32_t to);