Handle more corner cases with the CFG traversal.
This commit is contained in:
Родитель
0c9683cd85
Коммит
5ff11cc689
|
@ -0,0 +1,82 @@
|
||||||
|
#version 310 es
|
||||||
|
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||||
|
|
||||||
|
layout(binding = 0, std430) buffer SSBO
|
||||||
|
{
|
||||||
|
float data;
|
||||||
|
} _11;
|
||||||
|
|
||||||
|
void test()
|
||||||
|
{
|
||||||
|
float m;
|
||||||
|
if ((_11.data != 0.0))
|
||||||
|
{
|
||||||
|
float tmp = 10.0;
|
||||||
|
_11.data = tmp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float tmp_1 = 15.0;
|
||||||
|
_11.data = tmp_1;
|
||||||
|
}
|
||||||
|
if ((_11.data != 0.0))
|
||||||
|
{
|
||||||
|
float e;
|
||||||
|
if ((_11.data != 5.0))
|
||||||
|
{
|
||||||
|
if ((_11.data != 6.0))
|
||||||
|
{
|
||||||
|
e = 10.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e = 20.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (int(_11.data))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
float tmp_2 = 20.0;
|
||||||
|
_11.data = tmp_2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
float tmp_3 = 30.0;
|
||||||
|
_11.data = tmp_3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float f;
|
||||||
|
switch (int(_11.data))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
f = 30.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
f = 40.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
float h;
|
||||||
|
for (; (i < 20); i = (i + 1), h = (h + 10.0))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
_11.data = h;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
} while ((m != 20.0));
|
||||||
|
_11.data = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
test();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
#version 310 es
|
||||||
|
layout(local_size_x = 1) in;
|
||||||
|
|
||||||
|
layout(std430, binding = 0) buffer SSBO
|
||||||
|
{
|
||||||
|
float data;
|
||||||
|
};
|
||||||
|
|
||||||
|
void test()
|
||||||
|
{
|
||||||
|
// Test that variables local to a scope stay local.
|
||||||
|
if (data != 0.0)
|
||||||
|
{
|
||||||
|
float tmp = 10.0;
|
||||||
|
data = tmp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float tmp = 15.0;
|
||||||
|
data = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that variable access propagates up to dominator
|
||||||
|
if (data != 0.0)
|
||||||
|
{
|
||||||
|
float e;
|
||||||
|
if (data != 5.0)
|
||||||
|
{
|
||||||
|
if (data != 6.0)
|
||||||
|
e = 10.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
e = 20.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that variables local to a switch block stay local.
|
||||||
|
switch (int(data))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
float tmp = 20.0;
|
||||||
|
data = tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
float tmp = 30.0;
|
||||||
|
data = tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that multibranches propagate up to dominator.
|
||||||
|
float f;
|
||||||
|
switch (int(data))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
f = 30.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
f = 40.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that loops work.
|
||||||
|
// Interesting case here is propagating variable access from the continue block.
|
||||||
|
float h;
|
||||||
|
for (int i = 0; i < 20; i++, h += 10.0)
|
||||||
|
;
|
||||||
|
data = h;
|
||||||
|
|
||||||
|
// Do the same with do-while, gotta test all the hard cases.
|
||||||
|
float m;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
} while (m != 20.0);
|
||||||
|
data = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Test that we do the CFG analysis for all functions.
|
||||||
|
test();
|
||||||
|
}
|
||||||
|
|
|
@ -177,4 +177,53 @@ void DominatorBuilder::add_block(uint32_t block)
|
||||||
if (block != dominator)
|
if (block != dominator)
|
||||||
dominator = cfg.find_common_dominator(block, dominator);
|
dominator = cfg.find_common_dominator(block, dominator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DominatorBuilder::lift_continue_block_dominator()
|
||||||
|
{
|
||||||
|
// It is possible for a continue block to be the dominator if a variable is only accessed inside the while block of a do-while loop.
|
||||||
|
// We cannot safely declare variables inside a continue block, so move any variable declared
|
||||||
|
// in a continue block to the entry block to simplify.
|
||||||
|
// It makes very little sense for a continue block to ever be a dominator, so fall back to the simplest
|
||||||
|
// solution.
|
||||||
|
|
||||||
|
if (!dominator)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto &block = cfg.get_compiler().get<SPIRBlock>(dominator);
|
||||||
|
auto post_order = cfg.get_visit_order(dominator);
|
||||||
|
|
||||||
|
// If we are branching to a block with a higher post-order traversal index (continue blocks), we have a problem
|
||||||
|
// since we cannot create sensible GLSL code for this, fallback to entry block.
|
||||||
|
bool back_edge_dominator = false;
|
||||||
|
switch (block.terminator)
|
||||||
|
{
|
||||||
|
case SPIRBlock::Direct:
|
||||||
|
if (cfg.get_visit_order(block.next_block) > post_order)
|
||||||
|
back_edge_dominator = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPIRBlock::Select:
|
||||||
|
if (cfg.get_visit_order(block.true_block) > post_order)
|
||||||
|
back_edge_dominator = true;
|
||||||
|
if (cfg.get_visit_order(block.false_block) > post_order)
|
||||||
|
back_edge_dominator = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPIRBlock::MultiSelect:
|
||||||
|
for (auto &target : block.cases)
|
||||||
|
{
|
||||||
|
if (cfg.get_visit_order(target.block) > post_order)
|
||||||
|
back_edge_dominator = true;
|
||||||
|
}
|
||||||
|
if (block.default_block && cfg.get_visit_order(block.default_block) > post_order)
|
||||||
|
back_edge_dominator = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (back_edge_dominator)
|
||||||
|
dominator = cfg.get_function().entry_block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#define SPIRV_CROSS_CFG_HPP
|
#define SPIRV_CROSS_CFG_HPP
|
||||||
|
|
||||||
#include "spirv_cross.hpp"
|
#include "spirv_cross.hpp"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
namespace spirv_cross
|
namespace spirv_cross
|
||||||
{
|
{
|
||||||
|
@ -31,14 +32,26 @@ public:
|
||||||
return compiler;
|
return compiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Compiler &get_compiler() const
|
||||||
|
{
|
||||||
|
return compiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SPIRFunction &get_function() const
|
||||||
|
{
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t get_immediate_dominator(uint32_t block) const
|
uint32_t get_immediate_dominator(uint32_t block) const
|
||||||
{
|
{
|
||||||
return immediate_dominators[block];
|
return immediate_dominators[block];
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t get_post_order(uint32_t block) const
|
uint32_t get_visit_order(uint32_t block) const
|
||||||
{
|
{
|
||||||
return post_order[block];
|
int v = visit_order[block];
|
||||||
|
assert(v > 0);
|
||||||
|
return uint32_t(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t find_common_dominator(uint32_t a, uint32_t b) const;
|
uint32_t find_common_dominator(uint32_t a, uint32_t b) const;
|
||||||
|
@ -73,6 +86,8 @@ public:
|
||||||
return dominator;
|
return dominator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lift_continue_block_dominator();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const CFG &cfg;
|
const CFG &cfg;
|
||||||
uint32_t dominator = 0;
|
uint32_t dominator = 0;
|
||||||
|
|
|
@ -2912,6 +2912,8 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry)
|
||||||
for (auto &block : blocks)
|
for (auto &block : blocks)
|
||||||
builder.add_block(block);
|
builder.add_block(block);
|
||||||
|
|
||||||
|
builder.lift_continue_block_dominator();
|
||||||
|
|
||||||
// Add it to a per-block list of variables.
|
// Add it to a per-block list of variables.
|
||||||
uint32_t dominating_block = builder.get_dominator();
|
uint32_t dominating_block = builder.get_dominator();
|
||||||
// If all blocks here are dead code, this will be 0, so the variable in question
|
// If all blocks here are dead code, this will be 0, so the variable in question
|
||||||
|
|
|
@ -110,6 +110,7 @@ class Compiler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
friend class CFG;
|
friend class CFG;
|
||||||
|
friend class DominatorBuilder;
|
||||||
|
|
||||||
// The constructor takes a buffer of SPIR-V words and parses it.
|
// The constructor takes a buffer of SPIR-V words and parses it.
|
||||||
Compiler(std::vector<uint32_t> ir);
|
Compiler(std::vector<uint32_t> ir);
|
||||||
|
|
|
@ -5508,9 +5508,13 @@ bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method
|
||||||
}
|
}
|
||||||
else if (method == SPIRBlock::MergeToDirectForLoop)
|
else if (method == SPIRBlock::MergeToDirectForLoop)
|
||||||
{
|
{
|
||||||
uint32_t current_count = statement_count;
|
|
||||||
auto &child = get<SPIRBlock>(block.next_block);
|
auto &child = get<SPIRBlock>(block.next_block);
|
||||||
|
|
||||||
|
// This block may be a dominating block, so make sure we flush undeclared variables before building the for loop header.
|
||||||
|
flush_undeclared_variables(child);
|
||||||
|
|
||||||
|
uint32_t current_count = statement_count;
|
||||||
|
|
||||||
// If we're trying to create a true for loop,
|
// If we're trying to create a true for loop,
|
||||||
// we need to make sure that all opcodes before branch statement do not actually emit any code.
|
// we need to make sure that all opcodes before branch statement do not actually emit any code.
|
||||||
// We can then take the condition expression and create a for (; cond ; ) { body; } structure instead.
|
// We can then take the condition expression and create a for (; cond ; ) { body; } structure instead.
|
||||||
|
|
Загрузка…
Ссылка в новой задаче