Distinguish between cross edges and back edges.

Handle the weird opcodes CopyMemory/CopyObject.
This commit is contained in:
Hans-Kristian Arntzen 2016-11-18 09:59:54 +01:00
Родитель edbe867b74
Коммит 0c9683cd85
4 изменённых файлов: 114 добавлений и 9 удалений

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

@ -70,22 +70,31 @@ void CFG::build_immediate_dominators()
for (auto &edge : pred)
{
if (!immediate_dominators[block])
immediate_dominators[block] = edge;
else
if (immediate_dominators[block])
{
assert(immediate_dominators[edge]);
immediate_dominators[block] = update_common_dominator(block, edge);
}
else
immediate_dominators[block] = edge;
}
}
}
bool CFG::is_back_edge(uint32_t to) const
{
// We have a back edge if the visit order is set with the temporary magic value 0.
// Crossing edges will have already been recorded with a visit order.
return visit_order[to] == 0;
}
bool CFG::post_order_visit(uint32_t block_id)
{
// If we have already branched to this block (back edge), stop recursion.
// If our branches are back-edges, we do not record them.
// We have to record crossing edges however.
if (visit_order[block_id] >= 0)
return false;
return !is_back_edge(block_id);
// Block back-edges from recursively revisiting ourselves.
visit_order[block_id] = 0;
@ -120,8 +129,8 @@ bool CFG::post_order_visit(uint32_t block_id)
break;
}
// Then visit ourselves.
visit_order[block_id] = visit_count++;
// Then visit ourselves. Start counting at one, to let 0 be a magic value for testing back vs. crossing edges.
visit_order[block_id] = ++visit_count;
post_order.push_back(block_id);
return true;
}

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

@ -59,6 +59,7 @@ private:
uint32_t visit_count = 0;
uint32_t update_common_dominator(uint32_t a, uint32_t b);
bool is_back_edge(uint32_t to) const;
};
class DominatorBuilder

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

@ -82,6 +82,7 @@ bool Compiler::block_is_pure(const SPIRBlock &block)
break;
}
case OpCopyMemory:
case OpStore:
{
auto &type = expression_type(ops[0]);
@ -486,9 +487,25 @@ bool Compiler::InterfaceVariableAccessHandler::handle(Op opcode, const uint32_t
variable = args[0];
break;
case OpCopyMemory:
{
if (length < 3)
return false;
auto *var = compiler.maybe_get<SPIRVariable>(args[0]);
if (var && storage_class_is_interface(var->storage))
variables.insert(variable);
var = compiler.maybe_get<SPIRVariable>(args[1]);
if (var && storage_class_is_interface(var->storage))
variables.insert(variable);
break;
}
case OpAccessChain:
case OpInBoundsAccessChain:
case OpLoad:
case OpCopyObject:
case OpImageTexelPointer:
case OpAtomicLoad:
case OpAtomicExchange:
@ -2725,6 +2742,7 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry)
: compiler(compiler_)
{
}
bool follow_function_call(const SPIRFunction &)
{
// Only analyze within this function.
@ -2798,6 +2816,34 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry)
break;
}
case OpCopyMemory:
{
if (length < 3)
return false;
uint32_t lhs = args[0];
uint32_t rhs = args[1];
auto *var = compiler.maybe_get_backing_variable(lhs);
if (var && var->storage == StorageClassFunction)
accessed_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);
break;
}
case OpCopyObject:
{
if (length < 3)
return false;
auto *var = compiler.maybe_get_backing_variable(args[2]);
if (var && var->storage == StorageClassFunction)
accessed_variables_to_block[var->self].insert(current_block->self);
break;
}
case OpLoad:
{
if (length < 3)
@ -2809,7 +2855,34 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry)
break;
}
// TODO: OpFunctionCall and other opcodes which access variables.
case OpFunctionCall:
{
if (length < 3)
return false;
length -= 3;
args += 3;
for (uint32_t i = 0; i < length; i++)
{
auto *var = compiler.maybe_get_backing_variable(args[i]);
if (var && var->storage == StorageClassFunction)
accessed_variables_to_block[var->self].insert(current_block->self);
}
break;
}
case OpPhi:
{
if (length < 2)
return false;
// Phi nodes are implemented as function variables, so register an access here.
accessed_variables_to_block[args[1]].insert(current_block->self);
break;
}
// Atomics shouldn't be able to access function-local variables.
// Some GLSL builtins access a pointer.
default:
break;

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

@ -3678,14 +3678,31 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
break;
}
case OpCopyMemory:
{
uint32_t lhs = ops[0];
uint32_t rhs = ops[1];
if (lhs != rhs)
{
flush_variable_declaration(lhs);
flush_variable_declaration(rhs);
statement(to_expression(lhs), " = ", to_expression(rhs), ";");
register_write(lhs);
}
break;
}
case OpCopyObject:
{
uint32_t result_type = ops[0];
uint32_t id = ops[1];
uint32_t rhs = ops[2];
if (expression_is_lvalue(rhs))
bool pointer = get<SPIRType>(result_type).pointer;
if (expression_is_lvalue(rhs) && !pointer)
{
// Need a copy.
// For pointer types, we copy the pointer itself.
statement(declare_temporary(result_type, id), to_expression(rhs), ";");
set<SPIRExpression>(id, to_name(id), result_type, true);
}
@ -3694,7 +3711,12 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
// RHS expression is immutable, so just forward it.
// Copying these things really make no sense, but
// seems to be allowed anyways.
set<SPIRExpression>(id, to_expression(rhs), result_type, true);
auto &e = set<SPIRExpression>(id, to_expression(rhs), result_type, true);
if (pointer)
{
auto *var = maybe_get_backing_variable(rhs);
e.loaded_from = var ? var->self : 0;
}
}
break;
}