Preserve arguments with inout unless complete writes are made.

This commit is contained in:
Hans-Kristian Arntzen 2017-08-09 17:05:51 +02:00
parent 14cd000f3d
commit 744d0405b0
8 changed files with 377 additions and 6 deletions

View File

@ -0,0 +1,78 @@
struct B
{
float a;
float b;
};
struct _UBO
{
int some_value;
};
cbuffer UBO : register(c0)
{
_UBO _42;
};
void partial_inout(inout float4 x)
{
x.x = 10.0f;
}
void complete_inout(out float4 x)
{
x = float4(50.0f, 50.0f, 50.0f, 50.0f);
}
void branchy_inout(inout float4 v)
{
v.y = 20.0f;
if (_42.some_value == 20)
{
v = float4(50.0f, 50.0f, 50.0f, 50.0f);
}
}
void branchy_inout_2(out float4 v)
{
if (_42.some_value == 20)
{
v = float4(50.0f, 50.0f, 50.0f, 50.0f);
}
else
{
v = float4(70.0f, 70.0f, 70.0f, 70.0f);
}
v.y = 20.0f;
}
void partial_inout(inout B b)
{
b.b = 40.0f;
}
void frag_main()
{
float4 a = float4(10.0f, 10.0f, 10.0f, 10.0f);
float4 param = a;
partial_inout(param);
a = param;
float4 param_1;
complete_inout(param_1);
a = param_1;
float4 param_2 = a;
branchy_inout(param_2);
a = param_2;
float4 param_3;
branchy_inout_2(param_3);
a = param_3;
B b = { 10.0f, 20.0f };
B param_4 = b;
partial_inout(param_4);
b = param_4;
}
void main()
{
frag_main();
}

View File

@ -24,7 +24,7 @@ layout(binding = 2, std430) readonly buffer SSBO3
Foo foos[];
} foobar;
void baz(out Foo foo)
void baz(inout Foo foo)
{
uint ident = gl_GlobalInvocationID.x;
foo.a = indata.data[(4u * ident) + 0u];

View File

@ -9,7 +9,7 @@ struct Structy
layout(location = 0) out vec4 FragColor;
void foo2(out Structy f)
void foo2(inout Structy f)
{
f.c = vec4(10.0);
}

View File

@ -0,0 +1,109 @@
#version 310 es
precision mediump float;
precision highp int;
struct B
{
float a;
float b;
};
layout(binding = 0, std140) uniform UBO
{
mediump int some_value;
} _51;
void partial_inout(inout vec4 x)
{
x.x = 10.0;
}
void complete_inout(out vec4 x)
{
x = vec4(50.0);
}
void branchy_inout(inout vec4 v)
{
v.y = 20.0;
if (_51.some_value == 20)
{
v = vec4(50.0);
}
}
void branchy_inout_2(out vec4 v)
{
if (_51.some_value == 20)
{
v = vec4(50.0);
}
else
{
v = vec4(70.0);
}
v.y = 20.0;
}
void partial_inout(inout B b)
{
b.b = 40.0;
}
void complete_inout(out B b)
{
b = B(100.0, 200.0);
}
void branchy_inout(inout B b)
{
b.b = 20.0;
if (_51.some_value == 20)
{
b = B(10.0, 40.0);
}
}
void branchy_inout_2(out B b)
{
if (_51.some_value == 20)
{
b = B(10.0, 40.0);
}
else
{
b = B(70.0, 70.0);
}
b.b = 20.0;
}
void main()
{
vec4 a = vec4(10.0);
highp vec4 param = a;
partial_inout(param);
a = param;
highp vec4 param_1;
complete_inout(param_1);
a = param_1;
highp vec4 param_2 = a;
branchy_inout(param_2);
a = param_2;
highp vec4 param_3;
branchy_inout_2(param_3);
a = param_3;
B b = B(10.0, 20.0);
B param_4 = b;
partial_inout(param_4);
b = param_4;
B param_5;
complete_inout(param_5);
b = param_5;
B param_6 = b;
branchy_inout(param_6);
b = param_6;
B param_7;
branchy_inout_2(param_7);
b = param_7;
}

View File

@ -0,0 +1,64 @@
#version 310 es
precision mediump float;
layout(std140, binding = 0) uniform UBO
{
int some_value;
};
struct B
{
float a;
float b;
};
void partial_inout(inout vec4 x)
{
x.x = 10.0;
}
void partial_inout(inout B b)
{
b.b = 40.0;
}
// Make a complete write, but only conditionally ...
void branchy_inout(inout vec4 v)
{
v.y = 20.0;
if (some_value == 20)
{
v = vec4(50.0);
}
}
void branchy_inout_2(out vec4 v)
{
if (some_value == 20)
{
v = vec4(50.0);
}
else
{
v = vec4(70.0);
}
v.y = 20.0;
}
void complete_inout(out vec4 x)
{
x = vec4(50.0);
}
void main()
{
vec4 a = vec4(10.0);
partial_inout(a);
complete_inout(a);
branchy_inout(a);
branchy_inout_2(a);
B b = B(10.0, 20.0);
partial_inout(b);
}

View File

@ -0,0 +1,95 @@
#version 310 es
precision mediump float;
layout(std140, binding = 0) uniform UBO
{
int some_value;
};
struct B
{
float a;
float b;
};
void partial_inout(inout vec4 x)
{
x.x = 10.0;
}
void partial_inout(inout B b)
{
b.b = 40.0;
}
// Make a complete write, but only conditionally ...
void branchy_inout(inout vec4 v)
{
v.y = 20.0;
if (some_value == 20)
{
v = vec4(50.0);
}
}
void branchy_inout(inout B b)
{
b.b = 20.0;
if (some_value == 20)
{
b = B(10.0, 40.0);
}
}
void branchy_inout_2(out vec4 v)
{
if (some_value == 20)
{
v = vec4(50.0);
}
else
{
v = vec4(70.0);
}
v.y = 20.0;
}
void branchy_inout_2(out B b)
{
if (some_value == 20)
{
b = B(10.0, 40.0);
}
else
{
b = B(70.0, 70.0);
}
b.b = 20.0;
}
void complete_inout(out vec4 x)
{
x = vec4(50.0);
}
void complete_inout(out B b)
{
b = B(100.0, 200.0);
}
void main()
{
vec4 a = vec4(10.0);
partial_inout(a);
complete_inout(a);
branchy_inout(a);
branchy_inout_2(a);
B b = B(10.0, 20.0);
partial_inout(b);
complete_inout(b);
branchy_inout(b);
branchy_inout_2(b);
}

View File

@ -2923,7 +2923,8 @@ static bool exists_unaccessed_path_to_return(const CFG &cfg, uint32_t block, con
}
void Compiler::analyze_parameter_preservation(
SPIRFunction &entry, const CFG &cfg, const unordered_map<uint32_t, unordered_set<uint32_t>> &variable_to_blocks)
SPIRFunction &entry, const CFG &cfg, const unordered_map<uint32_t, unordered_set<uint32_t>> &variable_to_blocks,
const unordered_map<uint32_t, unordered_set<uint32_t>> &complete_write_blocks)
{
for (auto &arg : entry.arguments)
{
@ -2958,7 +2959,16 @@ void Compiler::analyze_parameter_preservation(
continue;
}
// If there is a path through the CFG where no block writes to the variable, the variable will be in an undefined state
// We have accessed a variable, but there was no complete writes to that variable.
// We deduce that we must preserve the argument.
itr = complete_write_blocks.find(arg.id);
if (itr == end(complete_write_blocks))
{
arg.read_count++;
continue;
}
// If there is a path through the CFG where no block completely writes to the variable, the variable will be in an undefined state
// when the function returns. We therefore need to implicitly preserve the variable in case there are writers in the function.
// Major case here is if a function is
// void foo(int &var) { if (cond) var = 10; }
@ -3036,6 +3046,10 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry)
auto *var = compiler.maybe_get_backing_variable(ptr);
if (var && var->storage == StorageClassFunction)
accessed_variables_to_block[var->self].insert(current_block->self);
// If we store through an access chain, we have a partial write.
if (var && var->self == ptr && var->storage == StorageClassFunction)
complete_write_variables_to_block[var->self].insert(current_block->self);
break;
}
@ -3063,6 +3077,10 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry)
if (var && var->storage == StorageClassFunction)
accessed_variables_to_block[var->self].insert(current_block->self);
// If we store through an access chain, we have a partial write.
if (var->self == lhs)
complete_write_variables_to_block[var->self].insert(current_block->self);
var = compiler.maybe_get_backing_variable(rhs);
if (var && var->storage == StorageClassFunction)
accessed_variables_to_block[var->self].insert(current_block->self);
@ -3103,6 +3121,10 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry)
auto *var = compiler.maybe_get_backing_variable(args[i]);
if (var && var->storage == StorageClassFunction)
accessed_variables_to_block[var->self].insert(current_block->self);
// Cannot easily prove if argument we pass to a function is completely written.
// Usually, functions write to a dummy variable,
// which is then copied to in full to the real argument.
}
break;
}
@ -3128,6 +3150,7 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry)
Compiler &compiler;
std::unordered_map<uint32_t, std::unordered_set<uint32_t>> accessed_variables_to_block;
std::unordered_map<uint32_t, std::unordered_set<uint32_t>> complete_write_variables_to_block;
const SPIRBlock *current_block = nullptr;
} handler(*this);
@ -3139,7 +3162,8 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry)
CFG cfg(*this, entry);
// Analyze if there are parameters which need to be implicitly preserved with an "in" qualifier.
analyze_parameter_preservation(entry, cfg, handler.accessed_variables_to_block);
analyze_parameter_preservation(entry, cfg, handler.accessed_variables_to_block,
handler.complete_write_variables_to_block);
unordered_map<uint32_t, uint32_t> potential_loop_variables;

View File

@ -644,7 +644,8 @@ protected:
void analyze_parameter_preservation(
SPIRFunction &entry, const CFG &cfg,
const std::unordered_map<uint32_t, std::unordered_set<uint32_t>> &variable_to_blocks);
const std::unordered_map<uint32_t, std::unordered_set<uint32_t>> &variable_to_blocks,
const std::unordered_map<uint32_t, std::unordered_set<uint32_t>> &complete_write_blocks);
// If a variable ID or parameter ID is found in this set, a sampler is actually a shadow/comparison sampler.
// SPIR-V does not support this distinction, so we must keep track of this information outside the type system.